On this page:
vertex
dc-path->vertices
average-angle
marker-angle-degrees
parse-marker-orient

10 Markers🔗ℹ

SVG markers (<marker>, placed via marker-start/marker-mid/marker-end or the marker shorthand) are positioned at a shape’s vertices moveto/lineto/curveto/arc endpoints, not curve control points — and oriented along the tangent direction(s) of whatever segment(s) meet there. This group is the vertex-and-tangent extraction and orientation-angle math behind that placement, reusing the same get-datum-based technique dc-path->polylines uses (racket/draw represents every built shape using only two segment shapes, so no separate arc-tangent math is needed even for native .arc/.ellipse geometry).

struct

(struct vertex (x y in-angle out-angle)
    #:extra-constructor-name make-vertex)
  x : real?
  y : real?
  in-angle : (or/c real? #f)
  out-angle : (or/c real? #f)
One vertex of a flattened path: its position (x ,y), and the tangent angle (in radians, dc<%>’s own clockwise-in-y-down convention) of the segment arriving at it (in-angle) and the segment leaving it (out-angle) — either is #f if this vertex is an endpoint of an open subpath with no segment on that side. A closed subpath that doesn’t already loop back to its own starting point gets an implicit closing vertex added (matching SVG’s own treatment of Z/z as generating its own vertex), consistent with how dc-path->polylines closes a polyline’s loop explicitly too.

A zigzag path with each of its vertices marked:

a zigzag line with a red dot at each of its four vertices

procedure

(dc-path->vertices p)  (listof vertex?)

  p : (is-a?/c dc-path%)
Extracts every vertex of p (across all of its subpaths), in order, as described above. This is what svg/svg calls internally to decide where to place marker-start/marker-mid/marker-end instances along a <path>, <line>, <polyline>, or <polygon>.

Example:
> (dc-path->vertices (car (path-data->dc-paths "M0,0 L100,0")))

(list (vertex 0.0 0.0 #f 0.0) (vertex 100.0 0.0 0.0 #f))

procedure

(average-angle a1 a2)  real?

  a1 : real?
  a2 : real?
Averages two angles a1/a2 (radians), correctly across the wraparound at ±180°, where a naive (/ (+ a1 a2) 2) would fail (averaging 179° and −179° should give ~180°, not 0°). Implemented by summing the two angles’ unit vectors and taking the resulting direction; if they point exactly opposite each other (an ambiguous bisector), returns perpendicular to a1 as a reasonable, consistent convention rather than an arbitrary one. This is what marker-angle-degrees uses to compute orient="auto"’s bisector at a vertex where both an incoming and an outgoing tangent exist.

Examples:
> (average-angle 0 0)

0

> (radians->degrees (average-angle (degrees->radians 179) (degrees->radians -179)))

180.0

Two tangent directions (blue: incoming, green: outgoing) and their bisector (red) at a vertex:

three rays from a common point: one blue, one green, and their red bisector between them

procedure

(marker-angle-degrees v orient is-start?)  real?

  v : vertex?
  orient : (or/c real? 'auto 'auto-start-reverse)
  is-start? : boolean?
Computes the angle (in degrees) to rotate a marker instance placed at vertex v, given its orient (as parse-marker-orient returns it) and whether this is the path’s marker-start vertex specifically (is-start?). A numeric orient is returned as-is; 'auto gives the bisector of v’s in-angle/out-angle (via average-angle), or whichever one of the two exists at an open path’s endpoint; 'auto-start-reverse is the same, plus 180° when is-start? is true — so a single arrowhead marker used as both marker-start and marker-end points consistently "into" the line at both ends rather than the same nominal direction at both.

procedure

(parse-marker-orient s)

  (or/c real? 'auto 'auto-start-reverse)
  s : (or/c string? #f)
Parses a <marker>’s orient attribute: "auto" and "auto-start-reverse" give the matching symbol; a numeric angle (in degrees, with or without a trailing deg unit specifier being ignored since only the leading number is read) gives that number; anything else (including an absent value) gives 0.

Examples:
> (parse-marker-orient "auto")

'auto

> (parse-marker-orient "45")

45

> (parse-marker-orient #f)

0

Arrowhead markers placed at each vertex of a curve, oriented via marker-angle-degrees with orient= 'auto each arrow points along the curve’s own tangent direction at that point:

a curved line with an arrowhead at each end, both pointing along the curve's own tangent direction