14
$\begingroup$

With the command Disk, it's easy to make a filled disk:

Graphics[{EdgeForm[Black], GrayLevel[0.84], 
  Disk[armCenter = {0, 0}, 18]}]

disk

But how would I create a filled version of a shape like this?

Graphics[{Circle[{106.79, 0}, 20, claw1a = {0.8, 2.89}], 
  Circle[{106.79, 0}, 25, claw1a], 
  Circle[{85, 5.6}, 2.5, claw1b = {2.89, 6.03}], 
  Circle[{122.54, 16.07}, 2.5, claw1c = {0.8, -2.35}]}]

claw

I imagined I'd use Polygon, but I would think that it's not the only option for this.

$\endgroup$
3
  • $\begingroup$ I really depends how complex your geometry is. What about RegionPlot? $\endgroup$
    – BlacKow
    Oct 3, 2016 at 20:11
  • 2
    $\begingroup$ FilledCurve is useful if your geometry can be expressed as Bezier curves or splines. $\endgroup$ Oct 3, 2016 at 20:12
  • 2
    $\begingroup$ ...and circle arcs are of course expressible as NURBS curves. $\endgroup$ Oct 3, 2016 at 20:14

3 Answers 3

11
$\begingroup$

As @m_goldberg points out, we can't build nice graphics objects out of circular arcs generated by Circle as you have cleverly done. This has been a problem for me in the past as I also love to use the angle specification in curve to generate circular arcs when building a graphic.

Use Bezier Curves

One option is to use Polygon as m_goldberg has shown you, however this will necessarily give you a collection of straight edges. Another is to use BezierCurves, which will allow us to make use of FilledCurve.

There are many guide out there describing how to approximate circles with Bezier curves, the upshot is that for quarter circles and less the approximation is very very good.

I have therefore written for myself a function which takes exactly the same arguments as Circle and generates an arc with BezierCurve underlying it:

BezierCircleArc[{x_, y_}, r_, {θ1_, θ2_}] := 
 Module[{α, p0, p1, p2, p3},
  α = 4/3 Tan[(θ2 - θ1)/4];

  p0 = {x, y} + r {Cos[θ1], Sin[θ1]};
  p3 = {x, y} + r {Cos[θ2], Sin[θ2]};
  p1 = p0 + α r {-Sin[θ1], Cos[θ1]};
  p2 = p3 + α r {Sin[θ2], -Cos[θ2]};

  BezierCurve[{p0, p1, p2, p3}]
  ]

We can therefore recreate your claw graphic using a direct replacement of Circle -> BezierCircleArc:

Graphics[{
  BezierCircleArc[{106.79, 0}, 20, claw1a = {0.8, 2.89}], 
  BezierCircleArc[{106.79, 0}, 25, claw1a],
  BezierCircleArc[{85, 5.6}, 2.5, claw1b = {2.89, 6.03}], 
  BezierCircleArc[{122.54, 16.07}, 2.5, claw1c = {0.8, -2.35}]
  }]

enter image description here

However we now have the ability to use functions such as FilledCurve that allow use to use the full power of Graphics styling options:

Graphics[{EdgeForm[Black], GrayLevel[.84],
 FilledCurve[{
   BezierCircleArc[{106.79, 0}, 20, claw1a = {0.8, 2.89}],
   BezierCircleArc[{85, 5.6}, 2.5, claw1b = {6.03 - 2π, 2.89 - 2π}][[;;, 2 ;;]],
   BezierCircleArc[{106.79, 0}, 25, Reverse@claw1a - 2π][[;;, 2 ;;]],
   BezierCircleArc[{122.54, 16.07}, 2.5, claw1c = {0.8, -2.35}][[;;, 2 ;;]]
   }]
 }]

The [[;;,2]] is to remove the first point from the BezierCurve as FilledCurve automatically adds it to stitch the curves together.

enter image description here

Accuracy

A note on the accuracy of BezierCircleArc. It's really great up to and a little beyond a quarter circle. It starts to deviate a little by a semicircle and goes awol beyond that. Simply break the arc into multiple sections to overcome this.

GraphicsRow[Table[
  Graphics[{Text[ToString[i/4.] <> "π", {0, 0}],
    Circle[{0, 0}, 1, {0, π i/4}],
    ColorData["Rainbow"][(i - 1)/5],
    BezierCircleArc[{0, 0}, 1, {0, π i/4}]
    }, PlotRange -> {{-1.1, 1.1}, {-1.1, 1.1}}],
  {i, 1, 6}], 0.2]

enter image description here

$\endgroup$
1
  • $\begingroup$ @JPeter, thank you for your attempted edit. Unfortunately it's not the done thing to edit this answer to show your results. I'm glad this approach has worked for you and it's interesting to see your result! Thanks! $\endgroup$ Oct 4, 2016 at 13:30
10
$\begingroup$

