@code3d/core 0.0.1-alpha.0 → 0.0.1-alpha.11
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 +43 -807
- package/THIRD_PARTY.md +40 -0
- package/bld/THIRD_PARTY_NOTICES.txt +182 -0
- package/bld/chunks/chunk-7FIFPADJ.js +1 -0
- package/bld/chunks/chunk-7FIFPADJ.js.map +7 -0
- package/bld/chunks/chunk-CL3E2DE4.js +19 -0
- package/bld/chunks/chunk-CL3E2DE4.js.map +7 -0
- package/bld/chunks/chunk-HZRQUHM2.js +30 -0
- package/bld/chunks/chunk-HZRQUHM2.js.map +7 -0
- package/bld/chunks/chunk-JBVIAMJ6.js +26474 -0
- package/bld/chunks/chunk-JBVIAMJ6.js.map +7 -0
- package/bld/chunks/chunk-QK6ZET47.js +31 -0
- package/bld/chunks/chunk-QK6ZET47.js.map +7 -0
- package/bld/library/bound-solver.d.ts +29 -5
- package/bld/library/bound-solver.d.ts.map +1 -1
- package/bld/library/cached.d.ts +35 -0
- package/bld/library/cached.d.ts.map +1 -0
- package/bld/library/font.d.ts +39 -0
- package/bld/library/font.d.ts.map +1 -0
- package/bld/library/google-font.d.ts +14 -0
- package/bld/library/google-font.d.ts.map +1 -0
- package/bld/library/index.d.ts +10 -2
- package/bld/library/index.d.ts.map +1 -1
- package/bld/library/index.js +94 -3
- package/bld/library/index.js.map +7 -1
- package/bld/library/inspect.d.ts +100 -0
- package/bld/library/inspect.d.ts.map +1 -0
- package/bld/library/kernel-artifact-codec.d.ts.map +1 -1
- package/bld/library/kernel-cache.d.ts +28 -7
- package/bld/library/kernel-cache.d.ts.map +1 -1
- package/bld/library/loft.d.ts.map +1 -1
- package/bld/library/material.d.ts +1 -1
- package/bld/library/material.d.ts.map +1 -1
- package/bld/library/open-cascade.d.ts +4 -0
- package/bld/library/open-cascade.d.ts.map +1 -0
- package/bld/library/relation-solver.d.ts +19 -8
- package/bld/library/relation-solver.d.ts.map +1 -1
- package/bld/library/replicad.d.ts +3 -2
- package/bld/library/replicad.d.ts.map +1 -1
- package/bld/library/replicad.js +10 -24
- package/bld/library/replicad.js.map +7 -1
- package/bld/library/retained-memory.d.ts.map +1 -1
- package/bld/library/runtime.d.ts +518 -155
- package/bld/library/runtime.d.ts.map +1 -1
- package/bld/library/sketch-solver.d.ts +1 -1
- package/bld/library/sketch-solver.d.ts.map +1 -1
- package/bld/library/sketch.d.ts +21 -1
- package/bld/library/sketch.d.ts.map +1 -1
- package/bld/library/text.d.ts +53 -0
- package/bld/library/text.d.ts.map +1 -0
- package/bld/library/three.js +3 -3
- package/bld/library/three.js.map +7 -1
- package/bld/library/topology.d.ts +6 -2
- package/bld/library/topology.d.ts.map +1 -1
- package/bld/node/index.d.ts.map +1 -1
- package/bld/node/index.js +96 -14
- package/bld/node/index.js.map +7 -1
- package/bld/node/replicad.js +13 -3
- package/bld/node/replicad.js.map +7 -1
- package/bld/tooling/index.d.ts +18 -15
- package/bld/tooling/index.d.ts.map +1 -1
- package/bld/tooling/index.js +165 -19
- package/bld/tooling/index.js.map +7 -1
- package/docs/api.md +764 -0
- package/docs/custom-primitives.mdx +58 -0
- package/docs/local-coordinates.md +187 -0
- package/docs/origins-and-rotation.mdx +105 -0
- package/docs/relations.mdx +450 -0
- package/docs/runtime.md +297 -0
- package/docs/shells.mdx +70 -0
- package/docs/sketches.md +83 -0
- package/docs/text.md +23 -0
- package/docs/topology.md +152 -0
- package/docs/values.md +119 -0
- package/package.json +20 -7
- package/src/library/bound-solver.ts +103 -73
- package/src/library/cached.ts +153 -0
- package/src/library/font.ts +186 -0
- package/src/library/google-font.ts +97 -0
- package/src/library/index.ts +53 -5
- package/src/library/inspect.ts +178 -0
- package/src/library/kernel-artifact-codec.ts +129 -20
- package/src/library/kernel-cache.ts +175 -39
- package/src/library/loft.ts +9 -1
- package/src/library/material.ts +3 -4
- package/src/library/open-cascade.ts +13 -0
- package/src/library/relation-solver.ts +230 -113
- package/src/library/replicad.ts +5 -5
- package/src/library/retained-memory.ts +6 -1
- package/src/library/runtime.ts +3769 -1178
- package/src/library/shell.ts +3 -3
- package/src/library/sketch-solver.ts +3 -1
- package/src/library/sketch.ts +112 -13
- package/src/library/text.ts +477 -0
- package/src/library/topology.ts +61 -17
- package/src/library/tsconfig.json +2 -1
- package/src/node/index.ts +14 -3
- package/src/node/tsconfig.json +2 -1
- package/src/tooling/index.ts +64 -51
- package/src/tooling/tsconfig.json +2 -1
- package/bld/library/alignment-geometry.js +0 -322
- package/bld/library/alignment-geometry.js.map +0 -1
- package/bld/library/bound-solver.js +0 -252
- package/bld/library/bound-solver.js.map +0 -1
- package/bld/library/extrude.js +0 -28
- package/bld/library/extrude.js.map +0 -1
- package/bld/library/kernel-artifact-codec.js +0 -148
- package/bld/library/kernel-artifact-codec.js.map +0 -1
- package/bld/library/kernel-cache.js +0 -245
- package/bld/library/kernel-cache.js.map +0 -1
- package/bld/library/kernel-shapes.js +0 -108
- package/bld/library/kernel-shapes.js.map +0 -1
- package/bld/library/loft.js +0 -161
- package/bld/library/loft.js.map +0 -1
- package/bld/library/material.js +0 -176
- package/bld/library/material.js.map +0 -1
- package/bld/library/model-color.js +0 -36
- package/bld/library/model-color.js.map +0 -1
- package/bld/library/open-cascade-error.js +0 -22
- package/bld/library/open-cascade-error.js.map +0 -1
- package/bld/library/relation-solver.js +0 -258
- package/bld/library/relation-solver.js.map +0 -1
- package/bld/library/retained-memory.js +0 -27
- package/bld/library/retained-memory.js.map +0 -1
- package/bld/library/runtime.js +0 -2954
- package/bld/library/runtime.js.map +0 -1
- package/bld/library/shell.js +0 -221
- package/bld/library/shell.js.map +0 -1
- package/bld/library/sketch-curve-intersections.js +0 -117
- package/bld/library/sketch-curve-intersections.js.map +0 -1
- package/bld/library/sketch-curves.js +0 -89
- package/bld/library/sketch-curves.js.map +0 -1
- package/bld/library/sketch-drag-rules.js +0 -522
- package/bld/library/sketch-drag-rules.js.map +0 -1
- package/bld/library/sketch-face.js +0 -72
- package/bld/library/sketch-face.js.map +0 -1
- package/bld/library/sketch-incidence.js +0 -114
- package/bld/library/sketch-incidence.js.map +0 -1
- package/bld/library/sketch-precision.js +0 -55
- package/bld/library/sketch-precision.js.map +0 -1
- package/bld/library/sketch-regions.js +0 -193
- package/bld/library/sketch-regions.js.map +0 -1
- package/bld/library/sketch-solver.js +0 -578
- package/bld/library/sketch-solver.js.map +0 -1
- package/bld/library/sketch.js +0 -650
- package/bld/library/sketch.js.map +0 -1
- package/bld/library/spatial.js +0 -195
- package/bld/library/spatial.js.map +0 -1
- package/bld/library/topology-id.js +0 -67
- package/bld/library/topology-id.js.map +0 -1
- package/bld/library/topology-inspection.js +0 -267
- package/bld/library/topology-inspection.js.map +0 -1
- package/bld/library/topology.js +0 -455
- package/bld/library/topology.js.map +0 -1
package/bld/library/sketch.js
DELETED
|
@@ -1,650 +0,0 @@
|
|
|
1
|
-
import { solveSketchProblem, } from './sketch-solver.js';
|
|
2
|
-
import { solveSketchDrag } from './sketch-drag-rules.js';
|
|
3
|
-
import { sketchRegions } from './sketch-regions.js';
|
|
4
|
-
import { sketchFaceModel, disposeModelObjects, isModelObject, } from './runtime.js';
|
|
5
|
-
import { sketchIncidences, sketchIncidencePoints, sketchIncidenceCurve, isPointOnSketchCurve, } from './sketch-incidence.js';
|
|
6
|
-
const definitions = new WeakMap();
|
|
7
|
-
const references = new WeakSet();
|
|
8
|
-
class SketchValue {
|
|
9
|
-
constructor(entries, options, base) {
|
|
10
|
-
const ids = new Set();
|
|
11
|
-
const points = new Map();
|
|
12
|
-
const copied = (entries ?? []).map(entry => {
|
|
13
|
-
const [kind, id, data] = entry;
|
|
14
|
-
if (!Number.isSafeInteger(id) || id < 1)
|
|
15
|
-
throw new Error('Sketch entity IDs must be positive safe integers.');
|
|
16
|
-
if (ids.has(id))
|
|
17
|
-
throw new Error(`Duplicate sketch entity ID ${id}.`);
|
|
18
|
-
ids.add(id);
|
|
19
|
-
if (kind === 'point') {
|
|
20
|
-
if (!Array.isArray(data))
|
|
21
|
-
return ['point', id, data];
|
|
22
|
-
if (data.length !== 2 || !data.every(Number.isFinite))
|
|
23
|
-
throw new Error(`Sketch point ${id} requires two finite coordinates.`);
|
|
24
|
-
const position = [data[0], data[1]];
|
|
25
|
-
points.set(id, position);
|
|
26
|
-
return ['point', id, position];
|
|
27
|
-
}
|
|
28
|
-
if (kind === 'circle' || kind === 'arc') {
|
|
29
|
-
if (!Number.isFinite(data[1]) || data[1] <= 0)
|
|
30
|
-
throw new Error(`Sketch ${kind} ${id} requires a positive finite radius.`);
|
|
31
|
-
}
|
|
32
|
-
if (kind === 'circle') {
|
|
33
|
-
if (data.length !== 2)
|
|
34
|
-
throw new Error(`Sketch circle ${id} requires center and radius.`);
|
|
35
|
-
return ['circle', id, [data[0], data[1]]];
|
|
36
|
-
}
|
|
37
|
-
if (kind === 'arc') {
|
|
38
|
-
if (data.length !== 5 || (data[4] !== 'cw' && data[4] !== 'ccw'))
|
|
39
|
-
throw new Error(`Sketch arc ${id} requires center, radius, start, end and cw/ccw direction.`);
|
|
40
|
-
return ['arc', id, [data[0], data[1], data[2], data[3], data[4]]];
|
|
41
|
-
}
|
|
42
|
-
if (kind !== 'line' || data.length !== 2)
|
|
43
|
-
throw new Error(`Invalid sketch entity ${id}.`);
|
|
44
|
-
return ['line', id, [data[0], data[1]]];
|
|
45
|
-
});
|
|
46
|
-
const ancestors = new Set();
|
|
47
|
-
for (let ancestor = base; ancestor; ancestor = definitions.get(ancestor)?.base)
|
|
48
|
-
ancestors.add(ancestor);
|
|
49
|
-
for (const [kind, id, data] of copied) {
|
|
50
|
-
if (kind === 'point' && Array.isArray(data))
|
|
51
|
-
continue;
|
|
52
|
-
const refs = kind === 'point'
|
|
53
|
-
? [data]
|
|
54
|
-
: kind === 'circle'
|
|
55
|
-
? [data[0]]
|
|
56
|
-
: kind === 'arc'
|
|
57
|
-
? [data[0], data[2], data[3]]
|
|
58
|
-
: data;
|
|
59
|
-
for (const ref of refs) {
|
|
60
|
-
if (typeof ref === 'number') {
|
|
61
|
-
if (!copied.some(e => e[0] === 'point' && e[1] === ref))
|
|
62
|
-
throw new Error(`Sketch ${kind} ${id} references missing local point ${ref}.`);
|
|
63
|
-
}
|
|
64
|
-
else if (!references.has(ref) || !ancestors.has(ref.sketch)) {
|
|
65
|
-
throw new Error(`Sketch ${kind} ${id} must reference a local or upstream point.`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
const resolving = new Set();
|
|
70
|
-
const resolve = (id) => {
|
|
71
|
-
const position = points.get(id);
|
|
72
|
-
if (position)
|
|
73
|
-
return position;
|
|
74
|
-
if (resolving.has(id))
|
|
75
|
-
throw new Error(`Cyclic sketch point alias at ${id}.`);
|
|
76
|
-
resolving.add(id);
|
|
77
|
-
const entry = copied.find(e => e[0] === 'point' && e[1] === id);
|
|
78
|
-
const ref = entry[2];
|
|
79
|
-
const resolved = typeof ref === 'number'
|
|
80
|
-
? resolve(ref)
|
|
81
|
-
: definitions.get(ref.sketch).points.get(ref.id);
|
|
82
|
-
resolving.delete(id);
|
|
83
|
-
points.set(id, resolved);
|
|
84
|
-
return resolved;
|
|
85
|
-
};
|
|
86
|
-
for (const [kind, id] of copied)
|
|
87
|
-
if (kind === 'point')
|
|
88
|
-
resolve(id);
|
|
89
|
-
const pointRef = (ref) => {
|
|
90
|
-
if (typeof ref === 'number'
|
|
91
|
-
? !points.has(ref)
|
|
92
|
-
: !references.has(ref) || !ancestors.has(ref.sketch))
|
|
93
|
-
throw new Error('Sketch constraints must reference a local or upstream point.');
|
|
94
|
-
};
|
|
95
|
-
const curveRef = (id, kind) => {
|
|
96
|
-
if (!copied.some(e => (kind === 'circular curve'
|
|
97
|
-
? e[0] === 'circle' || e[0] === 'arc'
|
|
98
|
-
: e[0] === kind) && e[1] === id))
|
|
99
|
-
throw new Error(`Sketch constraint references missing local ${kind} ${id}.`);
|
|
100
|
-
};
|
|
101
|
-
const constraints = (options?.constraints ?? []).map(([kind, data, value]) => {
|
|
102
|
-
if (kind === 'fixed') {
|
|
103
|
-
pointRef(data);
|
|
104
|
-
return [kind, data];
|
|
105
|
-
}
|
|
106
|
-
if (kind === 'horizontal' || kind === 'vertical') {
|
|
107
|
-
curveRef(data, 'line');
|
|
108
|
-
return [kind, data];
|
|
109
|
-
}
|
|
110
|
-
if (kind === 'parallel' ||
|
|
111
|
-
kind === 'perpendicular' ||
|
|
112
|
-
(kind === 'angle' && typeof data !== 'number')) {
|
|
113
|
-
if (!Array.isArray(data) || data.length !== 2 || data[0] === data[1])
|
|
114
|
-
throw new Error(`Sketch ${kind} constraint requires two distinct local lines.`);
|
|
115
|
-
data.forEach(id => curveRef(id, 'line'));
|
|
116
|
-
if (kind === 'angle') {
|
|
117
|
-
if (!Number.isFinite(value))
|
|
118
|
-
throw new Error('Sketch angle constraint requires a finite value.');
|
|
119
|
-
return [kind, [data[0], data[1]], value];
|
|
120
|
-
}
|
|
121
|
-
return [kind, [data[0], data[1]]];
|
|
122
|
-
}
|
|
123
|
-
if (kind === 'coincident') {
|
|
124
|
-
data.forEach(pointRef);
|
|
125
|
-
return [kind, [data[0], data[1]]];
|
|
126
|
-
}
|
|
127
|
-
if (kind === 'midpoint') {
|
|
128
|
-
if (data.length !== 3)
|
|
129
|
-
throw new Error('Sketch midpoint constraint requires three points.');
|
|
130
|
-
data.forEach(pointRef);
|
|
131
|
-
return [kind, [data[0], data[1], data[2]]];
|
|
132
|
-
}
|
|
133
|
-
if (kind === 'x' ||
|
|
134
|
-
kind === 'y' ||
|
|
135
|
-
kind === 'length' ||
|
|
136
|
-
kind === 'angle' ||
|
|
137
|
-
kind === 'radius' ||
|
|
138
|
-
kind === 'sweep') {
|
|
139
|
-
if (kind === 'x' || kind === 'y')
|
|
140
|
-
pointRef(data);
|
|
141
|
-
else
|
|
142
|
-
curveRef(data, kind === 'radius'
|
|
143
|
-
? 'circular curve'
|
|
144
|
-
: kind === 'sweep'
|
|
145
|
-
? 'arc'
|
|
146
|
-
: 'line');
|
|
147
|
-
if (kind === 'sweep' && !(value > 0 && value < 360))
|
|
148
|
-
throw new Error('Sketch sweep constraint requires degrees strictly between 0 and 360.');
|
|
149
|
-
const positive = kind === 'length' || kind === 'radius';
|
|
150
|
-
if (!Number.isFinite(value) || (positive && value <= 0))
|
|
151
|
-
throw new Error(`Sketch ${kind} constraint requires ${positive ? 'a positive' : 'a'} finite value.`);
|
|
152
|
-
return [kind, data, value];
|
|
153
|
-
}
|
|
154
|
-
throw new Error(`Unknown sketch constraint ${kind}.`);
|
|
155
|
-
});
|
|
156
|
-
const local = 'local';
|
|
157
|
-
const layers = new Map();
|
|
158
|
-
const identity = (value) => {
|
|
159
|
-
let id = layers.get(value);
|
|
160
|
-
if (!id)
|
|
161
|
-
layers.set(value, (id = `upstream:${layers.size}`));
|
|
162
|
-
return id;
|
|
163
|
-
};
|
|
164
|
-
const snapshots = [...ancestors]
|
|
165
|
-
.reverse()
|
|
166
|
-
.map(value => snapshotSketch(value, identity));
|
|
167
|
-
const unresolved = {
|
|
168
|
-
id: local,
|
|
169
|
-
entities: snapshotEntries(copied, local, identity, points),
|
|
170
|
-
constraints: snapshotConstraints(constraints, local, identity),
|
|
171
|
-
degreesOfFreedom: 0,
|
|
172
|
-
redundant: [],
|
|
173
|
-
};
|
|
174
|
-
const solved = solveSketchSnapshot([...snapshots, unresolved]);
|
|
175
|
-
const radii = new Map();
|
|
176
|
-
for (const entity of solved.entities)
|
|
177
|
-
if (entity.kind === 'point')
|
|
178
|
-
points.set(entity.id, entity.position);
|
|
179
|
-
else if (entity.kind === 'circle' || entity.kind === 'arc')
|
|
180
|
-
radii.set(entity.id, entity.radius);
|
|
181
|
-
definitions.set(this, {
|
|
182
|
-
base,
|
|
183
|
-
input: entries,
|
|
184
|
-
inputOptions: options,
|
|
185
|
-
entries: copied,
|
|
186
|
-
constraints,
|
|
187
|
-
points,
|
|
188
|
-
radii,
|
|
189
|
-
degreesOfFreedom: solved.degreesOfFreedom,
|
|
190
|
-
redundant: solved.redundant,
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
point(id) {
|
|
194
|
-
if (!definitions.get(this).points.has(id))
|
|
195
|
-
throw new Error(`Unknown local sketch point ${id}.`);
|
|
196
|
-
const ref = { sketch: this, id };
|
|
197
|
-
references.add(ref);
|
|
198
|
-
return ref;
|
|
199
|
-
}
|
|
200
|
-
derive(entries, options) {
|
|
201
|
-
return new SketchValue(entries, options, this);
|
|
202
|
-
}
|
|
203
|
-
face() {
|
|
204
|
-
const regions = sketchRegions(this.layers());
|
|
205
|
-
if (regions.length !== 1)
|
|
206
|
-
throw new Error(`sketch.face() requires exactly one closed region; found ${regions.length}. Use faces() for multiple regions.`);
|
|
207
|
-
return sketchFaceModel(regions[0]);
|
|
208
|
-
}
|
|
209
|
-
faces() {
|
|
210
|
-
const faces = [];
|
|
211
|
-
try {
|
|
212
|
-
for (const region of sketchRegions(this.layers()))
|
|
213
|
-
faces.push(sketchFaceModel(region));
|
|
214
|
-
return faces;
|
|
215
|
-
}
|
|
216
|
-
catch (error) {
|
|
217
|
-
disposeModelObjects(faces.flatMap(face => (isModelObject(face) ? [face] : [])));
|
|
218
|
-
throw error;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
layers() {
|
|
222
|
-
const values = [];
|
|
223
|
-
for (let value = this; value; value = definitions.get(value).base)
|
|
224
|
-
values.unshift(value);
|
|
225
|
-
return values.map(value => snapshotSketch(value, s => String(values.indexOf(s))));
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
/** Defines a sketch using [kind, ID, data] tuples. IDs belong to this layer. */
|
|
229
|
-
export function sketch(entries, options) {
|
|
230
|
-
return new SketchValue(entries, options);
|
|
231
|
-
}
|
|
232
|
-
export function isSketch(value) {
|
|
233
|
-
return value instanceof SketchValue;
|
|
234
|
-
}
|
|
235
|
-
export function sketchDefinition(value) {
|
|
236
|
-
return definitions.get(value);
|
|
237
|
-
}
|
|
238
|
-
/** Numeric geometry parameters, excluding identity and point references. */
|
|
239
|
-
export function sketchEntityParameters(entity) {
|
|
240
|
-
switch (entity.kind) {
|
|
241
|
-
case 'point':
|
|
242
|
-
return entity.alias ? [] : entity.position;
|
|
243
|
-
case 'circle':
|
|
244
|
-
case 'arc':
|
|
245
|
-
return [entity.radius];
|
|
246
|
-
case 'line':
|
|
247
|
-
return [];
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
export function withSketchEntityParameters(entity, parameters) {
|
|
251
|
-
switch (entity.kind) {
|
|
252
|
-
case 'point':
|
|
253
|
-
return entity.alias
|
|
254
|
-
? entity
|
|
255
|
-
: { ...entity, position: [parameters[0], parameters[1]] };
|
|
256
|
-
case 'circle':
|
|
257
|
-
case 'arc':
|
|
258
|
-
return { ...entity, radius: parameters[0] };
|
|
259
|
-
case 'line':
|
|
260
|
-
return entity;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
export function snapshotSketch(value, identity) {
|
|
264
|
-
const { base, entries, constraints, points, radii, degreesOfFreedom, redundant, } = sketchDefinition(value);
|
|
265
|
-
const id = identity(value);
|
|
266
|
-
return {
|
|
267
|
-
id,
|
|
268
|
-
base: base && identity(base),
|
|
269
|
-
entities: snapshotEntries(entries, id, identity, points).map(e => e.kind === 'point'
|
|
270
|
-
? { ...e, position: points.get(e.id) }
|
|
271
|
-
: e.kind === 'circle' || e.kind === 'arc'
|
|
272
|
-
? { ...e, radius: radii.get(e.id) }
|
|
273
|
-
: e),
|
|
274
|
-
constraints: snapshotConstraints(constraints, id, identity),
|
|
275
|
-
degreesOfFreedom,
|
|
276
|
-
redundant,
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
function pointAddress(id, identity) {
|
|
280
|
-
return (ref) => typeof ref === 'number'
|
|
281
|
-
? { layer: id, id: ref }
|
|
282
|
-
: { layer: identity(ref.sketch), id: ref.id };
|
|
283
|
-
}
|
|
284
|
-
function snapshotEntries(entries, id, identity, positions) {
|
|
285
|
-
const point = pointAddress(id, identity);
|
|
286
|
-
return entries.map(([kind, entityId, data]) => kind === 'point'
|
|
287
|
-
? Array.isArray(data)
|
|
288
|
-
? { kind, id: entityId, position: data }
|
|
289
|
-
: {
|
|
290
|
-
kind,
|
|
291
|
-
id: entityId,
|
|
292
|
-
position: positions.get(entityId),
|
|
293
|
-
alias: point(data),
|
|
294
|
-
}
|
|
295
|
-
: kind === 'circle'
|
|
296
|
-
? { kind, id: entityId, center: point(data[0]), radius: data[1] }
|
|
297
|
-
: kind === 'arc'
|
|
298
|
-
? {
|
|
299
|
-
kind,
|
|
300
|
-
id: entityId,
|
|
301
|
-
center: point(data[0]),
|
|
302
|
-
radius: data[1],
|
|
303
|
-
points: [point(data[2]), point(data[3])],
|
|
304
|
-
direction: data[4],
|
|
305
|
-
}
|
|
306
|
-
: { kind, id: entityId, points: [point(data[0]), point(data[1])] });
|
|
307
|
-
}
|
|
308
|
-
/** Resolve identity once for solving, dragging, snapping and topology edits. */
|
|
309
|
-
export function sketchPointResolver(layers) {
|
|
310
|
-
const points = new Map(layers.flatMap(layer => layer.entities.flatMap(e => e.kind === 'point'
|
|
311
|
-
? [
|
|
312
|
-
[
|
|
313
|
-
JSON.stringify([layer.id, e.id]),
|
|
314
|
-
{ ...e, layer: layer.id },
|
|
315
|
-
],
|
|
316
|
-
]
|
|
317
|
-
: [])));
|
|
318
|
-
const resolved = new Map();
|
|
319
|
-
const visiting = new Set();
|
|
320
|
-
const resolve = (ref) => {
|
|
321
|
-
const key = JSON.stringify([ref.layer, ref.id]);
|
|
322
|
-
const cached = resolved.get(key);
|
|
323
|
-
if (cached)
|
|
324
|
-
return cached;
|
|
325
|
-
const point = points.get(key);
|
|
326
|
-
if (!point)
|
|
327
|
-
throw new Error(`Missing sketch point ${ref.id}.`);
|
|
328
|
-
if (visiting.has(key))
|
|
329
|
-
throw new Error(`Cyclic sketch point alias at ${ref.id}.`);
|
|
330
|
-
visiting.add(key);
|
|
331
|
-
const target = point.alias
|
|
332
|
-
? resolve(point.alias)
|
|
333
|
-
: { layer: ref.layer, id: ref.id };
|
|
334
|
-
visiting.delete(key);
|
|
335
|
-
resolved.set(key, target);
|
|
336
|
-
return target;
|
|
337
|
-
};
|
|
338
|
-
for (const point of points.values())
|
|
339
|
-
resolve(point);
|
|
340
|
-
return resolve;
|
|
341
|
-
}
|
|
342
|
-
function snapshotConstraints(constraints, id, identity) {
|
|
343
|
-
const point = pointAddress(id, identity);
|
|
344
|
-
return constraints.map(([kind, data, value]) => {
|
|
345
|
-
switch (kind) {
|
|
346
|
-
case 'fixed':
|
|
347
|
-
return [kind, point(data)];
|
|
348
|
-
case 'coincident':
|
|
349
|
-
return [kind, [point(data[0]), point(data[1])]];
|
|
350
|
-
case 'midpoint':
|
|
351
|
-
return [kind, [point(data[0]), point(data[1]), point(data[2])]];
|
|
352
|
-
case 'x':
|
|
353
|
-
case 'y':
|
|
354
|
-
return [kind, point(data), value];
|
|
355
|
-
case 'horizontal':
|
|
356
|
-
case 'vertical':
|
|
357
|
-
return [kind, data];
|
|
358
|
-
case 'parallel':
|
|
359
|
-
case 'perpendicular':
|
|
360
|
-
return [kind, [data[0], data[1]]];
|
|
361
|
-
case 'length':
|
|
362
|
-
case 'radius':
|
|
363
|
-
case 'sweep':
|
|
364
|
-
return [kind, data, value];
|
|
365
|
-
case 'angle':
|
|
366
|
-
return typeof data === 'number'
|
|
367
|
-
? [kind, data, value]
|
|
368
|
-
: [kind, [data[0], data[1]], value];
|
|
369
|
-
}
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
function sketchGeometry(layers) {
|
|
373
|
-
const resolve = sketchPointResolver(layers);
|
|
374
|
-
// Array order is source layout, not numeric priority. Keep a stable local
|
|
375
|
-
// parameter basis (including the normalization origin), without reordering
|
|
376
|
-
// author entries, snapshots, layers, or constraint diagnostic indices.
|
|
377
|
-
const entities = layers.flatMap(layer => [...layer.entities]
|
|
378
|
-
.sort((a, b) => a.id - b.id)
|
|
379
|
-
.map(e => ({ ...e, layer: layer.id })));
|
|
380
|
-
const points = entities.filter(e => e.kind === 'point').filter(e => !e.alias);
|
|
381
|
-
const pointIndex = (ref) => {
|
|
382
|
-
ref = resolve(ref);
|
|
383
|
-
const index = points.findIndex(p => p.id === ref.id && p.layer === ref.layer);
|
|
384
|
-
if (index < 0)
|
|
385
|
-
throw new Error(`Missing sketch point ${ref.id}.`);
|
|
386
|
-
return index;
|
|
387
|
-
};
|
|
388
|
-
const lines = entities
|
|
389
|
-
.filter(e => e.kind === 'line')
|
|
390
|
-
.map(e => [pointIndex(e.points[0]), pointIndex(e.points[1])]);
|
|
391
|
-
const circles = entities.filter(e => e.kind === 'circle');
|
|
392
|
-
const arcs = entities.filter(e => e.kind === 'arc');
|
|
393
|
-
const geometry = {
|
|
394
|
-
points: points.map(p => p.position),
|
|
395
|
-
lines,
|
|
396
|
-
circles: circles.map(c => ({
|
|
397
|
-
center: pointIndex(c.center),
|
|
398
|
-
radius: c.radius,
|
|
399
|
-
})),
|
|
400
|
-
arcs: arcs.map(a => ({
|
|
401
|
-
center: pointIndex(a.center),
|
|
402
|
-
radius: a.radius,
|
|
403
|
-
points: [pointIndex(a.points[0]), pointIndex(a.points[1])],
|
|
404
|
-
direction: a.direction,
|
|
405
|
-
})),
|
|
406
|
-
};
|
|
407
|
-
return { points, pointIndex, lines, circles, arcs, geometry };
|
|
408
|
-
}
|
|
409
|
-
/** Includes gesture-only equations, not merely authored constraints. */
|
|
410
|
-
export function sketchDragRequiresSolver(layers) {
|
|
411
|
-
const local = layers.at(-1);
|
|
412
|
-
if (local.constraints.length ||
|
|
413
|
-
layers.some(layer => layer.entities.some(e => e.kind === 'arc')))
|
|
414
|
-
return true;
|
|
415
|
-
const { points, geometry } = sketchGeometry(layers);
|
|
416
|
-
return sketchIncidences(geometry).some(contact => sketchIncidencePoints(geometry, contact).some(i => points[i].layer === local.id));
|
|
417
|
-
}
|
|
418
|
-
/** Source replay must retain every gesture-start incidence, including aliases. */
|
|
419
|
-
export function assertSketchDragConnections(layers, reference) {
|
|
420
|
-
const original = sketchGeometry([...layers.slice(0, -1), reference]);
|
|
421
|
-
const current = sketchGeometry(layers);
|
|
422
|
-
for (const contact of sketchIncidences(original.geometry)) {
|
|
423
|
-
const point = current.points[current.pointIndex(original.points[contact.point])]
|
|
424
|
-
.position;
|
|
425
|
-
if (!isPointOnSketchCurve(point, sketchIncidenceCurve(current.geometry, contact)))
|
|
426
|
-
throw new Error('The source replay could not retain a point on its curve. No changes were applied.');
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
/** The same solver and snapshot contract serve evaluation and interactive dragging. */
|
|
430
|
-
export function solveSketchSnapshot(layers, drag) {
|
|
431
|
-
const local = layers.at(-1);
|
|
432
|
-
const { points, pointIndex, lines, circles, arcs, geometry } = sketchGeometry(layers);
|
|
433
|
-
const linePoints = (id) => {
|
|
434
|
-
const line = local.entities.find(e => e.kind === 'line' && e.id === id);
|
|
435
|
-
if (!line)
|
|
436
|
-
throw new Error(`Missing sketch line ${id}.`);
|
|
437
|
-
return [pointIndex(line.points[0]), pointIndex(line.points[1])];
|
|
438
|
-
};
|
|
439
|
-
const circleIndex = (id) => {
|
|
440
|
-
const index = circles.findIndex(c => c.id === id && c.layer === local.id);
|
|
441
|
-
if (index < 0)
|
|
442
|
-
throw new Error(`Missing sketch circle ${id}.`);
|
|
443
|
-
return index;
|
|
444
|
-
};
|
|
445
|
-
const arcIndex = (id) => arcs.findIndex(a => a.id === id && a.layer === local.id);
|
|
446
|
-
const constraints = local.constraints.map(([kind, data, value]) => {
|
|
447
|
-
switch (kind) {
|
|
448
|
-
case 'fixed': {
|
|
449
|
-
const point = pointIndex(data);
|
|
450
|
-
return { kind, point, position: points[point].position };
|
|
451
|
-
}
|
|
452
|
-
case 'x':
|
|
453
|
-
case 'y':
|
|
454
|
-
return { kind, point: pointIndex(data), value };
|
|
455
|
-
case 'horizontal':
|
|
456
|
-
case 'vertical':
|
|
457
|
-
return { kind, points: linePoints(data) };
|
|
458
|
-
case 'parallel':
|
|
459
|
-
case 'perpendicular':
|
|
460
|
-
return {
|
|
461
|
-
kind,
|
|
462
|
-
points: [...linePoints(data[0]), ...linePoints(data[1])],
|
|
463
|
-
};
|
|
464
|
-
case 'coincident':
|
|
465
|
-
return { kind, points: [pointIndex(data[0]), pointIndex(data[1])] };
|
|
466
|
-
case 'midpoint':
|
|
467
|
-
return {
|
|
468
|
-
kind,
|
|
469
|
-
points: [
|
|
470
|
-
pointIndex(data[0]),
|
|
471
|
-
pointIndex(data[1]),
|
|
472
|
-
pointIndex(data[2]),
|
|
473
|
-
],
|
|
474
|
-
};
|
|
475
|
-
case 'length':
|
|
476
|
-
return { kind, points: linePoints(data), value };
|
|
477
|
-
case 'angle':
|
|
478
|
-
return typeof data === 'number'
|
|
479
|
-
? { kind, points: linePoints(data), value }
|
|
480
|
-
: {
|
|
481
|
-
kind: 'lineAngle',
|
|
482
|
-
points: [...linePoints(data[0]), ...linePoints(data[1])],
|
|
483
|
-
value,
|
|
484
|
-
};
|
|
485
|
-
case 'radius':
|
|
486
|
-
return arcIndex(data) >= 0
|
|
487
|
-
? {
|
|
488
|
-
kind,
|
|
489
|
-
curve: 'arc',
|
|
490
|
-
index: arcIndex(data),
|
|
491
|
-
value,
|
|
492
|
-
}
|
|
493
|
-
: {
|
|
494
|
-
kind,
|
|
495
|
-
curve: 'circle',
|
|
496
|
-
index: circleIndex(data),
|
|
497
|
-
value,
|
|
498
|
-
};
|
|
499
|
-
case 'sweep':
|
|
500
|
-
return {
|
|
501
|
-
kind,
|
|
502
|
-
index: arcIndex(data),
|
|
503
|
-
value,
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
});
|
|
507
|
-
const problem = {
|
|
508
|
-
points: points.map(p => {
|
|
509
|
-
const position = [...p.position];
|
|
510
|
-
const upstream = p.layer !== local.id;
|
|
511
|
-
const locked = [upstream, upstream];
|
|
512
|
-
if (!upstream)
|
|
513
|
-
for (const lock of drag?.locks ?? [])
|
|
514
|
-
if (local.entities.some(e => e.kind === 'point' && e.id === lock.id) &&
|
|
515
|
-
pointIndex({ layer: local.id, id: lock.id }) === pointIndex(p)) {
|
|
516
|
-
position[lock.parameter] = lock.value;
|
|
517
|
-
locked[lock.parameter] = true;
|
|
518
|
-
}
|
|
519
|
-
return { position, locked };
|
|
520
|
-
}),
|
|
521
|
-
lines,
|
|
522
|
-
circles: circles.map(c => {
|
|
523
|
-
const upstream = c.layer !== local.id;
|
|
524
|
-
const lock = !upstream && drag?.locks?.find(lock => lock.id === c.id);
|
|
525
|
-
return {
|
|
526
|
-
center: pointIndex(c.center),
|
|
527
|
-
radius: lock ? lock.value : c.radius,
|
|
528
|
-
locked: upstream || !!lock,
|
|
529
|
-
};
|
|
530
|
-
}),
|
|
531
|
-
arcs: arcs.map(a => {
|
|
532
|
-
const upstream = a.layer !== local.id;
|
|
533
|
-
const lock = !upstream && drag?.locks?.find(lock => lock.id === a.id);
|
|
534
|
-
return {
|
|
535
|
-
center: pointIndex(a.center),
|
|
536
|
-
radius: lock ? lock.value : a.radius,
|
|
537
|
-
locked: upstream || !!lock,
|
|
538
|
-
points: [pointIndex(a.points[0]), pointIndex(a.points[1])],
|
|
539
|
-
direction: a.direction,
|
|
540
|
-
};
|
|
541
|
-
}),
|
|
542
|
-
constraints,
|
|
543
|
-
};
|
|
544
|
-
const target = drag && local.entities.find(e => e.id === drag.id);
|
|
545
|
-
const objective = drag &&
|
|
546
|
-
target &&
|
|
547
|
-
(target.kind === 'circle' || target.kind === 'arc'
|
|
548
|
-
? {
|
|
549
|
-
kind: 'radius',
|
|
550
|
-
curve: target.kind,
|
|
551
|
-
index: target.kind === 'circle'
|
|
552
|
-
? circleIndex(target.id)
|
|
553
|
-
: arcIndex(target.id),
|
|
554
|
-
value: Math.hypot(...drag.position.map((v, axis) => v - points[pointIndex(target.center)].position[axis])),
|
|
555
|
-
}
|
|
556
|
-
: {
|
|
557
|
-
kind: 'point',
|
|
558
|
-
point: pointIndex({ layer: local.id, id: drag.id }),
|
|
559
|
-
position: drag.position,
|
|
560
|
-
});
|
|
561
|
-
const prepared =
|
|
562
|
-
// Applying authored coordinate locks can change the displayed geometry.
|
|
563
|
-
// Satisfy those locks before matching a drag rule or preparing its reference.
|
|
564
|
-
drag &&
|
|
565
|
-
(problem.points.some((p, i) => p.position.some((v, axis) => v !== points[i].position[axis])) ||
|
|
566
|
-
problem.circles.some((c, i) => c.radius !== circles[i].radius) ||
|
|
567
|
-
problem.arcs.some((a, i) => a.radius !== arcs[i].radius))
|
|
568
|
-
? solvedProblem(problem)
|
|
569
|
-
: problem;
|
|
570
|
-
const referenceEntities = new Map(drag?.reference?.entities.map(e => [e.id, e]));
|
|
571
|
-
const reference = drag?.reference
|
|
572
|
-
? solvedProblem({
|
|
573
|
-
...prepared,
|
|
574
|
-
points: prepared.points.map((p, index) => {
|
|
575
|
-
const entity = points[index].layer === local.id &&
|
|
576
|
-
referenceEntities.get(points[index].id);
|
|
577
|
-
return entity && entity.kind === 'point'
|
|
578
|
-
? {
|
|
579
|
-
...p,
|
|
580
|
-
position: p.position.map((v, axis) => p.locked[axis] ? v : entity.position[axis]),
|
|
581
|
-
}
|
|
582
|
-
: p;
|
|
583
|
-
}),
|
|
584
|
-
circles: prepared.circles.map((c, index) => {
|
|
585
|
-
const entity = circles[index].layer === local.id &&
|
|
586
|
-
referenceEntities.get(circles[index].id);
|
|
587
|
-
return !c.locked && entity && entity.kind === 'circle'
|
|
588
|
-
? { ...c, radius: entity.radius }
|
|
589
|
-
: c;
|
|
590
|
-
}),
|
|
591
|
-
arcs: prepared.arcs.map((a, index) => {
|
|
592
|
-
const entity = arcs[index].layer === local.id &&
|
|
593
|
-
referenceEntities.get(arcs[index].id);
|
|
594
|
-
return !a.locked && entity && entity.kind === 'arc'
|
|
595
|
-
? { ...a, radius: entity.radius }
|
|
596
|
-
: a;
|
|
597
|
-
}),
|
|
598
|
-
})
|
|
599
|
-
: prepared;
|
|
600
|
-
const result = objective
|
|
601
|
-
? solveSketchDrag(prepared, objective, reference, drag?.reference
|
|
602
|
-
? sketchGeometry([...layers.slice(0, -1), drag.reference]).geometry
|
|
603
|
-
: geometry)
|
|
604
|
-
: solveSketchProblem(prepared);
|
|
605
|
-
const entities = local.entities.map(e => e.kind === 'point'
|
|
606
|
-
? {
|
|
607
|
-
...e,
|
|
608
|
-
position: result.positions[pointIndex({ layer: local.id, id: e.id })],
|
|
609
|
-
}
|
|
610
|
-
: e.kind === 'circle'
|
|
611
|
-
? { ...e, radius: result.radii[circleIndex(e.id)] }
|
|
612
|
-
: e.kind === 'arc'
|
|
613
|
-
? { ...e, radius: result.arcRadii[arcIndex(e.id)] }
|
|
614
|
-
: e);
|
|
615
|
-
for (const entity of entities) {
|
|
616
|
-
if ((entity.kind === 'circle' || entity.kind === 'arc') &&
|
|
617
|
-
(!Number.isFinite(entity.radius) || entity.radius <= 0))
|
|
618
|
-
throw new Error(`Sketch ${entity.kind} ${entity.id} requires a positive finite radius.`);
|
|
619
|
-
if (entity.kind === 'arc') {
|
|
620
|
-
const [center, a, b] = [entity.center, ...entity.points].map(p => result.positions[pointIndex(p)]);
|
|
621
|
-
if (Math.hypot(a[0] - center[0], a[1] - center[1]) === 0 ||
|
|
622
|
-
Math.hypot(a[0] - b[0], a[1] - b[1]) === 0)
|
|
623
|
-
throw new Error(`Sketch arc ${entity.id} requires a nonzero radius and distinct endpoints; use circle for a full circle.`);
|
|
624
|
-
}
|
|
625
|
-
if (entity.kind !== 'line')
|
|
626
|
-
continue;
|
|
627
|
-
const [a, b] = entity.points.map(p => result.positions[pointIndex(p)]);
|
|
628
|
-
if (Math.hypot(a[0] - b[0], a[1] - b[1]) === 0)
|
|
629
|
-
throw new Error(`Sketch line ${entity.id} has zero length.`);
|
|
630
|
-
}
|
|
631
|
-
return {
|
|
632
|
-
...local,
|
|
633
|
-
entities,
|
|
634
|
-
degreesOfFreedom: drag ? local.degreesOfFreedom : result.degreesOfFreedom,
|
|
635
|
-
redundant: drag ? local.redundant : result.redundant,
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
function solvedProblem(problem) {
|
|
639
|
-
const solved = solveSketchProblem(problem);
|
|
640
|
-
return {
|
|
641
|
-
...problem,
|
|
642
|
-
points: problem.points.map((p, i) => ({
|
|
643
|
-
...p,
|
|
644
|
-
position: solved.positions[i],
|
|
645
|
-
})),
|
|
646
|
-
circles: problem.circles.map((c, i) => ({ ...c, radius: solved.radii[i] })),
|
|
647
|
-
arcs: problem.arcs.map((a, i) => ({ ...a, radius: solved.arcRadii[i] })),
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
//# sourceMappingURL=sketch.js.map
|