UNPKG

gs-modelling

Version:

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

307 lines (282 loc) 12.4 kB
/** * Functions for working with polymehes. */ /** * Polymeshes are geometric objects that can have multiple faces and multiple closed wires. * Faces define the surfaces in the polymesh. They can have three or more vertices, * they can be concave or convex, and planar or non-planar. * Wires define the naked edges, i.e. edges without any neighbours. * The wires in a polymesh are calculated automatically based on the connectivity between the faces. * The edges are straight line segments joining two vertices. * The polymesh can be either a closed or an open. A closed polymesh has no wires. */ 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 * as error from "./_error_msgs_dev"; import {Arr} from "./libs/arr/arr"; // =============================================================================================================== // Pmesh Get ===================================================================================================== // =============================================================================================================== /** * Gets a polymesh from the model based on an ID number. * In the viewer, the object label can display (it starts with 'o'), which contains the ID. * For example, if the label is "o123", then the ID is the number 123. * * @param model Model to get polymesh from. * @param id ID number of polymesh. * @returns Polymesh object. */ export function Get(model: gs.IModel, id: number): gs.IPolymesh { const obj: gs.IObj = error.checkObjID(model, id, gs.EObjType.polymesh); return obj as gs.IPolymesh; } /** * Create a copy of a polymesh. * * @param polymesh The polymesh to copy. * @param copy_attribs If true, attributes are copied to the new circle. * @returns Polymesh object. */ export function Copy(polymesh: gs.IPolymesh, copy_attribs?: boolean): gs.IPolymesh { error.checkObj(polymesh, gs.EObjType.polymesh); return polymesh.copy(copy_attribs) as gs.IPolymesh; } /** * Copies a polymesh from one model into another model. * * @param model The model to copy to. * @param polymesh The polymesh object to copy. * @returns The copied polymesh object in the model. */ export function CopyToModel(model: gs.IModel, polymesh: gs.IPolymesh): gs.IPolymesh { error.checkObj(polymesh, gs.EObjType.polymesh); if (polymesh.getModel() === model) {throw new Error("Error: polymesh is already in model.");} //return model.getGeom().copyPolymeshFromModel(polymesh); throw new Error("Function not implemented yet.") } // =============================================================================================================== // Pmesh Constructors ============================================================================================ // =============================================================================================================== /** * Creates a polymesh from a list of lists of face corner points. * For example [[p1, p2, p3], [p3, p2, p4]] would create a polymesh with two triangular faces. * In this example, the two faces share points p2 and p3. * * @param points List of lists of face corner points. * @returns Polymesh object. */ export function FromPoints(points: gs.IPoint[][]): gs.IPolymesh { const model: gs.IModel = error.checkPointNestedList(points, 1, 3); return model.getGeom().addPolymesh(points); } /** * Creates a polymesh from a polyline. The polymesh will have a single face. * * @param pline Polyline object to create the polymesh from. * @returns Polymesh object with single face. */ export function FromPline(pline: gs.IPolyline): gs.IPolymesh { const model: gs.IModel = error.checkObj(pline, gs.EObjType.polyline); return model.getGeom().addPolymesh([pline.getPointsArr()]); } /** * Creates a triangulated mesh from two lists of points. * For example [[p1, p2, p3], [p4, p5, p6]] would create a polymesh with four triangular faces. * The triangles would be as follows: [p1, p2, p4], [p5, p4, p1], [p2, p3, p5], [p6, p5, p3]. * In this example, the two faces share points p2 and p3. * * @param points1 The first list of points. * @param points2 The second list of points. * @returns Polymesh object. */ export function TriStripFromPoints(points1: gs.IPoint[], points2: gs.IPoint[]): gs.IPolymesh { const model: gs.IModel = error.checkPointList(points1, 2); error.checkPointList(points2, 2); // sort, shortest list first const points: gs.IPoint[][] = []; if (points1.length < points2.length) { points[0] = points1; points[1] = points2; } else { points[0] = points2; points[1] = points1; } // create quads, the split into two triangles along shortest diagonal const tri_points: gs.IPoint[][] = []; for (let i = 0; i < points[0].length - 1; i++) { const p0: gs.IPoint = points[0][i]; const p1: gs.IPoint = points[0][i + 1]; const p2: gs.IPoint = points[1][i + 1]; const p3: gs.IPoint = points[1][i]; if (threex.distSquPointToPoint(p1, p3) < threex.distSquPointToPoint(p0, p2)) { tri_points.push([p0, p1, p3]); tri_points.push([p1, p2, p3]); } else { tri_points.push([p0, p1, p2]); tri_points.push([p0, p2, p3]); } } // add triangles for the remainder for (let i = points[0].length - 1; i < points[1].length - 1; i++) { const p0: gs.IPoint = points[1][i + 1]; const p1: gs.IPoint = points[1][i]; const p2: gs.IPoint = points[0][points[0].length - 1]; tri_points.push([p0, p1 , p2]); } // generate mesh and return return model.getGeom().addPolymesh(tri_points); } // =============================================================================================================== // Pmesh Simple Functions =============================================================================================== // =============================================================================================================== /** * Checks if the polymesh is closed. * * @param pmesh Polymesh object. * @return True if the polymesh is closed. */ export function isClosed(pmesh: gs.IPolymesh): boolean { error.checkObj(pmesh, gs.EObjType.polymesh); return pmesh.numWires() === 0; } /** * Get the number of faces in a polymesh. * * @param pmesh Polymesh object. * @return The number of faces. */ export function numFaces(pmesh: gs.IPolymesh): number { error.checkObj(pmesh, gs.EObjType.polymesh); return pmesh.numFaces(); } /** * Get the number of wires in a polymesh. * * @param pmesh Polymesh object. * @return The number of wires. */ export function numWires(pmesh: gs.IPolymesh): number { error.checkObj(pmesh, gs.EObjType.polymesh); return pmesh.numWires(); } /** * Get the number of edges in a polymesh, for both the wires and the faces. * * @param pmesh Polymesh object. * @return List of two numbers, number of wire edges and number of face edges. */ export function numEdges(pmesh: gs.IPolymesh): [number, number] { error.checkObj(pmesh, gs.EObjType.polymesh); return [pmesh.numWireEdges(), pmesh.numFaceEdges()]; } /** * Get the number of vertices in the polymesh, for both the wires and the faces. * * @param pmesh Polymesh object. * @return List of two numbers, number of wire vertices and number of face vertices. */ export function numVertices(pmesh: gs.IPolymesh): [number, number] { error.checkObj(pmesh, gs.EObjType.polymesh); return [pmesh.numWireVertices(), pmesh.numFaceVertices()]; } /** * Get all points in a polymesh. The sequence of points is in face order. * * @param pmesh Polymesh object. * @return List of points. */ export function getPoints(pmesh: gs.IPolymesh): gs.IPoint[] { error.checkObj(pmesh, gs.EObjType.polymesh); return Arr.flatten(pmesh.getPoints(gs.EGeomType.faces)); } // =============================================================================================================== // Pmesh Modelling Functions =============================================================================================== // =============================================================================================================== /** * Create a new polymesh by extruding an existing polymesh by a specified vector. * The original polymesh is not modified. * * New points are created by translating the existing points by the specified vector. * Top and bottom faces are created. * Four-sided faces are the created between the original and new points. * The faces are joined to create a polymesh. * * @param pmesh 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 extrude(pmesh: gs.IPolymesh, vector: gs.XYZ): gs.IPolymesh { // check args const model: gs.IModel = error.checkObj(pmesh, gs.EObjType.polymesh); error.checkXYZ(vector); // make a copy const pmesh1_points: gs.IPoint[][][] = pmesh.getPoints(); const pmesh2_points: gs.IPoint[][][] = utils.copyObjPoints(pmesh, false); threex.movePointsAddXYZ(Arr.flatten(pmesh2_points), vector); // create the sides const sides: gs.IPoint[][] = poly.pointsLoftLoop([pmesh1_points[0], pmesh2_points[0]], true); // combine everything const pmesh_points = [...pmesh1_points[1], ...pmesh2_points[1], ...sides]; // return the new polymesh return model.getGeom().addPolymesh(pmesh_points); } /** * Explodes a polymesh into smaller polymeshes, each with only one face. * The original polymesh is not modified. * * @param pmesh Polymesh to explode. * @returns List of polymeshes. */ export function explode(pmesh: gs.IPolymesh): gs.IPolymesh[] { error.checkObj(pmesh, gs.EObjType.polymesh); return this.extractFaces(pmesh, Arr.makeSeq(pmesh.numFaces())); } /** * Creates new set of polymeshes by extracting faces from an existing polymesh. * The original polymesh is not modified. * * The individual polymeshes are not joined. * * @param pmesh Polymesh to extract faces from * @param face_index Index numbers of polymesh faces to extract. * @returns List of new polymeshes. */ export function extractFaces(pmesh: gs.IPolymesh, face_index: number[]): gs.IPolymesh[] { const m: gs.IModel = error.checkObj(pmesh, gs.EObjType.polymesh); error.checkPosNums(face_index); // do the extraction const new_pmeshes: gs.IPolymesh[] = []; const faces: gs.IFace[] = pmesh.getFaces(); for (const i of face_index) { if (i >= faces.length) {throw new Error("Face index exceeds the number of faces.");} const points: gs.IPoint[] = faces[i].getVertices().map((v) => v.getPoint()); new_pmeshes.push(m.getGeom().addPolymesh([points])); } return new_pmeshes; } /** * Creates new set of polylines by extracting wires from an existing polymesh. * The original polymesh is not modified. * * Wires are the naked edges of a polymesh. They are always closed loops. * * @param pmesh Polymesh to extract wires from. * @param wire_index Index numbers of polymesh wires to extract. * @returns List of new polylines. */ export function extractWires(pmesh: gs.IPolymesh, wire_index: number[]): gs.IPolyline[] { const m: gs.IModel = error.checkObj(pmesh, gs.EObjType.polymesh); error.checkPosNums(wire_index); // do the extraction const new_plines: gs.IPolyline[] = []; const wires: gs.IWire[] = pmesh.getWires(); for (const i of wire_index) { if (i >= wires.length) {throw new Error("Face index exceeds the number of faces.");} const points: gs.IPoint[] = wires[i].getVertices().map((v) => v.getPoint()); new_plines.push(m.getGeom().addPolyline(points, true)); } return new_plines; }