evann0809jeux commited on
Commit
fd5a6a2
·
verified ·
1 Parent(s): d21441a

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +366 -70
index.html CHANGED
@@ -3,9 +3,10 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>3D Equation Solver with Parameter Visualization</title>
7
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
8
  <script src="https://cdn.jsdelivr.net/npm/mathjs@9.5.1/lib/browser/math.js"></script>
 
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
10
  <style>
11
  :root {
@@ -15,6 +16,9 @@
15
  --light-color: #ecf0f1;
16
  --success-color: #2ecc71;
17
  --warning-color: #f39c12;
 
 
 
18
  }
19
 
20
  * {
@@ -82,6 +86,11 @@
82
  #graph3d {
83
  width: 100%;
84
  height: 100%;
 
 
 
 
 
85
  }
86
 
87
  .form-group {
@@ -276,6 +285,61 @@
276
  font-size: 14px;
277
  }
278
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  @media (max-width: 768px) {
280
  .calculator {
281
  grid-template-columns: 1fr;
@@ -284,14 +348,20 @@
284
  .range-group {
285
  grid-template-columns: 1fr;
286
  }
 
 
 
 
 
 
287
  }
288
  </style>
289
  </head>
290
  <body>
291
  <div class="container">
292
  <header>
293
- <h1><i class="fas fa-calculator"></i> 3D Equation Solver</h1>
294
- <p class="subtitle">Visualize solutions for equations with parameter 'a'</p>
295
  </header>
296
 
297
  <div class="calculator">
@@ -322,7 +392,7 @@
322
  </div>
323
 
324
  <div class="form-group">
325
- <label for="x-range">x-axis range:</label>
326
  <div class="range-group">
327
  <div>
328
  <label for="x-min">Min</label>
@@ -335,17 +405,38 @@
335
  </div>
336
  </div>
337
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
  <button id="calculate-btn" class="btn btn-block">
339
  <i class="fas fa-chart-line"></i> Calculate & Visualize
340
  </button>
341
 
 
 
 
 
 
 
 
342
  <div class="examples">
343
  <h3>Example equations:</h3>
344
  <div class="example-item" onclick="setExample('x^2 + a*x - 5 = 0')">x² + a·x - 5 = 0 (Quadratic)</div>
345
- <div class="example-item" onclick="setExample('sin(x) + a = 0')">sin(x) + a = 0 (Trigonometric)</div>
346
- <div class="example-item" onclick="setExample('a*x^3 - x^2 + 2*a = 0')">a·x³ - + 2a = 0 (Cubic)</div>
347
- <div class="example-item" onclick="setExample('exp(x) - a*x = 0')"> - a·x = 0 (Exponential)</div>
348
- <div class="example-item" onclick="setExample('log(x + 1) - a = 0')">ln(x + 1) - a = 0 (Logarithmic)</div>
349
  </div>
350
  </div>
351
 
@@ -363,6 +454,24 @@
363
  <button class="graph-btn" id="toggle-rotation" title="Toggle rotation">
364
  <i class="fas fa-sync-alt"></i>
365
  </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  </div>
367
  </div>
368
  </div>
@@ -378,10 +487,12 @@
378
 
379
  <script>
380
  // Global variables
381
- let scene, camera, renderer, graphMesh;
382
  let rotationEnabled = false;
 
383
  let animationId = null;
384
  let solutionsData = [];
 
385
 
386
  // Initialize Three.js scene
387
  function initScene() {
@@ -401,13 +512,16 @@
401
  renderer.setSize(container.clientWidth, container.clientHeight);
402
  container.appendChild(renderer.domElement);
403
 
404
- // Add axes helper
405
- const axesHelper = new THREE.AxesHelper(5);
406
- scene.add(axesHelper);
 
 
 
 
407
 
408
- // Add grid helper
409
- const gridHelper = new THREE.GridHelper(10, 10);
410
- scene.add(gridHelper);
411
 
412
  // Add lights
413
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
@@ -429,16 +543,118 @@
429
  animationId = requestAnimationFrame(animate);
430
 
431
  if (rotationEnabled) {
432
- scene.rotation.y += 0.005;
 
 
 
433
  }
434
 
 
435
  renderer.render(scene, camera);
436
  }
437
 
438
  animate();
439
  }
440
 
