extrude operation on top of BREP framework

This commit is contained in:
Val Erastov 2017-03-16 02:45:27 -07:00
parent 2b0b408d73
commit 43780d3188
14 changed files with 72 additions and 53 deletions

View file

@ -14,7 +14,7 @@ export const CUT = mergeInfo('CUT', {
info: 'makes a cut based on 2D sketch'
});
export const PAD = mergeInfo('PAD', {
export const EXTRUDE = mergeInfo('EXTRUDE', {
info: 'extrudes 2D sketch'
});
@ -55,7 +55,7 @@ export const IMPORT_STL = mergeInfo('IMPORT_STL', {
});
requiresFaceSelection(CUT, 1);
requiresFaceSelection(PAD, 1);
requiresFaceSelection(EXTRUDE, 1);
requiresFaceSelection(REVOLVE, 1);
requiresSolidSelection(INTERSECTION, 2);

View file

@ -1,9 +1,9 @@
import {Matrix3, ORIGIN} from '../../../math/l3space'
import {Matrix3, BasisForPlane, 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 {subtract, union} from '../../../brep/operations/boolean'
import {Loop} from '../../../brep/topo/loop'
import {Shell} from '../../../brep/topo/shell'
import {ReadSketchFromFace} from './sketch-reader'
@ -11,24 +11,25 @@ import {ReadSketchFromFace} from './sketch-reader'
import {BREPSceneSolid} from '../../scene/brep-scene-object'
export function Extrude(app, params) {
return doOperation(app, params, false);
}
export function Cut(app, params) {
return doOperation(app, params, true);
}
export function doOperation(app, params, cut) {
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();
// }
//}
const extruder = new ParametricExtruder(face, params);
const cutter = combineCutters(sketch.map(s => extruder.extrude(s, face.brepFace.surface.normal))) ;
BREPValidator.validateToConsole(cutter);
const result = subtract(solid.shell, cutter);
const extruder = new ParametricExtruder(params);
let normal = face.brepFace.surface.normal;
if (!cut) normal = normal.negate();
const operand = combineShells(sketch.map(s => extruder.extrude(s, normal)));
BREPValidator.validateToConsole(operand);
const op = cut ? subtract : union;
const result = op(solid.shell, operand);
for (let newFace of result.faces) {
if (newFace.id == face.id) {
newFace.id = undefined;
@ -41,30 +42,29 @@ export function Cut(app, params) {
}
}
function combineCutters(cutters) {
if (cutters.length == 1) {
return cutters[0];
function combineShells(shells) {
if (shells.length == 1) {
return shells[0];
}
const cutter = new Shell();
cutters.forEach(c => c.faces.forEach(f => cutter.faces.push(f)));
shells.forEach(c => c.faces.forEach(f => cutter.faces.push(f)));
return cutter;
}
export class ParametricExtruder extends Extruder {
constructor(face, params) {
constructor(params) {
super();
this.face = face;
this.params = params;
}
prepareLidCalculation(baseNormal, lidNormal) {
let target;
this.basis = BasisForPlane(baseNormal);
if (this.params.rotation != 0) {
const basis = this.face.basis();
target = Matrix3.rotateMatrix(this.params.rotation * Math.PI / 180, basis[0], ORIGIN).apply(lidNormal);
target = Matrix3.rotateMatrix(this.params.rotation * Math.PI / 180, this.basis[0], ORIGIN).apply(lidNormal);
if (this.params.angle != 0) {
target = Matrix3.rotateMatrix(this.params.angle * Math.PI / 180, basis[2], ORIGIN)._apply(target);
target = Matrix3.rotateMatrix(this.params.angle * Math.PI / 180, this.basis[2], ORIGIN)._apply(target);
}
target._multiply(Math.abs(this.params.value));
} else {
@ -73,10 +73,11 @@ export class ParametricExtruder extends Extruder {
this.target = target;
}
calculateLid(basePoints) {
calculateLid(basePoints, baseNormal, lidNormal) {
if (this.params.prism != 1) {
const scale = this.params.prism;
const _3Dtr = this.face.brepFace.surface.get3DTransformation();
const _3Dtr = new Matrix3().setBasis(this.basis);
const _2Dtr = _3Dtr.invert();
const poly2d = basePoints.map(p => _2Dtr.apply(p));
basePoints = math.polygonOffset(poly2d, scale).map(p => _3Dtr.apply(p));

View file

@ -27,7 +27,7 @@ export class CutWizard extends PreviewWizard {
export class ExtrudeWizard extends PreviewWizard {
constructor(app, initialState) {
super(app, 'EXTRUDE', METADATA, new ExtrudePreviewer(false), initialState)
super(app, 'EXTRUDE', METADATA, initialState)
}
createPreviewObject(app, params) {
@ -48,7 +48,7 @@ export class ExtrudePreviewer extends SketchBasedPreviewer {
}
createImpl(app, params, sketch, face) {
const parametricExtruder = new ParametricExtruder(face, params);
const parametricExtruder = new ParametricExtruder(params);
const surface = face.brepFace.surface;
const baseNormal = this.inversed ? surface.normal : surface.normal.negate();

View file

@ -96,7 +96,7 @@ ExtrudeWizard.prototype.getParams = function() {
ExtrudeWizard.prototype.createRequest = function(done) {
done({
type : this.invert ? 'CUT' : 'PAD',
type : this.invert ? 'CUT' : 'EXTRUDE',
solids : [this.app.findSolidByCadId(this.face.solid.tCadId)],
face : this.app.findFace(this.face.id),
params : this.operationParams,

View file

@ -793,7 +793,7 @@ function materialize(index, detachedConfig) {
export const MESH_OPERATIONS = {
CUT : cut,
PAD : extrude,
EXTRUDE : extrude,
REVOLVE : performRevolve,
PLANE : function(app, request) {
let basis, depth = request.params.depth;

View file

@ -10,13 +10,11 @@ export const CUT = {
action: (app, params) => Cut(app, params)
};
export const PAD = {
export const EXTRUDE = {
icon: 'img/3d/extrude',
label: 'Extrude',
info: (p) => '(' + r(p.value) + ')',
action: (app, request) => {
}
action: (app, params) => Extrude(app, params)
};
export const REVOLVE = {

View file

@ -8,7 +8,7 @@ export const craft = {
label: 'craft',
cssIcons: ['magic'],
info: 'set of available craft operations on a solid',
actions: ['PAD', 'CUT', 'REVOLVE', 'SHELL']
actions: ['EXTRUDE', 'CUT', 'REVOLVE', 'SHELL']
};
export const primitives = {
@ -29,7 +29,7 @@ export const main = {
label: 'start',
cssIcons: ['rocket'],
info: 'common set of actions',
actions: ['PAD', 'CUT', 'SHELL', '-', 'INTERSECTION', 'DIFFERENCE', 'UNION', '-', 'PLANE', 'BOX', 'SPHERE', '-',
actions: ['EXTRUDE', 'CUT', 'SHELL', '-', 'INTERSECTION', 'DIFFERENCE', 'UNION', '-', 'PLANE', 'BOX', 'SPHERE', '-',
'EditFace', '-', 'DeselectAll', 'RefreshSketches']
};

View file

@ -20,7 +20,6 @@ import '../../css/app3d.less'
import * as BREPBuilder from '../brep/brep-builder'
import * as BREPPrimitives from '../brep/brep-primitives'
import * as BREPBool from '../brep/operations/boolean'
import * as BREPMeshBool from './craft/mesh/mesh-boolean'
import {BREPValidator} from '../brep/brep-validator'
import {BREPSceneSolid} from './scene/brep-scene-object'
import TPI from './tpi'
@ -262,7 +261,7 @@ App.prototype.BREPMeshTestImpl = function() {
//addToScene(box2);
//addToScene(box3);
let result = BREPMeshBool.subtract(box1, box2);
//let result = BREPMeshBool.subtract(box1, box2);
//result = BREPBool.subtract(result, box3);
//this.addShellOnScene(result);
//addToScene(box1);
@ -548,7 +547,7 @@ App.prototype.extrude = function() {
var app = this;
var solids = [polyFace.solid];
this.craft.modify({
type: 'PAD',
type: 'EXTRUDE',
solids : solids,
face : polyFace,
height : height

View file

@ -1,5 +1,5 @@
export function init() {
localStorage.setItem("TCAD.projects.sample", '{"history":[{"type":"PLANE","solids":[],"params":{"basis":[[1,0,0],[0,0,1],[0,1,0]],"depth":"0"},"protoParams":["XZ","0"]},{"type":"PAD","solids":[0],"face":"0:0","params":{"target":[0,-50,0],"expansionFactor":"1"},"protoParams":["50","1","0","0"]},{"type":"PAD","solids":[1],"face":"1:1","params":{"target":[0,-50,0],"expansionFactor":"1"},"protoParams":["50","1","0","0"]},{"type":"CUT","solids":[2],"face":"2:0","params":{"target":[0,252,0],"expansionFactor":"1"},"protoParams":["252","1","0","0"]},{"type":"CUT","solids":[3],"face":"1:1$","params":{"target":[0,50,0],"expansionFactor":"1"},"protoParams":["50","1","0","0"]}]}');
localStorage.setItem("TCAD.projects.sample", '{"history":[{"type":"PLANE","solids":[],"params":{"basis":[[1,0,0],[0,0,1],[0,1,0]],"depth":"0"},"protoParams":["XZ","0"]},{"type":"EXTRUDE","solids":[0],"face":"0:0","params":{"target":[0,-50,0],"expansionFactor":"1"},"protoParams":["50","1","0","0"]},{"type":"EXTRUDE","solids":[1],"face":"1:1","params":{"target":[0,-50,0],"expansionFactor":"1"},"protoParams":["50","1","0","0"]},{"type":"CUT","solids":[2],"face":"2:0","params":{"target":[0,252,0],"expansionFactor":"1"},"protoParams":["252","1","0","0"]},{"type":"CUT","solids":[3],"face":"1:1$","params":{"target":[0,50,0],"expansionFactor":"1"},"protoParams":["50","1","0","0"]}]}');
localStorage.setItem("TCAD.projects.sample.sketch.0:0", '{"layers":[{"name":"_dim","style":{"lineWidth":1,"strokeStyle":"#bcffc1","fillStyle":"#00FF00"},"data":[]},{"name":"__bounds__","style":{"lineWidth":2,"strokeStyle":"#fff5c3","fillStyle":"#000000"},"data":[{"id":6,"_class":"TCAD.TWO.Segment","aux":true,"edge":0,"points":[[0,[1,-400],[2,400]],[3,[4,-400],[5,-400]]]},{"id":13,"_class":"TCAD.TWO.Segment","aux":true,"edge":2,"points":[[7,[8,-400],[9,-400]],[10,[11,400],[12,-400]]]},{"id":20,"_class":"TCAD.TWO.Segment","aux":true,"edge":4,"points":[[14,[15,400],[16,-400]],[17,[18,400],[19,400]]]},{"id":27,"_class":"TCAD.TWO.Segment","aux":true,"edge":6,"points":[[21,[22,400],[23,400]],[24,[25,-400],[26,400]]]}]},{"name":"sketch","style":{"lineWidth":2,"strokeStyle":"#ffffff","fillStyle":"#000000"},"data":[{"id":34,"_class":"TCAD.TWO.Segment","points":[[28,[29,-80.41502600578134],[30,240.48794311524324]],[31,[32,252.10163324769275],[33,71.15131239804411]]]},{"id":41,"_class":"TCAD.TWO.Segment","points":[[35,[36,255.946878629896],[37,-145.76094357167156]],[38,[39,-91.17342089039929],[40,-338.36716169336114]]]},{"id":48,"_class":"TCAD.TWO.Segment","points":[[42,[43,-172.00749593627577],[44,-240.71428346724593]],[45,[46,-88.51020843368133],[47,-140.46311545122035]]]},{"id":55,"_class":"TCAD.TWO.Segment","points":[[49,[50,-102.18982576106004],[51,18.31440664196805]],[52,[53,-182.7982464866314],[54,86.82364838151852]]]},{"id":72,"_class":"TCAD.TWO.Arc","points":[[63,[64,255.946878629896],[65,-145.76094357167156]],[66,[67,252.10163324769275],[68,71.15131239804411]],[69,[70,196.33682709088268],[71,-38.32745196977044]]]},{"id":83,"_class":"TCAD.TWO.Arc","points":[[74,[75,-80.41502600578134],[76,240.48794311524324]],[77,[78,-182.7982464866314],[79,86.82364838151852]],[80,[81,-122.59914075444685],[82,157.65429488839598]]]},{"id":94,"_class":"TCAD.TWO.Arc","points":[[85,[86,-88.51020843368133],[87,-140.46311545122035]],[88,[89,-102.18982576106004],[90,18.31440664196805]],[91,[92,-175.53398017227],[93,-67.98267439986091]]]},{"id":105,"_class":"TCAD.TWO.Arc","points":[[96,[97,-172.00749593627577],[98,-240.71428346724593]],[99,[100,-91.17342089039929],[101,-338.36716169336114]],[102,[103,-122.4591797419898],[104,-281.9821285194346]]]}]},{"name":"_construction_","style":{"lineWidth":1,"strokeStyle":"#aaaaaa","fillStyle":"#000000"},"data":[]}],"constraints":[["Tangent",[72,41]],["Tangent",[72,34]],["coi",[63,35]],["coi",[66,31]],["Tangent",[83,34]],["Tangent",[83,55]],["coi",[74,28]],["coi",[77,52]],["Tangent",[94,48]],["Tangent",[94,55]],["coi",[85,45]],["coi",[88,49]],["Tangent",[105,48]],["Tangent",[105,41]],["coi",[96,42]],["coi",[99,38]]],"boundary":{"lines":[{"a":{"x":-400,"y":400},"b":{"x":-400,"y":-400}},{"a":{"x":-400,"y":-400},"b":{"x":400,"y":-400}},{"a":{"x":400,"y":-400},"b":{"x":400,"y":400}},{"a":{"x":400,"y":400},"b":{"x":-400,"y":400}}],"arcs":[],"circles":[]}}');
localStorage.setItem("TCAD.projects.sample.sketch.1:0", '{"boundary":{"lines":[{"a":{"x":80.41502600578134,"y":240.48794311524324},"b":{"x":-252.10163324769275,"y":71.15131239804411}},{"a":{"x":-255.946878629896,"y":-145.76094357167156},"b":{"x":91.17342089039929,"y":-338.36716169336114}},{"a":{"x":172.00749593627577,"y":-240.71428346724593},"b":{"x":88.51020843368133,"y":-140.46311545122035}},{"a":{"x":102.18982576106004,"y":18.31440664196805},"b":{"x":182.7982464866314,"y":86.82364838151852}}],"arcs":[{"a":{"x":-252.10163324769275,"y":71.15131239804411},"b":{"x":-255.946878629896,"y":-145.76094357167156},"c":{"x":-196.33682672526209,"y":-38.32745176660378}},{"a":{"x":91.17342089039929,"y":-338.36716169336114},"b":{"x":172.00749593627577,"y":-240.71428346724593},"c":{"x":122.45917974196499,"y":-281.98212851943595}},{"a":{"x":102.18982576106004,"y":18.31440664196805},"b":{"x":88.51020843368133,"y":-140.46311545122035},"c":{"x":175.5339801724776,"y":-67.98267439979745}},{"a":{"x":182.7982464866314,"y":86.82364838151852},"b":{"x":80.41502600578134,"y":240.48794311524324},"c":{"x":122.59914075460381,"y":157.65429488902345}}],"circles":[]}}');
localStorage.setItem("TCAD.projects.sample.sketch.1:1", '{"layers":[{"name":"_dim","style":{"lineWidth":1,"strokeStyle":"#bcffc1","fillStyle":"#00FF00"},"data":[]},{"name":"__bounds__","style":{"lineWidth":2,"strokeStyle":"#fff5c3","fillStyle":"#000000"},"data":[{"id":6,"_class":"TCAD.TWO.Segment","aux":true,"edge":0,"points":[[0,[1,-91.17342089039929],[2,-338.36716169336114]],[3,[4,255.946878629896],[5,-145.76094357167156]]]},{"id":13,"_class":"TCAD.TWO.Segment","aux":true,"edge":2,"points":[[7,[8,252.10163324769275],[9,71.15131239804411]],[10,[11,-80.41502600578134],[12,240.48794311524324]]]},{"id":20,"_class":"TCAD.TWO.Segment","aux":true,"edge":4,"points":[[14,[15,-182.7982464866314],[16,86.82364838151852]],[17,[18,-102.18982576106004],[19,18.31440664196805]]]},{"id":27,"_class":"TCAD.TWO.Segment","aux":true,"edge":6,"points":[[21,[22,-88.51020843368133],[23,-140.46311545122035]],[24,[25,-172.00749593627577],[26,-240.71428346724593]]]},{"id":37,"_class":"TCAD.TWO.Arc","aux":true,"edge":8,"points":[[28,[29,255.946878629896],[30,-145.76094357167156]],[31,[32,252.10163324769275],[33,71.15131239804411]],[34,[35,196.33682672526209],[36,-38.32745176660376]]]},{"id":48,"_class":"TCAD.TWO.Arc","aux":true,"edge":10,"points":[[39,[40,-80.41502600578134],[41,240.48794311524324]],[42,[43,-182.7982464866314],[44,86.82364838151852]],[45,[46,-122.59914075460384],[47,157.65429488902345]]]},{"id":59,"_class":"TCAD.TWO.Arc","aux":true,"edge":12,"points":[[50,[51,-88.51020843368133],[52,-140.46311545122035]],[53,[54,-102.18982576106004],[55,18.31440664196805]],[56,[57,-175.5339801724776],[58,-67.98267439979745]]]},{"id":70,"_class":"TCAD.TWO.Arc","aux":true,"edge":14,"points":[[61,[62,-172.00749593627577],[63,-240.71428346724593]],[64,[65,-91.17342089039929],[66,-338.36716169336114]],[67,[68,-122.45917974196257],[69,-281.982128519434]]]}]},{"name":"sketch","style":{"lineWidth":2,"strokeStyle":"#ffffff","fillStyle":"#000000"},"data":[{"id":75,"_class":"TCAD.TWO.Circle","c":[72,[73,-122.59914075460384],[74,157.65429488902345]],"r":92.9565103454672},{"id":77,"_class":"TCAD.TWO.EndPoint","location":[77,[78,30.202166630027865],[79,-54.24889318543422]]},{"id":83,"_class":"TCAD.TWO.Circle","c":[80,[81,-122.45917974196257],[82,-281.982128519434]],"r":64.48310377876552}]},{"name":"_construction_","style":{"lineWidth":1,"strokeStyle":"#aaaaaa","fillStyle":"#000000"},"data":[]}],"constraints":[["RR",[48,75]],["coi",[72,45]],["coi",[80,67]],["RR",[83,70]]]}');

View file

@ -6,9 +6,8 @@ import ToolBar from './toolbar'
import * as MenuConfig from '../menu/menu-config'
import * as Operations from '../craft/operations'
import Menu from '../menu/menu'
import {CutWizard} from '../craft/brep/wizards/cut-extrude'
import {ExtrudeWizard, 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/brep/wizards/box'
@ -45,7 +44,7 @@ function UI(app) {
var ui = this;
this.app.bus.subscribe("showSketches", (enabled) => {
var solids = app.findAllSolids();
var solids = app.findAllSolidsOnScene();
for (var i = 0; i < solids.length; i++) {
for (var j = 0; j < solids[i].sceneFaces.length; j++) {
var face = solids[i].sceneFaces[j];
@ -73,12 +72,11 @@ UI.prototype.createCraftToolBar = function (vertPos) {
var toolBar = new ToolBar(this.app);
toolBar.add(this.app.actionManager.actions['PLANE']);
toolBar.add(this.app.actionManager.actions['EditFace']);
toolBar.add(this.app.actionManager.actions['PAD']);
toolBar.add(this.app.actionManager.actions['EXTRUDE']);
toolBar.add(this.app.actionManager.actions['CUT']);
toolBar.add(this.app.actionManager.actions['REVOLVE']);
$('#viewer-container').append(toolBar.node);
toolBar.node.css({left: '10px',top : vertPos + 'px'});
return toolBar;
@ -172,8 +170,8 @@ UI.prototype.createWizard = function(type, overridingHistory, initParams, face)
let wizard = null;
if ('CUT' === type) {
wizard = new CutWizard(this.app, initParams);
} else if ('PAD' === type) {
wizard = new MeshExtrudeWizard(this.app, face, false, 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) {

View file

@ -1,7 +1,7 @@
export const keymap = {
'CUT': 'C',
'PAD': 'E',
'EXTRUDE': 'E',
'ZoomIn': '+',
'ZoomOut': '-',
'menu.craft': 'shift+C',

View file

@ -41,7 +41,10 @@ ModificationsPanel.prototype.updateList = function() {
id : i,
info: this.app.ui.getInfoForOp(op),
OnBind : (dom, data) => {
dom.css('background-image', 'url('+ getIconForOp(op)+')');
const icon = getIconForOp(op);
if (icon) {
dom.css('background-image', 'url('+ icon+')');
}
if (!op.face) {
dom.find('.require-face').addClass('action-disabled');
}

View file

@ -6,8 +6,23 @@ import {HalfEdge, Edge} from './topo/edge'
import {Line} from './geom/impl/line'
import {Plane} from './geom/impl/plane'
import {Point} from './geom/point'
import {BasisForPlane, Matrix3} from '../math/l3space'
import * as cad_utils from '../3d/cad-utils'
import * as math from '../math/math'
function isCCW(points, normal) {
const tr2d = new Matrix3().setBasis(BasisForPlane(normal)).invert();
const points2d = points.map(p => tr2d.apply(p));
return math.isCCW(points2d);
}
function checkCCW(points, normal) {
if (!isCCW(points, normal)) {
points = points.slice();
points.reverse();
}
return points;
}
export function createPrism(basePoints, height) {
return new SimpleExtruder(height).extrude(basePoints, cad_utils.normalOfCCWSeq(basePoints));
@ -21,8 +36,9 @@ export class Extruder {
calculateLid(basePoints) {
throw 'not implemented';
}
extrude(basePoints, normal) {
basePoints = checkCCW(basePoints, normal);
const baseLoop = createPlaneLoop(basePoints.map(p => new Vertex(p)));
const baseFace = createPlaneFace(normal, baseLoop);
const lidNormal = normal.multiply(-1);
@ -30,7 +46,7 @@ export class Extruder {
this.prepareLidCalculation(normal, lidNormal);
//iterateSegments(basePoints.map(p => new Vertex(p.plus(offVector))), (a, b) => lidSegments.push({a, b}));
const lidPoints = this.calculateLid(basePoints).reverse();
const lidPoints = this.calculateLid(basePoints, normal, lidNormal).reverse();
const lidLoop = createPlaneLoop(lidPoints.map(p => new Vertex(p)));
const shell = new Shell();

View file

@ -299,7 +299,11 @@ Bus.prototype.notify = function(event, data, sender) {
const callback = listenerList[i][0];
const listenerId = listenerList[i][1];
if (sender == undefined || listenerId == null || listenerId != sender) {
callback(data);
try {
callback(data);
} catch(e) {
console.error(e);
}
}
}
}