Skip to content

Commit

Permalink
feat(geom): re-add arc, cubic, quadratic ops, splitAt & other ops
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Jan 16, 2019
1 parent 1843702 commit fea8fbe
Show file tree
Hide file tree
Showing 16 changed files with 497 additions and 42 deletions.
109 changes: 100 additions & 9 deletions packages/geom3/src/api.ts
@@ -1,10 +1,14 @@
import { ICopy, IObjectOf, IToHiccup } from "@thi.ng/api";
import { isNumber } from "@thi.ng/checks";
import { equiv } from "@thi.ng/equiv";
import { cossin } from "@thi.ng/math";
import {
copy,
maddN,
add2,
maddN2,
mul2,
ReadonlyVec,
rotateZ,
set,
Vec
} from "@thi.ng/vectors3";
import { copyPoints } from "./internal/copy-points";
Expand Down Expand Up @@ -180,7 +184,94 @@ export class AABB implements
}

copy() {
return new AABB(copy(this.pos), copy(this.size), { ...this.attribs });
return new AABB(set([], this.pos), set([], this.size), { ...this.attribs });
}
}

export class Arc implements
HiccupShape,
IHiccupPathSegment {

pos: Vec;
r: Vec;
start: number;
end: number;
axis: number;
xl: boolean;
cw: boolean;
attribs: Attribs;

constructor(
pos: Vec,
r: Vec,
axis: number,
start: number,
end: number,
xl = false,
cw = false,
attribs?: Attribs) {

this.pos = pos;
this.r = r;
this.axis = axis;
this.start = start;
this.end = end;
this.xl = xl;
this.cw = cw;
this.attribs = attribs;
}

get type() {
return Type.ARC;
}

copy() {
return new Arc(
set([], this.pos),
set([], this.r),
this.axis,
this.start,
this.end,
this.xl,
this.cw,
{ ...this.attribs }
);
}

equiv(o: any) {
return o instanceof Arc &&
equiv(this.pos, o.pos) &&
equiv(this.r, o.r) &&
this.start === o.start &&
this.end === o.end &&
this.axis === o.axis &&
this.xl === o.xl &&
this.cw && o.cw;
}

pointAtTheta(theta: number, out: Vec = []) {
return add2(null, rotateZ(null, mul2(out, cossin(theta), this.r), this.axis), this.pos);
}

toHiccup() {
return ["path", this.attribs, [
["M", this.pointAtTheta(this.start)],
...this.toHiccupPathSegments()
]];
}

toHiccupPathSegments() {
return [
[
"A",
this.r[0],
this.r[1],
this.axis,
this.xl,
this.cw,
this.pointAtTheta(this.end)
]
];
}
}

Expand All @@ -202,7 +293,7 @@ export class Circle implements
}

copy() {
return new Circle(copy(this.pos), this.r, { ...this.attribs });
return new Circle(set([], this.pos), this.r, { ...this.attribs });
}

toHiccup() {
Expand Down Expand Up @@ -254,7 +345,7 @@ export class Ellipse implements
}

copy() {
return new Ellipse(copy(this.pos), copy(this.r), { ...this.attribs });
return new Ellipse(set([], this.pos), set([], this.r), { ...this.attribs });
}

toHiccup() {
Expand Down Expand Up @@ -417,11 +508,11 @@ export class Ray implements
}

copy() {
return new Ray(copy(this.pos), copy(this.dir), { ...this.attribs });
return new Ray(set([], this.pos), set([], this.dir), { ...this.attribs });
}

toHiccup() {
return ["line", this.attribs, this.pos, maddN([], this.pos, this.dir, 1e6)];
return ["line", this.attribs, this.pos, maddN2([], this.pos, this.dir, 1e6)];
}
}

Expand All @@ -443,7 +534,7 @@ export class Rect implements
}

copy() {
return new Rect(copy(this.pos), copy(this.size), { ...this.attribs });
return new Rect(set([], this.pos), set([], this.size), { ...this.attribs });
}

toHiccup() {
Expand All @@ -469,7 +560,7 @@ export class Sphere implements
}

copy() {
return new Sphere(copy(this.pos), this.r, { ...this.attribs });
return new Sphere(set([], this.pos), this.r, { ...this.attribs });
}