441
- // Parse equation and solve for x
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
  function solveEquation(equationStr, aValue) {
443
  try {
444
  // Parse the equation (remove spaces and split on '=')
@@ -465,60 +681,74 @@
465
  return solutions.map(sol => {
466
  // Handle complex numbers
467
  if (typeof sol === 'object' && sol.re !== undefined && sol.im !== undefined) {
468
- if (math.abs(sol.im) < 1e-10) {
469
- return sol.re;
470
- } else {
471
- return null; // Filter out complex solutions
472
- }
 
473
  }
474
- return math.evaluate(sol.toString(), scope);
475
- }).filter(val => val !== null);
476
  }
477
  } catch (e) {
478
  // If symbolic solving fails, try numerical methods
479
  }
480
 
481
- // Numerical solving using Newton's method
482
  const f = (x) => {
483
  const scope = { a: aValue, x: x };
484
  return math.evaluate(exprToSolve, scope);
485
  };
486
 
487
- const df = (x) => {
488
- const h = 0.0001;
489
- return (f(x + h) - f(x - h)) / (2 * h);
490
- };
491
-
492
  const solutions = [];
493
  const xMin = parseFloat(document.getElementById('x-min').value);
494
  const xMax = parseFloat(document.getElementById('x-max').value);
495
- const steps = 100;
496
- const stepSize = (xMax - xMin) / steps;
 
 
 
497
 
498
- // Find potential roots by checking sign changes
499
- for (let i = 0; i < steps; i++) {
500
- const x1 = xMin + i * stepSize;
501
- const x2 = x1 + stepSize;
502
- const y1 = f(x1);
503
- const y2 = f(x2);
504
-
505
- if (y1 * y2 < 0) {
506
- // Sign change detected, apply Newton's method
507
- let x = (x1 + x2) / 2;
508
- let prevX = x + 1;
509
  let iterations = 0;
510
 
511
- while (Math.abs(x - prevX) > 1e-10 && iterations < 100) {
512
- prevX = x;
513
- x = x - f(x) / df(x);
 
 
 
 
 
 
 
 
 
514
  iterations++;
515
  }
516
 
517
- if (iterations < 100) {
518
- // Check if this solution is already found (to avoid duplicates)
519
- const isDuplicate = solutions.some(sol => Math.abs(sol - x) < 1e-5);
520
- if (!isDuplicate) {
521
- solutions.push(x);
 
 
 
 
 
 
 
522
  }
523
  }
524
  }
@@ -548,7 +778,8 @@
548
  const aStep = (aMax - aMin) / steps;
549
 
550
  // Prepare data for graph
551
- const points = [];
 
552
  solutionsData = [];
553
 
554
  // Calculate solutions for each a value
@@ -563,23 +794,58 @@
563
  });
564
 
565
  // Add points to graph
566
- solutions.forEach(x => {
567
- points.push(new THREE.Vector3(x, a, 0));
 
 
 
 
 
 
568
  });
569
  }
570
 
571
- // Create geometry from points
572
- const geometry = new THREE.BufferGeometry().setFromPoints(points);
573
 
574
- // Create material
575
- const material = new THREE.PointsMaterial({
576
- color: 0xff6b6b,
577
- size: 0.1,
578
- sizeAttenuation: true
579
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
580
 
581
- // Create mesh and add to scene
582
- graphMesh = new THREE.Points(geometry, material);
583
  scene.add(graphMesh);
584
 
585
  // Update camera to fit the graph
@@ -613,6 +879,8 @@
613
 
614
  camera.position.set(center.x, center.y, cameraZ);
615
  camera.lookAt(center);
 
 
616
  }
617
 
618
  // Show solutions panel with calculated data
@@ -650,10 +918,19 @@
650
  valuesContainer.className = 'solution-values';
651
 
652
  item.solutions.forEach((sol, idx) => {
653
- const value = document.createElement('div');
654
- value.className = 'solution-value';
655
- value.textContent = `x${idx + 1} = ${sol.toFixed(4)}`;
656
- valuesContainer.appendChild(value);
 
 
 
 
 
 
 
 
 
657
  });
658
 
659
  solutionItem.appendChild(equation);
@@ -701,7 +978,7 @@
701
  const exprToSolve = `(${leftExpr}) - (${rightExpr})`;
702
 
703
  // Test evaluation
704
- math.evaluate(exprToSolve, { a: 1, x: 1 });
705
 
706
  errorElement.style.display = 'none';
707
  return true;
@@ -732,6 +1009,8 @@
732
  } else {
733
  camera.position.set(5, 5, 5);
734
  camera.lookAt(0, 0, 0);
 
 
735
  }
