@khanacademy/kmath 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/es/index.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/line.js +1 -4
- package/src/point.js +1 -4
- package/src/vector.js +32 -47
package/CHANGELOG.md
CHANGED
package/dist/es/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/number.js","../../src/vector.js","../../src/point.js","../../src/line.js","../../src/ray.js"],"sourcesContent":["// @flow\n/**\n * Number Utils\n * A number is a js-number, e.g. 5.12\n */\n\nimport _ from \"underscore\";\n\nexport const DEFAULT_TOLERANCE: number = 1e-9;\n\n// TODO: Should this just be Number.Epsilon\nexport const EPSILON: number = Math.pow(2, -42);\n\nexport function is(x: any): boolean {\n return _.isNumber(x) && !_.isNaN(x);\n}\n\nexport function equal(x: number, y: number, tolerance?: number): boolean {\n // Checking for undefined makes this function behave nicely\n // with vectors of different lengths that are _.zip'd together\n if (x == null || y == null) {\n return x === y;\n }\n // We check === here so that +/-Infinity comparisons work correctly\n if (x === y) {\n return true;\n }\n if (tolerance == null) {\n tolerance = DEFAULT_TOLERANCE;\n }\n return Math.abs(x - y) < tolerance;\n}\n\nexport function sign(\n x: number,\n tolerance?: number,\n): number /* Should be: 0 | 1 | -1 */ {\n return equal(x, 0, tolerance) ? 0 : Math.abs(x) / x;\n}\n\nexport function isInteger(num: number, tolerance?: number): boolean {\n return equal(Math.round(num), num, tolerance);\n}\n\n// Round a number to a certain number of decimal places\nexport function round(num: number, precision: number): number {\n const factor = Math.pow(10, precision);\n return Math.round(num * factor) / factor;\n}\n\n// Round num to the nearest multiple of increment\n// i.e. roundTo(83, 5) -> 85\nexport function roundTo(num: number, increment: number): number {\n return Math.round(num / increment) * increment;\n}\n\nexport function floorTo(num: number, increment: number): number {\n return Math.floor(num / increment) * increment;\n}\n\nexport function ceilTo(num: number, increment: number): number {\n return Math.ceil(num / increment) * increment;\n}\n\n/**\n * toFraction\n *\n * Returns a [numerator, denominator] array rational representation\n * of `decimal`\n *\n * See http://en.wikipedia.org/wiki/Continued_fraction for implementation\n * details\n *\n * toFraction(4/8) => [1, 2]\n * toFraction(0.66) => [33, 50]\n * toFraction(0.66, 0.01) => [2/3]\n * toFraction(283 + 1/3) => [850, 3]\n */\nexport function toFraction(\n decimal: number,\n tolerance: number = EPSILON, // can't be 0\n maxDenominator: number = 1000,\n): [number, number] {\n // Initialize everything to compute successive terms of\n // continued-fraction approximations via recurrence relation\n let n = [1, 0];\n let d = [0, 1];\n let a = Math.floor(decimal);\n let rem = decimal - a;\n\n while (d[0] <= maxDenominator) {\n if (equal(n[0] / d[0], decimal, tolerance)) {\n return [n[0], d[0]];\n }\n n = [a * n[0] + n[1], n[0]];\n d = [a * d[0] + d[1], d[0]];\n a = Math.floor(1 / rem);\n rem = 1 / rem - a;\n }\n\n // We failed to find a nice rational representation,\n // so return an irrational \"fraction\"\n return [decimal, 1];\n}\n","// @flow\n/**\n * Vector Utils\n * A vector is an array of numbers e.g. [0, 3, 4].\n */\n\nimport _ from \"underscore\";\nimport * as knumber from \"./number.js\";\n\nfunction arraySum(array: $ReadOnlyArray<number>): number {\n return array.reduce((memo, arg) => memo + arg, 0);\n}\n\nfunction arrayProduct(array: $ReadOnlyArray<number>): number {\n return array.reduce((memo, arg) => memo * arg, 1);\n}\n\nexport function is<T>(vec: $ReadOnlyArray<T>, dimension: number): boolean {\n if (!_.isArray(vec)) {\n return false;\n }\n if (dimension !== undefined && vec.length !== dimension) {\n return false;\n }\n return vec.every(knumber.is);\n}\n\n// Normalize to a unit vector\nexport function normalize(v: $ReadOnlyArray<number>): $ReadOnlyArray<number> {\n return scale(v, 1 / length(v));\n}\n\n// Length/magnitude of a vector\nexport function length(v: $ReadOnlyArray<number>): number {\n return Math.sqrt(dot(v, v));\n}\n// Dot product of two vectors\nexport function dot(\n a: $ReadOnlyArray<number>,\n b: $ReadOnlyArray<number>,\n): number {\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n const zipped = _.zip(a, b);\n const multiplied = zipped.map(arrayProduct);\n return arraySum(multiplied);\n}\n\n/* vector-add multiple [x, y] coords/vectors\n *\n * add([1, 2], [3, 4]) -> [4, 6]\n */\nexport function add(\n ...vecs: $ReadOnlyArray<$ReadOnlyArray<number>>\n): $ReadOnlyArray<number> {\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n const zipped = _.zip(...vecs);\n return zipped.map(arraySum);\n}\n\nexport function subtract(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> {\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n return _.zip(v1, v2).map((dim) => dim[0] - dim[1]);\n}\n\nexport function negate(v: $ReadOnlyArray<number>): $ReadOnlyArray<number> {\n return v.map((x) => {\n return -x;\n });\n}\n\n// Scale a vector\nexport function scale(\n v1: $ReadOnlyArray<number>,\n scalar: number,\n): $ReadOnlyArray<number> {\n return v1.map((x) => {\n return x * scalar;\n });\n}\n\nexport function equal(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n tolerance?: number,\n): boolean {\n // _.zip will nicely deal with the lengths, going through\n // the length of the longest vector. knumber.equal then\n // returns false for any number compared to the undefined\n // passed in if one of the vectors is shorter.\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n return _.zip(v1, v2).every((pair) =>\n knumber.equal(pair[0], pair[1], tolerance),\n );\n}\n\nexport function codirectional(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n tolerance?: number,\n): boolean {\n // The origin is trivially codirectional with all other vectors.\n // This gives nice semantics for codirectionality between points when\n // comparing their difference vectors.\n if (\n knumber.equal(length(v1), 0, tolerance) ||\n knumber.equal(length(v2), 0, tolerance)\n ) {\n return true;\n }\n\n v1 = normalize(v1);\n v2 = normalize(v2);\n\n return equal(v1, v2, tolerance);\n}\n\nexport function collinear(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n tolerance?: number,\n): boolean {\n return (\n codirectional(v1, v2, tolerance) ||\n codirectional(v1, negate(v2), tolerance)\n );\n}\n\n// Convert a cartesian coordinate into a radian polar coordinate\nexport function polarRadFromCart(\n v: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> {\n const radius = length(v);\n let theta = Math.atan2(v[1], v[0]);\n\n // Convert angle range from [-pi, pi] to [0, 2pi]\n if (theta < 0) {\n theta += 2 * Math.PI;\n }\n\n return [radius, theta];\n}\n\n// Converts a cartesian coordinate into a degree polar coordinate\nexport function polarDegFromCart(\n v: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> /* TODO: convert to tuple/Point */ {\n const polar = polarRadFromCart(v);\n return [polar[0], (polar[1] * 180) / Math.PI];\n}\n\n/* Convert a polar coordinate into a cartesian coordinate\n *\n * Examples:\n * cartFromPolarRad(5, Math.PI)\n * cartFromPolarRad([5, Math.PI])\n */\nexport function cartFromPolarRad(\n radius: number,\n theta?: number = 0,\n): $ReadOnlyArray<number> /* TODO: convert to tuple/Point */ {\n return [radius * Math.cos(theta), radius * Math.sin(theta)];\n}\n\n/* Convert a polar coordinate into a cartesian coordinate\n *\n * Examples:\n * cartFromPolarDeg(5, 30)\n * cartFromPolarDeg([5, 30])\n */\nexport function cartFromPolarDeg(\n radius: number,\n theta?: number = 0,\n): $ReadOnlyArray<number> {\n return cartFromPolarRad(radius, (theta * Math.PI) / 180);\n}\n\n// Rotate vector\nexport function rotateRad(\n v: $ReadOnlyArray<number>,\n theta: number,\n): $ReadOnlyArray<number> {\n const polar = polarRadFromCart(v);\n const angle = polar[1] + theta;\n return cartFromPolarRad(polar[0], angle);\n}\n\nexport function rotateDeg(\n v: $ReadOnlyArray<number>,\n theta: number,\n): $ReadOnlyArray<number> {\n const polar = polarDegFromCart(v);\n const angle = polar[1] + theta;\n return cartFromPolarDeg(polar[0], angle);\n}\n\n// Angle between two vectors\nexport function angleRad(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): number {\n return Math.acos(dot(v1, v2) / (length(v1) * length(v2)));\n}\n\nexport function angleDeg(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): number {\n return (angleRad(v1, v2) * 180) / Math.PI;\n}\n\n// Vector projection of v1 onto v2\nexport function projection(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> {\n const scalar = dot(v1, v2) / dot(v2, v2);\n return scale(v2, scalar);\n}\n\n// Round each number to a certain number of decimal places\nexport function round(\n vec: $ReadOnlyArray<number>,\n precision: $ReadOnlyArray<number> | number,\n): $ReadOnlyArray<number> {\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.round(elem, precision[i] || precision),\n );\n}\n\n// Round each number to the nearest increment\nexport function roundTo(\n vec: $ReadOnlyArray<number>,\n increment: $ReadOnlyArray<number> | number,\n): $ReadOnlyArray<number> {\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.roundTo(elem, increment[i] || increment),\n );\n}\n\nexport function floorTo(\n vec: $ReadOnlyArray<number>,\n increment: $ReadOnlyArray<number> | number,\n): $ReadOnlyArray<number> {\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.floorTo(elem, increment[i] || increment),\n );\n}\n\nexport function ceilTo(\n vec: $ReadOnlyArray<number>,\n increment: $ReadOnlyArray<number> | number,\n): $ReadOnlyArray<number> {\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.ceilTo(elem, increment[i] || increment),\n );\n}\n","// @flow\n/**\n * Point Utils\n * A point is an array of two numbers e.g. [0, 0].\n */\n\nimport _ from \"underscore\";\n\nimport * as kvector from \"./vector.js\";\nimport * as knumber from \"./number.js\";\n\n// A point, in 2D, 3D, or nD space.\nexport type Point = $ReadOnlyArray<number>;\n\n// Rotate point (around origin unless a center is specified)\nexport function rotateRad(point: Point, theta: number, center: Point): Point {\n if (center === undefined) {\n return kvector.rotateRad(point, theta);\n } else {\n return kvector.add(\n center,\n kvector.rotateRad(kvector.subtract(point, center), theta),\n );\n }\n}\n\nexport function rotateDeg(point: Point, theta: number, center: Point): Point {\n if (center === undefined) {\n return kvector.rotateDeg(point, theta);\n } else {\n return kvector.add(\n center,\n kvector.rotateDeg(kvector.subtract(point, center), theta),\n );\n }\n}\n\n// Distance between two points\nexport function distanceToPoint(point1: Point, point2: Point): number {\n return kvector.length(kvector.subtract(point1, point2));\n}\n\n// Distance between point and line\nexport function distanceToLine(point: Point, line: [Point, Point]): number {\n const lv = kvector.subtract(line[1], line[0]);\n const pv = kvector.subtract(point, line[0]);\n const projectedPv = kvector.projection(pv, lv);\n const distancePv = kvector.subtract(projectedPv, pv);\n return kvector.length(distancePv);\n}\n\n// Reflect point over line\nexport function reflectOverLine(\n point: Point,\n line: [Point, Point],\n): $ReadOnlyArray<number> /* TODO: convert to Point */ {\n const lv = kvector.subtract(line[1], line[0]);\n const pv = kvector.subtract(point, line[0]);\n const projectedPv = kvector.projection(pv, lv);\n const reflectedPv = kvector.subtract(kvector.scale(projectedPv, 2), pv);\n return kvector.add(line[0], reflectedPv);\n}\n\n/**\n * Compares two points, returning -1, 0, or 1, for use with\n * Array.prototype.sort\n *\n * Note: This technically doesn't satisfy the total-ordering\n * requirements of Array.prototype.sort unless equalityTolerance\n * is 0. In some cases very close points that compare within a\n * few equalityTolerances could appear in the wrong order.\n */\nexport function compare(\n point1: Point,\n point2: Point,\n equalityTolerance?: number,\n): number /* TODO: convert to -1 | 0 | 1 type */ {\n if (point1.length !== point2.length) {\n return point1.length - point2.length;\n }\n for (let i = 0; i < point1.length; i++) {\n if (!knumber.equal(point1[i], point2[i], equalityTolerance)) {\n return point1[i] - point2[i];\n }\n }\n return 0;\n}\n\n// Check if a value is a point\nexport const is = kvector.is;\n\n// Add and subtract vector(s)\nexport const addVector = kvector.add;\nexport const addVectors = kvector.add;\nexport const subtractVector = kvector.subtract;\nexport const equal = kvector.equal;\n\n// Convert from cartesian to polar and back\nexport const polarRadFromCart = kvector.polarRadFromCart;\nexport const polarDegFromCart = kvector.polarDegFromCart;\nexport const cartFromPolarRad = kvector.cartFromPolarRad;\nexport const cartFromPolarDeg = kvector.cartFromPolarDeg;\n\n// Rounding\nexport const round = kvector.round;\nexport const roundTo = kvector.roundTo;\nexport const floorTo = kvector.floorTo;\nexport const ceilTo = kvector.ceilTo;\n","// @flow\n/**\n * Line Utils\n * A line is an array of two points e.g. [[-5, 0], [5, 0]].\n */\n\nimport * as kpoint from \"./point.js\";\nimport * as kvector from \"./vector.js\";\n\nimport type {Point} from \"./point.js\";\n\nexport type Line = [Point, Point];\n\nexport function distanceToPoint(line: Line, point: Point): number {\n return kpoint.distanceToLine(point, line);\n}\n\nexport function reflectPoint(\n line: Line,\n point: Point,\n): $ReadOnlyArray<number> /* TODO: convert to Point */ {\n return kpoint.reflectOverLine(point, line);\n}\n\nexport function midpoint(line: Line): Point {\n return [(line[0][0] + line[1][0]) / 2, (line[0][1] + line[1][1]) / 2];\n}\n\nexport function equal(line1: Line, line2: Line, tolerance?: number): boolean {\n // TODO: A nicer implementation might just check collinearity of\n // vectors using underscore magick\n // Compare the directions of the lines\n const v1 = kvector.subtract(line1[1], line1[0]);\n const v2 = kvector.subtract(line2[1], line2[0]);\n if (!kvector.collinear(v1, v2, tolerance)) {\n return false;\n }\n // If the start point is the same for the two lines, then they are the same\n if (kpoint.equal(line1[0], line2[0])) {\n return true;\n }\n // Make sure that the direction to get from line1 to\n // line2 is the same as the direction of the lines\n const line1ToLine2Vector = kvector.subtract(line2[0], line1[0]);\n return kvector.collinear(v1, line1ToLine2Vector, tolerance);\n}\n","// @flow\n/**\n * Ray Utils\n * A ray (→) is an array of an endpoint and another point along the ray.\n * For example, [[0, 0], [1, 0]] is the ray starting at the origin and\n * traveling along the positive x-axis.\n */\n\nimport * as kvector from \"./vector.js\";\nimport * as kpoint from \"./point.js\";\n\nimport type {Point} from \"./point\";\n\nexport type Ray = [Point, Point];\n\nexport function equal(ray1: Ray, ray2: Ray, tolerance: number): boolean {\n // Compare the directions of the rays\n const v1 = kvector.subtract(ray1[1], ray1[0]);\n const v2 = kvector.subtract(ray2[1], ray2[0]);\n\n const sameOrigin = kpoint.equal(ray1[0], ray2[0]);\n const codirectional = kvector.codirectional(v1, v2, tolerance);\n\n return sameOrigin && codirectional;\n}\n"],"names":["EPSILON","Math","pow","is","x","_","isNumber","isNaN","equal","y","tolerance","abs","round","num","precision","factor","roundTo","increment","floorTo","floor","ceilTo","ceil","decimal","maxDenominator","n","d","a","rem","arraySum","array","reduce","memo","arg","arrayProduct","vec","dimension","isArray","undefined","length","every","knumber","normalize","v","scale","sqrt","dot","b","zip","map","add","zipped","subtract","v1","v2","dim","negate","scalar","pair","codirectional","collinear","polarRadFromCart","radius","theta","atan2","PI","polarDegFromCart","polar","cartFromPolarRad","cos","sin","cartFromPolarDeg","rotateRad","angle","rotateDeg","angleRad","acos","projection","elem","i","distanceToLine","point","line","lv","kvector","pv","reflectOverLine","reflectedPv","addVector","addVectors","subtractVector","center","point1","point2","equalityTolerance","kpoint","line1","line2","ray1","ray2","sameOrigin"],"mappings":"0BAQO,IAGMA,EAAkBC,KAAKC,IAAI,GAAI,IAErC,SAASC,EAAGC,GACf,OAAOC,EAAEC,SAASF,KAAOC,EAAEE,MAAMH,GAG9B,SAASI,EAAMJ,EAAWK,EAAWC,GAGxC,OAAS,MAALN,GAAkB,MAALK,EACNL,IAAMK,EAGbL,IAAMK,IAGO,MAAbC,IACAA,EApBiC,MAsB9BT,KAAKU,IAAIP,EAAIK,GAAKC,GAetB,SAASE,EAAMC,EAAaC,GACzBC,IAAAA,EAASd,KAAKC,IAAI,GAAIY,GACrBb,OAAAA,KAAKW,MAAMC,EAAME,GAAUA,EAK/B,SAASC,EAAQH,EAAaI,GAC1BhB,OAAAA,KAAKW,MAAMC,EAAMI,GAAaA,EAGlC,SAASC,EAAQL,EAAaI,GAC1BhB,OAAAA,KAAKkB,MAAMN,EAAMI,GAAaA,EAGlC,SAASG,EAAOP,EAAaI,GACzBhB,OAAAA,KAAKoB,KAAKR,EAAMI,GAAaA,wDArDC,iCAyBlC,SACHb,EACAM,GAEA,OAAOF,EAAMJ,EAAG,EAAGM,GAAa,EAAIT,KAAKU,IAAIP,GAAKA,aAG/C,SAAmBS,EAAaH,GACnC,OAAOF,EAAMP,KAAKW,MAAMC,GAAMA,EAAKH,oDAqChC,SACHY,GAWA,IAVAZ,IAAAA,yDAAoBV,EACpBuB,yDAAyB,IAIrBC,EAAI,CAAC,EAAG,GACRC,EAAI,CAAC,EAAG,GACRC,EAAIzB,KAAKkB,MAAMG,GACfK,EAAML,EAAUI,EAEbD,EAAE,IAAMF,GAAgB,CAC3B,GAAIf,EAAMgB,EAAE,GAAKC,EAAE,GAAIH,EAASZ,GACrB,MAAA,CAACc,EAAE,GAAIC,EAAE,IAEpBD,EAAI,CAACE,EAAIF,EAAE,GAAKA,EAAE,GAAIA,EAAE,IACxBC,EAAI,CAACC,EAAID,EAAE,GAAKA,EAAE,GAAIA,EAAE,IAExBE,EAAM,EAAIA,GADVD,EAAIzB,KAAKkB,MAAM,EAAIQ,IAMvB,MAAO,CAACL,EAAS,MC7FrB,SAASM,EAASC,GACd,OAAOA,EAAMC,QAAO,CAACC,EAAMC,IAAQD,EAAOC,GAAK,GAGnD,SAASC,EAAaJ,GAClB,OAAOA,EAAMC,QAAO,CAACC,EAAMC,IAAQD,EAAOC,GAAK,GAG5C,SAAS7B,EAAM+B,EAAwBC,GAC1C,QAAK9B,EAAE+B,QAAQF,WAGGG,IAAdF,GAA2BD,EAAII,SAAWH,IAGvCD,EAAIK,MAAMC,IAId,SAASC,EAAUC,GACfC,OAAAA,EAAMD,EAAG,EAAIJ,EAAOI,IAIxB,SAASJ,EAAOI,GACZzC,OAAAA,KAAK2C,KAAKC,EAAIH,EAAGA,IAGrB,SAASG,EACZnB,EACAoB,GAKOlB,OAAAA,EAFQvB,EAAE0C,IAAIrB,EAAGoB,GACEE,IAAIf,IAQ3B,SAASgB,IAIZ,IAAMC,EAAS7C,EAAE0C,kBACjB,OAAOG,EAAOF,IAAIpB,GAGf,SAASuB,EACZC,EACAC,GAGOhD,OAAAA,EAAE0C,IAAIK,EAAIC,GAAIL,KAAKM,GAAQA,EAAI,GAAKA,EAAI,KAG5C,SAASC,EAAOb,GACnB,OAAOA,EAAEM,KAAK5C,IACFA,IAKT,SAASuC,EACZS,EACAI,GAEA,OAAOJ,EAAGJ,KAAK5C,GACJA,EAAIoD,IAIZ,SAAShD,EACZ4C,EACAC,EACA3C,GAOA,OAAOL,EAAE0C,IAAIK,EAAIC,GAAId,OAAOkB,GACxBjB,EAAciB,EAAK,GAAIA,EAAK,GAAI/C,KAIjC,SAASgD,EACZN,EACAC,EACA3C,GAMI8B,SAAAA,EAAcF,EAAOc,GAAK,EAAG1C,KAC7B8B,EAAcF,EAAOe,GAAK,EAAG3C,KAQ1BF,EAHP4C,EAAKX,EAAUW,GACfC,EAAKZ,EAAUY,GAEM3C,GAGlB,SAASiD,EACZP,EACAC,EACA3C,GAEA,OACIgD,EAAcN,EAAIC,EAAI3C,IACtBgD,EAAcN,EAAIG,EAAOF,GAAK3C,GAK/B,SAASkD,EACZlB,GAEA,IAAMmB,EAASvB,EAAOI,GAClBoB,EAAQ7D,KAAK8D,MAAMrB,EAAE,GAAIA,EAAE,IAO/B,OAJIoB,EAAQ,IACRA,GAAS,EAAI7D,KAAK+D,IAGf,CAACH,EAAQC,GAIb,SAASG,EACZvB,GAEA,IAAMwB,EAAQN,EAAiBlB,GAC/B,MAAO,CAACwB,EAAM,GAAgB,IAAXA,EAAM,GAAYjE,KAAK+D,IASvC,SAASG,EACZN,GACAC,IAAAA,yDAAiB,EAEjB,MAAO,CAACD,EAAS5D,KAAKmE,IAAIN,GAAQD,EAAS5D,KAAKoE,IAAIP,IASjD,SAASQ,EACZT,GACAC,IAAAA,yDAAiB,EAEVK,OAAAA,EAAiBN,EAASC,EAAQ7D,KAAK+D,GAAM,KAIjD,SAASO,EACZ7B,EACAoB,GAEA,IAAMI,EAAQN,EAAiBlB,GACzB8B,EAAQN,EAAM,GAAKJ,EAClBK,OAAAA,EAAiBD,EAAM,GAAIM,GAG/B,SAASC,EACZ/B,EACAoB,GAEA,IAAMI,EAAQD,EAAiBvB,GACzB8B,EAAQN,EAAM,GAAKJ,EAClBQ,OAAAA,EAAiBJ,EAAM,GAAIM,GAI/B,SAASE,EACZtB,EACAC,GAEOpD,OAAAA,KAAK0E,KAAK9B,EAAIO,EAAIC,IAAOf,EAAOc,GAAMd,EAAOe,KAWjD,SAASuB,EACZxB,EACAC,GAGA,OAAOV,EAAMU,EADER,EAAIO,EAAIC,GAAMR,EAAIQ,EAAIA,IAKlC,SAASzC,EACZsB,EACApB,GAEOoB,OAAAA,EAAIc,KAAI,CAAC6B,EAAMC,IAGlBtC,EAAcqC,EAAM/D,EAAUgE,IAAMhE,KAKrC,SAASE,EACZkB,EACAjB,GAEOiB,OAAAA,EAAIc,KAAI,CAAC6B,EAAMC,IAGlBtC,EAAgBqC,EAAM5D,EAAU6D,IAAM7D,KAIvC,SAASC,EACZgB,EACAjB,GAEOiB,OAAAA,EAAIc,KAAI,CAAC6B,EAAMC,IAGlBtC,EAAgBqC,EAAM5D,EAAU6D,IAAM7D,KAIvC,SAASG,EACZc,EACAjB,GAEOiB,OAAAA,EAAIc,KAAI,CAAC6B,EAAMC,IAGlBtC,EAAeqC,EAAM5D,EAAU6D,IAAM7D,uQA1DtC,SACHmC,EACAC,GAEQqB,OAAmB,IAAnBA,EAAStB,EAAIC,GAAapD,KAAK+D,wDCvKpC,SAASe,EAAeC,EAAcC,GACzC,IAAMC,EAAKC,EAAiBF,EAAK,GAAIA,EAAK,IACpCG,EAAKD,EAAiBH,EAAOC,EAAK,IAGxC,OAAOE,EADYA,EADCA,EAAmBC,EAAIF,GACME,IAK9C,SAASC,EACZL,EACAC,GAEA,IAAMC,EAAKC,EAAiBF,EAAK,GAAIA,EAAK,IACpCG,EAAKD,EAAiBH,EAAOC,EAAK,IAElCK,EAAcH,EAAiBA,EADjBA,EAAmBC,EAAIF,GACqB,GAAIE,GAC7DD,OAAAA,EAAYF,EAAK,GAAIK,GA6BzB,IAAMnF,EAAKgF,EAGLI,EAAYJ,EACZK,EAAaL,EACbM,EAAiBN,EACjB3E,EAAQ2E,EAGRvB,EAAmBuB,EACnBlB,EAAmBkB,EACnBhB,EAAmBgB,EACnBb,EAAmBa,EAGnBvE,EAAQuE,EACRnE,EAAUmE,EACVjE,EAAUiE,EACV/D,EAAS+D,4CA5Ff,SAAmBH,EAAclB,EAAe4B,GAC/CA,YAAWrD,IAAXqD,EACOP,EAAkBH,EAAOlB,GAEzBqB,EACHO,EACAP,EAAkBA,EAAiBH,EAAOU,GAAS5B,eAKxD,SAAmBkB,EAAclB,EAAe4B,GAC/CA,YAAWrD,IAAXqD,EACOP,EAAkBH,EAAOlB,GAEzBqB,EACHO,EACAP,EAAkBA,EAAiBH,EAAOU,GAAS5B,qBAMxD,SAAyB6B,EAAeC,GAC3C,OAAOT,EAAeA,EAAiBQ,EAAQC,gDAiC5C,SACHD,EACAC,EACAC,GAEA,GAAIF,EAAOrD,SAAWsD,EAAOtD,OACzB,OAAOqD,EAAOrD,OAASsD,EAAOtD,OAElC,IAAK,IAAIwC,EAAI,EAAGA,EAAIa,EAAOrD,OAAQwC,IAC/B,IAAKtC,EAAcmD,EAAOb,GAAIc,EAAOd,GAAIe,GAC9BF,OAAAA,EAAOb,GAAKc,EAAOd,GAGlC,OAAO,iOCxEJ,SAAyBG,EAAYD,GACxC,OAAOc,EAAsBd,EAAOC,iBAGjC,SACHA,EACAD,GAEA,OAAOc,EAAuBd,EAAOC,aAGlC,SAAkBA,GACrB,MAAO,EAAEA,EAAK,GAAG,GAAKA,EAAK,GAAG,IAAM,GAAIA,EAAK,GAAG,GAAKA,EAAK,GAAG,IAAM,UAGhE,SAAec,EAAaC,EAAatF,GAI5C,IAAM0C,EAAK+B,EAAiBY,EAAM,GAAIA,EAAM,IAExC,QAACZ,EAAkB/B,EADZ+B,EAAiBa,EAAM,GAAIA,EAAM,IACbtF,OAI3BoF,EAAaC,EAAM,GAAIC,EAAM,KAM1Bb,EAAkB/B,EADE+B,EAAiBa,EAAM,GAAID,EAAM,IACXrF,iDC7B9C,SAAeuF,EAAWC,EAAWxF,GAExC,IAAM0C,EAAK+B,EAAiBc,EAAK,GAAIA,EAAK,IACpC5C,EAAK8B,EAAiBe,EAAK,GAAIA,EAAK,IAEpCC,EAAaL,EAAaG,EAAK,GAAIC,EAAK,IACxCxC,EAAgByB,EAAsB/B,EAAIC,EAAI3C,GAE7CyF,OAAAA,GAAczC"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/number.js","../../src/vector.js","../../src/point.js","../../src/line.js","../../src/ray.js"],"sourcesContent":["// @flow\n/**\n * Number Utils\n * A number is a js-number, e.g. 5.12\n */\n\nimport _ from \"underscore\";\n\nexport const DEFAULT_TOLERANCE: number = 1e-9;\n\n// TODO: Should this just be Number.Epsilon\nexport const EPSILON: number = Math.pow(2, -42);\n\nexport function is(x: any): boolean {\n return _.isNumber(x) && !_.isNaN(x);\n}\n\nexport function equal(x: number, y: number, tolerance?: number): boolean {\n // Checking for undefined makes this function behave nicely\n // with vectors of different lengths that are _.zip'd together\n if (x == null || y == null) {\n return x === y;\n }\n // We check === here so that +/-Infinity comparisons work correctly\n if (x === y) {\n return true;\n }\n if (tolerance == null) {\n tolerance = DEFAULT_TOLERANCE;\n }\n return Math.abs(x - y) < tolerance;\n}\n\nexport function sign(\n x: number,\n tolerance?: number,\n): number /* Should be: 0 | 1 | -1 */ {\n return equal(x, 0, tolerance) ? 0 : Math.abs(x) / x;\n}\n\nexport function isInteger(num: number, tolerance?: number): boolean {\n return equal(Math.round(num), num, tolerance);\n}\n\n// Round a number to a certain number of decimal places\nexport function round(num: number, precision: number): number {\n const factor = Math.pow(10, precision);\n return Math.round(num * factor) / factor;\n}\n\n// Round num to the nearest multiple of increment\n// i.e. roundTo(83, 5) -> 85\nexport function roundTo(num: number, increment: number): number {\n return Math.round(num / increment) * increment;\n}\n\nexport function floorTo(num: number, increment: number): number {\n return Math.floor(num / increment) * increment;\n}\n\nexport function ceilTo(num: number, increment: number): number {\n return Math.ceil(num / increment) * increment;\n}\n\n/**\n * toFraction\n *\n * Returns a [numerator, denominator] array rational representation\n * of `decimal`\n *\n * See http://en.wikipedia.org/wiki/Continued_fraction for implementation\n * details\n *\n * toFraction(4/8) => [1, 2]\n * toFraction(0.66) => [33, 50]\n * toFraction(0.66, 0.01) => [2/3]\n * toFraction(283 + 1/3) => [850, 3]\n */\nexport function toFraction(\n decimal: number,\n tolerance: number = EPSILON, // can't be 0\n maxDenominator: number = 1000,\n): [number, number] {\n // Initialize everything to compute successive terms of\n // continued-fraction approximations via recurrence relation\n let n = [1, 0];\n let d = [0, 1];\n let a = Math.floor(decimal);\n let rem = decimal - a;\n\n while (d[0] <= maxDenominator) {\n if (equal(n[0] / d[0], decimal, tolerance)) {\n return [n[0], d[0]];\n }\n n = [a * n[0] + n[1], n[0]];\n d = [a * d[0] + d[1], d[0]];\n a = Math.floor(1 / rem);\n rem = 1 / rem - a;\n }\n\n // We failed to find a nice rational representation,\n // so return an irrational \"fraction\"\n return [decimal, 1];\n}\n","// @flow\n/**\n * Vector Utils\n * A vector is an array of numbers e.g. [0, 3, 4].\n */\n\nimport _ from \"underscore\";\nimport * as knumber from \"./number.js\";\n\ntype Vector = $ReadOnlyArray<number>;\n\nfunction arraySum(array: $ReadOnlyArray<number>): number {\n return array.reduce((memo, arg) => memo + arg, 0);\n}\n\nfunction arrayProduct(array: $ReadOnlyArray<number>): number {\n return array.reduce((memo, arg) => memo * arg, 1);\n}\n\n/**\n * Checks if the given vector contains only numbers and, optionally, is of the\n * right dimension (length).\n *\n * is([1, 2, 3]) -> true\n * is([1, \"Hello\", 3]) -> false\n * is([1, 2, 3], 1) -> false\n */\nexport function is<T>(vec: $ReadOnlyArray<T>, dimension?: number): boolean {\n if (!_.isArray(vec)) {\n return false;\n }\n if (dimension !== undefined && vec.length !== dimension) {\n return false;\n }\n return vec.every(knumber.is);\n}\n\n// Normalize to a unit vector\nexport function normalize<V: Vector>(v: V): V {\n return scale(v, 1 / length(v));\n}\n\n// Length/magnitude of a vector\nexport function length(v: Vector): number {\n return Math.sqrt(dot(v, v));\n}\n// Dot product of two vectors\nexport function dot(a: Vector, b: Vector): number {\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n const zipped = _.zip(a, b);\n const multiplied = zipped.map(arrayProduct);\n return arraySum(multiplied);\n}\n\n/* vector-add multiple [x, y] coords/vectors\n *\n * add([1, 2], [3, 4]) -> [4, 6]\n */\nexport function add<V: Vector>(...vecs: $ReadOnlyArray<V>): V {\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n const zipped = _.zip(...vecs);\n return zipped.map(arraySum);\n}\n\nexport function subtract<V: Vector>(v1: V, v2: V): V {\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n return _.zip(v1, v2).map((dim) => dim[0] - dim[1]);\n}\n\nexport function negate<V: Vector>(v: V): V {\n // $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking\n return v.map((x) => {\n return -x;\n });\n}\n\n// Scale a vector\nexport function scale<V: Vector>(v1: V, scalar: number): V {\n // $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking\n return v1.map((x) => {\n return x * scalar;\n });\n}\n\nexport function equal(v1: Vector, v2: Vector, tolerance?: number): boolean {\n // _.zip will nicely deal with the lengths, going through\n // the length of the longest vector. knumber.equal then\n // returns false for any number compared to the undefined\n // passed in if one of the vectors is shorter.\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n return _.zip(v1, v2).every((pair) =>\n knumber.equal(pair[0], pair[1], tolerance),\n );\n}\n\nexport function codirectional(\n v1: Vector,\n v2: Vector,\n tolerance?: number,\n): boolean {\n // The origin is trivially codirectional with all other vectors.\n // This gives nice semantics for codirectionality between points when\n // comparing their difference vectors.\n if (\n knumber.equal(length(v1), 0, tolerance) ||\n knumber.equal(length(v2), 0, tolerance)\n ) {\n return true;\n }\n\n v1 = normalize(v1);\n v2 = normalize(v2);\n\n return equal(v1, v2, tolerance);\n}\n\nexport function collinear(v1: Vector, v2: Vector, tolerance?: number): boolean {\n return (\n codirectional(v1, v2, tolerance) ||\n codirectional(v1, negate(v2), tolerance)\n );\n}\n\n// Convert a cartesian coordinate into a radian polar coordinate\nexport function polarRadFromCart(\n v: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> {\n const radius = length(v);\n let theta = Math.atan2(v[1], v[0]);\n\n // Convert angle range from [-pi, pi] to [0, 2pi]\n if (theta < 0) {\n theta += 2 * Math.PI;\n }\n\n return [radius, theta];\n}\n\n// Converts a cartesian coordinate into a degree polar coordinate\nexport function polarDegFromCart(\n v: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> /* TODO: convert to tuple/Point */ {\n const polar = polarRadFromCart(v);\n return [polar[0], (polar[1] * 180) / Math.PI];\n}\n\n/* Convert a polar coordinate into a cartesian coordinate\n *\n * Examples:\n * cartFromPolarRad(5, Math.PI)\n * cartFromPolarRad([5, Math.PI])\n */\nexport function cartFromPolarRad(\n radius: number,\n theta?: number = 0,\n): $ReadOnlyArray<number> /* TODO: convert to tuple/Point */ {\n return [radius * Math.cos(theta), radius * Math.sin(theta)];\n}\n\n/* Convert a polar coordinate into a cartesian coordinate\n *\n * Examples:\n * cartFromPolarDeg(5, 30)\n * cartFromPolarDeg([5, 30])\n */\nexport function cartFromPolarDeg(\n radius: number,\n theta?: number = 0,\n): $ReadOnlyArray<number> {\n return cartFromPolarRad(radius, (theta * Math.PI) / 180);\n}\n\n// Rotate vector\nexport function rotateRad(\n v: $ReadOnlyArray<number>,\n theta: number,\n): $ReadOnlyArray<number> {\n const polar = polarRadFromCart(v);\n const angle = polar[1] + theta;\n return cartFromPolarRad(polar[0], angle);\n}\n\nexport function rotateDeg(\n v: $ReadOnlyArray<number>,\n theta: number,\n): $ReadOnlyArray<number> {\n const polar = polarDegFromCart(v);\n const angle = polar[1] + theta;\n return cartFromPolarDeg(polar[0], angle);\n}\n\n// Angle between two vectors\nexport function angleRad(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): number {\n return Math.acos(dot(v1, v2) / (length(v1) * length(v2)));\n}\n\nexport function angleDeg(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): number {\n return (angleRad(v1, v2) * 180) / Math.PI;\n}\n\n// Vector projection of v1 onto v2\nexport function projection(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> {\n const scalar = dot(v1, v2) / dot(v2, v2);\n return scale(v2, scalar);\n}\n\n// Round each number to a certain number of decimal places\nexport function round<V: Vector>(vec: V, precision: V | number): V {\n // $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.round(elem, precision[i] || precision),\n );\n}\n\n// Round each number to the nearest increment\nexport function roundTo<V: Vector>(vec: V, increment: V | number): V {\n // $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.roundTo(elem, increment[i] || increment),\n );\n}\n\nexport function floorTo<V: Vector>(vec: V, increment: V | number): V {\n // $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.floorTo(elem, increment[i] || increment),\n );\n}\n\nexport function ceilTo<V: Vector>(vec: V, increment: V | number): V {\n // $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.ceilTo(elem, increment[i] || increment),\n );\n}\n","// @flow\n/**\n * Point Utils\n * A point is an array of two numbers e.g. [0, 0].\n */\n\nimport _ from \"underscore\";\n\nimport * as kvector from \"./vector.js\";\nimport * as knumber from \"./number.js\";\n\n// A point, in 2D, 3D, or nD space.\nexport type Point = $ReadOnlyArray<number>;\n\n// Rotate point (around origin unless a center is specified)\nexport function rotateRad(point: Point, theta: number, center: Point): Point {\n if (center === undefined) {\n return kvector.rotateRad(point, theta);\n } else {\n return kvector.add(\n center,\n kvector.rotateRad(kvector.subtract(point, center), theta),\n );\n }\n}\n\nexport function rotateDeg(point: Point, theta: number, center: Point): Point {\n if (center === undefined) {\n return kvector.rotateDeg(point, theta);\n } else {\n return kvector.add(\n center,\n kvector.rotateDeg(kvector.subtract(point, center), theta),\n );\n }\n}\n\n// Distance between two points\nexport function distanceToPoint(point1: Point, point2: Point): number {\n return kvector.length(kvector.subtract(point1, point2));\n}\n\n// Distance between point and line\nexport function distanceToLine(point: Point, line: [Point, Point]): number {\n const lv = kvector.subtract(line[1], line[0]);\n const pv = kvector.subtract(point, line[0]);\n const projectedPv = kvector.projection(pv, lv);\n const distancePv = kvector.subtract(projectedPv, pv);\n return kvector.length(distancePv);\n}\n\n// Reflect point over line\nexport function reflectOverLine(point: Point, line: [Point, Point]): Point {\n const lv = kvector.subtract(line[1], line[0]);\n const pv = kvector.subtract(point, line[0]);\n const projectedPv = kvector.projection(pv, lv);\n const reflectedPv = kvector.subtract(kvector.scale(projectedPv, 2), pv);\n return kvector.add(line[0], reflectedPv);\n}\n\n/**\n * Compares two points, returning -1, 0, or 1, for use with\n * Array.prototype.sort\n *\n * Note: This technically doesn't satisfy the total-ordering\n * requirements of Array.prototype.sort unless equalityTolerance\n * is 0. In some cases very close points that compare within a\n * few equalityTolerances could appear in the wrong order.\n */\nexport function compare(\n point1: Point,\n point2: Point,\n equalityTolerance?: number,\n): number /* TODO: convert to -1 | 0 | 1 type */ {\n if (point1.length !== point2.length) {\n return point1.length - point2.length;\n }\n for (let i = 0; i < point1.length; i++) {\n if (!knumber.equal(point1[i], point2[i], equalityTolerance)) {\n return point1[i] - point2[i];\n }\n }\n return 0;\n}\n\n// Check if a value is a point\nexport const is = kvector.is;\n\n// Add and subtract vector(s)\nexport const addVector = kvector.add;\nexport const addVectors = kvector.add;\nexport const subtractVector = kvector.subtract;\nexport const equal = kvector.equal;\n\n// Convert from cartesian to polar and back\nexport const polarRadFromCart = kvector.polarRadFromCart;\nexport const polarDegFromCart = kvector.polarDegFromCart;\nexport const cartFromPolarRad = kvector.cartFromPolarRad;\nexport const cartFromPolarDeg = kvector.cartFromPolarDeg;\n\n// Rounding\nexport const round = kvector.round;\nexport const roundTo = kvector.roundTo;\nexport const floorTo = kvector.floorTo;\nexport const ceilTo = kvector.ceilTo;\n","// @flow\n/**\n * Line Utils\n * A line is an array of two points e.g. [[-5, 0], [5, 0]].\n */\n\nimport * as kpoint from \"./point.js\";\nimport * as kvector from \"./vector.js\";\n\nimport type {Point} from \"./point.js\";\n\nexport type Line = [Point, Point];\n\nexport function distanceToPoint(line: Line, point: Point): number {\n return kpoint.distanceToLine(point, line);\n}\n\nexport function reflectPoint(line: Line, point: Point): Point {\n return kpoint.reflectOverLine(point, line);\n}\n\nexport function midpoint(line: Line): Point {\n return [(line[0][0] + line[1][0]) / 2, (line[0][1] + line[1][1]) / 2];\n}\n\nexport function equal(line1: Line, line2: Line, tolerance?: number): boolean {\n // TODO: A nicer implementation might just check collinearity of\n // vectors using underscore magick\n // Compare the directions of the lines\n const v1 = kvector.subtract(line1[1], line1[0]);\n const v2 = kvector.subtract(line2[1], line2[0]);\n if (!kvector.collinear(v1, v2, tolerance)) {\n return false;\n }\n // If the start point is the same for the two lines, then they are the same\n if (kpoint.equal(line1[0], line2[0])) {\n return true;\n }\n // Make sure that the direction to get from line1 to\n // line2 is the same as the direction of the lines\n const line1ToLine2Vector = kvector.subtract(line2[0], line1[0]);\n return kvector.collinear(v1, line1ToLine2Vector, tolerance);\n}\n","// @flow\n/**\n * Ray Utils\n * A ray (→) is an array of an endpoint and another point along the ray.\n * For example, [[0, 0], [1, 0]] is the ray starting at the origin and\n * traveling along the positive x-axis.\n */\n\nimport * as kvector from \"./vector.js\";\nimport * as kpoint from \"./point.js\";\n\nimport type {Point} from \"./point\";\n\nexport type Ray = [Point, Point];\n\nexport function equal(ray1: Ray, ray2: Ray, tolerance: number): boolean {\n // Compare the directions of the rays\n const v1 = kvector.subtract(ray1[1], ray1[0]);\n const v2 = kvector.subtract(ray2[1], ray2[0]);\n\n const sameOrigin = kpoint.equal(ray1[0], ray2[0]);\n const codirectional = kvector.codirectional(v1, v2, tolerance);\n\n return sameOrigin && codirectional;\n}\n"],"names":["EPSILON","Math","pow","is","x","_","isNumber","isNaN","equal","y","tolerance","abs","round","num","precision","factor","roundTo","increment","floorTo","floor","ceilTo","ceil","decimal","maxDenominator","n","d","a","rem","arraySum","array","reduce","memo","arg","arrayProduct","vec","dimension","isArray","undefined","length","every","knumber","normalize","v","scale","sqrt","dot","b","zip","map","add","zipped","subtract","v1","v2","dim","negate","scalar","pair","codirectional","collinear","polarRadFromCart","radius","theta","atan2","PI","polarDegFromCart","polar","cartFromPolarRad","cos","sin","cartFromPolarDeg","rotateRad","angle","rotateDeg","angleRad","acos","projection","elem","i","distanceToLine","point","line","lv","kvector","pv","reflectOverLine","reflectedPv","addVector","addVectors","subtractVector","center","point1","point2","equalityTolerance","kpoint","line1","line2","ray1","ray2","sameOrigin"],"mappings":"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"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/number.js","../src/vector.js","../src/point.js","../src/line.js","../src/ray.js"],"sourcesContent":["// @flow\n/**\n * Number Utils\n * A number is a js-number, e.g. 5.12\n */\n\nimport _ from \"underscore\";\n\nexport const DEFAULT_TOLERANCE: number = 1e-9;\n\n// TODO: Should this just be Number.Epsilon\nexport const EPSILON: number = Math.pow(2, -42);\n\nexport function is(x: any): boolean {\n return _.isNumber(x) && !_.isNaN(x);\n}\n\nexport function equal(x: number, y: number, tolerance?: number): boolean {\n // Checking for undefined makes this function behave nicely\n // with vectors of different lengths that are _.zip'd together\n if (x == null || y == null) {\n return x === y;\n }\n // We check === here so that +/-Infinity comparisons work correctly\n if (x === y) {\n return true;\n }\n if (tolerance == null) {\n tolerance = DEFAULT_TOLERANCE;\n }\n return Math.abs(x - y) < tolerance;\n}\n\nexport function sign(\n x: number,\n tolerance?: number,\n): number /* Should be: 0 | 1 | -1 */ {\n return equal(x, 0, tolerance) ? 0 : Math.abs(x) / x;\n}\n\nexport function isInteger(num: number, tolerance?: number): boolean {\n return equal(Math.round(num), num, tolerance);\n}\n\n// Round a number to a certain number of decimal places\nexport function round(num: number, precision: number): number {\n const factor = Math.pow(10, precision);\n return Math.round(num * factor) / factor;\n}\n\n// Round num to the nearest multiple of increment\n// i.e. roundTo(83, 5) -> 85\nexport function roundTo(num: number, increment: number): number {\n return Math.round(num / increment) * increment;\n}\n\nexport function floorTo(num: number, increment: number): number {\n return Math.floor(num / increment) * increment;\n}\n\nexport function ceilTo(num: number, increment: number): number {\n return Math.ceil(num / increment) * increment;\n}\n\n/**\n * toFraction\n *\n * Returns a [numerator, denominator] array rational representation\n * of `decimal`\n *\n * See http://en.wikipedia.org/wiki/Continued_fraction for implementation\n * details\n *\n * toFraction(4/8) => [1, 2]\n * toFraction(0.66) => [33, 50]\n * toFraction(0.66, 0.01) => [2/3]\n * toFraction(283 + 1/3) => [850, 3]\n */\nexport function toFraction(\n decimal: number,\n tolerance: number = EPSILON, // can't be 0\n maxDenominator: number = 1000,\n): [number, number] {\n // Initialize everything to compute successive terms of\n // continued-fraction approximations via recurrence relation\n let n = [1, 0];\n let d = [0, 1];\n let a = Math.floor(decimal);\n let rem = decimal - a;\n\n while (d[0] <= maxDenominator) {\n if (equal(n[0] / d[0], decimal, tolerance)) {\n return [n[0], d[0]];\n }\n n = [a * n[0] + n[1], n[0]];\n d = [a * d[0] + d[1], d[0]];\n a = Math.floor(1 / rem);\n rem = 1 / rem - a;\n }\n\n // We failed to find a nice rational representation,\n // so return an irrational \"fraction\"\n return [decimal, 1];\n}\n","// @flow\n/**\n * Vector Utils\n * A vector is an array of numbers e.g. [0, 3, 4].\n */\n\nimport _ from \"underscore\";\nimport * as knumber from \"./number.js\";\n\nfunction arraySum(array: $ReadOnlyArray<number>): number {\n return array.reduce((memo, arg) => memo + arg, 0);\n}\n\nfunction arrayProduct(array: $ReadOnlyArray<number>): number {\n return array.reduce((memo, arg) => memo * arg, 1);\n}\n\nexport function is<T>(vec: $ReadOnlyArray<T>, dimension: number): boolean {\n if (!_.isArray(vec)) {\n return false;\n }\n if (dimension !== undefined && vec.length !== dimension) {\n return false;\n }\n return vec.every(knumber.is);\n}\n\n// Normalize to a unit vector\nexport function normalize(v: $ReadOnlyArray<number>): $ReadOnlyArray<number> {\n return scale(v, 1 / length(v));\n}\n\n// Length/magnitude of a vector\nexport function length(v: $ReadOnlyArray<number>): number {\n return Math.sqrt(dot(v, v));\n}\n// Dot product of two vectors\nexport function dot(\n a: $ReadOnlyArray<number>,\n b: $ReadOnlyArray<number>,\n): number {\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n const zipped = _.zip(a, b);\n const multiplied = zipped.map(arrayProduct);\n return arraySum(multiplied);\n}\n\n/* vector-add multiple [x, y] coords/vectors\n *\n * add([1, 2], [3, 4]) -> [4, 6]\n */\nexport function add(\n ...vecs: $ReadOnlyArray<$ReadOnlyArray<number>>\n): $ReadOnlyArray<number> {\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n const zipped = _.zip(...vecs);\n return zipped.map(arraySum);\n}\n\nexport function subtract(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> {\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n return _.zip(v1, v2).map((dim) => dim[0] - dim[1]);\n}\n\nexport function negate(v: $ReadOnlyArray<number>): $ReadOnlyArray<number> {\n return v.map((x) => {\n return -x;\n });\n}\n\n// Scale a vector\nexport function scale(\n v1: $ReadOnlyArray<number>,\n scalar: number,\n): $ReadOnlyArray<number> {\n return v1.map((x) => {\n return x * scalar;\n });\n}\n\nexport function equal(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n tolerance?: number,\n): boolean {\n // _.zip will nicely deal with the lengths, going through\n // the length of the longest vector. knumber.equal then\n // returns false for any number compared to the undefined\n // passed in if one of the vectors is shorter.\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n return _.zip(v1, v2).every((pair) =>\n knumber.equal(pair[0], pair[1], tolerance),\n );\n}\n\nexport function codirectional(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n tolerance?: number,\n): boolean {\n // The origin is trivially codirectional with all other vectors.\n // This gives nice semantics for codirectionality between points when\n // comparing their difference vectors.\n if (\n knumber.equal(length(v1), 0, tolerance) ||\n knumber.equal(length(v2), 0, tolerance)\n ) {\n return true;\n }\n\n v1 = normalize(v1);\n v2 = normalize(v2);\n\n return equal(v1, v2, tolerance);\n}\n\nexport function collinear(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n tolerance?: number,\n): boolean {\n return (\n codirectional(v1, v2, tolerance) ||\n codirectional(v1, negate(v2), tolerance)\n );\n}\n\n// Convert a cartesian coordinate into a radian polar coordinate\nexport function polarRadFromCart(\n v: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> {\n const radius = length(v);\n let theta = Math.atan2(v[1], v[0]);\n\n // Convert angle range from [-pi, pi] to [0, 2pi]\n if (theta < 0) {\n theta += 2 * Math.PI;\n }\n\n return [radius, theta];\n}\n\n// Converts a cartesian coordinate into a degree polar coordinate\nexport function polarDegFromCart(\n v: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> /* TODO: convert to tuple/Point */ {\n const polar = polarRadFromCart(v);\n return [polar[0], (polar[1] * 180) / Math.PI];\n}\n\n/* Convert a polar coordinate into a cartesian coordinate\n *\n * Examples:\n * cartFromPolarRad(5, Math.PI)\n * cartFromPolarRad([5, Math.PI])\n */\nexport function cartFromPolarRad(\n radius: number,\n theta?: number = 0,\n): $ReadOnlyArray<number> /* TODO: convert to tuple/Point */ {\n return [radius * Math.cos(theta), radius * Math.sin(theta)];\n}\n\n/* Convert a polar coordinate into a cartesian coordinate\n *\n * Examples:\n * cartFromPolarDeg(5, 30)\n * cartFromPolarDeg([5, 30])\n */\nexport function cartFromPolarDeg(\n radius: number,\n theta?: number = 0,\n): $ReadOnlyArray<number> {\n return cartFromPolarRad(radius, (theta * Math.PI) / 180);\n}\n\n// Rotate vector\nexport function rotateRad(\n v: $ReadOnlyArray<number>,\n theta: number,\n): $ReadOnlyArray<number> {\n const polar = polarRadFromCart(v);\n const angle = polar[1] + theta;\n return cartFromPolarRad(polar[0], angle);\n}\n\nexport function rotateDeg(\n v: $ReadOnlyArray<number>,\n theta: number,\n): $ReadOnlyArray<number> {\n const polar = polarDegFromCart(v);\n const angle = polar[1] + theta;\n return cartFromPolarDeg(polar[0], angle);\n}\n\n// Angle between two vectors\nexport function angleRad(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): number {\n return Math.acos(dot(v1, v2) / (length(v1) * length(v2)));\n}\n\nexport function angleDeg(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): number {\n return (angleRad(v1, v2) * 180) / Math.PI;\n}\n\n// Vector projection of v1 onto v2\nexport function projection(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> {\n const scalar = dot(v1, v2) / dot(v2, v2);\n return scale(v2, scalar);\n}\n\n// Round each number to a certain number of decimal places\nexport function round(\n vec: $ReadOnlyArray<number>,\n precision: $ReadOnlyArray<number> | number,\n): $ReadOnlyArray<number> {\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.round(elem, precision[i] || precision),\n );\n}\n\n// Round each number to the nearest increment\nexport function roundTo(\n vec: $ReadOnlyArray<number>,\n increment: $ReadOnlyArray<number> | number,\n): $ReadOnlyArray<number> {\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.roundTo(elem, increment[i] || increment),\n );\n}\n\nexport function floorTo(\n vec: $ReadOnlyArray<number>,\n increment: $ReadOnlyArray<number> | number,\n): $ReadOnlyArray<number> {\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.floorTo(elem, increment[i] || increment),\n );\n}\n\nexport function ceilTo(\n vec: $ReadOnlyArray<number>,\n increment: $ReadOnlyArray<number> | number,\n): $ReadOnlyArray<number> {\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.ceilTo(elem, increment[i] || increment),\n );\n}\n","// @flow\n/**\n * Point Utils\n * A point is an array of two numbers e.g. [0, 0].\n */\n\nimport _ from \"underscore\";\n\nimport * as kvector from \"./vector.js\";\nimport * as knumber from \"./number.js\";\n\n// A point, in 2D, 3D, or nD space.\nexport type Point = $ReadOnlyArray<number>;\n\n// Rotate point (around origin unless a center is specified)\nexport function rotateRad(point: Point, theta: number, center: Point): Point {\n if (center === undefined) {\n return kvector.rotateRad(point, theta);\n } else {\n return kvector.add(\n center,\n kvector.rotateRad(kvector.subtract(point, center), theta),\n );\n }\n}\n\nexport function rotateDeg(point: Point, theta: number, center: Point): Point {\n if (center === undefined) {\n return kvector.rotateDeg(point, theta);\n } else {\n return kvector.add(\n center,\n kvector.rotateDeg(kvector.subtract(point, center), theta),\n );\n }\n}\n\n// Distance between two points\nexport function distanceToPoint(point1: Point, point2: Point): number {\n return kvector.length(kvector.subtract(point1, point2));\n}\n\n// Distance between point and line\nexport function distanceToLine(point: Point, line: [Point, Point]): number {\n const lv = kvector.subtract(line[1], line[0]);\n const pv = kvector.subtract(point, line[0]);\n const projectedPv = kvector.projection(pv, lv);\n const distancePv = kvector.subtract(projectedPv, pv);\n return kvector.length(distancePv);\n}\n\n// Reflect point over line\nexport function reflectOverLine(\n point: Point,\n line: [Point, Point],\n): $ReadOnlyArray<number> /* TODO: convert to Point */ {\n const lv = kvector.subtract(line[1], line[0]);\n const pv = kvector.subtract(point, line[0]);\n const projectedPv = kvector.projection(pv, lv);\n const reflectedPv = kvector.subtract(kvector.scale(projectedPv, 2), pv);\n return kvector.add(line[0], reflectedPv);\n}\n\n/**\n * Compares two points, returning -1, 0, or 1, for use with\n * Array.prototype.sort\n *\n * Note: This technically doesn't satisfy the total-ordering\n * requirements of Array.prototype.sort unless equalityTolerance\n * is 0. In some cases very close points that compare within a\n * few equalityTolerances could appear in the wrong order.\n */\nexport function compare(\n point1: Point,\n point2: Point,\n equalityTolerance?: number,\n): number /* TODO: convert to -1 | 0 | 1 type */ {\n if (point1.length !== point2.length) {\n return point1.length - point2.length;\n }\n for (let i = 0; i < point1.length; i++) {\n if (!knumber.equal(point1[i], point2[i], equalityTolerance)) {\n return point1[i] - point2[i];\n }\n }\n return 0;\n}\n\n// Check if a value is a point\nexport const is = kvector.is;\n\n// Add and subtract vector(s)\nexport const addVector = kvector.add;\nexport const addVectors = kvector.add;\nexport const subtractVector = kvector.subtract;\nexport const equal = kvector.equal;\n\n// Convert from cartesian to polar and back\nexport const polarRadFromCart = kvector.polarRadFromCart;\nexport const polarDegFromCart = kvector.polarDegFromCart;\nexport const cartFromPolarRad = kvector.cartFromPolarRad;\nexport const cartFromPolarDeg = kvector.cartFromPolarDeg;\n\n// Rounding\nexport const round = kvector.round;\nexport const roundTo = kvector.roundTo;\nexport const floorTo = kvector.floorTo;\nexport const ceilTo = kvector.ceilTo;\n","// @flow\n/**\n * Line Utils\n * A line is an array of two points e.g. [[-5, 0], [5, 0]].\n */\n\nimport * as kpoint from \"./point.js\";\nimport * as kvector from \"./vector.js\";\n\nimport type {Point} from \"./point.js\";\n\nexport type Line = [Point, Point];\n\nexport function distanceToPoint(line: Line, point: Point): number {\n return kpoint.distanceToLine(point, line);\n}\n\nexport function reflectPoint(\n line: Line,\n point: Point,\n): $ReadOnlyArray<number> /* TODO: convert to Point */ {\n return kpoint.reflectOverLine(point, line);\n}\n\nexport function midpoint(line: Line): Point {\n return [(line[0][0] + line[1][0]) / 2, (line[0][1] + line[1][1]) / 2];\n}\n\nexport function equal(line1: Line, line2: Line, tolerance?: number): boolean {\n // TODO: A nicer implementation might just check collinearity of\n // vectors using underscore magick\n // Compare the directions of the lines\n const v1 = kvector.subtract(line1[1], line1[0]);\n const v2 = kvector.subtract(line2[1], line2[0]);\n if (!kvector.collinear(v1, v2, tolerance)) {\n return false;\n }\n // If the start point is the same for the two lines, then they are the same\n if (kpoint.equal(line1[0], line2[0])) {\n return true;\n }\n // Make sure that the direction to get from line1 to\n // line2 is the same as the direction of the lines\n const line1ToLine2Vector = kvector.subtract(line2[0], line1[0]);\n return kvector.collinear(v1, line1ToLine2Vector, tolerance);\n}\n","// @flow\n/**\n * Ray Utils\n * A ray (→) is an array of an endpoint and another point along the ray.\n * For example, [[0, 0], [1, 0]] is the ray starting at the origin and\n * traveling along the positive x-axis.\n */\n\nimport * as kvector from \"./vector.js\";\nimport * as kpoint from \"./point.js\";\n\nimport type {Point} from \"./point\";\n\nexport type Ray = [Point, Point];\n\nexport function equal(ray1: Ray, ray2: Ray, tolerance: number): boolean {\n // Compare the directions of the rays\n const v1 = kvector.subtract(ray1[1], ray1[0]);\n const v2 = kvector.subtract(ray2[1], ray2[0]);\n\n const sameOrigin = kpoint.equal(ray1[0], ray2[0]);\n const codirectional = kvector.codirectional(v1, v2, tolerance);\n\n return sameOrigin && codirectional;\n}\n"],"names":["EPSILON","Math","pow","is","x","_","isNumber","isNaN","equal","y","tolerance","abs","round","num","precision","factor","roundTo","increment","floorTo","floor","ceilTo","ceil","decimal","maxDenominator","n","d","a","rem","arraySum","array","reduce","memo","arg","arrayProduct","vec","dimension","isArray","undefined","length","every","knumber","normalize","v","scale","sqrt","dot","b","zip","map","add","zipped","subtract","v1","v2","dim","negate","scalar","pair","codirectional","collinear","polarRadFromCart","radius","theta","atan2","PI","polarDegFromCart","polar","cartFromPolarRad","cos","sin","cartFromPolarDeg","rotateRad","angle","rotateDeg","angleRad","acos","projection","elem","i","distanceToLine","point","line","lv","kvector","pv","reflectOverLine","reflectedPv","addVector","addVectors","subtractVector","center","point1","point2","equalityTolerance","kpoint","line1","line2","ray1","ray2","sameOrigin"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/number.js","../src/vector.js","../src/point.js","../src/line.js","../src/ray.js"],"sourcesContent":["// @flow\n/**\n * Number Utils\n * A number is a js-number, e.g. 5.12\n */\n\nimport _ from \"underscore\";\n\nexport const DEFAULT_TOLERANCE: number = 1e-9;\n\n// TODO: Should this just be Number.Epsilon\nexport const EPSILON: number = Math.pow(2, -42);\n\nexport function is(x: any): boolean {\n return _.isNumber(x) && !_.isNaN(x);\n}\n\nexport function equal(x: number, y: number, tolerance?: number): boolean {\n // Checking for undefined makes this function behave nicely\n // with vectors of different lengths that are _.zip'd together\n if (x == null || y == null) {\n return x === y;\n }\n // We check === here so that +/-Infinity comparisons work correctly\n if (x === y) {\n return true;\n }\n if (tolerance == null) {\n tolerance = DEFAULT_TOLERANCE;\n }\n return Math.abs(x - y) < tolerance;\n}\n\nexport function sign(\n x: number,\n tolerance?: number,\n): number /* Should be: 0 | 1 | -1 */ {\n return equal(x, 0, tolerance) ? 0 : Math.abs(x) / x;\n}\n\nexport function isInteger(num: number, tolerance?: number): boolean {\n return equal(Math.round(num), num, tolerance);\n}\n\n// Round a number to a certain number of decimal places\nexport function round(num: number, precision: number): number {\n const factor = Math.pow(10, precision);\n return Math.round(num * factor) / factor;\n}\n\n// Round num to the nearest multiple of increment\n// i.e. roundTo(83, 5) -> 85\nexport function roundTo(num: number, increment: number): number {\n return Math.round(num / increment) * increment;\n}\n\nexport function floorTo(num: number, increment: number): number {\n return Math.floor(num / increment) * increment;\n}\n\nexport function ceilTo(num: number, increment: number): number {\n return Math.ceil(num / increment) * increment;\n}\n\n/**\n * toFraction\n *\n * Returns a [numerator, denominator] array rational representation\n * of `decimal`\n *\n * See http://en.wikipedia.org/wiki/Continued_fraction for implementation\n * details\n *\n * toFraction(4/8) => [1, 2]\n * toFraction(0.66) => [33, 50]\n * toFraction(0.66, 0.01) => [2/3]\n * toFraction(283 + 1/3) => [850, 3]\n */\nexport function toFraction(\n decimal: number,\n tolerance: number = EPSILON, // can't be 0\n maxDenominator: number = 1000,\n): [number, number] {\n // Initialize everything to compute successive terms of\n // continued-fraction approximations via recurrence relation\n let n = [1, 0];\n let d = [0, 1];\n let a = Math.floor(decimal);\n let rem = decimal - a;\n\n while (d[0] <= maxDenominator) {\n if (equal(n[0] / d[0], decimal, tolerance)) {\n return [n[0], d[0]];\n }\n n = [a * n[0] + n[1], n[0]];\n d = [a * d[0] + d[1], d[0]];\n a = Math.floor(1 / rem);\n rem = 1 / rem - a;\n }\n\n // We failed to find a nice rational representation,\n // so return an irrational \"fraction\"\n return [decimal, 1];\n}\n","// @flow\n/**\n * Vector Utils\n * A vector is an array of numbers e.g. [0, 3, 4].\n */\n\nimport _ from \"underscore\";\nimport * as knumber from \"./number.js\";\n\ntype Vector = $ReadOnlyArray<number>;\n\nfunction arraySum(array: $ReadOnlyArray<number>): number {\n return array.reduce((memo, arg) => memo + arg, 0);\n}\n\nfunction arrayProduct(array: $ReadOnlyArray<number>): number {\n return array.reduce((memo, arg) => memo * arg, 1);\n}\n\n/**\n * Checks if the given vector contains only numbers and, optionally, is of the\n * right dimension (length).\n *\n * is([1, 2, 3]) -> true\n * is([1, \"Hello\", 3]) -> false\n * is([1, 2, 3], 1) -> false\n */\nexport function is<T>(vec: $ReadOnlyArray<T>, dimension?: number): boolean {\n if (!_.isArray(vec)) {\n return false;\n }\n if (dimension !== undefined && vec.length !== dimension) {\n return false;\n }\n return vec.every(knumber.is);\n}\n\n// Normalize to a unit vector\nexport function normalize<V: Vector>(v: V): V {\n return scale(v, 1 / length(v));\n}\n\n// Length/magnitude of a vector\nexport function length(v: Vector): number {\n return Math.sqrt(dot(v, v));\n}\n// Dot product of two vectors\nexport function dot(a: Vector, b: Vector): number {\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n const zipped = _.zip(a, b);\n const multiplied = zipped.map(arrayProduct);\n return arraySum(multiplied);\n}\n\n/* vector-add multiple [x, y] coords/vectors\n *\n * add([1, 2], [3, 4]) -> [4, 6]\n */\nexport function add<V: Vector>(...vecs: $ReadOnlyArray<V>): V {\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n const zipped = _.zip(...vecs);\n return zipped.map(arraySum);\n}\n\nexport function subtract<V: Vector>(v1: V, v2: V): V {\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n return _.zip(v1, v2).map((dim) => dim[0] - dim[1]);\n}\n\nexport function negate<V: Vector>(v: V): V {\n // $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking\n return v.map((x) => {\n return -x;\n });\n}\n\n// Scale a vector\nexport function scale<V: Vector>(v1: V, scalar: number): V {\n // $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking\n return v1.map((x) => {\n return x * scalar;\n });\n}\n\nexport function equal(v1: Vector, v2: Vector, tolerance?: number): boolean {\n // _.zip will nicely deal with the lengths, going through\n // the length of the longest vector. knumber.equal then\n // returns false for any number compared to the undefined\n // passed in if one of the vectors is shorter.\n // $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray\n return _.zip(v1, v2).every((pair) =>\n knumber.equal(pair[0], pair[1], tolerance),\n );\n}\n\nexport function codirectional(\n v1: Vector,\n v2: Vector,\n tolerance?: number,\n): boolean {\n // The origin is trivially codirectional with all other vectors.\n // This gives nice semantics for codirectionality between points when\n // comparing their difference vectors.\n if (\n knumber.equal(length(v1), 0, tolerance) ||\n knumber.equal(length(v2), 0, tolerance)\n ) {\n return true;\n }\n\n v1 = normalize(v1);\n v2 = normalize(v2);\n\n return equal(v1, v2, tolerance);\n}\n\nexport function collinear(v1: Vector, v2: Vector, tolerance?: number): boolean {\n return (\n codirectional(v1, v2, tolerance) ||\n codirectional(v1, negate(v2), tolerance)\n );\n}\n\n// Convert a cartesian coordinate into a radian polar coordinate\nexport function polarRadFromCart(\n v: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> {\n const radius = length(v);\n let theta = Math.atan2(v[1], v[0]);\n\n // Convert angle range from [-pi, pi] to [0, 2pi]\n if (theta < 0) {\n theta += 2 * Math.PI;\n }\n\n return [radius, theta];\n}\n\n// Converts a cartesian coordinate into a degree polar coordinate\nexport function polarDegFromCart(\n v: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> /* TODO: convert to tuple/Point */ {\n const polar = polarRadFromCart(v);\n return [polar[0], (polar[1] * 180) / Math.PI];\n}\n\n/* Convert a polar coordinate into a cartesian coordinate\n *\n * Examples:\n * cartFromPolarRad(5, Math.PI)\n * cartFromPolarRad([5, Math.PI])\n */\nexport function cartFromPolarRad(\n radius: number,\n theta?: number = 0,\n): $ReadOnlyArray<number> /* TODO: convert to tuple/Point */ {\n return [radius * Math.cos(theta), radius * Math.sin(theta)];\n}\n\n/* Convert a polar coordinate into a cartesian coordinate\n *\n * Examples:\n * cartFromPolarDeg(5, 30)\n * cartFromPolarDeg([5, 30])\n */\nexport function cartFromPolarDeg(\n radius: number,\n theta?: number = 0,\n): $ReadOnlyArray<number> {\n return cartFromPolarRad(radius, (theta * Math.PI) / 180);\n}\n\n// Rotate vector\nexport function rotateRad(\n v: $ReadOnlyArray<number>,\n theta: number,\n): $ReadOnlyArray<number> {\n const polar = polarRadFromCart(v);\n const angle = polar[1] + theta;\n return cartFromPolarRad(polar[0], angle);\n}\n\nexport function rotateDeg(\n v: $ReadOnlyArray<number>,\n theta: number,\n): $ReadOnlyArray<number> {\n const polar = polarDegFromCart(v);\n const angle = polar[1] + theta;\n return cartFromPolarDeg(polar[0], angle);\n}\n\n// Angle between two vectors\nexport function angleRad(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): number {\n return Math.acos(dot(v1, v2) / (length(v1) * length(v2)));\n}\n\nexport function angleDeg(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): number {\n return (angleRad(v1, v2) * 180) / Math.PI;\n}\n\n// Vector projection of v1 onto v2\nexport function projection(\n v1: $ReadOnlyArray<number>,\n v2: $ReadOnlyArray<number>,\n): $ReadOnlyArray<number> {\n const scalar = dot(v1, v2) / dot(v2, v2);\n return scale(v2, scalar);\n}\n\n// Round each number to a certain number of decimal places\nexport function round<V: Vector>(vec: V, precision: V | number): V {\n // $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.round(elem, precision[i] || precision),\n );\n}\n\n// Round each number to the nearest increment\nexport function roundTo<V: Vector>(vec: V, increment: V | number): V {\n // $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.roundTo(elem, increment[i] || increment),\n );\n}\n\nexport function floorTo<V: Vector>(vec: V, increment: V | number): V {\n // $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.floorTo(elem, increment[i] || increment),\n );\n}\n\nexport function ceilTo<V: Vector>(vec: V, increment: V | number): V {\n // $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking\n return vec.map((elem, i) =>\n // $FlowFixMe[prop-missing]\n // $FlowFixMe[incompatible-call]\n knumber.ceilTo(elem, increment[i] || increment),\n );\n}\n","// @flow\n/**\n * Point Utils\n * A point is an array of two numbers e.g. [0, 0].\n */\n\nimport _ from \"underscore\";\n\nimport * as kvector from \"./vector.js\";\nimport * as knumber from \"./number.js\";\n\n// A point, in 2D, 3D, or nD space.\nexport type Point = $ReadOnlyArray<number>;\n\n// Rotate point (around origin unless a center is specified)\nexport function rotateRad(point: Point, theta: number, center: Point): Point {\n if (center === undefined) {\n return kvector.rotateRad(point, theta);\n } else {\n return kvector.add(\n center,\n kvector.rotateRad(kvector.subtract(point, center), theta),\n );\n }\n}\n\nexport function rotateDeg(point: Point, theta: number, center: Point): Point {\n if (center === undefined) {\n return kvector.rotateDeg(point, theta);\n } else {\n return kvector.add(\n center,\n kvector.rotateDeg(kvector.subtract(point, center), theta),\n );\n }\n}\n\n// Distance between two points\nexport function distanceToPoint(point1: Point, point2: Point): number {\n return kvector.length(kvector.subtract(point1, point2));\n}\n\n// Distance between point and line\nexport function distanceToLine(point: Point, line: [Point, Point]): number {\n const lv = kvector.subtract(line[1], line[0]);\n const pv = kvector.subtract(point, line[0]);\n const projectedPv = kvector.projection(pv, lv);\n const distancePv = kvector.subtract(projectedPv, pv);\n return kvector.length(distancePv);\n}\n\n// Reflect point over line\nexport function reflectOverLine(point: Point, line: [Point, Point]): Point {\n const lv = kvector.subtract(line[1], line[0]);\n const pv = kvector.subtract(point, line[0]);\n const projectedPv = kvector.projection(pv, lv);\n const reflectedPv = kvector.subtract(kvector.scale(projectedPv, 2), pv);\n return kvector.add(line[0], reflectedPv);\n}\n\n/**\n * Compares two points, returning -1, 0, or 1, for use with\n * Array.prototype.sort\n *\n * Note: This technically doesn't satisfy the total-ordering\n * requirements of Array.prototype.sort unless equalityTolerance\n * is 0. In some cases very close points that compare within a\n * few equalityTolerances could appear in the wrong order.\n */\nexport function compare(\n point1: Point,\n point2: Point,\n equalityTolerance?: number,\n): number /* TODO: convert to -1 | 0 | 1 type */ {\n if (point1.length !== point2.length) {\n return point1.length - point2.length;\n }\n for (let i = 0; i < point1.length; i++) {\n if (!knumber.equal(point1[i], point2[i], equalityTolerance)) {\n return point1[i] - point2[i];\n }\n }\n return 0;\n}\n\n// Check if a value is a point\nexport const is = kvector.is;\n\n// Add and subtract vector(s)\nexport const addVector = kvector.add;\nexport const addVectors = kvector.add;\nexport const subtractVector = kvector.subtract;\nexport const equal = kvector.equal;\n\n// Convert from cartesian to polar and back\nexport const polarRadFromCart = kvector.polarRadFromCart;\nexport const polarDegFromCart = kvector.polarDegFromCart;\nexport const cartFromPolarRad = kvector.cartFromPolarRad;\nexport const cartFromPolarDeg = kvector.cartFromPolarDeg;\n\n// Rounding\nexport const round = kvector.round;\nexport const roundTo = kvector.roundTo;\nexport const floorTo = kvector.floorTo;\nexport const ceilTo = kvector.ceilTo;\n","// @flow\n/**\n * Line Utils\n * A line is an array of two points e.g. [[-5, 0], [5, 0]].\n */\n\nimport * as kpoint from \"./point.js\";\nimport * as kvector from \"./vector.js\";\n\nimport type {Point} from \"./point.js\";\n\nexport type Line = [Point, Point];\n\nexport function distanceToPoint(line: Line, point: Point): number {\n return kpoint.distanceToLine(point, line);\n}\n\nexport function reflectPoint(line: Line, point: Point): Point {\n return kpoint.reflectOverLine(point, line);\n}\n\nexport function midpoint(line: Line): Point {\n return [(line[0][0] + line[1][0]) / 2, (line[0][1] + line[1][1]) / 2];\n}\n\nexport function equal(line1: Line, line2: Line, tolerance?: number): boolean {\n // TODO: A nicer implementation might just check collinearity of\n // vectors using underscore magick\n // Compare the directions of the lines\n const v1 = kvector.subtract(line1[1], line1[0]);\n const v2 = kvector.subtract(line2[1], line2[0]);\n if (!kvector.collinear(v1, v2, tolerance)) {\n return false;\n }\n // If the start point is the same for the two lines, then they are the same\n if (kpoint.equal(line1[0], line2[0])) {\n return true;\n }\n // Make sure that the direction to get from line1 to\n // line2 is the same as the direction of the lines\n const line1ToLine2Vector = kvector.subtract(line2[0], line1[0]);\n return kvector.collinear(v1, line1ToLine2Vector, tolerance);\n}\n","// @flow\n/**\n * Ray Utils\n * A ray (→) is an array of an endpoint and another point along the ray.\n * For example, [[0, 0], [1, 0]] is the ray starting at the origin and\n * traveling along the positive x-axis.\n */\n\nimport * as kvector from \"./vector.js\";\nimport * as kpoint from \"./point.js\";\n\nimport type {Point} from \"./point\";\n\nexport type Ray = [Point, Point];\n\nexport function equal(ray1: Ray, ray2: Ray, tolerance: number): boolean {\n // Compare the directions of the rays\n const v1 = kvector.subtract(ray1[1], ray1[0]);\n const v2 = kvector.subtract(ray2[1], ray2[0]);\n\n const sameOrigin = kpoint.equal(ray1[0], ray2[0]);\n const codirectional = kvector.codirectional(v1, v2, tolerance);\n\n return sameOrigin && codirectional;\n}\n"],"names":["EPSILON","Math","pow","is","x","_","isNumber","isNaN","equal","y","tolerance","abs","round","num","precision","factor","roundTo","increment","floorTo","floor","ceilTo","ceil","decimal","maxDenominator","n","d","a","rem","arraySum","array","reduce","memo","arg","arrayProduct","vec","dimension","isArray","undefined","length","every","knumber","normalize","v","scale","sqrt","dot","b","zip","map","add","zipped","subtract","v1","v2","dim","negate","scalar","pair","codirectional","collinear","polarRadFromCart","radius","theta","atan2","PI","polarDegFromCart","polar","cartFromPolarRad","cos","sin","cartFromPolarDeg","rotateRad","angle","rotateDeg","angleRad","acos","projection","elem","i","distanceToLine","point","line","lv","kvector","pv","reflectOverLine","reflectedPv","addVector","addVectors","subtractVector","center","point1","point2","equalityTolerance","kpoint","line1","line2","ray1","ray2","sameOrigin"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/line.js
CHANGED
|
@@ -15,10 +15,7 @@ export function distanceToPoint(line: Line, point: Point): number {
|
|
|
15
15
|
return kpoint.distanceToLine(point, line);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export function reflectPoint(
|
|
19
|
-
line: Line,
|
|
20
|
-
point: Point,
|
|
21
|
-
): $ReadOnlyArray<number> /* TODO: convert to Point */ {
|
|
18
|
+
export function reflectPoint(line: Line, point: Point): Point {
|
|
22
19
|
return kpoint.reflectOverLine(point, line);
|
|
23
20
|
}
|
|
24
21
|
|
package/src/point.js
CHANGED
|
@@ -50,10 +50,7 @@ export function distanceToLine(point: Point, line: [Point, Point]): number {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// Reflect point over line
|
|
53
|
-
export function reflectOverLine(
|
|
54
|
-
point: Point,
|
|
55
|
-
line: [Point, Point],
|
|
56
|
-
): $ReadOnlyArray<number> /* TODO: convert to Point */ {
|
|
53
|
+
export function reflectOverLine(point: Point, line: [Point, Point]): Point {
|
|
57
54
|
const lv = kvector.subtract(line[1], line[0]);
|
|
58
55
|
const pv = kvector.subtract(point, line[0]);
|
|
59
56
|
const projectedPv = kvector.projection(pv, lv);
|
package/src/vector.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
import _ from "underscore";
|
|
8
8
|
import * as knumber from "./number.js";
|
|
9
9
|
|
|
10
|
+
type Vector = $ReadOnlyArray<number>;
|
|
11
|
+
|
|
10
12
|
function arraySum(array: $ReadOnlyArray<number>): number {
|
|
11
13
|
return array.reduce((memo, arg) => memo + arg, 0);
|
|
12
14
|
}
|
|
@@ -15,7 +17,15 @@ function arrayProduct(array: $ReadOnlyArray<number>): number {
|
|
|
15
17
|
return array.reduce((memo, arg) => memo * arg, 1);
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Checks if the given vector contains only numbers and, optionally, is of the
|
|
22
|
+
* right dimension (length).
|
|
23
|
+
*
|
|
24
|
+
* is([1, 2, 3]) -> true
|
|
25
|
+
* is([1, "Hello", 3]) -> false
|
|
26
|
+
* is([1, 2, 3], 1) -> false
|
|
27
|
+
*/
|
|
28
|
+
export function is<T>(vec: $ReadOnlyArray<T>, dimension?: number): boolean {
|
|
19
29
|
if (!_.isArray(vec)) {
|
|
20
30
|
return false;
|
|
21
31
|
}
|
|
@@ -26,19 +36,16 @@ export function is<T>(vec: $ReadOnlyArray<T>, dimension: number): boolean {
|
|
|
26
36
|
}
|
|
27
37
|
|
|
28
38
|
// Normalize to a unit vector
|
|
29
|
-
export function normalize(v:
|
|
39
|
+
export function normalize<V: Vector>(v: V): V {
|
|
30
40
|
return scale(v, 1 / length(v));
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
// Length/magnitude of a vector
|
|
34
|
-
export function length(v:
|
|
44
|
+
export function length(v: Vector): number {
|
|
35
45
|
return Math.sqrt(dot(v, v));
|
|
36
46
|
}
|
|
37
47
|
// Dot product of two vectors
|
|
38
|
-
export function dot(
|
|
39
|
-
a: $ReadOnlyArray<number>,
|
|
40
|
-
b: $ReadOnlyArray<number>,
|
|
41
|
-
): number {
|
|
48
|
+
export function dot(a: Vector, b: Vector): number {
|
|
42
49
|
// $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray
|
|
43
50
|
const zipped = _.zip(a, b);
|
|
44
51
|
const multiplied = zipped.map(arrayProduct);
|
|
@@ -49,43 +56,33 @@ export function dot(
|
|
|
49
56
|
*
|
|
50
57
|
* add([1, 2], [3, 4]) -> [4, 6]
|
|
51
58
|
*/
|
|
52
|
-
export function add(
|
|
53
|
-
...vecs: $ReadOnlyArray<$ReadOnlyArray<number>>
|
|
54
|
-
): $ReadOnlyArray<number> {
|
|
59
|
+
export function add<V: Vector>(...vecs: $ReadOnlyArray<V>): V {
|
|
55
60
|
// $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray
|
|
56
61
|
const zipped = _.zip(...vecs);
|
|
57
62
|
return zipped.map(arraySum);
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
export function subtract(
|
|
61
|
-
v1: $ReadOnlyArray<number>,
|
|
62
|
-
v2: $ReadOnlyArray<number>,
|
|
63
|
-
): $ReadOnlyArray<number> {
|
|
65
|
+
export function subtract<V: Vector>(v1: V, v2: V): V {
|
|
64
66
|
// $FlowFixMe[incompatible-call] underscore doesn't like $ReadOnlyArray
|
|
65
67
|
return _.zip(v1, v2).map((dim) => dim[0] - dim[1]);
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
export function negate(v:
|
|
70
|
+
export function negate<V: Vector>(v: V): V {
|
|
71
|
+
// $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking
|
|
69
72
|
return v.map((x) => {
|
|
70
73
|
return -x;
|
|
71
74
|
});
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
// Scale a vector
|
|
75
|
-
export function scale(
|
|
76
|
-
|
|
77
|
-
scalar: number,
|
|
78
|
-
): $ReadOnlyArray<number> {
|
|
78
|
+
export function scale<V: Vector>(v1: V, scalar: number): V {
|
|
79
|
+
// $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking
|
|
79
80
|
return v1.map((x) => {
|
|
80
81
|
return x * scalar;
|
|
81
82
|
});
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
export function equal(
|
|
85
|
-
v1: $ReadOnlyArray<number>,
|
|
86
|
-
v2: $ReadOnlyArray<number>,
|
|
87
|
-
tolerance?: number,
|
|
88
|
-
): boolean {
|
|
85
|
+
export function equal(v1: Vector, v2: Vector, tolerance?: number): boolean {
|
|
89
86
|
// _.zip will nicely deal with the lengths, going through
|
|
90
87
|
// the length of the longest vector. knumber.equal then
|
|
91
88
|
// returns false for any number compared to the undefined
|
|
@@ -97,8 +94,8 @@ export function equal(
|
|
|
97
94
|
}
|
|
98
95
|
|
|
99
96
|
export function codirectional(
|
|
100
|
-
v1:
|
|
101
|
-
v2:
|
|
97
|
+
v1: Vector,
|
|
98
|
+
v2: Vector,
|
|
102
99
|
tolerance?: number,
|
|
103
100
|
): boolean {
|
|
104
101
|
// The origin is trivially codirectional with all other vectors.
|
|
@@ -117,11 +114,7 @@ export function codirectional(
|
|
|
117
114
|
return equal(v1, v2, tolerance);
|
|
118
115
|
}
|
|
119
116
|
|
|
120
|
-
export function collinear(
|
|
121
|
-
v1: $ReadOnlyArray<number>,
|
|
122
|
-
v2: $ReadOnlyArray<number>,
|
|
123
|
-
tolerance?: number,
|
|
124
|
-
): boolean {
|
|
117
|
+
export function collinear(v1: Vector, v2: Vector, tolerance?: number): boolean {
|
|
125
118
|
return (
|
|
126
119
|
codirectional(v1, v2, tolerance) ||
|
|
127
120
|
codirectional(v1, negate(v2), tolerance)
|
|
@@ -221,10 +214,8 @@ export function projection(
|
|
|
221
214
|
}
|
|
222
215
|
|
|
223
216
|
// Round each number to a certain number of decimal places
|
|
224
|
-
export function round(
|
|
225
|
-
|
|
226
|
-
precision: $ReadOnlyArray<number> | number,
|
|
227
|
-
): $ReadOnlyArray<number> {
|
|
217
|
+
export function round<V: Vector>(vec: V, precision: V | number): V {
|
|
218
|
+
// $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking
|
|
228
219
|
return vec.map((elem, i) =>
|
|
229
220
|
// $FlowFixMe[prop-missing]
|
|
230
221
|
// $FlowFixMe[incompatible-call]
|
|
@@ -233,10 +224,8 @@ export function round(
|
|
|
233
224
|
}
|
|
234
225
|
|
|
235
226
|
// Round each number to the nearest increment
|
|
236
|
-
export function roundTo(
|
|
237
|
-
|
|
238
|
-
increment: $ReadOnlyArray<number> | number,
|
|
239
|
-
): $ReadOnlyArray<number> {
|
|
227
|
+
export function roundTo<V: Vector>(vec: V, increment: V | number): V {
|
|
228
|
+
// $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking
|
|
240
229
|
return vec.map((elem, i) =>
|
|
241
230
|
// $FlowFixMe[prop-missing]
|
|
242
231
|
// $FlowFixMe[incompatible-call]
|
|
@@ -244,10 +233,8 @@ export function roundTo(
|
|
|
244
233
|
);
|
|
245
234
|
}
|
|
246
235
|
|
|
247
|
-
export function floorTo(
|
|
248
|
-
|
|
249
|
-
increment: $ReadOnlyArray<number> | number,
|
|
250
|
-
): $ReadOnlyArray<number> {
|
|
236
|
+
export function floorTo<V: Vector>(vec: V, increment: V | number): V {
|
|
237
|
+
// $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking
|
|
251
238
|
return vec.map((elem, i) =>
|
|
252
239
|
// $FlowFixMe[prop-missing]
|
|
253
240
|
// $FlowFixMe[incompatible-call]
|
|
@@ -255,10 +242,8 @@ export function floorTo(
|
|
|
255
242
|
);
|
|
256
243
|
}
|
|
257
244
|
|
|
258
|
-
export function ceilTo(
|
|
259
|
-
|
|
260
|
-
increment: $ReadOnlyArray<number> | number,
|
|
261
|
-
): $ReadOnlyArray<number> {
|
|
245
|
+
export function ceilTo<V: Vector>(vec: V, increment: V | number): V {
|
|
246
|
+
// $FlowFixMe[incompatible-return] Flow's `.map()` libdef is lacking
|
|
262
247
|
return vec.map((elem, i) =>
|
|
263
248
|
// $FlowFixMe[prop-missing]
|
|
264
249
|
// $FlowFixMe[incompatible-call]
|