17
$\begingroup$

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.

$\endgroup$

4 Answers 4

11
$\begingroup$

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 ...

$\endgroup$
3
  • $\begingroup$ 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? $\endgroup$
    – Pirx
    Commented Sep 5, 2016 at 14:21
  • $\begingroup$ @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. $\endgroup$
    – Szabolcs
    Commented Sep 5, 2016 at 14:24
  • $\begingroup$ 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... $\endgroup$
    – Pirx
    Commented Sep 5, 2016 at 16:32
8
$\begingroup$

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

$\endgroup$
6
$\begingroup$

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

$\endgroup$
1
  • $\begingroup$ 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. $\endgroup$ Commented Dec 3, 2021 at 15:35
0
$\begingroup$

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]
$\endgroup$
1
  • 1
    $\begingroup$ What's ply in the last two lines of code? $\endgroup$
    – Michael E2
    Commented Jul 11, 2022 at 19:01

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.