diff --git a/dist/app.css b/dist/app.css new file mode 100644 index 0000000..66159fa --- /dev/null +++ b/dist/app.css @@ -0,0 +1,185 @@ +/* @tailwind base; */ + +.bg-gray-50 { + --tw-bg-opacity: 1; + background-color: rgba(249, 250, 251, var(--tw-bg-opacity)); +} + +.flex { + display: flex; +} + +.table { + display: table; +} + +.flex-col { + flex-direction: column; +} + +.items-center { + align-items: center; +} + +.justify-start { + justify-content: flex-start; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.h-6 { + height: 1.5rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.ml-2 { + margin-left: 0.5rem; +} + +.p-1 { + padding: 0.25rem; +} + +* { + --tw-shadow: 0 0 #0000; +} + +* { + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(59, 130, 246, 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; +} + +.visible { + visibility: visible; +} + +.w-6 { + width: 1.5rem; +} + +.w-full { + width: 100%; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +@keyframes ping { + 75%, 100% { + transform: scale(2); + opacity: 0; + } +} + +@keyframes pulse { + 50% { + opacity: .5; + } +} + +@keyframes bounce { + 0%, 100% { + transform: translateY(-25%); + animation-timing-function: cubic-bezier(0.8,0,1,1); + } + + 50% { + transform: none; + animation-timing-function: cubic-bezier(0,0,0.2,1); + } +} + +/* html, */ + +body { + margin: 0; + height: 100%; + font-family: sans-serif; + overflow: hidden; + --topNavH: 36px; + --sideNavW: 200px; +} + +#c { + position: absolute; + width: calc(100% - var(--sideNavW)); + height: calc(100% - var(--topNavH)); + bottom: 0; + right: 0; +} + +.topNav { + position: absolute; + height: var(--topNavH); + left:0; + right:0; + top:0; +} + +.sideNav { + position: absolute; + top: var(--topNavH); + left: 0; + bottom: 0; + width: var(--sideNavW); +} + +#labels > div { + position: absolute; + border: solid 1px black; +} + +.btn { + cursor: pointer; + --tw-bg-opacity: 1; + background-color: rgba(243, 244, 246, var(--tw-bg-opacity)); +} + +.btn:hover { + --tw-bg-opacity: 1; + background-color: rgba(229, 231, 235, var(--tw-bg-opacity)); +} + +.btn { + fill: currentColor; + --tw-text-opacity: 1; + color: rgba(5, 150, 105, var(--tw-text-opacity)); +} + +.btn:hover { + --tw-text-opacity: 1; + color: rgba(4, 120, 87, var(--tw-text-opacity)); +} + +@media (min-width: 640px) { +} + +@media (min-width: 768px) { +} + +@media (min-width: 1024px) { +} + +@media (min-width: 1280px) { +} + +@media (min-width: 1536px) { +} + diff --git a/dist/index.html b/dist/index.html index 0a329c4..dc20e8a 100644 --- a/dist/index.html +++ b/dist/index.html @@ -21,7 +21,7 @@
- + diff --git a/src/Sketch.js b/src/Sketch.js index 6099e2d..da4d2ad 100644 --- a/src/Sketch.js +++ b/src/Sketch.js @@ -2,11 +2,12 @@ import * as THREE from '../node_modules/three/src/Three'; -import { drawOnClick1, drawOnClick2, drawPreClick2, drawClear } from './drawEvents' -import { onHover, onDrag, onDragDim, onPick, onRelease } from './mouseEvents' -import { addDimension, setCoincident } from './constraintEvents' -import { get3PtArc } from './drawArc' import { _vec2, _vec3, raycaster, awaitPts } from './shared' + +import { drawOnClick1, drawOnClick2, drawPreClick2, drawClear } from './drawEvents' +import { onHover, onDrag, onPick, onRelease } from './mouseEvents' +import { setCoincident } from './constraintEvents' +import { get3PtArc } from './drawArc' import { replacer, reviver } from './utils' import { AxesHelper } from './sketchAxes' import { drawDimension, _onMoveDimension, setDimLines, updateDim } from './drawDimension'; diff --git a/src/app.css b/src/app.css index 20bc553..9c93aae 100644 --- a/src/app.css +++ b/src/app.css @@ -1,38 +1,36 @@ /* @tailwind base; */ @tailwind utilities; -html, +/* html, */ body { margin: 0; height: 100%; font-family: sans-serif; overflow: hidden; + --topNavH: 36px; + --sideNavW: 200px; } #c { position: absolute; - width: 100%; - height: 100%; - display: block; + width: calc(100% - var(--sideNavW)); + height: calc(100% - var(--topNavH)); + bottom: 0; + right: 0; } - -#react > div { - +.topNav { + position: absolute; + height: var(--topNavH); + left:0; + right:0; + top:0; } - -.btn-red { - cursor: pointer; - @apply bg-red-500 hover:bg-red-600 text-gray-50; -} - -.btn-grn { - cursor: pointer; - @apply bg-green-500 hover:bg-green-600 text-gray-50; -} - -.btn-blu { - cursor: pointer; - @apply bg-blue-500 hover:bg-blue-600 text-gray-50; +.sideNav { + position: absolute; + top: var(--topNavH); + left: 0; + bottom: 0; + width: var(--sideNavW); } diff --git a/src/app.jsx b/src/app.jsx index 6e3d797..0014030 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -1,214 +1,31 @@ +import ReactDOM from 'react-dom' +import React from 'react' + +import { createStore, applyMiddleware} from 'redux' +import { Provider } from 'react-redux' +import logger from 'redux-logger' + + +import { Tree } from './treeEntry' +import { NavBar } from './navBar' +import { reducer, preloadedState } from './reducer' -import React, { useEffect, useReducer, useRef, useState } from 'react'; import './app.css' -import { Provider, useDispatch, useSelector } from 'react-redux' -import { FaCube, FaEdit } from 'react-icons/fa' -import { MdEdit, MdDone, MdVisibilityOff, MdVisibility, MdDelete } from 'react-icons/md' -import * as Icon from "./icons"; -// import { color } from './shared' +window.store = createStore(reducer, preloadedState, applyMiddleware(logger)) -export const Root = ({ store }) => ( + +const App = ({ store }) => ( - + + ); - - -const App = () => { - const dispatch = useDispatch() - const treeEntries = useSelector(state => state.treeEntries) - const activeSketchId = useSelector(state => state.activeSketchId) - - - useEffect(() => { - if (!activeSketchId) { - sc.canvas.addEventListener('pointermove', sc.onHover) - sc.canvas.addEventListener('pointerdown', sc.onPick) - return () => { - sc.canvas.removeEventListener('pointermove', sc.onHover) - sc.canvas.removeEventListener('pointerdown', sc.onPick) - } - } - }, [activeSketchId]) - - - const btnz = [ - activeSketchId ? - [MdDone, () => { - treeEntries.byId[activeSketchId].deactivate() - sc.activeSketch = null - // sc.activeDim = this.activeSketch.obj3d.children[1].children - }, 'Finish'] : - [FaEdit, sc.addSketch, 'Sketch'] - , - [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) - - console.log(mesh, 'meshres') - dispatch({ type: 'rx-boolean', 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 <> -
- {/*
*/} - { - btnz.map(([Icon, fcn, txt], idx) => ( -
- -
{txt}
-
- )) - } -
- -
- {treeEntries.allIds.map((entId, idx) => ( - - ))} -
- - - -} - -const TreeEntry = ({ entId }) => { - - const treeEntries = useSelector(state => state.treeEntries.byId) - const dispatch = useDispatch() - - const activeSketchId = useSelector(state => state.activeSketchId) - - let obj3d, entry; - - entry = treeEntries[entId] - - if (entId[0] == "s") { - obj3d = treeEntries[entId].obj3d - } else { - obj3d = treeEntries[entId] - } - - const [_, forceUpdate] = useReducer(x => x + 1, 0); - - const vis = obj3d.visible - - return
-
{ - if (entId[0] == 'm') { - // entry.material.color.set(color.hover) - sc.render() - } - }} - onPointerLeave={() => { - const obj = entry - if (entId[0] == 'm' && !sc.selected.includes(obj)) { - // obj.material.color.set(color.mesh) - sc.render() - } - }} - onPointerDown={() => { - if (entId[0] == 'm') { - sc.selected.push( - entry - ) - sc.render() - } - }} - > - {entId} -
-
-
{ - activeSketchId && treeEntries[activeSketchId].deactivate() - entry.activate() - sc.clearSelection() - sc.activeSketch = entry; - }} - > - -
- -
{ - dispatch({ type: 'delete-node', id: entId }) - }} - > - -
- { - vis ? -
{ - obj3d.visible = false; - sc.render() - forceUpdate() - }} - > - -
- : -
{ - obj3d.visible = true; - sc.render() - forceUpdate() - }} - > - -
- } -
- -
- -} - -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 - -} +document.addEventListener('DOMContentLoaded', () => { + ReactDOM.render(, document.getElementById('react')); +}); diff --git a/src/mouseEvents.js b/src/mouseEvents.js index ceb4928..d5eabe6 100644 --- a/src/mouseEvents.js +++ b/src/mouseEvents.js @@ -43,6 +43,7 @@ export function onHover(e) { 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)) { @@ -50,7 +51,6 @@ export function onHover(e) { // break; // } // } - // console.log(hoverPts) let minDist = Infinity; diff --git a/src/navBar.jsx b/src/navBar.jsx new file mode 100644 index 0000000..da17d4c --- /dev/null +++ b/src/navBar.jsx @@ -0,0 +1,99 @@ + + +import React, { useEffect, useReducer} from 'react'; + +import { useDispatch, useSelector } from 'react-redux' + +import { FaCube, FaEdit } from 'react-icons/fa' +import { MdDone } from 'react-icons/md' +import * as Icon from "./icons"; + + + +export const NavBar = () => { + const dispatch = useDispatch() + const treeEntries = useSelector(state => state.treeEntries) + const activeSketchId = useSelector(state => state.activeSketchId) + + + useEffect(() => { + if (!activeSketchId) { + sc.canvas.addEventListener('pointermove', sc.onHover) + sc.canvas.addEventListener('pointerdown', sc.onPick) + return () => { + sc.canvas.removeEventListener('pointermove', sc.onHover) + sc.canvas.removeEventListener('pointerdown', sc.onPick) + } + } + }, [activeSketchId]) + + + const btnz = [ + activeSketchId ? + [MdDone, () => { + treeEntries.byId[activeSketchId].deactivate() + sc.activeSketch = null + // sc.activeDim = this.activeSketch.obj3d.children[1].children + }, 'Finish'] : + [FaEdit, sc.addSketch, 'Sketch'] + , + [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) + + console.log(mesh, 'meshres') + dispatch({ type: 'rx-boolean', 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
+ { + btnz.map(([Icon, fcn, txt], idx) => ( +
+ +
{txt}
+
+ )) + } +
+} + +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/index.jsx b/src/reducer.js similarity index 79% rename from src/index.jsx rename to src/reducer.js index ea5ff59..f4a193a 100644 --- a/src/index.jsx +++ b/src/reducer.js @@ -1,18 +1,19 @@ -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' import { DepTree } from './depTree.mjs' +import update from 'immutability-helper' +export const preloadedState = { + treeEntries: { + byId: {}, + allIds: [], + tree: {}, + order: {}, + }, +} - -function reducer(state = {}, action) { +export function reducer(state = {}, action) { switch (action.type) { case 'rx-sketch': return update(state, { @@ -84,21 +85,3 @@ function reducer(state = {}, action) { -const preloadedState = { - treeEntries: { - byId: {}, - allIds: [], - tree: {}, - order: {}, - }, -} - - - -window.store = createStore(reducer, preloadedState, applyMiddleware(logger)) - - -document.addEventListener('DOMContentLoaded', () => { - ReactDOM.render(, document.getElementById('react')); -}); - diff --git a/src/treeEntry.jsx b/src/treeEntry.jsx new file mode 100644 index 0000000..499d8de --- /dev/null +++ b/src/treeEntry.jsx @@ -0,0 +1,112 @@ + + +import React, { useReducer } from 'react'; +import { useDispatch, useSelector } from 'react-redux' +import { MdEdit, MdVisibilityOff, MdVisibility, MdDelete } from 'react-icons/md' + + +export const Tree = () => { + const treeEntries = useSelector(state => state.treeEntries) + + return
+ {treeEntries.allIds.map((entId, idx) => ( + + ))} +
+ +} + + +const TreeEntry = ({ entId }) => { + + const treeEntries = useSelector(state => state.treeEntries.byId) + const dispatch = useDispatch() + + const activeSketchId = useSelector(state => state.activeSketchId) + + let obj3d, entry; + + entry = treeEntries[entId] + + if (entId[0] == "s") { + obj3d = treeEntries[entId].obj3d + } else { + obj3d = treeEntries[entId] + } + + const [_, forceUpdate] = useReducer(x => x + 1, 0); + + const vis = obj3d.visible + + return
+
{ + if (entId[0] == 'm') { + // entry.material.color.set(color.hover) + sc.render() + } + }} + onPointerLeave={() => { + const obj = entry + if (entId[0] == 'm' && !sc.selected.includes(obj)) { + // obj.material.color.set(color.mesh) + sc.render() + } + }} + onPointerDown={() => { + if (entId[0] == 'm') { + sc.selected.push( + entry + ) + sc.render() + } + }} + > + {entId} +
+
+
{ + activeSketchId && treeEntries[activeSketchId].deactivate() + entry.activate() + sc.clearSelection() + sc.activeSketch = entry; + }} + > + +
+ +
{ + dispatch({ type: 'delete-node', id: entId }) + }} + > + +
+ { + vis ? +
{ + obj3d.visible = false; + sc.render() + forceUpdate() + }} + > + +
+ : +
{ + obj3d.visible = true; + sc.render() + forceUpdate() + }} + > + +
+ } +
+ +
+ +} \ No newline at end of file diff --git a/webpack.common.js b/webpack.common.js index db5858b..9d42bb3 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -4,7 +4,7 @@ const tailwindcss = require('tailwindcss') module.exports = { entry: { - index: './src/index.jsx', + app: './src/app.jsx', scene: './src/Scene.js', }, output: {