Giter Club home page Giter Club logo

prototypo-builder's Introduction

Prototypo Builder

## Making a sense of this code-base

This application is written in JS (ES2015). It's based on:

Being familiar with those tools, their underlying concepts and API is recommended before diving into this application.

Parametric Engine

At the heart of Prototypo-builder is our next generation Parametric engine. It's composed of two parts that we'll call The computer and The expander.

The computer
The computer is in charge of calculating the coordinates as well as any other property of all nodes in a glyph. It works very similarly to a spreadsheet: node properties can have static values, but they can also have formulas, such as `$height * 7`.
The expander
The expander is a collection of algorithms that stem an outline from a bezier skeleton. Currently, the builder is more of a playground than an application. We are experimenting with different algorithms and hope to keep more than one of them in the future application.

Data Model / State Shape

The data model of the app is described in src/reducers/index.js. We try to keep the state as normalized as possible (see Redux docs on Normalizing State Shape). That means many parts of the Data Model are not stored in the state. This is the case for expanded nodes which are calculated and stem from skeleton nodes. There is a notable exception with formulas: since formulas are potentially interdependent with other formulas in the same glyph, all formulas of all descendants of a glyph must be collected to be calculated at the same time. Searching for and gathering all formulas in a glyph is potentially very time-consuming, so we have de-normalized that part and stored it in its own sub-state, grouped by glyph.

Font Model

The font model is described in src/_utils/FontModel.js. We've made the choice to represent paths as flat lists of oncurve and offcurve points, instead of grouping them in bezier curves (e.g. {start, end, ctrl1, ctrl2}) or in nodes (e.g. {node, ctrlBefore, ctrlAfter}). src/_utils/Path.js offers forEachCurve and forEachNode methods to iterate over a path, though :-)

Coding style and conventions

Please install an ESLint and Editorconfig plugin for your code editor. Most coding rules are enforced by our eslint config. There are additional rules that must be respected. Note that Atom will report linting errors with root imports (e.g. from '~/_utils'). This is due to a bug in that eslint plugin.

80 characters max per LOC

After much debate, we have decided not to enforce a strict limit of 80 characters per LOC, as it doesn't necessarily result in more readable code. But you must be mindful of this limit and avoid exceeding it as much as possible. Keeping line shorts greatly improves readability when reading two files side by side on a single screen. However, the linter will warn when a line exceeds 90 characters.

Destructuring assignment

Destructuring assignment is good! It helps a lot keeping your LOCs under 80 chars and improves readability. However, keeping properties and methods namespaced often results in optimal readability.

/* Bad */
this.props.actions.updateProp(oncurveId, 'x', coord.x);
this.props.actions.updateProp(oncurveId, 'y', coord.y);
this.props.actions.addOncurve(pathId, oncurveId);

/* Avoid */
const { updateProp, addOncurve } = this.props.actions;

updateProp(oncurveId, 'x', coord.x);
updateProp(oncurveId, 'y', coord.y);
addOncurve(pathId, oncurveId);

/* Good */
const { actions } = this.props;

actions.updateProp(oncurveId, 'x', coord.x);
actions.updateProp(oncurveId, 'y', coord.y);
actions.addOncurve(pathId, oncurveId);

Destructuring imports

You must not use destructuring imports for methods in src/_utils/ files. Keeping methods namespaced helps a lot with readability, and figuring out in which file methods are located.

/* Bad */
import { mapCurve } from '~/_utils/Path';

const node = mapCurve( nodeId, childId, nodes );

/* Good */
import * as Path from '~/_utils/Path';

const node = Path.mapCurve( nodeId, childId, nodes );

Test Coverage

Using npm run test will test the code and provide full coverage using Istanbul. All files in src/_utils, and src/reducers should have 100% coverage at all time!

Performances

This application uses many algorithms that are inherently slow. Also, all these algorithms are implemented as pure functions, so that results can be memoized to avoid wasting resources calculating the same thing twice.

There are therefore two ways to optimize performances in the app:

  • Making sure browser VMs do not deoptimize our most critical algorithms
    • make sure methods are monomorphic (do not memoize )
  • Make sure memoization is used appropriately and most efficiently
    • do not include a part of the state that is highly likely to change in the memoization key (e.g. state.nodes)

prototypo-builder's People

Contributors

louisremi avatar aldrian avatar franzpoize avatar

Stargazers

 avatar Damian Marek avatar Jared Palmer avatar

Watchers

 avatar James Cloos avatar  avatar  avatar

prototypo-builder's Issues

Users must be required to specify angle units in formulas

In Prototypo, angle units must be specified in formulas: '15deg' or (3 * 5) + 'deg'.
This should be the same in the builder.

When the formula parser encounters an angle in degrees, it should be automatically converted to radians, so that internally the app only deals with radian angles.

move bezier.js code to its own project

François has spent some time making a pure functional version of bezier.js, this should leave in its own repository (currently located in src/containers/svg/_utils)

Document the structure of the project

  • Provide better explanation of the way the parametric engine works, especially in what order happen what calculation, as this is very important to understand what can and cannot be done inside formulas.
  • Provide a listing of skeleton-expanding algorithms we have already experimented with or we wish to experiment with in the future. Add comments about the pros and cons of those algorithms, as well as notes on the compatibility/incompatibility with variable fonts (some algorithms create unpredictable number of outline points, which make those outlines impossible to interpolate).
  • Document the hierarchy of parameters

Switch to preact in prod

This requires uncommenting the resolve part in cfg/dist.js (preact-compat might no longer be needed when preact v6.0 is out, thanks to preact aliases).

After doing this, the svg view no longer works. This is not due to preact only, since preact v5.0 should handle SVG correctly. This might be due to the way we render to SVG. To investigate further, we need to enable preact in dev mode, but as is, our config produces conflicts between preact and react hot loading. Switching to react-hot-loader might do the trick, but it prove very hard (and so far unsuccessful) to configure.

The generated script.js went from 382k to 267k though. The gain is real.

Improve linter

Make comma dangle required
Make newlines before else statements required
Make line break after variable declarations required

Make formulas local state ?

While editing formulas, we keep them in the state. This isn't necessarily required, we could just use setState instead. Maybe.

Add optimize-js to the build step

Optimize-js isn't production ready yet. I was unable to get source-map to function properly after using it. Plus we need to benchmark it to make sure the gains are significant.

There's a WIP branch: feat/optimize-js

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.