@chgibb/ngplasmid

## Motivation This project drives the circular visualizations of [PHAT](https://github.com/chgibb/phat). Previously, PHAT made us of [AngularPlasmid](https://github.com/vixis/angularplasmid) to drive its circular visualizations. This project is a (mostly)

Stats

StarsIssuesVersionUpdatedCreatedSize
@chgibb/ngplasmid
310.0.293 years ago4 years agoMinified + gzip package size for @chgibb/ngplasmid in KB

Readme

ngPlasmid

Motivation

This project drives the circular visualizations of PHAT. Previously, PHAT made us of AngularPlasmid to drive its circular visualizations. This project is a (mostly) compatible replacement for AngularPlasmid with a special focus on performance above all.

Usage

This project is available through npm as a Typescript only package. No Javascript is distributed.

npm install --save @chgibb/ngplasmid

Basic API

The basic API revolves around the Plasmid class, representing a traditional AngularPlasmid template. Plasmids can be loaded from either HTML or protocol buffers and serialised as protocol buffers. $scope objects can be set and mutated in order to alter the rendered output. See the HTML to SVG compiler used by tests for basic usage.

Adaptive Rendering

Plasmids can be set to experiment with rendering strategies on each call to Plasmid#renderStart in order to find the most performant one for their current template and the current machine. This is most useful in environments where one or more Plasmids of varying sizes and complexities are rendered over and over again (such as PHAT). By default, this is disabled. It can be enabled by calling Plasmid#enableAdaptiveRendering(). A Plasmid will select from its available strategies after it has run with each strategy Plasmid#adaptIterations times. By default, this is 2. Once a Plasmid has selected a strategy, it will use that strategy on every future call to Plasmid#renderStart() and Plasmid#renderEnd(). The current strategy can be overriden by calling Plasmid#changeRenderingStrategy(strategy).

Rendering Strategies

  • "normal": Typescript based. Recursively descends the Plasmids children and renders each in turn. Composes and returns the result. This is the default.
  • "preCalculateBatch": C++ based. ngPlasmid.node must have successfully compiled and be available in the current working directory for this strategy to run. <plasmidtrack>s and <trackmarker>s have their SVG path data precalculated in C++. If a <plasmidtrack> has more than a certain number of <trackmarker>s then the calculation is batched up and split into threads. Calls the "normal" strategy to complete the remainder.

Reference Compiler

The compiler under referenceCompiler/ uses ng-node-compile, which internally makes use of jsdom and AngularJS itself to simulate a web browser environment to render the template and then extract the SVG. This is woefully slow and inefficient for obvious reasons. Output from this method is considered to be the source of all truth in terms of what is and is not correct output.

HTML to SVG Compiler

Under HTMLToSVGCompiler/ is an example implementation which takes an HTML file to compile and a JSON file to use as $scope and dumps the result to standard out. This is used in testing.

HTML to Protocol Buffer Compiler

Under HTMLToPBCompiler/ is an example implementation which takes an HTML file and compiles it to a protocol buffer. This is used in testing.

Protocol Buffer to SVG Compiler

Under PBToSVGCompiler/ is an example implementation which takes a protocol buffer file and a JSON file to use as $scope and dumps the result to standard out. This is used in testing. This is envisioned to be used with extremely large inputs where the overhead of HTML parsing becomes the primary overhead.

Compatibility with AngularPlasmid

See AngularPlasmid's official examples
ngPlasmid aims to fully support the directives of AngularPlasmid as they are used in the official examples. Directive usage in the examples differs in some ways from what the official documentation states. Where there is a conflict, we defer to compatibility with the example and its output from actually running AngularPlasmid as opposed to what the documentation states. We support all directives as they are used in the official examples.

Breaking Changes, Incompatibility

See AngularPlasmid's official API docs

<markerlabel> Font Size

AngularPlasmid uses window.getComputedStyle to determine font size. We attempt to parse font size out of the <markerlabel>'s labelstyle attribute as a substitute. If we are unable to determine font size, it is assumed to be 0. This does not prevent the rendering of <markerlabel>s which do not explicitly specify font style, however the output SVG will differ slightly in text positioning than if ran through AngularPlasmid in a browser environment. This bug (and substitute behaviour) is currently present in JSDom as well. As such, there is no intention of trying to correct it at this time.

