Skip to content

Implementation guide

Amit Patel edited this page Aug 16, 2017 · 8 revisions

Each chapter will have a page (index.html) with the visualizations. Some chapters may have more than one page. The x.js files have algorithms and the c_x.js has the visualizations. Look at the pseudocode and the python code for starting points for the algorithms; however it is likely that algorithms will need to be written differently to support visualizations than if it were being written on its own.

Design

  1. Think about whether you want to display the “static” breakdown of an algorithm or the “dynamic” breakdown of an algorithm. For example this page about drawing lines breaks the steps down into linear interpolation, choosing the number of points, and snapping to a grid. That's the static (compile time) breakdown. In contrast, this page showing graph search breaks the steps down into node 1 expansion, node 2 expansion, node 3 expansion, etc. That's the dynamic (run time) breakdown.
  2. Think about whether the reader provides a problem that the page runs (example) or if the page provides a problem that the reader runs (example). Sometimes it's useful to have both. First let the reader solve the problem manually, then show how the algorithm solves the same problem. Or have the reader guess how the algorithm would behave, then show what the algorithm actually does.
  3. Some readers will not interact with all the diagrams. Try to find an initial state of the diagram so that it's meaningful and useful even before the reader interacts with it.
  4. Prefer direct manipulation (drag and drop, painting) over sliders, menus and other "form" controls.
  5. Although the browser emulates mouse events when using a touch device, consider what the interaction will be when on a touch device. We'll have both kinds of readers and it will be nice to have interactions that support everyone.

Code structure

  1. The focus is on making good visualizations. We are not building production-quality implementations of the AI algorithms. Code duplication is ok. We do not need algorithm or data structure abstractions other than what's needed for the visualizations.
  2. It's useful to think of the visualization as a pure function from the algorithm state and parameters to the svg. Think about what parameters you want to be able to change. Let the control over them (mouse clicks, sliders, drag & drop, animation, etc.) be outside the visualization function. Not all visualizations can work this way.
  3. It is useful to have a “diagram” object that stores the pieces of the visualization, e.g. a label, the visualization, etc. These are references to the two.js, d3.js, etc. objects. For d3.js store the group that has data() not the individual dom nodes, because you will use data(), enter(), exit() to update the nodes. For some problems the visualization structure will be tied to the algorithm input and for others the algorithm input will only be available at redraw time.
  4. The render function should read the algorithm state and set the properties of the diagram object. In d3.js use data() for repeated elements. For changed state, including adding/removing event handlers, use d3's enter() and exit(). In two.js you'll have to track these yourself and use jquery to add/remove event handlers.
  5. If there will be several variants of a diagram, put these variants into the diagram object. Sometimes these are booleans (if the diagram object contains a field or has a flag set, the renderer will draw something) and sometimes it can be structured as an array of optional layers (then you assemble a diagram by listing the layers that you want in it).
  6. For some algorithms you can make time a property of the diagram object. An animation can be implemented as a setInterval timer outside the diagram or render function. It will modify the time value, and then call the render function. More advanced: a pause/play button to start or stop this timer. Even more advanced: a slider that directly lets the reader control the time property, so that they can rewind, pause, etc. Not all algorithms can be implemented this way.
  7. For algorithms that run to completion and don't take too long, it is easier to record a trace of the algorithm. Run the algorithm once, record the steps, then use the animation/slider to see all the steps.

Code style

  1. If contributing to an existing chapter, follow the code style and libraries used in that chapter. We currently do not have much consistency across chapters, but we do want consistency within a chapter.
  2. Consider using a tool such as prettier. You can run prettier --write to reformat the code in a file.

Libraries

We are trying to be consistent within each chapter. Use d3, two, p5, three, jquery, or other libraries. Amit's experience, having used most of these, is that a library that provides access to svg directly (d3, jquery) will require more effort to learn but will also allow for nicer visualizations than libraries that abstract away the svg layer (two, p5). However, the abstract libraries will let you move more easily to Canvas or WebGL when needed.

Start with SVG and move to Canvas and WebGL when needed. SVG scales to different density screens and also gives us easier ways to attach mouse events and other effects. However SVG won't handle all types of visualizations so use Canvas or WebGL when SVG isn't a good match.

If you do not have a preference of which library to use, use SVG with either two.js+jquery or d3.js.

Other

  1. Prefer hsl(hue, saturation, lightness) for colors instead of rgb(r, g, b) or #rrggbb or #rgb. A good set of medium colors is hsl(hue, 50%, 50%) where 0 ≤ hue < 360. With hsl you can pick related colors (darker, brighter, grayer, bolder) more easily than with rgb.
  2. Normally we draw shapes with the top left being 0,0, or we draw them directly at the location desired. It's sometimes easier to draw shapes that are centered on 0,0. You can then use the transform attribute/style to move the shape. If you set transform="translate(x,y)" then the shape will be centered at x,y.
Clone this wiki locally