mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-01 10:48:38 -05:00
Merge remote-tracking branch 'origin/develop' into new-winding
; Conflicts: ; src/path/PathItem.Boolean.js
This commit is contained in:
commit
c9100a2b61
47 changed files with 584 additions and 414 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +1,2 @@
|
|||
/node_modules/
|
||||
/dist/
|
||||
/dist/*/
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
{
|
||||
"esversion": 5,
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"wsh": true,
|
||||
"evil": true,
|
||||
"trailing": false,
|
||||
"smarttabs": false,
|
||||
"sub": true,
|
||||
"supernew": true,
|
||||
"laxbreak": true,
|
||||
"eqeqeq": false,
|
||||
"eqnull": true,
|
||||
"loopfunc": true,
|
||||
"boss": true,
|
||||
"shadow": true
|
||||
"shadow": true,
|
||||
"funcscope": false,
|
||||
"latedef": "nofunc",
|
||||
"freeze": true
|
||||
}
|
||||
|
|
|
@ -38,6 +38,6 @@ script:
|
|||
- gulp jshint
|
||||
- gulp minify
|
||||
- gulp test
|
||||
- gulp dist
|
||||
- gulp zip
|
||||
after_script:
|
||||
- '[ "${TRAVIS_BRANCH}" = "develop" ] && [ "${TRAVIS_NODE_VERSION}" = "stable" ] && travis/deploy-prebuilt.sh'
|
||||
|
|
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -1,16 +1,38 @@
|
|||
# Change Log
|
||||
All notable changes to Paper.js shall be documented in this file, following
|
||||
common [CHANGELOG](http://keepachangelog.com/) conventions. As of `0.10.0`,
|
||||
Paper.js adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## `0.10.0` (Unreleased)
|
||||
## `0.10.3` (Unreleased)
|
||||
|
||||
### Changed
|
||||
- Loosely couple Node.js / Electron code to Canvas module, and treat its absence
|
||||
like a headless web worker context in the browser (#1103).
|
||||
|
||||
### Fixed
|
||||
- Prevent `Path#getStrokeBounds(matrix)` from accidentally modifying segments
|
||||
(#1102).
|
||||
- Compatibility with JSPM (#1104).
|
||||
|
||||
## `0.10.2`
|
||||
|
||||
### Fixed
|
||||
- Get published version to work correctly in Bower again.
|
||||
|
||||
## `0.10.1`
|
||||
|
||||
### Fixed
|
||||
- Correct a few issues with documentation and NPM publishing that slipped
|
||||
through in the `0.10.0` release.
|
||||
|
||||
## `0.10.0`
|
||||
|
||||
### Preamble
|
||||
|
||||
This is a huge release for Paper.js as we aim for a version `1.0.0` release
|
||||
later this year. There are many items in the changelog (and many more items not
|
||||
in the changelog) so here a high-level overview to frame the long list of
|
||||
changes:
|
||||
later this year. As of this version, all notable changes are documented in the
|
||||
change-log following common [CHANGELOG](http://keepachangelog.com/) conventions.
|
||||
Paper.js now also adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
There are many items in the changelog (and many more items not in the changelog)
|
||||
so here a high-level overview to frame the long list of changes:
|
||||
|
||||
- Boolean operations have been improved and overhauled for reliability and
|
||||
efficiency. These include the path functions to unite, intersect, subtract,
|
||||
|
@ -221,7 +243,7 @@ contribute to the code.
|
|||
invertible transformations (#558).
|
||||
- Scaling shadows now works correctly with browser- and view-zoom (#831).
|
||||
- `Path#arcTo()` correctly handles zero sizes.
|
||||
- `#importSVG()` handles onLoad and onError callbacks for string inputs that
|
||||
- `#importSVG()` handles `onLoad` and `onError` callbacks for string inputs that
|
||||
load external resources (#827).
|
||||
- `#importJSON()` and `#exportJSON()` now handle non-`Item` objects correctly
|
||||
(#392).
|
||||
|
@ -248,6 +270,8 @@ contribute to the code.
|
|||
rounding (#1045).
|
||||
- Improve reliability of fat-line clipping for curves that are very similar
|
||||
(#904).
|
||||
- Improve precision of `Numerical.solveQuadratic()` and
|
||||
`Numerical.solveCubic()` for edge-cases (#1085).
|
||||
|
||||
### Removed
|
||||
- Canvas attributes "resize" and "data-paper-resize" no longer cause paper to
|
||||
|
|
12
bower.json
12
bower.json
|
@ -14,14 +14,14 @@
|
|||
],
|
||||
"main": "dist/paper-full.js",
|
||||
"ignore": [
|
||||
"build",
|
||||
"components",
|
||||
"dist/paper-node.js",
|
||||
"projects",
|
||||
"node_modules",
|
||||
"package.json",
|
||||
"gulpfile.js",
|
||||
"gulp",
|
||||
"node_modules",
|
||||
"projects",
|
||||
"src",
|
||||
"test"
|
||||
"test",
|
||||
"travis"
|
||||
],
|
||||
"keywords": [
|
||||
"vector",
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"name": "paper",
|
||||
"version": "0.9.25",
|
||||
"description": "The Swiss Army Knife of Vector Graphics Scripting",
|
||||
"license": "MIT",
|
||||
"repo": "paperjs/paper.js",
|
||||
"main": [
|
||||
"dist/paper-full.js"
|
||||
],
|
||||
"scripts": [
|
||||
"dist/paper-full.js"
|
||||
],
|
||||
"files": [
|
||||
"AUTHORS.md",
|
||||
"LICENSE.txt",
|
||||
"README.md"
|
||||
],
|
||||
"keywords": [
|
||||
"vector",
|
||||
"graphic",
|
||||
"graphics",
|
||||
"2d",
|
||||
"geometry",
|
||||
"bezier",
|
||||
"curve",
|
||||
"curves",
|
||||
"path",
|
||||
"paths",
|
||||
"canvas",
|
||||
"svg",
|
||||
"paper",
|
||||
"paper.js"
|
||||
]
|
||||
}
|
1
dist/paper-core.js
vendored
Symbolic link
1
dist/paper-core.js
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../src/load.js
|
1
dist/paper-full.js
vendored
Symbolic link
1
dist/paper-full.js
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../src/load.js
|
|
@ -1 +1 @@
|
|||
Subproject commit 4a90c0501d3d0a1f5fec2363e9dce623d0758401
|
||||
Subproject commit b6f36edba33690cee908cadcb24515ec5be97b1d
|
|
@ -16,8 +16,7 @@ var gulp = require('gulp'),
|
|||
uncomment = require('gulp-uncomment'),
|
||||
whitespace = require('gulp-whitespace'),
|
||||
del = require('del'),
|
||||
extend = require('extend'),
|
||||
options = require('../utils/options.js')({ suffix: true });
|
||||
options = require('../utils/options.js');
|
||||
|
||||
// Options to be used in Prepro.js preprocessing through the global __options
|
||||
// object, merged in with the options required above.
|
||||
|
@ -48,10 +47,10 @@ buildNames.forEach(function(name) {
|
|||
evaluate: ['src/constants.js'],
|
||||
setup: function() {
|
||||
// Return objects to be defined in the preprocess-scope.
|
||||
// Note that this would be merge in with already existing
|
||||
// Note that this would be merged in with already existing
|
||||
// objects.
|
||||
return {
|
||||
__options: extend({}, options, buildOptions[name])
|
||||
__options: Object.assign({}, options, buildOptions[name])
|
||||
};
|
||||
}
|
||||
}))
|
||||
|
|
|
@ -15,7 +15,9 @@ var gulp = require('gulp'),
|
|||
merge = require('merge-stream'),
|
||||
zip = require('gulp-zip');
|
||||
|
||||
gulp.task('dist', ['minify', 'docs', 'clean:dist'], function() {
|
||||
gulp.task('dist', ['build', 'minify', 'docs']);
|
||||
|
||||
gulp.task('zip', ['clean:zip', 'dist'], function() {
|
||||
return merge(
|
||||
gulp.src([
|
||||
'dist/paper-full*.js',
|
||||
|
@ -31,7 +33,7 @@ gulp.task('dist', ['minify', 'docs', 'clean:dist'], function() {
|
|||
.pipe(gulp.dest('dist'));
|
||||
});
|
||||
|
||||
gulp.task('clean:dist', function() {
|
||||
gulp.task('clean:zip', function() {
|
||||
return del([
|
||||
'dist/paperjs.zip'
|
||||
]);
|
||||
|
|
|
@ -14,7 +14,7 @@ var gulp = require('gulp'),
|
|||
del = require('del'),
|
||||
rename = require('gulp-rename'),
|
||||
shell = require('gulp-shell'),
|
||||
options = require('../utils/options.js')({ suffix: true });
|
||||
options = require('../utils/options.js');
|
||||
|
||||
var docOptions = {
|
||||
local: 'docs', // Generates the offline docs
|
||||
|
@ -22,7 +22,7 @@ var docOptions = {
|
|||
};
|
||||
|
||||
gulp.task('docs', ['docs:local', 'build:full'], function() {
|
||||
gulp.src('dist/paper-full.js')
|
||||
return gulp.src('dist/paper-full.js')
|
||||
.pipe(rename({ basename: 'paper' }))
|
||||
.pipe(gulp.dest('dist/docs/assets/js/'));
|
||||
});
|
||||
|
|
|
@ -21,5 +21,5 @@ gulp.task('load', ['clean:load'], function() {
|
|||
});
|
||||
|
||||
gulp.task('clean:load', function() {
|
||||
return del([ 'dist/paper-full.js', 'dist/paper-core.js', 'dist/node/**' ]);
|
||||
return del([ 'dist/*.js', 'dist/node/**' ]);
|
||||
});
|
||||
|
|
|
@ -11,35 +11,57 @@
|
|||
*/
|
||||
|
||||
var gulp = require('gulp'),
|
||||
addSrc = require('gulp-add-src'),
|
||||
bump = require('gulp-bump'),
|
||||
git = require('gulp-git-streamed'),
|
||||
run = require('run-sequence'),
|
||||
shell = require('gulp-shell'),
|
||||
options = require('../utils/options.js')({ suffix: false });
|
||||
options = require('../utils/options.js');
|
||||
|
||||
gulp.task('publish', ['publish:bump', 'publish:release']);
|
||||
|
||||
gulp.task('publish:bump', function() {
|
||||
return gulp.src([ 'package.json', 'component.json' ])
|
||||
.pipe(bump({ version: options.version }))
|
||||
.pipe(gulp.dest('./'))
|
||||
.pipe(addSrc('src/options.js'))
|
||||
.pipe(git.add());
|
||||
});
|
||||
|
||||
gulp.task('publish:release', function() {
|
||||
gulp.task('publish', function() {
|
||||
if (options.branch !== 'develop') {
|
||||
throw new Error('Publishing is only allowed on the develop branch.');
|
||||
}
|
||||
return run(
|
||||
'publish:version',
|
||||
'publish:dist',
|
||||
'publish:commit',
|
||||
'publish:release',
|
||||
'publish:load'
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('publish:version', function() {
|
||||
// Reset the version value since we're executing this on the develop branch,
|
||||
// but we don't wan the published version suffixed with '-develop'.
|
||||
options.resetVersion();
|
||||
return gulp.src([ 'package.json' ])
|
||||
.pipe(bump({ version: options.version }))
|
||||
.pipe(gulp.dest('.'));
|
||||
});
|
||||
|
||||
gulp.task('publish:dist', ['dist']);
|
||||
|
||||
gulp.task('publish:commit', function() {
|
||||
var message = 'Release version ' + options.version;
|
||||
return gulp.src('.')
|
||||
.pipe(git.checkout('develop'))
|
||||
.pipe(git.add())
|
||||
.pipe(git.commit(message))
|
||||
.pipe(git.tag('v' + options.version, message))
|
||||
.pipe(git.tag('v' + options.version, message));
|
||||
});
|
||||
|
||||
gulp.task('publish:release', function() {
|
||||
return gulp.src('.')
|
||||
.pipe(git.checkout('master'))
|
||||
.pipe(git.merge('develop', { args: '-X theirs' }))
|
||||
.pipe(git.push('origin', 'master' ))
|
||||
.pipe(git.push('origin', 'develop' ))
|
||||
.pipe(git.push(null, null, { args: '--tags' } ))
|
||||
.pipe(shell('npm publish'))
|
||||
.pipe(git.checkout('develop'));
|
||||
.pipe(git.push('origin', ['master', 'develop'], { args: '--tags' }))
|
||||
.pipe(shell('npm publish'));
|
||||
});
|
||||
|
||||
gulp.task('publish:load', ['load'], function() {
|
||||
return gulp.src('dist')
|
||||
.pipe(git.checkout('develop'))
|
||||
.pipe(git.add())
|
||||
.pipe(git.commit('Switch back to load.js versions on develop branch.'))
|
||||
.pipe(git.push('origin', 'develop'));
|
||||
});
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
*/
|
||||
|
||||
var gulp = require('gulp'),
|
||||
qunits = require('gulp-qunits'),
|
||||
gutil = require('gulp-util'),
|
||||
qunits = require('gulp-qunits'),
|
||||
webserver = require('gulp-webserver');
|
||||
|
||||
gulp.task('test', ['test:phantom', 'test:node']);
|
||||
|
|
|
@ -14,7 +14,6 @@ var gulp = require('gulp'),
|
|||
path = require('path'),
|
||||
gutil = require('gulp-util');
|
||||
|
||||
|
||||
gulp.task('watch', function () {
|
||||
gulp.watch('src/**/*.js', ['jshint'])
|
||||
.on('change', function(event) {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
*/
|
||||
|
||||
var execSync = require('child_process').execSync,
|
||||
extend = require('extend'),
|
||||
// Require the __options object, so we have access to the version number and
|
||||
// make amendments, e.g. the release date.
|
||||
options = require('../../src/options.js');
|
||||
|
@ -20,19 +19,20 @@ function git(command) {
|
|||
return execSync('git ' + command).toString().trim();
|
||||
}
|
||||
|
||||
options.date = git('log -1 --pretty=format:%ad');
|
||||
options.branch = git('rev-parse --abbrev-ref HEAD');
|
||||
|
||||
// Get the date of the last commit from this branch for release date:
|
||||
var date = git('log -1 --pretty=format:%ad'),
|
||||
branch = git('rev-parse --abbrev-ref HEAD');
|
||||
var version = options.version,
|
||||
branch = options.branch;
|
||||
|
||||
extend(options, {
|
||||
date: date,
|
||||
branch: branch,
|
||||
// If we're not on the master branch, use the branch name as a suffix:
|
||||
suffix: branch === 'master' ? '' : '-' + branch
|
||||
});
|
||||
// If we're not on the master branch, use the branch name as a suffix:
|
||||
if (branch !== 'master')
|
||||
options.version += '-' + branch;
|
||||
|
||||
module.exports = function(opts) {
|
||||
return extend({}, options, opts && opts.suffix && {
|
||||
version: options.version + options.suffix
|
||||
});
|
||||
};
|
||||
// Allow the removal of the suffix again, as needed by the publish task.
|
||||
options.resetVersion = function() {
|
||||
options.version = version;
|
||||
}
|
||||
|
||||
module.exports = options;
|
||||
|
|
46
package.json
46
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "paper",
|
||||
"version": "0.9.25",
|
||||
"version": "0.10.2",
|
||||
"description": "The Swiss Army Knife of Vector Graphics Scripting",
|
||||
"license": "MIT",
|
||||
"homepage": "http://paperjs.org",
|
||||
|
@ -15,12 +15,19 @@
|
|||
],
|
||||
"main": "dist/paper-full.js",
|
||||
"scripts": {
|
||||
"lint": "jshint src",
|
||||
"prepublish": "gulp minify",
|
||||
"precommit": "gulp jshint",
|
||||
"prepush": "gulp test",
|
||||
"build": "gulp build",
|
||||
"dist": "gulp dist",
|
||||
"zip": "gulp zip",
|
||||
"docs": "gulp docs",
|
||||
"load": "gulp load",
|
||||
"jshint": "gulp jshint",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"files": [
|
||||
"AUTHORS.md",
|
||||
"CHANGELOG.md",
|
||||
"dist/",
|
||||
"examples/",
|
||||
"LICENSE.txt",
|
||||
|
@ -30,7 +37,7 @@
|
|||
"node": ">=4.0.0 <7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsdom": "^8.3.0",
|
||||
"jsdom": "^9.4.0",
|
||||
"source-map-support": "^0.4.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
@ -38,38 +45,39 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"acorn": "~0.5.0",
|
||||
"del": "^2.2.0",
|
||||
"extend": "^3.0.0",
|
||||
"gulp": "^3.9.0",
|
||||
"gulp-add-src": "^0.2.0",
|
||||
"gulp-bump": "^1.0.0",
|
||||
"del": "^2.2.1",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-bump": "^2.2.0",
|
||||
"gulp-cached": "^1.1.0",
|
||||
"gulp-git-streamed": "^1.0.0",
|
||||
"gulp-git-streamed": "^1.8.0",
|
||||
"gulp-jshint": "^2.0.0",
|
||||
"gulp-prepro": "^2.4.0",
|
||||
"gulp-qunits": "^2.0.1",
|
||||
"gulp-qunits": "^2.1.0",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-shell": "^0.5.2",
|
||||
"gulp-symlink": "^2.1.3",
|
||||
"gulp-uglify": "^1.5.1",
|
||||
"gulp-symlink": "^2.1.4",
|
||||
"gulp-uglify": "^1.5.4",
|
||||
"gulp-uncomment": "^0.3.0",
|
||||
"gulp-util": "^3.0.0",
|
||||
"gulp-util": "^3.0.7",
|
||||
"gulp-webserver": "^0.9.1",
|
||||
"gulp-whitespace": "^0.1.0",
|
||||
"gulp-zip": "^3.0.2",
|
||||
"jshint": "2.8.x",
|
||||
"gulp-zip": "^3.2.0",
|
||||
"husky": "^0.11.4",
|
||||
"jshint": "^2.9.2",
|
||||
"jshint-summary": "^0.4.0",
|
||||
"merge-stream": "^1.0.0",
|
||||
"prepro": "^2.4.0",
|
||||
"qunitjs": "^1.20.0",
|
||||
"qunitjs": "^1.23.0",
|
||||
"require-dir": "^0.3.0",
|
||||
"resemblejs": "^2.1.0",
|
||||
"stats.js": "0.0.14-master",
|
||||
"resemblejs": "^2.2.1",
|
||||
"run-sequence": "^1.2.2",
|
||||
"stats.js": "0.16.0",
|
||||
"straps": "^1.9.0"
|
||||
},
|
||||
"browser": {
|
||||
"canvas": false,
|
||||
"jsdom": false,
|
||||
"jsdom/lib/jsdom/living/generated/utils": false,
|
||||
"source-map-support": false,
|
||||
"./dist/node/window.js": false,
|
||||
"./dist/node/extend.js": false
|
||||
|
|
|
@ -80,10 +80,15 @@ var Emitter = {
|
|||
var handlers = this._callbacks && this._callbacks[type];
|
||||
if (!handlers)
|
||||
return false;
|
||||
var args = [].slice.call(arguments, 1);
|
||||
var args = [].slice.call(arguments, 1),
|
||||
// Set the current target to `this` if the event object defines
|
||||
// #target but not #currentTarget.
|
||||
setTarget = event && event.target && !event.currentTarget;
|
||||
// Create a clone of the handlers list so changes caused by on / off
|
||||
// won't throw us off track here:
|
||||
handlers = handlers.slice();
|
||||
if (setTarget)
|
||||
event.currentTarget = this;
|
||||
for (var i = 0, l = handlers.length; i < l; i++) {
|
||||
if (handlers[i].apply(this, args) === false) {
|
||||
// If the handler returns false, prevent the default behavior
|
||||
|
@ -94,6 +99,8 @@ var Emitter = {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (setTarget)
|
||||
delete event.currentTarget;
|
||||
return true;
|
||||
},
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* @name PaperScript
|
||||
* @namespace
|
||||
*/
|
||||
Base.exports.PaperScript = (function() {
|
||||
Base.exports.PaperScript = function() {
|
||||
// Locally turn of exports and define for inlined acorn.
|
||||
// Just declaring the local vars is enough, as they will be undefined.
|
||||
var exports, define,
|
||||
|
@ -347,7 +347,7 @@ Base.exports.PaperScript = (function() {
|
|||
}
|
||||
if (/^(inline|both)$/.test(sourceMaps)) {
|
||||
code += "\n//# sourceMappingURL=data:application/json;base64,"
|
||||
+ window.btoa(unescape(encodeURIComponent(
|
||||
+ self.btoa(unescape(encodeURIComponent(
|
||||
JSON.stringify(map))));
|
||||
}
|
||||
code += "\n//# sourceURL=" + (url || 'paperscript');
|
||||
|
@ -590,4 +590,4 @@ Base.exports.PaperScript = (function() {
|
|||
};
|
||||
// Pass on `this` as the binding object, so we can reference Acorn both in
|
||||
// development and in the built library.
|
||||
}).call(this);
|
||||
}.call(this);
|
||||
|
|
|
@ -45,7 +45,7 @@ var Key = new function() {
|
|||
keyMap = {}, // Map for currently pressed keys
|
||||
charMap = {}, // key -> char mappings for pressed keys
|
||||
metaFixMap, // Keys that will not receive keyup events due to Mac bug
|
||||
downKey; // The key from the keydown event, if it wasn't handled already
|
||||
downKey, // The key from the keydown event, if it wasn't handled already
|
||||
|
||||
// Use new Base() to convert into a Base object, for #toString()
|
||||
modifiers = new Base({
|
||||
|
|
|
@ -30,7 +30,7 @@ var MouseEvent = Event.extend(/** @lends MouseEvent# */{
|
|||
this.type = type;
|
||||
this.event = event;
|
||||
this.point = point;
|
||||
this._target = target;
|
||||
this.target = target;
|
||||
this.delta = delta;
|
||||
},
|
||||
|
||||
|
@ -51,19 +51,24 @@ var MouseEvent = Event.extend(/** @lends MouseEvent# */{
|
|||
* @type Point
|
||||
*/
|
||||
|
||||
// DOCS: document MouseEvent#target
|
||||
/**
|
||||
* The item that dispatched the event. It is different from
|
||||
* {@link #currentTarget} when the event handler is called during
|
||||
* the bubbling phase of the event.
|
||||
*
|
||||
* @name MouseEvent#target
|
||||
* @type Item
|
||||
*/
|
||||
getTarget: function() {
|
||||
// #_target may be a hitTest() function, in which case we need to
|
||||
// execute and override it the first time #target is requested.
|
||||
var target = this._target;
|
||||
if (typeof target === 'function')
|
||||
target = this._target = target();
|
||||
return target;
|
||||
},
|
||||
|
||||
/**
|
||||
* The current target for the event, as the event traverses the scene graph.
|
||||
* It always refers to the element the event handler has been attached to as
|
||||
* opposed to {@link #target} which identifies the element on
|
||||
* which the event occurred.
|
||||
*
|
||||
* @name MouseEvent#currentTarget
|
||||
* @type Item
|
||||
*/
|
||||
|
||||
// DOCS: document MouseEvent#delta
|
||||
/**
|
||||
|
@ -77,7 +82,7 @@ var MouseEvent = Event.extend(/** @lends MouseEvent# */{
|
|||
toString: function() {
|
||||
return "{ type: '" + this.type
|
||||
+ "', point: " + this.point
|
||||
+ ', target: ' + this.getTarget()
|
||||
+ ', target: ' + this.target
|
||||
+ (this.delta ? ', delta: ' + this.delta : '')
|
||||
+ ', modifiers: ' + this.getModifiers()
|
||||
+ ' }';
|
||||
|
|
|
@ -36,7 +36,7 @@ paper = new (PaperScope.inject(Base.exports, {
|
|||
// - PaperScript support in require() with sourceMaps
|
||||
// - exportFrames / exportImage on CanvasView
|
||||
if (paper.agent.node)
|
||||
require('./node/extend')(paper);
|
||||
require('./node/extend.js')(paper);
|
||||
|
||||
// https://github.com/umdjs/umd
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
|
|
13
src/init.js
13
src/init.js
|
@ -19,11 +19,12 @@
|
|||
/* global document:true, window:true */
|
||||
// Create a window variable valid in the paper.js scope, that points to the
|
||||
// native window in browsers and the emulated JSDom one in node.js
|
||||
// In workers, window is null (but self is defined), so we can use the validity
|
||||
// of the local window object to detect a worker-like context in the library.
|
||||
var window = self ? self.window : require('./node/window'),
|
||||
document = window && window.document;
|
||||
// Make sure 'self' always points to a window object, also on Node.js.
|
||||
// In workers, and on Node.js when no Canvas is present, `window` is null (but
|
||||
// `self` is defined), so we can use the validity of the local window object to
|
||||
// detect a worker-like context in the library.
|
||||
// Make sure `self` always points to a window object, also on Node.js.
|
||||
self = self || require('./node/window.js');
|
||||
// NOTE: We're not modifying the global `self` here. We receive its value passed
|
||||
// to the paper.js function scope, and this is the one that is modified here.
|
||||
self = self || window;
|
||||
var window = self.window,
|
||||
document = self.document;
|
||||
|
|
|
@ -1893,6 +1893,7 @@ new function() { // Injection scope for hit-test functions shared with project
|
|||
|| options.class && !(this instanceof options.class)),
|
||||
callback = options.match,
|
||||
that = this,
|
||||
bounds,
|
||||
res;
|
||||
|
||||
function match(hit) {
|
||||
|
@ -1913,7 +1914,7 @@ new function() { // Injection scope for hit-test functions shared with project
|
|||
if (checkSelf && (options.center || options.bounds) && this._parent) {
|
||||
// Don't get the transformed bounds, check against transformed
|
||||
// points instead
|
||||
var bounds = this.getInternalBounds();
|
||||
bounds = this.getInternalBounds();
|
||||
if (options.center) {
|
||||
res = checkBounds('center', 'Center');
|
||||
}
|
||||
|
|
|
@ -379,7 +379,7 @@ var Raster = Item.extend(/** @lends Raster# */{
|
|||
},
|
||||
|
||||
setSource: function(src) {
|
||||
var image = new window.Image(),
|
||||
var image = new self.Image(),
|
||||
crossOrigin = this._crossOrigin;
|
||||
if (crossOrigin)
|
||||
image.crossOrigin = crossOrigin;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
var Http = {
|
||||
request: function(options) {
|
||||
// Code borrowed from Coffee Script and extended:
|
||||
var xhr = new window.XMLHttpRequest();
|
||||
var xhr = new self.XMLHttpRequest();
|
||||
xhr.open((options.method || 'get').toUpperCase(), options.url,
|
||||
Base.pick(options.async, true));
|
||||
if (options.mimeType)
|
||||
|
|
|
@ -10,35 +10,41 @@
|
|||
* All rights reserved.
|
||||
*/
|
||||
|
||||
var Canvas = require('canvas'),
|
||||
idlUtils = require('jsdom/lib/jsdom/living/generated/utils');
|
||||
|
||||
// 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
|
||||
|
||||
module.exports = function(window) {
|
||||
var HTMLCanvasElement = window.HTMLCanvasElement;
|
||||
|
||||
function getImplementation(obj) {
|
||||
// Try implForWrapper() first, fall back on obj. This appears to be
|
||||
// necessary on v7.2.2, but not anymore once we can switch to 8.0.0
|
||||
var impl = idlUtils.implForWrapper(obj);
|
||||
return impl && impl._canvas ? impl : obj;
|
||||
var Canvas;
|
||||
try {
|
||||
Canvas = require('canvas');
|
||||
} catch(e) {
|
||||
// Remove `self.window`, so we still have the global `self` reference,
|
||||
// but no `window` object:
|
||||
// - On the browser, this corresponds to a worker context.
|
||||
// - On Node.js, it basically means the canvas is missing or not working
|
||||
// which can be treated the same way.
|
||||
delete window.window;
|
||||
console.info(
|
||||
'Unable to load Canvas module. Running in a headless context.');
|
||||
return;
|
||||
}
|
||||
|
||||
var idlUtils = require('jsdom/lib/jsdom/living/generated/utils'),
|
||||
HTMLCanvasElement = window.HTMLCanvasElement;
|
||||
|
||||
// Add fake HTMLCanvasElement#type property:
|
||||
Object.defineProperty(HTMLCanvasElement.prototype, 'type', {
|
||||
get: function() {
|
||||
var canvas = getImplementation(this)._canvas;
|
||||
var canvas = idlUtils.implForWrapper(this)._canvas;
|
||||
return canvas && canvas.type || 'image';
|
||||
},
|
||||
|
||||
set: function(type) {
|
||||
// Allow replacement of internal node-canvas, so we can switch to a
|
||||
// PDF canvas.
|
||||
var impl = getImplementation(this),
|
||||
var impl = idlUtils.implForWrapper(this),
|
||||
size = impl._canvas || impl;
|
||||
impl._canvas = new Canvas(size.width, size.height, type);
|
||||
impl._context = null;
|
||||
|
@ -49,7 +55,7 @@ module.exports = function(window) {
|
|||
['toBuffer', 'pngStream', 'createPNGStream', 'jpgStream', 'createJPGStream']
|
||||
.forEach(function(key) {
|
||||
HTMLCanvasElement.prototype[key] = function() {
|
||||
var canvas = getImplementation(this)._canvas;
|
||||
var canvas = idlUtils.implForWrapper(this)._canvas;
|
||||
return canvas[key].apply(canvas, arguments);
|
||||
};
|
||||
});
|
||||
|
|
|
@ -27,7 +27,7 @@ var document = jsdom.jsdom('<html><body></body></html>', {
|
|||
}),
|
||||
window = document.defaultView;
|
||||
|
||||
require('./canvas')(window);
|
||||
require('./canvas.js')(window);
|
||||
|
||||
// Define XMLSerializer and DOMParser shims, to emulate browser behavior.
|
||||
// Effort to bring this to jsdom: https://github.com/tmpvar/jsdom/issues/1368
|
||||
|
@ -52,25 +52,6 @@ XMLSerializer.prototype.serializeToString = function(node) {
|
|||
return text;
|
||||
};
|
||||
|
||||
function DOMParser() {
|
||||
}
|
||||
|
||||
DOMParser.prototype.parseFromString = function(string, contentType) {
|
||||
// Create a new document, since we're supposed to always return one.
|
||||
var doc = document.implementation.createHTMLDocument(''),
|
||||
body = doc.body,
|
||||
last;
|
||||
// Set the body's HTML, then change the DOM according the specs.
|
||||
body.innerHTML = string;
|
||||
// Remove all top-level children (<html><head/><body/></html>)
|
||||
while (last = doc.lastChild)
|
||||
doc.removeChild(last);
|
||||
// Insert the first child of the body at the top.
|
||||
doc.appendChild(body.firstChild);
|
||||
return doc;
|
||||
};
|
||||
|
||||
window.XMLSerializer = XMLSerializer;
|
||||
window.DOMParser = DOMParser;
|
||||
|
||||
module.exports = window;
|
||||
|
|
|
@ -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.9.25';
|
||||
var version = '0.10.2';
|
||||
// If this file is loaded in the browser, we're in load.js mode.
|
||||
var load = typeof window === 'object';
|
||||
|
||||
|
|
|
@ -123,4 +123,4 @@ var paper = function(self, undefined) {
|
|||
|
||||
/*#*/ include('export.js');
|
||||
return paper;
|
||||
}(typeof self === 'object' ? self : null);
|
||||
}.call(this, typeof self === 'object' ? self : null);
|
||||
|
|
|
@ -170,7 +170,32 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
|
|||
},
|
||||
|
||||
/**
|
||||
* The first Segment contained within the path.
|
||||
* Specifies whether the compound-path is fully closed, meaning all its
|
||||
* contained sub-paths are closed path.
|
||||
*
|
||||
* @bean
|
||||
* @type Boolean
|
||||
* @see Path#isClosed()
|
||||
*/
|
||||
isClosed: function() {
|
||||
var children = this._children;
|
||||
for (var i = 0, l = children.length; i < l; i++) {
|
||||
if (!children[i]._closed)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
setClosed: function(closed) {
|
||||
var children = this._children;
|
||||
for (var i = 0, l = children.length; i < l; i++) {
|
||||
children[i].setClosed(closed);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The first Segment contained within the compound-path, a short-cut to
|
||||
* calling {@link Path#getFirstSegment()} on {@link Item#getFirstChild()}.
|
||||
*
|
||||
* @bean
|
||||
* @type Segment
|
||||
|
@ -181,7 +206,8 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
|
|||
},
|
||||
|
||||
/**
|
||||
* The last Segment contained within the path.
|
||||
* The last Segment contained within the compound-path, a short-cut to
|
||||
* calling {@link Path#getLastSegment()} on {@link Item#getLastChild()}.
|
||||
*
|
||||
* @bean
|
||||
* @type Segment
|
||||
|
@ -207,7 +233,8 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
|
|||
},
|
||||
|
||||
/**
|
||||
* The first Curve contained within the path.
|
||||
* The first Curve contained within the compound-path, a short-cut to
|
||||
* calling {@link Path#getFirstCurve()} on {@link Item#getFirstChild()}.
|
||||
*
|
||||
* @bean
|
||||
* @type Curve
|
||||
|
@ -218,14 +245,15 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
|
|||
},
|
||||
|
||||
/**
|
||||
* The last Curve contained within the path.
|
||||
* The last Curve contained within the compound-path, a short-cut to
|
||||
* calling {@link Path#getLastCurve()} on {@link Item#getLastChild()}.
|
||||
*
|
||||
* @bean
|
||||
* @type Curve
|
||||
*/
|
||||
getLastCurve: function() {
|
||||
var last = this.getLastChild();
|
||||
return last && last.getFirstCurve();
|
||||
return last && last.getLastCurve();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -680,10 +680,16 @@ statics: /** @lends Curve */{
|
|||
c1 = v[coord + 2],
|
||||
c2 = v[coord + 4],
|
||||
p2 = v[coord + 6],
|
||||
c = 3 * (c1 - p1),
|
||||
b = 3 * (c2 - c1) - c,
|
||||
a = p2 - p1 - c - b;
|
||||
return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max);
|
||||
res = 0;
|
||||
// If val is outside the curve values, no solution is possible.
|
||||
if ( !(p1 < val && p2 < val && c1 < val && c2 < val ||
|
||||
p1 > val && p2 > val && c1 > val && c2 > val)) {
|
||||
var c = 3 * (c1 - p1),
|
||||
b = 3 * (c2 - c1) - c,
|
||||
a = p2 - p1 - c - b;
|
||||
res = Numerical.solveCubic(a, b, c, p1 - val, roots, min, max);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
getTimeOf: function(v, point) {
|
||||
|
@ -835,7 +841,6 @@ statics: /** @lends Curve */{
|
|||
* NOTE: padding is only used for Path.getBounds().
|
||||
*/
|
||||
_addBounds: function(v0, v1, v2, v3, coord, padding, min, max, roots) {
|
||||
padding /= 2; // strokePadding is in width, not radius
|
||||
// Code ported and further optimised from:
|
||||
// http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
|
||||
function add(value, padding) {
|
||||
|
@ -846,32 +851,49 @@ statics: /** @lends Curve */{
|
|||
if (right > max[coord])
|
||||
max[coord] = right;
|
||||
}
|
||||
// Calculate derivative of our bezier polynomial, divided by 3.
|
||||
// Doing so allows for simpler calculations of a, b, c and leads to the
|
||||
// same quadratic roots.
|
||||
var a = 3 * (v1 - v2) - v0 + v3,
|
||||
b = 2 * (v0 + v2) - 4 * v1,
|
||||
c = v1 - v0,
|
||||
count = Numerical.solveQuadratic(a, b, c, roots),
|
||||
// Add some tolerance for good roots, as t = 0, 1 are added
|
||||
// separately anyhow, and we don't want joins to be added with radii
|
||||
// in getStrokeBounds()
|
||||
tMin = /*#=*/Numerical.CURVETIME_EPSILON,
|
||||
tMax = 1 - tMin;
|
||||
// Only add strokeWidth to bounds for points which lie within 0 < t < 1
|
||||
// The corner cases for cap and join are handled in getStrokeBounds()
|
||||
add(v3, 0);
|
||||
for (var i = 0; i < count; i++) {
|
||||
var t = roots[i],
|
||||
u = 1 - t;
|
||||
// Test for good roots and only add to bounds if good.
|
||||
if (tMin < t && t < tMax)
|
||||
// Calculate bezier polynomial at t.
|
||||
add(u * u * u * v0
|
||||
+ 3 * u * u * t * v1
|
||||
+ 3 * u * t * t * v2
|
||||
+ t * t * t * v3,
|
||||
padding);
|
||||
|
||||
padding /= 2; // strokePadding is in width, not radius
|
||||
var minPad = min[coord] - padding,
|
||||
maxPad = max[coord] + padding;
|
||||
// Perform a rough bounds checking first: The curve can only extend the
|
||||
// current bounds if at least one value is outside the min-max range.
|
||||
if ( v0 < minPad || v1 < minPad || v2 < minPad || v3 < minPad ||
|
||||
v0 > maxPad || v1 > maxPad || v2 > maxPad || v3 > maxPad) {
|
||||
if (v1 < v0 != v1 < v3 && v2 < v0 != v2 < v3) {
|
||||
// If the values of a curve are sorted, the extrema are simply
|
||||
// the start and end point.
|
||||
add(v0, padding);
|
||||
add(v3, padding);
|
||||
} else {
|
||||
// Calculate derivative of our bezier polynomial, divided by 3.
|
||||
// Doing so allows for simpler calculations of a, b, c and leads
|
||||
// to the same quadratic roots.
|
||||
var a = 3 * (v1 - v2) - v0 + v3,
|
||||
b = 2 * (v0 + v2) - 4 * v1,
|
||||
c = v1 - v0,
|
||||
count = Numerical.solveQuadratic(a, b, c, roots),
|
||||
// Add some tolerance for good roots, as t = 0, 1 are added
|
||||
// separately anyhow, and we don't want joins to be added
|
||||
// with radii in getStrokeBounds()
|
||||
tMin = /*#=*/Numerical.CURVETIME_EPSILON,
|
||||
tMax = 1 - tMin;
|
||||
// Only add strokeWidth to bounds for points which lie within 0
|
||||
// < t < 1 The corner cases for cap and join are handled in
|
||||
// getStrokeBounds()
|
||||
add(v3, 0);
|
||||
for (var i = 0; i < count; i++) {
|
||||
var t = roots[i],
|
||||
u = 1 - t;
|
||||
// Test for good roots and only add to bounds if good.
|
||||
if (tMin < t && t < tMax)
|
||||
// Calculate bezier polynomial at t.
|
||||
add(u * u * u * v0
|
||||
+ 3 * u * u * t * v1
|
||||
+ 3 * u * t * t * v2
|
||||
+ t * t * t * v3,
|
||||
padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}}, Base.each(
|
||||
|
|
|
@ -1284,6 +1284,13 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
|
||||
// NOTE: Documentation is in PathItem#smooth()
|
||||
smooth: function(options) {
|
||||
var that = this,
|
||||
opts = options || {},
|
||||
type = opts.type || 'asymmetric',
|
||||
segments = this._segments,
|
||||
length = segments.length,
|
||||
closed = this._closed;
|
||||
|
||||
// Helper method to pick the right from / to indices.
|
||||
// Supports numbers and segment objects.
|
||||
// For numbers, the `to` index is exclusive, while for segments and
|
||||
|
@ -1312,15 +1319,10 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
: index < 0 ? index + length : index, length - 1);
|
||||
}
|
||||
|
||||
var that = this,
|
||||
opts = options || {},
|
||||
type = opts.type || 'asymmetric',
|
||||
segments = this._segments,
|
||||
length = segments.length,
|
||||
closed = this._closed,
|
||||
loop = closed && opts.from === undefined && opts.to === undefined,
|
||||
var loop = closed && opts.from === undefined && opts.to === undefined,
|
||||
from = getIndex(opts.from, 0),
|
||||
to = getIndex(opts.to, length - 1);
|
||||
|
||||
if (from > to) {
|
||||
if (closed) {
|
||||
from -= length;
|
||||
|
@ -2047,7 +2049,9 @@ new function() { // Scope for drawing
|
|||
// performance.
|
||||
|
||||
function drawHandles(ctx, segments, matrix, size) {
|
||||
var half = size / 2;
|
||||
var half = size / 2,
|
||||
coords = new Array(6),
|
||||
pX, pY;
|
||||
|
||||
function drawHandle(index) {
|
||||
var hX = coords[index],
|
||||
|
@ -2063,13 +2067,12 @@ new function() { // Scope for drawing
|
|||
}
|
||||
}
|
||||
|
||||
var coords = new Array(6);
|
||||
for (var i = 0, l = segments.length; i < l; i++) {
|
||||
var segment = segments[i];
|
||||
var segment = segments[i],
|
||||
selection = segment._selection;
|
||||
segment._transformCoordinates(matrix, coords);
|
||||
var selection = segment._selection,
|
||||
pX = coords[0],
|
||||
pY = coords[1];
|
||||
pX = coords[0];
|
||||
pY = coords[1];
|
||||
if (selection & /*#=*/SegmentSelection.HANDLE_IN)
|
||||
drawHandle(2);
|
||||
if (selection & /*#=*/SegmentSelection.HANDLE_OUT)
|
||||
|
@ -2699,22 +2702,19 @@ statics: {
|
|||
|
||||
_addBevelJoin: function(segment, join, radius, miterLimit, matrix,
|
||||
strokeMatrix, addPoint, isArea) {
|
||||
// Handles both 'bevel' and 'miter' joins, as they share a lot of code.
|
||||
// Handles both 'bevel' and 'miter' joins, as they share a lot of code,
|
||||
// using different matrices to transform segment points and stroke
|
||||
// vectors to support Style#strokeScaling.
|
||||
var curve2 = segment.getCurve(),
|
||||
curve1 = curve2.getPrevious(),
|
||||
point = curve2.getPointAtTime(0),
|
||||
normal1 = curve1.getNormalAtTime(1),
|
||||
normal2 = curve2.getNormalAtTime(0),
|
||||
step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius;
|
||||
normal1.setLength(step);
|
||||
normal2.setLength(step);
|
||||
// use different matrices to transform segment points and stroke vectors
|
||||
// to support Style#strokeScaling.
|
||||
if (matrix)
|
||||
matrix._transformPoint(point, point);
|
||||
if (strokeMatrix) {
|
||||
strokeMatrix._transformPoint(normal1, normal1);
|
||||
strokeMatrix._transformPoint(normal2, normal2);
|
||||
point = curve2.getPoint1().transform(matrix),
|
||||
normal1 = curve1.getNormalAtTime(1).multiply(radius)
|
||||
.transform(strokeMatrix),
|
||||
normal2 = curve2.getNormalAtTime(0).multiply(radius)
|
||||
.transform(strokeMatrix);
|
||||
if (normal1.getDirectedAngle(normal2) < 0) {
|
||||
normal1 = normal1.negate();
|
||||
normal2 = normal2.negate();
|
||||
}
|
||||
if (isArea) {
|
||||
addPoint(point);
|
||||
|
@ -2744,16 +2744,13 @@ statics: {
|
|||
_addSquareCap: function(segment, cap, radius, matrix, strokeMatrix,
|
||||
addPoint, isArea) {
|
||||
// Handles both 'square' and 'butt' caps, as they share a lot of code.
|
||||
// Calculate the corner points of butt and square caps
|
||||
var point = segment._point,
|
||||
// Calculate the corner points of butt and square caps, using different
|
||||
// matrices to transform segment points and stroke vectors to support
|
||||
// Style#strokeScaling.
|
||||
var point = segment._point.transform(matrix),
|
||||
loc = segment.getLocation(),
|
||||
normal = loc.getNormal().multiply(radius); // normal is normalized
|
||||
// use different matrices to transform segment points and stroke vectors
|
||||
// to support Style#strokeScaling.
|
||||
if (matrix)
|
||||
matrix._transformPoint(point, point);
|
||||
if (strokeMatrix)
|
||||
strokeMatrix._transformPoint(normal, normal);
|
||||
// NOTE: normal is normalized, so multiply instead of normalize.
|
||||
normal = loc.getNormal().multiply(radius).transform(strokeMatrix);
|
||||
if (isArea) {
|
||||
addPoint(point.subtract(normal));
|
||||
addPoint(point.add(normal));
|
||||
|
|
|
@ -47,10 +47,12 @@ PathItem.inject(new function() {
|
|||
* remove empty curves, #resolveCrossings() to resolve self-intersection
|
||||
* make sure all paths have correct winding direction.
|
||||
*/
|
||||
function preparePath(path, resolve) {
|
||||
function preparePath(path, closed) {
|
||||
var res = path.clone(false).reduce({ simplify: true })
|
||||
.transform(null, true, true);
|
||||
return resolve ? res.resolveCrossings() : res;
|
||||
if (closed)
|
||||
res.setClosed(true);
|
||||
return closed ? res.resolveCrossings() : res;
|
||||
}
|
||||
|
||||
function createResult(ctor, paths, reduce, path1, path2) {
|
||||
|
@ -74,8 +76,10 @@ PathItem.inject(new function() {
|
|||
// Add a simple boolean property to check for a given operation,
|
||||
// e.g. `if (operator.unite)`
|
||||
operator[operation] = true;
|
||||
// If path1 is open, delegate to computeOpenBoolean()
|
||||
if (!path1._children && !path1._closed)
|
||||
// If path1 is open, delegate to computeOpenBoolean().
|
||||
// NOTE: Do not access private _closed property here, since path1 may
|
||||
// be a CompoundPath.
|
||||
if (!path1.isClosed())
|
||||
return computeOpenBoolean(path1, path2, operator);
|
||||
// We do not modify the operands themselves, but create copies instead,
|
||||
// fas produced by the calls to preparePath().
|
||||
|
@ -139,11 +143,11 @@ PathItem.inject(new function() {
|
|||
|
||||
function computeOpenBoolean(path1, path2, operator) {
|
||||
// Only support subtract and intersect operations between an open
|
||||
// and a closed path. Assume that compound-paths are closed.
|
||||
// TODO: Should we complain about not supported operations?
|
||||
if (!path2 || !path2._children && !path2._closed
|
||||
|| !operator.subtract && !operator.intersect)
|
||||
return null;
|
||||
// and a closed path.
|
||||
if (!path2 || !operator.subtract && !operator.intersect) {
|
||||
throw new Error('Boolean operations on open paths only support ' +
|
||||
'subtraction and intersection with another path.');
|
||||
}
|
||||
var _path1 = preparePath(path1, false),
|
||||
_path2 = preparePath(path2, false),
|
||||
crossings = _path1.getCrossings(_path2),
|
||||
|
@ -612,7 +616,7 @@ PathItem.inject(new function() {
|
|||
// are bringing us back to the beginning, and are both valid,
|
||||
// meaning they are part of the boolean result.
|
||||
if (seg !== exclude && (isStart(seg) || isStart(nextSeg)
|
||||
|| !seg._visited && !nextSeg._visited
|
||||
|| nextSeg && !seg._visited && !nextSeg._visited
|
||||
// Self-intersections (!operator) don't need isValid() calls
|
||||
&& (!operator || isValid(seg) && (isValid(nextSeg)
|
||||
// If the next segment isn't valid, its intersection
|
||||
|
|
|
@ -99,8 +99,10 @@ var PathItem = Item.extend(/** @lends PathItem# */{
|
|||
coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g);
|
||||
var length = coords && coords.length;
|
||||
relative = command === lower;
|
||||
// Fix issues with z in the middle of SVG path data, not followed by
|
||||
// a m command, see #413:
|
||||
if (previous === 'z' && !/[mz]/.test(lower))
|
||||
this.moveTo(current = start);
|
||||
this.moveTo(current);
|
||||
switch (lower) {
|
||||
case 'm':
|
||||
case 'l':
|
||||
|
@ -170,6 +172,8 @@ var PathItem = Item.extend(/** @lends PathItem# */{
|
|||
// Merge first and last segment with Numerical.EPSILON tolerance
|
||||
// to address imprecisions in relative SVG data.
|
||||
this.closePath(/*#=*/Numerical.EPSILON);
|
||||
// Correctly handle relative m commands, see #1101:
|
||||
current = start;
|
||||
break;
|
||||
}
|
||||
previous = lower;
|
||||
|
@ -184,7 +188,7 @@ var PathItem = Item.extend(/** @lends PathItem# */{
|
|||
|
||||
_contains: function(point) {
|
||||
// NOTE: point is reverse transformed by _matrix, so we don't need to
|
||||
// apply here.
|
||||
// apply the matrix here.
|
||||
/*#*/ if (__options.nativeContains || !__options.booleanOperations) {
|
||||
// To compare with native canvas approach:
|
||||
var ctx = CanvasProvider.getContext(1, 1);
|
||||
|
|
|
@ -130,7 +130,7 @@ var Segment = Base.extend(/** @lends Segment# */{
|
|||
} else {
|
||||
point = arg0;
|
||||
}
|
||||
} else if (typeof arg0 === 'object') {
|
||||
} else if (arg0 == null || typeof arg0 === 'object') {
|
||||
// It doesn't matter if all of these arguments exist.
|
||||
// new SegmentPoint() produces creates points with (0, 0) otherwise.
|
||||
point = arg0;
|
||||
|
|
|
@ -171,7 +171,9 @@ var Style = Base.extend(new function() {
|
|||
var old = this._values[key];
|
||||
if (old !== value) {
|
||||
if (isColor) {
|
||||
if (old)
|
||||
// The old value may be a native string or other color
|
||||
// description that wasn't coerced to a color object yet
|
||||
if (old && old._owner !== undefined)
|
||||
old._owner = undefined;
|
||||
if (value && value.constructor === Color) {
|
||||
// Clone color if it already has an owner.
|
||||
|
|
|
@ -369,7 +369,7 @@ new function() {
|
|||
definitions = null;
|
||||
}
|
||||
return options.asString
|
||||
? new window.XMLSerializer().serializeToString(svg)
|
||||
? new self.XMLSerializer().serializeToString(svg)
|
||||
: svg;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@ new function() {
|
|||
// objects, dealing with baseVal, and item lists.
|
||||
// index is option, and if passed, causes a lookup in a list.
|
||||
|
||||
var rootSize;
|
||||
var definitions = {},
|
||||
rootSize;
|
||||
|
||||
function getValue(node, name, isString, allowNull, allowPercent) {
|
||||
// Interpret value as number. Never return NaN, but 0 instead.
|
||||
|
@ -547,16 +548,17 @@ new function() {
|
|||
return item;
|
||||
}
|
||||
|
||||
var definitions = {};
|
||||
function getDefinition(value) {
|
||||
// When url() comes from a style property, '#'' seems to be missing on
|
||||
// WebKit. We also get variations of quotes or no quotes, single or
|
||||
// double, so handle it all with one regular expression:
|
||||
var match = value && value.match(/\((?:["'#]*)([^"')]+)/),
|
||||
res = match && definitions[match[1]
|
||||
// This is required by Firefox, which can produce absolute urls
|
||||
// for local gradients, see #1001:
|
||||
.replace(window.location.href.split('#')[0] + '#', '')];
|
||||
name = match && match[1],
|
||||
res = name && definitions[window
|
||||
// This is required by Firefox, which can produce absolute
|
||||
// urls for local gradients, see #1001:
|
||||
? name.replace(window.location.href.split('#')[0] + '#', '')
|
||||
: name];
|
||||
// Patch in support for SVG's gradientUnits="objectBoundingBox" through
|
||||
// Color#_scaleToBounds
|
||||
if (res && res._scaleToBounds) {
|
||||
|
@ -659,7 +661,7 @@ new function() {
|
|||
|
||||
function onLoad(svg) {
|
||||
try {
|
||||
var node = typeof svg === 'object' ? svg : new window.DOMParser()
|
||||
var node = typeof svg === 'object' ? svg : new self.DOMParser()
|
||||
.parseFromString(svg, 'image/svg+xml');
|
||||
if (!node.nodeName) {
|
||||
node = null;
|
||||
|
|
|
@ -60,6 +60,11 @@ var Numerical = new function() {
|
|||
var abs = Math.abs,
|
||||
sqrt = Math.sqrt,
|
||||
pow = Math.pow,
|
||||
// Fallback to polyfill:
|
||||
log2 = Math.log2 || function(x) {
|
||||
return Math.log(x) * Math.LOG2E;
|
||||
},
|
||||
// Constants
|
||||
EPSILON = 1e-12,
|
||||
MACHINE_EPSILON = 1.12e-16;
|
||||
|
||||
|
@ -67,6 +72,44 @@ var Numerical = new function() {
|
|||
return value < min ? min : value > max ? max : value;
|
||||
}
|
||||
|
||||
function getDiscriminant(a, b, c) {
|
||||
// Ported from @hkrish's polysolve.c
|
||||
function split(v) {
|
||||
var x = v * 134217729,
|
||||
y = v - x,
|
||||
hi = y + x, // Don't optimize y away!
|
||||
lo = v - hi;
|
||||
return [hi, lo];
|
||||
}
|
||||
|
||||
var D = b * b - a * c,
|
||||
E = b * b + a * c;
|
||||
if (abs(D) * 3 < E) {
|
||||
var ad = split(a),
|
||||
bd = split(b),
|
||||
cd = split(c),
|
||||
p = b * b,
|
||||
dp = (bd[0] * bd[0] - p + 2 * bd[0] * bd[1]) + bd[1] * bd[1],
|
||||
q = a * c,
|
||||
dq = (ad[0] * cd[0] - q + ad[0] * cd[1] + ad[1] * cd[0])
|
||||
+ ad[1] * cd[1];
|
||||
D = (p - q) + (dp - dq); // Don’t omit parentheses!
|
||||
}
|
||||
return D;
|
||||
}
|
||||
|
||||
function getNormalizationFactor() {
|
||||
// Normalize coefficients à la Jenkins & Traub's RPOLY.
|
||||
// Normalization is done by scaling coefficients with a power of 2, so
|
||||
// that all the bits in the mantissa remain unchanged.
|
||||
// Use the infinity norm (max(sum(abs(a)…)) to determine the appropriate
|
||||
// scale factor. See @hkrish in #1087#issuecomment-231526156
|
||||
var norm = Math.max.apply(Math, arguments);
|
||||
return norm && (norm < 1e-8 || norm > 1e8)
|
||||
? pow(2, -Math.round(log2(norm)))
|
||||
: 0;
|
||||
}
|
||||
|
||||
return /** @lends Numerical */{
|
||||
TOLERANCE: 1e-6,
|
||||
/**
|
||||
|
@ -202,6 +245,8 @@ var Numerical = new function() {
|
|||
* Kahan W. - "To Solve a Real Cubic Equation"
|
||||
* http://www.cs.berkeley.edu/~wkahan/Math128/Cubic.pdf
|
||||
* Blinn J. - "How to solve a Quadratic Equation"
|
||||
* Harikrishnan G.
|
||||
* https://gist.github.com/hkrish/9e0de1f121971ee0fbab281f5c986de9
|
||||
*
|
||||
* @param {Number} a the quadratic term
|
||||
* @param {Number} b the linear term
|
||||
|
@ -215,58 +260,52 @@ var Numerical = new function() {
|
|||
* @author Harikrishnan Gopalakrishnan <hari.exeption@gmail.com>
|
||||
*/
|
||||
solveQuadratic: function(a, b, c, roots, min, max) {
|
||||
var count = 0,
|
||||
eMin = min - EPSILON,
|
||||
eMax = max + EPSILON,
|
||||
x1, x2 = Infinity,
|
||||
B = b,
|
||||
D;
|
||||
// a, b, c are expected to be the coefficients of the equation:
|
||||
// Ax² - 2Bx + C == 0, so we take b = -B/2:
|
||||
b /= -2;
|
||||
D = b * b - a * c; // Discriminant
|
||||
// If the discriminant is very small, we can try to pre-condition
|
||||
// the coefficients, so that we may get better accuracy
|
||||
if (D !== 0 && abs(D) < MACHINE_EPSILON) {
|
||||
// If the geometric mean of the coefficients is small enough
|
||||
var gmC = pow(abs(a * b * c), 1 / 3);
|
||||
if (gmC < 1e-8) {
|
||||
// We multiply with a factor to normalize the coefficients.
|
||||
// The factor is just the nearest exponent of 10, big enough
|
||||
// to raise all the coefficients to nearly [-1, +1] range.
|
||||
var mult = gmC === 0 ? 0 : pow(10,
|
||||
abs(Math.floor(Math.log(gmC) * Math.LOG10E)));
|
||||
a *= mult;
|
||||
b *= mult;
|
||||
c *= mult;
|
||||
// Recalculate the discriminant
|
||||
D = b * b - a * c;
|
||||
}
|
||||
}
|
||||
var x1, x2 = Infinity;
|
||||
if (abs(a) < EPSILON) {
|
||||
// This could just be a linear equation
|
||||
if (abs(B) < EPSILON)
|
||||
if (abs(b) < EPSILON)
|
||||
return abs(c) < EPSILON ? -1 : 0;
|
||||
x1 = -c / B;
|
||||
} else if (D >= -MACHINE_EPSILON) { // No real roots if D < 0
|
||||
var Q = D < 0 ? 0 : sqrt(D),
|
||||
R = b + (b < 0 ? -Q : Q);
|
||||
// Try to minimize floating point noise.
|
||||
if (R === 0) {
|
||||
x1 = c / a;
|
||||
x2 = -x1;
|
||||
} else {
|
||||
x1 = R / a;
|
||||
x2 = c / R;
|
||||
x1 = -c / b;
|
||||
} else {
|
||||
// a, b, c are expected to be the coefficients of the equation:
|
||||
// Ax² - 2Bx + C == 0, so we take b = -b/2:
|
||||
b *= -0.5;
|
||||
var D = getDiscriminant(a, b, c);
|
||||
// If the discriminant is very small, we can try to normalize
|
||||
// the coefficients, so that we may get better accuracy.
|
||||
if (D && abs(D) < MACHINE_EPSILON) {
|
||||
var f = getNormalizationFactor(abs(a), abs(b), abs(c));
|
||||
if (f) {
|
||||
a *= f;
|
||||
b *= f;
|
||||
c *= f;
|
||||
D = getDiscriminant(a, b, c);
|
||||
}
|
||||
}
|
||||
if (D >= -MACHINE_EPSILON) { // No real roots if D < 0
|
||||
var Q = D < 0 ? 0 : sqrt(D),
|
||||
R = b + (b < 0 ? -Q : Q);
|
||||
// Try to minimize floating point noise.
|
||||
if (R === 0) {
|
||||
x1 = c / a;
|
||||
x2 = -x1;
|
||||
} else {
|
||||
x1 = R / a;
|
||||
x2 = c / R;
|
||||
}
|
||||
}
|
||||
}
|
||||
var count = 0,
|
||||
boundless = min == null,
|
||||
minB = min - EPSILON,
|
||||
maxB = max + EPSILON;
|
||||
// We need to include EPSILON in the comparisons with min / max,
|
||||
// as some solutions are ever so lightly out of bounds.
|
||||
if (isFinite(x1) && (min == null || x1 > eMin && x1 < eMax))
|
||||
roots[count++] = min == null ? x1 : clamp(x1, min, max);
|
||||
if (isFinite(x1) && (boundless || x1 > minB && x1 < maxB))
|
||||
roots[count++] = boundless ? x1 : clamp(x1, min, max);
|
||||
if (x2 !== x1
|
||||
&& isFinite(x2) && (min == null || x2 > eMin && x2 < eMax))
|
||||
roots[count++] = min == null ? x2 : clamp(x2, min, max);
|
||||
&& isFinite(x2) && (boundless || x2 > minB && x2 < maxB))
|
||||
roots[count++] = boundless ? x2 : clamp(x2, min, max);
|
||||
return count;
|
||||
},
|
||||
|
||||
|
@ -283,6 +322,8 @@ var Numerical = new function() {
|
|||
* References:
|
||||
* Kahan W. - "To Solve a Real Cubic Equation"
|
||||
* http://www.cs.berkeley.edu/~wkahan/Math128/Cubic.pdf
|
||||
* Harikrishnan G.
|
||||
* https://gist.github.com/hkrish/9e0de1f121971ee0fbab281f5c986de9
|
||||
*
|
||||
* W. Kahan's paper contains inferences on accuracy of cubic
|
||||
* zero-finding methods. Also testing methods for robustness.
|
||||
|
@ -300,8 +341,25 @@ var Numerical = new function() {
|
|||
* @author Harikrishnan Gopalakrishnan <hari.exeption@gmail.com>
|
||||
*/
|
||||
solveCubic: function(a, b, c, d, roots, min, max) {
|
||||
var count = 0,
|
||||
x, b1, c2;
|
||||
var f = getNormalizationFactor(abs(a), abs(b), abs(c), abs(d)),
|
||||
x, b1, c2, qd, q;
|
||||
if (f) {
|
||||
a *= f;
|
||||
b *= f;
|
||||
c *= f;
|
||||
d *= f;
|
||||
}
|
||||
|
||||
function evaluate(x0) {
|
||||
x = x0;
|
||||
// Evaluate q, q', b1 and c2 at x
|
||||
var tmp = a * x;
|
||||
b1 = tmp + b;
|
||||
c2 = b1 * x + c;
|
||||
qd = (tmp + b1) * x + c2;
|
||||
q = c2 * x + d;
|
||||
}
|
||||
|
||||
// If a or d is zero, we only need to solve a quadratic, so we set
|
||||
// the coefficients appropriately.
|
||||
if (abs(a) < EPSILON) {
|
||||
|
@ -314,38 +372,24 @@ var Numerical = new function() {
|
|||
c2 = c;
|
||||
x = 0;
|
||||
} else {
|
||||
var ec = 1 + MACHINE_EPSILON, // 1.000...002
|
||||
x0, q, qd, t, r, s, tmp;
|
||||
// Here onwards we iterate for the leftmost root. Proceed to
|
||||
// deflate the cubic into a quadratic (as a side effect to the
|
||||
// iteration) and solve the quadratic.
|
||||
x = -(b / a) / 3;
|
||||
// Evaluate q, q', b1 and c2 at x
|
||||
tmp = a * x;
|
||||
b1 = tmp + b;
|
||||
c2 = b1 * x + c;
|
||||
qd = (tmp + b1) * x + c2;
|
||||
q = c2 * x + d;
|
||||
evaluate(-(b / a) / 3);
|
||||
// Get a good initial approximation.
|
||||
t = q / a;
|
||||
r = pow(abs(t), 1/3);
|
||||
s = t < 0 ? -1 : 1;
|
||||
t = -qd / a;
|
||||
// See Kahan's notes on why 1.324718*... works.
|
||||
r = t > 0 ? 1.3247179572 * Math.max(r, sqrt(t)) : r;
|
||||
x0 = x - s * r;
|
||||
var t = q / a,
|
||||
r = pow(abs(t), 1/3),
|
||||
s = t < 0 ? -1 : 1,
|
||||
td = -qd / a,
|
||||
// See Kahan's notes on why 1.324718*... works.
|
||||
rd = td > 0 ? 1.324717957244746 * Math.max(r, sqrt(td)) : r,
|
||||
x0 = x - s * rd;
|
||||
if (x0 !== x) {
|
||||
do {
|
||||
x = x0;
|
||||
// Evaluate q, q', b1 and c2 at x
|
||||
tmp = a * x;
|
||||
b1 = tmp + b;
|
||||
c2 = b1 * x + c;
|
||||
qd = (tmp + b1) * x + c2;
|
||||
q = c2 * x + d;
|
||||
// Newton's. Divide by ec to avoid x0 crossing over a
|
||||
// root.
|
||||
x0 = qd === 0 ? x : x - q / qd / ec;
|
||||
evaluate(x0);
|
||||
// Newton's. Divide by 1 + MACHINE_EPSILON (1.000...002)
|
||||
// to avoid x0 crossing over a root.
|
||||
x0 = qd === 0 ? x : x - q / qd / (1 + MACHINE_EPSILON);
|
||||
} while (s * x0 > s * x);
|
||||
// Adjust the coefficients for the quadratic.
|
||||
if (abs(a) * x * x > abs(d / x)) {
|
||||
|
@ -355,10 +399,12 @@ var Numerical = new function() {
|
|||
}
|
||||
}
|
||||
// The cubic has been deflated to a quadratic.
|
||||
var count = Numerical.solveQuadratic(a, b1, c2, roots, min, max);
|
||||
if (isFinite(x) && (count === 0 || x !== roots[count - 1])
|
||||
&& (min == null || x > min - EPSILON && x < max + EPSILON))
|
||||
roots[count++] = min == null ? x : clamp(x, min, max);
|
||||
var count = Numerical.solveQuadratic(a, b1, c2, roots, min, max),
|
||||
boundless = min == null;
|
||||
if (isFinite(x) && (count === 0
|
||||
|| count > 0 && x !== roots[0] && x !== roots[1])
|
||||
&& (boundless || x > min - EPSILON && x < max + EPSILON))
|
||||
roots[count++] = boundless ? x : clamp(x, min, max);
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1152,7 +1152,18 @@ new function() { // Injection scope for event handling on the browser
|
|||
fallbacks = {
|
||||
doubleclick: 'click',
|
||||
mousedrag: 'mousemove'
|
||||
};
|
||||
},
|
||||
// Various variables required by #_handleMouseEvent()
|
||||
wasInView = false,
|
||||
overView,
|
||||
downPoint,
|
||||
lastPoint,
|
||||
downItem,
|
||||
overItem,
|
||||
dragItem,
|
||||
clickItem,
|
||||
clickTime,
|
||||
dblClick;
|
||||
|
||||
// Returns true if event was prevented, false otherwise.
|
||||
function emitMouseEvent(obj, target, type, event, point, prevPoint,
|
||||
|
@ -1196,8 +1207,7 @@ new function() { // Injection scope for event handling on the browser
|
|||
}
|
||||
|
||||
// Returns true if event was stopped, false otherwise.
|
||||
function emitMouseEvents(view, hitItem, hitTest, type, event, point,
|
||||
prevPoint) {
|
||||
function emitMouseEvents(view, hitItem, type, event, point, prevPoint) {
|
||||
// Before handling events, process removeOn() calls for cleanup.
|
||||
// NOTE: As soon as there is one event handler receiving mousedrag
|
||||
// events, non of the removeOnMove() items will be removed while the
|
||||
|
@ -1219,9 +1229,7 @@ new function() { // Injection scope for event handling on the browser
|
|||
&& emitMouseEvent(hitItem, null, fallbacks[type] || type, event,
|
||||
point, prevPoint, dragItem)
|
||||
// Lastly handle the mouse events on the view, if we're still here.
|
||||
// Choose from the potential targets in the right sequence, with the
|
||||
// hitTest() function as the fall-back getter for MouseEvent#target.
|
||||
|| emitMouseEvent(view, dragItem || hitItem || hitTest, type, event,
|
||||
|| emitMouseEvent(view, dragItem || hitItem || view, type, event,
|
||||
point, prevPoint));
|
||||
}
|
||||
|
||||
|
@ -1251,20 +1259,6 @@ new function() { // Injection scope for event handling on the browser
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Various variables required by #_handleMouseEvent()
|
||||
*/
|
||||
var downPoint,
|
||||
lastPoint,
|
||||
downItem,
|
||||
overItem,
|
||||
dragItem,
|
||||
clickItem,
|
||||
clickTime,
|
||||
dblClick,
|
||||
overView,
|
||||
wasInView = false;
|
||||
|
||||
return {
|
||||
_viewEvents: viewEvents,
|
||||
|
||||
|
@ -1299,7 +1293,12 @@ new function() { // Injection scope for event handling on the browser
|
|||
// Run the hit-test on items first, but only if we're required to do
|
||||
// so for this given mouse event, see hitItems, #_countItemEvent():
|
||||
var inView = this.getBounds().contains(point),
|
||||
hitItem,
|
||||
hit = hitItems && inView && view._project.hitTest(point, {
|
||||
tolerance: 0,
|
||||
fill: true,
|
||||
stroke: true
|
||||
}),
|
||||
hitItem = hit && hit.item || null,
|
||||
// Keep track if view event should be handled, so we can use it
|
||||
// to decide if tool._handleMouseEvent() shall be called after.
|
||||
handle = false,
|
||||
|
@ -1308,28 +1307,6 @@ new function() { // Injection scope for event handling on the browser
|
|||
// mouse event types.
|
||||
mouse[type.substr(5)] = true;
|
||||
|
||||
// Provide a hit-test function that makes sure to only perform the
|
||||
// hit-test once, and only when it's actually required. This method
|
||||
// is passed to emitMouseEvents() and as target to emitMouseEvent(),
|
||||
// as the fall-back getter for MouseEvent#target.
|
||||
function hitTest() {
|
||||
//
|
||||
if (hitItem === undefined) {
|
||||
var hit = inView && view._project.hitTest(point, {
|
||||
tolerance: 0,
|
||||
fill: true,
|
||||
stroke: true
|
||||
});
|
||||
hitItem = hit && hit.item || null;
|
||||
}
|
||||
// Return the target with view as the fall-back, as expected by
|
||||
// MouseEvent#target.
|
||||
return hitItem || view;
|
||||
}
|
||||
|
||||
// Execute hitTest right away if we have events relying on hitItem.
|
||||
if (hitItems)
|
||||
hitTest();
|
||||
// Handle mouseenter / leave between items and views first.
|
||||
if (hitItems && hitItem !== overItem) {
|
||||
if (overItem) {
|
||||
|
@ -1355,9 +1332,8 @@ new function() { // Injection scope for event handling on the browser
|
|||
if ((inView || mouse.drag) && !point.equals(lastPoint)) {
|
||||
// Handle mousemove even if this is not actually a mousemove
|
||||
// event but the mouse has moved since the last event.
|
||||
emitMouseEvents(this, hitItem, hitTest,
|
||||
nativeMove ? type : 'mousemove', event,
|
||||
point, lastPoint);
|
||||
emitMouseEvents(this, hitItem, nativeMove ? type : 'mousemove',
|
||||
event, point, lastPoint);
|
||||
handle = true;
|
||||
}
|
||||
wasInView = inView;
|
||||
|
@ -1365,8 +1341,7 @@ new function() { // Injection scope for event handling on the browser
|
|||
// We emit mousedown only when in the view, and mouseup regardless,
|
||||
// as long as the mousedown event was inside.
|
||||
if (mouse.down && inView || mouse.up && downPoint) {
|
||||
emitMouseEvents(this, hitItem, hitTest, type, event,
|
||||
point, downPoint);
|
||||
emitMouseEvents(this, hitItem, type, event, point, downPoint);
|
||||
if (mouse.down) {
|
||||
// See if we're clicking again on the same item, within the
|
||||
// double-click time. Firefox uses 300ms as the max time
|
||||
|
@ -1383,9 +1358,8 @@ new function() { // Injection scope for event handling on the browser
|
|||
// not the view.
|
||||
if (!prevented && hitItem === downItem) {
|
||||
clickTime = Date.now();
|
||||
emitMouseEvents(this, hitItem, hitTest,
|
||||
dblClick ? 'doubleclick' : 'click', event,
|
||||
point, downPoint);
|
||||
emitMouseEvents(this, hitItem, dblClick ? 'doubleclick'
|
||||
: 'click', event, point, downPoint);
|
||||
dblClick = false;
|
||||
}
|
||||
downItem = dragItem = null;
|
||||
|
|
45
test/tests/Numerical.js
Normal file
45
test/tests/Numerical.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
QUnit.module('Numerical');
|
||||
|
||||
test('Numerical.solveQuadratic()', function() {
|
||||
function solve(s) {
|
||||
var roots = [],
|
||||
count = Numerical.solveQuadratic(s, 0, -s, roots);
|
||||
return roots;
|
||||
}
|
||||
|
||||
var expected = [1, -1];
|
||||
|
||||
equals(solve(1), expected,
|
||||
'Numerical.solveQuadratic().');
|
||||
equals(solve(Numerical.EPSILON), expected,
|
||||
'Numerical.solveQuadratic() with an identical set of' +
|
||||
'coefficients at different scale.');
|
||||
});
|
||||
|
||||
test('Numerical.solveCubic()', function() {
|
||||
function solve(s) {
|
||||
var roots = [],
|
||||
count = Numerical.solveCubic(0.5 * s, -s, -s, -s, roots);
|
||||
return roots;
|
||||
}
|
||||
|
||||
var expected = [2.919639565839418];
|
||||
|
||||
equals(solve(1), expected,
|
||||
'Numerical.solveCubic().');
|
||||
equals(solve(Numerical.EPSILON), expected,
|
||||
'Numerical.solveCubic() with an identical set of' +
|
||||
'coefficients at different scale.');
|
||||
});
|
|
@ -17,7 +17,7 @@ function testPoint(item, point, inside, message) {
|
|||
+ ' should be ' + (inside ? 'inside' : 'outside') + '.'));
|
||||
}
|
||||
|
||||
test('Path#contains() (Regular Polygon)', function() {
|
||||
test('Path#contains() (regular polygon: #208)', function() {
|
||||
var path = new Path.RegularPolygon([0, 0], 6, 20);
|
||||
|
||||
testPoint(path, new Point(0, -20), true);
|
||||
|
@ -39,7 +39,7 @@ test('Path#contains() (Regular Polygon)', function() {
|
|||
testPoint(path, new Point(10, 20), false);
|
||||
});
|
||||
|
||||
test('Path#contains() (Circle Contours)', function() {
|
||||
test('Path#contains() (circle contours)', function() {
|
||||
var path = new Path.Circle({
|
||||
center: [100, 100],
|
||||
radius: 50,
|
||||
|
@ -56,7 +56,7 @@ test('Path#contains() (Circle Contours)', function() {
|
|||
testPoint(path, path.bounds.bottomRight, false);
|
||||
});
|
||||
|
||||
test('Path#contains() (Transformed Circle Contours)', function() {
|
||||
test('Path#contains() (transformed circle contours)', function() {
|
||||
var path = new Path.Circle({
|
||||
center: [200, 200],
|
||||
radius: 50,
|
||||
|
@ -74,7 +74,7 @@ test('Path#contains() (Transformed Circle Contours)', function() {
|
|||
testPoint(path, path.bounds.bottomRight, false);
|
||||
});
|
||||
|
||||
test('Path#contains() (Round Rectangle)', function() {
|
||||
test('Path#contains() (round rectangle: #227)', function() {
|
||||
var rectangle = new Rectangle({
|
||||
point: new Point(0, 0),
|
||||
size: new Size(200, 40)
|
||||
|
@ -83,14 +83,14 @@ test('Path#contains() (Round Rectangle)', function() {
|
|||
testPoint(path, new Point(100, 20), true);
|
||||
});
|
||||
|
||||
test('Path#contains() (Open Circle)', function() {
|
||||
test('Path#contains() (open circle)', function() {
|
||||
var path = new Path.Circle([100, 100], 100);
|
||||
path.closed = false;
|
||||
path.fillColor = '#ff0000';
|
||||
testPoint(path, new Point(40, 160), false);
|
||||
});
|
||||
|
||||
test('CompoundPath#contains() (Donut)', function() {
|
||||
test('CompoundPath#contains() (donut)', function() {
|
||||
var path = new CompoundPath([
|
||||
new Path.Circle([0, 0], 50),
|
||||
new Path.Circle([0, 0], 25)
|
||||
|
@ -163,7 +163,7 @@ test('Shape#contains()', function() {
|
|||
testPoint(shape, new Point(1.1, 0).multiply(half), false);
|
||||
});
|
||||
|
||||
test('Path#contains() (Rectangle Contours)', function() {
|
||||
test('Path#contains() (rectangle contours)', function() {
|
||||
var path = new Path.Rectangle(new Point(100, 100), [200, 200]),
|
||||
curves = path.getCurves();
|
||||
|
||||
|
@ -174,7 +174,7 @@ test('Path#contains() (Rectangle Contours)', function() {
|
|||
});
|
||||
|
||||
|
||||
test('Path#contains() (Rotated Rectangle Contours)', function() {
|
||||
test('Path#contains() (rotated rectangle contours)', function() {
|
||||
var path = new Path.Rectangle(new Point(100, 100), [200, 200]),
|
||||
curves = path.getCurves();
|
||||
|
||||
|
@ -200,7 +200,7 @@ test('Path#contains() (touching stationary point with changing orientation)', fu
|
|||
testPoint(path, new Point(200, 200), true);
|
||||
});
|
||||
|
||||
test('Path#contains() (complex shape)', function() {
|
||||
test('Path#contains() (complex shape: #400)', function() {
|
||||
var path = new Path({
|
||||
pathData: 'M301 162L307 154L315 149L325 139.5L332.5 135.5L341 128.5L357.5 117.5L364.5 114.5L368.5 110.5L380 105.5L390.5 102L404 96L410.5 96L415 97.5L421 104L425.5 113.5L428.5 126L429.5 134L429.5 141L429.5 148L425.5 161.5L425.5 169L414 184.5L409.5 191L401 201L395 209L386 214.5L378.5 217L368 220L348 219.5L338 218L323.5 212.5L312 205.5L302.5 197.5L295.5 189L291.5 171.5L294 168L298 165.5L301 162z',
|
||||
fillColor: 'blue',
|
||||
|
@ -215,7 +215,7 @@ test('Path#contains() (complex shape)', function() {
|
|||
});
|
||||
|
||||
|
||||
test('Path#contains() (straight curves with zero-winding)', function() {
|
||||
test('Path#contains() (straight curves with zero-winding: #943)', function() {
|
||||
var pointData = [
|
||||
[[250, 230], true, true, false, true],
|
||||
[[200, 230], true, true, true, true],
|
||||
|
@ -282,7 +282,7 @@ test('Path#contains() (straight curves with zero-winding)', function() {
|
|||
}
|
||||
});
|
||||
|
||||
test('CompoundPath#contains() (nested touching circles)', function() {
|
||||
test('CompoundPath#contains() (nested touching circles: #944)', function() {
|
||||
var c1 = new Path.Circle({
|
||||
center: [200, 200],
|
||||
radius: 100
|
||||
|
|
|
@ -1013,7 +1013,7 @@ test('Isolated edge-cases from @iconexperience\'s boolean-test suite', function(
|
|||
]];
|
||||
var results = [
|
||||
['M240,270l4.48298,-20.84932l-14.48298,0.84932l7.67824,-56.20204l82.32176,46.20204z M237.67824,193.79796z', 'M244.48298,249.15068l6.05168,-28.14496l12.77751,27.04076z'],
|
||||
['M450,230l-87.53067,34.43944l-0.01593,-0.02371c-0.91969,-0.32388 -30.35274,-0.48069 -30.67835,2.89141l-2.76789,-52.67014z', 'M362.46933,264.43944l-0.01593,-0.02371c0.02221,0.00782 0.02779,0.01574 0.01593,0.02371z'],
|
||||
['M450,230l-87.53067,34.43944l-0.01593,-0.02371l0,0c-0.91969,-0.32388 -30.35274,-0.48069 -30.67835,2.89141l-2.76789,-52.67014z', 'M362.46933,264.43944l-0.01593,-0.02371c0.02221,0.00782 0.02779,0.01574 0.01593,0.02371z'],
|
||||
['M211.76471,391.55301c-1.13066,-11.28456 3.81977,12.25688 -5.47954,24.76763l-28.00902,-21.41225l-26.21252,2.62636l13.04584,-12.69198l-22.93592,-17.53397l30.159,10.50681l46.32376,-45.06725c-9.58101,10.09791 -8.78302,39.92732 -6.89161,58.80464z', 'M178.27615,394.90839l-13.16668,-10.06562l7.22309,-7.02716l39.43215,13.7374z'],
|
||||
['M138.31417,456.06811c1.62465,-1.18877 -18.69614,16.61617 -34.61033,15.37458l22.34488,-66.7556l-23.16516,-97.04078l209.03396,72.10619c-68.45789,0.5466 -139.96009,51.6986 -173.60335,76.31563z', 'M126.04871,404.68708l21.5947,-64.51445l-9.32924,115.89547z'],
|
||||
['M300.04122,200l-0.02301,55.81328l19.98178,24.18672l-19.98771,-9.82138l-0.01229,29.82138l-29.04124,-44.09743l-40.1367,-19.722z', 'M300.01822,255.81328l-0.00592,14.36534l-29.05353,-14.27606l-10.39571,-15.78528l29.74019,3.93662z'],
|
||||
|
@ -1035,9 +1035,9 @@ test('Isolated edge-cases from @iconexperience\'s boolean-test suite', function(
|
|||
['M150,290l30,-30l50,10l-24,16l14,14l-50,10l21.17647,-14.11765z', 'M206,286l14,14l-28.82353,-4.11765z'],
|
||||
['M324.40827,86.1091l0.45123,-0.29442l-16.06828,-25.33943l22.46895,-3.97479l18.72801,12.91832c4.53222,6.94617 -10.24683,11.75354 -25.05014,16.51976l15.98481,25.2078l-22.46194,3.97305l6.36357,-29.14428c-0.13874,0.04467 -0.27748,0.08933 -0.4162,0.134z M324.40827,86.1091l-24.66974,16.09646c-4.37001,-6.69756 10.04945,-11.38915 24.66974,-16.09646z', 'M324.8595,85.81469l0,0l0,0z M324.93803,85.93854c-0.03785,0.01219 -0.07571,0.02438 -0.11356,0.03656l0.03503,-0.16042z'],
|
||||
['M388.92139,223.32159c120.27613,-2.24369 208.69681,19.62691 208.69681,19.62691l0.0116,0.00003l0,0l21.89025,-21.07283c0,0 8.54573,10.72909 8.51542,21.16115c-0.03104,10.68629 -208.75655,121.23392 -208.75655,121.23392z', 'M597.62979,242.94854l-21.33555,20.53884l-8.25852,-20.6248z'],
|
||||
['M272.11155,196.81853l-22.11155,-66.81853l32.64261,64.05832c-6.17669,-1.04412 -8.29,-0.01335 -8.40866,0.04617l-0.00118,-0.00313c-0.09777,0.03858 -0.90866,0.4932 -2.12123,2.71717z', ''],
|
||||
['M272.11155,196.81853l-22.11155,-66.81853l32.64261,64.05832c-6.17669,-1.04412 -8.29,-0.01335 -8.40866,0.04617l-0.00118,-0.00313c-0.09777,0.03858 -0.90866,0.4932 -2.12123,2.71717z', 'M274.23395,194.10449l-0.00118,-0.00313c0.01275,-0.00503 0.01337,-0.00299 0.00118,0.00313z'],
|
||||
['M386.40934,270.73027c77.4169,15.77719 129.39044,24.64733 142.22581,18.90659l5.47489,12.24096l0,0l40.60627,23.71608l-21.58416,18.81428z', 'M528.63515,289.63686l5.47489,12.24096l-11.2043,-6.54387c2.3982,-4.10616 5.41393,-5.55599 5.72941,-5.69709z'],
|
||||
['M525,345l32.31797,18.04379l23.72504,-18.37449l6.0793,21.87157l-87.1223,33.45913z', 'M557.31797,363.04379l-23.71188,18.3643l-6.07498,-21.85933z'],
|
||||
['M525,345l32.31797,18.04379l23.72504,-18.37449l6.0793,21.87157l-87.1223,33.45913z', 'M557.31797,363.04379l0,0l0,0z M557.31797,363.04379l-23.71188,18.3643l-6.07498,-21.85933z'],
|
||||
['M250,150l-15.25375,32.05024l29.10275,-7.1942z M225.84314,153.10482l-45.84314,36.89518l63.48255,20.45333z', 'M205.60228,189.25463l0,0.2l0.01028,-0.20254z'],
|
||||
['M598.5487,408.11025l-42.28034,59.10612l0,0l-37.73535,-87.53938l36.05709,27.56285l0.78705,28.12706z', 'M556.26835,467.21638l-3.83913,-29.98828l2.94792,-1.86119z'],
|
||||
['M570,290l5.8176,33.58557l-28.17314,-14.439c-14.32289,2.0978 -28.17688,4.12693 -28.17688,4.12693l19.08162,-8.78834l-16.12739,-8.26544l27.9654,2.81326z', 'M538.5492,304.48516l11.83801,-5.45218l9.61279,0.96702l15.8176,23.58557z'],
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
|
||||
QUnit.module('Segment');
|
||||
|
||||
test('new Segment()', function() {
|
||||
var segment = new Segment(null, null, null);
|
||||
equals(segment.toString(), '{ point: { x: 0, y: 0 } }');
|
||||
});
|
||||
|
||||
test('new Segment(point)', function() {
|
||||
var segment = new Segment(new Point(10, 10));
|
||||
equals(segment.toString(), '{ point: { x: 10, y: 10 } }');
|
||||
|
@ -22,6 +27,11 @@ test('new Segment(x, y)', function() {
|
|||
equals(segment.toString(), '{ point: { x: 10, y: 10 } }');
|
||||
});
|
||||
|
||||
test('new Segment(undefined)', function() {
|
||||
var segment = new Segment(undefined);
|
||||
equals(segment.toString(), '{ point: { x: 0, y: 0 } }');
|
||||
});
|
||||
|
||||
test('new Segment(object)', function() {
|
||||
var segment = new Segment({ point: { x: 10, y: 10 }, handleIn: { x: 5, y: 5 }, handleOut: { x: 15, y: 15 } });
|
||||
equals(segment.toString(), '{ point: { x: 10, y: 10 }, handleIn: { x: 5, y: 5 }, handleOut: { x: 15, y: 15 } }');
|
||||
|
@ -32,6 +42,16 @@ test('new Segment(point, handleIn, handleOut)', function() {
|
|||
equals(segment.toString(), '{ point: { x: 10, y: 10 }, handleIn: { x: 5, y: 5 }, handleOut: { x: 15, y: 15 } }');
|
||||
});
|
||||
|
||||
test('new Segment(null, null, null)', function() {
|
||||
var segment = new Segment(null, null, null);
|
||||
equals(segment.toString(), '{ point: { x: 0, y: 0 } }');
|
||||
});
|
||||
|
||||
test('new Segment(undefined, null, null)', function() {
|
||||
var segment = new Segment(undefined, null, null);
|
||||
equals(segment.toString(), '{ point: { x: 0, y: 0 } }');
|
||||
});
|
||||
|
||||
test('new Segment(x, y, inX, inY, outX, outY)', function() {
|
||||
var segment = new Segment(10, 10, 5, 5, 15, 15);
|
||||
equals(segment.toString(), '{ point: { x: 10, y: 10 }, handleIn: { x: 5, y: 5 }, handleOut: { x: 15, y: 15 } }');
|
||||
|
|
|
@ -58,3 +58,5 @@
|
|||
|
||||
/*#*/ include('SvgImport.js');
|
||||
/*#*/ include('SvgExport.js');
|
||||
|
||||
/*#*/ include('Numerical.js');
|
||||
|
|
Loading…
Reference in a new issue