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$
3
  • 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 '20 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 '20 at 15:37
  • 3
    $\begingroup$ So in short, you wanted a parallel curve / offset curve of your original curve. $\endgroup$ – J. M.'s ennui Aug 14 '20 at 23:15
18
$\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:

$\endgroup$
4
  • $\begingroup$ It is exactly what I am looking for. Could you please explain to me how you did this? $\endgroup$ – acoustics Aug 14 '20 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 '20 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 '20 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 ennui Aug 16 '20 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.