feat: add whole-shape mirrored feature
This commit is contained in:
@@ -45,7 +45,7 @@ const initialTree: ModelTreeItem[] = [
|
||||
{ id: 'reference', label: 'Reference geometry', type: 'folder', children: ['DatumPlane', 'DatumAxis'] },
|
||||
]
|
||||
|
||||
const typeIdForItem = (item: ModelTreeItem) => item.type === 'body' ? 'PartDesign::Body' : item.type === 'sketch' ? 'Sketcher::SketchObject' : item.id.startsWith('box') ? 'Part::Box' : item.id.startsWith('cylinder') ? 'Part::Cylinder' : item.id.startsWith('sphere') ? 'Part::Sphere' : item.id.startsWith('cone') ? 'Part::Cone' : item.id.startsWith('union') ? 'Part::Fuse' : item.id.startsWith('cut') ? 'Part::Cut' : item.id.startsWith('intersection') ? 'Part::Common' : item.id.startsWith('pad') ? 'PartDesign::Pad' : item.id.startsWith('pocket') ? 'PartDesign::Pocket' : item.id.startsWith('revolution') ? 'PartDesign::Revolution' : item.id.startsWith('fillet') ? 'PartDesign::Fillet' : item.id.startsWith('chamfer') ? 'PartDesign::Chamfer' : item.id.startsWith('linear-pattern') ? 'PartDesign::LinearPattern' : item.id.startsWith('polar-pattern') ? 'PartDesign::PolarPattern' : item.id.startsWith('hole') ? 'PartDesign::Hole' : item.type === 'feature' ? 'PartDesign::Feature' : 'App::DocumentObjectGroup'
|
||||
const typeIdForItem = (item: ModelTreeItem) => item.type === 'body' ? 'PartDesign::Body' : item.type === 'sketch' ? 'Sketcher::SketchObject' : item.id.startsWith('box') ? 'Part::Box' : item.id.startsWith('cylinder') ? 'Part::Cylinder' : item.id.startsWith('sphere') ? 'Part::Sphere' : item.id.startsWith('cone') ? 'Part::Cone' : item.id.startsWith('union') ? 'Part::Fuse' : item.id.startsWith('cut') ? 'Part::Cut' : item.id.startsWith('intersection') ? 'Part::Common' : item.id.startsWith('pad') ? 'PartDesign::Pad' : item.id.startsWith('pocket') ? 'PartDesign::Pocket' : item.id.startsWith('revolution') ? 'PartDesign::Revolution' : item.id.startsWith('fillet') ? 'PartDesign::Fillet' : item.id.startsWith('chamfer') ? 'PartDesign::Chamfer' : item.id.startsWith('mirrored') ? 'PartDesign::Mirrored' : item.id.startsWith('linear-pattern') ? 'PartDesign::LinearPattern' : item.id.startsWith('polar-pattern') ? 'PartDesign::PolarPattern' : item.id.startsWith('hole') ? 'PartDesign::Hole' : item.type === 'feature' ? 'PartDesign::Feature' : 'App::DocumentObjectGroup'
|
||||
|
||||
const commonProperties = (item: ModelTreeItem): ObjectPropertySnapshot[] => [
|
||||
{ name: 'Label', label: 'Label', group: 'Identity', scope: 'data', type: 'App::PropertyString', value: item.label },
|
||||
@@ -116,6 +116,11 @@ const featureProperties = (item: ModelTreeItem): ObjectPropertySnapshot[] => {
|
||||
{ name: 'Distance', label: 'Distance', group: 'Parameters', scope: 'data', type: 'App::PropertyLength', value: 2, unit: 'mm', recompute: true },
|
||||
{ name: 'Base', label: 'Base', group: 'Dependencies', scope: 'data', type: 'App::PropertyLink', value: 'pocket', recompute: true },
|
||||
]
|
||||
if (item.id.startsWith('mirrored')) return [
|
||||
{ name: 'Base', label: 'Base feature', group: 'Mirrored', scope: 'data', type: 'App::PropertyLink', value: null, recompute: true },
|
||||
{ name: 'Plane', label: 'Mirror plane', group: 'Mirrored', scope: 'data', type: 'App::PropertyEnumeration', value: 'YZ plane', options: ['XY plane', 'XZ plane', 'YZ plane'], recompute: true },
|
||||
{ name: 'Fuse', label: 'Fuse result', group: 'Mirrored', scope: 'data', type: 'App::PropertyBool', value: true, recompute: true },
|
||||
]
|
||||
if (item.id.startsWith('linear-pattern')) return [
|
||||
{ name: 'Base', label: 'Base', group: 'Pattern', scope: 'data', type: 'App::PropertyLink', value: null, recompute: true },
|
||||
{ name: 'Occurrences', label: 'Occurrences', group: 'Pattern', scope: 'data', type: 'App::PropertyInteger', value: 2, recompute: true },
|
||||
@@ -202,12 +207,12 @@ const createDocument = (label = 'Pump Housing'): DocumentSnapshot => {
|
||||
return document
|
||||
}
|
||||
|
||||
const selectionRequired = new Set(['pad', 'pocket', 'revolution', 'fillet', 'chamfer', 'union', 'cut', 'intersection', 'check-shape', 'hole', 'linear-pattern', 'polar-pattern', 'measure-distance', 'measure-angle', 'measure-area', 'solve-sketch'])
|
||||
const selectionRequired = new Set(['pad', 'pocket', 'revolution', 'fillet', 'chamfer', 'mirrored', 'union', 'cut', 'intersection', 'check-shape', 'hole', 'linear-pattern', 'polar-pattern', 'measure-distance', 'measure-angle', 'measure-area', 'solve-sketch'])
|
||||
const systemCommands = new Set(['new-document', 'save', 'select-object'])
|
||||
const implementedCommandIds = new Set(['new-document', 'save', 'select-object', 'create-body', 'create-sketch', 'new-sketch', 'pad', 'pocket', 'revolution', 'fillet', 'chamfer', 'linear-pattern', 'polar-pattern', 'hole', 'primitive', 'union', 'cut', 'intersection', 'check-shape', 'solve-sketch'])
|
||||
const partDesignCommands = new Set(['create-body', 'create-sketch', 'pad', 'pocket', 'revolution', 'fillet', 'chamfer', 'linear-pattern', 'polar-pattern', 'hole'])
|
||||
const implementedCommandIds = new Set(['new-document', 'save', 'select-object', 'create-body', 'create-sketch', 'new-sketch', 'pad', 'pocket', 'revolution', 'fillet', 'chamfer', 'mirrored', 'linear-pattern', 'polar-pattern', 'hole', 'primitive', 'union', 'cut', 'intersection', 'check-shape', 'solve-sketch'])
|
||||
const partDesignCommands = new Set(['create-body', 'create-sketch', 'pad', 'pocket', 'revolution', 'fillet', 'chamfer', 'mirrored', 'linear-pattern', 'polar-pattern', 'hole'])
|
||||
const partCommands = new Set(['primitive', 'union', 'cut', 'intersection', 'check-shape'])
|
||||
const shapeSelectionCommands = new Set(['union', 'cut', 'intersection', 'check-shape', 'fillet', 'chamfer', 'linear-pattern', 'polar-pattern', 'hole'])
|
||||
const shapeSelectionCommands = new Set(['union', 'cut', 'intersection', 'check-shape', 'fillet', 'chamfer', 'mirrored', 'linear-pattern', 'polar-pattern', 'hole'])
|
||||
const featureSelectionCommands = new Set(['pad', 'pocket', 'revolution'])
|
||||
const shapeTypeIds = new Set([
|
||||
'Part::Box',
|
||||
@@ -224,6 +229,7 @@ const shapeTypeIds = new Set([
|
||||
'PartDesign::Revolution',
|
||||
'PartDesign::Fillet',
|
||||
'PartDesign::Chamfer',
|
||||
'PartDesign::Mirrored',
|
||||
'PartDesign::LinearPattern',
|
||||
'PartDesign::PolarPattern',
|
||||
'PartDesign::Hole',
|
||||
@@ -236,6 +242,7 @@ const featureCommands: Record<string, { label: string; detail: string }> = {
|
||||
revolution: { label: 'Revolution', detail: 'Angle 360 deg' },
|
||||
fillet: { label: 'Fillet', detail: 'Radius 3 mm' },
|
||||
chamfer: { label: 'Chamfer', detail: 'Length 2 mm' },
|
||||
mirrored: { label: 'Mirrored', detail: 'Whole Shape across YZ plane' },
|
||||
'linear-pattern': { label: 'Linear Pattern', detail: '2 occurrences over 20 mm' },
|
||||
'polar-pattern': { label: 'Polar Pattern', detail: '3 occurrences over 360 deg' },
|
||||
hole: { label: 'Hole', detail: 'Simple 5 mm diameter hole' },
|
||||
@@ -764,7 +771,7 @@ export function createMockFacade(): BitBybitWebCadFacade {
|
||||
task: { getActive: () => getState().task, begin: beginTask, update: (draft) => { if (state.task) state = { ...state, task: { ...state.task, draft: { ...state.task.draft, ...draft } } }; emitState() }, apply: applyTask, cancel: () => { if (state.task) state = { ...state, task: { ...state.task, status: 'cancelled' } }; emitState() } },
|
||||
diagnostics: { list: () => state.diagnostics.map(cloneDiagnostic), tree: () => buildDiagnosticTree(state.diagnostics), repair: repairDiagnostic },
|
||||
project: { capabilities: () => projectPersistence.capabilities(), subscribeExternalChanges: (listener) => projectPersistence.subscribeExternalChanges(listener), list: () => projectPersistence.list(), save: (document = getState().document) => projectPersistence.save(document), load: (documentId) => projectPersistence.load(documentId), loadCheckpoint: (documentId, version) => projectPersistence.loadCheckpoint(documentId, version), recovery: (documentId) => projectPersistence.recovery(documentId), fcstd: { inspect: (bytes, limits) => inspectFcstdArchive(bytes, limits) }, resource: projectPersistence.resource },
|
||||
geometry: { capabilities: () => geometryRuntime.capabilities(), initialize: () => geometryRuntime.initialize(), createBox: (input) => geometryRuntime.createBox(input), createCylinder: (input) => geometryRuntime.createCylinder(input), createSphere: (input) => geometryRuntime.createSphere(input), createCone: (input) => geometryRuntime.createCone(input), applyPlacement: (input) => geometryRuntime.applyPlacement(input), union: (input) => geometryRuntime.union(input), cut: (input) => geometryRuntime.cut(input), intersection: (input) => geometryRuntime.intersection(input), fillet: (input) => geometryRuntime.fillet(input), chamfer: (input) => geometryRuntime.chamfer(input), exportStep: (shape, fileName) => geometryRuntime.exportStep(shape, fileName), exportStl: (shape, fileName, precision) => geometryRuntime.exportStl(shape, fileName, precision), pad: (input) => geometryRuntime.pad(input), pocket: (input) => geometryRuntime.pocket(input), revolution: (input) => geometryRuntime.revolution(input), mesh: (shape, precision) => geometryRuntime.mesh(shape, precision), subshapes: (shape, precision) => geometryRuntime.subshapes(shape, precision), topology: (shape, precision) => geometryRuntime.topology(shape, precision), getObjectShape: (objectId) => { const shape = featureShapes.get(objectId); return shape ? { ...shape } : null }, release: (shape) => geometryRuntime.release(shape), dispose: () => { clearFeatureShapes(); geometryRuntime.dispose() } },
|
||||
geometry: { capabilities: () => geometryRuntime.capabilities(), initialize: () => geometryRuntime.initialize(), createBox: (input) => geometryRuntime.createBox(input), createCylinder: (input) => geometryRuntime.createCylinder(input), createSphere: (input) => geometryRuntime.createSphere(input), createCone: (input) => geometryRuntime.createCone(input), applyPlacement: (input) => geometryRuntime.applyPlacement(input), mirror: (input) => geometryRuntime.mirror(input), union: (input) => geometryRuntime.union(input), cut: (input) => geometryRuntime.cut(input), intersection: (input) => geometryRuntime.intersection(input), fillet: (input) => geometryRuntime.fillet(input), chamfer: (input) => geometryRuntime.chamfer(input), exportStep: (shape, fileName) => geometryRuntime.exportStep(shape, fileName), exportStl: (shape, fileName, precision) => geometryRuntime.exportStl(shape, fileName, precision), pad: (input) => geometryRuntime.pad(input), pocket: (input) => geometryRuntime.pocket(input), revolution: (input) => geometryRuntime.revolution(input), mesh: (shape, precision) => geometryRuntime.mesh(shape, precision), subshapes: (shape, precision) => geometryRuntime.subshapes(shape, precision), topology: (shape, precision) => geometryRuntime.topology(shape, precision), getObjectShape: (objectId) => { const shape = featureShapes.get(objectId); return shape ? { ...shape } : null }, release: (shape) => geometryRuntime.release(shape), dispose: () => { clearFeatureShapes(); geometryRuntime.dispose() } },
|
||||
viewport: { createAdapter: () => new ThreeViewportAdapter() },
|
||||
getState, subscribe: (listener) => { listeners.add(listener); return () => { listeners.delete(listener) } }, notify,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user