Skip to content

Commit

Permalink
feat(geom): add/update interfaces & impls
Browse files Browse the repository at this point in the history
- add CollateOpts, update collate() in both containers
- add generics for IVertices
- add ArcSamplingOpts
- update edges(), vertices() for Arc2 & Circle2
- add .toJSON() impls
- add @thi.ng/checks dep
  • Loading branch information
postspectacular committed Sep 30, 2018
1 parent 775cc8a commit 2657df6
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 66 deletions.
1 change: 1 addition & 0 deletions packages/geom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
},
"dependencies": {
"@thi.ng/api": "^4.2.1",
"@thi.ng/checks": "^1.5.11",
"@thi.ng/transducers": "^2.1.6",
"@thi.ng/vectors": "^1.3.0"
},
Expand Down
49 changes: 41 additions & 8 deletions packages/geom/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,36 @@ export interface IArcLength {
arcLength(): number;
}

export interface ArcSamplingOpts {
/**
* Number of points to sample & return. Defaults to
* `Arc2.DEFAULT_RES` if neither this nor `theta` option is given.
*/
num: number;
/**
* Target angle between sampled points. If greater than the actual
* range of the arc, only the 2 end points will be returned. This
* option is used to derive a `num` value and will take priority if
* `num` is given as well.
*
* This option is useful to adapt the sampling based on actual angle
* range, rather than a fixed number of samples.
*/
theta: number;
/**
* If `false` (default), the arc's end point will be omitted from
* the result array and if `num` option was given, results in
* `num-1` points. However, this option has no influence on the
* angular resolution calculation.
*
* This option is useful when building paths of consecutive
* segments, where the end point of one segment coincides with the
* start point of the next segment and so can be used to avoid
* duplicate vertices in the concatenated result.
*/
includeLast: boolean;
}

export interface IBounds<T> {
bounds(): T;
/**
Expand All @@ -50,19 +80,22 @@ export interface ICentroid<T> {
centroid(c?: T): T;
}

export interface CollateOpts {
buf: Vec;
start: number;
cstride: number;
estride: number;
}

export interface ICollate {
/**
* Collates all points into a single buffer and remaps existing
* vertices (by default). Points will written from given `start`
* index, using layout defined by `cstride` and `estride`.
*
* @param remap
* @param buf
* @param start
* @param cstride
* @param estride
* @param opts
*/
collate(remap?: boolean, buf?: Vec, start?: number, cstride?: number, estride?: number): this;
collate(opts?: Partial<CollateOpts>): Vec;
}

