wizard for cut operation

This commit is contained in:
Val Erastov 2017-01-17 01:57:40 -08:00
parent 0225493404
commit 8f3c07f952
13 changed files with 137 additions and 98 deletions

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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
});

View file

@ -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)));
};

View file

@ -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) {

View file

@ -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 = [];

View file

@ -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) => {
}

View file

@ -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() {

View file

@ -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;

View file

@ -42,7 +42,9 @@ export class SceneSolid {
}
vanish() {
throw 'not implemented';
this.cadGroup.parent.remove( this.cadGroup );
this.material.dispose();
this.mesh.geometry.dispose();
}
}

View file

@ -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) {

View file

@ -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");