11
$\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, 2020 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, 2020 at 15:37
  • 3
    $\begingroup$ So in short, you wanted a parallel curve / offset curve of your original curve. $\endgroup$ Aug 14, 2020 at 23:15

1 Answer 1

20
$\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 = Quiet @ 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/10], 
  ImageScaled[{0, .35}]], 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, 2020 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, 2020 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, 2020 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$ Aug 16, 2020 at 3:51

Your Answer

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

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