256 lines
7.4 KiB
TypeScript
256 lines
7.4 KiB
TypeScript
import type { PolylineProps } from 'cadapi'
|
||
import { Circle, Polyline, Polyline2Points } from 'cadapi'
|
||
import { EndType, JoinType } from 'js-angusj-clipper/web'
|
||
import type { Box3, Vector3 } from 'three'
|
||
import { Vector2 } from 'three'
|
||
import { clipperCpp } from '../ClipperCpp'
|
||
import type { Point } from '../Point'
|
||
import { Path, PathScale } from '../core/Path'
|
||
import type { IOffset } from './Simplify2'
|
||
import { SimplifyDouglasPeucker } from './Simplify2'
|
||
|
||
/** 内外接多边形 */
|
||
export function Circle2Points(circle: Circle, knifRadius: number, splitSize = 10, outside = false): Point[] {
|
||
let radius = circle.Radius
|
||
const an = Math.PI * 2 / splitSize
|
||
|
||
if (outside)
|
||
radius = radius / Math.cos(an / 2) + knifRadius
|
||
else
|
||
radius -= knifRadius
|
||
|
||
const cenP = circle.Center
|
||
const pts: Vector3[] = []
|
||
for (let i = 0; i < splitSize; i++)
|
||
pts.push(polar(cenP.clone(), an * i, radius))
|
||
|
||
return pts as Point[]
|
||
}
|
||
|
||
export function Curve2Path(curve: Polyline, outside = false): Path {
|
||
if (curve.IsClockWise)
|
||
curve.Reverse()
|
||
const w = new CurveWrap(curve, 3, outside)
|
||
return new Path(outside ? w.GetOutsidePoints() : w.GetInsidePoints())
|
||
}
|
||
|
||
export class CurveWrap {
|
||
BoundingBox: Box3
|
||
|
||
Area: number
|
||
|
||
SimplyPolyline: Polyline
|
||
SimplyOffset: IOffset
|
||
Used = false
|
||
Holes: CurveWrap[] = []
|
||
|
||
Points: Point[]
|
||
|
||
constructor(public Curve: Polyline | Circle, public KnifRadius: number, public IsOutside: boolean) {
|
||
this.BoundingBox = Curve.BoundingBox
|
||
|
||
if (Curve instanceof Polyline) {
|
||
const pts = Polyline2Points(Curve, IsOutside, 0)[1]
|
||
/**
|
||
* 精简算法SimplifyDouglasPeucker 会导致轮廓变大,
|
||
* 修改成直接取点 陈雄 QQ聊天记录 23.9.18
|
||
* 23.10.9 by lrx
|
||
*/
|
||
|
||
const [spts, offset] = SimplifyDouglasPeucker(pts, KnifRadius ** 2 + KnifRadius)
|
||
if (spts.length !== pts.length && spts.length > 2) {
|
||
this.SimplyOffset = offset
|
||
|
||
this.SimplyPolyline = Path2Polyline(spts)
|
||
this.Curve = this.SimplyPolyline// 保险起见,也更新它
|
||
this.Area = this.SimplyPolyline.Area
|
||
}
|
||
else// 此处更新多段线
|
||
{ this.Curve = Path2Polyline(pts) }
|
||
this.Points = spts
|
||
|
||
// 以下修改后的
|
||
// this.Curve = Path2Polyline(pts);
|
||
// this.Points = pts;
|
||
}
|
||
|
||
if (this.Area === undefined)
|
||
this.Area = this.Curve.Area
|
||
}
|
||
|
||
ContainsCurve(curve: CurveWrap): boolean {
|
||
if (this.SimplyPolyline)
|
||
return this.SimplyPolyline.PtInCurve(curve.Curve.StartPoint)
|
||
return this.Curve.PtInCurve(curve.Curve.StartPoint)
|
||
}
|
||
|
||
GetOutsidePoints(): Point[] {
|
||
if (this.Curve instanceof Circle) {
|
||
const pts = Circle2Points(this.Curve, this.KnifRadius, 10, true)
|
||
return pts
|
||
}
|
||
else {
|
||
const pl = this.SimplyPolyline || this.Curve
|
||
let offset = this.KnifRadius
|
||
if (this.SimplyOffset)
|
||
offset += this.SimplyOffset.positiveOffset
|
||
|
||
if (offset > 0) {
|
||
let pts = pl.GetStretchPoints() as Point[]
|
||
pts = clipperCpp.lib.offsetToPaths({
|
||
delta: offset * 1e4,
|
||
offsetInputs: [{ data: PathScale(pts, 1e4), joinType: JoinType.Miter, endType: EndType.ClosedPolygon }],
|
||
})[0]
|
||
try {
|
||
PathScale(pts, 1e-4)
|
||
}
|
||
catch {
|
||
console.log('err')
|
||
}
|
||
return pts
|
||
}
|
||
else { return this.Points }
|
||
}
|
||
}
|
||
|
||
GetInsidePoints(): Point[] {
|
||
return this.GetInsidePoints2(this.KnifRadius)
|
||
}
|
||
|
||
GetInsidePoints2(d: number): Point[] {
|
||
if (this.Curve instanceof Circle) {
|
||
const pts = Circle2Points(this.Curve, d, 10, false)
|
||
return pts
|
||
}
|
||
else {
|
||
const pl = this.SimplyPolyline || this.Curve
|
||
let offset = -d
|
||
if (this.SimplyOffset)
|
||
offset += this.SimplyOffset.negativeOffset
|
||
|
||
if (offset < -0.01) {
|
||
const pls = pl.GetOffsetCurves(offset)
|
||
if (pls.length)
|
||
return pls[0].GetStretchPoints()
|
||
}
|
||
else { return this.Points }
|
||
}
|
||
}
|
||
|
||
/** 引入Polyline 已经含刀半径, 获得精简后的点阵 */
|
||
GetOrgPoints(outside = true): Point[] {
|
||
if (this.Curve instanceof Circle) {
|
||
const pts = Circle2Points(this.Curve, 0, 10, outside)
|
||
return pts
|
||
}
|
||
else {
|
||
const pl = this.SimplyPolyline || this.Curve
|
||
let offset = 0
|
||
if (this.SimplyOffset)
|
||
offset += this.SimplyOffset.positiveOffset
|
||
|
||
if (offset > 0) {
|
||
let pts = pl.GetStretchPoints() as Point[]
|
||
pts = clipperCpp.lib.offsetToPaths({
|
||
delta: offset * 1e4,
|
||
offsetInputs: [{ data: PathScale(pts, 1e4), joinType: JoinType.Miter, endType: EndType.ClosedPolygon }],
|
||
})[0]
|
||
try {
|
||
PathScale(pts, 1e-4)
|
||
}
|
||
catch {
|
||
console.log('err')
|
||
}
|
||
return pts
|
||
}
|
||
else {
|
||
return this.Points
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/** 多段线 转点整 已弃用,整合到CAD api 23.11.2 */
|
||
// export function Polylin2Points(pl: Polyline, outside: boolean, knifRadius: number): [Polyline, Point[]]
|
||
// {
|
||
// let pts: Point[] = [];
|
||
|
||
// if (!outside) knifRadius = -knifRadius;
|
||
// if (pl.IsClockWise) pl.Reverse();
|
||
// for (let i = 0; i < pl.EndParam; i++)
|
||
// {
|
||
// pts.push(pl.GetPointAtParam(i));
|
||
|
||
// let bul = pl.GetBulgeAt(i);
|
||
// if (bul !== 0)
|
||
// {
|
||
// let arc = pl.GetCurveAtIndex(i) as Arc;
|
||
|
||
// let allAngle = arc.AllAngle;
|
||
// let arcLength = arc.Length;
|
||
|
||
// // let splitCount = Math.round(allAngle / 0.4);
|
||
// // if (arcLength < 300)
|
||
// // splitCount = 2;
|
||
// // else
|
||
// // splitCount = Math.max(arcLength / 200, splitCount,2);
|
||
|
||
// let minCount = Math.floor(allAngle * 4 / Math.PI);
|
||
// let splitCount = Math.round(allAngle / 0.4);
|
||
// if (arcLength < 300)
|
||
// splitCount = Math.max(2, minCount);
|
||
// else
|
||
// splitCount = Math.max(Math.floor(arcLength / 200), splitCount,2, minCount);
|
||
|
||
// let radius = arc.Radius;
|
||
// if (outside === bul > 0)
|
||
// radius = radius / Math.cos(allAngle / (splitCount * 2));
|
||
|
||
// let cp = arc.Center;
|
||
// for (let j = outside ? 0.5 : 0; j < splitCount; j++)
|
||
// {
|
||
// let a = arc.GetAngleAtParam(j * (1 / splitCount));
|
||
// let p = polar(cp.clone(), a, radius);
|
||
// pts.push(p);
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
// if (knifRadius !== 0)
|
||
// {
|
||
// pts = clipperCpp.lib.offsetToPaths({
|
||
// delta: knifRadius * 1e4,
|
||
// offsetInputs: [{ data: PathScale(pts, 1e4), joinType: JoinType.Miter, endType: EndType.ClosedPolygon }]
|
||
// })[0];
|
||
// PathScale(pts, 1e-4);
|
||
// }
|
||
// return [pl, pts];
|
||
// }
|
||
|
||
export function Path2Polyline(path: Point[]): Polyline {
|
||
const pl = new Polyline()
|
||
pl.LineData = path.map((p) => {
|
||
return { pt: new Vector2(p.x, p.y), bul: 0 }
|
||
})
|
||
pl.CloseMark = true
|
||
return pl
|
||
}
|
||
|
||
export function Points2Polyline(pts: any[]): Polyline {
|
||
const lined: PolylineProps[] = []
|
||
const count = pts.length
|
||
for (let i = 0; i < count; i++) {
|
||
const p0 = pts[i]
|
||
lined.push({ pt: new Vector2(p0.x, p0.y), bul: p0.bul })
|
||
}
|
||
const pls = new Polyline(lined)
|
||
pls.CloseMark = true
|
||
return pls
|
||
}
|
||
|
||
export function polar<T extends Vector2 | Vector3>(v: T, an: number, dis: number): T {
|
||
v.x += Math.cos(an) * dis
|
||
v.y += Math.sin(an) * dis
|
||
return v
|
||
}
|