736
  });
737
 
@@ -746,6 +1025,23 @@
746
  }
747
  });
748
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
749
  // Handle Enter key in equation input
750
  document.getElementById('equation').addEventListener('keypress', (e) => {
751
  if (e.key === 'Enter') {
@@ -754,5 +1050,5 @@
754
  });
755
  });
756
  </script>
757
- <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
758
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Interactive 3D Complex Equation Solver</title>
7
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
8
  <script src="https://cdn.jsdelivr.net/npm/mathjs@9.5.1/lib/browser/math.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script>
10
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
11
  <style>
12
  :root {
 
16
  --light-color: #ecf0f1;
17
  --success-color: #2ecc71;
18
  --warning-color: #f39c12;
19
+ --real-color: #4a6bff; /* Blue for real axis */
20
+ --imaginary-color: #ff6b6b; /* Red for imaginary axis */
21
+ --parameter-color: #2ecc71; /* Green for parameter axis */
22
  }
23
 
24
  * {
 
86
  #graph3d {
87
  width: 100%;
88
  height: 100%;
89
+ cursor: grab;
90
+ }
91
+
92
+ #graph3d:active {
93
+ cursor: grabbing;
94
  }
95
 
96
  .form-group {
 
285
  font-size: 14px;
286
  }
287
 
