UNPKG

@bitbybit-dev/base

Version:

Bit By Bit Developers Base CAD Library to Program Geometry

352 lines (351 loc) 12.6 kB
/** * Contains various methods for points. Point in bitbybit is simply an array containing 3 numbers for [x, y, z]. * Because of this form Point can be interchanged with Vector, which also is an array in [x, y, z] form. * When creating 2D points, z coordinate is simply set to 0 - [x, y, 0]. */ export class Point { constructor(geometryHelper, transforms) { this.geometryHelper = geometryHelper; this.transforms = transforms; } /** * Transforms the single point * @param inputs Contains a point and the transformations to apply * @returns Transformed point * @group transforms * @shortname transform point * @drawable true */ transformPoint(inputs) { const transformation = inputs.transformation; let transformedControlPoints = [inputs.point]; transformedControlPoints = this.geometryHelper.transformControlPoints(transformation, transformedControlPoints); return transformedControlPoints[0]; } /** * Transforms multiple points * @param inputs Contains points and the transformations to apply * @returns Transformed points * @group transforms * @shortname transform points * @drawable true */ transformPoints(inputs) { return this.geometryHelper.transformControlPoints(inputs.transformation, inputs.points); } /** * Transforms multiple points by multiple transformations * @param inputs Contains points and the transformations to apply * @returns Transformed points * @group transforms * @shortname transforms for points * @drawable true */ transformsForPoints(inputs) { if (inputs.points.length !== inputs.transformation.length) { throw new Error("You must provide equal nr of points and transformations"); } return inputs.points.map((pt, index) => { return this.geometryHelper.transformControlPoints(inputs.transformation[index], [pt])[0]; }); } /** * Translate multiple points * @param inputs Contains points and the translation vector * @returns Translated points * @group transforms * @shortname translate points * @drawable true */ translatePoints(inputs) { const translationTransform = this.transforms.translationXYZ({ translation: inputs.translation }); return this.geometryHelper.transformControlPoints(translationTransform, inputs.points); } /** * Translate multiple points * @param inputs Contains points and the translation vector * @returns Translated points * @group transforms * @shortname translate points with vectors * @drawable true */ translatePointsWithVectors(inputs) { if (inputs.points.length !== inputs.translations.length) { throw new Error("You must provide equal nr of points and translations"); } const translationTransforms = this.transforms.translationsXYZ({ translations: inputs.translations }); return inputs.points.map((pt, index) => { return this.geometryHelper.transformControlPoints(translationTransforms[index], [pt])[0]; }); } /** * Translate multiple points by x, y, z values provided * @param inputs Contains points and the translation in x y and z * @returns Translated points * @group transforms * @shortname translate xyz points * @drawable true */ translateXYZPoints(inputs) { const translationTransform = this.transforms.translationXYZ({ translation: [inputs.x, inputs.y, inputs.z] }); return this.geometryHelper.transformControlPoints(translationTransform, inputs.points); } /** * Scale multiple points by providing center point and x, y, z scale factors * @param inputs Contains points, center point and scale factors * @returns Scaled points * @group transforms * @shortname scale points on center * @drawable true */ scalePointsCenterXYZ(inputs) { const scaleTransforms = this.transforms.scaleCenterXYZ({ center: inputs.center, scaleXyz: inputs.scaleXyz }); return this.geometryHelper.transformControlPoints(scaleTransforms, inputs.points); } /** * Rotate multiple points by providing center point, axis and degrees of rotation * @param inputs Contains points, axis, center point and angle of rotation * @returns Rotated points * @group transforms * @shortname rotate points center axis * @drawable true */ rotatePointsCenterAxis(inputs) { const rotationTransforms = this.transforms.rotationCenterAxis({ center: inputs.center, axis: inputs.axis, angle: inputs.angle }); return this.geometryHelper.transformControlPoints(rotationTransforms, inputs.points); } /** * Measures the closest distance between a point and a collection of points * @param inputs Point from which to measure and points to measure the distance against * @returns Distance to closest point * @group extract * @shortname distance to closest pt * @drawable false */ closestPointFromPointsDistance(inputs) { return this.closestPointFromPointData(inputs).distance; } /** * Finds the closest point index between a point and a collection of points. Caution, index is not 0 based, it starts with 1. * @param inputs Point from which to find the index in a collection of points * @returns Closest point index * @group extract * @shortname index of closest pt * @drawable false */ closestPointFromPointsIndex(inputs) { return this.closestPointFromPointData(inputs).index; } /** * Finds the closest point in a collection * @param inputs Point and points collection to find the closest point in * @returns Closest point * @group extract * @shortname closest pt * @drawable true */ closestPointFromPoints(inputs) { return this.closestPointFromPointData(inputs).point; } /** * Finds the distance between two points * @param inputs Coordinates of start and end points * @returns Distance * @group measure * @shortname distance * @drawable false */ distance(inputs) { const x = inputs.endPoint[0] - inputs.startPoint[0]; const y = inputs.endPoint[1] - inputs.startPoint[1]; const z = inputs.endPoint[2] - inputs.startPoint[2]; return Math.sqrt(x * x + y * y + z * z); } /** * Finds the distances between the start point and multiple end points * @param inputs Coordinates of start and end points * @returns Distances * @group measure * @shortname distances to points * @drawable false */ distancesToPoints(inputs) { return inputs.endPoints.map(pt => { return this.distance({ startPoint: inputs.startPoint, endPoint: pt }); }); } /** * Multiply point by a specified amount * @param inputs The point to be multiplied and the amount of points to create * @returns Distance * @group transforms * @shortname multiply point * @drawable true */ multiplyPoint(inputs) { const points = []; for (let i = 0; i < inputs.amountOfPoints; i++) { points.push([inputs.point[0], inputs.point[1], inputs.point[2]]); } return points; } /** * Get x coordinate of the point * @param inputs The point * @returns X coordinate * @group get * @shortname x coord * @drawable false */ getX(inputs) { return inputs.point[0]; } /** * Get y coordinate of the point * @param inputs The point * @returns Y coordinate * @group get * @shortname y coord * @drawable false */ getY(inputs) { return inputs.point[1]; } /** * Get z coordinate of the point * @param inputs The point * @returns Z coordinate * @group get * @shortname z coord * @drawable false */ getZ(inputs) { return inputs.point[2]; } /** * Get average point of points * @param inputs The points * @returns point * @group extract * @shortname average point * @drawable true */ averagePoint(inputs) { const xVals = []; const yVals = []; const zVals = []; inputs.points.forEach(pt => { xVals.push(pt[0]); yVals.push(pt[1]); zVals.push(pt[2]); }); return [ xVals.reduce((p, c) => p + c, 0) / inputs.points.length, yVals.reduce((p, c) => p + c, 0) / inputs.points.length, zVals.reduce((p, c) => p + c, 0) / inputs.points.length, ]; } /** * Creates the xyz point * @param inputs xyz information * @returns point 3d * @group create * @shortname point xyz * @drawable true */ pointXYZ(inputs) { return [inputs.x, inputs.y, inputs.z]; } /** * Creates the xy point * @param inputs xy information * @returns point 3d * @group create * @shortname point xy * @drawable false */ pointXY(inputs) { return [inputs.x, inputs.y]; } /** * Creates the spiral out of multiple points * @param inputs Spiral information * @returns Specified number of points in the array along the spiral * @group create * @shortname spiral * @drawable true */ spiral(inputs) { const phi = inputs.phi; const b = Math.log(phi) / (Math.PI / inputs.widening); const spiral = []; const step = inputs.radius / inputs.numberPoints; for (let i = 0; i < inputs.radius; i += step) { const th = Math.log(i / inputs.factor) / b; const x = i * Math.cos(th); const y = i * Math.sin(th); spiral.push([x ? x : 0, y ? y : 0, 0]); } return spiral; } /** * Creates a flat point grid on XY plane. This grid contains center points for hexagons of the given radius. * Be aware that we control only the nr of hexagons to be made and not the length and width of the grid. * @param inputs Information about hexagon and the grid * @returns Points in the array on the grid * @group create * @shortname hex grid * @drawable true */ hexGrid(inputs) { const xLength = Math.sqrt(Math.pow(inputs.radiusHexagon, 2) - Math.pow(inputs.radiusHexagon / 2, 2)); const points = []; for (let ix = 0; ix < inputs.nrHexagonsX; ix++) { const coordX = ix * xLength * 2; for (let iy = 0; iy < inputs.nrHexagonsY; iy++) { const coordY = (inputs.radiusHexagon + inputs.radiusHexagon / 2) * iy; const adjustX = coordX + (iy % 2 === 0 ? 0 : xLength); points.push([adjustX, coordY, 0]); } } if (inputs.orientOnCenter) { const compensateX = points[points.length - 1][0] / 2; const compensateY = points[points.length - 1][1] / 2; points.forEach((p, index) => { points[index] = [p[0] - compensateX, p[1] - compensateY, 0]; }); } if (inputs.pointsOnGround) { points.forEach((p, index) => { points[index] = [p[0], 0, p[1]]; }); } return points; } /** * Removes consecutive duplicates from the point array with tolerance * @param inputs points, tolerance and check first and last * @returns Points in the array without consecutive duplicates * @group clean * @shortname remove duplicates * @drawable true */ removeConsecutiveDuplicates(inputs) { return this.geometryHelper.removeConsecutivePointDuplicates(inputs.points, inputs.checkFirstAndLast, inputs.tolerance); } closestPointFromPointData(inputs) { let distance = Number.MAX_SAFE_INTEGER; let closestPointIndex; let point; for (let i = 0; i < inputs.points.length; i++) { const pt = inputs.points[i]; const currentDist = this.distance({ startPoint: inputs.point, endPoint: pt }); if (currentDist < distance) { distance = currentDist; closestPointIndex = i; point = pt; } } return { index: closestPointIndex + 1, distance, point }; } }