From aaab82ce518ad41fab02649fb35cc58a37425392 Mon Sep 17 00:00:00 2001 From: Ayman Habib Date: Fri, 13 Dec 2024 17:27:01 -0800 Subject: [PATCH] Use Simbody's native PolygonalMesh rather than stale copy (#3972) * Use Simbody's native PolygonalMesh rather than the old proxy we used in opensim-core * Add method that takes normal String to workaround swig failure to handle SimTK::String for pathname --- Bindings/SWIGSimTK/PolygonalMesh.h | 267 ----------------------------- Bindings/simbody.i | 8 +- 2 files changed, 7 insertions(+), 268 deletions(-) delete mode 100644 Bindings/SWIGSimTK/PolygonalMesh.h diff --git a/Bindings/SWIGSimTK/PolygonalMesh.h b/Bindings/SWIGSimTK/PolygonalMesh.h deleted file mode 100644 index 88f5622325..0000000000 --- a/Bindings/SWIGSimTK/PolygonalMesh.h +++ /dev/null @@ -1,267 +0,0 @@ -#ifndef SimTK_SimTKCOMMON_POLYGONAL_MESH_H_ -#define SimTK_SimTKCOMMON_POLYGONAL_MESH_H_ - -/* -------------------------------------------------------------------------- * - * Simbody(tm): SimTKcommon * - * -------------------------------------------------------------------------- * - * This is part of the SimTK biosimulation toolkit originating from * - * Simbios, the NIH National Center for Physics-Based Simulation of * - * Biological Structures at Stanford, funded under the NIH Roadmap for * - * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody. * - * * - * Portions Copyright (c) 2005-2017 Stanford University and the Authors. * - * Authors: Peter Eastman * - * Contributors: Michael Sherman * - * * - * Licensed under the Apache License, Version 2.0 (the "License"); you may * - * not use this file except in compliance with the License. You may obtain a * - * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - * -------------------------------------------------------------------------- */ - -#include "SimTKcommon/Simmatrix.h" -#include "SimTKcommon/internal/PrivateImplementation.h" - -namespace SimTK { - -class PolygonalMesh; -class PolygonalMeshImpl; - -// We only want the template instantiation to occur once. This symbol is defined -// in the SimTKcommon compilation unit that defines the PolygonalMesh class but -// should not be defined any other time. -#ifndef SWIG -#ifndef SimTK_SIMTKCOMMON_DEFINING_POLYGONALMESH - extern template class PIMPLHandle; -#endif -#endif - -/** This class provides a description of a mesh made of polygonal faces (not -limited to triangles). Its primary purpose is for loading geometry from files, -which can then be used for visualization or collision detection. For example, -the following lines load a mesh from a Wavefront OBJ file, then create a -DecorativeMesh from it. -@code - PolygonalMesh mesh; - mesh.loadObjFile("teapot.obj"); - DecorativeMesh decoration(mesh); -@endcode -You can also read a polygon mesh from a VTK PolyData (.vtp) file, or an STL -file (.stl) that is in ascii or binary format. You can also build meshes -programmatically, and some static methods are provided here for generating some -common shapes. If you don't know what kind of file you have, you can attempt to -read it with the loadFile() method which will examine the file extension to -determine the expected format. - -The mesh has its own local frame and vertex locations are given in that -frame. You can scale and transform the vertices relative to that frame -(changing the values stored in the mesh) but more commonly the mesh will be -placed on a body relative to that body's frame, meaning you can re-use the -same mesh in various places. - -We expect this to be a large object so give it shared (reference) semantics; -that is, the copy constructor and copy assignment default to shallow copies -(both handles will refer to the same data). If you want to make a deep -(non-shared) copy of a PolygonalMesh, use the copyAssign() method provided by -the PIMPLHandle base class. **/ -class SimTK_SimTKCOMMON_EXPORT PolygonalMesh -: public PIMPLHandle { -public: - /** Create an empty %PolygonalMesh, with no vertices or faces. **/ - PolygonalMesh() {} - - /** Create a sphere-shaped mesh, with roughly uniform mesh elements. - - @param[in] radius - The radius of the underlying sphere. Vertices of the mesh will be on - the sphere, with mesh elements somewhat inside. - @param[in] resolution - Control for how dense a mesh to produce. Resolution 0 will produce an - octahedron (8 triangular faces). Resolution 1 (the default) gives - 32 faces, resolution 2 gives 128. In general for resolution n there - will be 2*4^(n+1) faces. - @return A %PolygonalMesh representing a sphere of the specified radius. **/ - static PolygonalMesh createSphereMesh(Real radius, - int resolution = 1); - - /** Create a brick-shaped mesh. A brick is a rectangular solid (a box) - centered at and aligned with the mesh local frame. Note that its size is - given with \e half dimensions. By default you will just get two mesh faces - along the longest edge of the brick, with all other edges roughly the same - size. You can control the mesh density with the \a resolution parameter. - - @param[in] halfDims - The half-dimensions of the brick. The extreme vertices are at - -halfDims and +halfDims, so the brick is centered around the mesh - local frame. - @param[in] resolution - Control for how dense a mesh to produce. For this shape, \a resolution - is interpreted as the number of extra vertices to insert in the - \e longest edge of the brick. Extra vertices are inserted into the - shorter edges if needed to keep the edge lengths approximately - uniform for every mesh face. \a resolution=0 gives only - vertices at the corners; the default is 1 meaning that the longest - edge is split once. - @return A %PolygonalMesh representing a brick of the requested size. - -

