UNPKG

gs-modelling

Version:

A set of 3D modelling functions for gs-JSON.

350 lines (322 loc) 14.9 kB
import * as gs from "gs-json"; import * as three from "three"; import * as threex from "./libs/threex/threex"; import * as poly from "./libs/poly/poly"; import * as utils from "./_utils_dev"; import {Arr} from "./libs/arr/arr"; /** * Offsets a polymesh along its normal by a specified distance * * Each face is moved by specified distance in the direction of its normal and rejoined (extended or * trimmed to fit) to create a new surface * @param pmesh Polymesh object * @param distance Distance to offset polymesh * @returns New offset polymesh if successful */ export function offset(pmesh: gs.IPolymesh, distance: number): gs.IPolymesh { // check args if (!pmesh.exists()) {throw new Error("polymesh has been deleted.");} const model: gs.IModel = pmesh.getModel(); const geom: gs.IGeom = model.getGeom(); // make a copy pmesh = pmesh.copy(true) as gs.IPolymesh; // create a map of point -> vertices in this pmesh const vertices: gs.IVertex[] = Arr.flatten(pmesh.getVertices(gs.EGeomType.faces)); const vertices_map: Map<number, gs.IVertex[]> = new Map(); for (const vertex of vertices) { const id: number = vertex.getPoint().getID(); if (!vertices_map.has(id)) {vertices_map.set(id, []);} vertices_map.get(id).push(vertex); } // move each point for (const [point_id, vertices] of vertices_map.entries()) { let normal: three.Vector3; if (vertices.length ===1) { normal = poly.getVertexNormal(vertices[0]); normal.setLength(distance); } else { // get the normal mean const vertex_normals: three.Vector3[] = []; normal = new three.Vector3(); for (const vertex of vertices) { const vertex_normal: three.Vector3 = poly.getVertexNormal(vertex); vertex_normals.push(vertex_normal); normal.add(vertex_normal); } const angle = normal.angleTo(vertex_normals[0]); const len: number = distance / Math.cos(angle); normal.setLength(len); } // set the point position const point: gs.IPoint = geom.getPoint(point_id); const old_pos: gs.XYZ = point.getPosition(); const new_pos: gs.XYZ = [old_pos[0] + normal.x, old_pos[1] + normal.y, old_pos[2] + normal.z]; point.setPosition(new_pos); } return pmesh; } /** * Flips all faces * @param pmesh Polymeshes to flipFaces * @returns New polymesh created from weld if successful, null if unsuccessful or on error */ export function flipFaces(pmesh: gs.IPolymesh): gs.IPolymesh { // check args if (!pmesh.exists()) {throw new Error("polymesh has been deleted.");} throw new Error("Not implemented exception"); } /** * Join a set of polymeshes to form a single polymesh. * * Returns null if polymeshes do not intersect or touch * @param pmeshes List of polymeshes to weld * @returns New polymesh created from weld if successful, null if unsuccessful or on error */ export function join(pmeshes: gs.IPolymesh[]): gs.IPolymesh[] { // check args const model: gs.IModel = pmeshes[0].getModel(); for (const pmesh of pmeshes) { if (!pmesh.exists()) {throw new Error("polymesh has been deleted.");} if (pmesh.getModel() !== model) {throw new Error("Polymeshes have to be in same model.");} } // collect the faces together in a points arr const mesh_points: gs.IPoint[][] = []; for (const pmesh of pmeshes) { for (const face of pmesh.getFaces()) { const points: gs.IPoint[] = face.getVertices().map((v) => v.getPoint()); mesh_points.push(points); } } // create a new pmesh const new_pmesh: gs.IPolymesh = model.getGeom().addPolymesh(mesh_points); // TODO check for disjoint polymeshes // return the new mesh return [new_pmesh]; } /** * Thicken ... * * @param pmesh A Polymesh to create a polymesh with a single polygon face. * @returns A polymesh if successful, null if unsuccessful or on error. */ export function thicken(pmesh: gs.IPolymesh, dist1: number, dist2: number): gs.IPolymesh { // check args if (!pmesh.exists()) {throw new Error("polymesh has been deleted.");} const model: gs.IModel = pmesh.getModel(); const pmesh1: gs.IPolymesh = offset(pmesh, dist1); const pmesh2: gs.IPolymesh = offset(pmesh, dist2 * -1); // TODO flip faces const wires1: gs.IWire[] = pmesh1.getWires(); const wires2: gs.IWire[] = pmesh2.getWires(); if (wires1.length !== wires2.length) {throw new Error("Error occured while thickening mesh.");} const sides: gs.IPolymesh[] = []; for (let i = 0; i < wires1.length; i++) { const points1 = wires1[i].getVertices().map((v) => v.getPoint()); const points2 = wires2[i].getVertices().map((v) => v.getPoint()); if (points1.length !== points2.length) {throw new Error("Error occured while thickening mesh.");} points1.push(points1[0]); points2.push(points2[0]); const mesh_points: gs.IPoint[][] = []; for (let j = 0; j < points1.length - 1; j++) { mesh_points.push([points1[j], points1[j+1], points2[j+1], points2[j]]) } const side_mesh: gs.IPolymesh = model.getGeom().addPolymesh(mesh_points); sides.push(side_mesh); } return join([pmesh1, pmesh2, ...sides])[0]; } /** * Loft a set of polylines with equal numbers of vertices. * * @param pmesh A polymesh to extrude * @param vector The vector defining the extrusion length and direction. * @returns A polymesh if successful, null if unsuccessful or on error. */ export function loft(pmeshes: gs.IPolymesh[], internal_edges: boolean): gs.IPolymesh { for (const pmesh of pmeshes) { if (!pmesh.exists()) {throw new Error("Pline has been deleted.");} } const m: gs.IModel = pmeshes[0].getModel(); throw new Error("method not implemented"); } /** * Copies polymeshes from one model to another * @param model_1 Model to copy from * @param model_2 Model to copy to * @returns List of polymeshes copied into specified model if successful */ export function _CopyFromModel(model_1: gs.IModel, model_2: gs.IModel ): gs.IPolymesh[] { throw new Error("Method not implemented"); } /** * Creates one or more polygons from planar polylines * http://developer.rhino3d.com/api/RhinoScriptSyntax/#surface-AddPlanarSrf * http://verbnurbs.com/docs/geom/ISurface/ (?) * * If a closed polyline is specified, it is used as the edge of the polygon<br/> * If multiple open polylines are specified, their intersections are found and if the resulting segments form * a closed polyline, the resulting closed polyline is used as the edge of the polygon<br/> * Returns null if closed polylines are not planar, or if polylines specified are not coplanar<br/> * Returns null if polylines specified do not intersect to form a closed polyline * @param plines List of polylines to create planar polygon from * @returns List of polygons created if successful, null if unsuccessful or on error */ function FromPlines(pline: gs.IPolyline): gs.IPolymesh { const model: gs.IModel = pline.getModel(); return model.getGeom().addPolymesh([pline.getPointsArr()]); } /** * Creates a closed box polymesh on a plane * * Box will be constructed with its top and bottom faces parallel to the plane specified, with the origin as * its volume centroid and its sides parallel to the x and y axis of the plane. * @param plane Plane to construct box on * @param length_x Length in x-direction * @param length_y Length in y-direction * @param length_z Length in z-direction * @returns New polymesh if successful, null if unsuccessful or on error */ function _BoxFromPlane(plane: gs.IPlane, length_x: number, length_y: number, length_z: number ): gs.IPolymesh { throw new Error("Method not implemented"); } // - Possibly Assignment 1 (WEEK 2-3) - /** * Creates a piped polymesh along a input polyline * http://developer.rhino3d.com/api/RhinoScriptSyntax/#surface-AddPipe * * Pipe constructed will have a circular cross section with the specified radius, angled to be perpendicular * to the input polyline throughout its length * @param polyline Rail polyline * @param radius List of radius values * @param cap Caps end with a flat surface if true * @returns New polymesh if successful, null if unsuccessful or on error */ function _PipeFromPline(polyline: gs.IPolyline, radius: [number, number], cap: boolean): gs.IPolymesh { throw new Error("Method not implemented"); } /** * Creates a rectangular polygon on a plane * * Rectangular polygon with be constructed parallel to the specified plane, with the origin as the area * centroid of the rectangle and its edges parallel to the x and y axis * @param plane Plane to construct rectangle on. Origin will be center of the rectangle * @param length_x Length in x-direction * @param length_y Length in y-direction * @returns New polymesh if successful, null if unsuccessful or on error */ function _RectFromPlane(plane: gs.IPlane, length_x: number, length_y: number ): gs.IPolymesh { throw new Error("Method not implemented"); } // =============================================================================================================== // Pmesh Functions =============================================================================================== // =============================================================================================================== /** * Calculates total surface area of a polymesh * * Each face is considered only once (does not take into account front and back of faces) * @param pmesh Polymesh object * @returns Total surface area of polymesh if successful */ export function _area(pmesh: gs.IPolymesh): boolean { throw new Error("Method not implemented"); } /** * Checks if the polymesh is closed * @param pmesh Polymesh object * @returns True if the polymesh is closed */ export function _isClosed(pmesh: gs.IPolymesh): number { //return pmesh.isClosed(); throw new Error("Method not implemented"); } /** * Explodes a polymesh into individual polygons * * Each polygonal face in the polymesh is returned as a separate polymesh object * @param pmesh Polymesh to explode * @param copy Perfroms transformation on duplicate copy of input polymesh * @returns List of new polymeshes created from explode */ export function _explode(pmesh: gs.IPolymesh, copy: boolean): gs.IPolymesh[] { throw new Error("Method not implemented"); } /** * Extracts a list of polygons from a polymesh * * Specified polygonal faces are removed from the polymesh and returned as individual polymesh objects<br/> * The remainder of the polymesh is rejoined as much as possible and returned as one polymesh if still intact, * or multiple polymeshes if they have been broken up<br/> * List returned is in order (from face 0 of orginal input pline) * @param pmesh Polymesh to extract segments from * @param polygon_index Index numbers of polygons to extract * @param return_remainder Returns polymeshes created from the remainder of the polymesh if true, returns only * specified segments if false * @param copy Perfroms transformation on duplicate copy of input polymesh * @returns List of new polymeshes created from extract */ export function _extract(pmesh: gs.IPolymesh, polygon_index: number[], return_remainder: boolean, copy: boolean): gs.IPolymesh[] { throw new Error("Method not implemented"); } // http://docs.autodesk.com/3DSMAX/15/ENU/3ds-Max-Help/images/GUID-72B1FF18-945C-4788-813B-E8FCC491F36C-low.png /** * Offsets a polymesh along its normal by a specified distance * * Each face is moved by specified distance in the direction of its normal and rejoined (extended or * trimmed to fit) to create a new surface * @param pmesh Polymesh object * @param distance Distance to offset polymesh * @param copy Perfroms transformation on duplicate copy of input polymesh * @returns New offset polymesh if successful */ export function _offset(pmesh: gs.IPolymesh, distance: number, copy: boolean): gs.IPolymesh { throw new Error("Method not implemented"); } /** * Calculates perimeter of a polymesh * @param pmesh Polymesh object * @returns Perimeter of polymesh if successful */ export function _perimeter(pmesh: gs.IPolymesh): boolean { throw new Error("Method not implemented"); } /** * Weld a list of polymeshes together * * Joins polymeshes together and returns a single polymesh<br/> * Returns null if polymeshes do not intersect or touch * @param pmeshes List of polymeshes to weld * @param is_closed Creates a closed polymesh object if true * @returns New polymesh created from weld if successful, null if unsuccessful or on error */ export function _weld(pmeshes: gs.IPolymesh[], is_closed: boolean): gs.IPolymesh { throw new Error("Method not implemented"); } // =============================================================================================================== // Old Functions No Longer in API ================================================================================ // =============================================================================================================== /** * Creates a polymesh by extruding a polymesh along a path polyline. * http://developer.rhino3d.com/api/RhinoScriptSyntax/#surface-ExtrudeSurface * @param m Model * @param polymesh Polymesh to extrude. * @param polyline Polyline to extrude along. * @param cap Extrusion capped at both ends if true. Open if false. * @returns New polymesh if successful, null if unsuccessful or on error */ function _Extrude(m: gs.IModel, polymesh: gs.IPolymesh, polyline: gs.IPolyline, cap: boolean): gs.IPolymesh { throw new Error("Method not implemented"); } /* * Performs a boolean difference operation on two 2D input polymeshes on a plane. * http://www.angusj.com/delphi/clipper.php * @param m Model * @param input0 Polymesh to subtract from. * @param input1 Polymesh to subtract. * @param plane Plane on which input polymeshes lie. * @param delete__input Deletes all input objects if true. * @returns New polymesh if successful, null if unsuccessful or on error */ function _BooleanDifference2D(m: gs.IModel, input0: gs.IPolymesh, input1: gs.IPolymesh, plane: gs.IPlane, delete_input: boolean): gs.IPolymesh { throw new Error("Method not implemented"); }