10
$\begingroup$

I have a closed curve. I am trying to enclose this closed curve with another closed curve, which looks identical in the shape of the first closed curve, but slightly bigger. How to do this?

pts = {{-1, 0}, {-1, 1}, {0, 0}, {1, 1}, {1, 0}};
Graphics[{Thick, BSplineCurve[pts, SplineClosed -> True]}]
$\endgroup$
  • 1
    $\begingroup$ Your question is unclearly formulated: what do do you mean by 'bigger"? how about pts = {{-1, 0}, {-1, 1}, {0, 0}, {1, 1}, {1, 0}}; Graphics[{Thick, BSplineCurve[pts, SplineClosed -> True], BSplineCurve[1.1*pts, SplineClosed -> True]}]? $\endgroup$ – user64494 Aug 14 at 15:34
  • $\begingroup$ Similar to the situation of this, enclosing a small rectangle by a bigger rectangle, all the side of the smaller rectangle is equal distance from the bigger rectangle $\endgroup$ – acoustics Aug 14 at 15:37
  • 3
    $\begingroup$ So in short, you wanted a parallel curve / offset curve of your original curve. $\endgroup$ – J. M.'s discontentment Aug 14 at 23:15
17
$\begingroup$

1. You can use BSplineFunction as follows:

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

ClearAll[explode, bsf]
explode[f_] := f[#] + #2 Cross @ Normalize[f'[#]] &;

bsf = BSplineFunction[pts, SplineClosed -> True];

Graphics[{Thick, BSplineCurve[pts, SplineClosed -> True], Blue, 
   Line[explode[bsf][#, .2] & /@ Subdivide[100]]}] // Framed

enter image description here

Graphics[{Thick, Line[bsf /@ Subdivide[100]], Blue, 
   Line[explode[bsf][#, .2] & /@ Subdivide[100]]}] // Framed

enter image description here

2. You can also use bsf and explode with ParametricPlot:

ParametricPlot[{bsf@t, explode[bsf][t, .2], explode[bsf][t, -.1]}, {t, 0, 1}, 
 PlotStyle -> {Black, Blue, Green}, BaseStyle -> Thick, Axes -> False, 
 ImageSize -> Large]

enter image description here

3. Alternatively, you can use SignedRegionDistance + ContourPlot:

srd = SignedRegionDistance[Polygon[bsf /@ Subdivide[100]]];

ContourPlot[srd[{x, y}], {x, -3/2, 3/2}, {y, -.5, 1}, 
 ContourShading -> None, 
 Contours -> {{.2, Blue}, {0., {Thick, Black}}}, 
 Frame -> False, ImageSize -> Large, AspectRatio -> Automatic]

enter image description here

Use the options ContourShading ->{PatternFilling["Grid", ImageScaled[1/12]], None,None} and Epilog->{Red, Disk[{0.,.75},.25], Black,Disk[{-.5,.8},.1],Disk[{.5,.8},.1]} to get

enter image description here

Related Q/As:

| improve this answer | |
$\endgroup$
  • $\begingroup$ It is exactly what I am looking for. Could you please explain to me how you did this? $\endgroup$ – acoustics Aug 14 at 16:03
  • 1
    $\begingroup$ @acoustics, if we have a function that describes the curve (luckily we have BSplineFunction), we can use its normalized gradient to get the directions at each point on the curve (that's the Normalize @ f'[t] piece) and move each point in that direction by the desired distance. $\endgroup$ – kglr Aug 14 at 16:15
  • $\begingroup$ I tried by extracting the surface normal vector at each point, then I tried scaling the points in that direction. But my results were wrong, the lines were intersecting. Anyways thanks. $\endgroup$ – acoustics Aug 14 at 16:18
  • 1
    $\begingroup$ @acoustics, in general parallel curves can be self-intersecting. There is a limited range of offsets where the resulting curves do not have that property. $\endgroup$ – J. M.'s discontentment Aug 16 at 3:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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