/*
* Copyright 2020 WICKLETS LLC
*
* This file is part of Wick Engine.
*
* Wick Engine is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Wick Engine is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Wick Engine. If not, see <https://www.gnu.org/licenses/>.
*/
Wick.Tool = class {
static get DOUBLE_CLICK_TIME () {
return 300;
}
static get DOUBLE_CLICK_MAX_DISTANCE () {
return 20;
}
/**
* Creates a new Wick Tool.
*/
constructor () {
this.paperTool = new this.paper.Tool();
// Attach onActivate event
this.paperTool.onActivate = (e) => {
this.onActivate(e);
}
// Attach onDeactivate event
this.paperTool.onDeactivate = (e) => {
this.onDeactivate(e);
}
// Attach mouse move event
this.paperTool.onMouseMove = (e) => {
this.onMouseMove(e);
}
// Attach mouse down + double click event
this.paperTool.onMouseDown = (e) => {
if(this.doubleClickEnabled &&
this._lastMousedownTimestamp !== null &&
e.timeStamp - this._lastMousedownTimestamp < Wick.Tool.DOUBLE_CLICK_TIME &&
e.point.subtract(this._lastMousedownPoint).length < Wick.Tool.DOUBLE_CLICK_MAX_DISTANCE) {
this.onDoubleClick(e);
} else {
this.onMouseDown(e);
}
this._lastMousedownTimestamp = e.timeStamp;
this._lastMousedownPoint = e.point;
}
// Attach key events
this.paperTool.onKeyDown = (e) => {
this.onKeyDown(e);
}
this.paperTool.onKeyUp = (e) => {
this.onKeyUp(e);
}
// Attach mouse move event
this.paperTool.onMouseDrag = (e) => {
this.onMouseDrag(e);
}
// Attach mouse up event
this.paperTool.onMouseUp = (e) => {
this.onMouseUp(e);
}
this._eventCallbacks = {};
this._lastMousedownTimestamp = null;
}
/**
* The paper.js scope to use.
*/
get paper () {
return Wick.View.paperScope;
}
/**
* The CSS cursor to display for this tool.
*/
get cursor () {
console.warn("Warning: Tool is missing a cursor!");
}
/**
* Called when the tool is activated
*/
onActivate (e) {
}
/**
* Called when the tool is deactivated (another tool is activated)
*/
onDeactivate (e) {
}
/**
* Called when the mouse moves and the tool is active.
*/
onMouseMove (e) {
this.setCursor(this.cursor);
}
/**
* Called when the mouse clicks the paper.js canvas and this is the active tool.
*/
onMouseDown (e) {
}
/**
* Called when the mouse is dragged on the paper.js canvas and this is the active tool.
*/
onMouseDrag (e) {
}
/**
* Called when the mouse is clicked on the paper.js canvas and this is the active tool.
*/
onMouseUp (e) {
}
/**
* Called when the mouse double clicks on the paper.js canvas and this is the active tool.
*/
onDoubleClick (e) {
}
/**
* Called when a key is pressed and this is the active tool.
*/
onKeyDown (e) {
}
/**
* Called when a key is released and this is the active tool.
*/
onKeyUp (e) {
}
/**
* Should reset the state of the tool.
*/
reset () {
}
/**
* Activates this tool in paper.js.
*/
activate () {
this.paperTool.activate();
}
/**
* Sets the cursor of the paper.js canvas that the tool belongs to.
* @param {string} cursor - a CSS cursor style
*/
setCursor (cursor) {
this.paper.view._element.style.cursor = cursor;
}
/**
* Attach a function to get called when an event happens.
* @param {string} eventName - the name of the event
* @param {function} fn - the function to call when the event is fired
*/
on (eventName, fn) {
this._eventCallbacks[eventName] = fn;
}
/**
* Call the functions attached to a given event.
* @param {string} eventName - the name of the event to fire
* @param {object} e - (optional) an object to attach some data to, if needed
* @param {string} actionName - Name of the action committed.
*/
fireEvent ({eventName, e, actionName}) {
if(!e) e = {};
if(!e.layers) {
e.layers = [this.paper.project.activeLayer];
}
var fn = this._eventCallbacks[eventName];
fn && fn(e, actionName);
}
/**
*
* @param {paper.Color} color - the color of the cursor
* @param {number} size - the width of the cursor image to generate
* @param {boolean} transparent - if set to true, color is ignored
*/
createDynamicCursor (color, size, transparent) {
var radius = size/2;
var canvas = document.createElement("canvas");
canvas.width = radius * 2 + 2;
canvas.height = radius * 2 + 2;
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.strokeStyle = transparent ? 'black' : invert(color);
context.stroke();
if(transparent) {
context.beginPath();
context.arc(centerX, centerY, radius-1, 0, 2 * Math.PI, false);
context.strokeStyle = 'white';
context.stroke();
} else {
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = color;
context.fill();
}
return 'url(' + canvas.toDataURL() + ') '+(radius+1)+' '+(radius+1)+',default';
}
/**
* Get a tool setting from the project. See Wick.ToolSettings for all options
* @param {string} name - the name of the setting to get
*/
getSetting (name) {
return this.project.toolSettings.getSetting(name);
}
/**
* Does this tool have a double click action? (override this in classes that extend Wick.Tool)
* @type {boolean}
*/
get doubleClickEnabled () {
return true;
}
/**
* Adds a paper.Path to the active frame's paper.Layer.
* @param {paper.Path} path - the path to add
* @param {Wick.Frame} frame - (optional) the frame to add the path to.
*/
addPathToProject (path, frame) {
// Avoid adding empty paths
if(!path) {
return;
}
if((path instanceof paper.Path) && path.segments.length === 0) {
return;
}
if((path instanceof paper.CompoundPath) && path.children.length === 0) {
return;
}
if(!this.project.activeFrame) {
// Automatically add a frame is there isn't one
this.project.insertBlankFrame();
this.project.view.render();
}
if(!path) {
console.error("Warning: addPathToProject: path is null/undefined");
return;
}
if(frame && frame !== this.project.activeFrame) {
/* If the path must be added to a frame other than the active frame,
* convert the paper.js path into a Wick path and add it to the given frame. */
var wickPath = new Wick.Path({
json: path.exportJSON({asString:false}),
});
frame.addPath(wickPath);
} else {
/* Otherwise, directly add the paper.js path to the paper.js project.
This is signifigantly faster than creating the Wick path, as this
method does not require a re-render of the canvas. */
this.paper.project.activeLayer.addChild(path);
}
}
}
Wick.Tools = {};