@constructorfleet/sketchforge-engine 1.26.0 → 1.28.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/dist/cadAnalysis.d.ts +94 -0
- package/dist/cadAnalysis.d.ts.map +1 -0
- package/dist/cadAnalysis.js +157 -0
- package/dist/cadAnalysis.js.map +1 -0
- package/dist/cadBakeMetadata.d.ts +11 -2
- package/dist/cadBakeMetadata.d.ts.map +1 -1
- package/dist/cadBakeMetadata.js +9 -0
- package/dist/cadBakeMetadata.js.map +1 -1
- package/dist/cadProjection.d.ts.map +1 -1
- package/dist/cadProjection.js.map +1 -1
- package/dist/cadSection.d.ts.map +1 -1
- package/dist/cadShell.d.ts +143 -0
- package/dist/cadShell.d.ts.map +1 -0
- package/dist/cadShell.js +313 -0
- package/dist/cadShell.js.map +1 -0
- package/dist/cadSolids.d.ts.map +1 -1
- package/dist/cadSolids.js +18 -1
- package/dist/cadSolids.js.map +1 -1
- package/dist/cutBrepFrameMigration.d.ts +46 -0
- package/dist/cutBrepFrameMigration.d.ts.map +1 -0
- package/dist/cutBrepFrameMigration.js +105 -0
- package/dist/cutBrepFrameMigration.js.map +1 -0
- package/dist/cutterPadding.d.ts.map +1 -1
- package/dist/domain/cameraProjection.js.map +1 -1
- package/dist/domain/sketchforge.d.ts +31 -1
- package/dist/domain/sketchforge.d.ts.map +1 -1
- package/dist/editorHistory.d.ts.map +1 -1
- package/dist/editorHistory.js.map +1 -1
- package/dist/engine.d.ts +3 -3
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +156 -78
- package/dist/engine.js.map +1 -1
- package/dist/exactBooleanParts.d.ts +4 -2
- package/dist/exactBooleanParts.d.ts.map +1 -1
- package/dist/exactBooleanParts.js +7 -3
- package/dist/exactBooleanParts.js.map +1 -1
- package/dist/exactBooleanSolids.d.ts.map +1 -1
- package/dist/gearProfile.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loftProfile.d.ts +1 -1
- package/dist/loftProfile.d.ts.map +1 -1
- package/dist/loftProfile.js.map +1 -1
- package/dist/meshEdgeProjection.d.ts +104 -0
- package/dist/meshEdgeProjection.d.ts.map +1 -0
- package/dist/meshEdgeProjection.js +439 -0
- package/dist/meshEdgeProjection.js.map +1 -0
- package/dist/meshImport.d.ts.map +1 -1
- package/dist/meshImport.js +2 -20
- package/dist/meshImport.js.map +1 -1
- package/dist/meshMassProperties.d.ts +92 -0
- package/dist/meshMassProperties.d.ts.map +1 -0
- package/dist/meshMassProperties.js +570 -0
- package/dist/meshMassProperties.js.map +1 -0
- package/dist/meshSection.d.ts +44 -0
- package/dist/meshSection.d.ts.map +1 -0
- package/dist/meshSection.js +154 -0
- package/dist/meshSection.js.map +1 -0
- package/dist/meshShellOverlap.d.ts +75 -0
- package/dist/meshShellOverlap.d.ts.map +1 -0
- package/dist/meshShellOverlap.js +797 -0
- package/dist/meshShellOverlap.js.map +1 -0
- package/dist/meshShellSolids.d.ts +44 -0
- package/dist/meshShellSolids.d.ts.map +1 -0
- package/dist/meshShellSolids.js +116 -0
- package/dist/meshShellSolids.js.map +1 -0
- package/dist/nodeSvgDomParser.d.ts +3 -0
- package/dist/nodeSvgDomParser.d.ts.map +1 -0
- package/dist/nodeSvgDomParser.js +53 -0
- package/dist/nodeSvgDomParser.js.map +1 -0
- package/dist/parametricRecompute.d.ts.map +1 -1
- package/dist/parametricRecompute.js +63 -10
- package/dist/parametricRecompute.js.map +1 -1
- package/dist/placementWorkplane.d.ts.map +1 -1
- package/dist/placementWorkplane.js.map +1 -1
- package/dist/projectAssets.d.ts +1 -1
- package/dist/projectStore.d.ts +1 -1
- package/dist/projectStore.d.ts.map +1 -1
- package/dist/projectStore.js +23 -2
- package/dist/projectStore.js.map +1 -1
- package/dist/revolveRequests.d.ts +57 -1
- package/dist/revolveRequests.d.ts.map +1 -1
- package/dist/revolveRequests.js +103 -4
- package/dist/revolveRequests.js.map +1 -1
- package/dist/sceneExport.d.ts +6 -0
- package/dist/sceneExport.d.ts.map +1 -1
- package/dist/sceneExport.js +6 -2
- package/dist/sceneExport.js.map +1 -1
- package/dist/serverCadInterference.d.ts +8 -45
- package/dist/serverCadInterference.d.ts.map +1 -1
- package/dist/serverCadInterference.js +21 -43
- package/dist/serverCadInterference.js.map +1 -1
- package/dist/serverCadOperations.d.ts +73 -4
- package/dist/serverCadOperations.d.ts.map +1 -1
- package/dist/serverCadOperations.js +356 -71
- package/dist/serverCadOperations.js.map +1 -1
- package/dist/serverCadProjection.d.ts +14 -1
- package/dist/serverCadProjection.d.ts.map +1 -1
- package/dist/serverCadProjection.js +135 -13
- package/dist/serverCadProjection.js.map +1 -1
- package/dist/serverCadProperties.d.ts +14 -7
- package/dist/serverCadProperties.d.ts.map +1 -1
- package/dist/serverCadProperties.js +60 -24
- package/dist/serverCadProperties.js.map +1 -1
- package/dist/serverCadSection.d.ts +21 -1
- package/dist/serverCadSection.d.ts.map +1 -1
- package/dist/serverCadSection.js +157 -43
- package/dist/serverCadSection.js.map +1 -1
- package/dist/serverCadShell.d.ts +7 -94
- package/dist/serverCadShell.d.ts.map +1 -1
- package/dist/serverCadShell.js +25 -272
- package/dist/serverCadShell.js.map +1 -1
- package/dist/serverCadThickness.d.ts +8 -8
- package/dist/serverCadThickness.d.ts.map +1 -1
- package/dist/serverCadThickness.js +15 -42
- package/dist/serverCadThickness.js.map +1 -1
- package/dist/serverShapeBooleans.d.ts +75 -0
- package/dist/serverShapeBooleans.d.ts.map +1 -1
- package/dist/serverShapeBooleans.js +251 -9
- package/dist/serverShapeBooleans.js.map +1 -1
- package/dist/serverShapeGroups.js.map +1 -1
- package/dist/serverStepExplode.d.ts +1 -2
- package/dist/serverStepExplode.d.ts.map +1 -1
- package/dist/serverStepExplode.js +15 -148
- package/dist/serverStepExplode.js.map +1 -1
- package/dist/shapeBounds.d.ts.map +1 -1
- package/dist/shapeCatalog.d.ts.map +1 -1
- package/dist/shapeCatalog.js +1 -0
- package/dist/shapeCatalog.js.map +1 -1
- package/dist/shapeRequests.d.ts +13 -0
- package/dist/shapeRequests.d.ts.map +1 -1
- package/dist/shapeRequests.js +14 -0
- package/dist/shapeRequests.js.map +1 -1
- package/dist/sketchCadProfile.d.ts +13 -0
- package/dist/sketchCadProfile.d.ts.map +1 -1
- package/dist/sketchCadProfile.js +108 -0
- package/dist/sketchCadProfile.js.map +1 -1
- package/dist/sketchCadSweep.js.map +1 -1
- package/dist/sketchEdgeSegmentLineage.d.ts +32 -0
- package/dist/sketchEdgeSegmentLineage.d.ts.map +1 -0
- package/dist/sketchEdgeSegmentLineage.js +49 -0
- package/dist/sketchEdgeSegmentLineage.js.map +1 -0
- package/dist/sketchEdgeSegmentLineageKernel.d.ts +83 -0
- package/dist/sketchEdgeSegmentLineageKernel.d.ts.map +1 -0
- package/dist/sketchEdgeSegmentLineageKernel.js +125 -0
- package/dist/sketchEdgeSegmentLineageKernel.js.map +1 -0
- package/dist/sketchFaceDraftKernel.d.ts +16 -9
- package/dist/sketchFaceDraftKernel.d.ts.map +1 -1
- package/dist/sketchFaceDraftKernel.js +19 -11
- package/dist/sketchFaceDraftKernel.js.map +1 -1
- package/dist/sketchRevolve.d.ts +13 -0
- package/dist/sketchRevolve.d.ts.map +1 -1
- package/dist/sketchRevolve.js +4 -1
- package/dist/sketchRevolve.js.map +1 -1
- package/dist/skfProject.d.ts +1 -1
- package/dist/skfProject.d.ts.map +1 -1
- package/dist/skfProject.js +63 -4
- package/dist/skfProject.js.map +1 -1
- package/dist/sphereTessellation.d.ts.map +1 -1
- package/dist/stepAssemblyExplode.d.ts +45 -0
- package/dist/stepAssemblyExplode.d.ts.map +1 -0
- package/dist/stepAssemblyExplode.js +195 -0
- package/dist/stepAssemblyExplode.js.map +1 -0
- package/dist/stepAssemblyHierarchy.js.map +1 -1
- package/dist/stepComponentMetadata.d.ts +1 -1
- package/dist/stepComponentMetadata.d.ts.map +1 -1
- package/dist/stepComponentMetadata.js +93 -39
- package/dist/stepComponentMetadata.js.map +1 -1
- package/dist/stepExport.d.ts +14 -1
- package/dist/stepExport.d.ts.map +1 -1
- package/dist/stepExport.js +81 -32
- package/dist/stepExport.js.map +1 -1
- package/dist/stepImportCommon.d.ts +4 -1
- package/dist/stepImportCommon.d.ts.map +1 -1
- package/dist/stepImportCommon.js +7 -6
- package/dist/stepImportCommon.js.map +1 -1
- package/dist/stlImport.js.map +1 -1
- package/dist/support/cadModifierTypes.d.ts +100 -1
- package/dist/support/cadModifierTypes.d.ts.map +1 -1
- package/dist/support/i18n/LocalizedError.d.ts.map +1 -1
- package/dist/support/i18n/locales/en/messages.json +42 -2
- package/dist/support/i18n/locales.d.ts.map +1 -1
- package/dist/support/lib/appTheme.d.ts.map +1 -1
- package/dist/support/lib/materialPresets.js.map +1 -1
- package/dist/support/lib/measurementUnits.d.ts.map +1 -1
- package/dist/support/lib/operator-configuration.json +13 -1
- package/dist/support/lib/operatorConfiguration.d.ts +5 -5
- package/dist/support/lib/workplaneGrid.js.map +1 -1
- package/dist/svgImport.d.ts +2 -0
- package/dist/svgImport.d.ts.map +1 -1
- package/dist/svgImport.js +7 -0
- package/dist/svgImport.js.map +1 -1
- package/dist/threadCatalog.js.map +1 -1
- package/dist/workplaneSettings.js.map +1 -1
- package/dist/workplaneShapes.d.ts +2 -2
- package/dist/workplaneShapes.d.ts.map +1 -1
- package/dist/workplaneShapes.js.map +1 -1
- package/package.json +7 -6
package/dist/engine.js
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* -- including the project lock, which a macro writing a directory needs and
|
|
15
15
|
* does not have today -- without pulling `next/server` in with it.
|
|
16
16
|
*/
|
|
17
|
-
import { allEdgeTreatmentEdgeCount, applyEdgeTreatment, applyBakedMirror, applyDraftPrism, applySketchFaceDraft, exactPlainSketchRevolveResult, isPlainFullTurnSketchRevolve, orientedSolidForShape, shapeEdges, shapeWithCadResult, ServerCadError, } from "./serverCadOperations.js";
|
|
18
|
-
import { resolveRetainedFaceDraft, retainedFaceDraftFields, retainedPrismDraftFields } from "./sketchFaceDraftKernel.js";
|
|
17
|
+
import { allEdgeTreatmentEdgeCount, applyEdgeTreatment, applyBakedMirror, applyDraftPrism, applySketchFaceDraft, exactPlainSketchRevolveResult, isPlainFullTurnSketchRevolve, orientedSolidForShape, shapeEdges, shapeWithCadResult, sketchResizeFactors, ServerCadError, } from "./serverCadOperations.js";
|
|
18
|
+
import { resolveRetainedFaceDraft, retainedDraftSourcePlacement, retainedFaceDraftFields, retainedPrismDraftFields, } from "./sketchFaceDraftKernel.js";
|
|
19
19
|
import { withCadKernel } from "./serverCadKernel.js";
|
|
20
20
|
import { cadSketchRegions, cadSketchSweepPaths } from "./sketchCadProfile.js";
|
|
21
21
|
import { applyShell, shapeFaces, } from "./serverCadShell.js";
|
|
@@ -35,9 +35,10 @@ import { serverManifoldRuntime } from "./serverManifoldRuntime.js";
|
|
|
35
35
|
import { createLocalId } from "./localIds.js";
|
|
36
36
|
import { invalidSketchSweepProfile, sketchSweepShapeFromRequest } from "./sketchSweepRequest.js";
|
|
37
37
|
import { sketchExtrusionShapeFromRequest } from "./sketchExtrusionRequest.js";
|
|
38
|
+
import { sketchSourceEdgesForEdgeIndexes } from "./sketchEdgeSegmentLineage.js";
|
|
38
39
|
import { edgeTreatmentAppliedFrame, findSketchOutput, recomputeSketchOutput, replaySingleAllEdgeTreatment, } from "./parametricRecompute.js";
|
|
39
40
|
import { boundsForShapes, footprintCenter, shapeBounds } from "./shapeBounds.js";
|
|
40
|
-
import { REQUESTABLE_SHAPE_KINDS, shapeFromRequest, shapeWithRequestedChanges, SHAPE_PARAMETERS, } from "./shapeRequests.js";
|
|
41
|
+
import { REQUESTABLE_SHAPE_KINDS, shapeFromRequest, shapeWithRequestedChanges, SHAPE_PARAMETERS, withResultAttributes, } from "./shapeRequests.js";
|
|
41
42
|
import { canonicalizeShape, cleanNearZero, cleanRotationDegrees, cloneWorkplaneShapeTreeWithFreshIds, withHoleMode, } from "./workplaneShapes.js";
|
|
42
43
|
const UNIQUE_CURVED_FACE_SURFACES = new Set(["cone", "sphere", "torus", "cylinder"]);
|
|
43
44
|
const FACE_DRAFT_AXES = new Set(["x", "y", "z", "-x", "-y", "-z"]);
|
|
@@ -98,6 +99,12 @@ async function findUniqueDraftFace(shape, normal) {
|
|
|
98
99
|
&& faceNormalMatches(face.normal, normal));
|
|
99
100
|
return matches.length === 1 ? matches[0] : null;
|
|
100
101
|
}
|
|
102
|
+
function unmirroredDraftSource(shape) {
|
|
103
|
+
return { ...shape, mirrorX: undefined, mirrorY: undefined, mirrorZ: undefined };
|
|
104
|
+
}
|
|
105
|
+
function retainDraftSourceMirrors(result, source) {
|
|
106
|
+
return { ...result, mirrorX: source.mirrorX, mirrorY: source.mirrorY, mirrorZ: source.mirrorZ };
|
|
107
|
+
}
|
|
101
108
|
/**
|
|
102
109
|
* Carry face metadata across a parameter edit only when a face has a unique
|
|
103
110
|
* counterpart. Curved faces may change their sampled normal as their defining
|
|
@@ -250,11 +257,12 @@ function alignEntry(shape, axis) {
|
|
|
250
257
|
: { min: bounds.minY, max: bounds.maxY };
|
|
251
258
|
return { id: shape.id, ...span, locked: shape.locked };
|
|
252
259
|
}
|
|
253
|
-
/** `ServerCadError` keeps its own wording
|
|
260
|
+
/** `ServerCadError` keeps its own wording; unexpected kernel errors stay visible in server logs. */
|
|
254
261
|
function cadFailure(error) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
262
|
+
if (error instanceof ServerCadError)
|
|
263
|
+
return { reason: "cad_refused", message: error.message, status: error.status };
|
|
264
|
+
console.error("Unexpected CAD kernel failure", error);
|
|
265
|
+
return { reason: "cad_failed" };
|
|
258
266
|
}
|
|
259
267
|
/**
|
|
260
268
|
* One shape from one request, built before any lock is taken.
|
|
@@ -270,17 +278,6 @@ function cadFailure(error) {
|
|
|
270
278
|
* and there is no reason to hold a project busy for it.
|
|
271
279
|
*/
|
|
272
280
|
async function builtShape(request, index) {
|
|
273
|
-
if (request.kind === "sketchSweep") {
|
|
274
|
-
const invalidProfile = invalidSketchSweepProfile(request.profile);
|
|
275
|
-
if (invalidProfile)
|
|
276
|
-
return failed({ reason: "invalid_shape_request", index, message: invalidProfile });
|
|
277
|
-
try {
|
|
278
|
-
return ok(await sketchSweepShapeFromRequest(request));
|
|
279
|
-
}
|
|
280
|
-
catch (error) {
|
|
281
|
-
return failed({ reason: "sweep_failed", index, message: error instanceof Error ? error.message : "The sketch sweep could not be built." });
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
281
|
if (request.kind === "sketchSweep") {
|
|
285
282
|
const invalidProfile = invalidSketchSweepProfile(request.profile);
|
|
286
283
|
if (invalidProfile)
|
|
@@ -365,7 +362,7 @@ export function createEngine(store) {
|
|
|
365
362
|
* them off the stored shapes, `booleans` is told which operands to treat as
|
|
366
363
|
* holes. `holes` being undefined is the former.
|
|
367
364
|
*/
|
|
368
|
-
async function combine(projectId, ids, holes) {
|
|
365
|
+
async function combine(projectId, ids, holes, attributes = {}) {
|
|
369
366
|
return mutating(projectId, async (document) => {
|
|
370
367
|
const resolved = shapesByIds(document, ids);
|
|
371
368
|
if (!resolved.ok)
|
|
@@ -386,9 +383,10 @@ export function createEngine(store) {
|
|
|
386
383
|
// from success. The editor treats the same case as a failed cut.
|
|
387
384
|
if (outcome.cutRemovedNothing)
|
|
388
385
|
return failed({ reason: "cut_removed_nothing" });
|
|
386
|
+
const group = withResultAttributes(outcome.group, attributes);
|
|
389
387
|
const consumed = new Set(ids);
|
|
390
|
-
const shapes = [...document.shapes.filter((shape) => !consumed.has(shape.id)),
|
|
391
|
-
return ok({ shape:
|
|
388
|
+
const shapes = [...document.shapes.filter((shape) => !consumed.has(shape.id)), group];
|
|
389
|
+
return ok({ shape: group, revision: await writeShapes(projectId, document, shapes) });
|
|
392
390
|
});
|
|
393
391
|
}
|
|
394
392
|
/** The shared tail of `treat` and `shell`: run the kernel, store the result on the shape. */
|
|
@@ -595,7 +593,13 @@ export function createEngine(store) {
|
|
|
595
593
|
}
|
|
596
594
|
if (faceDraftChanged && existing.faceDraft && requestedFaceDraft
|
|
597
595
|
&& requestedFaceDraft.sourceFaceId !== existing.faceDraft.sourceFaceId) {
|
|
598
|
-
|
|
596
|
+
let currentOutputFaces;
|
|
597
|
+
try {
|
|
598
|
+
currentOutputFaces = await shapeFaces(existing);
|
|
599
|
+
}
|
|
600
|
+
catch (error) {
|
|
601
|
+
return failed(cadFailure(error));
|
|
602
|
+
}
|
|
599
603
|
if (!canReselectUntreatedExtrusionFace
|
|
600
604
|
|| !existing.faceDraftSourceFaceIds?.includes(requestedFaceDraft.sourceFaceId)
|
|
601
605
|
|| currentOutputFaces.filter((face) => face.id === requestedFaceDraft.sourceFaceId && face.surfaceType === "plane").length !== 1) {
|
|
@@ -633,8 +637,47 @@ export function createEngine(store) {
|
|
|
633
637
|
let swept = existing;
|
|
634
638
|
if ((replacingSketchProfile && isSketchExtrusion) || (draftPrismChanged && isSketchExtrusion)
|
|
635
639
|
|| (faceDraftChanged && isSketchExtrusion)) {
|
|
640
|
+
// A treatment is built from the extrusion at the size it was shown
|
|
641
|
+
// (#539), and its record keeps that shape as `before`. Replaying it on
|
|
642
|
+
// a new profile rebuilds at that same resize, or the edit would hand
|
|
643
|
+
// back the treatment at the size the sketch was first drawn.
|
|
644
|
+
const treatmentSource = existing.edgeTreatmentHistory?.[0]?.before;
|
|
645
|
+
const recordedTreatmentScale = hasExistingTreatment && !existing.faceDraft && existing.draftPrismAngle === undefined
|
|
646
|
+
&& treatmentSource?.sketchOperation === "extrude" && treatmentSource.cadBrep
|
|
647
|
+
? sketchResizeFactors(treatmentSource)
|
|
648
|
+
: undefined;
|
|
649
|
+
// A treatment recorded before #539 kept the resize in `before` but was
|
|
650
|
+
// built, and stored, at the drawn size. Its shape is nearer that drawn
|
|
651
|
+
// size than the resized one on every resized axis, and its edit keeps
|
|
652
|
+
// the old behaviour: rebuilt at the profile's own size.
|
|
653
|
+
const nearerResized = (current, resized, factor) => Math.abs(factor - 1) < 1e-8 || Math.abs(current - resized) < Math.abs(current - resized / factor);
|
|
654
|
+
const treatmentSourceScale = recordedTreatmentScale && treatmentSource
|
|
655
|
+
&& nearerResized(existing.width, treatmentSource.width, recordedTreatmentScale.width)
|
|
656
|
+
&& nearerResized(existing.depth, treatmentSource.depth, recordedTreatmentScale.depth)
|
|
657
|
+
&& nearerResized(existing.height, treatmentSource.height, recordedTreatmentScale.height)
|
|
658
|
+
? recordedTreatmentScale
|
|
659
|
+
: undefined;
|
|
636
660
|
const sourceFrame = existing.faceDraftSourceFrame ?? existing.draftPrismSourceFrame ?? existing.cadBrepFrame;
|
|
637
|
-
const
|
|
661
|
+
const ratio = (drawn, source) => source && source > 0 && drawn > 0 ? drawn / source : 1;
|
|
662
|
+
const faceDraftSourceScale = (existing.faceDraft ? existing.faceDraftSourceScale : undefined) ?? (existing.faceDraft ? {
|
|
663
|
+
// Editor-written drafts predate this field. Their output bounds
|
|
664
|
+
// include the taper, so they cannot be used to infer the source
|
|
665
|
+
// resize ratio; these shapes were only retained before resize support.
|
|
666
|
+
width: 1, depth: 1, height: 1,
|
|
667
|
+
} : {
|
|
668
|
+
width: ratio(existing.width, sourceFrame?.width ?? existing.importedMesh?.baseWidth),
|
|
669
|
+
depth: ratio(existing.depth, sourceFrame?.depth ?? existing.importedMesh?.baseDepth),
|
|
670
|
+
height: ratio(existing.height, sourceFrame?.height ?? existing.importedMesh?.baseHeight),
|
|
671
|
+
});
|
|
672
|
+
const rebuildScale = existing.faceDraft || faceDraftChanged
|
|
673
|
+
? faceDraftSourceScale
|
|
674
|
+
: treatmentSourceScale ?? { width: 1, depth: 1, height: 1 };
|
|
675
|
+
// The extrusion is rebuilt at the height it was drawn and then scaled
|
|
676
|
+
// by `rebuildScale`, as its width is. Rebuilding a height-resized
|
|
677
|
+
// extrusion at its shown height applied that resize twice (#546).
|
|
678
|
+
const sourceHeight = existing.draftPrismSourceHeight ?? existing.faceDraftSourceFrame?.height
|
|
679
|
+
?? (treatmentSourceScale ? treatmentSource.height / treatmentSourceScale.height
|
|
680
|
+
: existing.height / rebuildScale.height);
|
|
638
681
|
try {
|
|
639
682
|
const rebuilt = await sketchExtrusionShapeFromRequest({
|
|
640
683
|
id: existing.id,
|
|
@@ -644,9 +687,9 @@ export function createEngine(store) {
|
|
|
644
687
|
hole: existing.hole,
|
|
645
688
|
profile: replacingSketchProfile ? changes.sketchProfile : existing.sketchProfile,
|
|
646
689
|
height: sourceHeight,
|
|
647
|
-
x:
|
|
648
|
-
z:
|
|
649
|
-
elevation:
|
|
690
|
+
x: 0,
|
|
691
|
+
z: 0,
|
|
692
|
+
elevation: 0,
|
|
650
693
|
rotation: existing.rotation,
|
|
651
694
|
rotationX: existing.rotationX,
|
|
652
695
|
rotationZ: existing.rotationZ,
|
|
@@ -656,6 +699,17 @@ export function createEngine(store) {
|
|
|
656
699
|
const rebuiltSource = {
|
|
657
700
|
...sourceMetadata,
|
|
658
701
|
...rebuilt,
|
|
702
|
+
mirrorX: existing.mirrorX,
|
|
703
|
+
mirrorY: existing.mirrorY,
|
|
704
|
+
mirrorZ: existing.mirrorZ,
|
|
705
|
+
...retainedDraftSourcePlacement(existing, rebuilt.cadBrepFrame, existing.faceDraft && !hasExistingTreatment ? existing.cadBrepFrame : undefined),
|
|
706
|
+
// A cleared draft's resize is not kept: the bounds carry it from here.
|
|
707
|
+
faceDraftSourceScale: clearingFaceDraft ? undefined
|
|
708
|
+
: existing.faceDraft || faceDraftChanged ? faceDraftSourceScale : existing.faceDraftSourceScale,
|
|
709
|
+
width: rebuilt.width * rebuildScale.width,
|
|
710
|
+
depth: rebuilt.depth * rebuildScale.depth,
|
|
711
|
+
height: rebuilt.height * rebuildScale.height,
|
|
712
|
+
size: Math.max(rebuilt.width * rebuildScale.width, rebuilt.depth * rebuildScale.depth),
|
|
659
713
|
id: existing.id,
|
|
660
714
|
sketchSourceId: existing.sketchSourceId ?? rebuilt.sketchSourceId,
|
|
661
715
|
faceDraft: undefined,
|
|
@@ -699,20 +753,29 @@ export function createEngine(store) {
|
|
|
699
753
|
source: replayedSource,
|
|
700
754
|
request: requestedFaceDraft,
|
|
701
755
|
profileChanged: replacingSketchProfile,
|
|
702
|
-
}, (kernel, shape) => orientedSolidForShape(kernel, shape)));
|
|
756
|
+
}, (kernel, shape) => orientedSolidForShape(kernel, unmirroredDraftSource(shape))));
|
|
703
757
|
const result = await applySketchFaceDraft(replayedSource, {
|
|
704
758
|
...requestedFaceDraft,
|
|
705
759
|
sourceFaceId: currentFaceId,
|
|
706
760
|
});
|
|
707
761
|
// The same record the editor writes for a draft it makes (#384).
|
|
762
|
+
const drafted = shapeWithCadResult(replayedSource, result);
|
|
708
763
|
swept = {
|
|
709
|
-
...
|
|
764
|
+
...retainDraftSourceMirrors(drafted, replayedSource),
|
|
765
|
+
...(!replayedSource.mirrorX && !replayedSource.mirrorY && !replayedSource.mirrorZ
|
|
766
|
+
? retainedDraftSourcePlacement({
|
|
767
|
+
...replayedSource,
|
|
768
|
+
faceDraftSourceFrame: undefined,
|
|
769
|
+
draftPrismSourceFrame: undefined,
|
|
770
|
+
}, drafted.cadBrepFrame)
|
|
771
|
+
: {}),
|
|
710
772
|
...retainedFaceDraftFields(replayedSource.faceIds, existing, { faceId: currentFaceId, normal: sourceFaceNormal, segmentMatch }, requestedFaceDraft),
|
|
773
|
+
faceDraftSourceScale,
|
|
711
774
|
};
|
|
712
775
|
}
|
|
713
776
|
else if (draftPrismAngle !== undefined && !replayDraftAndTreatment) {
|
|
714
777
|
swept = {
|
|
715
|
-
...shapeWithCadResult(replayedSource, await applyDraftPrism(replayedSource, { angle: draftPrismAngle })),
|
|
778
|
+
...retainDraftSourceMirrors(shapeWithCadResult(replayedSource, await applyDraftPrism(replayedSource, { angle: draftPrismAngle })), replayedSource),
|
|
716
779
|
draftPrismAngle,
|
|
717
780
|
};
|
|
718
781
|
}
|
|
@@ -762,6 +825,9 @@ export function createEngine(store) {
|
|
|
762
825
|
const rebuiltSweep = {
|
|
763
826
|
...sourceMetadata,
|
|
764
827
|
...resized,
|
|
828
|
+
mirrorX: existing.mirrorX,
|
|
829
|
+
mirrorY: existing.mirrorY,
|
|
830
|
+
mirrorZ: existing.mirrorZ,
|
|
765
831
|
id: existing.id,
|
|
766
832
|
sketchSourceId: existing.sketchSourceId ?? rebuilt.sketchSourceId,
|
|
767
833
|
faceIds: refreshedFaceIds,
|
|
@@ -790,13 +856,13 @@ export function createEngine(store) {
|
|
|
790
856
|
? existing.faceDraft.sourceFaceNormal
|
|
791
857
|
: undefined;
|
|
792
858
|
const selectedOnPrior = priorReference ? undefined
|
|
793
|
-
: (await shapeFaces(existing, isExactSketchRevolve ? { topology: "exact" } : undefined))
|
|
859
|
+
: (await shapeFaces(unmirroredDraftSource(existing), isExactSketchRevolve ? { topology: "exact" } : undefined))
|
|
794
860
|
.find((face) => face.id === requestedFaceDraft.sourceFaceId);
|
|
795
861
|
const sourceFaceNormal = priorReference ?? selectedOnPrior?.normal;
|
|
796
862
|
if (!sourceFaceNormal || selectedOnPrior && selectedOnPrior.surfaceType !== "plane") {
|
|
797
863
|
throw new Error("The selected source face has no stable planar reference.");
|
|
798
864
|
}
|
|
799
|
-
const currentFace = await findUniqueDraftFace(replayedSource, sourceFaceNormal);
|
|
865
|
+
const currentFace = await findUniqueDraftFace(unmirroredDraftSource(replayedSource), sourceFaceNormal);
|
|
800
866
|
if (!currentFace)
|
|
801
867
|
throw new Error("The selected source face is missing or ambiguous after the sketch edit.");
|
|
802
868
|
const faceDraft = { ...requestedFaceDraft, sourceFaceNormal };
|
|
@@ -805,7 +871,7 @@ export function createEngine(store) {
|
|
|
805
871
|
sourceFaceId: currentFace.id,
|
|
806
872
|
});
|
|
807
873
|
swept = {
|
|
808
|
-
...shapeWithCadResult(replayedSource, result),
|
|
874
|
+
...retainDraftSourceMirrors(shapeWithCadResult(replayedSource, result), replayedSource),
|
|
809
875
|
faceDraft,
|
|
810
876
|
faceDraftSourceFrame: existing.faceDraftSourceFrame ?? existing.cadBrepFrame,
|
|
811
877
|
};
|
|
@@ -841,18 +907,21 @@ export function createEngine(store) {
|
|
|
841
907
|
const sourceScale = existing.faceDraftSourceScale ?? revolveSourceScale(existing);
|
|
842
908
|
const rebuilt = resizeRebuiltRevolve(rebuiltAtNaturalScale, sourceScale);
|
|
843
909
|
if (requestedFaceDraft) {
|
|
844
|
-
const exactBase =
|
|
910
|
+
const exactBase = {
|
|
911
|
+
...retainDraftSourceMirrors(shapeWithCadResult(rebuilt, await exactPlainSketchRevolveResult(rebuilt)), rebuilt),
|
|
912
|
+
faceDraftSourceScale: sourceScale,
|
|
913
|
+
};
|
|
845
914
|
const priorReference = existing.faceDraft?.sourceFaceId === requestedFaceDraft.sourceFaceId
|
|
846
915
|
? existing.faceDraft.sourceFaceNormal
|
|
847
916
|
: undefined;
|
|
848
917
|
const selectedOnPrior = priorReference ? undefined
|
|
849
|
-
: (await shapeFaces(existing, { topology: "exact" }))
|
|
918
|
+
: (await shapeFaces(unmirroredDraftSource(existing), { topology: "exact" }))
|
|
850
919
|
.find((face) => face.id === requestedFaceDraft.sourceFaceId);
|
|
851
920
|
const sourceFaceNormal = priorReference ?? selectedOnPrior?.normal;
|
|
852
921
|
if (!sourceFaceNormal || selectedOnPrior && selectedOnPrior.surfaceType !== "plane") {
|
|
853
922
|
throw new Error("The selected source face has no stable planar reference.");
|
|
854
923
|
}
|
|
855
|
-
const currentFace = await findUniqueDraftFace(exactBase, sourceFaceNormal);
|
|
924
|
+
const currentFace = await findUniqueDraftFace(unmirroredDraftSource(exactBase), sourceFaceNormal);
|
|
856
925
|
if (!currentFace)
|
|
857
926
|
throw new Error("The selected source face is missing or ambiguous after the sketch edit.");
|
|
858
927
|
const faceDraft = { ...requestedFaceDraft, sourceFaceNormal };
|
|
@@ -861,7 +930,7 @@ export function createEngine(store) {
|
|
|
861
930
|
sourceFaceId: currentFace.id,
|
|
862
931
|
});
|
|
863
932
|
swept = {
|
|
864
|
-
...shapeWithCadResult(exactBase, result),
|
|
933
|
+
...retainDraftSourceMirrors(shapeWithCadResult(exactBase, result), exactBase),
|
|
865
934
|
faceDraft,
|
|
866
935
|
faceDraftSourceFrame: existing.faceDraftSourceFrame ?? existing.cadBrepFrame ?? exactBase.cadBrepFrame,
|
|
867
936
|
faceDraftSourceScale: sourceScale,
|
|
@@ -1119,8 +1188,8 @@ export function createEngine(store) {
|
|
|
1119
1188
|
const { revision } = await store.write(projectId, { ...document, assets }, shapes);
|
|
1120
1189
|
return ok({ shape: imported.shape, revision });
|
|
1121
1190
|
}),
|
|
1122
|
-
cut: (projectId, request) => combine(projectId, [...request.solidIds, ...request.holeIds], new Set(request.holeIds)),
|
|
1123
|
-
group: (projectId, ids) => combine(projectId, ids, undefined),
|
|
1191
|
+
cut: (projectId, request) => combine(projectId, [...request.solidIds, ...request.holeIds], new Set(request.holeIds), request),
|
|
1192
|
+
group: (projectId, ids, result) => combine(projectId, ids, undefined, result),
|
|
1124
1193
|
ungroup: (projectId, shapeId) => mutating(projectId, async (document) => {
|
|
1125
1194
|
const found = oneShape(document, shapeId);
|
|
1126
1195
|
if (!found.ok)
|
|
@@ -1244,47 +1313,56 @@ export function createEngine(store) {
|
|
|
1244
1313
|
return failed(cadFailure(error));
|
|
1245
1314
|
}
|
|
1246
1315
|
}),
|
|
1247
|
-
treat: (projectId, shapeId,
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1316
|
+
treat: (projectId, shapeId, requested) => {
|
|
1317
|
+
// An edge named twice is one edge: rounding it once is what the caller
|
|
1318
|
+
// gets, so it is counted, and recorded, once.
|
|
1319
|
+
const treatment = requested.edges ? { ...requested, edges: [...new Set(requested.edges)] } : requested;
|
|
1320
|
+
return cadTreatment(projectId, shapeId, (treatment.edges === undefined || treatment.edges.length === 0)
|
|
1321
|
+
&& treatment.kind === "chamfer" && (treatment.angle === undefined || Math.abs(treatment.angle - 45) < 0.001)
|
|
1322
|
+
? "all-edge-chamfer-treatment"
|
|
1323
|
+
: (treatment.edges === undefined || treatment.edges.length === 0)
|
|
1324
|
+
&& treatment.kind === "fillet"
|
|
1325
|
+
? "all-edge-treatment"
|
|
1326
|
+
: "edge-treatment", async (shape) => {
|
|
1327
|
+
const treated = shapeWithCadResult(shape, await applyEdgeTreatment(shape, treatment));
|
|
1328
|
+
// Every treatment is recorded, as the editor records its own. A source
|
|
1329
|
+
// edit rebuilds the solid from the sketch and replays this record; with
|
|
1330
|
+
// no record it rebuilt a bare solid and silently dropped the treatment.
|
|
1331
|
+
// Selected edges are recorded by sketch lineage -- segment and cap --
|
|
1332
|
+
// when every one of them has exactly one, never by their transient
|
|
1333
|
+
// indexes (#350). A selected-edge record without lineage cannot be
|
|
1334
|
+
// replayed, so a later source edit is refused rather than lossy.
|
|
1335
|
+
const allEdges = treatment.edges === undefined || treatment.edges.length === 0;
|
|
1336
|
+
const lineage = allEdges ? undefined : await sketchSourceEdgesForEdgeIndexes(shape, treatment.edges);
|
|
1337
|
+
const feature = {
|
|
1338
|
+
kind: treatment.kind,
|
|
1339
|
+
amount: treatment.amount,
|
|
1340
|
+
edgeCount: allEdges ? await allEdgeTreatmentEdgeCount(shape, treatment.kind) : treatment.edges.length,
|
|
1341
|
+
...(allEdges ? { allEdges: true } : {}),
|
|
1342
|
+
...(treatment.kind === "chamfer" ? { chamferAngle: treatment.angle ?? 45 } : {}),
|
|
1343
|
+
...(lineage?.status === "matched" ? { sourceEdges: lineage.sourceEdges } : {}),
|
|
1344
|
+
};
|
|
1345
|
+
// The snapshot drops its own history, as the editor's does, so each
|
|
1346
|
+
// treatment does not nest a copy of every earlier one.
|
|
1347
|
+
const { edgeTreatmentHistory: _ownHistory, ...before } = shape;
|
|
1348
|
+
return {
|
|
1349
|
+
...treated,
|
|
1350
|
+
edgeTreatments: [...(shape.edgeTreatments ?? []), feature],
|
|
1351
|
+
edgeTreatmentHistory: [
|
|
1352
|
+
...(shape.edgeTreatmentHistory ?? []),
|
|
1353
|
+
{
|
|
1354
|
+
id: createLocalId("edge-history"),
|
|
1355
|
+
createdAt: Date.now(),
|
|
1356
|
+
feature,
|
|
1357
|
+
before: structuredClone(before),
|
|
1358
|
+
appliedFrame: edgeTreatmentAppliedFrame(treated),
|
|
1359
|
+
},
|
|
1360
|
+
],
|
|
1361
|
+
};
|
|
1362
|
+
});
|
|
1363
|
+
},
|
|
1286
1364
|
draftPrism: (projectId, shapeId, angle) => cadTreatment(projectId, shapeId, "prism-draft", async (shape) => ({
|
|
1287
|
-
...shapeWithCadResult(shape, await applyDraftPrism(shape, { angle })),
|
|
1365
|
+
...retainDraftSourceMirrors(shapeWithCadResult(shape, await applyDraftPrism(shape, { angle })), shape),
|
|
1288
1366
|
...retainedPrismDraftFields(shape, angle),
|
|
1289
1367
|
})),
|
|
1290
1368
|
mirrorBaked: (projectId, ids, axis) => mutating(projectId, async (document) => {
|