xtorcga
Version:
Xtor Compute Geometry Algorithm Libary 计算几何算法库
177 lines (144 loc) • 5.07 kB
text/typescript
import { Vec3 } from "src/math/HVec3";
import Decimal from "decimal.js"
const EPSILON = new Decimal(1e-15);
enum Side {
COPLANAR = 0,
FRONT = 1,
BACK = 2,
SPANNING = 3,
}
/*
* @Description : 高精度多边形
* @Author : 赵耀圣
* @QQ : 549184003
* @Date : 2021-08-03 11:11:59
* @LastEditTime : 2021-08-03 15:49:25
* @FilePath : \cga.js\src\csg\polygon.ts
*/
export class Polygon {
w: Decimal = new Decimal(0);
normal: Vec3 = new Vec3;
vertices: Vec3[];
constructor(vertices: Vec3[] = []) {
this.vertices = vertices
this.vertices = vertices;
if (vertices.length > 0) {
this.calcPlane();
}
}
calcPlane() {
var a = this.vertices[0],
b = this.vertices[1],
c = this.vertices[2];
this.normal = b.clone().sub(a).cross(c.clone().sub(a)).normalize();
this.w = this.normal.clone().dot(a);
return this;
}
flip() {
this.normal.multiplyScalar(-1);
this.w.mul(-1);
this.vertices.reverse();
return this;
}
/**
* @description : 判断点与当前多边形所在的平面的位置关系
* @param {Vec3} vertex
* @return {*}
* @example :
*/
classifyVertex(vertex: Vec3) {
var side_value: Decimal = this.normal.dot(vertex).sub(this.w);
if (side_value.lessThan(-EPSILON)) {
return Side.BACK;
} else if (side_value > EPSILON) {
return Side.FRONT;
} else {
return Side.COPLANAR;
}
}
/**
* @description : 判断另一个多边形与当前多边形的位置关系
* @param {Polygon} polygon
* @return {*}
* @example :
*/
classifySide(polygon: Polygon) {
var vertex, classification,
num_positive = 0,
num_negative = 0;
const vslen = polygon.vertices.length;
let i;
for (i = 0; i < vslen; i++) {
vertex = polygon.vertices[i];
classification = this.classifyVertex(vertex);
if (classification === Side.FRONT) {
num_positive++;
} else if (classification === Side.BACK) {
num_negative++;
}
}
if (num_positive === vslen && num_negative === 0) {
return Side.FRONT;
} else if (num_positive === 0 && num_negative === vslen) {
return Side.BACK;
} else if (num_positive > 0 && num_negative > 0) {
return Side.SPANNING;
} else {
return Side.COPLANAR;
}
}
/**
* @description : 切割多边形
* @param {Polygon} polygon
* @param {Polygon} coplanar_front
* @param {Polygon} coplanar_back
* @param {Polygon} front
* @param {Polygon} back
* @return {*}
* @example :
*/
splitPolygon(polygon: Polygon, coplanar_front: Polygon[] = [], coplanar_back: Polygon[] = [], front: Polygon[] = [], back: Polygon[] = []) {
var classification = this.classifySide(polygon);
if (classification === Side.COPLANAR) {
(this.normal.dot(polygon.normal).greaterThan(0) ? coplanar_front : coplanar_back).push(polygon);
} else if (classification === Side.FRONT) {
front.push(polygon);
} else if (classification === Side.BACK) {
back.push(polygon);
} else {
var i: number, j: number,
ti: Side, tj: Side,
vi: Vec3, vj: Vec3,
t: Decimal, v: Vec3,
f: Vec3[] = [],
b: Vec3[] = [];
let vslen;
for (i = 0, vslen = polygon.vertices.length; i < vslen; i++) {
j = (i + 1) % vslen;
vi = polygon.vertices[i];
vj = polygon.vertices[j];
ti = this.classifyVertex(vi);
tj = this.classifyVertex(vj);
if (ti != Side.BACK) f.push(vi);
if (ti != Side.FRONT) b.push(vi);
if ((ti | tj) === Side.SPANNING) {
t = (this.w.sub(this.normal.dot(vi))).div(this.normal.dot(vj.clone().sub(vi)));
v = vi.lerp(vj, t);
f.push(v);
b.push(v);
}
}
if (f.length >= 3) front.push(new Polygon(f).calcPlane());
if (b.length >= 3) back.push(new Polygon(b).calcPlane());
}
}
clone(): Polygon {
var i, vslen = this.vertices.length
const polygon = new Polygon();
for (i = 0; i < vslen; i++) {
polygon.vertices.push(this.vertices[i].clone());
}
polygon.calcPlane();
return polygon;
}
}