Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement named matplotlib markers #61

Open
jimbojw opened this issue Sep 27, 2022 · 0 comments
Open

Implement named matplotlib markers #61

jimbojw opened this issue Sep 27, 2022 · 0 comments
Labels
enhancement New feature or request

Comments

@jimbojw
Copy link
Collaborator

jimbojw commented Sep 27, 2022

Depends on:

Currently, Megaplot can render regular polygons, circles, filled squares and glyphs of text. In addition, Megaplot should offer the ability to easily specify more complex shapes, like those in the list of matplot.markers.

Background: Sprites are objects which allow API users to specify attributes using the enter(), update() and exit() callbacks, each of which take a SpriteView instance which has getters and setters for the list of sprite attributes. One of those attributes is Sides, which determines whether the Sprite should render a circle/ellipse, rectangle, polygon, or sample from a signed distance field texture (SDF) used for text rendering.

An API user could implement a matplotlib marker via a combination of Sprite attributes. For example, to implement the matplotlib filled plus (+), given the proposed PolygonOffsetRadius API described in #60:

const sprite = scene.createSprite();
sprite.enter(s => {
  // A plus sign can be implemented as a 20-sided polygon.
  s.Sides = 20;

  // Where the offset radii jut in and out to trace the shape
  // as distances relative to the origin (O).
  //
  //          B-------A-------D
  //          |               |
  //          |               |
  //  D-------C               C-------B
  //  |                               |
  //  |                               |
  //  A               O               A
  //  |                               |
  //  |                               |
  //  B-------C               C-------D
  //          |               |
  //          |               |
  //          D-------A-------B
  //
  s.PolygonOffsetRadiusA = 0;
  s.PolygonOffsetRadiusB = 0.1180339887498949;  // sqrt(1 + .5^2) - 1.
  s.PolygonOffsetRadiusC = 0.7071067811865476;  // sqrt(2) / 2.
  s.PolygonOffsetRadiusD = 0.1180339887498949;  // sqrt(1 + .5^2) - 1.
});

To simplify specifying markers such as this, Megaplot should offer a Shape and/or ShapeView class which exposes convenient setters at a slightly higher level of abstraction.

Proposed example:

const shape = scene.createShape();

shape.enter((s: ShapeView) => {
  // Attributes of the ShapeView.
  s.Shape = CONSTANTS.SHAPE_PLUS_FILLED;
});

Behind the scenes, the setter for ShapeView's Shape attribute should set the Sides and PolygonOffsetRadius components appropriately.

Performance consideration: In principle, a ShapeView class could be a wrapper class around a SpriteView that the API user creates on demand. However, for performance reasons, this should be discouraged. Creating short-lived wrapper objects is slower than keeping those objects around cached. It also makes frequent garbage collection more likely.

So just as SpriteView objects are created early and cached, ShapeView objects should be created early and cached. And just like with a SpriteView, if the API user attempts to access it after the underlying swatch has been freed, an informative error should be thrown.

Alternative: An alternative API would be one which uses methods that operate directly on SpriteView objects to obtain the desired effect. Consider:

function makePlusFilled(s: SpriteView) {
  s.Sides = 20;
  s.PolygonOffsetRadiusA = 0;
  s.PolygonOffsetRadiusB = 0.1180339887498949;  // sqrt(1 + .5^2) - 1.
  s.PolygonOffsetRadiusC = 0.7071067811865476;  // sqrt(2) / 2.
  s.PolygonOffsetRadiusD = 0.1180339887498949;  // sqrt(1 + .5^2) - 1.
}

// ... later ...

const sprite = scene.createSprite();
sprite.enter(s => {
  makePlusFilled(s);
});

This implementation option has several advantages:

  • Simplicity — It's easy to implement.
  • Efficient — There's no memory allocation needed at all, just calling through to a utility function.

The disadvantage of this implementation approach is divergence from the proposed Text API in #57. While it would be possible to implement the Shape API using utility functions like the above, the same is not true of Text, which requires passing through to multiple Sprite instances which must be created and retired.

@jimbojw jimbojw added the enhancement New feature or request label Mar 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant