constructors.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. // Copyright 2021 The Manifold Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "csg_tree.h"
  15. #include "disjoint_sets.h"
  16. #include "impl.h"
  17. #include "manifold/manifold.h"
  18. #include "manifold/polygon.h"
  19. #include "parallel.h"
  20. namespace {
  21. using namespace manifold;
  22. template <typename P, typename I>
  23. std::shared_ptr<Manifold::Impl> SmoothImpl(
  24. const MeshGLP<P, I>& meshGL,
  25. const std::vector<Smoothness>& sharpenedEdges) {
  26. DEBUG_ASSERT(meshGL.halfedgeTangent.empty(), std::runtime_error,
  27. "when supplying tangents, the normal constructor should be used "
  28. "rather than Smooth().");
  29. MeshGLP<P, I> meshTmp = meshGL;
  30. meshTmp.faceID.resize(meshGL.NumTri());
  31. std::iota(meshTmp.faceID.begin(), meshTmp.faceID.end(), 0);
  32. std::shared_ptr<Manifold::Impl> impl =
  33. std::make_shared<Manifold::Impl>(meshTmp);
  34. impl->CreateTangents(impl->UpdateSharpenedEdges(sharpenedEdges));
  35. // Restore the original faceID
  36. const size_t numTri = impl->NumTri();
  37. for (size_t i = 0; i < numTri; ++i) {
  38. if (meshGL.faceID.size() == numTri) {
  39. impl->meshRelation_.triRef[i].faceID =
  40. meshGL.faceID[impl->meshRelation_.triRef[i].faceID];
  41. } else {
  42. impl->meshRelation_.triRef[i].faceID = -1;
  43. }
  44. }
  45. return impl;
  46. }
  47. } // namespace
  48. namespace manifold {
  49. /**
  50. * Constructs a smooth version of the input mesh by creating tangents; this
  51. * method will throw if you have supplied tangents with your mesh already. The
  52. * actual triangle resolution is unchanged; use the Refine() method to
  53. * interpolate to a higher-resolution curve.
  54. *
  55. * By default, every edge is calculated for maximum smoothness (very much
  56. * approximately), attempting to minimize the maximum mean Curvature magnitude.
  57. * No higher-order derivatives are considered, as the interpolation is
  58. * independent per triangle, only sharing constraints on their boundaries.
  59. *
  60. * @param meshGL input MeshGL.
  61. * @param sharpenedEdges If desired, you can supply a vector of sharpened
  62. * halfedges, which should in general be a small subset of all halfedges. Order
  63. * of entries doesn't matter, as each one specifies the desired smoothness
  64. * (between zero and one, with one the default for all unspecified halfedges)
  65. * and the halfedge index (3 * triangle index + [0,1,2] where 0 is the edge
  66. * between triVert 0 and 1, etc).
  67. *
  68. * At a smoothness value of zero, a sharp crease is made. The smoothness is
  69. * interpolated along each edge, so the specified value should be thought of as
  70. * an average. Where exactly two sharpened edges meet at a vertex, their
  71. * tangents are rotated to be colinear so that the sharpened edge can be
  72. * continuous. Vertices with only one sharpened edge are completely smooth,
  73. * allowing sharpened edges to smoothly vanish at termination. A single vertex
  74. * can be sharpened by sharping all edges that are incident on it, allowing
  75. * cones to be formed.
  76. */
  77. Manifold Manifold::Smooth(const MeshGL& meshGL,
  78. const std::vector<Smoothness>& sharpenedEdges) {
  79. return Manifold(SmoothImpl(meshGL, sharpenedEdges));
  80. }
  81. /**
  82. * Constructs a smooth version of the input mesh by creating tangents; this
  83. * method will throw if you have supplied tangents with your mesh already. The
  84. * actual triangle resolution is unchanged; use the Refine() method to
  85. * interpolate to a higher-resolution curve.
  86. *
  87. * By default, every edge is calculated for maximum smoothness (very much
  88. * approximately), attempting to minimize the maximum mean Curvature magnitude.
  89. * No higher-order derivatives are considered, as the interpolation is
  90. * independent per triangle, only sharing constraints on their boundaries.
  91. *
  92. * @param meshGL64 input MeshGL64.
  93. * @param sharpenedEdges If desired, you can supply a vector of sharpened
  94. * halfedges, which should in general be a small subset of all halfedges. Order
  95. * of entries doesn't matter, as each one specifies the desired smoothness
  96. * (between zero and one, with one the default for all unspecified halfedges)
  97. * and the halfedge index (3 * triangle index + [0,1,2] where 0 is the edge
  98. * between triVert 0 and 1, etc).
  99. *
  100. * At a smoothness value of zero, a sharp crease is made. The smoothness is
  101. * interpolated along each edge, so the specified value should be thought of as
  102. * an average. Where exactly two sharpened edges meet at a vertex, their
  103. * tangents are rotated to be colinear so that the sharpened edge can be
  104. * continuous. Vertices with only one sharpened edge are completely smooth,
  105. * allowing sharpened edges to smoothly vanish at termination. A single vertex
  106. * can be sharpened by sharping all edges that are incident on it, allowing
  107. * cones to be formed.
  108. */
  109. Manifold Manifold::Smooth(const MeshGL64& meshGL64,
  110. const std::vector<Smoothness>& sharpenedEdges) {
  111. return Manifold(SmoothImpl(meshGL64, sharpenedEdges));
  112. }
  113. /**
  114. * Constructs a tetrahedron centered at the origin with one vertex at (1,1,1)
  115. * and the rest at similarly symmetric points.
  116. */
  117. Manifold Manifold::Tetrahedron() {
  118. return Manifold(std::make_shared<Impl>(Impl::Shape::Tetrahedron));
  119. }
  120. /**
  121. * Constructs a unit cube (edge lengths all one), by default in the first
  122. * octant, touching the origin. If any dimensions in size are negative, or if
  123. * all are zero, an empty Manifold will be returned.
  124. *
  125. * @param size The X, Y, and Z dimensions of the box.
  126. * @param center Set to true to shift the center to the origin.
  127. */
  128. Manifold Manifold::Cube(vec3 size, bool center) {
  129. if (size.x < 0.0 || size.y < 0.0 || size.z < 0.0 || la::length(size) == 0.) {
  130. return Invalid();
  131. }
  132. mat3x4 m({{size.x, 0.0, 0.0}, {0.0, size.y, 0.0}, {0.0, 0.0, size.z}},
  133. center ? (-size / 2.0) : vec3(0.0));
  134. return Manifold(std::make_shared<Impl>(Manifold::Impl::Shape::Cube, m));
  135. }
  136. /**
  137. * A convenience constructor for the common case of extruding a circle. Can also
  138. * form cones if both radii are specified.
  139. *
  140. * @param height Z-extent
  141. * @param radiusLow Radius of bottom circle. Must be positive.
  142. * @param radiusHigh Radius of top circle. Can equal zero. Default is equal to
  143. * radiusLow.
  144. * @param circularSegments How many line segments to use around the circle.
  145. * Default is calculated by the static Defaults.
  146. * @param center Set to true to shift the center to the origin. Default is
  147. * origin at the bottom.
  148. */
  149. Manifold Manifold::Cylinder(double height, double radiusLow, double radiusHigh,
  150. int circularSegments, bool center) {
  151. if (height <= 0.0 || radiusLow <= 0.0) {
  152. return Invalid();
  153. }
  154. const double scale = radiusHigh >= 0.0 ? radiusHigh / radiusLow : 1.0;
  155. const double radius = fmax(radiusLow, radiusHigh);
  156. const int n = circularSegments > 2 ? circularSegments
  157. : Quality::GetCircularSegments(radius);
  158. SimplePolygon circle(n);
  159. const double dPhi = 360.0 / n;
  160. for (int i = 0; i < n; ++i) {
  161. circle[i] = {radiusLow * cosd(dPhi * i), radiusLow * sind(dPhi * i)};
  162. }
  163. Manifold cylinder = Manifold::Extrude({circle}, height, 0, 0.0, vec2(scale));
  164. if (center)
  165. cylinder = cylinder.Translate(vec3(0.0, 0.0, -height / 2.0)).AsOriginal();
  166. return cylinder;
  167. }
  168. /**
  169. * Constructs a geodesic sphere of a given radius.
  170. *
  171. * @param radius Radius of the sphere. Must be positive.
  172. * @param circularSegments Number of segments along its
  173. * diameter. This number will always be rounded up to the nearest factor of
  174. * four, as this sphere is constructed by refining an octahedron. This means
  175. * there are a circle of vertices on all three of the axis planes. Default is
  176. * calculated by the static Defaults.
  177. */
  178. Manifold Manifold::Sphere(double radius, int circularSegments) {
  179. if (radius <= 0.0) {
  180. return Invalid();
  181. }
  182. int n = circularSegments > 0 ? (circularSegments + 3) / 4
  183. : Quality::GetCircularSegments(radius) / 4;
  184. auto pImpl_ = std::make_shared<Impl>(Impl::Shape::Octahedron);
  185. pImpl_->Subdivide([n](vec3, vec4, vec4) { return n - 1; });
  186. for_each_n(autoPolicy(pImpl_->NumVert(), 1e5), pImpl_->vertPos_.begin(),
  187. pImpl_->NumVert(), [radius](vec3& v) {
  188. v = la::cos(kHalfPi * (1.0 - v));
  189. v = radius * la::normalize(v);
  190. if (std::isnan(v.x)) v = vec3(0.0);
  191. });
  192. pImpl_->Finish();
  193. // Ignore preceding octahedron.
  194. pImpl_->InitializeOriginal();
  195. return Manifold(pImpl_);
  196. }
  197. /**
  198. * Constructs a manifold from a set of polygons by extruding them along the
  199. * Z-axis.
  200. * Note that high twistDegrees with small nDivisions may cause
  201. * self-intersection. This is not checked here and it is up to the user to
  202. * choose the correct parameters.
  203. *
  204. * @param crossSection A set of non-overlapping polygons to extrude.
  205. * @param height Z-extent of extrusion.
  206. * @param nDivisions Number of extra copies of the crossSection to insert into
  207. * the shape vertically; especially useful in combination with twistDegrees to
  208. * avoid interpolation artifacts. Default is none.
  209. * @param twistDegrees Amount to twist the top crossSection relative to the
  210. * bottom, interpolated linearly for the divisions in between.
  211. * @param scaleTop Amount to scale the top (independently in X and Y). If the
  212. * scale is {0, 0}, a pure cone is formed with only a single vertex at the top.
  213. * Note that scale is applied after twist.
  214. * Default {1, 1}.
  215. */
  216. Manifold Manifold::Extrude(const Polygons& crossSection, double height,
  217. int nDivisions, double twistDegrees, vec2 scaleTop) {
  218. ZoneScoped;
  219. if (crossSection.size() == 0 || height <= 0.0) {
  220. return Invalid();
  221. }
  222. scaleTop.x = std::max(scaleTop.x, 0.0);
  223. scaleTop.y = std::max(scaleTop.y, 0.0);
  224. auto pImpl_ = std::make_shared<Impl>();
  225. ++nDivisions;
  226. auto& vertPos = pImpl_->vertPos_;
  227. Vec<ivec3> triVertsDH;
  228. auto& triVerts = triVertsDH;
  229. int nCrossSection = 0;
  230. bool isCone = scaleTop.x == 0.0 && scaleTop.y == 0.0;
  231. size_t idx = 0;
  232. PolygonsIdx polygonsIndexed;
  233. for (auto& poly : crossSection) {
  234. nCrossSection += poly.size();
  235. SimplePolygonIdx simpleIndexed;
  236. for (const vec2& polyVert : poly) {
  237. vertPos.push_back({polyVert.x, polyVert.y, 0.0});
  238. simpleIndexed.push_back({polyVert, static_cast<int>(idx++)});
  239. }
  240. polygonsIndexed.push_back(simpleIndexed);
  241. }
  242. for (int i = 1; i < nDivisions + 1; ++i) {
  243. double alpha = i / double(nDivisions);
  244. double phi = alpha * twistDegrees;
  245. vec2 scale = la::lerp(vec2(1.0), scaleTop, alpha);
  246. mat2 rotation({cosd(phi), sind(phi)}, {-sind(phi), cosd(phi)});
  247. mat2 transform = mat2({scale.x, 0.0}, {0.0, scale.y}) * rotation;
  248. size_t j = 0;
  249. size_t idx = 0;
  250. for (const auto& poly : crossSection) {
  251. for (size_t vert = 0; vert < poly.size(); ++vert) {
  252. size_t offset = idx + nCrossSection * i;
  253. size_t thisVert = vert + offset;
  254. size_t lastVert = (vert == 0 ? poly.size() : vert) - 1 + offset;
  255. if (i == nDivisions && isCone) {
  256. triVerts.push_back(ivec3(nCrossSection * i + j,
  257. lastVert - nCrossSection,
  258. thisVert - nCrossSection));
  259. } else {
  260. vec2 pos = transform * poly[vert];
  261. vertPos.push_back({pos.x, pos.y, height * alpha});
  262. triVerts.push_back(
  263. ivec3(thisVert, lastVert, thisVert - nCrossSection));
  264. triVerts.push_back(ivec3(lastVert, lastVert - nCrossSection,
  265. thisVert - nCrossSection));
  266. }
  267. }
  268. ++j;
  269. idx += poly.size();
  270. }
  271. }
  272. if (isCone)
  273. for (size_t j = 0; j < crossSection.size();
  274. ++j) // Duplicate vertex for Genus
  275. vertPos.push_back({0.0, 0.0, height});
  276. std::vector<ivec3> top = TriangulateIdx(polygonsIndexed);
  277. for (const ivec3& tri : top) {
  278. triVerts.push_back({tri[0], tri[2], tri[1]});
  279. if (!isCone) triVerts.push_back(tri + nCrossSection * nDivisions);
  280. }
  281. pImpl_->CreateHalfedges(triVertsDH);
  282. pImpl_->Finish();
  283. pImpl_->InitializeOriginal();
  284. pImpl_->MarkCoplanar();
  285. return Manifold(pImpl_);
  286. }
  287. /**
  288. * Constructs a manifold from a set of polygons by revolving this cross-section
  289. * around its Y-axis and then setting this as the Z-axis of the resulting
  290. * manifold. If the polygons cross the Y-axis, only the part on the positive X
  291. * side is used. Geometrically valid input will result in geometrically valid
  292. * output.
  293. *
  294. * @param crossSection A set of non-overlapping polygons to revolve.
  295. * @param circularSegments Number of segments along its diameter. Default is
  296. * calculated by the static Defaults.
  297. * @param revolveDegrees Number of degrees to revolve. Default is 360 degrees.
  298. */
  299. Manifold Manifold::Revolve(const Polygons& crossSection, int circularSegments,
  300. double revolveDegrees) {
  301. ZoneScoped;
  302. Polygons polygons;
  303. double radius = 0;
  304. for (const SimplePolygon& poly : crossSection) {
  305. size_t i = 0;
  306. while (i < poly.size() && poly[i].x < 0) {
  307. ++i;
  308. }
  309. if (i == poly.size()) {
  310. continue;
  311. }
  312. polygons.push_back({});
  313. const size_t start = i;
  314. do {
  315. if (poly[i].x >= 0) {
  316. polygons.back().push_back(poly[i]);
  317. radius = std::max(radius, poly[i].x);
  318. }
  319. const size_t next = i + 1 == poly.size() ? 0 : i + 1;
  320. if ((poly[next].x < 0) != (poly[i].x < 0)) {
  321. const double y = poly[next].y - poly[next].x *
  322. (poly[i].y - poly[next].y) /
  323. (poly[i].x - poly[next].x);
  324. polygons.back().push_back({0, y});
  325. }
  326. i = next;
  327. } while (i != start);
  328. }
  329. if (polygons.empty()) {
  330. return Invalid();
  331. }
  332. if (revolveDegrees > 360.0) {
  333. revolveDegrees = 360.0;
  334. }
  335. const bool isFullRevolution = revolveDegrees == 360.0;
  336. const int nDivisions =
  337. circularSegments > 2
  338. ? circularSegments
  339. : Quality::GetCircularSegments(radius) * revolveDegrees / 360;
  340. auto pImpl_ = std::make_shared<Impl>();
  341. auto& vertPos = pImpl_->vertPos_;
  342. Vec<ivec3> triVertsDH;
  343. auto& triVerts = triVertsDH;
  344. std::vector<int> startPoses;
  345. std::vector<int> endPoses;
  346. const double dPhi = revolveDegrees / nDivisions;
  347. // first and last slice are distinguished if not a full revolution.
  348. const int nSlices = isFullRevolution ? nDivisions : nDivisions + 1;
  349. for (const auto& poly : polygons) {
  350. size_t nPosVerts = 0;
  351. size_t nRevolveAxisVerts = 0;
  352. for (auto& pt : poly) {
  353. if (pt.x > 0) {
  354. nPosVerts++;
  355. } else {
  356. nRevolveAxisVerts++;
  357. }
  358. }
  359. for (size_t polyVert = 0; polyVert < poly.size(); ++polyVert) {
  360. const size_t startPosIndex = vertPos.size();
  361. if (!isFullRevolution) startPoses.push_back(startPosIndex);
  362. const vec2 currPolyVertex = poly[polyVert];
  363. const vec2 prevPolyVertex =
  364. poly[polyVert == 0 ? poly.size() - 1 : polyVert - 1];
  365. const int prevStartPosIndex =
  366. startPosIndex +
  367. (polyVert == 0 ? nRevolveAxisVerts + (nSlices * nPosVerts) : 0) +
  368. (prevPolyVertex.x == 0.0 ? -1 : -nSlices);
  369. for (int slice = 0; slice < nSlices; ++slice) {
  370. const double phi = slice * dPhi;
  371. if (slice == 0 || currPolyVertex.x > 0) {
  372. vertPos.push_back({currPolyVertex.x * cosd(phi),
  373. currPolyVertex.x * sind(phi), currPolyVertex.y});
  374. }
  375. if (isFullRevolution || slice > 0) {
  376. const int lastSlice = (slice == 0 ? nDivisions : slice) - 1;
  377. if (currPolyVertex.x > 0.0) {
  378. triVerts.push_back(ivec3(
  379. startPosIndex + slice, startPosIndex + lastSlice,
  380. // "Reuse" vertex of first slice if it lies on the revolve axis
  381. (prevPolyVertex.x == 0.0 ? prevStartPosIndex
  382. : prevStartPosIndex + lastSlice)));
  383. }
  384. if (prevPolyVertex.x > 0.0) {
  385. triVerts.push_back(
  386. ivec3(prevStartPosIndex + lastSlice, prevStartPosIndex + slice,
  387. (currPolyVertex.x == 0.0 ? startPosIndex
  388. : startPosIndex + slice)));
  389. }
  390. }
  391. }
  392. if (!isFullRevolution) endPoses.push_back(vertPos.size() - 1);
  393. }
  394. }
  395. // Add front and back triangles if not a full revolution.
  396. if (!isFullRevolution) {
  397. std::vector<ivec3> frontTriangles = Triangulate(polygons, pImpl_->epsilon_);
  398. for (auto& t : frontTriangles) {
  399. triVerts.push_back({startPoses[t.x], startPoses[t.y], startPoses[t.z]});
  400. }
  401. for (auto& t : frontTriangles) {
  402. triVerts.push_back({endPoses[t.z], endPoses[t.y], endPoses[t.x]});
  403. }
  404. }
  405. pImpl_->CreateHalfedges(triVertsDH);
  406. pImpl_->Finish();
  407. pImpl_->InitializeOriginal();
  408. pImpl_->MarkCoplanar();
  409. return Manifold(pImpl_);
  410. }
  411. /**
  412. * Constructs a new manifold from a vector of other manifolds. This is a purely
  413. * topological operation, so care should be taken to avoid creating
  414. * overlapping results. It is the inverse operation of Decompose().
  415. *
  416. * @param manifolds A vector of Manifolds to lazy-union together.
  417. */
  418. Manifold Manifold::Compose(const std::vector<Manifold>& manifolds) {
  419. std::vector<std::shared_ptr<CsgLeafNode>> children;
  420. for (const auto& manifold : manifolds) {
  421. children.push_back(manifold.pNode_->ToLeafNode());
  422. }
  423. return Manifold(CsgLeafNode::Compose(children));
  424. }
  425. /**
  426. * This operation returns a vector of Manifolds that are topologically
  427. * disconnected. If everything is connected, the vector is length one,
  428. * containing a copy of the original. It is the inverse operation of Compose().
  429. */
  430. std::vector<Manifold> Manifold::Decompose() const {
  431. ZoneScoped;
  432. DisjointSets uf(NumVert());
  433. // Graph graph;
  434. auto pImpl_ = GetCsgLeafNode().GetImpl();
  435. for (const Halfedge& halfedge : pImpl_->halfedge_) {
  436. if (halfedge.IsForward()) uf.unite(halfedge.startVert, halfedge.endVert);
  437. }
  438. std::vector<int> componentIndices;
  439. const int numComponents = uf.connectedComponents(componentIndices);
  440. if (numComponents == 1) {
  441. std::vector<Manifold> meshes(1);
  442. meshes[0] = *this;
  443. return meshes;
  444. }
  445. Vec<int> vertLabel(componentIndices);
  446. const int numVert = NumVert();
  447. std::vector<Manifold> meshes;
  448. for (int i = 0; i < numComponents; ++i) {
  449. auto impl = std::make_shared<Impl>();
  450. // inherit original object's precision
  451. impl->epsilon_ = pImpl_->epsilon_;
  452. impl->tolerance_ = pImpl_->tolerance_;
  453. Vec<int> vertNew2Old(numVert);
  454. const int nVert =
  455. copy_if(countAt(0), countAt(numVert), vertNew2Old.begin(),
  456. [i, &vertLabel](int v) { return vertLabel[v] == i; }) -
  457. vertNew2Old.begin();
  458. impl->vertPos_.resize(nVert);
  459. vertNew2Old.resize(nVert);
  460. gather(vertNew2Old.begin(), vertNew2Old.end(), pImpl_->vertPos_.begin(),
  461. impl->vertPos_.begin());
  462. Vec<int> faceNew2Old(NumTri());
  463. const auto& halfedge = pImpl_->halfedge_;
  464. const int nFace =
  465. copy_if(countAt(0_uz), countAt(NumTri()), faceNew2Old.begin(),
  466. [i, &vertLabel, &halfedge](int face) {
  467. return vertLabel[halfedge[3 * face].startVert] == i;
  468. }) -
  469. faceNew2Old.begin();
  470. if (nFace == 0) continue;
  471. faceNew2Old.resize(nFace);
  472. impl->GatherFaces(*pImpl_, faceNew2Old);
  473. impl->ReindexVerts(vertNew2Old, pImpl_->NumVert());
  474. impl->Finish();
  475. meshes.push_back(Manifold(impl));
  476. }
  477. return meshes;
  478. }
  479. } // namespace manifold