Controlling the mesh density:

- If you want a brick mesh where all the edges in the mesh are roughly the - same length, say \c wantEdgeLength, set \a resolution like this: - @code - Real wantEdgeLength = ...; - Vec3 halfDims = ...; - int resolution = (int)(max(halfDims)/wantEdgeLength + 0.5); - @endcode - - If you want a brick mesh where all the edges are roughly the same length - as the shortest edge of the brick, just set - wantEdgeLength=min(halfDims) in the above calculation. **/ - static PolygonalMesh createBrickMesh(const Vec3& halfDims, - int resolution = 1); - - /** Create a cylinder-shaped mesh, with the long axis in a given - direction. By default you'll get a 12 sided polygon as the base and - elements of roughly similar dimension along the edges. You can control the - mesh density with the \a resolution parameter. - - @param[in] axis - The central axis direction of the cylinder, in the mesh local frame. - This can be provided using the constants XAxis, YAxis, or ZAxis, or - you can provide a unit vector in any direction. - @param[in] radius - The cylinder radius. - @param[in] halfLength - Half the length of the cylinder along its axis. The bases are at - -halfLength and +halfLength along the \a axis, so the cylinder is - centered around the mesh local frame origin. - @param[in] resolution - Control for how dense a mesh to produce (see below for details). - @return A %PolygonalMesh representing a cylinder of the requested dimensions - and orientation. - -

Controlling the mesh density:

- At resolution 0 the base is a hexagon with six triangular faces, and the - tube is meshed with quad faces that are about as long - as the diameter of the base. Resolution 1 (the default) makes the base - a 12-sided polygon and introduces an intermediate 12-sided polygon of - have the diameter. There will be triangles in the center still, but - quad faces between the polygons. The length of the tube faces will be - reduced to match. Higher resolutions refine the mesh similarly. **/ - static PolygonalMesh createCylinderMesh(const UnitVec3& axis, - Real radius, - Real halfLength, - int resolution=1); - - /** Restore this %PolygonalMesh to its default-constructed state, meaning - that it will contain no vertices or faces after this call. **/ - void clear(); - - /** Get the number of faces in the mesh. **/ - int getNumFaces() const; - /** Get the number of vertices in the mesh. **/ - int getNumVertices() const; - - /** Get the position of a vertex in the mesh. - @param[in] vertex The index of the vertex (as returned by addVertex()). - @return The position of the specified vertex, measured and expressed in - the mesh local frame. **/ - const Vec3& getVertexPosition(int vertex) const; - /** Get the number of vertices that make up a particular face. - @param[in] face The index of the face (as returned by addFace()). **/ - int getNumVerticesForFace(int face) const; - /** Get the index of one of the vertices of a face. - @param[in] face The index of the face (as returned by addFace()). - @param[in] vertex The index of the vertex within the face (from 0, 1, or 2 - for a triangular face, etc.) These are ordered the same - way as when the face was defined. - @return The index of the specified vertex. **/ - int getFaceVertex(int face, int vertex) const; - - /** Add a vertex to the mesh. - @param[in] position The position of the vertex to add, measured and - expressed in the mesh local frame. - @return The index of the newly added vertex. **/ - int addVertex(const Vec3& position); - - /** Add a face to the mesh. Note that the ordering of the vertices defines - the outward normal for the face; they must be counterclockwise around the - desired normal. - - @param[in] vertices Indices of the vertices which make up the new face, - in counterclockwise order with respect to the face - normal. - @return The index of the newly added face. **/ - int addFace(const Array_& vertices); - - /** Scale a mesh by multiplying every vertex by a fixed value. Note that - this permanently modifies the vertex locations within the mesh. Since the - vertices are measured in the mesh local frame, scaling will appear to - occur around the mesh origin (that is, the origin will remain where it - was while everything else changes. - @param[in] scale The scale factor. Can be any value except zero. - @return A reference to this now-scaled mesh object. **/ - PolygonalMesh& scaleMesh(Real scale); - - /** %Transform a mesh by applying the given Transform to every vertex, - leaving the mesh permanently changed. This has the effect of replacing the - mesh local frame M with a new frame A. - @param[in] X_AM The transform giving the pose of the mesh local frame in - the new frame A. Every vertex v_M becomes v_A=X_AM*v_M. - @return A reference to this now-transformed mesh object. **/ - PolygonalMesh& transformMesh(const Transform& X_AM); - - /** Attempt to interpret the given file as a mesh file, with the format - determined from the file name extension. If we recognize the extension - we'll call one of the specialized methods below; see the descriptions for - more information. Ignoring case, we recognize: - - .obj : Wavefront OBJ file - - .stl : 3D Systems Stereolithography file (ascii or binary) - - .stla: ascii-only stl extension - - .vtp : VTK PolyData file (we can only read the ascii version) - - @param[in] pathname The name of a mesh file with a recognized extension. - **/ - void loadFile(const std::string& pathname); -#ifndef SWIG - /** Load a Wavefront OBJ (.obj) file, adding the vertices and faces it - contains to this mesh, and ignoring anything else in the file. The suffix - for these files is typically ".obj" but we don't check here. - @param[in] pathname The name of a .obj file. **/ - void loadObjFile(const String& pathname); - - /** Alternate signature for Wavefront OBJ format that takes an already-open - istream rather than a pathname. This is useful for testing since it - can be supplied by a stringstream rather than a file. - @param[in,out] file An input stream from which to load the file - contents. **/ - void loadObjFile(std::istream& file); - - /** Load a VTK PolyData (.vtp) file, adding the vertices and faces it - contains to this mesh and ignoring anything else in the file. The suffix - for these files is typically ".vtp" but we don't check here. - @param[in] pathname The name of a .vtp file. **/ - void loadVtpFile(const String& pathname); - - /** Load an STL file, adding the vertices and faces it contains to this - mesh and ignoring anything else in the file. The file may be in ascii or - binary format. If the suffix is ".stla" then it can only be ascii. - Otherwise, including ".stl" or anything else, we'll examine the contents to - determine which format is used. STL files include many repeated vertices; - we will collapse any that coincide to within a small tolerance so that there - is some hope of getting a connected surface. - @param[in] pathname The name of a .stl or .stla file. **/ - void loadStlFile(const String& pathname); -#endif -private: - explicit PolygonalMesh(PolygonalMeshImpl* impl) : HandleBase(impl) {} - void initializeHandleIfEmpty(); -}; - -} // namespace SimTK - -#endif // SimTK_SimTKCOMMON_POLYGONAL_MESH_H_ diff --git a/Bindings/simbody.i b/Bindings/simbody.i index 7911de25a9..c3211e1f68 100644 --- a/Bindings/simbody.i +++ b/Bindings/simbody.i @@ -305,7 +305,7 @@ namespace SimTK { %template(ArrayIndexInt) ArrayIndexTraits; } -%include +%include %include @@ -354,5 +354,11 @@ namespace SimTK { return key; } } +%extend PolygonalMesh { + void loadFile(const std::string& pathname) { + // SWIG cant handle SimTK::String despite autoconversion + $self->loadFile(pathname); + } +} }