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

1626 lines
42 KiB
TypeScript
Raw Normal View History

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