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