How to make an app

Developers with Early Access can access the app creation interface at easel.inventables.com/apps.

New apps are created with the basic app skeleton. All apps are written in javascript. They declare properties and an executor function which returns an SVG.

Properties

Apps declare a var properties in order to collect user input. Here are the 5 available property types:

var properties = [
    {id: "Your file", type: "file-input"},
    {id: "A number", type: "range", value: 50, min: 0, max: 100, step: 10},
    {id: "A string", type: "text", value: "initial string"},
    {id: "Options", type: "list", value: "a", options: [["a", "One"], ["b", "Two"]]},
    {id: "True or false", type: "boolean", value: false}
];

[more documentation about the way file input works, list works is probably needed here]

Executor

The second part of the app is the executor. It is a function called executor which gets 3 parameters.

var executor = function(params, success, failure) {
  var userProperties = params[0];
  var selectedVolumes = params[1];

  var userNumber = userProperties["A number"];
  var userBoolean = userProperties["True or false"];
  var fileUrl = userProperties["Your file"];

  // If you need to validate an input, return failure called with the error
  if (userNumber < 0) {
    return failure("You must use a positive number");
  }

  // Construct an SVG string and call success with it
  var svgHeader = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">';
  var rectElement = '<rect x="50" y="50" width="200" y="200" rx="' + userNumber + '" />';
  var svgFooter = '</svg>';

  success(svgHeader + rectElement + svgFooter);
};

More on the selectedVolumes API soon.

2 Likes

This is great! I’ve done a bit in Java and it seemed pretty simple to catch onto. I see that more information is going to be provided soon, would it be possible to see an example app, such as the gear generator? That would be great! Excited to start hammering into this!!

2 Likes

Me too. :smile:

If you bring up the console in Chrome before loading the gear generator and select Network, you can view the source of it.

Biggest problem I’ve got? Can’t decide what to make first.

Cheers

Ian

2 Likes

Here is an example app I am working on right now:

It shows an example of working with selected shapes. It currently has a limitation (which is due to D3’s clipping algorithm) that limits it to working properly only for convex shapes. I’m in the process of switching it over to use the javascript Clipper library for arbitrary shape support, so look for updates in the near future.

4 Likes

Got some tips from your code, thanks @JeffTalbot

If you want to stick to d3.js, Mike Bostock is very accommodating if you just drop him an email. You never know, he might write an app to turn some of his glorious demos into physical items. :smile:

1 Like

OK, I have this working but not sure how to share an app between ourselves. But I’ve put together an app that I can see at: http://easel.inventables.com/apps/16/edit and also within Easel and is named “Grid of Holes” (pasted below too, just in case).

It basically generates a grid of circles that you can change the count in the X and Y, diameter and spacing. Really useful for things like cutting a cooling grille in an electronics project case etc.

One thing I am noticing, is that the app always seems to execute twice. I’ve got a console.log in there to prove that. Now is that something I’m doing wrong, or is that normal in that it’s doing a draw on the app view first, then again for an offscreen SVG ready for import?

Cheers

Ian


// Grid of Holes - Ian Watkins - Test application
//
// Generates a regular grid of circles, e.g. to carve a cooling grille
// in a electronics project enclosure
//

var properties = [
  {id: 'Number of Circles in the X direction', type: 'range', value: 10, min: 2, max: 20, step: 1},
  {id: 'Number of Circles in the Y direction', type: 'range', value: 10, min: 2, max: 20, step: 1},
  {id: 'Diameter of each circle', type: 'range', value: 20.0, min: 0.1, max: 60, step: 0.1},
  {id: 'Space between each circle', type: 'range', value: 10.0, min: 0.1, max: 60, step: 0.1},
];

var executor = function(args, success, failure) {
  
  var xPoints = args[0]['Number of Circles in the X direction'],
      yPoints = args[0]['Number of Circles in the Y direction'],
      diameter = args[0]['Diameter of each circle'],
      offset = args[0]['Space between each circle'],

      svgHeader = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">',
      svgFooter = '</svg>';
      
  console.log('Grid Of Holes App, running more than once?');

  success(svgHeader + gridOfCircles(xPoints, yPoints, diameter, offset) + svgFooter);
  
};

var gridOfCircles = function(xPoints, yPoints, diameter, offset) {
  
  var svgGridOfCircles = '',
      svgCircle,
      radius = diameter / 2,
      xLoop,
      yLoop,
      cx,
      cy;
  
  // Y Loop
  for (yLoop = 0; yLoop < yPoints; yLoop++) {
    // X Loop
    for (xLoop = 0; xLoop < xPoints; xLoop++) {

      cx = radius + (xLoop * (diameter+ offset));
      cy = radius + (yLoop * (diameter+ offset));

      svgCircle = '<circle cx="' + cx + '" cy="' + cy + '" r="' + radius + '" stroke-width="0" fill="#000000"' + '/>';
      
      svgGridOfCircles = svgGridOfCircles + svgCircle;
    }
  }
  
  return svgGridOfCircles;
  
}

