Package Exports
- @rickosborne/planar
- @rickosborne/planar/2d
- @rickosborne/planar/2d.js
- @rickosborne/planar/angle
- @rickosborne/planar/angle.js
- @rickosborne/planar/bounding
- @rickosborne/planar/bounding.js
- @rickosborne/planar/centroid
- @rickosborne/planar/centroid.js
- @rickosborne/planar/constant
- @rickosborne/planar/constant.js
- @rickosborne/planar/index.js
- @rickosborne/planar/is-convex
- @rickosborne/planar/is-convex.js
- @rickosborne/planar/is-shape
- @rickosborne/planar/is-shape.js
- @rickosborne/planar/measure-angles
- @rickosborne/planar/measure-angles.js
- @rickosborne/planar/orientation
- @rickosborne/planar/orientation.js
- @rickosborne/planar/perimeter
- @rickosborne/planar/perimeter.js
- @rickosborne/planar/point
- @rickosborne/planar/point.js
- @rickosborne/planar/polygon-from
- @rickosborne/planar/polygon-from.js
- @rickosborne/planar/pretty-rad
- @rickosborne/planar/pretty-rad.js
- @rickosborne/planar/reverse-polygon
- @rickosborne/planar/reverse-polygon.js
- @rickosborne/planar/sign-of
- @rickosborne/planar/sign-of.js
- @rickosborne/planar/signed-area
- @rickosborne/planar/signed-area.js
Readme
@rickosborne/planar
A small collection of 2D Cartesian and graphics related data structures and algorithms. Built on @rickosborne/typical for types, @rickosborne/guard for guards, and @rickosborne/foundation for basic data structures and algorithms.
Usage
Install via your favorite package manager.
Each package supports CommonJS require
, ESM import
, and TypeScript usage.
You also have a choice: barrel imports or direct imports.
Barrel imports mean you're going to require/import everything from the same package-level namespace:
// CommonJS
const { isPlainObject, isListOf } = require("@rickosborne/guard");
// ESM / TypeScript
import { isPlainObject, isListOf } from "@rickosborne/guard";
Implications:
- Nice and simple.
- Your build system needs to do tree-shaking well ... or you'll end up adding the entire package even if you only import two functions.
The other option is to use direct imports:
// CommonJS
const { isPlainObject } = require("@rickosborne/guard/is-object");
const { isListOf } = require("@rickosborne/guard/is-list-of");
// ESM / TypeScript
import { isPlainObject } from "@rickosborne/guard/is-object.js";
import { isListOf } from "@rickosborne/guard/is-list-of.js";
Implications:
- You (probably) don't have to worry about tree-shaking as your build (likely) ends up with only the functions you need.
If you're using a modern build system, there aren't any strong reasons to prefer one way over the other. It's really just down to your personal preference.
A quick note about file extensions
Do you need to use file extensions? And if so, which extensions?
Honestly ... this is a dumpster fire question. It really comes down to your own setup and configuration.
Within each package itself:
- The CommonJS files all have
.cjs
extensions. - The ESM files all have
.mjs
extensions. - Node subpath exports have been set up to send
.js
imports to the.cjs
(viarequire
) or.mjs
(viaimport
) files, depending on your setup.
So, in theory, the only extension which won't work would be .ts
because the source isn't included.
If you run into a problem with a particular configuration, file a GitHub issue with:
- Your
tsconfig.json
'smodule
,moduleResolution
, andtarget
settings. - Your
package.json
'stype
andimports
settings. - An example of another package which imports correctly for you.
License
This package is licensed as CC-BY-NC-SA-4.0 unless otherwise noted. That is, Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.
API
Enums
Orientation
enum Orientation
Measure of the turn of an angle, based on the order of the three points which define it.
Functions
angle
angle: (area: number, orientation: Orientation, rad: number) => Angle
angleMeasures
angleMeasures: (a: Point, b: Point, c: Point) => AngleMeasures
Calculate the intermediary values used in measuring an angle defined by the intersection of two line segments along three points: A, B, and C.
boundingBoxOf
boundingBoxOf: (shape: Shape | LineSegment) => Rect
Calculate the bounding box of the given shape, meaning a minimum point plus a width and height. Note that the x and y values will be the numeric minimums, which will be bottom-left in Cartesian space and top-left in pixel space.
boundingBoxOfCircle
boundingBoxOfCircle: (circle: Circle) => Rect
Calculate the bounding box of the given circle, meaning a minimum point plus a width and height. Note that the x and y values will be the numeric minimums, which will be bottom-left in Cartesian space and top-left in pixel space.
boundingBoxOfLineSegment
boundingBoxOfLineSegment: (segment: LineSegment) => Rect
Calculate the bounding box which would contain the given line segment.
boundingBoxOfPath
boundingBoxOfPath: (path: Path) => Rect
Calculate the bounding box which would contain the points in the given path.
cartesianOrientationOfPolygon
cartesianOrientationOfPolygon: (poly: Polygon) => Orientation
Calculate the orientation of a polygon as defined by the order of its points and its signed area. This does not necessarily imply the polygon is convex. This version uses Cartesian coordinates, where the origin is in the bottom left. See also gfxOrientationOfPolygon.
centroidOfPolygon
centroidOfPolygon: (poly: Polygon) => Point
Calculate the centroid of a polygon, which is the weighted mean of its points. If the polygon has zero signed area, the centroid of the polygon's bounding box will be returned. The centroid is not guaranteed to be within the polygon if it is not convex.
detailedIsConvex
detailedIsConvex: (poly: Polygon) => IsConvex
More detailed check to see if a polygon is convex. Tracks "bad" points and returns them if found. Slower than fastIsConvex, and doesn't necessarily return a better result, though it may give more information in scenarios where you want to try to split the polygon into smaller convex polygons.
fastIsConvex
fastIsConvex: (poly: Polygon) => boolean
Performs a rudimentary check of a polygon, calculating angles at each vertex and checking for sign flips in the line segment cross products. The result may not be reliable for symmetric shapes where the signed areas cancel out. This version also does not track "bad" points.
gfxOrientationOfPolygon
gfxOrientationOfPolygon: (poly: Polygon) => Orientation
Calculate the orientation of a polygon as defined by the order of its points and its signed area. This does not necessarily imply the polygon is convex. This version uses graphics coordinates, where the origin is in the top left. See also cartesianOrientationOfPolygon.
isCircle
isCircle: (obj: unknown) => obj is Circle
Guard for whether the given value has x and y coordinates plus a radius r measure. May have more!
isDirectedSegment
isDirectedSegment: (obj: unknown) => obj is DirectedSegment
Guard for whether the given value has a and b Point properties. May have more!
isLineSegment
isLineSegment: (obj: unknown) => obj is LineSegment
Guard for whether the given value has blue and gold Point properties. May have more!
isPath
isPath: (obj: unknown, deep?: boolean) => obj is Path
Guard for whether the given value has a list of points. It will not exhaustively check the points for validity unless deep is true.
isPoint
isPoint: (obj: unknown) => obj is Point
Guard for whether the given value has x and y coordinates. May have more!
isRect
isRect: (obj: unknown) => obj is Rect
Guard for whether the given value has x and y coordinates plus w and h measures. May have more!
measureAngle
measureAngle: (a: Point, b: Point, c: Point) => Angle
Given three points which define the ordered intersection of two line segments, calculate the angle between them.
measureAngles
measureAngles: (poly: Polygon) => PolygonWithRadians
Upgrade a polygon to include angle measurements for its vertices.
perimeterOfPolygon
perimeterOfPolygon: (poly: Polygon) => number
point
point: (x: number, y: number) => Point
pointAdd
pointAdd: (a: Point, b: Point) => Point
pointEq
pointEq: (a: Point, b: Point) => boolean
polygonFromLineSegment
polygonFromLineSegment: (segment: LineSegment) => Polygon
Convert the given LineSegment to Polygon format. Arbitrarily places the left/bottom point first and right/top point second.
polygonFromRect
polygonFromRect: (rect: Rect) => Polygon
Convert the given Rect to Polygon format.
prettyRad
prettyRad: (rad: number, epsilon?: number) => string
Try to figure out a reasonably human-readable fraction for the given angle in radians.
reversePolygon
reversePolygon: <P extends Polygon>(poly: P) => P
Reverse the point order of a polygon, while keeping the starting point the same.
signedAreaOfPolygon
signedAreaOfPolygon: (poly: Polygon) => number
Calculate the signed area of a polygon.
signOf
signOf: (n: number) => Sign
Reduce a number to a unit (1) with a sign.
TypeAliases
Angle
type Angle = {
area: number;
orientation: Orientation;
rad: number;
};
An angle between two line segments, often as an interior angle for a polygon.
AngleMeasures
type AngleMeasures = {
abx: number;
aby: number;
bcx: number;
bcy: number;
area: number;
orientation: Orientation;
};
Mixin for the intermediary values used for calculating angles defined by the intersection of two line segments across three points: A, B, and C.
Circle
type Circle = Point & {
r: number;
};
An equal radius out from a center point.
ConvexWithOrientation
type ConvexWithOrientation = {
convex: true;
orientation: Orientation.Counter | Orientation.Clockwise;
};
Result of a calculation of whether a polygon is convex indicating the polygon is complex.
DirectedSegment
type DirectedSegment = {
a: Point;
b: Point;
};
A directed line segment joining two points in a specific order.
IsConvex
type IsConvex = Either<ConvexWithOrientation, NotConvex>;
Result of a calculation of whether a polygon is convex.
LineSegment
type LineSegment = {
blue: Point;
gold: Point;
};
An undirected line segment joining two points.
NotConvex
type NotConvex = {
badPoints?: number[];
convex: false;
firstOrientation?: Orientation | undefined;
overallOrientation?: Orientation | undefined;
};
Result of a calculation of whether a polygon is convex, indicating that it does not seem to be, or is still unknown.
Path
type Path = {
points: Point[];
};
An ordered sequence of points.
Point
type Point = {
x: number;
y: number;
};
Coordinates in 2D space.
Polygon
type Polygon = Path;
An ordered sequence of points, generally assuming the last point connects back around to the first.
PolygonWithRadians
type PolygonWithRadians = {
points: WithAngles<Point>[];
};
A sequence of points where the interior angle at each has been measured.
Rect
type Rect = Point & {
h: number;
w: number;
};
A rectangle with no rotation. Note that this structure does not encode whether the origin point is in the bottom left (Cartesian) or top left (graphics).
Shape
type Shape = Polygon | Rect | Circle;
For algorithms which work on basic shapes.
Sign
type Sign = -1 | 0 | 1;
Could be an Orientation, or just a simple indication of the sign of a number.
WithAngles
type WithAngles<T extends object> = T & Angle;
Mixin which adds angle information to some other type.
Variables
CCW
CCW = Orientation.Counter
Shorter version of Orientation.Counter.
CONVEX_TRUE_CCW
CONVEX_TRUE_CCW: IsConvex
Basic shared value for a true IsConvex result with a counterclockwise orientation.
CONVEX_TRUE_CW
CONVEX_TRUE_CW: IsConvex
Basic shared value for a true IsConvex result with a clockwise orientation.
CW
CW = Orientation.Clockwise
Shorter version of Orientation.Clockwise.
EPSILON
EPSILON = 0.00001
ORIGIN
ORIGIN: Readonly<{
x: 0;
y: 0;
}>
PI
PI: number
PI_2
PI_2: number
PI_2_3
PI_2_3: number
PI_3
PI_3: number
PI_3_4
PI_3_4: number
PI_4
PI_4: number
PI_5
PI_5: number
PI_6
PI_6: number
PI_7
PI_7: number
PI_8
PI_8: number
PI_9
PI_9: number
SQRT_2
SQRT_2: number
SQRT_3
SQRT_3: number
SQRT_5
SQRT_5: number
STRAIGHT
STRAIGHT = Orientation.Straight
Shorter version of Orientation.Straight.
TWO_PI
TWO_PI: number