diff --git a/src/Scene.js b/src/Scene.js index a45d77a..5668dec 100644 --- a/src/Scene.js +++ b/src/Scene.js @@ -9,7 +9,7 @@ import Stats from './stats.module.js'; import { add3DPoint } from './datums' import { extrude } from './extrude' import { onHover, onPick } from './mouseEvents'; -import { _vec2, _vec3, color, awaitSelection } from './shared' +import { _vec2, _vec3, color, awaitSelection, ptObj} from './shared' import {AxesHelper} from './axes' @@ -38,7 +38,7 @@ export class Scene { this.rect = this.canvas.getBoundingClientRect().toJSON() - this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas }); + this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas, antialias:true }); const size = 1; const near = 0; @@ -66,13 +66,23 @@ export class Scene { this.obj3d.add(helpersGroup); + for (let i=0; i<4;i ++) { + const freePt = ptObj() + freePt.matrixAutoUpdate = false + freePt.material.size=8 + freePt.visible = false + freePt.depthTest = false + helpersGroup.add(freePt); + } + + this.fptIdx = 0; + this.fptObj = {} + + this.axes = new AxesHelper(this.camera.zoom) this.axes.visible = false - helpersGroup.add(this.axes); - - const planeGeom = new THREE.PlaneGeometry(5, 5) const pxy = new THREE.Mesh( @@ -88,6 +98,7 @@ export class Scene { ); pxy.userData.type = 'plane' + pxy.layers.enable(1) pxy.add( new THREE.LineSegments( @@ -109,7 +120,7 @@ export class Scene { - const intensity = 1; + const intensity = 0.5; const light1 = new THREE.DirectionalLight(color.lighting, intensity); light1.position.set(10, 10, 10); this.obj3d.add(light1); @@ -122,6 +133,8 @@ export class Scene { + + this.render = render.bind(this); this.addSketch = addSketch.bind(this); this.extrude = extrude.bind(this); @@ -208,6 +221,45 @@ export class Scene { console.log('fireed') } + + + subtract (m1, m2) { + let bspA = CSG.fromMesh(m1) + let bspB = CSG.fromMesh(m2) + m1.traverse(e=>e.layers.disable(0)) + m2.traverse(e=>e.layers.disable(0)) + // m1.visible = false + // m2.visible = false + + // // Subtract one bsp from the other via .subtract... other supported modes are .union and .intersect + + let bspResult = bspA.subtract(bspB) + + // //Get the resulting mesh from the result bsp, and assign meshA.material to the resulting mesh + + let mesh = CSG.toMesh(bspResult, m1.matrix, m1.material) + mesh.userData.type = 'mesh' + mesh.name = `${m1.name}-${m2.name}` + + const edges = new THREE.EdgesGeometry( mesh.geometry, 15 ); + edges.type = 'BufferGeometry' + edges.parameters = undefined + + const line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( { color: 0x000000 } ) ); + line.userData.type = 'line' + + // const vertices = new THREE.Points( edges, new THREE.PointsMaterial({ color: 0x000000, size:4}) ); + const vertices = new THREE.Points( edges, new THREE.PointsMaterial() ); + vertices.userData.type = 'point' + vertices.layers.enable(1) + + mesh.add(line) + mesh.add(vertices) + + + sc.obj3d.add(mesh) + return mesh + } } @@ -299,12 +351,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 94ddbbd..87b557f 100644 --- a/src/Sketch.js +++ b/src/Sketch.js @@ -152,6 +152,7 @@ class Sketch { this.setDimLines() + this.obj3d.traverse(e=>e.layers.enable(0)) this.scene.axes.matrix = this.obj3d.matrix this.scene.axes.visible = true @@ -164,6 +165,7 @@ class Sketch { this.canvas.removeEventListener('pointermove', this.onHover) this.store.dispatch({ type: 'exit-sketch' }) this.labelContainer.innerHTML = "" + this.obj3d.traverse(e=>e.layers.disable(0)) this.scene.axes.visible = false } diff --git a/src/drawDimension.js b/src/drawDimension.js index 4cd2061..6bde2ae 100644 --- a/src/drawDimension.js +++ b/src/drawDimension.js @@ -4,8 +4,6 @@ import { color } from './shared' const lineMaterial = new THREE.LineBasicMaterial({ linewidth: 2, color: color.dimension, - opacity: 0.2, - transparent: true, }) @@ -26,7 +24,7 @@ export async function drawDimension() { const line = new THREE.LineSegments( new THREE.BufferGeometry().setAttribute('position', - new THREE.Float32BufferAttribute(3 * 8, 3) + new THREE.Float32BufferAttribute(Array(3 * 8).fill(-0.001), 3) ), lineMaterial.clone() ); diff --git a/src/extrude.js b/src/extrude.js index cdcadb7..8492ee2 100644 --- a/src/extrude.js +++ b/src/extrude.js @@ -77,23 +77,43 @@ export function extrude(sketch) { const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); - const material = new THREE.MeshPhongMaterial({ + + + const material = new THREE.MeshLambertMaterial({ + // const material = new THREE.MeshPhongMaterial({ color: color.mesh, emissive: color.emissive, + // flatShading:true, }); const mesh = new THREE.Mesh(geometry, material) mesh.name = 'm' + id++ mesh.userData.type = 'mesh' + mesh.layers.enable(1) - for (let i = 0; i < offSetPts.length; i += 2) { - if ( - offSetPts[i] == offSetPts[i - 2] && - offSetPts[i + 1] == offSetPts[i - 1] - ) continue; - mesh.add( - ptObj([offSetPts[i], offSetPts[i + 1], 8]) - ) - } + const edges = new THREE.EdgesGeometry( geometry, 15 ); + edges.type = 'BufferGeometry' + edges.parameters = undefined + + const line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( { color: 0x000000 } ) ); + line.userData.type = 'line' + + const vertices = new THREE.Points( edges, new THREE.PointsMaterial() ); + vertices.userData.type = 'point' + vertices.layers.enable(1) + + mesh.add(line) + mesh.add(vertices) + + // for (let i = 0; i < offSetPts.length; i += 2) { + // if ( + // offSetPts[i] == offSetPts[i - 2] && + // offSetPts[i + 1] == offSetPts[i - 1] + // ) continue; + // mesh.add( + // ptObj([offSetPts[i], offSetPts[i + 1], 0], false), + // ptObj([offSetPts[i], offSetPts[i + 1], 8], false), + // ) + // } mesh.matrixAutoUpdate = false; @@ -103,7 +123,6 @@ export function extrude(sketch) { - this.render() // sketch.visible = false diff --git a/src/mouseEvents.js b/src/mouseEvents.js index 9e2bde2..12e94b9 100644 --- a/src/mouseEvents.js +++ b/src/mouseEvents.js @@ -1,106 +1,112 @@ import * as THREE from '../node_modules/three/src/Three'; import { raycaster, color, hoverColor } from './shared'; + +let ptLoc + export function onHover(e) { if (this.mode || e.buttons) return raycaster.setFromCamera( new THREE.Vector2( - (e.clientX - this.rect.left)/ this.rect.width * 2 - 1, - - (e.clientY - this.rect.top)/ this.rect.height * 2 + 1 + (e.clientX - this.rect.left) / this.rect.width * 2 - 1, + - (e.clientY - this.rect.top) / this.rect.height * 2 + 1 ), this.camera ); - let hoverPts; - let idx = [] - if (this.obj3d.userData.type == 'sketch') { - hoverPts = raycaster.intersectObjects([...this.obj3d.children[1].children, ...this.obj3d.children]) - - if (hoverPts.length) { - - let minDist = Infinity; - for (let i = 0; i < hoverPts.length; i++) { - if (!hoverPts[i].distanceToRay) continue; - if (hoverPts[i].distanceToRay < minDist - 0.0001) { - minDist = hoverPts[i].distanceToRay - idx = [i] - } else if (Math.abs(hoverPts[i].distanceToRay - minDist) < 0.0001) { - idx.push(i) - } - } - - - // console.log(hoverPts, idx) - if (!idx.length) idx.push(0) - } - - - } else { - // hoverPts = raycaster.intersectObjects(this.obj3d.children) + if (this.obj3d.userData.type != 'sketch') { + raycaster.layers.set(1) hoverPts = raycaster.intersectObjects(this.obj3d.children, true) - - if (hoverPts.length) { - - // console.log(hoverPts) - // for (let i = 0; i < hoverPts.length; i++) { - // const obj = hoverPts[i].object - // if (['point', 'plane'].includes(obj.userData.type)) { - // idx.push(i) - // break; - // } - // } - - - let minDist = Infinity; - for (let i = 0; i < hoverPts.length; i++) { - if (!hoverPts[i].distanceToRay) continue; - - if (hoverPts[i].distanceToRay < minDist - 0.0001) { - minDist = hoverPts[i].distanceToRay - idx = [i] - } else if (Math.abs(hoverPts[i].distanceToRay - minDist) < 0.0001) { - idx.push(i) - } - } - - - - - if (!idx.length) { - const obj = hoverPts[0].object - if (obj.userData.type == "mesh" && obj.visible) { - idx.push(0) - } else if (['point', 'plane'].includes(obj.userData.type)) { - idx.push(0) - } - } - - } - - - + } else { + raycaster.layers.set(0) + hoverPts = raycaster.intersectObjects([...this.obj3d.children[1].children, ...this.obj3d.children]) } + let idx = [] + if (hoverPts.length) { + + let minDist = Infinity; + for (let i = 0; i < hoverPts.length; i++) { + if (!hoverPts[i].distanceToRay) continue; + if (hoverPts[i].distanceToRay < minDist - 0.0001) { + idx = [i] + + if (this.obj3d.userData.type != 'sketch') break + + minDist = hoverPts[i].distanceToRay + } else if (Math.abs(hoverPts[i].distanceToRay - minDist) < 0.0001) { + idx.push(i) + } + } + + if (!idx.length) { + idx.push(0) + } + + } + + if (idx.length) { // after filtering, if hovered objs still exists - 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 - for (let x = 0; x < this.hovered.length; x++) { + for (let x = 0; x < this.hovered.length; x++) { // first clear old hovers that are not selected const obj = this.hovered[x] - if (obj && !this.selected.includes(obj)) { - obj.material.color.set(color[obj.userData.type]) + if (!this.selected.includes(obj)) { + if (typeof obj == 'object') { + obj.material.color.set(color[obj.userData.type]) + + if (this.obj3d.userData.type != 'sketch') { + if (obj.userData.type == 'mesh') { + obj.children[0].material.color.set(color['line']) + } + } + + } else { + // this.obj3d.children[0].children[this.fptObj[obj]].visible = false + this.obj3d.children[0].children[0].visible = false + } } } this.hovered = [] for (let x = 0; x < idx.length; x++) { - const obj = hoverPts[idx[x]].object - obj.material.color.set(hoverColor[obj.userData.type]) + let obj = hoverPts[idx[x]].object + + if (this.obj3d.userData.type == 'sketch') { + obj.material.color.set(hoverColor[obj.userData.type]) + } else { + + if (obj.userData.type == 'mesh') { + obj.children[0].material.color.set(hoverColor['line']) + } else if (obj.userData.type == 'plane') { + obj.material.color.set(hoverColor[obj.userData.type]) + } else if (obj.userData.type == 'point') { + + ptLoc = obj.geometry.attributes.position.array + .slice( + 3 * hoverPts[idx[x]].index, + 3 * hoverPts[idx[x]].index + 3 + ) + + // const pp = this.obj3d.children[0].children[this.fptIdx % 3] + const pp = this.obj3d.children[0].children[0] + pp.geometry.attributes.position.array.set(ptLoc) + pp.matrix = obj.parent.matrix + pp.geometry.attributes.position.needsUpdate = true + pp.visible = true + + obj = hoverPts[idx[x]].index + + } + + } + + this.hovered.push(obj) } @@ -109,11 +115,22 @@ export function onHover(e) { } } else { // no hovered object after filtering if (this.hovered.length) { // if previously something was hovered, then we need to clear it + + for (let x = 0; x < this.hovered.length; x++) { const obj = this.hovered[x] - // console.log(obj, 'here') if (!this.selected.includes(obj)) { - obj.material.color.set(color[obj.userData.type]) + if (typeof obj == 'object') { + obj.material.color.set(color[obj.userData.type]) + if (this.obj3d.userData.type != 'sketch') { + if (obj.userData.type == 'mesh') { + obj.children[0].material.color.set(color['line']) + } + } + } else { + // this.obj3d.children[0].children[this.fptObj[obj]].visible = false + this.obj3d.children[0].children[0].visible = false + } } } this.hovered = [] @@ -122,6 +139,8 @@ export function onHover(e) { this.obj3d.dispatchEvent({ type: 'change' }) } } + + } let draggedLabel; @@ -129,40 +148,70 @@ export function onPick(e) { if (this.mode || e.buttons != 1) return if (this.hovered.length) { + const obj = this.hovered[this.hovered.length - 1] this.selected.push(this.hovered[this.hovered.length - 1]) - switch (this.hovered[0].userData.type) { - case 'dimension': - const idx = this.obj3d.children[1].children.indexOf(this.hovered[0]) - if (idx % 2) { - - this.onDragDim = this._onMoveDimension( - this.obj3d.children[1].children[idx], - this.obj3d.children[1].children[idx - 1], - ) - this.canvas.addEventListener('pointermove', this.onDragDim); - this.canvas.addEventListener('pointerup', this.onRelease) + if (this.obj3d.userData.type != 'sketch') { + if (typeof obj == 'object') { + if (obj.userData.type == "mesh") { + obj.material.color.set(hoverColor[obj.userData.type]) } + } else { + const pp = this.obj3d.children[0].children[this.fptIdx % 3 + 1] + const p0 = this.obj3d.children[0].children[0] - draggedLabel = this.obj3d.children[1].children[idx].label - draggedLabel.style.zIndex = -1; - break; - case 'point': + pp.geometry.attributes.position.array.set(p0.geometry.attributes.position.array) + pp.matrix = p0.matrix + pp.geometry.attributes.position.needsUpdate = true + pp.visible = true - this.canvas.addEventListener('pointermove', this.onDrag); - this.canvas.addEventListener('pointerup', this.onRelease) - break; + this.fptObj[obj] = this.fptIdx + this.fptIdx++ + } + } - default: - break; + if (typeof this.hovered[0] == 'object') { + switch (this.hovered[0].userData.type) { + case 'dimension': + const idx = this.obj3d.children[1].children.indexOf(this.hovered[0]) + if (idx % 2) { + + this.onDragDim = this._onMoveDimension( + this.obj3d.children[1].children[idx], + this.obj3d.children[1].children[idx - 1], + ) + this.canvas.addEventListener('pointermove', this.onDragDim); + this.canvas.addEventListener('pointerup', this.onRelease) + } + + draggedLabel = this.obj3d.children[1].children[idx].label + draggedLabel.style.zIndex = -1; + break; + case 'point': + + this.canvas.addEventListener('pointermove', this.onDrag); + this.canvas.addEventListener('pointerup', this.onRelease) + break; + + default: + break; + } } } else { for (let x = 0; x < this.selected.length; x++) { const obj = this.selected[x] - obj.material.color.set(color[obj.userData.type]) + if (typeof obj == 'object') { + obj.material.color.set(color[obj.userData.type]) + if (this.obj3d.userData.type != 'sketch' && obj.userData.type == 'mesh') { + obj.children[0].material.color.set(color['line']) + } + } else { + this.obj3d.children[0].children[this.fptObj[obj] + 1].visible = false + } } + this.obj3d.children[0].children[0].visible = false this.obj3d.dispatchEvent({ type: 'change' }) this.selected = [] } diff --git a/src/react/app.css b/src/react/app.css index 6175df8..8000c5a 100644 --- a/src/react/app.css +++ b/src/react/app.css @@ -60,6 +60,14 @@ body { } +.btn-green { + cursor: pointer; + @apply fill-current + bg-transparent text-gray-700 + hover:bg-gray-300 hover:text-green-200; +} + + .tooltip { position: fixed; display: block; diff --git a/src/react/depTree.mjs b/src/react/depTree.mjs index a5775e9..93cb31a 100644 --- a/src/react/depTree.mjs +++ b/src/react/depTree.mjs @@ -58,7 +58,7 @@ export class DepTree { this.allIds.splice(spliceIdx, 1) - const deletedObj = sc.obj3d.children.splice(spliceIdx + 4, 1)[0] + const deletedObj = sc.obj3d.children.splice(spliceIdx + 4, 1)[0] // first 4 elements are non geom deletedObj.traverse((obj)=>{ if (obj.geometry) obj.geometry.dispose() diff --git a/src/react/navBar.jsx b/src/react/navBar.jsx index 1818e80..95533da 100644 --- a/src/react/navBar.jsx +++ b/src/react/navBar.jsx @@ -43,7 +43,7 @@ export const NavBar = () => { 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) + const mesh = sc.subtract(m1, m2) console.log(mesh, 'meshres') dispatch({ type: 'rx-boolean', mesh, deps: [m1.name, m2.name] }) @@ -68,29 +68,3 @@ export const NavBar = () => { } } - -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") - - - let bspA = BoolOp.fromMesh(m1) - let bspB = BoolOp.fromMesh(m2) - m1.visible = false - m2.visible = false - - // // Subtract one bsp from the other via .subtract... other supported modes are .union and .intersect - - let bspResult = bspA.subtract(bspB) - - // //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.name = `${m1.name}-${m2.name}` - - sc.obj3d.add(meshResult) - - return meshResult - -} diff --git a/src/react/tree.jsx b/src/react/tree.jsx index 4b9882c..2b83318 100644 --- a/src/react/tree.jsx +++ b/src/react/tree.jsx @@ -4,6 +4,7 @@ import React, { useReducer } from 'react'; import { useDispatch, useSelector } from 'react-redux' import { MdEdit, MdVisibilityOff, MdVisibility, MdDelete } from 'react-icons/md' +import { FaCube, FaEdit } from 'react-icons/fa' export const Tree = () => { const treeEntries = useSelector(state => state.treeEntries) @@ -17,6 +18,12 @@ export const Tree = () => { } +const treeIcons = { + 'mesh': FaCube, + 'sketch': FaEdit +} + + const TreeEntry = ({ entId }) => { const treeEntries = useSelector(state => state.treeEntries.byId) @@ -28,18 +35,30 @@ const TreeEntry = ({ entId }) => { entry = treeEntries[entId] - if (entId[0] == "s") { + if (treeEntries[entId].obj3d) { obj3d = treeEntries[entId].obj3d } else { obj3d = treeEntries[entId] } + console.log(obj3d.userData.type) + let Icon = treeIcons[obj3d.userData.type] const [_, forceUpdate] = useReducer(x => x + 1, 0); - const vis = obj3d.visible + // const vis = obj3d.visible + const vis = obj3d.layers.mask&1 - return
-
{ + activeSketchId && treeEntries[activeSketchId].deactivate() + entry.activate() + sc.clearSelection() + sc.activeSketch = entry; + }} + > + +
{ if (entId[0] == 'm') { // entry.material.color.set(color.hover) @@ -64,46 +83,31 @@ const TreeEntry = ({ entId }) => { > {entId}
-
-
{ - activeSketchId && treeEntries[activeSketchId].deactivate() - entry.activate() - sc.clearSelection() - sc.activeSketch = entry; - }} - > - -
+
-
{ dispatch({ type: 'delete-node', id: entId }) }} - > - -
+ /> + { vis ? -
{ - obj3d.visible = false; + obj3d.traverse((e)=>e.layers.disable(0)) sc.render() forceUpdate() }} - > - -
+ /> : -
{ - obj3d.visible = true; + obj3d.traverse((e)=>e.layers.enable(0)) sc.render() forceUpdate() }} - > - -
+ /> }
diff --git a/src/shared.js b/src/shared.js index c8bb654..63f6c13 100644 --- a/src/shared.js +++ b/src/shared.js @@ -12,20 +12,16 @@ raycaster.params.Points.threshold = 0.1; const color = { - // background:0xdae1e7, - background:0xffffff, - // background:0xbbbbbb, + background:0xdae1e7, lighting: 0xFFFFFF, emissive: 0x072534, hover: 0x00ff00, point: 0x555555, //points - line: 0x555555, //lines - mesh: 0x156289, //mesh: + line: 0x000000, //lines + mesh: 0x9DCFED, //mesh: dimension: 0x0000ff, // - // plane: 0xdaacac, // - // planeBorder: 0xc59797, // plane: 0x88adcd, // planeBorder: 0xa7cae8, // } @@ -34,7 +30,7 @@ const hoverColor = { hover: 0x00ff00, point: 0x00ff00, //points line: 0x00ff00, //lines - mesh: 0x00ff00, //mesh: + mesh: 0xFAB601, //mesh: dimension: 0x00ff00, // plane: 0x005dff, // } @@ -52,7 +48,7 @@ const pointMaterial = new THREE.PointsMaterial({ }) -const ptObj = (n) => { +const ptObj = (n, visibility = true) => { const ret = new THREE.Points( new THREE.BufferGeometry().setAttribute('position', new THREE.Float32BufferAttribute(n || 3, 3) @@ -61,6 +57,7 @@ const ptObj = (n) => { ); ret.name = "p" + id++ ret.userData.type = 'point' + ret.visible = visibility return ret } @@ -145,6 +142,7 @@ async function awaitSelection(...criteria) { return null } +window.rc = raycaster export { lineMaterial, pointMaterial, _vec2, _vec3, raycaster, color, hoverColor, ptObj, lineObj, awaitSelection } \ No newline at end of file