extrude operation

This commit is contained in:
Val Erastov 2018-01-22 23:32:25 -08:00
parent a8d94398d1
commit 9b49e0735e
15 changed files with 152 additions and 55 deletions

View file

@ -9,12 +9,6 @@ const OPERATION_ACTIONS = [
},
...requiresFaceSelection(1)
},
{
id: 'EXTRUDE',
appearance: {
info: 'extrudes 2D sketch',
},
},
{
id: 'REVOLVE',
appearance: {

View file

@ -1,8 +1,8 @@
import {subtract, union, intersect} from '../../../brep/operations/boolean'
import {BREPSceneSolid} from '../../scene/wrappers/brepSceneObject'
import {update as updateStitching} from '../../../brep/stitching'
import {BREPValidator} from '../../../brep/brep-validator'
import {Shell} from '../../../brep/topo/shell'
import {subtract, union, intersect} from '../../brep/operations/boolean'
import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject'
import {update as updateStitching} from '../../brep/stitching'
import {BREPValidator} from '../../brep/brep-validator'
import {Shell} from '../../brep/topo/shell'
const BoolOpMap = {
'subtract': subtract,

View file

@ -43,7 +43,7 @@ export function activate({bus, services}) {
let result;
try {
result = op.run(services.cadRegistry.registry, request.params);
result = op.run(services.cadRegistry, request.params);
} catch (err) {
return err;
}

View file

@ -1,33 +1,22 @@
import {Matrix3, BasisForPlane, ORIGIN} from '../../../math/l3space'
import {Matrix3, ORIGIN} from '../../../math/l3space'
import * as math from '../../../math/math'
import Vector from 'math/vector';
import {enclose, iterateSegments} from '../../../brep/brep-enclose'
import * as stitching from '../../../brep/stitching'
import {Loop} from '../../../brep/topo/loop'
import {incRefCounter} from '../../../brep/topo/topo-object'
import {Line} from '../../../brep/geom/impl/line'
import {ReadSketchFromFace} from '../../sketch/sketchReader'
import {Segment} from '../../sketch/sketchModel'
import {isCurveClass} from '../../cad-utils'
import {BooleanOperation, combineShells} from './boolean-operation'
import {enclose} from '../../../brep/brep-enclose'
import {BooleanOperation, combineShells} from '../booleanOperation'
export function Extrude(app, params) {
return doOperation(app, params, false);
export function Extrude(cadRegistry, params) {
return doOperation(cadRegistry, params, false);
}
export function Cut(app, params) {
return doOperation(app, params, true);
export function Cut(cadRegistry, params) {
return doOperation(cadRegistry, params, true);
}
export function doOperation(app, params, cut) {
const face = app.findFace(params.face);
export function doOperation(cadRegistry, params, cut) {
const face = cadRegistry.findFace(params.face);
const solid = face.solid;
const savedFace = localStorage.getItem(app.faceStorageKey(face.id));
if (savedFace == null) return null;
const sketch = ReadSketchFromFace(app, face);
const sketch = face.sketch;
let plane = face.surface().tangentPlane(0, 0);
const details = getEncloseDetails(params, sketch.fetchContours(), plane, !cut, false);
const operand = combineShells(details.map(d => enclose(d.basePath, d.lidPath, d.baseSurface, d.lidSurface)));
@ -49,10 +38,10 @@ export function getEncloseDetails(params, contours, sketchSurface, invert) {
const targetDir = baseSurfaceNormal.negate();
if (params.rotation != 0) {
if (params.rotation !== 0) {
const basis = sketchSurface.basis();
target = Matrix3.rotateMatrix(params.rotation * Math.PI / 180, basis[0], ORIGIN).apply(targetDir);
if (params.angle != 0) {
if (params.angle !== 0) {
target = Matrix3.rotateMatrix(params.angle * Math.PI / 180, basis[2], ORIGIN)._apply(target);
}
target._multiply(value);
@ -67,7 +56,7 @@ export function getEncloseDetails(params, contours, sketchSurface, invert) {
if (invert) contour.reverse();
const lidPath = [];
var applyPrism = !math.equal(params.prism, 1);
let applyPrism = !math.equal(params.prism, 1);
for (let i = 0; i < basePath.length; ++i) {
const curve = basePath[i];
let lidCurve = curve.translate(target);

View file

@ -0,0 +1,16 @@
import {roundValueForPresentation as r} from "../operationHelper";
import {createWizardMetadata} from "./wizardMetadata";
import {createPreviewGeomProvider} from "./previewer";
import {Extrude} from "./cutExtrude";
export default {
id: 'EXTRUDE',
metadata: createWizardMetadata('height'),
label: 'Extrude',
icon: 'img/cad/extrude',
info: 'extrudes 2D sketch',
paramsInfo: value => `(${r(value)})`,
previewGeomProvider: createPreviewGeomProvider(false),
run: Extrude
};

View file

@ -0,0 +1,53 @@
import {getEncloseDetails} from "./cutExtrude";
import {curveTessParams} from "../../../brep/geom/impl/curve/curve-tess";
import Vector from "../../../../../modules/math/vector";
import {TriangulatePolygons} from "../../tess/triangulation";
import {createMeshGeometry} from "../../../../../modules/scene/geoms";
export function createPreviewGeomProvider(inversed) {
return function previewGeomProvider(params, services) {
const face = services.cadRegistry.findFace(params.face);
if (!face) return null;
let sketch = face.sketch.fetchContours();
const encloseDetails = getEncloseDetails(params, sketch, face.surface().tangentPlane(0, 0), !inversed);
const triangles = [];
for (let {basePath, lidPath, baseSurface, lidSurface} of encloseDetails) {
const basePoints = [];
const lidPoints = [];
for (let i = 0; i < basePath.length; ++i) {
let baseNurbs = basePath[i];
let lidNurbs = lidPath[i];
let tessCurve = params.prism > 1 ? lidNurbs : baseNurbs;
const us = curveTessParams(tessCurve.impl, tessCurve.uMin, tessCurve.uMax);
const base = us.map(u => baseNurbs.point(u));
const lid = us.map(u => lidNurbs.point(u));
const n = base.length;
for (let p = n - 1, q = 0; q < n; p = q++) {
triangles.push([base[p], base[q], lid[q]]);
triangles.push([lid[q], lid[p], base[p]]);
}
base.forEach(p => basePoints.push(p));
lid.forEach(p => lidPoints.push(p));
}
function collectOnSurface(points, normal) {
TriangulatePolygons([points], normal, (v) => v.toArray(), (arr) => new Vector().set3(arr))
.forEach(tr => triangles.push(tr));
}
collectOnSurface(basePoints, baseSurface.normal);
collectOnSurface(lidPoints, lidSurface.normal);
}
return createMeshGeometry(triangles);
}
}

View file

@ -0,0 +1,11 @@
import {CURRENT_SELECTION as S} from "../wizard/wizardPlugin";
export function createWizardMetadata(valueLabel) {
return [
['value' , 'number', 50, {label: valueLabel}],
['prism' , 'number', 1 , {min: 0, step: 0.1, round: 1}],
['angle' , 'number', 0 , {}],
['rotation', 'number', 0 , {step: 5}],
['face' , 'face' , S ]
];
}

View file

@ -0,0 +1,4 @@
export function roundValueForPresentation(value) {
return value.toPrecision(4).replace(/\.0$/, '');
}

View file

@ -9,7 +9,7 @@ const METADATA = [
['depth' , 'number', 500, {min: 0}]
];
function createBox(solids, {width, height, depth}) {
function createBox(cadRegistry, {width, height, depth}) {
return {
outdated: [],
created: [new BREPSceneSolid(box(width, height, depth))]
@ -20,9 +20,10 @@ export default {
id: 'BOX',
metadata: METADATA,
label: 'Box',
icon: 'img/cad/box',
info: 'creates new object box',
paramsInfo: ({width, height, depth}) => `(${width}, ${height}, ${depth})`,
previewer: createPreviewer(({width, height, depth}) => createBoxGeometry(width, height, depth)),
previewGeomProvider: ({width, height, depth}) => createBoxGeometry(width, height, depth),
run: createBox
};

View file

@ -23,4 +23,6 @@ export const TOKENS = {
WIZARDS: createToken('wizards'),
OPEN: createToken('wizards', 'open'),
CLOSE: createToken('wizards', 'close'),
};
};
export const CURRENT_SELECTION = {};

View file

@ -0,0 +1,8 @@
import React from 'react';
import TextControl from "../../../../../../modules/ui/components/controls/TextControl";
export default function FaceSelectionControl(props) {
return <TextControl {...props} />
}

View file

@ -1,4 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import Window from 'ui/components/Window';
import Stack from 'ui/components/Stack';
import Field from 'ui/components/controls/Field';
@ -8,14 +9,22 @@ 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';
import FaceSelectionControl from './FaceSelectionControl';
import {CURRENT_SELECTION} from "../../../craft/wizard/wizardPlugin";
export default class Wizard extends React.Component {
constructor({initialState, metadata, previewer}) {
constructor({initialState, metadata, previewer}, {services: {selection}}) {
super();
this.params = {};
metadata.forEach(([name,, v]) => this.params[name] = v);
metadata.forEach(([name, type, v]) => {
if (type === 'face' && v === CURRENT_SELECTION) {
let selectedFace = selection.face()[0];
v = selectedFace ? selectedFace.id : '';
}
this.params[name] = v
});
Object.assign(this.params, initialState);
@ -68,10 +77,17 @@ export default class Wizard extends React.Component {
};
if (type === 'number') {
return <NumberControl {...commonProps} />
} else if (type === 'face') {
return <FaceSelectionControl {...commonProps} />
} else {
return <TextControl {...commonProps} />
}
}
static contextTypes = {
services: PropTypes.object
};
}

View file

@ -3,17 +3,18 @@ import PropTypes from 'prop-types';
import {TOKENS as WIZARD_TOKENS} from '../../../craft/wizard/wizardPlugin';
import connect from 'ui/connect';
import Wizard from "./Wizard";
import {createPreviewer} from "../../../preview/scenePreviewer";
function WizardManager({wizards, close}, {services}) {
return wizards.map( ({type, initialState}, wizardIndex) => {
let {metadata, previewer, run} = services.operation.get(type);
let {metadata, previewGeomProvider, run} = services.operation.get(type);
function onOK(params) {
close();
services.craft.modify({type, params});
}
previewer = previewer.bind(null, {services});
let previewer = createPreviewer(previewGeomProvider, {services});
return <Wizard key={wizardIndex} previewer={previewer} metadata={metadata}
onOK={onOK}
onCancel={close}

View file

@ -1,7 +1,9 @@
import boxOperation from '../craft/primitives/boxOperation';
import extrudeOperation from '../craft/cutExtrude/extrudeOperation';
export function activate({bus, services}) {
export function activate({services}) {
services.operation.registerOperations([
boxOperation
boxOperation,
extrudeOperation
])
}

View file

@ -3,9 +3,9 @@ import {createTransparentPhongMaterial} from 'scene/materials';
import {createMesh} from 'scene/objects/mesh';
export function createPreviewer(sceneGeometryCreator) {
export function createPreviewer(sceneGeometryCreator, {services}) {
return function({services}, initParams) {
return function(params) {
const previewGroup = SceneGraph.createGroup();
SceneGraph.addToGroup(services.cadScene.workGroup, previewGroup);
@ -21,7 +21,7 @@ export function createPreviewer(sceneGeometryCreator) {
function update(params) {
destroyPreviewObject();
previewObject = createMesh(sceneGeometryCreator(params), IMAGINARY_SURFACE_MATERIAL);
previewObject = createMesh(sceneGeometryCreator(params, services), IMAGINARY_SURFACE_MATERIAL);
previewGroup.add(previewObject);
services.viewer.render();
}
@ -31,19 +31,19 @@ export function createPreviewer(sceneGeometryCreator) {
SceneGraph.removeFromGroup(services.cadScene.workGroup, previewGroup);
services.viewer.render();
}
update(initParams);
update(params);
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 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);