288
+ .solution-value.real {
289
+ background-color: #e8f4ff;
290
+ border-left: 3px solid var(--real-color);
291
+ }
292
+
293
+ .solution-value.imaginary {
294
+ background-color: #ffebee;
295
+ border-left: 3px solid var(--imaginary-color);
296
+ }
297
+
298
+ .controls-info {
299
+ margin-top: 15px;
300
+ font-size: 13px;
301
+ color: #7f8c8d;
302
+ padding: 10px;
303
+ background-color: #f8f9fa;
304
+ border-radius: 6px;
305
+ }
306
+
307
+ .controls-info i {
308
+ margin-right: 5px;
309
+ color: var(--primary-color);
310
+ }
311
+
312
+ .axis-label {
313
+ position: absolute;
314
+ font-size: 14px;
315
+ font-weight: bold;
316
+ pointer-events: none;
317
+ }
318
+
319
+ .legend {
320
+ position: absolute;
321
+ bottom: 20px;
322
+ left: 20px;
323
+ background-color: rgba(255, 255, 255, 0.8);
324
+ padding: 10px;
325
+ border-radius: 5px;
326
+ font-size: 12px;
327
+ z-index: 10;
328
+ }
329
+
330
+ .legend-item {
331
+ display: flex;
332
+ align-items: center;
333
+ margin-bottom: 5px;
334
+ }
335
+
336
+ .legend-color {
337
+ width: 15px;
338
+ height: 15px;
339
+ margin-right: 8px;
340
+ border-radius: 3px;
341
+ }
342
+
343
  @media (max-width: 768px) {
344
  .calculator {
345
  grid-template-columns: 1fr;
 
348
  .range-group {
349
  grid-template-columns: 1fr;
350
  }
351
+
352
+ .legend {
353
+ bottom: 10px;
354
+ left: 10px;
355
+ font-size: 10px;
356
+ }
357
  }
358
  </style>
359
  </head>
360
  <body>
361
  <div class="container">
362
  <header>
363
+ <h1><i class="fas fa-calculator"></i> Complex 3D Equation Solver</h1>
364
+ <p class="subtitle">Visualize real and imaginary solutions with parameter 'a'</p>
365
  </header>
366
 
367
  <div class="calculator">
 
392
  </div>
393
 
394
  <div class="form-group">
395
+ <label for="x-range">x-axis range (real part):</label>
396
  <div class="range-group">
397
  <div>
398
  <label for="x-min">Min</label>
 
405
  </div>
406
  </div>
407
 
408
+ <div class="form-group">
409
+ <label for="y-range">y-axis range (imaginary part):</label>
410
+ <div class="range-group">
411
+ <div>
412
+ <label for="y-min">Min</label>
413
+ <input type="number" id="y-min" value="-5" step="0.1">
414
+ </div>
415
+ <div>
416
+ <label for="y-max">Max</label>
417
+ <input type="number" id="y-max" value="5" step="0.1">
418
+ </div>
419
+ </div>
420
+ </div>
421
+
422
  <button id="calculate-btn" class="btn btn-block">
423
  <i class="fas fa-chart-line"></i> Calculate & Visualize
424
  </button>
425
 
426
+ <div class="controls-info">
427
+ <p><i class="fas fa-mouse-pointer"></i> <strong>3D Controls:</strong></p>
428
+ <p><i class="fas fa-arrows-alt"></i> Left-click + drag to rotate</p>
429
+ <p><i class="fas fa-hand-paper"></i> Right-click + drag to pan</p>
430
+ <p><i class="fas fa-mouse"></i> Scroll to zoom</p>
431
+ </div>
432
+
433
  <div class="examples">
434
  <h3>Example equations:</h3>
435
  <div class="example-item" onclick="setExample('x^2 + a*x - 5 = 0')">x² + a·x - 5 = 0 (Quadratic)</div>
436
+ <div class="example-item" onclick="setExample('x^3 - a = 0')">x³ - a = 0 (Cubic)</div>
437
+ <div class="example-item" onclick="setExample('exp(x) - a = 0')"> - a = 0 (Exponential)</div>
438
+ <div class="example-item" onclick="setExample('sin(x) - a = 0')">sin(x) - a = 0 (Trigonometric)</div>
439
+ <div class="example-item" onclick="setExample('x^4 + a*x^2 + 1 = 0')">x + a·x² + 1 = 0 (Quartic)</div>
440
  </div>
441
  </div>
442
 
 
454
  <button class="graph-btn" id="toggle-rotation" title="Toggle rotation">
455
  <i class="fas fa-sync-alt"></i>
456
  </button>
457
+ <button class="graph-btn" id="toggle-imaginary" title="Toggle imaginary solutions">
458
+ <i class="fas fa-project-diagram"></i>
459
+ </button>
460
+ </div>
461
+
462
+ <div class="legend">
463
+ <div class="legend-item">
464
+ <div class="legend-color" style="background-color: var(--parameter-color);"></div>
465
+ <span>Parameter 'a' axis</span>
466
+ </div>
467
+ <div class="legend-item">
468
+ <div class="legend-color" style="background-color: var(--real-color);"></div>
469
+ <span>Real part of x</span>
470
+ </div>
471
+ <div class="legend-item">
472
+ <div class="legend-color" style="background-color: var(--imaginary-color);"></div>
473
+ <span>Imaginary part of x</span>
474
+ </div>
475
  </div>
476
  </div>
477
  </div>
 
487
 
488
  <script>
489
  // Global variables
490
+ let scene, camera, renderer, graphMesh, controls;
491
  let rotationEnabled = false;
492
+ let showImaginary = true;
493
  let animationId = null;
494
  let solutionsData = [];
495
+ let axisLabels = [];
496
 
497
  // Initialize Three.js scene
498
  function initScene() {
 
512
  renderer.setSize(container.clientWidth, container.clientHeight);
513
  container.appendChild(renderer.domElement);
514
 
515
+ // Add orbit controls
516
+ controls = new THREE.OrbitControls(camera, renderer.domElement);
517
+ controls.enableDamping = true;
518
+ controls.dampingFactor = 0.05;
519
+ controls.screenSpacePanning = false;
520
+ controls.minDistance = 1;
521
+ controls.maxDistance = 50;
522
 
523
+ // Add colored axes
524
+ createAxes();
 
525
 
526
  // Add lights
527
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
 
543
  animationId = requestAnimationFrame(animate);
544
 
545
  if (rotationEnabled) {
546
+ controls.autoRotate = true;
547
+ controls.autoRotateSpeed = 1.0;
548
+ } else {
549
+ controls.autoRotate = false;
550
  }
551
 
552
+ controls.update();
553
  renderer.render(scene, camera);
554
  }
555
 
556
  animate();
557
  }
558
 
