using unified face coordinate system for sketches and datums

This commit is contained in:
Val Erastov 2018-10-26 00:30:39 -07:00
parent 49dffd435b
commit e7cf64f54c
20 changed files with 231 additions and 97 deletions

View file

@ -102,7 +102,7 @@ export function createBoundingSurface(points, plane) {
return createBoundingSurfaceFrom2DPoints(points2d, plane);
}
export function createBoundingSurfaceFrom2DPoints(points2d, plane, minWidth, minHeight, offset = 0, ) {
export function createBoundingSurfaceFrom2DPoints(points2d, plane, minWidth, minHeight, offset = 0) {
let bBox = new BBox();
points2d.forEach(p => bBox.checkPoint(p));

View file

@ -3,6 +3,7 @@ import {Plane} from './geom/impl/plane'
import {createPrism, enclose} from './brep-enclose'
import {AXIS, Matrix3} from '../math/l3space'
import {Circle} from '../cad/sketch/sketchModel'
import CSys from '../math/csys';
export function box(w, h, d, tr) {
const wh = w * 0.5;
@ -22,7 +23,11 @@ export function box(w, h, d, tr) {
export function cylinder(r, h, tr) {
tr = tr || IDENTITY;
let circle1 = new Circle(-1, new Point(0,0,0), r).toNurbs( new Plane(tr.apply(AXIS.Z), h));
let normal = tr.apply(AXIS.Z);
let plane = new Plane(normal, h);
let csys = CSys.fromNormalAndDir(normal.multiply(h), normal, plane.basis()[0]);
let circle1 = new Circle(-1, new Point(0,0,0), r).toNurbs(csys);
let circle2 = circle1.translate(tr.apply(new Point(0,0,-h)));
return enclose([circle1], [circle2]);
}

View file

@ -40,7 +40,7 @@ export default class NurbsSurface {
}
eval(u, v, order) {
this.verb.derivatives(u, v, order);
return this.verb.derivatives(u, v, order);
}
normal(u, v) {

View file

@ -19,32 +19,26 @@ export function doOperation(params, {cadRegistry, sketcher}, cut) {
let sketch = sketcher.readSketch(face.id);
if (!sketch) throw 'illegal state';
let plane = face.surface.tangentPlane(0, 0);
const details = getEncloseDetails(params, sketch.fetchContours(), plane, !cut, false);
const details = getEncloseDetails(params, sketch.fetchContours(), face.csys, face.surface, !cut, false);
const operand = combineShells(details.map(d => enclose(d.basePath, d.lidPath, d.baseSurface, d.lidSurface)));
return BooleanOperation(face, solid, operand, cut ? 'subtract' : 'union');
}
export function getEncloseDetails(params, contours, sketchSurface, invert) {
export function getEncloseDetails(params, contours, csys, sketchSurface, invert) {
let value = params.value;
if (value < 0) {
value = Math.abs(value);
invert = !invert;
}
const baseSurface = invert ? sketchSurface.invert() : sketchSurface;
let target;
const targetDir = invert ? csys.z : csys.z.negate();
let baseSurfaceNormal = baseSurface.normal;
const targetDir = baseSurfaceNormal.negate();
let target;
if (params.rotation !== 0) {
const basis = sketchSurface.basis();
target = Matrix3.rotateMatrix(params.rotation * Math.PI / 180, basis[0], ORIGIN).apply(targetDir);
target = Matrix3.rotateMatrix(params.rotation * Math.PI / 180, csys.x, ORIGIN).apply(targetDir);
if (params.angle !== 0) {
target = Matrix3.rotateMatrix(params.angle * Math.PI / 180, basis[2], ORIGIN)._apply(target);
target = Matrix3.rotateMatrix(params.angle * Math.PI / 180, csys.z, ORIGIN)._apply(target);
}
target._multiply(value);
} else {
@ -54,7 +48,7 @@ export function getEncloseDetails(params, contours, sketchSurface, invert) {
let details = [];
for (let contour of contours) {
if (invert) contour.reverse();
const basePath = contour.transferOnSurface(sketchSurface);
const basePath = contour.transferInCoordinateSystem(csys);
if (invert) contour.reverse();
const lidPath = [];
@ -68,6 +62,7 @@ export function getEncloseDetails(params, contours, sketchSurface, invert) {
lidPath.push(lidCurve);
}
const baseSurface = sketchSurface.tangentPlane(0, 0);
const lidSurface = baseSurface.translate(target).invert();
details.push({basePath, lidPath, baseSurface, lidSurface});
}

View file

@ -14,7 +14,7 @@ export function createPreviewGeomProvider(inversed) {
if (!face) return null;
let sketch = face.sketch.fetchContours();
const encloseDetails = getEncloseDetails(params, sketch, face.surface.tangentPlane(0, 0), !inversed);
const encloseDetails = getEncloseDetails(params, sketch, face.csys, face.surface, !inversed);
const triangles = [];
for (let {basePath, lidPath, baseSurface, lidSurface} of encloseDetails) {

View file

@ -0,0 +1,12 @@
import React from 'react';
import {Group} from '../../wizard/components/form/Form';
import {NumberField, RadioButtonsField, ReadOnlyValueField} from '../../wizard/components/form/Fields';
import SingleEntity from '../../wizard/components/form/SingleEntity';
import {RadioButton} from 'ui/components/controls/RadioButtons';
export default function PlaneWizard() {
return <Group>
<ReadOnlyValueField name='datum'/>
</Group>;
}

View file

@ -0,0 +1,6 @@
export default {
datum: {
type: 'datum',
defaultValue: {type: 'selection'}
}
}

View file

@ -0,0 +1,52 @@
import {createMeshGeometry} from 'scene/geoms';
import {Plane} from '../../../../brep/geom/impl/plane';
import Vector from 'math/vector';
import PlaneWizard from './PlaneWizard';
import {MOpenFaceShell} from '../../../model/mopenFace';
import schema from './planeOpSchema';
import {CSysPlaneSurfacePrototype} from '../../../model/surfacePrototype';
const WIDTH = 750;
const HEIGHT = 750;
function createPlane(params, services) {
let mDatum = services.cadRegistry.findDatum(params.datum);
return {
outdated: [mDatum],
created: [new MOpenFaceShell(new CSysPlaneSurfacePrototype(mDatum.csys), mDatum.csys)]
}
}
function previewGeomProvider(params, services) {
let mDatum = services.cadRegistry.findDatum(params.datum);
if (!mDatum) {
return null;
}
let tr = mDatum.csys.outTransformation;
const a = tr._apply(new Vector(0, 0, 0));
const b = tr._apply(new Vector(WIDTH, 0, 0));
const c = tr._apply(new Vector(WIDTH, HEIGHT, 0));
const d = tr._apply(new Vector(0, HEIGHT, 0));
let trs = [[a, b, c], [a, c, d]];
return createMeshGeometry(trs);
}
export default {
id: 'PLANE_FROM_DATUM',
label: 'Plane',
icon: 'img/cad/plane',
info: 'creates new object plane off of datum',
paramsInfo: ({datum}) => `(${datum})`,
previewGeomProvider,
run: createPlane,
form: PlaneWizard,
schema
};

View file

@ -1,7 +1,7 @@
import React from 'react';
import {Group} from '../wizard/components/form/Form';
import {NumberField, RadioButtonsField} from '../wizard/components/form/Fields';
import SingleEntity from '../wizard/components/form/SingleEntity';
import {Group} from '../../wizard/components/form/Form';
import {NumberField, RadioButtonsField} from '../../wizard/components/form/Fields';
import SingleEntity from '../../wizard/components/form/SingleEntity';
import {RadioButton} from 'ui/components/controls/RadioButtons';

View file

@ -1,11 +1,11 @@
import {createMeshGeometry} from 'scene/geoms';
import {STANDARD_BASES} from '../../../math/l3space';
import {Plane} from '../../../brep/geom/impl/plane';
import {STANDARD_BASES} from '../../../../math/l3space';
import {Plane} from '../../../../brep/geom/impl/plane';
import Vector from 'math/vector';
import PlaneWizard from './PlaneWizard';
import {MOpenFaceShell} from '../../model/mopenFace';
import schema from './planeOpSchema';
import {PlaneSurfacePrototype} from '../../model/surfacePrototype';
import PlaneWizard from './SimplePlaneWizard';
import {MOpenFaceShell} from '../../../model/mopenFace';
import schema from './simplePlaneOpSchema';
import {PlaneSurfacePrototype} from '../../../model/surfacePrototype';
function paramsToPlane({orientation, parallelTo, depth}, cadRegistry) {
let face = null;

View file

@ -9,27 +9,27 @@ export function Revolve(app, params) {
const surface = face.surface;
const sketch = ReadSketchFromFace(app, face);
const pivot = evalPivot(params.pivot, sketch, surface);
const pivot = evalPivot(params.pivot, sketch, face.csys);
const shells = [];
const contours = sketch.fetchContours();
for (let contour of contours) {
const basePath = contour.transferOnSurface(surface);
const basePath = contour.transferInCoordinateSystem(face.csys);
const shell = revolve(basePath, surface, pivot.p0, pivot.v, params.angle);
shells.push(shell);
}
const operand = combineShells(shells)
const operand = combineShells(shells);
return BooleanOperation(face, solid, operand, 'union');
}
export function evalPivot(pivot, sketch, surface) {
export function evalPivot(pivot, sketch, csys) {
const segment = sketch.findById(pivot);
if (segment == null) {
return null;
}
const tr = surface.get3DTransformation();
const tr = csys.outTransformation;
const p0 = tr.apply(segment.a);
const v = tr.apply(segment.b).minus(p0)._normalize()
const v = tr.apply(segment.b).minus(p0)._normalize();
return {p0, v}
}

View file

@ -10,40 +10,63 @@ export class MFace extends MObject {
static TYPE = 'face';
constructor(id, shell, surface) {
constructor(id, shell, surface, csys) {
super(id);
this.id = id;
this.shell = shell;
this.surface = surface;
this.sketchObjects = [];
this._csys = csys
}
normal() {
return this.surface.normalInMiddle();
return this.csys.z;
}
depth() {
return this.surface.tangentPlaneInMiddle().w;
this.evalCSys();
return this.w;
}
calcBasis() {
return BasisForPlane(this.normal());
};
basis() {
if (!this._basis) {
this._basis = this.calcBasis();
this._basis = [this.csys.x, this.csys.y, this.csys.z];
}
return this._basis;
}
get csys() {
if (!this._csys) {
let [x,y,z] = this.basis();
this._csys = new CSys(this.normal().multiply(this.depth()), x, y, z);
}
this.evalCSys();
return this._csys;
}
get isPlaneBased() {
return this.surface.simpleSurface && this.surface.simpleSurface.isPlane;
}
evalCSys() {
if (!this._csys) {
if (this.isPlaneBased) {
let [x, y, z] = this.surface.simpleSurface.basis();
let origin = z.multiply(this.surface.simpleSurface.w);
this._csys = new CSys(origin, x, y, z);
} else {
let origin = this.surface.southWestPoint();
let z = this.surface.normalUV(0, 0);
let derivatives = this.surface.impl.eval(0, 0, 1);
let x = new Vector().set3(derivatives[1][0])._normalize();
let y = new Vector().set3(derivatives[0][1])._normalize();
if (this.surface.inverted) {
const t = x;
x = y;
y = t;
}
this._csys = new CSys(origin, x, y, z);
}
this.w = this.csys.w();
}
}
setSketch(sketch) {
this.sketch = sketch;
@ -76,14 +99,22 @@ export class MFace extends MObject {
get sketchToWorldTransformation() {
if (!this._sketchToWorldTransformation) {
this._sketchToWorldTransformation = this.surface.tangentPlaneInMiddle().get3DTransformation();
if (this.isPlaneBased) {
this._sketchToWorldTransformation = this.csys.outTransformation;
} else {
throw 'sketches are supported only for planes yet';
}
}
return this._sketchToWorldTransformation;
}
get worldToSketchTransformation() {
if (!this._worldToSketchTransformation) {
this._worldToSketchTransformation = this.sketchToWorldTransformation.invert();
if (this.isPlaneBased) {
this._worldToSketchTransformation = this.csys.inTransformation;
} else {
throw 'sketches are supported only for planes yet';
}
}
return this._worldToSketchTransformation;
}

View file

@ -3,11 +3,11 @@ import {MFace} from './mface';
export class MOpenFaceShell extends MShell {
constructor(surfacePrototype) {
constructor(surfacePrototype, csys) {
super();
this.surfacePrototype = surfacePrototype;
this.faces.push(new MFace(this.id + '/SURFACE', this,
surfacePrototype.boundTo([], 100, 100)));
surfacePrototype.boundTo([], 100, 100), csys));
}
get face() {

View file

@ -1,4 +1,6 @@
import {createBoundingSurfaceFrom2DPoints} from '../../brep/brep-builder';
import NurbsSurface from '../../brep/geom/surfaces/nurbsSurface';
import {BrepSurface} from '../../brep/geom/surfaces/brepSurface';
export class SurfacePrototype {
@ -18,4 +20,35 @@ export class PlaneSurfacePrototype extends SurfacePrototype {
boundTo(points2dOnSurface, minWidth, minHeight, offset) {
return createBoundingSurfaceFrom2DPoints(points2dOnSurface, this.plane, minWidth, minHeight, offset);
}
}
/*
* The only difference PlaneSurfacePrototype is show phony surface
* when no sketching from csys origin for estetic purposes.
* When sketch exists behaves like PlaneSurfacePrototype using csys transformation though
*/
export class CSysPlaneSurfacePrototype extends SurfacePrototype {
constructor(csys) {
super();
this.csys = csys;
this.plane = {
get3DTransformation: () => this.csys.outTransformation
}
}
boundTo(points2dOnSurface, minWidth, minHeight, offset) {
if (points2dOnSurface.length === 0) {
let dx = this.csys.x.multiply(minWidth);
let dy = this.csys.y.multiply(minHeight);
let origin = this.csys.origin;
return new BrepSurface(new NurbsSurface(verb.geom.NurbsSurface.byKnotsControlPointsWeights( 1, 1, [0,0,1,1], [0,0,1,1],
[ [ origin.plus(dy).data(), origin.plus(dx)._plus(dy).data()] ,
[ origin.data(), origin.plus(dx ).data() ] ] )));
}
return createBoundingSurfaceFrom2DPoints(points2dOnSurface, this.plane, minWidth, minHeight, offset);
}
}

View file

@ -43,7 +43,7 @@ export default [
label: 'datum',
cssIcons: ['magic'],
info: 'operations on datum',
actions: ['DATUM_ROTATE', 'DATUM_MOVE']
actions: ['DATUM_ROTATE', 'DATUM_MOVE', '-', 'PLANE_FROM_DATUM']
// actions: ['DATUM_MOVE', 'DATUM_ROTATE', 'DATUM_REBASE', '-', 'PLANE_FROM_DATUM', 'BOX', 'SPHERE', 'TORUS',
// 'CONE', 'CYLINDER']
},

View file

@ -1,12 +1,13 @@
import boxOperation from '../craft/primitives/boxOperation';
import extrudeOperation from '../craft/cutExtrude/extrudeOperation';
import cutOperation from '../craft/cutExtrude/cutOperation';
import planeOperation from '../craft/primitives/planeOperation';
import planeOperation from '../craft/primitives/simplePlane/simplePlaneOperation';
import filletOperation from '../craft/fillet/filletOperation';
import revolveOperation from '../craft/revolve/revolveOperation';
import createDatumOperation from '../craft/datum/create/createDatumOperation';
import moveDatumOperation from '../craft/datum/move/moveDatumOperation';
import rotateDatumOperation from '../craft/datum/rotate/rotateDatumOperation';
import datumOperation from '../craft/primitives/plane/planeOperation';
export function activate({services}) {
services.operation.registerOperations([
@ -18,6 +19,7 @@ export function activate({services}) {
filletOperation,
createDatumOperation,
moveDatumOperation,
rotateDatumOperation
rotateDatumOperation,
datumOperation
])
}

View file

@ -6,6 +6,7 @@ import {LUT} from '../../math/bezier-cubic'
import {distanceAB, isCCW, makeAngle0_360} from '../../math/math'
import {normalizeCurveEnds} from '../../brep/geom/impl/nurbs-ext';
import Vector from '../../../../modules/math/vector';
import {AXIS, ORIGIN} from '../../math/l3space';
const RESOLUTION = 20;
@ -31,8 +32,8 @@ class SketchPrimitive {
return this.constructor.name !== 'Segment';
}
toNurbs(plane) {
let verbNurbs = this.toVerbNurbs(plane, to3DTrFunc(plane));
toNurbs(csys) {
let verbNurbs = this.toVerbNurbs(csys.outTransformation.apply, csys);
if (this.inverted) {
verbNurbs = verbNurbs.reverse();
}
@ -43,7 +44,7 @@ class SketchPrimitive {
return new BrepCurve(new NurbsCurve(verbNurbs));
}
toVerbNurbs(plane, _3dtr) {
toVerbNurbs(tr) {
throw 'not implemented'
}
@ -63,8 +64,8 @@ export class Segment extends SketchPrimitive {
return [this.a, this.b];
}
toVerbNurbs(plane, _3dtr) {
return new verb.geom.Line(_3dtr(this.a).data(), _3dtr(this.b).data());
toVerbNurbs(tr) {
return new verb.geom.Line(tr(this.a).data(), tr(this.b).data());
}
}
@ -103,8 +104,11 @@ export class Arc extends SketchPrimitive {
return points;
}
toVerbNurbs(plane, _3dtr) {
const basis = plane.basis();
toVerbNurbs(tr, csys) {
const basisX = csys.x;
const basisY = csys.y;
const startAngle = makeAngle0_360(Math.atan2(this.a.y - this.c.y, this.a.x - this.c.x));
const endAngle = makeAngle0_360(Math.atan2(this.b.y - this.c.y, this.b.x - this.c.x));
@ -113,16 +117,16 @@ export class Arc extends SketchPrimitive {
angle = Math.PI * 2 + angle;
}
function pointAtAngle(angle) {
const dx = basis[0].multiply(Math.cos(angle));
const dy = basis[1].multiply(Math.sin(angle));
const dx = basisX.multiply(Math.cos(angle));
const dy = basisY.multiply(Math.sin(angle));
return dx.plus(dy);
}
const xAxis = pointAtAngle(startAngle);
const yAxis = pointAtAngle(startAngle + Math.PI * 0.5);
let arc = new verb.geom.Arc(_3dtr(this.c).data(), xAxis.data(), yAxis.data(), distanceAB(this.c, this.a), 0, Math.abs(angle));
let arc = new verb.geom.Arc(tr(this.c).data(), xAxis.data(), yAxis.data(), distanceAB(this.c, this.a), 0, Math.abs(angle));
return adjustEnds(arc, _3dtr(this.a), _3dtr(this.b))
return adjustEnds(arc, tr(this.a), tr(this.b))
}
}
@ -184,7 +188,7 @@ export class EllipticalArc extends SketchPrimitive {
return points;
}
toVerbNurbs(plane, _3dtr) {
toVerbNurbs(tr) {
const xAxis = this.ep2.minus(this.ep1)._multiply(0.5);
const yAxis = new Vector(xAxis.y, xAxis.x)._normalize()._multiply(this.r) ;
const center = this.ep1.plus(xAxis);
@ -192,8 +196,8 @@ export class EllipticalArc extends SketchPrimitive {
const startAngle = makeAngle0_360(Math.atan2(this.a.y - center.y, this.a.x - center.x));
const endAngle = makeAngle0_360(Math.atan2(this.b.y - center.y, this.b.x - center.x));
let arc = new verb.geom.EllipseArc(_3dtr(center).data(), _3dtr(xAxis).data(), _3dtr(yAxis).data(), startAngle, endAngle);
return adjustEnds(arc, _3dtr(this.a), _3dtr(this.b))
let arc = new verb.geom.EllipseArc(tr(center).data(), tr(xAxis).data(), tr(yAxis).data(), startAngle, endAngle);
return adjustEnds(arc, tr(this.a), tr(this.b))
}
}
@ -223,9 +227,10 @@ export class Circle extends SketchPrimitive {
}
toVerbNurbs(plane, _3dtr) {
const basis = plane.basis();
return new verb.geom.Circle(_3dtr(this.c).data(), basis[0].data(), basis[1].data(), this.r);
toVerbNurbs(tr, csys) {
const basisX = csys.x;
const basisY = csys.y;
return new verb.geom.Circle(tr(this.c).data(), basisX.data(), basisY.data(), this.r);
}
}
@ -252,9 +257,9 @@ export class Contour {
this.segments.push(obj);
}
tessellateOnSurface(surface) {
tessellateOnSurface(csys) {
const cc = new CompositeCurve();
const tr = to3DTrFunc(surface);
const tr = csys.outTransformation;
let prev = null;
let firstPoint = null;
@ -273,7 +278,7 @@ export class Contour {
tessellation[n - 1] = firstPoint;
}
cc.add(segment.toNurbs(surface), prev, segment);
cc.add(segment.toNurbs(csys), prev, segment);
prev = tessellation[n - 1];
//It might be an optimization for segments
@ -286,14 +291,11 @@ export class Contour {
return cc;
}
transferOnSurface(surface) {
transferInCoordinateSystem(csys) {
const cc = [];
let prev = null;
let firstPoint = null;
for (let segIdx = 0; segIdx < this.segments.length; ++segIdx) {
let segment = this.segments[segIdx];
cc.push(segment.toNurbs(surface));
cc.push(segment.toNurbs(csys));
}
return cc;
}
@ -320,13 +322,6 @@ export class Contour {
}
}
function to3DTrFunc(surface) {
const _3dTransformation = surface.get3DTransformation();
return function (v) {
return _3dTransformation.apply(v);
}
}
class CompositeCurve {
constructor() {

View file

@ -17,21 +17,25 @@ export default class CSys {
this.z = z;
}
get inTransformation() {
w() {
return this.z.dot(this.origin);
}
get outTransformation() {
if (!this._inTr) {
const basis = new Matrix3().setBasis([this.x, this.y, this.z]);
const translate = new Matrix3();
translate.tx = this.origin.x;
translate.ty = this.origin.y;
translate.tz = this.origin.z;
this._inTr = basis.combine(translate);
basis.tx = this.origin.x;
basis.ty = this.origin.y;
basis.tz = this.origin.z;
this._inTr = basis;//basis.combine(translate);
}
return this._inTr;
}
get outTransformation() {
get inTransformation() {
if (!this._outTr) {
this._outTr = this.inTransformation().invert();
this._outTr = this.outTransformation.invert();
}
return this._outTr;
}

View file

@ -19,9 +19,12 @@ export const STANDARD_BASES = freeze({
});
/** @constructor */
function Matrix3() {
this.reset();
class Matrix3 {
constructor() {
this.reset();
}
apply = vector => this.__apply(vector, new Vector());
}
Matrix3.prototype.reset = function() {
@ -181,10 +184,6 @@ Matrix3.prototype.combine = function(transform, out) {
return m;
};
Matrix3.prototype.apply = function(vector) {
return this.__apply(vector, new Vector())
};
Matrix3.prototype._apply = function(vector) {
return this.__apply(vector, vector);
};