UNPKG

@teachinglab/omd

Version:

omd

259 lines (229 loc) 8.42 kB
export class mathUtils { /** * Calculate distance between two points * @param {Object} p1 - First point {x, y} * @param {Object} p2 - Second point {x, y} * @returns {number} Distance */ static distance(p1, p2) { const dx = p2.x - p1.x; const dy = p2.y - p1.y; return Math.sqrt(dx * dx + dy * dy); } /** * Calculate squared distance (faster than distance when just comparing) * @param {Object} p1 - First point {x, y} * @param {Object} p2 - Second point {x, y} * @returns {number} Squared distance */ static distanceSquared(p1, p2) { const dx = p2.x - p1.x; const dy = p2.y - p1.y; return dx * dx + dy * dy; } /** * Calculate angle between two points * @param {Object} p1 - First point {x, y} * @param {Object} p2 - Second point {x, y} * @returns {number} Angle in radians */ static angle(p1, p2) { return Math.atan2(p2.y - p1.y, p2.x - p1.x); } /** * Calculate angle in degrees * @param {Object} p1 - First point {x, y} * @param {Object} p2 - Second point {x, y} * @returns {number} Angle in degrees */ static angleDegrees(p1, p2) { return this.angle(p1, p2) * 180 / Math.PI; } /** * Linear interpolation between two values * @param {number} a - Start value * @param {number} b - End value * @param {number} t - Interpolation factor (0-1) * @returns {number} Interpolated value */ static lerp(a, b, t) { return a + (b - a) * t; } /** * Linear interpolation between two points * @param {Object} p1 - Start point {x, y} * @param {Object} p2 - End point {x, y} * @param {number} t - Interpolation factor (0-1) * @returns {Object} Interpolated point {x, y} */ static lerpPoint(p1, p2, t) { return { x: this.lerp(p1.x, p2.x, t), y: this.lerp(p1.y, p2.y, t) }; } /** * Clamp value between min and max * @param {number} value - Value to clamp * @param {number} min - Minimum value * @param {number} max - Maximum value * @returns {number} Clamped value */ static clamp(value, min, max) { return Math.min(Math.max(value, min), max); } /** * Normalize value from one range to another * @param {number} value - Input value * @param {number} inMin - Input range minimum * @param {number} inMax - Input range maximum * @param {number} outMin - Output range minimum * @param {number} outMax - Output range maximum * @returns {number} Normalized value */ static map(value, inMin, inMax, outMin, outMax) { return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; } /** * Calculate distance from point to line segment * @param {Object} point - Point {x, y} * @param {Object} lineStart - Line start {x, y} * @param {Object} lineEnd - Line end {x, y} * @returns {number} Distance */ static distanceToLineSegment(point, lineStart, lineEnd) { const dx = lineEnd.x - lineStart.x; const dy = lineEnd.y - lineStart.y; const length = Math.sqrt(dx * dx + dy * dy); if (length === 0) { return this.distance(point, lineStart); } const t = Math.max(0, Math.min(1, ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / (length * length))); const projection = { x: lineStart.x + t * dx, y: lineStart.y + t * dy }; return this.distance(point, projection); } /** * Check if point is inside circle * @param {Object} point - Point {x, y} * @param {Object} center - Circle center {x, y} * @param {number} radius - Circle radius * @returns {boolean} True if point is inside circle */ static pointInCircle(point, center, radius) { return this.distanceSquared(point, center) <= radius * radius; } /** * Check if point is inside rectangle * @param {Object} point - Point {x, y} * @param {Object} rect - Rectangle {x, y, width, height} * @returns {boolean} True if point is inside rectangle */ static pointInRect(point, rect) { return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height; } /** * Smooth an array of points using averaging * @param {Array} points - Array of points * @param {number} factor - Smoothing factor (0-1) * @returns {Array} Smoothed points */ static smoothPoints(points, factor = 0.5) { if (points.length < 3) return points; const smoothed = [points[0]]; // Keep first point for (let i = 1; i < points.length - 1; i++) { const prev = points[i - 1]; const curr = points[i]; const next = points[i + 1]; const avgX = (prev.x + curr.x + next.x) / 3; const avgY = (prev.y + curr.y + next.y) / 3; smoothed.push({ x: this.lerp(curr.x, avgX, factor), y: this.lerp(curr.y, avgY, factor), ...curr // Preserve other properties }); } smoothed.push(points[points.length - 1]); // Keep last point return smoothed; } /** * Generate bezier curve points * @param {Object} p0 - Control point 0 * @param {Object} p1 - Control point 1 * @param {Object} p2 - Control point 2 * @param {Object} p3 - Control point 3 * @param {number} steps - Number of steps * @returns {Array} Curve points */ static bezierCurve(p0, p1, p2, p3, steps = 20) { const points = []; for (let i = 0; i <= steps; i++) { const t = i / steps; const u = 1 - t; const tt = t * t; const uu = u * u; const uuu = uu * u; const ttt = tt * t; const x = uuu * p0.x + 3 * uu * t * p1.x + 3 * u * tt * p2.x + ttt * p3.x; const y = uuu * p0.y + 3 * uu * t * p1.y + 3 * u * tt * p2.y + ttt * p3.y; points.push({ x, y }); } return points; } /** * Convert degrees to radians * @param {number} degrees - Angle in degrees * @returns {number} Angle in radians */ static degreesToRadians(degrees) { return degrees * Math.PI / 180; } /** * Convert radians to degrees * @param {number} radians - Angle in radians * @returns {number} Angle in degrees */ static radiansToDegrees(radians) { return radians * 180 / Math.PI; } /** * Rotate point around center * @param {Object} point - Point to rotate {x, y} * @param {Object} center - Center of rotation {x, y} * @param {number} angle - Rotation angle in radians * @returns {Object} Rotated point {x, y} */ static rotatePoint(point, center, angle) { const cos = Math.cos(angle); const sin = Math.sin(angle); const dx = point.x - center.x; const dy = point.y - center.y; return { x: center.x + dx * cos - dy * sin, y: center.y + dx * sin + dy * cos }; } /** * Generate random number between min and max * @param {number} min - Minimum value * @param {number} max - Maximum value * @returns {number} Random number */ static random(min, max) { return Math.random() * (max - min) + min; } /** * Generate random integer between min and max * @param {number} min - Minimum value (inclusive) * @param {number} max - Maximum value (inclusive) * @returns {number} Random integer */ static randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } }