master
howard 2021-04-06 21:21:09 -07:00
parent c6a3803b8d
commit b54b871fa3
10 changed files with 452 additions and 257 deletions

185
dist/app.css vendored Normal file
View File

@ -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) {
}

2
dist/index.html vendored
View File

@ -21,7 +21,7 @@
<div id="react"></div>
<div id="labels"></div>
<div id="stats"></div>
<script src="index.bundle.js"></script>
<script src="app.bundle.js"></script>
<script src="scene.bundle.js"></script>
<script src="solver.js"></script>
<script src="lz-string.min.js"></script>

View File

@ -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';

View File

@ -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);
}

View File

@ -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 }) => (
<Provider store={store}>
<App></App>
<NavBar />
<Tree />
</Provider>
);
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 <>
<div className='absolute flex ml-auto mr-auto left-0 right-0 justify-center'>
{/* <div className='absolute flex justify-center'> */}
{
btnz.map(([Icon, fcn, txt], idx) => (
<div className="btn flex items-center justify-start p-1 text-lg" key={idx}
onClick={fcn}
>
<Icon className="w-6 h-6" />
<div className="ml-2">{txt}</div>
</div>
))
}
</div>
<div className='absolute left-0 top-36 w-40 flex flex-col'>
{treeEntries.allIds.map((entId, idx) => (
<TreeEntry key={idx} entId={entId} />
))}
</div>
</>
}
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 <div className='bg-gray-50 flex justify-between w-full'>
<div className="btn"
onPointerEnter={() => {
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}
</div>
<div className='flex'>
<div className='btn'
onClick={() => {
activeSketchId && treeEntries[activeSketchId].deactivate()
entry.activate()
sc.clearSelection()
sc.activeSketch = entry;
}}
>
<MdEdit />
</div>
<div className='btn'
onClick={() => {
dispatch({ type: 'delete-node', id: entId })
}}
>
<MdDelete />
</div>
{
vis ?
<div className='btn'
onClick={() => {
obj3d.visible = false;
sc.render()
forceUpdate()
}}
>
<MdVisibility />
</div>
:
<div className='btn'
onClick={() => {
obj3d.visible = true;
sc.render()
forceUpdate()
}}
>
<MdVisibilityOff />
</div>
}
</div>
</div>
}
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(<App store={store} />, document.getElementById('react'));
});

View File

@ -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;

99
src/navBar.jsx Normal file
View File

@ -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 <div className='topNav flex justify-center'>
{
btnz.map(([Icon, fcn, txt], idx) => (
<div className="btn flex items-center justify-start p-1 text-lg" key={idx}
onClick={fcn}
>
<Icon className="w-6 h-6" />
<div className="ml-2" tooltip="wtf">{txt}</div>
</div>
))
}
</div>
}
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
}

View File

@ -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(<Root store={store} />, document.getElementById('react'));
});

112
src/treeEntry.jsx Normal file
View File

@ -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 <div className='sideNav flex flex-col'>
{treeEntries.allIds.map((entId, idx) => (
<TreeEntry key={idx} entId={entId} />
))}
</div>
}
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 <div className='bg-gray-50 flex justify-between w-full'>
<div className="btn"
onPointerEnter={() => {
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}
</div>
<div className='flex'>
<div className='btn'
onClick={() => {
activeSketchId && treeEntries[activeSketchId].deactivate()
entry.activate()
sc.clearSelection()
sc.activeSketch = entry;
}}
>
<MdEdit />
</div>
<div className='btn'
onClick={() => {
dispatch({ type: 'delete-node', id: entId })
}}
>
<MdDelete />
</div>
{
vis ?
<div className='btn'
onClick={() => {
obj3d.visible = false;
sc.render()
forceUpdate()
}}
>
<MdVisibility />
</div>
:
<div className='btn'
onClick={() => {
obj3d.visible = true;
sc.render()
forceUpdate()
}}
>
<MdVisibilityOff />
</div>
}
</div>
</div>
}

View File

@ -4,7 +4,7 @@ const tailwindcss = require('tailwindcss')
module.exports = {
entry: {
index: './src/index.jsx',
app: './src/app.jsx',
scene: './src/Scene.js',
},
output: {