UNPKG

jsdk-offical

Version:

JSDK is the most comprehensive TypeScript framework, like JDK.

308 lines (270 loc) 12.6 kB
/** * @project JSDK * @license MIT * @website https://github.com/fengboyue/jsdk * * @version 2.5.0 * @author Frank.Feng */ /// <reference path="Shape.ts" /> module JS { export namespace math { let P = Point2, V = Vector2; export namespace geom { export class Line implements Shape { x1: number; y1: number; x2: number; y2: number; static X = new Line(0, 0, 1, 0); static Y = new Line(0, 0, 0, 1); static toLine(p1: ArrayPoint2, p2: ArrayPoint2): Line { return new Line().set(p1, p2) } /** * Caculate the slope of Segment P1P2. * 计算线段(p1,p2)的斜率。当线段与X轴垂直时,斜率不存在 * @return {number} when P1P2 is vertical to the X, then return null. */ static slope( p1: ArrayPoint2, p2: ArrayPoint2 ): number { let a = p2[0] - p1[0], b = p2[1] - p1[1]; return a == 0 ? null : b / a; } /** * Returns the relation position of Line1(p1,p2) and Line2(p3,p4). * 直线1与直线2的位置关系。 * * @return {Int} -1 is Parallel and Non-Collinear; 0 is Collinear; 1 is Cross and Non-Vertical; 2 is Cross and Vertical. */ static position( p1: ArrayPoint2, p2: ArrayPoint2, p3: ArrayPoint2, p4: ArrayPoint2 ): number { let T = this, same1 = P.equal(p1, p2), same2 = P.equal(p3, p4); if (same1 && same2) return 0; if (same1 && !same2) return T.isCollinear(p1, p3, p4) ? 0 : -1; if (!same1 && same2) return T.isCollinear(p1, p2, p3) ? 0 : -1; let k1 = T.slope(p1, p2), k2 = T.slope(p3, p4); if ((k1 == null && k2 === 0) || (k2 == null && k1 === 0)) return 2; if ((k1 == null && k2 == null) || Floats.equal(k1, k2)) {//平行 return V.whichSide(p1, p2, p3) == 0 ? 0 : -1 } else {//相交 return Floats.equal(k1 * k2, -1) ? 2 : 1 } } /** * P1,P2,P3 is collinear. * 三点是否共线 */ static isCollinear(p1: ArrayPoint2, p2: ArrayPoint2, p3: ArrayPoint2) { return V.whichSide(p1, p2, p3) == 0; } /** * Line is collinear with Line2. * 两直线或线段是否共线 */ static isCollinearLine(l1: Line | Segment, l2: Line | Segment) { let p1 = l1.p1(), p2 = l1.p2(), p3 = l2.p1(), p4 = l2.p2(); return this.isCollinear(p1, p2, p3) && this.isCollinear(p1, p2, p4); } /** * Returns the square of the distance from a point to a line. * The distance measured is the distance between the specified * point and the closest point on the infinitely-extended line * defined by the specified coordinates. If the specified point * intersects the line, this method returns 0.0. * * @return a value that is the square of the distance from the * specified point to the specified line. */ static distanceSqToPoint( p1: ArrayPoint2, p2: ArrayPoint2, p: ArrayPoint2) { let x1 = p1[0], y1 = p1[1], x2 = p2[0], y2 = p2[1], px = p[0], py = p[1]; // Adjust vectors relative to x1,y1 // x2,y2 becomes relative vector from x1,y1 to end of segment x2 -= x1; y2 -= y1; // px,py becomes relative vector from x1,y1 to test point px -= x1; py -= y1; let dot = px * x2 + py * y2, // dotprod is the length of the px,py vector // projected on the x1,y1=>x2,y2 vector times the // length of the x1,y1=>x2,y2 vector proj = dot * dot / (x2 * x2 + y2 * y2), // Distance to line is now the length of the relative point // vector minus the length of its projection onto the line lenSq = px * px + py * py - proj; if (lenSq < 0) lenSq = 0; return lenSq; } /** * Returns the distance from a point to a line. * The distance measured is the distance between the specified * point and the closest point on the infinitely-extended line * defined by the specified coordinates. If the specified point * intersects the line, this method returns 0.0. * * @return a value that is the distance from the specified * point to the specified line. */ static distanceToPoint( p1: ArrayPoint2, p2: ArrayPoint2, p: ArrayPoint2) { return Math.sqrt(this.distanceSqToPoint(p1, p2, p)); } constructor() constructor(x1: number, y1: number, x2: number, y2: number) constructor(x1?: number, y1?: number, x2?: number, y2?: number) { this.x1 = x1 || 0; this.y1 = y1 || 0; this.x2 = x2 || 0; this.y2 = y2 || 0; } toSegment() { return new Segment(this.x1, this.y1, this.x2, this.y2) } toVector(): Vector2 { return new V(this.x2-this.x1,this.y2-this.y1) } p1(): ArrayPoint2 p1(x: number, y: number): this p1(x?: number, y?: number): any { if (x == void 0) return [this.x1, this.y1]; this.x1 = x; this.y1 = y; return this } p2(): ArrayPoint2 p2(x: number, y: number): this p2(x?: number, y?: number): any { if (x == void 0) return [this.x2, this.y2]; this.x2 = x; this.y2 = y; return this } vertexes(): ArrayPoint2[] vertexes(ps: ArrayPoint2[]): this vertexes(ps?: ArrayPoint2[]): any { if (arguments.length == 0) { return [this.p1(), this.p2()] } let p1 = ps[0], p2 = ps[1]; this.p1(p1[0], p1[1]); return this.p2(p2[0], p2[1]) } set(l: Line | Segment): this set(p1: ArrayPoint2, p2: ArrayPoint2): this set(pt1: ArrayPoint2 | Line, pt2?: ArrayPoint2) { let len = arguments.length, p1 = len == 1 ? (<Line>pt1).p1() : <ArrayPoint2>pt1, p2 = len == 1 ? (<Line>pt1).p2() : <ArrayPoint2>pt2; this.x1 = p1[0]; this.y1 = p1[1]; this.x2 = p2[0]; this.y2 = p2[1]; return this } clone() { return <this>new Line(this.x1, this.y1, this.x2, this.y2) } equals(s: Line): boolean { return Line.position(s.p1(), s.p2(), this.p1(), this.p2()) == 0 } isEmpty() { return this.x1 == 0 && this.y1 == 0 && this.x2 == 0 && this.y2 == 0 } inside(s: ArrayPoint2 | Segment): boolean { let T = this; if (!s || T.isEmpty()) return false; if (Types.isArray(s)) return Line.isCollinear(T.p1(), T.p2(), <ArrayPoint2>s) if ((<Segment>s).isEmpty()) return false; return (<Segment>s).vertexes().every(p => { return T.inside(p) }) } onside(p: ArrayPoint2): boolean { return this.inside(p) } intersects(s: Segment | Line): boolean { if (!s || this.isEmpty() || s.isEmpty()) return false; let pos = Line.position(this.p1(), this.p2(), s.p1(), s.p2()); if (pos < 0) return false; if (s instanceof Segment) { return s.crossLine(this) != null } else { return true } } bounds(): Rect { return null } /** * 斜率 */ slope() { return (this.y2 - this.y1) / (this.x2 - this.x1) } perimeter(): number { return Infinity } private _cpOfLinePoint(p1: ArrayPoint2, p2: ArrayPoint2, p3: ArrayPoint2): ArrayPoint2 { let p1p2 = V.toVector(p1, p2), p1p3 = V.toVector(p1, p3), p = p1p3.getProject(p1p2), d = p.length(), pp = P.polar2xy(d, p.radian()); return [pp[0] + p1[0], pp[1] + p1[1]] } private _cpOfLineLine(p1: ArrayPoint2, p2: ArrayPoint2, p3: ArrayPoint2, p4: ArrayPoint2): ArrayPoint2 { let x1 = p1[0], y1 = p1[1], x2 = p2[0], y2 = p2[1], x3 = p3[0], y3 = p3[1], x4 = p4[0], y4 = p4[1]; if (Line.position(p1, p2, p3, p4) < 1) return null; let x = ((x1 - x2) * (x3 * y4 - x4 * y3) - (x3 - x4) * (x1 * y2 - x2 * y1)) / ((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4)), y = ((y1 - y2) * (x3 * y4 - x4 * y3) - (x1 * y2 - x2 * y1) * (y3 - y4)) / ((y1 - y2) * (x3 - x4) - (x1 - x2) * (y3 - y4)); return [x, y] } private _cpOfLineRay(p1: ArrayPoint2, p2: ArrayPoint2, p3: ArrayPoint2, rad: number): ArrayPoint2 { let p4 = P.toPoint(p3).toward(10, rad).toArray(), p = this._cpOfLineLine(p1, p2, p3, p4); if (!p) return null; return V.toVector(p3, p4).parallelTo(V.toVector(p3, p)) ? p : null } /** * Returns a vertical cross point with P. * 求点P到直线的垂直交点。 */ crossPoint(p: ArrayPoint2): ArrayPoint2 { return this._cpOfLinePoint(this.p1(), this.p2(), p) } /** * Returns a cross point of Line L. * 求与直线的交点。 * @return {ArrayPoint2} If this line is parallel to Line L, then return null. */ crossLine(l: Line): ArrayPoint2 { return this._cpOfLineLine(this.p1(), this.p2(), l.p1(), l.p2()); } /** * Returns a cross point of this line and Ray(p, rad). * 直线与射线的交点。 * @return {ArrayPoint2} If the cross point is not exist, then return null. */ crossRay(p: ArrayPoint2, rad: number) { return this._cpOfLineRay(this.p1(), this.p2(), p, rad) } } } } } import Line = JS.math.geom.Line;