jsketcher/web/app/brep/brep-builder.js

152 lines
4.6 KiB
JavaScript

import {Shell} from './topo/shell'
import {Vertex} from './topo/vertex'
import {Loop} from './topo/loop'
import {Face} from './topo/face'
import {HalfEdge, Edge} from './topo/edge'
import {Line} from './geom/impl/line'
import {NurbsSurface, NurbsCurve} from './geom/impl/nurbs'
import {Plane} from './geom/impl/plane'
import {Point} from './geom/point'
import {BasisForPlane, Matrix3} from '../math/l3space'
import {CompositeCurve} from './geom/curve'
import * as cad_utils from '../3d/cad-utils'
import * as math from '../math/math'
import mergeNullFace from './null-face-merge'
import {invert} from './operations/boolean'
import BBox from "../math/bbox";
function isCCW(points, normal) {
const tr2d = new Matrix3().setBasis(BasisForPlane(normal)).invert();
const points2d = points.map(p => tr2d.apply(p));
return math.isCCW(points2d);
}
function checkCCW(points, normal) {
if (!isCCW(points, normal)) {
points = points.slice();
points.reverse();
}
return points;
}
export function createPrism(basePoints, height) {
const normal = cad_utils.normalOfCCWSeq(basePoints);
const baseSurface = new Plane(normal, normal.dot(basePoints[0]));
const extrudeVector = baseSurface.normal.multiply( - height);
const lidSurface = baseSurface.translate(extrudeVector).invert();
const lidPoints = basePoints.map(p => p.plus(extrudeVector));
const basePath = new CompositeCurve();
const lidPath = new CompositeCurve();
for (let i = 0; i < basePoints.length; i++) {
let j = (i + 1) % basePoints.length;
basePath.add(NurbsCurve.createLinearNurbs(basePoints[i], basePoints[j]), basePoints[i], null);
lidPath.add(NurbsCurve.createLinearNurbs(lidPoints[i], lidPoints[j]), lidPoints[i], null);
}
return enclose(basePath, lidPath, baseSurface, lidSurface);
}
export function enclose(basePath, lidPath, basePlane, lidPlane) {
if (basePath.points.length !== lidPath.points.length) {
throw 'illegal arguments';
}
const walls = [];
const n = basePath.points.length;
for (let i = 0; i < n; i++) {
let j = (i + 1) % n;
const wall = createWall(basePath.curves[i], lidPath.curves[i]);
walls.push(wall);
}
return assemble(walls, basePlane, lidPlane)
}
function assemble(walls, basePlane, lidPlane) {
const shell = new Shell();
const wallEdges = [];
const baseEdges = [];
const lidEdges = [];
for (let w of walls) {
let wallEdge = Edge.fromCurve(w.isoCurveAlignV(0));
wallEdges.push(wallEdge);
}
for (let i = 0; i < wallEdges.length; ++i) {
let j = (i + 1) % wallEdges.length;
let curr = wallEdges[i];
let next = wallEdges[j];
let wall = walls[i];
let baseEdge = new Edge(wall.isoCurveAlignU(1), curr.halfEdge1.vertexB, next.halfEdge1.vertexB);
let lidEdge = new Edge(wall.isoCurveAlignU(0), curr.halfEdge1.vertexA, next.halfEdge1.vertexA);
baseEdges.push(baseEdge);
lidEdges.push(lidEdge);
let wallFace = new Face(wall);
wallFace.outerLoop.halfEdges.push(baseEdge.halfEdge2, curr.halfEdge2, lidEdge.halfEdge1, next.halfEdge1);
wallFace.outerLoop.link();
shell.faces.push(wallFace);
}
const base = new Face();
const lid = new Face();
lidEdges.reverse();
baseEdges.forEach(e => base.outerLoop.halfEdges.push(e.halfEdge1));
lidEdges.forEach(e => lid.outerLoop.halfEdges.push(e.halfEdge2));
base.outerLoop.link();
lid.outerLoop.link();
base.surface = createBoundingNurbs(base.outerLoop.asPolygon(), basePlane);
lid.surface = createBoundingNurbs(lid.outerLoop.asPolygon(), lidPlane);
shell.faces.push(base, lid);
shell.faces.forEach(f => f.shell = shell);
return shell;
}
function createBoundingNurbs(points, plane) {
if (!plane) {
const normal = cad_utils.normalOfCCWSeq(points);
const w = points[0].dot(normal);
plane = new Plane(normal, w);
}
let to2D = plane.get2DTransformation();
let points2d = points.map(p => to2D.apply(p));
let bBox = new BBox();
points2d.forEach(p => bBox.checkPoint(p));
let to3D = plane.get3DTransformation();
let polygon = bBox.toPolygon();
polygon = polygon.map(p => to3D._apply(p));
const nurbs = new NurbsSurface(new verb.geom.ExtrudedSurface(new verb.geom.Line(
polygon[0].data(), polygon[1].data()), polygon[2].minus(polygon[1]).data()));
return nurbs;
}
function bothClassOf(o1, o2, className) {
return o1.constructor.name === className && o2.constructor.name === className;
}
export function createWall(curve1, curve2) {
if (bothClassOf(curve1, curve2, 'Line')) {
throw 'unsupported'
} else if (bothClassOf(curve1, curve2, 'NurbsCurve')) {
return new NurbsSurface(verb.geom.NurbsSurface.byLoftingCurves([curve2.verb, curve1.verb], 1));
} else {
throw 'unsupported';
}
}