PAIR-code / megaplot

Apache License 2.0
19 stars 5 forks source link

Implement mid-level `Text` object and `TextView` for attributes #57

Open jimbojw opened 2 years ago

jimbojw commented 2 years ago

Currently, when specifying how text should render, the TextSelection object offers three methods: text(), align() and verticalAlign(). These methods allow the API user to specify what text should be displayed, and how that text should be aligned.

The implementation of TextSelection currently uses Selection as an intermediary. Each element of a TextSelection is a Selection of sprites where each sprite is one glyph of text.

Instead, a new, mid-level Text object should be implemented which extends Sprite and offers additional attributes for text specification.

Proposed example:

const text: Text = scene.createText();
text.enter((t: TextView) => {
  // Attributes unique to TextView.
  t.TextString = 'String of text to render.';
  t.TextAnchorX = -1;  // Box hangs to the left of the point.
  t.TextAnchorY = 0;   // Box middle-aligned vertically.
  t.TextAlignX = -1;   // Text is left-aligned in the box.
  t.TextAlignY = 1;    // Text is top-aligned in the box.

  // Inherited SpriteView attributes.
  t.SizeWorld = [.25, .25];
  t.FillColor = [0, 0, 0, 1];
});

API proposal:

  /**
   * String of text to render.
   */
  {
    attributeName: 'TextString',
  },

  /**
   * How the text bounding box should be anchored relative to the position.
   * 
   *   -1,1         1,1
   *     +-----+-----+
   *     |           |
   *     +     *     +
   *     |           |
   *     +-----+-----+
   *   -1,-1         1,-1
   *
   * So for example, the default value of 0,0 will place the text bounding
   * box centered over the specified position. A value of 1,1 would have the
   * bounding box hang off underneath and to the left.
   */
  {
    attributeName: 'TextAnchor',
    isInterpolable: true,
    components: ['X', 'Y'],
  },

  /**
   * How text should be aligned relative to the bounding box.
   * 
   *   -1,1         1,1
   *     +-----+-----+
   *     |           |
   *     +     *     +
   *     |           |
   *     +-----+-----+
   *   -1,-1         1,-1
   *
   * The default value of 0,0 places text center-aligned horizontally and
   * middle-aligned vertically. A value of -1,1 would place text starting
   * at the top-left corner.
   */
  {
    attributeName: 'TextAlign',
    isInterpolable: true,
    components: ['X', 'Y'],
  },

While this API proposal is written in the form of a SpriteAttribute, it would not be implemented at that layer of the API. Instead, a TextView instance would call through to multiple Sprites, one per glyph.

It's an open question whether the Text object would use Selection as its implementation, or manage a collection of Sprite instances on its own.