Files
cut-abstractions/samples/handleAbility/common/base/CAD.ts

1626 lines
42 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

export class Vector2d
{
m_X = 0
m_Y = 0
/** 长度 */
Length = 0
/** 角度 */
Angle = 0
constructor(x: number, y: number)
{
this.m_X = x
this.m_Y = y
this.GetLength()
this.GetAngle()
}
private GetLength()
{
this.Length = Math.sqrt(this.m_X * this.m_X + this.m_Y * this.m_Y)
}
// 角度
private GetAngle()
{
let a = Math.atan2(this.m_Y, this.m_X)
if (a < 0)
a = Math.PI * 2 + a
this.Angle = a
}
Normal(): Vector2d
{
if (this.Length != 0)
{
let k = 1 / this.Length
this.m_X *= k
this.m_Y *= k
this.Length = 1
}
return this
}
static OpDivide(v: Vector2d, c: number): Vector2d
{
return new Vector2d(v.m_X / c, v.m_Y / c)
}
static OpMultiply(v: Vector2d, c: number): Vector2d
{
return new Vector2d(v.m_X * c, v.m_Y * c)
}
static OpAdd(v1: Vector2d, v2: Vector2d): Vector2d
{
return new Vector2d(v1.m_X + v2.m_X, v1.m_Y + v2.m_Y)
}
static OpMultiplyValue(v1: Vector2d, v2: Vector2d): number
{
return v1.m_X * v2.m_X + v1.m_Y * v2.m_Y
}
/** 点积 */
DotProduct(v1: Vector2d): number
{
return Vector2d.OpMultiplyValue(this, v1)
}
/** 叉积 */
CrossProduct(v: Vector2d): number
{
return this.m_X * v.m_Y - this.m_Y * v.m_X
}
IsEqual(v: Vector2d, fuzz = 1e-3): boolean
{
return DoubleUtil.EqualX(this.m_X, v.m_X, fuzz) && DoubleUtil.EqualX(this.m_Y, v.m_Y, fuzz)
}
/** 返回和另外一个向量的夹角 */
AngleTo(v: Vector2d): number
{
let an = Math.abs(this.Angle - v.Angle)
if (DoubleUtil.EqualX(v.Length, 0, 1e-5) || DoubleUtil.EqualX(this.Length, 0, 1e-5))
{
return -Math.PI// 随缘给
}
else if (Math.abs(Math.sin(an)) < 0)
{
return this.Normal().IsEqual(v.Normal()) ? 0 : Math.PI
}
else
{
let p0 = new Point2d(0, 0)
let p1 = Point2d.OpAdd(new Point2d(0, 0), this)
let p2 = Point2d.OpAdd(new Point2d(0, 0), v)
let s = this.sign(Utils.Det(p1, p0, p2))
if (an > Math.PI)
{
return ((2 * Math.PI) - an) * s
}
else
{
return an * s
}
}
}
private sign(x: number): number
{
if (x == 0)
{
return 0
}
else if (x > 0)
{
return -1
}
else
{
return 1
}
}
}
/** CAD 点 */
export class Point2d
{
m_X = 0
m_Y = 0
constructor(x: number, y: number)
{
this.m_X = x
this.m_Y = y
}
DistensTo(pt: Point2d): number
{
return Point2d.OpSubtract(pt, this).Length
}
Polar(angle: number, distens: number): Point2d
{
let x1 = this.m_X + Math.cos(angle) * distens
let y1 = this.m_Y + Math.sin(angle) * distens
return new Point2d(x1, y1)
}
Mid(pt: Point2d): Point2d
{
return new Point2d((this.m_X + pt.m_X) * 0.5, (this.m_Y + pt.m_Y) * 0.5)
}
AsVector(): Vector2d
{
return new Vector2d(this.m_X, this.m_Y)
}
/** 减以 - */
static OpSubtract(p1: Point2d, p2: Point2d): Vector2d
{
return new Vector2d(p1.m_X - p2.m_X, p1.m_Y - p2.m_Y)
}
static OpAdd(p1: Point2d, p2: Vector2d): Point2d
{
return new Point2d(p1.m_X + p2.m_X, p1.m_Y + p2.m_Y)
}
}
function equaln(v1: number, v2: number, fuzz = 1e-5)
{
return Math.abs(v1 - v2) <= fuzz
}
function equalp(v1: Point2d, v2: Point2d, fuzz = 1e-5)
{
return equaln(v1.m_X, v2.m_X, fuzz) && equaln(v1.m_Y, v2.m_Y, fuzz)
}
/** 二维曲线:直线,圆弧 */
export abstract class Curve2d
{
// 求交点
abstract IntersectWith(cu: Curve2d, retIns: Point2d[]): void
abstract Offset(distens: number): Curve2d
// 点在线内部
abstract PtInCurve(pt: Point2d): boolean
abstract ClosePointTo(pt: Point2d): Point2d
// 获得参数 返回0或1
abstract GetParametersAt(pt: Point2d): number
abstract Parse()
PtOnCurve(p: Point2d): boolean
{
return equalp(p, this.m_StartPoint) || equalp(p, this.m_EndPoint) || this.ParamOnCurve(this.GetParametersAt(p))
}
ParamOnCurve(param: number, fuzz = 1e-6): boolean { return !Number.isNaN(param) && param >= -fuzz && param <= 1 + fuzz }
get EndPoint(): Point2d
{
return this.m_EndPoint
}
set EndPoint(p: Point2d)
{
this.m_EndPoint = p
try
{
this.Parse()
} catch (error)
{
}
}
get StartPoint(): Point2d
{
return this.m_StartPoint
}
set StartPoint(p: Point2d)
{
this.m_StartPoint = p
try
{
this.Parse()
} catch (error)
{
}
}
protected m_StartPoint: Point2d
protected m_EndPoint: Point2d
tagData: number // 特殊数据 异形边 封边值
tagData2: number // 特殊数据 侧孔数
}
/** 直线 */
export class Line2d extends Curve2d
{
get toString()
{
return `[${this.m_StartPoint.m_X.toFixed(1)},${this.m_StartPoint.m_Y.toFixed(1)}] [${this.m_EndPoint.m_X.toFixed(1)},${this.m_EndPoint.m_Y.toFixed(1)}]`
}
constructor(p1: Point2d, p2: Point2d)
{
super()
this.m_StartPoint = p1
this.m_EndPoint = p2
this.Parse()
}
/** 求交点 */
IntersectWith(cu: Curve2d, retIns: Point2d[])
{
if (cu instanceof Line2d)
{
let l = cu as Line2d
let dx1 = this.StartPoint.m_X - this.EndPoint.m_X
let dx2 = l.StartPt.m_X - l.EndPt.m_X
let dx3 = l.EndPt.m_X - this.EndPoint.m_X
let dy1 = this.StartPoint.m_Y - this.EndPoint.m_Y
let dy2 = l.StartPt.m_Y - l.EndPt.m_Y
let dy3 = l.EndPt.m_Y - this.EndPoint.m_Y
let det = (dx2 * dy1) - (dy2 * dx1)
let pt = new Point2d(0, 0)
if (DoubleUtil.EqualX(det, 0.0, 1e-5))
{
if (DoubleUtil.EqualX(dx2 * dy3, dy2 * dx3, 1e-5))
{
if (l.StartPoint.DistensTo(this.EndPoint) < 1e-3)
{
retIns.push(this.EndPoint)
}
}
return
}
let ratio = ((dx1 * dy3) - (dy1 * dx3)) / det
pt.m_X = (ratio * dx2) + l.EndPt.m_X
pt.m_Y = (ratio * dy2) + l.EndPt.m_Y
retIns.push(pt)
}
else
{
let arc = cu as Arc2d
arc.IntersectWith(this, retIns)
}
}
/** 偏移 */
Offset(distens: number): Curve2d
{
let an = Point2d.OpSubtract(this.EndPt, this.StartPt).Angle - Math.PI * 0.5
return new Line2d(this.StartPt.Polar(an, distens), this.EndPt.Polar(an, distens))
}
/** 判断点在线内 */
PtInCurve(pt: Point2d): boolean
{
// 首先判断平行
let minX = Math.min(this.m_StartPoint.m_X, this.m_EndPoint.m_X)
let maxX = Math.max(this.m_StartPoint.m_X, this.m_EndPoint.m_X)
if (DoubleUtil.EqualX(minX, maxX, 1e-3))
{
let minY = Math.min(this.m_StartPoint.m_Y, this.m_EndPoint.m_Y)
let maxY = Math.max(this.m_StartPoint.m_Y, this.m_EndPoint.m_Y)
return (pt.m_Y >= minY - 1e-4 && pt.m_Y <= maxY + 1e-4)
}
if (pt.m_X >= minX - 0.0001 && pt.m_X <= maxX + 0.0001)
{
let vec = Point2d.OpSubtract(this.m_EndPoint, this.m_StartPoint)// 标准向量
let k = vec.m_Y / vec.m_X
return DoubleUtil.EqualX(this.m_StartPoint.m_Y + k * (pt.m_X - this.m_StartPoint.m_X), pt.m_Y, 0.01)
}
else
{
return false
}
}
ClosePointTo(pt: Point2d): Point2d
{
let an = Point2d.OpSubtract(this.m_EndPoint, this.m_StartPoint).Angle + Math.PI / 2
let vec = new Point2d(0, 0).Polar(an, 1).AsVector()
let p2 = Point2d.OpAdd(pt, vec)
let line = new Line2d(pt, p2)
let ptLst: Point2d[] = []
this.IntersectWith(line, ptLst)
return ptLst[0]
}
Parse()
{
if (this.EndPoint != null)
{
this.m_Length = this.StartPoint.DistensTo(this.EndPoint)
}
}
GetParametersAt(pt: Point2d): number
{
let nearPt = this.ClosePointTo(pt)
let vec = Point2d.OpSubtract(nearPt, this.m_StartPoint)
let vec2 = Point2d.OpSubtract(this.EndPoint, this.StartPoint)
let an = vec.DotProduct(vec2)
return Math.sign(an) * vec.Length / this.m_Length
}
get StartPt(): Point2d { return this.StartPoint }
get EndPt(): Point2d { return this.EndPoint }
m_Length: number
static New(x0: number, y0: number, x1: number, y1: number): Line2d
{
let p0 = new Point2d(x0, y0)
let p1 = new Point2d(x1, y1)
return new Line2d(p0, p1)
}
}
/** 圆弧 */
export class Arc2d extends Curve2d
{
constructor(p1: Point2d, p2: Point2d, bul: number)
{
super()
this.m_StartPoint = p1
this.m_EndPoint = p2
this.m_Bul = bul
let vec = Point2d.OpSubtract(p2, p1)
let an = vec.Angle
let length = vec.Length
this.m_Radius = length / Math.sin(2 * Math.atan(bul)) / 2
this.m_AllAngle = Math.atan(bul) * 4
let delDis = bul * length / 2
let toDis = this.m_Radius - delDis
an += Math.PI * 0.5
this.m_Center = p1.Mid(p2)
this.m_Center = this.m_Center.Polar(an, toDis)
this.m_StartAngle = Point2d.OpSubtract(this.StartPoint, this.m_Center).Angle
this.m_EndAngle = Point2d.OpSubtract(this.EndPoint, this.m_Center).Angle
if (this.m_Bul < 0)
this.m_Radius = Math.abs(this.m_Radius)
}
/** 圆心 */
m_Center: Point2d
/** 半径 */
m_Radius: number
/** 起始弧度 */
m_StartAngle: number
/** 结束弧度 */
m_EndAngle: number
/** 所有的弧度 */
m_AllAngle: number
IntersectWith(cu: Curve2d, retIns: Point2d[])
{
if (cu instanceof Line2d)
{
let l = cu as Line2d
let a = DoubleUtil.Sqr(l.EndPt.m_X - l.StartPt.m_X) + DoubleUtil.Sqr(l.EndPt.m_Y - l.StartPt.m_Y)
let b = (2.0) * ((l.EndPt.m_X - l.StartPt.m_X) * (l.StartPt.m_X - this.m_Center.m_X)
+ (l.EndPt.m_Y - l.StartPt.m_Y) * (l.StartPt.m_Y - this.m_Center.m_Y))
let c = DoubleUtil.Sqr(this.m_Center.m_X) + DoubleUtil.Sqr(this.m_Center.m_Y)
+ DoubleUtil.Sqr(l.StartPt.m_X) + DoubleUtil.Sqr(l.StartPt.m_Y)
- (2.0) * (this.m_Center.m_X * l.StartPt.m_X + this.m_Center.m_Y * l.StartPt.m_Y) - DoubleUtil.Sqr(this.m_Radius)
let det = b * b - (4.0) * a * c
if (DoubleUtil.EqualX(det, 0.0, 0.1))
{
let delta = -b / ((2.0) * a)
let pt = new Point2d(l.StartPt.m_X + delta * (l.EndPt.m_X - l.StartPt.m_X), l.StartPt.m_Y + delta * (l.EndPt.m_Y - l.StartPt.m_Y))
retIns.push(pt)
return
}
else if (det > (0.0))
{
let sqrt_det = Math.sqrt(det)
let delta = (-b + sqrt_det) / ((2.0) * a)
let p2 = new Point2d(l.StartPt.m_X + delta * (l.EndPt.m_X - l.StartPt.m_X), l.StartPt.m_Y + delta * (l.EndPt.m_Y - l.StartPt.m_Y))
delta = (-b - sqrt_det) / ((2.0) * a)
let p3 = new Point2d(l.StartPt.m_X + delta * (l.EndPt.m_X - l.StartPt.m_X), l.StartPt.m_Y + delta * (l.EndPt.m_Y - l.StartPt.m_Y))
retIns.push(p2)
retIns.push(p3)
return
}
}
else if (cu instanceof Arc2d)
{
let arc = cu as (Arc2d)
let dist = arc.m_Center.DistensTo(this.m_Center)
if (dist < Math.abs(this.m_Radius - cu.m_Radius) - 1e-3
|| dist > (this.m_Radius + cu.m_Radius + 1e-3))
return
let dstsqr = dist * dist
let r1sqr = this.m_Radius * this.m_Radius
let r2sqr = arc.m_Radius * arc.m_Radius
let a = (dstsqr - r2sqr + r1sqr) / (2 * dist)
let h = Math.sqrt(Math.abs(r1sqr - (a * a)))
let ratio_a = a / dist
let ratio_h = h / dist
let dx = arc.m_Center.m_X - this.m_Center.m_X
let dy = arc.m_Center.m_Y - this.m_Center.m_Y
let phix = this.m_Center.m_X + (ratio_a * dx)
let phiy = this.m_Center.m_Y + (ratio_a * dy)
dx = dx * ratio_h
dy = dy * ratio_h
let pt = new Point2d(phix + dy, phiy - dx)
let p2 = new Point2d(phix - dy, phiy + dx)
retIns.push(pt)
retIns.push(p2)
}
}
Offset(distens: number): Curve2d
{
let rn = new Arc2d(new Point2d(0, 0), new Point2d(0, 0), 0)
rn.m_Center = this.m_Center
rn.m_Radius = this.m_Radius
rn.m_StartAngle = this.m_StartAngle
rn.m_EndAngle = this.m_EndAngle
if (this.m_Bul > 0)
rn.m_Radius += distens
else
rn.m_Radius -= distens
rn.Bul = this.m_Bul
rn.ParsePointFormAngle()
return rn
}
Parse()
{
// 解析角度
this.ParseAngleFormPoint()
this.ParseAllAngle()
this.ParseBul()
}
ParseAllAngle()
{
this.m_AllAngle = this.ComputeAnlge(this.m_EndAngle)
}
ComputeAnlge(endAngle: number)
{
// 顺时针
if (this.m_Bul < 0)
{
if (this.m_StartAngle > endAngle)
return this.m_StartAngle - endAngle
else // 越过0点绘制圆弧
return (Math.PI * 2) - (endAngle - this.m_StartAngle)
}
else
{
if (endAngle > this.m_StartAngle)
return endAngle - this.m_StartAngle
else
return (Math.PI * 2) - (this.m_StartAngle - endAngle)
}
}
ParseBul()
{
this.m_Bul = Math.tan(this.m_AllAngle * 0.25) * (this.m_Bul < 0 ? -1 : 1)
}
ParsePointFormAngle()
{
this.m_StartPoint = this.m_Center.Polar(this.m_StartAngle, this.m_Radius)
this.m_EndPoint = this.m_Center.Polar(this.m_EndAngle, this.m_Radius)
}
ParseAngleFormPoint()
{
this.m_StartAngle = Point2d.OpSubtract(this.StartPoint, this.m_Center).Angle
this.m_EndAngle = Point2d.OpSubtract(this.EndPoint, this.m_Center).Angle
}
PtInCurve(pt: Point2d): boolean
{
let param = this.GetParametersAt(pt)
return param > -1e-3 && param < 1.0001
}
ClosePointTo(pt: Point2d): Point2d
{
return null
}
GetParametersAt(pt: Point2d): number
{
let vec = Point2d.OpSubtract(pt, this.m_Center)
let an = vec.Angle
this.ParseAllAngle()
// 如果以pt为终点,那么所有的角度为
let ptAllAn = this.ComputeAnlge(an)
let allAn = this.m_AllAngle
// 减去圆弧角度,剩余角度的一半
let surplusAngleHalf = Math.PI - allAn / 2
if (ptAllAn > allAn + surplusAngleHalf)// 返回负数
return ((ptAllAn - allAn) - (surplusAngleHalf * 2)) / allAn
else// 返回正数
return ptAllAn / allAn
}
GetAngleAtParam(param: number)
{
return this.clampRad(this.m_StartAngle + param * this.m_AllAngle)
}
private clampRad(an: number)
{
an = an % (Math.PI * 2)
if (an < 0)
an += Math.PI * 2
return an
}
/** 凸度. */
m_Bul: number
get Bul() { return this.m_Bul }
set Bul(v: number) { this.m_Bul = v }
static New(x0: number, y0: number, x1: number, y1: number, bul: number): Arc2d
{
return new Arc2d(new Point2d(x0, y0), new Point2d(x1, y1), bul)
}
}
/** 圆弧 */
export class Arc2d_new extends Curve2d
{
constructor(p1: Point2d, p2: Point2d, bul: number)
{
super()
this.m_StartPoint = p1
this.m_EndPoint = p2
this.m_Bul = bul
let vec = Point2d.OpSubtract(p2, p1)
let an = vec.Angle
let length = vec.Length
this.m_Radius = length / Math.sin(2 * Math.atan(bul)) / 2
this.m_AllAngle = Math.atan(bul) * 4
let delDis = bul * length / 2
let toDis = this.m_Radius - delDis
an += Math.PI * 0.5
this.m_Center = p1.Mid(p2)
this.m_Center = this.m_Center.Polar(an, toDis)
this.m_StartAngle = Point2d.OpSubtract(this.StartPoint, this.m_Center).Angle
this.m_EndAngle = Point2d.OpSubtract(this.EndPoint, this.m_Center).Angle
if (this.m_Bul < 0)
this.m_Radius = Math.abs(this.m_Radius)
}
/** 圆心 */
m_Center: Point2d
/** 半径 */
m_Radius: number
/** 起始弧度 */
m_StartAngle: number
/** 结束弧度 */
m_EndAngle: number
/** 所有的弧度 */
m_AllAngle: number
IntersectWith(cu: Curve2d, retIns: Point2d[])
{
if (cu instanceof Line2d)
{
let l = cu as Line2d
let a = DoubleUtil.Sqr(l.EndPt.m_X - l.StartPt.m_X) + DoubleUtil.Sqr(l.EndPt.m_Y - l.StartPt.m_Y)
let b = (2.0) * ((l.EndPt.m_X - l.StartPt.m_X) * (l.StartPt.m_X - this.m_Center.m_X)
+ (l.EndPt.m_Y - l.StartPt.m_Y) * (l.StartPt.m_Y - this.m_Center.m_Y))
let c = DoubleUtil.Sqr(this.m_Center.m_X) + DoubleUtil.Sqr(this.m_Center.m_Y)
+ DoubleUtil.Sqr(l.StartPt.m_X) + DoubleUtil.Sqr(l.StartPt.m_Y)
- (2.0) * (this.m_Center.m_X * l.StartPt.m_X + this.m_Center.m_Y * l.StartPt.m_Y) - DoubleUtil.Sqr(this.m_Radius)
let det = b * b - (4.0) * a * c
if (DoubleUtil.EqualX(det, 0.0, 0.1))
{
let delta = -b / ((2.0) * a)
let pt = new Point2d(l.StartPt.m_X + delta * (l.EndPt.m_X - l.StartPt.m_X), l.StartPt.m_Y + delta * (l.EndPt.m_Y - l.StartPt.m_Y))
retIns.push(pt)
return
}
else if (det > (0.0))
{
let sqrt_det = Math.sqrt(det)
let delta = (-b + sqrt_det) / ((2.0) * a)
let p2 = new Point2d(l.StartPt.m_X + delta * (l.EndPt.m_X - l.StartPt.m_X), l.StartPt.m_Y + delta * (l.EndPt.m_Y - l.StartPt.m_Y))
delta = (-b - sqrt_det) / ((2.0) * a)
let p3 = new Point2d(l.StartPt.m_X + delta * (l.EndPt.m_X - l.StartPt.m_X), l.StartPt.m_Y + delta * (l.EndPt.m_Y - l.StartPt.m_Y))
retIns.push(p2)
retIns.push(p3)
return
}
}
else if (cu instanceof Arc2d)
{
let arc = cu as (Arc2d)
let dist = arc.m_Center.DistensTo(this.m_Center)
if (dist < Math.abs(this.m_Radius - cu.m_Radius) - 1e-3
|| dist > (this.m_Radius + cu.m_Radius + 1e-3))
return
let dstsqr = dist * dist
let r1sqr = this.m_Radius * this.m_Radius
let r2sqr = arc.m_Radius * arc.m_Radius
let a = (dstsqr - r2sqr + r1sqr) / (2 * dist)
let h = Math.sqrt(Math.abs(r1sqr - (a * a)))
let ratio_a = a / dist
let ratio_h = h / dist
let dx = arc.m_Center.m_X - this.m_Center.m_X
let dy = arc.m_Center.m_Y - this.m_Center.m_Y
let phix = this.m_Center.m_X + (ratio_a * dx)
let phiy = this.m_Center.m_Y + (ratio_a * dy)
dx = dx * ratio_h
dy = dy * ratio_h
let pt = new Point2d(phix + dy, phiy - dx)
let p2 = new Point2d(phix - dy, phiy + dx)
retIns.push(pt)
retIns.push(p2)
}
}
Offset(distens: number): Curve2d
{
let rn = new Arc2d(new Point2d(0, 0), new Point2d(0, 0), 0)
rn.m_Center = this.m_Center
rn.m_Radius = this.m_Radius
rn.m_StartAngle = this.m_StartAngle
rn.m_EndAngle = this.m_EndAngle
if (this.m_Bul > 0)
rn.m_Radius += distens
else
rn.m_Radius -= distens
rn.Bul = this.m_Bul
rn.ParsePointFormAngle()
return rn
}
Parse()
{
// 解析角度
this.ParseAngleFormPoint()
this.ParseAllAngle()
this.ParseBul()
}
ParseAllAngle()
{
this.m_AllAngle = this.ComputeAnlge(this.m_EndAngle)
}
ComputeAnlge(endAngle: number)
{
// 顺时针
if (this.m_Bul < 0)
{
if (this.m_StartAngle > endAngle)
return this.m_StartAngle - endAngle
else // 越过0点绘制圆弧
return (Math.PI * 2) - (endAngle - this.m_StartAngle)
}
else
{
if (endAngle > this.m_StartAngle)
return endAngle - this.m_StartAngle
else
return (Math.PI * 2) - (this.m_StartAngle - endAngle)
}
}
ParseBul()
{
this.m_Bul = Math.tan(this.m_AllAngle * 0.25) * (this.m_Bul < 0 ? -1 : 1)
}
ParsePointFormAngle()
{
this.m_StartPoint = this.m_Center.Polar(this.m_StartAngle, this.m_Radius)
this.m_EndPoint = this.m_Center.Polar(this.m_EndAngle, this.m_Radius)
}
ParseAngleFormPoint()
{
this.m_StartAngle = Point2d.OpSubtract(this.StartPoint, this.m_Center).Angle
this.m_EndAngle = Point2d.OpSubtract(this.EndPoint, this.m_Center).Angle
}
PtInCurve(pt: Point2d): boolean
{
let param = this.GetParametersAt(pt)
return param > -1e-3 && param < 1.0001
}
ClosePointTo(pt: Point2d): Point2d
{
return null
}
GetParametersAt(pt: Point2d): number
{
let vec = Point2d.OpSubtract(pt, this.m_Center)
let an = vec.Angle
this.ParseAllAngle()
// 如果以pt为终点,那么所有的角度为
let ptAllAn = this.ComputeAnlge(an)
let allAn = this.m_AllAngle
// 减去圆弧角度,剩余角度的一半
let surplusAngleHalf = Math.PI - allAn / 2
if (ptAllAn > allAn + surplusAngleHalf)// 返回负数
return ((ptAllAn - allAn) - (surplusAngleHalf * 2)) / allAn
else// 返回正数
return ptAllAn / allAn
}
GetAngleAtParam(param: number)
{
return this.clampRad(this.m_StartAngle + param * this.m_AllAngle)
}
private clampRad(an: number)
{
an = an % (Math.PI * 2)
if (an < 0)
an += Math.PI * 2
return an
}
/** 凸度. */
m_Bul: number
get Bul() { return this.m_Bul }
set Bul(v: number) { this.m_Bul = v }
static New(x0: number, y0: number, x1: number, y1: number, bul: number): Arc2d
{
return new Arc2d(new Point2d(x0, y0), new Point2d(x1, y1), bul)
}
}
function EntityEncode(c: Curve2d)
{
if (c instanceof Line2d)
return 1
else return 2
}
function EntityEncode2(c1: Curve2d, c2: Curve2d)
{
return EntityEncode(c1) & EntityEncode(c2)
}
/** CAD 运算库 */
export class Utils
{
static Intersec(e1: Curve2d, e2: Curve2d, oldE1: Curve2d): Point2d
{
if (e1.EndPoint.DistensTo(e2.StartPoint) < 1e-2)
{
return e1.EndPoint
}
let ptsIns: Point2d[] = []
e1.IntersectWith(e2, ptsIns)
if (ptsIns.length == 1)
{
return ptsIns[0]
}
else if (ptsIns.length == 2)
{
// 求一个最近的点.
let pt = oldE1.EndPoint
let d1 = pt.DistensTo(ptsIns[0])
let d2 = pt.DistensTo(ptsIns[1])
return d1 < d2 ? ptsIns[0] : ptsIns[1]
}
else // 0点
{
return null
}
}
/** 曲线偏移 */
static offsetPoints(points, offsetDistens: number): any[]
{
let ens: Curve2d[] = []
for (let i = 0; i < points.length; i++)
{
let j = i + 1
if (j == points.length)
j = 0
let line = new Line2d(new Point2d(points[i].x, points[i].y), new Point2d(points[j].x, points[j].y))
if (line.m_Length == 0)
continue
ens.push(line)
}
let ens2 = Utils.OffsetCurveList(ens, offsetDistens)
let newPs = []
for (let c of ens2)
{
newPs.push({ x: c.StartPoint.m_X, y: c.StartPoint.m_Y })
}
return newPs
}
// 1.曲线外偏移.
//* 2.曲线尝试首尾相连
//* ->如果两条不相连,导致曲线连接失败,将添加圆弧过度
//*
//* 2017-11-17对曲线相连算法进行修改.
//* 现在会先求出连接点,然后在重新进行曲线连接,避免连接点出错. 参见#118
static OffsetCurveList(ens: Curve2d[], offsetDistens: number): Curve2d[]
{
// 只处理偏移的曲线
let offEns: Curve2d[] = []
ens = ens.filter(e => e.StartPoint.DistensTo(e.EndPoint) > 0.05)
for (const item of ens)
{
offEns.push(item.Offset(offsetDistens))
}
// 处理偏移 并且对交点进行处理
let offEns2: Curve2d[] = []
let linkPts: Point2d[] = []
// 计算链接点,如果有必要会新增圆弧
for (let i = 0; i < offEns.length; i++)
{
let index2 = i + 1 == offEns.length ? 0 : i + 1
let e1 = offEns[i]
let e2 = offEns[index2]
offEns2.push(offEns[i])
let iPts: Point2d[] = []
e1.IntersectWith(e2, iPts)
let tPts = iPts.filter(p => e1.PtOnCurve(p) && e2.PtOnCurve(p))
let code = EntityEncode2(e1, e2)
if (code === 1)
{
if (tPts.length > 0)
linkPts.push(tPts[0])// 直接连接
else
{
let refP = ens[i].EndPoint
let distSq = iPts[0].DistensTo(refP)
if (distSq > offsetDistens ** 2 * 2.1) // 补圆弧
{
let arc = new Arc2d(e1.EndPoint, e2.StartPoint, Math.sign(offsetDistens))
arc.m_Center = ens[i].EndPoint
arc.m_Radius = offsetDistens// 半径
linkPts.push(e1.EndPoint)
linkPts.push(e2.StartPoint)
offEns2.push(arc)// 添加圆弧.
}
else
linkPts.push(iPts[0])
}
}
else if (
code === 2
&& (
equalp((<Arc2d>e1).m_Center, (<Arc2d>e2).m_Center, 0.1) // 都是圆弧,且同心圆(由于精度丢失,这里我们给0.01的容差)
|| equalp(e1.EndPoint, e2.StartPoint, 0.1))
)
linkPts.push(e1.EndPoint)
else
{
function SelectNearP(pts: Point2d[], refPt: Point2d): Point2d
{
if (pts.length > 1)
{
let dist1 = refPt.DistensTo(pts[0])
let dist2 = refPt.DistensTo(pts[1])
return dist1 <= dist2 ? pts[0] : pts[1]
}
return pts[0]
}
let refP = ens[i].EndPoint
if (tPts.length > 0)
linkPts.push(SelectNearP(iPts, refP))// 直接连接
else
{
let arc = new Arc2d(e1.EndPoint, e2.StartPoint, Math.sign(offsetDistens))
arc.m_Center = ens[i].EndPoint
arc.m_Radius = offsetDistens// 半径
linkPts.push(e1.EndPoint)
linkPts.push(e2.StartPoint)
offEns2.push(arc)// 添加圆弧.
}
}
}
// 更新链接点位置
for (let i = 0; i < offEns2.length; i++)
{
offEns2[i].EndPoint = linkPts[i]
let nextIndex = i == offEns2.length - 1 ? 0 : i + 1
offEns2[nextIndex].StartPoint = linkPts[i]
}
// 测试数据
// TestPolyline([ens, offEns2])
return offEns2
}
/** //自交曲线优化 去除自交部分. */
static RemoveSelfIntersect(cus: Curve2d[]): Curve2d[]
{
let res = new Array<Curve2d>()
let cout = cus.length
for (let i = 0; i < cout; i++)
{
let cu = cus[i]
res.push(cu)
for (let j = cout - 2; j > i + 1; j--)
{
let cu2 = cus[j]
let pts = new Array<Point2d>()
cu.IntersectWith(cu2, pts)
//
if (pts.length == 0)
{
continue
}
if (pts.length == 2)
{
if (cu instanceof Line2d)
{
if (cu.GetParametersAt(pts[0]) > cu.GetParametersAt(pts[1]))
{
pts[0] = pts[1]
}
}
else if (cu instanceof Arc2d)
{
// 使用上一条线. 因为我们保证它和上一条线是有一个交点的
let arc = cu as Arc2d
// last cu
let lastIndex = i - 1
if (i == 0)
lastIndex = cout - 1
let lastCu = cus[lastIndex]
let insPts = new Array<Point2d>()
lastCu.IntersectWith(cu, insPts)
for (let insP of insPts)
{
let p1 = arc.GetParametersAt(insP)
let p2 = arc.GetParametersAt(insP)
// 从起点圆弧开始
if (insP.DistensTo(cu.StartPoint) < 1e-3)
{
if (p1 > p2)
{
pts[0] = pts[1]
}
break
}
// 从终点圆弧开始.
else if (insP.DistensTo(cu.EndPoint) < 1e-3)
{
if (p1 < p2)
{
pts[0] = pts[1]
}
break
}
}
}
}
if (cu.PtInCurve(pts[0]) && cu2.PtInCurve(pts[0]))
{
Utils.changeP(cus, cu, i, -1, pts[0])
Utils.changeP(cus, cu, j, 1, pts[0])
i = j - 1
break
}
}
}
Utils.RemoveZeroCurve(res)
return res
}
static changeP(cus: Curve2d[], cu: Curve2d, index: number, next: number, _pt: Point2d)
{
let cout = cus.length
let lastIndex = index + next
let _cu = cus[index]
if (lastIndex == -1)
lastIndex = cout - 1
else if (lastIndex == cout)
lastIndex = 0
let lastCu = cus[lastIndex]
let insPts = new Array<Point2d>()
lastCu.IntersectWith(_cu, insPts)
for (const insP of insPts)
{
if (insP.DistensTo(_cu.StartPoint) < 1e-3)
{
_cu.EndPoint = _pt
break
}
else if (insP.DistensTo(_cu.EndPoint) < 1e-3)
{
_cu.StartPoint = _pt
break
}
}
if (cu instanceof Arc2d)
{
cu.Parse()
}
}
static RemoveZeroCurve(cus: Curve2d[])
{
// remove zero length curve
/*
cus.RemoveAll((Curve2d o) =>
{
if (o is Line2d)
{
return ((Line2d)o).m_Length < 1e-3;
}
else if (o is Arc2d)
{
return ((Arc2d)o).m_AllAngle < 1e-3;
}
return false;
});
*/
let length = cus.length
let isZero = false
for (let i = length - 1; i >= 0; i--)
{
let o = cus[i]
isZero = false
if (o instanceof Line2d)
{
let line = o as Line2d
isZero = line.m_Length < 1e-3
}
else if (o instanceof Arc2d)
{
let arc = o as Arc2d
isZero = arc.m_AllAngle < 1e-3
}
if (isZero)
{
cus.splice(i, 1)
}
}
}
/** 曲线倒角 */
static FilletCurveList(culist: Curve2d[], offsetDistens: number): Curve2d[]
{
/*
let rn = new List<Curve2d>();
let len = cuList.Count;
for (int i = 0; i < len; i++)
{
let en = cuList[i];
let index =i==len-1 ? 0 : i + 1;
rn.Add(en);
if (en is Line2d && cuList[index] is Line2d)
{
let l1 = en as Line2d;
let l2 = cuList[index] as Line2d;
if (l1.m_Length < offsetDistens || l2.m_Length < offsetDistens
|| (l1.EndPoint-l1.StartPoint).CrossProduct(l2.EndPoint-l2.StartPoint) <1e-3
)
{
continue;
}
let tempEn1 = en.Offset(-offsetDistens) as Line2d;
let tempEn2 = cuList[index].Offset(-offsetDistens)as Line2d;
let pts = new List<Point2d>();
tempEn1.IntersectWith(tempEn2, pts);
if (pts.Count>0 && tempEn1.PtInCurve(pts.First()))
{
let pt = pts.First();
//求交点.
en.EndPoint= en.ClosePointTo(pt);
let en2 = cuList[index];
en2.StartPoint = en2.ClosePointTo(pt);
let arc2d = new Arc2d(en.EndPoint, en2.StartPoint, 1);//凸圆弧
arc2d.m_Center = pt;
arc2d.m_Radius = pt.DistensTo(en.EndPoint);
arc2d.Parse();//解析凸度 角度
rn.Add(arc2d);
}
}
}
return rn;
*/
let rn = new Array<Curve2d>()
let len = culist.length
for (let i = 0; i < len; i++)
{
let en = culist[i]
let index = i == len - 1 ? 0 : i + 1
rn.push(en)
if (en instanceof Line2d && culist[index] instanceof Line2d)
{
let l1 = en as Line2d
let l2 = culist[index] as Line2d
if (l1.m_Length < offsetDistens || l2.m_Length < offsetDistens
|| (Point2d.OpSubtract(l1.EndPoint, l1.StartPoint)).CrossProduct(Point2d.OpSubtract(l2.EndPoint, l2.StartPoint)) < 1e-3
)
{
continue
}
let tempEn1 = en.Offset(-offsetDistens) as Line2d
let tempEn2 = culist[index].Offset(-offsetDistens) as Line2d
let pts = new Array<Point2d>()
tempEn1.IntersectWith(tempEn2, pts)
if (pts.length > 0 && tempEn1.PtInCurve(pts[0]))
{
let pt = pts[0]
// 求交点.
en.EndPoint = en.ClosePointTo(pt)
let en2 = culist[index]
en2.StartPoint = en2.ClosePointTo(pt)
let arc2d = new Arc2d(en.EndPoint, en2.StartPoint, 1)// 凸圆弧
arc2d.m_Center = pt
arc2d.m_Radius = pt.DistensTo(en.EndPoint)
arc2d.Parse()// 解析凸度 角度
rn.push(arc2d)
}
}
}
return rn
}
/** 圆弧细分 如果大于90度则分割 */
static SplitCurveListArc(cuList: Curve2d[]): Curve2d[]
{
/* C# 代码
let rnList = new List<Curve2d>();//返回的曲线表
foreach (let cu in cuList)
{
if (cu is Arc2d)
{
let arc = cu as Arc2d;
arc.Parse();//解析圆弧的凸度 角度
if (arc.m_AllAngle > Math.PI * 0.5)
{
int cout =(int)( arc.m_AllAngle / (Math.PI * 0.5)) + 1;
let an = arc.m_AllAngle / cout;
let startAn = arc.m_StartAngle;
for (int i = 0; i < cout; i++)
{
let arcNew = new Arc2d(arc.StartPoint, arc.EndPoint, 0);
arcNew.m_Radius = arc.m_Radius;
arcNew.m_Center = arc.m_Center;
arcNew.m_StartAngle = startAn;
arcNew.m_EndAngle = arcNew.m_StartAngle + an;
startAn = arcNew.m_EndAngle;
rnList.Add(arcNew);
}
}
else
{
rnList.Add(cu);
}
}
else
{
rnList.Add(cu);
}
}
return rnList;
*/
let rnList = new Array<Curve2d>()// 返回的曲线表
for (let cu of cuList)
{
if (cu instanceof Arc2d)
{
let arc = cu as Arc2d
arc.Parse()// 解析圆弧的凸度 角度
if (arc.m_AllAngle > Math.PI * 0.5)
{
let cout = arc.m_AllAngle / (Math.PI * 0.5) + 1
let an = arc.m_AllAngle / cout
let startAn = arc.m_StartAngle
for (let i = 0; i < cout; i++)
{
let arcNew = new Arc2d(arc.StartPoint, arc.EndPoint, 0)
arcNew.m_Radius = arc.m_Radius
arcNew.m_Center = arc.m_Center
arcNew.m_StartAngle = startAn
arcNew.m_EndAngle = arcNew.m_StartAngle + an
startAn = arcNew.m_EndAngle
rnList.push(arcNew)
}
}
else
{
rnList.push(cu)
}
}
else
{
rnList.push(cu)
}
}
return rnList
}
// 三点面积
static Det(p1: Point2d, p2: Point2d, p3: Point2d): number
{
return this.Det2(p1.AsVector(), p2.AsVector(), p3.AsVector())
}
// 三点面积
static Det2(p1: Vector2d, p2: Vector2d, p3: Vector2d): number
{
return p1.CrossProduct(p2) + p2.CrossProduct(p3) + p3.CrossProduct(p1)
}
//
static AngleForm3Pt(pSrc: Point2d, p1: Point2d, p2: Point2d): number
{
/*
double angle = 0.0f; // 夹角
let vec1 = p1 - pSrc;
let vec2 = p2 - pSrc;
double productValue = vec1 * vec2;
double cosValue = productValue / (vec1.Length * vec2.Length); // 余弦公式
// acos的输入参数范围必须在[-1, 1]之间,否则会"domain error"
// 对输入参数作校验和处理
if (cosValue < -1 && cosValue > -2)
cosValue = -1;
else if (cosValue > 1 && cosValue < 2)
cosValue = 1;
// acos返回的是弧度值转换为角度值
angle = Math.Acos(cosValue) * 180 / Math.PI;
return angle;
*/
let angle = 0.0 // 夹角
let vec1 = Point2d.OpSubtract(p1, pSrc)
let vec2 = Point2d.OpSubtract(p2, pSrc)
let productValue = Vector2d.OpMultiplyValue(vec1, vec2)
let cosValue = productValue / (vec1.Length * vec2.Length) // 余弦公式
// acos的输入参数范围必须在[-1, 1]之间,否则会"domain error"
// 对输入参数作校验和处理
if (cosValue < -1 && cosValue > -2)
cosValue = -1
else if (cosValue > 1 && cosValue < 2)
cosValue = 1
// acos返回的是弧度值转换为角度值
angle = Math.acos(cosValue) * 180 / Math.PI
return angle
}
}
export class DoubleUtil
{
static EqualX(v1: number, v2: number, fuzz: number): boolean
{
return (Math.abs(v1 - v2) < fuzz)
}
static Sqr(v: number): number
{
return v * v
}
}
export class CADExt
{
static GetX(arc: Curve2d): number
{
return Math.min(arc.StartPoint.m_X, arc.EndPoint.m_X)
}
static GetY(arc: Curve2d): number
{
return Math.min(arc.StartPoint.m_Y, arc.EndPoint.m_Y)
}
static GetWidth(arc: Curve2d): number
{
return Math.abs(arc.StartPoint.m_X - arc.EndPoint.m_X)
}
static GetHeight(arc: Curve2d): number
{
return Math.abs(arc.StartPoint.m_Y - arc.EndPoint.m_Y)
}
static GetRadius(p1: Point2d, p2: Point2d, bul: number): number
{
// let arc = new Arc2d(p1, p2, bul);
// return arc.m_Radius;
// this.m_Radius = length / Math.sin(2 * Math.atan(bul)) / 2;
return CADExt.getR(p1.m_X, p1.m_Y, p2.m_X, p2.m_Y, bul)
}
static getR(x1: number, y1: number, x2: number, y2: number, bul: number): number
{
let length = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
return length / Math.sin(2 * Math.atan(bul)) / 2
}
/** 圆弧分割成count段 */
static SplitArc(arc: Arc2d, count: number): Arc2d[]
{
let list: Arc2d[]
list = []
let an = arc.m_AllAngle / count
let an_0 = arc.m_StartAngle
for (let i = 0; i < count; i++)
{
let arcNew = new Arc2d(arc.StartPoint, arc.EndPoint, 0)
arcNew.m_Radius = arc.m_Radius
arcNew.m_Center = arc.m_Center
arcNew.m_StartAngle = an_0
arcNew.m_EndAngle = arcNew.m_StartAngle + an
an_0 = arcNew.m_EndAngle
arcNew.ParsePointFormAngle()
list.push(arcNew)
}
return list
}
static SplitArcByCell(ar: Arc2d, cellWidth: number): any[]
{
// 弧长
let fullLenth = Math.abs(ar.m_Radius * (ar.m_EndAngle - ar.m_StartAngle))
let count = fullLenth / cellWidth
if (count < 2)
count = 2
let childs = CADExt.SplitArc(ar, count)
let pts = []
for (let c of childs)
{
pts.push({ x: c.StartPoint.m_X, y: c.StartPoint.m_Y })
}
pts.push({ x: ar.EndPoint.m_X, y: ar.EndPoint.m_Y })
return pts
}
/** 根据两点,深的弧度 中间的n个点 */
static getCurvePoints(x1: number, y1: number, x2: number, y2: number, d: number, num: number): any
{
// atan(h/(L/2)) * 4 h:
let dis = 0.5 * Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
let mul = d / dis
let arc = new Arc2d(new Point2d(x1, y1), new Point2d(x2, y2), mul)
let arc_list = CADExt.SplitArc(arc, num - 1)
let points = []
for (let li of arc_list)
{
points.push({ x: li.StartPoint.m_X, y: li.StartPoint.m_Y })
}
points.push({ x: x2, y: y2 })
return points
}
/** 求弧长 */
static getLength(arc: Arc2d): number
{
return arc.m_Radius * Math.abs(arc.m_AllAngle)
}
/** 按固定线长分割圆弧 */
static getPointsFromCurve(x1: number, y1: number, x2: number, y2: number, bul: number, dis: number)
{
let arc = new Arc2d(new Point2d(x1, y1), new Point2d(x2, y2), bul)
let length = this.getLength(arc)
let count = Math.max(4, Math.ceil(length / dis))
let arc_list = CADExt.SplitArc(arc, count)
let points = []
for (let li of arc_list)
{
points.push({ x: li.StartPoint.m_X, y: li.StartPoint.m_Y })
}
points.push({ x: x2, y: y2 })
return points
}
/** 返回弧形中心点 (不是圆心点) */
static getCenterPoint(arc: Arc2d): Point2d
{
let arcNew = new Arc2d(arc.StartPoint, arc.EndPoint, 0)
arcNew.m_Radius = arc.m_Radius
arcNew.m_Center = arc.m_Center
arcNew.m_StartAngle = arc.m_StartAngle
arcNew.m_EndAngle = arc.m_StartAngle + arc.m_AllAngle / 2
arcNew.ParsePointFormAngle()
return arcNew.EndPoint
}
/** 获得第3点到前2点的距离差 */
static getDisOff(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): number
{
let dis31 = Math.sqrt((x3 - x1) * (x3 - x1) + (y3 - y1) * (y3 - y1))
let dis32 = Math.sqrt((x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2))
let dis12 = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
return dis31 + dis32 - dis12
}
}
function TestPolyline(offEnsS: Curve2d[][])
{
let str = `[${offEnsS.length}`
for (let i = 0; i < offEnsS.length; i++)
{
let offEns = offEnsS[i]
let linedString = `${offEns.length},`
for (let cu of offEns)
{
let bul = (cu instanceof Arc2d) ? cu.m_Bul : 0
linedString += `[${cu.StartPoint.m_X},${cu.StartPoint.m_Y}],${bul},`
}
str += `,"Polyline",8,2,116,false,1,${7 + i % 2},0,[1,0,0,0,0,1,0,0,0,0,1,0,${Math.floor(i / 2) * 1000},0,0,1],0,0,true,[1,0,0,0,0,1,0,0,0,0,1,0,3000,0,0,1],0,2,${linedString}true`
}
str += ']'
console.log(str)
copyTextToClipboard(str)
}
let DebugCurves: (Curve2d[])[] = []
export function CADDebugInit()
{
DebugCurves = []
}
export function CADCopyPolylines()
{
TestPolyline(DebugCurves)
DebugCurves = []
}
function fallbackCopyTextToClipboard(text: string)
{
let textArea = document.createElement('textarea')
textArea.value = text
document.body.appendChild(textArea)
textArea.focus()
textArea.select()
try
{
let successful = document.execCommand('copy')
} catch (err)
{
}
document.body.removeChild(textArea)
}
export async function copyTextToClipboard(text: string)
{
if (!navigator.clipboard)
{
fallbackCopyTextToClipboard(text)
return
}
return await navigator.clipboard.writeText(text)
}
// ref: https://stackoverflow.com/questions/400212/how-do-i-copy-to-the-clipboard-in-javascript
/**
* 读取剪切板的字符串
*/
export async function readClipboardText()
{
if (navigator.clipboard)
return await navigator.clipboard.readText()
return ''
}