technical debt

master
howard 2021-04-11 00:57:39 -07:00
parent 606e83e2d6
commit 67d6bf8090
13 changed files with 1030 additions and 153 deletions

754
lib/TrackballControls.js Normal file
View File

@ -0,0 +1,754 @@
import {
EventDispatcher,
MOUSE,
Quaternion,
Vector2,
Vector3
} from '../../../build/three.module.js';
var TrackballControls = function ( object, domElement ) {
if ( domElement === undefined ) console.warn( 'THREE.TrackballControls: The second parameter "domElement" is now mandatory.' );
if ( domElement === document ) console.error( 'THREE.TrackballControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
var scope = this;
var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
this.object = object;
this.domElement = domElement;
// API
this.enabled = true;
this.screen = { left: 0, top: 0, width: 0, height: 0 };
this.rotateSpeed = 1.0;
this.zoomSpeed = 1.2;
this.panSpeed = 0.3;
this.noRotate = false;
this.noZoom = false;
this.noPan = false;
this.staticMoving = false;
this.dynamicDampingFactor = 0.2;
this.minDistance = 0;
this.maxDistance = Infinity;
this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
// internals
this.target = new Vector3();
var EPS = 0.000001;
var lastPosition = new Vector3();
var lastZoom = 1;
var _state = STATE.NONE,
_keyState = STATE.NONE,
_eye = new Vector3(),
_movePrev = new Vector2(),
_moveCurr = new Vector2(),
_lastAxis = new Vector3(),
_lastAngle = 0,
_zoomStart = new Vector2(),
_zoomEnd = new Vector2(),
_touchZoomDistanceStart = 0,
_touchZoomDistanceEnd = 0,
_panStart = new Vector2(),
_panEnd = new Vector2();
// for reset
this.target0 = this.target.clone();
this.position0 = this.object.position.clone();
this.up0 = this.object.up.clone();
this.zoom0 = this.object.zoom;
// events
var changeEvent = { type: 'change' };
var startEvent = { type: 'start' };
var endEvent = { type: 'end' };
// methods
this.handleResize = function () {
var box = scope.domElement.getBoundingClientRect();
// adjustments come from similar code in the jquery offset() function
var d = scope.domElement.ownerDocument.documentElement;
scope.screen.left = box.left + window.pageXOffset - d.clientLeft;
scope.screen.top = box.top + window.pageYOffset - d.clientTop;
scope.screen.width = box.width;
scope.screen.height = box.height;
};
var getMouseOnScreen = ( function () {
var vector = new Vector2();
return function getMouseOnScreen( pageX, pageY ) {
vector.set(
( pageX - scope.screen.left ) / scope.screen.width,
( pageY - scope.screen.top ) / scope.screen.height
);
return vector;
};
}() );
var getMouseOnCircle = ( function () {
var vector = new Vector2();
return function getMouseOnCircle( pageX, pageY ) {
vector.set(
( ( pageX - scope.screen.width * 0.5 - scope.screen.left ) / ( scope.screen.width * 0.5 ) ),
( ( scope.screen.height + 2 * ( scope.screen.top - pageY ) ) / scope.screen.width ) // screen.width intentional
);
return vector;
};
}() );
this.rotateCamera = ( function () {
var axis = new Vector3(),
quaternion = new Quaternion(),
eyeDirection = new Vector3(),
objectUpDirection = new Vector3(),
objectSidewaysDirection = new Vector3(),
moveDirection = new Vector3(),
angle;
return function rotateCamera() {
moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
angle = moveDirection.length();
if ( angle ) {
_eye.copy( scope.object.position ).sub( scope.target );
eyeDirection.copy( _eye ).normalize();
objectUpDirection.copy( scope.object.up ).normalize();
objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
axis.crossVectors( moveDirection, _eye ).normalize();
angle *= scope.rotateSpeed;
quaternion.setFromAxisAngle( axis, angle );
_eye.applyQuaternion( quaternion );
scope.object.up.applyQuaternion( quaternion );
_lastAxis.copy( axis );
_lastAngle = angle;
} else if ( ! scope.staticMoving && _lastAngle ) {
_lastAngle *= Math.sqrt( 1.0 - scope.dynamicDampingFactor );
_eye.copy( scope.object.position ).sub( scope.target );
quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
_eye.applyQuaternion( quaternion );
scope.object.up.applyQuaternion( quaternion );
}
_movePrev.copy( _moveCurr );
};
}() );
this.zoomCamera = function () {
var factor;
if ( _state === STATE.TOUCH_ZOOM_PAN ) {
factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
_touchZoomDistanceStart = _touchZoomDistanceEnd;
if ( scope.object.isPerspectiveCamera ) {
_eye.multiplyScalar( factor );
} else if ( scope.object.isOrthographicCamera ) {
scope.object.zoom *= factor;
scope.object.updateProjectionMatrix();
} else {
console.warn( 'THREE.TrackballControls: Unsupported camera type' );
}
} else {
factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * scope.zoomSpeed;
if ( factor !== 1.0 && factor > 0.0 ) {
if ( scope.object.isPerspectiveCamera ) {
_eye.multiplyScalar( factor );
} else if ( scope.object.isOrthographicCamera ) {
scope.object.zoom /= factor;
scope.object.updateProjectionMatrix();
} else {
console.warn( 'THREE.TrackballControls: Unsupported camera type' );
}
}
if ( scope.staticMoving ) {
_zoomStart.copy( _zoomEnd );
} else {
_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
}
}
};
this.panCamera = ( function () {
var mouseChange = new Vector2(),
objectUp = new Vector3(),
pan = new Vector3();
return function panCamera() {
mouseChange.copy( _panEnd ).sub( _panStart );
if ( mouseChange.lengthSq() ) {
if ( scope.object.isOrthographicCamera ) {
var scale_x = ( scope.object.right - scope.object.left ) / scope.object.zoom / scope.domElement.clientWidth;
var scale_y = ( scope.object.top - scope.object.bottom ) / scope.object.zoom / scope.domElement.clientWidth;
mouseChange.x *= scale_x;
mouseChange.y *= scale_y;
}
mouseChange.multiplyScalar( _eye.length() * scope.panSpeed );
pan.copy( _eye ).cross( scope.object.up ).setLength( mouseChange.x );
pan.add( objectUp.copy( scope.object.up ).setLength( mouseChange.y ) );
scope.object.position.add( pan );
scope.target.add( pan );
if ( scope.staticMoving ) {
_panStart.copy( _panEnd );
} else {
_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( scope.dynamicDampingFactor ) );
}
}
};
}() );
this.checkDistances = function () {
if ( ! scope.noZoom || ! scope.noPan ) {
if ( _eye.lengthSq() > scope.maxDistance * scope.maxDistance ) {
scope.object.position.addVectors( scope.target, _eye.setLength( scope.maxDistance ) );
_zoomStart.copy( _zoomEnd );
}
if ( _eye.lengthSq() < scope.minDistance * scope.minDistance ) {
scope.object.position.addVectors( scope.target, _eye.setLength( scope.minDistance ) );
_zoomStart.copy( _zoomEnd );
}
}
};
this.update = function () {
_eye.subVectors( scope.object.position, scope.target );
if ( ! scope.noRotate ) {
scope.rotateCamera();
}
if ( ! scope.noZoom ) {
scope.zoomCamera();
}
if ( ! scope.noPan ) {
scope.panCamera();
}
scope.object.position.addVectors( scope.target, _eye );
if ( scope.object.isPerspectiveCamera ) {
scope.checkDistances();
scope.object.lookAt( scope.target );
if ( lastPosition.distanceToSquared( scope.object.position ) > EPS ) {
scope.dispatchEvent( changeEvent );
lastPosition.copy( scope.object.position );
}
} else if ( scope.object.isOrthographicCamera ) {
scope.object.lookAt( scope.target );
if ( lastPosition.distanceToSquared( scope.object.position ) > EPS || lastZoom !== scope.object.zoom ) {
scope.dispatchEvent( changeEvent );
lastPosition.copy( scope.object.position );
lastZoom = scope.object.zoom;
}
} else {
console.warn( 'THREE.TrackballControls: Unsupported camera type' );
}
};
this.reset = function () {
_state = STATE.NONE;
_keyState = STATE.NONE;
scope.target.copy( scope.target0 );
scope.object.position.copy( scope.position0 );
scope.object.up.copy( scope.up0 );
scope.object.zoom = scope.zoom0;
scope.object.updateProjectionMatrix();
_eye.subVectors( scope.object.position, scope.target );
scope.object.lookAt( scope.target );
scope.dispatchEvent( changeEvent );
lastPosition.copy( scope.object.position );
lastZoom = scope.object.zoom;
};
// listeners
function onPointerDown( event ) {
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
case 'mouse':
case 'pen':
onMouseDown( event );
break;
// TODO touch
}
}
function onPointerMove( event ) {
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
case 'mouse':
case 'pen':
onMouseMove( event );
break;
// TODO touch
}
}
function onPointerUp( event ) {
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
case 'mouse':
case 'pen':
onMouseUp( event );
break;
// TODO touch
}
}
function keydown( event ) {
if ( scope.enabled === false ) return;
window.removeEventListener( 'keydown', keydown );
if ( _keyState !== STATE.NONE ) {
return;
} else if ( event.keyCode === scope.keys[ STATE.ROTATE ] && ! scope.noRotate ) {
_keyState = STATE.ROTATE;
} else if ( event.keyCode === scope.keys[ STATE.ZOOM ] && ! scope.noZoom ) {
_keyState = STATE.ZOOM;
} else if ( event.keyCode === scope.keys[ STATE.PAN ] && ! scope.noPan ) {
_keyState = STATE.PAN;
}
}
function keyup() {
if ( scope.enabled === false ) return;
_keyState = STATE.NONE;
window.addEventListener( 'keydown', keydown );
}
function onMouseDown( event ) {
event.preventDefault();
event.stopPropagation();
if ( _state === STATE.NONE ) {
switch ( event.button ) {
case scope.mouseButtons.LEFT:
_state = STATE.ROTATE;
break;
case scope.mouseButtons.MIDDLE:
_state = STATE.ZOOM;
break;
case scope.mouseButtons.RIGHT:
_state = STATE.PAN;
break;
default:
_state = STATE.NONE;
}
}
var state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
if ( state === STATE.ROTATE && ! scope.noRotate ) {
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
_movePrev.copy( _moveCurr );
} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
_zoomEnd.copy( _zoomStart );
} else if ( state === STATE.PAN && ! scope.noPan ) {
_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
_panEnd.copy( _panStart );
}
scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
scope.dispatchEvent( startEvent );
}
function onMouseMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
var state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
if ( state === STATE.ROTATE && ! scope.noRotate ) {
_movePrev.copy( _moveCurr );
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
} else if ( state === STATE.PAN && ! scope.noPan ) {
_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
}
}
function onMouseUp( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
_state = STATE.NONE;
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
scope.dispatchEvent( endEvent );
}
function mousewheel( event ) {
if ( scope.enabled === false ) return;
if ( scope.noZoom === true ) return;
event.preventDefault();
event.stopPropagation();
switch ( event.deltaMode ) {
case 2:
// Zoom in pages
_zoomStart.y -= event.deltaY * 0.025;
break;
case 1:
// Zoom in lines
_zoomStart.y -= event.deltaY * 0.01;
break;
default:
// undefined, 0, assume pixels
_zoomStart.y -= event.deltaY * 0.00025;
break;
}
scope.dispatchEvent( startEvent );
scope.dispatchEvent( endEvent );
}
function touchstart( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
switch ( event.touches.length ) {
case 1:
_state = STATE.TOUCH_ROTATE;
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_movePrev.copy( _moveCurr );
break;
default: // 2 or more
_state = STATE.TOUCH_ZOOM_PAN;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
_panStart.copy( getMouseOnScreen( x, y ) );
_panEnd.copy( _panStart );
break;
}
scope.dispatchEvent( startEvent );
}
function touchmove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
switch ( event.touches.length ) {
case 1:
_movePrev.copy( _moveCurr );
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
break;
default: // 2 or more
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
_panEnd.copy( getMouseOnScreen( x, y ) );
break;
}
}
function touchend( event ) {
if ( scope.enabled === false ) return;
switch ( event.touches.length ) {
case 0:
_state = STATE.NONE;
break;
case 1:
_state = STATE.TOUCH_ROTATE;
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_movePrev.copy( _moveCurr );
break;
}
scope.dispatchEvent( endEvent );
}
function contextmenu( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
}
this.dispose = function () {
scope.domElement.removeEventListener( 'contextmenu', contextmenu );
scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
scope.domElement.removeEventListener( 'wheel', mousewheel );
scope.domElement.removeEventListener( 'touchstart', touchstart );
scope.domElement.removeEventListener( 'touchend', touchend );
scope.domElement.removeEventListener( 'touchmove', touchmove );
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
window.removeEventListener( 'keydown', keydown );
window.removeEventListener( 'keyup', keyup );
};
this.domElement.addEventListener( 'contextmenu', contextmenu );
this.domElement.addEventListener( 'pointerdown', onPointerDown );
this.domElement.addEventListener( 'wheel', mousewheel );
this.domElement.addEventListener( 'touchstart', touchstart );
this.domElement.addEventListener( 'touchend', touchend );
this.domElement.addEventListener( 'touchmove', touchmove );
this.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
this.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
window.addEventListener( 'keydown', keydown );
window.addEventListener( 'keyup', keyup );
this.handleResize();
// force an update at start
this.update();
};
TrackballControls.prototype = Object.create( EventDispatcher.prototype );
TrackballControls.prototype.constructor = TrackballControls;
export { TrackballControls };

