plotboilerplate 1.28.1 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/index.esm.js +527 -53
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/dist/plotboilerplate.browser.min.js +1 -1
- package/dist/plotboilerplate.browser.min.js.map +1 -1
- package/dist/plotboilerplate.js +528 -51
- package/dist/plotboilerplate.min.js +1 -1
- package/package.json +1 -1
- package/src/cjs/Circle.d.ts +86 -3
- package/src/cjs/Circle.js +179 -4
- package/src/cjs/Circle.js.map +1 -1
- package/src/cjs/CubicBezierCurve.d.ts +34 -3
- package/src/cjs/CubicBezierCurve.js +79 -12
- package/src/cjs/CubicBezierCurve.js.map +1 -1
- package/src/cjs/Line.d.ts +46 -1
- package/src/cjs/Line.js +80 -1
- package/src/cjs/Line.js.map +1 -1
- package/src/cjs/Triangle.d.ts +40 -4
- package/src/cjs/Triangle.js +127 -31
- package/src/cjs/Triangle.js.map +1 -1
- package/src/cjs/VertTuple.d.ts +16 -2
- package/src/cjs/VertTuple.js +20 -2
- package/src/cjs/VertTuple.js.map +1 -1
- package/src/cjs/Vertex.d.ts +25 -2
- package/src/cjs/Vertex.js +43 -1
- package/src/cjs/Vertex.js.map +1 -1
- package/src/cjs/utils/algorithms/minimalContainingCircleFromPoints.d.ts +21 -0
- package/src/cjs/utils/algorithms/minimalContainingCircleFromPoints.js +73 -0
- package/src/cjs/utils/algorithms/minimalContainingCircleFromPoints.js.map +1 -0
- package/src/cjs/utils/algorithms/minimalEnclosingCircleFromPoints.d.ts +21 -0
- package/src/cjs/utils/algorithms/minimalEnclosingCircleFromPoints.js +73 -0
- package/src/cjs/utils/algorithms/minimalEnclosingCircleFromPoints.js.map +1 -0
- package/src/cjs/utils/algorithms/pointsMinimalContainingCircle.d.ts +21 -0
- package/src/cjs/utils/algorithms/pointsMinimalContainingCircle.js +73 -0
- package/src/cjs/utils/algorithms/pointsMinimalContainingCircle.js.map +1 -0
- package/src/cjs/utils/algorithms/solveApollonius3.d.ts +69 -0
- package/src/cjs/utils/algorithms/solveApollonius3.js +118 -0
- package/src/cjs/utils/algorithms/solveApollonius3.js.map +1 -0
- package/src/esm/Circle.d.ts +86 -3
- package/src/esm/Circle.js +179 -4
- package/src/esm/Circle.js.map +1 -1
- package/src/esm/CubicBezierCurve.d.ts +34 -3
- package/src/esm/CubicBezierCurve.js +79 -12
- package/src/esm/CubicBezierCurve.js.map +1 -1
- package/src/esm/Line.d.ts +46 -1
- package/src/esm/Line.js +80 -1
- package/src/esm/Line.js.map +1 -1
- package/src/esm/Triangle.d.ts +40 -4
- package/src/esm/Triangle.js +128 -32
- package/src/esm/Triangle.js.map +1 -1
- package/src/esm/VertTuple.d.ts +16 -2
- package/src/esm/VertTuple.js +20 -2
- package/src/esm/VertTuple.js.map +1 -1
- package/src/esm/Vertex.d.ts +25 -2
- package/src/esm/Vertex.js +44 -2
- package/src/esm/Vertex.js.map +1 -1
- package/src/esm/utils/algorithms/minimalContainingCircleFromPoints.d.ts +21 -0
- package/src/esm/utils/algorithms/minimalContainingCircleFromPoints.js +69 -0
- package/src/esm/utils/algorithms/minimalContainingCircleFromPoints.js.map +1 -0
- package/src/esm/utils/algorithms/minimalEnclosingCircleFromPoints.d.ts +21 -0
- package/src/esm/utils/algorithms/minimalEnclosingCircleFromPoints.js +69 -0
- package/src/esm/utils/algorithms/minimalEnclosingCircleFromPoints.js.map +1 -0
- package/src/esm/utils/algorithms/pointsMinimalContainingCircle.d.ts +21 -0
- package/src/esm/utils/algorithms/pointsMinimalContainingCircle.js +69 -0
- package/src/esm/utils/algorithms/pointsMinimalContainingCircle.js.map +1 -0
- package/src/esm/utils/algorithms/solveApollonius3.d.ts +69 -0
- package/src/esm/utils/algorithms/solveApollonius3.js +114 -0
- package/src/esm/utils/algorithms/solveApollonius3.js.map +1 -0
- package/src/index-gl.js +6 -4
- package/src/index.js +25 -17
- package/src/ts/Circle.ts +200 -5
- package/src/ts/CubicBezierCurve.ts +133 -48
- package/src/ts/Line.ts +84 -1
- package/src/ts/Triangle.ts +148 -41
- package/src/ts/VertTuple.ts +29 -2
- package/src/ts/Vertex.ts +44 -2
- package/src/ts/utils/algorithms/minimalEnclosingCircleFromPoints.ts +69 -0
- package/src/ts/utils/algorithms/solveApollonius3.ts +132 -0
package/dist/index.esm.js
CHANGED
|
@@ -348,7 +348,16 @@ class VertexListeners {
|
|
|
348
348
|
* @modified 2022-08-23 Added the `closestPoint` function.
|
|
349
349
|
* @modified 2025-04-09 Added the `Circle.move(amount: XYCoords)` method.
|
|
350
350
|
* @modified 2025-04-16 Class `Circle` now implements interface `Intersectable`.
|
|
351
|
-
* @
|
|
351
|
+
* @modified 2026-06-10 Adding the utility function `Circle.circleUtils.containsPoint`.
|
|
352
|
+
* @modified 2026-06-10 Adding the `Circle.clone` method.
|
|
353
|
+
* @modified 2026-01-13 Adding helper function `Circle.circleUtils.containsPoint` and refactored the member method `containsPoint`.
|
|
354
|
+
* @modified 2026-07-03 Adding the optional `epsilon` parameter to the `Circle.containsCircle` method.
|
|
355
|
+
* @modified 2026-07-03 Fixing the `Circle.clone` method; the center had not been cloned at all, this was fixed.
|
|
356
|
+
* @modified 2026-07-08 Adding the `Circle.setRadius` method (for chaining).
|
|
357
|
+
* @mofified 2026-07-31 Adding the `radicalAxis(Circle)` method. Added the `Circle.circleUtils.createRadicalAxisHelperCircle` and `.circleDistance` helper methods.
|
|
358
|
+
* @modified 2026-08-03 Adding `Circle.tangentsFromPoint`.
|
|
359
|
+
* @modified 2026-08-16 Adding the `Circle.sectorAngleByArcLength` method and the `Circle.circleUtils.sectorAngleByArcLength` helper method.
|
|
360
|
+
* @version 1.8.0
|
|
352
361
|
**/
|
|
353
362
|
/**
|
|
354
363
|
* @classdesc A simple circle: center point and radius.
|
|
@@ -379,6 +388,20 @@ class Circle {
|
|
|
379
388
|
this.center = center;
|
|
380
389
|
this.radius = radius;
|
|
381
390
|
}
|
|
391
|
+
/**
|
|
392
|
+
* Set the radius of this circle.
|
|
393
|
+
* The method is meant for chaining, you may also alter the `radius` attribute directly.
|
|
394
|
+
*
|
|
395
|
+
* @method setRadius
|
|
396
|
+
* @param {number} radius - The amount to move.
|
|
397
|
+
* @instance
|
|
398
|
+
* @memberof Circle
|
|
399
|
+
* @return {Circle} this for chaining
|
|
400
|
+
**/
|
|
401
|
+
setRadius(radius) {
|
|
402
|
+
this.radius = radius;
|
|
403
|
+
return this;
|
|
404
|
+
}
|
|
382
405
|
/**
|
|
383
406
|
* Move the circle by the given amount.
|
|
384
407
|
*
|
|
@@ -402,7 +425,8 @@ class Circle {
|
|
|
402
425
|
* @return {boolean} `true` if the given point is inside this circle.
|
|
403
426
|
*/
|
|
404
427
|
containsPoint(point) {
|
|
405
|
-
return this.center.distance(point) < this.radius
|
|
428
|
+
// return this.center.distance(point) < this.radius;//
|
|
429
|
+
return Circle.circleUtils.containsPoint(this.center, this.radius, point);
|
|
406
430
|
}
|
|
407
431
|
/**
|
|
408
432
|
* Check if the given circle is fully contained inside this circle.
|
|
@@ -413,8 +437,13 @@ class Circle {
|
|
|
413
437
|
* @memberof Circle
|
|
414
438
|
* @return {boolean} `true` if any only if the given circle is completely inside this circle.
|
|
415
439
|
*/
|
|
416
|
-
containsCircle(circle) {
|
|
417
|
-
|
|
440
|
+
containsCircle(circle, epsilon) {
|
|
441
|
+
if (typeof epsilon === "undefined" || Number.isNaN(epsilon)) {
|
|
442
|
+
return this.center.distance(circle.center) + circle.radius < this.radius;
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
return this.center.distance(circle.center) + circle.radius < this.radius + Math.abs(epsilon);
|
|
446
|
+
}
|
|
418
447
|
}
|
|
419
448
|
/**
|
|
420
449
|
* Calculate the distance from this circle to the given line.
|
|
@@ -617,6 +646,40 @@ class Circle {
|
|
|
617
646
|
});
|
|
618
647
|
}
|
|
619
648
|
//--- END --- Implement interface `Intersectable`
|
|
649
|
+
/**
|
|
650
|
+
* Calculate the radical axis of this and a different circle.
|
|
651
|
+
* The two circles must not be co-centric.
|
|
652
|
+
*
|
|
653
|
+
* See this article for details:
|
|
654
|
+
* https://www.cut-the-knot.org/Curriculum/Geometry/GeoGebra/RadicalAxes.shtml
|
|
655
|
+
*
|
|
656
|
+
* @method radicalAxis
|
|
657
|
+
* @instance
|
|
658
|
+
* @memberof Circle
|
|
659
|
+
* @param {Circle} circleB - The second circle to calculated the radical axis for.
|
|
660
|
+
* @return {Line} A line defining the radical axis of the two circles.
|
|
661
|
+
**/
|
|
662
|
+
radicalAxis(circleB) {
|
|
663
|
+
var helperCircle = Circle.circleUtils.createRadicalAxisHelperCircle(this, circleB);
|
|
664
|
+
var intersectionLineA = this.circleIntersection(helperCircle);
|
|
665
|
+
var intersectionLineB = circleB.circleIntersection(helperCircle);
|
|
666
|
+
var intersection = this.circleIntersection(circleB);
|
|
667
|
+
if (!intersectionLineA || !intersectionLineB) {
|
|
668
|
+
console.error("Critical error: none of the two intersections must be null.");
|
|
669
|
+
return intersection; // Fallback
|
|
670
|
+
}
|
|
671
|
+
var lineA = new Line(intersectionLineA.a, intersectionLineA.b);
|
|
672
|
+
var lineB = new Line(intersectionLineB.a, intersectionLineB.b);
|
|
673
|
+
var firstRadicalAxisPoint = lineA.intersection(lineB);
|
|
674
|
+
var centerConnectLine = new Line(this.center, circleB.center);
|
|
675
|
+
var secondRadicalAxisPoint = centerConnectLine.getClosestPoint(firstRadicalAxisPoint);
|
|
676
|
+
// Create a mirrored version of the second radical axis point so both are located
|
|
677
|
+
// symmetrical from the circle connect point.
|
|
678
|
+
// (currently the second point is located ON the connect line)
|
|
679
|
+
var secondRadicalAxisPoint_mirrored = secondRadicalAxisPoint.clone().scale(2.0, firstRadicalAxisPoint);
|
|
680
|
+
var radicalAxis = new Line(firstRadicalAxisPoint, secondRadicalAxisPoint_mirrored);
|
|
681
|
+
return intersection && intersection.length() > radicalAxis.length() ? intersection : radicalAxis;
|
|
682
|
+
}
|
|
620
683
|
/**
|
|
621
684
|
* Calculate the closest point on the outline of this circle to the given point.
|
|
622
685
|
*
|
|
@@ -640,6 +703,80 @@ class Circle {
|
|
|
640
703
|
return lineIntersection.b;
|
|
641
704
|
}
|
|
642
705
|
}
|
|
706
|
+
/**
|
|
707
|
+
* Get the two tangent vectors for the given point.
|
|
708
|
+
* If the point is on or in the circle then null is returned.
|
|
709
|
+
*
|
|
710
|
+
* @method tangentsFromPoint
|
|
711
|
+
* @instance
|
|
712
|
+
* @memberof Circle
|
|
713
|
+
* @param {Vertex} vert - The point to find the two tangents for.
|
|
714
|
+
* @return {[Vector,Vector]} The two tangent vector
|
|
715
|
+
**/
|
|
716
|
+
tangentsFromPoint(vert) {
|
|
717
|
+
// Inspired by
|
|
718
|
+
// https://www.omnicalculator.com/math/tangent-circle
|
|
719
|
+
const centerDistance = this.center.distance(vert);
|
|
720
|
+
const tangentLength = Math.sqrt(centerDistance * centerDistance - this.radius * this.radius);
|
|
721
|
+
// console.log("this.radius ", this.radius, "centerDistance", centerDistance, "tangentLength", tangentLength);
|
|
722
|
+
if (Number.isNaN(tangentLength)) {
|
|
723
|
+
// vertex is inside circle
|
|
724
|
+
console.log("tangentLength is NaN", tangentLength);
|
|
725
|
+
return null;
|
|
726
|
+
}
|
|
727
|
+
const helperCircle = new Circle(vert, tangentLength);
|
|
728
|
+
const intersection = this.circleIntersection(helperCircle);
|
|
729
|
+
if (!intersection) {
|
|
730
|
+
// No intersection (vertex is inside circle)
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
733
|
+
return [new Vector(intersection.a, vert), new Vector(intersection.b, vert)];
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Calculate inner sector angle for this circle and a given circle arc length.
|
|
737
|
+
*
|
|
738
|
+
* @param {number} sectorArcLength - The desired arc length (in units).
|
|
739
|
+
* @returns The sector's inner angle (in radians).
|
|
740
|
+
*/
|
|
741
|
+
sectorAngleByArcLength(sectorArcLength) {
|
|
742
|
+
return Circle.circleUtils.sectorAngleByArcLength(sectorArcLength, this.radius);
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Create a deep copy of this circle.
|
|
746
|
+
*
|
|
747
|
+
* @method clone
|
|
748
|
+
* @return {Circle} A new circle, an exact copy of this one.
|
|
749
|
+
* @instance
|
|
750
|
+
* @memberof Circle
|
|
751
|
+
**/
|
|
752
|
+
clone() {
|
|
753
|
+
return new Circle(this.center.clone(), this.radius);
|
|
754
|
+
}
|
|
755
|
+
static fromICircle(obj) {
|
|
756
|
+
return new Circle(new Vertex$1(obj.center), obj.radius);
|
|
757
|
+
}
|
|
758
|
+
// static fromObject(obj: object): Circle {
|
|
759
|
+
// if (!obj) {
|
|
760
|
+
// return null;
|
|
761
|
+
// }
|
|
762
|
+
// if (typeof obj !== "object") {
|
|
763
|
+
// return null;
|
|
764
|
+
// }
|
|
765
|
+
// if (!obj.hasOwnProperty("center") || typeof (obj as any).center != "object") {
|
|
766
|
+
// return null;
|
|
767
|
+
// }
|
|
768
|
+
// if (!obj.hasOwnProperty("radius") || typeof !(obj as any).radius != "number") {
|
|
769
|
+
// return null;
|
|
770
|
+
// }
|
|
771
|
+
// var center = (obj as any).center;
|
|
772
|
+
// if (!center.hasOwnProperty("x") || typeof (center as any).x != "number") {
|
|
773
|
+
// return null;
|
|
774
|
+
// }
|
|
775
|
+
// if (!center.hasOwnProperty("y") || typeof (center as any).y != "number") {
|
|
776
|
+
// return null;
|
|
777
|
+
// }
|
|
778
|
+
// return new Circle(new Vertex(center), (obj as any).radius);
|
|
779
|
+
// }
|
|
643
780
|
/**
|
|
644
781
|
* This function should invalidate any installed listeners and invalidate this object.
|
|
645
782
|
* After calling this function the object might not hold valid data any more and
|
|
@@ -655,6 +792,43 @@ Circle.circleUtils = {
|
|
|
655
792
|
/* return new Vertex( Math.sin(angle) * radius,
|
|
656
793
|
Math.cos(angle) * radius ); */
|
|
657
794
|
return new Vertex$1(Math.cos(angle) * radius, Math.sin(angle) * radius);
|
|
795
|
+
},
|
|
796
|
+
containsPoint: (circleCenter, circleRadius, point) => {
|
|
797
|
+
// TODO: cleanup
|
|
798
|
+
// return (
|
|
799
|
+
// (circle.center.x - point.x) * (circle.center.x - point.x) + (circle.center.y - point.y) * (circle.center.y - point.y) <=
|
|
800
|
+
// circle.radius * circle.radius
|
|
801
|
+
// );
|
|
802
|
+
return geomutils.dist4(point.x, point.y, circleCenter.x, circleCenter.y) < circleRadius;
|
|
803
|
+
},
|
|
804
|
+
createRadicalAxisHelperCircle: (circleA, circleB) => {
|
|
805
|
+
// We need to create a helper circle that intersects BOTH other circles,
|
|
806
|
+
// each in TWO points.
|
|
807
|
+
var circleConnectLine = new Vector(circleA.center.clone(), circleB.center);
|
|
808
|
+
circleConnectLine.a.set(circleConnectLine.vertAt(0.5));
|
|
809
|
+
// Get the perpendicular by half the circle distance.
|
|
810
|
+
var bisector = circleConnectLine.perp();
|
|
811
|
+
var bisectorLength = Math.max(circleA.radius, circleB.radius, bisector.length());
|
|
812
|
+
bisector.setLength(bisectorLength);
|
|
813
|
+
return new Circle(bisector.b, bisector.b.distance(circleA.center));
|
|
814
|
+
},
|
|
815
|
+
/**
|
|
816
|
+
* Calculate the outer distance between two circles. If the circles touch then the
|
|
817
|
+
* distance is 0.0.
|
|
818
|
+
* If the circles intersect then the distance in negative.
|
|
819
|
+
*/
|
|
820
|
+
circleDistance: (circleA, circleB) => {
|
|
821
|
+
return circleA.center.distance(circleB.center) - circleA.radius - circleB.radius;
|
|
822
|
+
},
|
|
823
|
+
/**
|
|
824
|
+
* Calculate the inner sector angle for a given circle arc length and radius.
|
|
825
|
+
*
|
|
826
|
+
* @param {number} sectorArcLength - The desired arc length.
|
|
827
|
+
* @param {number} circleRadius - The circle's radius.
|
|
828
|
+
* @returns The sector angle in radians.
|
|
829
|
+
*/
|
|
830
|
+
sectorAngleByArcLength: (sectorArcLength, circleRadius) => {
|
|
831
|
+
return sectorArcLength / circleRadius;
|
|
658
832
|
}
|
|
659
833
|
};
|
|
660
834
|
|
|
@@ -687,7 +861,10 @@ Circle.circleUtils = {
|
|
|
687
861
|
* @modified 2025-14-16 Class `Triangle` now implements interface `IBounded`.
|
|
688
862
|
* @modified 2025-14-16 Class `Triangle` now implements interface `Intersectable`.
|
|
689
863
|
* @modified 2025-14-16 Added method `Triangle.move`.
|
|
690
|
-
* @
|
|
864
|
+
* @modified 2026-06-10 Refactoring the `Trianble.bounds` method and added a plain `Triangle.utils.bounds` method.
|
|
865
|
+
* @modified 2026-06-10 Refactoring the `Trianble.calcCircumCircle` method and added a plain `Triangle.utils.calcCircumCircle` method.
|
|
866
|
+
* @modified 2026-06-13 Adding `Triangle.getVertices()`.
|
|
867
|
+
* @version 2.11.0
|
|
691
868
|
*
|
|
692
869
|
* @file Triangle
|
|
693
870
|
* @fileoverview A simple triangle class: three vertices.
|
|
@@ -832,7 +1009,7 @@ class Triangle {
|
|
|
832
1009
|
* after triangle vertex changes.
|
|
833
1010
|
*
|
|
834
1011
|
* @method getCircumcircle
|
|
835
|
-
* @return {
|
|
1012
|
+
* @return {Circle} - The circle touching exactly all three triangle vertices.
|
|
836
1013
|
* @instance
|
|
837
1014
|
* @memberof Triangle
|
|
838
1015
|
*/
|
|
@@ -841,6 +1018,33 @@ class Triangle {
|
|
|
841
1018
|
this.calcCircumcircle();
|
|
842
1019
|
return new Circle(this.center.clone(), this.radius);
|
|
843
1020
|
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Calculates the minimun enclosing circle.
|
|
1023
|
+
*
|
|
1024
|
+
* @method getMinimumEnclosingCircle
|
|
1025
|
+
* @return {Object} - { center:Vertex, radius:float }
|
|
1026
|
+
* @instance
|
|
1027
|
+
* @memberof Triangle
|
|
1028
|
+
* @returns
|
|
1029
|
+
*/
|
|
1030
|
+
getMinimumEnclosingCircle() {
|
|
1031
|
+
// First option: two points construct a circle and the third point is contained.
|
|
1032
|
+
var circleAB = VertTuple.vtutils.calcCircumcircle(this.a, this.b);
|
|
1033
|
+
if (Circle.circleUtils.containsPoint(circleAB.center, circleAB.radius, this.c)) {
|
|
1034
|
+
return Circle.fromICircle(circleAB);
|
|
1035
|
+
}
|
|
1036
|
+
var circleBC = VertTuple.vtutils.calcCircumcircle(this.b, this.c);
|
|
1037
|
+
if (Circle.circleUtils.containsPoint(circleBC.center, circleBC.radius, this.c)) {
|
|
1038
|
+
return Circle.fromICircle(circleBC);
|
|
1039
|
+
}
|
|
1040
|
+
var circleCA = VertTuple.vtutils.calcCircumcircle(this.c, this.a);
|
|
1041
|
+
if (Circle.circleUtils.containsPoint(circleCA.center, circleCA.radius, this.c)) {
|
|
1042
|
+
return Circle.fromICircle(circleCA);
|
|
1043
|
+
}
|
|
1044
|
+
// If none of the three upper cases applies: return the circumcircle.
|
|
1045
|
+
var circumCircle = Triangle.utils.calcCircumcircle(this.a, this.b, this.c);
|
|
1046
|
+
return Circle.fromICircle(circumCircle);
|
|
1047
|
+
}
|
|
844
1048
|
/**
|
|
845
1049
|
* Check if this triangle and the passed triangle share an
|
|
846
1050
|
* adjacent edge.
|
|
@@ -879,6 +1083,17 @@ class Triangle {
|
|
|
879
1083
|
//if( this.c.equals(vert1) && this.a.equals(vert2) || this.c.equals(vert2) && this.a.equals(vert1) )
|
|
880
1084
|
return this.b;
|
|
881
1085
|
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Get the three triangele vertices in a 3-element array.
|
|
1088
|
+
*
|
|
1089
|
+
* @method getVertices
|
|
1090
|
+
* @returns {[Vertex, Vertex, Vertex]} - The three vertices – real instances, not copies.
|
|
1091
|
+
* @instance
|
|
1092
|
+
* @memberof Triangle
|
|
1093
|
+
*/
|
|
1094
|
+
getVertices() {
|
|
1095
|
+
return [this.a, this.b, this.c];
|
|
1096
|
+
}
|
|
882
1097
|
/**
|
|
883
1098
|
* Re-compute the circumcircle of this triangle (if the vertices
|
|
884
1099
|
* have changed).
|
|
@@ -892,32 +1107,35 @@ class Triangle {
|
|
|
892
1107
|
* @memberof Triangle
|
|
893
1108
|
*/
|
|
894
1109
|
calcCircumcircle() {
|
|
895
|
-
// From
|
|
896
|
-
// http://www.exaflop.org/docs/cgafaq/cga1.html
|
|
897
|
-
const A = this.b.x - this.a.x;
|
|
898
|
-
const B = this.b.y - this.a.y;
|
|
899
|
-
const C = this.c.x - this.a.x;
|
|
900
|
-
const D = this.c.y - this.a.y;
|
|
901
|
-
const E = A * (this.a.x + this.b.x) + B * (this.a.y + this.b.y);
|
|
902
|
-
const F = C * (this.a.x + this.c.x) + D * (this.a.y + this.c.y);
|
|
903
|
-
const G = 2.0 * (A * (this.c.y - this.b.y) - B * (this.c.x - this.b.x));
|
|
904
|
-
let dx, dy;
|
|
905
|
-
if (Math.abs(G) < Triangle.EPSILON) {
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
this.
|
|
920
|
-
|
|
1110
|
+
// // From
|
|
1111
|
+
// // http://www.exaflop.org/docs/cgafaq/cga1.html
|
|
1112
|
+
// const A: number = this.b.x - this.a.x;
|
|
1113
|
+
// const B: number = this.b.y - this.a.y;
|
|
1114
|
+
// const C: number = this.c.x - this.a.x;
|
|
1115
|
+
// const D: number = this.c.y - this.a.y;
|
|
1116
|
+
// const E: number = A * (this.a.x + this.b.x) + B * (this.a.y + this.b.y);
|
|
1117
|
+
// const F: number = C * (this.a.x + this.c.x) + D * (this.a.y + this.c.y);
|
|
1118
|
+
// const G: number = 2.0 * (A * (this.c.y - this.b.y) - B * (this.c.x - this.b.x));
|
|
1119
|
+
// let dx: number, dy: number;
|
|
1120
|
+
// if (Math.abs(G) < Triangle.EPSILON) {
|
|
1121
|
+
// // Collinear - find extremes and use the midpoint
|
|
1122
|
+
// const bounds: Bounds = this.bounds();
|
|
1123
|
+
// this.center = new Vertex((bounds.min.x + bounds.max.x) / 2, (bounds.min.y + bounds.max.y) / 2);
|
|
1124
|
+
// dx = this.center.x - bounds.min.x;
|
|
1125
|
+
// dy = this.center.y - bounds.min.y;
|
|
1126
|
+
// } else {
|
|
1127
|
+
// const cx: number = (D * E - B * F) / G;
|
|
1128
|
+
// const cy: number = (A * F - C * E) / G;
|
|
1129
|
+
// this.center = new Vertex(cx, cy);
|
|
1130
|
+
// dx = this.center.x - this.a.x;
|
|
1131
|
+
// dy = this.center.y - this.a.y;
|
|
1132
|
+
// }
|
|
1133
|
+
// this.radius_squared = dx * dx + dy * dy;
|
|
1134
|
+
// this.radius = Math.sqrt(this.radius_squared);
|
|
1135
|
+
const tmpCircle = Triangle.utils.calcCircumcircle(this.a, this.b, this.c);
|
|
1136
|
+
this.center = new Vertex$1(tmpCircle.center.x, tmpCircle.center.y);
|
|
1137
|
+
this.radius = tmpCircle.radius;
|
|
1138
|
+
this.radius_squared = tmpCircle.radius_squared;
|
|
921
1139
|
} // END calcCircumcircle
|
|
922
1140
|
/**
|
|
923
1141
|
* Check if the passed vertex is inside this triangle's
|
|
@@ -944,7 +1162,11 @@ class Triangle {
|
|
|
944
1162
|
* @memberof Triangle
|
|
945
1163
|
*/
|
|
946
1164
|
bounds() {
|
|
947
|
-
return new Bounds(
|
|
1165
|
+
// return new Bounds(
|
|
1166
|
+
// new Vertex(Triangle.utils.min3(this.a.x, this.b.x, this.c.x), Triangle.utils.min3(this.a.y, this.b.y, this.c.y)),
|
|
1167
|
+
// new Vertex(Triangle.utils.max3(this.a.x, this.b.x, this.c.x), Triangle.utils.max3(this.a.y, this.b.y, this.c.y))
|
|
1168
|
+
// );
|
|
1169
|
+
return Triangle.utils.bounds(this.a, this.b, this.c);
|
|
948
1170
|
}
|
|
949
1171
|
//--- BEGIN --- Implement interface `Intersectable`
|
|
950
1172
|
/**
|
|
@@ -1067,8 +1289,9 @@ class Triangle {
|
|
|
1067
1289
|
* @return Vertex The incenter of this triangle.
|
|
1068
1290
|
**/
|
|
1069
1291
|
getIncenter() {
|
|
1070
|
-
if (!this.center || !this.radius)
|
|
1292
|
+
if (!this.center || !this.radius) {
|
|
1071
1293
|
this.calcCircumcircle();
|
|
1294
|
+
}
|
|
1072
1295
|
return this.center.clone();
|
|
1073
1296
|
}
|
|
1074
1297
|
/**
|
|
@@ -1134,9 +1357,55 @@ Triangle.utils = {
|
|
|
1134
1357
|
* @param {XYCords} c - The first vertex.
|
|
1135
1358
|
* @returns {nmber}
|
|
1136
1359
|
*/
|
|
1137
|
-
determinant(a, b, c) {
|
|
1360
|
+
determinant: (a, b, c) => {
|
|
1138
1361
|
return (b.y - a.y) * (c.x - b.x) - (c.y - b.y) * (b.x - a.x);
|
|
1139
|
-
}
|
|
1362
|
+
},
|
|
1363
|
+
bounds: (a, b, c) => {
|
|
1364
|
+
return new Bounds(new Vertex$1(Triangle.utils.min3(a.x, b.x, c.x), Triangle.utils.min3(a.y, b.y, c.y)), new Vertex$1(Triangle.utils.max3(a.x, b.x, c.x), Triangle.utils.max3(a.y, b.y, c.y)));
|
|
1365
|
+
},
|
|
1366
|
+
/**
|
|
1367
|
+
* Re-compute the circumcircle of this triangle (if the vertices
|
|
1368
|
+
* have changed).
|
|
1369
|
+
*
|
|
1370
|
+
* The circumcenter and radius are stored in this.center and
|
|
1371
|
+
* this.radius. There is a third result: radius_squared (for internal computations).
|
|
1372
|
+
*
|
|
1373
|
+
* @method calcCircumcircle
|
|
1374
|
+
* @return void
|
|
1375
|
+
* @instance
|
|
1376
|
+
* @memberof Triangle
|
|
1377
|
+
*/
|
|
1378
|
+
calcCircumcircle: (a, b, c) => {
|
|
1379
|
+
// From
|
|
1380
|
+
// http://www.exaflop.org/docs/cgafaq/cga1.html
|
|
1381
|
+
const A = b.x - a.x;
|
|
1382
|
+
const B = b.y - a.y;
|
|
1383
|
+
const C = c.x - a.x;
|
|
1384
|
+
const D = c.y - a.y;
|
|
1385
|
+
const E = A * (a.x + b.x) + B * (a.y + b.y);
|
|
1386
|
+
const F = C * (a.x + c.x) + D * (a.y + c.y);
|
|
1387
|
+
const G = 2.0 * (A * (c.y - b.y) - B * (c.x - b.x));
|
|
1388
|
+
let dx, dy;
|
|
1389
|
+
let center;
|
|
1390
|
+
if (Math.abs(G) < Triangle.EPSILON) {
|
|
1391
|
+
// Collinear - find extremes and use the midpoint
|
|
1392
|
+
// const bounds: Bounds = this.bounds();
|
|
1393
|
+
const bounds = Triangle.utils.bounds(a, b, c);
|
|
1394
|
+
center = new Vertex$1((bounds.min.x + bounds.max.x) / 2, (bounds.min.y + bounds.max.y) / 2);
|
|
1395
|
+
dx = center.x - bounds.min.x;
|
|
1396
|
+
dy = center.y - bounds.min.y;
|
|
1397
|
+
}
|
|
1398
|
+
else {
|
|
1399
|
+
const cx = (D * E - B * F) / G;
|
|
1400
|
+
const cy = (A * F - C * E) / G;
|
|
1401
|
+
center = new Vertex$1(cx, cy);
|
|
1402
|
+
dx = center.x - a.x;
|
|
1403
|
+
dy = center.y - a.y;
|
|
1404
|
+
}
|
|
1405
|
+
const radius_squared = dx * dx + dy * dy;
|
|
1406
|
+
const radius = Math.sqrt(radius_squared);
|
|
1407
|
+
return { center: center, radius: radius, radius_squared: radius_squared };
|
|
1408
|
+
} // END calcCircumcircle
|
|
1140
1409
|
};
|
|
1141
1410
|
|
|
1142
1411
|
/**
|
|
@@ -1298,7 +1567,8 @@ const geomutils = {
|
|
|
1298
1567
|
* @modified 2025-03-24 Making the second parameter `center` of the `Vertex.rotate` method optional.
|
|
1299
1568
|
* @modified 2025-04-13 Adding the `Vertex.move(amount: XYCoords)` method (does the same as `add`, added by naming convention).
|
|
1300
1569
|
* @modified 2025-05-07 Class `Vertex` is now implementing interface `IBounded` (to meet convention).
|
|
1301
|
-
* @
|
|
1570
|
+
* @modified 2026-06-10 Adding methods `Vertex.findClosestPoint` and `Vertex.findFarestPoint`.
|
|
1571
|
+
* @version 2.12.0
|
|
1302
1572
|
*
|
|
1303
1573
|
* @file Vertex
|
|
1304
1574
|
* @public
|
|
@@ -1858,6 +2128,47 @@ class Vertex$1 {
|
|
|
1858
2128
|
return Bounds.computeFromVertices([this]);
|
|
1859
2129
|
}
|
|
1860
2130
|
//--- END --- Implement interface `IBounded`
|
|
2131
|
+
/**
|
|
2132
|
+
* Find the one of two given points that's closest to this point.
|
|
2133
|
+
*
|
|
2134
|
+
* @method findClosestPoint
|
|
2135
|
+
* @instance
|
|
2136
|
+
* @memberof Vertex
|
|
2137
|
+
* @param {XYCoords | Vertex} pointA
|
|
2138
|
+
* @param {XYCoords | Vertex} pointB
|
|
2139
|
+
* @returns {XYCoords | Vertex}
|
|
2140
|
+
*/
|
|
2141
|
+
findClosestPoint(pointA, pointB) {
|
|
2142
|
+
// TODO: put this implementation to geomutils?
|
|
2143
|
+
var distA = this.distance(pointA);
|
|
2144
|
+
var distB = this.distance(pointB);
|
|
2145
|
+
if (distA < distB) {
|
|
2146
|
+
return pointA;
|
|
2147
|
+
}
|
|
2148
|
+
else {
|
|
2149
|
+
return pointB;
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
/**
|
|
2153
|
+
* Find the one of two given points that's farest from this point.
|
|
2154
|
+
*
|
|
2155
|
+
* @method findFarestPoint
|
|
2156
|
+
* @instance
|
|
2157
|
+
* @memberof Vertex
|
|
2158
|
+
* @param {XYCoords | Vertex} pointA
|
|
2159
|
+
* @param {XYCoords | Vertex} pointB
|
|
2160
|
+
* @returns {XYCoords | Vertex}
|
|
2161
|
+
*/
|
|
2162
|
+
findFarestPoint(pointA, pointB) {
|
|
2163
|
+
var distA = this.distance(pointA);
|
|
2164
|
+
var distB = this.distance(pointB);
|
|
2165
|
+
if (distA > distB) {
|
|
2166
|
+
return pointA;
|
|
2167
|
+
}
|
|
2168
|
+
else {
|
|
2169
|
+
return pointB;
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
1861
2172
|
/**
|
|
1862
2173
|
* Get a string representation of this vertex.
|
|
1863
2174
|
*
|
|
@@ -1944,7 +2255,7 @@ Vertex$1.utils = {
|
|
|
1944
2255
|
* @param {number?} precision - (optional) The numeric precision to be used (number of precision digits).
|
|
1945
2256
|
* @returns {string}
|
|
1946
2257
|
*/
|
|
1947
|
-
arrayToJSON(vertices, precision) {
|
|
2258
|
+
arrayToJSON: (vertices, precision) => {
|
|
1948
2259
|
return JSON.stringify(vertices.map(function (vert) {
|
|
1949
2260
|
return typeof precision === undefined
|
|
1950
2261
|
? { x: vert.x, y: vert.y }
|
|
@@ -1971,7 +2282,9 @@ Vertex$1.utils = {
|
|
|
1971
2282
|
* @modified 2025-03-31 Added the `VertTuple.revert` method.
|
|
1972
2283
|
* @modified 2025-04-15 Changed param of `VertTuple.moveTo` method from `Vertex` to `XYCoords`.
|
|
1973
2284
|
* @modified 2025-04-15 Added method `VertTuple.move` method.
|
|
1974
|
-
* @
|
|
2285
|
+
* @modified 2026-06-10 Adding helper function `VertTuple.utils.calcCircumcircle`.
|
|
2286
|
+
* @modified 2026-06-17 Adding method `VertTuple.asLine` for converting Vector to Line instances.
|
|
2287
|
+
* @version 1.6.0
|
|
1975
2288
|
*/
|
|
1976
2289
|
/**
|
|
1977
2290
|
* @classdesc An abstract base classes for vertex tuple constructs, like Lines or Vectors.
|
|
@@ -2255,7 +2568,7 @@ class VertTuple {
|
|
|
2255
2568
|
/**
|
|
2256
2569
|
* Create a deep clone of this instance.
|
|
2257
2570
|
*
|
|
2258
|
-
* @method
|
|
2571
|
+
* @method clone
|
|
2259
2572
|
* @return {T} A type safe clone if this instance.
|
|
2260
2573
|
* @instance
|
|
2261
2574
|
* @memberof VertTuple
|
|
@@ -2263,6 +2576,17 @@ class VertTuple {
|
|
|
2263
2576
|
clone() {
|
|
2264
2577
|
return this.factory(this.a.clone(), this.b.clone());
|
|
2265
2578
|
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Converts this `Vector` to a `Line` (segment).
|
|
2581
|
+
*
|
|
2582
|
+
* @method asLine
|
|
2583
|
+
* @return {T} A type safe clone if this instance.
|
|
2584
|
+
* @instance
|
|
2585
|
+
* @memberof VertTuple
|
|
2586
|
+
**/
|
|
2587
|
+
asLine() {
|
|
2588
|
+
return new Line(this.a, this.b);
|
|
2589
|
+
}
|
|
2266
2590
|
/**
|
|
2267
2591
|
* Create a string representation of this line.
|
|
2268
2592
|
*
|
|
@@ -2291,6 +2615,10 @@ class VertTuple {
|
|
|
2291
2615
|
VertTuple.vtutils = {
|
|
2292
2616
|
dist2: (v, w) => {
|
|
2293
2617
|
return (v.x - w.x) * (v.x - w.x) + (v.y - w.y) * (v.y - w.y);
|
|
2618
|
+
},
|
|
2619
|
+
calcCircumcircle: (p1, p2) => {
|
|
2620
|
+
const p1x = p1.x, p1y = p1.y, p2x = p2.x, p2y = p2.y, cx = 0.5 * (p1x + p2x), cy = 0.5 * (p1y + p2y);
|
|
2621
|
+
return { center: { x: cx, y: cy }, radius: Math.sqrt((p1x - cx) * (p1x - cx) + (p1y - cy) * (p1y - cy)) };
|
|
2294
2622
|
}
|
|
2295
2623
|
};
|
|
2296
2624
|
|
|
@@ -2489,7 +2817,8 @@ Vector.utils = {
|
|
|
2489
2817
|
* @modified 2023-09-25 Changed param type of `intersection()` from Line to VertTuple.
|
|
2490
2818
|
* @modified 2025-04-15 Class `Line` now implements interface `Intersectable`.
|
|
2491
2819
|
* @modified 2025-04-16 Class `Line` now implements interface `IBounded`.
|
|
2492
|
-
* @
|
|
2820
|
+
* @modified 2026-08-16 Adding methods `Line.trimStart` and `Line.trimEnd`. Adding methods `Line.trimStartAt` and `Line.trimEndAt`.
|
|
2821
|
+
* @version 2.5.0
|
|
2493
2822
|
*
|
|
2494
2823
|
* @file Line
|
|
2495
2824
|
* @public
|
|
@@ -2616,6 +2945,84 @@ class Line extends VertTuple {
|
|
|
2616
2945
|
return this;
|
|
2617
2946
|
}
|
|
2618
2947
|
//--- END Implement PathSegment ---
|
|
2948
|
+
/**
|
|
2949
|
+
* Trim this line segment from the start point by the given amount.
|
|
2950
|
+
* The amount must be positive and should be withing the segment's length. If the amount exceeds the segment's length
|
|
2951
|
+
* then the length of the resulting line will be zero (0.0).
|
|
2952
|
+
*
|
|
2953
|
+
* @method trimStart
|
|
2954
|
+
* @memberof Line
|
|
2955
|
+
* @param {number} amount - The positive amount to trim the line from the start point `a`.
|
|
2956
|
+
* @returns {Line} This for chaining, with updated point `a`.
|
|
2957
|
+
*/
|
|
2958
|
+
trimStart(amount) {
|
|
2959
|
+
// Calculate the relative position `t` on this line.
|
|
2960
|
+
var t = amount / this.length();
|
|
2961
|
+
// `t` should be inside 0..1 – otherwise the amount was too large or negative.
|
|
2962
|
+
if (t < 0.0) {
|
|
2963
|
+
return this;
|
|
2964
|
+
}
|
|
2965
|
+
if (t > 1.0) {
|
|
2966
|
+
// Set the line to length zero (endpoint only)
|
|
2967
|
+
this.a = this.b.clone();
|
|
2968
|
+
return this;
|
|
2969
|
+
}
|
|
2970
|
+
this.a = this.vertAt(t);
|
|
2971
|
+
return this;
|
|
2972
|
+
}
|
|
2973
|
+
/**
|
|
2974
|
+
* Trim this line segment from the start point by the given relative amount.
|
|
2975
|
+
* The amount must be positive and should be within 0.0 and 1.0. If the amount exceeds the segment's length
|
|
2976
|
+
* then the length of the resulting line will be zero (0.0).
|
|
2977
|
+
*
|
|
2978
|
+
* @method trimStartAt
|
|
2979
|
+
* @memberof Line
|
|
2980
|
+
* @param {number} amount - The positive amount to trim the line from the start point `a`.
|
|
2981
|
+
* @returns {Line} This for chaining, with updated point `a`.
|
|
2982
|
+
*/
|
|
2983
|
+
trimStartAt(relativeAmount) {
|
|
2984
|
+
// Calculate the relative position `t` on this line.
|
|
2985
|
+
return this.trimStart(relativeAmount * this.length());
|
|
2986
|
+
}
|
|
2987
|
+
/**
|
|
2988
|
+
* Trim this line segment from the end point by the given amount.
|
|
2989
|
+
* The amount must be positive and should be withing the segment's length. If the amount exceeds the segment's length
|
|
2990
|
+
* then the length of the resulting line will be zero (0.0).
|
|
2991
|
+
*
|
|
2992
|
+
* @method trimEnd
|
|
2993
|
+
* @memberof Line
|
|
2994
|
+
* @param {number} amount - The positive amount to trim the line from the end point `b`.
|
|
2995
|
+
* @returns {Line} This for chaining, with updated point `b`.
|
|
2996
|
+
*/
|
|
2997
|
+
trimEnd(amount) {
|
|
2998
|
+
// Calculate the relative position `t` on this line.
|
|
2999
|
+
var t = 1.0 - amount / this.length();
|
|
3000
|
+
// `t` should be inside 0..1 – otherwise the amount was too large or negative.
|
|
3001
|
+
if (t < 0.0) {
|
|
3002
|
+
return this;
|
|
3003
|
+
}
|
|
3004
|
+
if (t > 1.0) {
|
|
3005
|
+
// Set the line to length zero (endpoint only)
|
|
3006
|
+
this.b = this.a.clone();
|
|
3007
|
+
return this;
|
|
3008
|
+
}
|
|
3009
|
+
this.b = this.vertAt(t);
|
|
3010
|
+
return this;
|
|
3011
|
+
}
|
|
3012
|
+
/**
|
|
3013
|
+
* Trim this line segment from the end point by the given relative amount.
|
|
3014
|
+
* The amount must be positive and should be within 0.0 and 1.0. If the amount exceeds the segment's length
|
|
3015
|
+
* then the length of the resulting line will be zero (0.0).
|
|
3016
|
+
*
|
|
3017
|
+
* @method trimEndAt
|
|
3018
|
+
* @memberof Line
|
|
3019
|
+
* @param {number} amount - The positive amount to trim the line from the start point `a`.
|
|
3020
|
+
* @returns {Line} This for chaining, with updated point `a`.
|
|
3021
|
+
*/
|
|
3022
|
+
trimEndAt(relativeAmount) {
|
|
3023
|
+
// Calculate the relative position `t` on this line.
|
|
3024
|
+
return this.trimEnd(relativeAmount * this.length());
|
|
3025
|
+
}
|
|
2619
3026
|
//--- BEGIN --- Implement interface `Intersectable`
|
|
2620
3027
|
/**
|
|
2621
3028
|
* Get all line intersections with this polygon.
|
|
@@ -3918,8 +4325,9 @@ class Bounds {
|
|
|
3918
4325
|
* @modified 2025-04-18 Added evaluation method for cubic Bézier curves `CubicBezierCurve.utils.evaluateT`.
|
|
3919
4326
|
* @modified 2025-04-18 Refactored method `CubicBezierCurve.getPointAt` to use `evaluateT`.
|
|
3920
4327
|
* @modified 2025-04-18 Fixed the `CubicBezierCurve.getBounds` method: now returning the real bounding box. Before it was an approximated one.
|
|
3921
|
-
* @modified 2025
|
|
3922
|
-
* @
|
|
4328
|
+
* @modified 2025-04-18 Added helper methods for bounding box calculation `CubucBezierCurve.util.cubicPolyMinMax` and `cubicPoly`.
|
|
4329
|
+
* @modified 2026-09-09 Adding methods `CubicBezierCurve.trimStartEnd` and `CubicBezierCurve.trimStartEndAt`.
|
|
4330
|
+
* @version 2.10.0
|
|
3923
4331
|
*
|
|
3924
4332
|
* @file CubicBezierCurve
|
|
3925
4333
|
* @public
|
|
@@ -4368,6 +4776,7 @@ class CubicBezierCurve {
|
|
|
4368
4776
|
this.endControlPoint.set(subCurbePoints[3]);
|
|
4369
4777
|
this.updateArcLengths();
|
|
4370
4778
|
return this;
|
|
4779
|
+
// return this.trimStartEndAt(t, null);
|
|
4371
4780
|
}
|
|
4372
4781
|
/**
|
|
4373
4782
|
* Trim off the end of this curve. The position parameter `uValue` is the absolute position on the
|
|
@@ -4403,7 +4812,79 @@ class CubicBezierCurve {
|
|
|
4403
4812
|
this.endControlPoint.set(subCurbePoints[3]);
|
|
4404
4813
|
this.updateArcLengths();
|
|
4405
4814
|
return this;
|
|
4815
|
+
// return this.trimStartEndAt(null, t);
|
|
4816
|
+
}
|
|
4817
|
+
/**
|
|
4818
|
+
* Trim off a start and end section of this curve. The position parameters `uStart` and `uEnd` are the absolute positions in [0..arcLength].
|
|
4819
|
+
* The remaining curve will be the one in the bounds `[uStart,uEnd]` (so `[0.0,uStart]` and `[uEnd,1.0]` are cut off).
|
|
4820
|
+
*
|
|
4821
|
+
* Parameters out of bounds (< 0.0 or > arcLength) are ignored.
|
|
4822
|
+
* If `uEnd` is smaller than `uStart` then a curve with length zero (0) at `uStart` is returned.
|
|
4823
|
+
*
|
|
4824
|
+
* @method trimStartEndAt
|
|
4825
|
+
* @instance
|
|
4826
|
+
* @memberof CubicBezierCurve
|
|
4827
|
+
* @param {number} tStart - The relative position parameter where to cut off the head curve.
|
|
4828
|
+
* @param {number} tEnd - The relative position parameter where to cut off the tail curve.
|
|
4829
|
+
* @returns {CubicBezierCurve} `this` for chanining.
|
|
4830
|
+
*/
|
|
4831
|
+
trimStartEnd(uStart, uEnd) {
|
|
4832
|
+
return this.trimStartEndAt(this.convertU2T(uStart), this.convertU2T(uEnd));
|
|
4833
|
+
}
|
|
4834
|
+
/**
|
|
4835
|
+
* Trim off a start and end section of this curve. The position parameters `tStart` and `tEnd` are the relative positions in [0..1].
|
|
4836
|
+
* The remaining curve will be the one in the bounds `[tStart,tEnd]` (so `[0.0,tStart]` and `[tEnd,1.0]` are cut off).
|
|
4837
|
+
*
|
|
4838
|
+
* Parameters out of bounds (< 0.0 or > 1.0) are ignored.
|
|
4839
|
+
* If `tEnd` is smaller than `tStart` then a curve with length zero (0) at `tStart` is returned.
|
|
4840
|
+
*
|
|
4841
|
+
* @method trimStartEndAt
|
|
4842
|
+
* @instance
|
|
4843
|
+
* @memberof CubicBezierCurve
|
|
4844
|
+
* @param {number} tStart - The relative position parameter where to cut off the head curve.
|
|
4845
|
+
* @param {number} tEnd - The relative position parameter where to cut off the tail curve.
|
|
4846
|
+
* @returns {CubicBezierCurve} `this` for chanining.
|
|
4847
|
+
*/
|
|
4848
|
+
trimStartEndAt(tStart, tEnd) {
|
|
4849
|
+
const cleanTrimStart = Math.min(Math.max(0.0, tStart), 1.0);
|
|
4850
|
+
const cleanTrimEnd = Math.min(Math.max(cleanTrimStart, tEnd), 1.0);
|
|
4851
|
+
this.trimStartAt(cleanTrimStart);
|
|
4852
|
+
const relativeTrimEnd = (cleanTrimEnd - cleanTrimStart) / (1.0 - cleanTrimStart);
|
|
4853
|
+
this.trimEndAt(relativeTrimEnd);
|
|
4854
|
+
return this;
|
|
4406
4855
|
}
|
|
4856
|
+
// __trimStartEndAt(tStart: number, tEnd: number): CubicBezierCurve {
|
|
4857
|
+
// var finalCurvePoints = [
|
|
4858
|
+
// this.startPoint.clone(),
|
|
4859
|
+
// this.endPoint.clone(),
|
|
4860
|
+
// this.startControlPoint.clone(),
|
|
4861
|
+
// this.endControlPoint.clone()
|
|
4862
|
+
// ];
|
|
4863
|
+
// if (typeof tStart === "number" && !Number.isNaN(tStart)) {
|
|
4864
|
+
// const subCurvePointsStart = CubicBezierCurve.utils.getSubCurvePointsAt(this, tStart, 1.0);
|
|
4865
|
+
// finalCurvePoints[0].set(subCurvePointsStart[0]);
|
|
4866
|
+
// finalCurvePoints[2].set(subCurvePointsStart[2]);
|
|
4867
|
+
// // this.startPoint.set(subCurvePointsStart[0]);
|
|
4868
|
+
// // this.startControlPoint.set(subCurvePointsStart[2]);
|
|
4869
|
+
// // this.endPoint.set(subCurvePointsStart[1]);
|
|
4870
|
+
// // this.endControlPoint.set(subCurvePointsStart[3]);
|
|
4871
|
+
// }
|
|
4872
|
+
// if (typeof tEnd === "number" && !Number.isNaN(tEnd)) {
|
|
4873
|
+
// const subCurvePointsEnd = CubicBezierCurve.utils.getSubCurvePointsAt(this, 0.0, tEnd);
|
|
4874
|
+
// // this.startPoint.set(subCurvePointsEnd[0]);
|
|
4875
|
+
// // this.startControlPoint.set(subCurvePointsEnd[2]);
|
|
4876
|
+
// // this.endPoint.set(subCurvePointsEnd[1]);
|
|
4877
|
+
// // this.endControlPoint.set(subCurvePointsEnd[3]);
|
|
4878
|
+
// finalCurvePoints[1].set(subCurvePointsEnd[1]);
|
|
4879
|
+
// finalCurvePoints[3].set(subCurvePointsEnd[3]);
|
|
4880
|
+
// }
|
|
4881
|
+
// this.startPoint.set(finalCurvePoints[0]);
|
|
4882
|
+
// this.endPoint.set(finalCurvePoints[1]);
|
|
4883
|
+
// this.startControlPoint.set(finalCurvePoints[2]);
|
|
4884
|
+
// this.endControlPoint.set(finalCurvePoints[3]);
|
|
4885
|
+
// this.updateArcLengths();
|
|
4886
|
+
// return this;
|
|
4887
|
+
// }
|
|
4407
4888
|
/**
|
|
4408
4889
|
* Get a sub curve at the given start end end positions (values on the curve's length, between 0 and curve.arcLength).
|
|
4409
4890
|
*
|
|
@@ -4755,10 +5236,7 @@ CubicBezierCurve.END_POINT = 3;
|
|
|
4755
5236
|
*/
|
|
4756
5237
|
CubicBezierCurve.utils = {
|
|
4757
5238
|
evaluateT: (p0, p1, p2, p3, t) => {
|
|
4758
|
-
return p0 * Math.pow(1.0 - t, 3) +
|
|
4759
|
-
p1 * 3 * t * Math.pow(1.0 - t, 2) +
|
|
4760
|
-
p2 * 3 * Math.pow(t, 2) * (1.0 - t) +
|
|
4761
|
-
p3 * Math.pow(t, 3);
|
|
5239
|
+
return (p0 * Math.pow(1.0 - t, 3) + p1 * 3 * t * Math.pow(1.0 - t, 2) + p2 * 3 * Math.pow(t, 2) * (1.0 - t) + p3 * Math.pow(t, 3));
|
|
4762
5240
|
},
|
|
4763
5241
|
cubicPolyMinMax: (p0, p1, p2, p3) => {
|
|
4764
5242
|
// var polyX = CubicBezierCurve.utils.cubicPoly2(
|
|
@@ -4830,7 +5308,7 @@ CubicBezierCurve.utils = {
|
|
|
4830
5308
|
* @param {number} tEnd – The end offset if the desired cub curve (must be in [0..1]).
|
|
4831
5309
|
* @instance
|
|
4832
5310
|
* @memberof CubicBezierCurve
|
|
4833
|
-
* @return {
|
|
5311
|
+
* @return {[Vertex, Vertex, Vertex, Vertex]} The sub curve as curve vertices.
|
|
4834
5312
|
**/
|
|
4835
5313
|
getSubCurvePointsAt: (curve, tStart, tEnd) => {
|
|
4836
5314
|
const startVec = new Vector(curve.getPointAt(tStart), curve.getTangentAt(tStart));
|
|
@@ -4919,11 +5397,7 @@ CubicBezierCurve.utils = {
|
|
|
4919
5397
|
* @returns {[number,number,number]}
|
|
4920
5398
|
*/
|
|
4921
5399
|
cubicPoly: (p0, p1, p2, p3) => {
|
|
4922
|
-
return [
|
|
4923
|
-
3 * p3 - 9 * p2 + 9 * p1 - 3 * p0,
|
|
4924
|
-
6 * p0 - 12 * p1 + 6 * p2,
|
|
4925
|
-
3 * p1 - 3 * p0
|
|
4926
|
-
];
|
|
5400
|
+
return [3 * p3 - 9 * p2 + 9 * p1 - 3 * p0, 6 * p0 - 12 * p1 + 6 * p2, 3 * p1 - 3 * p0];
|
|
4927
5401
|
},
|
|
4928
5402
|
/**
|
|
4929
5403
|
* sign of number, but is division safe: no zero returned :)
|