@thi.ng/geom
Advanced tools
Comparing version 0.1.0 to 0.2.0
@@ -39,3 +39,3 @@ import { IObjectOf } from "@thi.ng/api/api"; | ||
point?: Vec2; | ||
geo?: IBoundsRaw<Vec2> & IVertices<Vec2, any> & IHiccupPathSegment; | ||
geo?: IBoundsRaw<Vec2> & IVertices<Vec2, any> & IHiccupPathSegment & IToCubic; | ||
} | ||
@@ -161,2 +161,5 @@ export interface SamplingOpts { | ||
} | ||
export interface IToCubic { | ||
toCubic(): Iterable<any>; | ||
} | ||
export interface IToPolygon<O> { | ||
@@ -163,0 +166,0 @@ toPolygon(opts?: O): any; |
import { ICopy } from "@thi.ng/api"; | ||
import { Vec2 } from "@thi.ng/vectors/vec2"; | ||
import { Attribs, HiccupArc2, IBounds, IBoundsRaw, IEdges, IVertices, JsonArc2, SamplingOpts } from "./api"; | ||
import { Cubic2 } from "./bezier2"; | ||
import { Rect2 } from "./rect2"; | ||
@@ -23,7 +24,8 @@ export declare class Arc2 implements IBounds<Rect2>, IBoundsRaw<Vec2>, ICopy<Arc2>, IEdges<Vec2[]>, IVertices<Vec2, number | Partial<SamplingOpts>> { | ||
pointAt(t: number): Vec2; | ||
pointAtTheta(theta: number): Vec2; | ||
pointAtTheta(theta: number, pos?: Vec2): Vec2; | ||
vertices(opts?: number | Partial<SamplingOpts>): Vec2[]; | ||
toCubic(): Cubic2[]; | ||
toHiccup(): (string | import("@thi.ng/api/api").IObjectOf<any>)[]; | ||
toHiccupPathSegments(): (string | number | Vec2)[][]; | ||
toHiccupPathSegments(): (string | number | boolean | Vec2)[][]; | ||
toJSON(): JsonArc2; | ||
} |
80
arc2.js
@@ -5,5 +5,7 @@ "use strict"; | ||
const is_plain_object_1 = require("@thi.ng/checks/is-plain-object"); | ||
const angle_1 = require("@thi.ng/math/angle"); | ||
const api_1 = require("@thi.ng/math/api"); | ||
const interval_1 = require("@thi.ng/math/interval"); | ||
const mix_1 = require("@thi.ng/math/mix"); | ||
const prec_1 = require("@thi.ng/math/prec"); | ||
const range_1 = require("@thi.ng/transducers/iter/range"); | ||
@@ -16,2 +18,3 @@ const push_1 = require("@thi.ng/transducers/rfn/push"); | ||
const api_2 = require("./api"); | ||
const bezier2_1 = require("./bezier2"); | ||
const bounds_1 = require("./internal/bounds"); | ||
@@ -33,5 +36,6 @@ const edges_1 = require("./internal/edges"); | ||
const m = a.subNew(b).mulN(0.5); | ||
const p = new vec2_1.Vec2([co * m.x + si * m.y, -si * m.x + co * m.y]); | ||
const px2 = p.x * p.x; | ||
const py2 = p.y * p.y; | ||
const px = co * m.x + si * m.y; | ||
const py = -si * m.x + co * m.y; | ||
const px2 = px * px; | ||
const py2 = py * py; | ||
const l = px2 / (r.x * r.x) + py2 / (r.y * r.y); | ||
@@ -43,8 +47,8 @@ l > 1 && r.mulN(Math.sqrt(l)); | ||
const rypx = ry2 * px2; | ||
const root = ((large === clockwise) ? -1 : 1) * | ||
Math.sqrt(Math.abs((rx2 * ry2 - rxpy - rypx)) / (rxpy + rypx)); | ||
const tc = new vec2_1.Vec2([r.x * p.y / r.y, -r.y * p.x / r.x]).mulN(root); | ||
const c = new vec2_1.Vec2([co * tc.x - si * tc.y, si * tc.x + co * tc.y]).add(a.mixNewN(b)); | ||
const d1 = new vec2_1.Vec2([(p.x - tc.x) / r.x, (p.y - tc.y) / r.y]); | ||
const d2 = new vec2_1.Vec2([(-p.x - tc.x) / r.x, (-p.y - tc.y) / r.y]); | ||
const rad = ((large === clockwise) ? -1 : 1) * | ||
Math.sqrt(Math.max(0, rx2 * ry2 - rxpy - rypx) / (rxpy + rypx)); | ||
const tc = new vec2_1.Vec2([rad * r.x / r.y * py, rad * -r.y / r.x * px]); | ||
const c = new vec2_1.Vec2([co * tc.x - si * tc.y + (a.x + b.x) / 2, si * tc.x + co * tc.y + (a.y + b.y) / 2]); | ||
const d1 = new vec2_1.Vec2([(px - tc.x) / r.x, (py - tc.y) / r.y]); | ||
const d2 = new vec2_1.Vec2([(-px - tc.x) / r.x, (-py - tc.y) / r.y]); | ||
const theta = vec2_1.Vec2.X_AXIS.angleBetween(d1, true); | ||
@@ -92,4 +96,5 @@ let delta = d1.angleBetween(d2, true); | ||
} | ||
pointAtTheta(theta) { | ||
return new vec2_1.Vec2([Math.cos(theta), Math.sin(theta)]) | ||
pointAtTheta(theta, pos) { | ||
return (pos || new vec2_1.Vec2()) | ||
.setS(Math.cos(theta), Math.sin(theta)) | ||
.mul(this.r) | ||
@@ -126,16 +131,55 @@ .rotate(this.axis) | ||
} | ||
toCubic() { | ||
const p = this.pointAtTheta(this.start); | ||
const q = this.pointAtTheta(this.end); | ||
const [rx, ry] = this.r; | ||
const [sphi, cphi] = angle_1.sincos(this.axis); | ||
const dx = cphi * (p.x - q.x) / 2 + sphi * (p.y - q.y) / 2; | ||
const dy = -sphi * (p.x - q.x) / 2 + cphi * (p.y - q.y) / 2; | ||
if ((dx === 0 && dy === 0) || this.r.magSq() < api_1.EPS) { | ||
return [bezier2_1.Cubic2.fromLine(p, q, Object.assign({}, this.attribs))]; | ||
} | ||
const mapP = (x, y) => { | ||
x *= rx; | ||
y *= ry; | ||
return new vec2_1.Vec2([ | ||
cphi * x - sphi * y, | ||
sphi * x + cphi * y | ||
]).add(this.pos); | ||
}; | ||
const res = []; | ||
const delta = this.end - this.start; | ||
const n = Math.max(prec_1.roundEps(Math.abs(delta) / api_1.HALF_PI), 1); | ||
// https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/core/svg/svg_path_parser.cc#L253 | ||
const d = delta / n; | ||
const t = 8 / 6 * Math.tan(0.25 * d); | ||
if (!isFinite(t)) { | ||
return [bezier2_1.Cubic2.fromLine(p, q)]; | ||
} | ||
for (let i = n, theta = this.start; i > 0; i--, theta += d) { | ||
const [s1, c1] = angle_1.sincos(theta); | ||
const [s2, c2] = angle_1.sincos(theta + d); | ||
const curve = new bezier2_1.Cubic2([ | ||
mapP(c1, s1), | ||
mapP(c1 - s1 * t, s1 + c1 * t), | ||
mapP(c2 + s2 * t, s2 - c2 * t), | ||
mapP(c2, s2), | ||
]); | ||
res.push(curve); | ||
} | ||
return res; | ||
} | ||
toHiccup() { | ||
return ["path", this.attribs, | ||
[ | ||
return ["path", this.attribs, [ | ||
["M", this.pointAtTheta(this.start)], | ||
...this.toHiccupPathSegments() | ||
] | ||
]; | ||
]]; | ||
} | ||
toHiccupPathSegments() { | ||
return [["A", | ||
this.r, | ||
this.r.x, | ||
this.r.y, | ||
this.axis, | ||
this.xl ? 1 : 0, | ||
this.clockwise ? 1 : 0, | ||
this.xl, | ||
this.clockwise, | ||
this.pointAtTheta(this.end) | ||
@@ -142,0 +186,0 @@ ]]; |
import { ICopy } from "@thi.ng/api"; | ||
import { IMath, ReadonlyVec, Vec } from "@thi.ng/vectors/api"; | ||
import { Vec2 } from "@thi.ng/vectors/vec2"; | ||
import { Attribs, IVertices, JsonCubic2, JsonQuadratic2, SamplingOpts } from "./api"; | ||
import { Attribs, IToCubic, IVertices, JsonCubic2, JsonQuadratic2, SamplingOpts } from "./api"; | ||
import { PointContainer2 } from "./container2"; | ||
export declare const mixQuadratic1: (a: number, b: number, c: number, t: number) => number; | ||
export declare const mixCubic1: (a: number, b: number, c: number, d: number, t: number) => number; | ||
export declare const mixQuadratic: <T extends ICopy<T> & IMath<T>>(a: T, b: T, c: T, t: number) => T; | ||
export declare const mixCubic: <T extends ICopy<T> & IMath<T>>(a: T, b: T, c: T, d: T, t: number) => T; | ||
export declare class Cubic2 extends PointContainer2 implements IVertices<Vec2, number | Partial<SamplingOpts>> { | ||
export declare class Cubic2 extends PointContainer2 implements IToCubic, IVertices<Vec2, number | Partial<SamplingOpts>> { | ||
static fromJSON(spec: JsonCubic2): Cubic2; | ||
static fromLine(a: Vec2, b: Vec2): Cubic2; | ||
static fromLine(a: Vec2, b: Vec2, attribs?: Attribs): Cubic2; | ||
copy(): Cubic2; | ||
@@ -25,2 +23,3 @@ boundsRaw(): [Vec2, Vec2]; | ||
vertices(opts?: Partial<SamplingOpts>): any; | ||
toCubic(): this[]; | ||
toHiccup(): (string | import("@thi.ng/api/api").IObjectOf<any>)[]; | ||
@@ -34,5 +33,5 @@ toHiccupPathSegments(): (string | Vec2)[][]; | ||
} | ||
export declare class Quadratic2 extends PointContainer2 implements IVertices<Vec2, number | Partial<SamplingOpts>> { | ||
export declare class Quadratic2 extends PointContainer2 implements IToCubic, IVertices<Vec2, number | Partial<SamplingOpts>> { | ||
static fromJSON(spec: JsonQuadratic2): Quadratic2; | ||
static fromLine(a: Vec2, b: Vec2): Quadratic2; | ||
static fromLine(a: Vec2, b: Vec2, attribs?: Attribs): Quadratic2; | ||
copy(): Quadratic2; | ||
@@ -50,3 +49,3 @@ boundsRaw(): [Vec2, Vec2]; | ||
vertices(opts?: number | Partial<SamplingOpts>): any; | ||
toCubic(): Cubic2; | ||
toCubic(): Cubic2[]; | ||
toHiccup(): (string | import("@thi.ng/api/api").IObjectOf<any>)[]; | ||
@@ -53,0 +52,0 @@ toHiccupPathSegments(): (string | Vec2)[][]; |
@@ -6,2 +6,3 @@ "use strict"; | ||
const interval_1 = require("@thi.ng/math/interval"); | ||
const mix_1 = require("@thi.ng/math/mix"); | ||
const vec2_1 = require("@thi.ng/vectors/vec2"); | ||
@@ -12,12 +13,2 @@ const api_1 = require("./api"); | ||
const sampler_1 = require("./sampler"); | ||
exports.mixQuadratic1 = (a, b, c, t) => { | ||
const s = 1 - t; | ||
return a * s * s + b * 2 * s * t + c * t * t; | ||
}; | ||
exports.mixCubic1 = (a, b, c, d, t) => { | ||
const t2 = t * t; | ||
const s = 1 - t; | ||
const s2 = s * s; | ||
return a * s2 * s + b * 3 * s2 * t + c * 3 * t2 * s + d * t2 * t; | ||
}; | ||
exports.mixQuadratic = (a, b, c, t) => { | ||
@@ -47,3 +38,3 @@ const s = 1 - t; | ||
if (t > 0 && t < 1) { | ||
const x = exports.mixCubic1(pa, pb, pc, pd, t); | ||
const x = mix_1.mixCubic(pa, pb, pc, pd, t); | ||
x < l && (l = x); | ||
@@ -67,4 +58,4 @@ x > h && (h = x); | ||
} | ||
static fromLine(a, b) { | ||
return new Cubic2([a, a.mixNewN(b, 1 / 3), b.mixNewN(a, 1 / 3), b]); | ||
static fromLine(a, b, attribs) { | ||
return new Cubic2([a, a.mixNewN(b, 1 / 3), b.mixNewN(a, 1 / 3), b], attribs); | ||
} | ||
@@ -126,3 +117,3 @@ copy() { | ||
for (let t = 0; t < opts.num; t++) { | ||
res.push(exports.mixCubic1(a.x, b.x, c.x, d.x, t * delta), exports.mixCubic1(a.y, b.y, c.y, d.y, t * delta)); | ||
res.push(mix_1.mixCubic(a.x, b.x, c.x, d.x, t * delta), mix_1.mixCubic(a.y, b.y, c.y, d.y, t * delta)); | ||
} | ||
@@ -132,2 +123,5 @@ opts.last && res.push(d.x, d.y); | ||
} | ||
toCubic() { | ||
return [this]; | ||
} | ||
toHiccup() { | ||
@@ -154,4 +148,4 @@ return ["path", this.attribs, | ||
} | ||
static fromLine(a, b) { | ||
return new Quadratic2([a, a.mixNewN(b), b]); | ||
static fromLine(a, b, attribs) { | ||
return new Quadratic2([a, a.mixNewN(b), b], attribs); | ||
} | ||
@@ -227,3 +221,3 @@ copy() { | ||
for (let t = 0; t < opts.num; t++) { | ||
res.push(exports.mixQuadratic1(a.x, b.x, c.x, t * delta), exports.mixQuadratic1(a.y, b.y, c.y, t * delta)); | ||
res.push(mix_1.mixQuadratic(a.x, b.x, c.x, t * delta), mix_1.mixQuadratic(a.y, b.y, c.y, t * delta)); | ||
} | ||
@@ -235,8 +229,10 @@ opts.last && res.push(c.x, c.y); | ||
const [a, b, c] = this.points; | ||
return new Cubic2([ | ||
a.copy(), | ||
a.mulNewN(1 / 3).maddN(b, 2 / 3), | ||
c.mulNewN(1 / 3).maddN(b, 2 / 3), | ||
c.copy() | ||
]); | ||
return [ | ||
new Cubic2([ | ||
a.copy(), | ||
a.mulNewN(1 / 3).maddN(b, 2 / 3), | ||
c.mulNewN(1 / 3).maddN(b, 2 / 3), | ||
c.copy() | ||
]) | ||
]; | ||
} | ||
@@ -243,0 +239,0 @@ toHiccup() { |
@@ -6,2 +6,13 @@ # Change Log | ||
# [0.2.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/geom@0.1.0...@thi.ng/geom@0.2.0) (2018-10-21) | ||
### Features | ||
* **geom:** add IToCubic, add/update impls ([ce131d4](https://github.com/thi-ng/umbrella/commit/ce131d4)) | ||
# 0.1.0 (2018-10-17) | ||
@@ -8,0 +19,0 @@ |
import { ReadonlyVec, Vec } from "@thi.ng/vectors/api"; | ||
import { Vec2 } from "@thi.ng/vectors/vec2"; | ||
import { Attribs, HiccupLine2, SamplingOpts } from "./api"; | ||
import { Attribs, HiccupLine2, IToCubic, SamplingOpts } from "./api"; | ||
import { Cubic2 } from "./bezier2"; | ||
import { PointContainer2 } from "./container2"; | ||
import { Rect2 } from "./rect2"; | ||
export declare class Line2 extends PointContainer2 { | ||
export declare class Line2 extends PointContainer2 implements IToCubic { | ||
constructor(points: Vec2[], attribs?: Attribs); | ||
@@ -15,2 +16,3 @@ copy(): Line2; | ||
clipRect(bounds: [Vec2, Vec2] | Rect2): Line2; | ||
toCubic(): Cubic2[]; | ||
toHiccup(): HiccupLine2; | ||
@@ -17,0 +19,0 @@ toHiccupPathSegments(): (string | Vec2)[][]; |
@@ -7,2 +7,3 @@ "use strict"; | ||
const vec2_1 = require("@thi.ng/vectors/vec2"); | ||
const bezier2_1 = require("./bezier2"); | ||
const container2_1 = require("./container2"); | ||
@@ -58,2 +59,5 @@ const liang_barsky_1 = require("./internal/liang-barsky"); | ||
} | ||
toCubic() { | ||
return [bezier2_1.Cubic2.fromLine(this.points[0], this.points[1], Object.assign({}, this.attribs))]; | ||
} | ||
toHiccup() { | ||
@@ -60,0 +64,0 @@ return ["line", this.attribs || {}, this.points[0], this.points[1]]; |
{ | ||
"name": "@thi.ng/geom", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "2D/3D geometry primitives", | ||
@@ -24,14 +24,14 @@ "main": "./index.js", | ||
"@types/mocha": "^5.2.5", | ||
"@types/node": "^10.5.5", | ||
"@types/node": "^10.12.0", | ||
"mocha": "^5.2.0", | ||
"nyc": "^12.0.2", | ||
"typedoc": "^0.12.0", | ||
"typescript": "^3.0.1" | ||
"nyc": "^13.1.0", | ||
"typedoc": "^0.13.0", | ||
"typescript": "^3.1.3" | ||
}, | ||
"dependencies": { | ||
"@thi.ng/api": "^4.2.2", | ||
"@thi.ng/checks": "^1.5.12", | ||
"@thi.ng/math": "^0.1.0", | ||
"@thi.ng/transducers": "^2.2.0", | ||
"@thi.ng/vectors": "^1.4.0" | ||
"@thi.ng/api": "^4.2.3", | ||
"@thi.ng/checks": "^1.5.13", | ||
"@thi.ng/math": "^0.2.0", | ||
"@thi.ng/transducers": "^2.2.1", | ||
"@thi.ng/vectors": "^1.4.1" | ||
}, | ||
@@ -50,3 +50,3 @@ "keywords": [ | ||
}, | ||
"gitHead": "6e563377f8cdbdda904559c956a2a558eb9fed64" | ||
"gitHead": "5bb513915cb3c533bd4278f6f365389b3664f4d1" | ||
} |
@@ -17,2 +17,3 @@ import { Vec2 } from "@thi.ng/vectors/vec2"; | ||
vertices(opts?: number | Partial<SamplingOpts>): Vec2[]; | ||
normalize(): Path2; | ||
toPolygon(opts?: number | Partial<SamplingOpts>): Polygon2; | ||
@@ -19,0 +20,0 @@ toPolyline(res?: number): Polyline2; |
14
path2.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const is_number_1 = require("@thi.ng/checks/is-number"); | ||
const implements_function_1 = require("@thi.ng/checks/implements-function"); | ||
const angle_1 = require("@thi.ng/math/angle"); | ||
@@ -16,2 +17,4 @@ const eqdelta_1 = require("@thi.ng/math/eqdelta"); | ||
const douglas_peucker_1 = require("./internal/douglas\u2013peucker"); | ||
const mapcat_1 = require("@thi.ng/transducers/xform/mapcat"); | ||
const map_1 = require("@thi.ng/transducers/xform/map"); | ||
class Path2 { | ||
@@ -65,3 +68,3 @@ constructor(segments, attribs) { | ||
else { | ||
res.push(s); | ||
res.push(Object.assign({}, s)); | ||
} | ||
@@ -76,4 +79,3 @@ } | ||
} | ||
this.segments = res; | ||
return this; | ||
return new Path2(res); | ||
} | ||
@@ -87,3 +89,2 @@ vertices(opts) { | ||
const v = ensure_array_1.ensureArray(s.geo.vertices(Object.assign({}, _opts, { last: i === n && !this.closed }))); | ||
// console.log(i, SegmentType[s.type], v.toString()); | ||
verts = verts.concat(v); | ||
@@ -94,2 +95,7 @@ } | ||
} | ||
normalize() { | ||
return new Path2([...mapcat_1.mapcat((s) => implements_function_1.implementsFunction(s.geo, "toCubic") ? | ||
map_1.map((c) => ({ type: 4 /* CUBIC */, geo: c }), s.geo.toCubic()) : | ||
[s], this.segments)]); | ||
} | ||
toPolygon(opts) { | ||
@@ -96,0 +102,0 @@ return new polygon2_1.Polygon2(this.vertices(opts)); |
import { ICopy, IToHiccup } from "@thi.ng/api/api"; | ||
import { ReadonlyVec, Vec } from "@thi.ng/vectors/api"; | ||
import { Vec2 } from "@thi.ng/vectors/vec2"; | ||
import { Attribs, IArcLength, IArea, IEdges, IVertices, SamplingOpts, SubdivKernel } from "./api"; | ||
import { Attribs, IArcLength, IArea, IEdges, IToCubic, IVertices, SamplingOpts, SubdivKernel } from "./api"; | ||
import { Cubic2 } from "./bezier2"; | ||
import { PointContainer2 } from "./container2"; | ||
export declare class Polyline2 extends PointContainer2 implements IArcLength, IArea, ICopy<Polyline2>, IEdges<Vec2[]>, IVertices<Vec2, void | number | Partial<SamplingOpts>>, IToHiccup { | ||
export declare class Polyline2 extends PointContainer2 implements IArcLength, IArea, ICopy<Polyline2>, IEdges<Vec2[]>, IVertices<Vec2, void | number | Partial<SamplingOpts>>, IToCubic, IToHiccup { | ||
copy(): Polyline2; | ||
@@ -14,2 +15,3 @@ edges(): IterableIterator<Vec2[]>; | ||
vertices(opts?: number | Partial<SamplingOpts>): any[]; | ||
toCubic(): IterableIterator<Cubic2>; | ||
toHiccup(): (string | import("@thi.ng/api/api").IObjectOf<any>)[]; | ||
@@ -16,0 +18,0 @@ toHiccupPathSegments(): any[]; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const is_plain_object_1 = require("@thi.ng/checks/is-plain-object"); | ||
const map_1 = require("@thi.ng/transducers/xform/map"); | ||
const partition_1 = require("@thi.ng/transducers/xform/partition"); | ||
const bezier2_1 = require("./bezier2"); | ||
const container2_1 = require("./container2"); | ||
@@ -44,2 +47,5 @@ const arc_length_1 = require("./internal/arc-length"); | ||
} | ||
toCubic() { | ||
return map_1.map(([a, b]) => bezier2_1.Cubic2.fromLine(a, b, Object.assign({}, this.attribs)), partition_1.partition(2, 1, this.points)); | ||
} | ||
toHiccup() { | ||
@@ -46,0 +52,0 @@ return this._toHiccup("polyline"); |
938054
3443
- Removed@thi.ng/math@0.1.0(transitive)
Updated@thi.ng/api@^4.2.3
Updated@thi.ng/checks@^1.5.13
Updated@thi.ng/math@^0.2.0
Updated@thi.ng/transducers@^2.2.1
Updated@thi.ng/vectors@^1.4.1