refactor 3d wizards

This commit is contained in:
Val Erastov 2016-09-10 17:02:42 -07:00
parent 16028b60f4
commit e54786fc58
5 changed files with 306 additions and 282 deletions

View file

@ -2,7 +2,8 @@ import * as tk from '../ui/toolkit'
import * as cad_utils from './cad-utils'
import * as math from '../math/math'
import * as workbench from './workbench'
import {ExtrudeWizard, PlaneWizard} from './wizards/wizards'
import {ExtrudeWizard} from './wizards/extrude'
import {PlaneWizard} from './wizards/plane'
import {TransformWizard} from './wizards/transform'
import {IO} from '../sketcher/io'
@ -10,7 +11,7 @@ function UI(app) {
this.app = app;
this.viewer = app.viewer;
var mainBox = new tk.Box();
var mainBox = this.mainBox = new tk.Box();
mainBox.root.css({height : '100%'});
var propFolder = new tk.Folder("Solid's Properties");
var debugFolder = new tk.Folder("Debug");
@ -48,7 +49,7 @@ function UI(app) {
var historyWizard = null;
function updateHistoryPointer() {
if (historyWizard != null) {
historyWizard.close();
historyWizard.dispose();
historyWizard = null;
}
@ -59,7 +60,7 @@ function UI(app) {
rows.removeClass('history-selected');
rows.eq(craft.historyPointer).addClass('history-selected');
var op = craft.history[craft.historyPointer];
historyWizard = UI.createWizard(op, app, mainBox);
historyWizard = ui.createWizardForOperation(op, app);
finishHistory.root.show();
} else {
finishHistory.root.hide();
@ -84,9 +85,9 @@ function UI(app) {
if (historyWizard != null) {
var craft = ui.app.craft;
var op = JSON.parse(JSON.stringify(craft.history[craft.historyPointer]));
op.protoParams = historyWizard.currentParams();
historyWizard.close();
historyWizard = UI.createWizard(op, app, mainBox);
op.protoParams = historyWizard.getParams();
historyWizard.dispose();
historyWizard = ui.createWizardForOperation(op, app);
}
});
@ -94,13 +95,13 @@ function UI(app) {
//updateHistoryPointer();
});
function cutExtrude(isCut) {
return function() {
if (app.viewer.selectionMgr.selection.length == 0) {
var selection = app.viewer.selectionMgr.selection;
if (selection.length == 0) {
return;
}
UI.createCutExtrudeWizard(isCut, ui.app, app.viewer.selectionMgr.selection[0], mainBox);
ui.registerWizard(new ExtrudeWizard(ui.app, selection[0], isCut), false);
}
}
@ -109,7 +110,7 @@ function UI(app) {
edit.root.click(tk.methodRef(app, "sketchFace"));
refreshSketches.root.click(tk.methodRef(app, "refreshSketches"));
addPlane.root.click(function() {
UI.createPlaneWizard(app, mainBox);
ui.registerWizard(new PlaneWizard(app.viewer), false)
});
printSolids.root.click(function () {
app.findAllSolids().map(function(o) {
@ -156,6 +157,15 @@ function UI(app) {
});
}
UI.prototype.registerWizard = function(wizard, overridingHistory) {
wizard.ui.box.root.css({left : (this.mainBox.root.width() + 10) + 'px', top : 0});
var craft = this.app.craft;
wizard.apply = function() {
craft.modify(wizard.createRequest(), overridingHistory);
};
return wizard;
};
UI.prototype.getInfoForOp = function(op) {
var p = op.params;
var norm2 = math.norm2;
@ -171,138 +181,23 @@ UI.prototype.getInfoForOp = function(op) {
return op.type;
};
UI.createWizard = function(op, app, alignComponent) {
UI.prototype.createWizardForOperation = function(op) {
var initParams = op.protoParams;
var face = op.face !== undefined ? app.findFace(op.face) : null;
var face = op.face !== undefined ? this.app.findFace(op.face) : null;
if (face != null) {
app.viewer.selectionMgr.select(face);
this.app.viewer.selectionMgr.select(face);
}
var wizard;
if ('CUT' === op.type) {
return UI.createCutExtrudeWizard(true, app, face, alignComponent, initParams, true);
wizard = new ExtrudeWizard(this.app, face, true, initParams);
} else if ('PAD' === op.type) {
return UI.createCutExtrudeWizard(false, app, face, alignComponent, initParams, true);
wizard = new ExtrudeWizard(this.app, face, false, initParams);
} else if ('PLANE' === op.type) {
return UI.createPlaneWizard(app, alignComponent, initParams, true);
wizard = new PlaneWizard(this.app.viewer, initParams);
}
return null;
};
UI.createCutExtrudeWizard = function (isCut, app, face, alignComponent, initParams, overriding) {
function def(index, fallback) {
return !!initParams ? initParams[index] : fallback;
}
var normal = cad_utils.vec(face.csgGroup.plane.normal);
var polygons = workbench.getSketchedPolygons3D(app, face);
var box = new tk.Box();
box.root.css({left : (alignComponent.root.width() + 10) + 'px', top : 0});
var folder = new tk.Folder(isCut ? "Cut Options" : "Extrude Options");
tk.add(box, folder);
var theValue = new tk.Number(isCut ? "Depth" : "Height", def(0, 50));
var scale = new tk.Number("Expansion", def(1, 1), 0.1, 1);
var deflection = new tk.Number("Deflection", def(2, 0), 1);
var angle = new tk.Number("Angle", def(3, 0), 5);
var wizard = new ExtrudeWizard(app.viewer, polygons);
function onChange() {
var depthValue = theValue.input.val();
var scaleValue = scale.input.val();
var deflectionValue = deflection.input.val();
var angleValue = angle.input.val();
if (isCut) depthValue *= -1;
wizard.update(face._basis, normal, depthValue, scaleValue, deflectionValue, angleValue);
app.viewer.render()
}
theValue.input.on('t-change', onChange);
scale.input.on('t-change', onChange);
deflection.input.on('t-change', onChange);
angle.input.on('t-change', onChange);
onChange();
tk.add(folder, theValue);
tk.add(folder, scale);
tk.add(folder, deflection);
tk.add(folder, angle);
function close() {
box.close();
wizard.dispose();
}
function protoParams() {
var depthValue = theValue.input.val();
var scaleValue = scale.input.val();
var deflectionValue = deflection.input.val();
var angleValue = angle.input.val();
return [depthValue, scaleValue, deflectionValue, angleValue];
}
function applyCut() {
app.craft.modify({
type: 'CUT',
solids : [app.findSolid(face.solid.tCadId)],
face : app.findFace(face.id),
params : wizard.operationParams,
protoParams : protoParams()
}, overriding);
close();
}
function applyExtrude() {
app.craft.modify({
type: 'PAD',
solids : [app.findSolid(face.solid.tCadId)],
face : app.findFace(face.id),
params : wizard.operationParams,
protoParams : protoParams()
}, overriding);
close();
}
tk.add(folder, new tk.ButtonRow(["Cancel", "OK"], [close, isCut ? applyCut : applyExtrude]));
return new UI.WizardRef(wizard, box, close, protoParams);
};
UI.createPlaneWizard = function (app, alignComponent, initParams, overiding) {
var box = new tk.Box();
box.root.css({left : (alignComponent.root.width() + 10) + 'px', top : 0});
var folder = new tk.Folder("Add a Plane");
tk.add(box, folder);
var choice = ['XY', 'XZ', 'ZY'];
var orientation = new tk.InlineRadio(choice, choice, !initParams ? 0 : choice.indexOf(initParams[0]));
var depth = new tk.Number("Depth", !initParams ? 0 : initParams[1]);
tk.add(folder, orientation);
tk.add(folder, depth);
var wizard = new PlaneWizard(app.viewer);
var orientationValue, w;
function onChange() {
wizard.update(orientationValue = orientation.getValue(), w = depth.input.val());
}
function close() {
box.close();
wizard.dispose();
}
function protoParams() {
return [orientationValue, w];
}
function ok() {
app.craft.modify({
type: 'PLANE',
solids : [],
params : wizard.operationParams,
protoParams : protoParams()
}, overiding);
close();
}
orientation.root.find('input:radio').change(onChange);
depth.input.on('t-change', onChange);
onChange();
tk.add(folder, new tk.ButtonRow(["Cancel", "OK"], [close, ok]));
return new UI.WizardRef(wizard, box, close, protoParams);
};
UI.WizardRef = function(wizard, box, close, currentParams) {
this.wizard = wizard;
this.box = box;
this.close = close;
this.currentParams = currentParams;
this.registerWizard(wizard, true);
return wizard;
};
export {UI}

View file

@ -0,0 +1,114 @@
import * as tk from '../../ui/toolkit.js'
import * as workbench from '../workbench'
import * as cad_utils from '../cad-utils'
import Vector from '../../math/vector'
import {Matrix3, ORIGIN} from '../../math/l3space'
import {OpWizard, IMAGINE_MATERIAL, BASE_MATERIAL, addOkCancelLogic} from './wizard-commons'
export function ExtrudeWizard(app, face, invert, initParams) {
OpWizard.call(this, app.viewer);
this.app = app;
this.face = face;
this.invert = invert;
addOkCancelLogic(this);
this.updatePolygons();
this.ui = {};
if (!initParams) initParams = ExtrudeWizard.DEFAULT_PARAMS;
this.createUI.apply(this, initParams);
this.synch();
}
ExtrudeWizard.prototype = Object.create( OpWizard.prototype );
ExtrudeWizard.DEFAULT_PARAMS = [50, 1, 0, 0];
ExtrudeWizard.prototype.apply = function() {};
ExtrudeWizard.prototype.updatePolygons = function() {
this.polygons = workbench.getSketchedPolygons3D(this.app, this.face);
};
ExtrudeWizard.prototype.update = function(depth, scale, deflection, angle) {
if (this.invert) depth *= -1; //depth;
var basis = this.face.basis();
var normal = new Vector().setV(this.face.csgGroup.plane.normal);
var linesCounter = 0;
var target;
if (deflection != 0) {
target = normal.copy();
if (depth < 0) target._negate();
target = Matrix3.rotateMatrix(deflection * Math.PI / 180, basis[0], ORIGIN)._apply(target);
if (angle != 0) {
target = Matrix3.rotateMatrix(angle * Math.PI / 180, basis[2], ORIGIN)._apply(target);
}
target._multiply(Math.abs(depth));
} else {
target = normal.multiply(depth)
}
for (var i = 0; i < this.polygons.length; i++) {
var poly = this.polygons[i];
var lid = cad_utils.calculateExtrudedLid(poly, normal, target, scale);
var p, q, n = poly.length;
for (p = n - 1, q = 0; q < n; p = q++) {
this.setupLine(linesCounter ++, poly[p], poly[q], BASE_MATERIAL);
this.setupLine(linesCounter ++, lid[p], lid[q], IMAGINE_MATERIAL);
}
for (q = 0; q < n; q++) {
this.setupLine(linesCounter ++, poly[q], lid[q], IMAGINE_MATERIAL);
}
}
this.operationParams = {
target : target,
expansionFactor : scale
}
};
ExtrudeWizard.prototype.createUI = function (depth, scale, deflection, angle) {
var ui = this.ui;
ui.box = new tk.Box();
var folder = new tk.Folder(this.invert ? "Cut Options" : "Extrude Options");
tk.add(ui.box, folder);
ui.theValue = new tk.Number(this.invert ? "Depth" : "Height", depth);
ui.scale = new tk.Number("Expansion", scale, 0.1, 1);
ui.deflection = new tk.Number("Deflection", deflection, 1);
ui.angle = new tk.Number("Angle", angle, 5);
var onChange = tk.methodRef(this, "synch");
ui.theValue.input.on('t-change', onChange);
ui.scale.input.on('t-change', onChange);
ui.deflection.input.on('t-change', onChange);
ui.angle.input.on('t-change', onChange);
tk.add(folder, ui.theValue);
tk.add(folder, ui.scale);
tk.add(folder, ui.deflection);
tk.add(folder, ui.angle);
tk.add(folder, new tk.ButtonRow(["Cancel", "OK"], [tk.methodRef(this, "cancelClick"), tk.methodRef(this, "okClick")]));
};
ExtrudeWizard.prototype.synch = function() {
this.update.apply(this, this.getParams());
this.app.viewer.render();
};
ExtrudeWizard.prototype.getParams = function() {
var depthValue = this.ui.theValue.input.val();
var scaleValue = this.ui.scale.input.val();
var deflectionValue = this.ui.deflection.input.val();
var angleValue = this.ui.angle.input.val();
return [depthValue, scaleValue, deflectionValue, angleValue];
};
ExtrudeWizard.prototype.createRequest = function(params) {
return {
type : this.invert ? 'CUT' : 'PAD',
solids : [this.app.findSolid(this.face.solid.tCadId)],
face : this.app.findFace(this.face.id),
params : this.operationParams,
protoParams : this.getParams()
};
};
ExtrudeWizard.prototype.dispose = function() {
OpWizard.prototype.dispose.call(this);
this.ui.box.close();
};

101
web/app/3d/wizards/plane.js Normal file
View file

@ -0,0 +1,101 @@
import {AXIS, IDENTITY_BASIS} from '../../math/l3space'
import * as tk from '../../ui/toolkit.js'
import {FACE_COLOR} from '../cad-utils'
import {addOkCancelLogic} from './wizard-commons'
export function PlaneWizard(viewer, initParams) {
this.previewGroup = new THREE.Object3D();
this.viewer = viewer;
addOkCancelLogic(this);
viewer.scene.add(this.previewGroup);
this.previewGroup.add(this.plane = this.createPlane());
this.operationParams = {
basis : IDENTITY_BASIS,
depth : 0
};
if (!initParams) {
initParams = PlaneWizard.DEFAULT_PARAMS;
}
this.ui = {};
this.createUI.apply(this, initParams);
this.synch();
}
PlaneWizard.DEFAULT_PARAMS = ['XY', 0];
PlaneWizard.prototype.createPlane = function() {
var geometry = new THREE.PlaneGeometry(750,750,1,1,1);
var material = new THREE.MeshLambertMaterial( { color : FACE_COLOR, transparent: true, opacity:0.5, side: THREE.DoubleSide });
return new THREE.Mesh(geometry, material);
};
PlaneWizard.prototype.update = function(orientation, w) {
if (orientation === 'XY') {
this.plane.rotation.x = 0;
this.plane.rotation.y = 0;
this.plane.rotation.z = 0;
this.plane.position.x = 0;
this.plane.position.y = 0;
this.plane.position.z = w;
this.operationParams.basis = IDENTITY_BASIS;
} else if (orientation === 'XZ') {
this.plane.rotation.x = Math.PI / 2;
this.plane.rotation.y = 0;
this.plane.rotation.z = 0;
this.plane.position.x = 0;
this.plane.position.y = w;
this.plane.position.z = 0;
this.operationParams.basis = [AXIS.X, AXIS.Z, AXIS.Y];
} else if (orientation === 'ZY') {
this.plane.rotation.x = 0;
this.plane.rotation.y = Math.PI / 2;
this.plane.rotation.z = 0;
this.plane.position.x = w;
this.plane.position.y = 0;
this.plane.position.z = 0;
this.operationParams.basis = [AXIS.Z, AXIS.Y, AXIS.X];
} else {
throw orientation + " isn't supported yet";
}
this.operationParams.depth = w;
this.viewer.render();
};
PlaneWizard.prototype.createUI = function(orientation, w) {
this.ui.box = new tk.Box();
var folder = new tk.Folder("Add a Plane");
tk.add(this.ui.box, folder);
var choice = ['XY', 'XZ', 'ZY'];
this.ui.orientation = new tk.InlineRadio(choice, choice, choice.indexOf(orientation));
this.ui.depth = new tk.Number("Depth", w);
tk.add(folder, this.ui.orientation);
tk.add(folder, this.ui.depth);
var onChange = tk.methodRef(this, "synch");
this.ui.orientation.root.find('input:radio').change(onChange);
this.ui.depth.input.on('t-change', onChange);
tk.add(folder, new tk.ButtonRow(["Cancel", "OK"], [tk.methodRef(this, "cancelClick"), tk.methodRef(this, "okClick")]));
};
PlaneWizard.prototype.synch = function() {
this.update.apply(this, this.getParams());
this.viewer.render();
};
PlaneWizard.prototype.getParams = function() {
return [this.ui.orientation.getValue(), this.ui.depth.input.val()]
};
PlaneWizard.prototype.createRequest = function(params) {
return {
type: 'PLANE',
solids : [],
params : this.operationParams,
protoParams : this.getParams()
}
};
PlaneWizard.prototype.dispose = function() {
this.viewer.scene.remove(this.previewGroup);
this.ui.box.close();
this.viewer.render();
};

View file

@ -0,0 +1,60 @@
import DPR from '../../utils/dpr'
const IMAGINE_MATERIAL = new THREE.LineBasicMaterial({
color: 0xFA8072,
linewidth: 1/DPR,
depthWrite: false,
depthTest: false
});
const BASE_MATERIAL = new THREE.LineBasicMaterial({
color: 0x8B0000,
linewidth: 3/DPR,
depthWrite: false,
depthTest: false
});
function addOkCancelLogic(wizard) {
wizard.apply = function() {};
wizard.onCancel = function() {};
wizard.okClick = function() {
this.apply();
this.dispose();
};
wizard.cancelClick = function() {
this.onCancel();
this.dispose();
};
}
function OpWizard(viewer) {
this.previewGroup = new THREE.Object3D();
this.lines = [];
this.viewer = viewer;
viewer.scene.add(this.previewGroup);
}
OpWizard.prototype.setupLine = function(lineId, a, b, material) {
var line = this.lines[lineId];
if (line === undefined) {
var lg = new THREE.Geometry();
lg.vertices.push(new THREE.Vector3().copy(a));
lg.vertices.push(new THREE.Vector3().copy(b));
line = new THREE.Line(lg, material);
line.renderOrder = 1e10;
this.previewGroup.add(line);
this.lines[lineId] = line;
} else {
line.geometry.vertices[0] = new THREE.Vector3().copy(a);
line.geometry.vertices[1] = new THREE.Vector3().copy(b);
line.geometry.verticesNeedUpdate = true;
}
};
OpWizard.prototype.dispose = function() {
this.viewer.scene.remove(this.previewGroup);
this.viewer.render();
};
export {OpWizard, IMAGINE_MATERIAL, BASE_MATERIAL, addOkCancelLogic}

View file

@ -1,146 +0,0 @@
import Vector from '../../math/vector'
import * as cad_utils from '../cad-utils'
import * as math from '../../math/math'
import {Matrix3, AXIS, ORIGIN, IDENTITY_BASIS} from '../../math/l3space'
import DPR from '../../utils/dpr'
var IMAGINE_MATERIAL = new THREE.LineBasicMaterial({
color: 0xFA8072,
linewidth: 1/DPR,
depthWrite: false,
depthTest: false
});
var BASE_MATERIAL = new THREE.LineBasicMaterial({
color: 0x8B0000,
linewidth: 3/DPR,
depthWrite: false,
depthTest: false
});
function OpWizard(viewer) {
this.previewGroup = new THREE.Object3D();
this.lines = [];
this.viewer = viewer;
viewer.scene.add(this.previewGroup);
}
OpWizard.prototype.setupLine = function(lineId, a, b, material) {
var line = this.lines[lineId];
if (line === undefined) {
var lg = new THREE.Geometry();
lg.vertices.push(new THREE.Vector3().copy(a));
lg.vertices.push(new THREE.Vector3().copy(b));
line = new THREE.Line(lg, material);
line.renderOrder = 1e10;
this.previewGroup.add(line);
this.lines[lineId] = line;
} else {
line.geometry.vertices[0] = new THREE.Vector3().copy(a);
line.geometry.vertices[1] = new THREE.Vector3().copy(b);
line.geometry.verticesNeedUpdate = true;
}
};
OpWizard.prototype.dispose = function() {
this.viewer.scene.remove(this.previewGroup);
this.viewer.render();
};
function ExtrudeWizard(viewer, polygons) {
OpWizard.call(this, viewer);
this.polygons = polygons;
}
ExtrudeWizard.prototype = Object.create( OpWizard.prototype );
ExtrudeWizard.prototype.update = function(basis, normal, depth, scale, deflection, angle) {
var linesCounter = 0;
var target;
if (deflection != 0) {
target = normal.copy();
if (depth < 0) target._negate();
target = Matrix3.rotateMatrix(deflection * Math.PI / 180, basis[0], ORIGIN)._apply(target);
if (angle != 0) {
target = Matrix3.rotateMatrix(angle * Math.PI / 180, basis[2], ORIGIN)._apply(target);
}
target._multiply(Math.abs(depth));
} else {
target = normal.multiply(depth)
}
for (var i = 0; i < this.polygons.length; i++) {
var poly = this.polygons[i];
var lid = cad_utils.calculateExtrudedLid(poly, normal, target, scale);
var p, q, n = poly.length;
for (p = n - 1, q = 0; q < n; p = q++) {
this.setupLine(linesCounter ++, poly[p], poly[q], BASE_MATERIAL);
this.setupLine(linesCounter ++, lid[p], lid[q], IMAGINE_MATERIAL);
}
for (q = 0; q < n; q++) {
this.setupLine(linesCounter ++, poly[q], lid[q], IMAGINE_MATERIAL);
}
}
this.operationParams = {
target : target,
expansionFactor : scale
}
};
function PlaneWizard(viewer) {
this.previewGroup = new THREE.Object3D();
this.viewer = viewer;
viewer.scene.add(this.previewGroup);
this.previewGroup.add(this.plane = this.createPlane());
this.viewer.render();
this.operationParams = {
basis : IDENTITY_BASIS,
depth : 0
};
}
PlaneWizard.prototype.createPlane = function() {
var geometry = new THREE.PlaneGeometry(750,750,1,1,1);
var material = new THREE.MeshLambertMaterial( { color : cad_utils.FACE_COLOR, transparent: true, opacity:0.5, side: THREE.DoubleSide });
var plane = new THREE.Mesh(geometry, material);
return plane;
};
PlaneWizard.prototype.update = function(orientation, w) {
if (orientation === 'XY') {
this.plane.rotation.x = 0;
this.plane.rotation.y = 0;
this.plane.rotation.z = 0;
this.plane.position.x = 0;
this.plane.position.y = 0;
this.plane.position.z = w;
this.operationParams.basis = IDENTITY_BASIS;
} else if (orientation === 'XZ') {
this.plane.rotation.x = Math.PI / 2;
this.plane.rotation.y = 0;
this.plane.rotation.z = 0;
this.plane.position.x = 0;
this.plane.position.y = w;
this.plane.position.z = 0;
this.operationParams.basis = [AXIS.X, AXIS.Z, AXIS.Y];
} else if (orientation === 'ZY') {
this.plane.rotation.x = 0;
this.plane.rotation.y = Math.PI / 2;
this.plane.rotation.z = 0;
this.plane.position.x = w;
this.plane.position.y = 0;
this.plane.position.z = 0;
this.operationParams.basis = [AXIS.Z, AXIS.Y, AXIS.X];
} else {
throw orientation + " isn't supported yet";
}
this.operationParams.depth = w;
this.viewer.render();
};
PlaneWizard.prototype.dispose = function() {
this.viewer.scene.remove(this.previewGroup);
this.viewer.render();
};
export {ExtrudeWizard, PlaneWizard}