d3-3d
Version:
D3.js plugin for 3d visualization written in Typescript
469 lines • 16.5 kB
TypeScript
//#region src/types.d.ts
export type Point2D = {
x: number;
y: number;
};
export type Point3D = {
x: number;
y: number;
z: number;
};
export type ProjectedPoint = Point2D & {
projected: Point2D;
};
export type RotatedPoint = Point2D & {
rotated: Point3D;
};
export type TransformedPoint<T> = T & {
rotated: Point3D;
projected: Point2D;
};
/**
* The fields a shape carries on its array besides its points, such as a
* `color` set with `Object.assign(points, { color })`. `.data` keeps them.
* The indices of a tuple are string keys, so `${number}` leaves them out.
*/
export type ShapeFields<Shape extends unknown[]> = Omit<Shape, keyof unknown[] | `${number}`>;
export interface RotationAngles {
x: number;
y: number;
z: number;
rotateCenter: Point3D;
}
export interface TransformOptions<Datum> {
origin: Point2D;
rotateCenter: Point3D;
rotateX: number;
rotateY: number;
rotateZ: number;
scale: number;
/** The distance of the eye from the rotation center; `Infinity`, the default, is orthographic. */
perspective?: number;
x: (point: Datum) => number;
y: (point: Datum) => number;
z: (point: Datum) => number;
}
/**
* An axis aligned box in world space.
*
* `x`, `y` and `z` are the corner of the box, `w`, `d` and `h` its extent
* along the three axes and `r` the corner radius of its plan outline.
*/
export interface IsoBoxSpec {
x: number;
y: number;
z: number;
w: number;
d: number;
h: number;
r?: number;
}
/**
* A rectangle in world space, described by its corner and the two vectors
* that span it.
*/
export interface IsoPlaneSpec {
origin: Point3D;
u: Point3D;
v: Point3D;
}
/**
* One axis of a chart box in data units: its extent, from the first to the
* last value of `domain`, and the values of its ticks.
*/
export interface AxisSpec {
domain: number[];
ticks: number[];
}
/**
* The three axes of a chart box.
*/
export interface AxesSpec {
x: AxisSpec;
y: AxisSpec;
z: AxisSpec;
}
/**
* The viewport that frames a projected scene.
*/
export interface IsoFrame {
viewBox: string;
width: number;
height: number;
}
//#endregion
//#region src/shapes/shape.d.ts
/**
* The view of a scene: how it is turned, scaled and placed on the screen.
* Every shape is a camera, and `camera3D()` is a camera without data.
*/
interface Camera3DInstance {
scale(): number;
scale(scale: number): this;
origin(): Point2D;
origin(origin: Point2D): this;
perspective(): number;
perspective(distance: number): this;
rotateX(): number;
rotateX(angle: number): this;
rotateY(): number;
rotateY(angle: number): this;
rotateZ(): number;
rotateZ(angle: number): this;
rotationCenter(): Point3D;
rotationCenter(center: Point3D): this;
rotationCenter(center?: Point3D): this | Point3D;
camera(camera: Camera3DInstance): this;
unproject(point: Point2D, planePoint: Point3D, planeNormal: Point3D): Point3D | null;
matrix(viewport?: {
width: number;
height: number;
depth?: number;
}): Float32Array;
viewDirection(): Point3D;
}
interface ShapeInstance<Datum = Point3D> extends Camera3DInstance {
x(): (point: Datum) => number;
x(x: (point: Datum) => number): this;
y(): (point: Datum) => number;
y(y: (point: Datum) => number): this;
z(): (point: Datum) => number;
z(z: (point: Datum) => number): this;
}
/**
* A camera without data: one view for several shapes, for a canvas or for a
* WebGL shader.
*/
export declare function camera3D(): Camera3DInstance;
//#endregion
//#region src/shapes/axes.d.ts
/**
* One wall of the chart box: its four corners, whether it faces the camera
* and its gridlines, one segment per tick inside the box. `axis` is the axis
* the wall stands across and `side` the end of its domain the wall sits at,
* 0 at the first value and 1 at the last.
*/
type AxisWall = TransformedPoint<Point3D>[] & {
axis: keyof AxesSpec;
side: 0 | 1;
ccw: boolean;
centroid: Point3D;
grid: TransformedPoint<Point3D>[][];
};
/**
* One edge of the chart box along an axis: its ticks, which keep their
* value under the axis key, and the screen direction away from the box.
* `sides` are the ends of the two other axes the edge runs at, in the order
* of the walls they span, and `chosen` marks the edge `axes` puts the axis on.
*/
type AxisEdge = TransformedPoint<Point3D>[] & {
axis: keyof AxesSpec;
normal: Point2D;
sides: [0 | 1, 0 | 1];
chosen: boolean;
};
interface Axes3DInstance extends ShapeInstance<Point3D> {
data(spec: AxesSpec): AxisWall[];
draw(wall: AxisWall): string;
axes(spec: AxesSpec): AxisEdge[];
edges(spec: AxesSpec): AxisEdge[];
}
export declare function axes3D(): Axes3DInstance;
//#endregion
//#region src/shapes/cubes.d.ts
type CubeFaceName = "front" | "back" | "left" | "right" | "top" | "bottom";
type CubeFace<Datum = Point3D> = TransformedPoint<Datum>[] & {
centroid: Point3D;
ccw: boolean;
face: CubeFaceName;
};
type Cube<Datum = Point3D, Shape extends Datum[] = Datum[]> = TransformedPoint<Datum>[] & ShapeFields<Shape> & {
faces: CubeFace<Datum>[];
centroid: Point3D;
};
interface Cubes3DInstance<Datum = Point3D> extends ShapeInstance<Datum> {
data<Shape extends Datum[]>(data: Shape[]): Cube<Datum, Shape>[];
draw(face: TransformedPoint<Datum>[]): string;
}
export declare function cubes3D<Datum = Point3D>(): Cubes3DInstance<Datum>;
//#endregion
//#region src/shapes/gridPlanes.d.ts
type GridPlane<Datum> = TransformedPoint<Datum>[] & {
ccw: boolean;
centroid: Point3D;
plane: string;
};
interface GridPlanes3DInstance<Datum = Point3D> extends ShapeInstance<Datum> {
rows(): number;
rows(rows: number): this;
data(data: Datum[]): GridPlane<Datum>[];
draw(gridPlanes: TransformedPoint<Datum>[]): string;
}
export declare function gridPlanes3D<Datum = Point3D>(): GridPlanes3DInstance<Datum>;
//#endregion
//#region src/shapes/iso.d.ts
/**
* The affine frame of a face: the transform that carries the local
* coordinates of the face onto the screen.
*/
type IsoPlane = {
matrix: string;
};
/**
* A projected box: the two visible paths and the frames of its faces.
*/
type IsoBox = {
/** The rotated centre of the box, ready for `sort`. */
centroid: Point3D;
/** The path of the visible side of the box. */
wall: string;
/** The path of the top face. */
top: string;
topPlane: IsoPlane;
frontPlane: IsoPlane;
sidePlane: IsoPlane;
};
interface Iso3DInstance extends ShapeInstance<Point3D> {
data(data: IsoBoxSpec[]): IsoBox[];
plane(spec: IsoPlaneSpec): IsoPlane;
path(points: Point3D[]): string;
project(points: Point3D[]): Point2D[];
frame(points: Point3D[], margin?: number, aspect?: number): IsoFrame;
}
export declare function iso3D(): Iso3DInstance;
//#endregion
//#region src/shapes/lines.d.ts
type Line<Datum, Shape extends Datum[] = Datum[]> = TransformedPoint<Datum>[] & ShapeFields<Shape> & {
centroid: Point3D;
};
interface Lines3DInstance<Datum = Point3D> extends ShapeInstance<Datum> {
data<Shape extends Datum[]>(data: Shape[]): Line<Datum, Shape>[];
draw(line: TransformedPoint<Datum>[]): string;
}
export declare function lines3D<Datum = Point3D>(): Lines3DInstance<Datum>;
//#endregion
//#region src/shapes/lineStrips.d.ts
type LineStrip<Datum, Shape extends Datum[] = Datum[]> = TransformedPoint<Datum>[] & ShapeFields<Shape> & {
centroid: Point3D;
};
interface LineStrips3DInstance<Datum = Point3D> extends ShapeInstance<Datum> {
data<Shape extends Datum[]>(data: Shape[]): LineStrip<Datum, Shape>[];
draw(lineStrip: TransformedPoint<Datum>[]): string;
}
export declare function lineStrips3D<Datum = Point3D>(): LineStrips3DInstance<Datum>;
//#endregion
//#region src/shapes/planes.d.ts
type Plane<Datum, Shape extends Datum[] = Datum[]> = TransformedPoint<Datum>[] & ShapeFields<Shape> & {
ccw: boolean;
centroid: Point3D;
};
interface Planes3DInstance<Datum = Point3D> extends ShapeInstance<Datum> {
data<Shape extends Datum[]>(data: Shape[]): Plane<Datum, Shape>[];
draw(plane: TransformedPoint<Datum>[]): string;
}
export declare function planes3D<Datum = Point3D>(): Planes3DInstance<Datum>;
//#endregion
//#region src/shapes/points.d.ts
type Point<Datum> = TransformedPoint<Datum> & {
centroid: Point3D;
};
interface Points3DInstance<Datum = Point3D> extends ShapeInstance<Datum> {
data(data: Datum[]): Point<Datum>[];
}
export declare function points3D<Datum = Point3D>(): Points3DInstance<Datum>;
//#endregion
//#region src/shapes/polygons.d.ts
type Polygon<Datum, Shape extends Datum[] = Datum[]> = TransformedPoint<Datum>[] & ShapeFields<Shape> & {
ccw: boolean;
centroid: Point3D;
};
interface Polygons3DInstance<Datum = Point3D> extends ShapeInstance<Datum> {
data<Shape extends Datum[]>(data: Shape[]): Polygon<Datum, Shape>[];
draw(polygon: TransformedPoint<Datum>[]): string;
}
export declare function polygons3D<Datum = Point3D>(): Polygons3DInstance<Datum>;
//#endregion
//#region src/shapes/triangles.d.ts
type Triangle<Datum, Shape extends Datum[] = Datum[]> = TransformedPoint<Datum>[] & ShapeFields<Shape> & {
ccw: boolean;
centroid: Point3D;
};
interface Triangles3DInstance<Datum = Point3D> extends ShapeInstance<Datum> {
data<Shape extends Datum[]>(data: Shape[]): Triangle<Datum, Shape>[];
draw(triangle: TransformedPoint<Datum>[]): string;
}
export declare function triangles3D<Datum = Point3D>(): Triangles3DInstance<Datum>;
//#endregion
//#region src/sort.d.ts
interface HasCentroid {
centroid: {
z: number;
};
}
export declare function sort<T extends HasCentroid>(a: T, b: T): number;
//#endregion
//#region src/counter-clockwise.d.ts
export declare function ccw<T>(polygon: ReadonlyArray<TransformedPoint<T>>): boolean;
//#endregion
//#region src/centroid.d.ts
export declare function centroid<T>(polygon: ReadonlyArray<TransformedPoint<T>>): Point3D;
//#endregion
//#region src/normal.d.ts
/**
* The unit normal of a polygon, by Newell's method: exact for a flat
* polygon of any size, an average for a slightly bent one.
*
* It points to the side from which the vertices run counter-clockwise on
* the screen, where y grows downwards: the side `ccw` reports. So the faces
* of `cubes3D` point outwards, and a face faces the camera where
* `dot(normal, viewDirection) > 0`, or in perspective where
* `dot(normal, eye − vertex) > 0`.
*
* Returns the zero vector for a polygon without an area.
*/
export declare function normal(polygon: ReadonlyArray<Point3D>): Point3D;
//#endregion
//#region src/box.d.ts
/**
* The eight corners of a box in the order `cubes3D` expects: front top
* left, front bottom left, front bottom right and front top right, then
* the same four at the back.
*
* The box stands on the point (x, y, z): its footprint is centered on x and
* z, `width` along x and `depth` along z, and it rises `height` from y
* upwards, towards -y as on the screen. Its front is the side at +z.
*
* `topWidth` and `topDepth` size the top apart from the footprint, for a
* tapered shaft; a top of zero width is a ridge, one of zero size an apex.
*/
export declare function box(spec: {
x: number;
y: number;
z: number;
width: number;
height: number;
depth: number;
topWidth?: number;
topDepth?: number;
}): Point3D[];
//#endregion
//#region src/lathe.d.ts
/**
* The surface of revolution of a profile: the triangles that `triangles3D`
* expects.
*
* The profile lies in the x-y plane, x the radius and y the height, and is
* turned around the y axis in `segments` steps. Between two rings every step
* is a quad of two triangles. A ring of radius 0 is a single point, so a
* quad next to it is one triangle, and a profile that starts and ends on the
* axis closes into a solid.
*
* The profile runs upwards, from a large y to a small one as on the screen.
* Then every triangle runs counter-clockwise when it is seen from outside:
* its `normal` points outwards and `ccw` tells the outside from the inside.
* A profile that runs downwards turns the surface inside out.
*/
export declare function lathe(profile: ReadonlyArray<Point2D>, segments: number): Point3D[][];
//#endregion
//#region src/sphere.d.ts
/**
* The triangles of a sphere around the point (x, y, z): the triangles that
* `triangles3D` expects.
*
* A half circle from the bottom pole to the top one is turned around the y
* axis by `lathe` in `segments` steps, and half as many bands, rounded up,
* run from pole to pole, so the quads between them are close to square. The
* poles lie on the axis, so the bands next to them close in fans. Below 3
* segments there is no sphere and the list is empty.
*
* Every triangle runs counter-clockwise when it is seen from outside: its
* `normal` points outwards and `ccw` tells the front of the sphere from its
* back.
*/
export declare function sphere(spec: {
x: number;
y: number;
z: number;
radius: number;
}, segments: number): Point3D[][];
//#endregion
//#region src/strips.d.ts
/**
* Cuts a polyline into strips of `size` points that share their end points,
* the last one shorter if the points run out.
*
* `sort` orders a line strip by the depth of its middle, so a long line that
* passes in front of a shape and behind it again is painted wrong as a
* whole. Cut into strips, every piece sorts on its own depth. A `size` of 2
* cuts the line into its segments.
*/
export declare function strips<T>(points: ReadonlyArray<T>, size: number): T[][];
//#endregion
//#region src/catmull-rom.d.ts
/**
* The uniform Catmull–Rom spline through `points`, `steps` samples per
* segment: a smooth curve that passes through every point, ready for
* `lineStrips3D`.
*
* The end points stand in for their missing neighbours, so the curve starts
* on the first point and ends on the last.
*/
export declare function catmullRom(points: ReadonlyArray<Point3D>, steps: number): Point3D[];
//#endregion
//#region src/axis-spec.d.ts
/**
* A continuous scale with ticks, such as d3's `scaleLinear`.
*/
interface TickScale {
domain(): number[];
ticks(count?: number): number[];
}
/**
* One axis of `axes3D` from a scale: the domain of the scale and about
* `count` of its ticks.
*
* Call `.nice()` on the scale first, so the box ends on a tick.
*/
export declare function axisSpec(scale: TickScale, count?: number): AxisSpec;
//#endregion
//#region src/transform.d.ts
export declare function transform<Datum = Point3D>(data: Datum[], options: TransformOptions<Datum>): TransformedPoint<Datum>[];
//#endregion
//#region src/projection-orthographic.d.ts
interface OrthographicOptions {
origin: Point2D;
scale: number;
}
export declare function orthographic(d: Point3D, options: OrthographicOptions): Point2D;
//#endregion
//#region src/projection-perspective.d.ts
interface PerspectiveOptions extends OrthographicOptions {
rotateCenter: Point3D;
/** The distance of the eye from the rotation center, in model units. */
distance: number;
}
/**
* Projects a rotated point as seen from an eye `distance` in front of the
* rotation center, on the rotated z axis that points towards the viewer.
*
* A point in the plane of the center keeps the scale of `orthographic`, a
* nearer one is drawn larger and a farther one smaller, by
* `k = distance / (distance − (z − center.z))`. An infinite distance gives
* `k = 1`: the orthographic projection. The point has to lie in front of
* the eye.
*/
export declare function perspective(d: Point3D, options: PerspectiveOptions): Point2D;
//#endregion
//#region src/projection-matrix.d.ts
/**
* Projects packed x, y, z triples through `matrix` into `out`: screen x,
* screen y and depth, three numbers per point. Pass the same `out` every
* frame to project a large point cloud without allocating.
*
* x and y are divided by w, which is 1 unless the matrix is perspective.
* The depth is not, so it stays the rotated z that `sort` reads.
*/
export declare function projectPositions(positions: ArrayLike<number>, matrix: ArrayLike<number>, out?: Float32Array): Float32Array;
//#endregion
export type { Axes3DInstance, AxisEdge, AxisWall, Camera3DInstance, Cube, CubeFace, CubeFaceName, Cubes3DInstance, GridPlane, GridPlanes3DInstance, HasCentroid, Iso3DInstance, IsoBox, IsoPlane, Line, LineStrip, LineStrips3DInstance, Lines3DInstance, OrthographicOptions, PerspectiveOptions, Plane, Planes3DInstance, Point, Points3DInstance, Polygon, Polygons3DInstance, ShapeInstance, TickScale, Triangle, Triangles3DInstance };
//# sourceMappingURL=index.d.ts.map