loft operation and loft previewer

This commit is contained in:
Val Erastov 2019-01-14 22:17:07 -08:00
parent 558ca766ae
commit 6e16e39383
15 changed files with 202 additions and 59 deletions

View file

@ -1,20 +1,41 @@
import {BoxGeometry, Face3, Geometry, Vector3} from 'three';
export function createBoxGeometry(width, height, depth) {
return new THREE.BoxGeometry(width, height, depth);
return new BoxGeometry(width, height, depth);
}
export function createMeshGeometry(triangles) {
const geometry = new THREE.Geometry();
const geometry = new Geometry();
for (let tr of triangles) {
const a = geometry.vertices.length;
const b = a + 1;
const c = a + 2;
const face = new THREE.Face3(a, b, c);
const face = new Face3(a, b, c);
tr.forEach(v => geometry.vertices.push(v.three()));
geometry.faces.push(face);
}
geometry.mergeVertices();
geometry.computeFaceNormals();
return geometry;
}
export function createSmoothMeshGeometryFromData(tessInfo) {
const geometry = new Geometry();
const vec = arr => new Vector3().fromArray(arr);
for (let [tr, normals] of tessInfo) {
if (!normals || normals.find(n => n[0] === null || n[1] === null || n[2] === null)) {
normals = undefined;
}
const a = geometry.vertices.length;
const b = a + 1;
const c = a + 2;
const face = new Face3(a, b, c, normals && normals.map(vec));
tr.forEach(v => geometry.vertices.push(vec(v)));
geometry.faces.push(face);
}
geometry.mergeVertices();
geometry.computeFaceNormals();
return geometry;
}

View file

