Today I reworked how patterns are loaded and stored.

Leather shorts and sequins shirt

By patterns, I mean complex objects that can be used in place of a colour for any stroke or fill. This includes CanvasGradients and CanvasPattern - which can be created from images, existing canvas drawings, and many, many other sources.

Before, patterns were synchronously loaded at the same time the code is loaded, where CanvasPattern from images were forced to use images stored in the form of dataURIs. This was not a scalable solution since storing 100 different images in code is crazy for anyone that wants to use some, but not all of the patterns. The load time would’ve also been atrocious.

The new method uses Javascript’s Promise mechanism to lazily load patterns as you need them, and makes it extremely easy to create a new pattern. For example, to create a new repeating pattern from an image:

// register the pattern so we know what we mean when we say "black leather"
da.addPattern("black leather", "http://www.textures123.com/free-texture/leather/leather-texture05.jpg");

// first use of the pattern; we'll load it asynchronously and cache it for future uses
var myPants = da.Clothes.create(da.TightShorts,
        {
            // specify size to adjust how zoomed in the pattern is
            fill      : da.getPattern("black leather", 50),
            pantLength: 0.7
        });

// wear it to have it shown
PC.wearClothing(myPants);

// actually do the rendering
da.draw(canvasGroup, PC);

Draw waits until all the necessary and unloaded patterns have been loaded before drawing. This means if you added 100 patterns, but are only using 1 of them, you only load the 1 you’re using and don’t end up wasting bandwidth and time loading the ones you haven’t used yet.

One consequence of making draw asynchronous is the exports that it returns might also have to be dealt with asynchronously. This can be done using:

var ex = da.draw(canvasGroup, PC);
if (ex instanceof Promise) {
    ex.then(function (exports) {
        ex = exports;
    });
}

However, this is only if you actually need the exports.

Pattern Varieties

In addition to links, you can also pass in functions for producing patterns. For example, to create a washed out jeans pattern, where there’s more wear near the knees and center of the pants, you can create a gradient:

da.addPattern("washed jeans", function (ctx) {
    var grd = ctx.createLinearGradient(0, 0, 100, 0);
    grd.addColorStop(0, "rgb(0,68,110)");
    grd.addColorStop(0.2, "rgb(0,110,160)");
    grd.addColorStop(0.5, "rgb(0,75,140)");
    grd.addColorStop(0.8, "rgb(0,110,160)");
    grd.addColorStop(1, "rgb(0,68,110)");
    return grd;
});

The factory method will be given ctx and ex (exports including draw points). Methods that take only ctx will be statically cached (meaning we actually cache the produced pattern since we can create it with any context), but methods that also require exports will be dynamically cached (since we need to know the exports each time).