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 { 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; } }