<svgelement>

The <svgelement> directive is (currently) not supported and (currently) outside the scope of this project.

<plasmidapi>

The <plasmidapi> directive is (currently) not supported and (currently) outside the scope of this project.

Attributes Marked Numeric in the Official Docs

Every directive attribute marked as Type Numeric in the official docs is treated as a float. This does not appear to have any negative consequences though is of note.

ng- Attributes and Directives

All AngularJS ng- attributes and directives are not supported. None are used in the official examples and are (currently) outside the scope of this project.

String Interpolation {{ }}

Attribute Value

The entire attribute value to be interpolated must be an interpolation expression. i.e. <plasmid radius="{{map.radius}}" ... >. Something like <plasmid radius="1{{map.radius}}" ...> is not supported and the expression will be returned into the attribute as is with no interpolation.

Interpolating Objects

Expressions of the form "{{map}}" where map : {...some object...} will evaluate to [object Object]. AngularJS will dump the entire raw JSON string into the attribute, with quotes and special characters properly escaped for HTML. As there is no scenario where interpolating an object is not an error, we return [object Object] in the interests of speed.

String Literals

String literals MUST be quoted using '(single quote). Unquoted strings are treated as variables and will be interpolated as undefined if they do not exist.

Numeric Literals

All numeric literals are treated as floats internally. Numeric literals cannot be arbitrarily inserted. i.e. {{123map.someProperty}} will result in a lookup for the property someProperty on object 123map which will be interpolated as undefined.

Operators

Currently only the + addition operator is supported. Inserting string or numeric literals into an expression must be done through an operator. i.e. {{'hello '+map.myName}} or {{map.mapWidth+150}}

Type Coercion

Types are coerced according to the literals being operated on. i.e. {{'hello '+map.radius}} where map : {radius : 150} will coerce map.radius to a string and append it to 'hello '. The same behaviour is true for numeric literals, i.e. {{map.mapName+100}} will coerce map.mapName to a number and add 100 to it. If map.mapName is not a number, the result will be NaN.

One-Time Variable Bindings

One-time bound variables (::) are treated as regular variables. They are not handled specially.

Attributes Supporting Interpolation

In the interests of speed and the requirements of the downstream project, only the following attributes currently support interpolation:

  • <plasmid>
    • plasmidheight
    • plasmidwidth
  • <plasmidtrack>
    • width
    • trackstyle
    • radius
  • <tracklabel>
    • text
  • <trackscale>
    • interval
    • direction
    • vadjust
    • showlabels
  • <trackmarker>
    • start
    • end
    • wadjust
    • markerstyle
  • <markerlabel>
    • lineclass
    • showline
    • vadjust
    • text

Performance

ngPlasmid can be anywhere from ~5x to ~160x faster. ngPlasmid tends to perform far better in terms of speed over the reference compiler the larger the input is. With small inputs, the main bottleneck is disk I/O and NodeJS' startup time. Speed, as well as correctness with the reference implementation is tested on each commit. See Travis logs for compilation and optimization time for each test file vs the reference compiler.

To give a sense of performance, especially with large inputs:
As of a0e2687, compiling 237,180 points of genomic coverage data generated by PHAT from an AngularPlasmid HTML template (tests/HPV1630CoverageTracks.html, 601 <plasmidtrack>s, 24,322 <trackmarker>s, 607 individual interpolation expressions) to SVG takes the reference ~137.363 seconds, ngPlasmid's HTML to SVG, ~1.161 seconds, and ngPlasmid's Protocol Buffer to SVG ~.833 seconds after ~.825 seconds to first compile the HTML to Protocol Buffer. See Travis log.

Dependencies

Plasmid -> SVG functionality is available completely without dependencies. If you want to compile AngularPlasmid HTML templates to SVG, htmlparser2 ^3.9.2 is required. Any use cases involving Protocol Buffers requires protobufjs ^6.8.0.

If you find any bugs or have a feature request, please open an issue on github!

The npm package download data comes from npm's download counts api and package details come from npms.io.