Designing Web Applications for Digital Fabrication

Digital fabrication applications make mass customization possible for general audiences. Nonetheless, there are still unique challenges in developing digital fabrication web applications.

Introduction

Digital fabrication tools such as laser cutters, affordable CNC routers and mills, and 3D printers have opened opportunities for new types of creations and customized objects. However, current CAD design tools are still too complicated for many users. Even for experienced users, CAD design can be tedious and error-prone.

I think digital fabrication applications tailored to specific types of objects are a great way to make mass customization accessible to large numbers of users. The digital fabrication application presents the user with a very limited number of design parameters, which the application then uses to generate the design. By limiting the number of options, the design process is greatly simplified, making it faster, easier, and more robust.

MakerCase Design Philosophy

MakerCase is a digital fabrication application for designing custom project cases that can be manufactured using a laser cutter.

From my own experiences, I found that case design can be tedious and frustrating.  Previously, I used Autodesk Inventor to model a case in three-dimensions.  I then used that three-dimensional model as a template for designing flat panels, and finally arranged copies of the flat panels on a single sheet for export to the laser cutter.  There are lots of details that need to be exactly right to get flat panels to fit together correctly and form a three-dimensional case with the right size.  Making any small changes to the design required reviewing everything to ensure that the panels still fit together.  And more advanced techniques, like compensating for the cutting kerf of the laser, required extensive and confusing manual editing.

My goal with MakerCase was to make the case design process simple, foolproof, and accessible.  I wanted to make designing a case as simple as just writing down a list of case specifications, with all of the difficult and tedious aspects of case design, such as making sure panels interlocked and compensating for laser cutting kerf, handled automatically.

With that in mind, my design motto for MakerCase became “A tool, not CAD.” Rather build a full-featured CAD application that could handle cases of any arbitrary shape or complexity, I decided to make a tool that could be used to create the most common case shape: a simple box.  For every design feature, I decided to only require a minimum amount of information from the user, with other design parameters determined automatically with “safe” default values.

To make it accessible, I decided to write it as a web-based application, so that anyone with a browser could instantly use it.  Web technologies have come a long way in recent years, and it is now possible to make interactive applications with complex, real-time rendered two- and three-dimensional graphics.

Nonetheless, there are still unique challenges in developing digital fabrication web applications.

MakerCase Overview

  1. Written in javascript on client and server
  2. Approximately 5,000 lines of code
    1. 4,500 lines for client browser
    2. 500 lines for server
  3. Uses three.js for 3D rendering
  4. Software rendering mode for compatibility – not WebGL
  5. JQuery for browser user interface and utility functions
  6. JCanvas for panel editor (may change to SVG)
  7. Server written with Node.js/Express and hosted on Heroku.

Reusable Graphics Modules

MakerCase presents graphics in four different views.  First, there is a three-dimensional model of the entire case.  Second, the user can open a two-dimensional panel editor to add features like text and holes to any panel of the case.  The application generates a blueprint view showing the panels arranged for cutting. And finally, the application provides a blueprint file that the user can download and send to a laser cutter.

To further complicate things, different views use the graphics data in different ways and different formats.  The three-dimensional model uses the graphics data to generate texture maps for a 3D model.  The panel editor uses the graphics data to generate interactive, two-dimensional sprites that can be dragged and moved by the user.  Both of these views render graphics to an HTML canvas.  The blueprint view and output file show all of the panels and their features arranged on a sheet and are in SVG format.

I decided to use a modular, object-oriented architecture to generate graphics data with minimal duplication.  In MakerCase, every graphical element of the box is referred to as a feature. Features include the various types of box edges, such as finger and t-slot joints, as well as holes and text added to panels.

Each feature object includes its specific parameter values and code for drawing itself. For example, here is feature object for a round hole.

function roundHoleFeature(params,renderCanvas,renderSVG){
	//Params: {face, x, y, d, featureId}

	var self = this;
	self.params = params || {};
	self.type = "roundHole";
	self.renderCanvas = renderCanvas || roundHoleRenderCanvas;
	self.renderSVG = renderSVG || roundHoleRenderSVG;
}

This feature includes its parameter values, such as the hole position and hole diameter, and two methods, one for drawing itself to an HTML canvas and another for generating corresponding SVG code. For simple features, like the round hole, these rendering functions are separate.

However, in more complicated features like the finger and t-slot edges, there is a single function that outputs a set of points describing the feature. Both the canvas and SVG rendering functions call this common function, and then convert this point data into canvas or SVG objects.

The data model for the entire case is a simple container object with a few parameters.


