2
$\begingroup$

I'm trying to construct a BoundaryMeshRegion from a set of 3D surfaces generated using ParametricPlot3D. Here's my workflow:

  1. I created several ParametricPlot3D surfaces using equations of the form:
 ParametricPlot3D[{fx, fy, fz}, {u, umin, umax}, {v, vmin, vmax}]

In this case, u represents the parameter for each surface. However, for my surfaces, the parameter u (called t in my code) differs in its range across some surfaces. Specifically:

  • The t range for two of the surfaces is the same.
  • For the other two surfaces, the t range is different.
  1. I extracted points, lines, and polygons from the plots using Cases.
  2. Attempted to create a BoundaryMeshRegion by combining the extracted data.

2D Grapics:

enter image description here

a = ParametricPlot[{2 Cos[t], 2 Sin[t]}, {t, 0, Pi}, PlotRange -> All];
b = ParametricPlot[{4 + 2 Cos[t], 2 Sin[t]}, {t, Pi, 2 Pi}, 
   PlotRange -> All];
c = ParametricPlot[{8 + 2 Cos[t], 2 Sin[t]}, {t, 0, Pi}];
d = ParametricPlot[{4 + 6 Cos[t], 6 Sin[t]}, {t, Pi, 2 Pi}, 
   PlotRange -> All];

3D Grapics:

enter image description here

However, I get the error:
BoundaryMeshRegion::coplnr: The vertices in the polygon [...] are not coplanar.

Here’s a simplified version of my code:

(* Parameters *)
wrap = 4 \[Pi];
P1 = 100;
P2 = 20;
P = P1 + ((P2 - P1)/wrap)*t;
L = 1/(2 Pi)*Integrate[P, {t, 0, \[Phi]r}];

(* Transformation Matrix *)
M = ({
    {Cos[\[Phi]r], -Sin[\[Phi]r], 0, 0},
    {Sin[\[Phi]r], Cos[\[Phi]r], 0, 0},
    {0, 0, 1, L},
    {0, 0, 0, 1}
   });

(* Parametric Equations *)
{X[1], Y[1]} = {2 Cos[t], 2 Sin[t]} /. \[Phi]r -> wrap;
{X[2], Y[2]} = {4 + 2 Cos[t], 2 Sin[t]} /. \[Phi]r -> wrap;
{X[3], Y[3]} = {8 + 2 Cos[t], 2 Sin[t]} /. \[Phi]r -> wrap;
{X[4], Y[4]} = {4 + 6 Cos[t], 6 Sin[t]} /. \[Phi]r -> wrap;

(* Transformation *)
{abx, aby, abz, dm} = M . {X[1], Y[1], 0, 1};
{bcx, bcy, bcz, dm} = M . {X[2], Y[2], 0, 1};
{cdx, cdy, cdz, dm} = M . {X[3], Y[3], 0, 1};
{dex, dey, dez, dm} = M . {X[4], Y[4], 0, 1};