the app always seems to execute twice

I noticed that issue myself while working on my app. We will look into it. In general, though, your app should be idempotent, so this would mainly be a performance issue.

Currently “admins” are able to see and use private apps. I just played around with yours and it works pretty well.

I wonder if it would be better to constrain it to a max width and height and grow/shrink the circles based on the settings within that rectangle? The final object could then be scaled on the Easel canvas after import.

1 Like

I thought about that too Jeff. Create and select a rectangle first before starting the app and have it do a fill of holes? I guess that is how many would use it.

Is there a way to find out the bit size that has been set within an app? No point scaling a ton of holes to 2mm if you are running a 3.175mm bit.

I’ll have a think.

Cheers

Ian

Hmm… That might be one way to go. The problem I noticed was that I could increase my x & y circle counts to a point where the circles went outside the window the app was running in. It might be better to have a fixed viewport that the circles get scaled up or down within. If you have a selected object, that object could fill that viewport (which is how the voronoi app works), or you could just have a fixed size viewport and then let the user scale the resulting shape to whatever size they want at the end. It would be as if the grid automatically got zoomed to fit the same width/height no matter what variables they selected on the left.

We don’t pass the bit size in right now, but we could add it. Are there any other parameters would be useful?

Understood. When I get some time to get into this deeper, I’ll look at the viewport issue. Makes a lot of sense.

Passed in prams like bit size are of course nice to haves as you could scale objects based on it. But as the user can change bit size after your app has run you cannot of course rely on it.

But right now, bit size and workpiece size as set by the user would be useful. I think although it appears Easel works within mm internally, could the apps also be told what units are set in the 2D view? That way we can scale the objects to the units the user is working in.

Cheers

Ian

Noticed that although apps still run twice for every slider step move (and only a performance issue right now), that none of the apps run at all at startup any more until the user moves/changes a control.

Is this a design change?

And possibly related to the above, every app throws an error at startup as below (from Chrome console):

This is an issue that we are aware of and have a fix coming out for.

This post has a link to a google form that you can submit to apply for early access:

Cheers,
Jeff

1 Like

Can you install Easel Local?

And it’s fixed. This indeed was the same reason for the error that you were seeing in the console. Thanks!

1 Like

Hmm… I don’t have any experience with Wine.

None of those questions are dumb and majority have already been asked Angus. Mainly the answers at the moment are “Not yet” or “No”. :smile:

My understanding is that the API for building apps is very early days so these kind of things don’t get asked/don’t come up until you get something out of the door that in turn generates this kind of discussion. It’ll be an iterative process.

Specifics wise, no, apps currently cannot know anything about what is happening is Easel itself. The only thing you can currently pass in is a selected feature, and this is optional.

I’ve been playing with app creation for a few weeks now and really like what has been done so far. But I agree, knowing more about what other things are happening in Easel would make apps much more useful.

I’m sure it’ll get there :smile:

Cheers

Ian

Ian is correct: your app doesn’t receive any information from Easel other than the parameters defined for your app and the (optional) paths of selected shapes. We’re definitely looking for questions and feedback like this in order to learn about how the API should work.

Could you tell us a little more about what kinds of things you are trying build and how you would use the data? The specific use cases might help us as we’re rethinking the APIs.

You can set an explicit width and height on the SVG element you return, and you can also use a viewbox to scale the coordinate system. For an example, you could refer to the source of some of the other apps, like the Voronoi one:

This will be changing in the near future so that shapes are returned in Easel’s native object model rather than as an SVG.

When Easel imports an SVG, it will import any paths with a “fill” as fills. It will import stroked paths as outlines.

Easel will map the stroke or fill color of a path to a cut depth using a grayscale-to-cut-depth approach where rgb(0, 0, 0) (black) means the cut is all the way through, and rgb(255, 255, 255) (white) means no cut at all, and anything in between is scaled linearly.

This will be more straightforward once we’ve made the transition to returning Easel’s native object model, but the way to do this with SVG is using the width, height, and viewBox of the svg element. Set the width and height to a value with a unit (cm, mm, or in). The values in viewBox (in order: left, top, width, height) define the “internal” coordinate system for all the path and other elements inside the SVG. They can be any numbers, but I think it’s easiest if you use 0 for the left and top, and the same numbers as you used for the width and height values, like this:

<svg xmlns="http://www.w3.org/2000/svg"
    width="4in" height="5in" viewBox="0 0 4 5">
    <!-- note there are no units for the viewBox numbers -->
    <!-- the `stroke-width` and `d` values below are in inches now -->
    <path fill="none" stroke="#000" stroke-width="0.25"
        d="M 2,1 L 3,1 3,3 Q 3,4 2,4 Q 1,4 1,3" />
</svg>

That’s something that will be possible once apps can use Easel’s native object model, but that unfortunately SVG has no way to express.