559
+ // Create colored axes with labels
560
+ function createAxes() {
561
+ // Remove old axes if they exist
562
+ scene.children.forEach(child => {
563
+ if (child instanceof THREE.AxesHelper || child instanceof THREE.Line) {
564
+ scene.remove(child);
565
+ }
566
+ });
567
+
568
+ // Remove old labels
569
+ axisLabels.forEach(label => {
570
+ if (label.parentNode) {
571
+ label.parentNode.removeChild(label);
572
+ }
573
+ });
574
+ axisLabels = [];
575
+
576
+ const length = 5;
577
+ const headLength = 0.3;
578
+ const headWidth = 0.2;
579
+
580
+ // X-axis (Real part of x) - Blue
581
+ const xArrow = new THREE.ArrowHelper(
582
+ new THREE.Vector3(1, 0, 0),
583
+ new THREE.Vector3(0, 0, 0),
584
+ length,
585
+ '0x' + getComputedStyle(document.documentElement).getPropertyValue('--real-color').substring(1),
586
+ headLength,
587
+ headWidth
588
+ );
589
+ scene.add(xArrow);
590
+
591
+ // Y-axis (Parameter a) - Green
592
+ const yArrow = new THREE.ArrowHelper(
593
+ new THREE.Vector3(0, 1, 0),
594
+ new THREE.Vector3(0, 0, 0),
595
+ length,
596
+ '0x' + getComputedStyle(document.documentElement).getPropertyValue('--parameter-color').substring(1),
597
+ headLength,
598
+ headWidth
599
+ );
600
+ scene.add(yArrow);
601
+
602
+ // Z-axis (Imaginary part of x) - Red
603
+ const zArrow = new THREE.ArrowHelper(
604
+ new THREE.Vector3(0, 0, 1),
605
+ new THREE.Vector3(0, 0, 0),
606
+ length,
607
+ '0x' + getComputedStyle(document.documentElement).getPropertyValue('--imaginary-color').substring(1),
608
+ headLength,
609
+ headWidth
610
+ );
611
+ scene.add(zArrow);
612
+
613
+ // Add grid for real and parameter axes (X-Y plane)
614
+ const gridXY = new THREE.GridHelper(10, 10, 0xcccccc, 0xcccccc);
615
+ gridXY.rotation.x = Math.PI / 2;
616
+ scene.add(gridXY);
617
+
618
+ // Add grid for real and imaginary axes (X-Z plane)
619
+ const gridXZ = new THREE.GridHelper(10, 10, 0xcccccc, 0xcccccc);
620
+ scene.add(gridXZ);
621
+
622
+ // Create axis labels
623
+ createAxisLabel('Re(x)', length + 0.5, 0, 0, 'var(--real-color)');
624
+ createAxisLabel('a', 0, length + 0.5, 0, 'var(--parameter-color)');
625
+ createAxisLabel('Im(x)', 0, 0, length + 0.5, 'var(--imaginary-color)');
626
+ }
627
+
628
+ // Create axis label
629
+ function createAxisLabel(text, x, y, z, color) {
630
+ const label = document.createElement('div');
631
+ label.className = 'axis-label';
632
+ label.textContent = text;
633
+ label.style.color = color;
634
+ document.getElementById('graph3d').appendChild(label);
635
+ axisLabels.push(label);
636
+
637
+ function updatePosition() {
638
+ const vector = new THREE.Vector3(x, y, z);
639
+ vector.project(camera);
640
+
641
+ const elementWidth = label.offsetWidth;
642
+ const elementHeight = label.offsetHeight;
643
+
644
+ const xPos = (vector.x * 0.5 + 0.5) * renderer.domElement.clientWidth - elementWidth / 2;
645
+ const yPos = (-(vector.y * 0.5) + 0.5) * renderer.domElement.clientHeight - elementHeight / 2;
646
+
647
+ label.style.transform = `translate(${xPos}px, ${yPos}px)`;
648
+ }
649
+
650
+ // Initial position
651
+ updatePosition();
652
+
653
+ // Update on camera change
654
+ controls.addEventListener('change', updatePosition);
655
+ }
656
+
657
+ // Parse equation and solve for x (including complex solutions)
658
  function solveEquation(equationStr, aValue) {
659
  try {
660
  // Parse the equation (remove spaces and split on '=')
 
681
  return solutions.map(sol => {
682
  // Handle complex numbers
683
  if (typeof sol === 'object' && sol.re !== undefined && sol.im !== undefined) {
684
+ return { re: sol.re, im: sol.im };
685
+ }
686
+ // Real solutions
687
+ const value = math.evaluate(sol.toString(), scope);
688
+ if (typeof value === 'object' && value.re !== undefined && value.im !== undefined) {
689
+ return value;
690
  }
691
+ return { re: value, im: 0 };
692
+ });
693
  }
694
  } catch (e) {
695
  // If symbolic solving fails, try numerical methods
696
  }
