mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-07 08:53:25 +01:00
global coordinate system widget
This commit is contained in:
parent
88ce8f3890
commit
fe9d7c1a81
12 changed files with 409 additions and 25 deletions
67
modules/scene/cameraControlRenderer.js
Normal file
67
modules/scene/cameraControlRenderer.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import {AmbientLight, PerspectiveCamera, Scene, SpotLight, WebGLRenderer} from 'three';
|
||||
import DPR from '../dpr';
|
||||
import {MeshArrow} from './objects/auxiliary';
|
||||
import * as SceneGraph from './sceneGraph';
|
||||
import {AXIS} from '../../web/app/math/l3space';
|
||||
|
||||
export default function(container) {
|
||||
|
||||
function createBasisArrow(axis, color) {
|
||||
return new MeshArrow(axis, color, 1, 0.3, 0.15, 0.02);
|
||||
}
|
||||
|
||||
let xAxis = createBasisArrow(AXIS.X, 0xFF0000);
|
||||
let yAxis = createBasisArrow(AXIS.Y, 0x00FF00);
|
||||
let zAxis = createBasisArrow(AXIS.Z, 0x0000FF);
|
||||
|
||||
let root = SceneGraph.createGroup();
|
||||
let csys = SceneGraph.createGroup();
|
||||
|
||||
let scene = new Scene();
|
||||
csys.add(xAxis);
|
||||
csys.add(yAxis);
|
||||
csys.add(zAxis);
|
||||
|
||||
root.add(csys);
|
||||
scene.add(root);
|
||||
|
||||
let ambientLight = new AmbientLight(0x0f0f0f);
|
||||
scene.add(ambientLight);
|
||||
|
||||
let spotLight = new SpotLight(0xffffff);
|
||||
spotLight.position.set(0, 0, 5);
|
||||
spotLight.castShadow = true;
|
||||
scene.add(spotLight);
|
||||
|
||||
let camera = new PerspectiveCamera( 25, 1, 0.1, 2000 );
|
||||
camera.position.x = 0;
|
||||
camera.position.y = 0;
|
||||
camera.position.z = 5;
|
||||
|
||||
let renderer = new WebGLRenderer({ alpha: true });
|
||||
renderer.setPixelRatio(DPR);
|
||||
// renderer.setClearColor(0x000000, 1);
|
||||
// renderer.setClearAlpha(0);
|
||||
renderer.setSize( container.clientWidth, container.clientHeight );
|
||||
container.appendChild( renderer.domElement );
|
||||
|
||||
function renderScene() {
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
function render(cameraToSync) {
|
||||
root.quaternion.setFromRotationMatrix( cameraToSync.matrixWorldInverse );
|
||||
renderScene();
|
||||
}
|
||||
|
||||
function dispose() {
|
||||
xAxis.dispose();
|
||||
yAxis.dispose();
|
||||
zAxis.dispose();
|
||||
renderer.dispose();
|
||||
}
|
||||
|
||||
return {
|
||||
render, dispose
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
import DPR from 'dpr';
|
||||
import {ArrowHelper, Vector3} from 'three';
|
||||
import {ArrowHelper, CylinderBufferGeometry, Mesh, MeshBasicMaterial, Object3D, Vector3} from 'three';
|
||||
import {createMeshLineGeometry} from './meshLine';
|
||||
|
||||
export function createArrow(length, arrowLength, arrowHead, axis, color, opacity, materialMixins) {
|
||||
let arrow = new ArrowHelper(new Vector3().copy(axis), new Vector3(0, 0, 0), length, color, arrowLength, arrowHead);
|
||||
arrow.updateMatrix();
|
||||
arrow.line.material.linewidth = 1/DPR;
|
||||
arrow.line.material.linewidth = 1 / DPR;
|
||||
if (opacity !== undefined) {
|
||||
arrow.line.material.opacity = opacity;
|
||||
arrow.line.material.transparent = true;
|
||||
|
|
@ -17,3 +18,63 @@ export function createArrow(length, arrowLength, arrowHead, axis, color, opacity
|
|||
}
|
||||
return arrow;
|
||||
}
|
||||
|
||||
let tipGeometry = null;
|
||||
let lineGeometry = null;
|
||||
|
||||
export class MeshArrow extends Object3D {
|
||||
|
||||
constructor(dir, color, length, headLength, headWidth, lineWidth) {
|
||||
super();
|
||||
|
||||
if (color === undefined) color = 0xffff00;
|
||||
if (length === undefined) length = 1;
|
||||
if (headLength === undefined) headLength = 0.2 * length;
|
||||
if (headWidth === undefined) headWidth = 0.2 * headLength;
|
||||
if (lineWidth === undefined) lineWidth = 0.2 * headWidth;
|
||||
|
||||
if (!tipGeometry) {
|
||||
tipGeometry = new CylinderBufferGeometry(0, 0.5, 1, 5, 1);
|
||||
tipGeometry.translate(0, -0.5, 0);
|
||||
lineGeometry = createMeshLineGeometry([[0, 0, 0], [0, 1, 0]], 1);
|
||||
}
|
||||
|
||||
// dir is assumed to be normalized
|
||||
|
||||
let cone = new Mesh(tipGeometry, new MeshBasicMaterial({color}));
|
||||
let line = new Mesh(lineGeometry, new MeshBasicMaterial({color}));
|
||||
|
||||
line.matrixAutoUpdate = false;
|
||||
cone.matrixAutoUpdate = false;
|
||||
|
||||
this.add(line);
|
||||
this.add(cone);
|
||||
|
||||
if (dir.y > 0.99999) {
|
||||
this.quaternion.set(0, 0, 0, 1);
|
||||
} else if (dir.y < -0.99999) {
|
||||
this.quaternion.set(1, 0, 0, 0);
|
||||
} else {
|
||||
let axis = new Vector3();
|
||||
let radians;
|
||||
axis.set(dir.z, 0, -dir.x).normalize();
|
||||
radians = Math.acos(dir.y);
|
||||
this.quaternion.setFromAxisAngle(axis, radians);
|
||||
}
|
||||
|
||||
line.scale.set(lineWidth, Math.max(0, length - headLength), lineWidth);
|
||||
line.updateMatrix();
|
||||
|
||||
cone.scale.set(headWidth, headLength, headWidth);
|
||||
cone.position.y = length;
|
||||
cone.updateMatrix();
|
||||
|
||||
this.cone = cone;
|
||||
this.line = line;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.cone.material.dispose();
|
||||
this.line.material.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
145
modules/scene/objects/facetedCube.js
Normal file
145
modules/scene/objects/facetedCube.js
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
import {Face3, Geometry, Vector3} from 'three';
|
||||
|
||||
export default function facetedCube(size, w) {
|
||||
let d = size * 0.5;
|
||||
let l = d - w;
|
||||
const v = (x,y,z) => new Vector3(x,y,z);
|
||||
let geom = new Geometry();
|
||||
|
||||
//front
|
||||
geom.vertices.push(v(-l, -l, d));
|
||||
geom.vertices.push(v(l, -l, d));
|
||||
geom.vertices.push(v(l, l, d));
|
||||
geom.vertices.push(v(-l, l, d));
|
||||
|
||||
//top
|
||||
geom.vertices.push(v(-l, d, l));
|
||||
geom.vertices.push(v(l, d, l));
|
||||
geom.vertices.push(v(l, d, -l));
|
||||
geom.vertices.push(v(-l, d, -l));
|
||||
|
||||
//back
|
||||
geom.vertices.push(v(-l, -l, -d));
|
||||
geom.vertices.push(v(l, -l, -d));
|
||||
geom.vertices.push(v(l, l, -d));
|
||||
geom.vertices.push(v(-l, l, -d));
|
||||
|
||||
//bottom
|
||||
geom.vertices.push(v(-l, -d, l));
|
||||
geom.vertices.push(v(l, -d, l));
|
||||
geom.vertices.push(v(l, -d, -l));
|
||||
geom.vertices.push(v(-l, -d, -l));
|
||||
|
||||
//left
|
||||
geom.vertices.push(v(-d, -l, -l));
|
||||
geom.vertices.push(v(-d, -l, l));
|
||||
geom.vertices.push(v(-d, l, l));
|
||||
geom.vertices.push(v(-d, l, -l));
|
||||
|
||||
//right
|
||||
geom.vertices.push(v(d, -l, -l));
|
||||
geom.vertices.push(v(d, -l, l));
|
||||
geom.vertices.push(v(d, l, l));
|
||||
geom.vertices.push(v(d, l, -l));
|
||||
|
||||
//front
|
||||
geom.faces.push( new Face3( 0, 1, 2 ) );
|
||||
geom.faces.push( new Face3( 2, 3, 0 ) );
|
||||
|
||||
//top
|
||||
geom.faces.push( new Face3( 4, 5, 6 ) );
|
||||
geom.faces.push( new Face3( 6, 7, 4 ) );
|
||||
|
||||
//back
|
||||
geom.faces.push( new Face3( 10, 9, 8) );
|
||||
geom.faces.push( new Face3( 8, 11, 10 ) );
|
||||
|
||||
//bottom
|
||||
geom.faces.push( new Face3( 14, 13, 12) );
|
||||
geom.faces.push( new Face3( 12, 15, 14 ) );
|
||||
|
||||
//left
|
||||
geom.faces.push( new Face3( 16, 17, 18 ) );
|
||||
geom.faces.push( new Face3( 18, 19, 16 ) );
|
||||
|
||||
// right
|
||||
geom.faces.push( new Face3( 22, 21, 20 ) );
|
||||
geom.faces.push( new Face3( 20, 23, 22 ) );
|
||||
|
||||
//front-top
|
||||
geom.faces.push( new Face3( 4, 3, 2) );
|
||||
geom.faces.push( new Face3( 2, 5, 4 ) );
|
||||
|
||||
//top-back
|
||||
geom.faces.push( new Face3( 7, 6, 10) );
|
||||
geom.faces.push( new Face3( 10, 11, 7 ) );
|
||||
|
||||
// back-bottom
|
||||
geom.faces.push( new Face3( 8, 9, 14) );
|
||||
geom.faces.push( new Face3( 14, 15, 8 ) );
|
||||
|
||||
//bottom-left
|
||||
geom.faces.push( new Face3( 15, 12, 17) );
|
||||
geom.faces.push( new Face3( 17, 16, 15 ) );
|
||||
|
||||
//bottom-right
|
||||
geom.faces.push( new Face3( 20, 21, 13) );
|
||||
geom.faces.push( new Face3( 13, 14, 20 ) );
|
||||
|
||||
//top-right
|
||||
geom.faces.push( new Face3( 6, 5, 22) );
|
||||
geom.faces.push( new Face3( 22, 23, 6 ) );
|
||||
|
||||
//top-left
|
||||
geom.faces.push( new Face3( 19, 18, 4) );
|
||||
geom.faces.push( new Face3( 4, 7, 19 ) );
|
||||
|
||||
//front-left
|
||||
geom.faces.push( new Face3( 18, 17, 0) );
|
||||
geom.faces.push( new Face3( 0, 3, 18 ) );
|
||||
|
||||
//front-bottom
|
||||
geom.faces.push( new Face3( 12, 13, 1) );
|
||||
geom.faces.push( new Face3( 1, 0, 12 ) );
|
||||
|
||||
//right-back
|
||||
geom.faces.push( new Face3( 9, 10, 23) );
|
||||
geom.faces.push( new Face3( 23, 20, 9 ) );
|
||||
|
||||
//front-right
|
||||
geom.faces.push( new Face3( 21, 22, 2) );
|
||||
geom.faces.push( new Face3( 2, 1, 21 ) );
|
||||
|
||||
//back-left
|
||||
geom.faces.push( new Face3( 16, 19, 11) );
|
||||
geom.faces.push( new Face3( 11, 8, 16 ) );
|
||||
|
||||
|
||||
//front-top-left
|
||||
geom.faces.push( new Face3( 4, 18, 3 ) );
|
||||
|
||||
//front-top-right
|
||||
geom.faces.push( new Face3( 2, 22, 5 ) );
|
||||
|
||||
//top-right-back
|
||||
geom.faces.push( new Face3( 23, 10, 6 ) );
|
||||
|
||||
// top-left-back
|
||||
geom.faces.push( new Face3( 7, 11, 19 ) );
|
||||
|
||||
// front-left-bottom
|
||||
geom.faces.push( new Face3( 17, 12, 0 ) );
|
||||
|
||||
// front-right-bottom
|
||||
geom.faces.push( new Face3( 1, 13, 21 ) );
|
||||
|
||||
// back-right-bottom
|
||||
geom.faces.push( new Face3( 20, 14, 9 ) );
|
||||
|
||||
// back-left-bottom
|
||||
geom.faces.push( new Face3( 8, 15, 16 ) );
|
||||
|
||||
geom.computeFaceNormals();
|
||||
|
||||
return geom;
|
||||
}
|
||||
45
modules/scene/objects/meshLine.js
Normal file
45
modules/scene/objects/meshLine.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import * as vec from '../../../web/app/math/vec';
|
||||
import {perpendicularVector} from '../../../web/app/math/math';
|
||||
import {Face3, Geometry, Vector3} from 'three';
|
||||
|
||||
export function createMeshLineGeometry(points, width) {
|
||||
const vThree = arr => new Vector3().fromArray(arr);
|
||||
const geometry = new Geometry();
|
||||
let base = null;
|
||||
for (let i = 1; i < points.length; i++) {
|
||||
|
||||
let a = points[i - 1];
|
||||
let b = points[i];
|
||||
let ab = vec._normalize(vec.sub(b, a));
|
||||
|
||||
let dirs = [];
|
||||
dirs[0] = perpendicularVector(ab);
|
||||
dirs[1] = vec.cross(ab, dirs[0]);
|
||||
dirs[2] = vec.negate(dirs[0]);
|
||||
dirs[3] = vec.negate(dirs[1]);
|
||||
|
||||
dirs.forEach(d => vec._mul(d, width));
|
||||
if (base === null) {
|
||||
base = dirs.map(d => vec.add(a, d));
|
||||
}
|
||||
let lid = dirs.map(d => vec.add(b, d));
|
||||
|
||||
let off = geometry.vertices.length;
|
||||
base.forEach(p => geometry.vertices.push(vThree(p)));
|
||||
lid.forEach(p => geometry.vertices.push(vThree(p)));
|
||||
base = lid;
|
||||
|
||||
[
|
||||
[0, 4, 3],
|
||||
[3, 4, 7],
|
||||
[2, 3, 7],
|
||||
[7, 6, 2],
|
||||
[0, 1, 5],
|
||||
[5, 4, 0],
|
||||
[1, 2, 6],
|
||||
[6, 5, 1],
|
||||
].forEach(([a, b, c]) => geometry.faces.push(new Face3(a + off, b + off, c + off)));
|
||||
}
|
||||
geometry.computeFaceNormals();
|
||||
return geometry;
|
||||
}
|
||||
|
|
@ -4,12 +4,13 @@ import './utils/vectorThreeEnhancement';
|
|||
|
||||
export default class SceneSetUp {
|
||||
|
||||
constructor(container) {
|
||||
constructor(container, onRendered) {
|
||||
|
||||
this.container = container;
|
||||
this.scene = new THREE.Scene();
|
||||
this.rootGroup = this.scene;
|
||||
|
||||
this.onRendered = onRendered;
|
||||
|
||||
this.setUpCamerasAndLights();
|
||||
this.setUpControls();
|
||||
|
||||
|
|
@ -195,6 +196,7 @@ export default class SceneSetUp {
|
|||
render() {
|
||||
this.light.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z);
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
this.onRendered();
|
||||
};
|
||||
|
||||
domElement() {
|
||||
|
|
|
|||
34
web/app/cad/dom/components/CameraControl.jsx
Normal file
34
web/app/cad/dom/components/CameraControl.jsx
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ls from './CameraControl.less';
|
||||
import mapContext from 'ui/mapContext';
|
||||
import cameraControlRenderer from 'scene/cameraControlRenderer';
|
||||
|
||||
@mapContext(({services, streams}) => ({
|
||||
getCamera: () => services.viewer && services.viewer.sceneSetup.camera,
|
||||
sceneRendered$: streams.cadScene.sceneRendered
|
||||
}))
|
||||
export default class CameraControl extends React.Component {
|
||||
|
||||
render () {
|
||||
return <div className={ls.cameraControl} />
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let container = ReactDOM.findDOMNode(this);
|
||||
this.renderer = cameraControlRenderer(container);
|
||||
this.detacher = this.props.sceneRendered$.attach(() => {
|
||||
let camera = this.props.getCamera();
|
||||
if (camera) {
|
||||
this.renderer.render(camera)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.detacher();
|
||||
this.renderer.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
10
web/app/cad/dom/components/CameraControl.less
Normal file
10
web/app/cad/dom/components/CameraControl.less
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
@size: 80px;
|
||||
|
||||
.cameraControl {
|
||||
top: -@size;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
width: @size;
|
||||
height: @size;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
|
@ -8,9 +8,9 @@ import WizardManager from '../../craft/wizard/components/WizardManager';
|
|||
import FloatView from './FloatView';
|
||||
import HistoryTimeline from '../../craft/ui/HistoryTimeline';
|
||||
import SelectedModificationInfo from '../../craft/ui/SelectedModificationInfo';
|
||||
import Explorer from './Explorer';
|
||||
import BottomStack from './BottomStack';
|
||||
import SketcherToolbars from './SketcherToolbars';
|
||||
import CameraControl from './CameraControl';
|
||||
|
||||
|
||||
export default class View3d extends React.Component {
|
||||
|
|
@ -33,6 +33,7 @@ export default class View3d extends React.Component {
|
|||
</Abs>
|
||||
</Abs>
|
||||
<BottomStack>
|
||||
<CameraControl />
|
||||
<HistoryTimeline />
|
||||
<PlugableControlBar/>
|
||||
</BottomStack>
|
||||
|
|
|
|||
|
|
@ -57,7 +57,11 @@ export default function startApplication(callback) {
|
|||
...applicationPlugins,
|
||||
ViewSyncPlugin
|
||||
];
|
||||
|
||||
let allPlugins = [...preUIPlugins, ...plugins];
|
||||
|
||||
defineStreams(allPlugins, context);
|
||||
|
||||
activatePlugins(preUIPlugins, context);
|
||||
|
||||
startReact(context, () => {
|
||||
|
|
@ -68,6 +72,14 @@ export default function startApplication(callback) {
|
|||
});
|
||||
}
|
||||
|
||||
export function defineStreams(plugins, context) {
|
||||
for (let plugin of plugins) {
|
||||
if (plugin.defineStreams) {
|
||||
plugin.defineStreams(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function activatePlugins(plugins, context) {
|
||||
for (let plugin of plugins) {
|
||||
plugin.activate(context);
|
||||
|
|
|
|||
|
|
@ -1,19 +1,24 @@
|
|||
import Viewer, {CAMERA_MODE} from './viewer';
|
||||
import CadScene from "./cadScene";
|
||||
import {externalState} from '../../../../modules/lstream';
|
||||
import {InPlaceSketcher} from '../sketch/inPlaceSketcher';
|
||||
import Viewer from './viewer';
|
||||
import CadScene from './cadScene';
|
||||
import {externalState, stream} from 'lstream';
|
||||
|
||||
export function defineStreams({streams, services}) {
|
||||
streams.cadScene = {
|
||||
sceneRendered: stream(),
|
||||
cameraMode: externalState(() => services.viewer.getCameraMode(), mode => viewer.setCameraMode(mode))
|
||||
};
|
||||
}
|
||||
|
||||
export function activate({streams, services}) {
|
||||
let {dom} = services;
|
||||
|
||||
let viewer = new Viewer(dom.viewerContainer);
|
||||
const onRendered = () => streams.cadScene.sceneRendered.next();
|
||||
|
||||
let viewer = new Viewer(dom.viewerContainer, onRendered);
|
||||
|
||||
services.viewer = viewer;
|
||||
services.cadScene = new CadScene(viewer.sceneSetup.rootGroup);
|
||||
|
||||
streams.cadScene = {
|
||||
cameraMode: externalState(() => viewer.getCameraMode(), mode => viewer.setCameraMode(mode))
|
||||
};
|
||||
|
||||
|
||||
// let sketcher3D = new Sketcher3D(dom.viewerContainer);
|
||||
// services.viewer.setCameraMode(CAMERA_MODE.ORTHOGRAPHIC);
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import SceneSetup from 'scene/sceneSetup';
|
|||
|
||||
export default class Viewer {
|
||||
|
||||
constructor(container) {
|
||||
this.sceneSetup = new SceneSetup(container);
|
||||
constructor(container, onRendered) {
|
||||
this.sceneSetup = new SceneSetup(container, onRendered);
|
||||
this.renderRequested = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
import Vector from 'math/vector';
|
||||
|
||||
var ORIGIN = new Vector(0, 0, 0);
|
||||
const freeze = Object.freeze;
|
||||
|
||||
var AXIS = {
|
||||
X : new Vector(1, 0, 0),
|
||||
Y : new Vector(0, 1, 0),
|
||||
Z : new Vector(0, 0, 1)
|
||||
};
|
||||
const ORIGIN = freeze(new Vector(0, 0, 0));
|
||||
|
||||
var IDENTITY_BASIS = [AXIS.X, AXIS.Y, AXIS.Z];
|
||||
const AXIS = freeze({
|
||||
X : freeze(new Vector(1, 0, 0)),
|
||||
Y : freeze(new Vector(0, 1, 0)),
|
||||
Z : freeze(new Vector(0, 0, 1))
|
||||
});
|
||||
|
||||
export const STANDARD_BASES = {
|
||||
const IDENTITY_BASIS = Object.freeze([AXIS.X, AXIS.Y, AXIS.Z]);
|
||||
|
||||
export const STANDARD_BASES = freeze({
|
||||
'XY': IDENTITY_BASIS,
|
||||
'XZ': [AXIS.X, AXIS.Z, AXIS.Y],
|
||||
'ZY': [AXIS.Z, AXIS.Y, AXIS.X]
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
/** @constructor */
|
||||
|
|
|
|||
Loading…
Reference in a new issue