flo-mat 4.1.3 → 4.1.5
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/__tests__/cp-node/circle-identity.spec.ts +56 -0
- package/__tests__/find-2-prong/get-medial/dd-get-medial-point-coeffs-same-curve.spec.ts +8 -6
- package/__tests__/find-2-prong/get-medial/dd-get-medial-point-coeffs.spec.ts +4 -2
- package/__tests__/specific-cases/specific-cases.spec.ts +1 -14
- package/__tests__/specific-cases/test-svgs/achispado.svg +37 -24
- package/browser/index.js +2969 -2315
- package/browser/index.min.js +1 -1
- package/node/add-n-prong.ts/join-spokes.js +3 -2
- package/node/add-n-prong.ts/join-spokes.js.map +1 -1
- package/node/cp-node/fs/enhance-cp-node.d.ts +9 -9
- package/node/cp-node/fs/get-angle-between-mat-curves.d.ts +1 -1
- package/node/cp-node/fs/get-angle-between-mat-curves.js +2 -1
- package/node/cp-node/fs/get-angle-between-mat-curves.js.map +1 -1
- package/node/cp-node/fs/get-edge-direction.js +3 -2
- package/node/cp-node/fs/get-edge-direction.js.map +1 -1
- package/node/cp-node/fs/get-implied-boundary-bezier-between.d.ts +1 -1
- package/node/cp-node/fs/get-implied-boundary-bezier-between.js +3 -2
- package/node/cp-node/fs/get-implied-boundary-bezier-between.js.map +1 -1
- package/node/cp-node/fs/get-mat-curve-between.d.ts +1 -0
- package/node/cp-node/fs/get-mat-curve-between.js +1 -0
- package/node/cp-node/fs/get-mat-curve-between.js.map +1 -1
- package/node/cp-node/fs/insert-cp-node.d.ts +12 -1
- package/node/cp-node/fs/insert-cp-node.js +21 -6
- package/node/cp-node/fs/insert-cp-node.js.map +1 -1
- package/node/cp-node/fs/is-fully-terminating.d.ts +1 -1
- package/node/cp-node/fs/is-fully-terminating.js +3 -5
- package/node/cp-node/fs/is-fully-terminating.js.map +1 -1
- package/node/cp-node/fs/is-on-same-circle.d.ts +3 -0
- package/node/cp-node/fs/is-on-same-circle.js +4 -4
- package/node/cp-node/fs/is-on-same-circle.js.map +1 -1
- package/node/cp-node/fs/is-order-correct.d.ts +9 -1
- package/node/cp-node/fs/is-order-correct.js +51 -10
- package/node/cp-node/fs/is-order-correct.js.map +1 -1
- package/node/cp-node/fs/remove-cp-node.js +1 -1
- package/node/cp-node/fs/remove-cp-node.js.map +1 -1
- package/node/cp-node/stringification/from-stringifyable.js +16 -0
- package/node/cp-node/stringification/from-stringifyable.js.map +1 -1
- package/node/debug/functions/check-ordering.d.ts +4 -0
- package/node/debug/functions/check-ordering.js +43 -0
- package/node/debug/functions/check-ordering.js.map +1 -0
- package/node/find-2-prong/add-2-prong.js +8 -2
- package/node/find-2-prong/add-2-prong.js.map +1 -1
- package/node/find-2-prong/calc-seperation-tolerance.js +2 -2
- package/node/find-2-prong/calc-seperation-tolerance.js.map +1 -1
- package/node/find-2-prong/find-and-add-2-prongs.js +1 -1
- package/node/find-2-prong/find-and-add-2-prongs.js.map +1 -1
- package/node/find-2-prong/get-initial-bezier-pieces.js +2 -2
- package/node/find-2-prong/get-initial-bezier-pieces.js.map +1 -1
- package/node/find-3-prong/find-3-prong-for-delta3s.d.ts +1 -1
- package/node/find-3-prong/find-3-prong-for-delta3s.js +1 -1
- package/node/find-3-prong/find-3-prong-for-delta3s.js.map +1 -1
- package/node/find-3-prong/find-3-prong.d.ts +1 -1
- package/node/find-3-prong/find-3-prong.js.map +1 -1
- package/node/find-3-prong/find-and-add-3-prong.js +1 -1
- package/node/find-3-prong/find-and-add-3-prong.js.map +1 -1
- package/node/find-3-prong/find-and-add-3-prongs-on-loop.d.ts +1 -1
- package/node/find-3-prong/find-and-add-3-prongs-on-loop.js +6 -2
- package/node/find-3-prong/find-and-add-3-prongs-on-loop.js.map +1 -1
- package/node/find-3-prong/three-prong-info.d.ts +1 -1
- package/node/find-mat/create-initial-cp-tree.js +2 -2
- package/node/find-mat/create-initial-cp-tree.js.map +1 -1
- package/node/find-mat/find-mat.js +2 -2
- package/node/find-mat/find-mat.js.map +1 -1
- package/node/find-mat/get-for-1-prongs-on-loop.js +3 -1
- package/node/find-mat/get-for-1-prongs-on-loop.js.map +1 -1
- package/node/geometry/get-closest-squared-distance-to-rotated-rect.js +2 -1
- package/node/geometry/get-closest-squared-distance-to-rotated-rect.js.map +1 -1
- package/node/mat/add-to-cp-tree.d.ts +4 -1
- package/node/mat/add-to-cp-tree.js +11 -4
- package/node/mat/add-to-cp-tree.js.map +1 -1
- package/node/mat/get-boundary-piece-beziers.d.ts +2 -2
- package/node/mat/get-boundary-piece-beziers.js +1 -1
- package/node/mat/get-boundary-piece-beziers.js.map +1 -1
- package/node/mat/get-closeby-cp-if-exist.js +4 -3
- package/node/mat/get-closeby-cp-if-exist.js.map +1 -1
- package/node/mat/{get-cp-node-to-left-or-same.d.ts → get-cp-node-neighbors.d.ts} +2 -2
- package/node/mat/get-cp-node-neighbors.js +31 -0
- package/node/mat/get-cp-node-neighbors.js.map +1 -0
- package/node/point-on-shape/point-on-shape.d.ts +5 -0
- package/node/reconstruct-from-mat/reconstruct-from-mat.js.map +1 -1
- package/package.json +15 -15
- package/src/add-n-prong.ts/join-spokes.ts +4 -2
- package/src/cp-node/fs/get-angle-between-mat-curves.ts +4 -2
- package/src/cp-node/fs/get-edge-direction.ts +4 -2
- package/src/cp-node/fs/get-implied-boundary-bezier-between.ts +6 -6
- package/src/cp-node/fs/get-mat-curve-between.ts +1 -0
- package/src/cp-node/fs/insert-cp-node.ts +22 -7
- package/src/cp-node/fs/is-fully-terminating.ts +3 -7
- package/src/cp-node/fs/is-on-same-circle.ts +6 -7
- package/src/cp-node/fs/is-order-correct copy.ts +60 -0
- package/src/cp-node/fs/is-order-correct.ts +61 -13
- package/src/cp-node/fs/remove-cp-node.ts +1 -1
- package/src/cp-node/stringification/from-stringifyable.ts +17 -0
- package/src/debug/functions/check-ordering.ts +44 -61
- package/src/find-2-prong/add-2-prong.ts +9 -2
- package/src/find-2-prong/calc-seperation-tolerance.ts +2 -2
- package/src/find-2-prong/find-and-add-2-prongs.ts +1 -1
- package/src/find-2-prong/get-initial-bezier-pieces.ts +2 -2
- package/src/find-3-prong/find-3-prong-for-delta3s.ts +15 -7
- package/src/find-3-prong/find-3-prong.ts +1 -1
- package/src/find-3-prong/find-and-add-3-prong.ts +1 -4
- package/src/find-3-prong/find-and-add-3-prongs-on-loop.ts +6 -3
- package/src/find-3-prong/three-prong-info.ts +1 -1
- package/src/find-mat/create-initial-cp-tree.ts +2 -2
- package/src/find-mat/find-mat.ts +3 -2
- package/src/find-mat/get-for-1-prongs-on-loop.ts +3 -1
- package/src/geometry/get-closest-squared-distance-to-rotated-rect.ts +4 -4
- package/src/mat/add-to-cp-tree copy.ts +71 -0
- package/src/mat/add-to-cp-tree.ts +12 -4
- package/src/mat/get-boundary-piece-beziers.ts +2 -2
- package/src/mat/get-closeby-cp-if-exist.ts +5 -3
- package/src/mat/{get-cp-node-to-left-or-same.ts → get-cp-node-neighbors.ts} +12 -10
- package/src/point-on-shape/point-on-shape.ts +5 -0
- package/src/utils/deg-to-rad.ts +4 -2
- package/src/utils/rad-to-deg.ts +4 -1
- package/webpack.config.cjs +7 -2
- package/node/mat/get-cp-node-to-left-or-same.js +0 -30
- package/node/mat/get-cp-node-to-left-or-same.js.map +0 -1
|
@@ -2,6 +2,8 @@ import type { CpNode } from "../cp-node/cp-node.js";
|
|
|
2
2
|
import type { Circle } from "../geometry/circle.js";
|
|
3
3
|
import type { Mutable } from "../utils/mutable.js";
|
|
4
4
|
|
|
5
|
+
const { atan2 } = Math;
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
function joinSpokes(
|
|
7
9
|
circle: Circle,
|
|
@@ -30,8 +32,8 @@ function byAngle(circle: Circle) {
|
|
|
30
32
|
a = [a[0] - c[0], a[1] - c[1]];
|
|
31
33
|
b = [b[0] - c[0], b[1] - c[1]];
|
|
32
34
|
|
|
33
|
-
const a_ =
|
|
34
|
-
const b_ =
|
|
35
|
+
const a_ = atan2(a[1], a[0]);
|
|
36
|
+
const b_ = atan2(b[1], b[0]);
|
|
35
37
|
|
|
36
38
|
return b_ - a_;
|
|
37
39
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { CpNode } from "../cp-node.js";
|
|
1
2
|
import { tangent } from "flo-bezier3";
|
|
2
3
|
import { cross, toUnitVector } from "flo-vector2d";
|
|
3
|
-
import { CpNode } from "../cp-node.js";
|
|
4
4
|
import { getMatCurveToNext } from "./get-mat-curve-to-next.js";
|
|
5
5
|
|
|
6
|
+
const { abs, asin, PI: π } = Math;
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
*
|
|
@@ -24,7 +26,7 @@ function getInitialDegAngleBetweenMatCurves(
|
|
|
24
26
|
));
|
|
25
27
|
|
|
26
28
|
const cross_ = cross(tan1, tan2);
|
|
27
|
-
const angle =
|
|
29
|
+
const angle = abs(asin( cross_ ) * (180/π));
|
|
28
30
|
|
|
29
31
|
return angle;
|
|
30
32
|
}
|
|
@@ -4,6 +4,8 @@ import { toUnitVector, fromTo, rotate, translate, rotate90Degrees, reverse as re
|
|
|
4
4
|
import { tangent } from 'flo-bezier3';
|
|
5
5
|
import { isPosSharpCorner } from '../../point-on-shape/is-pos-sharp-corner.js';
|
|
6
6
|
|
|
7
|
+
const { sqrt } = Math;
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Returns a line segment of unit length starting in the given Vertex center and
|
|
@@ -54,8 +56,8 @@ function getEdgeDirection(
|
|
|
54
56
|
// Recall the identities sin(acos(x)) = sqrt(1-x^2), etc. Also
|
|
55
57
|
// recall the half angle formulas. Then the rotation matrix, R, can
|
|
56
58
|
// be calculated.
|
|
57
|
-
const cosθ =
|
|
58
|
-
const sinθ =
|
|
59
|
+
const cosθ = sqrt((1 + x)/2);
|
|
60
|
+
const sinθ = sqrt((1 - x)/2);
|
|
59
61
|
|
|
60
62
|
vDir = rotate(sinθ, cosθ, tan2);
|
|
61
63
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import type { CpNode } from '../cp-node.js';
|
|
1
2
|
import { tangent } from 'flo-bezier3';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
import { CpNode } from '../cp-node.js';
|
|
3
|
+
import { fromTo, dot, cross, translate, lineLineIntersection, toUnitVector } from 'flo-vector2d';
|
|
4
|
+
|
|
5
|
+
const { abs } = Math;
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
/** @internal */
|
|
@@ -56,8 +56,8 @@ function getImpliedBoundaryBezierBetween(
|
|
|
56
56
|
const w1 = fromTo(lA[0], lA[1]); // This is a unit vector
|
|
57
57
|
const w2 = fromTo(lB[0], lB[1]); // This is a unit vector
|
|
58
58
|
|
|
59
|
-
const d1 =
|
|
60
|
-
const d2 =
|
|
59
|
+
const d1 = abs(cross(c, w1)) / (3*3);
|
|
60
|
+
const d2 = abs(cross(c, w2)) / (3*3);
|
|
61
61
|
|
|
62
62
|
if (d1 > TOLERANCE_ADD_2PRONG || d2 > TOLERANCE_ADD_2PRONG) {
|
|
63
63
|
// not within tolerance
|
|
@@ -5,19 +5,34 @@ import { RbTree } from "flo-ll-rb-tree";
|
|
|
5
5
|
import { isOrderCorrect } from "./is-order-correct.js";
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @param insertIfOrderWrong order cannot be checked for hole-closers since they're on different loops
|
|
11
|
+
* @param isHoleClosing
|
|
12
|
+
* @param isIntersection
|
|
13
|
+
* @param cpTree
|
|
14
|
+
* @param pos
|
|
15
|
+
* @param pred predecessor (to `pos`) on sampe loop (found via `cpTree`)
|
|
16
|
+
* @param succ successor (to `pos`) on sampe loop (found via `cpTree`)
|
|
17
|
+
* @param lastInsertId
|
|
18
|
+
*/
|
|
8
19
|
function insertCpNode(
|
|
9
20
|
insertIfOrderWrong: boolean,
|
|
10
21
|
isHoleClosing: boolean,
|
|
11
22
|
isIntersection: boolean,
|
|
12
23
|
cpTree: RbTree<CpNode>,
|
|
13
24
|
pos: PointOnShape,
|
|
14
|
-
|
|
25
|
+
pred: CpNode | undefined,
|
|
26
|
+
succ: CpNode | undefined,
|
|
15
27
|
lastInsertId: { id: number }): CpNode | undefined {
|
|
16
28
|
|
|
17
|
-
if (
|
|
18
|
-
!isOrderCorrect(cpTree, pos,
|
|
19
|
-
|
|
20
|
-
if (!
|
|
29
|
+
if (pred !== undefined && succ !== undefined) {
|
|
30
|
+
// !isOrderCorrect(cpTree, pos, pred.next, false)) {
|
|
31
|
+
const orderCorrect = isOrderCorrect(cpTree, pred, pos, succ, false);
|
|
32
|
+
// if (!orderCorrect) {
|
|
33
|
+
// console.log('aaaaa');
|
|
34
|
+
// }
|
|
35
|
+
if (!insertIfOrderWrong && !orderCorrect) {
|
|
21
36
|
return undefined;
|
|
22
37
|
}
|
|
23
38
|
}
|
|
@@ -34,8 +49,8 @@ function insertCpNode(
|
|
|
34
49
|
};
|
|
35
50
|
lastInsertId.id++;
|
|
36
51
|
|
|
37
|
-
const prev =
|
|
38
|
-
const next =
|
|
52
|
+
const prev = pred === undefined ? cpNode : pred;
|
|
53
|
+
const next = pred === undefined ? cpNode : prev.next;
|
|
39
54
|
|
|
40
55
|
(next as Mutable<CpNode>).prev = cpNode;
|
|
41
56
|
(prev as Mutable<CpNode>).next = cpNode;
|
|
@@ -5,13 +5,10 @@ import { isTrivial } from "./is-trivial.js";
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Like isTerminating
|
|
8
|
+
* Like `isTerminating` but only returns true if all `CpNode`s on the circle
|
|
9
9
|
* (except this.prevOnCircle) is terminating.
|
|
10
10
|
*/
|
|
11
11
|
function isFullyTerminating(cpNode: CpNode) {
|
|
12
|
-
// const otherOnCircle = getAllOnCircle(cpNode.prevOnCircle, true);
|
|
13
|
-
|
|
14
|
-
// return otherOnCircle.every(isTerminating);
|
|
15
12
|
if (!isTerminating(cpNode)) {
|
|
16
13
|
return false;
|
|
17
14
|
}
|
|
@@ -24,9 +21,8 @@ function isFullyTerminating(cpNode: CpNode) {
|
|
|
24
21
|
cpNode = cpNode.prev;
|
|
25
22
|
}
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return otherOnCircle.every(isTerminating);
|
|
24
|
+
return getAllOnCircle(cpNode.prevOnCircle, true)
|
|
25
|
+
.every(isTerminating);
|
|
30
26
|
}
|
|
31
27
|
|
|
32
28
|
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { CpNode } from "../cp-node.js";
|
|
2
|
-
import { getAllOnCircle } from "./get-all-on-circle.js";
|
|
3
2
|
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
|
-
* Returns true if the 2 given `CpNode`s are on the same maximal disk
|
|
5
|
+
* Returns true if the 2 given `CpNode`s are on the same maximal disk
|
|
7
6
|
* circle.
|
|
8
|
-
*
|
|
7
|
+
*
|
|
8
|
+
* * O(1): all `CpNode`s on the same on-circle ring share the same `Circle`
|
|
9
|
+
* object, and no other ring does (see `PointOnShape.circle`)
|
|
10
|
+
*
|
|
9
11
|
* @param cpNode1 A `CpNode`.
|
|
10
12
|
* @param cpNode2 Another `CpNode`
|
|
11
13
|
*/
|
|
@@ -13,10 +15,7 @@ function isOnSameCircle(
|
|
|
13
15
|
cpNode1: CpNode,
|
|
14
16
|
cpNode2: CpNode) {
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
const cpNodes = getAllOnCircle(cpNode1);
|
|
18
|
-
|
|
19
|
-
return cpNodes.indexOf(cpNode2) >= 0;
|
|
18
|
+
return cpNode1.pointOnShape.circle === cpNode2.pointOnShape.circle;
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { CpNode } from "../cp-node.js";
|
|
2
|
+
import { RbTree } from "flo-ll-rb-tree";
|
|
3
|
+
import { PointOnShape } from "../../point-on-shape/point-on-shape.js";
|
|
4
|
+
import { comparePoss } from "../../point-on-shape/compare-poss.js";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param cpTree
|
|
10
|
+
* @param pos
|
|
11
|
+
* @param succ
|
|
12
|
+
* @param alreadyInserted set to `true` if the given `pos` has already been
|
|
13
|
+
* inserted, `false` otherwise.
|
|
14
|
+
*/
|
|
15
|
+
function isOrderCorrect(
|
|
16
|
+
cpTree: RbTree<CpNode>,
|
|
17
|
+
pred: CpNode,
|
|
18
|
+
pos: PointOnShape,
|
|
19
|
+
succ: CpNode,
|
|
20
|
+
alreadyInserted: boolean): boolean {
|
|
21
|
+
|
|
22
|
+
if (cpTree.size <= (alreadyInserted ? 2 : 1)) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const c = comparePoss(pos, succ.pointOnShape);
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if (c < 0) { return true; }
|
|
30
|
+
if (c === 0) { return false; }
|
|
31
|
+
|
|
32
|
+
const minNode = cpTree.getMinNode()!.datum;
|
|
33
|
+
const maxNode = cpTree.getMaxNode()!.datum;
|
|
34
|
+
|
|
35
|
+
// larger than all -> crossing zero on loop
|
|
36
|
+
const largerThanAll = comparePoss(pos, maxNode.pointOnShape!) >= 0;
|
|
37
|
+
const r =
|
|
38
|
+
largerThanAll &&
|
|
39
|
+
minNode === succ;
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
// const r = (pos === max.pointOnShape) && (next === min);
|
|
43
|
+
|
|
44
|
+
if (!r) {
|
|
45
|
+
console.log(c);
|
|
46
|
+
console.log(cpTree.toArr().map(v => {
|
|
47
|
+
return {
|
|
48
|
+
curveIdx: v.pointOnShape.curve.idx,
|
|
49
|
+
t: v.pointOnShape.t,
|
|
50
|
+
order: v.pointOnShape.order,
|
|
51
|
+
order2: v.pointOnShape.order2
|
|
52
|
+
}
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return r;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
export { isOrderCorrect }
|
|
@@ -4,27 +4,75 @@ import { PointOnShape } from "../../point-on-shape/point-on-shape.js";
|
|
|
4
4
|
import { comparePoss } from "../../point-on-shape/compare-poss.js";
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param cpTree
|
|
10
|
+
* @param pos
|
|
11
|
+
* @param succ
|
|
12
|
+
* @param alreadyInserted set to `true` if the given `pos` has already been
|
|
13
|
+
* inserted, `false` otherwise.
|
|
14
|
+
*/
|
|
7
15
|
function isOrderCorrect(
|
|
8
16
|
cpTree: RbTree<CpNode>,
|
|
17
|
+
pred: CpNode,
|
|
9
18
|
pos: PointOnShape,
|
|
10
|
-
|
|
19
|
+
succ: CpNode,
|
|
20
|
+
alreadyInserted: boolean): boolean {
|
|
11
21
|
|
|
12
|
-
|
|
13
|
-
if (c < 0) { return true; }
|
|
14
|
-
if (c === 0) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const minNode = cpTree.getMinNode();
|
|
19
|
-
if (minNode === undefined) { // if tree not empty
|
|
22
|
+
if (cpTree.size <= (alreadyInserted ? 2 : 1)) {
|
|
20
23
|
return true;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
const
|
|
24
|
-
const
|
|
26
|
+
const a = pred.pointOnShape;
|
|
27
|
+
const b = pos;
|
|
28
|
+
const c = succ.pointOnShape;
|
|
29
|
+
|
|
30
|
+
// Let a = pred, b = pos, and c = succ.
|
|
31
|
+
// For a cyclic order we need:
|
|
32
|
+
// `(a < b && b < c)` || `(b < c && c < a)` || `(c < a && a < b)`
|
|
33
|
+
// Let `X = a < b, Y = b < c, Z = c < a` then
|
|
34
|
+
// `(X && Y)` || `(Y && Z)` || `(Z && X)`
|
|
35
|
+
|
|
36
|
+
const _X = comparePoss(a, b);
|
|
37
|
+
if (_X === 0) { // two points must have different order
|
|
38
|
+
// console.log('aaa');
|
|
39
|
+
// throw 'a';
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const _Y = comparePoss(b, c);
|
|
43
|
+
if (_Y === 0) { // two points must have different order
|
|
44
|
+
// console.log('bbb');
|
|
45
|
+
// throw 'b';
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const _Z = comparePoss(c, a);
|
|
49
|
+
if (_Z === 0) {
|
|
50
|
+
// console.log('bbb');
|
|
51
|
+
// throw 'c';
|
|
52
|
+
return false;
|
|
53
|
+
} // impossible anyway at this point
|
|
54
|
+
|
|
55
|
+
// Now neither `_X` nor `_Y` nor `_Z` === `0`
|
|
56
|
+
const X = _X < 0;
|
|
57
|
+
const Y = _Y < 0;
|
|
58
|
+
const Z = _Z < 0;
|
|
59
|
+
|
|
60
|
+
// Cyclic order condition
|
|
61
|
+
if ((X && Y) || (Y && Z) || (Z && X)) { return true; }
|
|
62
|
+
|
|
63
|
+
// throw 'd';
|
|
64
|
+
|
|
65
|
+
// console.log(_X, _Y, _Z);
|
|
66
|
+
// console.log(cpTree.toArr().map(v => {
|
|
67
|
+
// return {
|
|
68
|
+
// curveIdx: v.pointOnShape.curve.idx,
|
|
69
|
+
// t: v.pointOnShape.t,
|
|
70
|
+
// order: v.pointOnShape.order,
|
|
71
|
+
// order2: v.pointOnShape.order2
|
|
72
|
+
// }
|
|
73
|
+
// }));
|
|
25
74
|
|
|
26
|
-
|
|
27
|
-
return (comparePoss(max.pointOnShape!, pos) < 0 && min === next);
|
|
75
|
+
return false;
|
|
28
76
|
}
|
|
29
77
|
|
|
30
78
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { PointOnShape } from "../../point-on-shape/point-on-shape.js";
|
|
2
2
|
import type { CpNode } from "../cp-node";
|
|
3
3
|
import type { CpNodeStringifyable } from "./cp-node-stringifyable";
|
|
4
|
+
import type { Mutable } from "../../utils/mutable.js";
|
|
4
5
|
import { EDGES } from "./cp-node-stringifyable";
|
|
6
|
+
import { getAllOnCircle } from "../fs/get-all-on-circle.js";
|
|
5
7
|
import { loopFromStringifyable } from './loop-from-stringifyable.js';
|
|
6
8
|
|
|
7
9
|
|
|
@@ -51,6 +53,21 @@ function fromStringifyable(
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
//--------------------------------------------------------------------------
|
|
57
|
+
// Third pass: one shared `Circle` object per on-circle ring, as when the
|
|
58
|
+
// MAT was built (the JSON gave every contact point its own copy).
|
|
59
|
+
//--------------------------------------------------------------------------
|
|
60
|
+
const done = new Set<CpNode>();
|
|
61
|
+
for (const cpNode of cpNodes) {
|
|
62
|
+
if (done.has(cpNode)) { continue; }
|
|
63
|
+
|
|
64
|
+
const { circle } = cpNode.pointOnShape;
|
|
65
|
+
for (const cp of getAllOnCircle(cpNode)) {
|
|
66
|
+
done.add(cp);
|
|
67
|
+
(cp.pointOnShape as Mutable<PointOnShape>).circle = circle;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
54
71
|
return cpNodes[0];
|
|
55
72
|
}
|
|
56
73
|
|
|
@@ -4,7 +4,18 @@ import { CpNode } from "../../cp-node/cp-node.js";
|
|
|
4
4
|
import { getAllOnLoop } from "../../cp-node/fs/get-all-on-loop.js";
|
|
5
5
|
import { getProngCount } from "../../cp-node/fs/get-prong-count.js";
|
|
6
6
|
import { isOrderCorrect } from "../../cp-node/fs/is-order-correct.js";
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
interface Order {
|
|
10
|
+
prongCount: number;
|
|
11
|
+
cpNode: CpNode;
|
|
12
|
+
p: number[];
|
|
13
|
+
/** From highest to lowest significance */
|
|
14
|
+
curveIdx: number;
|
|
15
|
+
t: number;
|
|
16
|
+
order: number;
|
|
17
|
+
order2: number;
|
|
18
|
+
}
|
|
8
19
|
|
|
9
20
|
|
|
10
21
|
function checkOrdering(
|
|
@@ -16,81 +27,53 @@ function checkOrdering(
|
|
|
16
27
|
const cpNodes = getAllOnLoop(cpStart);
|
|
17
28
|
|
|
18
29
|
const wrongsPerLoop: Map<Loop,[Order,Order][]> = new Map();
|
|
19
|
-
for (const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
// const loopB = posB.curve.loop;
|
|
32
|
-
// const cpTreeB = cpTrees.get(loopB)!;
|
|
33
|
-
|
|
30
|
+
for (const cpNode of cpNodes) {
|
|
31
|
+
const pos = cpNode.pointOnShape;
|
|
32
|
+
const loop = pos.curve.loop;
|
|
33
|
+
|
|
34
|
+
if (loop !== cpNode.prev.pointOnShape.curve.loop ||
|
|
35
|
+
loop !== cpNode.next.pointOnShape.curve.loop) {
|
|
36
|
+
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const cpTree = cpTrees.get(loop)!;
|
|
41
|
+
|
|
34
42
|
const orderCorrect = isOrderCorrect(
|
|
35
|
-
|
|
36
|
-
)
|
|
43
|
+
cpTree, cpNode.prev, cpNode.pointOnShape, cpNode.next, true
|
|
44
|
+
);
|
|
37
45
|
if (!orderCorrect) {
|
|
38
|
-
const A
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
curveIdx: idxA,
|
|
43
|
-
t: posA.t,
|
|
44
|
-
order: posA.order,
|
|
45
|
-
order2: posA.order2
|
|
46
|
-
}
|
|
47
|
-
const B: Order = {
|
|
48
|
-
prongCount: getProngCount(cpNodeB),
|
|
49
|
-
p: pB,
|
|
50
|
-
cpNode: cpNodeB,
|
|
51
|
-
curveIdx: idxB,
|
|
52
|
-
t: posB.t,
|
|
53
|
-
order: posB.order,
|
|
54
|
-
order2: posB.order2
|
|
55
|
-
}
|
|
56
|
-
const loopA = A.cpNode.pointOnShape.curve.loop;
|
|
57
|
-
let wrongs = wrongsPerLoop.get(loopA);
|
|
58
|
-
if (wrongs === undefined) {
|
|
59
|
-
wrongs = [];
|
|
60
|
-
wrongsPerLoop.set(loopA, wrongs);
|
|
61
|
-
}
|
|
46
|
+
const A = toOrder(cpNode);
|
|
47
|
+
const B = toOrder(cpNode.next);
|
|
48
|
+
|
|
49
|
+
let wrongs = wrongsPerLoop.getOrInsert(loop, []);
|
|
62
50
|
wrongs.push([A,B]);
|
|
63
51
|
}
|
|
64
52
|
}
|
|
65
53
|
|
|
66
54
|
for (const [loop,wrongs] of wrongsPerLoop) {
|
|
67
|
-
let maxOrder = -Infinity;
|
|
68
|
-
let max: Order | undefined = undefined;
|
|
69
55
|
for (const wrong of wrongs) {
|
|
70
56
|
const A = wrong[0];
|
|
71
57
|
const B = wrong[1];
|
|
72
|
-
const c = comparePoss(A.cpNode.pointOnShape, B.cpNode.pointOnShape);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// console.log(wrong[1]);
|
|
78
|
-
// console.log('---------');
|
|
58
|
+
// const c = comparePoss(A.cpNode.pointOnShape, B.cpNode.pointOnShape);
|
|
59
|
+
|
|
60
|
+
console.log(A);
|
|
61
|
+
console.log(B);
|
|
62
|
+
console.log('---------');
|
|
79
63
|
}
|
|
80
64
|
}
|
|
81
|
-
// }
|
|
82
65
|
}
|
|
83
66
|
|
|
84
67
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
order2
|
|
68
|
+
function toOrder(
|
|
69
|
+
cpNode: CpNode): Order {
|
|
70
|
+
|
|
71
|
+
const prongCount = getProngCount(cpNode);
|
|
72
|
+
const { pointOnShape } = cpNode;
|
|
73
|
+
const { p, t, order, order2, curve } = pointOnShape;
|
|
74
|
+
const curveIdx = curve.idx;
|
|
75
|
+
|
|
76
|
+
return { cpNode, prongCount, p, curveIdx, t, order, order2 };
|
|
94
77
|
}
|
|
95
78
|
|
|
96
79
|
|
|
@@ -83,13 +83,19 @@ function closeHole(
|
|
|
83
83
|
|
|
84
84
|
const [cpNodeSource, cpNodeAntipode] = cpNodes as Mutable<CpNode>[];
|
|
85
85
|
|
|
86
|
+
// The twins form their own on-circle ring (a separate MAT vertex - that
|
|
87
|
+
// is what cuts the hole), so they get their own (equal) `Circle` object:
|
|
88
|
+
// one `Circle` object per ring.
|
|
89
|
+
const twinCircle: Circle = { ...cpNodeSource.pointOnShape.circle };
|
|
90
|
+
|
|
86
91
|
// Antipode - create and insert twin
|
|
87
92
|
const posAntipode = cpNodeAntipode.pointOnShape;
|
|
88
93
|
const cpNodeAntipodeTwin = insertCpNode(
|
|
89
94
|
true, true, false,
|
|
90
95
|
cpTrees.get(posAntipode.curve.loop)!,
|
|
91
|
-
{ ...posAntipode, order2: +1 },
|
|
96
|
+
{ ...posAntipode, circle: twinCircle, order2: +1 },
|
|
92
97
|
cpNodeAntipode,
|
|
98
|
+
undefined, // unused
|
|
93
99
|
meta.lastInsertId
|
|
94
100
|
)! as Mutable<CpNode>;
|
|
95
101
|
cpNodeAntipodeTwin.holeCloserTwin = cpNodeAntipode;
|
|
@@ -100,8 +106,9 @@ function closeHole(
|
|
|
100
106
|
const cpNodeSourceTwin = insertCpNode(
|
|
101
107
|
true, true, false,
|
|
102
108
|
cpTrees.get(posSource.curve.loop)!,
|
|
103
|
-
{ ...
|
|
109
|
+
{ ...posSource!, circle: twinCircle, order2: -1 },
|
|
104
110
|
cpNodeSource.prev,
|
|
111
|
+
undefined, // unused
|
|
105
112
|
meta.lastInsertId
|
|
106
113
|
)! as Mutable<CpNode>;
|
|
107
114
|
cpNodeSourceTwin.holeCloserTwin = cpNodeSource;
|
|
@@ -89,10 +89,10 @@ export { calcSeperationTolerance }
|
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
// function degToRad(degrees: number) {
|
|
92
|
-
// return degrees/360*2*
|
|
92
|
+
// return degrees/360*2*PI;
|
|
93
93
|
// }
|
|
94
94
|
// function radToDeg(rad: number) {
|
|
95
|
-
// return rad*360/2/
|
|
95
|
+
// return rad*360/2/PI;
|
|
96
96
|
// }
|
|
97
97
|
|
|
98
98
|
|
|
@@ -37,7 +37,7 @@ function findAndAdd2Prongs(
|
|
|
37
37
|
for (let i=0; i<for2Prongs.length; i++) {
|
|
38
38
|
const pos = for2Prongs[i];
|
|
39
39
|
|
|
40
|
-
const angles = getValidAngles(angleIncrement_, pos);
|
|
40
|
+
const angles = for1Prong ? [0] : getValidAngles(angleIncrement_, pos);
|
|
41
41
|
|
|
42
42
|
for (const angle of angles) {
|
|
43
43
|
const twoProngInfo = find2Prong_(
|
|
@@ -2,7 +2,7 @@ import type { Loop } from 'flo-boolean';
|
|
|
2
2
|
import type { CurvePiece } from '../mat/curve-piece.js';
|
|
3
3
|
import type { PrePointOnShape } from '../point-on-shape/point-on-shape.js';
|
|
4
4
|
import type { MatMeta } from '../mat/mat-meta.js';
|
|
5
|
-
import {
|
|
5
|
+
import { getCpNodeNeighbors } from '../mat/get-cp-node-neighbors.js';
|
|
6
6
|
import { getBoundaryPieceBeziers } from '../mat/get-boundary-piece-beziers.js';
|
|
7
7
|
import { calcPosOrder } from '../point-on-shape/calc-pos-order.js';
|
|
8
8
|
import { isPosCorner } from '../point-on-shape/is-pos-corner.js';
|
|
@@ -38,7 +38,7 @@ function getInitialCurvePieces(
|
|
|
38
38
|
: calcPosOrder(xO, yPos)
|
|
39
39
|
: 0;
|
|
40
40
|
|
|
41
|
-
const cpNode =
|
|
41
|
+
const cpNode = getCpNodeNeighbors(cpTrees.get(loop)!, yPos, order, 0)?.[0];
|
|
42
42
|
if (!cpNode ||
|
|
43
43
|
// The special case if there is only a single sharp corner or
|
|
44
44
|
// two-way terminating 2-prong currently in the MAT. Don't remove!
|
|
@@ -28,7 +28,7 @@ const { min, abs, asin } = Math;
|
|
|
28
28
|
* @internal
|
|
29
29
|
*/
|
|
30
30
|
function find3ProngForDelta3s(
|
|
31
|
-
δs: CpNode
|
|
31
|
+
δs: [CpNode, CpNode][],
|
|
32
32
|
idx: number,
|
|
33
33
|
k: number,
|
|
34
34
|
curvePiecess: CurvePiece[][],
|
|
@@ -37,25 +37,33 @@ function find3ProngForDelta3s(
|
|
|
37
37
|
const TOLERANCE = 2**(maxCoordPowerOf2 - 32);
|
|
38
38
|
const MAX_ITERATIONS = 10;
|
|
39
39
|
|
|
40
|
-
const δs_ = [
|
|
40
|
+
const δs_: [[CpNode,CpNode], [CpNode,CpNode], [CpNode,CpNode]] = [
|
|
41
41
|
δs[0],
|
|
42
42
|
δs[idx],
|
|
43
43
|
δs[δs.length-1]
|
|
44
44
|
];
|
|
45
45
|
|
|
46
|
-
const curvePieces_ = [
|
|
46
|
+
const curvePieces_: [CurvePiece[], CurvePiece[], CurvePiece[]] = [
|
|
47
47
|
curvePiecess[0],
|
|
48
48
|
curvePiecess[idx],
|
|
49
|
-
curvePiecess[δs.length-1]
|
|
49
|
+
curvePiecess[δs.length - 1]
|
|
50
50
|
];
|
|
51
51
|
|
|
52
|
-
const δ3ss
|
|
52
|
+
const δ3ss: [
|
|
53
|
+
[[CpNode, CpNode], [CpNode, CpNode], [CpNode, CpNode]],
|
|
54
|
+
[[CpNode, CpNode], [CpNode, CpNode], [CpNode, CpNode]],
|
|
55
|
+
[[CpNode, CpNode], [CpNode, CpNode], [CpNode, CpNode]]
|
|
56
|
+
] = [
|
|
53
57
|
[δs_[0], δs_[1], δs_[2]],
|
|
54
58
|
[δs_[1], δs_[2], δs_[0]],
|
|
55
59
|
[δs_[2], δs_[0], δs_[1]],
|
|
56
60
|
];
|
|
57
61
|
|
|
58
|
-
const curvePiecess_
|
|
62
|
+
const curvePiecess_: [
|
|
63
|
+
[CurvePiece[], CurvePiece[], CurvePiece[]],
|
|
64
|
+
[CurvePiece[], CurvePiece[], CurvePiece[]],
|
|
65
|
+
[CurvePiece[], CurvePiece[], CurvePiece[]]
|
|
66
|
+
] = [
|
|
59
67
|
[curvePieces_[0], curvePieces_[1], curvePieces_[2]],
|
|
60
68
|
[curvePieces_[1], curvePieces_[2], curvePieces_[0]],
|
|
61
69
|
[curvePieces_[2], curvePieces_[0], curvePieces_[1]],
|
|
@@ -166,7 +174,7 @@ function find3ProngForDelta3s(
|
|
|
166
174
|
const v2 = toUnitVector( tangent(pos.curve.ps, pos.t) );
|
|
167
175
|
|
|
168
176
|
// Cross is more numerically stable than Vector.dot at angles a
|
|
169
|
-
// multiple of
|
|
177
|
+
// multiple of π **and** is close to the actual angle value
|
|
170
178
|
// and can thus just be added to cone method of looking at
|
|
171
179
|
// tolerance.
|
|
172
180
|
|
|
@@ -14,7 +14,7 @@ import { getBoundaryPieceBeziers } from '../mat/get-boundary-piece-beziers.js';
|
|
|
14
14
|
* @internal
|
|
15
15
|
*/
|
|
16
16
|
function find3Prong(
|
|
17
|
-
δs: CpNode
|
|
17
|
+
δs: [CpNode, CpNode][],
|
|
18
18
|
maxCoordPowerOf2: number): ThreeProngInfo {
|
|
19
19
|
|
|
20
20
|
// FUTURE if `δs.length > (some threshold (say 4 or 5))` then add two-prongs
|