View File

@ -7,7 +7,7 @@ import { Sketch } from './Sketch'
import Stats from '../lib/stats.module.js'; import Stats from '../lib/stats.module.js';
import { extrude } from './extrude' import { extrude } from './extrude'
import { onHover, onPick } from './mouseEvents'; import { onHover, onPick, setHover } from './mouseEvents';
import { _vec2, _vec3, color, awaitSelection, ptObj } from './shared' import { _vec2, _vec3, color, awaitSelection, ptObj } from './shared'
import { AxesHelper } from './axes' import { AxesHelper } from './axes'
@ -50,7 +50,6 @@ export class Scene {
cameraDist * Math.cos(xzAngle) cameraDist * Math.cos(xzAngle)
); );
// const controls = new OrbitControls(camera, view1Elem);
const controls = new TrackballControls(this.camera, this.canvas); const controls = new TrackballControls(this.camera, this.canvas);
controls.target.set(0, 0, 0); controls.target.set(0, 0, 0);
controls.update(); controls.update();
@ -126,6 +125,7 @@ export class Scene {
this.extrude = extrude.bind(this); this.extrude = extrude.bind(this);
this.onHover = onHover.bind(this); this.onHover = onHover.bind(this);
this.onPick = onPick.bind(this); this.onPick = onPick.bind(this);
this.setHover = setHover.bind(this);
this.awaitSelection = awaitSelection.bind(this); this.awaitSelection = awaitSelection.bind(this);
this.obj3d.addEventListener('change', this.render); this.obj3d.addEventListener('change', this.render);
@ -184,7 +184,7 @@ export class Scene {
} else if (k[0] == 'm') { } else if (k[0] == 'm') {
entries[k] = loader.parse(state.treeEntries.byId[k]) entries[k] = loader.parse(state.treeEntries.byId[k])
console.log(entries[k]) // console.log(entries[k])
this.obj3d.add(entries[k]) this.obj3d.add(entries[k])
} }
@ -207,7 +207,7 @@ export class Scene {
this.selected = [] this.selected = []
for (let x = 0; x < this.hovered.length; x++) { for (let x = 0; x < this.hovered.length; x++) {
const obj = this.selected[x] const obj = this.hovered[x]
obj.material.color.set(color[obj.userData.type]) obj.material.color.set(color[obj.userData.type])
if (obj.userData.type == 'plane') { if (obj.userData.type == 'plane') {
obj.material.opacity = 0.05 obj.material.opacity = 0.05
@ -221,12 +221,84 @@ export class Scene {
} }
hover(obj) {
if (typeof obj == 'object' && !this.selected.includes(obj)) {
if (obj.userData.type == 'plane') {
obj.material.opacity = 0.02
obj.children[0].material.color.set(color['planeBorder'])
} else {
if (obj.userData.type == 'mesh') {
obj.material.emissive.set(color.emissive)
}
obj.material.color.set(color[obj.userData.type])
}
}
if (typeof obj == 'object' && !this.selected.includes(obj)) {
if (obj.userData.type == 'plane') {
obj.material.opacity = 0.02
obj.children[0].material.color.set(color['planeBorder'])
} else {
if (obj.userData.type == 'mesh') {
obj.material.emissive.set(color.emissive)
}
obj.material.color.set(color[obj.userData.type])
}
}
obj.material.color.set(color[obj.userData.type])
if (obj.userData.type == 'mesh') {
obj.material.emissive.set(color.emissive)
} else if (obj.userData.type == 'plane') {
obj.material.opacity = 0.02
obj.children[0].material.color.set(color['planeBorder'])
}
if (obj.userData.type == 'selpoint') {
obj.visible = false
}
if (typeof obj == 'object') {
if (obj.userData.type == 'plane') {
obj.material.opacity = 0.06
obj.children[0].material.color.set(hoverColor['planeBorder'])
} else {
if (obj.userData.type == 'mesh') {
obj.material.emissive.set(hoverColor.emissive)
}
obj.material.color.set(hoverColor[obj.userData.type])
}
}
}
subtract(m1, m2) { subtract(m1, m2) {
let bspA = CSG.fromMesh(m1) let bspA = CSG.fromMesh(m1)
let bspB = CSG.fromMesh(m2) let bspB = CSG.fromMesh(m2)
m1.traverse(e => e.layers.disableAll()) m1.visible = false
m2.traverse(e => e.layers.disableAll()) m2.visible = false
m1.traverse(e => e.layers.disable(1))
m2.traverse(e => e.layers.disable(1))
// // Subtract one bsp from the other via .subtract... other supported modes are .union and .intersect // // Subtract one bsp from the other via .subtract... other supported modes are .union and .intersect
@ -241,9 +313,8 @@ export class Scene {
const vertices = new THREE.Points(mesh.geometry, new THREE.PointsMaterial()); const vertices = new THREE.Points(mesh.geometry, new THREE.PointsMaterial({ size: 0 }));
vertices.userData.type = 'point' vertices.userData.type = 'point'
vertices.layers.disable(0)
vertices.layers.enable(1) vertices.layers.enable(1)
// mesh.add(line) // mesh.add(line)
@ -251,6 +322,15 @@ export class Scene {
sc.obj3d.add(mesh) sc.obj3d.add(mesh)
this.store.dispatch({
type: 'set-entry-visibility', obj: {
[m1.name]: false,
[m2.name]: false,
[mesh.name]: true,
}
})
return mesh return mesh
} }
} }
@ -333,18 +413,18 @@ async function addSketch() {
this.clearSelection() this.clearSelection()
sketch.activate()
this.activeSketch = sketch
sketch.obj3d.addEventListener('change', this.render); sketch.obj3d.addEventListener('change', this.render);
this.render()
console.log('render') console.log('render')
this.store.dispatch({ type: 'rx-sketch', obj: sketch }) this.store.dispatch({ type: 'rx-sketch', obj: sketch })
sketch.activate()
this.render()
} }
window.sc = new Scene(store) window.sc = new Scene(store)
sc.loadState() // sc.loadState()
// sc.camera.layers.enable(1) // sc.camera.layers.enable(1)
// rc.layers.set(1) // rc.layers.set(1)

View File

@ -152,9 +152,11 @@ class Sketch {
this.setDimLines() this.setDimLines()
this.obj3d.traverse(e=>e.layers.enable(0)) this.obj3d.traverse(e=>e.layers.enable(2))
this.obj3d.visible = true
this.scene.axes.matrix = this.obj3d.matrix this.scene.axes.matrix = this.obj3d.matrix
this.scene.axes.visible = true this.scene.axes.visible = true
this.scene.activeSketch = this
window.sketcher = this window.sketcher = this
} }
@ -165,8 +167,10 @@ class Sketch {
this.canvas.removeEventListener('pointermove', this.onHover) this.canvas.removeEventListener('pointermove', this.onHover)
this.store.dispatch({ type: 'exit-sketch' }) this.store.dispatch({ type: 'exit-sketch' })
this.labelContainer.innerHTML = "" this.labelContainer.innerHTML = ""
this.obj3d.traverse(e=>e.layers.disable(0)) this.obj3d.visible = false
this.obj3d.traverse(e=>e.layers.disable(2))
this.scene.axes.visible = false this.scene.axes.visible = false
this.scene.activeSketch = null
} }

View File

@ -38,7 +38,8 @@ export async function drawDimension() {
line.userData.ids = selection.map(e => e.name) line.userData.ids = selection.map(e => e.name)
line.layers.enable(2)
point.layers.enable(2)

View File

@ -21,6 +21,10 @@ export function drawOnClick1(e) {
this.toPush = drawPoint.call(this, mouseLoc) this.toPush = drawPoint.call(this, mouseLoc)
} }
this.toPush.forEach(element => {
element.layers.enable(2)
});
this.updatePoint = this.obj3d.children.length this.updatePoint = this.obj3d.children.length
this.obj3d.add(...this.toPush) this.obj3d.add(...this.toPush)
this.linkedObjs.set(this.l_id, [this.mode, this.toPush.map(e => e.name)]) this.linkedObjs.set(this.l_id, [this.mode, this.toPush.map(e => e.name)])

View File

@ -72,12 +72,10 @@ export function extrude(sketch) {
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
// const material = new THREE.MeshLambertMaterial({ // const material = new THREE.MeshLambertMaterial({
const material = new THREE.MeshPhongMaterial({ const material = new THREE.MeshPhongMaterial({
color: color.mesh, color: color.mesh,
emissive: color.emissive, emissive: color.emissive,
// flatShading:true,
}); });
const mesh = new THREE.Mesh(geometry, material) const mesh = new THREE.Mesh(geometry, material)
@ -95,9 +93,14 @@ export function extrude(sketch) {
this.obj3d.add(mesh) this.obj3d.add(mesh)
this.render()
this.store.dispatch({ type: 'rx-extrusion', mesh, sketchId: sketch.obj3d.name }) this.store.dispatch({ type: 'rx-extrusion', mesh, sketchId: sketch.obj3d.name })
if (this.activeSketch == sketch) {
this.activeSketch = null
sketch.deactivate()
}
this.render()
} }

View File

@ -22,7 +22,8 @@ export function onHover(e) {
raycaster.layers.set(1) raycaster.layers.set(1)
hoverPts = raycaster.intersectObjects(this.obj3d.children, true) hoverPts = raycaster.intersectObjects(this.obj3d.children, true)
} else { } else {
raycaster.layers.set(0) // raycaster.layers.set(0)
raycaster.layers.set(2)
hoverPts = raycaster.intersectObjects([...this.obj3d.children[1].children, ...this.obj3d.children]) hoverPts = raycaster.intersectObjects([...this.obj3d.children[1].children, ...this.obj3d.children])
} }
@ -58,12 +59,7 @@ export function onHover(e) {
for (let x = 0; x < this.hovered.length; x++) { // first clear old hovers that are not selected for (let x = 0; x < this.hovered.length; x++) { // first clear old hovers that are not selected
const obj = this.hovered[x] const obj = this.hovered[x]
if (typeof obj == 'object' && !this.selected.includes(obj)) { if (typeof obj == 'object' && !this.selected.includes(obj)) {
if (obj.userData.type == 'plane') { setHover(obj, 0)
obj.material.opacity = 0.02
obj.children[0].material.color.set(color['planeBorder'])
} else {
obj.material.color.set(color[obj.userData.type])
}
} }
} }
this.hovered = [] this.hovered = []
@ -71,23 +67,14 @@ export function onHover(e) {
for (let x = 0; x < idx.length; x++) { for (let x = 0; x < idx.length; x++) {
let obj = hoverPts[idx[x]].object let obj = hoverPts[idx[x]].object
if (this.obj3d.userData.type == 'sketch') { setHover(obj, 1, false)
obj.material.color.set(hoverColor[obj.userData.type])
} else {
if (obj.userData.type == 'mesh') {
obj.material.color.set(color['meshTempHover'])
} else if (obj.userData.type == 'plane') {
obj.material.opacity = 0.06
obj.children[0].material.color.set(hoverColor['planeBorder'])
} else if (obj.userData.type == 'point') {
if (this.obj3d.userData.type != 'sketch' && obj.userData.type == 'point') {
ptLoc = obj.geometry.attributes.position.array ptLoc = obj.geometry.attributes.position.array
.slice( .slice(
3 * hoverPts[idx[x]].index, 3 * hoverPts[idx[x]].index,
3 * hoverPts[idx[x]].index + 3 3 * hoverPts[idx[x]].index + 3
) )
// const pp = this.obj3d.children[0].children[this.fptIdx % 3] // const pp = this.obj3d.children[0].children[this.fptIdx % 3]
const pp = this.obj3d.children[0].children[0] const pp = this.obj3d.children[0].children[0]
pp.geometry.attributes.position.array.set(ptLoc) pp.geometry.attributes.position.array.set(ptLoc)
@ -96,12 +83,8 @@ export function onHover(e) {
pp.visible = true pp.visible = true
obj = hoverPts[idx[x]].index obj = hoverPts[idx[x]].index
} }
}
this.hovered.push(obj) this.hovered.push(obj)
} }
@ -114,14 +97,12 @@ export function onHover(e) {
for (let x = 0; x < this.hovered.length; x++) { for (let x = 0; x < this.hovered.length; x++) {
const obj = this.hovered[x] const obj = this.hovered[x]
if (typeof obj == 'object' && !this.selected.includes(obj)) { if (typeof obj == 'object' && !this.selected.includes(obj)) {
if (obj.userData.type == 'plane') { setHover(obj, 0)
obj.material.opacity = 0.02
obj.children[0].material.color.set(color['planeBorder'])
} else {
obj.material.color.set(color[obj.userData.type])
}
} }
} }
this.hovered = [] this.hovered = []
@ -145,12 +126,9 @@ export function onPick(e) {
this.selected.push(obj) this.selected.push(obj)
} else { } else {
if (typeof obj == 'object') { if (typeof obj == 'object') {
if (obj.userData.type == 'plane') {
obj.material.opacity = 0.06 setHover(obj, 1)
obj.children[0].material.color.set(hoverColor['planeBorder'])
} else {
obj.material.color.set(hoverColor[obj.userData.type])
}
} else { } else {
const pp = this.obj3d.children[0].children[this.fptIdx % 3 + 1] const pp = this.obj3d.children[0].children[this.fptIdx % 3 + 1]
const p0 = this.obj3d.children[0].children[0] const p0 = this.obj3d.children[0].children[0]
@ -198,16 +176,11 @@ export function onPick(e) {
} else { } else {
for (let x = 0; x < this.selected.length; x++) { for (let x = 0; x < this.selected.length; x++) {
const obj = this.selected[x] const obj = this.selected[x]
obj.material.color.set(color[obj.userData.type])
setHover(obj, 0)
if (obj.userData.type == 'selpoint') { if (obj.userData.type == 'selpoint') {
obj.visible = false obj.visible = false
} else {
if (obj.userData.type == 'plane') {
obj.material.opacity = 0.02
obj.children[0].material.color.set(color['planeBorder'])
} else {
obj.material.color.set(color[obj.userData.type])
}
} }
} }
this.obj3d.dispatchEvent({ type: 'change' }) this.obj3d.dispatchEvent({ type: 'change' })
@ -237,22 +210,55 @@ export function onDrag(e) {
} }
export function setHover(obj, state, meshHover = true) {
let colObj, visible
if (state == 1) {
colObj = hoverColor
visible = true
} else {
colObj = color
visible = false
}
switch (obj.userData.type) {
case 'plane':
obj.material.opacity = colObj.opacity
obj.children[0].material.color.set(colObj['planeBorder'])
break;
case 'sketch':
obj.visible = visible
break;
case 'mesh':
if (meshHover) {
obj.material.emissive.set(colObj.emissive)
} else {
break
}
default:
obj.material.color.set(colObj[obj.userData.type])
break;
}
// if (obj.userData.type == 'plane') {
// obj.material.opacity = colObj.opacity
// obj.children[0].material.color.set(colObj['planeBorder'])
// } else if (obj.userData.type != 'mesh') {
// obj.material.color.set(colObj[obj.userData.type])
// } else if (meshHover) {
// obj.material.emissive.set(colObj.emissive)
// obj.material.color.set(colObj[obj.userData.type])
// }
}
export function onRelease() { export function onRelease() {
this.canvas.removeEventListener('pointermove', this.onDrag) this.canvas.removeEventListener('pointermove', this.onDrag)
this.canvas.removeEventListener('pointermove', this.onDragDim) this.canvas.removeEventListener('pointermove', this.onDragDim)
this.canvas.removeEventListener('pointerup', this.onRelease) this.canvas.removeEventListener('pointerup', this.onRelease)
// for (let x = 3; x < this.obj3d.children.length; x++) {
// const obj = this.obj3d.children[x]
// obj.geometry.computeBoundingSphere()
// }
// for (let x = 0; x < this.obj3d.children[1].children.length; x++) {
// const obj = this.obj3d.children[1].children[x]
// obj.geometry.computeBoundingSphere()
// }
this.updateBoundingSpheres() this.updateBoundingSpheres()
if (draggedLabel) { if (draggedLabel) {
draggedLabel.style.zIndex = 0; draggedLabel.style.zIndex = 0;
} }

View File

@ -1,10 +1,7 @@
/* @tailwind base; */
@tailwind utilities; @tailwind utilities;
* { * {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
box-sizing: border-box; box-sizing: border-box;
} }
@ -52,19 +49,12 @@ body {
hover:bg-gray-500 hover:text-gray-200; hover:bg-gray-500 hover:text-gray-200;
} }
.btn-light {
cursor: pointer;
@apply fill-current
bg-transparent text-gray-700
hover:bg-gray-300 hover:text-gray-700;
}
.btn-green { .btn-green {
cursor: pointer; cursor: pointer;
@apply fill-current @apply fill-current
bg-transparent text-gray-200 bg-transparent text-gray-200
hover:bg-transparent hover:text-green-200; hover:bg-transparent hover:text-green-400;
} }

View File

@ -13,8 +13,7 @@ import * as Icon from "./icons";
export const NavBar = () => { export const NavBar = () => {
const dispatch = useDispatch() const dispatch = useDispatch()
const treeEntries = useSelector(state => state.treeEntries) const treeEntries = useSelector(state => state.treeEntries)
const activeSketchId = useSelector(state => state.activeSketchId) const activeSketchId = useSelector(state => state.treeEntries.activeSketchId)
useEffect(() => { useEffect(() => {
if (!activeSketchId) { if (!activeSketchId) {
@ -35,6 +34,7 @@ export const NavBar = () => {
// dispatch({ type: 'update-descendents', sketch}) // dispatch({ type: 'update-descendents', sketch})
sc.activeSketch = null sc.activeSketch = null
sc.render() sc.render()
forceUpdate()
// sc.activeDim = this.activeSketch.obj3d.children[1].children // sc.activeDim = this.activeSketch.obj3d.children[1].children
}, 'Finish'] : }, 'Finish'] :
[FaEdit, sc.addSketch, 'Sketch [s]'] [FaEdit, sc.addSketch, 'Sketch [s]']

View File

@ -10,10 +10,9 @@ export const preloadedState = {
allIds: [], allIds: [],
tree: {}, tree: {},
order: {}, order: {},
visible: {},
activeSketchId: ""
}, },
ui: {
toolTipImmediate: false
}
} }
export function reducer(state = {}, action) { export function reducer(state = {}, action) {
@ -24,18 +23,33 @@ export function reducer(state = {}, action) {
byId: { [action.obj.obj3d.name]: { $set: action.obj } }, byId: { [action.obj.obj3d.name]: { $set: action.obj } },
allIds: { $push: [action.obj.obj3d.name] }, allIds: { $push: [action.obj.obj3d.name] },
tree: { [action.obj.obj3d.name]: { $set: {} } }, tree: { [action.obj.obj3d.name]: { $set: {} } },
order: { [action.obj.obj3d.name]: { $set: state.treeEntries.allIds.length } } order: { [action.obj.obj3d.name]: { $set: state.treeEntries.allIds.length } },
visible: { [action.obj.obj3d.name]: { $set: true } },
}, },
}) })
case 'set-entry-visibility': {
return update(state, {
treeEntries: {
visible: { $merge: action.obj },
},
})
}
case 'set-active-sketch': case 'set-active-sketch':
return update(state, { return update(state, {
treeEntries: {
visible: { [action.sketch]: { $set: true } },
activeSketchId: { $set: action.sketch }, activeSketchId: { $set: action.sketch },
},
}) })
case 'exit-sketch': case 'exit-sketch':
return { return update(state, {
...state, activeSketchId: '' treeEntries: {
} activeSketchId: { $set: "" },
visible: { [state.treeEntries.activeSketchId]: { $set: false } },
},
})
case 'rx-extrusion': case 'rx-extrusion':
return update(state, { return update(state, {
@ -48,7 +62,10 @@ export function reducer(state = {}, action) {
[action.sketchId]: { [action.mesh.name]: { $set: true } }, [action.sketchId]: { [action.mesh.name]: { $set: true } },
[action.mesh.name]: { $set: {} } [action.mesh.name]: { $set: {} }
}, },
order: { [action.mesh.name]: { $set: state.treeEntries.allIds.length } } order: { [action.mesh.name]: { $set: state.treeEntries.allIds.length } },
visible: {
[action.mesh.name]: { $set: true }
}
} }
}) })
case 'rx-boolean': case 'rx-boolean':
@ -62,6 +79,7 @@ export function reducer(state = {}, action) {
tree: { tree: {
[action.deps[0]]: { [action.mesh.name]: { $set: true } }, [action.deps[0]]: { [action.mesh.name]: { $set: true } },
[action.deps[1]]: { [action.mesh.name]: { $set: true } }, [action.deps[1]]: { [action.mesh.name]: { $set: true } },
[action.mesh.name]: { $set: {} }
}, },
order: { [action.mesh.name]: { $set: state.treeEntries.allIds.length } } order: { [action.mesh.name]: { $set: state.treeEntries.allIds.length } }
} }

View File

@ -1,8 +1,8 @@
import React, { useReducer } from 'react'; import React, { useReducer, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { MdEdit, MdVisibilityOff, MdVisibility, MdDelete } from 'react-icons/md' import { MdVisibilityOff, MdVisibility, MdDelete } from 'react-icons/md'
import { FaCube, FaEdit } from 'react-icons/fa' import { FaCube, FaEdit } from 'react-icons/fa'
@ -29,89 +29,100 @@ const TreeEntry = ({ entId }) => {
const treeEntries = useSelector(state => state.treeEntries.byId) const treeEntries = useSelector(state => state.treeEntries.byId)
const dispatch = useDispatch() const dispatch = useDispatch()
const activeSketchId = useSelector(state => state.activeSketchId) const activeSketchId = useSelector(state => state.treeEntries.activeSketchId)
// const activeSketchId = treeEntries.activeSketchId
let obj3d, entry; const visible = useSelector(state => state.treeEntries.visible[entId])
let obj3d, sketch;
entry = treeEntries[entId]
if (treeEntries[entId].obj3d) { if (treeEntries[entId].obj3d) {
obj3d = treeEntries[entId].obj3d obj3d = treeEntries[entId].obj3d
sketch = treeEntries[entId]
} else { } else {
obj3d = treeEntries[entId] obj3d = treeEntries[entId]
} }
console.log(obj3d.userData.type)
let Icon = treeIcons[obj3d.userData.type] let Icon = treeIcons[obj3d.userData.type]
const [_, forceUpdate] = useReducer(x => x + 1, 0); const [_, forceUpdate] = useReducer(x => x + 1, 0);
// const vis = obj3d.visible
const vis = obj3d.layers.mask & 1 // const vis = obj3d.layers.mask & 1
return <div className='btn select-none flex justify-start w-full h-7 items-center text-sm' return <div className='btn select-none flex justify-start w-full h-7 items-center text-sm'
onDoubleClick={() => { onDoubleClick={() => {
if (entId[0] == 's') {
activeSketchId && treeEntries[activeSketchId].deactivate() activeSketchId && treeEntries[activeSketchId].deactivate()
console.log(entry) sketch.activate()
entry.activate()
sc.clearSelection() sc.clearSelection()
sc.activeSketch = entry; sc.activeSketch = sketch;
}}
>
<Icon className='h-full w-auto p-1.5' />
<div className="btn pl-1"
onPointerEnter={() => {
if (entId[0] == 'm') {
// entry.material.color.set(color.hover)
sc.render()
} }
}}
onPointerEnter={() => {
sc.setHover(obj3d, 1)
sc.render()
}} }}
onPointerLeave={() => { onPointerLeave={() => {
const obj = entry // console.log('activeid',activeSketchId,'visstate',visState)
if (entId[0] == 'm' && !sc.selected.includes(obj)) { if (visible & entId[0] == 's') return
// obj.material.color.set(color.mesh) if (sc.selected.includes(obj3d) || activeSketchId == obj3d.name) return
sc.setHover(obj3d, 0)
sc.render() sc.render()
}
}} }}
onPointerDown={() => { onClick={() => {
if (entId[0] == 'm') { if (entId[0] == 'm') {
sc.selected.push( sc.selected.push(
entry obj3d
) )
sc.render() sc.render()
} }
}} }}
> >
<Icon className='h-full w-auto p-1.5' />
<div className="btn pl-1">
{entId} {entId}
</div> </div>
<div className='flex h-full ml-auto'> <div className='flex h-full ml-auto'>
<MdDelete className='btn-green h-full w-auto p-1.5' <MdDelete className='btn-green h-full w-auto p-1.5'
onClick={() => { onClick={(e) => {
dispatch({ type: 'delete-node', id: entId }) dispatch({ type: 'delete-node', id: entId })
sc.render()
e.stopPropagation()
}} }}
/> />
{ {
vis ? visible ?
<MdVisibility className='btn-green h-full w-auto p-1.5' <MdVisibility className='btn-green h-full w-auto p-1.5'
onClick={() => { onClick={(e) => {
obj3d.traverse((e) => e.layers.disableAll()) e.stopPropagation()
console.log('hide')
dispatch({ type: "set-entry-visibility", obj: {[entId]:false} })
obj3d.visible = false;
if (obj3d.userData.type == 'mesh') {
obj3d.traverse((e) => e.layers.disable(1))
}
sc.render() sc.render()
forceUpdate() forceUpdate()
}} }}
/> />
: :
<MdVisibilityOff className='btn-green h-full w-auto p-1.5' <MdVisibilityOff className='btn-green h-full w-auto p-1.5'
onClick={() => { onClick={(e) => {
if (obj3d.userData.type == 'sketch') { e.stopPropagation()
obj3d.traverse((e) => e.layers.enable(0)) console.log('show')
} else { obj3d.visible = true;
dispatch({ type: "set-entry-visibility", obj: {[entId]:true} })
if (obj3d.userData.type == 'mesh') {
obj3d.traverse((e) => { obj3d.traverse((e) => {
e.layers.enable(0)
e.layers.enable(1) e.layers.enable(1)
}) })
} }
sc.render() sc.render()
forceUpdate() forceUpdate()

View File

@ -22,18 +22,23 @@ const color = {
line: 0xffffff, line: 0xffffff,
mesh: 0x9DCFED, mesh: 0x9DCFED,
dimension: 0x0000ff, dimension: 0x0000ff,
plane: 0xffff00, plane: 0xffff00,
planeBorder: 0x2e2e00, planeBorder: 0x2e2e00,
opacity: 0.02
} }
const hoverColor = { const hoverColor = {
emissive: 0x343407,
point: 0x00ff00, point: 0x00ff00,
selpoint: 0xff0000, selpoint: 0xff0000,
line: 0x00ff00, line: 0x00ff00,
mesh: 0xFAB601, mesh: 0xFAB601,
dimension: 0x00ff00, dimension: 0x00ff00,
plane: 0xffff00, plane: 0xffff00,
planeBorder: 0x919100, planeBorder: 0x919100,
opacity: 0.06
} }

View File

@ -12,15 +12,16 @@ boolean flesh out refresh / replace mesh
- hidden bodies messes up hover highlight \\ fixed - hidden bodies messes up hover highlight \\ fixed
- add for union and intersect - add for union and intersect
- consume skeches after extrude // done - consume skeches after extrude // done
- selection hover disspates when rehovered //fixed
- boolean unable to select click //fixed
vertical // done vertical // done
horizontal // done horizontal // done
- hover sync between tree and work area
- select sketch for extrusion - select sketch for extrusion
- boolean unable to select click
auto update extrude auto update extrude
extrude dialogue extrude dialogue