mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 16:33:15 +01:00
wizard react support
This commit is contained in:
parent
ea5a3ae93e
commit
ceb9b89616
36 changed files with 580 additions and 262 deletions
|
|
@ -105,6 +105,10 @@ export default class Bus {
|
|||
this.state[forToken] = initValue;
|
||||
}
|
||||
|
||||
disableState(forToken) {
|
||||
this.keepStateFor.delete(forToken);
|
||||
delete this.state[forToken];
|
||||
}
|
||||
}
|
||||
|
||||
export function createToken(...fqn) {
|
||||
|
|
|
|||
25
modules/gems/camelCaseSplit.js
Normal file
25
modules/gems/camelCaseSplit.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
export default function camelCaseSplit(str) {
|
||||
function isUpperCase(str) {
|
||||
return str.toUpperCase() === str;
|
||||
}
|
||||
|
||||
const words = [];
|
||||
let word = '';
|
||||
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const c = str.charAt(i);
|
||||
if (c === '_' || c === '-') {
|
||||
continue;
|
||||
}
|
||||
const dot = c === '.';
|
||||
if ((dot || isUpperCase(c)) && word.length !== 0) {
|
||||
words.push(word);
|
||||
word = '';
|
||||
}
|
||||
if (!dot) word += c;
|
||||
}
|
||||
if (word.length !== 0){
|
||||
words.push(word);
|
||||
}
|
||||
return words;
|
||||
}
|
||||
23
modules/gems/shallowEqual.js
Normal file
23
modules/gems/shallowEqual.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
export default function shallowEqual(objA, objB) {
|
||||
if (objA === objB) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let aKeys = Object.keys(objA);
|
||||
let bKeys = Object.keys(objB);
|
||||
let len = aKeys.length;
|
||||
|
||||
if (bKeys.length !== len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
let key = aKeys[i];
|
||||
|
||||
if (objA[key] !== objB[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
20
modules/scene/geoms.js
Normal file
20
modules/scene/geoms.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
export function createBoxGeometry(width, height, depth) {
|
||||
return new THREE.BoxGeometry(width, height, depth);
|
||||
}
|
||||
|
||||
export function createMeshGeometry(triangles) {
|
||||
const geometry = new THREE.Geometry();
|
||||
|
||||
for (let tr of triangles) {
|
||||
const a = geometry.vertices.length;
|
||||
const b = a + 1;
|
||||
const c = a + 2;
|
||||
const face = new THREE.Face3(a, b, c);
|
||||
tr.forEach(v => geometry.vertices.push(v.three()));
|
||||
geometry.faces.push(face);
|
||||
}
|
||||
geometry.mergeVertices();
|
||||
geometry.computeFaceNormals();
|
||||
return geometry;
|
||||
}
|
||||
9
modules/scene/objects/mesh.js
Normal file
9
modules/scene/objects/mesh.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import {createMeshGeometry} from '../geoms';
|
||||
|
||||
export function createMesh(geometry, material) {
|
||||
return new THREE.Mesh(geometry, material);
|
||||
}
|
||||
|
||||
export function createMeshFromTriangles(triangles, material) {
|
||||
return createMesh(createMeshGeometry(triangles), material);
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import DPR from 'dpr';
|
||||
import './utils/threeLoader'
|
||||
import './utils/threeLoader';
|
||||
import './utils/vectorThreeEnhancement';
|
||||
|
||||
export default class SceneSetUp {
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ export default class Window extends React.Component {
|
|||
width: this.state.width,
|
||||
height: this.state.height,
|
||||
left: this.state.left,
|
||||
top: this.state.top
|
||||
top: this.state.top,
|
||||
zIndex: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import React from 'react';
|
|||
|
||||
import ls from './Button.less'
|
||||
|
||||
export default function Button({text, type}) {
|
||||
export default function Button({text, type, onClick}) {
|
||||
|
||||
return <button className={ls[type]}>{text}</button>
|
||||
return <button onClick={onClick} className={ls[type]}>{text}</button>
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export default class InputControl extends React.Component {
|
|||
let {type, inputRef, ...props} = this.props;
|
||||
|
||||
return <div className={ls[type]}>
|
||||
<input type='text' ref={inputRef} {...props} spellcheck='false' />
|
||||
<input type='text' ref={inputRef} {...props} spellCheck='false' />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,7 @@ export default class NumberControl extends React.Component {
|
|||
|
||||
onWheel = (e) => {
|
||||
let {baseStep, round, min, max, onChange, accelerator} = this.props;
|
||||
let delta = 0;
|
||||
if ( e.wheelDelta ) { // WebKit / Opera / Explorer 9
|
||||
delta = e.wheelDelta;
|
||||
} else if ( e.detail ) { // Firefox
|
||||
delta = - e.detail;
|
||||
}
|
||||
let delta = e.deltaY;
|
||||
let val = e.target.value;
|
||||
if (!val) val = 0;
|
||||
let step = baseStep * (e.shiftKey ? accelerator : 1);
|
||||
|
|
|
|||
|
|
@ -11,19 +11,3 @@ export default class TextControl extends React.Component {
|
|||
onChange={e => onChange(e.target.value)} />
|
||||
}
|
||||
}
|
||||
|
||||
TextControl.propTypes = {
|
||||
baseStep: PropTypes.number,
|
||||
round: PropTypes.number,
|
||||
min: PropTypes.number,
|
||||
max: PropTypes.number,
|
||||
accelerator: PropTypes.number,
|
||||
initValue: PropTypes.number.isRequired,
|
||||
onChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
TextControl.defaultProps = {
|
||||
baseStep: 1,
|
||||
round: 0,
|
||||
accelerator: 100
|
||||
};
|
||||
|
|
@ -1,15 +1,16 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import shallowEqual from "../gems/shallowEqual";
|
||||
|
||||
export default function connect(tokens, WrappedComponent, staticProps, mapper, dispatchMapper) {
|
||||
export default function connect(WrappedComponent, tokens, {staticProps, mapProps, mapActions}) {
|
||||
|
||||
if (!Array.isArray(tokens)) {
|
||||
tokens = [tokens];
|
||||
}
|
||||
|
||||
mapper = createMapper(mapper);
|
||||
mapProps = createMapper(mapProps);
|
||||
|
||||
dispatchMapper = dispatchMapper || function(dispatch) {
|
||||
mapActions = mapActions || function(dispatch) {
|
||||
return dispatch;
|
||||
};
|
||||
|
||||
|
|
@ -19,7 +20,7 @@ export default function connect(tokens, WrappedComponent, staticProps, mapper, d
|
|||
super();
|
||||
this.mounted = false;
|
||||
this.stateProps = {};
|
||||
this.dispatchProps = dispatchMapper(this.dispatch);
|
||||
this.dispatchProps = mapActions(this.dispatch);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
|
|
@ -37,12 +38,17 @@ export default function connect(tokens, WrappedComponent, staticProps, mapper, d
|
|||
}
|
||||
|
||||
setExternalState = (state) => {
|
||||
this.stateProps = mapper(state);
|
||||
this.stateProps = mapProps(state);
|
||||
if (this.mounted) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return !shallowEqual(this.props, nextProps);
|
||||
|
||||
}
|
||||
|
||||
dispatch = (event, data) => {
|
||||
this.context.bus.dispatch(event, data);
|
||||
};
|
||||
|
|
@ -51,6 +57,9 @@ export default function connect(tokens, WrappedComponent, staticProps, mapper, d
|
|||
return <WrappedComponent {...this.stateProps} {...this.dispatchProps} {...staticProps} />
|
||||
}
|
||||
|
||||
componentDidCatch() {
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
bus: PropTypes.object
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,12 +29,6 @@ const OPERATION_ACTIONS = [
|
|||
},
|
||||
...requiresFaceSelection(1)
|
||||
},
|
||||
{
|
||||
id: 'BOX',
|
||||
appearance: {
|
||||
info: 'creates new object box'
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'PLANE',
|
||||
appearance: {
|
||||
|
|
@ -78,7 +72,7 @@ const OPERATION_ACTIONS = [
|
|||
|
||||
function mergeInfo(action) {
|
||||
const op = Operations[action.id];
|
||||
action.invoke = app => app.ui.initOperation(action.id);
|
||||
action.invoke = ({services}) => services.operation.startOperation(action.id);
|
||||
Object.assign(action.appearance, {
|
||||
label: op.label,
|
||||
icon32: op.icon + '32.png',
|
||||
|
|
|
|||
|
|
@ -1,136 +0,0 @@
|
|||
import {Wizard} from './wizard'
|
||||
import {ReadSketchFromFace} from '../../sketch/sketch-reader'
|
||||
import {Loop} from '../../../../brep/topo/loop'
|
||||
|
||||
export class PreviewWizard extends Wizard {
|
||||
|
||||
constructor(app, opearation, metadata, initialState) {
|
||||
super(app, opearation, metadata, initialState);
|
||||
this.operation = opearation;
|
||||
this.previewGroup = new THREE.Object3D();
|
||||
this.previewObject = null;
|
||||
this.app.viewer.workGroup.add(this.previewGroup);
|
||||
this.updatePreview();
|
||||
app.bus.subscribe('refreshSketch', this.onSketchUpdate);
|
||||
}
|
||||
|
||||
onSketchUpdate = () => {
|
||||
this.updatePreview();
|
||||
};
|
||||
|
||||
createPreviewObject() {throw 'abstract'};
|
||||
|
||||
updatePreview() {
|
||||
this.destroyPreviewObject();
|
||||
this.previewObject = this.createPreviewObject(this.app, this.readFormFields());
|
||||
if (this.previewObject !== null) {
|
||||
this.previewGroup.add( this.previewObject );
|
||||
}
|
||||
this.app.viewer.render();
|
||||
}
|
||||
|
||||
destroyPreviewObject() {
|
||||
if (this.previewObject !== null) {
|
||||
this.previewGroup.remove( this.previewObject );
|
||||
this.previewObject.geometry.dispose();
|
||||
this.previewObject = null;
|
||||
}
|
||||
}
|
||||
|
||||
onUIChange() {
|
||||
super.onUIChange();
|
||||
this.updatePreview();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.app.bus.unSubscribe('refreshSketch', this.onSketchUpdate);
|
||||
this.destroyPreviewObject();
|
||||
this.app.viewer.workGroup.remove(this.previewGroup);
|
||||
this.app.viewer.render();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
PreviewWizard.createMesh = function(triangles) {
|
||||
const geometry = new THREE.Geometry();
|
||||
|
||||
for (let tr of triangles) {
|
||||
const a = geometry.vertices.length;
|
||||
const b = a + 1;
|
||||
const c = a + 2;
|
||||
const face = new THREE.Face3(a, b, c);
|
||||
tr.forEach(v => geometry.vertices.push(v.three()));
|
||||
geometry.faces.push(face);
|
||||
}
|
||||
geometry.mergeVertices();
|
||||
geometry.computeFaceNormals();
|
||||
|
||||
return new THREE.Mesh(geometry, IMAGINARY_SURFACE_MATERIAL);
|
||||
};
|
||||
|
||||
export class SketchBasedPreviewer {
|
||||
|
||||
constructor() {
|
||||
//this.fixToCCW = true;
|
||||
}
|
||||
|
||||
createImpl(app, params, sketch, face) {
|
||||
throw 'not implemented';
|
||||
}
|
||||
|
||||
create(app, params) {
|
||||
const face = app.findFace(params.face);
|
||||
if (!face) return null;
|
||||
const triangles = this.createImpl(app, params, face.sketch.fetchContours(), face);
|
||||
return PreviewWizard.createMesh(triangles);
|
||||
}
|
||||
}
|
||||
|
||||
export class SketchBasedNurbsPreviewer {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
createNurbses(app, params, sketch, face) {
|
||||
throw 'not implemented';
|
||||
}
|
||||
|
||||
createMesh(app, params) {
|
||||
const face = app.findFace(params.face);
|
||||
if (!face) return null;
|
||||
const needSketchRead = !this.sketch || params.face != this.face;
|
||||
if (needSketchRead) {
|
||||
this.sketch = ReadSketchFromFace(app, face);
|
||||
this.face = params.face;
|
||||
}
|
||||
const nurbses = this.createNurbses(app, params, this.sketch, face);
|
||||
const geom = new THREE.Geometry();
|
||||
|
||||
for (let nurbs of nurbses) {
|
||||
const off = geom.vertices.length;
|
||||
const tess = nurbs.tessellate({maxDepth: 3});
|
||||
const points = [];
|
||||
tess.points.forEach(p => geom.vertices.push(new THREE.Vector3().fromArray(p)));
|
||||
for (let faceIndices of tess.faces) {
|
||||
let normales = faceIndices.map(function(x) {
|
||||
var vn = tess.normals[x];
|
||||
return new THREE.Vector3( vn[0], vn[1], vn[2] );
|
||||
});
|
||||
const face = new THREE.Face3(faceIndices[0] + off, faceIndices[1] + off, faceIndices[2] + off, normales);
|
||||
geom.faces.push(face);
|
||||
}
|
||||
}
|
||||
return new THREE.Mesh(geom, IMAGINARY_SURFACE_MATERIAL);
|
||||
}
|
||||
}
|
||||
|
||||
export const IMAGINARY_SURFACE_MATERIAL = new THREE.MeshPhongMaterial({
|
||||
vertexColors: THREE.FaceColors,
|
||||
color: 0xFA8072,
|
||||
transparent: true,
|
||||
opacity: 0.5,
|
||||
shininess: 0,
|
||||
depthWrite: false,
|
||||
depthTest: false,
|
||||
side : THREE.DoubleSide
|
||||
});
|
||||
67
web/app/cad/craft/craftPlugin.js
Normal file
67
web/app/cad/craft/craftPlugin.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import {BoxWizard} from "./mesh/wizards/box";
|
||||
|
||||
export function activate({bus, services}) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function createWizard(type, overridingHistory, initParams, face) {
|
||||
let wizard = null;
|
||||
if ('CUT' === type) {
|
||||
wizard = new CutWizard(this.app, initParams);
|
||||
} else if ('EXTRUDE' === type) {
|
||||
wizard = new ExtrudeWizard(this.app, initParams);
|
||||
} else if ('REVOLVE' === type) {
|
||||
wizard = new RevolveWizard(this.app, face, initParams);
|
||||
} else if ('PLANE' === type) {
|
||||
wizard = new PlaneWizard(this.app, initParams);
|
||||
} else if ('BOX' === type) {
|
||||
wizard = new BoxWizard(this.app, initParams);
|
||||
} else if ('SPHERE' === type) {
|
||||
wizard = new SphereWizard(this.app.viewer, initParams);
|
||||
} else if ('IMPORT_STL' === type) {
|
||||
wizard = new ImportWizard(this.app.viewer, initParams);
|
||||
} else {
|
||||
console.log('unknown operation');
|
||||
}
|
||||
if (wizard != null) {
|
||||
this.registerWizard(wizard, overridingHistory);
|
||||
}
|
||||
return wizard;
|
||||
};
|
||||
|
||||
|
||||
function startOperation(id) {
|
||||
let selection = services.selection.face();
|
||||
|
||||
if ('CUT' === type) {
|
||||
wizard = new CutWizard(this.app, initParams);
|
||||
} else if ('EXTRUDE' === type) {
|
||||
wizard = new ExtrudeWizard(this.app, initParams);
|
||||
} else if ('REVOLVE' === type) {
|
||||
wizard = new RevolveWizard(this.app, face, initParams);
|
||||
} else if ('PLANE' === type) {
|
||||
wizard = new PlaneWizard(this.app, initParams);
|
||||
} else if ('BOX' === type) {
|
||||
wizard = new BoxWizard(this.app, initParams);
|
||||
} else if ('SPHERE' === type) {
|
||||
wizard = new SphereWizard(this.app.viewer, initParams);
|
||||
} else if ('IMPORT_STL' === type) {
|
||||
wizard = new ImportWizard(this.app.viewer, initParams);
|
||||
} else {
|
||||
console.log('unknown operation');
|
||||
}
|
||||
if (wizard != null) {
|
||||
this.registerWizard(wizard, overridingHistory);
|
||||
}
|
||||
return wizard;
|
||||
|
||||
return this.createWizard(op, false, undefined, selection[0]);
|
||||
|
||||
}
|
||||
|
||||
services.operation = {
|
||||
startOperation
|
||||
}
|
||||
}
|
||||
56
web/app/cad/craft/operationPlugin.js
Normal file
56
web/app/cad/craft/operationPlugin.js
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import {createToken} from 'bus';
|
||||
import {TOKENS as WIZARD_TOKENS} from './wizard/wizardPlugin'
|
||||
|
||||
export function activate(context) {
|
||||
let {bus, services} = context;
|
||||
|
||||
let registry = {};
|
||||
|
||||
function addOperation(descriptor, actions) {
|
||||
let {id, label, info, icon, actionParams} = descriptor;
|
||||
|
||||
let opAction = {
|
||||
id: id,
|
||||
appearance: {
|
||||
label,
|
||||
info,
|
||||
icon32: icon + '32.png',
|
||||
icon96: icon + '96.png',
|
||||
},
|
||||
invoke: () => bus.dispatch(WIZARD_TOKENS.OPEN, {type: id}),
|
||||
...actionParams
|
||||
};
|
||||
actions.push(opAction);
|
||||
|
||||
registry[id] = descriptor;
|
||||
|
||||
bus.subscribe(TOKENS.RUN, ({type, params}) => registry[type].run(params));
|
||||
}
|
||||
|
||||
function registerOperations(operations) {
|
||||
let actions = [];
|
||||
for (let op of operations) {
|
||||
addOperation(op, actions);
|
||||
}
|
||||
services.action.registerActions(actions);
|
||||
}
|
||||
|
||||
function get(id) {
|
||||
let op = registry[id];
|
||||
if (!op) {
|
||||
this `operation ${id} is not registered`;
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
services.operation = {
|
||||
registerOperations,
|
||||
registry,
|
||||
get
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const TOKENS = {
|
||||
RUN: createToken('operation', 'run'),
|
||||
};
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
// import {MESH_OPERATIONS} from './mesh/workbench'
|
||||
// import {Extrude, Cut} from './brep/cut-extrude'
|
||||
// import {Revolve} from './brep/revolve'
|
||||
// import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject'
|
||||
import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject'
|
||||
// import {PlaneSceneObject} from '../scene/wrappers/planeSceneObject'
|
||||
// import {box} from '../../brep/brep-primitives'
|
||||
import {box} from '../../brep/brep-primitives'
|
||||
|
||||
export const CUT = {
|
||||
icon: 'img/cad/cut',
|
||||
|
|
|
|||
28
web/app/cad/craft/primitives/boxOperation.js
Normal file
28
web/app/cad/craft/primitives/boxOperation.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import {box} from '../../../brep/brep-primitives'
|
||||
import {BREPSceneSolid} from '../../scene/wrappers/brepSceneObject';
|
||||
import {createPreviewer} from "../../preview/scenePreviewer";
|
||||
import {createBoxGeometry} from "../../../../../modules/scene/geoms";
|
||||
|
||||
const METADATA = [
|
||||
['width' , 'number', 500, {min: 0}],
|
||||
['height' , 'number', 500, {min: 0}],
|
||||
['depth' , 'number', 500, {min: 0}]
|
||||
];
|
||||
|
||||
function createBox(solids, {width, height, depth}) {
|
||||
return {
|
||||
outdated: [],
|
||||
created: [new BREPSceneSolid(box(width, height, depth))]
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
id: 'BOX',
|
||||
metadata: METADATA,
|
||||
label: 'Box',
|
||||
info: 'creates new object box',
|
||||
paramsInfo: ({width, height, depth}) => `(${width}, ${height}, ${depth})`,
|
||||
previewer: createPreviewer(({width, height, depth}) => createBoxGeometry(width, height, depth)),
|
||||
run: createBox
|
||||
};
|
||||
|
||||
26
web/app/cad/craft/wizard/wizardPlugin.js
Normal file
26
web/app/cad/craft/wizard/wizardPlugin.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import {createToken} from 'bus';
|
||||
|
||||
export function activate({bus, services}) {
|
||||
|
||||
bus.enableState(TOKENS.WIZARDS, []);
|
||||
bus.subscribe(TOKENS.OPEN, ({type, initialState, overridingHistory}) => {
|
||||
|
||||
let wizard = {
|
||||
type,
|
||||
initialState,
|
||||
overridingHistory,
|
||||
};
|
||||
|
||||
bus.updateState(TOKENS.WIZARDS, opened => [...opened, wizard])
|
||||
});
|
||||
|
||||
bus.subscribe(TOKENS.CLOSE, wizard => {
|
||||
bus.updateState(TOKENS.WIZARDS, opened => opened.filter(w => w === wizard));
|
||||
});
|
||||
}
|
||||
|
||||
export const TOKENS = {
|
||||
WIZARDS: createToken('wizards'),
|
||||
OPEN: createToken('wizards', 'open'),
|
||||
CLOSE: createToken('wizards', 'close'),
|
||||
};
|
||||
|
|
@ -4,7 +4,7 @@ import ls from './ActionInfo.less';
|
|||
import AuxWidget from '../../../../../modules/ui/components/AuxWidget';
|
||||
import connect from '../../../../../modules/ui/connect';
|
||||
import {TOKENS as ACTION_TOKENS} from '../../actions/actionSystemPlugin';
|
||||
import {TOKENS as KeyboardTokens} from "../../keyboard/keyboardPlugin";
|
||||
import {TOKENS as KeyboardTokens} from '../../keyboard/keyboardPlugin';
|
||||
|
||||
function ActionInfo({actionId, x, y, info, hint, hotKey}) {
|
||||
let visible = !!actionId;
|
||||
|
|
@ -19,6 +19,7 @@ function ActionInfo({actionId, x, y, info, hint, hotKey}) {
|
|||
</AuxWidget>;
|
||||
}
|
||||
|
||||
export default connect([ACTION_TOKENS.HINT, KeyboardTokens.KEYMAP], ActionInfo, undefined,
|
||||
([ hintInfo, keymap ]) => (Object.assign({hotKey: hintInfo && keymap[hintInfo.actionId]}, hintInfo)) );
|
||||
export default connect(ActionInfo, [ACTION_TOKENS.HINT, KeyboardTokens.KEYMAP], {
|
||||
mapProps: ([ hintInfo, keymap ]) => (Object.assign({hotKey: hintInfo && keymap[hintInfo.actionId]}, hintInfo))
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -15,10 +15,13 @@ export default function PlugableControlBar() {
|
|||
function ButtonGroup({actions}) {
|
||||
return actions.map(actionRef => {
|
||||
let [id, overrides] = toIdAndOverrides(actionRef);
|
||||
let Comp = connect([ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)],
|
||||
ActionButton, {actionId: id},
|
||||
([appearance, state]) => Object.assign({}, appearance, state, overrides),
|
||||
mapActionBehavior(id)
|
||||
let Comp = connect(ActionButton,
|
||||
[ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)],
|
||||
{
|
||||
staticProps: {actionId: id},
|
||||
mapProps: ([appearance, state]) => Object.assign({}, appearance, state, overrides),
|
||||
mapActions: mapActionBehavior(id)
|
||||
}
|
||||
);
|
||||
return <Comp key={id}/>;
|
||||
});
|
||||
|
|
@ -46,8 +49,12 @@ class ActionButton extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
const LeftGroup = connect(UI_TOKENS.CONTROL_BAR_LEFT, ButtonGroup, undefined, ([actions]) => ({actions}));
|
||||
const RightGroup = connect(UI_TOKENS.CONTROL_BAR_RIGHT, ButtonGroup, undefined, ([actions]) => ({actions}));
|
||||
const BUTTON_CONNECTOR = {
|
||||
mapProps: ([actions]) => ({actions})
|
||||
};
|
||||
|
||||
const LeftGroup = connect(ButtonGroup, UI_TOKENS.CONTROL_BAR_LEFT, BUTTON_CONNECTOR);
|
||||
const RightGroup = connect(ButtonGroup, UI_TOKENS.CONTROL_BAR_RIGHT, BUTTON_CONNECTOR);
|
||||
|
||||
function getMenuData(el) {
|
||||
//TODO: make more generic
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@ function ConfigurableToolbar({actions, small, ...props}) {
|
|||
{actions.map(actionRef => {
|
||||
let [id, overrides] = toIdAndOverrides(actionRef);
|
||||
let Comp = connect(
|
||||
[ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)],
|
||||
ActionButton, {small},
|
||||
([appearance, state]) => Object.assign({}, appearance, state, overrides));
|
||||
ActionButton,
|
||||
[ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)], {
|
||||
staticProps: {small},
|
||||
mapProps: ([appearance, state]) => Object.assign({}, appearance, state, overrides)
|
||||
});
|
||||
return <Comp key={id}/>
|
||||
})}
|
||||
</Toolbar>
|
||||
|
|
@ -37,7 +39,10 @@ function ActionButton({label, icon96, cssIcons, small, enabled, visible, onClick
|
|||
}
|
||||
|
||||
export function createPlugableToolbar(configToken, small) {
|
||||
return connect(configToken, ConfigurableToolbar, {small}, ([actions]) => ({actions}) );
|
||||
return connect(ConfigurableToolbar, configToken, {
|
||||
staticProps: {small},
|
||||
mapProps: ([actions]) => ({actions})
|
||||
});
|
||||
}
|
||||
|
||||
export const PlugableToolbarLeft = createPlugableToolbar(UI_TOKENS.TOOLBAR_BAR_LEFT);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import MenuHolder from "../menu/MenuHolder";
|
||||
import MenuHolder from '../menu/MenuHolder';
|
||||
import {TOKENS as MENU_TOKENS} from '../menu/menuPlugin';
|
||||
|
||||
import WindowSystem from 'ui/WindowSystem';
|
||||
import ActionInfo from "../actionInfo/ActionInfo";
|
||||
import ActionInfo from '../actionInfo/ActionInfo';
|
||||
|
||||
export default class UISystem extends React.Component {
|
||||
|
||||
|
|
@ -17,12 +17,15 @@ export default class UISystem extends React.Component {
|
|||
</div>
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
closeAllUpPopups = () => {
|
||||
let openedMenus = this.context.bus.state[MENU_TOKENS.OPENED];
|
||||
if (openedMenus && openedMenus.length !== 0) {
|
||||
this.context.bus.dispatch(MENU_TOKENS.CLOSE_ALL);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
|
|
|
|||
|
|
@ -24,9 +24,15 @@ import ButtonGroup from "ui/components/controls/ButtonGroup";
|
|||
import Button from "ui/components/controls/Button";
|
||||
import TextControl from './../../../../../modules/ui/components/controls/TextControl';
|
||||
import UISystem from './UISystem';
|
||||
import WizardManager from './wizard/WizardManager';
|
||||
|
||||
|
||||
export default class View3d extends React.PureComponent {
|
||||
export default class View3d extends React.Component {
|
||||
|
||||
shouldComponentUpdate() {
|
||||
//we don't want the dom to be updated under any circumstances or we loose the WEB-GL container
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <UISystem className={ls.root} >
|
||||
|
|
@ -43,8 +49,8 @@ export default class View3d extends React.PureComponent {
|
|||
<PlugableToolbarRight />
|
||||
</Abs>
|
||||
<PlugableControlBar />
|
||||
<WizardManager />
|
||||
|
||||
<WindowSystem />
|
||||
<Window initWidth={250} initLeft={500} title="Test">
|
||||
<Stack >
|
||||
<Field>
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ const DEFAULT_VIEW = {id: 'view3d', label: '3D View', Component: View3d};
|
|||
|
||||
export default class WebApplication extends React.Component {
|
||||
|
||||
constructor({bus}) {
|
||||
constructor({appContext}) {
|
||||
super();
|
||||
this.bus = bus;
|
||||
this.appContext = appContext;
|
||||
this.views = [DEFAULT_VIEW, {id: 'XXX', label: '3D View2', Component: Fragment}];
|
||||
this.state = {
|
||||
activeView: DEFAULT_VIEW
|
||||
|
|
@ -49,11 +49,12 @@ export default class WebApplication extends React.Component {
|
|||
}
|
||||
|
||||
getChildContext() {
|
||||
return {bus: this.bus};
|
||||
return this.appContext;
|
||||
}
|
||||
|
||||
static childContextTypes = {
|
||||
bus: PropTypes.object
|
||||
bus: PropTypes.object,
|
||||
services: PropTypes.object
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
81
web/app/cad/dom/components/wizard/Wizard.jsx
Normal file
81
web/app/cad/dom/components/wizard/Wizard.jsx
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import React from 'react';
|
||||
import Window from 'ui/components/Window';
|
||||
import Stack from 'ui/components/Stack';
|
||||
import Field from 'ui/components/controls/Field';
|
||||
import Label from 'ui/components/controls/Label';
|
||||
import camelCaseSplit from 'gems/camelCaseSplit';
|
||||
import NumberControl from 'ui/components/controls/NumberControl';
|
||||
import TextControl from 'ui/components/controls/TextControl';
|
||||
import Button from 'ui/components/controls/Button';
|
||||
import ButtonGroup from 'ui/components/controls/ButtonGroup';
|
||||
|
||||
export default class Wizard extends React.Component {
|
||||
|
||||
constructor({initialState, metadata, previewer}) {
|
||||
super();
|
||||
this.params = {};
|
||||
|
||||
metadata.forEach(([name,, v]) => this.params[name] = v);
|
||||
|
||||
Object.assign(this.params, initialState);
|
||||
|
||||
this.preview = previewer(this.params);
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
// all controls are unmanaged and they should keep their state
|
||||
// if the wizard manager gets updated when a new wizard appears
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
let {left, title, metadata, onOK, onCancel} = this.props;
|
||||
let onClose = () => {
|
||||
this.onClose();
|
||||
onCancel();
|
||||
};
|
||||
return <Window initWidth={250} initLeft={left} title={title} onClose={onClose}>
|
||||
<Stack >
|
||||
{metadata.map(([name, type, , params], index) => {
|
||||
return <Field key={index}>
|
||||
<Label>{uiLabel(name)}</Label>
|
||||
{this.controlForType(name, type, params)}
|
||||
</Field>
|
||||
} )}
|
||||
<ButtonGroup>
|
||||
<Button text='Cancel' onClick={onClose} />
|
||||
<Button text='OK' type='accent' onClick={() => {
|
||||
this.onClose();
|
||||
onOK(this.params);
|
||||
}} />
|
||||
</ButtonGroup>
|
||||
</Stack>
|
||||
</Window>;
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.preview.dispose();
|
||||
}
|
||||
|
||||
controlForType(name, type, params) {
|
||||
const onChange = val => {
|
||||
this.params[name] = val;
|
||||
this.preview.update(this.params);
|
||||
};
|
||||
let initValue = this.params[name];
|
||||
let commonProps = {
|
||||
onChange, initValue, ...params
|
||||
};
|
||||
if (type === 'number') {
|
||||
return <NumberControl {...commonProps} />
|
||||
} else {
|
||||
return <TextControl {...commonProps} />
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function uiLabel(name) {
|
||||
return camelCaseSplit(name).map(w => w.toLowerCase()).join(' ');
|
||||
}
|
||||
|
||||
32
web/app/cad/dom/components/wizard/WizardManager.jsx
Normal file
32
web/app/cad/dom/components/wizard/WizardManager.jsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {TOKENS as WIZARD_TOKENS} from '../../../craft/wizard/wizardPlugin';
|
||||
import connect from 'ui/connect';
|
||||
import Wizard from "./Wizard";
|
||||
|
||||
function WizardManager({wizards, close}, {services}) {
|
||||
return wizards.map( ({type, initialState}, wizardIndex) => {
|
||||
let {metadata, previewer, run} = services.operation.get(type);
|
||||
|
||||
function onOK(params) {
|
||||
close();
|
||||
run(type, params);
|
||||
}
|
||||
previewer = previewer.bind(null, {services});
|
||||
return <Wizard key={wizardIndex} previewer={previewer} metadata={metadata}
|
||||
onOK={onOK}
|
||||
onCancel={close}
|
||||
initialState={initialState} title={type} left={70 + wizardIndex * 250 + 20} />
|
||||
});
|
||||
}
|
||||
|
||||
WizardManager.contextTypes = {
|
||||
services: PropTypes.object
|
||||
};
|
||||
|
||||
export default connect(WizardManager, WIZARD_TOKENS.WIZARDS, {
|
||||
mapProps: ([wizards]) => ({wizards}),
|
||||
mapActions: dispatch => ({
|
||||
close: wizard => dispatch(WIZARD_TOKENS.CLOSE, wizard)
|
||||
})
|
||||
});
|
||||
|
|
@ -10,8 +10,11 @@ import {TOKENS as KeyboardTokens} from "../../keyboard/keyboardPlugin";
|
|||
function MenuHolder({menus}) {
|
||||
return menus.map(({id, actions}) => {
|
||||
let menuToken = MENU_TOKENS.menuState(id);
|
||||
return React.createElement(connect([menuToken, KeyboardTokens.KEYMAP],
|
||||
ActionMenu, {actions}, [,keymap => ({keymap})]), {key: id});
|
||||
let connectedMenu = connect(ActionMenu, [menuToken, KeyboardTokens.KEYMAP], {
|
||||
staticProps: {actions},
|
||||
mapProps: [,keymap => ({keymap})]
|
||||
});
|
||||
return React.createElement(connectedMenu, {key: id});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -23,13 +26,12 @@ function ActionMenu({actions, keymap, ...props}) {
|
|||
}
|
||||
const runToken = ACTION_TOKENS.actionRun(action);
|
||||
return React.createElement(
|
||||
connect([ACTION_TOKENS.actionState(action), ACTION_TOKENS.actionAppearance(action)],
|
||||
ActionMenuItem,
|
||||
{hotKey: keymap[action]}, undefined,
|
||||
dispatch => ({
|
||||
connect(ActionMenuItem, [ACTION_TOKENS.actionState(action), ACTION_TOKENS.actionAppearance(action)], {
|
||||
staticProps: {hotKey: keymap[action]},
|
||||
mapActions: dispatch => ({
|
||||
onClick: () => dispatch(runToken)
|
||||
})),
|
||||
{key: action});
|
||||
})
|
||||
}), {key: action});
|
||||
})}
|
||||
</Menu>;
|
||||
}
|
||||
|
|
@ -60,7 +62,9 @@ function ActionMenuItem({label, cssIcons, icon32, icon96, onClick, enabled, hotK
|
|||
}
|
||||
|
||||
|
||||
export default connect(MENU_TOKENS.MENUS, MenuHolder, undefined, ([menus]) => ({menus}));
|
||||
export default connect(MenuHolder, MENU_TOKENS.MENUS, {
|
||||
mapProps: ([menus]) => ({menus})
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import React from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
import WebApplication from './components/WebApplication';
|
||||
|
||||
export default function startReact(bus, callback) {
|
||||
export default function startReact(context, callback) {
|
||||
return ReactDOM.render(
|
||||
<WebApplication bus={bus} />,
|
||||
<WebApplication appContext={context} />,
|
||||
document.getElementById('app'),
|
||||
callback
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import * as ActionSystemPlugin from '../actions/actionSystemPlugin';
|
|||
import * as UiEntryPointsPlugin from '../dom/uiEntryPointsPlugin';
|
||||
import * as MenuPlugin from '../dom/menu/menuPlugin';
|
||||
import * as KeyboardPlugin from '../keyboard/keyboardPlugin';
|
||||
import * as WizardPlugin from '../craft/wizard/wizardPlugin';
|
||||
import * as OperationPlugin from '../craft/operationPlugin';
|
||||
|
||||
import * as PartModellerPlugin from '../part/partModellerPlugin';
|
||||
|
||||
|
|
@ -20,7 +22,9 @@ export default function startApplication(callback) {
|
|||
ActionSystemPlugin,
|
||||
MenuPlugin,
|
||||
UiEntryPointsPlugin,
|
||||
KeyboardPlugin
|
||||
KeyboardPlugin,
|
||||
WizardPlugin,
|
||||
OperationPlugin
|
||||
];
|
||||
|
||||
let plugins = [
|
||||
|
|
@ -38,14 +42,14 @@ export default function startApplication(callback) {
|
|||
|
||||
activatePlugins(preUIPlugins, context);
|
||||
|
||||
startReact(context.bus, () => {
|
||||
startReact(context, () => {
|
||||
activatePlugins(plugins, context);
|
||||
context.services.viewer.render();
|
||||
callback(context);
|
||||
});
|
||||
}
|
||||
|
||||
function activatePlugins(plugins, context) {
|
||||
export function activatePlugins(plugins, context) {
|
||||
for (let plugin of plugins) {
|
||||
plugin.activate(context);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import '../../../modules/scene/utils/vectorThreeEnhancement'
|
||||
import Bus from 'bus'
|
||||
import {Viewer} from './scene/viewer'
|
||||
import {UI} from './ui/ctrl'
|
||||
|
|
|
|||
|
|
@ -1,25 +1,12 @@
|
|||
import CoreActions from '../actions/coreActions';
|
||||
import OperationActions from '../actions/operationActions';
|
||||
import HistoryActions from '../actions/historyActions';
|
||||
import {TOKENS as UI_TOKENS} from '../dom/uiEntryPointsPlugin';
|
||||
import menuConfig from "./menuConfig";
|
||||
import * as UIConfigPlugin from './uiConfigPlugin';
|
||||
import * as PartOperationsPlugin from './partOperationsPlugin';
|
||||
import {activatePlugins} from "../init/startApplication";
|
||||
|
||||
export function activate({bus, services}) {
|
||||
const PART_MODELLER_PLUGINS = [
|
||||
UIConfigPlugin,
|
||||
PartOperationsPlugin
|
||||
];
|
||||
|
||||
services.action.registerActions(CoreActions);
|
||||
services.action.registerActions(OperationActions);
|
||||
services.action.registerActions(HistoryActions);
|
||||
|
||||
services.menu.registerMenus(menuConfig);
|
||||
|
||||
bus.dispatch(UI_TOKENS.CONTROL_BAR_LEFT, ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'Donate', 'GitHub']);
|
||||
bus.dispatch(UI_TOKENS.CONTROL_BAR_RIGHT, [
|
||||
['Info', {label: null}],
|
||||
['RefreshSketches', {label: null}],
|
||||
['ShowSketches', {label: 'sketches'}], ['DeselectAll', {label: null}], ['ToggleCameraMode', {label: null}]
|
||||
]);
|
||||
|
||||
bus.dispatch(UI_TOKENS.TOOLBAR_BAR_LEFT, ['PLANE', 'EditFace', 'EXTRUDE', 'CUT', 'REVOLVE']);
|
||||
bus.dispatch(UI_TOKENS.TOOLBAR_BAR_LEFT_SECONDARY, ['INTERSECTION', 'DIFFERENCE', 'UNION']);
|
||||
bus.dispatch(UI_TOKENS.TOOLBAR_BAR_RIGHT, ['Save', 'StlExport']);
|
||||
export function activate(context) {
|
||||
activatePlugins(PART_MODELLER_PLUGINS, context);
|
||||
}
|
||||
7
web/app/cad/part/partOperationsPlugin.js
Normal file
7
web/app/cad/part/partOperationsPlugin.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import boxOperation from '../craft/primitives/boxOperation';
|
||||
|
||||
export function activate({bus, services}) {
|
||||
services.operation.registerOperations([
|
||||
boxOperation
|
||||
])
|
||||
}
|
||||
25
web/app/cad/part/uiConfigPlugin.js
Normal file
25
web/app/cad/part/uiConfigPlugin.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import CoreActions from '../actions/coreActions';
|
||||
import OperationActions from '../actions/operationActions';
|
||||
import HistoryActions from '../actions/historyActions';
|
||||
import {TOKENS as UI_TOKENS} from '../dom/uiEntryPointsPlugin';
|
||||
import menuConfig from "./menuConfig";
|
||||
|
||||
export function activate({bus, services}) {
|
||||
|
||||
services.action.registerActions(CoreActions);
|
||||
services.action.registerActions(OperationActions);
|
||||
services.action.registerActions(HistoryActions);
|
||||
|
||||
services.menu.registerMenus(menuConfig);
|
||||
|
||||
bus.dispatch(UI_TOKENS.CONTROL_BAR_LEFT, ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'Donate', 'GitHub']);
|
||||
bus.dispatch(UI_TOKENS.CONTROL_BAR_RIGHT, [
|
||||
['Info', {label: null}],
|
||||
['RefreshSketches', {label: null}],
|
||||
['ShowSketches', {label: 'sketches'}], ['DeselectAll', {label: null}], ['ToggleCameraMode', {label: null}]
|
||||
]);
|
||||
|
||||
bus.dispatch(UI_TOKENS.TOOLBAR_BAR_LEFT, ['PLANE', 'EditFace', 'EXTRUDE', 'CUT', 'REVOLVE']);
|
||||
bus.dispatch(UI_TOKENS.TOOLBAR_BAR_LEFT_SECONDARY, ['INTERSECTION', 'DIFFERENCE', 'UNION']);
|
||||
bus.dispatch(UI_TOKENS.TOOLBAR_BAR_RIGHT, ['Save', 'StlExport']);
|
||||
}
|
||||
76
web/app/cad/preview/scenePreviewer.js
Normal file
76
web/app/cad/preview/scenePreviewer.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import * as SceneGraph from 'scene/sceneGraph';
|
||||
import {createTransparentPhongMaterial} from 'scene/materials';
|
||||
import {createMesh} from 'scene/objects/mesh';
|
||||
|
||||
|
||||
export function createPreviewer(sceneGeometryCreator) {
|
||||
|
||||
return function({services}, initParams) {
|
||||
const previewGroup = SceneGraph.createGroup();
|
||||
SceneGraph.addToGroup(services.cadScene.workGroup, previewGroup);
|
||||
|
||||
let previewObject = null;
|
||||
function destroyPreviewObject() {
|
||||
if (previewObject === null) {
|
||||
return;
|
||||
}
|
||||
previewGroup.remove(previewObject);
|
||||
previewObject.geometry.dispose();
|
||||
previewObject = null;
|
||||
}
|
||||
|
||||
function update(params) {
|
||||
destroyPreviewObject();
|
||||
previewObject = createMesh(sceneGeometryCreator(params), IMAGINARY_SURFACE_MATERIAL);
|
||||
previewGroup.add(previewObject);
|
||||
services.viewer.render();
|
||||
}
|
||||
|
||||
function dispose() {
|
||||
destroyPreviewObject();
|
||||
SceneGraph.removeFromGroup(services.cadScene.workGroup, previewGroup);
|
||||
services.viewer.render();
|
||||
}
|
||||
update(initParams);
|
||||
|
||||
return {update, dispose};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// function sketchBasedPreviewCreator(params) {
|
||||
// const face = app.findFace(params.face);
|
||||
// if (!face) return null;
|
||||
// const triangles = this.createImpl(app, params, face.sketch.fetchContours(), face);
|
||||
// return createMeshFromTriangles(triangles, IMAGINARY_SURFACE_MATERIAL);
|
||||
// }
|
||||
//
|
||||
// function sketchBasedNurbsPreviewCreator(params) {
|
||||
// const face = app.findFace(params.face);
|
||||
// if (!face) return null;
|
||||
// const needSketchRead = !this.sketch || params.face != this.face;
|
||||
// if (needSketchRead) {
|
||||
// this.sketch = ReadSketchFromFace(app, face);
|
||||
// this.face = params.face;
|
||||
// }
|
||||
// const nurbses = this.createNurbses(app, params, this.sketch, face);
|
||||
// const geom = new THREE.Geometry();
|
||||
//
|
||||
// for (let nurbs of nurbses) {
|
||||
// const off = geom.vertices.length;
|
||||
// const tess = nurbs.tessellate({maxDepth: 3});
|
||||
// const points = [];
|
||||
// tess.points.forEach(p => geom.vertices.push(new THREE.Vector3().fromArray(p)));
|
||||
// for (let faceIndices of tess.faces) {
|
||||
// let normales = faceIndices.map(function(x) {
|
||||
// var vn = tess.normals[x];
|
||||
// return new THREE.Vector3( vn[0], vn[1], vn[2] );
|
||||
// });
|
||||
// const face = new THREE.Face3(faceIndices[0] + off, faceIndices[1] + off, faceIndices[2] + off, normales);
|
||||
// geom.faces.push(face);
|
||||
// }
|
||||
// }
|
||||
// return new THREE.Mesh(geom, IMAGINARY_SURFACE_MATERIAL);
|
||||
// }
|
||||
|
||||
export const IMAGINARY_SURFACE_MATERIAL = createTransparentPhongMaterial(0xFA8072, 0.5);
|
||||
|
|
@ -41,32 +41,6 @@ export function swap(arr, i1, i2) {
|
|||
arr[i2] = tmp;
|
||||
}
|
||||
|
||||
export function camelCaseSplit(str) {
|
||||
function isUpperCase(str) {
|
||||
return str.toUpperCase() == str;
|
||||
}
|
||||
|
||||
const words = [];
|
||||
let word = '';
|
||||
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const c = str.charAt(i);
|
||||
if (c == '_' || c == '-') {
|
||||
continue;
|
||||
}
|
||||
const dot = c === '.';
|
||||
if ((dot || isUpperCase(c)) && word.length != 0) {
|
||||
words.push(word);
|
||||
word = '';
|
||||
}
|
||||
if (!dot) word += c;
|
||||
}
|
||||
if (word.length != 0){
|
||||
words.push(word);
|
||||
}
|
||||
return words;
|
||||
}
|
||||
|
||||
export function defineIterable(obj, name, iteratorFactory) {
|
||||
obj[name] = {};
|
||||
obj[name][Symbol.iterator] = iteratorFactory;
|
||||
|
|
|
|||
Loading…
Reference in a new issue