(* Parametric Plots *)
plot = {
   ParametricPlot3D[
    {abx, aby, abz} // Evaluate, {t, 0, Pi}, {\[Phi]r, 0, wrap},
    MaxRecursion -> 0, PlotPoints -> {400, 400}, 
    MeshFunctions -> {#4 &}, Mesh -> {{0, wrap}}, 
    MeshStyle -> Directive@{Thick, Red}, Boxed -> False, 
    Axes -> False, Method -> {"BoundaryOffset" -> False}
   ],
   ParametricPlot3D[
    {bcx, bcy, bcz} // Evaluate, {t, Pi, 2 Pi}, {\[Phi]r, 0, wrap},
    MaxRecursion -> 0, PlotPoints -> {400, 400}, 
    MeshFunctions -> {#4 &}, Mesh -> {{0, wrap}}, 
    MeshStyle -> Directive@{Thick, Red}, Boxed -> False, 
    Axes -> False, Method -> {"BoundaryOffset" -> False}
   ],
   ParametricPlot3D[
    {cdx, cdy, cdz} // Evaluate, {t, 0, Pi}, {\[Phi]r, 0, wrap},
    MaxRecursion -> 0, PlotPoints -> {400, 400}, 
    MeshFunctions -> {#4 &}, Mesh -> {{0, wrap}}, 
    MeshStyle -> Directive@{Thick, Red}, Boxed -> False, 
    Axes -> False, Method -> {"BoundaryOffset" -> False}
   ],
   ParametricPlot3D[
    {dex, dey, dez} // Evaluate, {t, Pi, 2 Pi}, {\[Phi]r, 0, wrap},
    MaxRecursion -> 0, PlotPoints -> {400, 400}, 
    MeshFunctions -> {#4 &}, Mesh -> {{0, wrap}}, 
    MeshStyle -> Directive@{Thick, Red}, Boxed -> False, 
    Axes -> False, Method -> {"BoundaryOffset" -> False}
   ]
};

(* Extract Points and Polygons *)
pts = Cases[plot, GraphicsComplex[pts_, rest__] :> pts, -1][[1]];
polys = Cases[plot, GraphicsGroup[data_] :> data, -1][[1, 1]];
lines = Cases[plot, _Line, -1];

(* Create MeshRegion *)
reg = BoundaryMeshRegion[pts, 
  Polygon[Join[lines /. Line[pts_] :> Rest@pts, First@First@polys]]]

My Questions:

  1. How can I address the non-coplanar vertex issue? Is there a reliable way to preprocess the data to ensure coplanarity?
  2. Does the fact that the t ranges differ between parametric equations affect how the points or polygons should be combined?
  3. Should I use an alternative method (e.g., MeshRegion) instead of BoundaryMeshRegion?
  4. Is there a way to triangulate the extracted surface data to resolve this issue?

Thank you for your time and insights—any help would be greatly appreciated!

$\endgroup$
3
  • 1
    $\begingroup$ What is wrap? please post the full code about the 2D and 3D graphics. $\endgroup$
    – cvgmt
    Commented Nov 19 at 10:36
  • $\begingroup$ @cvgmt sorry my bad, it's already post the full correct code. $\endgroup$
    – kai29lol
    Commented Nov 20 at 5:47
  • $\begingroup$ @cvgmt hi, do you have any idea to make this worked? $\endgroup$
    – kai29lol
    Commented Nov 20 at 15:14

2 Answers 2

2
$\begingroup$

The same way as

Clear["Global`*"];
wrap = 4 π;
P1 = 100;
P2 = 20;
P = P1 + ((P2 - P1)/wrap)*t;
L = 1/(2 Pi)*Integrate[P, {t, 0, ϕr}];
M = ({{Cos[ϕr], -Sin[ϕr], 0, 0}, {Sin[ϕr], 
     Cos[ϕr], 0, 0}, {0, 0, 1, L}, {0, 0, 0, 1}});
semicircles = 
 RandomSample@{Circle[{0, 0}, 2, {0, π}], 
   Circle[{4, 0}, 6, {π, 2 π}], Circle[{8, 0}, 2, {0, π}],
    Circle[{4, 0}, 2, {π, 2 π}]}; 
curve = 
 DiscretizeRegion[RegionUnion[semicircles], AccuracyGoal -> 3];
reg = RegionBoundary[
   BoundaryMeshRegion[MeshCoordinates[curve], MeshCells[curve, 1]]];
pts = MeshCoordinates[reg][[MeshCells[reg, 1][[;; , 1, 1]]]];
ptss = Table[
   Most /@ (M . Join[#, {0, 1}] &) /@ pts, {ϕr, 0, wrap, .05}];
{m, n, p} = Dimensions[ptss];
bm = BoundaryMeshRegion[
  Catenate[
   ptss], {Table[
    Polygon[{{#1[[1]], #2[[1]], #2[[2]]}, {#1[[2]], #1[[1]], \
#2[[2]]}}] & @@@ 
     Thread@{Partition[Range[1, n] + j*n, 2, 1, 1], 
       Partition[Range[1, n] + (j + 1)*n, 2, 1, 1]}, {j, 0, m - 2}], 
   Polygon[Range[1, n]], Polygon[Range[1, n] + (m - 1)*n]}]
bm // Volume

7528.19

enter image description here

  • Reply to comment. Since BoundaryDiscretizeGraphics[Show@{a, b, c, d}] fail after version 13, we have to use another approach to get the boundary line.
Clear["Global`*"];
wrap = 4 π;
P1 = 100;
P2 = 20;
P = P1 + ((P2 - P1)/wrap)*t;
L = 1/(2 Pi)*Integrate[P, {t, 0, ϕr}];
M = ({{Cos[ϕr], -Sin[ϕr], 0, 0}, {Sin[ϕr], 
     Cos[ϕr], 0, 0}, {0, 0, 1, L}, {0, 0, 0, 1}});
a = ParametricPlot[{2 Cos[t], 2 Sin[t]}, {t, 0, Pi}, PlotRange -> All];
b = ParametricPlot[{4 + 2 Cos[t], 2 Sin[t]}, {t, Pi, 2 Pi}, 
   PlotRange -> All];
c = ParametricPlot[{8 + 2 Cos[t], 2 Sin[t]}, {t, 0, Pi}];
d = ParametricPlot[{4 + 6 Cos[t], 6 Sin[t]}, {t, Pi, 2 Pi}, 
   PlotRange -> All];
reg1 = DiscretizeGraphics[Show@{a, b, c, d}]; 
reg = 
 RegionBoundary@
  BoundaryMeshRegion[MeshCoordinates[reg1], MeshCells[reg1, 1]]; 
pts =
  MeshCoordinates[reg][[MeshCells[reg, 1][[;; , 1, 1]]]];
ptss = Table[
   Most /@ (M . Join[#, {0, 1}] &) /@ pts, {ϕr, 0, wrap, .05}];
{m, n, p} = Dimensions[ptss];
bm = BoundaryMeshRegion[
  Catenate[
   ptss], {Table[
    Polygon[{{#1[[1]], #2[[1]], #2[[2]]}, {#1[[2]], #1[[1]], \
#2[[2]]}}] & @@@ 
     Thread@{Partition[Range[1, n] + j*n, 2, 1, 1], 
       Partition[Range[1, n] + (j + 1)*n, 2, 1, 1]}, {j, 0, m - 2}], 
   Polygon[Range[1, n]], Polygon[Range[1, n] + (m - 1)*n]}]
$\endgroup$
3
  • $\begingroup$ I am trying to combine semicircles defined using ParametricPlot into a single region and then discretize it. When I run this, I get the following errors: - RegionUnion::reg: is not a correctly specified region. - DiscretizeRegion::regp: A correctly specified region expected at position 1.... It seems that the objects generated by ParametricPlot are not being recognized as regions. How can I fix this and properly combine the semicircles into a single unified curve for discretization? Is there a better way to approach this problem? @cvgmt $\endgroup$
    – kai29lol
    Commented Nov 21 at 10:19
  • $\begingroup$ @kai29lol We may test reg1 = DiscretizeGraphics[Show@{a, b, c, d}]; reg = RegionBoundary@ BoundaryMeshRegion[MeshCoordinates[reg1], MeshCells[reg1, 1]]; pts = (MeshCoordinates[ reg][[MeshCells[reg, 1][[;; , 1, 1]]]])[[1 ;; -1 ;; 5]]; $\endgroup$
    – cvgmt
    Commented Nov 21 at 11:14
  • $\begingroup$ you are my saviour @cvgmt, thank you. $\endgroup$
    – kai29lol
    Commented Nov 24 at 2:22
6
$\begingroup$

There are a couple issues with your current approach.

(1) You're trying to make a Polygon cell from the points on a spiraling line (red line shown in plots). I'm not sure what your intention is here, but this produces the badly formed Polygon that BoundaryMeshRegion is complaining about. You can have it ignore the coplanarity issue by setting an internal option BoundaryMeshRegion[..., Method -> {"CoplanarityTolerance" -> Infinity}], however the Polygon is still badly formed.

(2) You're constructing a BoundaryMeshRegion, though the shape you're creating is not enclosed on the top and bottom, so there is no clear boundary between inside and outside.

To get a MeshRegion of all the combined plots you can use:

merged = RegionUnion[DiscretizeGraphics /@ plot]

MeshRegion of merged surface

If you do need a closed surface for BoundaryMeshRegion, you can do some more work to create and add the Polygon caps for the top and bottom.

findCaps[mesh_?MeshRegionQ] :=
 Block[{connectivity, neighborCounts, edgeCount, edgeIndices, 
        edgeVertices, holeGraphs, holePolygons, caps},
  connectivity = MeshConnectivityGraph[mesh, {1, 2}];
  neighborCounts = VertexDegree[connectivity];
  edgeCount = MeshCellCount[mesh, 1];
  edgeIndices = Select[Range[edgeCount], neighborCounts[[#]] === 1 &];
  edgeVertices = MeshCells[mesh, 1, "Multicells" -> True][[1, 1]][[edgeIndices]];
  holeGraphs = Select[Graph[edgeVertices] // ConnectedGraphComponents, Length[VertexList[#]] > 10 &];
  holePolygons = Polygon[FindHamiltonianPath /@ holeGraphs];
  caps = MeshRegion[MeshCoordinates[mesh], holePolygons];
  caps /; MeshRegionQ[caps]
  ]
caps = findCaps[merged];
closed = RegionUnion[merged, caps];
bmr = BoundaryMeshRegion[MeshCoordinates[closed], MeshCells[closed, 2, "Multicells" -> True]]

BoundaryMeshRegion of closed surface

Now the shape has a well-defined interior:

Volume[bmr]

5020.65

Note: For these examples I set wrap = 2 Pi and updated the ParametricPlot3D calls to use PlotPoints -> {120, 120}. It should work for higher PlotPoint values, but the findCaps function will take much longer.

$\endgroup$
4
  • $\begingroup$ hi @Alec, I found there is some code error when I try to run the code mathematica (* Merging and BoundaryMeshRegion *) merged = RegionUnion[DiscretizeGraphics /@ plot]; caps = findCaps[merged]; closed = RegionUnion[merged, caps]; bmr = BoundaryMeshRegion[MeshCoordinates[closed], MeshCells[closed, 2]]; this error come up BoundaryMeshRegion::binsect: The boundary curves self-intersect or cross each other... $\endgroup$
    – kai29lol
    Commented Nov 20 at 6:07
  • $\begingroup$ do you have any solution for this? $\endgroup$
    – kai29lol
    Commented Nov 21 at 10:08
  • $\begingroup$ @kai29lol – I'm not seeing that error on my side, but it's likely due to the plots not generating meshes that align properly. Typically, when you want a region it's better to construct it directly as cvgmt does in their answer, instead of going through a plot function. The meshes generated by plots are more intended to look nice, and might not work as well geometrically. $\endgroup$
    – Alec
    Commented Nov 21 at 16:57
  • $\begingroup$ mind if I asked what version is your mathematica? Because like cvgmt said, version might be an issue. $\endgroup$
    – kai29lol
    Commented Nov 22 at 2:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.