mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-08 01:13:27 +01:00
168 lines
4.1 KiB
TypeScript
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;
|
|
}
|
|
}
|