UNPKG

gs-json

Version:

gs-JSON is a domain agnostic unifying 3D file format for geometric and semantic modelling (hence the 'gs').

378 lines 14.7 kB
import * as three from "three"; import * as gs from "../../gs-json"; import * as threex from "../threex/threex"; /** * Calculate a set of xyz position on the circle/arc ir ellipse/arc. The number of points = length / resolution. * With resolution from 0.0001 to 0.5, 0.0001 being a higher resolution than 0.5 */ export function getRenderXYZs(obj, resolution) { switch (obj.getObjType()) { case 3 /* circle */: return circleGetRenderXYZs(obj, resolution); case 4 /* ellipse */: return ellipseGetRenderXYZs(obj, resolution); default: throw new Error("Invalid object type."); } } /** * Calculate the length of the circle or arc. */ export function circleLength(circle) { const rad = circle.getRadius(); const angles = circle.getAngles(); // calculate the angle of the arc let arc_angle; if (angles === undefined) { return 2 * Math.PI * rad; } else if (angles[0] < angles[1]) { arc_angle = (angles[1] - angles[0]); } else { arc_angle = (angles[0] - angles[1]); } return 2 * Math.PI * rad * (arc_angle / 360); } /** * Calculate the xyz position at parameter t on the circle or arc. The t parameter range is from 0 to 1. */ export function circleEvaluate(circle, t) { const rad = circle.getRadius(); const angles = circle.getAngles(); // set arc start and arc end angles, in radians let ang_start; let ang_end; if (angles === undefined) { ang_start = 0; ang_end = Math.PI * 2; } else { ang_start = angles[0] * (Math.PI / 180); ang_end = angles[1] * (Math.PI / 180); } // calculate the angle of the arc let arc_angle; if (ang_start < ang_end) { arc_angle = ang_end - ang_start; } else { arc_angle = ((Math.PI * 2) - ang_start) + ang_end; } // create matrix to map from XY plane into the 3D plane for circle const matrix_inv = threex.matrixInv(threex.xformMatrixFromXYZs(circle.getOrigin().getPosition(), circle.getAxes())); // calculate the point const alpha = ang_start + (t * arc_angle); const point = new three.Vector3(rad * Math.cos(alpha), rad * Math.sin(alpha), 0); point.applyMatrix4(matrix_inv); // return the points return point.toArray(); } /** * Project a point on a circle, and calculate the parameter t. */ export function circleEvaluatePoint(circle, point) { const angles = circle.getAngles(); // create matrix to map from the 3D plane for circle into the XY plane const matrix = threex.xformMatrixFromXYZs(circle.getOrigin().getPosition(), circle.getAxes()); // map the point onto the XY plane const xyz_2d = threex.multXYZMatrix(point.getPosition(), matrix); // calculate the angle between the point vector and the x axis, in radians let point_angle = Math.atan2(xyz_2d[1], xyz_2d[0]); if (point_angle < 0) { point_angle += (2 * Math.PI); } // calculate t for a closed circle if (angles === undefined) { return point_angle / (2 * Math.PI); } // convert angles to radians const ang_start = angles[0] * (Math.PI / 180); const ang_end = angles[1] * (Math.PI / 180); // calculate t for an arc if (ang_start < ang_end) { // the point is on the arc if ((point_angle >= ang_start) && (point_angle <= ang_end)) { return (point_angle - ang_start) / (ang_end - ang_start); } else { const rotated = point_angle - ((ang_start + ang_end) / 2); if (rotated > Math.PI) { return 0; } else { return 1; } } } else { // add up the lower and upper angles const arc_angle_lower = ((Math.PI * 2) - ang_start); const arc_angle_upper = ang_end; const arc_angle = arc_angle_lower + arc_angle_upper; if (point_angle >= ang_start) { return (point_angle - ang_start) / arc_angle; } else if (point_angle <= ang_end) { return (arc_angle_lower + point_angle) / arc_angle; } else { const rotated = point_angle - ((ang_start + ang_end) / 2); console.log(ang_start, ang_end, point_angle, rotated); if (rotated < Math.PI) { return 0; } else { return 1; } } } } /** * Calculate a set of xyz position on the circle or arc. The number of points = length / resolution. * With resolution from 0.0001 to 0.5, 0.0001 being a higher resolution than 0.5 */ export function circleGetRenderXYZs(circle, resolution) { const rad = circle.getRadius(); const angles = circle.getAngles(); // calculat the arc start angle let arc_start; if (angles === undefined) { arc_start = 0; } else { arc_start = angles[0] * (Math.PI / 180); } // calculate the angle of the arc let arc_angle; if (angles === undefined) { arc_angle = 2 * Math.PI; } else if (angles[0] < angles[1]) { arc_angle = (angles[1] - angles[0]) * (Math.PI / 180); } else { arc_angle = (angles[0] - angles[1]) * (Math.PI / 180); } // calculate number of points let N = Math.floor(arc_angle / (Math.PI / 36)); if (N < 3) { N = 3; } // create matrix to map from XY plane into the 3D plane for circle const matrix_inv = threex.matrixInv(threex.xformMatrixFromXYZs(circle.getOrigin().getPosition(), circle.getAxes())); // main loop to create points const xyz_points = []; for (let k = 0; k < N; k++) { const t = k / (N - 1); const alpha = arc_start + (t * arc_angle); const point = new three.Vector3(rad * Math.cos(alpha), rad * Math.sin(alpha), 0); point.applyMatrix4(matrix_inv); xyz_points.push(point.toArray()); } // return the points return xyz_points; } /** * Calculate the length of the conic curve. */ export function ellipseLength(curve) { // ConicCurve assumed to be an ellipse or circle; const vector_x = curve.getAxes()[0]; const vector_y = curve.getAxes()[1]; // Initial vector_x and vector_y require to be (almost) orthogonal const threshold = 1e-6; if (Math.abs(vector_x[0] * vector_y[0] + vector_x[1] * vector_y[1] + vector_x[2] * vector_y[2]) >= threshold) { throw new Error("Orthogonal vectors are required for that Ellipse / Conic length calculation"); } const a = Math.sqrt(vector_x[0] * vector_x[0] + vector_x[1] * vector_x[1] + vector_x[2] * vector_x[2]); const b = Math.sqrt(vector_y[0] * vector_y[0] + vector_y[1] * vector_y[1] + vector_y[2] * vector_y[2]); const u = [a, 0]; const v = [0, b]; const angle_1 = curve.getAngles()[0] * (2 * Math.PI) / 360; const angle_2 = curve.getAngles()[1] * (2 * Math.PI) / 360; // Radians, although input angles are expected in Degrees if (Math.abs(a - b) < threshold) { return a * Math.abs(angle_2 - angle_1); } // Range [x1,x2] for length calculation would provide 2 circle arcs, // Whereas Angle_1 / Angle_2 provide a unique circle arc. let eccentricity = null; if (a > b) { eccentricity = Math.sqrt(1 - (b / a) * (b / a)); } if (b > a) { eccentricity = Math.sqrt(1 - (a / b) * (a / b)); } const K = 1000; let theta = null; const d_th = (angle_2 - angle_1) / K; let distance = 0; for (let k = 0; k < K; k++) { theta = angle_1 + k * (angle_2 - angle_1) / K; distance = distance + d_th * Math.sqrt(1 - eccentricity * Math.sin(theta) * eccentricity * Math.sin(theta)); // distance along the curve assessed and updated at each timestep; } distance = Math.max(a, b) * distance; return distance; } /** * Calculate the xyz position at parameter t. The t parameter range is from 0 to 1. */ export function ellipseEvaluate(curve, t) { // ConicCurve assumed to be an ellipse or circle; const vector_x = curve.getAxes()[0]; const vector_y = curve.getAxes()[1]; // Initial vector_x and vector_y require to be (almost) orthogonal const threshold = 1e-6; if (Math.abs(vector_x[0] * vector_y[0] + vector_x[1] * vector_y[1] + vector_x[2] * vector_y[2]) >= threshold) { throw new Error("Orthogonal vectors are required for that Ellipse / Conic length calculation"); } const a = Math.sqrt(vector_x[0] * vector_x[0] + vector_x[1] * vector_x[1] + vector_x[2] * vector_x[2]); const b = Math.sqrt(vector_y[0] * vector_y[0] + vector_y[1] * vector_y[1] + vector_y[2] * vector_y[2]); const u = [a, 0]; const v = [0, b]; const z_uv = [0, 0, u[0] * v[1] - u[1] * v[0]]; // cross product const angle_1 = curve.getAngles()[0] * (2 * Math.PI) / 360; const angle_2 = curve.getAngles()[1] * (2 * Math.PI) / 360; const l = ellipseLength(curve); let epsilon = 1; let theta = null; const K = 1000; // Does this not depend on the length of the ellipse? let x = null; let y = null; let r = null; let theta_t = null; const param = b * b / a; const m = new gs.Model(); const g = m.getGeom(); const pt = g.addPoint([0, 0, 0]); let curve_theta = null; for (let k = 0; k < K; k++) { while (epsilon >= 0) { theta = (angle_1 + k * (angle_2 - angle_1) / K); curve_theta = g.addEllipse(curve.getOrigin(), curve.getAxes()[0], curve.getAxes()[1], [curve.getAngles()[0], theta]); // Why is this adding ellipses to the model? epsilon = t * l - ellipseLength(curve_theta); if (epsilon < 0) { theta_t = theta; } } } let eccentricity = null; if (a > b) { eccentricity = Math.sqrt(1 - (b / a) * (b / a)); } if (b > a) { eccentricity = Math.sqrt(1 - (a / b) * (a / b)); } r = param / (1 + eccentricity * Math.cos(theta_t)); x = r * Math.cos(theta_t); // expressed in the plan inferred by (u,v) y = r * Math.sin(theta_t); // expressed in the plan inferred by (u,v) const U1 = new three.Vector3(curve.getAxes()[0][0], curve.getAxes()[0][1], curve.getAxes()[0][2]); const V1 = new three.Vector3(curve.getAxes()[1][0], curve.getAxes()[1][1], curve.getAxes()[1][2]); U1.normalize(); V1.normalize(); const O1O2 = new three.Vector3(curve.getOrigin()[0], curve.getOrigin()[1], curve.getOrigin()[2]); const O2P = threex.addVectors(U1.multiplyScalar(x), V1.multiplyScalar(y)); const O1P = threex.addVectors(O1O2, O2P); return [O1P.x, O1P.y, O1P.z]; // Should work.. } /** * Calculate a set of xyz position on the ellipse. The number of points = length / resolution. */ export function ellipseGetRenderXYZs(curve, resolution) { const O = curve.getOrigin().getPosition(); const renderingXYZs = []; const renderXYZs = []; let r = null; let theta = 0; let d_theta = 0; const U1 = new three.Vector3(...curve.getAxes()[0]).normalize(); const V1 = new three.Vector3(...curve.getAxes()[1]).normalize(); const a = new three.Vector3(...curve.getAxes()[0]).length(); const b = new three.Vector3(...curve.getAxes()[1]).length(); const L = Math.PI * Math.sqrt(2 * (a * a + b * b) - (a - b) * (a - b) / 2); const l = L * resolution; const param = b * b / a; const c = Math.sqrt(Math.abs(a * a - b * b)); if (a >= b) { const e = Math.sqrt(1 - b * b / (a * a)); let N = 0; let eps = 1; while (eps > 0) { theta = theta + d_theta; eps = Math.PI * 2 - theta; N++; r = param / (1 + e * Math.cos(theta)); d_theta = l / r; } N--; theta = 0; d_theta = 0; for (let k = 0; k < N; k++) { theta = theta + d_theta; r = param / (1 + e * Math.cos(theta)); d_theta = l / r; renderingXYZs.push([(r * Math.cos(theta)) + c, r * Math.sin(theta), 0]); } } if (b > a) { const e = Math.sqrt(1 - a * a / (b * b)); let N = 0; let eps = 1; while (eps > 0) { theta = theta + d_theta; eps = Math.PI * 2 - theta; N++; r = param / (1 + e * Math.cos(theta)); d_theta = l / r; } N--; theta = 0; d_theta = 0; for (let k = 0; k < N; k++) { theta = theta + d_theta; r = param / (1 + e * Math.cos(theta)); d_theta = l / r; renderingXYZs.push([r * Math.cos(theta), (r * Math.sin(theta)) + c, 0]); } } const results = []; for (const point of renderingXYZs) { results.push(new three.Vector3(point[0], point[1], point[2])); } const O1 = new three.Vector3(0, 0, 0); const e1 = new three.Vector3(1, 0, 0); const e2 = new three.Vector3(0, 1, 0); const e3 = new three.Vector3(0, 0, 1); const C1 = new three.Vector3(curve.getOrigin().getPosition()[0], curve.getOrigin().getPosition()[1], curve.getOrigin().getPosition()[2]); const W1 = threex.crossVectors(U1, V1, true); const C1O1 = threex.subVectors(O1, C1, false); const vec_O_1 = new three.Vector3(C1O1.dot(U1), C1O1.dot(V1), C1O1.dot(W1)); const x1 = new three.Vector3(e1.dot(U1), e1.dot(V1), e1.dot(W1)); const y1 = new three.Vector3(e2.dot(U1), e2.dot(V1), e2.dot(W1)); let z1 = new three.Vector3(); z1 = z1.crossVectors(x1, y1); const m1 = new three.Matrix4(); const o_neg = vec_O_1.clone().negate(); m1.setPosition(o_neg); let m2 = new three.Matrix4(); m2 = m2.makeBasis(x1.normalize(), y1.normalize(), z1.normalize()); m2 = m2.getInverse(m2); const m3 = new three.Matrix4(); const rotation1 = m3.multiplyMatrices(m2, m1); const results_c1 = []; for (const point of results) { results_c1.push(threex.multVectorMatrix(point, rotation1)); } for (const point of results_c1) { renderXYZs.push([point.x, point.y, point.z]); } // console.log("rendering is "); // console.log(renderingXYZs); throw new Error("Method not implemented"); // return renderXYZs; } //# sourceMappingURL=conics.js.map