697
 
698
+ // Numerical solving using Newton's method for complex numbers
699
  const f = (x) => {
700
  const scope = { a: aValue, x: x };
701
  return math.evaluate(exprToSolve, scope);
702
  };
703
 
 
 
 
 
 
704
  const solutions = [];
705
  const xMin = parseFloat(document.getElementById('x-min').value);
706
  const xMax = parseFloat(document.getElementById('x-max').value);
707
+ const yMin = parseFloat(document.getElementById('y-min').value);
708
+ const yMax = parseFloat(document.getElementById('y-max').value);
709
+ const steps = 20; // Reduced for performance
710
+ const xStep = (xMax - xMin) / steps;
711
+ const yStep = (yMax - yMin) / steps;
712
 
713
+ // Grid search for initial guesses
714
+ for (let i = 0; i <= steps; i++) {
715
+ for (let j = 0; j <= steps; j++) {
716
+ const xRe = xMin + i * xStep;
717
+ const xIm = yMin + j * yStep;
718
+ const x = math.complex(xRe, xIm);
719
+
720
+ // Apply Newton's method
721
+ let prevX = math.complex(Infinity, Infinity);
722
+ let currentX = x;
 
723
  let iterations = 0;
724
 
725
+ while (math.abs(math.subtract(currentX, prevX)) > 1e-10 && iterations < 50) {
726
+ prevX = currentX;
727
+
728
+ // Complex derivative using finite differences
729
+ const h = math.complex(1e-8, 1e-8);
730
+ const fVal = f(currentX);
731
+ const fValH = f(math.add(currentX, h));
732
+ const derivative = math.divide(math.subtract(fValH, fVal), h);
733
+
734
+ if (math.abs(derivative) < 1e-10) break; // Avoid division by zero
735
+
736
+ currentX = math.subtract(currentX, math.divide(fVal, derivative));
737
  iterations++;
738
  }
739
 
740
+ if (iterations < 50) {
741
+ // Check if solution is within bounds
742
+ if (currentX.re >= xMin && currentX.re <= xMax &&
743
+ currentX.im >= yMin && currentX.im <= yMax) {
744
+ // Check if this solution is already found (to avoid duplicates)
745
+ const isDuplicate = solutions.some(sol =>
746
+ math.abs(math.subtract(sol, currentX)) < 1e-5
747
+ );
748
+
749
+ if (!isDuplicate) {
750
+ solutions.push(currentX);
751
+ }
752
  }
753
  }
754
  }
 
778
  const aStep = (aMax - aMin) / steps;
779
 
780
  // Prepare data for graph
781
+ const realPoints = [];
782
+ const imaginaryPoints = [];
783
  solutionsData = [];
784
 
785
  // Calculate solutions for each a value
 
794
  });
795
 
796
  // Add points to graph
797
+ solutions.forEach(solution => {
798
+ // Real solutions (z = 0)
799
+ realPoints.push(new THREE.Vector3(solution.re, a, 0));
800
+
801
+ // Imaginary solutions (if enabled)
802
+ if (showImaginary && Math.abs(solution.im) > 1e-10) {
803
+ imaginaryPoints.push(new THREE.Vector3(solution.re, a, solution.im));
804
+ }
805
  });
806
  }
807
 
808
+ // Create group to hold both real and imaginary points
809
+ const group = new THREE.Group();
810
 
