working dependency tree

master
howard 2021-04-05 21:52:19 -07:00
parent 40a692ed6a
commit 3b49000e2b
11 changed files with 424 additions and 132 deletions

13
package-lock.json generated
View File

@ -10,6 +10,7 @@
"babel-loader": "^8.2.2",
"css-loader": "^5.1.3",
"gh-pages": "^3.1.0",
"immutability-helper": "^3.1.1",
"mini-css-extract-plugin": "^1.4.0",
"postcss": "^8.2.9",
"postcss-loader": "^5.2.0",
@ -3633,6 +3634,12 @@
"postcss": "^8.1.0"
}
},
"node_modules/immutability-helper": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-3.1.1.tgz",
"integrity": "sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ==",
"dev": true
},
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -11406,6 +11413,12 @@
"dev": true,
"requires": {}
},
"immutability-helper": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-3.1.1.tgz",
"integrity": "sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ==",
"dev": true
},
"import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",

View File

@ -11,6 +11,7 @@
"babel-loader": "^8.2.2",
"css-loader": "^5.1.3",
"gh-pages": "^3.1.0",
"immutability-helper": "^3.1.1",
"mini-css-extract-plugin": "^1.4.0",
"postcss": "^8.2.9",
"postcss-loader": "^5.2.0",

View File

@ -5,8 +5,7 @@ import './app.css'
import { Provider, useDispatch, useSelector } from 'react-redux'
import { FaCube, FaEdit } from 'react-icons/fa'
import { MdEdit, MdDone, MdVisibilityOff, MdVisibility } from 'react-icons/md'
import { RiShape2Fill } from 'react-icons/ri'
import { MdEdit, MdDone, MdVisibilityOff, MdVisibility, MdDelete } from 'react-icons/md'
import * as Icon from "./icons";
import { color } from './utils/shared'
@ -22,16 +21,11 @@ export const Root = ({ store }) => (
const App = () => {
const dispatch = useDispatch()
const treeEntries = useSelector(state => state.treeEntries)
const activeSketchNid = useSelector(state => state.activeSketchNid)
// const [state, setState] = useState('x')
// useEffect(()=>{
// console.log('hereeee')
// },[state])
const activeSketchId = useSelector(state => state.activeSketchId)
useEffect(() => {
if (!activeSketchNid) {
if (!activeSketchId) {
sc.canvas.addEventListener('pointermove', sc.onHover)
sc.canvas.addEventListener('pointerdown', sc.onPick)
return () => {
@ -39,27 +33,38 @@ const App = () => {
sc.canvas.removeEventListener('pointerdown', sc.onPick)
}
}
}, [activeSketchNid])
}, [activeSketchId])
const btnz = [
activeSketchNid ?
activeSketchId ?
[MdDone, () => {
treeEntries.byNid[activeSketchNid].deactivate()
treeEntries.byId[activeSketchId].deactivate()
sc.activeSketch = null
// sc.activeDim = this.activeSketch.obj3d.children[1].children
}, 'Finish'] :
[FaEdit, sc.addSketch, 'Sketch']
,
[FaCube, () => sc.extrude(treeEntries.byNid[activeSketchNid]), 'Extrude'],
[Icon.Union, () => sc.extrude(treeEntries.byNid[activeSketchNid]), 'Union'],
[Icon.Subtract, subtract, 'Subtract'],
[Icon.Intersect, () => sc.extrude(treeEntries.byNid[activeSketchNid]), 'Intersect'],
[Icon.Dimension, () => sc.extrude(treeEntries.byNid[activeSketchNid]), 'Dimension'],
[Icon.Line, () => sc.extrude(treeEntries.byNid[activeSketchNid]), 'Line'],
[Icon.Arc, () => sc.extrude(treeEntries.byNid[activeSketchNid]), 'Arc'],
[FaCube, () => sc.extrude(treeEntries.byId[activeSketchId]), 'Extrude'],
[Icon.Union, () => sc.extrude(treeEntries.byId[activeSketchId]), 'Union'],
[Icon.Subtract, ()=> {
if (sc.selected.length != 2 || !sc.selected.every(e => e.userData.type == 'mesh')) return
// console.log('here')
const [m1, m2] = sc.selected
const mesh = subtract(m1,m2)
dispatch({ type: 'rx-extrusion', mesh, deps:[m1.name,m2.name] })
sc.render()
forceUpdate()
}, 'Subtract'],
[Icon.Intersect, () => sc.extrude(treeEntries.byId[activeSketchId]), 'Intersect'],
[Icon.Dimension, () => sc.extrude(treeEntries.byId[activeSketchId]), 'Dimension'],
[Icon.Line, () => sc.extrude(treeEntries.byId[activeSketchId]), 'Line'],
[Icon.Arc, () => sc.extrude(treeEntries.byId[activeSketchId]), 'Arc'],
]
const [_, forceUpdate] = useReducer(x => x + 1, 0);
return <div className='absolute left-0 w-40 flex flex-col'>
{
btnz.map(([Icon, fcn, txt], idx) => (
@ -73,7 +78,7 @@ const App = () => {
}
<div className=''>
{treeEntries.allNids.map((entId, idx) => (
{treeEntries.allIds.map((entId, idx) => (
<TreeEntry key={idx} entId={entId} />
))}
</div>
@ -84,9 +89,10 @@ const App = () => {
const TreeEntry = ({ entId }) => {
const treeEntries = useSelector(state => state.treeEntries.byNid)
const treeEntries = useSelector(state => state.treeEntries.byId)
const dispatch = useDispatch()
const activeSketchNid = useSelector(state => state.activeSketchNid)
const activeSketchId = useSelector(state => state.activeSketchId)
let obj3d, entry;
@ -105,7 +111,7 @@ const TreeEntry = ({ entId }) => {
return <div className='bg-gray-50 flex justify-between w-full'>
<div className='btn'
onClick={() => {
activeSketchNid && treeEntries[activeSketchNid].deactivate()
activeSketchId && treeEntries[activeSketchId].deactivate()
entry.activate()
sc.activeSketch = entry;
}}
@ -113,6 +119,13 @@ const TreeEntry = ({ entId }) => {
<MdEdit />
</div>
<div className='btn'
onClick={() => {
dispatch({type:'delete-node',id:entId})
}}
>
<MdDelete/>
</div>
{
vis ?
<div className='btn'
@ -165,12 +178,10 @@ const TreeEntry = ({ entId }) => {
}
const subtract = () => {
const subtract = (m1,m2) => {
// //Create a bsp tree from each of the meshes
// console.log(sc.selected.length != 2 || !sc.selected.every(e => e.userData.type == 'mesh'), "wtf")
if (sc.selected.length != 2 || !sc.selected.every(e => e.userData.type == 'mesh')) return
// console.log('here')
const [m1, m2] = sc.selected
let bspA = BoolOp.fromMesh(m1)
let bspB = BoolOp.fromMesh(m2)
@ -184,9 +195,12 @@ const subtract = () => {
// //Get the resulting mesh from the result bsp, and assign meshA.material to the resulting mesh
let meshResult = BoolOp.toMesh(bspResult, m1.matrix, m1.material)
meshResult.userData.type = 'mesh'
meshResult.userData.name = `${m1.name}-${m2.name}`
sc.obj3d.add(meshResult)
sc.render()
return meshResult
}

102
src/depTree copy.js Normal file
View File

@ -0,0 +1,102 @@
class DepTree {
constructor() {
this.tree = {}
this.order = {}
this.arr = []
}
addParent(id) {
if (this.tree[id]) return
this.tree[id] = new Set()
this.order[id] = this.arr.length
this.arr.push(id)
}
addChild(childId, ...parentIds) {
for (let parentId of parentIds) {
this.tree[parentId].add(childId)
}
this.addParent(childId)
}
deleteNode(id) {
this.visited = new Set()
this.nodesToDel = []
this.dfs(id)
const idx = []
this.nodesToDel.sort((a, b) => this.order[b] - this.order[a])
for (let id of this.nodesToDel) {
idx.push(this.order[id])
this.arr.splice(this.order[id], 1)
delete this.tree[id]
delete this.order[id]
}
for (let i = idx[idx.length - 1]; i < this.arr.length; i++) {
this.order[this.arr[i]] = i
}
const nodeToDelSet = new Set(this.nodesToDel)
for (let k in this.tree) {
for (let ent of this.tree[k]) {
if (nodeToDelSet.has(ent)) {
this.tree[k].delete(ent)
}
}
}
return idx
}
dfs(id) {
this.visited.add(id)
this.nodesToDel.push(id)
for (let x of this.tree[id]) {
if (!this.visited.has(x)) {
this.dfs(x)
}
}
}
}
const dt = new DepTree()
dt.addParent('r1')
dt.addParent('r2')
dt.addChild('r3', 'r1', 'r2')
dt.addParent('r4')
dt.addChild('r5', 'r4', 'r3')
dt.addChild('r6', 'r1', 'r5')
dt.addChild('r7', 'r3', 'r5')
// const x = dt.deleteNode('r3')
// console.log(x)
// console.log(dt.arr, dt.order)
// expectd output
// [ 6, 5, 4, 2 ]
// [ 'r1', 'r2', 'r4' ] { r1: 0, r2: 1, r4: 2 }
const x = dt.deleteNode('r5')
console.log(x)
console.log(dt.tree)

120
src/depTree.mjs Normal file
View File

@ -0,0 +1,120 @@
export class DepTree {
constructor(obj) {
if (obj) {
console.log('load', obj)
this.order = { ...obj.order }
this.byId = { ...obj.byId }
this.allIds = [...obj.allIds]
this.tree = {}
for (let k in obj.tree) {
this.tree[k] = { ...obj.tree[k] }
}
} else {
this.tree = {}
this.order = {}
this.allIds = []
}
}
addParent(id) {
if (this.tree[id]) return
this.tree[id] = {}
this.order[id] = this.allIds.length
this.allIds.push(id)
}
addChild(childId, ...parentIds) {
for (let parentId of parentIds) {
this.tree[parentId][childId] = true;
}
this.addParent(childId)
}
deleteNode(id) {
const dfs = (id) => {
visited.add(id)
nodesToDel.push(id)
for (let k in this.tree[id]) {
if (!visited.has(k)) {
dfs(k)
}
}
}
const visited = new Set()
const nodesToDel = []
dfs(id)
nodesToDel.sort((a, b) => this.order[b] - this.order[a])
let spliceIdx;
for (let id of nodesToDel) {
spliceIdx = this.order[id]
this.allIds.splice(spliceIdx, 1)
const deletedObj = sc.obj3d.children.splice(spliceIdx + 4, 1)[0]
deletedObj.traverse((obj)=>{
if (obj.geometry) obj.geometry.dispose()
if (obj.material) obj.material.dispose()
})
delete this.tree[id]
delete this.byId[id]
delete this.order[id]
}
for (let i = spliceIdx; i < this.allIds.length; i++) {
this.order[this.allIds[i]] = i
}
const nodeToDelSet = new Set(nodesToDel)
for (let k in this.tree) {
for (let m in this.tree[k]) {
if (nodeToDelSet.has(m)) {
delete this.tree[k][m]
}
}
}
return this
}
}
// const dt = new DepTree()
// dt.addParent('r1')
// dt.addParent('r2')
// dt.addChild('r3', 'r1', 'r2')
// dt.addParent('r4')
// dt.addChild('r5', 'r4', 'r3')
// dt.addChild('r6', 'r1', 'r5')
// dt.addChild('r7', 'r3', 'r5')
// dt.addParent('r8')
// // console.log(dt)
// // const x = dt.deleteNode('r3')
// // console.log(x)
// // console.log(dt.allIds, dt.order)
// // [ 6, 5, 4, 2 ]
// // [ 'r1', 'r2', 'r4' ] { r1: 0, r2: 1, r4: 2 }
// const x = dt.deleteNode('r5')
// console.log(dt)
// DepTree {
// tree: { r1: { r3: true }, r2: { r3: true }, r3: {}, r4: {}, r8: {} },
// order: { r1: 0, r2: 1, r3: 2, r4: 3, r8: 4 },
// allIds: [ 'r1', 'r2', 'r3', 'r4', 'r8' ]
// }

View File

@ -98,7 +98,7 @@ export function extrude(sketch) {
this.render()
// sketch.visible = false
this.store.dispatch({ type: 'rx-extrusion', mesh, sketch })
this.store.dispatch({ type: 'rx-extrusion', mesh, sketchId: sketch.obj3d.name })
}

View File

@ -2,43 +2,64 @@
import ReactDOM from 'react-dom'
import React from 'react'
import { Root } from './app.jsx'
import update from 'immutability-helper';
import { createStore, applyMiddleware } from 'redux'
import logger from 'redux-logger'
let _entId = 0
import { DepTree } from './depTree.mjs'
function reducer(state = {}, action) {
switch (action.type) {
case 'rx-sketch':
return {
...state,
return update(state, {
treeEntries: {
byNid: { ...state.treeEntries.byNid, [action.obj.obj3d.name]: action.obj },
allNids: [...state.treeEntries.allNids, action.obj.obj3d.name]
}
}
byId: { [action.obj.obj3d.name]: { $set: action.obj } },
allIds: { $push: [action.obj.obj3d.name] },
tree: { [action.obj.obj3d.name]: { $set: {} } },
order: { [action.obj.obj3d.name]: { $set: state.treeEntries.allIds.length } }
},
})
case 'set-active-sketch':
return {
...state, activeSketchNid: action.sketch
}
return update(state, {
activeSketchId: { $set: action.sketch },
})
case 'exit-sketch':
return {
...state, activeSketchNid: ''
...state, activeSketchId: ''
}
case 'rx-extrusion':
return {
...state,
return update(state, {
treeEntries: {
byNid: { ...state.treeEntries.byNid, [action.mesh.name]: action.mesh },
allNids: [...state.treeEntries.allNids, action.mesh.name]
byId: {
[action.mesh.name]: { $set: action.mesh }
},
mesh2sketch: {
...state.mesh2sketch,
[action.sketch.obj3d.name]: action.mesh.name
}
allIds: { $push: [action.mesh.name] },
tree: {
[action.sketchId]: { [action.mesh.name]: { $set: true } },
},
order: { [action.mesh.name]: { $set: state.treeEntries.allIds.length } }
}
})
case 'delete-node':
const depTree = new DepTree(state.treeEntries)
const obj = depTree.deleteNode(action.id)
return update(state, {
treeEntries: {$set: obj}
})
case 'restore-state':
return action.state
default:
@ -48,12 +69,13 @@ function reducer(state = {}, action) {
const preloadedState = {
treeEntries: {
byNid: {},
allNids: []
}
byId: {},
allIds: [],
tree: {},
order: {},
},
}

View File

@ -9,7 +9,7 @@ import { get3PtArc } from './drawArc'
import { _vec2, _vec3, raycaster, awaitPts } from '../utils/shared'
import { replacer, reviver } from '../utils/mapJSONReplacer'
import { AxesHelper } from '../utils/axes'
import { drawDimension, _onMoveDimension, setDimLines } from './drawDimension';
import { drawDimension, _onMoveDimension, setDimLines, updateDim } from './drawDimension';
@ -114,6 +114,7 @@ class Sketch {
this.drawDimension = drawDimension.bind(this)
this._onMoveDimension = _onMoveDimension.bind(this)
this.setDimLines = setDimLines.bind(this)
this.updateDim = updateDim.bind(this)
this.awaitPts = awaitPts.bind(this);

View File

@ -87,29 +87,9 @@ export async function drawDimension() {
point.name = this.c_id
point.userData.type = 'dimension'
const updateDim = (c_id) => (ev_focus) => {
const value = ev_focus.target.textContent
console.log(value)
document.addEventListener('keydown', (e) => {
if (e.key == 'Enter') {
e.preventDefault()
const ent = this.constraints.get(c_id)
ent[1] = parseFloat(ev_focus.target.textContent)
this.constraints.set(c_id, ent)
this.updateOtherBuffers()
this.solve()
sc.render()
ev_focus.target.blur()
this.updateBoundingSpheres()
} else if (e.key == 'Escape') {
ev_focus.target.textContent = value
getSelection().empty()
ev_focus.target.blur()
}
})
}
point.label.addEventListener('focus', updateDim(this.c_id))
point.label.addEventListener('focus', this.updateDim(this.c_id))
@ -133,12 +113,39 @@ const p2 = new THREE.Vector2()
const p3 = new THREE.Vector2()
let dir, hyp, proj, perp, p1e, p2e, nids, _p1, _p2;
export function updateDim(c_id) {
return (ev_focus) => {
const value = ev_focus.target.textContent
document.addEventListener('keydown', (e) => {
if (e.key == 'Enter') {
e.preventDefault()
const ent = this.constraints.get(c_id)
ent[1] = parseFloat(ev_focus.target.textContent)
this.constraints.set(c_id, ent)
this.updateOtherBuffers()
this.solve()
sc.render()
ev_focus.target.blur()
this.updateBoundingSpheres()
} else if (e.key == 'Escape') {
ev_focus.target.textContent = value
getSelection().empty()
ev_focus.target.blur()
}
})
}
}
export function _onMoveDimension(point, line) {
nids = line.userData.nids
_p1 = this.obj3d.children[sketcher.objIdx.get(nids[0])].geometry.attributes.position.array
_p2 = this.obj3d.children[sketcher.objIdx.get(nids[1])].geometry.attributes.position.array
_p1 = this.obj3d.children[this.objIdx.get(nids[0])].geometry.attributes.position.array
_p2 = this.obj3d.children[this.objIdx.get(nids[1])].geometry.attributes.position.array
p1.set(_p1[0], _p1[1])
p2.set(_p2[0], _p2[1])
@ -163,27 +170,6 @@ export function _onMoveDimension(point, line) {
export function setDimLines() {
const updateDim = (c_id) => (ev_focus) => {
const value = ev_focus.target.textContent
console.log(value)
document.addEventListener('keydown', (e) => {
if (e.key == 'Enter') {
e.preventDefault()
const ent = this.constraints.get(c_id)
ent[1] = parseFloat(ev_focus.target.textContent)
this.constraints.set(c_id, ent)
this.updateOtherBuffers()
this.solve()
sc.render()
ev_focus.target.blur()
this.updateBoundingSpheres()
} else if (e.key == 'Escape') {
ev_focus.target.textContent = value
getSelection().empty()
ev_focus.target.blur()
}
})
}
const restoreLabels = this.labelContainer.childElementCount == 0;
@ -201,17 +187,14 @@ export function setDimLines() {
point.label.contentEditable = true;
this.labelContainer.append(point.label)
point.label.addEventListener('focus', updateDim(this.c_id))
point.label.addEventListener('focus', this.updateDim(this.c_id))
}
nids = dims[i].userData.nids
_p1 = this.obj3d.children[sketcher.objIdx.get(nids[0])].geometry.attributes.position.array
_p2 = this.obj3d.children[sketcher.objIdx.get(nids[1])].geometry.attributes.position.array
_p1 = this.obj3d.children[this.objIdx.get(nids[0])].geometry.attributes.position.array
_p2 = this.obj3d.children[this.objIdx.get(nids[1])].geometry.attributes.position.array
const offset = dims[i + 1].userData.offset

View File

@ -13,20 +13,11 @@ export function onHover(e) {
);
let hoverPts;
let idx = []
if (this.obj3d.userData.type == 'sketch') {
hoverPts = raycaster.intersectObjects([...this.obj3d.children[1].children, ...this.obj3d.children])
// if (!hoverPts.length) {
// hoverPts = raycaster.intersectObjects(this.obj3d.children)
// }
} else {
hoverPts = raycaster.intersectObjects(this.obj3d.children, true)
}
// if (hoverDim.length) {
// }
let idx = []
if (hoverPts.length) {
let minDist = Infinity;
for (let i = 0; i < hoverPts.length; i++) {
@ -42,6 +33,31 @@ export function onHover(e) {
if (!idx.length) idx.push(0)
}
} else {
// hoverPts = raycaster.intersectObjects(this.obj3d.children)
hoverPts = raycaster.intersectObjects(this.obj3d.children,true)
// for (let i = 0; i < hoverPts.length; i++) {
// const obj = hoverPts[i].object
// if (obj.userData.type == "mesh" && obj.visible || obj.userData.type == "plane") {
// idx.push(i)
// }
// }
if (hoverPts.length) {
// console.log(hoverPts)
if (!idx.length) idx.push(0)
}
}
if (idx.length) { // after filtering, hovered objs still exists
if (hoverPts[idx[0]].object != this.hovered[0]) { // if the previous hovered obj is not the same as current

View File

@ -1,11 +1,31 @@
fix css on design tree (a lot of work) \
clear dim on exit exit sketch / rehydrate when back or after loading
boolean flesh out refresh / replace mesh
dimension to origin
clear dim on exit exit sketch / rehydrate when back or after loading \\\ done
boolean flesh out refresh / replace mesh / delete mesh
- need to auto hide ( consume) when new boolean created
- create derived part using relationship as name
- sensible default names, like extrude 1, sketch 1, leverage react for this
- hidden bodies messes up hover highlight
extrude edit dialog. directio and magnitude
fix extrusion loop find
dimension to origin
reattaching sketch
file save
stl export
angle
other constraints / sprite
finish mode messed up after restore