@ -1,4 +1,4 @@
import {DATUM, DATUM_AXIS, EDGE, FACE, SHELL, SKETCH_OBJECT} from '../scene/entites';
import {DATUM, DATUM_AXIS, EDGE, FACE, LOOP, SHELL, SKETCH_OBJECT} from '../scene/entites';
import {MShell} from '../model/mshell';
@ -70,6 +70,19 @@ export function activate({streams, services}) {
}
return datum.getAxisByLiteral(axisLiteral);
}
function findLoop(loopId) {
let [shellId, faceId, loopLocalId] = loopId.split('/');
let face = findFace(shellId+'/'+faceId);
if (face) {
for (let loop of face.sketchLoops) {
if (loop.id === loopId) {
return loop;
}
}
}
return null;
}
function findEntity(entity, id) {
switch (entity) {
@ -79,12 +92,13 @@ export function activate({streams, services}) {
case SKETCH_OBJECT: return findSketchObject(id);
case DATUM: return findDatum(id);
case DATUM_AXIS: return findDatumAxis(id);
case LOOP: return findLoop(id);
default: throw 'unsupported';
}
}
services.cadRegistry = {
getAllShells, findShell, findFace, findEdge, findSketchObject, findEntity, findDatum, findDatumAxis,
getAllShells, findShell, findFace, findEdge, findSketchObject, findEntity, findDatum, findDatumAxis, findLoop,
get modelIndex() {
return streams.cadRegistry.modelIndex.value;
},

View file

@ -99,6 +99,24 @@ export function activate(ctx) {
consumed,
created: [readShellData(data.result, consumed, operandsA[0].csys)]
}
},
loft: function(params) {
let engineParams = {
sections: params.sections.map(sec => readSketchContour(sec.contour, sec.face)),
preview: params.preview,
tolerance: TOLERANCE,
deflection: DEFLECTION,
};
let data = callEngine(engineParams, Module._SPI_loft);
if (params.preview) {
return data;
}
throw 'unsupported';
// let consumed = [...operandsA, ...operandsB];
// return {
// consumed,
// created: [readShellData(data.result, consumed, operandsA[0].csys)]
// }
}
}
}
@ -180,47 +198,47 @@ function managedByE0(mShell) {
return externals && externals.engine === 'e0';
}
function readSketchContour(contour, face) {
let tr = face.csys.outTransformation;
let path = [];
contour.segments.forEach(s => {
if (s.isCurve) {
if (s.constructor.name === 'Circle') {
const dir = face.csys.z.data();
path.push({TYPE: CURVE_TYPES.CIRCLE, c: tr.apply(s.c).data(), dir, r: s.r});
} else if (s.constructor.name === 'Arc') {
let a = s.inverted ? s.b : s.a;
let b = s.inverted ? s.a : s.b;
let tangent = tr._apply(s.c.minus(a))._cross(face.csys.z)._normalize();
if (s.inverted) {
tangent._negate();
}
path.push({
TYPE: CURVE_TYPES.ARC,
a: tr.apply(a).data(),
b: tr.apply(b).data(),
tangent: tangent.data()
});
} else {
let nurbs = s.toNurbs(face.csys).impl;
path.push(Object.assign({TYPE: CURVE_TYPES.B_SPLINE}, nurbs.serialize()));
}
} else {
let ab = [s.a, s.b];
if (s.inverted) {
ab.reverse();
}
ab = ab.map(v => tr.apply(v).data());
path.push({TYPE: CURVE_TYPES.SEGMENT, a: ab[0], b: ab[1]});
}
});
return path;
}
function readSketch(face, request, sketcher) {
let sketch = sketcher.readSketch(face.id);
if (!sketch) throw 'illegal state';
let tr = face.csys.outTransformation;
let paths = sketch.fetchContours().map(c => {
let path = [];
c.segments.forEach(s => {
if (s.isCurve) {
if (s.constructor.name === 'Circle') {
const dir = face.csys.z.data();
path.push({TYPE: CURVE_TYPES.CIRCLE, c: tr.apply(s.c).data(), dir, r: s.r});
} else if (s.constructor.name === 'Arc') {
let a = s.inverted ? s.b : s.a;
let b = s.inverted ? s.a : s.b;
let tangent = tr._apply(s.c.minus(a))._cross(face.csys.z)._normalize();
if (s.inverted) {
tangent._negate();
}
path.push({
TYPE: CURVE_TYPES.ARC,
a: tr.apply(a).data(),
b: tr.apply(b).data(),
tangent: tangent.data()
});
} else {
let nurbs = s.toNurbs(face.csys).impl;
path.push(Object.assign({TYPE: CURVE_TYPES.B_SPLINE}, nurbs.serialize()));
}
} else {
let ab = [s.a, s.b];
if (s.inverted) {
ab.reverse();
}
ab = ab.map(v => tr.apply(v).data());
path.push({TYPE: CURVE_TYPES.SEGMENT, a: ab[0], b: ab[1]});
}
});
return path;
});
return paths;
return sketch.fetchContours().map(c => readSketchContour(c, face));
}
function createExtrudeCommand(request, {cadRegistry, sketcher}, invert) {

View file

@ -0,0 +1,12 @@
import React from 'react';
import {Group} from '../wizard/components/form/Form';
import Entity from '../wizard/components/form/Entity';
import BooleanChoice from '../wizard/components/form/BooleanChioce';
export default function LoftForm() {
return <Group>
<Entity name='sections' entity='loop' />
<BooleanChoice name='boolean' />
</Group>;
}

View file

@ -0,0 +1,25 @@
import schema from './schema';
import {loftPreviewGeomProvider} from './loftPreviewer';
import {assignBooleanParams} from '../primitives/booleanOptionHelper';
import LoftForm from './LoftForm';
export default {
id: 'LOFT',
label: 'Loft',
icon: 'img/cad/revolve',
info: 'creates a loft cross selected sections shape',
paramsInfo: () => '',
previewGeomProvider: loftPreviewGeomProvider,
run: runLoft,
form: LoftForm,
schema
};
function runLoft(params, services) {
services.craftEngine.loft(assignBooleanParams({
sections: params.sections.map(services.cadRegistry.findLoop),
}, params, services.cadRegistry.getAllShells));
}

View file

@ -0,0 +1,14 @@
import {createSmoothMeshGeometryFromData} from '../../../../../modules/scene/geoms';
export function loftPreviewGeomProvider(params, services) {
const tessInfo = services.craftEngine.loft({
sections: params.sections.map(services.cadRegistry.findLoop),
preview: true
});
console.dir(tessInfo);
return createSmoothMeshGeometryFromData(tessInfo);
}

View file

@ -0,0 +1,15 @@
export default {
sections: {
type: 'array',
itemType: 'loop',
defaultValue: {
type: 'selection',
}
},
boolean: {
type: 'enum',
values: ['INTERSECT', 'SUBTRACT', 'UNION'],
defaultValue: 'UNION',
optional: true
}
}

View file

@ -1,4 +1,4 @@
import {DATUM, DATUM_AXIS, EDGE, FACE, SHELL, SKETCH_OBJECT} from '../../scene/entites';
import {DATUM, DATUM_AXIS, EDGE, FACE, LOOP, SHELL, SKETCH_OBJECT} from '../../scene/entites';
export function activate(ctx) {
ctx.streams.wizard.wizardContext.attach(wizCtx => {
@ -150,6 +150,12 @@ function createPickHandlerFromSchema(wizCtx) {
} else {
selectToFirst(DATUM_AXIS, model.id);
}
} else if (modelType === LOOP) {
if (activeEntity === LOOP) {
selectActive(model.id);
} else {
selectToFirst(LOOP, model.id);
}
}
return false;
};

View file

@ -10,9 +10,7 @@ export class MLoop extends MObject {
}
export class MSketchLoop extends MObject {
static TYPE = 'loop';
export class MSketchLoop extends MLoop {
constructor(id, face, sketchObjects, contour) {
super(id);

View file

@ -12,7 +12,8 @@ import sphereOperation from '../craft/primitives/sphere/sphereOperation';
import cylinderOperation from '../craft/primitives/cylinder/cylinderOperation';
import torusOperation from '../craft/primitives/torus/torusOperation';
import coneOperation from '../craft/primitives/cone/coneOperation';
import spatialCurve from '../craft/spatialCurve/spatialCurveOperation';
import spatialCurveOperation from '../craft/spatialCurve/spatialCurveOperation';
import loftOperation from '../craft/loft/loftOperation';
import {intersectionOperation, subtractOperation, unionOperation} from '../craft/boolean/booleanOperation';
@ -32,7 +33,8 @@ export function activate({services}) {
cylinderOperation,
torusOperation,
coneOperation,
spatialCurve,
spatialCurveOperation,
loftOperation,
intersectionOperation,
subtractOperation,
unionOperation

View file

@ -250,7 +250,7 @@ export function runSandbox({bus, services, services: { viewer, cadScene, cadRegi
// o2.setMoveMode(DatumObject3D.AXIS.Z);
// cadScene.auxGroup.add(o2);
services.action.run('SPATIAL_CURVE');
services.action.run('LOFT');
}

View file

@ -1,19 +1,16 @@
import * as mask from 'gems/mask'
import {getAttribute, setAttribute} from 'scene/objectData';
import {FACE, EDGE, SKETCH_OBJECT, DATUM, SHELL, DATUM_AXIS} from '../entites';
import {state} from 'lstream';
import {distinctState} from '../../../../../modules/lstream';
import {FACE, EDGE, SKETCH_OBJECT, DATUM, SHELL, DATUM_AXIS, LOOP} from '../entites';
export const PICK_KIND = {
FACE: mask.type(1),
SKETCH: mask.type(2),
EDGE: mask.type(3),
DATUM: mask.type(4),
DATUM_AXIS: mask.type(5)
DATUM_AXIS: mask.type(5),
LOOP: mask.type(6)
};
export const SELECTABLE_ENTITIES = [FACE, EDGE, SKETCH_OBJECT, DATUM, SHELL];
const DEFAULT_SELECTION_MODE = Object.freeze({
shell: false,
vertex: false,
@ -95,7 +92,7 @@ export function activate(context) {
const deselectAll = () => services.marker.clear();
function handlePick(event) {
raycastObjects(event, PICK_KIND.FACE | PICK_KIND.SKETCH | PICK_KIND.EDGE | PICK_KIND.DATUM_AXIS, pickHandler);
raycastObjects(event, PICK_KIND.FACE | PICK_KIND.SKETCH | PICK_KIND.EDGE | PICK_KIND.DATUM_AXIS | PICK_KIND.LOOP, pickHandler);
}
function pick(obj) {
@ -146,6 +143,15 @@ export function activate(context) {
}
return false;
},
(pickResult) => {
if (mask.is(kind, PICK_KIND.LOOP) && !!pickResult.face) {
let faceV = getAttribute(pickResult.face, LOOP);
if (faceV) {
return !visitor(faceV.model, event);
}
}
return false;
},
(pickResult) => {
if (mask.is(kind, PICK_KIND.FACE) && !!pickResult.face) {
let faceV = getAttribute(pickResult.face, FACE);

View file

@ -4,6 +4,7 @@ import {MEdge} from '../model/medge';
import {MVertex} from '../model/mvertex';
import {MSketchObject} from '../model/msketchObject';
import {MDatum, MDatumAxis} from '../model/mdatum';
import {MLoop} from '../model/mloop';
export const SHELL = MShell.TYPE;
export const FACE = MFace.TYPE;
@ -12,8 +13,9 @@ export const VERTEX = MVertex.TYPE;
export const SKETCH_OBJECT = MSketchObject.TYPE;
export const DATUM = MDatum.TYPE;
export const DATUM_AXIS = MDatumAxis.TYPE;
export const LOOP = MLoop.TYPE;
export const ENTITIES = [SHELL, DATUM, FACE, EDGE, VERTEX, SKETCH_OBJECT, DATUM_AXIS];
export const ENTITIES = [SHELL, DATUM, FACE, EDGE, VERTEX, SKETCH_OBJECT, DATUM_AXIS, LOOP];
export const PART_MODELING_ENTITIES = [SHELL, FACE, EDGE, VERTEX, SKETCH_OBJECT];
export const ASSEMBLY_ENTITIES = [SHELL, FACE, EDGE, VERTEX];

View file

@ -1,7 +1,10 @@
import {state} from 'lstream';
import {SELECTABLE_ENTITIES} from './controls/pickControlPlugin';
import {addToListInMap} from 'gems/iterables';
import {EMPTY_ARRAY} from '../../../../modules/gems/iterables';
import {DATUM, FACE, SHELL, SKETCH_OBJECT, EDGE} from './entites';
export const SELECTABLE_ENTITIES = [FACE, EDGE, SKETCH_OBJECT, DATUM, SHELL];
export function defineStreams(ctx) {
ctx.streams.selection = {};

View file

@ -6,6 +6,8 @@ import {DoubleSide, Geometry, Mesh} from 'three';
import {surfaceAndPolygonsToGeom} from '../wrappers/brepSceneObject';
import {TriangulatePolygons} from '../../tess/triangulation';
import Vector from '../../../../../modules/math/vector';
import {LOOP} from '../entites';
import {setAttribute} from '../../../../../modules/scene/objectData';
export class SketchLoopView extends View {
constructor(mLoop) {
@ -35,6 +37,11 @@ export class SketchLoopView extends View {
surfaceAndPolygonsToGeom(surface, tess, this.mesh.geometry);
this.mesh.geometry.mergeVertices();
for (let i = 0; i < geometry.faces.length; i++) {
const meshFace = geometry.faces[i];
setAttribute(meshFace, LOOP, this);
}
this.rootGroup.add(this.mesh);
this.mesh.onMouseEnter = (e) => {
this.mesh.material.visible = true;