From 8f3c07f9522eec4e5d6f6f7615127db6f03dc7fe Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Tue, 17 Jan 2017 01:57:40 -0800 Subject: [PATCH] wizard for cut operation --- web/app/3d/craft/brep/cut-extrude.js | 35 ++++++++++++--- web/app/3d/craft/brep/sketch-reader.js | 4 +- web/app/3d/craft/brep/wizards/cut-extrude.js | 21 ++++----- .../3d/craft/brep/wizards/preview-wizard.js | 45 +++++++++++++------ web/app/3d/craft/brep/wizards/wizard.js | 39 ++++++++++------ web/app/3d/craft/craft.js | 35 +++------------ web/app/3d/craft/mesh/workbench.js | 4 +- web/app/3d/craft/operations.js | 9 ++-- web/app/3d/modeler-app.js | 8 ++-- web/app/3d/scene/mesh-scene-object.js | 8 +--- web/app/3d/scene/scene-object.js | 4 +- web/app/3d/ui/ctrl.js | 19 ++++++-- web/app/brep/operations/boolean.js | 4 +- 13 files changed, 137 insertions(+), 98 deletions(-) diff --git a/web/app/3d/craft/brep/cut-extrude.js b/web/app/3d/craft/brep/cut-extrude.js index 249babc1..5f1fe944 100644 --- a/web/app/3d/craft/brep/cut-extrude.js +++ b/web/app/3d/craft/brep/cut-extrude.js @@ -2,15 +2,40 @@ import {Matrix3, ORIGIN} from '../../../math/l3space' import * as math from '../../../math/math' import Vector from '../../../math/vector' import {Extruder} from '../../../brep/brep-builder' +import {BREPValidator} from '../../../brep/brep-validator' +import {subtract} from '../../../brep/operations/boolean' +import {Loop} from '../../../brep/topo/loop' +import {ReadSketchFromFace} from './sketch-reader' + +import {BREPSceneSolid} from '../../scene/brep-scene-object' export function Extrude(app, params) { } +export function Cut(app, params) { + const face = app.findFace(params.face); + const solid = face.solid; + const sketch = ReadSketchFromFace(app, face); + for (let polygon of sketch) { + if (!Loop.isPolygonCCWOnSurface(polygon, face.brepFace.surface)) { + polygon.reverse(); + } + } - -export function Cut(face, params) { + const extruder = new ParametricExtruder(face, params); + const cutter = extruder.extrude(sketch[0]); + BREPValidator.validateToConsole(cutter); + const newSolid = new BREPSceneSolid(subtract(solid.shell, cutter)); + //const newSolid = new BREPSceneSolid(cutter); + solid.vanish(); + + app.viewer.workGroup.add(newSolid.cadGroup); + app.bus.notify('solid-list', { + solids: [], + needRefresh: [newSolid] + }); } @@ -32,7 +57,7 @@ export class ParametricExtruder extends Extruder { } target._multiply(Math.abs(this.params.value)); } else { - target = normal.multiply(Math.abs(this.params.value)); + target = lidNormal.multiply(Math.abs(this.params.value)); } this.target = target; } @@ -40,10 +65,10 @@ export class ParametricExtruder extends Extruder { calculateLid(basePoints) { if (this.params.prism != 1) { const scale = this.params.prism < 0.001 ? 0.0001 : this.params.prism; - const _3Dtr = this.face.surface.get3DTransformation(); + const _3Dtr = this.face.brepFace.surface.get3DTransformation(); const _2Dtr = _3Dtr.invert(); const poly2d = basePoints.map(p => _2Dtr.apply(p)); - basePoints = math.polygonOffset(poly2d, scale).map(p => _2Dtr.apply(p)); + basePoints = math.polygonOffset(poly2d, scale).map(p => _3Dtr.apply(p)); } return basePoints.map(p => p.plus(this.target)); } diff --git a/web/app/3d/craft/brep/sketch-reader.js b/web/app/3d/craft/brep/sketch-reader.js index 5d146f77..cb8c6e8b 100644 --- a/web/app/3d/craft/brep/sketch-reader.js +++ b/web/app/3d/craft/brep/sketch-reader.js @@ -4,6 +4,6 @@ import {sortPolygons, getSketchedPolygons3D} from '../mesh/workbench' // here will be function of conversion sketch objects to brep DS -export function ReadSketchFromFace(app, faceId) { - return getSketchedPolygons3D(app, faceId); +export function ReadSketchFromFace(app, face) { + return getSketchedPolygons3D(app, face); } \ No newline at end of file diff --git a/web/app/3d/craft/brep/wizards/cut-extrude.js b/web/app/3d/craft/brep/wizards/cut-extrude.js index 83938a0e..f6d6f56f 100644 --- a/web/app/3d/craft/brep/wizards/cut-extrude.js +++ b/web/app/3d/craft/brep/wizards/cut-extrude.js @@ -1,5 +1,5 @@ import {CURRENT_SELECTION as S} from './wizard' -import {PreviewWizard, SketchBasedPreviewMaker} from './preview-wizard' +import {PreviewWizard, SketchBasedPreviewer} from './preview-wizard' import {ParametricExtruder} from '../cut-extrude' const METADATA = [ @@ -10,9 +10,9 @@ const METADATA = [ ['face' , 'face' , S ] ]; -class Cut extends PreviewWizard { +export class CutWizard extends PreviewWizard { constructor(app, initialState) { - super(app, 'CUT', METADATA, null, initialState) + super(app, 'CUT', METADATA, new ExtrudePreviewer(true), initialState) } uiLabel(name) { @@ -21,9 +21,9 @@ class Cut extends PreviewWizard { } } -class Extrude extends PreviewWizard { +export class ExtrudeWizard extends PreviewWizard { constructor(app, initialState) { - super(app, 'EXTRUDE', METADATA, new ExtrudePreviewMaker(), initialState) + super(app, 'EXTRUDE', METADATA, new ExtrudePreviewer(false), initialState) } uiLabel(name) { @@ -32,18 +32,19 @@ class Extrude extends PreviewWizard { } } -export class ExtrudePreviewMaker extends SketchBasedPreviewMaker{ +export class ExtrudePreviewer extends SketchBasedPreviewer { - constructor(cut) { + constructor(inversed) { super(); - this.cut = cut; + this.inversed = inversed; } createImpl(app, params, sketch, face) { const parametricExtruder = new ParametricExtruder(face, params); - const baseNormal = this.cut ? face.surface.normal : face.surface.normal.negate(); - const lidNormal = this.cut ? baseNormal.negate() : face.surface.normal; + const surface = face.brepFace.surface; + const baseNormal = this.inversed ? surface.normal : surface.normal.negate(); + const lidNormal = this.inversed ? baseNormal.negate() : surface.normal; parametricExtruder.prepareLidCalculation(baseNormal, lidNormal); diff --git a/web/app/3d/craft/brep/wizards/preview-wizard.js b/web/app/3d/craft/brep/wizards/preview-wizard.js index 8ccfb673..10bed0a9 100644 --- a/web/app/3d/craft/brep/wizards/preview-wizard.js +++ b/web/app/3d/craft/brep/wizards/preview-wizard.js @@ -4,20 +4,26 @@ import {Loop} from '../../../../brep/topo/loop' export class PreviewWizard extends Wizard { - constructor(app, opearation, metadata, previewMaker, initialState) { + constructor(app, opearation, metadata, previewer, initialState) { super(app, opearation, metadata, initialState); + this.operation = opearation; this.previewGroup = new THREE.Object3D(); - this.previewMaker = previewMaker; + this.previewer = previewer; this.previewObject = null; this.app.viewer.workGroup.add(this.previewGroup); this.updatePreview(); } + createRequest() { + return { + type: this.operation, + params: this.readFormFields() + }; + } + updatePreview() { - if (this.previewObject != null) { - this.destroyPreviewObject(); - } - this.previewObject = this.previewMaker.create(this.app, this.readFormFields()); + this.destroyPreviewObject(); + this.previewObject = this.previewer.create(this.app, this.readFormFields()); if (this.previewObject != null) { this.previewGroup.add( this.previewObject ); } @@ -25,14 +31,22 @@ export class PreviewWizard extends Wizard { } destroyPreviewObject() { - this.previewGroup.parent.remove( this.previewObject ); - this.previewObject.geometry.dispose(); - this.previewGroup = null; + if (this.previewObject != null) { + this.previewGroup.remove( this.previewObject ); + this.previewObject.geometry.dispose(); + this.previewObject = null; + } + } + + onUIChange() { + super.onUIChange(); + this.updatePreview(); } - dispose() { + this.destroyPreviewObject(); this.app.viewer.workGroup.remove(this.previewGroup); + this.app.viewer.render(); super.dispose(); } } @@ -54,7 +68,7 @@ PreviewWizard.createMesh = function(triangles) { return new THREE.Mesh(geometry, IMAGINARY_SURFACE_MATERIAL); }; -export class SketchBasedPreviewMaker { +export class SketchBasedPreviewer { constructor() { this.fixToCCW = true; @@ -69,15 +83,16 @@ export class SketchBasedPreviewMaker { if (!face) return null; const needSketchRead = !this.sketch || params.face != this.face; if (needSketchRead) { - this.sketch = ReadSketchFromFace(app, params.face); + this.sketch = ReadSketchFromFace(app, face); for (let polygon of this.sketch) { - if (!Loop.isPolygonCCWOnSurface(polygon, face.surface) && this.fixToCCW) { + if (!Loop.isPolygonCCWOnSurface(polygon, face.brepFace.surface) && this.fixToCCW) { polygon.reverse(); } } this.face = params.face; } - return this.createImpl(app, params, this.sketch, face); + const triangles = this.createImpl(app, params, this.sketch, face); + return PreviewWizard.createMesh(triangles); } } @@ -87,6 +102,8 @@ export const IMAGINARY_SURFACE_MATERIAL = new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, shininess: 0, + depthWrite: false, + depthTest: false, side : THREE.DoubleSide }); diff --git a/web/app/3d/craft/brep/wizards/wizard.js b/web/app/3d/craft/brep/wizards/wizard.js index 3ac1e438..dece5f99 100644 --- a/web/app/3d/craft/brep/wizards/wizard.js +++ b/web/app/3d/craft/brep/wizards/wizard.js @@ -4,7 +4,7 @@ export class Wizard { constructor(app, opearation, metadata, initialState) { this.app = app; - this.metadata = params; + this.metadata = metadata; this.formFields = {}; this.box = this.createUI(opearation, metadata); if (initialState != undefined) { @@ -16,6 +16,10 @@ export class Wizard { return name; } + focus() { + this.box.root.find('input, select').first().focus() + } + createUI(operation, metadata) { const box = new tk.Box($('#view-3d')); const folder = new tk.Folder(operation); @@ -23,10 +27,10 @@ export class Wizard { for (let def of metadata) { const name = def[0]; const type = def[1]; - const defaultValue = def[1]; + const defaultValue = def[2]; const params = def[3]; const label = this.uiLabel(name); - const formItem = createFormField(name, label, type, defaultValue, params); + const formItem = this.createFormField(name, label, type, params); formItem.setter(defaultValue); tk.add(folder, formItem.ui); this.formFields[name] = formItem; @@ -48,17 +52,21 @@ export class Wizard { } okClick() { + this.apply(); this.dispose(); } - onUIChange() { - + apply() { + this.app.craft.modify(this.createRequest(), false); } + + onUIChange() {} readFormFields() { const params = {}; - for (let field of this.formFields) { - params[field.name] = field.getter(); + const keys = Object.keys(this.formFields); + for (let key of keys) { + params[key] = this.formFields[key].getter(); } return params; } @@ -80,13 +88,13 @@ export class Wizard { createFormField(name, label, type, params) { if (type == 'number') { - const number = tk.config(tk.Number(label, 0, params.step, params.round), params); + const number = tk.config(new tk.Number(label, 0, params.step, params.round), params); number.input.on('t-change', () => this.onUIChange(name)); - return Field.fromInput(number.input); + return Field.fromInput(number, Field.TEXT_TO_NUMBER_COERCION); } else if (type == 'face') { const face = new tk.Text(label, ''); face.input.on('change', () => this.onUIChange(name)); - return Field.fromInput(face.input, undefined, (faceId) => { + return Field.fromInput(face, undefined, (faceId) => { if (faceId === CURRENT_SELECTION) { let selection = this.app.viewer.selectionMgr.selection[0]; return selection ? selection.id : ''; @@ -100,17 +108,20 @@ function FaceSelectionListener() { this.callbacks = []; } -function Field(getter, setter) { +function Field(ui, getter, setter) { + this.ui = ui; this.getter = getter; this.setter = setter; } -Field.NO_COERCION = (v) => v; +Field.NO_COERCION = (v) => v; +Field.NUMBER_TO_TEXT_COERCION = (v) => v + ""; +Field.TEXT_TO_NUMBER_COERCION = (v) => parseFloat(v); -Field.fromInput = function (input, getterCoercer, setterCoercer) { +Field.fromInput = function (inputEl, getterCoercer, setterCoercer) { getterCoercer = getterCoercer || Field.NO_COERCION; setterCoercer = setterCoercer || Field.NO_COERCION; - return new Field(() => getterCoercer(input.val()), (value) => input.val(setterCoercer(value))); + return new Field(inputEl, () => getterCoercer(inputEl.input.val()), (value) => inputEl.input.val(setterCoercer(value))); }; diff --git a/web/app/3d/craft/craft.js b/web/app/3d/craft/craft.js index 7a39c1aa..dc1089b1 100644 --- a/web/app/3d/craft/craft.js +++ b/web/app/3d/craft/craft.js @@ -2,6 +2,7 @@ import Counters from '../counters' export function Craft(app) { this.app = app; + this.operations = {}; this.history = []; this.solids = []; this._historyPointer = 0; @@ -18,6 +19,10 @@ export function Craft(app) { }); } +Craft.prototype.registerOperation = function(name, action) { + this.operations[name] = action; +}; + Craft.prototype.remove = function(modificationIndex) { const history = this.history; history.splice(modificationIndex, history.length - modificationIndex); @@ -62,35 +67,7 @@ Craft.prototype.modifyInternal = function(request) { var op = this.operations[request.type]; if (!op) return; - var newSolids = op(this.app, request.params); - if (newSolids == null) return; - const toUpdate = []; - for (let i = 0; i < request.solids.length; i++) { - let solid = request.solids[i]; - var indexToRemove = this.solids.indexOf(solid); - if (indexToRemove != -1) { - let updatedIdx = newSolids.findIndex((s) => s.id == solid.id); - if (updatedIdx != -1) { - toUpdate[updatedIdx] = indexToRemove; - } else { - this.solids.splice(indexToRemove, 1); - } - } - solid.vanish(); - } - for (let i = 0; i < newSolids.length; i++) { - let solid = newSolids[i]; - if (toUpdate[i] !== undefined) { - this.solids[toUpdate[i]] = solid; - } else { - this.solids.push(solid); - } - this.app.viewer.workGroup.add(solid.cadGroup); - } - this.app.bus.notify('solid-list', { - solids: this.solids, - needRefresh: newSolids - }); + op(this.app, request.params); }; Craft.prototype.modify = function(request, overriding) { diff --git a/web/app/3d/craft/mesh/workbench.js b/web/app/3d/craft/mesh/workbench.js index e475997d..2a1b4e3b 100644 --- a/web/app/3d/craft/mesh/workbench.js +++ b/web/app/3d/craft/mesh/workbench.js @@ -170,7 +170,7 @@ export function getSketchedPolygons3D(app, face) { var geom = readSketchGeom(JSON.parse(savedFace), face.id, false); var polygons2D = cad_utils.sketchToPolygons(geom); - var normal = face.csgGroup.normal; + var normal = face.normal(); var depth = null; var sketchedPolygons = []; for (var i = 0; i < polygons2D.length; i++) { @@ -180,7 +180,7 @@ export function getSketchedPolygons3D(app, face) { if (depth == null) { var _3dTransformation = new Matrix3().setBasis(face.basis()); //we lost depth or z off in 2d sketch, calculate it again - depth = face.csgGroup.plane.w; + depth = face.depth(); } var polygon = []; diff --git a/web/app/3d/craft/operations.js b/web/app/3d/craft/operations.js index 4b90c01d..0bfbd81f 100644 --- a/web/app/3d/craft/operations.js +++ b/web/app/3d/craft/operations.js @@ -1,18 +1,17 @@ import {MESH_OPERATIONS} from './mesh/workbench' -import {Extrude} from './brep/cut-extrude' +import {Extrude, Cut} from './brep/cut-extrude' export const CUT = { icon: 'img/3d/cut', label: 'Cut', - info: (p) => '(' + r(p.depth) + ')', - action: (app, request) => { - } + info: (p) => '(' + r(p.value) + ')', + action: (app, params) => Cut(app, params) }; export const PAD = { icon: 'img/3d/extrude', label: 'Extrude', - info: (p) => '(' + r(p.height) + ')', + info: (p) => '(' + r(p.value) + ')', action: (app, request) => { } diff --git a/web/app/3d/modeler-app.js b/web/app/3d/modeler-app.js index c215de4d..a9f58ecf 100644 --- a/web/app/3d/modeler-app.js +++ b/web/app/3d/modeler-app.js @@ -33,8 +33,8 @@ function App() { this.tabSwitcher = new TabSwitcher($('#tab-switcher'), $('#view-3d')); this.controlBar = new ControlBar(this, $('#control-bar')); - this.ui = new UI(this); this.craft = new Craft(this); + this.ui = new UI(this); AddDebugSupport(this); @@ -44,6 +44,8 @@ function App() { this.load(); } + this.BREPTest(); + this._refreshSketches(); this.viewer.render(); @@ -69,11 +71,11 @@ function App() { } app._refreshSketches(); }); - this.BREPTest(); } App.prototype.BREPTest = function() { - setTimeout(() => this.BREPTestImpl()); + this.BREPTestImpl(); + //setTimeout(() => this.BREPTestImpl()); }; App.prototype.BREPTestImpl1 = function() { diff --git a/web/app/3d/scene/mesh-scene-object.js b/web/app/3d/scene/mesh-scene-object.js index 6fa97266..bfe632e6 100644 --- a/web/app/3d/scene/mesh-scene-object.js +++ b/web/app/3d/scene/mesh-scene-object.js @@ -78,13 +78,7 @@ export class MeshSceneSolid extends SceneSolid { this.processWires(); }; - - vanish () { - this.cadGroup.parent.remove( this.cadGroup ); - this.material.dispose(); - this.mesh.geometry.dispose(); - } - + collectCurvedSurface(face) { var derivedFrom = getDerivedFrom(face.csgGroup.shared); if (derivedFrom === null || !isCurveClass(derivedFrom._class)) return; diff --git a/web/app/3d/scene/scene-object.js b/web/app/3d/scene/scene-object.js index c0b1cb64..4f8a8cbd 100644 --- a/web/app/3d/scene/scene-object.js +++ b/web/app/3d/scene/scene-object.js @@ -42,7 +42,9 @@ export class SceneSolid { } vanish() { - throw 'not implemented'; + this.cadGroup.parent.remove( this.cadGroup ); + this.material.dispose(); + this.mesh.geometry.dispose(); } } diff --git a/web/app/3d/ui/ctrl.js b/web/app/3d/ui/ctrl.js index 5709a4b5..2a95b558 100644 --- a/web/app/3d/ui/ctrl.js +++ b/web/app/3d/ui/ctrl.js @@ -6,7 +6,9 @@ import ToolBar from './toolbar' import * as MenuConfig from '../menu/menu-config' import * as Operations from '../craft/operations' import Menu from '../menu/menu' -import {ExtrudeWizard} from '../craft/mesh/wizards/extrude' +import {CutWizard} from '../craft/brep/wizards/cut-extrude' + +import {ExtrudeWizard as MeshExtrudeWizard} from '../craft/mesh/wizards/extrude' import {RevolveWizard} from '../craft/mesh/wizards/revolve' import {PlaneWizard} from '../craft/mesh/wizards/plane' import {BoxWizard} from '../craft/mesh/wizards/box' @@ -56,6 +58,15 @@ function UI(app) { app.bus.subscribe("solid-pick", function(solid) { ui.registerWizard(new TransformWizard(app.viewer, solid)); }); + registerOperations(app); +} + +function registerOperations(app) { + const opNames = Object.keys(Operations); + for (let opName of opNames) { + console.log('Registering Operation ' + opName); + app.craft.registerOperation(opName, Operations[opName].action); + } } UI.prototype.createCraftToolBar = function (vertPos) { @@ -121,7 +132,7 @@ UI.prototype.fillControlBar = function() { }; UI.prototype.registerWizard = function(wizard, overridingHistory) { - wizard.ui.box.root.css({left : (this.mainBox.root.width() + this.craftToolBar.node.width() + 30) + 'px', top : 0}); + wizard.box.root.css({left : (this.mainBox.root.width() + this.craftToolBar.node.width() + 30) + 'px', top : 0}); var craft = this.app.craft; wizard.onRequestReady = function(request) { if (request.invalidAndShouldBeDropped == true) { @@ -167,9 +178,9 @@ UI.prototype.createWizardForOperation = function(op) { UI.prototype.createWizard = function(type, overridingHistory, initParams, face) { let wizard = null; if ('CUT' === type) { - wizard = new ExtrudeWizard(this.app, face, true, initParams); + wizard = new CutWizard(this.app, initParams); } else if ('PAD' === type) { - wizard = new ExtrudeWizard(this.app, face, false, initParams); + wizard = new MeshExtrudeWizard(this.app, face, false, initParams); } else if ('REVOLVE' === type) { wizard = new RevolveWizard(this.app, face, initParams); } else if ('PLANE' === type) { diff --git a/web/app/brep/operations/boolean.js b/web/app/brep/operations/boolean.js index 99ed75d6..b4de0dae 100644 --- a/web/app/brep/operations/boolean.js +++ b/web/app/brep/operations/boolean.js @@ -77,7 +77,7 @@ export function BooleanAlgorithm( shell1, shell2, type ) { } const loop = new Loop(); while (edge) { - //__DEBUG__.AddHalfEdge(edge); + __DEBUG__.AddHalfEdge(edge); const isNew = faceData.newEdges.indexOf(edge) != -1; if (isNew) newLoops.add(loop); @@ -435,7 +435,7 @@ function intersectFaceWithEdge(face, edge, result, vertecies) { //TODO: should check if point on a vertex then exclude two edges of the vertex from further intersection test cuz it would produce three identical Nodes if (pointBelongsToFace(pointOfIntersection, face)) { let vertexOfIntersection; - if (math.areVectorsEqual(edge.vertexA.point, pointOfIntersection, TOLERANCE)) { + if (math.areVectorsEqual(edge.vertexA.point, pointOfIntersection, TOLERANCE)) { //TODO: TOLERANCE^2 ??? vertecies.add(edge.vertexA); vertexOfIntersection = edge.vertexA; //console.log("point A on surface");