@daeinc/geom
Advanced tools
Comparing version 0.9.0 to 0.10.0
@@ -1,4 +0,6 @@ | ||
export declare type Pt = number[]; | ||
export declare type Pts = number[][]; | ||
export declare type GenericObject = Record<string, any>; | ||
import * as _thi_ng_vectors from '@thi.ng/vectors'; | ||
type Pt = number[]; | ||
type Pts = number[][]; | ||
type GenericObject = Record<string, any>; | ||
/** | ||
@@ -17,3 +19,3 @@ * generates an array of paths (excl. original 2 paths) | ||
*/ | ||
export declare const blendPath: (path1: Pts, path2: Pts, numBlends: number, guidePath?: Pts) => number[][][]; | ||
declare const blendPath: (path1: Pts, path2: Pts, numBlends: number) => Pts[]; | ||
/** | ||
@@ -25,3 +27,3 @@ * the resulting function is transformed to draw from center [0, 0] | ||
*/ | ||
export declare const createShapeFunc: (pts: Pts, anchor?: Pt) => (x: number, y: number, w: number, h: number) => Pts; | ||
declare const createShapeFunc: (pts: Pts, anchor?: Pt) => (x: number, y: number, w: number, h: number) => Pts; | ||
/** | ||
@@ -33,3 +35,3 @@ * calculate distance between two point[]s | ||
*/ | ||
export declare const dist: (pt1: Pt, pt2: Pt) => number; | ||
declare const dist: (pt1: Pt, pt2: Pt) => number; | ||
/** | ||
@@ -41,3 +43,3 @@ * squared distance (x^2 + y^2) between two point[]s | ||
*/ | ||
export declare const distSq: (pt1: Pt, pt2: Pt) => number; | ||
declare const distSq: (pt1: Pt, pt2: Pt) => number; | ||
/** | ||
@@ -56,3 +58,3 @@ * extrude path in 2d space | ||
*/ | ||
export declare const extrudePath: (path: Pts, numPointsToExtrude: number, offset: Pt, mode?: "start" | "end" | "both", shapeFunc?: () => Pts) => number[][]; | ||
declare const extrudePath: (path: Pts, numPointsToExtrude: number, offset: Pt, mode?: "start" | "end" | "both") => number[][]; | ||
/** | ||
@@ -67,3 +69,3 @@ * generate extra points for smooth hard corners of path | ||
*/ | ||
export declare const generateSmoothPath: (pts: Pts, smoothFactor: number) => import("@thi.ng/vectors").Vec[]; | ||
declare const generateSmoothPath: (pts: Pts, smoothFactor: number) => _thi_ng_vectors.Vec[]; | ||
/** | ||
@@ -78,3 +80,3 @@ * atan2() gives angle between [-PI, PI] | ||
*/ | ||
export declare const getAngleBetween: (pt1: Pt, pt2: Pt) => number; | ||
declare const getAngleBetween: (pt1: Pt, pt2: Pt) => number; | ||
/** | ||
@@ -89,3 +91,3 @@ * take an array of points and return total length of path | ||
*/ | ||
export declare const getPathLength: (path: Pts) => number; | ||
declare const getPathLength: (path: Pts) => number; | ||
/** | ||
@@ -97,3 +99,3 @@ * converts angle [-pi, pi] to [0, 2pi) | ||
*/ | ||
export declare const getPositiveAngleBetween: (pt1: Pt, pt2: Pt) => number; | ||
declare const getPositiveAngleBetween: (pt1: Pt, pt2: Pt) => number; | ||
/** | ||
@@ -104,15 +106,20 @@ * calculate each segment length(distance) | ||
*/ | ||
export declare const getSegmentLengths: (pts: Pts) => number[]; | ||
export declare const interpolateArray: (arrStart: number[], arrTarget: number[], t: number) => number[]; | ||
declare const getSegmentLengths: (pts: Pts) => number[]; | ||
declare const interpolateArray: (arrStart: number[], arrTarget: number[], t: number, out?: number[] | undefined) => number[]; | ||
/** | ||
* mix/lerp 2d number array. usually used for path data of [x, y] | ||
* | ||
* @param pathStart array of [x, y] to start | ||
* @param pathTarget array of [x, y] to target | ||
* @param t 0..1 | ||
* @param out array to mutate | ||
* @returns 2d array | ||
*/ | ||
export declare const interpolatePath: (pathStart: Pts, pathTarget: Pts, t: number) => number[][]; | ||
declare const interpolatePath: (pathStart: Pts, pathTarget: Pts, t: number, out?: Pts) => Pts; | ||
/** | ||
* interpolate object with {string:number}. ie. {x:10}. | ||
* both objects must have same keys. | ||
* | ||
* TODO: can i mutate object with out parameter? | ||
* | ||
* @param objStart object to start from | ||
@@ -123,3 +130,3 @@ * @param objTarget object to interpolate to | ||
*/ | ||
export declare const interpolateObject: (objStart: GenericObject, objTarget: GenericObject, t: number) => GenericObject; | ||
declare const interpolateObject: (objStart: GenericObject, objTarget: GenericObject, t: number) => GenericObject; | ||
/** | ||
@@ -132,8 +139,10 @@ * interpolate number, number[], number[][] or generic object | ||
* - every if condition is redundant to check start AND target | ||
* | ||
* @param start | ||
* @param target | ||
* @param t | ||
* @param out array to mutate (with 1d or 2d array) | ||
* @returns | ||
*/ | ||
export declare const interpolate: <T>(start: T, target: T, t: number) => number | GenericObject | T; | ||
declare const interpolate: (start: number | Pt | Pts | GenericObject, target: number | Pt | Pts | GenericObject, t: number, out?: number | Pt | Pts | GenericObject) => number | GenericObject; | ||
/** | ||
@@ -145,3 +154,3 @@ * project a point on a line using vector. | ||
*/ | ||
export declare const projectPointOnLine: (pt: Pt, line: Pts) => Pt; | ||
declare const projectPointOnLine: (pt: Pt, line: Pts) => Pt; | ||
/** | ||
@@ -153,3 +162,3 @@ * reflect a point on another point or a line | ||
*/ | ||
export declare const reflectPoint: (pt: Pt, axis: Pt | Pts) => Pt; | ||
declare const reflectPoint: (pt: Pt, axis: Pt | Pts) => Pt; | ||
/** | ||
@@ -161,3 +170,3 @@ * reflect a path either on a point or a line | ||
*/ | ||
export declare const reflectPath: (pts: Pts, axis: Pt | Pts) => Pts; | ||
declare const reflectPath: (pts: Pts, axis: Pt | Pts) => Pts; | ||
/** | ||
@@ -171,3 +180,3 @@ * TODO: haven't tested "anchor" yet | ||
*/ | ||
export declare const rotatePoint: (pt: Pt, angle: number, anchor?: number[], precision?: number) => number[]; | ||
declare const rotatePoint: (pt: Pt, angle: number, anchor?: number[], precision?: number) => number[]; | ||
/** | ||
@@ -179,3 +188,3 @@ * scale a single point | ||
*/ | ||
export declare const scalePoint: (pt: Pt, size: Pt) => Pt; | ||
declare const scalePoint: (pt: Pt, size: Pt) => Pt; | ||
/** | ||
@@ -187,27 +196,4 @@ * take normalized path data and return [ x, y ] scaled to width and height | ||
*/ | ||
export declare const scalePath: (path: Pts, size: Pt) => Pts; | ||
/** | ||
* by default, path t value is based on number of points. this function calculates t based on each segment length. | ||
* | ||
* TODO: | ||
* - implement | ||
* @param path | ||
* @returns {number[]} t values for each pt index | ||
*/ | ||
export declare const calcTByLength: (path: Pts) => number[]; | ||
/** | ||
* combine 2 paths by a single connecting point. | ||
* if connecting points are the same, then add only one. (no duplicates) | ||
* | ||
* TODO: | ||
* - implementation | ||
* - what modes to use?: "start-first", "start-last", "end-first", "end-last"? | ||
* - snap one path to another by automatically moving it? | ||
* - meet at halfway if two end points are not close enough (threshold) | ||
* @param path1 array of [x, y] | ||
* @param path2 array of [x, y] | ||
* @param mode from which point to which point to connect? | ||
* @returns path a single combined path | ||
*/ | ||
export declare const combinePath: (path1: Pts, path2: Pts, mode: string) => Pts; | ||
//# sourceMappingURL=index.d.ts.map | ||
declare const scalePath: (path: Pts, size: Pt) => Pts; | ||
export { GenericObject, Pt, Pts, blendPath, createShapeFunc, dist, distSq, extrudePath, generateSmoothPath, getAngleBetween, getPathLength, getPositiveAngleBetween, getSegmentLengths, interpolate, interpolateArray, interpolateObject, interpolatePath, projectPointOnLine, reflectPath, reflectPoint, rotatePoint, scalePath, scalePoint }; |
@@ -1,369 +0,182 @@ | ||
import { mix, reflect, roundF, TWO_PI } from "@daeinc/math"; | ||
import { interpolateArray as importedInterpolateArray } from "@daeinc/array"; | ||
import { add2 as add, dot2 as dot, mag, mul2 as mul, sub2 as sub, vec2, normalize2 as normalize, } from "@thi.ng/vectors"; | ||
/** | ||
* generates an array of paths (excl. original 2 paths) | ||
* | ||
* TODO: | ||
* - guidePath: input another path to use as shaping path. | ||
* this path should already be resampled so that path points can be used as numBlends, | ||
* or use numBlends param to resample within this function. | ||
* @param path1 array of [x, y] to blend from | ||
* @param path2 array of [x, y] to blend to | ||
* @param numBlends how many blended paths to generate (excl. two original paths) | ||
* @param guidePath optional. custom path that blended paths will follow along. | ||
* @returns 3d array of paths [number of blends][each blended path][x, y] | ||
*/ | ||
export const blendPath = (path1, path2, numBlends, guidePath) => { | ||
return Array(numBlends) | ||
.fill([]) | ||
.map((_, i) => { | ||
const t = (i + 1) / (numBlends + 1); | ||
return interpolatePath(path1, path2, t); | ||
}); | ||
import { TWO_PI, mix, reflect, roundF } from '@daeinc/math'; | ||
import { interpolateArray as interpolateArray$1 } from '@daeinc/array'; | ||
import { sub2, mul2, add2, vec2, dot2, mag, normalize2 } from '@thi.ng/vectors'; | ||
// index.ts | ||
var blendPath = (path1, path2, numBlends) => { | ||
return Array(numBlends).fill([]).map((_, i) => { | ||
const t = (i + 1) / (numBlends + 1); | ||
return interpolatePath(path1, path2, t); | ||
}); | ||
}; | ||
/** | ||
* the resulting function is transformed to draw from center [0, 0] | ||
* @param pts must be a normalized array (0..1) of [x, y]s | ||
* @param anchor normalized center point [x, y] | ||
* @returns function to draw shape with given params (x,y,w,h) | ||
*/ | ||
export const createShapeFunc = (pts, anchor = [0.5, 0.5]) => { | ||
return (x, y, w, h) => pts.map((pt) => { | ||
const xdiff = pt[0] - anchor[0]; | ||
const ydiff = pt[1] - anchor[1]; | ||
return [x + xdiff * w, y + ydiff * h]; | ||
}); | ||
var createShapeFunc = (pts, anchor = [0.5, 0.5]) => { | ||
return (x, y, w, h) => pts.map((pt) => { | ||
const xdiff = pt[0] - anchor[0]; | ||
const ydiff = pt[1] - anchor[1]; | ||
return [x + xdiff * w, y + ydiff * h]; | ||
}); | ||
}; | ||
/** | ||
* calculate distance between two point[]s | ||
* @param pt1 | ||
* @param pt2 | ||
* @returns | ||
*/ | ||
export const dist = (pt1, pt2) => { | ||
return Math.sqrt(Math.pow(pt2[0] - pt1[0], 2) + Math.pow(pt2[1] - pt1[1], 2)); | ||
var dist = (pt1, pt2) => { | ||
return Math.sqrt(Math.pow(pt2[0] - pt1[0], 2) + Math.pow(pt2[1] - pt1[1], 2)); | ||
}; | ||
/** | ||
* squared distance (x^2 + y^2) between two point[]s | ||
* @param pt1 | ||
* @param pt2 | ||
* @returns | ||
*/ | ||
export const distSq = (pt1, pt2) => { | ||
return Math.pow(pt2[0] - pt1[0], 2) + Math.pow(pt2[1] - pt1[1], 2); | ||
var distSq = (pt1, pt2) => { | ||
return Math.pow(pt2[0] - pt1[0], 2) + Math.pow(pt2[1] - pt1[1], 2); | ||
}; | ||
/** | ||
* extrude path in 2d space | ||
* | ||
* TODO: | ||
* - instead of preventing numPoints<path length, continue to extrude. use modulo. | ||
* - add custom shapeFunc | ||
* @param path array of [ x, y ] | ||
* @param numPointsToExtrude how many points to use for extruding (mirroring). useful when extruding same path again. | ||
* @param offset [ x, y ] how much +/- in each dimension | ||
* @param mode start (reverse direction) | end | both (closed path) | ||
* @param shapeFunc optional. function on how to extrude if other than straight line | ||
* @returns path | ||
*/ | ||
export const extrudePath = (path, numPointsToExtrude, offset, mode = "end", shapeFunc) => { | ||
if (numPointsToExtrude > path.length) { | ||
throw new Error("extrudePath(): numPointsToExtrude can't exceed length of path"); | ||
var extrudePath = (path, numPointsToExtrude, offset, mode = "end") => { | ||
if (numPointsToExtrude > path.length) { | ||
throw new Error( | ||
"extrudePath(): numPointsToExtrude can't exceed length of path" | ||
); | ||
} | ||
const clone = [...path]; | ||
if (mode === "end") { | ||
for (let i = path.length - 1; i >= path.length - numPointsToExtrude; i--) { | ||
const pt = path[i]; | ||
clone.push([pt[0] + offset[0], pt[1] + offset[1]]); | ||
} | ||
const clone = [...path]; | ||
if (mode === "end") { | ||
for (let i = path.length - 1; i >= path.length - numPointsToExtrude; i--) { | ||
const pt = path[i]; | ||
clone.push([pt[0] + offset[0], pt[1] + offset[1]]); | ||
} | ||
} else if (mode === "start") { | ||
clone.reverse(); | ||
for (let i = 0; i < numPointsToExtrude; i++) { | ||
const pt = path[i]; | ||
clone.push([pt[0] + offset[0], pt[1] + offset[1]]); | ||
} | ||
else if (mode === "start") { | ||
// push and reverse (faster than unshift) | ||
clone.reverse(); | ||
for (let i = 0; i < numPointsToExtrude; i++) { | ||
const pt = path[i]; | ||
clone.push([pt[0] + offset[0], pt[1] + offset[1]]); | ||
} | ||
// clone.reverse(); | ||
} else if (mode === "both") { | ||
for (let i = path.length - 1; i >= path.length - numPointsToExtrude; i--) { | ||
const pt = path[i]; | ||
clone.push([pt[0] + offset[0], pt[1] + offset[1]]); | ||
} | ||
else if (mode === "both") { | ||
for (let i = path.length - 1; i >= path.length - numPointsToExtrude; i--) { | ||
const pt = path[i]; | ||
clone.push([pt[0] + offset[0], pt[1] + offset[1]]); | ||
} | ||
clone.push(path[0]); | ||
} | ||
return clone; | ||
clone.push(path[0]); | ||
} | ||
return clone; | ||
}; | ||
/** | ||
* generate extra points for smooth hard corners of path | ||
* | ||
* TODO: test | ||
* | ||
* @param pts point array | ||
* @param smoothFactor how smooth | ||
* @returns point array | ||
*/ | ||
export const generateSmoothPath = (pts, smoothFactor) => { | ||
const smoothPoints = []; | ||
smoothPoints.push(pts[0]); | ||
for (let i = 0; i < pts.length - 1; i++) { | ||
const a = pts[i]; | ||
const b = pts[i + 1]; | ||
const diff = sub([], b, a); | ||
const diffScaled1 = mul([], diff, [smoothFactor, smoothFactor]); | ||
const mid1 = add([], a, diffScaled1); | ||
const diffScaled2 = mul([], diff, [1 - smoothFactor, 1 - smoothFactor]); | ||
const mid2 = add([], a, diffScaled2); | ||
smoothPoints.push(mid1, mid2, b); | ||
} | ||
return smoothPoints; | ||
var generateSmoothPath = (pts, smoothFactor) => { | ||
const smoothPoints = []; | ||
smoothPoints.push(pts[0]); | ||
for (let i = 0; i < pts.length - 1; i++) { | ||
const a = pts[i]; | ||
const b = pts[i + 1]; | ||
const diff = sub2([], b, a); | ||
const diffScaled1 = mul2([], diff, [smoothFactor, smoothFactor]); | ||
const mid1 = add2([], a, diffScaled1); | ||
const diffScaled2 = mul2([], diff, [1 - smoothFactor, 1 - smoothFactor]); | ||
const mid2 = add2([], a, diffScaled2); | ||
smoothPoints.push(mid1, mid2, b); | ||
} | ||
return smoothPoints; | ||
}; | ||
/** | ||
* atan2() gives angle between [-PI, PI] | ||
* | ||
* REVIEW: order or points matter, so what's the best way? | ||
* | ||
* @param pt1 | ||
* @param pt2 | ||
* @returns angle between [-PI, PI] | ||
*/ | ||
export const getAngleBetween = (pt1, pt2) => { | ||
return Math.atan2(pt2[1] - pt1[1], pt2[0] - pt1[0]); | ||
var getAngleBetween = (pt1, pt2) => { | ||
return Math.atan2(pt2[1] - pt1[1], pt2[0] - pt1[0]); | ||
}; | ||
/** | ||
* take an array of points and return total length of path | ||
* | ||
* REVIEW: | ||
* - which is better, this or using getSegmentLengths()? | ||
* | ||
* @param path array of [ x, y ] points | ||
* @returns total length of path | ||
*/ | ||
export const getPathLength = (path) => { | ||
return path.reduce((totalLen, pt, i, arr) => { | ||
if (arr.length < 2) | ||
return 0; // handle single point length | ||
if (i === arr.length - 1) | ||
return totalLen; // skip last one (no i+1 there) | ||
return (totalLen + | ||
Math.sqrt(Math.pow(arr[i + 1][0] - arr[i][0], 2) + | ||
Math.pow(arr[i + 1][1] - arr[i][1], 2))); | ||
}, 0); | ||
var getPathLength = (path) => { | ||
return path.reduce((totalLen, _pt, i, arr) => { | ||
if (arr.length < 2) | ||
return 0; | ||
if (i === arr.length - 1) | ||
return totalLen; | ||
return totalLen + Math.sqrt( | ||
Math.pow(arr[i + 1][0] - arr[i][0], 2) + Math.pow(arr[i + 1][1] - arr[i][1], 2) | ||
); | ||
}, 0); | ||
}; | ||
/** | ||
* converts angle [-pi, pi] to [0, 2pi) | ||
* @param pt1 | ||
* @param pt2 | ||
* @returns angle between [0, TWO_PI] | ||
*/ | ||
export const getPositiveAngleBetween = (pt1, pt2) => { | ||
const angle = getAngleBetween(pt1, pt2); | ||
return angle >= 0 ? angle : angle + TWO_PI; | ||
var getPositiveAngleBetween = (pt1, pt2) => { | ||
const angle = getAngleBetween(pt1, pt2); | ||
return angle >= 0 ? angle : angle + TWO_PI; | ||
}; | ||
/** | ||
* calculate each segment length(distance) | ||
* @param pts array of points [ x, y ] | ||
* @returns array of segment lengths | ||
*/ | ||
export const getSegmentLengths = (pts) => { | ||
const result = []; | ||
for (let i = 0; i < pts.length - 1; i++) { | ||
const d = dist(pts[i], pts[i + 1]); | ||
result.push(d); | ||
} | ||
return result; | ||
var getSegmentLengths = (pts) => { | ||
const result = []; | ||
for (let i = 0; i < pts.length - 1; i++) { | ||
const d = dist(pts[i], pts[i + 1]); | ||
result.push(d); | ||
} | ||
return result; | ||
}; | ||
export const interpolateArray = importedInterpolateArray; // REVIEW: hmm... | ||
/** | ||
* mix/lerp 2d number array. usually used for path data of [x, y] | ||
* @param pathStart array of [x, y] to start | ||
* @param pathTarget array of [x, y] to target | ||
* @param t 0..1 | ||
* @returns 2d array | ||
*/ | ||
export const interpolatePath = (pathStart, pathTarget, t) => { | ||
if (pathStart.length === 0 || pathTarget.length === 0) | ||
throw new Error("interpolatePath(): path cannot be empty"); | ||
if (pathStart.length !== pathTarget.length) | ||
throw new Error("interpolatePath(): length must be same"); | ||
return Array(pathStart.length) | ||
.fill([]) | ||
.map((_, i) => { | ||
return [ | ||
mix(pathStart[i][0], pathTarget[i][0], t), | ||
mix(pathStart[i][1], pathTarget[i][1], t), | ||
]; | ||
}); | ||
var interpolateArray = interpolateArray$1; | ||
var interpolatePath = (pathStart, pathTarget, t, out) => { | ||
if (pathStart.length === 0 || pathTarget.length === 0) | ||
throw new Error("interpolatePath(): path cannot be empty"); | ||
if (pathStart.length !== pathTarget.length) | ||
throw new Error("interpolatePath(): length must be same"); | ||
out = out || new Array(pathStart.length).fill([]).map(() => []); | ||
for (let i = 0; i < pathStart.length; i++) { | ||
out[i][0] = mix(pathStart[i][0], pathTarget[i][0], t); | ||
out[i][1] = mix(pathStart[i][1], pathTarget[i][1], t); | ||
} | ||
return out; | ||
}; | ||
/** | ||
* interpolate object with {string:number}. ie. {x:10}. | ||
* both objects must have same keys. | ||
* @param objStart object to start from | ||
* @param objTarget object to interpolate to | ||
* @param t 0..1 | ||
* @returns interpolated object | ||
*/ | ||
export const interpolateObject = (objStart, objTarget, t) => { | ||
const obj = {}; | ||
if (Object.keys(objStart).length !== Object.keys(objTarget).length) | ||
throw new Error("interpolateObject(): objects must have same keys"); | ||
for (const key in objStart) { | ||
if (!(key in objTarget)) | ||
throw new Error("interpolateObject(): objects must have same keys"); | ||
obj[key] = mix(objStart[key], objTarget[key], t); | ||
} | ||
return obj; | ||
var interpolateObject = (objStart, objTarget, t) => { | ||
if (Object.keys(objStart).length !== Object.keys(objTarget).length) | ||
throw new Error("interpolateObject(): objects must have same keys"); | ||
const out = {}; | ||
for (const key in objStart) { | ||
if (!(key in objTarget)) | ||
throw new Error("interpolateObject(): objects must have same keys"); | ||
out[key] = mix(objStart[key], objTarget[key], t); | ||
} | ||
return out; | ||
}; | ||
/** | ||
* interpolate number, number[], number[][] or generic object | ||
* | ||
* TODO: | ||
* - currently, string or boolean uses start value. (should it be t=0.5?) | ||
* - review TS implementation | ||
* - every if condition is redundant to check start AND target | ||
* @param start | ||
* @param target | ||
* @param t | ||
* @returns | ||
*/ | ||
export const interpolate = ( | ||
// start: number | number[] | Pts | GenericObject, | ||
// target: number | number[] | Pts | GenericObject, | ||
start, target, t) => { | ||
if (typeof start !== typeof target) | ||
throw new Error("interpolate(): both start and target args must be of same type"); | ||
if (typeof start === "number" && typeof target === "number") { | ||
return mix(start, target, t); | ||
var interpolate = (start, target, t, out) => { | ||
if (typeof start !== typeof target) | ||
throw new Error( | ||
"interpolate(): both start and target args must be of same type" | ||
); | ||
if (typeof start === "number" && typeof target === "number") { | ||
return mix(start, target, t); | ||
} else if (Array.isArray(start) && Array.isArray(target)) { | ||
if (start[0].constructor === Array && target[0].constructor === Array) { | ||
return interpolatePath(start, target, t, out); | ||
} else { | ||
out = out || new Array(start.length); | ||
return interpolateArray(start, target, t, out); | ||
} | ||
else if (Array.isArray(start) && Array.isArray(target)) { | ||
if (start[0].constructor === Array && target[0].constructor === Array) { | ||
// 2d array | ||
return interpolatePath(start, target, t); | ||
} | ||
else { | ||
// 1d array | ||
return interpolateArray(start, target, t); | ||
} | ||
// } else if (start.constructor === Object) { | ||
} | ||
else if (typeof start === "object" && | ||
start !== null && | ||
typeof target === "object" && | ||
target !== null) { | ||
// object | ||
return interpolateObject(start, target, t); | ||
} | ||
else { | ||
// string or boolean | ||
return start; | ||
} | ||
} else if (typeof start === "object" && start !== null && typeof target === "object" && target !== null) { | ||
return interpolateObject(start, target, t); | ||
} else { | ||
return start; | ||
} | ||
}; | ||
/** | ||
* project a point on a line using vector. | ||
* @param pt point | ||
* @param line line segment | ||
* @returns point on the line | ||
*/ | ||
export const projectPointOnLine = (pt, line) => { | ||
const ptVec = vec2(pt[0] - line[1][0], pt[1] - line[1][1]); | ||
const lineVec = vec2(line[0][0] - line[1][0], line[0][1] - line[1][1]); | ||
const prod = dot(ptVec, lineVec); | ||
const proj = prod / mag(lineVec); | ||
const projVec = vec2(proj, proj); | ||
const result = normalize(lineVec, lineVec); | ||
mul(result, lineVec, projVec); | ||
add(result, result, line[1]); | ||
return [result[0], result[1]]; | ||
var projectPointOnLine = (pt, line) => { | ||
const ptVec = vec2(pt[0] - line[1][0], pt[1] - line[1][1]); | ||
const lineVec = vec2(line[0][0] - line[1][0], line[0][1] - line[1][1]); | ||
const prod = dot2(ptVec, lineVec); | ||
const proj = prod / mag(lineVec); | ||
const projVec = vec2(proj, proj); | ||
const result = normalize2(lineVec, lineVec); | ||
mul2(result, lineVec, projVec); | ||
add2(result, result, line[1]); | ||
return [result[0], result[1]]; | ||
}; | ||
/** | ||
* reflect a point on another point or a line | ||
* @param pt source point to be mirrored | ||
* @param axis mirror axis. either point (or line) | ||
* @returns | ||
*/ | ||
export const reflectPoint = (pt, axis) => { | ||
if (axis[0].constructor === Array) { | ||
const projVec = vec2(...projectPointOnLine(pt, axis)); | ||
const distVec = sub([], vec2(pt[0], pt[1]), projVec); | ||
const reflVec = sub(projVec, projVec, distVec); | ||
return [reflVec[0], reflVec[1]]; | ||
} | ||
else { | ||
return [ | ||
reflect(pt[0], axis[0]), | ||
reflect(pt[1], axis[1]), | ||
]; | ||
} | ||
var reflectPoint = (pt, axis) => { | ||
if (axis[0].constructor === Array) { | ||
const projVec = vec2( | ||
...projectPointOnLine(pt, axis) | ||
); | ||
const distVec = sub2([], vec2(pt[0], pt[1]), projVec); | ||
const reflVec = sub2(projVec, projVec, distVec); | ||
return [reflVec[0], reflVec[1]]; | ||
} else { | ||
return [ | ||
reflect(pt[0], axis[0]), | ||
reflect(pt[1], axis[1]) | ||
]; | ||
} | ||
}; | ||
/** | ||
* reflect a path either on a point or a line | ||
* @param pts data that needs to be mirrored | ||
* @param axis mirror axis. either point or line | ||
* @returns | ||
*/ | ||
export const reflectPath = (pts, axis) => { | ||
return pts.map((pt) => reflectPoint(pt, axis)); | ||
var reflectPath = (pts, axis) => { | ||
return pts.map((pt) => reflectPoint(pt, axis)); | ||
}; | ||
/** | ||
* TODO: haven't tested "anchor" yet | ||
* REVIEW: need to round the result? | ||
* @param pt | ||
* @param angle | ||
* @param anchor | ||
* @returns | ||
*/ | ||
export const rotatePoint = (pt, angle, anchor = [0, 0], precision = 5) => { | ||
const x = anchor[0] + Math.cos(angle) * pt[0]; | ||
const y = anchor[1] + Math.sin(angle) * pt[1]; | ||
return [roundF(x, precision), roundF(y, precision)]; | ||
var rotatePoint = (pt, angle, anchor = [0, 0], precision = 5) => { | ||
const x = anchor[0] + Math.cos(angle) * pt[0]; | ||
const y = anchor[1] + Math.sin(angle) * pt[1]; | ||
return [roundF(x, precision), roundF(y, precision)]; | ||
}; | ||
/** | ||
* scale a single point | ||
* @param pt a point [x, y] | ||
* @param size [width, height] to scale to | ||
* @returns scaled point [x, y]` | ||
*/ | ||
export const scalePoint = (pt, size) => { | ||
const [x, y] = pt; | ||
const [w, h] = size; | ||
return [x * w, y * h]; | ||
var scalePoint = (pt, size) => { | ||
const [x, y] = pt; | ||
const [w, h] = size; | ||
return [x * w, y * h]; | ||
}; | ||
/** | ||
* take normalized path data and return [ x, y ] scaled to width and height | ||
* @param path array of [x, y] normalized point pairs | ||
* @param size [width, height] to scale to | ||
* @returns new array of [x, y] | ||
*/ | ||
export const scalePath = (path, size) => { | ||
return path.map((pt) => scalePoint(pt, size)); | ||
var scalePath = (path, size) => { | ||
return path.map((pt) => scalePoint(pt, size)); | ||
}; | ||
/** | ||
* by default, path t value is based on number of points. this function calculates t based on each segment length. | ||
* | ||
* TODO: | ||
* - implement | ||
* @param path | ||
* @returns {number[]} t values for each pt index | ||
*/ | ||
export const calcTByLength = (path) => { | ||
return []; | ||
}; | ||
/** | ||
* combine 2 paths by a single connecting point. | ||
* if connecting points are the same, then add only one. (no duplicates) | ||
* | ||
* TODO: | ||
* - implementation | ||
* - what modes to use?: "start-first", "start-last", "end-first", "end-last"? | ||
* - snap one path to another by automatically moving it? | ||
* - meet at halfway if two end points are not close enough (threshold) | ||
* @param path1 array of [x, y] | ||
* @param path2 array of [x, y] | ||
* @param mode from which point to which point to connect? | ||
* @returns path a single combined path | ||
*/ | ||
export const combinePath = (path1, path2, mode) => { | ||
return path1; | ||
}; | ||
export { blendPath, createShapeFunc, dist, distSq, extrudePath, generateSmoothPath, getAngleBetween, getPathLength, getPositiveAngleBetween, getSegmentLengths, interpolate, interpolateArray, interpolateObject, interpolatePath, projectPointOnLine, reflectPath, reflectPoint, rotatePoint, scalePath, scalePoint }; | ||
//# sourceMappingURL=out.js.map | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@daeinc/geom", | ||
"version": "0.9.0", | ||
"version": "0.10.0", | ||
"description": "Geometry utilities", | ||
"type": "module", | ||
"types": "./dist/index.d.ts", | ||
@@ -15,5 +16,5 @@ "main": "./dist/index.js", | ||
"watch": "tsc --watch", | ||
"test": "vitest watch --config ./vitest.config.js", | ||
"test": "vitest", | ||
"demo": "parcel ./demo/index.html --dist-dir ./demo/build --no-source-maps --no-cache", | ||
"build": "tsc --build --clean && tsc --build" | ||
"build": "tsc --noemit && tsup" | ||
}, | ||
@@ -23,10 +24,12 @@ "author": "Daeinc", | ||
"dependencies": { | ||
"@daeinc/array": "^0.5.0", | ||
"@daeinc/math": "^0.5.0", | ||
"@thi.ng/vectors": "^7.5.31" | ||
"@daeinc/array": "^0.6.1", | ||
"@daeinc/math": "^0.6.0", | ||
"@thi.ng/vectors": "^7.6.3" | ||
}, | ||
"devDependencies": { | ||
"@daeinc/canvas": "^0.12.1", | ||
"@daeinc/draw": "^0.2.1", | ||
"vitest": "^0.27.2" | ||
"@daeinc/canvas": "^0.13.0", | ||
"@daeinc/draw": "^0.4.0", | ||
"tsup": "^6.6.3", | ||
"typescript": "^4.9.5", | ||
"vitest": "^0.28.5" | ||
}, | ||
@@ -33,0 +36,0 @@ "publishConfig": { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
38298
Yes
5
6
360
+ Added@daeinc/array@0.6.1(transitive)
+ Added@daeinc/math@0.6.1(transitive)
- Removed@daeinc/array@0.5.1(transitive)
- Removed@daeinc/math@0.5.0(transitive)
Updated@daeinc/array@^0.6.1
Updated@daeinc/math@^0.6.0
Updated@thi.ng/vectors@^7.6.3