diff --git a/web/app/cad/actions/operationActions.js b/web/app/cad/actions/operationActions.js
index 392e8de3..7270c7bb 100644
--- a/web/app/cad/actions/operationActions.js
+++ b/web/app/cad/actions/operationActions.js
@@ -9,12 +9,6 @@ const OPERATION_ACTIONS = [
},
...requiresFaceSelection(1)
},
- {
- id: 'EXTRUDE',
- appearance: {
- info: 'extrudes 2D sketch',
- },
- },
{
id: 'REVOLVE',
appearance: {
diff --git a/web/app/cad/craft/brep/boolean-operation.js b/web/app/cad/craft/booleanOperation.js
similarity index 75%
rename from web/app/cad/craft/brep/boolean-operation.js
rename to web/app/cad/craft/booleanOperation.js
index 8dd015ae..53a8b617 100644
--- a/web/app/cad/craft/brep/boolean-operation.js
+++ b/web/app/cad/craft/booleanOperation.js
@@ -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,
diff --git a/web/app/cad/craft/craftPlugin.js b/web/app/cad/craft/craftPlugin.js
index bba73268..d4122e24 100644
--- a/web/app/cad/craft/craftPlugin.js
+++ b/web/app/cad/craft/craftPlugin.js
@@ -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;
}
diff --git a/web/app/cad/craft/brep/cut-extrude.js b/web/app/cad/craft/cutExtrude/cutExtrude.js
similarity index 60%
rename from web/app/cad/craft/brep/cut-extrude.js
rename to web/app/cad/craft/cutExtrude/cutExtrude.js
index e371eafa..b50d91a0 100644
--- a/web/app/cad/craft/brep/cut-extrude.js
+++ b/web/app/cad/craft/cutExtrude/cutExtrude.js
@@ -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);
diff --git a/web/app/cad/craft/cutExtrude/extrudeOperation.js b/web/app/cad/craft/cutExtrude/extrudeOperation.js
new file mode 100644
index 00000000..b204d648
--- /dev/null
+++ b/web/app/cad/craft/cutExtrude/extrudeOperation.js
@@ -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
+};
+
diff --git a/web/app/cad/craft/cutExtrude/previewer.js b/web/app/cad/craft/cutExtrude/previewer.js
new file mode 100644
index 00000000..7150f02e
--- /dev/null
+++ b/web/app/cad/craft/cutExtrude/previewer.js
@@ -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);
+ }
+}
+
diff --git a/web/app/cad/craft/cutExtrude/wizardMetadata.js b/web/app/cad/craft/cutExtrude/wizardMetadata.js
new file mode 100644
index 00000000..d9d8276e
--- /dev/null
+++ b/web/app/cad/craft/cutExtrude/wizardMetadata.js
@@ -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 ]
+ ];
+}
\ No newline at end of file
diff --git a/web/app/cad/craft/operationHelper.js b/web/app/cad/craft/operationHelper.js
new file mode 100644
index 00000000..c547e9b6
--- /dev/null
+++ b/web/app/cad/craft/operationHelper.js
@@ -0,0 +1,4 @@
+
+export function roundValueForPresentation(value) {
+ return value.toPrecision(4).replace(/\.0$/, '');
+}
diff --git a/web/app/cad/craft/primitives/boxOperation.js b/web/app/cad/craft/primitives/boxOperation.js
index 9a3539c4..e7fa32e7 100644
--- a/web/app/cad/craft/primitives/boxOperation.js
+++ b/web/app/cad/craft/primitives/boxOperation.js
@@ -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
};
diff --git a/web/app/cad/craft/wizard/wizardPlugin.js b/web/app/cad/craft/wizard/wizardPlugin.js
index 51853294..ebd5633f 100644
--- a/web/app/cad/craft/wizard/wizardPlugin.js
+++ b/web/app/cad/craft/wizard/wizardPlugin.js
@@ -23,4 +23,6 @@ export const TOKENS = {
WIZARDS: createToken('wizards'),
OPEN: createToken('wizards', 'open'),
CLOSE: createToken('wizards', 'close'),
-};
\ No newline at end of file
+};
+
+export const CURRENT_SELECTION = {};
diff --git a/web/app/cad/dom/components/wizard/FaceSelectionControl.jsx b/web/app/cad/dom/components/wizard/FaceSelectionControl.jsx
new file mode 100644
index 00000000..669c30ae
--- /dev/null
+++ b/web/app/cad/dom/components/wizard/FaceSelectionControl.jsx
@@ -0,0 +1,8 @@
+import React from 'react';
+
+import TextControl from "../../../../../../modules/ui/components/controls/TextControl";
+
+export default function FaceSelectionControl(props) {
+ return
+}
+
diff --git a/web/app/cad/dom/components/wizard/Wizard.jsx b/web/app/cad/dom/components/wizard/Wizard.jsx
index 44a99d01..ce6dd2b6 100644
--- a/web/app/cad/dom/components/wizard/Wizard.jsx
+++ b/web/app/cad/dom/components/wizard/Wizard.jsx
@@ -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
+ } else if (type === 'face') {
+ return
} else {
return
}
}
+
+ static contextTypes = {
+ services: PropTypes.object
+ };
+
}
diff --git a/web/app/cad/dom/components/wizard/WizardManager.jsx b/web/app/cad/dom/components/wizard/WizardManager.jsx
index a78c534e..413b75ad 100644
--- a/web/app/cad/dom/components/wizard/WizardManager.jsx
+++ b/web/app/cad/dom/components/wizard/WizardManager.jsx
@@ -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