Your problem is that your claw shape is just a collection of circular arcs. It isn't recognized by Mathematica as a graphics object like Disk. We will have to build a graphics object that looks likes your collection of arcs, but which Graphics will recognize. I think the easiest such object is a polygon which has vertices taken from your arcs.

Your claw shape (for reference).

claw =
  {Circle[{106.79, 0}, 20, {0.8, 2.89}],
   Circle[{106.79, 0}, 25, {0.8, 2.89}],
   Circle[{85, 5.6}, 2.5, {2.89, 6.03}],
   Circle[{122.54, 16.07}, 2.5, {0.8, -2.35}]};

Points that lie along the arcs in your claw shape.

claw1aPts =
   Join[
     Table[ReIm[20. Exp[I u] + 106.9], {u, Subdivide[.8, 2.89, 15]}],
     Reverse @ 
       Table[ReIm[2.5 Exp[I u] + 85. + 5.6 I], {u, Subdivide[2.89, 6.03, 7]}],
     Reverse @ 
       Table[ReIm[25. Exp[I u] + 106.9], {u, Subdivide[.8, 2.89, 15]}],
     Table[ReIm[2.5 Exp[I u] + 122.54 + 16.07 I], {u, Subdivide[0.8, -2.35, 7]}]];

The order of the points is critical -- hence the use of Reverse. Let's see how these point lie along your claw shape.

Graphics[{claw, AbsolutePointSize[6], Point[claw1aPts]}]

points

They don't look too bad, but you can always refine the set of points for your actual application if necessary. Now lets make a demo of opening and closing claws from these points.

Manipulate[
  Graphics[
    {u = 
       GeometricTransformation[
         {EdgeForm[Black], GrayLevel[.84], 
          Polygon[claw1aPts]}, {RotationTransform[θ Degree, {85, 5.6}]}], 
     GeometricTransformation[u, ReflectionTransform[{0, 1}, {85, 0}]], 
     Dashed, InfiniteLine[{{0, 0}, {1, 0}}]},
     PlotRange -> {{75, 130}, {-45, 45}}, Frame -> True],
  {u, None},
  {θ, -15, 45, 1, Appearance -> "Labeled"}]

demo

$\endgroup$
3
$\begingroup$

Here's a routine based on this previous answer for drawing a rounded version of Annulus[], which uses FilledCurve[] + BSplineCurve[]. Its only caveat is that it is restricted to acute and obtuse angles:

roundedSector[{h_, k_}, {r1_, r2_}, {θ1_, θ2_}] /; 0 < r1 < r2 && 0 < θ2 - θ1 <= π := 
       Module[{cw, p1, p2, rs, rt, scp, ska, skb, sw, ϕ1, ϕ2},
              scp = (r2 - r1) {{1, 1}, {-1, 1}, {-1, 0}}/2; sw = {1, 1/2, 1/2, 1};
              ska = {0, 0, 0, 1, 1, 1}; skb = {0, 0, 0, 1/2, 1, 1, 1}; 
              rs = r1^2 + 6 r1 r2 + r2^2; rt = (r1 - r2) Sqrt[(3 r1 + r2) (r1 + 3 r2)];
              ϕ1 = ArcTan @@ -RotationTransform[θ1][{-rs, rt}] // N;
              ϕ2 = ArcTan @@ RotationTransform[θ2][{rs, rt}] // N;
              cw = Cos[(ϕ2 - ϕ1)/2];
              p1 = Through[{Cos, Sin}[ϕ1]]; p2 = Through[{Cos, Sin}[ϕ2]];
              {BSplineCurve[TranslationTransform[{h, k}][
                            r2 {p1, Normalize[(p1 + p2)/2]/cw , p2}], SplineDegree -> 2,
                            SplineKnots -> ska, SplineWeights -> {1, cw, 1}], 
               BSplineCurve[Composition[TranslationTransform[{h, k} + (r1 + r2) p2/2],
                                        RotationTransform[ϕ2]][scp], SplineDegree -> 2, 
                            SplineKnots -> skb, SplineWeights -> sw], 
               BSplineCurve[TranslationTransform[{h, k}][
                            r1 {Normalize[(p1 + p2)/2]/cw , p1}], SplineDegree -> 2, 
                            SplineKnots -> ska, SplineWeights -> {1, cw, 1}], 
               BSplineCurve[Composition[TranslationTransform[{h, k} + (r1 + r2) p1/2], 
                                        RotationTransform[ϕ1 + π]][scp], SplineDegree -> 2,
                            SplineKnots -> skb, SplineWeights -> sw]} // FilledCurve]

Example:

Graphics[{Annulus[{1, 1}, {3/2, 2.}, {π/6, 7 π/8}],
          Blue, roundedSector[{1, 1}, {3/2, 2}, {π/6, 7 π/8}]}]

annulus and its rounded version

$\endgroup$
1
  • $\begingroup$ P. S. The cheaper version, which involved judicious use of Annulus[] +Disk[], displayed seams that I could not remove, so I did not post that. $\endgroup$ Dec 11, 2016 at 7:29

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.