export interface IEdges<T> {
Expand All @@ -74,6 +107,6 @@ export interface IToPolygon2 {
toPolygon2(opts?: any): any;
}

export interface IVertices<T> {
vertices(opts?: any): Iterable<T>;
export interface IVertices<T, O> {
vertices(opts?: O): Iterable<T>;
}
58 changes: 42 additions & 16 deletions packages/geom/src/arc2.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { TAU } from "@thi.ng/vectors/math";
import { Vec2, setS2, mul2, rotate2, add2 } from "@thi.ng/vectors/vec2";
import { IVertices, IEdges } from "./api";
import { isNumber } from "@thi.ng/checks/is-number";
import { Vec } from "@thi.ng/vectors/api";
import { TAU } from "@thi.ng/vectors/math";
import {
add2,
rotate2,
setS2,
Vec2
} from "@thi.ng/vectors/vec2";
import { ArcSamplingOpts, IEdges, IVertices } from "./api";
import { edges } from "./func/edges";

export class Arc2 implements
IEdges<Vec2[]>,
IVertices<Vec2> {
IVertices<Vec2, number | Partial<ArcSamplingOpts>> {

static from2Points(
a: Readonly<Vec2>,
Expand Down Expand Up @@ -65,24 +71,44 @@ export class Arc2 implements
this.xl = xl;
}

edges(num = Arc2.DEFAULT_RES) {
return edges(this.vertices(num));
edges(opts?: Partial<ArcSamplingOpts>) {
return edges(this.vertices(opts));
}

vertices(num = Arc2.DEFAULT_RES) {
const res: Vec = new Array(num * 2);
vertices(opts?: number | Partial<ArcSamplingOpts>) {
opts = isNumber(opts) ?
{ num: opts, includeLast: true } :
{ num: Arc2.DEFAULT_RES, ...opts };
let num: number;
const start = this.start;
const delta = (this.end - start) / (num - 1);
let delta = this.end - start;
num = opts.theta ?
Math.max(Math.ceil(1 + delta / opts.theta), 2) :
opts.num;
delta /= (num - 1);
opts.includeLast !== true && num--;
const pts: Vec = new Array(num * 2);
const pos = this.pos;
const r = this.r;
const [rx, ry] = this.r;
const axis = this.axis;
for (let i = 0, j = 0; i < num; i++ , j += 2) {
const t = start + i * delta;
setS2(res, Math.cos(t), Math.sin(t), j);
mul2(res, r.buf, j, r.i, 1, r.s);
rotate2(res, axis, j);
add2(res, pos.buf, j, pos.i, 1, pos.s);
setS2(pts, Math.cos(t) * rx, Math.sin(t) * ry, j);
rotate2(pts, axis, j);
add2(pts, pos.buf, j, pos.i, 1, pos.s);
}
return Vec2.mapBuffer(res, num);
return Vec2.mapBuffer(pts, num);
}

toJSON() {
return {
type: "arc2",
pos: this.pos.toJSON(),
r: this.r.toJSON(),
start: this.start,
end: this.end,
xl: this.xl,
clockwise: this.clockwise
};
}
}
}
45 changes: 37 additions & 8 deletions packages/geom/src/circle2.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { IObjectOf, IToHiccup } from "@thi.ng/api/api";
import { IToPolygon2, IVertices, IBounds, ICentroid, IArcLength } from "@thi.ng/geom/src/api";
import { isNumber } from "@thi.ng/checks/is-number";
import { normRange } from "@thi.ng/transducers/iter/norm-range";
import { Vec } from "@thi.ng/vectors/api";
import { mix1, PI, TAU } from "@thi.ng/vectors/math";
import { setS2, toCartesian2, Vec2 } from "@thi.ng/vectors/vec2";
import {
ArcSamplingOpts,
IArcLength,
IBounds,
ICentroid,
IToPolygon2,
IVertices
} from "./api";
import { circumCenter } from "./func/circumcenter";
import { edges } from "./func/edges";
import { Polygon2 } from "./poly2";

export class Circle2 implements
Expand All @@ -13,7 +22,7 @@ export class Circle2 implements
ICentroid<Vec2>,
IToHiccup,
IToPolygon2,
IVertices<Vec2> {
IVertices<Vec2, number | Partial<ArcSamplingOpts>> {

static from3Points(a: Readonly<Vec2>, b: Readonly<Vec2>, c: Readonly<Vec2>) {
const o = circumCenter(a, b, c);
Expand All @@ -38,10 +47,14 @@ export class Circle2 implements
return new Circle2(this.pos.copy(), this.r, { ...this.attribs });
}

edges(opts?: Partial<ArcSamplingOpts>) {
return edges(this.vertices(opts));
}

verticesRaw(
from: number,
to: number,
res: number,
num: number,
inclLast: boolean,
dest: Vec = [],
destOffset = 0,
Expand All @@ -53,7 +66,7 @@ export class Circle2 implements
const po = this.pos.i;
const ps = this.pos.s;
const r = this.r;
for (let t of normRange(inclLast ? res - 1 : res, inclLast)) {
for (let t of normRange(inclLast ? num - 1 : num, inclLast)) {
toCartesian2(
setS2(dest, r, mix1(from, to, t), destOffset, cstride),
pos, destOffset, po, cstride, ps
Expand All @@ -63,8 +76,16 @@ export class Circle2 implements
return dest;
}

vertices(res = Circle2.DEFAULT_RES) {
return Vec2.mapBuffer(this.verticesRaw(0, TAU, res, false), res);
vertices(opts?: number | Partial<ArcSamplingOpts>) {
const [num, last] = isNumber(opts) ?
[opts, true] :
[
opts.theta ?
Math.max(Math.ceil(1 + TAU / opts.theta), 2) :
opts.num,
opts.includeLast === true
];
return Vec2.mapBuffer(this.verticesRaw(0, TAU, num, last), num);
}

area() {
Expand Down Expand Up @@ -98,13 +119,21 @@ export class Circle2 implements
return c ? c.set(this.pos) : this.pos;
}

toPolygon2(res = Circle2.DEFAULT_RES) {
return new Polygon2(this.vertices(res));
toPolygon2(opts?: Partial<ArcSamplingOpts>) {
return new Polygon2(this.vertices(opts));
}

toHiccup() {
return ["circle", this.attribs, this.pos, this.r];
}

toJSON() {
return {
type: "circle2",
pos: this.pos.toJSON(),
r: this.r,
};
}
}

export const circle2 = (pos: Vec2, r = 1, attribs?: IObjectOf<any>) =>
Expand Down
53 changes: 37 additions & 16 deletions packages/geom/src/container2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
import { Mat23 } from "@thi.ng/vectors/mat23";
import { Vec2, vec2 } from "@thi.ng/vectors/vec2";
import {
CollateOpts,
IBounds,
ICentroid,
ICollate,
IVertices
} from "./api";
import { bounds } from "./func/bounds";
import { convexHull2 } from "./func/convex-hull";
import { Vec } from "@thi.ng/vectors/api";

export class PointContainer2 implements
IBounds<Vec2[]>,
ICentroid<Vec2>,
ICollate,
IVertices<Vec2> {
IVertices<Vec2, void> {

points: Vec2[];
attribs: IObjectOf<any>;
Expand All @@ -30,21 +30,30 @@ export class PointContainer2 implements
yield* this.vertices();
}

collate(remap = true, buf: Vec, start = 0, cstride = 1, estride = 2) {
if (!remap) {
this.points = this._copy();
} else {
const pts = this.points;
const n = pts.length;
buf = Vec2.intoBuffer(buf || new Array(start + n * estride).fill(0), pts, start, cstride, estride);
for (let i = 0; i < n; i++) {
const p = pts[i];
p.buf = buf;
p.i = start + i * estride;
p.s = cstride;
}
collate(opts?: Partial<CollateOpts>) {
opts = {
start: 0,
cstride: 1,
estride: 2,
...opts
};
const { start, cstride, estride } = opts;
const pts = this.points;
const n = pts.length;
const buf = Vec2.intoBuffer(
opts.buf || new Array(start + n * estride).fill(0),
pts,
start,
cstride,
estride
);
for (let i = 0; i < n; i++) {
const p = pts[i];
p.buf = buf;
p.i = start + i * estride;
p.s = cstride;
}
return this;
return buf;
}

vertices() {
Expand Down Expand Up @@ -120,4 +129,16 @@ export class PointContainer2 implements
protected _copy() {
return Vec2.mapBuffer(Vec2.intoBuffer([], this.points), this.points.length);
}

protected _toJSON(type: string) {
return {
type,
attribs: this.attribs,
points: this.points.map((p) => p.toJSON())
};
}

protected _toHiccup(type: string) {
return [type, this.attribs, this.vertices()];
}
}
Loading

0 comments on commit 2657df6

Please sign in to comment.