How can I draw a partial arc of an ellipse, defined by intersection points?

I am hoping to draw an “ellipse with a bite out of it”, like pictured:

This one I created by brute-force (code below) but in general this is not a great approach.

#import "@preview/cetz:0.4.2": *

#canvas({
    import draw: *

    arc((0,0), start: 30deg, stop: 330deg, radius: (2,1), name: "O", anchor: "origin")
    hide({
      circle((2,0), radius: 0.58, name: "A")
    })
    intersections("OA", "O", "A")
    arc-through("OA.0", "A.west", "OA.1", name: "OA-arc")
})

I have tried a few approaches using get-ctx to compute the angle from the origin to the intersection points of the smaller circle, but have not had a single approach create the arc I would expect.

Is it possible to take two intersecting ellipses, and create this shape from them?

Thank you in advance for any help!

As a final example, this code does not behave how I would expect at all, even when handling a simple case of horizontal ellipses.

#canvas({
  import draw: *

  circle((0,0), radius: (2,1), name: "O", update-position: false)
  circle("O.east", radius: 1, name: "A", update-position: false)
  intersections("OA", "O", "A")
  arc-through("OA.0", "A.west", "OA.1", stroke:color.blue, mode: "PIE", update-position: false)
  arc-through("OA.0", "O.west", "OA.1", radius: (2,1), stroke: color.red, mode: "PIE", update-position: false)
})

Unfortunately, I do not still have my example where I tried to use get-ctx, but they produced similar results to above.

You can calculate the third point for arc-through by offsetting the mid-point between O.start and O.end horizontally:

#import "@preview/cetz:0.4.2": *

#canvas({
    import draw: *

    merge-path({
      arc((0,0), start: 30deg, stop: 330deg, radius: (2,1), name: "O", anchor: "origin")
  
      let bite-inset = 0.35
      arc-through("O.end", (rel: (-bite-inset, 0), to: ("O.end", 50%, "O.start")), "O.start")
    }, close: true, fill: blue)
})

Gives:

4 Likes

And another version, that calculates the bite radius by measuring the distance between the arc’s start/end anchor and the east anchor of a circle with the same radius as the arc’s:

#import "@preview/cetz:0.4.2"

#cetz.canvas({
    import cetz.draw: *

    merge-path({
      let radius = (2, 1)
      arc((0,0), start: 30deg, stop: 330deg, radius: radius, name: "O", anchor: "origin")

      hide(circle((0,0), radius: radius, name: "OC"))
      arc-through("O.end", ((center, pt) => {
        let radius = cetz.vector.dist(center, pt)
        return cetz.vector.sub(center, (radius, 0, 0))
      }, "OC.east", "O.start"), "O.start")
    }, close: true, fill: blue)
})

2 Likes

Thanks so much! This is a nice approach, and good enough to use in my case (and seems to close the question as stated) but I am still hunting for a way to draw arcs of ellipses through intersection points. Perhaps I aught to make a new question, but I was hoping a fully general solution would also allow me to create figures like this:

#canvas({
  import draw: *
  
  circle((0,0), radius: (8, 4), name: "G")
  circle((rel: (-6, 0.4), to: "G.center"), radius: 0.3, name: "A")
  
  hide({
    circle("A", radius: 3, name: "inner")
    circle("A", radius: 4, name: "outer")
  })
  
  intersections("i", "G", "inner")
  intersections("o", "G", "outer")
  
  merge-path(
    stroke: color.purple + 2pt,
    fill: color.purple.transparentize(50%),
    close: true,
    {
      arc-through("i.0", "inner.east", "i.1")
      line("i.1", "o.1")
      arc-through("o.1", "outer.east", "o.0")
      line("o.0", "i.0")
    }
  )
})

In this case, it has been made with straight line interpolation, but this does not scale well to large radial segments (inner and outer radius 3 and 6 respectively).

Naturally this gets even worse if we cross the center of the ellipse.

If anyone has a solution that allows “highlighting” of arc segments between points, possibly by computing the angle of each intersection from the origin, then I would love to hear it.