mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-08 01:13:27 +01:00
improving BREP boolean algorithm / sector analysis
This commit is contained in:
parent
8f1e847afc
commit
d89ba309b8
9 changed files with 250 additions and 117 deletions
|
|
@ -36,6 +36,24 @@ function addGlobalDebugActions(app) {
|
|||
AddVertex: (v) => {
|
||||
window.__DEBUG__.AddPoint(v.point);
|
||||
},
|
||||
AddPolygon: (vertices, color) => {
|
||||
for (let i = 0; i < vertices.length; i ++) {
|
||||
__DEBUG__.AddSegment(vertices[i].point, vertices[(i + 1) % vertices.length].point, color);
|
||||
}
|
||||
},
|
||||
AddPlane: (plane) => {
|
||||
const geo = new THREE.PlaneBufferGeometry(2000, 2000, 8, 8);
|
||||
const coplanarPoint = plane.normal.multiply(plane.w);
|
||||
const focalPoint = coplanarPoint.plus(plane.normal);
|
||||
geo.lookAt(focalPoint.three());
|
||||
geo.translate(coplanarPoint.x, coplanarPoint.y, coplanarPoint.z);
|
||||
const mat = new THREE.MeshBasicMaterial({ color: 0x000000, side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.3, });
|
||||
const planeObj = new THREE.Mesh(geo, mat);
|
||||
debugGroup.add(planeObj);
|
||||
app.viewer.render();
|
||||
},
|
||||
AddHalfEdge: (he, color) => {
|
||||
window.__DEBUG__.AddSegment(he.vertexA.point, he.vertexB.point, color);
|
||||
},
|
||||
|
|
@ -93,7 +111,7 @@ function clearGroup(g) {
|
|||
|
||||
function createLine(a, b, color) {
|
||||
color = color || 0xFA8072;
|
||||
const debugLineMaterial = new THREE.LineBasicMaterial({color, linewidth: 3});
|
||||
const debugLineMaterial = new THREE.LineBasicMaterial({color, linewidth: 10});
|
||||
const lg = new THREE.Geometry();
|
||||
lg.vertices.push(a.three());
|
||||
lg.vertices.push(b.three());
|
||||
|
|
|
|||
|
|
@ -148,3 +148,13 @@ export function iterateSegments(items, callback) {
|
|||
callback(items[i], items[j], i, j);
|
||||
}
|
||||
}
|
||||
|
||||
export function invertLoop(loop) {
|
||||
for (let halfEdge of loop.halfEdges) {
|
||||
const t = halfEdge.vertexA;
|
||||
halfEdge.vertexA = halfEdge.vertexB;
|
||||
halfEdge.vertexB = t;
|
||||
}
|
||||
loop.halfEdges.reverse();
|
||||
linkSegments(loop.halfEdges);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@ export class Plane extends Surface {
|
|||
//TODO: store this.normal.multiply(this.w) in a field since it's constant value
|
||||
}
|
||||
|
||||
equals(other, tol) {
|
||||
return other instanceof Plane &&
|
||||
math.areVectorsEqual(this.normal, other.normal, tol) &&
|
||||
math.areEqual(this.w, other.w, tol);
|
||||
}
|
||||
|
||||
toParametricForm() {
|
||||
const basis = BasisForPlane(this.normal);
|
||||
return new ParametricPlane(this.normal.multiply(this.w), basis.x, basis.y);
|
||||
|
|
|
|||
|
|
@ -48,19 +48,15 @@ export function subtract( shell1, shell2 ) {
|
|||
export function invert( shell ) {
|
||||
for (let face of shell.faces) {
|
||||
face.surface = face.surface.invert();
|
||||
invertLoop(face.outerLoop);
|
||||
for (let loop of face.loops) {
|
||||
invertLoop(loop);
|
||||
}
|
||||
}
|
||||
BREPValidator.validateToConsole(shell);
|
||||
}
|
||||
|
||||
function invertLoop(loop) {
|
||||
for (let halfEdge of loop.halfEdges) {
|
||||
const t = halfEdge.vertexA;
|
||||
halfEdge.vertexA = halfEdge.vertexB;
|
||||
halfEdge.vertexB = t;
|
||||
}
|
||||
loop.halfEdges.reverse();
|
||||
BREPBuilder.linkSegments(loop.halfEdges);
|
||||
BREPBuilder.invertLoop(loop);
|
||||
}
|
||||
|
||||
export function BooleanAlgorithm( shell1, shell2, type ) {
|
||||
|
|
@ -204,11 +200,22 @@ function sew(allFaces) {
|
|||
if (sewed.has(h1)) {
|
||||
continue;
|
||||
}
|
||||
const neighbors = findNeighborhood(allFaces, face, h1);
|
||||
if (neighbors.length == 0) {
|
||||
const neighborhood = findNeighborhood(allFaces, face, h1);
|
||||
if (neighborhood.all.length == 1) {
|
||||
continue FACES;
|
||||
}
|
||||
let h2 = neighborhoodAnalysis(h1, neighbors, analyzedNeighbors);
|
||||
|
||||
let h2;
|
||||
if (neighborhood.all.length == 2 && neighborhood.side2.length == 1) {
|
||||
h2 = neighborhood.side2[0];
|
||||
} else {
|
||||
h2 = analyzedNeighbors.get(h1);
|
||||
if (h2 === undefined) { // null indicates edge can't be sewed
|
||||
neighborhoodAnalysis(neighborhood, analyzedNeighbors);
|
||||
}
|
||||
h2 = analyzedNeighbors.get(h1);
|
||||
}
|
||||
|
||||
if (h2 == null) {
|
||||
continue FACES;
|
||||
}
|
||||
|
|
@ -230,98 +237,82 @@ function sew(allFaces) {
|
|||
return sewedFaces;
|
||||
}
|
||||
|
||||
function neighborhoodAnalysis(edge, neighbors, analized) {
|
||||
if (neighbors.opposite.length > 1 || neighbors.other != null) {
|
||||
function edgeV(edge) {
|
||||
return edge.vertexB.point.minus(edge.vertexA.point)._normalize();
|
||||
}
|
||||
|
||||
let paired = analized.get(edge);
|
||||
if (paired) {
|
||||
return paired;
|
||||
}
|
||||
function neighborhoodAnalysis(neighborhood, analized) {
|
||||
|
||||
let a1 = neighbors.opposite[0];
|
||||
let a2 = neighbors.opposite[1];
|
||||
let b1 = edge;
|
||||
let b2 = neighbors.other;
|
||||
if (!a1 || !a2) {
|
||||
a1 = edge;
|
||||
a2 = neighbors.other;
|
||||
b1 = neighbors.opposite[0];
|
||||
b2 = neighbors.opposite[1];
|
||||
}
|
||||
function encloses(e1, e2, testeeE) {
|
||||
const f1 = e1.loop.face;
|
||||
const f2 = e2.loop.face;
|
||||
const testee = testeeE.loop.face;
|
||||
|
||||
if (!a2) {
|
||||
throw 'unsupported neighborhood case'
|
||||
}
|
||||
const normal = edgeV(e1);
|
||||
const t1 = f1.surface.normal.cross(normal)._normalize();
|
||||
const t2 = f2.surface.normal.cross(edgeV(e2))._normalize();
|
||||
const t3 = testee.surface.normal.cross(edgeV(testeeE))._normalize();
|
||||
|
||||
const a1N = a1.loop.face.surface.normal;
|
||||
const a2N = a2.loop.face.surface.normal;
|
||||
const b1N = b1.loop.face.surface.normal;
|
||||
const normal = a1N.cross(b1N);
|
||||
//__DEBUG__.AddSegment(e1.vertexA.point, e1.vertexA.point.plus(normal.multiply(100)), 0xffffff);
|
||||
//__DEBUG__.AddSegment(e1.vertexA.point, e1.vertexA.point.plus(t1.multiply(100)), 0x00ff00);
|
||||
//__DEBUG__.AddSegment(e1.vertexA.point, e1.vertexA.point.plus(t2.multiply(100)), 0x00ffff);
|
||||
//__DEBUG__.AddSegment(e1.vertexA.point, e1.vertexA.point.plus(t3.multiply(100)), 0xff0000);
|
||||
|
||||
if (b2 == null) {
|
||||
const dist1 = leftTurningMeasure(b1N, a1N.negate(), normal);
|
||||
const dist2 = leftTurningMeasure(b1N, a2N.negate(), normal);
|
||||
if (dist1 > dist2) {
|
||||
analized.set(b1, a1);
|
||||
analized.set(a1, b1);
|
||||
analized.set(a2, null);
|
||||
} else {
|
||||
analized.set(b1, a2);
|
||||
analized.set(a2, b1);
|
||||
analized.set(a1, null);
|
||||
const angle = leftTurningMeasure(t1, t2, normal);
|
||||
const testAngle = leftTurningMeasure(t1, t3, normal);
|
||||
return testAngle > angle;
|
||||
}
|
||||
|
||||
let paired = new Set();
|
||||
for (let e1 of neighborhood.side1) {
|
||||
SIDE_2:
|
||||
for (let e2 of neighborhood.side2) {
|
||||
if (analized.has(e2)) continue;
|
||||
for (let t of neighborhood.all) {
|
||||
if (t == e1 || t == e2) {
|
||||
continue;
|
||||
}
|
||||
if (encloses(e1, e2, t)) {
|
||||
continue SIDE_2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const b2N = b2.loop.face.surface.normal;
|
||||
const dist1 = leftTurningMeasure(b1N, a1N.negate(), normal);
|
||||
const dist2 = leftTurningMeasure(b1N, a2N.negate(), normal);
|
||||
let closestDist, closestOption1, closestOption2, leftOver1, leftOver2;
|
||||
if (dist1 > dist2) {
|
||||
closestOption1 = a1;
|
||||
leftOver1 = a2;
|
||||
closestDist = dist1;
|
||||
} else {
|
||||
closestOption1 = a2;
|
||||
leftOver1 = a1;
|
||||
closestDist = dist2;
|
||||
}
|
||||
// concurrent in between
|
||||
if (leftTurningMeasure(b1N, b2N, normal) > closestDist) {
|
||||
closestOption2 = b2;
|
||||
leftOver2 = b1;
|
||||
} else {
|
||||
closestOption2 = b1;
|
||||
leftOver2 = b2;
|
||||
}
|
||||
analized.set(closestOption1, closestOption2);
|
||||
analized.set(closestOption2, closestOption1);
|
||||
analized.set(leftOver1, leftOver2);
|
||||
analized.set(leftOver2, leftOver1);
|
||||
return analized.get(edge);
|
||||
analized.set(e1, e2);
|
||||
analized.set(e2, e1);
|
||||
paired.add(e1);
|
||||
paired.add(e2);
|
||||
}
|
||||
}
|
||||
|
||||
for (let e of neighborhood.all) {
|
||||
if (!paired.has(e)) {
|
||||
analized.set(e, null);
|
||||
}
|
||||
} else {
|
||||
return neighbors.opposite[0];
|
||||
}
|
||||
}
|
||||
|
||||
function findNeighborhood(allFaces, skipFace, forEdge) {
|
||||
const result = {
|
||||
opposite: [],
|
||||
other: null
|
||||
side1: [forEdge],
|
||||
side2: [],
|
||||
all: [forEdge]
|
||||
};
|
||||
|
||||
for (let face of allFaces) {
|
||||
if (face == skipFace) continue;
|
||||
for (let e of face.edges) {
|
||||
if (areEdgesOpposite(e, forEdge)) {
|
||||
result.opposite.push(e)
|
||||
result.side2.push(e);
|
||||
result.all.push(e);
|
||||
} else if (e != forEdge && areEdgesEqual(e, forEdge)) {
|
||||
result.other = e;
|
||||
result.side1.push(e);
|
||||
result.all.push(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function mergeVertices(shell1, shell2) {
|
||||
export function mergeVertices(shell1, shell2) {
|
||||
const toSwap = new Map();
|
||||
for (let v1 of shell1.vertices) {
|
||||
for (let v2 of shell2.vertices) {
|
||||
|
|
@ -443,13 +434,14 @@ function areEdgesOpposite(e1, e2) {
|
|||
|
||||
function splitNewEdgesIfNeeded(faceData) {
|
||||
for (let oe of faceData.face.edges) {
|
||||
for (let ne of faceData.newEdges) {
|
||||
if (math.areEqual(Math.abs(ne.edge.curve.v.dot(oe.edge.curve.v)), 1, TOLERANCE_SQ) &&
|
||||
math.areEqual(Math.abs(ne.edge.curve.v.dot(ne.vertexA.point.minus(oe.vertexA.point)._normalize())), 1, TOLERANCE_SQ)) {
|
||||
const line = Line.fromSegment(ne.vertexA.point, ne.vertexB.point);
|
||||
const length = math.distanceAB3(ne.vertexA.point, ne.vertexB.point);
|
||||
for (let i = 0; i < faceData.newEdges.length; ++i) {
|
||||
let ne = faceData.newEdges[i];
|
||||
if ( math.areEqual(Math.abs(ne.edge.curve.v.dot(oe.edge.curve.v)), 1, TOLERANCE) &&
|
||||
math.areEqual(Math.abs(ne.edge.curve.v.dot(ne.vertexA.point.minus(oe.vertexA.point)._normalize())), 1, TOLERANCE)) {
|
||||
|
||||
function check(vertex) {
|
||||
const line = Line.fromSegment(ne.vertexA.point, ne.vertexB.point);
|
||||
const length = math.distanceAB3(ne.vertexA.point, ne.vertexB.point);
|
||||
if (ne.vertexA != vertex && ne.vertexB != vertex) {
|
||||
const t = line.t(vertex.point);
|
||||
if (t >= 0 && t <= length) {
|
||||
|
|
@ -488,8 +480,11 @@ function merge(face, newEdges) {
|
|||
|
||||
for (let e of newEdges) {
|
||||
if (e == null) continue;
|
||||
if (findCoincidentEdge(e, allEdges) == null) {
|
||||
const existingEdge = findCoincidentEdge(e, allEdges);
|
||||
if (existingEdge == null) {
|
||||
allEdges.push(e);
|
||||
} else {
|
||||
EdgeSolveData.createIfEmpty(existingEdge).newEdgeFlag = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -498,7 +493,7 @@ function merge(face, newEdges) {
|
|||
allEdges = allEdges.filter(e => e != null);
|
||||
//put new edges to the tail
|
||||
bringNewEdgesToTheTail(allEdges);
|
||||
squash(face, allEdges)
|
||||
squash(face, allEdges);
|
||||
if (DEBUG.EDGE_MERGING) {
|
||||
for (let e of allEdges) __DEBUG__.AddHalfEdge(e, 0xffff00);
|
||||
}
|
||||
|
|
@ -620,7 +615,7 @@ function traverseFaces(face, validFaces, callback) {
|
|||
}
|
||||
}
|
||||
|
||||
function loopsToFaces(originFace, loops, out) {
|
||||
export function loopsToFaces(originFace, loops, out) {
|
||||
function createFaces(nestedLoop, surface) {
|
||||
const loop = nestedLoop.loop;
|
||||
const newFace = new Face(surface);
|
||||
|
|
@ -703,25 +698,27 @@ function cleanUpSolveData(shell) {
|
|||
}
|
||||
}
|
||||
|
||||
function findMaxTurningLeft(edge, edges, normal) {
|
||||
function findMaxTurningLeft(pivotEdge, edges, normal) {
|
||||
edges = edges.slice();
|
||||
function edgeVector(edge) {
|
||||
return edge.vertexB.point.minus(edge.vertexA.point)._normalize();
|
||||
}
|
||||
const edgeV = edgeVector(edge);
|
||||
const pivot = pivotEdge.vertexA.point.minus(pivotEdge.vertexB.point)._normalize();
|
||||
edges.sort((e1, e2) => {
|
||||
return leftTurningMeasure(edgeV, edgeVector(e1), normal) - leftTurningMeasure(edgeV, edgeVector(e2), normal);
|
||||
return leftTurningMeasure(pivot, edgeVector(e1), normal) - leftTurningMeasure(pivot, edgeVector(e2), normal);
|
||||
});
|
||||
return edges[0];
|
||||
return edges[edges.length - 1];
|
||||
}
|
||||
|
||||
function leftTurningMeasure(v1, v2, normal) {
|
||||
let measure = v1.dot(v2);
|
||||
if (v1.cross(v2).dot(normal) < 0) {
|
||||
measure *= -1;
|
||||
measure += 2;
|
||||
measure = -(2 + measure);
|
||||
}
|
||||
return measure
|
||||
measure -= 1;//shift to the zero
|
||||
|
||||
//make it positive all the way
|
||||
return -measure;
|
||||
}
|
||||
|
||||
function intersectFaces(shell1, shell2, inverseCrossEdgeDirection) {
|
||||
|
|
@ -957,9 +954,9 @@ function findCloserOnCurve(nodes, toNode, curve) {
|
|||
for (let i = 0; i < nodes.length; i++) {
|
||||
let node = nodes[i];
|
||||
if (node == null) continue;
|
||||
let inward = toNode.normal * node.normal < 0;
|
||||
let distance = Math.abs(origin - curve.t(node.point));
|
||||
if (inward && distance < heroDistance) {
|
||||
let distance = (origin - curve.t(node.point)) * node.normal;
|
||||
if (distance < 0) continue;
|
||||
if (distance < heroDistance) {
|
||||
hero = i;
|
||||
heroDistance = distance;
|
||||
}
|
||||
|
|
@ -993,7 +990,7 @@ function intersectFaceWithEdge(face, edge, result) {
|
|||
const length = ab.length();
|
||||
const v = ab._multiply(1 / length);
|
||||
|
||||
if (math.areEqual(edge.edge.curve.v.dot(face.surface.normal), 0, TOLERANCE_SQ)) {
|
||||
if (math.areEqual(edge.edge.curve.v.dot(face.surface.normal), 0, TOLERANCE)) {
|
||||
if (math.areEqual(face.surface.normal.dot(edge.vertexA.point), face.surface.w, TOLERANCE)) {
|
||||
classifyAndAdd(edge.vertexA.point, true, false);
|
||||
classifyAndAdd(edge.vertexB.point, false, true);
|
||||
|
|
@ -1012,7 +1009,7 @@ function intersectFaceWithEdge(face, edge, result) {
|
|||
classifyAndAdd(pointOfIntersection, coiA, coiB)
|
||||
}
|
||||
}
|
||||
function classifyAndAdd(pointOfIntersection, coiA, coiB, dir) {
|
||||
function classifyAndAdd(pointOfIntersection, coiA, coiB) {
|
||||
const classRes = classifyPointToFace(pointOfIntersection, face);
|
||||
if (classRes.inside) {
|
||||
let vertexOfIntersection;
|
||||
|
|
@ -1029,9 +1026,6 @@ function intersectFaceWithEdge(face, edge, result) {
|
|||
}
|
||||
|
||||
const node = new Node(vertexOfIntersection, edge);
|
||||
if (dir) {
|
||||
node.dir = dir;
|
||||
}
|
||||
|
||||
result.push(node);
|
||||
if (classRes.edge) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,13 @@ export class Edge extends TopoObject {
|
|||
this.halfEdge1 = null;
|
||||
this.halfEdge2 = null;
|
||||
}
|
||||
|
||||
link(halfEdge1, halfEdge2) {
|
||||
halfEdge1.edge = this;
|
||||
halfEdge2.edge = this;
|
||||
this.halfEdge1 = halfEdge1;
|
||||
this.halfEdge2 = halfEdge2;
|
||||
}
|
||||
}
|
||||
|
||||
export class HalfEdge extends TopoObject {
|
||||
|
|
@ -22,8 +29,62 @@ export class HalfEdge extends TopoObject {
|
|||
this.prev = null;
|
||||
}
|
||||
|
||||
setAB(a, b) {
|
||||
this.vertexA = a;
|
||||
this.vertexB = b;
|
||||
}
|
||||
|
||||
twin() {
|
||||
return this.edge.halfEdge1 == this ? this.edge.halfEdge2 : this.edge.halfEdge1;
|
||||
}
|
||||
|
||||
split(vertex) {
|
||||
|
||||
function splitHalfEdge(h) {
|
||||
const newEdge = new HalfEdge();
|
||||
newEdge.vertexA = vertex;
|
||||
newEdge.vertexB = h.vertexB;
|
||||
h.vertexB = newEdge.vertexA;
|
||||
|
||||
h.vertexA.edges.add(newEdge);
|
||||
h.vertexA.edges.remove(h);
|
||||
vertex.edges.add(newEdge);
|
||||
|
||||
return newEdge;
|
||||
}
|
||||
|
||||
const orig = this;
|
||||
const twin = orig.twin();
|
||||
|
||||
if (orig.vertexA == vertex || orig.vertexB == vertex) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newOrig = splitHalfEdge(orig);
|
||||
const newTwin = splitHalfEdge(twin);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
orig.next = newOrig;
|
||||
twin.next = newTwin;
|
||||
|
||||
newOrig.loop = orig.loop;
|
||||
newTwin.loop = twin.loop;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
@ -7,12 +7,23 @@ export class Shell extends TopoObject {
|
|||
this.defineIterable('vertices', () => verticesGenerator(this));
|
||||
this.defineIterable('edges', () => edges(this))
|
||||
}
|
||||
|
||||
reindexVertices() {
|
||||
for (let e of this.edges) {
|
||||
e.halfEdge1.vertexA.edges.clear();
|
||||
e.halfEdge1.vertexB.edges.clear();
|
||||
}
|
||||
for (let e of this.edges) {
|
||||
e.halfEdge1.vertexA.edges.add(e.halfEdge1);
|
||||
e.halfEdge2.vertexA.edges.add(e.halfEdge2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function* verticesGenerator(shell) {
|
||||
const seen = new Set();
|
||||
for (let face of shell.faces) {
|
||||
for (let edge of face.outerLoop.halfEdges) {
|
||||
for (let edge of face.edges) {
|
||||
if (!seen.has(edge.vertexA)) {
|
||||
seen.add(edge.vertexA);
|
||||
yield edge.vertexA;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,15 @@ export class Vertex extends TopoObject {
|
|||
constructor(point) {
|
||||
super();
|
||||
this.point = point;
|
||||
this.edges = [];
|
||||
this.edges = new Set();
|
||||
}
|
||||
|
||||
edgeFor(other) {
|
||||
for (let e of this.edges) {
|
||||
if (e.vertexB == other) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,23 +5,38 @@ export default class BBox {
|
|||
constructor() {
|
||||
this.minX = Number.MAX_VALUE;
|
||||
this.minY = Number.MAX_VALUE;
|
||||
this.minZ = Number.MAX_VALUE;
|
||||
this.maxX = -Number.MAX_VALUE;
|
||||
this.maxY = -Number.MAX_VALUE;
|
||||
this.maxZ = -Number.MAX_VALUE;
|
||||
}
|
||||
|
||||
checkBounds(x, y) {
|
||||
checkBounds(x, y, z) {
|
||||
z = z || 0;
|
||||
this.minX = Math.min(this.minX, x);
|
||||
this.minY = Math.min(this.minY, y);
|
||||
this.minZ = Math.min(this.minZ, z);
|
||||
this.maxX = Math.max(this.maxX, x);
|
||||
this.maxY = Math.max(this.maxY, y);
|
||||
this.maxZ = Math.max(this.maxZ, z);
|
||||
}
|
||||
|
||||
checkPoint(p) {
|
||||
this.checkBounds(p.x, p.y);
|
||||
this.checkBounds(p.x, p.y, p.z);
|
||||
}
|
||||
|
||||
center() {
|
||||
return new Vector(this.minX + (this.maxX - this.minX) / 2, this.minY + (this.maxY - this.minY) / 2, 0)
|
||||
return new Vector(this.minX + (this.maxX - this.minX) / 2,
|
||||
this.minY + (this.maxY - this.minY) / 2,
|
||||
this.minZ + (this.maxZ - this.minZ) / 2)
|
||||
}
|
||||
|
||||
min() {
|
||||
return new Vector(this.minX, this.minY, this.minZ)
|
||||
}
|
||||
|
||||
max() {
|
||||
return new Vector(this.maxX, this.maxY, this.maxZ)
|
||||
}
|
||||
|
||||
width() {
|
||||
|
|
@ -32,11 +47,17 @@ export default class BBox {
|
|||
return this.maxY - this.minY;
|
||||
}
|
||||
|
||||
depth() {
|
||||
return this.maxZ - this.minZ;
|
||||
}
|
||||
|
||||
expand(delta) {
|
||||
this.minX -= delta;
|
||||
this.minY -= delta;
|
||||
this.minZ -= delta;
|
||||
this.maxX += delta;
|
||||
this.maxY += delta;
|
||||
this.maxZ += delta;
|
||||
}
|
||||
|
||||
toPolygon() {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,14 @@ Vector.prototype.length = function() {
|
|||
return Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z);
|
||||
};
|
||||
|
||||
Vector.prototype.lengthSquared = function() {
|
||||
return this.dot(this);
|
||||
};
|
||||
|
||||
Vector.prototype.distanceToSquared = function(a) {
|
||||
return this.minus(a).lengthSquared();
|
||||
};
|
||||
|
||||
Vector.prototype.minus = function(vector) {
|
||||
return new Vector(this.x - vector.x, this.y - vector.y, this.z - vector.z);
|
||||
};
|
||||
|
|
@ -118,8 +126,4 @@ Vector.prototype.three = function() {
|
|||
return new THREE.Vector3(this.x, this.y, this.z);
|
||||
};
|
||||
|
||||
Vector.prototype.csg = function() {
|
||||
return new CSG.Vector3D(this.x, this.y, this.z);
|
||||
};
|
||||
|
||||
export default Vector;
|
||||
Loading…
Reference in a new issue