Skip to content

Clarify glyph model API by splitting identity, authored layers, and resolved instances #109

Description

@kostyafarber

Problem

The current glyph model API makes it difficult to reason about which object owns identity, authored source data, resolved geometry, rendering, hit testing, and mutation.

Today, several model objects expose geometry-related APIs with different meanings:

  • Glyph mixes identity, primary source state, cross-source resolution, interpolation, instance creation, outline creation, and some edit/session factory behavior.
  • GlyphLayer represents editable authored source data, but also exposes .geometry as a snapshot.
  • GlyphInstance.geometry represents resolved read/hit-test geometry at a designspace location.
  • Glyph.geometryAt(location) resolves a snapshot, including exact-source and interpolated fallback behavior.

This makes call sites ambiguous, especially in tools that need to hit-test resolved geometry but mutate an authored source layer.

Proposed Model

Split the API around the actual domain concepts:

font.glyph(glyphId)                       // identity / metadata only
font.layer(glyphId, sourceId)             // editable authored source layer
font.instance(glyphId, location)          // resolved read/render/hit-test view
font.editableLayerAt(glyphId, location)   // nullable exact-source mutation surface

Design intent:

  • Glyph / GlyphRecord answers identity and metadata questions only.
  • GlyphLayer is the mutation surface for authored source data.
  • GlyphInstance is read-only and represents the resolved shape at a designspace location.
  • font.layer(glyphId, sourceId) is source-addressed: the caller already knows the authored source identity.
  • font.editableLayerAt(glyphId, location) is location-addressed: it returns the authored layer only when the designspace location exactly maps to a source.

Example intended use:

const instance = font.instance(glyphId, location);
const layer = font.editableLayerAt(glyphId, location);

if (!layer) return; // interpolated or fallback view, not directly editable

const hit = instance.geometry.hitSegment(pos, radius);
if (hit) layer.splitSegment(hit.segmentId, hit.t);

Design Rules

  • Only GlyphLayer mutates authored geometry.
  • GlyphInstance should not be an edit context or own editability.
  • Resolved geometry and authored source geometry should have distinct names, e.g. view/snapshot terminology rather than a shared .geometry noun everywhere.
  • Cross-source resolution, interpolation, and component resolution should live at the font/resolver level, not on the glyph identity object.

Why

Variable-font locations make "the glyph" ambiguous: there is glyph identity, authored source data, interpolated/resolved geometry, rendered outline, and editability. Treating these as separate model concepts should make the API easier to teach, reduce circular responsibilities, and make invalid operations harder to express.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions