diff --git a/dist/solver.wasm b/dist/solver.wasm index a4a67f0..29d8128 100755 Binary files a/dist/solver.wasm and b/dist/solver.wasm differ diff --git a/src/Scene.js b/src/Scene.js index f06ea0f..da467c9 100644 --- a/src/Scene.js +++ b/src/Scene.js @@ -6,7 +6,7 @@ import { TrackballControls } from '../lib/trackball' import { Sketch } from './Sketch' import Stats from '../lib/stats.module.js'; -import { extrude } from './extrude' +import { extrude, flipBufferGeometryNormals } from './extrude' import { onHover, onPick } from './mouseEvents'; import { _vec2, _vec3, color, awaitSelection, ptObj, setHover } from './shared' @@ -26,9 +26,19 @@ const eq = (a1, a2) => { window.loader = new THREE.ObjectLoader(); window.id = 0 +window.sid = 1 +window.mid = 1 + + +const pointMaterial = new THREE.PointsMaterial({ + color: color.selpoint, + size: 4, +}) export class Scene { constructor(store) { + this.sid = 1 + this.mid = 1 this.store = store; this.canvas = document.querySelector('#c'); @@ -63,10 +73,14 @@ export class Scene { for (let i = 0; i < 4; i++) { - const freePt = ptObj() + const freePt = new THREE.Points( + new THREE.BufferGeometry().setAttribute('position', + new THREE.Float32BufferAttribute(3, 3) + ), + pointMaterial.clone() + ) + freePt.matrixAutoUpdate = false - freePt.material.size = 4 - freePt.material.color.set(color.selpoint) freePt.visible = false freePt.depthTest = false freePt.userData.type = 'selpoint' @@ -160,37 +174,49 @@ export class Scene { saveState() { localStorage.setItem( - 'sv', JSON.stringify([id, this.store.getState()]) + 'sv2', JSON.stringify([id, this.sid, this.mid, this.store.getState().treeEntries]) ) } loadState() { //uglyyy - const [curid, state] = JSON.parse( - localStorage.getItem('sv') + const [curid, cursid, curmid, state] = JSON.parse( + localStorage.getItem('sv2') ) window.id = curid + this.sid = cursid + this.mid = curmid - const entries = state.treeEntries.byId + const entries = state.byId for (let k in entries) { if (k[0] == 's') { entries[k].obj3d = loader.parse(entries[k].obj3d) this.obj3d.add(entries[k].obj3d) - entries[k] = new Sketch(this, state.treeEntries.byId[k]) + entries[k] = new Sketch(this, state.byId[k]) entries[k].obj3d.addEventListener('change', this.render) // !! took 3 hours to realize - } else if (k[0] == 'm') { + } else if (k[0] == 'e') { + + entries[k] = loader.parse(state.byId[k]) + + if (entries[k].userData.inverted) { + flipBufferGeometryNormals(entries[k].geometry) + } + + this.obj3d.add(entries[k]) + + } else { + entries[k] = loader.parse(state.byId[k]) - entries[k] = loader.parse(state.treeEntries.byId[k]) - // console.log(entries[k]) this.obj3d.add(entries[k]) } } this.store.dispatch({ type: 'restore-state', state }) + return state } clearSelection() { @@ -244,8 +270,9 @@ export class Scene { let mesh = CSG.toMesh(bspResult, m1.matrix, m1.material) mesh.userData.type = 'mesh' + mesh.userData.featureInfo = [m1.name, m2.name, op] - mesh.name = `(${m1.name}${opChar}${m2.name})` + mesh.name = `(${m1.name} ${opChar} ${m2.name})` mesh.layers.enable(1) const vertices = new THREE.Points(mesh.geometry, new THREE.PointsMaterial({ size: 0 })); @@ -288,7 +315,7 @@ function render() { this.renderer.render(this.obj3d, this.camera); if (this.activeSketch) { - dims = this.activeSketch.obj3d.children[1].children + dims = this.activeSketch.dimGroup.children matrix = this.activeSketch.obj3d.matrix for (idx = 1; idx < dims.length; idx += 2) { @@ -347,7 +374,7 @@ async function addSketch() { } window.sc = new Scene(store) -// sc.loadState() +sc.loadState() // sc.camera.layers.enable(1) // rc.layers.set(1) \ No newline at end of file diff --git a/src/Sketch.js b/src/Sketch.js index 09fcece..bece5c6 100644 --- a/src/Sketch.js +++ b/src/Sketch.js @@ -5,7 +5,7 @@ import * as THREE from '../node_modules/three/src/Three'; import { _vec2, _vec3, raycaster, awaitSelection, ptObj, setHover } from './shared' import { drawOnClick1, drawOnClick2, drawPreClick2, drawOnClick3, drawPreClick3, drawClear, drawPoint } from './drawEvents' -import { onHover, onDrag, onPick, onRelease} from './mouseEvents' +import { onHover, onDrag, onPick, onRelease } from './mouseEvents' import { setCoincident, setOrdinate, setTangent } from './constraintEvents' import { get3PtArc } from './drawArc' import { replacer, reviver } from './utils' @@ -37,7 +37,7 @@ class Sketch { if (preload === undefined) { this.obj3d = new THREE.Group() - this.obj3d.name = "s" + id++ + this.obj3d.name = "s" + scene.sid++ this.obj3d.userData.type = "sketch" this.obj3d.matrixAutoUpdate = false; @@ -50,8 +50,10 @@ class Sketch { this.c_id = 1; this.obj3d.add(new THREE.Group()); - this.obj3d.add(new THREE.Group()); - this.obj3d.add(new THREE.Group()); + + this.geomStartIdx = this.obj3d.children.length + this.obj3d.userData.geomStartIdx = this.geomStartIdx + this.dimGroup = this.obj3d.children[this.geomStartIdx - 1] this.labels = [] @@ -80,6 +82,8 @@ class Sketch { this.constraints = JSON.parse(preload.constraints, reviver) this.c_id = preload.c_id; + this.geomStartIdx = this.obj3d.userData.geomStartIdx + this.dimGroup = this.obj3d.children[this.geomStartIdx - 1] this.updatePointsBuffer() this.updateOtherBuffers() @@ -385,13 +389,14 @@ class Sketch { updateBoundingSpheres() { - for (let x = 3; x < this.obj3d.children.length; x++) { // geometry boundign spheres + + for (let x = this.geomStartIdx; x < this.obj3d.children.length; x++) { // geometry boundign spheres const obj = this.obj3d.children[x] obj.geometry.computeBoundingSphere() } - for (let x = 0; x < this.obj3d.children[1].children.length; x++) { // dimension bounding sphere - const obj = this.obj3d.children[1].children[x] + for (let x = 0; x < this.dimGroup.children.length; x++) { // dimension bounding sphere + const obj = this.dimGroup.children[x] obj.geometry.computeBoundingSphere() } } @@ -432,16 +437,16 @@ class Sketch { Module["_solver"]( this.obj3d.children.length, pts_buffer, this.constraints.size, constraints_buffer, - this.linkedObjs.size, links_buffer) + this.linkedObjs.size, links_buffer, + this.geomStartIdx + ) /* - loop to update all the children that are points - - why +6? we skip first two triplets because it refers to a non-geometry children - we also sneak in updating lines children as well, by checking when ptsBuf[ptr] is NaN */ - for (let i = 3, ptr = (pts_buffer >> 2) + 9; i < this.obj3d.children.length; i += 1, ptr += 3) { - // for (let i = 0, ptr = (pts_buffer >> 2) + 3; i < this.obj3d.children.length; i += 1, ptr += 3) { + for (let i = this.geomStartIdx, ptr = (pts_buffer >> 2) + this.geomStartIdx * 3; i < this.obj3d.children.length; i += 1, ptr += 3) { const pos = this.obj3d.children[i].geometry.attributes.position; if (isNaN(Module.HEAPF32[ptr])) { diff --git a/src/drawDimension.js b/src/drawDimension.js index 5d357f0..ea7b5fe 100644 --- a/src/drawDimension.js +++ b/src/drawDimension.js @@ -92,7 +92,7 @@ export async function drawDimension() { line.layers.enable(2) point.layers.enable(2) - this.obj3d.children[1].add(line).add(point) + this.dimGroup.add(line).add(point) const onMove = this._onMoveDimension(point, line) point.label = document.createElement('div'); point.label.textContent = dimVal.toFixed(3); @@ -138,7 +138,7 @@ export async function drawDimension() { } else { - this.obj3d.children[1].children.splice(this.obj3d.children[1].length - 2, 2).forEach( + this.dimGroup.children.splice(this.dimGroup.length - 2, 2).forEach( e => { e.geometry.dispose() e.material.dispose() @@ -209,7 +209,7 @@ export function _onMoveDimension(point, line) { export function setDimLines() { const restoreLabels = this.labelContainer.childElementCount == 0; - const dims = this.obj3d.children[1].children + const dims = this.dimGroup.children let point, dist; for (let i = 0; i < dims.length; i += 2) { if (restoreLabels) { diff --git a/src/extrude.js b/src/extrude.js index 9c68abf..665c5ec 100644 --- a/src/extrude.js +++ b/src/extrude.js @@ -1,7 +1,7 @@ import * as THREE from '../node_modules/three/src/Three'; -import { color} from './shared' +import { color } from './shared' export function extrude(sketch, depth) { - console.log(sketch,'here') + console.log(sketch, 'here') let constraints = sketch.constraints; let linkedObjs = sketch.linkedObjs; @@ -68,20 +68,43 @@ export function extrude(sketch, depth) { findPair(children[4]) //??? need fixing const shape = new THREE.Shape(v2s); + // const extrudeSettings = { depth: Math.abs(depth), bevelEnabled: false }; const extrudeSettings = { depth, bevelEnabled: false }; const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); // const material = new THREE.MeshLambertMaterial({ - const material = new THREE.MeshPhongMaterial({ - color: color.mesh, - emissive: color.emissive, - }); + // const material = new THREE.MeshPhongMaterial({ + // color: color.mesh, + // emissive: color.emissive, + // }); - const mesh = new THREE.Mesh(geometry, material) - mesh.name = 'm' + id++ + // const mesh = new THREE.Mesh(geometry, material) + + // const material = new THREE.MeshPhongMaterial({ + // color: color.mesh, + // }); + // const wireframe = new THREE.EdgesGeometry( geometry ); + // const mesh = new THREE.LineSegments( wireframe ); + // // mesh.material.depthTest = false; + // // mesh.material.opacity = 0.25; + // // mesh.material.transparent = true; + // mesh.material.transparent = false; + + const edges = new THREE.EdgesGeometry( geometry, 15 ); + edges.type = 'BufferGeometry' + edges.parameters = undefined + + const mesh = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( { color: 0x000000 } ) ); + + + + mesh.name = 'e' + this.mid++ + // mesh.name = 'm' + sketch.obj3d.name.slice(1) + // mesh.name = 'e' + sketch.obj3d.name.slice(1) mesh.userData.type = 'mesh' + mesh.userData.featureInfo = [sketch.obj3d.name, depth] mesh.layers.enable(1) const vertices = new THREE.Points(mesh.geometry, new THREE.PointsMaterial({ size: 0 })); @@ -89,13 +112,27 @@ export function extrude(sketch, depth) { vertices.layers.enable(1) mesh.add(vertices) + + + + + + + mesh.matrixAutoUpdate = false; mesh.matrix.multiply(sketch.obj3d.matrix) + if (depth < 0) { + flipBufferGeometryNormals(mesh.geometry) + mesh.userData.inverted = true + } + + this.obj3d.add(mesh) this.store.dispatch({ type: 'rx-extrusion', mesh, sketchId: sketch.obj3d.name }) + // sketch.userData if (this.activeSketch == sketch) { this.activeSketch = null sketch.deactivate() @@ -107,3 +144,75 @@ export function extrude(sketch, depth) { + + + +export function flipBufferGeometryNormals(geometry) { + //https://stackoverflow.com/a/54496265 + const tempXYZ = [0, 0, 0]; + + // flip normals + for (let i = 0; i < geometry.attributes.normal.array.length / 9; i++) { + // cache a coordinates + tempXYZ[0] = geometry.attributes.normal.array[i * 9]; + tempXYZ[1] = geometry.attributes.normal.array[i * 9 + 1]; + tempXYZ[2] = geometry.attributes.normal.array[i * 9 + 2]; + + // overwrite a with c + geometry.attributes.normal.array[i * 9] = + geometry.attributes.normal.array[i * 9 + 6]; + geometry.attributes.normal.array[i * 9 + 1] = + geometry.attributes.normal.array[i * 9 + 7]; + geometry.attributes.normal.array[i * 9 + 2] = + geometry.attributes.normal.array[i * 9 + 8]; + + // overwrite c with stored a values + geometry.attributes.normal.array[i * 9 + 6] = tempXYZ[0]; + geometry.attributes.normal.array[i * 9 + 7] = tempXYZ[1]; + geometry.attributes.normal.array[i * 9 + 8] = tempXYZ[2]; + } + + // change face winding order + for (let i = 0; i < geometry.attributes.position.array.length / 9; i++) { + // cache a coordinates + tempXYZ[0] = geometry.attributes.position.array[i * 9]; + tempXYZ[1] = geometry.attributes.position.array[i * 9 + 1]; + tempXYZ[2] = geometry.attributes.position.array[i * 9 + 2]; + + // overwrite a with c + geometry.attributes.position.array[i * 9] = + geometry.attributes.position.array[i * 9 + 6]; + geometry.attributes.position.array[i * 9 + 1] = + geometry.attributes.position.array[i * 9 + 7]; + geometry.attributes.position.array[i * 9 + 2] = + geometry.attributes.position.array[i * 9 + 8]; + + // overwrite c with stored a values + geometry.attributes.position.array[i * 9 + 6] = tempXYZ[0]; + geometry.attributes.position.array[i * 9 + 7] = tempXYZ[1]; + geometry.attributes.position.array[i * 9 + 8] = tempXYZ[2]; + } + + // flip UV coordinates + for (let i = 0; i < geometry.attributes.uv.array.length / 6; i++) { + // cache a coordinates + tempXYZ[0] = geometry.attributes.uv.array[i * 6]; + tempXYZ[1] = geometry.attributes.uv.array[i * 6 + 1]; + + // overwrite a with c + geometry.attributes.uv.array[i * 6] = + geometry.attributes.uv.array[i * 6 + 4]; + geometry.attributes.uv.array[i * 6 + 1] = + geometry.attributes.uv.array[i * 6 + 5]; + + // overwrite c with stored a values + geometry.attributes.uv.array[i * 6 + 4] = tempXYZ[0]; + geometry.attributes.uv.array[i * 6 + 5] = tempXYZ[1]; + } + + geometry.attributes.normal.needsUpdate = true; + geometry.attributes.position.needsUpdate = true; + geometry.attributes.uv.needsUpdate = true; +} + + diff --git a/src/mouseEvents.js b/src/mouseEvents.js index 45a2e04..213c8d0 100644 --- a/src/mouseEvents.js +++ b/src/mouseEvents.js @@ -24,7 +24,7 @@ export function onHover(e) { } else { // raycaster.layers.set(0) raycaster.layers.set(2) - hoverPts = raycaster.intersectObjects([...this.obj3d.children[1].children, ...this.obj3d.children]) + hoverPts = raycaster.intersectObjects([...this.dimGroup.children, ...this.obj3d.children]) } @@ -171,27 +171,26 @@ export function onPick(e) { switch (obj.userData.type) { case 'dimension': - const idx = this.obj3d.children[1].children.indexOf(this.hovered[0]) + const idx = this.dimGroup.children.indexOf(this.hovered[0]) if (idx % 2) { // we only allow tag point (odd idx) to be dragged this.onDragDim = this._onMoveDimension( - this.obj3d.children[1].children[idx], - this.obj3d.children[1].children[idx - 1], + this.dimGroup.children[idx], + this.dimGroup.children[idx - 1], ) this.canvas.addEventListener('pointermove', this.onDragDim); this.canvas.addEventListener('pointerup', () => { - onDimMoveEnd(this.obj3d.children[1].children[idx]) + onDimMoveEnd(this.dimGroup.children[idx]) this.onRelease() }) } - draggedLabel = this.obj3d.children[1].children[idx].label + draggedLabel = this.dimGroup.children[idx].label draggedLabel.style.zIndex = -1; break; case 'point': this.canvas.addEventListener('pointermove', this.onDrag); this.canvas.addEventListener('pointerup', () => { - // onDimMoveEnd(this.obj3d.children[1].children[idx]) this.onRelease() }) break; diff --git a/src/react/app.css b/src/react/app.css index 0326244..0569650 100644 --- a/src/react/app.css +++ b/src/react/app.css @@ -86,4 +86,22 @@ body { border: solid 6px transparent; border-bottom-color: white; border-top: none; -} \ No newline at end of file +} + +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +/* Firefox */ +input[type=number] { + -moz-appearance: textfield; + border:none; + background-color:transparent; + outline: none; + text-align:right; +} + +/* input:focus[type=number] { */ +/* } */ diff --git a/src/react/app.jsx b/src/react/app.jsx index 2c84bc1..95afbdd 100644 --- a/src/react/app.jsx +++ b/src/react/app.jsx @@ -26,7 +26,10 @@ const preloadedState = { } // const store = createStore(reducer, preloadedState, applyMiddleware(logger)) + + const store = createStore(reducer, {}, applyMiddleware(logger)) +// const store = createStore(reducer, sc.loadState(), applyMiddleware(logger)) const App = ({ store }) => { const [dialog, setDialog] = useState() @@ -34,7 +37,7 @@ const App = ({ store }) => { return - + }; diff --git a/src/react/dialog.jsx b/src/react/dialog.jsx index f21dbb8..404e0ce 100644 --- a/src/react/dialog.jsx +++ b/src/react/dialog.jsx @@ -9,8 +9,8 @@ import { GiVerticalFlip } from 'react-icons/gi' import * as Icon from "./icons"; -export const Dialog = ({ dd }) => { - if (!dd) return null +export const Dialog = ({ dialog, setDialog }) => { + if (!dialog) return null const dispatch = useDispatch() const treeEntriesById = useSelector(state => state.treeEntries.byId) @@ -24,8 +24,10 @@ export const Dialog = ({ dd }) => { const extrude = () => { if (sc.activeSketch) { sc.extrude(sc.activeSketch, ref.current.value) + setDialog(null) } else if (sc.selected.length === 1 && sc.selected[0].userData.type == 'sketch') { sc.extrude(treeEntriesById[sc.selected[0].name], ref.current.value) + setDialog(null) } else { console.log('invalid selection') } @@ -36,11 +38,16 @@ export const Dialog = ({ dd }) => { return
+ ref.current.value *= -1} + /> - - + /> + setDialog(null)} + />
} diff --git a/src/react/navBar.jsx b/src/react/navBar.jsx index d06935b..a665bd5 100644 --- a/src/react/navBar.jsx +++ b/src/react/navBar.jsx @@ -4,15 +4,13 @@ import React, { useEffect, useReducer } from 'react'; import { useDispatch, useSelector } from 'react-redux' -import { FaCube, FaEdit } from 'react-icons/fa' -import { BsBoxArrowUp } from 'react-icons/bs' +import { FaEdit } from 'react-icons/fa' import { MdDone, MdSave, MdFolder } from 'react-icons/md' import * as Icon from "./icons"; -export const NavBar = ({setDialog}) => { +export const NavBar = ({ setDialog }) => { const dispatch = useDispatch() - const treeEntriesById = useSelector(state => state.treeEntries.byId) const activeSketchId = useSelector(state => state.treeEntries.activeSketchId) @@ -24,16 +22,7 @@ export const NavBar = ({setDialog}) => { sc.render() forceUpdate() } - const extrude = () => { - setDialog('dd') - // if (sc.activeSketch) { - // sc.extrude(sc.activeSketch) - // } else if (sc.selected.length === 1 && sc.selected[0].userData.type == 'sketch') { - // sc.extrude(treeEntriesById[sc.selected[0].name]) - // } else { - // console.log('invalid selection') - // } - } + const extrude = () => { setDialog(true) } const addSketch = () => { sc.addSketch() @@ -59,12 +48,10 @@ export const NavBar = ({setDialog}) => { const btnz = [ [MdDone, () => { - // treeEntriesById[activeSketchId].deactivate() - // dispatch({ type: 'update-descendents', sketch}) sc.activeSketch.deactivate() + // dispatch({ type: 'update-descendents', sketch}) sc.render() forceUpdate() - // sc.activeDim = this.activeSketch.obj3d.children[1].children }, 'Finish'], [Icon.Extrude, extrude, 'Extrude [e]'], [Icon.Dimension, () => sc.activeSketch.command('d'), 'Dimension [d]'], diff --git a/src/react/reducer.js b/src/react/reducer.js index 36fd3af..dd96053 100644 --- a/src/react/reducer.js +++ b/src/react/reducer.js @@ -48,7 +48,7 @@ export function treeEntries(state = defaultState, action) { }, allIds: { $push: [action.mesh.name] }, tree: { - [action.sketchId]: { [action.mesh.name]: { $set: true } }, + [action.sketchId]: { [action.mesh.name]: { $set: true} }, [action.mesh.name]: { $set: {} } }, order: { [action.mesh.name]: { $set: state.allIds.length } }, diff --git a/src/shared.js b/src/shared.js index 696f1da..a94afce 100644 --- a/src/shared.js +++ b/src/shared.js @@ -166,7 +166,7 @@ function setHover(obj, state, meshHover = true) { break; case 'mesh': if (meshHover) { - obj.material.emissive.set(colObj.emissive) + // obj.material.emissive.set(colObj.emissive) } else { break } diff --git a/todo.txt b/todo.txt index 7dc3a7f..e45f9e0 100644 --- a/todo.txt +++ b/todo.txt @@ -25,30 +25,35 @@ constraint angle // done button panel cleanup // done 3 pt arc // done tangent // done to the best of my ability +-boolean highlights when hovering over parent why? because they share the same material +- should unselect after boolean see above + +extrude dialogue / done +better default ent names / done + -unable to delete arc -hover not clearing sometimes +hover not clearing sometimes in sketch dim tags are not clearing -should unselect after boolean - auto update extrude -extrude dialogue loopfind especially arc file save, stl export - -better default ent names +add cancle soft button for line arc reattach sketch auto snap constraint labels,equal parallel + + +tree relation tool tip tree ent renaming @@ -60,6 +65,10 @@ hover state for sketch await selection 3 point arc implementation +saerch tree for loop finding // need dev effor + +dep tree for biuidling design treee + diff --git a/wasm/solver.c b/wasm/solver.c index 22ce85a..465e520 100644 --- a/wasm/solver.c +++ b/wasm/solver.c @@ -33,7 +33,7 @@ static void *CheckMalloc(size_t n) * entities and constraints. *---------------------------------------------------------------------------*/ -int solver(int nPts, float *p_ptr, int nConst, float *c_ptr, int nLinks, float *l_ptr) +int solver(int nPts, float *p_ptr, int nConst, float *c_ptr, int nLinks, float *l_ptr, int geomStartIdx) { // printf("first %i \n", (int)*(l_ptr + 1)); Slvs_hGroup g; @@ -111,7 +111,9 @@ int solver(int nPts, float *p_ptr, int nConst, float *c_ptr, int nLinks, float * SLVS_C_POINTS_COINCIDENT, 200, -1, - 101, 3, -1, -1); + 101, geomStartIdx, -1, -1); + + // it's 2 + nConst because c_id at this point is 2 for (; c_id < 2 + nConst; c_id++, c_ptr += 6) { sys.constraint[sys.constraints++] = Slvs_MakeConstraint(