From fc1e9a0706eac14c669b91c24a58b19bf4ced615 Mon Sep 17 00:00:00 2001 From: K Date: Mon, 18 Dec 2023 16:27:31 -0500 Subject: [PATCH 1/8] python autogen docstrings --- bindings/python/docstrings.inl | 229 +++++++++++++++++++++++++++++++++ bindings/python/gen_docs.py | 114 ++++++++++++++++ bindings/python/manifold3d.cpp | 62 ++------- 3 files changed, 351 insertions(+), 54 deletions(-) create mode 100644 bindings/python/docstrings.inl create mode 100644 bindings/python/gen_docs.py diff --git a/bindings/python/docstrings.inl b/bindings/python/docstrings.inl new file mode 100644 index 000000000..30884135e --- /dev/null +++ b/bindings/python/docstrings.inl @@ -0,0 +1,229 @@ +#pragma once +namespace manifold_docs { +const auto manifold_cube = R"___()___"; +const auto manifold_manifold = R"___(Construct an empty Manifold.)___"; +const auto manifold_manifold_1 = R"___()___"; +const auto manifold_operator_eq = R"___()___"; +const auto manifold_manifold_2 = R"___()___"; +const auto manifold_manifold_3 = R"___()___"; +const auto manifold_manifold_4 = R"___()___"; +const auto manifold_invalid = R"___()___"; +const auto manifold_operator_eq_1 = R"___()___"; +const auto manifold_get_csg_leaf_node = R"___()___"; +const auto manifold_manifold_5 = R"___(Convert a MeshGL into a Manifold, retaining its properties and merging only +the positions according to the merge vectors. Will return an empty Manifold +and set an Error Status if the result is not an oriented 2-manifold. Will +collapse degenerate triangles and unnecessary vertices. +All fields are read, making this structure suitable for a lossless round-trip +of data from GetMeshGL. For multi-material input, use ReserveIDs to set a +unique originalID for each material, and sort the materials into triangle +runs. +:param mesh_gl: The input MeshGL. +:param property_tolerance: A vector of precision values for each property +beyond position. If specified, the propertyTolerance vector must have size = +numProp - 3. This is the amount of interpolation error allowed before two +neighboring triangles are considered to be on a property boundary edge. +Property boundary edges will be retained across operations even if the +triangles are coplanar. Defaults to 1e-5, which works well for most +properties in the [-1, 1] range.)___"; +const auto manifold_manifold_6 = R"___(Convert a Mesh into a Manifold. Will return an empty Manifold +and set an Error Status if the Mesh is not an oriented 2-manifold. Will +collapse degenerate triangles and unnecessary vertices. +:param mesh: The input Mesh.)___"; +const auto manifold_get_mesh = R"___(This returns a Mesh of simple vectors of vertices and triangles suitable for +saving or other operations outside of the context of this library.)___"; +const auto manifold_get_mesh_gl = R"___(The most complete output of this library, returning a MeshGL that is designed +to easily push into a renderer, including all interleaved vertex properties +that may have been input. It also includes relations to all the input meshes +that form a part of this result and the transforms applied to each. +:param normal_idx: If the original MeshGL inputs that formed this manifold had +properties corresponding to normal vectors, you can specify which property +channels these are (x, y, z), which will cause this output MeshGL to +automatically update these normals according to the applied transforms and +front/back side. Each channel must be >= 3 and < numProp, and all original +MeshGLs must use the same channels for their normals.)___"; +const auto manifold_is_empty = R"___(Does the Manifold have any triangles?)___"; +const auto manifold_status = R"___(Returns the reason for an input Mesh producing an empty Manifold. This Status +only applies to Manifolds newly-created from an input Mesh - once they are +combined into a new Manifold via operations, the status reverts to NoError, +simply processing the problem mesh as empty. Likewise, empty meshes may still +show NoError, for instance if they are small enough relative to their +precision to be collapsed to nothing.)___"; +const auto manifold_num_vert = R"___(The number of vertices in the Manifold.)___"; +const auto manifold_num_edge = R"___(The number of edges in the Manifold.)___"; +const auto manifold_num_tri = R"___(The number of triangles in the Manifold.)___"; +const auto manifold_num_prop = R"___(The number of properties per vertex in the Manifold.)___"; +const auto manifold_num_prop_vert = R"___(The number of property vertices in the Manifold. This will always be >= +NumVert, as some physical vertices may be duplicated to account for different +properties on different neighboring triangles.)___"; +const auto manifold_bounding_box = R"___(Returns the axis-aligned bounding box of all the Manifold's vertices.)___"; +const auto manifold_precision = R"___(Returns the precision of this Manifold's vertices, which tracks the +approximate rounding error over all the transforms and operations that have +led to this state. Any triangles that are colinear within this precision are +considered degenerate and removed. This is the value of ε defining +[ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid).)___"; +const auto manifold_genus = R"___(The genus is a topological property of the manifold, representing the number +of "handles". A sphere is 0, torus 1, etc. It is only meaningful for a single +mesh, so it is best to call Decompose() first.)___"; +const auto manifold_get_properties = R"___(Returns the surface area and volume of the manifold.)___"; +const auto manifold_original_id = R"___(If this mesh is an original, this returns its meshID that can be referenced +by product manifolds' MeshRelation. If this manifold is a product, this +returns -1.)___"; +const auto manifold_as_original = R"___(This function condenses all coplanar faces in the relation, and +collapses those edges. In the process the relation to ancestor meshes is lost +and this new Manifold is marked an original. Properties are preserved, so if +they do not match across an edge, that edge will be kept.)___"; +const auto manifold_reserve_ids = R"___(Returns the first of n sequential new unique mesh IDs for marking sets of +triangles that can be looked up after further operations. Assign to +MeshGL.runOriginalID vector.)___"; +const auto manifold_impl__reserve_ids = R"___()___"; +const auto manifold_matches_tri_normals = R"___(The triangle normal vectors are saved over the course of operations rather +than recalculated to avoid rounding error. This checks that triangles still +match their normal vectors within Precision().)___"; +const auto manifold_num_degenerate_tris = R"___(The number of triangles that are colinear within Precision(). This library +attempts to remove all of these, but it cannot always remove all of them +without changing the mesh by too much.)___"; +const auto manifold_num_overlaps = R"___(This is a checksum-style verification of the collider, simply returning the +total number of edge-face bounding box overlaps between this and other. +:param other: A Manifold to overlap with.)___"; +const auto manifold_translate = R"___(Move this Manifold in space. This operation can be chained. Transforms are +combined and applied lazily. +:param v: The vector to add to every vertex.)___"; +const auto manifold_scale = R"___(Scale this Manifold in space. This operation can be chained. Transforms are +combined and applied lazily. +:param v: The vector to multiply every vertex by per component.)___"; +const auto manifold_rotate = R"___(Applies an Euler angle rotation to the manifold, first about the X axis, then +Y, then Z, in degrees. We use degrees so that we can minimize rounding error, +and eliminate it completely for any multiples of 90 degrees. Additionally, +more efficient code paths are used to update the manifold when the transforms +only rotate by multiples of 90 degrees. This operation can be chained. +Transforms are combined and applied lazily. +:param x_degrees: First rotation, degrees about the X-axis. +:param y_degrees: Second rotation, degrees about the Y-axis. +:param z_degrees: Third rotation, degrees about the Z-axis.)___"; +const auto manifold_transform = R"___(Transform this Manifold in space. The first three columns form a 3x3 matrix +transform and the last is a translation vector. This operation can be +chained. Transforms are combined and applied lazily. +:param m: The affine transform matrix to apply to all the vertices.)___"; +const auto manifold_mirror = R"___(Mirror this Manifold over the plane described by the unit form of the given +normal vector. If the length of the normal is zero, an empty Manifold is +returned. This operation can be chained. Transforms are combined and applied +lazily. +:param normal: The normal vector of the plane to be mirrored over)___"; +const auto manifold_warp = R"___(This function does not change the topology, but allows the vertices to be +moved according to any arbitrary input function. It is easy to create a +function that warps a geometrically valid object into one which overlaps, but +that is not checked here, so it is up to the user to choose their function +with discretion. +:param warp_func: A function that modifies a given vertex position.)___"; +const auto manifold_warp_batch = R"___(Same as Manifold::Warp but calls warpFunc with with +a VecView which is roughly equivalent to std::span +pointing to all vec3 elements to be modified in-place +:param warp_func: A function that modifies multiple vertex positions.)___"; +const auto manifold_set_properties = R"___(Create a new copy of this manifold with updated vertex properties by +supplying a function that takes the existing position and properties as +input. You may specify any number of output properties, allowing creation and +removal of channels. Note: undefined behavior will result if you read past +the number of input properties or write past the number of output properties. +:param num_prop: The new number of properties per vertex. +:param prop_func: A function that modifies the properties of a given vertex.)___"; +const auto manifold_calculate_curvature = R"___(Curvature is the inverse of the radius of curvature, and signed such that +positive is convex and negative is concave. There are two orthogonal +principal curvatures at any point on a manifold, with one maximum and the +other minimum. Gaussian curvature is their product, while mean +curvature is their sum. This approximates them for every vertex and assigns +them as vertex properties on the given channels. +:param gaussian_idx: The property channel index in which to store the Gaussian +curvature. An index < 0 will be ignored (stores nothing). The property set +will be automatically expanded to include the channel index specified. +:param mean_idx: The property channel index in which to store the mean +curvature. An index < 0 will be ignored (stores nothing). The property set +will be automatically expanded to include the channel index specified.)___"; +const auto manifold_refine = R"___(Increase the density of the mesh by splitting every edge into n pieces. For +instance, with n = 2, each triangle will be split into 4 triangles. These +will all be coplanar (and will not be immediately collapsed) unless the +Mesh/Manifold has halfedgeTangents specified (e.g. from the Smooth() +constructor), in which case the new vertices will be moved to the +interpolated surface according to their barycentric coordinates. +:param n: The number of pieces to split every edge into. Must be > 1.)___"; +const auto manifold_boolean = R"___(The central operation of this library: the Boolean combines two manifolds +into another by calculating their intersections and removing the unused +portions. +[ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid) +inputs will produce ε-valid output. ε-invalid input may fail +triangulation. +These operations are optimized to produce nearly-instant results if either +input is empty or their bounding boxes do not overlap. +:param second: The other Manifold. +:param op: The type of operation to perform.)___"; +const auto manifold_batch_boolean = R"___()___"; +const auto manifold_operator_plus = R"___(Shorthand for Boolean Union.)___"; +const auto manifold_operator_plus_eq = R"___(Shorthand for Boolean Union assignment.)___"; +const auto manifold_operator_minus = R"___(Shorthand for Boolean Difference.)___"; +const auto manifold_operator_minus_eq = R"___(Shorthand for Boolean Difference assignment.)___"; +const auto manifold_operator_xor = R"___(Shorthand for Boolean Intersection.)___"; +const auto manifold_operator_xor_eq = R"___(Shorthand for Boolean Intersection assignment.)___"; +const auto manifold_split = R"___(Split cuts this manifold in two using the cutter manifold. The first result +is the intersection, second is the difference. This is more efficient than +doing them separately. +:param cutter:)___"; +const auto manifold_split_by_plane = R"___(Convenient version of Split() for a half-space. +:param normal: This vector is normal to the cutting plane and its length does +not matter. The first result is in the direction of this vector, the second +result is on the opposite side. +:param origin_offset: The distance of the plane from the origin in the +direction of the normal vector.)___"; +const auto manifold_trim_by_plane = R"___(Identical to SplitByPlane(), but calculating and returning only the first +result. +:param normal: This vector is normal to the cutting plane and its length does +not matter. The result is in the direction of this vector from the plane. +:param origin_offset: The distance of the plane from the origin in the +direction of the normal vector.)___"; +const auto manifold_slice = R"___(Returns the cross section of this object parallel to the X-Y plane at the +specified Z height, defaulting to zero. Using a height equal to the bottom of +the bounding box will return the bottom faces, while using a height equal to +the top of the bounding box will return empty.)___"; +const auto manifold_project = R"___(Returns a cross section representing the projected outline of this object +onto the X-Y plane.)___"; +const auto manifold_hull = R"___(Compute the convex hull of a set of points. If the given points are fewer +than 4, or they are all coplanar, an empty Manifold will be returned. +:param pts: A vector of 3-dimensional points over which to compute a convex +hull.)___"; +const auto manifold_hull_1 = R"___(Compute the convex hull of this manifold.)___"; +const auto manifold_hull_2 = R"___(Compute the convex hull enveloping a set of manifolds. +:param manifolds: A vector of manifolds over which to compute a convex hull.)___"; +const auto triangulate = R"___(@brief Triangulates a set of ε-valid polygons. If the input is not +ε-valid, the triangulation may overlap, but will always return a +manifold result that matches the input edge directions. +:param polygons: The set of polygons, wound CCW and representing multiple +polygons and/or holes. +:param precision: The value of ε, bounding the uncertainty of the +input. +@return std::vector The triangles, referencing the original +polygon points in order.)___"; +const auto triangulate_poly = R"___()___"; +const auto triangulate_idx = R"___()___"; +const auto set_min_circular_angle = R"___(Sets an angle constraint the default number of circular segments for the +CrossSection::circle, Manifold::cylinder, Manifold::sphere, and +Manifold::revolve constructors. The number of segments will be rounded up +to the nearest factor of four. +:param angle: The minimum angle in degrees between consecutive segments. The +angle will increase if the the segments hit the minimum edge length. +Default is 10 degrees.)___"; +const auto set_min_circular_edge_length = R"___(Sets a length constraint the default number of circular segments for the +CrossSection::circle, Manifold::cylinder, Manifold::sphere, and +Manifold::revolve constructors. The number of segments will be rounded up +to the nearest factor of four. +:param length: The minimum length of segments. The length will +increase if the the segments hit the minimum angle. Default is 1.0.)___"; +const auto set_circular_segments = R"___(Sets the default number of circular segments for the +CrossSection::circle, Manifold::cylinder, Manifold::sphere, and +Manifold::revolve constructors. Overrides the edge length and angle +constraints and sets the number of segments to exactly this value. +:param number: Number of circular segments. Default is 0, meaning no +constraint is applied.)___"; +const auto get_circular_segments = R"___(Determine the result of the set_min_circular_angle, +set_min_circular_edge_length, and set_circular_segments defaults. +:param radius: For a given radius of circle, determine how many default +segments there will be.)___"; +} // namespace manifold_docs \ No newline at end of file diff --git a/bindings/python/gen_docs.py b/bindings/python/gen_docs.py new file mode 100644 index 000000000..a260bfada --- /dev/null +++ b/bindings/python/gen_docs.py @@ -0,0 +1,114 @@ + +# %% +from os.path import dirname +import re + +base = dirname(dirname(dirname(__file__))) + +manifold_h = f'{base}/src/manifold/include/manifold.h' +public_h = f'{base}/src/utilities/include/public.h' +polygon_cpp = f'{base}/src/polygon/src/polygon.cpp' +manifold_cpp = f'{base}/src/manifold/src/manifold.cpp' + +method_re = re.compile(r'Manifold::([\w\-\+\^\=\:]+)\(') +param_re = re.compile(r'@param (\w+)') +comments = {} + +def snake_case(name): + # name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) + return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower() + +def method_key(name): + name = re.sub('\+', '_plus', name) + name = re.sub('\-', '_minus', name) + name = re.sub('\^', '_xor', name) + name = re.sub('\=', '_eq', name) + name = re.sub('\:', '_', name) + name = re.sub('\~', 'destroy_', name) + return name + +comment = '' +with open(manifold_cpp) as f: + for line in f: + line = line.lstrip() + if line.startswith('/**'): + comment = '' + elif line.startswith('*/'): + pass + elif line.startswith('*') and comment is not None: + comment += line[1:].lstrip() + elif m := method_re.search(line): + method = 'manifold_'+method_key(snake_case(m[1])) + splices = [] + last = 0 + for m in param_re.finditer(comment): + b, e = m.span() + var = snake_case(m[1]) + splices += [ + comment[last:b], + f':param {var}:' + ] + last = e + splices += [comment[last:]] + comment = ''.join(splices) + orig_method = method + n = 1 + while method in comments: + method = f'{orig_method}_{n}' + n += 1 + comments[method] = comment + comment = '' + +comments +# %% + +# comments = {} + +qual_re = re.compile(r'([\w\-\+\^\=\:]+)\(') +comment = '' +patterns = ["Circular", "Triangulate"] +for fname in [polygon_cpp, public_h]: + with open(fname) as f: + for line in f: + line = line.lstrip() + if line.startswith('/**'): + comment = '' + elif line.startswith('*/'): + pass + elif line.startswith('*') and comment is not None: + comment += line[1:].lstrip() + elif m := qual_re.search(line): + allow = sum(p in line for p in patterns) + if allow: + method = method_key(snake_case(m[1])) + splices = [] + last = 0 + for m in param_re.finditer(comment): + b, e = m.span() + var = snake_case(m[1]) + splices += [ + comment[last:b], + f':param {var}:' + ] + last = e + splices += [comment[last:]] + comment = ''.join(splices) + + def re_snake_case(m): + return (m[1] or '')+snake_case(m[2]) + comment = re.sub(r'(\w+::)*(\w+)\(\)', re_snake_case, comment) + + comments[method] = comment + comment = '' + +comments +# %% + +gen_h = f'{base}/bindings/python/docstrings.inl' +with open(gen_h, 'w') as f: + f.write('#pragma once\n') + f.write('namespace manifold_docs {\n') + for key, doc in comments.items(): + f.write(f'const auto {key} = R"___({doc.strip()})___";\n') + f.write('} // namespace manifold_docs') +# %% diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 6e0bd1357..92dbe2649 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -16,6 +16,7 @@ #include #include "cross_section.h" +#include "docstrings.inl" #include "manifold.h" #include "nanobind/nanobind.h" #include "nanobind/ndarray.h" @@ -206,73 +207,26 @@ NB_MODULE(manifold3d, m) { m.doc() = "Python binding for the Manifold library."; m.def("set_min_circular_angle", Quality::SetMinCircularAngle, - nb::arg("angle"), - "Sets an angle constraint the default number of circular segments for " - "the CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), " - "and Manifold::Revolve() constructors. The number of segments will be " - "rounded up to the nearest factor of four." - "\n\n" - ":param angle: The minimum angle in degrees between consecutive " - "segments. The angle will increase if the the segments hit the minimum " - "edge length.\n" - "Default is 10 degrees."); + nb::arg("angle"), manifold_docs::set_min_circular_angle); m.def("set_min_circular_edge_length", Quality::SetMinCircularEdgeLength, - nb::arg("length"), - "Sets a length constraint the default number of circular segments for " - "the CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), " - "and Manifold::Revolve() constructors. The number of segments will be " - "rounded up to the nearest factor of four." - "\n\n" - ":param length: The minimum length of segments. The length will " - "increase if the the segments hit the minimum angle. Default is 1.0."); + nb::arg("length"), manifold_docs::set_min_circular_edge_length); m.def("set_circular_segments", Quality::SetCircularSegments, - nb::arg("number"), - "Sets the default number of circular segments for the " - "CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and " - "Manifold::Revolve() constructors. Overrides the edge length and angle " - "constraints and sets the number of segments to exactly this value." - "\n\n" - ":param number: Number of circular segments. Default is 0, meaning no " - "constraint is applied."); + nb::arg("number"), manifold_docs::set_circular_segments); m.def("get_circular_segments", Quality::GetCircularSegments, - nb::arg("radius"), - "Determine the result of the SetMinCircularAngle(), " - "SetMinCircularEdgeLength(), and SetCircularSegments() defaults." - "\n\n" - ":param radius: For a given radius of circle, determine how many " - "default"); + nb::arg("radius"), manifold_docs::get_circular_segments); m.def("triangulate", &Triangulate, nb::arg("polygons"), nb::arg("precision") = -1, // TODO document - "Given a list polygons (each polygon shape=(N,2) dtype=float), " - "returns the indices of the triangle vertices as a " - "numpy.ndarray(shape=(N,3), dtype=np.uint32)."); + manifold_docs::triangulate); nb::class_(m, "Manifold") - .def(nb::init<>(), "Construct empty Manifold object") + .def(nb::init<>(), manifold_docs::manifold_manifold) .def(nb::init &>(), nb::arg("mesh"), nb::arg("property_tolerance") = nb::list(), - "Convert a MeshGL into a Manifold, retaining its properties and " - "merging onlythe positions according to the merge vectors. Will " - "return an empty Manifoldand set an Error Status if the result is " - "not an oriented 2-manifold. Willcollapse degenerate triangles and " - "unnecessary vertices.\n\n" - "All fields are read, making this structure suitable for a lossless " - "round-tripof data from GetMeshGL. For multi-material input, use " - "ReserveIDs to set aunique originalID for each material, and sort " - "the materials into triangleruns.\n\n" - ":param meshGL: The input MeshGL.\n" - ":param propertyTolerance: A vector of precision values for each " - "property beyond position. If specified, the propertyTolerance " - "vector must have size = numProp - 3. This is the amount of " - "interpolation error allowed before two neighboring triangles are " - "considered to be on a property boundary edge. Property boundary " - "edges will be retained across operations even if thetriangles are " - "coplanar. Defaults to 1e-5, which works well for most properties " - "in the [-1, 1] range.") + manifold_docs::manifold_manifold_5) .def(nb::self + nb::self, "Boolean union.") .def(nb::self - nb::self, "Boolean difference.") .def(nb::self ^ nb::self, "Boolean intersection.") From 14baa5521fd32f30e71eb263f574a85535b6c900 Mon Sep 17 00:00:00 2001 From: K Date: Mon, 18 Dec 2023 19:09:00 -0500 Subject: [PATCH 2/8] use function signature hashes in docstring key --- bindings/python/docstrings.inl | 569 +++++++++++++++++++++++---------- bindings/python/gen_docs.py | 122 +++---- bindings/python/manifold3d.cpp | 534 +++++++------------------------ 3 files changed, 561 insertions(+), 664 deletions(-) diff --git a/bindings/python/docstrings.inl b/bindings/python/docstrings.inl index 30884135e..e37dfdce0 100644 --- a/bindings/python/docstrings.inl +++ b/bindings/python/docstrings.inl @@ -1,16 +1,223 @@ #pragma once +#include namespace manifold_docs { -const auto manifold_cube = R"___()___"; -const auto manifold_manifold = R"___(Construct an empty Manifold.)___"; -const auto manifold_manifold_1 = R"___()___"; -const auto manifold_operator_eq = R"___()___"; -const auto manifold_manifold_2 = R"___()___"; -const auto manifold_manifold_3 = R"___()___"; -const auto manifold_manifold_4 = R"___()___"; -const auto manifold_invalid = R"___()___"; -const auto manifold_operator_eq_1 = R"___()___"; -const auto manifold_get_csg_leaf_node = R"___()___"; -const auto manifold_manifold_5 = R"___(Convert a MeshGL into a Manifold, retaining its properties and merging only +const auto cross_section__area_f5458809be32 = R"___(Return the total area covered by complex polygons making up the +CrossSection.)___"; +const auto cross_section__batch_boolean_d17f25dd9195 = R"___(Perform the given boolean operation on a list of CrossSections. In case of +Subtract, all CrossSections in the tail are differenced from the head.)___"; +const auto cross_section__boolean_d0a145760f3d = R"___(Perform the given boolean operation between this and another CrossSection.)___"; +const auto cross_section__bounds_47149b83cada = R"___(Returns the axis-aligned bounding rectangle of all the CrossSections' +vertices.)___"; +const auto cross_section__circle_72e80d0e2b44 = R"___(Constructs a circle of a given radius. +:param radius: Radius of the circle. Must be positive. +:param circular_segments: Number of segments along its diameter. Default is +calculated by the static Quality defaults according to the radius.)___"; +const auto cross_section__compose_1a076380de50 = R"___(Construct a CrossSection from a vector of other CrossSections (batch +boolean union).)___"; +const auto cross_section__cross_section_138b32c68b57 = R"___(Create a 2d cross-section from an axis-aligned rectangle (bounding box). +:param rect: An axis-aligned rectangular bounding box.)___"; +const auto cross_section__cross_section_381df1ec382b = R"___(The default constructor is an empty cross-section (containing no contours).)___"; +const auto cross_section__cross_section_4459865c1e2f = R"___(Create a 2d cross-section from a set of contours (complex polygons). A +boolean union operation (with Positive filling rule by default) is +performed to combine overlapping polygons and ensure the resulting +CrossSection is free of intersections. +:param contours: A set of closed paths describing zero or more complex +polygons. +:param fillrule: The filling rule used to interpret polygon sub-regions in +contours.)___"; +const auto cross_section__cross_section_aad37c257207 = R"___(Create a 2d cross-section from a single contour. A boolean union operation +(with Positive filling rule by default) is performed to ensure the +resulting CrossSection is free of self-intersections. +:param contour: A closed path outlining the desired cross-section. +:param fillrule: The filling rule used to interpret polygon sub-regions +created by self-intersections in contour.)___"; +const auto cross_section__cross_section_c56006cfa78c = R"___(The copy constructor avoids copying the underlying paths vector (sharing +with its parent via shared_ptr), however subsequent transformations, and +their application will not be shared. It is generally recommended to avoid +this, opting instead to simply create CrossSections with the available +const methods.)___"; +const auto cross_section__decompose_17ae5159e6e5 = R"___(This operation returns a vector of CrossSections that are topologically +disconnected, each containing one outline contour with zero or more +holes.)___"; +const auto cross_section__hull_014f76304d06 = R"___(Compute the convex hull enveloping a set of cross-sections. +:param cross_sections: A vector of cross-sections over which to compute a +convex hull.)___"; +const auto cross_section__hull_3f1ad9eaa499 = R"___(Compute the convex hull of this cross-section.)___"; +const auto cross_section__hull_c94ccc3c0fe6 = R"___(Compute the convex hull of a set of points/polygons. If the given points are +fewer than 3, an empty CrossSection will be returned. +:param pts: A vector of vectors of 2-dimensional points over which to compute +a convex hull.)___"; +const auto cross_section__hull_e609862eb256 = R"___(Compute the convex hull of a set of points. If the given points are fewer +than 3, an empty CrossSection will be returned. +:param pts: A vector of 2-dimensional points over which to compute a convex +hull.)___"; +const auto cross_section__is_empty_25b4b2d4e0ad = R"___(Does the CrossSection contain any contours?)___"; +const auto cross_section__mirror_a10119e40f21 = R"___(Mirror this CrossSection over the arbitrary axis described by the unit form +of the given vector. If the length of the vector is zero, an empty +CrossSection is returned. This operation can be chained. Transforms are +combined and applied lazily. +:param ax: the axis to be mirrored over)___"; +const auto cross_section__num_contour_5894fa74e5f5 = R"___(Return the number of contours (both outer and inner paths) in the +CrossSection.)___"; +const auto cross_section__num_vert_9dd2efd31062 = R"___(Return the number of vertices in the CrossSection.)___"; +const auto cross_section__offset_b3675b4b0ed0 = R"___(Inflate the contours in CrossSection by the specified delta, handling +corners according to the given JoinType. +:param delta: Positive deltas will cause the expansion of outlining contours +to expand, and retraction of inner (hole) contours. Negative deltas will +have the opposite effect. +:param jt: The join type specifying the treatment of contour joins +(corners). +:param miter_limit: The maximum distance in multiples of delta that vertices +can be offset from their original positions with before squaring is +applied, when the join type is Miter (default is 2, which is the +minimum allowed). See the [Clipper2 +MiterLimit](http://www.angusj.com/clipper2/Docs/Units/Clipper.Offset/Classes/ClipperOffset/Properties/MiterLimit.htm) +page for a visual example. +:param circular_segments: Number of segments per 360 degrees of +JoinType::Round corners (roughly, the number of vertices that +will be added to each contour). Default is calculated by the static Quality +defaults according to the radius.)___"; +const auto cross_section__operator_minus_04b4d727817f = R"___(Compute the boolean difference of a (clip) cross-section from another +(subject).)___"; +const auto cross_section__operator_minus_eq_bdfdaab4f47a = R"___(Compute the boolean difference of a (clip) cross-section from a another +(subject), assigning the result to the subject.)___"; +const auto cross_section__operator_plus_d3c26b9c5ca3 = R"___(Compute the boolean union between two cross-sections.)___"; +const auto cross_section__operator_plus_eq_9daa44be9a1d = R"___(Compute the boolean union between two cross-sections, assigning the result +to the first.)___"; +const auto cross_section__operator_xor_76de317c9be1 = R"___(Compute the boolean intersection between two cross-sections.)___"; +const auto cross_section__operator_xor_eq_705353363fb1 = R"___(Compute the boolean intersection between two cross-sections, assigning the +result to the first.)___"; +const auto cross_section__rotate_7c6bae9524e7 = R"___(Applies a (Z-axis) rotation to the CrossSection, in degrees. This operation +can be chained. Transforms are combined and applied lazily. +:param degrees: degrees about the Z-axis to rotate.)___"; +const auto cross_section__scale_8913c878f656 = R"___(Scale this CrossSection in space. This operation can be chained. Transforms +are combined and applied lazily. +:param v: The vector to multiply every vertex by per component.)___"; +const auto cross_section__simplify_dbac3e60acf4 = R"___(Remove vertices from the contours in this CrossSection that are less than +the specified distance epsilon from an imaginary line that passes through +its two adjacent vertices. Near duplicate vertices and collinear points +will be removed at lower epsilons, with elimination of line segments +becoming increasingly aggressive with larger epsilons. +It is recommended to apply this function following Offset, in order to +clean up any spurious tiny line segments introduced that do not improve +quality in any meaningful way. This is particularly important if further +offseting operations are to be performed, which would compound the issue.)___"; +const auto cross_section__square_67088e89831e = R"___(Constructs a square with the given XY dimensions. By default it is +positioned in the first quadrant, touching the origin. If any dimensions in +size are negative, or if all are zero, an empty Manifold will be returned. +:param size: The X, and Y dimensions of the square. +:param center: Set to true to shift the center to the origin.)___"; +const auto cross_section__to_polygons_6f4cb60dbd78 = R"___(Return the contours of this CrossSection as a Polygons.)___"; +const auto cross_section__transform_baddfca7ede3 = R"___(Transform this CrossSection in space. The first two columns form a 2x2 +matrix transform and the last is a translation vector. This operation can +be chained. Transforms are combined and applied lazily. +:param m: The affine transform matrix to apply to all the vertices.)___"; +const auto cross_section__translate_339895387e15 = R"___(Move this CrossSection in space. This operation can be chained. Transforms +are combined and applied lazily. +:param v: The vector to add to every vertex.)___"; +const auto cross_section__warp_180cafeaaad1 = R"___(Move the vertices of this CrossSection (creating a new one) according to +any arbitrary input function, followed by a union operation (with a +Positive fill rule) that ensures any introduced intersections are not +included in the result. +:param warp_func: A function that modifies a given vertex position.)___"; +const auto cross_section__warp_batch_f843ce28c677 = R"___(Same as CrossSection::Warp but calls warpFunc with +a VecView which is roughly equivalent to std::span +pointing to all vec2 elements to be modified in-place +:param warp_func: A function that modifies multiple vertex positions.)___"; +const auto get_circular_segments_3f31ccff2bbc = R"___(Determine the result of the SetMinCircularAngle(), +SetMinCircularEdgeLength(), and SetCircularSegments() defaults. +:param radius: For a given radius of circle, determine how many default +segments there will be.)___"; +const auto manifold__as_original_2653ccf900dd = R"___(This function condenses all coplanar faces in the relation, and +collapses those edges. In the process the relation to ancestor meshes is lost +and this new Manifold is marked an original. Properties are preserved, so if +they do not match across an edge, that edge will be kept.)___"; +const auto manifold__boolean_f506a86f19f9 = R"___(The central operation of this library: the Boolean combines two manifolds +into another by calculating their intersections and removing the unused +portions. +[ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid) +inputs will produce ε-valid output. ε-invalid input may fail +triangulation. +These operations are optimized to produce nearly-instant results if either +input is empty or their bounding boxes do not overlap. +:param second: The other Manifold. +:param op: The type of operation to perform.)___"; +const auto manifold__bounding_box_f0b931ac75e5 = R"___(Returns the axis-aligned bounding box of all the Manifold's vertices.)___"; +const auto manifold__calculate_curvature_0bb0af918c76 = R"___(Curvature is the inverse of the radius of curvature, and signed such that +positive is convex and negative is concave. There are two orthogonal +principal curvatures at any point on a manifold, with one maximum and the +other minimum. Gaussian curvature is their product, while mean +curvature is their sum. This approximates them for every vertex and assigns +them as vertex properties on the given channels. +:param gaussian_idx: The property channel index in which to store the Gaussian +curvature. An index < 0 will be ignored (stores nothing). The property set +will be automatically expanded to include the channel index specified. +:param mean_idx: The property channel index in which to store the mean +curvature. An index < 0 will be ignored (stores nothing). The property set +will be automatically expanded to include the channel index specified.)___"; +const auto manifold__compose_6c382bb1612b = R"___(Constructs a new manifold from a vector of other manifolds. This is a purely +topological operation, so care should be taken to avoid creating +overlapping results. It is the inverse operation of Decompose(). +:param manifolds: A vector of Manifolds to lazy-union together.)___"; +const auto manifold__cube_64d1c43c52ed = R"___(Constructs a unit cube (edge lengths all one), by default in the first +octant, touching the origin. If any dimensions in size are negative, or if +all are zero, an empty Manifold will be returned. +:param size: The X, Y, and Z dimensions of the box. +:param center: Set to true to shift the center to the origin.)___"; +const auto manifold__cylinder_af7b1b7dc893 = R"___(A convenience constructor for the common case of extruding a circle. Can also +form cones if both radii are specified. +:param height: Z-extent +:param radius_low: Radius of bottom circle. Must be positive. +:param radius_high: Radius of top circle. Can equal zero. Default is equal to +radiusLow. +:param circular_segments: How many line segments to use around the circle. +Default is calculated by the static Defaults. +:param center: Set to true to shift the center to the origin. Default is +origin at the bottom.)___"; +const auto manifold__decompose_88ccab82c740 = R"___(This operation returns a vector of Manifolds that are topologically +disconnected. If everything is connected, the vector is length one, +containing a copy of the original. It is the inverse operation of Compose().)___"; +const auto manifold__extrude_bc84f1554abe = R"___(Constructs a manifold from a set of polygons by extruding them along the +Z-axis. +Note that high twistDegrees with small nDivisions may cause +self-intersection. This is not checked here and it is up to the user to +choose the correct parameters. +:param cross_section: A set of non-overlapping polygons to extrude. +:param height: Z-extent of extrusion. +:param n_divisions: Number of extra copies of the crossSection to insert into +the shape vertically; especially useful in combination with twistDegrees to +avoid interpolation artifacts. Default is none. +:param twist_degrees: Amount to twist the top crossSection relative to the +bottom, interpolated linearly for the divisions in between. +:param scale_top: Amount to scale the top (independently in X and Y). If the +scale is {0, 0}, a pure cone is formed with only a single vertex at the top. +Note that scale is applied after twist. +Default {1, 1}.)___"; +const auto manifold__genus_75c215a950f8 = R"___(The genus is a topological property of the manifold, representing the number +of "handles". A sphere is 0, torus 1, etc. It is only meaningful for a single +mesh, so it is best to call Decompose() first.)___"; +const auto manifold__get_mesh_5654c880b26b = R"___(This returns a Mesh of simple vectors of vertices and triangles suitable for +saving or other operations outside of the context of this library.)___"; +const auto manifold__get_mesh_gl_731af09ec81f = R"___(The most complete output of this library, returning a MeshGL that is designed +to easily push into a renderer, including all interleaved vertex properties +that may have been input. It also includes relations to all the input meshes +that form a part of this result and the transforms applied to each. +:param normal_idx: If the original MeshGL inputs that formed this manifold had +properties corresponding to normal vectors, you can specify which property +channels these are (x, y, z), which will cause this output MeshGL to +automatically update these normals according to the applied transforms and +front/back side. Each channel must be >= 3 and < numProp, and all original +MeshGLs must use the same channels for their normals.)___"; +const auto manifold__get_properties_ba109b37a202 = R"___(Returns the surface area and volume of the manifold.)___"; +const auto manifold__hull_1fd92449f7cb = R"___(Compute the convex hull of this manifold.)___"; +const auto manifold__hull_b0113f48020a = R"___(Compute the convex hull of a set of points. If the given points are fewer +than 4, or they are all coplanar, an empty Manifold will be returned. +:param pts: A vector of 3-dimensional points over which to compute a convex +hull.)___"; +const auto manifold__hull_e1568f7f16f2 = R"___(Compute the convex hull enveloping a set of manifolds. +:param manifolds: A vector of manifolds over which to compute a convex hull.)___"; +const auto manifold__is_empty_8f3c4e98cca8 = R"___(Does the Manifold have any triangles?)___"; +const auto manifold__manifold_37129c244d43 = R"___(Convert a MeshGL into a Manifold, retaining its properties and merging only the positions according to the merge vectors. Will return an empty Manifold and set an Error Status if the result is not an oriented 2-manifold. Will collapse degenerate triangles and unnecessary vertices. @@ -26,73 +233,68 @@ neighboring triangles are considered to be on a property boundary edge. Property boundary edges will be retained across operations even if the triangles are coplanar. Defaults to 1e-5, which works well for most properties in the [-1, 1] range.)___"; -const auto manifold_manifold_6 = R"___(Convert a Mesh into a Manifold. Will return an empty Manifold +const auto manifold__manifold_6dd024847cd9 = R"___(Convert a Mesh into a Manifold. Will return an empty Manifold and set an Error Status if the Mesh is not an oriented 2-manifold. Will collapse degenerate triangles and unnecessary vertices. :param mesh: The input Mesh.)___"; -const auto manifold_get_mesh = R"___(This returns a Mesh of simple vectors of vertices and triangles suitable for -saving or other operations outside of the context of this library.)___"; -const auto manifold_get_mesh_gl = R"___(The most complete output of this library, returning a MeshGL that is designed -to easily push into a renderer, including all interleaved vertex properties -that may have been input. It also includes relations to all the input meshes -that form a part of this result and the transforms applied to each. -:param normal_idx: If the original MeshGL inputs that formed this manifold had -properties corresponding to normal vectors, you can specify which property -channels these are (x, y, z), which will cause this output MeshGL to -automatically update these normals according to the applied transforms and -front/back side. Each channel must be >= 3 and < numProp, and all original -MeshGLs must use the same channels for their normals.)___"; -const auto manifold_is_empty = R"___(Does the Manifold have any triangles?)___"; -const auto manifold_status = R"___(Returns the reason for an input Mesh producing an empty Manifold. This Status -only applies to Manifolds newly-created from an input Mesh - once they are -combined into a new Manifold via operations, the status reverts to NoError, -simply processing the problem mesh as empty. Likewise, empty meshes may still -show NoError, for instance if they are small enough relative to their -precision to be collapsed to nothing.)___"; -const auto manifold_num_vert = R"___(The number of vertices in the Manifold.)___"; -const auto manifold_num_edge = R"___(The number of edges in the Manifold.)___"; -const auto manifold_num_tri = R"___(The number of triangles in the Manifold.)___"; -const auto manifold_num_prop = R"___(The number of properties per vertex in the Manifold.)___"; -const auto manifold_num_prop_vert = R"___(The number of property vertices in the Manifold. This will always be >= +const auto manifold__manifold_c130481162c9 = R"___(Construct an empty Manifold.)___"; +const auto manifold__matches_tri_normals_c662986590f4 = R"___(The triangle normal vectors are saved over the course of operations rather +than recalculated to avoid rounding error. This checks that triangles still +match their normal vectors within Precision().)___"; +const auto manifold__mirror_d798a49656cc = R"___(Mirror this Manifold over the plane described by the unit form of the given +normal vector. If the length of the normal is zero, an empty Manifold is +returned. This operation can be chained. Transforms are combined and applied +lazily. +:param normal: The normal vector of the plane to be mirrored over)___"; +const auto manifold__num_degenerate_tris_d86f985c281a = R"___(The number of triangles that are colinear within Precision(). This library +attempts to remove all of these, but it cannot always remove all of them +without changing the mesh by too much.)___"; +const auto manifold__num_edge_61b0f3dc7f99 = R"___(The number of edges in the Manifold.)___"; +const auto manifold__num_overlaps_4a8cd704b45a = R"___(This is a checksum-style verification of the collider, simply returning the +total number of edge-face bounding box overlaps between this and other. +:param other: A Manifold to overlap with.)___"; +const auto manifold__num_prop_745ca800e017 = R"___(The number of properties per vertex in the Manifold.)___"; +const auto manifold__num_prop_vert_a7ba865d3e11 = R"___(The number of property vertices in the Manifold. This will always be >= NumVert, as some physical vertices may be duplicated to account for different properties on different neighboring triangles.)___"; -const auto manifold_bounding_box = R"___(Returns the axis-aligned bounding box of all the Manifold's vertices.)___"; -const auto manifold_precision = R"___(Returns the precision of this Manifold's vertices, which tracks the +const auto manifold__num_tri_56a67713dff8 = R"___(The number of triangles in the Manifold.)___"; +const auto manifold__num_vert_93a4106c8a53 = R"___(The number of vertices in the Manifold.)___"; +const auto manifold__operator_minus_2ecfe0a1eb86 = R"___(Shorthand for Boolean Difference.)___"; +const auto manifold__operator_minus_eq_f6df5d8bc05c = R"___(Shorthand for Boolean Difference assignment.)___"; +const auto manifold__operator_plus_a473f00d1659 = R"___(Shorthand for Boolean Union.)___"; +const auto manifold__operator_plus_eq_b7ba3403e755 = R"___(Shorthand for Boolean Union assignment.)___"; +const auto manifold__operator_xor_4da4601e403b = R"___(Shorthand for Boolean Intersection.)___"; +const auto manifold__operator_xor_eq_0116ab409eee = R"___(Shorthand for Boolean Intersection assignment.)___"; +const auto manifold__original_id_d1c064807f94 = R"___(If this mesh is an original, this returns its meshID that can be referenced +by product manifolds' MeshRelation. If this manifold is a product, this +returns -1.)___"; +const auto manifold__precision_bb888ab8ec11 = R"___(Returns the precision of this Manifold's vertices, which tracks the approximate rounding error over all the transforms and operations that have led to this state. Any triangles that are colinear within this precision are considered degenerate and removed. This is the value of ε defining [ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid).)___"; -const auto manifold_genus = R"___(The genus is a topological property of the manifold, representing the number -of "handles". A sphere is 0, torus 1, etc. It is only meaningful for a single -mesh, so it is best to call Decompose() first.)___"; -const auto manifold_get_properties = R"___(Returns the surface area and volume of the manifold.)___"; -const auto manifold_original_id = R"___(If this mesh is an original, this returns its meshID that can be referenced -by product manifolds' MeshRelation. If this manifold is a product, this -returns -1.)___"; -const auto manifold_as_original = R"___(This function condenses all coplanar faces in the relation, and -collapses those edges. In the process the relation to ancestor meshes is lost -and this new Manifold is marked an original. Properties are preserved, so if -they do not match across an edge, that edge will be kept.)___"; -const auto manifold_reserve_ids = R"___(Returns the first of n sequential new unique mesh IDs for marking sets of +const auto manifold__project_e28980e0f682 = R"___(Returns a cross section representing the projected outline of this object +onto the X-Y plane.)___"; +const auto manifold__refine_c64a4fc78137 = R"___(Increase the density of the mesh by splitting every edge into n pieces. For +instance, with n = 2, each triangle will be split into 4 triangles. These +will all be coplanar (and will not be immediately collapsed) unless the +Mesh/Manifold has halfedgeTangents specified (e.g. from the Smooth() +constructor), in which case the new vertices will be moved to the +interpolated surface according to their barycentric coordinates. +:param n: The number of pieces to split every edge into. Must be > 1.)___"; +const auto manifold__reserve_ids_a514f84c6343 = R"___(Returns the first of n sequential new unique mesh IDs for marking sets of triangles that can be looked up after further operations. Assign to MeshGL.runOriginalID vector.)___"; -const auto manifold_impl__reserve_ids = R"___()___"; -const auto manifold_matches_tri_normals = R"___(The triangle normal vectors are saved over the course of operations rather -than recalculated to avoid rounding error. This checks that triangles still -match their normal vectors within Precision().)___"; -const auto manifold_num_degenerate_tris = R"___(The number of triangles that are colinear within Precision(). This library -attempts to remove all of these, but it cannot always remove all of them -without changing the mesh by too much.)___"; -const auto manifold_num_overlaps = R"___(This is a checksum-style verification of the collider, simply returning the -total number of edge-face bounding box overlaps between this and other. -:param other: A Manifold to overlap with.)___"; -const auto manifold_translate = R"___(Move this Manifold in space. This operation can be chained. Transforms are -combined and applied lazily. -:param v: The vector to add to every vertex.)___"; -const auto manifold_scale = R"___(Scale this Manifold in space. This operation can be chained. Transforms are -combined and applied lazily. -:param v: The vector to multiply every vertex by per component.)___"; -const auto manifold_rotate = R"___(Applies an Euler angle rotation to the manifold, first about the X axis, then +const auto manifold__revolve_c916603e7a75 = R"___(Constructs a manifold from a set of polygons by revolving this cross-section +around its Y-axis and then setting this as the Z-axis of the resulting +manifold. If the polygons cross the Y-axis, only the part on the positive X +side is used. Geometrically valid input will result in geometrically valid +output. +:param cross_section: A set of non-overlapping polygons to revolve. +:param circular_segments: Number of segments along its diameter. Default is +calculated by the static Defaults. +:param revolve_degrees: Number of degrees to revolve. Default is 360 degrees.)___"; +const auto manifold__rotate_f0bb7b7d2f38 = R"___(Applies an Euler angle rotation to the manifold, first about the X axis, then Y, then Z, in degrees. We use degrees so that we can minimize rounding error, and eliminate it completely for any multiples of 90 degrees. Additionally, more efficient code paths are used to update the manifold when the transforms @@ -101,98 +303,134 @@ Transforms are combined and applied lazily. :param x_degrees: First rotation, degrees about the X-axis. :param y_degrees: Second rotation, degrees about the Y-axis. :param z_degrees: Third rotation, degrees about the Z-axis.)___"; -const auto manifold_transform = R"___(Transform this Manifold in space. The first three columns form a 3x3 matrix -transform and the last is a translation vector. This operation can be -chained. Transforms are combined and applied lazily. -:param m: The affine transform matrix to apply to all the vertices.)___"; -const auto manifold_mirror = R"___(Mirror this Manifold over the plane described by the unit form of the given -normal vector. If the length of the normal is zero, an empty Manifold is -returned. This operation can be chained. Transforms are combined and applied -lazily. -:param normal: The normal vector of the plane to be mirrored over)___"; -const auto manifold_warp = R"___(This function does not change the topology, but allows the vertices to be -moved according to any arbitrary input function. It is easy to create a -function that warps a geometrically valid object into one which overlaps, but -that is not checked here, so it is up to the user to choose their function -with discretion. -:param warp_func: A function that modifies a given vertex position.)___"; -const auto manifold_warp_batch = R"___(Same as Manifold::Warp but calls warpFunc with with -a VecView which is roughly equivalent to std::span -pointing to all vec3 elements to be modified in-place -:param warp_func: A function that modifies multiple vertex positions.)___"; -const auto manifold_set_properties = R"___(Create a new copy of this manifold with updated vertex properties by +const auto manifold__scale_244be87a307d = R"___(Scale this Manifold in space. This operation can be chained. Transforms are +combined and applied lazily. +:param v: The vector to multiply every vertex by per component.)___"; +const auto manifold__set_properties_6a3457d9ec71 = R"___(Create a new copy of this manifold with updated vertex properties by supplying a function that takes the existing position and properties as input. You may specify any number of output properties, allowing creation and removal of channels. Note: undefined behavior will result if you read past the number of input properties or write past the number of output properties. :param num_prop: The new number of properties per vertex. :param prop_func: A function that modifies the properties of a given vertex.)___"; -const auto manifold_calculate_curvature = R"___(Curvature is the inverse of the radius of curvature, and signed such that -positive is convex and negative is concave. There are two orthogonal -principal curvatures at any point on a manifold, with one maximum and the -other minimum. Gaussian curvature is their product, while mean -curvature is their sum. This approximates them for every vertex and assigns -them as vertex properties on the given channels. -:param gaussian_idx: The property channel index in which to store the Gaussian -curvature. An index < 0 will be ignored (stores nothing). The property set -will be automatically expanded to include the channel index specified. -:param mean_idx: The property channel index in which to store the mean -curvature. An index < 0 will be ignored (stores nothing). The property set -will be automatically expanded to include the channel index specified.)___"; -const auto manifold_refine = R"___(Increase the density of the mesh by splitting every edge into n pieces. For -instance, with n = 2, each triangle will be split into 4 triangles. These -will all be coplanar (and will not be immediately collapsed) unless the -Mesh/Manifold has halfedgeTangents specified (e.g. from the Smooth() -constructor), in which case the new vertices will be moved to the -interpolated surface according to their barycentric coordinates. -:param n: The number of pieces to split every edge into. Must be > 1.)___"; -const auto manifold_boolean = R"___(The central operation of this library: the Boolean combines two manifolds -into another by calculating their intersections and removing the unused -portions. -[ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid) -inputs will produce ε-valid output. ε-invalid input may fail -triangulation. -These operations are optimized to produce nearly-instant results if either -input is empty or their bounding boxes do not overlap. -:param second: The other Manifold. -:param op: The type of operation to perform.)___"; -const auto manifold_batch_boolean = R"___()___"; -const auto manifold_operator_plus = R"___(Shorthand for Boolean Union.)___"; -const auto manifold_operator_plus_eq = R"___(Shorthand for Boolean Union assignment.)___"; -const auto manifold_operator_minus = R"___(Shorthand for Boolean Difference.)___"; -const auto manifold_operator_minus_eq = R"___(Shorthand for Boolean Difference assignment.)___"; -const auto manifold_operator_xor = R"___(Shorthand for Boolean Intersection.)___"; -const auto manifold_operator_xor_eq = R"___(Shorthand for Boolean Intersection assignment.)___"; -const auto manifold_split = R"___(Split cuts this manifold in two using the cutter manifold. The first result -is the intersection, second is the difference. This is more efficient than -doing them separately. -:param cutter:)___"; -const auto manifold_split_by_plane = R"___(Convenient version of Split() for a half-space. +const auto manifold__slice_7d90a75e7913 = R"___(Returns the cross section of this object parallel to the X-Y plane at the +specified Z height, defaulting to zero. Using a height equal to the bottom of +the bounding box will return the bottom faces, while using a height equal to +the top of the bounding box will return empty.)___"; +const auto manifold__smooth_66eaffd9331b = R"___(Constructs a smooth version of the input mesh by creating tangents; this +method will throw if you have supplied tangents with your mesh already. The +actual triangle resolution is unchanged; use the Refine() method to +interpolate to a higher-resolution curve. +By default, every edge is calculated for maximum smoothness (very much +approximately), attempting to minimize the maximum mean Curvature magnitude. +No higher-order derivatives are considered, as the interpolation is +independent per triangle, only sharing constraints on their boundaries. +:param mesh_gl: input MeshGL. +:param sharpened_edges: If desired, you can supply a vector of sharpened +halfedges, which should in general be a small subset of all halfedges. Order +of entries doesn't matter, as each one specifies the desired smoothness +(between zero and one, with one the default for all unspecified halfedges) +and the halfedge index (3 * triangle index + [0,1,2] where 0 is the edge +between triVert 0 and 1, etc). +At a smoothness value of zero, a sharp crease is made. The smoothness is +interpolated along each edge, so the specified value should be thought of as +an average. Where exactly two sharpened edges meet at a vertex, their +tangents are rotated to be colinear so that the sharpened edge can be +continuous. Vertices with only one sharpened edge are completely smooth, +allowing sharpened edges to smoothly vanish at termination. A single vertex +can be sharpened by sharping all edges that are incident on it, allowing +cones to be formed.)___"; +const auto manifold__smooth_f788c5a0c633 = R"___(Constructs a smooth version of the input mesh by creating tangents; this +method will throw if you have supplied tangents with your mesh already. The +actual triangle resolution is unchanged; use the Refine() method to +interpolate to a higher-resolution curve. +By default, every edge is calculated for maximum smoothness (very much +approximately), attempting to minimize the maximum mean Curvature magnitude. +No higher-order derivatives are considered, as the interpolation is +independent per triangle, only sharing constraints on their boundaries. +:param mesh: input Mesh. +:param sharpened_edges: If desired, you can supply a vector of sharpened +halfedges, which should in general be a small subset of all halfedges. Order +of entries doesn't matter, as each one specifies the desired smoothness +(between zero and one, with one the default for all unspecified halfedges) +and the halfedge index (3 * triangle index + [0,1,2] where 0 is the edge +between triVert 0 and 1, etc). +At a smoothness value of zero, a sharp crease is made. The smoothness is +interpolated along each edge, so the specified value should be thought of as +an average. Where exactly two sharpened edges meet at a vertex, their +tangents are rotated to be colinear so that the sharpened edge can be +continuous. Vertices with only one sharpened edge are completely smooth, +allowing sharpened edges to smoothly vanish at termination. A single vertex +can be sharpened by sharping all edges that are incident on it, allowing +cones to be formed.)___"; +const auto manifold__sphere_6781451731f0 = R"___(Constructs a geodesic sphere of a given radius. +:param radius: Radius of the sphere. Must be positive. +:param circular_segments: Number of segments along its +diameter. This number will always be rounded up to the nearest factor of +four, as this sphere is constructed by refining an octahedron. This means +there are a circle of vertices on all three of the axis planes. Default is +calculated by the static Defaults.)___"; +const auto manifold__split_by_plane_f411533a14aa = R"___(Convenient version of Split() for a half-space. :param normal: This vector is normal to the cutting plane and its length does not matter. The first result is in the direction of this vector, the second result is on the opposite side. :param origin_offset: The distance of the plane from the origin in the direction of the normal vector.)___"; -const auto manifold_trim_by_plane = R"___(Identical to SplitByPlane(), but calculating and returning only the first +const auto manifold__split_fc2847c7afae = R"___(Split cuts this manifold in two using the cutter manifold. The first result +is the intersection, second is the difference. This is more efficient than +doing them separately. +:param cutter:)___"; +const auto manifold__status_b1c2b69ee41e = R"___(Returns the reason for an input Mesh producing an empty Manifold. This Status +only applies to Manifolds newly-created from an input Mesh - once they are +combined into a new Manifold via operations, the status reverts to NoError, +simply processing the problem mesh as empty. Likewise, empty meshes may still +show NoError, for instance if they are small enough relative to their +precision to be collapsed to nothing.)___"; +const auto manifold__tetrahedron_7e95f682f35b = R"___(Constructs a tetrahedron centered at the origin with one vertex at (1,1,1) +and the rest at similarly symmetric points.)___"; +const auto manifold__transform_0390744b2b46 = R"___(Transform this Manifold in space. The first three columns form a 3x3 matrix +transform and the last is a translation vector. This operation can be +chained. Transforms are combined and applied lazily. +:param m: The affine transform matrix to apply to all the vertices.)___"; +const auto manifold__translate_fcadc2ca8d6d = R"___(Move this Manifold in space. This operation can be chained. Transforms are +combined and applied lazily. +:param v: The vector to add to every vertex.)___"; +const auto manifold__trim_by_plane_066ac34a84b0 = R"___(Identical to SplitByPlane(), but calculating and returning only the first result. :param normal: This vector is normal to the cutting plane and its length does not matter. The result is in the direction of this vector from the plane. :param origin_offset: The distance of the plane from the origin in the direction of the normal vector.)___"; -const auto manifold_slice = R"___(Returns the cross section of this object parallel to the X-Y plane at the -specified Z height, defaulting to zero. Using a height equal to the bottom of -the bounding box will return the bottom faces, while using a height equal to -the top of the bounding box will return empty.)___"; -const auto manifold_project = R"___(Returns a cross section representing the projected outline of this object -onto the X-Y plane.)___"; -const auto manifold_hull = R"___(Compute the convex hull of a set of points. If the given points are fewer -than 4, or they are all coplanar, an empty Manifold will be returned. -:param pts: A vector of 3-dimensional points over which to compute a convex -hull.)___"; -const auto manifold_hull_1 = R"___(Compute the convex hull of this manifold.)___"; -const auto manifold_hull_2 = R"___(Compute the convex hull enveloping a set of manifolds. -:param manifolds: A vector of manifolds over which to compute a convex hull.)___"; -const auto triangulate = R"___(@brief Triangulates a set of ε-valid polygons. If the input is not +const auto manifold__warp_4cc67905f424 = R"___(This function does not change the topology, but allows the vertices to be +moved according to any arbitrary input function. It is easy to create a +function that warps a geometrically valid object into one which overlaps, but +that is not checked here, so it is up to the user to choose their function +with discretion. +:param warp_func: A function that modifies a given vertex position.)___"; +const auto manifold__warp_batch_0b44f7bbe36b = R"___(Same as Manifold::Warp but calls warpFunc with with +a VecView which is roughly equivalent to std::span +pointing to all vec3 elements to be modified in-place +:param warp_func: A function that modifies multiple vertex positions.)___"; +const auto set_circular_segments_7ebd2a75022a = R"___(Sets the default number of circular segments for the +CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and +Manifold::Revolve() constructors. Overrides the edge length and angle +constraints and sets the number of segments to exactly this value. +:param number: Number of circular segments. Default is 0, meaning no +constraint is applied.)___"; +const auto set_min_circular_angle_69463d0b8fac = R"___(Sets an angle constraint the default number of circular segments for the +CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and +Manifold::Revolve() constructors. The number of segments will be rounded up +to the nearest factor of four. +:param angle: The minimum angle in degrees between consecutive segments. The +angle will increase if the the segments hit the minimum edge length. +Default is 10 degrees.)___"; +const auto set_min_circular_edge_length_bb1c70addca7 = R"___(Sets a length constraint the default number of circular segments for the +CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and +Manifold::Revolve() constructors. The number of segments will be rounded up +to the nearest factor of four. +:param length: The minimum length of segments. The length will +increase if the the segments hit the minimum angle. Default is 1.0.)___"; +const auto triangulate_025ab0b4046f = R"___(@brief Triangulates a set of ε-valid polygons. If the input is not ε-valid, the triangulation may overlap, but will always return a manifold result that matches the input edge directions. :param polygons: The set of polygons, wound CCW and representing multiple @@ -201,29 +439,24 @@ polygons and/or holes. input. @return std::vector The triangles, referencing the original polygon points in order.)___"; -const auto triangulate_poly = R"___()___"; -const auto triangulate_idx = R"___()___"; -const auto set_min_circular_angle = R"___(Sets an angle constraint the default number of circular segments for the -CrossSection::circle, Manifold::cylinder, Manifold::sphere, and -Manifold::revolve constructors. The number of segments will be rounded up -to the nearest factor of four. -:param angle: The minimum angle in degrees between consecutive segments. The -angle will increase if the the segments hit the minimum edge length. -Default is 10 degrees.)___"; -const auto set_min_circular_edge_length = R"___(Sets a length constraint the default number of circular segments for the -CrossSection::circle, Manifold::cylinder, Manifold::sphere, and -Manifold::revolve constructors. The number of segments will be rounded up -to the nearest factor of four. -:param length: The minimum length of segments. The length will -increase if the the segments hit the minimum angle. Default is 1.0.)___"; -const auto set_circular_segments = R"___(Sets the default number of circular segments for the -CrossSection::circle, Manifold::cylinder, Manifold::sphere, and -Manifold::revolve constructors. Overrides the edge length and angle -constraints and sets the number of segments to exactly this value. -:param number: Number of circular segments. Default is 0, meaning no -constraint is applied.)___"; -const auto get_circular_segments = R"___(Determine the result of the set_min_circular_angle, -set_min_circular_edge_length, and set_circular_segments defaults. -:param radius: For a given radius of circle, determine how many default -segments there will be.)___"; +const auto triangulate_8f1ad11752db = R"___(Ear-clipping triangulator based on David Eberly's approach from Geometric +Tools, but adjusted to handle epsilon-valid polygons, and including a +fallback that ensures a manifold triangulation even for overlapping polygons. +This is an O(n^2) algorithm, but hopefully this is not a big problem as the +number of edges in a given polygon is generally much less than the number of +triangles in a mesh, and relatively few faces even need triangulation. +The main adjustments for robustness involve clipping the sharpest ears first +(a known technique to get higher triangle quality), and doing an exhaustive +search to determine ear convexity exactly if the first geometric result is +within precision.)___"; +const auto triangulate_idx_9847f0a1f0f8 = R"___(@brief Triangulates a set of ε-valid polygons. If the input is not +ε-valid, the triangulation may overlap, but will always return a +manifold result that matches the input edge directions. +:param polys: The set of polygons, wound CCW and representing multiple +polygons and/or holes. These have 2D-projected positions as well as +references back to the original vertices. +:param precision: The value of ε, bounding the uncertainty of the +input. +@return std::vector The triangles, referencing the original +vertex indicies.)___"; } // namespace manifold_docs \ No newline at end of file diff --git a/bindings/python/gen_docs.py b/bindings/python/gen_docs.py index a260bfada..fd0c1e7f1 100644 --- a/bindings/python/gen_docs.py +++ b/bindings/python/gen_docs.py @@ -1,23 +1,17 @@ - # %% + from os.path import dirname +from hashlib import md5 import re base = dirname(dirname(dirname(__file__))) -manifold_h = f'{base}/src/manifold/include/manifold.h' -public_h = f'{base}/src/utilities/include/public.h' -polygon_cpp = f'{base}/src/polygon/src/polygon.cpp' -manifold_cpp = f'{base}/src/manifold/src/manifold.cpp' - -method_re = re.compile(r'Manifold::([\w\-\+\^\=\:]+)\(') -param_re = re.compile(r'@param (\w+)') -comments = {} - def snake_case(name): - # name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower() +def param_snake_case(m): + return f':{snake_case(m[0][1:])}:' + def method_key(name): name = re.sub('\+', '_plus', name) name = re.sub('\-', '_minus', name) @@ -27,47 +21,20 @@ def method_key(name): name = re.sub('\~', 'destroy_', name) return name -comment = '' -with open(manifold_cpp) as f: - for line in f: - line = line.lstrip() - if line.startswith('/**'): - comment = '' - elif line.startswith('*/'): - pass - elif line.startswith('*') and comment is not None: - comment += line[1:].lstrip() - elif m := method_re.search(line): - method = 'manifold_'+method_key(snake_case(m[1])) - splices = [] - last = 0 - for m in param_re.finditer(comment): - b, e = m.span() - var = snake_case(m[1]) - splices += [ - comment[last:b], - f':param {var}:' - ] - last = e - splices += [comment[last:]] - comment = ''.join(splices) - orig_method = method - n = 1 - while method in comments: - method = f'{orig_method}_{n}' - n += 1 - comments[method] = comment - comment = '' - -comments -# %% +def close_paren(s): + cnt = 0 + for i in range(len(s)): + if s[i] == '(': + cnt += 1 + elif s[i] == ')': + cnt -= 1 + if cnt == 0: + return i + return -1 -# comments = {} - -qual_re = re.compile(r'([\w\-\+\^\=\:]+)\(') -comment = '' -patterns = ["Circular", "Triangulate"] -for fname in [polygon_cpp, public_h]: +def collect(fname, matcher): + param_re = re.compile(r'@param (\w+)') + comment = '' with open(fname) as f: for line in f: line = line.lstrip() @@ -77,38 +44,49 @@ def method_key(name): pass elif line.startswith('*') and comment is not None: comment += line[1:].lstrip() - elif m := qual_re.search(line): - allow = sum(p in line for p in patterns) - if allow: - method = method_key(snake_case(m[1])) - splices = [] - last = 0 - for m in param_re.finditer(comment): - b, e = m.span() - var = snake_case(m[1]) - splices += [ - comment[last:b], - f':param {var}:' - ] - last = e - splices += [comment[last:]] - comment = ''.join(splices) + elif comment and (m := matcher(line)): + while (close := close_paren(line)) < 0: + line += next(f) + line = re.sub(r'\s', '', line[:close+1]) + method = method_key(snake_case(m[1])) + comment = re.sub(param_re, param_snake_case, comment) + lhash = md5(line.encode("utf-8")).hexdigest() + method = f'{method}_{lhash[:12]}' + comments[method] = comment + comment = '' - def re_snake_case(m): - return (m[1] or '')+snake_case(m[2]) - comment = re.sub(r'(\w+::)*(\w+)\(\)', re_snake_case, comment) +comments = {} - comments[method] = comment - comment = '' +method_re = re.compile(r'(\w+::[\w\-\+\^\=\:]+)\(') +function_re = re.compile(r'([\w\-\+\^\=\:]+)\(') +# we don't handle inline functions in classes very well +# so instead just white-list functions we want +def select_functions(s): + m = function_re.search(s) + if m and 'Triangulate' in m[0]: + return m + if m and 'Circular' in m[0]: + return m + return None + +collect(f'{base}/src/manifold/src/manifold.cpp', lambda s: method_re.search(s)) +collect(f'{base}/src/manifold/src/constructors.cpp', lambda s: method_re.search(s)) +collect(f'{base}/src/cross_section/src/cross_section.cpp', lambda s: method_re.search(s)) +collect(f'{base}/src/polygon/src/polygon.cpp', select_functions) +collect(f'{base}/src/utilities/include/public.h', select_functions) + +comments = dict(sorted(comments.items())) comments # %% gen_h = f'{base}/bindings/python/docstrings.inl' with open(gen_h, 'w') as f: f.write('#pragma once\n') + f.write('#include \n') f.write('namespace manifold_docs {\n') for key, doc in comments.items(): f.write(f'const auto {key} = R"___({doc.strip()})___";\n') f.write('} // namespace manifold_docs') + # %% diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 92dbe2649..87965e934 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -203,64 +203,58 @@ std::vector toVector(const T *arr, size_t size) { return std::vector(arr, arr + size); } +using namespace manifold_docs; + +// strip original :params: and replace with ours +const std::string manifold__rotate_vec = + std::string(manifold__rotate_f0bb7b7d2f38) + .substr(0, std::string(manifold__rotate_f0bb7b7d2f38).find(":param")) + + ":param v: [X, Y, Z] rotation in degrees."; + NB_MODULE(manifold3d, m) { m.doc() = "Python binding for the Manifold library."; m.def("set_min_circular_angle", Quality::SetMinCircularAngle, - nb::arg("angle"), manifold_docs::set_min_circular_angle); + nb::arg("angle"), set_min_circular_angle_69463d0b8fac); m.def("set_min_circular_edge_length", Quality::SetMinCircularEdgeLength, - nb::arg("length"), manifold_docs::set_min_circular_edge_length); + nb::arg("length"), set_min_circular_edge_length_bb1c70addca7); m.def("set_circular_segments", Quality::SetCircularSegments, - nb::arg("number"), manifold_docs::set_circular_segments); + nb::arg("number"), set_circular_segments_7ebd2a75022a); m.def("get_circular_segments", Quality::GetCircularSegments, - nb::arg("radius"), manifold_docs::get_circular_segments); + nb::arg("radius"), get_circular_segments_3f31ccff2bbc); m.def("triangulate", &Triangulate, nb::arg("polygons"), nb::arg("precision") = -1, // TODO document - manifold_docs::triangulate); + triangulate_025ab0b4046f); nb::class_(m, "Manifold") - .def(nb::init<>(), manifold_docs::manifold_manifold) + .def(nb::init<>(), manifold__manifold_c130481162c9) .def(nb::init &>(), nb::arg("mesh"), nb::arg("property_tolerance") = nb::list(), - manifold_docs::manifold_manifold_5) + manifold__manifold_37129c244d43) .def(nb::self + nb::self, "Boolean union.") .def(nb::self - nb::self, "Boolean difference.") .def(nb::self ^ nb::self, "Boolean intersection.") .def( "hull", [](const Manifold &self) { return self.Hull(); }, - "Compute the convex hull of all points in this manifold.") + manifold__hull_1fd92449f7cb) .def_static( "batch_hull", [](std::vector ms) { return Manifold::Hull(ms); }, - nb::arg("manifolds"), - "Compute the convex hull enveloping a set of manifolds.") + nb::arg("manifolds"), manifold__hull_e1568f7f16f2) .def_static( "hull_points", [](std::vector pts) { return Manifold::Hull(pts); }, - nb::arg("pts"), - "Compute the convex hull enveloping a set of 3d points.") - .def( - "transform", &Manifold::Transform, nb::arg("m"), - "Transform this Manifold in space. The first three columns form a " - "3x3 matrix transform and the last is a translation vector. This " - "operation can be chained. Transforms are combined and applied " - "lazily.\n" - "\n\n" - ":param m: The affine transform matrix to apply to all the vertices.") + nb::arg("pts"), manifold__hull_b0113f48020a) + .def("transform", &Manifold::Transform, nb::arg("m"), + manifold__transform_0390744b2b46) .def("translate", &Manifold::Translate, nb::arg("t"), - "Move this Manifold in space. This operation can be chained. " - "Transforms are combined and applied lazily." - "\n\n" - ":param t: The vector to add to every vertex.") + manifold__translate_fcadc2ca8d6d) .def("scale", &Manifold::Scale, nb::arg("v"), - "Scale this Manifold in space. This operation can be chained. " - "Transforms are combined and applied lazily." - "\n\n" - ":param v: The vector to multiply every vertex by component.") + manifold__scale_244be87a307d) .def( "scale", [](const Manifold &m, float s) { @@ -272,41 +266,16 @@ NB_MODULE(manifold3d, m) { "\n\n" ":param s: The scalar to multiply every vertex by component.") .def("mirror", &Manifold::Mirror, nb::arg("v"), - "Mirror this Manifold in space. This operation can be chained. " - "Transforms are combined and applied lazily." - "\n\n" - ":param mirror: The vector defining the axis of mirroring.") + manifold__mirror_d798a49656cc) .def( "rotate", [](const Manifold &self, glm::vec3 v) { return self.Rotate(v.x, v.y, v.z); }, - nb::arg("v"), - "Applies an Euler angle rotation to the manifold, first about the X " - "axis, then Y, then Z, in degrees. We use degrees so that we can " - "minimize rounding error, and eliminate it completely for any " - "multiples of 90 degrees. Additionally, more efficient code paths " - "are used to update the manifold when the transforms only rotate by " - "multiples of 90 degrees. This operation can be chained. Transforms " - "are combined and applied lazily." - "\n\n" - ":param v: [X, Y, Z] rotation in degrees.") - .def( - "warp", &Manifold::Warp, nb::arg("f"), - "This function does not change the topology, but allows the vertices " - "to be moved according to any arbitrary input function. It is easy " - "to create a function that warps a geometrically valid object into " - "one which overlaps, but that is not checked here, so it is up to " - "the user to choose their function with discretion." - "\n\n" - ":param f: A function that modifies a given vertex position.") + nb::arg("v"), manifold__rotate_vec.c_str()) + .def("warp", &Manifold::Warp, nb::arg("f"), manifold__warp_4cc67905f424) .def("warp_batch", &Manifold::WarpBatch, nb::arg("f"), - "Same as Manifold.warp but calls `f` with a " - "ndarray(shape=(N,3), dtype=float) and expects an ndarray " - "of the same shape and type in return. The input array can be " - "modified and returned if desired. " - "\n\n" - ":param f: A function that modifies multiple vertex positions.") + manifold__warp_batch_0b44f7bbe36b) .def( "set_properties", // TODO this needs a batch version! [](const Manifold &self, int newNumProp, @@ -337,87 +306,23 @@ NB_MODULE(manifold3d, m) { }); }, nb::arg("new_num_prop"), nb::arg("f"), - "Create a new copy of this manifold with updated vertex properties " - "by supplying a function that takes the existing position and " - "properties as input. You may specify any number of output " - "properties, allowing creation and removal of channels. Note: " - "undefined behavior will result if you read past the number of input " - "properties or write past the number of output properties." - "\n\n" - ":param new_num_prop: The new number of properties per vertex." - ":param f: A function that modifies the properties of a given " - "vertex.") - .def( - "calculate_curvature", &Manifold::CalculateCurvature, - nb::arg("gaussian_idx"), nb::arg("mean_idx"), - "Curvature is the inverse of the radius of curvature, and signed " - "such that positive is convex and negative is concave. There are two " - "orthogonal principal curvatures at any point on a manifold, with " - "one maximum and the other minimum. Gaussian curvature is their " - "product, while mean curvature is their sum. This approximates them " - "for every vertex and assigns them as vertex properties on the given " - "channels." - "\n\n" - ":param gaussianIdx: The property channel index in which to store " - "the Gaussian curvature. An index < 0 will be ignored (stores " - "nothing). The property set will be automatically expanded to " - "include the channel index specified.\n" - ":param meanIdx: The property channel index in which to store the " - "mean curvature. An index < 0 will be ignored (stores nothing). The " - "property set will be automatically expanded to include the channel " - "index specified.") + manifold__set_properties_6a3457d9ec71) + .def("calculate_curvature", &Manifold::CalculateCurvature, + nb::arg("gaussian_idx"), nb::arg("mean_idx"), + manifold__calculate_curvature_0bb0af918c76) .def("refine", &Manifold::Refine, nb::arg("n"), - "Increase the density of the mesh by splitting every edge into n " - "pieces. For instance, with n = 2, each triangle will be split into " - "4 triangles. These will all be coplanar (and will not be " - "immediately collapsed) unless the Mesh/Manifold has " - "halfedgeTangents specified (e.g. from the Smooth() constructor), " - "in which case the new vertices will be moved to the interpolated " - "surface according to their barycentric coordinates." - "\n\n" - ":param n: The number of pieces to split every edge into. Must be > " - "1.") + manifold__refine_c64a4fc78137) .def("to_mesh", &Manifold::GetMeshGL, nb::arg("normal_idx") = glm::ivec3(0), - "The most complete output of this library, returning a MeshGL that " - "is designed to easily push into a renderer, including all " - "interleaved vertex properties that may have been input. It also " - "includes relations to all the input meshes that form a part of " - "this result and the transforms applied to each." - "\n\n" - ":param normal_idx: If the original MeshGL inputs that formed this " - "manifold had properties corresponding to normal vectors, you can " - "specify which property channels these are (x, y, z), which will " - "cause this output MeshGL to automatically update these normals " - "according to the applied transforms and front/back side. Each " - "channel must be >= 3 and < numProp, and all original MeshGLs must " - "use the same channels for their normals.") - .def("num_vert", &Manifold::NumVert, - "The number of vertices in the Manifold.") - .def("num_edge", &Manifold::NumEdge, - "The number of edges in the Manifold.") - .def("num_tri", &Manifold::NumTri, - "The number of triangles in the Manifold.") - .def("num_prop", &Manifold::NumProp, - "The number of properties per vertex in the Manifold") + manifold__get_mesh_gl_731af09ec81f) + .def("num_vert", &Manifold::NumVert, manifold__num_vert_93a4106c8a53) + .def("num_edge", &Manifold::NumEdge, manifold__num_edge_61b0f3dc7f99) + .def("num_tri", &Manifold::NumTri, manifold__num_tri_56a67713dff8) + .def("num_prop", &Manifold::NumProp, manifold__num_prop_745ca800e017) .def("num_prop_vert", &Manifold::NumPropVert, - "The number of property vertices in the Manifold. This will always " - "be >= NumVert, as some physical vertices may be duplicated to " - "account for different properties on different neighboring " - "triangles.") - .def("precision", &Manifold::Precision, - "Returns the precision of this Manifold's vertices, which tracks " - "the approximate rounding error over all the transforms and " - "operations that have led to this state. Any triangles that are " - "colinear within this precision are considered degenerate and " - "removed. This is the value of ε defining " - "[ε-valid](https://github.com/elalish/manifold/wiki/" - "Manifold-Library#definition-of-%CE%B5-valid).") - .def("genus", &Manifold::Genus, - "The genus is a topological property of the manifold, representing " - "the number of \"handles\". A sphere is 0, torus 1, etc. It is only " - "meaningful for a single mesh, so it is best to call Decompose() " - "first.") + manifold__num_prop_vert_a7ba865d3e11) + .def("precision", &Manifold::Precision, manifold__precision_bb888ab8ec11) + .def("genus", &Manifold::Genus, manifold__genus_75c215a950f8) .def( "volume", [](const Manifold &self) { return self.GetProperties().volume; }, @@ -429,67 +334,21 @@ NB_MODULE(manifold3d, m) { "Get the surface area of the manifold\n This is clamped to zero for " "a given face if they are within the Precision().") .def("original_id", &Manifold::OriginalID, - "If this mesh is an original, this returns its meshID that can be " - "referenced by product manifolds' MeshRelation. If this manifold is " - "a product, this returns -1.") + manifold__original_id_d1c064807f94) .def("as_original", &Manifold::AsOriginal, - "This function condenses all coplanar faces in the relation, and " - "collapses those edges. In the process the relation to ancestor " - "meshes is lost and this new Manifold is marked an original. " - "Properties are preserved, so if they do not match across an edge, " - "that edge will be kept.") - .def("is_empty", &Manifold::IsEmpty, - "Does the Manifold have any triangles?") - .def( - "decompose", &Manifold::Decompose, - "This operation returns a vector of Manifolds that are " - "topologically disconnected. If everything is connected, the vector " - "is length one, containing a copy of the original. It is the inverse " - "operation of Compose().") + manifold__as_original_2653ccf900dd) + .def("is_empty", &Manifold::IsEmpty, manifold__is_empty_8f3c4e98cca8) + .def("decompose", &Manifold::Decompose, manifold__decompose_88ccab82c740) .def("split", &Manifold::Split, nb::arg("cutter"), - "Split cuts this manifold in two using the cutter manifold. The " - "first result is the intersection, second is the difference. This " - "is more efficient than doing them separately." - "\n\n" - ":param cutter: This is the manifold to cut by.\n") + manifold__split_fc2847c7afae) .def("split_by_plane", &Manifold::SplitByPlane, nb::arg("normal"), - nb::arg("origin_offset"), - "Convenient version of Split() for a half-space." - "\n\n" - ":param normal: This vector is normal to the cutting plane and its " - "length does not matter. The first result is in the direction of " - "this vector, the second result is on the opposite side.\n" - ":param origin_offset: The distance of the plane from the origin in " - "the direction of the normal vector.") - .def( - "trim_by_plane", &Manifold::TrimByPlane, nb::arg("normal"), - nb::arg("origin_offset"), - "Identical to SplitByPlane(), but calculating and returning only the " - "first result." - "\n\n" - ":param normal: This vector is normal to the cutting plane and its " - "length does not matter. The result is in the direction of this " - "vector from the plane.\n" - ":param origin_offset: The distance of the plane from the origin in " - "the direction of the normal vector.") + nb::arg("origin_offset"), manifold__split_by_plane_f411533a14aa) + .def("trim_by_plane", &Manifold::TrimByPlane, nb::arg("normal"), + nb::arg("origin_offset"), manifold__trim_by_plane_066ac34a84b0) .def("slice", &Manifold::Slice, nb::arg("height"), - "Returns the cross section of this object parallel to the X-Y plane " - "at the specified height. Using a height equal to the bottom of the " - "bounding box will return the bottom faces, while using a height " - "equal to the top of the bounding box will return empty." - "\n\n" - ":param height: The Z-level of the slice, defaulting to zero.") - .def("project", &Manifold::Project, - "Returns a cross section representing the projected outline of this " - "object onto the X-Y plane.") - .def("status", &Manifold::Status, - "Returns the reason for an input Mesh producing an empty Manifold. " - "This Status only applies to Manifolds newly-created from an input " - "Mesh - once they are combined into a new Manifold via operations, " - "the status reverts to NoError, simply processing the problem mesh " - "as empty. Likewise, empty meshes may still show NoError, for " - "instance if they are small enough relative to their precision to " - "be collapsed to nothing.") + manifold__slice_7d90a75e7913) + .def("project", &Manifold::Project, manifold__project_e28980e0f682) + .def("status", &Manifold::Status, manifold__status_b1c2b69ee41e) .def( "bounding_box", [](const Manifold &self) { @@ -515,81 +374,23 @@ NB_MODULE(manifold3d, m) { }, nb::arg("mesh"), nb::arg("sharpened_edges") = nb::list(), nb::arg("edge_smoothness") = nb::list(), - "Constructs a smooth version of the input mesh by creating tangents; " - "this method will throw if you have supplied tangents with your " - "mesh already. The actual triangle resolution is unchanged; use the " - "Refine() method to interpolate to a higher-resolution curve." - "\n\n" - "By default, every edge is calculated for maximum smoothness (very " - "much approximately), attempting to minimize the maximum mean " - "Curvature magnitude. No higher-order derivatives are considered, " - "as the interpolation is independent per triangle, only sharing " - "constraints on their boundaries." - "\n\n" - ":param mesh: input Mesh.\n" - ":param sharpened_edges: If desired, you can supply a vector of " - "sharpened halfedges, which should in general be a small subset of " - "all halfedges. The halfedge index is " - "(3 * triangle index + [0,1,2] where 0 is the edge between triVert 0 " - "and 1, etc)." - ":param edge_smoothness: must be same length as shapened_edges. " - "Each entry specifies the desired smoothness (between zero and one, " - "with one being the default for all unspecified halfedges)" - "\n\n" - "At a smoothness value of zero, a sharp crease is made. The " - "smoothness is interpolated along each edge, so the specified value " - "should be thought of as an average. Where exactly two sharpened " - "edges meet at a vertex, their tangents are rotated to be colinear " - "so that the sharpened edge can be continuous. Vertices with only " - "one sharpened edge are completely smooth, allowing sharpened edges " - "to smoothly vanish at termination. A single vertex can be sharpened " - "by sharping all edges that are incident on it, allowing cones to be " - "formed.") + // todo params slightly diff + manifold__smooth_66eaffd9331b) .def_static("compose", &Manifold::Compose, nb::arg("manifolds"), - "combine several manifolds into one without checking for " - "intersections.") - .def_static( - "tetrahedron", &Manifold::Tetrahedron, - "Constructs a tetrahedron centered at the origin with one vertex at " - "(1,1,1) and the rest at similarly symmetric points.") - .def_static( - "cube", &Manifold::Cube, nb::arg("size") = glm::vec3{1, 1, 1}, - nb::arg("center") = false, - "Constructs a unit cube (edge lengths all one), by default in the " - "first octant, touching the origin." - "\n\n" - ":param size: The X, Y, and Z dimensions of the box.\n" - ":param center: Set to true to shift the center to the origin.") - .def_static( - "cylinder", &Manifold::Cylinder, nb::arg("height"), - nb::arg("radius_low"), nb::arg("radius_high") = -1.0f, - nb::arg("circular_segments") = 0, nb::arg("center") = false, - "A convenience constructor for the common case of extruding a " - "circle. Can also form cones if both radii are specified." - "\n\n" - ":param height: Z-extent\n" - ":param radius_low: Radius of bottom circle. Must be positive.\n" - ":param radius_high: Radius of top circle. Can equal zero. Default " - "(-1) is equal to radius_low.\n" - ":param circular_segments: How many line segments to use around the " - "circle. Default (-1) is calculated by the static Defaults.\n" - ":param center: Set to true to shift the center to the origin. " - "Default is origin at the bottom.") - .def_static( - "sphere", &Manifold::Sphere, nb::arg("radius"), - nb::arg("circular_segments") = 0, - "Constructs a geodesic sphere of a given radius.\n" - "\n" - ":param radius: Radius of the sphere. Must be positive.\n" - ":param circular_segments: Number of segments along its diameter. " - "This number will always be rounded up to the nearest factor of " - "four, as this sphere is constructed by refining an octahedron. This " - "means there are a circle of vertices on all three of the axis " - "planes. Default is calculated by the static Defaults.") + manifold__compose_6c382bb1612b) + .def_static("tetrahedron", &Manifold::Tetrahedron, + manifold__tetrahedron_7e95f682f35b) + .def_static("cube", &Manifold::Cube, nb::arg("size") = glm::vec3{1, 1, 1}, + nb::arg("center") = false, manifold__cube_64d1c43c52ed) + .def_static("cylinder", &Manifold::Cylinder, nb::arg("height"), + nb::arg("radius_low"), nb::arg("radius_high") = -1.0f, + nb::arg("circular_segments") = 0, nb::arg("center") = false, + manifold__cylinder_af7b1b7dc893) + .def_static("sphere", &Manifold::Sphere, nb::arg("radius"), + nb::arg("circular_segments") = 0, + manifold__sphere_6781451731f0) .def_static("reserve_ids", Manifold::ReserveIDs, nb::arg("n"), - "Returns the first of n sequential new unique mesh IDs for " - "marking sets of triangles that can be looked up after " - "further operations. Assign to MeshGL.runOriginalID vector"); + manifold__reserve_ids_a514f84c6343); nb::class_(m, "Mesh") .def( @@ -689,15 +490,15 @@ NB_MODULE(manifold3d, m) { "level_set", [](const std::function &f, std::vector bounds, float edgeLength, float level = 0.0) { - // Same format as Manifold.bounding_box - Box bound = {glm::vec3(bounds[0], bounds[1], bounds[2]), - glm::vec3(bounds[3], bounds[4], bounds[5])}; - - std::function cppToPython = [&f](glm::vec3 v) { - return f(v.x, v.y, v.z); - }; - Mesh result = LevelSet(cppToPython, bound, edgeLength, level, false); - return MeshGL(result); + // Same format as Manifold.bounding_box + Box bound = {glm::vec3(bounds[0], bounds[1], bounds[2]), + glm::vec3(bounds[3], bounds[4], bounds[5])}; + + std::function cppToPython = [&f](glm::vec3 v) { + return f(v.x, v.y, v.z); + }; + Mesh result = LevelSet(cppToPython, bound, edgeLength, level, false); + return MeshGL(result); }, nb::arg("f"), nb::arg("bounds"), nb::arg("edgeLength"), nb::arg("level") = 0.0, @@ -774,30 +575,19 @@ NB_MODULE(manifold3d, m) { "onwards). This class makes use of the " "[Clipper2](http://www.angusj.com/clipper2/Docs/Overview.htm) library " "for polygon clipping (boolean) and offsetting operations.") - .def(nb::init<>(), "Construct empty CrossSection object") + .def(nb::init<>(), cross_section__cross_section_381df1ec382b) .def(nb::init>, CrossSection::FillRule>(), nb::arg("polygons"), nb::arg("fillrule") = CrossSection::FillRule::Positive, - "Create a 2d cross-section from a set of contours (complex " - "polygons). A boolean union operation (with Positive filling rule " - "by default) performed to combine overlapping polygons and ensure " - "the resulting CrossSection is free of intersections." - "\n\n" - ":param contours: A set of closed paths describing zero or more " - "complex polygons.\n" - ":param fillrule: The filling rule used to interpret polygon " - "sub-regions in contours.") - .def("area", &CrossSection::Area, - "Return the total area covered by complex polygons making up the " - "CrossSection.") + cross_section__cross_section_4459865c1e2f) + .def("area", &CrossSection::Area, cross_section__area_f5458809be32) .def("num_vert", &CrossSection::NumVert, - "Return the number of vertices in the CrossSection.") + cross_section__num_vert_9dd2efd31062) .def("num_contour", &CrossSection::NumContour, - "Return the number of contours (both outer and inner paths) in the " - "CrossSection.") + cross_section__num_contour_5894fa74e5f5) .def("is_empty", &CrossSection::IsEmpty, - "Does the CrossSection contain any contours?") + cross_section__is_empty_25b4b2d4e0ad) .def( "bounds", [](const CrossSection &self) { @@ -807,21 +597,11 @@ NB_MODULE(manifold3d, m) { "Return bounding box of CrossSection as tuple(" "min_x, min_y, max_x, max_y)") .def("translate", &CrossSection::Translate, nb::arg("v"), - "Move this CrossSection in space. This operation can be chained. " - "Transforms are combined and applied lazily." - "\n\n" - ":param v: The vector to add to every vertex.") + cross_section__translate_339895387e15) .def("rotate", &CrossSection::Rotate, nb::arg("angle"), - "Applies a (Z-axis) rotation to the CrossSection, in degrees. This " - "operation can be chained. Transforms are combined and applied " - "lazily." - "\n\n" - ":param degrees: degrees about the Z-axis to rotate.") + cross_section__rotate_7c6bae9524e7) .def("scale", &CrossSection::Scale, nb::arg("v"), - "Scale this CrossSection in space. This operation can be chained. " - "Transforms are combined and applied lazily." - "\n\n" - ":param v: The vector to multiply every vertex by per component.") + cross_section__scale_8913c878f656) .def( "scale", [](const CrossSection &self, float s) { @@ -832,140 +612,46 @@ NB_MODULE(manifold3d, m) { "Transforms are combined and applied lazily." "\n\n" ":param s: The scalar to multiply every vertex by per component.") - .def( - "mirror", &CrossSection::Mirror, nb::arg("ax"), - "Mirror this CrossSection over the arbitrary axis described by the " - "unit form of the given vector. If the length of the vector is zero, " - "an empty CrossSection is returned. This operation can be chained. " - "Transforms are combined and applied lazily." - "\n\n" - ":param ax: the axis to be mirrored over") - .def( - "transform", &CrossSection::Transform, nb::arg("m"), - "Transform this CrossSection in space. The first two columns form a " - "2x2 matrix transform and the last is a translation vector. This " - "operation can be chained. Transforms are combined and applied " - "lazily." - "\n\n" - ":param m: The affine transform matrix to apply to all the vertices.") + .def("mirror", &CrossSection::Mirror, nb::arg("ax"), + cross_section__mirror_a10119e40f21) + .def("transform", &CrossSection::Transform, nb::arg("m"), + cross_section__transform_baddfca7ede3) .def("warp", &CrossSection::Warp, nb::arg("f"), - "Move the vertices of this CrossSection (creating a new one) " - "according to any arbitrary input function, followed by a union " - "operation (with a Positive fill rule) that ensures any introduced " - "intersections are not included in the result." - "\n\n" - ":param warpFunc: A function that modifies a given vertex position.") + cross_section__warp_180cafeaaad1) .def("warp_batch", &CrossSection::WarpBatch, nb::arg("f"), - "Same as CrossSection.warp but calls `f` with a " - "ndarray(shape=(N,2), dtype=float) and expects an ndarray " - "of the same shape and type in return. The input array can be " - "modified and returned if desired. " - "\n\n" - ":param f: A function that modifies multiple vertex positions.") + cross_section__warp_batch_f843ce28c677) .def("simplify", &CrossSection::Simplify, nb::arg("epsilon") = 1e-6, - "Remove vertices from the contours in this CrossSection that are " - "less than the specified distance epsilon from an imaginary line " - "that passes through its two adjacent vertices. Near duplicate " - "vertices and collinear points will be removed at lower epsilons, " - "with elimination of line segments becoming increasingly aggressive " - "with larger epsilons." - "\n\n" - "It is recommended to apply this function following Offset, in " - "order to clean up any spurious tiny line segments introduced that " - "do not improve quality in any meaningful way. This is particularly " - "important if further offseting operations are to be performed, " - "which would compound the issue.") + cross_section__simplify_dbac3e60acf4) .def("offset", &CrossSection::Offset, nb::arg("delta"), nb::arg("join_type"), nb::arg("miter_limit") = 2.0, - nb::arg("circular_segments") = 0, - "Inflate the contours in CrossSection by the specified delta, " - "handling corners according to the given JoinType." - "\n\n" - ":param delta: Positive deltas will cause the expansion of " - "outlining contours to expand, and retraction of inner (hole) " - "contours. Negative deltas will have the opposite effect.\n" - ":param jt: The join type specifying the treatment of contour joins " - "(corners).\n" - ":param miter_limit: The maximum distance in multiples of delta " - "that vertices can be offset from their original positions with " - "before squaring is applied, when the join type is Miter " - "(default is 2, which is the minimum allowed). See the [Clipper2 " - "MiterLimit](http://www.angusj.com/clipper2/Docs/Units/" - "Clipper.Offset/Classes/ClipperOffset/Properties/MiterLimit.htm) " - "page for a visual example.\n" - ":param circular_segments: Number of segments per 360 degrees of " - "JoinType::Round corners (roughly, the number of vertices " - "that will be added to each contour). Default is calculated by the " - "static Quality defaults according to the radius.") - .def(nb::self + nb::self, "Boolean union.") - .def(nb::self - nb::self, "Boolean difference.") - .def(nb::self ^ nb::self, "Boolean intersection.") + nb::arg("circular_segments") = 0, cross_section__offset_b3675b4b0ed0) + .def(nb::self + nb::self, cross_section__operator_plus_d3c26b9c5ca3) + .def(nb::self - nb::self, cross_section__operator_minus_04b4d727817f) + .def(nb::self ^ nb::self, cross_section__operator_xor_76de317c9be1) .def( "hull", [](const CrossSection &self) { return self.Hull(); }, - "Compute the convex hull of this cross-section.") + cross_section__hull_3f1ad9eaa499) .def_static( "batch_hull", [](std::vector cs) { return CrossSection::Hull(cs); }, - nb::arg("cross_sections"), - "Compute the convex hull enveloping a set of cross-sections.") + nb::arg("cross_sections"), cross_section__hull_014f76304d06) .def_static( "hull_points", [](std::vector pts) { return CrossSection::Hull(pts); }, - nb::arg("pts"), - "Compute the convex hull enveloping a set of 2d points.") + nb::arg("pts"), cross_section__hull_c94ccc3c0fe6) .def("decompose", &CrossSection::Decompose, - "This operation returns a vector of CrossSections that are " - "topologically disconnected, each containing one outline contour " - "with zero or more holes.") + cross_section__decompose_17ae5159e6e5) .def("to_polygons", &CrossSection::ToPolygons, - "Returns the vertices of the cross-section's polygons " - "as a List[ndarray(shape=(*,2), dtype=float)].") - .def( - "extrude", &Manifold::Extrude, nb::arg("height"), - nb::arg("n_divisions") = 0, nb::arg("twist_degrees") = 0.0f, - nb::arg("scale_top") = std::make_tuple(1.0f, 1.0f), - "Constructs a manifold from the set of polygons by extruding them " - "along the Z-axis.\n" - "\n" - ":param height: Z-extent of extrusion.\n" - ":param nDivisions: Number of extra copies of the crossSection to " - "insert into the shape vertically; especially useful in combination " - "with twistDegrees to avoid interpolation artifacts. Default is " - "none.\n" - ":param twistDegrees: Amount to twist the top crossSection relative " - "to the bottom, interpolated linearly for the divisions in between.\n" - ":param scaleTop: Amount to scale the top (independently in X and " - "Y). If the scale is (0, 0), a pure cone is formed with only a " - "single vertex at the top. Default (1, 1).") - .def( - "revolve", &Manifold::Revolve, nb::arg("circular_segments") = 0, - nb::arg("revolve_degrees") = 360.0, - "Constructs a manifold from the set of polygons by revolving this " - "cross-section around its Y-axis and then setting this as the Z-axis " - "of the resulting manifold. If the polygons cross the Y-axis, only " - "the part on the positive X side is used. Geometrically valid input " - "will result in geometrically valid output.\n" - "\n" - ":param circular_segments: Number of segments along its diameter. " - "Default is calculated by the static Defaults.\n" - ":param revolve_degrees: rotation angle for the sweep.") - .def_static( - "square", &CrossSection::Square, nb::arg("size"), - nb::arg("center") = false, - "Constructs a square with the given XY dimensions. By default it is " - "positioned in the first quadrant, touching the origin. If any " - "dimensions in size are negative, or if all are zero, an empty " - "Manifold will be returned." - "\n\n" - ":param size: The X, and Y dimensions of the square.\n" - ":param center: Set to true to shift the center to the origin.") - .def_static( - "circle", &CrossSection::Circle, nb::arg("radius"), - nb::arg("circular_segments") = 0, - "Constructs a circle of a given radius." - "\n\n" - ":param radius: Radius of the circle. Must be positive.\n" - ":param circular_segments: Number of segments along its diameter. " - "Default is calculated by the static Quality defaults according to " - "the radius."); + cross_section__to_polygons_6f4cb60dbd78) + .def("extrude", &Manifold::Extrude, nb::arg("height"), + nb::arg("n_divisions") = 0, nb::arg("twist_degrees") = 0.0f, + nb::arg("scale_top") = std::make_tuple(1.0f, 1.0f), + manifold__extrude_bc84f1554abe) + .def("revolve", &Manifold::Revolve, nb::arg("circular_segments") = 0, + nb::arg("revolve_degrees") = 360.0, manifold__revolve_c916603e7a75) + .def_static("square", &CrossSection::Square, nb::arg("size"), + nb::arg("center") = false, cross_section__square_67088e89831e) + .def_static("circle", &CrossSection::Circle, nb::arg("radius"), + nb::arg("circular_segments") = 0, + cross_section__circle_72e80d0e2b44); } From 9e97798d60667cd49d9f3caefa9632689111dc8e Mon Sep 17 00:00:00 2001 From: K Date: Tue, 19 Dec 2023 18:38:32 -0500 Subject: [PATCH 3/8] remove sys.path debug code --- bindings/python/examples/gyroid_module.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bindings/python/examples/gyroid_module.py b/bindings/python/examples/gyroid_module.py index e57040531..3a7119dc4 100644 --- a/bindings/python/examples/gyroid_module.py +++ b/bindings/python/examples/gyroid_module.py @@ -15,11 +15,7 @@ """ import math -import sys import numpy as np - -sys.path.append("/Users/k/projects/python/badcad/wip/manifold/build/bindings/python") - from manifold3d import Mesh, Manifold From 2f36394a983037a35db9cb7970f45e6c817d997f Mon Sep 17 00:00:00 2001 From: K Date: Wed, 20 Dec 2023 18:11:12 -0500 Subject: [PATCH 4/8] docstrings use param names --- ...{docstrings.inl => autogen_docstrings.inl} | 252 +++++++++--------- bindings/python/gen_docs.py | 51 ++-- bindings/python/manifold3d.cpp | 215 ++++++++------- 3 files changed, 258 insertions(+), 260 deletions(-) rename bindings/python/{docstrings.inl => autogen_docstrings.inl} (64%) diff --git a/bindings/python/docstrings.inl b/bindings/python/autogen_docstrings.inl similarity index 64% rename from bindings/python/docstrings.inl rename to bindings/python/autogen_docstrings.inl index e37dfdce0..3fff48c12 100644 --- a/bindings/python/docstrings.inl +++ b/bindings/python/autogen_docstrings.inl @@ -1,23 +1,29 @@ #pragma once -#include -namespace manifold_docs { -const auto cross_section__area_f5458809be32 = R"___(Return the total area covered by complex polygons making up the + +// AUTOGENERATED BY gen_docs.py + +namespace manifold_docstrings { +const char* cross_section__area = R"___(Return the total area covered by complex polygons making up the CrossSection.)___"; -const auto cross_section__batch_boolean_d17f25dd9195 = R"___(Perform the given boolean operation on a list of CrossSections. In case of +const char* cross_section__batch_boolean__cross_sections__op = R"___(Perform the given boolean operation on a list of CrossSections. In case of Subtract, all CrossSections in the tail are differenced from the head.)___"; -const auto cross_section__boolean_d0a145760f3d = R"___(Perform the given boolean operation between this and another CrossSection.)___"; -const auto cross_section__bounds_47149b83cada = R"___(Returns the axis-aligned bounding rectangle of all the CrossSections' +const char* cross_section__boolean__second__op = R"___(Perform the given boolean operation between this and another CrossSection.)___"; +const char* cross_section__bounds = R"___(Returns the axis-aligned bounding rectangle of all the CrossSections' vertices.)___"; -const auto cross_section__circle_72e80d0e2b44 = R"___(Constructs a circle of a given radius. +const char* cross_section__circle__radius__circular_segments = R"___(Constructs a circle of a given radius. :param radius: Radius of the circle. Must be positive. :param circular_segments: Number of segments along its diameter. Default is calculated by the static Quality defaults according to the radius.)___"; -const auto cross_section__compose_1a076380de50 = R"___(Construct a CrossSection from a vector of other CrossSections (batch +const char* cross_section__compose__cross_sections = R"___(Construct a CrossSection from a vector of other CrossSections (batch boolean union).)___"; -const auto cross_section__cross_section_138b32c68b57 = R"___(Create a 2d cross-section from an axis-aligned rectangle (bounding box). -:param rect: An axis-aligned rectangular bounding box.)___"; -const auto cross_section__cross_section_381df1ec382b = R"___(The default constructor is an empty cross-section (containing no contours).)___"; -const auto cross_section__cross_section_4459865c1e2f = R"___(Create a 2d cross-section from a set of contours (complex polygons). A +const char* cross_section__cross_section = R"___(The default constructor is an empty cross-section (containing no contours).)___"; +const char* cross_section__cross_section__contour__fillrule = R"___(Create a 2d cross-section from a single contour. A boolean union operation +(with Positive filling rule by default) is performed to ensure the +resulting CrossSection is free of self-intersections. +:param contour: A closed path outlining the desired cross-section. +:param fillrule: The filling rule used to interpret polygon sub-regions +created by self-intersections in contour.)___"; +const char* cross_section__cross_section__contours__fillrule = R"___(Create a 2d cross-section from a set of contours (complex polygons). A boolean union operation (with Positive filling rule by default) is performed to combine overlapping polygons and ensure the resulting CrossSection is free of intersections. @@ -25,42 +31,38 @@ CrossSection is free of intersections. polygons. :param fillrule: The filling rule used to interpret polygon sub-regions in contours.)___"; -const auto cross_section__cross_section_aad37c257207 = R"___(Create a 2d cross-section from a single contour. A boolean union operation -(with Positive filling rule by default) is performed to ensure the -resulting CrossSection is free of self-intersections. -:param contour: A closed path outlining the desired cross-section. -:param fillrule: The filling rule used to interpret polygon sub-regions -created by self-intersections in contour.)___"; -const auto cross_section__cross_section_c56006cfa78c = R"___(The copy constructor avoids copying the underlying paths vector (sharing +const char* cross_section__cross_section__other = R"___(The copy constructor avoids copying the underlying paths vector (sharing with its parent via shared_ptr), however subsequent transformations, and their application will not be shared. It is generally recommended to avoid this, opting instead to simply create CrossSections with the available const methods.)___"; -const auto cross_section__decompose_17ae5159e6e5 = R"___(This operation returns a vector of CrossSections that are topologically +const char* cross_section__cross_section__rect = R"___(Create a 2d cross-section from an axis-aligned rectangle (bounding box). +:param rect: An axis-aligned rectangular bounding box.)___"; +const char* cross_section__decompose = R"___(This operation returns a vector of CrossSections that are topologically disconnected, each containing one outline contour with zero or more holes.)___"; -const auto cross_section__hull_014f76304d06 = R"___(Compute the convex hull enveloping a set of cross-sections. +const char* cross_section__hull = R"___(Compute the convex hull of this cross-section.)___"; +const char* cross_section__hull__cross_sections = R"___(Compute the convex hull enveloping a set of cross-sections. :param cross_sections: A vector of cross-sections over which to compute a convex hull.)___"; -const auto cross_section__hull_3f1ad9eaa499 = R"___(Compute the convex hull of this cross-section.)___"; -const auto cross_section__hull_c94ccc3c0fe6 = R"___(Compute the convex hull of a set of points/polygons. If the given points are +const char* cross_section__hull__polys = R"___(Compute the convex hull of a set of points/polygons. If the given points are fewer than 3, an empty CrossSection will be returned. :param pts: A vector of vectors of 2-dimensional points over which to compute a convex hull.)___"; -const auto cross_section__hull_e609862eb256 = R"___(Compute the convex hull of a set of points. If the given points are fewer +const char* cross_section__hull__pts = R"___(Compute the convex hull of a set of points. If the given points are fewer than 3, an empty CrossSection will be returned. :param pts: A vector of 2-dimensional points over which to compute a convex hull.)___"; -const auto cross_section__is_empty_25b4b2d4e0ad = R"___(Does the CrossSection contain any contours?)___"; -const auto cross_section__mirror_a10119e40f21 = R"___(Mirror this CrossSection over the arbitrary axis described by the unit form +const char* cross_section__is_empty = R"___(Does the CrossSection contain any contours?)___"; +const char* cross_section__mirror__ax = R"___(Mirror this CrossSection over the arbitrary axis described by the unit form of the given vector. If the length of the vector is zero, an empty CrossSection is returned. This operation can be chained. Transforms are combined and applied lazily. :param ax: the axis to be mirrored over)___"; -const auto cross_section__num_contour_5894fa74e5f5 = R"___(Return the number of contours (both outer and inner paths) in the +const char* cross_section__num_contour = R"___(Return the number of contours (both outer and inner paths) in the CrossSection.)___"; -const auto cross_section__num_vert_9dd2efd31062 = R"___(Return the number of vertices in the CrossSection.)___"; -const auto cross_section__offset_b3675b4b0ed0 = R"___(Inflate the contours in CrossSection by the specified delta, handling +const char* cross_section__num_vert = R"___(Return the number of vertices in the CrossSection.)___"; +const char* cross_section__offset__delta__jointype__miter_limit__circular_segments = R"___(Inflate the contours in CrossSection by the specified delta, handling corners according to the given JoinType. :param delta: Positive deltas will cause the expansion of outlining contours to expand, and retraction of inner (hole) contours. Negative deltas will @@ -77,23 +79,23 @@ page for a visual example. JoinType::Round corners (roughly, the number of vertices that will be added to each contour). Default is calculated by the static Quality defaults according to the radius.)___"; -const auto cross_section__operator_minus_04b4d727817f = R"___(Compute the boolean difference of a (clip) cross-section from another +const char* cross_section__operator_minus__q = R"___(Compute the boolean difference of a (clip) cross-section from another (subject).)___"; -const auto cross_section__operator_minus_eq_bdfdaab4f47a = R"___(Compute the boolean difference of a (clip) cross-section from a another +const char* cross_section__operator_minus_eq__q = R"___(Compute the boolean difference of a (clip) cross-section from a another (subject), assigning the result to the subject.)___"; -const auto cross_section__operator_plus_d3c26b9c5ca3 = R"___(Compute the boolean union between two cross-sections.)___"; -const auto cross_section__operator_plus_eq_9daa44be9a1d = R"___(Compute the boolean union between two cross-sections, assigning the result +const char* cross_section__operator_plus__q = R"___(Compute the boolean union between two cross-sections.)___"; +const char* cross_section__operator_plus_eq__q = R"___(Compute the boolean union between two cross-sections, assigning the result to the first.)___"; -const auto cross_section__operator_xor_76de317c9be1 = R"___(Compute the boolean intersection between two cross-sections.)___"; -const auto cross_section__operator_xor_eq_705353363fb1 = R"___(Compute the boolean intersection between two cross-sections, assigning the +const char* cross_section__operator_xor__q = R"___(Compute the boolean intersection between two cross-sections.)___"; +const char* cross_section__operator_xor_eq__q = R"___(Compute the boolean intersection between two cross-sections, assigning the result to the first.)___"; -const auto cross_section__rotate_7c6bae9524e7 = R"___(Applies a (Z-axis) rotation to the CrossSection, in degrees. This operation +const char* cross_section__rotate__degrees = R"___(Applies a (Z-axis) rotation to the CrossSection, in degrees. This operation can be chained. Transforms are combined and applied lazily. :param degrees: degrees about the Z-axis to rotate.)___"; -const auto cross_section__scale_8913c878f656 = R"___(Scale this CrossSection in space. This operation can be chained. Transforms +const char* cross_section__scale__scale = R"___(Scale this CrossSection in space. This operation can be chained. Transforms are combined and applied lazily. :param v: The vector to multiply every vertex by per component.)___"; -const auto cross_section__simplify_dbac3e60acf4 = R"___(Remove vertices from the contours in this CrossSection that are less than +const char* cross_section__simplify__epsilon = R"___(Remove vertices from the contours in this CrossSection that are less than the specified distance epsilon from an imaginary line that passes through its two adjacent vertices. Near duplicate vertices and collinear points will be removed at lower epsilons, with elimination of line segments @@ -102,37 +104,37 @@ It is recommended to apply this function following Offset, in order to clean up any spurious tiny line segments introduced that do not improve quality in any meaningful way. This is particularly important if further offseting operations are to be performed, which would compound the issue.)___"; -const auto cross_section__square_67088e89831e = R"___(Constructs a square with the given XY dimensions. By default it is +const char* cross_section__square__size__center = R"___(Constructs a square with the given XY dimensions. By default it is positioned in the first quadrant, touching the origin. If any dimensions in size are negative, or if all are zero, an empty Manifold will be returned. :param size: The X, and Y dimensions of the square. :param center: Set to true to shift the center to the origin.)___"; -const auto cross_section__to_polygons_6f4cb60dbd78 = R"___(Return the contours of this CrossSection as a Polygons.)___"; -const auto cross_section__transform_baddfca7ede3 = R"___(Transform this CrossSection in space. The first two columns form a 2x2 +const char* cross_section__to_polygons = R"___(Return the contours of this CrossSection as a Polygons.)___"; +const char* cross_section__transform__m = R"___(Transform this CrossSection in space. The first two columns form a 2x2 matrix transform and the last is a translation vector. This operation can be chained. Transforms are combined and applied lazily. :param m: The affine transform matrix to apply to all the vertices.)___"; -const auto cross_section__translate_339895387e15 = R"___(Move this CrossSection in space. This operation can be chained. Transforms +const char* cross_section__translate__v = R"___(Move this CrossSection in space. This operation can be chained. Transforms are combined and applied lazily. :param v: The vector to add to every vertex.)___"; -const auto cross_section__warp_180cafeaaad1 = R"___(Move the vertices of this CrossSection (creating a new one) according to +const char* cross_section__warp__warp_func = R"___(Move the vertices of this CrossSection (creating a new one) according to any arbitrary input function, followed by a union operation (with a Positive fill rule) that ensures any introduced intersections are not included in the result. :param warp_func: A function that modifies a given vertex position.)___"; -const auto cross_section__warp_batch_f843ce28c677 = R"___(Same as CrossSection::Warp but calls warpFunc with +const char* cross_section__warp_batch__warp_func = R"___(Same as CrossSection::Warp but calls warpFunc with a VecView which is roughly equivalent to std::span pointing to all vec2 elements to be modified in-place :param warp_func: A function that modifies multiple vertex positions.)___"; -const auto get_circular_segments_3f31ccff2bbc = R"___(Determine the result of the SetMinCircularAngle(), +const char* get_circular_segments__radius = R"___(Determine the result of the SetMinCircularAngle(), SetMinCircularEdgeLength(), and SetCircularSegments() defaults. :param radius: For a given radius of circle, determine how many default segments there will be.)___"; -const auto manifold__as_original_2653ccf900dd = R"___(This function condenses all coplanar faces in the relation, and +const char* manifold__as_original = R"___(This function condenses all coplanar faces in the relation, and collapses those edges. In the process the relation to ancestor meshes is lost and this new Manifold is marked an original. Properties are preserved, so if they do not match across an edge, that edge will be kept.)___"; -const auto manifold__boolean_f506a86f19f9 = R"___(The central operation of this library: the Boolean combines two manifolds +const char* manifold__boolean__second__op = R"___(The central operation of this library: the Boolean combines two manifolds into another by calculating their intersections and removing the unused portions. [ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid) @@ -142,8 +144,8 @@ These operations are optimized to produce nearly-instant results if either input is empty or their bounding boxes do not overlap. :param second: The other Manifold. :param op: The type of operation to perform.)___"; -const auto manifold__bounding_box_f0b931ac75e5 = R"___(Returns the axis-aligned bounding box of all the Manifold's vertices.)___"; -const auto manifold__calculate_curvature_0bb0af918c76 = R"___(Curvature is the inverse of the radius of curvature, and signed such that +const char* manifold__bounding_box = R"___(Returns the axis-aligned bounding box of all the Manifold's vertices.)___"; +const char* manifold__calculate_curvature__gaussian_idx__mean_idx = R"___(Curvature is the inverse of the radius of curvature, and signed such that positive is convex and negative is concave. There are two orthogonal principal curvatures at any point on a manifold, with one maximum and the other minimum. Gaussian curvature is their product, while mean @@ -155,16 +157,16 @@ will be automatically expanded to include the channel index specified. :param mean_idx: The property channel index in which to store the mean curvature. An index < 0 will be ignored (stores nothing). The property set will be automatically expanded to include the channel index specified.)___"; -const auto manifold__compose_6c382bb1612b = R"___(Constructs a new manifold from a vector of other manifolds. This is a purely +const char* manifold__compose__manifolds = R"___(Constructs a new manifold from a vector of other manifolds. This is a purely topological operation, so care should be taken to avoid creating overlapping results. It is the inverse operation of Decompose(). :param manifolds: A vector of Manifolds to lazy-union together.)___"; -const auto manifold__cube_64d1c43c52ed = R"___(Constructs a unit cube (edge lengths all one), by default in the first +const char* manifold__cube__size__center = R"___(Constructs a unit cube (edge lengths all one), by default in the first octant, touching the origin. If any dimensions in size are negative, or if all are zero, an empty Manifold will be returned. :param size: The X, Y, and Z dimensions of the box. :param center: Set to true to shift the center to the origin.)___"; -const auto manifold__cylinder_af7b1b7dc893 = R"___(A convenience constructor for the common case of extruding a circle. Can also +const char* manifold__cylinder__height__radius_low__radius_high__circular_segments__center = R"___(A convenience constructor for the common case of extruding a circle. Can also form cones if both radii are specified. :param height: Z-extent :param radius_low: Radius of bottom circle. Must be positive. @@ -174,10 +176,10 @@ radiusLow. Default is calculated by the static Defaults. :param center: Set to true to shift the center to the origin. Default is origin at the bottom.)___"; -const auto manifold__decompose_88ccab82c740 = R"___(This operation returns a vector of Manifolds that are topologically +const char* manifold__decompose = R"___(This operation returns a vector of Manifolds that are topologically disconnected. If everything is connected, the vector is length one, containing a copy of the original. It is the inverse operation of Compose().)___"; -const auto manifold__extrude_bc84f1554abe = R"___(Constructs a manifold from a set of polygons by extruding them along the +const char* manifold__extrude__cross_section__height__n_divisions__twist_degrees__scale_top = R"___(Constructs a manifold from a set of polygons by extruding them along the Z-axis. Note that high twistDegrees with small nDivisions may cause self-intersection. This is not checked here and it is up to the user to @@ -193,12 +195,12 @@ bottom, interpolated linearly for the divisions in between. scale is {0, 0}, a pure cone is formed with only a single vertex at the top. Note that scale is applied after twist. Default {1, 1}.)___"; -const auto manifold__genus_75c215a950f8 = R"___(The genus is a topological property of the manifold, representing the number +const char* manifold__genus = R"___(The genus is a topological property of the manifold, representing the number of "handles". A sphere is 0, torus 1, etc. It is only meaningful for a single mesh, so it is best to call Decompose() first.)___"; -const auto manifold__get_mesh_5654c880b26b = R"___(This returns a Mesh of simple vectors of vertices and triangles suitable for +const char* manifold__get_mesh = R"___(This returns a Mesh of simple vectors of vertices and triangles suitable for saving or other operations outside of the context of this library.)___"; -const auto manifold__get_mesh_gl_731af09ec81f = R"___(The most complete output of this library, returning a MeshGL that is designed +const char* manifold__get_mesh_gl__normal_idx = R"___(The most complete output of this library, returning a MeshGL that is designed to easily push into a renderer, including all interleaved vertex properties that may have been input. It also includes relations to all the input meshes that form a part of this result and the transforms applied to each. @@ -208,16 +210,21 @@ channels these are (x, y, z), which will cause this output MeshGL to automatically update these normals according to the applied transforms and front/back side. Each channel must be >= 3 and < numProp, and all original MeshGLs must use the same channels for their normals.)___"; -const auto manifold__get_properties_ba109b37a202 = R"___(Returns the surface area and volume of the manifold.)___"; -const auto manifold__hull_1fd92449f7cb = R"___(Compute the convex hull of this manifold.)___"; -const auto manifold__hull_b0113f48020a = R"___(Compute the convex hull of a set of points. If the given points are fewer +const char* manifold__get_properties = R"___(Returns the surface area and volume of the manifold.)___"; +const char* manifold__hull = R"___(Compute the convex hull of this manifold.)___"; +const char* manifold__hull__manifolds = R"___(Compute the convex hull enveloping a set of manifolds. +:param manifolds: A vector of manifolds over which to compute a convex hull.)___"; +const char* manifold__hull__pts = R"___(Compute the convex hull of a set of points. If the given points are fewer than 4, or they are all coplanar, an empty Manifold will be returned. :param pts: A vector of 3-dimensional points over which to compute a convex hull.)___"; -const auto manifold__hull_e1568f7f16f2 = R"___(Compute the convex hull enveloping a set of manifolds. -:param manifolds: A vector of manifolds over which to compute a convex hull.)___"; -const auto manifold__is_empty_8f3c4e98cca8 = R"___(Does the Manifold have any triangles?)___"; -const auto manifold__manifold_37129c244d43 = R"___(Convert a MeshGL into a Manifold, retaining its properties and merging only +const char* manifold__is_empty = R"___(Does the Manifold have any triangles?)___"; +const char* manifold__manifold = R"___(Construct an empty Manifold.)___"; +const char* manifold__manifold__mesh = R"___(Convert a Mesh into a Manifold. Will return an empty Manifold +and set an Error Status if the Mesh is not an oriented 2-manifold. Will +collapse degenerate triangles and unnecessary vertices. +:param mesh: The input Mesh.)___"; +const char* manifold__manifold__mesh_gl__property_tolerance = R"___(Convert a MeshGL into a Manifold, retaining its properties and merging only the positions according to the merge vectors. Will return an empty Manifold and set an Error Status if the result is not an oriented 2-manifold. Will collapse degenerate triangles and unnecessary vertices. @@ -233,59 +240,54 @@ neighboring triangles are considered to be on a property boundary edge. Property boundary edges will be retained across operations even if the triangles are coplanar. Defaults to 1e-5, which works well for most properties in the [-1, 1] range.)___"; -const auto manifold__manifold_6dd024847cd9 = R"___(Convert a Mesh into a Manifold. Will return an empty Manifold -and set an Error Status if the Mesh is not an oriented 2-manifold. Will -collapse degenerate triangles and unnecessary vertices. -:param mesh: The input Mesh.)___"; -const auto manifold__manifold_c130481162c9 = R"___(Construct an empty Manifold.)___"; -const auto manifold__matches_tri_normals_c662986590f4 = R"___(The triangle normal vectors are saved over the course of operations rather +const char* manifold__matches_tri_normals = R"___(The triangle normal vectors are saved over the course of operations rather than recalculated to avoid rounding error. This checks that triangles still match their normal vectors within Precision().)___"; -const auto manifold__mirror_d798a49656cc = R"___(Mirror this Manifold over the plane described by the unit form of the given +const char* manifold__mirror__normal = R"___(Mirror this Manifold over the plane described by the unit form of the given normal vector. If the length of the normal is zero, an empty Manifold is returned. This operation can be chained. Transforms are combined and applied lazily. :param normal: The normal vector of the plane to be mirrored over)___"; -const auto manifold__num_degenerate_tris_d86f985c281a = R"___(The number of triangles that are colinear within Precision(). This library +const char* manifold__num_degenerate_tris = R"___(The number of triangles that are colinear within Precision(). This library attempts to remove all of these, but it cannot always remove all of them without changing the mesh by too much.)___"; -const auto manifold__num_edge_61b0f3dc7f99 = R"___(The number of edges in the Manifold.)___"; -const auto manifold__num_overlaps_4a8cd704b45a = R"___(This is a checksum-style verification of the collider, simply returning the +const char* manifold__num_edge = R"___(The number of edges in the Manifold.)___"; +const char* manifold__num_overlaps__other = R"___(This is a checksum-style verification of the collider, simply returning the total number of edge-face bounding box overlaps between this and other. :param other: A Manifold to overlap with.)___"; -const auto manifold__num_prop_745ca800e017 = R"___(The number of properties per vertex in the Manifold.)___"; -const auto manifold__num_prop_vert_a7ba865d3e11 = R"___(The number of property vertices in the Manifold. This will always be >= +const char* manifold__num_prop = R"___(The number of properties per vertex in the Manifold.)___"; +const char* manifold__num_prop_vert = R"___(The number of property vertices in the Manifold. This will always be >= NumVert, as some physical vertices may be duplicated to account for different properties on different neighboring triangles.)___"; -const auto manifold__num_tri_56a67713dff8 = R"___(The number of triangles in the Manifold.)___"; -const auto manifold__num_vert_93a4106c8a53 = R"___(The number of vertices in the Manifold.)___"; -const auto manifold__operator_minus_2ecfe0a1eb86 = R"___(Shorthand for Boolean Difference.)___"; -const auto manifold__operator_minus_eq_f6df5d8bc05c = R"___(Shorthand for Boolean Difference assignment.)___"; -const auto manifold__operator_plus_a473f00d1659 = R"___(Shorthand for Boolean Union.)___"; -const auto manifold__operator_plus_eq_b7ba3403e755 = R"___(Shorthand for Boolean Union assignment.)___"; -const auto manifold__operator_xor_4da4601e403b = R"___(Shorthand for Boolean Intersection.)___"; -const auto manifold__operator_xor_eq_0116ab409eee = R"___(Shorthand for Boolean Intersection assignment.)___"; -const auto manifold__original_id_d1c064807f94 = R"___(If this mesh is an original, this returns its meshID that can be referenced +const char* manifold__num_tri = R"___(The number of triangles in the Manifold.)___"; +const char* manifold__num_vert = R"___(The number of vertices in the Manifold.)___"; +const char* manifold__operator_minus__q = R"___(Shorthand for Boolean Difference.)___"; +const char* manifold__operator_minus_eq__q = R"___(Shorthand for Boolean Difference assignment.)___"; +const char* manifold__operator_plus__q = R"___(Shorthand for Boolean Union.)___"; +const char* manifold__operator_plus_eq__q = R"___(Shorthand for Boolean Union assignment.)___"; +const char* manifold__operator_xor__q = R"___(Shorthand for Boolean Intersection.)___"; +const char* manifold__operator_xor_eq__q = R"___(Shorthand for Boolean Intersection assignment.)___"; +const char* manifold__original_id = R"___(If this mesh is an original, this returns its meshID that can be referenced by product manifolds' MeshRelation. If this manifold is a product, this returns -1.)___"; -const auto manifold__precision_bb888ab8ec11 = R"___(Returns the precision of this Manifold's vertices, which tracks the +const char* manifold__precision = R"___(Returns the precision of this Manifold's vertices, which tracks the approximate rounding error over all the transforms and operations that have led to this state. Any triangles that are colinear within this precision are considered degenerate and removed. This is the value of ε defining [ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid).)___"; -const auto manifold__project_e28980e0f682 = R"___(Returns a cross section representing the projected outline of this object +const char* manifold__project = R"___(Returns a cross section representing the projected outline of this object onto the X-Y plane.)___"; -const auto manifold__refine_c64a4fc78137 = R"___(Increase the density of the mesh by splitting every edge into n pieces. For +const char* manifold__refine__n = R"___(Increase the density of the mesh by splitting every edge into n pieces. For instance, with n = 2, each triangle will be split into 4 triangles. These will all be coplanar (and will not be immediately collapsed) unless the Mesh/Manifold has halfedgeTangents specified (e.g. from the Smooth() constructor), in which case the new vertices will be moved to the interpolated surface according to their barycentric coordinates. :param n: The number of pieces to split every edge into. Must be > 1.)___"; -const auto manifold__reserve_ids_a514f84c6343 = R"___(Returns the first of n sequential new unique mesh IDs for marking sets of +const char* manifold__reserve_ids__n = R"___(Returns the first of n sequential new unique mesh IDs for marking sets of triangles that can be looked up after further operations. Assign to MeshGL.runOriginalID vector.)___"; -const auto manifold__revolve_c916603e7a75 = R"___(Constructs a manifold from a set of polygons by revolving this cross-section +const char* manifold__revolve__cross_section__circular_segments__revolve_degrees = R"___(Constructs a manifold from a set of polygons by revolving this cross-section around its Y-axis and then setting this as the Z-axis of the resulting manifold. If the polygons cross the Y-axis, only the part on the positive X side is used. Geometrically valid input will result in geometrically valid @@ -294,7 +296,7 @@ output. :param circular_segments: Number of segments along its diameter. Default is calculated by the static Defaults. :param revolve_degrees: Number of degrees to revolve. Default is 360 degrees.)___"; -const auto manifold__rotate_f0bb7b7d2f38 = R"___(Applies an Euler angle rotation to the manifold, first about the X axis, then +const char* manifold__rotate__x_degrees__y_degrees__z_degrees = R"___(Applies an Euler angle rotation to the manifold, first about the X axis, then Y, then Z, in degrees. We use degrees so that we can minimize rounding error, and eliminate it completely for any multiples of 90 degrees. Additionally, more efficient code paths are used to update the manifold when the transforms @@ -303,21 +305,21 @@ Transforms are combined and applied lazily. :param x_degrees: First rotation, degrees about the X-axis. :param y_degrees: Second rotation, degrees about the Y-axis. :param z_degrees: Third rotation, degrees about the Z-axis.)___"; -const auto manifold__scale_244be87a307d = R"___(Scale this Manifold in space. This operation can be chained. Transforms are +const char* manifold__scale__v = R"___(Scale this Manifold in space. This operation can be chained. Transforms are combined and applied lazily. :param v: The vector to multiply every vertex by per component.)___"; -const auto manifold__set_properties_6a3457d9ec71 = R"___(Create a new copy of this manifold with updated vertex properties by +const char* manifold__set_properties__num_prop__prop_func = R"___(Create a new copy of this manifold with updated vertex properties by supplying a function that takes the existing position and properties as input. You may specify any number of output properties, allowing creation and removal of channels. Note: undefined behavior will result if you read past the number of input properties or write past the number of output properties. :param num_prop: The new number of properties per vertex. :param prop_func: A function that modifies the properties of a given vertex.)___"; -const auto manifold__slice_7d90a75e7913 = R"___(Returns the cross section of this object parallel to the X-Y plane at the +const char* manifold__slice__height = R"___(Returns the cross section of this object parallel to the X-Y plane at the specified Z height, defaulting to zero. Using a height equal to the bottom of the bounding box will return the bottom faces, while using a height equal to the top of the bounding box will return empty.)___"; -const auto manifold__smooth_66eaffd9331b = R"___(Constructs a smooth version of the input mesh by creating tangents; this +const char* manifold__smooth__mesh__sharpened_edges = R"___(Constructs a smooth version of the input mesh by creating tangents; this method will throw if you have supplied tangents with your mesh already. The actual triangle resolution is unchanged; use the Refine() method to interpolate to a higher-resolution curve. @@ -325,7 +327,7 @@ By default, every edge is calculated for maximum smoothness (very much approximately), attempting to minimize the maximum mean Curvature magnitude. No higher-order derivatives are considered, as the interpolation is independent per triangle, only sharing constraints on their boundaries. -:param mesh_gl: input MeshGL. +:param mesh: input Mesh. :param sharpened_edges: If desired, you can supply a vector of sharpened halfedges, which should in general be a small subset of all halfedges. Order of entries doesn't matter, as each one specifies the desired smoothness @@ -340,7 +342,7 @@ continuous. Vertices with only one sharpened edge are completely smooth, allowing sharpened edges to smoothly vanish at termination. A single vertex can be sharpened by sharping all edges that are incident on it, allowing cones to be formed.)___"; -const auto manifold__smooth_f788c5a0c633 = R"___(Constructs a smooth version of the input mesh by creating tangents; this +const char* manifold__smooth__mesh_gl__sharpened_edges = R"___(Constructs a smooth version of the input mesh by creating tangents; this method will throw if you have supplied tangents with your mesh already. The actual triangle resolution is unchanged; use the Refine() method to interpolate to a higher-resolution curve. @@ -348,7 +350,7 @@ By default, every edge is calculated for maximum smoothness (very much approximately), attempting to minimize the maximum mean Curvature magnitude. No higher-order derivatives are considered, as the interpolation is independent per triangle, only sharing constraints on their boundaries. -:param mesh: input Mesh. +:param mesh_gl: input MeshGL. :param sharpened_edges: If desired, you can supply a vector of sharpened halfedges, which should in general be a small subset of all halfedges. Order of entries doesn't matter, as each one specifies the desired smoothness @@ -363,83 +365,74 @@ continuous. Vertices with only one sharpened edge are completely smooth, allowing sharpened edges to smoothly vanish at termination. A single vertex can be sharpened by sharping all edges that are incident on it, allowing cones to be formed.)___"; -const auto manifold__sphere_6781451731f0 = R"___(Constructs a geodesic sphere of a given radius. +const char* manifold__sphere__radius__circular_segments = R"___(Constructs a geodesic sphere of a given radius. :param radius: Radius of the sphere. Must be positive. :param circular_segments: Number of segments along its diameter. This number will always be rounded up to the nearest factor of four, as this sphere is constructed by refining an octahedron. This means there are a circle of vertices on all three of the axis planes. Default is calculated by the static Defaults.)___"; -const auto manifold__split_by_plane_f411533a14aa = R"___(Convenient version of Split() for a half-space. +const char* manifold__split__cutter = R"___(Split cuts this manifold in two using the cutter manifold. The first result +is the intersection, second is the difference. This is more efficient than +doing them separately. +:param cutter:)___"; +const char* manifold__split_by_plane__normal__origin_offset = R"___(Convenient version of Split() for a half-space. :param normal: This vector is normal to the cutting plane and its length does not matter. The first result is in the direction of this vector, the second result is on the opposite side. :param origin_offset: The distance of the plane from the origin in the direction of the normal vector.)___"; -const auto manifold__split_fc2847c7afae = R"___(Split cuts this manifold in two using the cutter manifold. The first result -is the intersection, second is the difference. This is more efficient than -doing them separately. -:param cutter:)___"; -const auto manifold__status_b1c2b69ee41e = R"___(Returns the reason for an input Mesh producing an empty Manifold. This Status +const char* manifold__status = R"___(Returns the reason for an input Mesh producing an empty Manifold. This Status only applies to Manifolds newly-created from an input Mesh - once they are combined into a new Manifold via operations, the status reverts to NoError, simply processing the problem mesh as empty. Likewise, empty meshes may still show NoError, for instance if they are small enough relative to their precision to be collapsed to nothing.)___"; -const auto manifold__tetrahedron_7e95f682f35b = R"___(Constructs a tetrahedron centered at the origin with one vertex at (1,1,1) +const char* manifold__tetrahedron = R"___(Constructs a tetrahedron centered at the origin with one vertex at (1,1,1) and the rest at similarly symmetric points.)___"; -const auto manifold__transform_0390744b2b46 = R"___(Transform this Manifold in space. The first three columns form a 3x3 matrix +const char* manifold__transform__m = R"___(Transform this Manifold in space. The first three columns form a 3x3 matrix transform and the last is a translation vector. This operation can be chained. Transforms are combined and applied lazily. :param m: The affine transform matrix to apply to all the vertices.)___"; -const auto manifold__translate_fcadc2ca8d6d = R"___(Move this Manifold in space. This operation can be chained. Transforms are +const char* manifold__translate__v = R"___(Move this Manifold in space. This operation can be chained. Transforms are combined and applied lazily. :param v: The vector to add to every vertex.)___"; -const auto manifold__trim_by_plane_066ac34a84b0 = R"___(Identical to SplitByPlane(), but calculating and returning only the first +const char* manifold__trim_by_plane__normal__origin_offset = R"___(Identical to SplitByPlane(), but calculating and returning only the first result. :param normal: This vector is normal to the cutting plane and its length does not matter. The result is in the direction of this vector from the plane. :param origin_offset: The distance of the plane from the origin in the direction of the normal vector.)___"; -const auto manifold__warp_4cc67905f424 = R"___(This function does not change the topology, but allows the vertices to be +const char* manifold__warp__warp_func = R"___(This function does not change the topology, but allows the vertices to be moved according to any arbitrary input function. It is easy to create a function that warps a geometrically valid object into one which overlaps, but that is not checked here, so it is up to the user to choose their function with discretion. :param warp_func: A function that modifies a given vertex position.)___"; -const auto manifold__warp_batch_0b44f7bbe36b = R"___(Same as Manifold::Warp but calls warpFunc with with +const char* manifold__warp_batch__warp_func = R"___(Same as Manifold::Warp but calls warpFunc with with a VecView which is roughly equivalent to std::span pointing to all vec3 elements to be modified in-place :param warp_func: A function that modifies multiple vertex positions.)___"; -const auto set_circular_segments_7ebd2a75022a = R"___(Sets the default number of circular segments for the +const char* set_circular_segments__number = R"___(Sets the default number of circular segments for the CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and Manifold::Revolve() constructors. Overrides the edge length and angle constraints and sets the number of segments to exactly this value. :param number: Number of circular segments. Default is 0, meaning no constraint is applied.)___"; -const auto set_min_circular_angle_69463d0b8fac = R"___(Sets an angle constraint the default number of circular segments for the +const char* set_min_circular_angle__angle = R"___(Sets an angle constraint the default number of circular segments for the CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and Manifold::Revolve() constructors. The number of segments will be rounded up to the nearest factor of four. :param angle: The minimum angle in degrees between consecutive segments. The angle will increase if the the segments hit the minimum edge length. Default is 10 degrees.)___"; -const auto set_min_circular_edge_length_bb1c70addca7 = R"___(Sets a length constraint the default number of circular segments for the +const char* set_min_circular_edge_length__length = R"___(Sets a length constraint the default number of circular segments for the CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and Manifold::Revolve() constructors. The number of segments will be rounded up to the nearest factor of four. :param length: The minimum length of segments. The length will increase if the the segments hit the minimum angle. Default is 1.0.)___"; -const auto triangulate_025ab0b4046f = R"___(@brief Triangulates a set of ε-valid polygons. If the input is not -ε-valid, the triangulation may overlap, but will always return a -manifold result that matches the input edge directions. -:param polygons: The set of polygons, wound CCW and representing multiple -polygons and/or holes. -:param precision: The value of ε, bounding the uncertainty of the -input. -@return std::vector The triangles, referencing the original -polygon points in order.)___"; -const auto triangulate_8f1ad11752db = R"___(Ear-clipping triangulator based on David Eberly's approach from Geometric +const char* triangulate = R"___(Ear-clipping triangulator based on David Eberly's approach from Geometric Tools, but adjusted to handle epsilon-valid polygons, and including a fallback that ensures a manifold triangulation even for overlapping polygons. This is an O(n^2) algorithm, but hopefully this is not a big problem as the @@ -449,7 +442,16 @@ The main adjustments for robustness involve clipping the sharpest ears first (a known technique to get higher triangle quality), and doing an exhaustive search to determine ear convexity exactly if the first geometric result is within precision.)___"; -const auto triangulate_idx_9847f0a1f0f8 = R"___(@brief Triangulates a set of ε-valid polygons. If the input is not +const char* triangulate__polygons__precision = R"___(@brief Triangulates a set of ε-valid polygons. If the input is not +ε-valid, the triangulation may overlap, but will always return a +manifold result that matches the input edge directions. +:param polygons: The set of polygons, wound CCW and representing multiple +polygons and/or holes. +:param precision: The value of ε, bounding the uncertainty of the +input. +@return std::vector The triangles, referencing the original +polygon points in order.)___"; +const char* triangulate_idx__polys__precision = R"___(@brief Triangulates a set of ε-valid polygons. If the input is not ε-valid, the triangulation may overlap, but will always return a manifold result that matches the input edge directions. :param polys: The set of polygons, wound CCW and representing multiple diff --git a/bindings/python/gen_docs.py b/bindings/python/gen_docs.py index fd0c1e7f1..540e34e60 100644 --- a/bindings/python/gen_docs.py +++ b/bindings/python/gen_docs.py @@ -1,5 +1,3 @@ -# %% - from os.path import dirname from hashlib import md5 import re @@ -21,16 +19,19 @@ def method_key(name): name = re.sub('\~', 'destroy_', name) return name -def close_paren(s): - cnt = 0 - for i in range(len(s)): - if s[i] == '(': - cnt += 1 - elif s[i] == ')': - cnt -= 1 - if cnt == 0: - return i - return -1 +parens_re = re.compile(r'[^(]+\(([^(]*(\(.*\))*[^(]*\))', flags=re.DOTALL) +args_re = re.compile(r'^[^,^\(^\)]*(\(.*\))*[^,^\(^\)]*[\s\&\*]([0-9\w]+)\s*[,\)]', flags=re.DOTALL) + +def parse_args(s): + par = parens_re.match(s) + if not par: + return None + out = [] + arg_str = par[1] + while (m := re.search(args_re, arg_str)): + out += [snake_case(m[2])] + arg_str = arg_str[m.span()[1]:] + return out def collect(fname, matcher): param_re = re.compile(r'@param (\w+)') @@ -45,13 +46,15 @@ def collect(fname, matcher): elif line.startswith('*') and comment is not None: comment += line[1:].lstrip() elif comment and (m := matcher(line)): - while (close := close_paren(line)) < 0: + while (args := parse_args(line)) is None: line += next(f) - line = re.sub(r'\s', '', line[:close+1]) + if len(line) > 500: + break + method = method_key(snake_case(m[1])) comment = re.sub(param_re, param_snake_case, comment) - lhash = md5(line.encode("utf-8")).hexdigest() - method = f'{method}_{lhash[:12]}' + method = '__'.join([method, *args]) + assert method not in comments comments[method] = comment comment = '' @@ -60,7 +63,7 @@ def collect(fname, matcher): method_re = re.compile(r'(\w+::[\w\-\+\^\=\:]+)\(') function_re = re.compile(r'([\w\-\+\^\=\:]+)\(') -# we don't handle inline functions in classes very well +# we don't handle inline functions in classes properly # so instead just white-list functions we want def select_functions(s): m = function_re.search(s) @@ -77,16 +80,12 @@ def select_functions(s): collect(f'{base}/src/utilities/include/public.h', select_functions) comments = dict(sorted(comments.items())) -comments -# %% -gen_h = f'{base}/bindings/python/docstrings.inl' +gen_h = f'{base}/bindings/python/autogen_docstrings.inl' with open(gen_h, 'w') as f: - f.write('#pragma once\n') - f.write('#include \n') - f.write('namespace manifold_docs {\n') + f.write('#pragma once\n\n') + f.write('// AUTOGENERATED BY gen_docs.py\n\n') + f.write('namespace manifold_docstrings {\n') for key, doc in comments.items(): - f.write(f'const auto {key} = R"___({doc.strip()})___";\n') + f.write(f'const char* {key} = R"___({doc.strip()})___";\n') f.write('} // namespace manifold_docs') - -# %% diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 87965e934..6d30eebef 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -15,8 +15,8 @@ #include #include +#include "autogen_docstrings.inl" #include "cross_section.h" -#include "docstrings.inl" #include "manifold.h" #include "nanobind/nanobind.h" #include "nanobind/ndarray.h" @@ -203,58 +203,58 @@ std::vector toVector(const T *arr, size_t size) { return std::vector(arr, arr + size); } -using namespace manifold_docs; +using namespace manifold_docstrings; // strip original :params: and replace with ours -const std::string manifold__rotate_vec = - std::string(manifold__rotate_f0bb7b7d2f38) - .substr(0, std::string(manifold__rotate_f0bb7b7d2f38).find(":param")) + +const std::string manifold__rotate_xyz = + manifold__rotate__x_degrees__y_degrees__z_degrees; +const std::string manifold__rotate__v = + manifold__rotate_xyz.substr(0, manifold__rotate_xyz.find(":param")) + ":param v: [X, Y, Z] rotation in degrees."; NB_MODULE(manifold3d, m) { m.doc() = "Python binding for the Manifold library."; m.def("set_min_circular_angle", Quality::SetMinCircularAngle, - nb::arg("angle"), set_min_circular_angle_69463d0b8fac); + nb::arg("angle"), set_min_circular_angle__angle); m.def("set_min_circular_edge_length", Quality::SetMinCircularEdgeLength, - nb::arg("length"), set_min_circular_edge_length_bb1c70addca7); + nb::arg("length"), set_min_circular_edge_length__length); m.def("set_circular_segments", Quality::SetCircularSegments, - nb::arg("number"), set_circular_segments_7ebd2a75022a); + nb::arg("number"), set_circular_segments__number); m.def("get_circular_segments", Quality::GetCircularSegments, - nb::arg("radius"), get_circular_segments_3f31ccff2bbc); + nb::arg("radius"), get_circular_segments__radius); m.def("triangulate", &Triangulate, nb::arg("polygons"), nb::arg("precision") = -1, // TODO document - triangulate_025ab0b4046f); + triangulate__polygons__precision); nb::class_(m, "Manifold") - .def(nb::init<>(), manifold__manifold_c130481162c9) + .def(nb::init<>(), manifold__manifold) .def(nb::init &>(), nb::arg("mesh"), nb::arg("property_tolerance") = nb::list(), - manifold__manifold_37129c244d43) - .def(nb::self + nb::self, "Boolean union.") - .def(nb::self - nb::self, "Boolean difference.") - .def(nb::self ^ nb::self, "Boolean intersection.") + manifold__manifold__mesh_gl__property_tolerance) + .def(nb::self + nb::self, manifold__operator_plus__q) + .def(nb::self - nb::self, manifold__operator_minus__q) + .def(nb::self ^ nb::self, manifold__operator_xor__q) .def( "hull", [](const Manifold &self) { return self.Hull(); }, - manifold__hull_1fd92449f7cb) + manifold__hull) .def_static( "batch_hull", [](std::vector ms) { return Manifold::Hull(ms); }, - nb::arg("manifolds"), manifold__hull_e1568f7f16f2) + nb::arg("manifolds"), manifold__hull__manifolds) .def_static( "hull_points", [](std::vector pts) { return Manifold::Hull(pts); }, - nb::arg("pts"), manifold__hull_b0113f48020a) + nb::arg("pts"), manifold__hull__pts) .def("transform", &Manifold::Transform, nb::arg("m"), - manifold__transform_0390744b2b46) + manifold__transform__m) .def("translate", &Manifold::Translate, nb::arg("t"), - manifold__translate_fcadc2ca8d6d) - .def("scale", &Manifold::Scale, nb::arg("v"), - manifold__scale_244be87a307d) + manifold__translate__v) + .def("scale", &Manifold::Scale, nb::arg("v"), manifold__scale__v) .def( "scale", [](const Manifold &m, float s) { @@ -262,22 +262,21 @@ NB_MODULE(manifold3d, m) { }, nb::arg("s"), "Scale this Manifold in space. This operation can be chained. " - "Transforms are combined and applied lazily." - "\n\n" + "Transforms are combined and applied lazily.\n\n" ":param s: The scalar to multiply every vertex by component.") - .def("mirror", &Manifold::Mirror, nb::arg("v"), - manifold__mirror_d798a49656cc) + .def("mirror", &Manifold::Mirror, nb::arg("v"), manifold__mirror__normal) .def( "rotate", [](const Manifold &self, glm::vec3 v) { return self.Rotate(v.x, v.y, v.z); }, - nb::arg("v"), manifold__rotate_vec.c_str()) - .def("warp", &Manifold::Warp, nb::arg("f"), manifold__warp_4cc67905f424) - .def("warp_batch", &Manifold::WarpBatch, nb::arg("f"), - manifold__warp_batch_0b44f7bbe36b) + nb::arg("v"), manifold__rotate__v.c_str()) + .def("warp", &Manifold::Warp, nb::arg("warp_func"), + manifold__warp__warp_func) + .def("warp_batch", &Manifold::WarpBatch, nb::arg("warp_func"), + manifold__warp_batch__warp_func) .def( - "set_properties", // TODO this needs a batch version! + "set_properties", [](const Manifold &self, int newNumProp, const std::function(m, "Mesh") .def( @@ -575,19 +572,16 @@ NB_MODULE(manifold3d, m) { "onwards). This class makes use of the " "[Clipper2](http://www.angusj.com/clipper2/Docs/Overview.htm) library " "for polygon clipping (boolean) and offsetting operations.") - .def(nb::init<>(), cross_section__cross_section_381df1ec382b) + .def(nb::init<>(), cross_section__cross_section) .def(nb::init>, CrossSection::FillRule>(), - nb::arg("polygons"), + nb::arg("contours"), nb::arg("fillrule") = CrossSection::FillRule::Positive, - cross_section__cross_section_4459865c1e2f) - .def("area", &CrossSection::Area, cross_section__area_f5458809be32) - .def("num_vert", &CrossSection::NumVert, - cross_section__num_vert_9dd2efd31062) - .def("num_contour", &CrossSection::NumContour, - cross_section__num_contour_5894fa74e5f5) - .def("is_empty", &CrossSection::IsEmpty, - cross_section__is_empty_25b4b2d4e0ad) + cross_section__cross_section__contours__fillrule) + .def("area", &CrossSection::Area, cross_section__area) + .def("num_vert", &CrossSection::NumVert, cross_section__num_vert) + .def("num_contour", &CrossSection::NumContour, cross_section__num_contour) + .def("is_empty", &CrossSection::IsEmpty, cross_section__is_empty) .def( "bounds", [](const CrossSection &self) { @@ -597,11 +591,11 @@ NB_MODULE(manifold3d, m) { "Return bounding box of CrossSection as tuple(" "min_x, min_y, max_x, max_y)") .def("translate", &CrossSection::Translate, nb::arg("v"), - cross_section__translate_339895387e15) - .def("rotate", &CrossSection::Rotate, nb::arg("angle"), - cross_section__rotate_7c6bae9524e7) - .def("scale", &CrossSection::Scale, nb::arg("v"), - cross_section__scale_8913c878f656) + cross_section__translate__v) + .def("rotate", &CrossSection::Rotate, nb::arg("degrees"), + cross_section__rotate__degrees) + .def("scale", &CrossSection::Scale, nb::arg("scale"), + cross_section__scale__scale) .def( "scale", [](const CrossSection &self, float s) { @@ -613,45 +607,48 @@ NB_MODULE(manifold3d, m) { "\n\n" ":param s: The scalar to multiply every vertex by per component.") .def("mirror", &CrossSection::Mirror, nb::arg("ax"), - cross_section__mirror_a10119e40f21) + cross_section__mirror__ax) .def("transform", &CrossSection::Transform, nb::arg("m"), - cross_section__transform_baddfca7ede3) - .def("warp", &CrossSection::Warp, nb::arg("f"), - cross_section__warp_180cafeaaad1) - .def("warp_batch", &CrossSection::WarpBatch, nb::arg("f"), - cross_section__warp_batch_f843ce28c677) + cross_section__transform__m) + .def("warp", &CrossSection::Warp, nb::arg("warp_func"), + cross_section__warp__warp_func) + .def("warp_batch", &CrossSection::WarpBatch, nb::arg("warp_func"), + cross_section__warp_batch__warp_func) .def("simplify", &CrossSection::Simplify, nb::arg("epsilon") = 1e-6, - cross_section__simplify_dbac3e60acf4) - .def("offset", &CrossSection::Offset, nb::arg("delta"), - nb::arg("join_type"), nb::arg("miter_limit") = 2.0, - nb::arg("circular_segments") = 0, cross_section__offset_b3675b4b0ed0) - .def(nb::self + nb::self, cross_section__operator_plus_d3c26b9c5ca3) - .def(nb::self - nb::self, cross_section__operator_minus_04b4d727817f) - .def(nb::self ^ nb::self, cross_section__operator_xor_76de317c9be1) + cross_section__simplify__epsilon) + .def( + "offset", &CrossSection::Offset, nb::arg("delta"), + nb::arg("join_type"), nb::arg("miter_limit") = 2.0, + nb::arg("circular_segments") = 0, + cross_section__offset__delta__jointype__miter_limit__circular_segments) + .def(nb::self + nb::self, cross_section__operator_plus__q) + .def(nb::self - nb::self, cross_section__operator_minus__q) + .def(nb::self ^ nb::self, cross_section__operator_xor__q) .def( "hull", [](const CrossSection &self) { return self.Hull(); }, - cross_section__hull_3f1ad9eaa499) + cross_section__hull) .def_static( "batch_hull", [](std::vector cs) { return CrossSection::Hull(cs); }, - nb::arg("cross_sections"), cross_section__hull_014f76304d06) + nb::arg("cross_sections"), cross_section__hull__cross_sections) .def_static( "hull_points", [](std::vector pts) { return CrossSection::Hull(pts); }, - nb::arg("pts"), cross_section__hull_c94ccc3c0fe6) - .def("decompose", &CrossSection::Decompose, - cross_section__decompose_17ae5159e6e5) - .def("to_polygons", &CrossSection::ToPolygons, - cross_section__to_polygons_6f4cb60dbd78) - .def("extrude", &Manifold::Extrude, nb::arg("height"), - nb::arg("n_divisions") = 0, nb::arg("twist_degrees") = 0.0f, - nb::arg("scale_top") = std::make_tuple(1.0f, 1.0f), - manifold__extrude_bc84f1554abe) + nb::arg("pts"), cross_section__hull__pts) + .def("decompose", &CrossSection::Decompose, cross_section__decompose) + .def("to_polygons", &CrossSection::ToPolygons, cross_section__to_polygons) + .def( + "extrude", &Manifold::Extrude, nb::arg("height"), + nb::arg("n_divisions") = 0, nb::arg("twist_degrees") = 0.0f, + nb::arg("scale_top") = std::make_tuple(1.0f, 1.0f), + manifold__extrude__cross_section__height__n_divisions__twist_degrees__scale_top) .def("revolve", &Manifold::Revolve, nb::arg("circular_segments") = 0, - nb::arg("revolve_degrees") = 360.0, manifold__revolve_c916603e7a75) + nb::arg("revolve_degrees") = 360.0, + manifold__revolve__cross_section__circular_segments__revolve_degrees) .def_static("square", &CrossSection::Square, nb::arg("size"), - nb::arg("center") = false, cross_section__square_67088e89831e) + nb::arg("center") = false, + cross_section__square__size__center) .def_static("circle", &CrossSection::Circle, nb::arg("radius"), nb::arg("circular_segments") = 0, - cross_section__circle_72e80d0e2b44); + cross_section__circle__radius__circular_segments); } From 72ab01d149f806a8267fcf21b5e40ed567738421 Mon Sep 17 00:00:00 2001 From: K Date: Wed, 20 Dec 2023 18:34:58 -0500 Subject: [PATCH 5/8] autogen docstrings during cmake --- bindings/python/CMakeLists.txt | 10 + bindings/python/autogen_docstrings.inl | 464 ------------------------- bindings/python/gen_docs.py | 86 +++-- bindings/python/manifold3d.cpp | 2 +- 4 files changed, 61 insertions(+), 501 deletions(-) delete mode 100644 bindings/python/autogen_docstrings.inl diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 4b1da0ba7..a451ca546 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -24,6 +24,16 @@ target_compile_options(manifold3d PRIVATE ${MANIFOLD_FLAGS} -DMODULE_NAME=manifo target_compile_features(manifold3d PUBLIC cxx_std_17) set_target_properties(manifold3d PROPERTIES OUTPUT_NAME "manifold3d") +message(Python_EXECUTABLE = ${Python_EXECUTABLE}) +add_custom_target( + autogen_docstrings + ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen_docs.py + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + BYPRODUCTS autogen_docstrings.inl +) +target_include_directories(manifold3d PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +add_dependencies(manifold3d autogen_docstrings) + if(SKBUILD) install( TARGETS manifold3d diff --git a/bindings/python/autogen_docstrings.inl b/bindings/python/autogen_docstrings.inl deleted file mode 100644 index 3fff48c12..000000000 --- a/bindings/python/autogen_docstrings.inl +++ /dev/null @@ -1,464 +0,0 @@ -#pragma once - -// AUTOGENERATED BY gen_docs.py - -namespace manifold_docstrings { -const char* cross_section__area = R"___(Return the total area covered by complex polygons making up the -CrossSection.)___"; -const char* cross_section__batch_boolean__cross_sections__op = R"___(Perform the given boolean operation on a list of CrossSections. In case of -Subtract, all CrossSections in the tail are differenced from the head.)___"; -const char* cross_section__boolean__second__op = R"___(Perform the given boolean operation between this and another CrossSection.)___"; -const char* cross_section__bounds = R"___(Returns the axis-aligned bounding rectangle of all the CrossSections' -vertices.)___"; -const char* cross_section__circle__radius__circular_segments = R"___(Constructs a circle of a given radius. -:param radius: Radius of the circle. Must be positive. -:param circular_segments: Number of segments along its diameter. Default is -calculated by the static Quality defaults according to the radius.)___"; -const char* cross_section__compose__cross_sections = R"___(Construct a CrossSection from a vector of other CrossSections (batch -boolean union).)___"; -const char* cross_section__cross_section = R"___(The default constructor is an empty cross-section (containing no contours).)___"; -const char* cross_section__cross_section__contour__fillrule = R"___(Create a 2d cross-section from a single contour. A boolean union operation -(with Positive filling rule by default) is performed to ensure the -resulting CrossSection is free of self-intersections. -:param contour: A closed path outlining the desired cross-section. -:param fillrule: The filling rule used to interpret polygon sub-regions -created by self-intersections in contour.)___"; -const char* cross_section__cross_section__contours__fillrule = R"___(Create a 2d cross-section from a set of contours (complex polygons). A -boolean union operation (with Positive filling rule by default) is -performed to combine overlapping polygons and ensure the resulting -CrossSection is free of intersections. -:param contours: A set of closed paths describing zero or more complex -polygons. -:param fillrule: The filling rule used to interpret polygon sub-regions in -contours.)___"; -const char* cross_section__cross_section__other = R"___(The copy constructor avoids copying the underlying paths vector (sharing -with its parent via shared_ptr), however subsequent transformations, and -their application will not be shared. It is generally recommended to avoid -this, opting instead to simply create CrossSections with the available -const methods.)___"; -const char* cross_section__cross_section__rect = R"___(Create a 2d cross-section from an axis-aligned rectangle (bounding box). -:param rect: An axis-aligned rectangular bounding box.)___"; -const char* cross_section__decompose = R"___(This operation returns a vector of CrossSections that are topologically -disconnected, each containing one outline contour with zero or more -holes.)___"; -const char* cross_section__hull = R"___(Compute the convex hull of this cross-section.)___"; -const char* cross_section__hull__cross_sections = R"___(Compute the convex hull enveloping a set of cross-sections. -:param cross_sections: A vector of cross-sections over which to compute a -convex hull.)___"; -const char* cross_section__hull__polys = R"___(Compute the convex hull of a set of points/polygons. If the given points are -fewer than 3, an empty CrossSection will be returned. -:param pts: A vector of vectors of 2-dimensional points over which to compute -a convex hull.)___"; -const char* cross_section__hull__pts = R"___(Compute the convex hull of a set of points. If the given points are fewer -than 3, an empty CrossSection will be returned. -:param pts: A vector of 2-dimensional points over which to compute a convex -hull.)___"; -const char* cross_section__is_empty = R"___(Does the CrossSection contain any contours?)___"; -const char* cross_section__mirror__ax = R"___(Mirror this CrossSection over the arbitrary axis described by the unit form -of the given vector. If the length of the vector is zero, an empty -CrossSection is returned. This operation can be chained. Transforms are -combined and applied lazily. -:param ax: the axis to be mirrored over)___"; -const char* cross_section__num_contour = R"___(Return the number of contours (both outer and inner paths) in the -CrossSection.)___"; -const char* cross_section__num_vert = R"___(Return the number of vertices in the CrossSection.)___"; -const char* cross_section__offset__delta__jointype__miter_limit__circular_segments = R"___(Inflate the contours in CrossSection by the specified delta, handling -corners according to the given JoinType. -:param delta: Positive deltas will cause the expansion of outlining contours -to expand, and retraction of inner (hole) contours. Negative deltas will -have the opposite effect. -:param jt: The join type specifying the treatment of contour joins -(corners). -:param miter_limit: The maximum distance in multiples of delta that vertices -can be offset from their original positions with before squaring is -applied, when the join type is Miter (default is 2, which is the -minimum allowed). See the [Clipper2 -MiterLimit](http://www.angusj.com/clipper2/Docs/Units/Clipper.Offset/Classes/ClipperOffset/Properties/MiterLimit.htm) -page for a visual example. -:param circular_segments: Number of segments per 360 degrees of -JoinType::Round corners (roughly, the number of vertices that -will be added to each contour). Default is calculated by the static Quality -defaults according to the radius.)___"; -const char* cross_section__operator_minus__q = R"___(Compute the boolean difference of a (clip) cross-section from another -(subject).)___"; -const char* cross_section__operator_minus_eq__q = R"___(Compute the boolean difference of a (clip) cross-section from a another -(subject), assigning the result to the subject.)___"; -const char* cross_section__operator_plus__q = R"___(Compute the boolean union between two cross-sections.)___"; -const char* cross_section__operator_plus_eq__q = R"___(Compute the boolean union between two cross-sections, assigning the result -to the first.)___"; -const char* cross_section__operator_xor__q = R"___(Compute the boolean intersection between two cross-sections.)___"; -const char* cross_section__operator_xor_eq__q = R"___(Compute the boolean intersection between two cross-sections, assigning the -result to the first.)___"; -const char* cross_section__rotate__degrees = R"___(Applies a (Z-axis) rotation to the CrossSection, in degrees. This operation -can be chained. Transforms are combined and applied lazily. -:param degrees: degrees about the Z-axis to rotate.)___"; -const char* cross_section__scale__scale = R"___(Scale this CrossSection in space. This operation can be chained. Transforms -are combined and applied lazily. -:param v: The vector to multiply every vertex by per component.)___"; -const char* cross_section__simplify__epsilon = R"___(Remove vertices from the contours in this CrossSection that are less than -the specified distance epsilon from an imaginary line that passes through -its two adjacent vertices. Near duplicate vertices and collinear points -will be removed at lower epsilons, with elimination of line segments -becoming increasingly aggressive with larger epsilons. -It is recommended to apply this function following Offset, in order to -clean up any spurious tiny line segments introduced that do not improve -quality in any meaningful way. This is particularly important if further -offseting operations are to be performed, which would compound the issue.)___"; -const char* cross_section__square__size__center = R"___(Constructs a square with the given XY dimensions. By default it is -positioned in the first quadrant, touching the origin. If any dimensions in -size are negative, or if all are zero, an empty Manifold will be returned. -:param size: The X, and Y dimensions of the square. -:param center: Set to true to shift the center to the origin.)___"; -const char* cross_section__to_polygons = R"___(Return the contours of this CrossSection as a Polygons.)___"; -const char* cross_section__transform__m = R"___(Transform this CrossSection in space. The first two columns form a 2x2 -matrix transform and the last is a translation vector. This operation can -be chained. Transforms are combined and applied lazily. -:param m: The affine transform matrix to apply to all the vertices.)___"; -const char* cross_section__translate__v = R"___(Move this CrossSection in space. This operation can be chained. Transforms -are combined and applied lazily. -:param v: The vector to add to every vertex.)___"; -const char* cross_section__warp__warp_func = R"___(Move the vertices of this CrossSection (creating a new one) according to -any arbitrary input function, followed by a union operation (with a -Positive fill rule) that ensures any introduced intersections are not -included in the result. -:param warp_func: A function that modifies a given vertex position.)___"; -const char* cross_section__warp_batch__warp_func = R"___(Same as CrossSection::Warp but calls warpFunc with -a VecView which is roughly equivalent to std::span -pointing to all vec2 elements to be modified in-place -:param warp_func: A function that modifies multiple vertex positions.)___"; -const char* get_circular_segments__radius = R"___(Determine the result of the SetMinCircularAngle(), -SetMinCircularEdgeLength(), and SetCircularSegments() defaults. -:param radius: For a given radius of circle, determine how many default -segments there will be.)___"; -const char* manifold__as_original = R"___(This function condenses all coplanar faces in the relation, and -collapses those edges. In the process the relation to ancestor meshes is lost -and this new Manifold is marked an original. Properties are preserved, so if -they do not match across an edge, that edge will be kept.)___"; -const char* manifold__boolean__second__op = R"___(The central operation of this library: the Boolean combines two manifolds -into another by calculating their intersections and removing the unused -portions. -[ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid) -inputs will produce ε-valid output. ε-invalid input may fail -triangulation. -These operations are optimized to produce nearly-instant results if either -input is empty or their bounding boxes do not overlap. -:param second: The other Manifold. -:param op: The type of operation to perform.)___"; -const char* manifold__bounding_box = R"___(Returns the axis-aligned bounding box of all the Manifold's vertices.)___"; -const char* manifold__calculate_curvature__gaussian_idx__mean_idx = R"___(Curvature is the inverse of the radius of curvature, and signed such that -positive is convex and negative is concave. There are two orthogonal -principal curvatures at any point on a manifold, with one maximum and the -other minimum. Gaussian curvature is their product, while mean -curvature is their sum. This approximates them for every vertex and assigns -them as vertex properties on the given channels. -:param gaussian_idx: The property channel index in which to store the Gaussian -curvature. An index < 0 will be ignored (stores nothing). The property set -will be automatically expanded to include the channel index specified. -:param mean_idx: The property channel index in which to store the mean -curvature. An index < 0 will be ignored (stores nothing). The property set -will be automatically expanded to include the channel index specified.)___"; -const char* manifold__compose__manifolds = R"___(Constructs a new manifold from a vector of other manifolds. This is a purely -topological operation, so care should be taken to avoid creating -overlapping results. It is the inverse operation of Decompose(). -:param manifolds: A vector of Manifolds to lazy-union together.)___"; -const char* manifold__cube__size__center = R"___(Constructs a unit cube (edge lengths all one), by default in the first -octant, touching the origin. If any dimensions in size are negative, or if -all are zero, an empty Manifold will be returned. -:param size: The X, Y, and Z dimensions of the box. -:param center: Set to true to shift the center to the origin.)___"; -const char* manifold__cylinder__height__radius_low__radius_high__circular_segments__center = R"___(A convenience constructor for the common case of extruding a circle. Can also -form cones if both radii are specified. -:param height: Z-extent -:param radius_low: Radius of bottom circle. Must be positive. -:param radius_high: Radius of top circle. Can equal zero. Default is equal to -radiusLow. -:param circular_segments: How many line segments to use around the circle. -Default is calculated by the static Defaults. -:param center: Set to true to shift the center to the origin. Default is -origin at the bottom.)___"; -const char* manifold__decompose = R"___(This operation returns a vector of Manifolds that are topologically -disconnected. If everything is connected, the vector is length one, -containing a copy of the original. It is the inverse operation of Compose().)___"; -const char* manifold__extrude__cross_section__height__n_divisions__twist_degrees__scale_top = R"___(Constructs a manifold from a set of polygons by extruding them along the -Z-axis. -Note that high twistDegrees with small nDivisions may cause -self-intersection. This is not checked here and it is up to the user to -choose the correct parameters. -:param cross_section: A set of non-overlapping polygons to extrude. -:param height: Z-extent of extrusion. -:param n_divisions: Number of extra copies of the crossSection to insert into -the shape vertically; especially useful in combination with twistDegrees to -avoid interpolation artifacts. Default is none. -:param twist_degrees: Amount to twist the top crossSection relative to the -bottom, interpolated linearly for the divisions in between. -:param scale_top: Amount to scale the top (independently in X and Y). If the -scale is {0, 0}, a pure cone is formed with only a single vertex at the top. -Note that scale is applied after twist. -Default {1, 1}.)___"; -const char* manifold__genus = R"___(The genus is a topological property of the manifold, representing the number -of "handles". A sphere is 0, torus 1, etc. It is only meaningful for a single -mesh, so it is best to call Decompose() first.)___"; -const char* manifold__get_mesh = R"___(This returns a Mesh of simple vectors of vertices and triangles suitable for -saving or other operations outside of the context of this library.)___"; -const char* manifold__get_mesh_gl__normal_idx = R"___(The most complete output of this library, returning a MeshGL that is designed -to easily push into a renderer, including all interleaved vertex properties -that may have been input. It also includes relations to all the input meshes -that form a part of this result and the transforms applied to each. -:param normal_idx: If the original MeshGL inputs that formed this manifold had -properties corresponding to normal vectors, you can specify which property -channels these are (x, y, z), which will cause this output MeshGL to -automatically update these normals according to the applied transforms and -front/back side. Each channel must be >= 3 and < numProp, and all original -MeshGLs must use the same channels for their normals.)___"; -const char* manifold__get_properties = R"___(Returns the surface area and volume of the manifold.)___"; -const char* manifold__hull = R"___(Compute the convex hull of this manifold.)___"; -const char* manifold__hull__manifolds = R"___(Compute the convex hull enveloping a set of manifolds. -:param manifolds: A vector of manifolds over which to compute a convex hull.)___"; -const char* manifold__hull__pts = R"___(Compute the convex hull of a set of points. If the given points are fewer -than 4, or they are all coplanar, an empty Manifold will be returned. -:param pts: A vector of 3-dimensional points over which to compute a convex -hull.)___"; -const char* manifold__is_empty = R"___(Does the Manifold have any triangles?)___"; -const char* manifold__manifold = R"___(Construct an empty Manifold.)___"; -const char* manifold__manifold__mesh = R"___(Convert a Mesh into a Manifold. Will return an empty Manifold -and set an Error Status if the Mesh is not an oriented 2-manifold. Will -collapse degenerate triangles and unnecessary vertices. -:param mesh: The input Mesh.)___"; -const char* manifold__manifold__mesh_gl__property_tolerance = R"___(Convert a MeshGL into a Manifold, retaining its properties and merging only -the positions according to the merge vectors. Will return an empty Manifold -and set an Error Status if the result is not an oriented 2-manifold. Will -collapse degenerate triangles and unnecessary vertices. -All fields are read, making this structure suitable for a lossless round-trip -of data from GetMeshGL. For multi-material input, use ReserveIDs to set a -unique originalID for each material, and sort the materials into triangle -runs. -:param mesh_gl: The input MeshGL. -:param property_tolerance: A vector of precision values for each property -beyond position. If specified, the propertyTolerance vector must have size = -numProp - 3. This is the amount of interpolation error allowed before two -neighboring triangles are considered to be on a property boundary edge. -Property boundary edges will be retained across operations even if the -triangles are coplanar. Defaults to 1e-5, which works well for most -properties in the [-1, 1] range.)___"; -const char* manifold__matches_tri_normals = R"___(The triangle normal vectors are saved over the course of operations rather -than recalculated to avoid rounding error. This checks that triangles still -match their normal vectors within Precision().)___"; -const char* manifold__mirror__normal = R"___(Mirror this Manifold over the plane described by the unit form of the given -normal vector. If the length of the normal is zero, an empty Manifold is -returned. This operation can be chained. Transforms are combined and applied -lazily. -:param normal: The normal vector of the plane to be mirrored over)___"; -const char* manifold__num_degenerate_tris = R"___(The number of triangles that are colinear within Precision(). This library -attempts to remove all of these, but it cannot always remove all of them -without changing the mesh by too much.)___"; -const char* manifold__num_edge = R"___(The number of edges in the Manifold.)___"; -const char* manifold__num_overlaps__other = R"___(This is a checksum-style verification of the collider, simply returning the -total number of edge-face bounding box overlaps between this and other. -:param other: A Manifold to overlap with.)___"; -const char* manifold__num_prop = R"___(The number of properties per vertex in the Manifold.)___"; -const char* manifold__num_prop_vert = R"___(The number of property vertices in the Manifold. This will always be >= -NumVert, as some physical vertices may be duplicated to account for different -properties on different neighboring triangles.)___"; -const char* manifold__num_tri = R"___(The number of triangles in the Manifold.)___"; -const char* manifold__num_vert = R"___(The number of vertices in the Manifold.)___"; -const char* manifold__operator_minus__q = R"___(Shorthand for Boolean Difference.)___"; -const char* manifold__operator_minus_eq__q = R"___(Shorthand for Boolean Difference assignment.)___"; -const char* manifold__operator_plus__q = R"___(Shorthand for Boolean Union.)___"; -const char* manifold__operator_plus_eq__q = R"___(Shorthand for Boolean Union assignment.)___"; -const char* manifold__operator_xor__q = R"___(Shorthand for Boolean Intersection.)___"; -const char* manifold__operator_xor_eq__q = R"___(Shorthand for Boolean Intersection assignment.)___"; -const char* manifold__original_id = R"___(If this mesh is an original, this returns its meshID that can be referenced -by product manifolds' MeshRelation. If this manifold is a product, this -returns -1.)___"; -const char* manifold__precision = R"___(Returns the precision of this Manifold's vertices, which tracks the -approximate rounding error over all the transforms and operations that have -led to this state. Any triangles that are colinear within this precision are -considered degenerate and removed. This is the value of ε defining -[ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid).)___"; -const char* manifold__project = R"___(Returns a cross section representing the projected outline of this object -onto the X-Y plane.)___"; -const char* manifold__refine__n = R"___(Increase the density of the mesh by splitting every edge into n pieces. For -instance, with n = 2, each triangle will be split into 4 triangles. These -will all be coplanar (and will not be immediately collapsed) unless the -Mesh/Manifold has halfedgeTangents specified (e.g. from the Smooth() -constructor), in which case the new vertices will be moved to the -interpolated surface according to their barycentric coordinates. -:param n: The number of pieces to split every edge into. Must be > 1.)___"; -const char* manifold__reserve_ids__n = R"___(Returns the first of n sequential new unique mesh IDs for marking sets of -triangles that can be looked up after further operations. Assign to -MeshGL.runOriginalID vector.)___"; -const char* manifold__revolve__cross_section__circular_segments__revolve_degrees = R"___(Constructs a manifold from a set of polygons by revolving this cross-section -around its Y-axis and then setting this as the Z-axis of the resulting -manifold. If the polygons cross the Y-axis, only the part on the positive X -side is used. Geometrically valid input will result in geometrically valid -output. -:param cross_section: A set of non-overlapping polygons to revolve. -:param circular_segments: Number of segments along its diameter. Default is -calculated by the static Defaults. -:param revolve_degrees: Number of degrees to revolve. Default is 360 degrees.)___"; -const char* manifold__rotate__x_degrees__y_degrees__z_degrees = R"___(Applies an Euler angle rotation to the manifold, first about the X axis, then -Y, then Z, in degrees. We use degrees so that we can minimize rounding error, -and eliminate it completely for any multiples of 90 degrees. Additionally, -more efficient code paths are used to update the manifold when the transforms -only rotate by multiples of 90 degrees. This operation can be chained. -Transforms are combined and applied lazily. -:param x_degrees: First rotation, degrees about the X-axis. -:param y_degrees: Second rotation, degrees about the Y-axis. -:param z_degrees: Third rotation, degrees about the Z-axis.)___"; -const char* manifold__scale__v = R"___(Scale this Manifold in space. This operation can be chained. Transforms are -combined and applied lazily. -:param v: The vector to multiply every vertex by per component.)___"; -const char* manifold__set_properties__num_prop__prop_func = R"___(Create a new copy of this manifold with updated vertex properties by -supplying a function that takes the existing position and properties as -input. You may specify any number of output properties, allowing creation and -removal of channels. Note: undefined behavior will result if you read past -the number of input properties or write past the number of output properties. -:param num_prop: The new number of properties per vertex. -:param prop_func: A function that modifies the properties of a given vertex.)___"; -const char* manifold__slice__height = R"___(Returns the cross section of this object parallel to the X-Y plane at the -specified Z height, defaulting to zero. Using a height equal to the bottom of -the bounding box will return the bottom faces, while using a height equal to -the top of the bounding box will return empty.)___"; -const char* manifold__smooth__mesh__sharpened_edges = R"___(Constructs a smooth version of the input mesh by creating tangents; this -method will throw if you have supplied tangents with your mesh already. The -actual triangle resolution is unchanged; use the Refine() method to -interpolate to a higher-resolution curve. -By default, every edge is calculated for maximum smoothness (very much -approximately), attempting to minimize the maximum mean Curvature magnitude. -No higher-order derivatives are considered, as the interpolation is -independent per triangle, only sharing constraints on their boundaries. -:param mesh: input Mesh. -:param sharpened_edges: If desired, you can supply a vector of sharpened -halfedges, which should in general be a small subset of all halfedges. Order -of entries doesn't matter, as each one specifies the desired smoothness -(between zero and one, with one the default for all unspecified halfedges) -and the halfedge index (3 * triangle index + [0,1,2] where 0 is the edge -between triVert 0 and 1, etc). -At a smoothness value of zero, a sharp crease is made. The smoothness is -interpolated along each edge, so the specified value should be thought of as -an average. Where exactly two sharpened edges meet at a vertex, their -tangents are rotated to be colinear so that the sharpened edge can be -continuous. Vertices with only one sharpened edge are completely smooth, -allowing sharpened edges to smoothly vanish at termination. A single vertex -can be sharpened by sharping all edges that are incident on it, allowing -cones to be formed.)___"; -const char* manifold__smooth__mesh_gl__sharpened_edges = R"___(Constructs a smooth version of the input mesh by creating tangents; this -method will throw if you have supplied tangents with your mesh already. The -actual triangle resolution is unchanged; use the Refine() method to -interpolate to a higher-resolution curve. -By default, every edge is calculated for maximum smoothness (very much -approximately), attempting to minimize the maximum mean Curvature magnitude. -No higher-order derivatives are considered, as the interpolation is -independent per triangle, only sharing constraints on their boundaries. -:param mesh_gl: input MeshGL. -:param sharpened_edges: If desired, you can supply a vector of sharpened -halfedges, which should in general be a small subset of all halfedges. Order -of entries doesn't matter, as each one specifies the desired smoothness -(between zero and one, with one the default for all unspecified halfedges) -and the halfedge index (3 * triangle index + [0,1,2] where 0 is the edge -between triVert 0 and 1, etc). -At a smoothness value of zero, a sharp crease is made. The smoothness is -interpolated along each edge, so the specified value should be thought of as -an average. Where exactly two sharpened edges meet at a vertex, their -tangents are rotated to be colinear so that the sharpened edge can be -continuous. Vertices with only one sharpened edge are completely smooth, -allowing sharpened edges to smoothly vanish at termination. A single vertex -can be sharpened by sharping all edges that are incident on it, allowing -cones to be formed.)___"; -const char* manifold__sphere__radius__circular_segments = R"___(Constructs a geodesic sphere of a given radius. -:param radius: Radius of the sphere. Must be positive. -:param circular_segments: Number of segments along its -diameter. This number will always be rounded up to the nearest factor of -four, as this sphere is constructed by refining an octahedron. This means -there are a circle of vertices on all three of the axis planes. Default is -calculated by the static Defaults.)___"; -const char* manifold__split__cutter = R"___(Split cuts this manifold in two using the cutter manifold. The first result -is the intersection, second is the difference. This is more efficient than -doing them separately. -:param cutter:)___"; -const char* manifold__split_by_plane__normal__origin_offset = R"___(Convenient version of Split() for a half-space. -:param normal: This vector is normal to the cutting plane and its length does -not matter. The first result is in the direction of this vector, the second -result is on the opposite side. -:param origin_offset: The distance of the plane from the origin in the -direction of the normal vector.)___"; -const char* manifold__status = R"___(Returns the reason for an input Mesh producing an empty Manifold. This Status -only applies to Manifolds newly-created from an input Mesh - once they are -combined into a new Manifold via operations, the status reverts to NoError, -simply processing the problem mesh as empty. Likewise, empty meshes may still -show NoError, for instance if they are small enough relative to their -precision to be collapsed to nothing.)___"; -const char* manifold__tetrahedron = R"___(Constructs a tetrahedron centered at the origin with one vertex at (1,1,1) -and the rest at similarly symmetric points.)___"; -const char* manifold__transform__m = R"___(Transform this Manifold in space. The first three columns form a 3x3 matrix -transform and the last is a translation vector. This operation can be -chained. Transforms are combined and applied lazily. -:param m: The affine transform matrix to apply to all the vertices.)___"; -const char* manifold__translate__v = R"___(Move this Manifold in space. This operation can be chained. Transforms are -combined and applied lazily. -:param v: The vector to add to every vertex.)___"; -const char* manifold__trim_by_plane__normal__origin_offset = R"___(Identical to SplitByPlane(), but calculating and returning only the first -result. -:param normal: This vector is normal to the cutting plane and its length does -not matter. The result is in the direction of this vector from the plane. -:param origin_offset: The distance of the plane from the origin in the -direction of the normal vector.)___"; -const char* manifold__warp__warp_func = R"___(This function does not change the topology, but allows the vertices to be -moved according to any arbitrary input function. It is easy to create a -function that warps a geometrically valid object into one which overlaps, but -that is not checked here, so it is up to the user to choose their function -with discretion. -:param warp_func: A function that modifies a given vertex position.)___"; -const char* manifold__warp_batch__warp_func = R"___(Same as Manifold::Warp but calls warpFunc with with -a VecView which is roughly equivalent to std::span -pointing to all vec3 elements to be modified in-place -:param warp_func: A function that modifies multiple vertex positions.)___"; -const char* set_circular_segments__number = R"___(Sets the default number of circular segments for the -CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and -Manifold::Revolve() constructors. Overrides the edge length and angle -constraints and sets the number of segments to exactly this value. -:param number: Number of circular segments. Default is 0, meaning no -constraint is applied.)___"; -const char* set_min_circular_angle__angle = R"___(Sets an angle constraint the default number of circular segments for the -CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and -Manifold::Revolve() constructors. The number of segments will be rounded up -to the nearest factor of four. -:param angle: The minimum angle in degrees between consecutive segments. The -angle will increase if the the segments hit the minimum edge length. -Default is 10 degrees.)___"; -const char* set_min_circular_edge_length__length = R"___(Sets a length constraint the default number of circular segments for the -CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and -Manifold::Revolve() constructors. The number of segments will be rounded up -to the nearest factor of four. -:param length: The minimum length of segments. The length will -increase if the the segments hit the minimum angle. Default is 1.0.)___"; -const char* triangulate = R"___(Ear-clipping triangulator based on David Eberly's approach from Geometric -Tools, but adjusted to handle epsilon-valid polygons, and including a -fallback that ensures a manifold triangulation even for overlapping polygons. -This is an O(n^2) algorithm, but hopefully this is not a big problem as the -number of edges in a given polygon is generally much less than the number of -triangles in a mesh, and relatively few faces even need triangulation. -The main adjustments for robustness involve clipping the sharpest ears first -(a known technique to get higher triangle quality), and doing an exhaustive -search to determine ear convexity exactly if the first geometric result is -within precision.)___"; -const char* triangulate__polygons__precision = R"___(@brief Triangulates a set of ε-valid polygons. If the input is not -ε-valid, the triangulation may overlap, but will always return a -manifold result that matches the input edge directions. -:param polygons: The set of polygons, wound CCW and representing multiple -polygons and/or holes. -:param precision: The value of ε, bounding the uncertainty of the -input. -@return std::vector The triangles, referencing the original -polygon points in order.)___"; -const char* triangulate_idx__polys__precision = R"___(@brief Triangulates a set of ε-valid polygons. If the input is not -ε-valid, the triangulation may overlap, but will always return a -manifold result that matches the input edge directions. -:param polys: The set of polygons, wound CCW and representing multiple -polygons and/or holes. These have 2D-projected positions as well as -references back to the original vertices. -:param precision: The value of ε, bounding the uncertainty of the -input. -@return std::vector The triangles, referencing the original -vertex indicies.)___"; -} // namespace manifold_docs \ No newline at end of file diff --git a/bindings/python/gen_docs.py b/bindings/python/gen_docs.py index 540e34e60..ee5f14838 100644 --- a/bindings/python/gen_docs.py +++ b/bindings/python/gen_docs.py @@ -4,23 +4,30 @@ base = dirname(dirname(dirname(__file__))) + def snake_case(name): - return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower() + return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower() + def param_snake_case(m): - return f':{snake_case(m[0][1:])}:' - + return f":{snake_case(m[0][1:])}:" + + def method_key(name): - name = re.sub('\+', '_plus', name) - name = re.sub('\-', '_minus', name) - name = re.sub('\^', '_xor', name) - name = re.sub('\=', '_eq', name) - name = re.sub('\:', '_', name) - name = re.sub('\~', 'destroy_', name) + name = re.sub("\+", "_plus", name) + name = re.sub("\-", "_minus", name) + name = re.sub("\^", "_xor", name) + name = re.sub("\=", "_eq", name) + name = re.sub("\:", "_", name) + name = re.sub("\~", "destroy_", name) return name -parens_re = re.compile(r'[^(]+\(([^(]*(\(.*\))*[^(]*\))', flags=re.DOTALL) -args_re = re.compile(r'^[^,^\(^\)]*(\(.*\))*[^,^\(^\)]*[\s\&\*]([0-9\w]+)\s*[,\)]', flags=re.DOTALL) + +parens_re = re.compile(r"[^(]+\(([^(]*(\(.*\))*[^(]*\))", flags=re.DOTALL) +args_re = re.compile( + r"^[^,^\(^\)]*(\(.*\))*[^,^\(^\)]*[\s\&\*]([0-9\w]+)\s*[,\)]", flags=re.DOTALL +) + def parse_args(s): par = parens_re.match(s) @@ -28,22 +35,23 @@ def parse_args(s): return None out = [] arg_str = par[1] - while (m := re.search(args_re, arg_str)): + while m := re.search(args_re, arg_str): out += [snake_case(m[2])] - arg_str = arg_str[m.span()[1]:] + arg_str = arg_str[m.span()[1] :] return out + def collect(fname, matcher): - param_re = re.compile(r'@param (\w+)') - comment = '' + param_re = re.compile(r"@param (\w+)") + comment = "" with open(fname) as f: for line in f: line = line.lstrip() - if line.startswith('/**'): - comment = '' - elif line.startswith('*/'): + if line.startswith("/**"): + comment = "" + elif line.startswith("*/"): pass - elif line.startswith('*') and comment is not None: + elif line.startswith("*") and comment is not None: comment += line[1:].lstrip() elif comment and (m := matcher(line)): while (args := parse_args(line)) is None: @@ -53,39 +61,45 @@ def collect(fname, matcher): method = method_key(snake_case(m[1])) comment = re.sub(param_re, param_snake_case, comment) - method = '__'.join([method, *args]) + method = "__".join([method, *args]) assert method not in comments comments[method] = comment - comment = '' + comment = "" + comments = {} -method_re = re.compile(r'(\w+::[\w\-\+\^\=\:]+)\(') -function_re = re.compile(r'([\w\-\+\^\=\:]+)\(') +method_re = re.compile(r"(\w+::[\w\-\+\^\=\:]+)\(") +function_re = re.compile(r"([\w\-\+\^\=\:]+)\(") + # we don't handle inline functions in classes properly # so instead just white-list functions we want def select_functions(s): m = function_re.search(s) - if m and 'Triangulate' in m[0]: + if m and "Triangulate" in m[0]: return m - if m and 'Circular' in m[0]: + if m and "Circular" in m[0]: return m return None -collect(f'{base}/src/manifold/src/manifold.cpp', lambda s: method_re.search(s)) -collect(f'{base}/src/manifold/src/constructors.cpp', lambda s: method_re.search(s)) -collect(f'{base}/src/cross_section/src/cross_section.cpp', lambda s: method_re.search(s)) -collect(f'{base}/src/polygon/src/polygon.cpp', select_functions) -collect(f'{base}/src/utilities/include/public.h', select_functions) + +collect(f"{base}/src/manifold/src/manifold.cpp", lambda s: method_re.search(s)) +collect(f"{base}/src/manifold/src/constructors.cpp", lambda s: method_re.search(s)) +collect( + f"{base}/src/cross_section/src/cross_section.cpp", lambda s: method_re.search(s) +) +collect(f"{base}/src/polygon/src/polygon.cpp", select_functions) +collect(f"{base}/src/utilities/include/public.h", select_functions) comments = dict(sorted(comments.items())) -gen_h = f'{base}/bindings/python/autogen_docstrings.inl' -with open(gen_h, 'w') as f: - f.write('#pragma once\n\n') - f.write('// AUTOGENERATED BY gen_docs.py\n\n') - f.write('namespace manifold_docstrings {\n') +gen_h = f"autogen_docstrings.inl" +with open(gen_h, "w") as f: + f.write("#pragma once\n\n") + f.write("// --- AUTO GENERATED ---\n") + f.write("// gen_docs.py is run by cmake build\n\n") + f.write("namespace manifold_docstrings {\n") for key, doc in comments.items(): f.write(f'const char* {key} = R"___({doc.strip()})___";\n') - f.write('} // namespace manifold_docs') + f.write("} // namespace manifold_docs") diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 6d30eebef..361529caf 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -15,7 +15,7 @@ #include #include -#include "autogen_docstrings.inl" +#include "autogen_docstrings.inl" // generated in build folder #include "cross_section.h" #include "manifold.h" #include "nanobind/nanobind.h" From 99f0c30b6933a21429ea826254211baf1f389ccf Mon Sep 17 00:00:00 2001 From: K Date: Thu, 21 Dec 2023 08:15:55 -0500 Subject: [PATCH 6/8] document gen_docs.py --- bindings/python/gen_docs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bindings/python/gen_docs.py b/bindings/python/gen_docs.py index ee5f14838..4215e1809 100644 --- a/bindings/python/gen_docs.py +++ b/bindings/python/gen_docs.py @@ -4,6 +4,12 @@ base = dirname(dirname(dirname(__file__))) +# This script runs with no CLI arguments +# and scrapes documentation comments and c++ function signatures +# from the manifold sources, in order to generate a c++ header file +# exposing the comments as string variables named by function names. +# This allows python bindings to re-use the c++ comments directly. +# Some snake-casing of params is applied for python use case. def snake_case(name): return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower() From ce63a8333751495bac280f8c0eb1e7581094fc99 Mon Sep 17 00:00:00 2001 From: K Date: Thu, 21 Dec 2023 21:03:35 -0500 Subject: [PATCH 7/8] more readme, remap mesh_gl references --- bindings/python/README.md | 24 ++++++++++++++++++++++++ bindings/python/gen_docs.py | 22 ++++++++++------------ 2 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 bindings/python/README.md diff --git a/bindings/python/README.md b/bindings/python/README.md new file mode 100644 index 000000000..a39d9a373 --- /dev/null +++ b/bindings/python/README.md @@ -0,0 +1,24 @@ +# Python Bindings + +## Autogenerated Doc-Strings + +Doc-strings for the python API wrapper are generated by gen_docs.py +The script is run automatically during the build to keep python +doc strings fully up-to-date with c++ sources. + +It scrapes documentation comments and c++ function signatures +from the manifold c++ sources, in order to generate a c++ header file +exposing the comments as string variables named by function names. +This allows python bindings to re-use the c++ comments directly. + +Some snake-casing of params is applied for python use case. + +--- + +When modifying the Manifold C++ sources, you may need to update +gen_docs.py. For example, top-level free functions are white-listed, +so if you add a new one, you will need to add it in gen_docs.py. + +Similarly, the list of source files to parse is also white listed, +so if you define functions in new files that need python wrappers, +you will also need to up gen_docs.py. \ No newline at end of file diff --git a/bindings/python/gen_docs.py b/bindings/python/gen_docs.py index 4215e1809..3a7c17d60 100644 --- a/bindings/python/gen_docs.py +++ b/bindings/python/gen_docs.py @@ -4,19 +4,17 @@ base = dirname(dirname(dirname(__file__))) -# This script runs with no CLI arguments -# and scrapes documentation comments and c++ function signatures -# from the manifold sources, in order to generate a c++ header file -# exposing the comments as string variables named by function names. -# This allows python bindings to re-use the c++ comments directly. -# Some snake-casing of params is applied for python use case. - def snake_case(name): return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower() -def param_snake_case(m): - return f":{snake_case(m[0][1:])}:" +def python_param_modifier(comment): + # p = f":{snake_case(m[0][1:])}:" + comment = re.sub(r"@(param \w+)", lambda m: f':{snake_case(m[1])}:', comment) + # python API renames `MeshGL` to `Mesh` + comment = re.sub('mesh_gl', 'mesh', comment) + comment = re.sub('MeshGL', 'Mesh', comment) + return comment def method_key(name): @@ -47,8 +45,7 @@ def parse_args(s): return out -def collect(fname, matcher): - param_re = re.compile(r"@param (\w+)") +def collect(fname, matcher, param_modifier=python_param_modifier): comment = "" with open(fname) as f: for line in f: @@ -66,7 +63,8 @@ def collect(fname, matcher): break method = method_key(snake_case(m[1])) - comment = re.sub(param_re, param_snake_case, comment) + # comment = re.sub(param_re, param_modifier, comment) + comment = param_modifier(comment) method = "__".join([method, *args]) assert method not in comments comments[method] = comment From 50ab9f760a93b33267ed58ca91e07f5c1e7b5f5f Mon Sep 17 00:00:00 2001 From: K Date: Sat, 23 Dec 2023 08:15:10 -0500 Subject: [PATCH 8/8] readme about verify python docstrings, autogen depend on sources --- bindings/python/CMakeLists.txt | 1 + bindings/python/README.md | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index a451ca546..cb9643b54 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -32,6 +32,7 @@ add_custom_target( BYPRODUCTS autogen_docstrings.inl ) target_include_directories(manifold3d PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +add_dependencies(autogen_docstrings manifold sdf polygon) add_dependencies(manifold3d autogen_docstrings) if(SKBUILD) diff --git a/bindings/python/README.md b/bindings/python/README.md index a39d9a373..c20eac498 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -21,4 +21,20 @@ so if you add a new one, you will need to add it in gen_docs.py. Similarly, the list of source files to parse is also white listed, so if you define functions in new files that need python wrappers, -you will also need to up gen_docs.py. \ No newline at end of file +you will also need to up gen_docs.py. + +To verify that python docs are correct after changes, you can +run the following commends from the manifold repo root: +``` +pip install . +python -c 'import manifold3d; help(manifold3d)' +``` + +Alternateively you could generate stubs with roughly the same info +``` +pip install nanobind-stubgen +pip install . +nanobind-stubgen manifold3d +``` +It will emit some warnings and write a file `manifold3d.pyi` +which will show all the function signatures and docstrings.