xtorcga
Version:
Xtor Compute Geometry Algorithm Libary 计算几何算法库
339 lines (285 loc) • 10.7 kB
text/typescript
/*
* @Description :
* @Author : 赵耀圣
* @Q群 : 632839661
* @Date : 2020-12-10 15:01:42
* @LastEditTime : 2021-03-12 10:13:55
* @FilePath : \cga.js\src\struct\3d\Plane.ts
*/
import { Vec3 } from '../../math/Vec3';
import { approximateEqual, sign, delta4 } from "../../math/Math";
import { Segment } from './Segment';
import { Orientation } from '../data/type';
import { Triangle } from './Triangle';
import { IGeometry } from '../../render/geometry';
import { ISplitResult } from '../../alg/split';
import { Line } from './Line';
import { Polyline } from './Polyline';
import { Path } from './Path';
import { indexable } from '../../render/mesh';
class Plane {
normal: Vec3;
w: number;
origin: Vec3;
constructor(normal = Vec3.UnitZ, w = 0) {
this.normal = normal;
this.w = w;
this.origin = this.normal.clone().multiplyScalar(w)
// this.w = this.normal.dot(this.origin)
}
static setFromPointNormal(p: Vec3, normal: Vec3) {
const plane = new Plane();
plane.setFromPointNormal(p, normal)
return plane;
}
setFromPointNormal(p: Vec3, normal: Vec3) {
this.normal = normal;
this.w = p.dot(normal)
}
set(normal: Vec3, w: number) {
this.normal = normal;
this.w = w;
}
setComponents(x: number, y: number, z: number, w: number) {
this.normal.set(x, y, z);
this.w = w;
return this;
}
normalize() {
const inverseNormalLength = 1.0 / this.normal.length();
this.normal.multiplyScalar(inverseNormalLength);
this.w *= inverseNormalLength;
return this;
}
setFromThreePoint(p0: Vec3, p1: Vec3, p2: Vec3) {
this.normal = p1.clone().sub(p0).cross(p2.clone().sub(p0)).normalize();
this.w = p0.dot(this.normal);
}
negate() {
this.normal.negate();
this.w = -this.w;
}
/**
* 判断一个点在平面的正面或者反面
* @param {Vec3} point
* @returns {Number} -1 or 1 or z
*/
frontback(point: any) {
let value = this.normal.dot(point);
if (approximateEqual(value, 0))
return 0;
return sign(this.normal.dot(point));
}
//---Distance-------------------------------------------------------------------------------
distancePoint(point: any) {
return this.normal.dot(point) - this.w;
}
distanceRay(ray: any) {
}
distanceLine(line: any) {
}
distanceSegment(segment: any) {
}
distancePlane(plane: any) {
}
//---Intersect-----------------------------------
/**
* 只返回交点
* Lw --Lightweight
* @param {Segment|Array<Vec3> segment
*/
intersectSegmentLw(segment: Segment | Vec3[]) {
let orientation0 = this.orientationPoint(segment[0]);
let orientation1 = this.orientationPoint(segment[0]);
let orientation = orientation0 | orientation1;
if (orientation === Orientation.Common)
return segment;
if (orientation === Orientation.Intersect) {
var dist = segment[0].clone().sub(this.origin).dot(this.normal);
var intersectPoint = this.normal.clone().multiplyScalar(dist).add(segment[0]);
return intersectPoint;
}
return null;
}
intersectLine(line: Line, result?: Vec3) {
return line.intersectPlane(this, result)
}
/**
* 切割线段 代码完成 等待测试
* @param {Segment} segment
* @returns {
* positive: [], //正面点
* negative: [],// 反面位置点
* common: [], 在平面上的点
* orientation: Orientation.None 线段的总体位置
* };
*/
splitSegment(segment: Segment | Vec3[]) {
const result: any = {
positive: [],
negative: [],
common: [],
orientation: Orientation.None
};
let orientation0 = this.orientationPoint(segment[0]);
let orientation1 = this.orientationPoint(segment[1]);
let orientation = orientation0 | orientation1;
result.orientation = orientation;
if (orientation0 === Orientation.Positive)
result.positive.push(segment[0]);
else if (orientation0 === Orientation.Negative)
result.negative.push(segment[0]);
else
result.common.push(segment[0]);
if (orientation1 === Orientation.Positive)
result.positive.push(segment[1]);
else if (orientation1 === Orientation.Negative)
result.negative.push(segment[1]);
else
result.common.push(segment[1]);
if (orientation === Orientation.Intersect) {
var dist = segment[0].clone().sub(this.origin).dot(this.normal);
var intersectPoint = this.normal.clone().multiplyScalar(dist).add(segment[0]);
result.positive.push(intersectPoint);
result.negative.push(intersectPoint);
result.intersectPoint = intersectPoint;
}
return result;
}
/**
* 切割三角形 编码完成 等待测试
* @param {Triangle} triangle
*/
splitTriangle(triangle: Triangle | Vec3[]): ISplitResult {
const result: ISplitResult = {
negative: [],
positive: [], common: [], orientation: Orientation.None
};
var scope = this;
const orientations: Orientation[] = (triangle as Vec3[]).map((p: Vec3) => scope.orientationPoint(p));
var consis = 0;
var pos = 0;
var neg = 0;
for (let i = 0; i < triangle.length; i++) {
var orientation = orientations[i];
if (orientation === Orientation.Positive)
pos++;
else if (orientation === Orientation.Negative)
neg++;
else
consis++
}
var hasConsis = consis > 0;
var hasFront = pos > 0;
var hasBack = neg > 0;
const negTris = result.positive,
posTris = result.negative;
if (hasBack && !hasFront) {
//反面
result.orientation = Orientation.Negative;
result.negative.push(...triangle)
} else if (!hasBack && hasFront) {
//正面
result.orientation = Orientation.Positive;
result.positive.push(...triangle)
} else if (hasFront && hasBack) {
//相交 共面点最多只有一个
result.orientation = Orientation.Intersect;
for (var i = 0; i < 3; i++) {
if (orientations[i] || orientations[(i + 1) % 3] === Orientation.Intersect) {
if (orientations[i] === Orientation.Positive) {
posTris.push(triangle[i]);
} else if (orientations[i] == Orientation.Negative) {
negTris.push(triangle[i]);
} else {
negTris.push(triangle[i]);
posTris.push(triangle[i]);
result.common.push(triangle[i]);
}
var intersectPoint = this.intersectSegmentLw([triangle[i],
triangle[(i + 1) % 3]]);
if (intersectPoint) {
if (!Array.isArray(intersectPoint))
result.common.push(intersectPoint);
}
}
}
} else {
// 三点共面
result.orientation = Orientation.Common;
result.common.push(...triangle)
}
return result;
}
/**
* 平面切割线段
* @param polyVS
*/
splitPolyVS(polyVS: Vec3[]) {
polyVS = [...polyVS];
indexable(polyVS);
let jd0 = -1;//找出第一个交点
let jdp0: any;//找出第一个交点
let lastOriention = this.orientationPoint(polyVS[0]);
for (let i = 0; i < polyVS.length - 1; i++) {
const v = polyVS[i];
const oriention = this.orientationPoint(v);
if (oriention === Orientation.Common || lastOriention !== Orientation.None && lastOriention !== oriention) {
jd0 = i;
jdp0 = v.clone();
lastOriention = oriention;
break;
}
lastOriention = oriention;
//TODO
}
}
//---orientation------------------------------
/**
* 点在平面的位置判断
* @param {Point} point
* @returns {Orientation} 方位
*/
orientationPoint(point: Vec3): Orientation {
let signDistance = this.normal.clone().dot(point) - this.w;
if (Math.abs(signDistance) < delta4)
return Orientation.Intersect;
else if (signDistance < 0)
return Orientation.Negative;
else /* if (signDistance > 0) */
return Orientation.Positive;
}
//静态API
/**
* @description : 平面分割几何体
* @param {Plane} plane
* @param {IGeometry} geometry
* @return {IGeometry[]} 返回多个几何体
* @example :
*/
static splitGeometry(plane: Plane, geometry: IGeometry) {
var indices = geometry.index!;
var positions = geometry.position!;
for (let i = 0; i < indices.length; i += 3) {
const index_a = indices[i * 3] * 3;
const index_b = indices[i * 3 + 1] * 3;
const index_c = indices[i * 3 + 2] * 3;
_v1.set(positions[index_a], positions[index_a + 1], positions[index_a + 2]);
_v2.set(positions[index_b], positions[index_b + 1], positions[index_b + 2]);
_v3.set(positions[index_c], positions[index_c + 1], positions[index_c + 2]);
var data: ISplitResult = plane.splitTriangle(_tris);
if (data.common.length > 0) {
//共面处理
}
if (data.negative.length > 0) {
}
if (data.positive.length > 0) {
}
}
}
}
const _v1 = new Vec3();
const _v2 = new Vec3();
const _v3 = new Vec3();
const _tris = [_v1, _v2, _v3];
export { Plane }