var boxObj = {
 boxDimW:4,
 boxDimH:4,
 boxDimD:4,
 boxUnits:"inches",
 boxMatThickness: .118,
 boxEdgeType:"flat",
 boxFacesFeatureLists: {
 "Right": [],
 "Left": [],
 "Top": [],
 "Bottom": [],
 "Front": [],
 "Back": []
 }
 };

The boxFacesFeaturesLists is a container for storing the features of each case panel. Each panel is associated with an array that holds the feature objects for that panel. By default, each panel includes at least an edge feature object that specifies the type of edge (flat, finger joint, or t-slot joint). As new features are added to panels, additional feature objects are added to the corresponding panel’s feature array.

Each application view uses this container to draw the required graphics data. For the three-dimensional case view, the application selects each panel in the boxFacesFeaturesLists container and cycles through every feature object in the panel’s array, calling the canvas rendering method for each feature.

Here’s an example feature drawing loop:

var canvasIndex = 0;
for ( var featureList in boxObj.boxFacesFeatureLists ) {
  for (var i = 0; i < boxObj.boxFacesFeatureLists[featureList].length; i++){
    boxObj.boxFacesFeatureLists[featureList[i].renderCanvas(textureCanvases[canvasIndex]);
  }
  canvasIndex++;
}

textureCanvases is an array of HTML canvases used as texture maps for the case panels. This loop selects each panel in the feature list and cycles through every feature in each panel’s feature array, calling each feature’s renderCanvas method to draw itself to the selected panel’s texture map canvas.

After cycling through the array, the each panel’s texture map canvas will have all of the panel’s features drawn on it. This canvas is then passed to the 3D renderer as a texture map for a face of the three-dimensional model of the object.

The panel editor and blueprint view work similarly. Each one cycles through one or more of the panels’ arrays and calls the rendering function of each feature object. The resulting HTML canvas or SVG image is then displayed to the user. The same SVG rendering functions are also called to generate the blueprint file that can be sent to the laser cutter.

Nested Transformation Groups

Another feature that greatly simplified rendering was the use of nested coordinate transformations. HTML canvases and SVG both allow graphics entities to be grouped together and transformed (translated, scaled and/or rotated). Multiple transformations can be nested to any number of levels.

MakerCase uses nested coordinate transformations to separate the positioning and scaling of graphics entities for each viewing context from the feature rendering code. For example, every feature object always draws itself in its natural, physical units, regardless of the context. For example, a round hole with a diameter of 0.5″ will be drawn by its rendering methods as a circle with d=0.5 in the three-dimensional case view, the panel editor view, the blueprint image view, and the blueprint file itself. A scaling transformation group is wrapped around the graphics entities to resize the graphics for each view, with each view having a different scaling factor.

Similarly, nested transformation groups are used to position panels and their features in a blueprint. Each panel is represented on a blueprint by a coordinate transformation group that specifies the panel’s position on a sheet. The edges and other features of each panel are added to its transformation group. This automatically positions each feature correctly on the sheet.

Here’s an example of nested transformation groups in a blueprint file:

<!-- Front--> 
<g transform="translate(22.5000185, 22.5000185) " > 
<polygon style="stroke:#000000; fill:none; stroke-width:0.90000074" 
  points="0, 0 0, 360.000296 360.000296, 360.000296 360.000296, 0" /> 
</g>
<!-- Back--> 
<g transform="translate(382.50031449999994, 22.5000185) " > 
<polyline style="stroke:#000000; fill:none; stroke-width:0.90000074" 
  points="0, 0 360.000296, 0 360.000296, 360.000296 0, 360.000296" /> 
</g>

In this example, each panel’s edges are represented by a polygon that begins at the origin (0,0). However, a transformation group around each panel translates each polygon to a different location on the blueprint sheet.

As discussed below, nested transformations are also used extensively in converting text features to vector paths.

Saving User-Generated Content

For security reasons, web browsers do not permit a web page to save data to the local file system. However, web browsers will allow users to download data from a server and save it to a local file. Taking advantage of this exception, MakerCase uses a lightweight reflector server to allow users to save their blueprint files.

In MakerCase, the client-side code is responsible for generating the blueprint file data. When a user asks to save a blueprint file, the browser sends the blueprint data to the server via an HTTP POST operation. The server receives the blueprint data and sends it back to the client browser. The server response includes the HTTP header attributes Content-Disposition: attachment and filename=FILENAME to direct the browser to save the downloaded data in a file.

Here’s an example of client-side code:

//Generate SVG data for a given set of panels arranged on a sheet.
var svgOut = generateSVG(panelArray,"file");

//Attach the SVG data to a web form
$("#blueprintDataID").val(svgOut);

//Submit the web form to the server
document.getElementById("hiddenBlueprintForm").submit();

And here’s an example of corresponding server code:

app.post('/simpledownload', function (req, res){
  //Blueprint data is passed to this function by the req.body.blueprintData object.

  //Set HTTP header for response to save data as a file attachment  
  res.writeHead(200, {"Content-Type": "image/svg+xml", "Content-Disposition": "attachment; filename=caseplans.svg"});

  //Write incoming blueprint data into to the response.	
  res.write(req.body.blueprintData);
	
  //Complete the response and send it to the client.
  res.end();
	
});

Generating Graphics for Laser Cutters

Creating graphics for display is straightforward, but laser cutters impose additional requirements. Graphics that look correct won’t necessarily be cut correctly by a laser cutter.

Polygons, polylines, and continuous paths

When drawing two-dimensional graphics, the drawing order is often irrelevant. However, when cutting out shapes with a laser cutter, it is best to define each cut-out shape as a single, continuous path. This ensures that the laser doesn’t undershoot a vertex (leaving the shape incompletely cut out) or overshoot a vertex (causing a cut line to extend past the boundary of the shape).

Additionally, if two panels share an edge, you should avoid cutting over the same edge twice.

SVG provides polygon, polyline, and path commands for drawing continuous paths. Here’s an example of the drawing commands and line segment ordering for drawing a set of panels.

Screen Shot 2013-06-18 at 10.22.35 AM

Laser cutting kerf

When cutting with a laser, the beam burns away a small amount of material as it cuts. Because of this, features will be slightly larger or smaller than their intended size. The kerf is typically on the order of 0.005″, but varies from machine to machine and even changes over the life of the laser.

The laser cutting kerf tends to make holes bigger and tabs smaller than intended. As a result, parts fit together loosely without compensating for kerf. If you are bolting or gluing parts together, then there is no need for kerf compensation. However, if you want panels to fit snugly, you will need to compensate for kerf.

In general, cutting kerf increases the size of holes and decreases the size of tabs. Another way to think of it is cutting kerf increases negative space and decreases positive space. Therefore, you should decrease the size of holes by the cutting kerf and increase the size of tabs by the cutting kerf to compensate. It can get confusing, so it helps to draw it out.

2013-05-05 20.00.24

Text to vector path conversion

Most laser cutters don’t include any font support and require text to be converted into vector paths. One approach is to have the client upload the image data to the server, as is done for the file download process, and have the server run an image editor like Inkscape via the command line to convert text to paths. For example:

$ inkscape --with-gui --verb EditSelectAll--verb ObjectToPath--verb FileSave\
           --verb FileQuit file.svg

In my case, I wanted a pure javascript implementation to keep the server hosting costs low, so I wrote a simple text to SVG path conversion library.

Google web fonts can be downloaded in SVG format. Each font is composed of glyphs, each representing the shape of a single character as an SVG path element.

The text conversion code first identifies any SVG tags in an incoming drawing. For each text tag, it generates a transformation group representing the text’s position in the blueprint. The text conversion code then loops through each of the characters in the text tag, looks up the SVG path data for each character in the tag, and adds this path data to the tag’s transformation group.

Each character’s corresponding glyph SVG path data is assigned to its own transformation group within the tag transformation group. The glyph’s transformation group specifies the horizontal position of the glyph within the tag transformation group. Each glyph includes a “horizAdvX” parameter that specifies the start location of the next character. This value is accumulated in the character processing loop to get the start location of each glyph’s transformation group. The glyph transformation group also scales the path data to the correct size and flips it vertically (because SVG font data is strangely stored upside down).

Once the SVG path data for a text tag is complete, the server removes the original text tag and replaces it with the SVG data.

Laser line widths and colors

Most laser cutters can perform vector cutting, vector engraving, and raster engraving. However, there is no standard file format or image convention for specifying which of these operations to perform.

For example, some laser cutters will use vector cutting on any line set to a “hairline” width. Other laser cutters are configured to vector cut lines any line set to a specific color.

Because of this, it is a good idea to let users configure the vector cutting, vector engraving, and raster engraving line widths and fill colors. For example, this can be done in code as:

svgOutput = "<polygon style=\"stroke:" + svgCutColor + \
"; fill:" + svgFillColor + \
"; stroke-width:" + svgCutWidth*pageScale + "\" ";

Closing

As digital fabrication tools such as laser printers and 3D printers open opportunities for new types of creations, design applications that simplify the creation process for average users will be invaluable.

I hope this content has inspired you to consider the possibilities for digital fabrication application. If you have any questions or feedback, you are welcome to contact me at jh@jonhollander.me.

Comments are closed.

Copyright 2017 jonhollander.me · RSS Feed · Log in

Business Theme by Organic Themes · WordPress Hosting

Organic Themes