Hi,
I am playing around with cetz and trying to create a custom element that behave like native ones.
I first tried a naive approach with this code (I am currently focusing on being able to use the anchors facilities) :
#import "@preview/cetz:0.4.2"
#set page(width: auto, height: auto, margin: .5cm)
#cetz.canvas({
  import cetz.draw: *
  grid((-10, -10), (10, 10), stroke: gray + 0.5pt)
  let head = (pos, anchor: none, radius: 4, name: none) => group(name: name, {
    let r = radius / 3
    set-origin(pos)
    cetz.draw.anchor("left-eye", (r, -r))
    cetz.draw.anchor("right-eye", (-r, -r))
    cetz.draw.anchor("mouth", (0, r))
    if anchor != none { set-origin(anchor) }
    circle((0, 0), radius: radius)
    circle((-r, r), radius: r / 2)
    circle((r, r), radius: r / 2)
    line((-r, -r), (r, -r))
    cetz.draw.anchor("left-eye", (-r, r))
    cetz.draw.anchor("right-eye", (r, r))
    cetz.draw.anchor("mouth", (0, -r))
  })
  circle((-4, 4), radius: .2, fill: green, name: "green")
  head("green")
  circle((-2, -4), radius: .2, fill: red, name: "red")
  head("red", anchor: "left-eye")
  head((5, 5), name: "a")
  circle("a.left-eye", radius: .2, fill: blue)
})
But obviously, this is not the right approach… ![]()
I took a look at cetz code and tried to copy the def of circle from shape.typ to create my head element.
#import "@preview/cetz:0.4.2"
#set page(width: auto, height: auto, margin: .5cm)
#let head(..points-style, name: none, anchor: none) = {
  import cetz.draw: *
  //import cetz.coordinate: *
  let style = points-style.named()
  let points = points-style.pos()
  assert(points.len() in (1, 2),
    message: "circle expects one or two points, got " + repr(points))
  assert(points.len() != 2 or "radius" not in style,
    message: "unexpected radius for circle constructed by two points")
  (ctx => {
    let (center, outer) = if points.len() == 1 {
      (points.at(0), none)
    } else {
      points
    }
    let (ctx, center) = cetz.coordinate.resolve(ctx, center)
    let style = cetz.styles.resolve(ctx.style, merge: style, root: "circle")
    // If we got two points, use the second one to calculate
    // the radius.
    let (rx, ry) = if outer != none {
      (ctx, outer) = coordinate.resolve(ctx, outer, update: false)
      (vector.dist(center, outer),) * 2
    } else {
      cetz.util.resolve-radius(style.radius).map(cetz.util.resolve-number.with(ctx))
    }
    let (cx, cy, cz) = center
    let drawables = cetz.drawable.ellipse(
      cx, cy, cz,
      rx, ry,
      fill: style.fill,
      stroke: style.stroke
    )
    let (transform, anchors) = anchor_.setup(
      (_) => center,
      ("center",),
      default: "center",
      name: name,
      offset-anchor: anchor,
      transform: ctx.transform,
      border-anchors: true,
      path-anchors: true,
      radii: (rx*2, ry*2),
      path: drawables,
    )
    return (
      ctx: ctx,
      name: name,
      anchors: anchors,
      drawables: drawable.apply-transform(transform, drawables),
    )
  },)
}
#cetz.canvas({
  import cetz.draw: *
  grid((-10, -10), (10, 10), stroke: gray + 0.5pt)
  head((0,0))
})
I wasn’t even able to compile it since cetz does not export functions from anchor (Anchor | CeTZ Documentation), at least, I couldn’t find it. So I have the error unknown variable: anchor_.
So, my question is, does anyone has an example of how to create a custom element ?