feat: persist primitive task parameters
This commit is contained in:
@@ -347,7 +347,11 @@ function TaskPanel({ workbench, facade, showNotice }: { workbench: Workbench; fa
|
|||||||
const isSketch = workbench === 'Sketcher'
|
const isSketch = workbench === 'Sketcher'
|
||||||
const activeTask = facade.task.getActive()
|
const activeTask = facade.task.getActive()
|
||||||
const primitiveType = activeTask?.commandId === 'primitive' && typeof activeTask.draft.primitiveType === 'string' ? activeTask.draft.primitiveType : 'Box'
|
const primitiveType = activeTask?.commandId === 'primitive' && typeof activeTask.draft.primitiveType === 'string' ? activeTask.draft.primitiveType : 'Box'
|
||||||
return <div className="task-panel"><div className="task-actions-top"><button className="button button-primary" onClick={() => { facade.task.apply(); showNotice('Task accepted') }}>OK</button><button className="button button-outline" onClick={() => { facade.task.update({ preview: true }); showNotice('Preview applied') }}>Apply</button><button className="button button-quiet" onClick={() => { facade.task.cancel(); showNotice('Task cancelled') }}>Cancel</button></div><div className="task-header"><div className="task-icon"><Pencil size={16} /></div><div><span className="eyebrow">Active command</span><h2>{isSketch ? 'Edit Sketch' : definition.taskTitle}</h2></div><Badge tone="cyan">Preview</Badge></div><div className="task-body"><div className="task-step"><span className="step-index">1</span><div><strong>{isSketch ? 'Geometry and constraints' : definition.objectType}</strong><span>{definition.taskSummary}</span></div></div>{activeTask?.commandId === 'primitive' && <label className="field-label">Primitive type<select className="field-input" value={primitiveType} onChange={(event) => facade.task.update({ primitiveType: event.target.value })}><option>Box</option><option>Cylinder</option><option>Sphere</option><option>Cone</option></select></label>}<label className="field-label">Primary value <span className="field-unit">mm</span><input className="field-input" value={isSketch ? 'Fully constrained' : '42.00'} readOnly /></label><label className="field-label">Operation<select className="field-input"><option>{workbench === 'Part Design' ? 'Dimension' : 'Contextual preview'}</option><option>Through all</option><option>Up to face</option></select></label><label className="check-row"><input type="checkbox" defaultChecked /><span>Preview result in viewport</span></label><div className="task-note"><AlertTriangle size={14} /><span>Changes remain local until the document is recomputed.</span></div></div></div>
|
const primitiveFields = primitiveType === 'Box' ? ['width', 'length', 'height'] : primitiveType === 'Cylinder' ? ['radius', 'height', 'angle'] : primitiveType === 'Sphere' ? ['radius'] : ['radius1', 'radius2', 'height', 'angle']
|
||||||
|
const primitiveUnits: Record<string, string> = { width: 'mm', length: 'mm', height: 'mm', radius: 'mm', radius1: 'mm', radius2: 'mm', angle: 'deg' }
|
||||||
|
const primitiveFallbacks: Record<string, number> = { width: 10, length: 10, height: 10, radius: 5, radius1: 5, radius2: 0, angle: 360 }
|
||||||
|
const primitiveLabel = (field: string) => field === 'radius1' ? 'Bottom radius' : field === 'radius2' ? 'Top radius' : field[0].toUpperCase() + field.slice(1)
|
||||||
|
return <div className="task-panel"><div className="task-actions-top"><button className="button button-primary" onClick={() => { facade.task.apply(); showNotice('Task accepted') }}>OK</button><button className="button button-outline" onClick={() => { facade.task.update({ preview: true }); showNotice('Preview applied') }}>Apply</button><button className="button button-quiet" onClick={() => { facade.task.cancel(); showNotice('Task cancelled') }}>Cancel</button></div><div className="task-header"><div className="task-icon"><Pencil size={16} /></div><div><span className="eyebrow">Active command</span><h2>{isSketch ? 'Edit Sketch' : definition.taskTitle}</h2></div><Badge tone="cyan">Preview</Badge></div><div className="task-body"><div className="task-step"><span className="step-index">1</span><div><strong>{isSketch ? 'Geometry and constraints' : definition.objectType}</strong><span>{definition.taskSummary}</span></div></div>{activeTask?.commandId === 'primitive' && <><label className="field-label">Primitive type<select className="field-input" value={primitiveType} onChange={(event) => facade.task.update({ primitiveType: event.target.value })}><option>Box</option><option>Cylinder</option><option>Sphere</option><option>Cone</option></select></label>{primitiveFields.map((field) => <label className="field-label" key={field}>{primitiveLabel(field)} <span className="field-unit">{primitiveUnits[field]}</span><input className="field-input" type="number" min={field === 'radius2' ? 0 : 0.001} step={primitiveUnits[field] === 'deg' ? 1 : 0.1} value={typeof activeTask.draft[field] === 'number' ? Number(activeTask.draft[field]) : primitiveFallbacks[field]} onChange={(event) => facade.task.update({ [field]: Number(event.target.value) })} /></label>)}</>}<label className="field-label">Primary value <span className="field-unit">mm</span><input className="field-input" value={isSketch ? 'Fully constrained' : '42.00'} readOnly /></label><label className="field-label">Operation<select className="field-input"><option>{workbench === 'Part Design' ? 'Dimension' : 'Contextual preview'}</option><option>Through all</option><option>Up to face</option></select></label><label className="check-row"><input type="checkbox" defaultChecked /><span>Preview result in viewport</span></label><div className="task-note"><AlertTriangle size={14} /><span>Changes remain local until the document is recomputed.</span></div></div></div>
|
||||||
}
|
}
|
||||||
|
|
||||||
function PropertyPanel({ facade, objectId, scope, showNotice }: { facade: BitBybitWebCadFacade; objectId: string; scope: 'data' | 'view'; showNotice: (message: string) => void }) {
|
function PropertyPanel({ facade, objectId, scope, showNotice }: { facade: BitBybitWebCadFacade; objectId: string; scope: 'data' | 'view'; showNotice: (message: string) => void }) {
|
||||||
|
|||||||
@@ -303,6 +303,12 @@ export function createMockFacade(): BitBybitWebCadFacade {
|
|||||||
tree.push(item)
|
tree.push(item)
|
||||||
}
|
}
|
||||||
const objectSnapshot = createObjectSnapshot(item)
|
const objectSnapshot = createObjectSnapshot(item)
|
||||||
|
if (commandId === 'primitive') {
|
||||||
|
objectSnapshot.properties = objectSnapshot.properties.map((property) => {
|
||||||
|
const candidate = draft[property.name.toLowerCase()]
|
||||||
|
return typeof candidate === 'number' && Number.isFinite(candidate) ? { ...property, value: candidate } : property
|
||||||
|
})
|
||||||
|
}
|
||||||
if (isPartObject && commandId !== 'primitive') {
|
if (isPartObject && commandId !== 'primitive') {
|
||||||
objectSnapshot.properties = objectSnapshot.properties.map((property) => property.name === 'Base' && state.selectedObjectId ? { ...property, value: state.selectedObjectId } : property)
|
objectSnapshot.properties = objectSnapshot.properties.map((property) => property.name === 'Base' && state.selectedObjectId ? { ...property, value: state.selectedObjectId } : property)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -794,9 +794,12 @@ test('Part workbench commands create primitive and boolean document objects', ()
|
|||||||
facade.task.apply()
|
facade.task.apply()
|
||||||
assert.equal(facade.app.document.getObject('box')?.typeId, 'Part::Box')
|
assert.equal(facade.app.document.getObject('box')?.typeId, 'Part::Box')
|
||||||
facade.gui.command.execute({ commandId: 'primitive' })
|
facade.gui.command.execute({ commandId: 'primitive' })
|
||||||
facade.task.update({ primitiveType: 'Cylinder' })
|
facade.task.update({ primitiveType: 'Cylinder', radius: 3, height: 12, angle: 180 })
|
||||||
facade.task.apply()
|
facade.task.apply()
|
||||||
assert.equal(facade.app.document.getObject('cylinder')?.typeId, 'Part::Cylinder')
|
assert.equal(facade.app.document.getObject('cylinder')?.typeId, 'Part::Cylinder')
|
||||||
|
assert.equal(facade.app.document.getObject('cylinder')?.properties.find((property) => property.name === 'Radius')?.value, 3)
|
||||||
|
assert.equal(facade.app.document.getObject('cylinder')?.properties.find((property) => property.name === 'Height')?.value, 12)
|
||||||
|
assert.equal(facade.app.document.getObject('cylinder')?.properties.find((property) => property.name === 'Angle')?.value, 180)
|
||||||
|
|
||||||
assert.equal(facade.gui.command.getState('union').status, 'enabled')
|
assert.equal(facade.gui.command.getState('union').status, 'enabled')
|
||||||
facade.gui.command.execute({ commandId: 'union' })
|
facade.gui.command.execute({ commandId: 'union' })
|
||||||
|
|||||||
Reference in New Issue
Block a user