diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..9e70902f --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,38 @@ + + +# Description/Steps to reproduce + + + +# Link to reproduction test-case + + + +# Expected result + + + +# Additional information + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..65cb209d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,24 @@ +### Description + + +#### Related issues + + + +- relates to + +### Checklist + + + +- [ ] New tests added or existing tests modified to cover all changes +- [ ] Code conforms with the [style + guide](https://github.com/paperjs/paper.js/blob/develop/RULES.md) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f2a7951..af4ca06c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Change Log +## `0.11.5` + +### Fixed +- Fix `Curve#isSelected()` to correctly reflect the state of `#handle1` (#1378). +- Key Events: Fix auto-filling issue on Chrome (#1358, #1365). +- Boolean: Check that overlaps are on the right path (#1321). +- Boolean: Add better filtering for invalid segments (#1385). + +### Added + +- Node.js: Add JPEG support to exportFrames() (#1166). + ## `0.11.4` ### Changed diff --git a/RULES.md b/RULES.md new file mode 100644 index 00000000..ccd3bd67 --- /dev/null +++ b/RULES.md @@ -0,0 +1,3 @@ +# Paper.js Style Guide + +Coming *soon*. diff --git a/dist/paper-core.js b/dist/paper-core.js index 32270f7f..61497ac6 100644 --- a/dist/paper-core.js +++ b/dist/paper-core.js @@ -1,5 +1,5 @@ /*! - * Paper.js v0.11.4 - The Swiss Army Knife of Vector Graphics Scripting. + * Paper.js v0.11.5 - The Swiss Army Knife of Vector Graphics Scripting. * http://paperjs.org/ * * Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey @@ -9,7 +9,7 @@ * * All rights reserved. * - * Date: Wed Jun 7 16:56:44 2017 +0200 + * Date: Thu Oct 5 16:16:29 2017 +0200 * *** * @@ -778,7 +778,7 @@ var PaperScope = Base.extend({ } }, - version: "0.11.4", + version: "0.11.5", getView: function() { var project = this.project; @@ -6133,7 +6133,7 @@ var Curve = Base.extend({ isSelected: function() { return this.getPoint1().isSelected() - && this.getHandle2().isSelected() + && this.getHandle1().isSelected() && this.getHandle2().isSelected() && this.getPoint2().isSelected(); }, @@ -10350,16 +10350,20 @@ PathItem.inject(new function() { function collect(inter, end) { while (inter && inter !== end) { var other = inter._segment, - path = other._path, - next = other.getNext() || path && path.getFirstSegment(), - nextInter = next && next._intersection; - if (other !== segment && (isStart(other) || isStart(next) - || next && (isValid(other) && (isValid(next) - || nextInter && isValid(nextInter._segment))))) { - crossings.push(other); + path = other && other._path; + if (path) { + var next = other.getNext() || path.getFirstSegment(), + nextInter = next._intersection; + if (other !== segment && (isStart(other) + || isStart(next) + || next && (isValid(other) && (isValid(next) + || nextInter && isValid(nextInter._segment)))) + ) { + crossings.push(other); + } + if (collectStarts) + starts.push(other); } - if (collectStarts) - starts.push(other); inter = inter._next; } } @@ -10450,7 +10454,8 @@ PathItem.inject(new function() { visited.length = 0; do { seg = branch && branch.crossings.shift(); - if (!seg) { + if (!seg || !seg._path) { + seg = null; branch = branches.pop(); if (branch) { visited = branch.visited; @@ -10516,9 +10521,9 @@ PathItem.inject(new function() { var children = this._children, paths = children || [this]; - function hasOverlap(seg) { + function hasOverlap(seg, path) { var inter = seg && seg._intersection; - return inter && inter._overlap; + return inter && inter._overlap && inter._path === path; } var hasOverlaps = false, @@ -10534,10 +10539,12 @@ PathItem.inject(new function() { return inter.hasOverlap(); }, clearCurves); for (var i = overlaps.length - 1; i >= 0; i--) { - var seg = overlaps[i]._segment, + var overlap = overlaps[i], + path = overlap._path, + seg = overlap._segment, prev = seg.getPrevious(), next = seg.getNext(); - if (hasOverlap(prev) && hasOverlap(next)) { + if (hasOverlap(prev, path) && hasOverlap(next, path)) { seg.remove(); prev._handleOut._set(0, 0); next._handleIn._set(0, 0); @@ -13083,8 +13090,9 @@ var Key = new function() { key = /^U\+/.test(key) ? String.fromCharCode(parseInt(key.substr(2), 16)) : /^Arrow[A-Z]/.test(key) ? key.substr(5) - : key === 'Unidentified' ? String.fromCharCode(event.keyCode) - : key; + : key === 'Unidentified' || key === undefined + ? String.fromCharCode(event.keyCode) + : key; return keyLookup[key] || (key.length > 1 ? Base.hyphenate(key) : key.toLowerCase()); } diff --git a/dist/paper-full.js b/dist/paper-full.js index 38ec383e..ca645b17 100644 --- a/dist/paper-full.js +++ b/dist/paper-full.js @@ -1,5 +1,5 @@ /*! - * Paper.js v0.11.4 - The Swiss Army Knife of Vector Graphics Scripting. + * Paper.js v0.11.5 - The Swiss Army Knife of Vector Graphics Scripting. * http://paperjs.org/ * * Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey @@ -9,7 +9,7 @@ * * All rights reserved. * - * Date: Wed Jun 7 16:56:44 2017 +0200 + * Date: Thu Oct 5 16:16:29 2017 +0200 * *** * @@ -778,7 +778,7 @@ var PaperScope = Base.extend({ } }, - version: "0.11.4", + version: "0.11.5", getView: function() { var project = this.project; @@ -6133,7 +6133,7 @@ var Curve = Base.extend({ isSelected: function() { return this.getPoint1().isSelected() - && this.getHandle2().isSelected() + && this.getHandle1().isSelected() && this.getHandle2().isSelected() && this.getPoint2().isSelected(); }, @@ -10350,16 +10350,20 @@ PathItem.inject(new function() { function collect(inter, end) { while (inter && inter !== end) { var other = inter._segment, - path = other._path, - next = other.getNext() || path && path.getFirstSegment(), - nextInter = next && next._intersection; - if (other !== segment && (isStart(other) || isStart(next) - || next && (isValid(other) && (isValid(next) - || nextInter && isValid(nextInter._segment))))) { - crossings.push(other); + path = other && other._path; + if (path) { + var next = other.getNext() || path.getFirstSegment(), + nextInter = next._intersection; + if (other !== segment && (isStart(other) + || isStart(next) + || next && (isValid(other) && (isValid(next) + || nextInter && isValid(nextInter._segment)))) + ) { + crossings.push(other); + } + if (collectStarts) + starts.push(other); } - if (collectStarts) - starts.push(other); inter = inter._next; } } @@ -10450,7 +10454,8 @@ PathItem.inject(new function() { visited.length = 0; do { seg = branch && branch.crossings.shift(); - if (!seg) { + if (!seg || !seg._path) { + seg = null; branch = branches.pop(); if (branch) { visited = branch.visited; @@ -10516,9 +10521,9 @@ PathItem.inject(new function() { var children = this._children, paths = children || [this]; - function hasOverlap(seg) { + function hasOverlap(seg, path) { var inter = seg && seg._intersection; - return inter && inter._overlap; + return inter && inter._overlap && inter._path === path; } var hasOverlaps = false, @@ -10534,10 +10539,12 @@ PathItem.inject(new function() { return inter.hasOverlap(); }, clearCurves); for (var i = overlaps.length - 1; i >= 0; i--) { - var seg = overlaps[i]._segment, + var overlap = overlaps[i], + path = overlap._path, + seg = overlap._segment, prev = seg.getPrevious(), next = seg.getNext(); - if (hasOverlap(prev) && hasOverlap(next)) { + if (hasOverlap(prev, path) && hasOverlap(next, path)) { seg.remove(); prev._handleOut._set(0, 0); next._handleIn._set(0, 0); @@ -13083,8 +13090,9 @@ var Key = new function() { key = /^U\+/.test(key) ? String.fromCharCode(parseInt(key.substr(2), 16)) : /^Arrow[A-Z]/.test(key) ? key.substr(5) - : key === 'Unidentified' ? String.fromCharCode(event.keyCode) - : key; + : key === 'Unidentified' || key === undefined + ? String.fromCharCode(event.keyCode) + : key; return keyLookup[key] || (key.length > 1 ? Base.hyphenate(key) : key.toLowerCase()); } diff --git a/examples/Node.js/BooleanOperations.js b/examples/Node.js/BooleanOperations.js index 9050f7ab..aeacd13a 100644 --- a/examples/Node.js/BooleanOperations.js +++ b/examples/Node.js/BooleanOperations.js @@ -24,7 +24,7 @@ http.createServer(function(request, response) { intersection.translate(250, 0); view.update(); } - var stream = canvas.createPNGStream(); + var stream = canvas.pngStream(); stream.on('data', function(chunk) { response.write(chunk); }); diff --git a/examples/Node.js/RasterRemote.js b/examples/Node.js/RasterRemote.js index 17115c8d..9226aa4f 100644 --- a/examples/Node.js/RasterRemote.js +++ b/examples/Node.js/RasterRemote.js @@ -17,7 +17,7 @@ raster.onLoad = function() { // Saving the canvas to a file. out = fs.createWriteStream(__dirname + '/canvas.png'); - stream = canvas.createPNGStream(); + stream = canvas.pngStream(); stream.on('data', function(chunk) { out.write(chunk); diff --git a/package.json b/package.json index ae325a8f..44add035 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "paper", - "version": "0.11.4", + "version": "0.11.5", "description": "The Swiss Army Knife of Vector Graphics Scripting", "license": "MIT", "homepage": "http://paperjs.org", diff --git a/packages/paper-jsdom b/packages/paper-jsdom index 167a9451..afd2bbbf 160000 --- a/packages/paper-jsdom +++ b/packages/paper-jsdom @@ -1 +1 @@ -Subproject commit 167a945169b50bac5c709c4abb97b075085a5a16 +Subproject commit afd2bbbf1cea00f1f94ff89c8a3dd370888ac705 diff --git a/packages/paper-jsdom-canvas b/packages/paper-jsdom-canvas index 5c2f0e0e..f6794e07 160000 --- a/packages/paper-jsdom-canvas +++ b/packages/paper-jsdom-canvas @@ -1 +1 @@ -Subproject commit 5c2f0e0e816745a685d7f5367e4edcac75d8a66d +Subproject commit f6794e0749cfb65d5138f3512fc0eee755bc1829 diff --git a/src/event/Key.js b/src/event/Key.js index 6b7b0d85..c8746429 100644 --- a/src/event/Key.js +++ b/src/event/Key.js @@ -81,8 +81,9 @@ var Key = new function() { // Use short version for arrow keys: ArrowLeft -> Left : /^Arrow[A-Z]/.test(key) ? key.substr(5) // This is far from ideal, but what else can we do? - : key === 'Unidentified' ? String.fromCharCode(event.keyCode) - : key; + : key === 'Unidentified' || key === undefined + ? String.fromCharCode(event.keyCode) + : key; return keyLookup[key] || // Hyphenate camel-cased special keys, lower-case normal ones: (key.length > 1 ? Base.hyphenate(key) : key.toLowerCase()); diff --git a/src/node/canvas.js b/src/node/canvas.js index 84a0550a..d758643b 100644 --- a/src/node/canvas.js +++ b/src/node/canvas.js @@ -13,7 +13,7 @@ // Add some useful extensions to HTMLCanvasElement: // - HTMLCanvasElement#type, so we can switch to a PDF canvas // - Various Node-Canvas methods, routed through from HTMLCanvasElement: -// toBuffer, pngStream, createPNGStream, jpgStream, createJPGStream +// toBuffer, pngStream, createPNGStream, jpegStream, createJPEGStream module.exports = function(self, requireName) { var Canvas; @@ -55,11 +55,12 @@ module.exports = function(self, requireName) { }); // Extend HTMLCanvasElement with useful methods from the underlying Canvas: - ['toBuffer', 'pngStream', 'createPNGStream', 'jpgStream', 'createJPGStream'] - .forEach(function(key) { - HTMLCanvasElement.prototype[key] = function() { - var canvas = idlUtils.implForWrapper(this)._canvas; - return canvas[key].apply(canvas, arguments); - }; - }); + var methods = ['toBuffer', 'pngStream', 'createPNGStream', 'jpegStream', + 'createJPEGStream']; + methods.forEach(function(key) { + HTMLCanvasElement.prototype[key] = function() { + var canvas = idlUtils.implForWrapper(this)._canvas; + return canvas[key].apply(canvas, arguments); + }; + }); }; diff --git a/src/node/extend.js b/src/node/extend.js index 1b342e7f..d2e1ef66 100644 --- a/src/node/extend.js +++ b/src/node/extend.js @@ -67,7 +67,7 @@ module.exports = function(paper) { }, /** - * @deprecated use use {@link #createCanvas(width, height)} instead. + * @deprecated, use use {@link #createCanvas(width, height)} instead. */ Canvas: '#createCanvas' }); @@ -87,9 +87,12 @@ module.exports = function(paper) { fps: 30, prefix: 'frame-', amount: 1, + format: 'png' // Supported: 'png' or 'jpeg' }, options); if (!options.directory) throw new Error('Missing options.directory'); + if (options.format && !/^(jpeg|png)$/.test(options.format)) + throw new Error('Unsupported format. Use "png" or "jpeg"'); var view = this, count = 0, frameDuration = 1 / options.fps, @@ -108,8 +111,9 @@ module.exports = function(paper) { time: frameDuration * count, count: count })); - var file = path.join(options.directory, options.prefix + - (paddedStr + count).slice(-padding) + '.png'); + var file = path.join(options.directory, + options.prefix + (paddedStr + count).slice(-padding) + + '.' + options.format); var out = view.exportImage(file, function() { // Once the file has been closed, export the next fame: var then = Date.now(); @@ -140,8 +144,8 @@ module.exports = function(paper) { exportImage: function(path, callback) { this.update(); var out = fs.createWriteStream(path), - stream = this._element.createPNGStream(); - // Pipe the png stream to the write stream: + format = /\.jp(e?)g$/.test(path) ? 'jpeg' : 'png', + stream = this._element[format + 'Stream'](); stream.pipe(out); if (callback) { out.on('close', callback); diff --git a/src/options.js b/src/options.js index 298ec95a..c4013cea 100644 --- a/src/options.js +++ b/src/options.js @@ -17,7 +17,7 @@ // The paper.js version. // NOTE: Adjust value here before calling `gulp publish`, which then updates and // publishes the various JSON package files automatically. -var version = '0.11.4'; +var version = '0.11.5'; // If this file is loaded in the browser, we're in load.js mode. var load = typeof window === 'object'; diff --git a/src/path/Curve.js b/src/path/Curve.js index 33a887ef..83699a6a 100644 --- a/src/path/Curve.js +++ b/src/path/Curve.js @@ -341,7 +341,7 @@ var Curve = Base.extend(/** @lends Curve# */{ */ isSelected: function() { return this.getPoint1().isSelected() - && this.getHandle2().isSelected() + && this.getHandle1().isSelected() && this.getHandle2().isSelected() && this.getPoint2().isSelected(); }, diff --git a/src/path/PathItem.Boolean.js b/src/path/PathItem.Boolean.js index cb0d4534..c78d6b9d 100644 --- a/src/path/PathItem.Boolean.js +++ b/src/path/PathItem.Boolean.js @@ -825,21 +825,25 @@ PathItem.inject(new function() { function collect(inter, end) { while (inter && inter !== end) { var other = inter._segment, - path = other._path, - next = other.getNext() || path && path.getFirstSegment(), - nextInter = next && next._intersection; - // See if this segment and the next are both not visited - // yet, or are bringing us back to the beginning, and are - // both valid, meaning they are part of the boolean result. - if (other !== segment && (isStart(other) || isStart(next) - || next && (isValid(other) && (isValid(next) - // If the next segment isn't valid, its intersection - // to which we may switch might be, so check that. - || nextInter && isValid(nextInter._segment))))) { - crossings.push(other); + path = other && other._path; + if (path) { + var next = other.getNext() || path.getFirstSegment(), + nextInter = next._intersection; + // See if this segment and the next are not visited yet, + // or are bringing us back to the start, and are both + // valid, meaning they're part of the boolean result. + if (other !== segment && (isStart(other) + || isStart(next) + || next && (isValid(other) && (isValid(next) + // If next segment isn't valid, its intersection + // to which we may switch may be, so check that. + || nextInter && isValid(nextInter._segment)))) + ) { + crossings.push(other); + } + if (collectStarts) + starts.push(other); } - if (collectStarts) - starts.push(other); inter = inter._next; } } @@ -970,7 +974,8 @@ PathItem.inject(new function() { // the list of crossings when the branch is created above. do { seg = branch && branch.crossings.shift(); - if (!seg) { + if (!seg || !seg._path) { + seg = null; // If there are no segments left, try previous // branches until we find one that works. branch = branches.pop(); @@ -1145,9 +1150,9 @@ PathItem.inject(new function() { // Support both path and compound-path items paths = children || [this]; - function hasOverlap(seg) { + function hasOverlap(seg, path) { var inter = seg && seg._intersection; - return inter && inter._overlap; + return inter && inter._overlap && inter._path === path; } // First collect all overlaps and crossings while taking not of the @@ -1169,10 +1174,12 @@ PathItem.inject(new function() { return inter.hasOverlap(); }, clearCurves); for (var i = overlaps.length - 1; i >= 0; i--) { - var seg = overlaps[i]._segment, + var overlap = overlaps[i], + path = overlap._path, + seg = overlap._segment, prev = seg.getPrevious(), next = seg.getNext(); - if (hasOverlap(prev) && hasOverlap(next)) { + if (hasOverlap(prev, path) && hasOverlap(next, path)) { seg.remove(); prev._handleOut._set(0, 0); next._handleIn._set(0, 0); diff --git a/test/helpers.js b/test/helpers.js index 4192b66e..20f1b13e 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -226,9 +226,6 @@ var comparePixels = function(actual, expected, message, options) { ok = Math.abs(100 - identical) <= tolerance, text = identical.toFixed(fixed) + '% identical', detail = text; - if (!ok) { - console.log(actual, expected); - } if (!ok && actual instanceof PathItem && expected instanceof PathItem) { detail += '\nExpected:\n' + expected.pathData + diff --git a/test/tests/Path_Boolean.js b/test/tests/Path_Boolean.js index 83bbf048..083b212b 100644 --- a/test/tests/Path_Boolean.js +++ b/test/tests/Path_Boolean.js @@ -21,7 +21,6 @@ function testOperations(path1, path2, results) { compareBoolean(function() { return path2.intersect(path1); }, results[3]); compareBoolean(function() { return path1.exclude(path2); }, results[4]); compareBoolean(function() { return path2.exclude(path1); }, results[4]); - } test('Boolean operations without crossings', function() { @@ -935,6 +934,12 @@ test('#1261', function() { 'M933.13,1023.97l-516.19,-171.71l67.33,-11.27l0.0109,0.00363l539.7591,-90.30363z'); }); +test('#1321', function() { + var path = PathItem.create('M24,38l2,1l-2,1l-2,-1z M26,39l2,1l-2,1l-2,-1z M28,40l2,1l-2,1l-2,-1z') + compareBoolean(function() { return path.unite(); }, + 'M24,38l6,3l-2,1l-6,-3z'); +}) + test('Selected edge-cases from @hari\'s boolean-test suite', function() { var g = PathItem.create('M316.6,266.4Q332.6,266.4,343.8,272.8Q355,279.2,362,289.8Q369,300.4,372.2,313.6Q375.4,326.8,375.4,340.4Q375.4,354.8,372,369.2Q368.6,383.6,361.4,395Q354.2,406.4,342.4,413.4Q330.6,420.4,313.8,420.4Q297,420.4,285.8,413.4Q274.6,406.4,267.8,395Q261,383.6,258.2,369.6Q255.4,355.6,255.4,341.6Q255.4,326.8,258.8,313.2Q262.2,299.6,269.6,289.2Q277,278.8,288.6,272.6Q300.2,266.4,316.6,266.4Z M315,236.4Q288.2,236.4,269.8,246.6Q251.4,256.8,240.2,272.6Q229,288.4,224.2,307.8Q219.4,327.2,219.4,345.6Q219.4,366.8,225.2,385.8Q231,404.8,242.6,419Q254.2,433.2,271.4,441.6Q288.6,450,311.8,450Q331.8,450,349.6,441Q367.4,432,376.2,412.8L377,412.8L377,426.4Q377,443.6,373.6,458Q370.2,472.4,362.6,482.6Q355,492.8,343.4,498.6Q331.8,504.4,315,504.4Q306.6,504.4,297.4,502.6Q288.2,500.8,280.4,496.8Q272.6,492.8,267.2,486.4Q261.8,480,261.4,470.8L227.4,470.8Q228.2,487.6,236.2,499.2Q244.2,510.8,256.4,518Q268.6,525.2,283.6,528.4Q298.6,531.6,313,531.6Q362.6,531.6,385.8,506.4Q409,481.2,409,430.4L409,241.2L377,241.2L377,270.8L376.6,270.8Q367.4,253.6,351,245Q334.6,236.4,315,236.4Z'); var u = PathItem.create('M253,316.74Q242.25,316.74,232.77,318.39Q218.77,320.83,208.21,328.52Q197.65,336.21,191.32,349.4Q185,362.6,183.59,382.95Q182.01,405.69,189.83,423.08Q197.64,440.46,216.05,452.56L215.99,453.36L183.27,451.09L181.06,483.01L387.37,497.31L389.72,463.39L273.2,455.32Q259.23,454.35,247.72,449.74Q236.21,445.14,227.96,436.95Q219.7,428.76,215.7,417.05Q211.7,405.35,212.78,389.78Q214.14,370.23,226.09,359.83Q236.68,350.61,252.94,350.61Q255.02,350.61,257.19,350.76L396.85,360.44L399.2,326.52L263.53,317.12Q258.12,316.74,253,316.74Z');