17

Let's say I have some closed curve, which could be given by a parametric representation, or by a closed spline as in:

pts = {{-1, 0}, {-1, 1}, {0, 0}, {1, 1}, {1, 0}};

which looks like so:

Graphics[{Thick, BSplineCurve[pts, SplineClosed -> True]}]

enter image description here

My question is, is there an efficient way to convert the space inside the boundary to a Region (which could then be postprocessed by any of Mathematica's functions that act on such objects)? I don't see any built-in functionality that would achieve this. Do I have to define a Boolean function that tests whether a point lies within the area enclosed by the curve, and plug that into ImplicitRegion? If so, what would be a good approach to do this?

Of course, extending this idea to the 3D case (a parametric closed surface defining a 3D region) would be of interest as well.

Pirx
  • 4,139
  • 12
  • 37

4 Answers4

11

In principle this should work:

pts = {{-1, 0}, {-1, 1}, {0, 0}, {1, 1}, {1, 0}};

g = Graphics[{FilledCurve@BSplineCurve[pts, SplineClosed -> True]}]

enter image description here

DiscretizeGraphics[g]

enter image description here

But as you can see, the result is wrong.

This may be the same bug as described here:

You may want to report it to Wolfram again in the hope that more reports equal a higher likelihood of fixing it ...

Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
  • Bummer! I'll report it, but I won't get my hopes up. Someone else recently commented about Region-associated functionality being incomplete, and that seems to be the case. One would think that what I want to do corresponds to functionality that is required frequently. So, I'll have to figure out how to define my own Boolean function that I can plug into ImplicitRegion? That might end up being very slow, certainly if I use contour integrals. Are there any good (=fast) algorithms for this that I should use? – Pirx Sep 05 '16 at 14:21
  • @Pirx You could approximate your curve with a polygon. Do you really need a BSplineCurve or is this question more general? A polygon is already a region as it is and it is fast to work with. – Szabolcs Sep 05 '16 at 14:24
  • The question is intended to be more general. Yes, you are correct, certainly in 2D I could just convert whatever curve I have into a polygon. I wonder if there is perhaps a pre-defined function in Mma that does this faster than code I could come up with on my own? I realize this is simple enough to code, but perhaps there's already a better way. More generally, however, being able to just feed curves or surfaces into some function that converts them into regions in a smart way would be desirable. Also, the extension of the polynomial idea into 3D would be less trivial, I think... – Pirx Sep 05 '16 at 16:32
8

The problem mentionned by @Szabolcs in his answer is solved on Mathematica version 12.2 (the problem is present on Mathematica 12.1)

$Version

pts = {{-1, 0}, {-1, 1}, {0, 0}, {1, 1}, {1, 0}}; g = Graphics[{FilledCurve@BSplineCurve[pts, SplineClosed -> True]}] DiscretizeGraphics[g]

enter image description here

andre314
  • 18,474
  • 1
  • 36
  • 69
6

It seems that the DiscretizeGraphics code doesn't know how to handle the SplineClosed->True option of the BSplineCurve object. As a workaround, you can use my FullBSplineCurve function to convert the SplineClosed->True option into an equivalent SplineKnots specification, and then perform the discretization:

DiscretizeGraphics @ FilledCurve @ FullBSplineCurve @ BSplineCurve[
    {{-1, 0}, {-1, 1}, {0, 0}, {1, 1}, {1, 0}},
    SplineClosed->True
]

enter image description here

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
  • FullBSplineCurve seems to fail with numeric SplineWeights and either SplineClosed -> True or SplineKnots -> "Unclamped". Example: BSplineCurve[{{50, 70}, {70, 50}, {50, 30}, {30, 50}}, SplineWeights -> {1, 1, 1, 1}, SplineClosed -> True]. The result is renderer differently both by Graphics and DiscretizeGraphics. – Alexey Popkov Dec 03 '21 at 15:35
0

A clean solution is to 1) use BSplineFunction, 2) calculate points along the closed curve, 3) call these points a Polygon. See below

poly = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
weights = {7.8, 9.1, 4.8, 7.8};

bSplineFunction = BSplineFunction[poly, SplineClosed -> True, SplineDegree -> 3, SplineWeights -> weights]; bsPolygonPts = Table[bSplineFunction[i], {i, 0, 1, .01}];

bSplinePts = Graphics[Point[bsPolygonPts]];

bSplineCurve = Graphics[{Red, BSplineCurve[poly, SplineClosed -> True, SplineDegree -> 3, SplineWeights -> weights]}];

bSplinePolygon = Graphics[{Red, Polygon[bsPolygonPts]}];

Show[ply, bSplineCurve, bSplinePts] Show[ply, bSplinePolygon]