toHiccup() {
Expand Down
81 changes: 78 additions & 3 deletions packages/geom3/src/ctors/cubic.ts
@@ -1,5 +1,17 @@
import { mixN, Vec } from "@thi.ng/vectors3";
import { Attribs, Cubic } from "../api";
import {
EPS,
HALF_PI,
roundEps,
sincos
} from "@thi.ng/math";
import {
add2,
magSq2,
mixN2,
set2,
Vec
} from "@thi.ng/vectors3";
import { Arc, Attribs, Cubic } from "../api";
import { argAttribs } from "../internal/args";

export function cubic(a: Vec, b: Vec, c: Vec, d: Vec, attribs?: Attribs): Cubic;
Expand All @@ -9,6 +21,69 @@ export function cubic(...args: any[]) {
return new Cubic(args.length === 1 ? args[0] : args, attr);
}

export const cubicFromArc =
(arc: Arc) => {
const p = arc.pointAtTheta(arc.start);
const q = arc.pointAtTheta(arc.end);
const [rx, ry] = arc.r;
const [sphi, cphi] = sincos(arc.axis);
const dx = cphi * (p[0] - q[0]) / 2 + sphi * (p[1] - q[1]) / 2;
const dy = -sphi * (p[0] - q[0]) / 2 + cphi * (p[1] - q[1]) / 2;
if ((dx === 0 && dy === 0) || magSq2(arc.r) < EPS) {
return [cubicFromLine(p, q, { ...arc.attribs })];
}

const mapP = (x: number, y: number) => {
x *= rx;
y *= ry;
return add2(
null,
[
cphi * x - sphi * y,
sphi * x + cphi * y
],
arc.pos
);
};

const res: Cubic[] = [];
const delta = arc.end - arc.start;
const n = Math.max(roundEps(Math.abs(delta) / 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 [cubicFromLine(p, q, { ...arc.attribs })];
}
for (let i = n, theta = arc.start; i > 0; i-- , theta += d) {
const [s1, c1] = sincos(theta);
const [s2, c2] = sincos(theta + d);
const curve = new Cubic(
[
mapP(c1, s1),
mapP(c1 - s1 * t, s1 + c1 * t),
mapP(c2 + s2 * t, s2 - c2 * t),
mapP(c2, s2),
],
{ ...arc.attribs }
);
res.push(curve);
}
return res;
};

export const cubicFromLine =
(a: Vec, b: Vec, attribs?: Attribs) =>
new Cubic([a, mixN([], a, b, 1 / 3), mixN([], b, a, 1 / 3), b], attribs);
new Cubic([a, mixN2([], a, b, 1 / 3), mixN2([], b, a, 1 / 3), b], attribs);

export const cubicFromQuadratic =
(a: Vec, b: Vec, c: Vec, attribs?: Attribs) =>
new Cubic(
[
set2([], a),
mixN2([], a, b, 2 / 3),
mixN2([], c, b, 2 / 3),
set2([], c)
],
attribs
);
2 changes: 2 additions & 0 deletions packages/geom3/src/index.ts
Expand Up @@ -14,6 +14,7 @@ export * from "./ctors/triangle";

export * from "./ops/arc-length";
export * from "./ops/area";
export * from "./ops/as-cubic";
export * from "./ops/as-polygon";
export * from "./ops/as-svg";
export * from "./ops/bounds";
Expand All @@ -28,6 +29,7 @@ export * from "./ops/point-at";
export * from "./ops/point-inside";
export * from "./ops/resample";
export * from "./ops/simplify";
export * from "./ops/split-at";
export * from "./ops/subdiv-curve";
export * from "./ops/tangent-at";
export * from "./ops/transform";
Expand Down
3 changes: 2 additions & 1 deletion packages/geom3/src/internal/bounds.ts
@@ -1,4 +1,5 @@
import { max, min, Vec } from "@thi.ng/vectors3";
import { VecPair } from "../api";

/**
* Computes the nD bounds of given vectors. `vmin` should be initialized
Expand All @@ -12,7 +13,7 @@ import { max, min, Vec } from "@thi.ng/vectors3";
* @param vmax
*/
export const boundsRaw =
(pts: ReadonlyArray<Vec>, vmin: Vec, vmax: Vec): [Vec, Vec] => {
(pts: ReadonlyArray<Vec>, vmin: Vec, vmax: Vec): VecPair => {

for (let i = pts.length; --i >= 0;) {
const p = pts[i];
Expand Down
4 changes: 2 additions & 2 deletions packages/geom3/src/internal/copy-points.ts
@@ -1,4 +1,4 @@
import { copy, ReadonlyVec } from "@thi.ng/vectors3";
import { ReadonlyVec, set } from "@thi.ng/vectors3";

export const copyPoints =
(pts: ReadonlyVec[]) => pts.map((p) => copy(p));
(pts: ReadonlyVec[]) => pts.map((p) => set([], p));
24 changes: 21 additions & 3 deletions packages/geom3/src/internal/sampler.ts
@@ -1,13 +1,14 @@
import { isPlainObject } from "@thi.ng/checks";
import { peek } from "@thi.ng/transducers";
import {
copy,
dist,
mixN,
normalize,
ReadonlyVec,
sub,
Vec
Vec,
eqDelta,
set
} from "@thi.ng/vectors3";
import { DEFAULT_SAMPLES, SamplingOpts, VecPair } from "../api";
import { copyPoints } from "./copy-points";
Expand Down Expand Up @@ -83,6 +84,23 @@ export class Sampler {
undefined;
}

splitAt(t: number) {
if (t <= 0 || t >= 1) {
return [this.points];
}
const p = this.pointAt(t);
const i = Math.max(1, this.indexAt(t));
const head = this.points.slice(0, i);
const tail = this.points.slice(i);
if (!eqDelta(head[i - 1], p)) {
head.push(p);
}
if (!eqDelta(tail[0], p)) {
tail.unshift(p);
}
return [head, tail];
}

indexAt(t: number) {
const pts = this.points;
const n = pts.length - 1;
Expand Down Expand Up @@ -118,7 +136,7 @@ export class Sampler {
result.push(mixN([], pts[i - 1], pts[i], (ct - p) / (index[i] - p)));
}
if (includeLast) {
result.push(copy(peek(pts)));
result.push(set([], peek(pts)));
}
return result;
}
Expand Down

0 comments on commit fea8fbe

Please sign in to comment.