|
36 | 36 | ArrowBufferGeometry, |
37 | 37 | blockGeometry, |
38 | 38 | checksum, |
| 39 | + gaussLegendre, |
39 | 40 | } from '../utils.js'; |
40 | 41 |
|
41 | 42 | import { mathToJSFunction } from './mathutils'; |
|
125 | 126 | }); |
126 | 127 |
|
127 | 128 | let minimize = $state(false); |
| 129 | + let approxVol = $state(); |
| 130 | + let volumeInt = $state(); |
128 | 131 |
|
129 | 132 | const colorMaterial = new THREE.MeshPhongMaterial({ |
130 | 133 | color: 0xffffff, |
|
218 | 221 | // return (x, y, t) => z.evaluate({ x, y, t }); |
219 | 222 | // }); |
220 | 223 |
|
| 224 | + let A = $derived(math.evaluate(params.a)); |
| 225 | + let B = $derived(math.evaluate(params.b)); |
| 226 | + let C = $derived(mathToJSFunction(params.c, ['x'])); |
| 227 | + let D = $derived(mathToJSFunction(params.d, ['x'])); |
221 | 228 | let func = $derived(mathToJSFunction(params.z, ['x', 'y', 't'])); |
222 | 229 |
|
223 | 230 | const tangentVectors = function () { |
|
334 | 341 | $effect(() => (surfaceMesh.visible = graphVisible)); |
335 | 342 |
|
336 | 343 | const updateSurface = function () { |
337 | | - const { a, b, c, d } = params; |
338 | | - const A = math.parse(a).evaluate(); |
339 | | - const B = math.parse(b).evaluate(); |
| 344 | + // const { a, b, c, d } = params; |
| 345 | + // const A = math.parse(a).evaluate(); |
| 346 | + // const B = math.parse(b).evaluate(); |
340 | 347 |
|
341 | | - const C = math.parse(c); |
342 | | - const D = math.parse(d); |
| 348 | + // const C = math.parse(c); |
| 349 | + // const D = math.parse(d); |
343 | 350 |
|
344 | 351 | const geometry = new ParametricGeometry( |
345 | 352 | (u, v, vec) => { |
346 | 353 | const U = A + (B - A) * u; |
347 | | - const cU = C.evaluate({ x: U }); |
348 | | - const dU = D.evaluate({ x: U }); |
| 354 | + const cU = C(U); |
| 355 | + const dU = D(U); |
349 | 356 |
|
350 | 357 | const V = (1 - v) * cU + v * dU; |
351 | 358 |
|
|
354 | 361 | data.nX, |
355 | 362 | data.nX, |
356 | 363 | ); |
357 | | - const meshGeometry = meshLines(params, data.rNum, data.cNum, data.nX); |
| 364 | + const meshGeometry = meshLines(data.rNum, data.cNum, data.nX); |
358 | 365 | if (!meshGeometry) { |
359 | 366 | // Geometry calculation failed, there must have been an |
360 | 367 | // input error. |
|
403 | 410 | updateBoxes(); |
404 | 411 | } |
405 | 412 |
|
| 413 | + volumeInt = integrateSurface(); |
| 414 | +
|
406 | 415 | render(); |
407 | 416 | }; |
408 | 417 |
|
409 | | - const meshLines = function (rData, rNum = 10, cNum = 10, nX = 30) { |
410 | | - let { a, b, c, d, t0, t1 } = rData; |
| 418 | + const meshLines = function (rNum = 10, cNum = 10, nX = 50) { |
| 419 | + // let { a, b, c, d, t0, t1 } = rData; |
411 | 420 |
|
412 | | - const A = math.parse(a).evaluate(); |
413 | | - const B = math.parse(b).evaluate(); |
414 | | - const C = math.parse(c).compile(); |
415 | | - const D = math.parse(d).compile(); |
| 421 | + // const A = math.parse(a).evaluate(); |
| 422 | + // const B = math.parse(b).evaluate(); |
| 423 | + // const C = math.parse(c).compile(); |
| 424 | + // const D = math.parse(d).compile(); |
416 | 425 |
|
417 | 426 | const du = (B - A) / rNum; |
418 | 427 | const dx = (B - A) / lcm(nX, cNum); |
419 | 428 | const points = []; |
420 | 429 |
|
421 | 430 | for (let u = A; u <= B; u += du) { |
422 | | - const cU = C.evaluate({ x: u }); |
423 | | - const dU = D.evaluate({ x: u }); |
| 431 | + const cU = C(u); |
| 432 | + const dU = D(u); |
424 | 433 |
|
425 | 434 | const dy = (dU - cU) / lcm(nX, rNum); |
426 | 435 | // args.x = u; |
|
438 | 447 |
|
439 | 448 | // v-Meshes |
440 | 449 | // args.x = A; |
441 | | - cMin = C.evaluate({ x: A }); |
442 | | - dMax = D.evaluate({ x: A }); |
| 450 | + cMin = C(A); |
| 451 | + dMax = D(A); |
443 | 452 | for (let u = A + dx; u <= B; u += dx) { |
444 | 453 | // args.x = u; |
445 | | - cMin = Math.min(cMin, C.evaluate({ x: u })); |
446 | | - dMax = Math.max(dMax, D.evaluate({ x: u })); |
| 454 | + cMin = Math.min(cMin, C(u)); |
| 455 | + dMax = Math.max(dMax, D(u)); |
447 | 456 | } |
448 | 457 |
|
449 | 458 | for (let v = cMin; v <= dMax; v += (dMax - cMin) / cNum) { |
450 | 459 | const zs = marchingSegments( |
451 | | - (x) => (C.evaluate({ x: x }) - v) * (v - D.evaluate({ x: x })), |
| 460 | + (x) => (C(x) - v) * (v - D(x)), |
452 | 461 | A, |
453 | 462 | B, |
454 | 463 | nX, |
|
457 | 466 | let nextZero = zs.shift(); |
458 | 467 | for (let u = A; u <= B - dx + tol; u += dx) { |
459 | 468 | // args.x = u; |
460 | | - if (C.evaluate({ x: u }) <= v && v <= D.evaluate({ x: u })) { |
| 469 | + if (C(u) <= v && v <= D(u)) { |
461 | 470 | points.push(new THREE.Vector3(u, v, func(u, v, tVal))); |
462 | 471 | if (nextZero < u + dx) { |
463 | 472 | // args.x = nextZero; |
|
499 | 508 | return geometry; |
500 | 509 | }; |
501 | 510 |
|
| 511 | + function integrateSurface(n = 20) { |
| 512 | + return gaussLegendre( |
| 513 | + (x) => gaussLegendre((y) => func(x, y, tVal), C(x), D(x), n), |
| 514 | + A, |
| 515 | + B, |
| 516 | + n, |
| 517 | + ); |
| 518 | + } |
| 519 | +
|
502 | 520 | const evolveSurface = function (t) { |
503 | | - boxMeshVisible = false; |
| 521 | + if (boxMeshVisible) { |
| 522 | + volumeInt = integrateSurface(); |
| 523 | + updateBoxes(); |
| 524 | + } |
| 525 | + // boxMeshVisible = false; |
504 | 526 |
|
505 | 527 | // the front and back surfaces share a geometry. The meshlines are separate |
506 | 528 | for (let j = 0; j < 3; j += 2) { |
|
709 | 731 | element.geometry.dispose(); |
710 | 732 | levelHolder.remove(element); |
711 | 733 | } |
712 | | - const { a, b, c, d } = params; |
| 734 | + // const { a, b, c, d } = params; |
713 | 735 | // const t0 = math.parse(params.t0).evaluate(); |
714 | 736 | // const t1 = math.parse(params.t1).evaluate(); |
715 | 737 | // const t = t0 + tau * (t1 - t0); |
716 | | - let C = 0, |
717 | | - D = 0, |
718 | | - zMin = 0, |
| 738 | + let yMin = C(A), |
| 739 | + yMax = D(A); |
| 740 | + let zMin = 0, |
719 | 741 | zMax = 0; |
720 | | - const [A, B] = [math.evaluate(a), math.evaluate(b)]; |
| 742 | + // const [A, B] = [math.evaluate(a), math.evaluate(b)]; |
721 | 743 | for (let i = 0; i <= data.nL; i++) { |
722 | | - C = Math.min( |
723 | | - C, |
724 | | - math.evaluate(c, { x: A + ((B - A) * i) / data.nL }), |
725 | | - ); |
726 | | - D = Math.max( |
727 | | - D, |
728 | | - math.evaluate(d, { x: A + ((B - A) * i) / data.nL }), |
729 | | - ); |
| 744 | + yMin = Math.min(yMin, C(A + ((B - A) * i) / data.nL)); |
| 745 | + yMax = Math.max(yMax, D(A + ((B - A) * i) / data.nL)); |
730 | 746 | for (let j = 0; j <= data.nL; j++) { |
731 | 747 | const Z = func( |
732 | 748 | A + ((B - A) * i) / data.nL, |
733 | | - C + ((D - C) * j) / data.nL, |
| 749 | + yMin + ((yMax - yMin) * j) / data.nL, |
734 | 750 | tVal, |
735 | 751 | ); |
736 | 752 | zMin = Math.min(zMin, Z); |
|
749 | 765 | }, |
750 | 766 | xmin: A, |
751 | 767 | xmax: B, |
752 | | - ymin: C, |
753 | | - ymax: D, |
| 768 | + ymin: yMin, |
| 769 | + ymax: yMax, |
754 | 770 | level: lev, |
755 | 771 | zLevel: 0, |
756 | 772 | nX: 200, |
|
816 | 832 | boxMesh.material = colorMaterial; |
817 | 833 |
|
818 | 834 | let boxMeshVisible = $state(false); |
819 | | - $effect(() => (boxMesh.visible = boxMeshVisible)); |
| 835 | + $effect(() => { |
| 836 | + boxMesh.visible = boxMeshVisible; |
| 837 | + untrack(() => { |
| 838 | + volumeInt = integrateSurface(); |
| 839 | + }); |
| 840 | + }); |
820 | 841 |
|
821 | 842 | scene.add(boxMesh); |
822 | 843 | const boxMeshEdges = new THREE.LineSegments( |
|
826 | 847 | boxMesh.add(boxMeshEdges); |
827 | 848 |
|
828 | 849 | const updateBoxes = function () { |
829 | | - const { a, b, c, d } = params; |
830 | | - try { |
831 | | - [ |
832 | | - math.evaluate(a), |
833 | | - math.evaluate(b), |
834 | | - math.evaluate(c), |
835 | | - math.evaluate(d), |
836 | | - ]; |
837 | | - } catch (e) { |
838 | | - console.error("Can't show integral boxes on nonconstant bounds", e); |
839 | | - return; |
840 | | - } |
841 | | -
|
842 | | - const [A, B, C, D] = [ |
843 | | - math.evaluate(a), |
844 | | - math.evaluate(b), |
845 | | - math.evaluate(c), |
846 | | - math.evaluate(d), |
847 | | - ]; |
| 850 | + // const { a, b, c, d } = params; |
| 851 | + // try { |
| 852 | + // [ |
| 853 | + // math.evaluate(a), |
| 854 | + // math.evaluate(b), |
| 855 | + // math.evaluate(c), |
| 856 | + // math.evaluate(d), |
| 857 | + // ]; |
| 858 | + // } catch (e) { |
| 859 | + // console.error("Can't show integral boxes on nonconstant bounds", e); |
| 860 | + // return; |
| 861 | + // } |
| 862 | +
|
| 863 | + // const [A, B, C, D] = [ |
| 864 | + // math.evaluate(a), |
| 865 | + // math.evaluate(b), |
| 866 | + // math.evaluate(c), |
| 867 | + // math.evaluate(d), |
| 868 | + // ]; |
848 | 869 |
|
849 | 870 | // const t = T0 + tau * (T1 - T0); |
850 | 871 |
|
|
865 | 886 | data.s, |
866 | 887 | data.t, |
867 | 888 | ); |
| 889 | +
|
| 890 | + approxVol = boxMesh.geometry.volume; |
| 891 | +
|
868 | 892 | boxMeshEdges.geometry = new THREE.EdgesGeometry(boxMesh.geometry); |
869 | 893 | }; |
870 | 894 |
|
|
1324 | 1348 | }} |
1325 | 1349 | class="box box-2" |
1326 | 1350 | /> |
| 1351 | + <p class="box-all"> |
| 1352 | + <M |
| 1353 | + s={`\\sum_{i =1}^N\\sum_{j=1}^N f\\,\\Delta A = ${approxVol.toFixed(3)}`} |
| 1354 | + display |
| 1355 | + /> |
| 1356 | + </p> |
| 1357 | + <p class="box-all"> |
| 1358 | + A more accurate estimation: |
| 1359 | + <M |
| 1360 | + s={`\\iint\\limits_{\\mathcal R} f\\,dA \\approx ${volumeInt.toFixed(3)}`} |
| 1361 | + display |
| 1362 | + /> |
| 1363 | + </p> |
1327 | 1364 | {/if} |
1328 | 1365 | </div> |
1329 | 1366 | </div> |
|
0 commit comments