From df5ed02401f8a370ee3a0587f860738ff8fdafb4 Mon Sep 17 00:00:00 2001 From: ChenX Date: Sun, 27 Jan 2019 15:10:56 +0800 Subject: [PATCH] =?UTF-8?q?fix=20#IR8AN=20=E7=9B=B4=E7=BA=BFJoin=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E6=9B=B4=E9=AB=98=E7=9A=84=E5=AE=B9=E5=B7=AE=E5=80=BC?= =?UTF-8?q?,=E8=BD=AE=E5=BB=93=E5=B8=83=E5=B0=94=E7=A7=BB=E9=99=A40?= =?UTF-8?q?=E9=9D=A2=E7=A7=AF=E7=9A=84=E8=BD=AE=E5=BB=93,?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __test__/Booloperate/bool2.test.ts | 16 ++++++++ __test__/Polyline/join.test.ts | 9 +++++ src/DatabaseServices/Contour.ts | 2 +- src/DatabaseServices/Line.ts | 23 ++++++++--- src/Geometry/CurveIntersection.ts | 5 +-- src/Geometry/GeUtils.ts | 64 ++++++++++++------------------ 6 files changed, 70 insertions(+), 49 deletions(-) diff --git a/__test__/Booloperate/bool2.test.ts b/__test__/Booloperate/bool2.test.ts index d96f93d6b..9e23a5af1 100644 --- a/__test__/Booloperate/bool2.test.ts +++ b/__test__/Booloperate/bool2.test.ts @@ -17,3 +17,19 @@ test("#IRFL2面域消失", () => reg.BooleanOper(regs[1], BoolOpeartionType.Intersection); expect(reg.ShapeManager.ShapeList.length).toBe(1); }); + +test('#IR8AN交集错误', () => +{ + let data = + [2, "Region", 2, 1, 104, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 111.34512004857766, 13.95089482226993, 0, 1], 0, 1, 1, 1, 1, "Polyline", 2, 1, 0, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 111.34512004857766, 13.95089482226993, 0, 1], 0, 2, 4, [-97.39422719143477, -0.0000019651270122267306], 0, [674.3281411676799, -0.0000019651270122267306], 0, [674.3281421679894, 1199.9999984537135], 0, [-97.3942261911252, 1199.9999984537135], 0, true, 0, "Region", 2, 1, 105, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 13.95089285714289, 113.95089285714292, 0, 1], 0, 1, 1, 1, 1, "Polyline", 2, 1, 0, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 13.95089285714289, 113.95089285714292, 0, 1], 0, 2, 13, [314.3387826639938, -99.99999883531063], 0, [318, -100], 0, [318, 0], 0, [579.5436603415255, 67.84500522108829], 0, [530.3246820683962, 361.2662670472655], 0, [446.27460696874186, 251.38290037066872], 0, [495.4935852418712, 1057.9616385444915], 0, [151.43841386299755, 1055.5264270248226], 0, [151.43841386299755, 155.52640117119142], 0, [0, 200], 0, [0, 0], 0, [140.39432277924786, -16.215571265653125], 0, [140.3943227792479, -100], 0, true, 0] + + let regs: Region[] = LoadRegionsFromFileData(data); + expect(regs.length).toBe(2); + let reg = regs[0].Clone(); + reg.BooleanOper(regs[1], BoolOpeartionType.Subtract); + expect(reg.ShapeManager.ShapeList.length).toBe(2); + + reg = regs[0].Clone(); + reg.BooleanOper(regs[1], BoolOpeartionType.Intersection); + expect(reg.ShapeManager.ShapeList.length).toBe(1); +}); diff --git a/__test__/Polyline/join.test.ts b/__test__/Polyline/join.test.ts index 2f2023088..bde5d92f5 100644 --- a/__test__/Polyline/join.test.ts +++ b/__test__/Polyline/join.test.ts @@ -1,6 +1,7 @@ import { LoadEntityFromFileData } from "../Utils/LoadEntity.util"; import { Polyline } from "../../src/DatabaseServices/Polyline"; import { equaln } from "../../src/Geometry/GeUtils"; +import { Line } from "../../src/DatabaseServices/Line"; test('多段线与多段线Join', () => { @@ -40,3 +41,11 @@ test('多段线与多段线Join', () => expect(pl1.Join(pl2)).toBe(1); expect(equaln(pl1.Length, length)).toBeTruthy(); }); + +test('一定精度容差内的直线Join', () => +{ + let data = + [2, "Line", 2, 1, 168, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -97.39422719143477, 40.30682517259278, 0, 1], 1, 1, [140.3943227792479, -100, 0], [314.3387826639938, -99.99999883531063, 0], "Line", 2, 1, 169, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -97.39422719143477, 40.30682517259278, 0, 1], 1, 1, [314.3387826639938, -99.99999883531063, 0], [318, -100, 0]] + let lines = LoadEntityFromFileData(data) as Line[]; + expect(lines[0].Join(lines[1])).toBe(1); +}); diff --git a/src/DatabaseServices/Contour.ts b/src/DatabaseServices/Contour.ts index 5ffa2e4ad..0028d2605 100644 --- a/src/DatabaseServices/Contour.ts +++ b/src/DatabaseServices/Contour.ts @@ -145,7 +145,7 @@ export class Contour for (let r of routes) cs.push(r.curve); let c = Contour.Combine(cs, false); - if (!equalCurve(c, this.Curve) && !equalCurve(c, target.Curve)) + if (!equalCurve(c, this.Curve) && !equalCurve(c, target.Curve) && c.Area > 1e-3) cuGroups.push(cs); } } diff --git a/src/DatabaseServices/Line.ts b/src/DatabaseServices/Line.ts index 9a9451475..0eec8160f 100644 --- a/src/DatabaseServices/Line.ts +++ b/src/DatabaseServices/Line.ts @@ -227,9 +227,9 @@ export class Line extends Curve { let sp = this.StartPoint; let ep = this.EndPoint; - if (equalv3(pt, sp, 1e-12)) + if (equalv3(pt, sp, 1e-8)) return { closestPt: sp, param: 0 }; - else if (equalv3(pt, ep, 1e-12)) + else if (equalv3(pt, ep, 1e-8)) return { closestPt: ep, param: 1 }; let direction = this.GetFistDeriv(0); @@ -238,7 +238,7 @@ export class Line extends Curve if (length === 0) { let param = NaN; - if (equalv3(pt, this.StartPoint, 1e-7)) + if (equalv3(pt, this.StartPoint, 1e-6)) param = 0; return { closestPt: sp, param: param }; } @@ -292,11 +292,22 @@ export class Line extends Curve { if (cu instanceof Line) { - //如果不平行 - if (!isParallelTo(this.GetFistDeriv(0), cu.GetFistDeriv(0))) + //平行 + if (!isParallelTo(this.GetFistDeriv(0).normalize(), cu.GetFistDeriv(0).normalize())) return Status.False; - let [param1, param2] = arraySortByNumber([this.GetParamAtPoint(cu.StartPoint), this.GetParamAtPoint(cu.EndPoint)]); + let sp = cu.StartPoint; + let { closestPt: cp1, param: param1 } = this.GetClosestAtPoint(sp, true); + if (!equalv3(sp, cp1, 1e-5))//点在曲线上,允许较低的精度 + return; + + let ep = cu.EndPoint; + let { closestPt: cp2, param: param2 } = this.GetClosestAtPoint(ep, true); + if (!equalv3(ep, cp2, 1e-5)) + return; + + if (param1 > param2) + [param1, param2] = [param2, param1]; if (allowGap || Math.max(0, param1) < Math.min(1, param2) + 1e-5) { diff --git a/src/Geometry/CurveIntersection.ts b/src/Geometry/CurveIntersection.ts index 8d6e2ddd5..16b1417bb 100644 --- a/src/Geometry/CurveIntersection.ts +++ b/src/Geometry/CurveIntersection.ts @@ -1,7 +1,6 @@ import { Box3, Vector3 } from "three"; import { Curve } from "../DatabaseServices/Curve"; import { IntersectOption } from "../GraphicsSystem/IntersectWith"; -import { greater } from "./GeUtils"; /** * @@ -46,9 +45,9 @@ export class CurveIntersection //过滤掉不需要计算的曲线 let c2b = this.boxMap.get(c2); - if (greater(c2b.min.x, c1b.max.x, 1e-6)) + if (c2b.min.x - c1b.max.x > 1e-6) break; - if (greater(c2b.min.y, c1b.max.y, 1e-6)) + if (c2b.min.y - c1b.max.y > 1e-6) continue; let pts = c1.IntersectWith(c2, IntersectOption.OnBothOperands); diff --git a/src/Geometry/GeUtils.ts b/src/Geometry/GeUtils.ts index 61f4254ca..3145ae9b0 100644 --- a/src/Geometry/GeUtils.ts +++ b/src/Geometry/GeUtils.ts @@ -1,7 +1,6 @@ -import * as THREE from 'three'; -import { Box3, BufferGeometry, Geometry, Matrix4, Vector, Vector2, Vector3, BufferAttribute } from 'three'; -import { Matrix2 } from './Matrix2'; +import { Box3, BufferGeometry, Geometry, Matrix4, Object3D, Vector, Vector2, Vector3, Line, Mesh } from 'three'; import { ToFixed } from '../Common/Utils'; +import { Matrix2 } from './Matrix2'; export const cZeroVec = new Vector3(); export const cXAxis = new Vector3(1, 0, 0); @@ -35,15 +34,6 @@ export function equalv2(v1: Vector2, v2: Vector2, fuzz = 1e-8) return equaln(v1.x, v2.x, fuzz) && equaln(v1.y, v2.y, fuzz); } -export function less(v1, v2, fuzz = 0) -{ - return v1 - v2 < fuzz; -} -export function greater(v1, v2, fuzz = 0) -{ - return v1 - v2 > fuzz; -} - /** * 按照极坐标的方式移动一个点 * @@ -72,21 +62,21 @@ export function angle(v: Vector3 | Vector2) /** * 求两个向量的夹角,顺时针为负,逆时针为正 * - * @param {THREE.Vector3} v1 - * @param {THREE.Vector3} v2 - * @param {THREE.Vector3} [ref] 参考向量,如果为世界坐标系则为0,0,1 + * @param {Vector3} v1 + * @param {Vector3} v2 + * @param {Vector3} [ref] 参考向量,如果为世界坐标系则为0,0,1 * @returns */ -export function angleTo(v1: THREE.Vector3, v2: THREE.Vector3, ref: THREE.Vector3 = new THREE.Vector3(0, 0, 1)) +export function angleTo(v1: Vector3, v2: Vector3, ref: Vector3 = new Vector3(0, 0, 1)) { if (!ref.equals(new Vector3(0, 0, 1))) { //任意轴坐标系. 使用相机的构造矩阵. ref.multiplyScalar(-1); let up = getLoocAtUpVec(ref); - let refOcs = new THREE.Matrix4(); + let refOcs = new Matrix4(); refOcs.lookAt(cZeroVec, ref, up); - let refOcsInv = new THREE.Matrix4().getInverse(refOcs); + let refOcsInv = new Matrix4().getInverse(refOcs); v1.applyMatrix4(refOcsInv); v2.applyMatrix4(refOcsInv); v1.z = 0; @@ -98,7 +88,7 @@ export function angleTo(v1: THREE.Vector3, v2: THREE.Vector3, ref: THREE.Vector3 return cv.z === 0 ? v1.angleTo(v2) : v1.angleTo(v2) * cv.z; } -export function getLoocAtUpVec(dir: THREE.Vector3): THREE.Vector3 +export function getLoocAtUpVec(dir: Vector3): Vector3 { if (dir.equals(cZeroVec)) { @@ -107,53 +97,49 @@ export function getLoocAtUpVec(dir: THREE.Vector3): THREE.Vector3 let norm = dir.clone().normalize(); if (norm.equals(cZAxis)) { - return new THREE.Vector3(0, 1, 0); + return new Vector3(0, 1, 0); } else if (norm.equals(cZAxis.clone().negate())) { - return new THREE.Vector3(0, -1, 0); + return new Vector3(0, -1, 0); } else { - let xv: THREE.Vector3 = new THREE.Vector3(); + let xv: Vector3 = new Vector3(); xv.crossVectors(cZAxis, norm); - let up = new THREE.Vector3(); + let up = new Vector3(); up.crossVectors(norm, xv); return up; } } -export function createLookAtMat4(dir: THREE.Vector3): THREE.Matrix4 +export function createLookAtMat4(dir: Vector3): Matrix4 { let up = getLoocAtUpVec(dir); - let mat = new THREE.Matrix4(); + let mat = new Matrix4(); mat.lookAt(cZeroVec, dir, up); return mat; } /** - * 判断2个向量是不是平行. - * @export - * @param {THREE.Vector3} v1 - * @param {THREE.Vector3} v2 - * @returns + * 判断2个向量是不是平行,尽量传入单位向量,才能保证计算精度 */ -export function isParallelTo(v1: THREE.Vector3, v2: THREE.Vector3): boolean +export function isParallelTo(v1: Vector3, v2: Vector3): boolean { - return v1.clone().cross(v2).lengthSq() < 1e-9; + return v1.clone().cross(v2).lengthSq() < 1e-8; } -export function ptToString(v: THREE.Vector3, fractionDigits: number = 3): string +export function ptToString(v: Vector3, fractionDigits: number = 3): string { return v.toArray().map(o => ToFixed(o, fractionDigits)).join(","); } -export function midPoint(v1: THREE.Vector3, v2: THREE.Vector3): THREE.Vector3 +export function midPoint(v1: Vector3, v2: Vector3): Vector3 { return v1.clone().add(v2).multiplyScalar(0.5); } -export function midPoint2(v1: THREE.Vector2, v2: THREE.Vector2): THREE.Vector2 +export function midPoint2(v1: Vector2, v2: Vector2): Vector2 { return v1.clone().add(v2).multiplyScalar(0.5); } @@ -164,7 +150,7 @@ export function midPoint2(v1: THREE.Vector2, v2: THREE.Vector2): THREE.Vector2 * @param [updateMatrix] 是否应该更新对象矩阵 * @returns box */ -export function GetBox(obj: THREE.Object3D, updateMatrix?: boolean): THREE.Box3 +export function GetBox(obj: Object3D, updateMatrix?: boolean): Box3 { let box = new Box3(); if (updateMatrix) obj.updateMatrixWorld(false); @@ -189,7 +175,7 @@ export function GetBox(obj: THREE.Object3D, updateMatrix?: boolean): THREE.Box3 return box; } -export function GetBoxArr(arr: Array): THREE.Box3 +export function GetBoxArr(arr: Array): Box3 { let box = new Box3(); for (let o of arr) @@ -257,9 +243,9 @@ export function angleTo2Pi(an: number) if (an < 0) an += Math.PI * 2 return an; } -export function updateGeometry(l: THREE.Line | THREE.Mesh, geometry: Geometry | BufferGeometry) +export function updateGeometry(l: Line | Mesh, geometry: Geometry | BufferGeometry) { - let geo = l.geometry as THREE.Geometry; + let geo = l.geometry as Geometry; geo.dispose(); l.geometry = geometry; geometry.computeBoundingSphere();