brep-io-kernel 1.0.97 → 1.0.99
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/dist-kernel/brep-kernel.js +24918 -23977
- package/package.json +1 -1
- package/src/BREP/SolidMethods/booleanOps.js +20 -4
- package/src/UI/sketcher/SketchMode3D.js +157 -33
- package/src/UI/sketcher/dimensions.js +59 -0
- package/src/UI/sketcher/glyphs.js +31 -0
- package/src/UI/sketcher/highlights.js +3 -1
- package/src/features/hole/HoleFeature.js +264 -17
- package/src/features/sketch/SketchFeature.js +117 -12
- package/src/features/sketch/sketchSolver2D/ConstraintEngine.js +42 -7
- package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +104 -0
- package/src/tests/fixtures/sketchSolverTopology/README.md +46 -0
- package/src/tests/fixtures/sketchSolverTopology/coincident_chain_fixture.json +48 -0
- package/src/tests/fixtures/sketchSolverTopology/rect_width_height_fixture.json +43 -0
- package/src/tests/fixtures/sketchSolverTopology/sketch_throttel_expression_sequence_fixture.json +25 -0
- package/src/tests/partFiles/sketch_throttel_testing.BREP.json +562 -0
- package/src/tests/sketchSolverTopologyFixtureLoader.js +308 -0
- package/src/tests/test_sketch_solver_topology_stability.js +348 -0
- package/src/tests/tests.js +17 -0
|
@@ -5,12 +5,20 @@ import { distance, calculateAngle } from "./mathHelpersMod.js";
|
|
|
5
5
|
|
|
6
6
|
// === Constraint function table ===
|
|
7
7
|
const constraintFunctions = constraints.constraintFunctions;
|
|
8
|
+
let globalDistanceSolveCycleId = 0;
|
|
8
9
|
|
|
9
10
|
// === Engine that performs numeric solving on a sketch snapshot ===
|
|
10
11
|
class ConstraintEngine {
|
|
11
12
|
constructor(sketchJSON) {
|
|
12
13
|
const sketch = JSON.parse(sketchJSON);
|
|
13
|
-
this.points = sketch.points.map(p => new Point(
|
|
14
|
+
this.points = sketch.points.map(p => new Point(
|
|
15
|
+
p.id,
|
|
16
|
+
p.x,
|
|
17
|
+
p.y,
|
|
18
|
+
p.fixed,
|
|
19
|
+
p.construction,
|
|
20
|
+
p.externalReference
|
|
21
|
+
));
|
|
14
22
|
this.geometries = sketch.geometries || [];
|
|
15
23
|
this.constraints = sketch.constraints || [];
|
|
16
24
|
}
|
|
@@ -63,6 +71,17 @@ class ConstraintEngine {
|
|
|
63
71
|
|
|
64
72
|
solve(iterations = 100) {
|
|
65
73
|
const decimalsPlaces = 6;
|
|
74
|
+
this._distanceSolveCycleId = ++globalDistanceSolveCycleId;
|
|
75
|
+
const hasPendingDistanceTargetSlides = () => {
|
|
76
|
+
const slideTol = Number.isFinite(constraints?.tolerance) ? Number(constraints.tolerance) : 1e-8;
|
|
77
|
+
return this.constraints.some((c) => (
|
|
78
|
+
c?.type === "⟺" &&
|
|
79
|
+
c?._distanceThrottleActive === true &&
|
|
80
|
+
Number.isFinite(c?._distanceRequestedTarget) &&
|
|
81
|
+
Number.isFinite(c?._distanceAppliedTarget) &&
|
|
82
|
+
Math.abs(c._distanceRequestedTarget - c._distanceAppliedTarget) > slideTol
|
|
83
|
+
));
|
|
84
|
+
};
|
|
66
85
|
|
|
67
86
|
// Implied constraints for certain geometry types (e.g., arcs, bezier splines)
|
|
68
87
|
let nextTempId =
|
|
@@ -101,6 +120,7 @@ class ConstraintEngine {
|
|
|
101
120
|
this.tidyDecimalsOfPoints(decimalsPlaces, true);
|
|
102
121
|
|
|
103
122
|
// Ground first, then everything
|
|
123
|
+
this._distanceSolvePassToken = `${this._distanceSolveCycleId}:pre`;
|
|
104
124
|
this.processConstraintsOfType("⏚");
|
|
105
125
|
this.processConstraintsOfType("all");
|
|
106
126
|
|
|
@@ -114,6 +134,7 @@ class ConstraintEngine {
|
|
|
114
134
|
let converged = false;
|
|
115
135
|
|
|
116
136
|
for (let i = 0; i < iterations; i++) {
|
|
137
|
+
this._distanceSolvePassToken = `${this._distanceSolveCycleId}:${i}`;
|
|
117
138
|
for (const t of order) {
|
|
118
139
|
this.processConstraintsOfType(t);
|
|
119
140
|
this.processConstraintsOfType("≡"); // keep coincident snapping frequently
|
|
@@ -126,7 +147,7 @@ class ConstraintEngine {
|
|
|
126
147
|
}
|
|
127
148
|
|
|
128
149
|
const cur = JSON.stringify(this.points);
|
|
129
|
-
if (cur === prev) {
|
|
150
|
+
if (cur === prev && !hasPendingDistanceTargetSlides()) {
|
|
130
151
|
converged = true;
|
|
131
152
|
break;
|
|
132
153
|
}
|
|
@@ -150,7 +171,14 @@ class ConstraintEngine {
|
|
|
150
171
|
|
|
151
172
|
// Return a new sketch object mirroring input structure
|
|
152
173
|
const updatedSketch = {
|
|
153
|
-
points: this.points.map(p => ({
|
|
174
|
+
points: this.points.map(p => ({
|
|
175
|
+
id: p.id,
|
|
176
|
+
x: p.x,
|
|
177
|
+
y: p.y,
|
|
178
|
+
fixed: p.fixed,
|
|
179
|
+
construction: p.construction === true,
|
|
180
|
+
externalReference: p.externalReference === true
|
|
181
|
+
})),
|
|
154
182
|
geometries: this.geometries,
|
|
155
183
|
constraints: this.constraints.filter(c => !c.temporary) // drop temporaries
|
|
156
184
|
};
|
|
@@ -160,11 +188,13 @@ class ConstraintEngine {
|
|
|
160
188
|
}
|
|
161
189
|
|
|
162
190
|
class Point {
|
|
163
|
-
constructor(id, x, y, fixed = false) {
|
|
191
|
+
constructor(id, x, y, fixed = false, construction = false, externalReference = false) {
|
|
164
192
|
this.id = id;
|
|
165
193
|
this.x = x;
|
|
166
194
|
this.y = y;
|
|
167
195
|
this.fixed = fixed;
|
|
196
|
+
this.construction = construction === true;
|
|
197
|
+
this.externalReference = externalReference === true;
|
|
168
198
|
}
|
|
169
199
|
}
|
|
170
200
|
|
|
@@ -188,7 +218,7 @@ export class ConstraintSolver {
|
|
|
188
218
|
this.appState = opts.appState || { mode: "", type: "", requiredSelections: 0 };
|
|
189
219
|
|
|
190
220
|
this.sketchObject = opts.sketch ? sanitizeSketch(opts.sketch) : {
|
|
191
|
-
points: [{ id: 0, x: 0, y: 0, fixed: true }],
|
|
221
|
+
points: [{ id: 0, x: 0, y: 0, fixed: true, construction: true, externalReference: false }],
|
|
192
222
|
geometries: [],
|
|
193
223
|
constraints: [{ id: 0, type: "⏚", points: [0] }]
|
|
194
224
|
};
|
|
@@ -892,14 +922,19 @@ export class ConstraintSolver {
|
|
|
892
922
|
function sanitizeSketch(sketch) {
|
|
893
923
|
const s = {
|
|
894
924
|
points: Array.isArray(sketch.points) ? sketch.points.map(p => ({
|
|
895
|
-
id: +p.id,
|
|
925
|
+
id: +p.id,
|
|
926
|
+
x: +p.x,
|
|
927
|
+
y: +p.y,
|
|
928
|
+
fixed: !!p.fixed,
|
|
929
|
+
construction: (typeof p?.construction === "boolean") ? p.construction : (+p?.id === 0),
|
|
930
|
+
externalReference: !!p?.externalReference
|
|
896
931
|
})) : [],
|
|
897
932
|
geometries: Array.isArray(sketch.geometries) ? sketch.geometries.slice() : [],
|
|
898
933
|
constraints: Array.isArray(sketch.constraints) ? sketch.constraints.slice() : []
|
|
899
934
|
};
|
|
900
935
|
|
|
901
936
|
// Ensure at least an origin and ground if empty
|
|
902
|
-
if (s.points.length === 0) s.points.push({ id: 0, x: 0, y: 0, fixed: true });
|
|
937
|
+
if (s.points.length === 0) s.points.push({ id: 0, x: 0, y: 0, fixed: true, construction: true, externalReference: false });
|
|
903
938
|
if (!s.constraints.some(c => c.type === "⏚")) {
|
|
904
939
|
s.constraints.push({ id: 0, type: "⏚", points: [0] });
|
|
905
940
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
import { calculateAngle, rotatePoint, distance, roundToDecimals } from "./mathHelpersMod.js";
|
|
3
3
|
let tolerance = 0.00001;
|
|
4
|
+
let distanceSlideThresholdRatio = 0.10;
|
|
5
|
+
let distanceSlideStepRatio = 0.10;
|
|
6
|
+
let distanceSlideMinStep = 0.001;
|
|
4
7
|
const constraintFunctions = [];
|
|
5
8
|
|
|
6
9
|
const normalizeAngle = (angle) => ((angle % 360) + 360) % 360;
|
|
@@ -9,6 +12,88 @@ const shortestAngleDelta = (target, current) => {
|
|
|
9
12
|
return (delta > 180) ? delta - 360 : delta;
|
|
10
13
|
};
|
|
11
14
|
|
|
15
|
+
function relativeDeltaRatio(a, b, floor = tolerance) {
|
|
16
|
+
const denom = Math.max(Math.abs(a), Math.abs(b), floor);
|
|
17
|
+
return Math.abs(a - b) / denom;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function resolveDistanceTargetForSolvePass(solverObject, constraint, requestedTarget) {
|
|
21
|
+
if (constraint?.type !== "⟺" || !Number.isFinite(requestedTarget)) {
|
|
22
|
+
return requestedTarget;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const previousRequested = Number.isFinite(constraint._distanceRequestedTarget)
|
|
26
|
+
? constraint._distanceRequestedTarget
|
|
27
|
+
: null;
|
|
28
|
+
|
|
29
|
+
let appliedTarget = Number.isFinite(constraint._distanceAppliedTarget)
|
|
30
|
+
? constraint._distanceAppliedTarget
|
|
31
|
+
: (previousRequested ?? requestedTarget);
|
|
32
|
+
|
|
33
|
+
const targetChanged = previousRequested !== null &&
|
|
34
|
+
Math.abs(requestedTarget - previousRequested) > tolerance;
|
|
35
|
+
|
|
36
|
+
if (previousRequested === null) {
|
|
37
|
+
constraint._distanceThrottleActive = false;
|
|
38
|
+
appliedTarget = requestedTarget;
|
|
39
|
+
} else if (targetChanged) {
|
|
40
|
+
const rel = relativeDeltaRatio(requestedTarget, previousRequested, tolerance);
|
|
41
|
+
if (rel > distanceSlideThresholdRatio) {
|
|
42
|
+
constraint._distanceThrottleActive = true;
|
|
43
|
+
} else {
|
|
44
|
+
constraint._distanceThrottleActive = false;
|
|
45
|
+
appliedTarget = requestedTarget;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
constraint._distanceRequestedTarget = requestedTarget;
|
|
50
|
+
|
|
51
|
+
if (!constraint._distanceThrottleActive) {
|
|
52
|
+
constraint._distanceAppliedTarget = requestedTarget;
|
|
53
|
+
return requestedTarget;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const solvePassToken = (typeof solverObject?._distanceSolvePassToken === "string")
|
|
57
|
+
? solverObject._distanceSolvePassToken
|
|
58
|
+
: null;
|
|
59
|
+
const lastAppliedPassToken = (typeof constraint._distanceLastAppliedPassToken === "string")
|
|
60
|
+
? constraint._distanceLastAppliedPassToken
|
|
61
|
+
: null;
|
|
62
|
+
|
|
63
|
+
if (solvePassToken !== null && lastAppliedPassToken === solvePassToken) {
|
|
64
|
+
return Number.isFinite(constraint._distanceAppliedTarget)
|
|
65
|
+
? constraint._distanceAppliedTarget
|
|
66
|
+
: appliedTarget;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const delta = requestedTarget - appliedTarget;
|
|
70
|
+
const absDelta = Math.abs(delta);
|
|
71
|
+
if (absDelta <= tolerance) {
|
|
72
|
+
constraint._distanceThrottleActive = false;
|
|
73
|
+
constraint._distanceAppliedTarget = requestedTarget;
|
|
74
|
+
if (solvePassToken !== null) constraint._distanceLastAppliedPassToken = solvePassToken;
|
|
75
|
+
return requestedTarget;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Scale slide speed by the remaining gap so large drops (e.g. 1000 -> 1)
|
|
79
|
+
// can still settle within a single solve evaluation.
|
|
80
|
+
const maxStep = Math.max(
|
|
81
|
+
distanceSlideMinStep,
|
|
82
|
+
absDelta * distanceSlideStepRatio
|
|
83
|
+
);
|
|
84
|
+
const step = Math.min(absDelta, maxStep);
|
|
85
|
+
appliedTarget += Math.sign(delta) * step;
|
|
86
|
+
|
|
87
|
+
if (Math.abs(requestedTarget - appliedTarget) <= tolerance) {
|
|
88
|
+
appliedTarget = requestedTarget;
|
|
89
|
+
constraint._distanceThrottleActive = false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
constraint._distanceAppliedTarget = appliedTarget;
|
|
93
|
+
if (solvePassToken !== null) constraint._distanceLastAppliedPassToken = solvePassToken;
|
|
94
|
+
return appliedTarget;
|
|
95
|
+
}
|
|
96
|
+
|
|
12
97
|
|
|
13
98
|
(constraintFunctions["━"] = function (solverObject, constraint, points, constraintValue) {
|
|
14
99
|
// Horizontal constraint
|
|
@@ -74,8 +159,15 @@ const shortestAngleDelta = (target, current) => {
|
|
|
74
159
|
if (isNaN(constraintValue) | constraintValue == undefined | constraintValue == null) {
|
|
75
160
|
targetDistance = currentDistance;
|
|
76
161
|
constraint.value = currentDistance;
|
|
162
|
+
if (constraint?.type === "⟺") {
|
|
163
|
+
constraint._distanceRequestedTarget = currentDistance;
|
|
164
|
+
constraint._distanceAppliedTarget = currentDistance;
|
|
165
|
+
constraint._distanceThrottleActive = false;
|
|
166
|
+
constraint._distanceLastAppliedPassToken = null;
|
|
167
|
+
}
|
|
77
168
|
}
|
|
78
169
|
|
|
170
|
+
targetDistance = resolveDistanceTargetForSolvePass(solverObject, constraint, targetDistance);
|
|
79
171
|
|
|
80
172
|
|
|
81
173
|
let diff = roundToDecimals(Math.abs(targetDistance) - currentDistance, 4);
|
|
@@ -691,6 +783,18 @@ const shortestAngleDelta = (target, current) => {
|
|
|
691
783
|
export const constraints = {
|
|
692
784
|
get tolerance() { return tolerance; },
|
|
693
785
|
set tolerance(value) { tolerance = value; },
|
|
786
|
+
get distanceSlideThresholdRatio() { return distanceSlideThresholdRatio; },
|
|
787
|
+
set distanceSlideThresholdRatio(value) {
|
|
788
|
+
if (Number.isFinite(value) && value >= 0) distanceSlideThresholdRatio = Number(value);
|
|
789
|
+
},
|
|
790
|
+
get distanceSlideStepRatio() { return distanceSlideStepRatio; },
|
|
791
|
+
set distanceSlideStepRatio(value) {
|
|
792
|
+
if (Number.isFinite(value) && value >= 0) distanceSlideStepRatio = Number(value);
|
|
793
|
+
},
|
|
794
|
+
get distanceSlideMinStep() { return distanceSlideMinStep; },
|
|
795
|
+
set distanceSlideMinStep(value) {
|
|
796
|
+
if (Number.isFinite(value) && value >= 0) distanceSlideMinStep = Number(value);
|
|
797
|
+
},
|
|
694
798
|
constraintFunctions,
|
|
695
799
|
};
|
|
696
800
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Sketch Solver Topology Fixtures
|
|
2
|
+
|
|
3
|
+
Drop `.json` files in this folder to add sketch-solver topology regression tests automatically.
|
|
4
|
+
|
|
5
|
+
Each file becomes its own test in `pnpm test` via `registerSketchSolverTopologyFixtureTests(...)`.
|
|
6
|
+
|
|
7
|
+
## Fixture schema
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"name": "rect_width_height_fixture",
|
|
12
|
+
"sketch": { "points": [], "geometries": [], "constraints": [] },
|
|
13
|
+
"sourcePartFile": "src/tests/partFiles/your_case.BREP.json",
|
|
14
|
+
"sourceFeatureId": "S1",
|
|
15
|
+
"initialSolvePasses": 4,
|
|
16
|
+
"edits": [
|
|
17
|
+
{ "constraintId": 5, "value": 110 },
|
|
18
|
+
{ "expressionValues": { "x": 1200, "y": 2500, "z": 300 } }
|
|
19
|
+
],
|
|
20
|
+
"expect": {
|
|
21
|
+
"topologyUnchanged": true,
|
|
22
|
+
"distances": [
|
|
23
|
+
{ "a": 0, "b": 1, "value": 110, "tol": 0.06 }
|
|
24
|
+
],
|
|
25
|
+
"anchors": [
|
|
26
|
+
{ "pointId": 0, "x": 0, "y": 0, "tol": 0.01 }
|
|
27
|
+
],
|
|
28
|
+
"coincidentPairs": [
|
|
29
|
+
{ "a": 1, "b": 2, "tol": 0.01 }
|
|
30
|
+
],
|
|
31
|
+
"orientationLoops": [
|
|
32
|
+
{ "pointIds": [0, 1, 2, 3], "preserveSign": true, "minAbsArea": 1.0 }
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Notes
|
|
39
|
+
|
|
40
|
+
- Provide either `sketch` directly, or `sourcePartFile` (optional `sourceFeatureId`) to pull a sketch from an existing part file.
|
|
41
|
+
- `edits` are applied in order; solver runs after each edit.
|
|
42
|
+
- `constraintId/value` edits set one dimension directly.
|
|
43
|
+
- `expressionValues` edits evaluate every `constraint.valueExpr` using provided variables (useful for `x/y/z` expression-driven sketches).
|
|
44
|
+
- If `expect.topologyUnchanged` is omitted, it defaults to `true`.
|
|
45
|
+
- `tol` defaults to `0.01` when omitted.
|
|
46
|
+
- A fixture fails fast with a clear message if IDs are invalid or references break.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coincident_chain_fixture",
|
|
3
|
+
"sketch": {
|
|
4
|
+
"points": [
|
|
5
|
+
{ "id": 0, "x": 0, "y": 0, "fixed": true },
|
|
6
|
+
{ "id": 1, "x": 40, "y": 0, "fixed": false },
|
|
7
|
+
{ "id": 2, "x": 40, "y": 0, "fixed": false },
|
|
8
|
+
{ "id": 3, "x": 40, "y": 30, "fixed": false },
|
|
9
|
+
{ "id": 4, "x": 40, "y": 30, "fixed": false },
|
|
10
|
+
{ "id": 5, "x": 80, "y": 30, "fixed": false }
|
|
11
|
+
],
|
|
12
|
+
"geometries": [
|
|
13
|
+
{ "id": 200, "type": "line", "points": [0, 1], "construction": false },
|
|
14
|
+
{ "id": 201, "type": "line", "points": [2, 3], "construction": false },
|
|
15
|
+
{ "id": 202, "type": "line", "points": [4, 5], "construction": false }
|
|
16
|
+
],
|
|
17
|
+
"constraints": [
|
|
18
|
+
{ "id": 0, "type": "⏚", "points": [0] },
|
|
19
|
+
{ "id": 10, "type": "≡", "points": [1, 2] },
|
|
20
|
+
{ "id": 11, "type": "≡", "points": [3, 4] },
|
|
21
|
+
{ "id": 12, "type": "━", "points": [0, 1] },
|
|
22
|
+
{ "id": 13, "type": "│", "points": [2, 3] },
|
|
23
|
+
{ "id": 14, "type": "━", "points": [4, 5] },
|
|
24
|
+
{ "id": 15, "type": "⟺", "points": [0, 1], "value": 40 },
|
|
25
|
+
{ "id": 16, "type": "⟺", "points": [2, 3], "value": 30 },
|
|
26
|
+
{ "id": 17, "type": "⟺", "points": [4, 5], "value": 40 }
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"edits": [
|
|
30
|
+
{ "constraintId": 16, "value": 75 },
|
|
31
|
+
{ "constraintId": 15, "value": 60 }
|
|
32
|
+
],
|
|
33
|
+
"expect": {
|
|
34
|
+
"topologyUnchanged": true,
|
|
35
|
+
"distances": [
|
|
36
|
+
{ "a": 0, "b": 1, "value": 60, "tol": 0.1 },
|
|
37
|
+
{ "a": 2, "b": 3, "value": 75, "tol": 0.1 },
|
|
38
|
+
{ "a": 4, "b": 5, "value": 40, "tol": 0.1 }
|
|
39
|
+
],
|
|
40
|
+
"anchors": [
|
|
41
|
+
{ "pointId": 0, "x": 0, "y": 0, "tol": 0.02 }
|
|
42
|
+
],
|
|
43
|
+
"coincidentPairs": [
|
|
44
|
+
{ "a": 1, "b": 2, "tol": 0.02 },
|
|
45
|
+
{ "a": 3, "b": 4, "tol": 0.02 }
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rect_width_height_fixture",
|
|
3
|
+
"sketch": {
|
|
4
|
+
"points": [
|
|
5
|
+
{ "id": 0, "x": 0, "y": 0, "fixed": true },
|
|
6
|
+
{ "id": 1, "x": 40, "y": 0, "fixed": false },
|
|
7
|
+
{ "id": 2, "x": 40, "y": 20, "fixed": false },
|
|
8
|
+
{ "id": 3, "x": 0, "y": 20, "fixed": false }
|
|
9
|
+
],
|
|
10
|
+
"geometries": [
|
|
11
|
+
{ "id": 100, "type": "line", "points": [0, 1], "construction": false },
|
|
12
|
+
{ "id": 101, "type": "line", "points": [1, 2], "construction": false },
|
|
13
|
+
{ "id": 102, "type": "line", "points": [2, 3], "construction": false },
|
|
14
|
+
{ "id": 103, "type": "line", "points": [3, 0], "construction": false }
|
|
15
|
+
],
|
|
16
|
+
"constraints": [
|
|
17
|
+
{ "id": 0, "type": "⏚", "points": [0] },
|
|
18
|
+
{ "id": 1, "type": "━", "points": [0, 1] },
|
|
19
|
+
{ "id": 2, "type": "━", "points": [2, 3] },
|
|
20
|
+
{ "id": 3, "type": "│", "points": [1, 2] },
|
|
21
|
+
{ "id": 4, "type": "│", "points": [3, 0] },
|
|
22
|
+
{ "id": 5, "type": "⟺", "points": [0, 1], "value": 40 },
|
|
23
|
+
{ "id": 6, "type": "⟺", "points": [1, 2], "value": 20 }
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"edits": [
|
|
27
|
+
{ "constraintId": 5, "value": 110 },
|
|
28
|
+
{ "constraintId": 6, "value": 45 }
|
|
29
|
+
],
|
|
30
|
+
"expect": {
|
|
31
|
+
"topologyUnchanged": true,
|
|
32
|
+
"distances": [
|
|
33
|
+
{ "a": 0, "b": 1, "value": 110, "tol": 0.08 },
|
|
34
|
+
{ "a": 1, "b": 2, "value": 45, "tol": 0.08 }
|
|
35
|
+
],
|
|
36
|
+
"anchors": [
|
|
37
|
+
{ "pointId": 0, "x": 0, "y": 0, "tol": 0.02 }
|
|
38
|
+
],
|
|
39
|
+
"orientationLoops": [
|
|
40
|
+
{ "pointIds": [0, 1, 2, 3], "preserveSign": true, "minAbsArea": 1.0 }
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/tests/fixtures/sketchSolverTopology/sketch_throttel_expression_sequence_fixture.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sketch_throttel_expression_sequence_fixture",
|
|
3
|
+
"sourcePartFile": "src/tests/partFiles/sketch_throttel_testing.BREP.json",
|
|
4
|
+
"sourceFeatureId": "S1",
|
|
5
|
+
"initialSolvePasses": 4,
|
|
6
|
+
"edits": [
|
|
7
|
+
{ "expressionValues": { "x": 10, "y": 20, "z": 10 } },
|
|
8
|
+
{ "expressionValues": { "x": 1200, "y": 2500, "z": 300 } }
|
|
9
|
+
],
|
|
10
|
+
"expect": {
|
|
11
|
+
"topologyUnchanged": true,
|
|
12
|
+
"distances": [
|
|
13
|
+
{ "a": 7, "b": 2, "value": 1200, "tol": 0.2 },
|
|
14
|
+
{ "a": 5, "b": 6, "value": 2500, "tol": 0.2 }
|
|
15
|
+
],
|
|
16
|
+
"anchors": [
|
|
17
|
+
{ "pointId": 0, "x": 0, "y": 0, "tol": 0.02 }
|
|
18
|
+
],
|
|
19
|
+
"coincidentPairs": [
|
|
20
|
+
{ "a": 1, "b": 2, "tol": 0.02 },
|
|
21
|
+
{ "a": 3, "b": 4, "tol": 0.02 },
|
|
22
|
+
{ "a": 6, "b": 7, "tol": 0.02 }
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|