jsketcher/modules/brep/topo/edge.ts
2022-08-15 23:47:20 -07:00

168 lines
4.1 KiB
TypeScript

import {TopoObject} from './topo-object'
import {Vertex} from "./vertex";
import BrepCurve from "geom/curves/brepCurve";
import {Loop} from "./loop";
import Vector, {UnitVector} from "math/vector";
import {Tessellation1D} from "engine/tessellation";
import _ from 'lodash';
import {Point} from 'geom/point';
import { BREPValidator } from 'brep/brep-validator';
export class Edge extends TopoObject {
curve: BrepCurve;
halfEdge1: HalfEdge;
halfEdge2: HalfEdge;
constructor(curve: BrepCurve, a: Vertex, b: Vertex) {
super();
this.curve = curve;
this.halfEdge1 = new HalfEdge(this, false, a, b);
this.halfEdge2 = new HalfEdge(this, true, b, a);
}
static fromCurve(curve: BrepCurve): Edge {
const a = new Vertex(curve.startPoint());
const b = new Vertex(curve.endPoint());
return new Edge(curve, a, b);
}
invert(): void {
const t = this.halfEdge1;
this.halfEdge1 = this.halfEdge2;
this.halfEdge2 = t;
this.halfEdge1.inverted = false;
this.halfEdge2.inverted = true;
this.curve = this.curve.invert();
}
clone(): Edge {
const clone = new Edge(this.curve, this.halfEdge1.vertexA, this.halfEdge1.vertexB);
Object.assign(clone.data, this.data);
Object.assign(clone.halfEdge1.data, this.halfEdge1.data);
Object.assign(clone.halfEdge2.data, this.halfEdge2.data);
return clone;
}
splitByPoint(pt: Vector) {
const [c1, c2] = this.curve.split(pt);
const e1 = Edge.fromCurve(c1);
const e2 = Edge.fromCurve(c2);
this.halfEdge1.insertAfter(e1.halfEdge1);
e1.halfEdge1.insertAfter(e2.halfEdge1);
let anchorIndex = this.halfEdge2.loop.halfEdges.indexOf(this.halfEdge2) - 1;
if (anchorIndex == -1) {
anchorIndex = this.halfEdge2.loop.halfEdges.length - 1;
}
const anchor = this.halfEdge2.loop.halfEdges[anchorIndex];
anchor.insertAfter(e2.halfEdge2);
e2.halfEdge2.insertAfter(e1.halfEdge2);
this.halfEdge2.delete();
this.halfEdge1.delete();
// const errors = BREPValidator.validate(this.halfEdge1.loop.face.shell);
// console.log(errors);
}
getHalfEdge(predicate: (halfEdge: HalfEdge) => boolean): HalfEdge {
if (predicate(this.halfEdge1)) {
return this.halfEdge1;
} else if (predicate(this.halfEdge2)) {
return this.halfEdge2;
} else {
return null;
}
}
}
export class HalfEdge extends TopoObject {
edge: Edge;
inverted: boolean;
vertexA: Vertex;
vertexB: Vertex;
loop: Loop;
next: HalfEdge;
prev: HalfEdge;
constructor(edge: Edge, inverted: boolean, a: Vertex, b: Vertex) {
super();
this.edge = edge;
this.inverted = inverted;
this.vertexA = a;
this.vertexB = b;
this.loop = null;
this.next = null;
this.prev = null;
}
twin(): HalfEdge {
return this.edge.halfEdge1 === this ? this.edge.halfEdge2 : this.edge.halfEdge1;
}
tangentAtStart(): UnitVector {
return this.tangent(this.vertexA.point);
}
tangentAtEnd(): UnitVector {
return this.tangent(this.vertexB.point);
}
tangent(point: Vector): UnitVector {
const tangent = this.edge.curve.tangentAtPoint(point)._normalize();
if (this.inverted) {
tangent._negate();
}
return tangent;
}
tessellate(tessTol?: number, scale?: number): Tessellation1D<Vector> {
let res = this.edge.curve.tessellate(tessTol, scale);
if (this.inverted) {
res = res.slice().reverse();
}
return res;
}
insertAfter(he: HalfEdge) {
he.loop = this.loop;
const next = this.next;
this.next = he;
he.next = next;
next.prev = he;
he.prev = this;
const index = this.loop.halfEdges.indexOf(this);
this.loop.halfEdges.splice(index + 1, 0, he);
}
delete() {
const next = this.next;
const prev = this.prev;
prev.next = next;
next.prev = prev;
_.pull(this.loop.halfEdges, this);
}
replace(he: HalfEdge) {
if (this.edge.halfEdge1 === this) {
this.edge.halfEdge1 = he;
} else {
this.edge.halfEdge2 = he;
}
he.edge = this.edge;
he.loop = this.loop;
he.prev = this.prev;
he.prev.next = he;
he.next = this.next;
he.next.prev = he;
}
}