811
+ // Create geometry for real solutions (blue)
812
+ if (realPoints.length > 0) {
813
+ const realGeometry = new THREE.BufferGeometry().setFromPoints(realPoints);
814
+ const realMaterial = new THREE.PointsMaterial({
815
+ color: getComputedStyle(document.documentElement).getPropertyValue('--real-color'),
816
+ size: 0.1,
817
+ sizeAttenuation: true
818
+ });
819
+ const realMesh = new THREE.Points(realGeometry, realMaterial);
820
+ group.add(realMesh);
821
+ }
822
+
823
+ // Create geometry for imaginary solutions (red)
824
+ if (showImaginary && imaginaryPoints.length > 0) {
825
+ const imaginaryGeometry = new THREE.BufferGeometry().setFromPoints(imaginaryPoints);
826
+ const imaginaryMaterial = new THREE.PointsMaterial({
827
+ color: getComputedStyle(document.documentElement).getPropertyValue('--imaginary-color'),
828
+ size: 0.1,
829
+ sizeAttenuation: true
830
+ });
831
+ const imaginaryMesh = new THREE.Points(imaginaryGeometry, imaginaryMaterial);
832
+ group.add(imaginaryMesh);
833
+ }
834
+
835
+ // Add line connecting real solutions for the same a value
836
+ if (realPoints.length > 0) {
837
+ const lineGeometry = new THREE.BufferGeometry().setFromPoints(realPoints);
838
+ const lineMaterial = new THREE.LineBasicMaterial({
839
+ color: getComputedStyle(document.documentElement).getPropertyValue('--real-color'),
840
+ transparent: true,
841
+ opacity: 0.3
842
+ });
843
+ const line = new THREE.Line(lineGeometry, lineMaterial);
844
+ group.add(line);
845
+ }
846
 
847
+ // Add the group to the scene
848
+ graphMesh = group;
849
  scene.add(graphMesh);
850
 
851
  // Update camera to fit the graph
 
879
 
880
  camera.position.set(center.x, center.y, cameraZ);
881
  camera.lookAt(center);
882
+ controls.target.copy(center);
883
+ controls.update();
884
  }
885
 
886
  // Show solutions panel with calculated data
 
918
  valuesContainer.className = 'solution-values';
919
 
920
  item.solutions.forEach((sol, idx) => {
921
+ // Real part
922
+ const realValue = document.createElement('div');
923
+ realValue.className = 'solution-value real';
924
+ realValue.textContent = `Re(x${idx + 1}) = ${sol.re.toFixed(4)}`;
925
+ valuesContainer.appendChild(realValue);
926
+
927
+ // Imaginary part (if significant)
928
+ if (Math.abs(sol.im) > 1e-10) {
929
+ const imagValue = document.createElement('div');
930
+ imagValue.className = 'solution-value imaginary';
931
+ imagValue.textContent = `Im(x${idx + 1}) = ${sol.im.toFixed(4)}i`;
932
+ valuesContainer.appendChild(imagValue);
933
+ }
934
  });
935
 
936
  solutionItem.appendChild(equation);
 
978
  const exprToSolve = `(${leftExpr}) - (${rightExpr})`;
979
 
980
  // Test evaluation
981
+ math.evaluate(exprToSolve, { a: 1, x: math.complex(1, 1) });
982
 
983
  errorElement.style.display = 'none';
984
  return true;
 
1009
  } else {
1010
  camera.position.set(5, 5, 5);
1011
  camera.lookAt(0, 0, 0);
1012
+ controls.target.set(0, 0, 0);
1013
+ controls.update();
1014
  }
1015
  });
1016
 
 
1025
  }
1026
  });
1027
 
1028
+ // Toggle imaginary solutions button
1029
+ document.getElementById('toggle-imaginary').addEventListener('click', () => {
1030
+ showImaginary = !showImaginary;
1031
+ const icon = document.querySelector('#toggle-imaginary i');
1032
+ if (showImaginary) {
1033
+ icon.style.color = 'var(--primary-color)';
1034
+ } else {
1035
+ icon.style.color = 'var(--dark-color)';
1036
+ }
1037
+
1038
+ // Recreate graph with new setting
1039
+ const equation = document.getElementById('equation').value;
1040
+ if (equation && validateEquation(equation)) {
1041
+ createGraph(equation);
1042
+ }
1043
+ });
1044
+
1045
  // Handle Enter key in equation input
1046
  document.getElementById('equation').addEventListener('keypress', (e) => {
1047
  if (e.key === 'Enter') {
 
1050
  });
1051
  });
1052
  </script>
1053
+ </body>
1054
  </html>