/* * Paper.js - The Swiss Army Knife of Vector Graphics Scripting. * http://paperjs.org/ * * Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey * http://scratchdisk.com/ & http://jonathanpuckey.com/ * * Distributed under the MIT license. See LICENSE file for details. * * All rights reserved. */ var fs = require('fs'), path = require('path'); module.exports = function(paper) { if (paper.PaperScript) { var sourceMapSupprt = 'require("source-map-support").install(paper.PaperScript.sourceMapSupport);\n', sourceMaps = {}; paper.PaperScript.sourceMapSupport = { retrieveSourceMap: function(source) { var map = sourceMaps[source]; return map ? { url: source, map: map } : null; } }; // Register the .pjs extension for automatic compilation as PaperScript require.extensions['.pjs'] = function(module, filename) { // Requiring a PaperScript on Node.js returns an initialize method which // needs to receive a Canvas object when called and returns the // PaperScope. module.exports = function(canvas) { var source = fs.readFileSync(filename, 'utf8'), code = sourceMapSupprt + source, compiled = paper.PaperScript.compile(code, { url: filename, source: source, sourceMaps: true, offset: -1 // remove sourceMapSupprt... }), scope = new paper.PaperScope(); // Keep track of sourceMaps so retrieveSourceMap() can link them up scope.setup(canvas); scope.__filename = filename; scope.__dirname = path.dirname(filename); // Expose core methods and values scope.require = require; scope.console = console; sourceMaps[filename] = compiled.map; paper.PaperScript.execute(compiled, scope); return scope; }; }; } paper.PaperScope.inject({ createCanvas: function(width, height, type) { // Do not use CanvasProvider.getCanvas(), since we may be changing // the underlying node-canvas and don't want to release it after // back into the pool. var canvas = paper.document.createElement('canvas'); canvas.width = width; canvas.height = height; canvas.type = type; return canvas; }, /** * @deprecated use use {@link #createCanvas(width, height)} instead. */ Canvas: '#createCanvas' }); // Override requestAnimationFrame() to avoid setInterval() timers. // NOTE: In Node.js, we only support manual updating for now, but // View#exportFrames() below offers a way to emulate animations by exporting // them frame by frame at the given frame-rate. paper.DomEvent.requestAnimationFrame = function(callback) { }; // Node.js based image exporting code. paper.CanvasView.inject({ // DOCS: CanvasView#exportFrames(options); exportFrames: function(options) { options = paper.Base.set({ fps: 30, prefix: 'frame-', amount: 1, }, options); if (!options.directory) throw new Error('Missing options.directory'); var view = this, count = 0, frameDuration = 1 / options.fps, startTime = Date.now(), lastTime = startTime, padding = options.padding || ((options.amount - 1) + '').length, paddedStr = Array(padding + 1).join('0'); // Start exporting frames by exporting the first frame: exportFrame(options); function exportFrame() { // Convert to a Base object, for #toString() view.emit('frame', new paper.Base({ delta: frameDuration, time: frameDuration * count, count: count })); var file = path.join(options.directory, options.prefix + (paddedStr + count).slice(-padding) + '.png'); var out = view.exportImage(file, function() { // Once the file has been closed, export the next fame: var then = Date.now(); if (options.onProgress) { options.onProgress({ count: count, amount: options.amount, percentage: Math.round((count + 1) / options.amount * 10000) / 100, time: then - startTime, delta: then - lastTime }); } lastTime = then; if (++count < options.amount) { exportFrame(); } else { // Call onComplete handler when finished: if (options.onComplete) { options.onComplete(); } } }); } }, // DOCS: CanvasView#exportImage(path, callback); exportImage: function(path, callback) { this.update(); var out = fs.createWriteStream(path), stream = this._element.createPNGStream(); // Pipe the png stream to the write stream: stream.pipe(out); if (callback) { out.on('close', callback); } return out; } }); };