370 lines
10 KiB
TypeScript
370 lines
10 KiB
TypeScript
import type { PolylineProps } from 'cadapi'
|
||
import { CADFiler, Circle, Polyline, Status, VKnifToolPath, isTargetCurInOrOnSourceCur } from 'cadapi'
|
||
import type { Box3 } from 'three'
|
||
import { Vector2, Vector3 } from 'three'
|
||
import { arrayRemoveDuplicateBySort } from '../ArrayExt'
|
||
import type { Curve2d } from '../base/CAD'
|
||
import { Arc2d, Point2d, copyTextToClipboard } from '../base/CAD'
|
||
import { CurveWrap } from './Curves2Parts'
|
||
|
||
// import type { Curve2d } from '../../common/base/CAD'
|
||
|
||
export class PolylineHelper {
|
||
/** 创建闭合多段线 */
|
||
static create(pts: any[], closeMark = false): Polyline {
|
||
let lined: PolylineProps[] = []
|
||
let count = pts.length
|
||
for (let i = 0; i < count; i++) {
|
||
let p0 = pts[i]
|
||
|
||
lined.push({ pt: new Vector2(p0.x, p0.y), bul: p0.bul || 0 })
|
||
}
|
||
let pls = new Polyline(lined)
|
||
pls.CloseMark = closeMark
|
||
return pls
|
||
}
|
||
|
||
static createByCurve2d(curs: Curve2d[], closeMark = true): Polyline {
|
||
let lined: PolylineProps[] = []
|
||
for (let cur of curs) {
|
||
let x = cur.StartPoint.m_X
|
||
let y = cur.StartPoint.m_Y
|
||
let bul = 0
|
||
if (cur instanceof Arc2d)
|
||
bul = cur.Bul || 0
|
||
lined.push({ pt: new Vector2(x, y), bul })
|
||
}
|
||
let pls = new Polyline(lined)
|
||
pls.CloseMark = true
|
||
return pls
|
||
}
|
||
|
||
static createByPts(pts: any[], buls: number[], closeMark = false): Polyline {
|
||
let plps: PolylineProps[] = []
|
||
let count = pts.length
|
||
for (let i = 0; i < count; i++) {
|
||
let p0 = pts[i]
|
||
plps.push({ pt: new Vector2(p0.x, p0.y), bul: buls[i] })
|
||
}
|
||
let pls = new Polyline(plps)
|
||
pls.CloseMark = closeMark
|
||
return pls
|
||
}
|
||
|
||
static getSimplePoints(pts: any[], offset: number): any[] {
|
||
let pl = PolylineHelper.create(pts)
|
||
pl.CloseMark = true
|
||
let cureW = new CurveWrap(pl, offset, true)
|
||
let pts2 = cureW.GetOutsidePoints()
|
||
arrayRemoveDuplicateBySort(pts2, (p1, p2) => (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) < 1e-2)
|
||
return pts2
|
||
}
|
||
|
||
static createByWidthLength(w: number, l: number): Polyline {
|
||
let plps: PolylineProps[] = []
|
||
plps.push({ pt: new Vector2(0, 0), bul: 0 })
|
||
plps.push({ pt: new Vector2(w, 0), bul: 0 })
|
||
plps.push({ pt: new Vector2(w, l), bul: 0 })
|
||
plps.push({ pt: new Vector2(0, l), bul: 0 })
|
||
let pls = new Polyline(plps)
|
||
pls.CloseMark = true
|
||
return pls
|
||
}
|
||
|
||
/** 多段线,添加位置移动 返回新多段线 */
|
||
static moveTo(pl: Polyline, x: number, y: number): Polyline {
|
||
let lindData = pl.LineData
|
||
let pos = pl.Position
|
||
|
||
let newPts: PolylineProps[] = []
|
||
for (let p of lindData) {
|
||
let nx = p.pt.x + pos.x + x
|
||
let ny = p.pt.y + pos.y + y
|
||
if (ny < 7.9) {
|
||
// console.log('修边小于 7.9????', ny)
|
||
}
|
||
let bul = p.bul
|
||
newPts.push({ pt: new Vector2(nx, ny), bul })
|
||
}
|
||
let npl = new Polyline(newPts)
|
||
npl.CloseMark = pl.CloseMark
|
||
return npl
|
||
}
|
||
|
||
/** 重设 多段线的几点 */
|
||
static resetPosition(pl: Polyline): Polyline {
|
||
let lindData = pl.LineData
|
||
let pos = pl.Position
|
||
|
||
let newPts: PolylineProps[] = []
|
||
for (let p of lindData) {
|
||
let nx = p.pt.x + pos.x
|
||
let ny = p.pt.y + pos.y
|
||
let bul = p.bul
|
||
newPts.push({ pt: new Vector2(nx, ny), bul })
|
||
}
|
||
let npl = new Polyline(newPts)
|
||
npl.CloseMark = pl.CloseMark
|
||
return npl
|
||
}
|
||
|
||
/** 获得v型刀走刀路径 */o
|
||
static getVModelPoints(pl: Polyline, depth: number, ang: number): any[] {
|
||
// let ang = Math.PI * (0.5 * angle) / 180 ;
|
||
let ps = []
|
||
let bx = pl.Position.x
|
||
let by = pl.Position.y
|
||
if (ang > 0.01) {
|
||
let rt = VKnifToolPath(pl, depth, ang / 2)
|
||
ps = rt.map((t) => { return { x: t.pt.x + bx, y: t.pt.y + by, z: t.pt.z, bul: t.bul, r: 0 } })
|
||
}
|
||
else {
|
||
ps = pl.LineData.map((t) => { return { x: t.pt.x + bx, y: t.pt.y + by, z: 0, bul: t.bul, r: 0 } })
|
||
}
|
||
for (let i = 0; i < ps.length; i++) {
|
||
let p = ps[i]
|
||
if (p.bul == 0)
|
||
continue
|
||
let p2 = (i == ps.length - 1 ? ps[0] : ps[i + 1])
|
||
let r = this.getArcRadius(p.x, p.y, p2.x, p2.y, p.bul)
|
||
p.r = r
|
||
}
|
||
return ps
|
||
}
|
||
|
||
static ConverPolyLin2Circle(polyline: Polyline, fuzz = 0.1): Circle | undefined {
|
||
let box = polyline.BoundingBox
|
||
let size = box.getSize(new Vector3())
|
||
if (!this.equaln(size.x, size.y, fuzz))// 盒子四方
|
||
return undefined
|
||
|
||
let circleLength = 2 * Math.PI * size.x
|
||
if (!this.equaln(circleLength, polyline.Length, fuzz * 2))
|
||
return undefined
|
||
|
||
let circleArea = Math.PI * size.x * size.x
|
||
if (!this.equaln(circleArea, polyline.Area, fuzz * 2))
|
||
return undefined
|
||
|
||
let r = size.x// 必须备份(因为我们要复用这个vector变量)
|
||
return new Circle(box.getCenter(size), r)
|
||
}
|
||
// 有问题
|
||
static getVModelPoints_offset(pl: Polyline, offset: number, depth: number, angle: number) {
|
||
let npl = offset == 0 ? pl : pl.GetOffsetCurves(offset)[0]
|
||
// if(offset != 0)
|
||
// {
|
||
// ClipboardTest.write2PolyLine(pl,npl);
|
||
// }
|
||
return PolylineHelper.getVModelPoints(npl, depth, angle)
|
||
}
|
||
|
||
static getArcRadius(x1: number, y1: number, x2: number, y2: number, bul: number): number {
|
||
let bul2 = Math.abs(bul)
|
||
let d = Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) / 2
|
||
return 0.5 * d * (1 + bul2 ** 2) / bul2
|
||
}
|
||
|
||
// 圆 转 多段线
|
||
static cicleToPolyline(c: Circle): Polyline {
|
||
let arcs = c.GetSplitCurves([0, 0.5])
|
||
let pl = Polyline.FastCombine(arcs)
|
||
return pl
|
||
}
|
||
|
||
/** 判断多段线是否 重叠 */
|
||
static isIntersect(pl1: Polyline, pl2: Polyline): boolean {
|
||
let box1 = this.getBox(pl1)
|
||
let box2 = this.getBox(pl2)
|
||
|
||
if (!box1.intersectsBox(box2))
|
||
return false // 肯定不相交
|
||
|
||
let ipts = pl1.IntersectWith(pl2, 0)
|
||
if (ipts.length === 0) {
|
||
if (pl1.Area > pl2.Area)// 缓存面积
|
||
{
|
||
if (isTargetCurInOrOnSourceCur(pl1, pl2))
|
||
return true // 包含
|
||
}
|
||
else {
|
||
if (isTargetCurInOrOnSourceCur(pl2, pl1))
|
||
return true // 包含
|
||
}
|
||
return false
|
||
}
|
||
else {
|
||
return true // 有交点 一定有交集
|
||
}
|
||
}
|
||
|
||
// 多段线 圆弧合并
|
||
static mergeCurve(pl2: Polyline): Polyline {
|
||
const curves = pl2.Explode()
|
||
arrayRemoveDuplicateBySort(curves, (c1, c2) => {
|
||
return c1.Join(c2) === Status.True
|
||
})
|
||
|
||
return Polyline.FastCombine(curves)
|
||
}
|
||
|
||
/**
|
||
* pl2 包含在pl1 内
|
||
*
|
||
*/
|
||
// static isInside(pl1:Polyline,pl2:Polyline):boolean
|
||
// {
|
||
// let box1 = this.getBox(pl1);
|
||
// let box2 = this.getBox(pl2);
|
||
// if (!box1.intersectsBox(box2)) return false; //肯定不相交
|
||
|
||
// let ipts = pl1.IntersectWith(pl2, 0);
|
||
// if (ipts.length > 0) return true; //有交点 一定有交集
|
||
// }
|
||
|
||
/**
|
||
* 两片板的干涉检查
|
||
*
|
||
* @param pl1
|
||
* @param pls1_inner
|
||
* @param pls1_model
|
||
* @param pl2
|
||
* @param pls2_inner
|
||
* @param pls2_model
|
||
* @returns
|
||
*/
|
||
static isOverLap(pl1: Polyline, pls1_inner: Polyline[], pls1_model: Polyline[], pl2: Polyline, pls2_inner: Polyline[], pls2_model: Polyline[]) {
|
||
// 是否干涉, 被包含在造型洞,不算干涉
|
||
let isOverlap = this.boxIsOverlap(pl1, pls1_inner, pl2, pls2_inner)
|
||
|
||
if (isOverlap)
|
||
return true
|
||
|
||
// 造型 ,2v 刀路 是否干涉
|
||
for (let pl1_model of pls1_model) {
|
||
if (pl1_model.IntersectWith(pl2, 0).length > 0)
|
||
return true
|
||
for (let pl2_inner of pls2_inner) {
|
||
if (pl1_model.IntersectWith(pl2_inner, 0).length > 0)
|
||
return true
|
||
}
|
||
}
|
||
|
||
for (let pl2_model of pls2_model) {
|
||
if (pl2_model.IntersectWith(pl1, 0).length > 0)
|
||
return true
|
||
for (let pl1_inner of pls1_inner) {
|
||
if (pl2_model.IntersectWith(pl1_inner, 0).length > 0)
|
||
return true
|
||
}
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
private static boxIsOverlap(pl1: Polyline, pls1_inner: Polyline[], pl2: Polyline, pls2_inner: Polyline[]) {
|
||
let box1 = this.getBox(pl1)
|
||
let box2 = this.getBox(pl2)
|
||
|
||
if (!box1.intersectsBox(box2))
|
||
return false // 肯定不相交
|
||
|
||
let ipts = pl1.IntersectWith(pl2, 0)
|
||
if (ipts.length > 0)
|
||
return true // 有交点 一定有交集
|
||
|
||
if (pl1.Area > pl2.Area)// 缓存面积
|
||
{
|
||
if (isTargetCurInOrOnSourceCur(pl1, pl2)) // pl1包含 pl2
|
||
{
|
||
for (let mpl of pls1_inner) // 如果pl1有造型洞包含pl2, 则表示不干涉,返回false
|
||
|
||
{
|
||
if (isTargetCurInOrOnSourceCur(mpl, pl2) == true)
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
}
|
||
else {
|
||
if (isTargetCurInOrOnSourceCur(pl2, pl1)) // pl2包含 pl1
|
||
{
|
||
for (let mpl of pls2_inner) // 如果pl2有造型洞包含pl1, 则表示不干涉,返回false
|
||
|
||
{
|
||
if (isTargetCurInOrOnSourceCur(mpl, pl1) == true)
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
/** 判断 点是否在多段线内 */
|
||
static isPointInPolyline(pl1: Polyline, x: number, y: number): boolean {
|
||
return pl1.PtInCurve(new Vector3(x, y, 0))
|
||
}
|
||
|
||
static getBox(pl1: Polyline): Box3 {
|
||
if (!pl1.box_tp)
|
||
pl1.box_tp = pl1.BoundingBox
|
||
|
||
return pl1.box_tp as Box3
|
||
}
|
||
|
||
static getArea(pl1: Polyline): number {
|
||
if (!pl1.area_tp)
|
||
pl1.area_tp = pl1.Area
|
||
|
||
return pl1.area_tp as number
|
||
}
|
||
|
||
static getPath(pl: Polyline): Path2D {
|
||
let path = new Path2D()
|
||
let p0 = pl.LineData[0].pt
|
||
path.moveTo(p0.x, p0.y)
|
||
|
||
for (let i = 0; i < pl.LineData.length; i++) {
|
||
let p0 = pl.LineData[i].pt
|
||
let p1 = (i == pl.LineData.length - 1) ? pl.LineData[0].pt : pl.LineData[i + 1].pt
|
||
let bul = pl.LineData[i].bul
|
||
if (bul == 0) {
|
||
path.lineTo(p1.x, p1.y)
|
||
}
|
||
else {
|
||
let arc = new Arc2d(new Point2d(p0.x, p0.y), new Point2d(p1.x, p1.y), bul)
|
||
path.arc(arc.m_Center.m_X, arc.m_Center.m_Y, arc.m_Radius, arc.m_StartAngle, arc.m_EndAngle, bul < 0)
|
||
}
|
||
}
|
||
path.closePath()
|
||
return path
|
||
}
|
||
|
||
static equaln(v1: number, v2: number, fuzz = 1e-5) {
|
||
return Math.abs(v1 - v2) <= fuzz
|
||
}
|
||
|
||
static toClipboard(en: Polyline | any) {
|
||
let f = new CADFiler()
|
||
f.Write(1)// 实体个数
|
||
f.WriteObject(en)
|
||
|
||
copyTextToClipboard(f.ToString())
|
||
}
|
||
|
||
static getStrPLs(ens: any[]) {
|
||
if (ens.length == 0)
|
||
return ''
|
||
let f = new CADFiler()
|
||
f.Write(ens.length)// 实体个数
|
||
for (let en of ens)
|
||
f.WriteObject(en)
|
||
|
||
return f.ToString()
|
||
}
|
||
}
|