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
|
|||
|
}
|