fix dirty file save logic

master
howard 2021-04-18 20:53:02 -07:00
parent 132be08553
commit 662ced6edd
11 changed files with 205 additions and 118 deletions

File diff suppressed because one or more lines are too long

1
example_parts/test3.json Normal file

File diff suppressed because one or more lines are too long

1
example_parts/test4.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -36,12 +36,12 @@ export class Scene {
this.sid = 1 this.sid = 1
this.mid = 1 this.mid = 1
this.store = store;
this.canvas = document.querySelector('#c'); this.canvas = document.querySelector('#c');
this.rect = this.canvas.getBoundingClientRect().toJSON() this.rect = this.canvas.getBoundingClientRect().toJSON()
this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas }); this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas });
this.store = store
const size = 1; const size = 1;
const near = 0; const near = 0;
@ -178,11 +178,6 @@ export class Scene {
} }
saveScene() {
return JSON.stringify([id, this.sid, this.mid, this.store.getState().treeEntries])
}
clearScene() { clearScene() {
const deleted = this.obj3d.children.splice(1) const deleted = this.obj3d.children.splice(1)
@ -240,7 +235,6 @@ export class Scene {
} }
} }
this.store.dispatch({ type: 'restore-state', state })
return state return state
} }
@ -262,17 +256,10 @@ export class Scene {
this.obj3d.add(mesh) this.obj3d.add(mesh)
this.store.dispatch({ type: 'rx-extrusion', mesh, sketchId: sketch.obj3d.name }) return mesh
if (this.activeSketch == sketch) {
this.store.dispatch({ type: 'finish-sketch' })
sketch.deactivate()
}
this.render()
} }
boolOp(m1, m2, op, refresh = false) { boolOp(m1, m2, op) {
let bspA = CSG.fromMesh(m1) let bspA = CSG.fromMesh(m1)
let bspB = CSG.fromMesh(m2) let bspB = CSG.fromMesh(m2)
m1.visible = false m1.visible = false
@ -312,33 +299,16 @@ export class Scene {
mesh.add(vertices) mesh.add(vertices)
if (!refresh) {
sc.obj3d.add(mesh)
this.store.dispatch({
type: 'set-entry-visibility', obj: {
[m1.name]: false,
[m2.name]: false,
[mesh.name]: true,
}
})
this.store.dispatch({
type: 'rx-boolean', mesh, deps: [m1.name, m2.name]
})
} else {
return mesh return mesh
}
} }
refreshNode(id) { refreshNode(id, { byId, tree }) {
let curId let curId
let que = [id] let que = [id]
let idx = 0 let idx = 0
// let newNodes = {} // let newNodes = {}
const { byId, tree } = this.store.getState().treeEntries
while (idx < que.length) { while (idx < que.length) {
curId = que[idx++] curId = que[idx++]
@ -348,7 +318,7 @@ export class Scene {
if (info.length == 2) { if (info.length == 2) {
newNode = extrude(byId[info[0]], info[1]) newNode = extrude(byId[info[0]], info[1])
} else if (info.length == 3) { } else if (info.length == 3) {
newNode = this.boolOp(byId[info[0]], byId[info[1]], info[2], true) newNode = this.boolOp(byId[info[0]], byId[info[1]], info[2])
} }
byId[curId].geometry.copy(newNode.geometry) byId[curId].geometry.copy(newNode.geometry)
byId[curId].geometry.parameters = newNode.geometry.parameters // took 2 hours to figure out byId[curId].geometry.parameters = newNode.geometry.parameters // took 2 hours to figure out
@ -433,14 +403,9 @@ async function addSketch() {
this.clearSelection() this.clearSelection()
sketch.obj3d.addEventListener('change', this.render); sketch.obj3d.addEventListener('change', this.render);
this.store.dispatch({ type: 'rx-sketch', obj: sketch })
sketch.activate()
this.render()
return sketch
} }

View File

@ -9,7 +9,6 @@ import { onHover, onDrag, onPick, onRelease, clearSelection} from './mouseEvents
import { setCoincident, setOrdinate, setTangent } from './constraintEvents' import { setCoincident, setOrdinate, setTangent } from './constraintEvents'
import { get3PtArc } from './drawArc' import { get3PtArc } from './drawArc'
import { replacer, reviver } from './utils' import { replacer, reviver } from './utils'
import { AxesHelper } from './sketchAxes'
import { drawDimension, _onMoveDimension, setDimLines, updateDim } from './drawDimension'; import { drawDimension, _onMoveDimension, setDimLines, updateDim } from './drawDimension';
@ -19,7 +18,6 @@ class Sketch {
constructor(scene, preload) { constructor(scene, preload) {
// [0]:x, [1]:y, [2]:z // [0]:x, [1]:y, [2]:z
this.ptsBuf = new Float32Array(this.max_pts * 3).fill(NaN) this.ptsBuf = new Float32Array(this.max_pts * 3).fill(NaN)
@ -96,7 +94,6 @@ class Sketch {
this.camera = scene.camera this.camera = scene.camera
this.canvas = scene.canvas this.canvas = scene.canvas
this.rect = scene.rect this.rect = scene.rect
this.store = scene.store;
@ -153,15 +150,28 @@ class Sketch {
} }
setClean() {
this.hasChanged = false
this.idOnActivate = id
this.c_idOnActivate = this.c_id
const changeDetector = (e) => {
if (this.selected.length && e.buttons) {
this.canvas.removeEventListener('pointermove', changeDetector)
this.hasChanged = true
}
}
this.canvas.addEventListener('pointermove', changeDetector)
}
activate() { activate() {
console.log('activate sketch')
window.addEventListener('keydown', this.onKeyPress) window.addEventListener('keydown', this.onKeyPress)
this.canvas.addEventListener('pointerdown', this.onPick) this.canvas.addEventListener('pointerdown', this.onPick)
this.canvas.addEventListener('pointermove', this.onHover) this.canvas.addEventListener('pointermove', this.onHover)
this.store.dispatch({ type: 'set-active-sketch', activeSketchId: this.obj3d.name })
this.setDimLines() this.setDimLines()
this.obj3d.traverse(e => e.layers.enable(2)) this.obj3d.traverse(e => e.layers.enable(2))
@ -172,21 +182,12 @@ class Sketch {
window.sketcher = this window.sketcher = this
// overkill but good solution if this check was more costly this.setClean()
this.hasChanged = false
this.idOnActivate = id
this.c_idOnActivate = this.c_id
// console.log(this,this.selected)
const changeDetector = (e) => {
if (this.selected.length && e.buttons) {
this.canvas.removeEventListener('pointermove', changeDetector)
this.hasChanged = true
}
}
this.canvas.addEventListener('pointermove', changeDetector)
} }
deactivate() { deactivate() {
console.log('deactivate')
window.removeEventListener('keydown', this.onKeyPress) window.removeEventListener('keydown', this.onKeyPress)
this.canvas.removeEventListener('pointerdown', this.onPick) this.canvas.removeEventListener('pointerdown', this.onPick)
this.canvas.removeEventListener('pointermove', this.onHover) this.canvas.removeEventListener('pointermove', this.onHover)

View File

@ -10,22 +10,29 @@ import * as Icon from "./icons";
export const Dialog = () => { export const Dialog = () => {
const dialog = useSelector(state => state.ui.dialog) const dialog = useSelector(state => state.ui.dialog)
const treeEntries = useSelector(state => state.treeEntries)
const dispatch = useDispatch() const dispatch = useDispatch()
const ref = useRef() const ref = useRef()
useEffect(() => { useEffect(() => {
console.log(dialog)
if (!ref.current) return if (!ref.current) return
ref.current.focus() ref.current.focus()
}, [dialog]) }, [dialog])
const extrude = () => { const extrude = () => {
sc.extrude(dialog.target, ref.current.value) const mesh = sc.extrude(dialog.target, ref.current.value)
sc.render()
dispatch({ type: 'rx-extrusion', mesh, sketchId: dialog.target.obj3d.name })
if (sc.activeSketch == dialog.target) {
dispatch({ type: 'finish-sketch' })
dialog.target.deactivate()
}
dispatch({ type: "clear-dialog" }) dispatch({ type: "clear-dialog" })
sc.render()
} }
const extrudeEdit = () => { const extrudeEdit = () => {
@ -33,11 +40,12 @@ export const Dialog = () => {
dialog.target.userData.featureInfo[1] = ref.current.value dialog.target.userData.featureInfo[1] = ref.current.value
sc.refreshNode(dialog.target.name) sc.refreshNode(dialog.target.name, treeEntries)
dispatch({ type: 'set-modified', status: true })
sc.render()
dispatch({ type: "clear-dialog" }) dispatch({ type: "clear-dialog" })
sc.render()
} }
@ -53,7 +61,13 @@ export const Dialog = () => {
onClick={extrude} onClick={extrude}
/> />
<MdClose className="btn w-auto h-full p-3.5 mr-6" <MdClose className="btn w-auto h-full p-3.5 mr-6"
onClick={() => dispatch({ type: "clear-dialog" })} onClick={() => {
if (sc.activeSketch == dialog.target) { // if extrude dialog launched from sketch mode we set dialog back to the sketch dialog
dispatch({ type: 'set-dialog', action: 'sketch' })
} else {
dispatch({ type: "clear-dialog" })
}
}}
/> />
</> </>
case 'extrude-edit': case 'extrude-edit':
@ -79,7 +93,9 @@ export const Dialog = () => {
|| sc.activeSketch.idOnActivate != id || sc.activeSketch.idOnActivate != id
|| sc.activeSketch.c_idOnActivate != sc.activeSketch.c_id || sc.activeSketch.c_idOnActivate != sc.activeSketch.c_id
) { ) {
sc.refreshNode(sc.activeSketch.obj3d.name) sc.refreshNode(sc.activeSketch.obj3d.name, treeEntries)
dispatch({ type: 'set-modified', status: true })
} }
dispatch({ type: 'finish-sketch' }) dispatch({ type: 'finish-sketch' })
@ -96,10 +112,11 @@ export const Dialog = () => {
|| sc.activeSketch.c_idOnActivate != sc.activeSketch.c_id || sc.activeSketch.c_idOnActivate != sc.activeSketch.c_id
) { ) {
dispatch({ type: "restore-sketch" }) dispatch({ type: "restore-sketch" })
} else { // dispatch({ type: 'set-modified', status: false })
dispatch({ type: 'finish-sketch' })
} }
dispatch({ type: 'finish-sketch' })
sc.activeSketch.deactivate() sc.activeSketch.deactivate()
sc.render() sc.render()
dispatch({ type: "clear-dialog" }) dispatch({ type: "clear-dialog" })

View File

@ -22,6 +22,8 @@ export function STLExport(filename) {
const time = (new Date(Date.now() - tzoffset)).toISOString().slice(0, -5).replace(/:/g, '-'); const time = (new Date(Date.now() - tzoffset)).toISOString().slice(0, -5).replace(/:/g, '-');
saveLegacy(new Blob([result], { type: 'model/stl' }), `${filename}_${time}.stl`); saveLegacy(new Blob([result], { type: 'model/stl' }), `${filename}_${time}.stl`);
} else {
alert('please select one body to export')
} }
} }
@ -48,7 +50,7 @@ export async function saveFileAs(file, dispatch) {
const opts = { const opts = {
types: [{ types: [{
// description: 'Text file', description: 'Text file',
accept: { 'application/json': ['.json'] }, accept: { 'application/json': ['.json'] },
}], }],
}; };
@ -114,11 +116,12 @@ export async function openFile(dispatch) {
try { try {
const file = await fileHandle.getFile(); const file = await fileHandle.getFile();
const text = await file.text();; const text = await file.text();;
sc.loadState(text)
dispatch({ type: 'restore-state', state: sc.loadState(text) })
dispatch({ type: 'set-file-handle', fileHandle }) dispatch({ type: 'set-file-handle', fileHandle })
// app.setModified(false);
// app.setFocus(true); // app.setFocus(true);
} catch (ex) { } catch (ex) {
const msg = `An error occured reading ${fileHandle}`; const msg = `An error occured reading ${fileHandle}`;
console.error(msg, ex); console.error(msg, ex);
@ -127,3 +130,30 @@ export async function openFile(dispatch) {
}; };
export function confirmDiscard(modified) {
if (!modified) {
return true;
}
const confirmMsg = 'Discard changes? All changes will be lost.';
return confirm(confirmMsg);
};
export async function verifyPermission(fileHandle) {
const opts = {
mode:'readwrite'
};
// Check if we already have permission, if so, return true.
if (await fileHandle.queryPermission(opts) === 'granted') {
return true;
}
// Request permission to the file, if the user grants permission, return true.
if (await fileHandle.requestPermission(opts) === 'granted') {
return true;
}
// The user did nt grant permission, return false.
return false;
}

View File

@ -4,43 +4,88 @@ import React, { useEffect, useReducer } from 'react';
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { FaEdit, FaFileDownload } from 'react-icons/fa' import { FaEdit } from 'react-icons/fa'
import { MdSave, MdFolder, MdFileUpload, MdInsertDriveFile } from 'react-icons/md' import { MdSave, MdFolder, MdInsertDriveFile } from 'react-icons/md'
import { FaRegFolderOpen, FaFile } from 'react-icons/fa'
import * as Icon from "./icons"; import * as Icon from "./icons";
import { Dialog } from './dialog' import { Dialog } from './dialog'
import { STLExport, saveFile, openFile } from './fileHelpers' import { STLExport, saveFile, openFile, verifyPermission } from './fileHelpers'
export const NavBar = () => { export const NavBar = () => {
const dispatch = useDispatch() const dispatch = useDispatch()
const activeSketchId = useSelector(state => state.treeEntries.activeSketchId) const sketchActive = useSelector(state => state.ui.sketchActive)
const treeEntriesById = useSelector(state => state.treeEntries.byId) const treeEntries = useSelector(state => state.treeEntries)
const fileHandle = useSelector(state => state.ui.fileHandle) const fileHandle = useSelector(state => state.ui.fileHandle)
const modified = useSelector(state => state.ui.modified)
const boolOp = (code) => { const boolOp = (code) => {
if (sc.selected.length != 2 || !sc.selected.every(e => e.userData.type == 'mesh')) return if (sc.selected.length != 2 || !sc.selected.every(e => e.userData.type == 'mesh')) return
const [m1, m2] = sc.selected const [m1, m2] = sc.selected
sc.boolOp(m1, m2, code)
const mesh = sc.boolOp(m1, m2, code)
sc.obj3d.add(mesh)
dispatch({
type: 'set-entry-visibility', obj: {
[m1.name]: false,
[m2.name]: false,
[mesh.name]: true,
}
})
dispatch({
type: 'rx-boolean', mesh, deps: [m1.name, m2.name]
})
sc.render() sc.render()
forceUpdate() forceUpdate()
} }
const addSketch = () => { const addSketch = async () => {
sc.addSketch() const sketch = await sc.addSketch()
dispatch({ type: 'rx-sketch', obj: sketch })
sketch.activate()
sc.render()
dispatch({ type: 'set-dialog', action: 'sketch' }) dispatch({ type: 'set-dialog', action: 'sketch' })
forceUpdate() forceUpdate()
} }
const confirmDiscard = () => !modified ? true : confirm('Discard changes? All changes will be lost.')
useEffect(() => {
const onBeforeUnload = (e) => {
if (modified ||
(sc.activeSketch &&
(
sc.activeSketch.hasChanged
|| sc.activeSketch.idOnActivate != id
|| sc.activeSketch.c_idOnActivate != sc.activeSketch.c_id
)
)
) {
e.preventDefault();
e.returnValue = `There are unsaved changes. Are you sure you want to leave?`;
}
}
window.addEventListener('beforeunload', onBeforeUnload)
return () => window.removeEventListener('beforeunload', onBeforeUnload)
}, [modified])
useEffect(() => { // hacky way to handle mounting and unmounting mouse listeners for feature mode useEffect(() => { // hacky way to handle mounting and unmounting mouse listeners for feature mode
if (!activeSketchId) { if (!sketchActive) {
sc.canvas.addEventListener('pointermove', sc.onHover) sc.canvas.addEventListener('pointermove', sc.onHover)
sc.canvas.addEventListener('pointerdown', sc.onPick) sc.canvas.addEventListener('pointerdown', sc.onPick)
return () => { return () => {
@ -48,11 +93,10 @@ export const NavBar = () => {
sc.canvas.removeEventListener('pointerdown', sc.onPick) sc.canvas.removeEventListener('pointerdown', sc.onPick)
} }
} }
}, [activeSketchId]) }, [sketchActive])
const sketchModeButtons = [ const sketchModeButtons = [
[Icon.Extrude, () => { [Icon.Extrude, () => {
dispatch({ type: 'finish-sketch' })
dispatch({ type: 'set-dialog', action: 'extrude', target: sc.activeSketch }) dispatch({ type: 'set-dialog', action: 'extrude', target: sc.activeSketch })
}, 'Extrude [e]'], }, 'Extrude [e]'],
@ -63,32 +107,43 @@ export const NavBar = () => {
[Icon.Vertical, () => sc.activeSketch.command('v'), 'Vertical [v]'], [Icon.Vertical, () => sc.activeSketch.command('v'), 'Vertical [v]'],
[Icon.Horizontal, () => sc.activeSketch.command('h'), 'Horizontal [h]'], [Icon.Horizontal, () => sc.activeSketch.command('h'), 'Horizontal [h]'],
[Icon.Tangent, () => sc.activeSketch.command('t'), 'Tangent [t]'], [Icon.Tangent, () => sc.activeSketch.command('t'), 'Tangent [t]'],
[Icon.Tangent, () => sc.activeSketch.command('t'), 'Tangent [t]'], [MdSave,
async () => {
if(await verifyPermission(fileHandle) === false) return
sc.refreshNode(sc.activeSketch.obj3d.name, treeEntries)
sc.activeSketch.clearSelection()
saveFile(fileHandle, JSON.stringify([id, sc.sid, sc.mid, treeEntries]), dispatch)
sc.render()
sc.activeSketch.setClean()
}
, 'Save']
] ]
const partModeButtons = [ const partModeButtons = [
[FaEdit, addSketch, 'Sketch [s]'], [FaEdit, addSketch, 'Sketch [s]'],
[Icon.Extrude, () => { [Icon.Extrude, () => {
dispatch({ type: 'set-dialog', action: 'extrude', target: treeEntriesById[sc.selected[0].name] }) dispatch({ type: 'set-dialog', action: 'extrude', target: treeEntries.byId[sc.selected[0].name] })
}, 'Extrude [e]'], }, 'Extrude'],
[Icon.Union, () => boolOp('u'), 'Union'], [Icon.Union, () => boolOp('u'), 'Union'],
[Icon.Subtract, () => boolOp('s'), 'Subtract'], [Icon.Subtract, () => boolOp('s'), 'Subtract'],
[Icon.Intersect, () => boolOp('i'), 'Intersect'], [Icon.Intersect, () => boolOp('i'), 'Intersect'],
[MdInsertDriveFile, () => { [MdInsertDriveFile, () => {
if (!confirmDiscard()) return
sc.newPart() sc.newPart()
dispatch({ type: 'new-part' }) dispatch({ type: 'new-part' })
sc.render() sc.render()
}, 'New [ctrl+n]'], }, 'New'],
[MdSave, [MdSave,
() => { () => {
saveFile(fileHandle, sc.saveScene(), dispatch) saveFile(fileHandle, JSON.stringify([id, sc.sid, sc.mid, treeEntries]), dispatch)
} }
, 'Save [ctrl+s]'], , 'Save'],
[MdFolder, () => { [MdFolder, () => {
if (!confirmDiscard()) return
openFile(dispatch).then( openFile(dispatch).then(
()=>sc.render() sc.render
) )
}, 'Open'], }, 'Open'],
[Icon.Stl, () => { [Icon.Stl, () => {
@ -106,7 +161,7 @@ export const NavBar = () => {
</div> </div>
<div className='w-auto h-full flex-none'> <div className='w-auto h-full flex-none'>
{ {
activeSketchId ? sketchActive ?
sketchModeButtons.map(([Icon, fcn, txt, shortcut], idx) => ( sketchModeButtons.map(([Icon, fcn, txt, shortcut], idx) => (
<Icon className="btn w-auto h-full p-3.5" tooltip={txt} <Icon className="btn w-auto h-full p-3.5" tooltip={txt}
onClick={fcn} key={idx} onClick={fcn} key={idx}

View File

@ -10,7 +10,6 @@ const defaultState = {
tree: {}, tree: {},
order: {}, order: {},
visible: {}, visible: {},
activeSketchId: ""
} }
let cache let cache
@ -33,21 +32,19 @@ export function treeEntries(state = defaultState, action) {
} }
case 'set-active-sketch': case 'set-active-sketch':
cache = JSON.stringify(state.byId[action.activeSketchId]) cache = JSON.stringify(action.sketch)
return update(state, { return update(state, {
visible: { [action.activeSketchId]: { $set: true } }, visible: { [action.sketch.obj3d.name]: { $set: true } },
activeSketchId: { $set: action.activeSketchId },
}) })
case 'finish-sketch': case 'finish-sketch':
return update(state, { return update(state, {
activeSketchId: { $set: "" }, visible: { [sc.activeSketch.obj3d.name]: { $set: false } },
visible: { [state.activeSketchId]: { $set: false } },
}) })
case 'restore-sketch': case 'restore-sketch':
const sketch = sc.loadSketch(cache) const sketch = sc.loadSketch(cache)
const deletedObj = sc.obj3d.children.splice(state.order[state.activeSketchId] + 1, 1, const deletedObj = sc.obj3d.children.splice(state.order[sc.activeSketch.obj3d.name] + 1, 1,
sketch.obj3d sketch.obj3d
)[0] )[0]
@ -59,9 +56,7 @@ export function treeEntries(state = defaultState, action) {
sc.activeSketch = sketch sc.activeSketch = sketch
return update(state, { return update(state, {
activeSketchId: { $set: "" }, byId: { [sc.activeSketch.obj3d.name]: { $set: sketch } },
byId: { [state.activeSketchId]: { $set: sketch } },
visible: { [state.activeSketchId]: { $set: false } },
}) })
case 'rx-extrusion': case 'rx-extrusion':
@ -108,7 +103,18 @@ export function treeEntries(state = defaultState, action) {
export function ui(state = { dialog: {}, filePane: false }, action) { export function ui(state = { dialog: {}, filePane: false }, action) {
switch (action.type) { switch (action.type) {
case 'set-active-sketch':
return update(state, {
sketchActive: { $set: true },
})
case 'rx-sketch':
return update(state, {
sketchActive: { $set: true },
})
case 'finish-sketch':
return update(state, {
sketchActive: { $set: false },
})
case 'set-dialog': case 'set-dialog':
return update(state, { return update(state, {
dialog: { $set: { target: action.target, action: action.action } }, dialog: { $set: { target: action.target, action: action.action } },
@ -127,6 +133,18 @@ export function ui(state = { dialog: {}, filePane: false }, action) {
fileHandle: { $set: null }, fileHandle: { $set: null },
modified: { $set: false }, modified: { $set: false },
}) })
case 'set-modified':
return update(state, {
modified: { $set: action.status },
})
case 'delete-node':
return update(state, {
modified: { $set: true },
})
case 'rx-extrusion':
return update(state, {
modified: { $set: true },
})
default: default:
return state return state
} }

View File

@ -8,10 +8,12 @@ import { FaCube, FaEdit } from 'react-icons/fa'
export const Tree = () => { export const Tree = () => {
const treeEntries = useSelector(state => state.treeEntries) const treeEntries = useSelector(state => state.treeEntries)
const ref = useRef() const fileHandle = useSelector(state => state.ui.fileHandle)
return <div className='sideNav flex flex-col bg-gray-800'> return <div className='sideNav flex flex-col bg-gray-800'>
<input className='w-16 text-gray-50 h-7 mx-1 border-0 focus:outline-none bg-transparent' type="text" defaultValue="untitled" step="0.1" ref={ref} /> <div className='w-16 text-gray-50 h-7 mx-1 border-0 focus:outline-none bg-transparent'>
{fileHandle ? fileHandle.name.replace(/\.[^/.]+$/, "") : 'untitled'}
</div>
{treeEntries.allIds.map((entId, idx) => ( {treeEntries.allIds.map((entId, idx) => (
<TreeEntry key={idx} entId={entId} /> <TreeEntry key={idx} entId={entId} />
))} ))}
@ -55,7 +57,11 @@ const TreeEntry = ({ entId }) => {
dispatch({ type: 'finish-sketch' }) dispatch({ type: 'finish-sketch' })
sc.activeSketch.deactivate() sc.activeSketch.deactivate()
} }
sketch.activate() sketch.activate()
dispatch({ type: 'set-active-sketch', sketch })
sc.clearSelection() sc.clearSelection()
sc.activeSketch = sketch; sc.activeSketch = sketch;
dispatch({ type: 'set-dialog', action: 'sketch' }) dispatch({ type: 'set-dialog', action: 'sketch' })
@ -120,14 +126,6 @@ const TreeEntry = ({ entId }) => {
e.stopPropagation() e.stopPropagation()
}} }}
/> />
{/* <MdRefresh className='btn-green h-full w-auto p-1.5'
onClick={(e) => {
e.stopPropagation()
sc.refreshNode(entId)
sc.render()
}}
/> */}
{ {
visible ? visible ?

View File

@ -36,6 +36,7 @@ auto update extrude // done
extrude edit dialog // done extrude edit dialog // done
file save, stl export// done file save, stl export// done
-unable cancel out of new sketches //fixed seemingly
-sometimes unable to hit return and change dimensionk -sometimes unable to hit return and change dimensionk
-unable to delete arc -unable to delete arc