mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-09 18:02:50 +01:00
NURBS suport for BREP boolean / adjusting the API
This commit is contained in:
parent
a2da7e348d
commit
60e1455bb1
10 changed files with 306 additions and 1119 deletions
|
|
@ -24,6 +24,8 @@ import * as BREPBool from '../brep/operations/boolean'
|
|||
import {BREPValidator} from '../brep/brep-validator'
|
||||
import {BREPSceneSolid} from './scene/brep-scene-object'
|
||||
import TPI from './tpi'
|
||||
import {createBox, createSphere, createCylinder} from "../hds/hds-builder";
|
||||
// import {createSphere, rayMarchOntoCanvas, sdfIntersection, sdfSolid, sdfSubtract, sdfTransform, sdfUnion} from "../hds/sdf";
|
||||
|
||||
function App() {
|
||||
this.id = this.processHints();
|
||||
|
|
@ -83,27 +85,51 @@ App.prototype.addShellOnScene = function(shell, skin) {
|
|||
};
|
||||
|
||||
App.prototype.scratchCode = function() {
|
||||
const a = BREPBuilder.createPrism(ap.map(p => new this.TPI.brep.geom.Point().set3(p)), 500);
|
||||
const b = BREPBuilder.createPrism(bp.map(p => new this.TPI.brep.geom.Point().set3(p)), 500);
|
||||
|
||||
this.addShellOnScene(a, {
|
||||
color: 0x800080,
|
||||
transparent: true,
|
||||
opacity: 0.5,
|
||||
});
|
||||
this.addShellOnScene(b, {
|
||||
color: 0xfff44f,
|
||||
transparent: true,
|
||||
opacity: 0.5,
|
||||
});
|
||||
//this.addShellOnScene(a);
|
||||
//this.addShellOnScene(b);
|
||||
const result = BREPBool.subtract(a, b);
|
||||
this.addShellOnScene(result);
|
||||
|
||||
let box = createBox(500, 500, 500);
|
||||
let sphere = createSphere([0, 200, 0], 300);
|
||||
let clylinder = createCylinder(150, 500);
|
||||
|
||||
this.viewer.workGroup.add(box.toThreeMesh());
|
||||
// this.viewer.workGroup.add(sphere.toThreeMesh());
|
||||
this.viewer.workGroup.add(clylinder.toThreeMesh());
|
||||
this.viewer.render();
|
||||
};
|
||||
|
||||
|
||||
App.prototype.raytracing = function() {
|
||||
let box = createBox(800, 800, 800);
|
||||
this.viewer.workGroup.add(box.toThreeMesh());
|
||||
|
||||
let win = $('<div><canvas width="1000" height="1000" /></div>')
|
||||
.css({
|
||||
'position': 'absolute',
|
||||
'width': '800px',
|
||||
'height': '800px',
|
||||
'left': '20px',
|
||||
'top': '20px',
|
||||
'z-order': 999999
|
||||
});
|
||||
win.appendTo($('body'));
|
||||
const canvas = win.find('canvas')[0];
|
||||
console.log(canvas);
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// ctx.fillStyle = 'green';
|
||||
// ctx.fillRect(10, 10, 100, 100);
|
||||
|
||||
let sphere = createSphere(new Vector(), 600);
|
||||
let sphere2 = sdfTransform(sphere, new Matrix3().translate(-150, 300, 0));
|
||||
let solid = sdfSolid(box);
|
||||
let solid2 = sdfTransform(sphere, new Matrix3().translate(-150, 300, 0));
|
||||
let result = sdfSubtract(sphere, sphere2);
|
||||
let solid3 = sdfSubtract(solid, solid2);
|
||||
|
||||
let width = this.viewer.container.clientWidth;
|
||||
let height = this.viewer.container.clientHeight;
|
||||
rayMarchOntoCanvas(solid3, this.viewer.camera, width, height, canvas, 2000, 10);
|
||||
this.viewer.render();
|
||||
}
|
||||
|
||||
App.prototype.processHints = function() {
|
||||
let id = window.location.hash.substring(1);
|
||||
if (!id) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {SelectionManager, SketchSelectionManager, EdgeSelectionManager} from './
|
|||
|
||||
function Viewer(bus, container) {
|
||||
this.bus = bus;
|
||||
this.container = container;
|
||||
function aspect() {
|
||||
return container.clientWidth / container.clientHeight;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ export class Line extends Curve {
|
|||
|
||||
constructor(p0, v) {
|
||||
super();
|
||||
throw 'only nurbs for now'
|
||||
this.p0 = p0;
|
||||
this.v = v;
|
||||
this._pointsCache = new Map();
|
||||
|
|
|
|||
|
|
@ -2,10 +2,14 @@ import verb from 'verb-nurbs'
|
|||
import {Matrix3} from '../../../math/l3space'
|
||||
import * as math from '../../../math/math'
|
||||
import {Point} from '../point'
|
||||
import {Surface} from "../surface";
|
||||
import Vector from "../../../math/vector";
|
||||
import {Curve} from "../curve";
|
||||
|
||||
export class NurbsCurve {
|
||||
export class NurbsCurve extends Curve {
|
||||
|
||||
constructor(verbCurve) {
|
||||
super();
|
||||
this.verb = verbCurve;
|
||||
}
|
||||
|
||||
|
|
@ -50,18 +54,30 @@ export class NurbsCurve {
|
|||
tangentAtPoint(point) {
|
||||
return new Point().set3(this.verb.tangent(this.verb.closestParam(point.data())));
|
||||
}
|
||||
|
||||
tangentAtParam(param) {
|
||||
return new Point().set3(this.verb.tangent(param ));
|
||||
}
|
||||
|
||||
closestDistanceToPoint(point) {
|
||||
const closest = this.verb.closestPoint(point.data());
|
||||
return math.distance3(point.x, point.y, point.z, closest[0], closest[1], closest[2]);
|
||||
}
|
||||
|
||||
tangent(point) {
|
||||
return new Point().set3(this.verb.tangent( this.verb.closestParam(point.data() )));
|
||||
|
||||
split(point) {
|
||||
return this.verb.split(this.verb.closestParam(point.data)).map(v => new NurbsCurve(v));
|
||||
}
|
||||
|
||||
intersect(other, tolerance) {
|
||||
return verb.geom.Intersect.curves(this.verb, other.verb, tolerance);
|
||||
}
|
||||
|
||||
invert() {
|
||||
return new NurbsCurve(this.verb.reverse());
|
||||
}
|
||||
|
||||
intersect(other, tolerance) {
|
||||
return verb.geom.Intersect.curves(this.verb, other.verb, tolerance).map(i => new Point().set3(i.point0));
|
||||
point(u) {
|
||||
return new Point().set3(this.verb.point(u));
|
||||
}
|
||||
|
||||
static createByPoints(points, degeree) {
|
||||
|
|
@ -78,37 +94,36 @@ NurbsCurve.prototype.createLinearNurbs = function(a, b) {
|
|||
return NurbsCurve.createLinearNurbs(a, b);
|
||||
};
|
||||
|
||||
|
||||
export class NurbsSurface {
|
||||
export class NurbsSurface extends Surface {
|
||||
|
||||
constructor(verbSurface) {
|
||||
super();
|
||||
this.verb = verbSurface;
|
||||
}
|
||||
|
||||
isCognateCurve(curve) {
|
||||
return curve.constructor.name == 'NurbsCurve';
|
||||
this.inverted = false;
|
||||
}
|
||||
|
||||
toNurbs() {
|
||||
return this;
|
||||
}
|
||||
|
||||
coplanarUnsignedForSameClass(other, tol) {
|
||||
//throw 'not implemented'
|
||||
return false;
|
||||
|
||||
normal(point) {
|
||||
let uv = this.verb.closestParam(point.data());
|
||||
let normal = new Vector().set3(this.verb.normal(uv[0], uv[1]));
|
||||
if (this.inverted) {
|
||||
normal._negate();
|
||||
}
|
||||
return normal;
|
||||
}
|
||||
|
||||
intersectForSameClass(other, tol) {
|
||||
const curves = verb.geom.Intersect.surfaces(this.verb, other.verb, tol);
|
||||
return curves.map(curve => new NurbsCurve(curve));
|
||||
let inverted = this.inverted !== other.inverted;
|
||||
return curves.map(curve => new NurbsCurve(inverted ? curve.reverse() : curve));
|
||||
}
|
||||
|
||||
classifyCognateCurve(line, tol) {
|
||||
const parallel = math.areEqual(line.v.dot(this.normal), 0, tol);
|
||||
const pointOnPlane = math.areEqual(this.normal.dot(line.p0), this.w, tol);
|
||||
return {
|
||||
hit: !parallel || pointOnPlane,
|
||||
parallel
|
||||
}
|
||||
|
||||
invert() {
|
||||
let inverted = new NurbsSurface(this.verb);
|
||||
inverted.inverted = !this.inverted;
|
||||
return inverted;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ export class Plane extends Surface {
|
|||
|
||||
constructor(normal, w) {
|
||||
super();
|
||||
throw 'only nurbs for now'
|
||||
this.normal = normal;
|
||||
this.w = w;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,19 +5,6 @@ export class Surface {
|
|||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
classifyCognateCurve(curve, tol) {
|
||||
throw 'not implemented';
|
||||
}
|
||||
|
||||
classifyCurve(curve, tol) {
|
||||
if (this.isCognateCurve(curve)) {
|
||||
return this.classifyCognateCurve(curve, tol)
|
||||
}
|
||||
return this.toNurbs().classifyCognateCurve(curve.toNurbs(), tol);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
intersectForSameClass() {
|
||||
|
|
@ -33,19 +20,6 @@ export class Surface {
|
|||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
coplanarUnsignedForSameClass(other, tol) {
|
||||
throw 'not implemented';
|
||||
}
|
||||
|
||||
coplanarUnsigned(other, tol) {
|
||||
if (this.isSameClass(other)) {
|
||||
return this.coplanarUnsignedForSameClass(other, tol)
|
||||
}
|
||||
return this.toNurbs().coplanarUnsignedForSameClass(other.toNurbs());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
equalsForSameClass(other, tol) {
|
||||
throw 'not implemented';
|
||||
}
|
||||
|
|
@ -59,16 +33,16 @@ export class Surface {
|
|||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
normal(point) {
|
||||
throw 'not implemented';
|
||||
}
|
||||
|
||||
toNurbs() {
|
||||
throw 'not implemented';
|
||||
}
|
||||
|
||||
isSameClass(other) {
|
||||
return this.constructor.name == other.constructor.name;
|
||||
}
|
||||
|
||||
isCognateCurve(curve) {
|
||||
return false;
|
||||
return this.constructor.name === other.constructor.name;
|
||||
}
|
||||
}
|
||||
Surface.prototype.isPlane = false;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,106 +2,46 @@ import {TopoObject} from './topo-object'
|
|||
|
||||
export class Edge extends TopoObject {
|
||||
|
||||
constructor(curve) {
|
||||
constructor(curve, a, b) {
|
||||
super();
|
||||
this.curve = curve;
|
||||
this.halfEdge1 = null;
|
||||
this.halfEdge2 = null;
|
||||
this.halfEdge1 = new HalfEdge(this, false, a, b);
|
||||
this.halfEdge2 = new HalfEdge(this, true, b, a);
|
||||
}
|
||||
|
||||
link(halfEdge1, halfEdge2) {
|
||||
halfEdge1.edge = this;
|
||||
halfEdge2.edge = this;
|
||||
this.halfEdge1 = halfEdge1;
|
||||
this.halfEdge2 = halfEdge2;
|
||||
|
||||
invert() {
|
||||
const t = this.halfEdge1;
|
||||
this.halfEdge1 = this.halfEdge2;
|
||||
this.halfEdge2 = t;
|
||||
this.halfEdge1.inverted = false;
|
||||
this.halfEdge2.inverted = true;
|
||||
this.curve = this.curve.invert();
|
||||
}
|
||||
}
|
||||
|
||||
export class HalfEdge extends TopoObject {
|
||||
class HalfEdge extends TopoObject {
|
||||
|
||||
constructor() {
|
||||
constructor(edge, inverted, a, b) {
|
||||
super();
|
||||
this.edge = null;
|
||||
this.vertexA = null;
|
||||
this.vertexB = null;
|
||||
this.edge = edge;
|
||||
this.inverted = inverted;
|
||||
this.vertexA = a;
|
||||
this.vertexB = b;
|
||||
this.loop = null;
|
||||
this.next = null;
|
||||
this.prev = null;
|
||||
}
|
||||
|
||||
static create(a, b, loop, edge) {
|
||||
const e = new HalfEdge().setAB(a, b);
|
||||
e.loop = loop;
|
||||
e.edge = edge;
|
||||
return e;
|
||||
}
|
||||
|
||||
setAB(a, b) {
|
||||
this.vertexA = a;
|
||||
this.vertexB = b;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
twin() {
|
||||
return this.edge.halfEdge1 == this ? this.edge.halfEdge2 : this.edge.halfEdge1;
|
||||
return this.edge.halfEdge1 === this ? this.edge.halfEdge2 : this.edge.halfEdge1;
|
||||
}
|
||||
|
||||
|
||||
splitHalfEdge(vertex) {
|
||||
const h = this;
|
||||
const newEdge = new HalfEdge();
|
||||
newEdge.vertexA = vertex;
|
||||
newEdge.vertexB = h.vertexB;
|
||||
h.vertexB = newEdge.vertexA;
|
||||
|
||||
h.vertexA.edges.add(newEdge);
|
||||
h.vertexA.edges.delete(h);
|
||||
vertex.edges.add(newEdge);
|
||||
|
||||
return newEdge;
|
||||
}
|
||||
|
||||
split(vertex) {
|
||||
|
||||
const orig = this;
|
||||
const twin = orig.twin();
|
||||
|
||||
if (orig.vertexA == vertex || orig.vertexB == vertex) {
|
||||
return;
|
||||
tangent(point) {
|
||||
let tangent = this.edge.curve.tangentAtPoint(point);
|
||||
tangent._normalize();
|
||||
if (this.inverted) {
|
||||
tangent._negate();
|
||||
}
|
||||
|
||||
const newOrig = orig.splitHalfEdge(vertex);
|
||||
const newTwin = twin.splitHalfEdge(vertex);
|
||||
|
||||
|
||||
orig.edge.link(orig, newTwin);
|
||||
new Edge(orig.edge.curve).link(twin, newOrig);
|
||||
|
||||
orig.loop.halfEdges.splice(orig.loop.halfEdges.indexOf(orig) + 1, 0, newOrig);
|
||||
twin.loop.halfEdges.splice(twin.loop.halfEdges.indexOf(twin) + 1, 0, newTwin);
|
||||
|
||||
function insertToLL(orig, newOrig) {
|
||||
orig.next.prev = newOrig
|
||||
newOrig.next = orig.next;
|
||||
orig.next = newOrig;
|
||||
newOrig.prev = orig;
|
||||
}
|
||||
|
||||
insertToLL(orig, newOrig);
|
||||
insertToLL(twin, newTwin);
|
||||
|
||||
newOrig.loop = orig.loop;
|
||||
newTwin.loop = twin.loop;
|
||||
return tangent;
|
||||
}
|
||||
}
|
||||
|
||||
HalfEdge.fromVertices = function(a, b, curve) {
|
||||
const halfEdge1 = new HalfEdge();
|
||||
const halfEdge2 = new HalfEdge();
|
||||
|
||||
halfEdge1.setAB(a, b);
|
||||
halfEdge2.setAB(b, a);
|
||||
|
||||
new Edge(curve).link(halfEdge1, halfEdge2);
|
||||
return halfEdge1;
|
||||
};
|
||||
|
|
@ -15,7 +15,7 @@ export class Face extends TopoObject {
|
|||
}
|
||||
|
||||
export function* loopsGenerator(face) {
|
||||
if (face.outerLoop != null) {
|
||||
if (face.outerLoop !== null) {
|
||||
yield face.outerLoop;
|
||||
}
|
||||
for (let innerLoop of face.innerLoops) {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,28 @@ const CASE = {
|
|||
app.addShellOnScene(result);
|
||||
env.done();
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* https://github.com/xibyte/jsketcher/issues/47
|
||||
*/
|
||||
testFacesAreInSamePlane_BUG_47: function(env) {
|
||||
test.modeller(env.test((win, app) => {
|
||||
|
||||
const mat = new Matrix3();
|
||||
const m = mat.translate(0, 0, 10);
|
||||
|
||||
const box1 = app.TPI.brep.primitives.box(100, 100, 100, undefined);
|
||||
const box2 = app.TPI.brep.primitives.box(100, 100, 100, mat);
|
||||
const shell = app.TPI.brep.bool.union(box1, box2);
|
||||
|
||||
// app.addShellOnScene(box1);
|
||||
// app.addShellOnScene(box2);
|
||||
app.addShellOnScene(shell);
|
||||
env.done();
|
||||
}));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
def('simple.union');
|
||||
|
|
|
|||
Loading…
Reference in a new issue