Add miniPaint as new frontend base

This commit is contained in:
motion
2026-01-26 12:20:16 -05:00
parent bb859105ff
commit 6ae4cbcb77
306 changed files with 60729 additions and 4050 deletions
+116
View File
@@ -0,0 +1,116 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import GUI_tools_class from './../core/gui/gui-tools.js';
import Base_gui_class from './../core/base-gui.js';
import Base_selection_class from './../core/base-selection.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
class Animation_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.GUI_tools = new GUI_tools_class();
this.Base_gui = new Base_gui_class();
this.name = 'animation';
this.intervalID = null;
this.index = 0;
this.toggle_layer_visibility_action = new app.Actions.Toggle_layer_visibility_action();
this.disable_selection(ctx);
}
load() {
//nothing
}
render(ctx, layer) {
//nothing
}
/**
* disable_selection
*/
disable_selection(ctx) {
var sel_config = {
enable_background: false,
enable_borders: false,
enable_controls: false,
enable_rotation: false,
enable_move: false,
data_function: function () {
return null;
},
};
this.Base_selection = new Base_selection_class(ctx, sel_config, this.name);
}
on_params_update(data) {
if(data.key != "play")
return;
var params = this.getParams();
if (config.layers.length == 1) {
alertify.error('Can not animate 1 layer.');
return;
}
this.stop();
if (params.play == true) {
this.start(params.delay);
}
}
on_activate() {
return [
new app.Actions.Stop_animation_action(false)
];
}
on_leave() {
return [
new app.Actions.Stop_animation_action(true)
];
}
start(delay) {
var _this = this;
delay = parseInt(delay);
if (delay < 0)
delay = 50;
this.intervalID = window.setInterval(function () {
_this.play(_this);
}, delay);
}
stop() {
new app.Actions.Stop_animation_action(true).do();
}
play(_this) {
for (var i in config.layers) {
config.layers[i].visible = false;
}
//show 1
if (config.layers[this.index] != undefined) {
this.toggle_layer_visibility_action.layer_id = config.layers[this.index].id;
this.toggle_layer_visibility_action.do();
}
//change index
if (config.layers[this.index + 1] != undefined) {
this.index++;
}
else {
this.index = 0;
}
}
}
;
export default Animation_class;
+140
View File
@@ -0,0 +1,140 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
import ImageFilters from './../libs/imagefilters.js';
import Helper_class from './../libs/helpers.js';
class Blur_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.ctx = ctx;
this.name = 'blur';
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
this.started = false;
}
load() {
this.default_events();
}
default_dragMove(event) {
if (config.TOOL.name != this.name)
return;
this.mousemove(event);
//mouse cursor
var mouse = this.get_mouse_info(event);
var params = this.getParams();
this.show_mouse_cursor(mouse.x, mouse.y, params.size, 'circle');
}
mousedown(e) {
this.started = false;
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.click_valid == false) {
return;
}
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
if (config.layer.rotate || 0 > 0) {
alertify.error('Erase on rotate object is disabled. Please rasterize first.');
return;
}
this.started = true;
//get canvas from layer
this.tmpCanvas = document.createElement('canvas');
this.tmpCanvasCtx = this.tmpCanvas.getContext("2d");
this.tmpCanvas.width = config.layer.width_original;
this.tmpCanvas.height = config.layer.height_original;
this.tmpCanvasCtx.drawImage(config.layer.link, 0, 0);
//do blur
this.blur_general('click', mouse, params.size, params.strength);
//register tmp canvas for faster redraw
config.layer.link_canvas = this.tmpCanvas;
config.need_render = true;
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
if (this.started == false) {
return;
}
//do blur
this.blur_general('move', mouse, params.size, params.strength);
//draw draft preview
config.need_render = true;
}
mouseup(e) {
if (this.started == false) {
return;
}
delete config.layer.link_canvas;
app.State.do_action(
new app.Actions.Bundle_action('blur_tool', 'Blur Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
//decrease memory
this.tmpCanvas.width = 1;
this.tmpCanvas.height = 1;
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
}
blur_general(type, mouse, size, strength) {
var ctx = this.tmpCanvasCtx;
var mouse_x = Math.round(mouse.x) - config.layer.x;
var mouse_y = Math.round(mouse.y) - config.layer.y;
//adapt to origin size
mouse_x = this.adaptSize(mouse_x, 'width');
mouse_y = this.adaptSize(mouse_y, 'height');
var size_w = this.adaptSize(size, 'width');
var size_h = this.adaptSize(size, 'height');
//find center
var center_x = mouse_x - Math.round(size_w / 2);
var center_y = mouse_y - Math.round(size_h / 2);
//convert float coords to integers
center_x = Math.round(center_x);
center_y = Math.round(center_y);
mouse_x = Math.round(mouse_x);
mouse_y = Math.round(mouse_y);
if (type == 'move') {
strength = strength / 2;
if (strength < 1)
strength = 1;
}
var imageData = ctx.getImageData(center_x, center_y, size_w, size_h);
var filtered = ImageFilters.StackBlur(imageData, strength); //add effect
this.Helper.image_round(this.tmpCanvasCtx, mouse_x, mouse_y, size_w, size_h, filtered);
}
}
export default Blur_class;
+570
View File
@@ -0,0 +1,570 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
class Brush_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.name = 'brush';
this.layer = {};
this.params_hash = false;
this.pressure_supported = false;
this.pointer_pressure = 0; // has range [0 - 1]
this.max_speed = 20;
this.power = 2; //how speed affects size
this.event_links = [];
this.data_index = 0;
}
load() {
var _this = this;
var is_touch = false;
//pointer events
document.addEventListener('pointerdown', function (event) {
_this.pointerdown(event);
});
document.addEventListener('pointermove', function (event) {
_this.pointermove(event);
});
//mouse events
document.addEventListener('mousedown', function (event) {
if(is_touch)
return;
_this.dragStart(event);
});
document.addEventListener('mousemove', function (event) {
if(is_touch)
return;
_this.dragMove(event);
});
document.addEventListener('mouseup', function (event) {
if(is_touch)
return;
_this.dragEnd(event);
});
// collect touch events
document.addEventListener('touchstart', function (event) {
is_touch = true;
_this.dragStart(event);
});
document.addEventListener('touchmove', function (event) {
_this.dragMove(event);
});
document.addEventListener('touchend', function (event) {
_this.dragEnd(event);
});
}
pointerdown(e) {
// Devices that don't actually support pen pressure can give 0.5 as a false reading.
// It is highly unlikely a real pen will read exactly 0.5 at the start of a stroke.
if (e.pressure && e.pressure !== 0 && e.pressure !== 0.5 && e.pressure <= 1) {
this.pressure_supported = true;
this.pointer_pressure = e.pressure;
} else {
this.pressure_supported = false;
}
}
pointermove(e) {
// Pressure of exactly 1 seems to be an input error, sometimes I see it when lifting the pen
// off the screen when pressure reading should be near 0.
if (this.pressure_supported && e.pressure < 1) {
this.pointer_pressure = e.pressure;
}
}
dragStart(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
this.click_counter++;
var mouse = this.get_mouse_info(event);
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
var events = [];
if (event.changedTouches) {
events = event.changedTouches;
}
else{
events.push(event);
}
for(var i = 0; i < events.length; i++){
var identifier = null;
if(typeof events[i].identifier != "undefined") {
identifier = events[i].identifier;
}
this.event_links.push({
identifier: identifier,
index: this.data_index,
});
_this.mousedown_action(events[i], this.data_index, identifier);
this.data_index++;
}
}
dragMove(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
if (typeof event.changedTouches == "undefined") {
//mouse cursor
var mouse = _this.get_mouse_info(event);
var params = _this.getParams();
_this.show_mouse_cursor(mouse.x, mouse.y, params.size, 'circle');
}
var mouse = this.get_mouse_info(event);
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
var events = [];
if (event.changedTouches) {
events = event.changedTouches;
}
else{
events.push(event);
}
for(var i = 0; i < events.length; i++){
var identifier = null;
if(typeof events[i].identifier != "undefined") {
identifier = events[i].identifier;
}
for(var j = 0; i < this.event_links.length; j++){
if(this.event_links[j].identifier == identifier){
//found link
_this.mousemove_action(events[i], this.event_links[j].index);
break;
}
}
}
}
dragEnd(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
var mouse = this.get_mouse_info(event);
if (mouse.click_valid == false) {
return;
}
var events = [];
if (event.changedTouches) {
events = event.changedTouches;
}
else{
events.push(event);
}
for(var i = 0; i < events.length; i++){
var identifier = null;
if(typeof events[i].identifier != "undefined") {
//unlink
identifier = events[i].identifier;
}
for(var j = 0; i < this.event_links.length; j++){
if(this.event_links[j].identifier == identifier){
this.event_links.splice(j, 1);
break;
}
}
_this.mouseup_action(events[i]);
}
}
mousedown_action(e, index, event_identifier) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false)
return;
var params_hash = this.get_params_hash();
if (config.layer.type != this.name || params_hash != this.params_hash) {
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
data: [[]],
params: this.clone(this.getParams()),
status: 'draft',
render_function: [this.name, 'render'],
x: 0,
y: 0,
width: config.WIDTH,
height: config.HEIGHT,
hide_selection_if_active: true,
rotate: null,
is_vector: true,
color: config.COLOR
};
app.State.do_action(
new app.Actions.Bundle_action('new_brush_layer', 'New Brush Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
this.params_hash = params_hash;
//reset event links index
this.data_index = 0;
index = 0;
this.event_links = [];
this.event_links.push({
identifier: event_identifier,
index: this.data_index,
});
}
else {
const new_data = JSON.parse(JSON.stringify(config.layer.data));
new_data.push([]);
app.State.do_action(
new app.Actions.Bundle_action('update_brush_layer', 'Update Brush Layer', [
new app.Actions.Update_layer_action(config.layer.id, {
data: new_data
})
])
);
}
//in case of undo, recalculate index
for(var i = index; i >= 0; i++){
if(typeof config.layer.data[index] != "undefined"){
break;
}
index--;
}
var current_group = config.layer.data[index];
var params = this.getParams();
//detect line size
var size = params.size;
var new_size = size;
if (params.pressure == true) {
if (this.pressure_supported) {
new_size = size * this.pointer_pressure * 2;
}
else {
new_size = size + size / this.max_speed * mouse.speed_average * this.power;
new_size = Math.max(new_size, size / 4);
new_size = Math.round(new_size);
}
}
var mouse_coords = this.get_mouse_coordinates_from_event(e);
var mouse_x = mouse_coords.x;
var mouse_y = mouse_coords.y;
current_group.push([mouse_x - config.layer.x, mouse_y - config.layer.y, new_size]);
this.Base_layers.render();
}
mousemove_action(e, index) {
var mouse = this.get_mouse_info(e);
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
//in case of undo, recalculate index
for(var i = index; i >= 0; i++){
if(typeof config.layer.data[index] != "undefined"){
break;
}
index--;
}
var params = this.getParams();
var current_group = config.layer.data[index];
//detect line size
var size = params.size;
var new_size = size;
if (params.pressure == true) {
if (this.pressure_supported) {
new_size = size * this.pointer_pressure * 2;
}
else {
new_size = size + size / this.max_speed * mouse.speed_average * this.power;
new_size = Math.max(new_size, size / 4);
new_size = Math.round(new_size);
}
}
var mouse_coords = this.get_mouse_coordinates_from_event(e);
var mouse_x = mouse_coords.x;
var mouse_y = mouse_coords.y;
current_group.push([mouse_x - config.layer.x, mouse_y - config.layer.y, new_size]);
config.layer.status = 'draft';
this.Base_layers.render();
}
mouseup_action(e, index) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false) {
config.layer.status = null;
return;
}
config.layer.status = null;
this.check_dimensions();
this.Base_layers.render();
}
render(ctx, layer) {
if (layer.data.length == 0)
return;
var params = layer.params;
var size = params.size;
//set styles
ctx.save();
ctx.fillStyle = layer.color;
ctx.strokeStyle = layer.color;
ctx.lineWidth = params.size;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.translate(layer.x, layer.y);
var data = layer.data;
//check for legacy format
data = this.check_legacy_format(data);
var n = data.length;
for (var k = 0; k < n; k++) {
var group_data = data[k]; //data from mouse down till mouse release
var group_n = group_data.length;
if (params.pressure == false) {
//stabilized lines method does not support multiple line sizes
this.render_stabilized(ctx, group_data);
}
else {
if (group_data[0]) {
ctx.beginPath();
ctx.moveTo(group_data[0][0], group_data[0][1]);
for (var i = 1; i < group_n; i++) {
if (group_data[i] === null) {
//break
ctx.beginPath();
}
else {
//line
ctx.lineWidth = group_data[i][2];
if (group_data[i - 1] == null && group_data[i + 1] == null) {
//exception - point
ctx.arc(group_data[i][0], group_data[i][1], size / 2, 0, 2 * Math.PI, false);
ctx.fill();
}
else if (group_data[i - 1] != null) {
//lines
ctx.lineWidth = group_data[i][2];
ctx.beginPath();
ctx.moveTo(group_data[i - 1][0], group_data[i - 1][1]);
ctx.lineTo(group_data[i][0], group_data[i][1]);
ctx.stroke();
}
}
}
if (group_data[1] == null) {
//point
ctx.beginPath();
ctx.arc(group_data[0][0], group_data[0][1], size / 2, 0, 2 * Math.PI, false);
ctx.fill();
}
}
}
}
ctx.translate(-layer.x, -layer.y);
ctx.restore();
}
/**
* draw stabilized lines
* author: Manoj Verma
* source: https://stackoverflow.com/questions/7891740/drawing-smooth-lines-with-canvas/44810470#44810470
*
* @param ctx
* @param queue
*/
render_stabilized(ctx, queue) {
var data = JSON.parse(JSON.stringify(queue));
var n = data.length;
if (data.length == 1) {
//point
var point = data[0];
ctx.beginPath();
ctx.arc(point[0], point[1], point[2] / 2, 0, 2 * Math.PI, false);
ctx.fill();
return;
}
else if (data.length <= 5) {
//not enough points yet
for (var i = 1; i < n; i++) {
ctx.beginPath();
ctx.moveTo(data[i - 1][0], data[i - 1][1]);
ctx.lineTo(data[i][0], data[i][1]);
ctx.stroke();
}
return;
}
//fix for loose ending, so lets duplicate last point
data.push([data[n - 1][0], data[n - 1][1]]);
ctx.beginPath();
ctx.moveTo(data[0][0], data[0][1]);
//prepare
var temp_data1 = [data[0]];
var c, d;
for (var i = 1; i < data.length - 1; i = i+1) {
c = (data[i][0] + data[i + 1][0]) / 2;
d = (data[i][1] + data[i + 1][1]) / 2;
temp_data1.push([c, d]);
}
var temp_data2 = [temp_data1[0]];
for (var i = 1; i < temp_data1.length - 1; i = i+1) {
c = (temp_data1[i][0] + temp_data1[i + 1][0]) / 2;
d = (temp_data1[i][1] + temp_data1[i + 1][1]) / 2;
temp_data2.push([c, d]);
}
var temp_data = [temp_data2[0]];
for (var i = 1; i < temp_data2.length - 1; i = i+1) {
c = (temp_data2[i][0] + temp_data2[i + 1][0]) / 2;
d = (temp_data2[i][1] + temp_data2[i + 1][1]) / 2;
temp_data.push([c, d]);
}
//draw
for (var i = 1; i < temp_data.length - 2; i = i+1) {
c = (temp_data[i][0] + temp_data[i + 1][0]) / 2;
d = (temp_data[i][1] + temp_data[i + 1][1]) / 2;
ctx.quadraticCurveTo(temp_data[i][0], temp_data[i][1], c, d);
}
// For the last 2 points
ctx.quadraticCurveTo(
temp_data[i][0],
temp_data[i][1],
temp_data[i+1][0],
temp_data[i+1][1]
);
ctx.stroke();
}
check_legacy_format(data) {
//check for legacy format
if(data.length > 0 && typeof data[0][0] == "number"){
//convert
var legacy = JSON.parse(JSON.stringify(data));
data = [];
data.push([]);
var group_index = 0;
for(var i in legacy){
if(legacy[i] === null){
data.push([]);
group_index++;
}
else {
data[group_index].push([legacy[i][0], legacy[i][1], legacy[i][2]]);
}
}
}
return data;
}
/**
* recalculate layer x, y, width and height values.
*/
check_dimensions() {
var data = JSON.parse(JSON.stringify(config.layer.data)); // Deep copy for history
this.check_legacy_format(data);
if(config.layer.data.length == 0 || data[0].length == 0)
return;
//find bounds
var min_x = data[0][0][0];
var min_y = data[0][0][1];
var max_x = data[0][0][0];
var max_y = data[0][0][1];
var n = data.length;
for (var k = 0; k < n; k++) {
var group_data = data[k];
var group_n = group_data.length;
for (var i = 1; i < group_n; i++) {
min_x = Math.min(min_x, group_data[i][0]);
min_y = Math.min(min_y, group_data[i][1]);
max_x = Math.max(max_x, group_data[i][0]);
max_y = Math.max(max_y, group_data[i][1]);
}
}
//move current data
for (var k = 0; k < n; k++) {
var group_data = data[k];
var group_n = group_data.length;
for (var i = 0; i < group_n; i++) {
group_data[i][0] = group_data[i][0] - min_x;
group_data[i][1] = group_data[i][1] - min_y;
}
}
//change layers bounds
app.State.do_action(
new app.Actions.Update_layer_action(config.layer.id, {
x: config.layer.x + min_x,
y: config.layer.y + min_y,
width: max_x - min_x,
height: max_y - min_y,
data
}),
{
merge_with_history: ['new_brush_layer', 'update_brush_layer']
}
);
}
}
export default Brush_class;
+118
View File
@@ -0,0 +1,118 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
import glfx from './../libs/glfx.js';
import Helper_class from './../libs/helpers.js';
class BulgePinch_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.fx_filter = false;
this.Helper = new Helper_class();
this.ctx = ctx;
this.name = 'bulge_pinch';
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
this.started = false;
}
load() {
this.default_events();
}
default_dragMove(event) {
if (config.TOOL.name != this.name)
return;
//mouse cursor
var mouse = this.get_mouse_info(event);
var params = this.getParams();
this.show_mouse_cursor(mouse.x, mouse.y, params.radius, 'circle');
}
mousedown(e) {
this.started = false;
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.click_valid == false) {
return;
}
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
this.started = true;
//get canvas from layer
this.tmpCanvas = document.createElement('canvas');
this.tmpCanvasCtx = this.tmpCanvas.getContext("2d");
this.tmpCanvas.width = config.layer.width_original;
this.tmpCanvas.height = config.layer.height_original;
this.tmpCanvasCtx.drawImage(config.layer.link, 0, 0);
//apply
this.bulgePinch_general(mouse, params.power, params.radius, params.bulge);
//register tmp canvas for faster redraw
config.layer.link_canvas = this.tmpCanvas;
config.need_render = true;
}
mouseup(e) {
if (this.started == false) {
return;
}
delete config.layer.link_canvas;
app.State.do_action(
new app.Actions.Bundle_action('bulge_pinch_tool', 'Bulge/Pinch Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
//decrease memory
this.tmpCanvas.width = 1;
this.tmpCanvas.height = 1;
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
}
bulgePinch_general(mouse, power, radius, bulge) {
if (this.fx_filter == false) {
//init glfx lib
this.fx_filter = glfx.canvas();
}
var ctx = this.tmpCanvasCtx;
var mouse_x = Math.round(mouse.x) - config.layer.x;
var mouse_y = Math.round(mouse.y) - config.layer.y;
//adapt to origin size
mouse_x = this.adaptSize(mouse_x, 'width');
mouse_y = this.adaptSize(mouse_y, 'height');
//convert float coords to integers
mouse_x = Math.round(mouse_x);
mouse_y = Math.round(mouse_y);
power = power / 100;
if (power > 1) {
//max 100%
power = 1;
}
if (bulge == false)
power = -1 * power;
var texture = this.fx_filter.texture(this.tmpCanvas);
this.fx_filter.draw(texture).bulgePinch(mouse_x, mouse_y, radius, power).update(); //effect
this.tmpCanvasCtx.clearRect(0, 0, this.tmpCanvas.width, this.tmpCanvas.height);
this.tmpCanvasCtx.drawImage(this.fx_filter, 0, 0);
}
}
export default BulgePinch_class;
+335
View File
@@ -0,0 +1,335 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import Layer_raster_class from './../modules/layer/raster.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
class Clone_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.Layer_raster = new Layer_raster_class();
this.ctx = ctx;
this.name = 'clone';
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
this.started = false;
this.clone_coords = null;
this.pressTimer = null;
}
load() {
var _this = this;
var is_touch = false;
//mouse events
document.addEventListener('mousedown', function (event) {
if(is_touch)
return;
_this.dragStart(event);
});
document.addEventListener('mousemove', function (event) {
if(is_touch)
return;
_this.dragMove(event);
});
document.addEventListener('mouseup', function (event) {
if(is_touch)
return;
_this.dragEnd(event);
});
// collect touch events
document.addEventListener('touchstart', function (event) {
is_touch = true;
_this.dragStart(event);
});
document.addEventListener('touchmove', function (event) {
_this.dragMove(event);
});
document.addEventListener('touchend', function (event) {
_this.dragEnd(event);
});
document.addEventListener('contextmenu', function (event) {
_this.mouseRightClick(event);
});
}
dragStart(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
_this.mousedown(event);
var mouse = this.get_mouse_info(event);
if (mouse.click_valid == true) {
this.pressTimer = window.setTimeout(function() {
//long press success
_this.mouseLongClick();
}, 2000);
}
}
dragMove(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
_this.mousemove(event);
//mouse cursor
var mouse = _this.get_mouse_info(event);
var params = _this.getParams();
_this.show_mouse_cursor(mouse.x, mouse.y, params.size, 'circle');
clearTimeout(this.pressTimer);
}
dragEnd(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
_this.mouseup(event);
clearTimeout(this.pressTimer);
}
on_params_update() {
var params = this.getParams();
var strict_element = document.getElementById('strict');
if (params.circle == false) {
//hide strict controls
strict_element.style.display = 'none';
}
else {
//show strict controls
strict_element.style.display = 'block';
}
}
mouseRightClick(e) {
if (config.TOOL.name != this.name)
return;
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (e.which == 3 && mouse.valid == true) {
e.preventDefault();
}
if (params.source_layer.value == 'Previous' && config.layer.type === null) {
this.Layer_raster.raster();
}
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
if (config.layer.rotate || 0 > 0) {
alertify.error('Erase on rotate object is disabled. Please rasterize first.');
return;
}
if (e.which == 3 && mouse.valid == true) {
//right click - save coords
var mouse_x = this.adaptSize(mouse.x, 'width');
var mouse_y = this.adaptSize(mouse.y, 'height');
this.clone_coords = {
x: mouse_x,
y: mouse_y,
};
alertify.success('Source coordinates saved.');
}
}
mouseLongClick(){
var params = this.getParams();
var mouse = this.get_mouse_info();
if (params.source_layer.value == 'Previous' && config.layer.type === null) {
this.Layer_raster.raster();
}
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
if (config.layer.rotate || 0 > 0) {
alertify.error('Erase on rotate object is disabled. Please rasterize first.');
return;
}
var mouse_x = this.adaptSize(mouse.x, 'width');
var mouse_y = this.adaptSize(mouse.y, 'height');
this.clone_coords = {
x: mouse_x,
y: mouse_y,
};
alertify.success('Source coordinates saved.');
}
mousedown(e) {
this.started = false;
var mouse = this.get_mouse_info(e);
var params = this.getParams();
var layer = config.layer;
var previous_layer = this.Base_layers.find_previous(config.layer.id);
if (mouse.click_valid == false) {
return;
}
if (params.source_layer.value == 'Previous' && config.layer.type === null) {
this.Layer_raster.raster();
}
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
if (config.layer.rotate || 0 > 0) {
alertify.error('Erase on rotate object is disabled. Please rasterize first.');
return;
}
if (this.clone_coords === null) {
alertify.error('Source is empty, right click on image or use long press to save source position.');
return;
}
if (layer.width != layer.width_original || layer.height != layer.height_original) {
alertify.error('Clone tool disabled for resized image. Please rasterize first.');
return;
}
if (params.source_layer.value == 'Previous' &&
(previous_layer.width != previous_layer.width_original
|| previous_layer.height != previous_layer.height_original)) {
alertify.error('Clone tool disabled for resized image. Please rasterize first.');
return;
}
if (params.source_layer.value == 'Previous') {
if (previous_layer == null) {
alertify.error('Can not find previous layer.');
return;
}
if (previous_layer.type != 'image') {
alertify.error('Previous layer must be image, convert it to raster to apply this tool.');
return;
}
}
this.started = true;
//get canvas from layer
this.tmpCanvas = document.createElement('canvas');
this.tmpCanvasCtx = this.tmpCanvas.getContext("2d");
this.tmpCanvas.width = config.layer.width_original;
this.tmpCanvas.height = config.layer.height_original;
this.tmpCanvasCtx.drawImage(config.layer.link, 0, 0);
//clone
this.clone_general(this.tmpCanvas, this.tmpCanvas, 'click', mouse);
//register tmp canvas for progress redraw
config.layer.link_canvas = this.tmpCanvas;
config.need_render = true;
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
if (this.started == false) {
return;
}
//clone
this.clone_general(this.tmpCanvas, this.tmpCanvas, 'move', mouse);
//draw draft preview
config.need_render = true;
}
mouseup(e) {
if (this.started == false) {
return;
}
delete config.layer.link_canvas;
app.State.do_action(
new app.Actions.Bundle_action('clone_tool', 'Clone Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
//decrease memory
this.tmpCanvas.width = 1;
this.tmpCanvas.height = 1;
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
}
clone_general(canvas_from, canvas_to, type, mouse) {
var params = this.getParams();
var mouse_x = Math.round(mouse.x) - config.layer.x;
var mouse_y = Math.round(mouse.y) - config.layer.y;
var half = Math.round(params.size / 2);
//adapt to origin size
mouse_x = this.adaptSize(mouse_x, 'width');
mouse_y = this.adaptSize(mouse_y, 'height');
//convert float coords to integers
mouse_x = Math.round(mouse_x);
mouse_y = Math.round(mouse_y);
//create source canvas
var canvas_source = document.createElement("canvas");
var ctx_source = canvas_source.getContext("2d");
var w = Math.ceil(params.size);
var h = Math.ceil(params.size);
canvas_source.width = w;
canvas_source.height = h;
//add data
var x_from = Math.round(this.clone_coords.x - (mouse.click_x - mouse_x));
var y_from = Math.round(this.clone_coords.y - (mouse.click_y - mouse_y));
if (params.anti_aliasing == false) {
ctx_source.arc(half, half, half, 0, Math.PI * 2, false);
ctx_source.clip();
}
if (params.source_layer.value == 'Previous') {
var previous_layer = this.Base_layers.find_previous(config.layer.id);
x_from = Math.round(this.clone_coords.x - (mouse.click_x - mouse_x)) - previous_layer.x + config.layer.x;
y_from = Math.round(this.clone_coords.y - (mouse.click_y - mouse_y)) - previous_layer.y + config.layer.y;
ctx_source.drawImage(previous_layer.link, x_from - half, y_from - half, w, h, 0, 0, w, h);
}
else {
ctx_source.drawImage(canvas_from, x_from - half, y_from - half, w, h, 0, 0, w, h);
}
//apply anti aliasing
if (params.anti_aliasing == true) {
var gradient = ctx_source.createRadialGradient(half, half, 0, half, half, half + 1);
gradient.addColorStop(0, 'white');
gradient.addColorStop(0.3, 'white');
gradient.addColorStop(1, 'transparent');
ctx_source.fillStyle = gradient;
ctx_source.globalCompositeOperation = 'destination-in';
ctx_source.fillRect(0, 0, params.size, params.size);
ctx_source.globalCompositeOperation = 'source-over';
}
//finish
canvas_to.getContext("2d").drawImage(canvas_source, mouse_x - half, mouse_y - half);
}
}
export default Clone_class;
+306
View File
@@ -0,0 +1,306 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import GUI_tools_class from './../core/gui/gui-tools.js';
import Base_gui_class from './../core/base-gui.js';
import Base_selection_class from './../core/base-selection.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
class Crop_class extends Base_tools_class {
constructor(ctx) {
super();
var _this = this;
this.Base_layers = new Base_layers_class();
this.Base_gui = new Base_gui_class();
this.GUI_tools = new GUI_tools_class();
this.ctx = ctx;
this.name = 'crop';
this.selection = {
x: null,
y: null,
width: null,
height: null,
};
var sel_config = {
enable_background: true,
enable_borders: true,
enable_controls: true,
crop_lines: true,
enable_rotation: false,
enable_move: false,
data_function: function () {
return _this.selection;
},
};
this.mousedown_selection = null;
this.Base_selection = new Base_selection_class(ctx, sel_config, this.name);
}
load() {
this.default_events();
}
default_dragStart(event) {
this.is_mousedown_canvas = false;
if (config.TOOL.name != this.name)
return;
if (!event.target.closest('#main_wrapper'))
return;
this.is_mousedown_canvas = true;
this.mousedown(event);
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
if (this.Base_selection.is_drag == false || mouse.click_valid == false)
return;
this.mousedown_selection = JSON.parse(JSON.stringify(this.selection));
if (this.Base_selection.mouse_lock !== null) {
return;
}
//create new selection
this.Base_selection.set_selection(mouse.x, mouse.y, 0, 0);
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
if (this.Base_selection.is_drag == false || mouse.is_drag == false) {
return;
}
if (e.type == 'mousedown' && mouse.click_valid == false) {
return;
}
if (this.Base_selection.mouse_lock !== null) {
return;
}
var width = mouse.x - mouse.click_x;
var height = mouse.y - mouse.click_y;
if(e.ctrlKey == true || e.metaKey){
//ctrl is pressed - crop will be calculated based on global width and height ratio
var ratio = config.WIDTH / config.HEIGHT;
var width_new = Math.round(height * ratio);
var height_new = Math.round(width / ratio);
if(Math.abs(width * 100 / width_new) > Math.abs(height * 100 / height_new)){
if (width * 100 / width_new > 0)
height = height_new;
else
height = -height_new;
}
else{
if (height * 100 / height_new > 0)
width = width_new;
else
width = -width_new;
}
}
this.Base_selection.set_selection(null, null, width, height);
}
mouseup(e) {
var mouse = this.get_mouse_info(e);
if (!this.Base_selection.is_drag) {
return;
}
if (e.type == 'mousedown' && mouse.click_valid == false) {
return;
}
var width = mouse.x - this.selection.x;
var height = mouse.y - this.selection.y;
if (width == 0 || height == 0) {
//cancel selection
this.Base_selection.reset_selection();
config.need_render = true;
return;
}
if (this.selection.width != null) {
//make sure coords not negative
var details = this.selection;
var x = details.x;
var y = details.y;
if (details.width < 0) {
x = x + details.width;
}
if (details.height < 0) {
y = y + details.height;
}
this.selection = {
x: x,
y: y,
width: Math.abs(details.width),
height: Math.abs(details.height),
};
}
//control boundaries
if (this.selection.x < 0) {
this.selection.width += this.selection.x;
this.selection.x = 0;
}
if (this.selection.y < 0) {
this.selection.height += this.selection.y;
this.selection.y = 0;
}
if (this.selection.x + this.selection.width > config.WIDTH) {
this.selection.width = config.WIDTH - this.selection.x;
}
if (this.selection.y + this.selection.height > config.HEIGHT) {
this.selection.height = config.HEIGHT - this.selection.y;
}
app.State.do_action(
new app.Actions.Set_selection_action(this.selection.x, this.selection.y, this.selection.width, this.selection.height, this.mousedown_selection)
);
}
render(ctx, layer) {
//nothing
}
/**
* do actual crop
*/
async on_params_update() {
var params = this.getParams();
var selection = this.selection;
params.crop = true;
this.GUI_tools.show_action_attributes();
if (selection.width == null || selection.width == 0 || selection.height == 0) {
alertify.error('Empty selection');
return;
}
//check for rotation
var rotated_name = false;
for (var i in config.layers) {
var link = config.layers[i];
if (link.type == null)
continue;
if(link.rotate > 0){
rotated_name = link.name;
break;
}
}
if (rotated_name !== false) {
alertify.error('Crop on rotated layer is not supported. Convert it to raster to continue.' + '('+ rotated_name + ')');
return;
}
//controll boundaries
selection.x = Math.max(selection.x, 0);
selection.y = Math.max(selection.y, 0);
selection.width = Math.min(selection.width, config.WIDTH);
selection.height = Math.min(selection.height, config.HEIGHT);
let actions = [];
for (var i in config.layers) {
var link = config.layers[i];
if (link.type == null)
continue;
let x = link.x;
let y = link.y;
let width = link.width;
let height = link.height;
let width_original = link.width_original;
let height_original = link.height_original;
//move
x -= parseInt(selection.x);
y -= parseInt(selection.y);
if (link.type == 'image') {
//also remove unvisible data
let left = 0;
if (x < 0)
left = -x;
let top = 0;
if (y < 0)
top = -y;
let right = 0;
if (x + width > selection.width)
right = x + width - selection.width;
let bottom = 0;
if (y + height > selection.height)
bottom = y + height - selection.height;
let crop_width = width - left - right;
let crop_height = height - top - bottom;
//if image was streched
let width_ratio = (width / width_original);
let height_ratio = (height / height_original);
//create smaller canvas
let canvas = document.createElement('canvas');
let ctx = canvas.getContext("2d");
canvas.width = crop_width / width_ratio;
canvas.height = crop_height / height_ratio;
//cut required part
ctx.translate(-left / width_ratio, -top / height_ratio);
canvas.getContext("2d").drawImage(link.link, 0, 0);
ctx.translate(0, 0);
actions.push(
new app.Actions.Update_layer_image_action(canvas, link.id)
);
//update attributes
width = Math.ceil(canvas.width * width_ratio);
height = Math.ceil(canvas.height * height_ratio);
x += left;
y += top;
width_original = canvas.width;
height_original = canvas.height;
}
actions.push(
new app.Actions.Update_layer_action(link.id, {
x,
y,
width,
height,
width_original,
height_original
})
);
}
actions.push(
new app.Actions.Prepare_canvas_action('undo'),
new app.Actions.Update_config_action({
WIDTH: parseInt(selection.width),
HEIGHT: parseInt(selection.height)
}),
new app.Actions.Prepare_canvas_action('do'),
new app.Actions.Reset_selection_action(this.selection)
);
await app.State.do_action(
new app.Actions.Bundle_action('crop_tool', 'Crop Tool', actions)
);
}
on_leave() {
return [
new app.Actions.Reset_selection_action()
];
}
}
export default Crop_class;
+135
View File
@@ -0,0 +1,135 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
import ImageFilters from './../libs/imagefilters.js';
import Helper_class from './../libs/helpers.js';
class Desaturate_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.ctx = ctx;
this.name = 'desaturate';
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
this.started = false;
}
load() {
this.default_events();
}
default_dragMove(event) {
if (config.TOOL.name != this.name)
return;
this.mousemove(event);
//mouse cursor
var mouse = this.get_mouse_info(event);
var params = this.getParams();
this.show_mouse_cursor(mouse.x, mouse.y, params.size, 'circle');
}
mousedown(e) {
this.started = false;
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.click_valid == false) {
return;
}
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
if (config.layer.rotate || 0 > 0) {
alertify.error('Erase on rotate object is disabled. Please rasterize first.');
return;
}
this.started = true;
//get canvas from layer
this.tmpCanvas = document.createElement('canvas');
this.tmpCanvasCtx = this.tmpCanvas.getContext("2d");
this.tmpCanvas.width = config.layer.width_original;
this.tmpCanvas.height = config.layer.height_original;
this.tmpCanvasCtx.drawImage(config.layer.link, 0, 0);
//do desaturate
this.desaturate_general('click', mouse, params.size, params.anti_aliasing);
//register tmp canvas for faster redraw
config.layer.link_canvas = this.tmpCanvas;
config.need_render = true;
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
if (this.started == false) {
return;
}
//do desaturate
this.desaturate_general('move', mouse, params.size, params.anti_aliasing);
//draw draft preview
config.need_render = true;
}
mouseup(e) {
if (this.started == false) {
return;
}
delete config.layer.link_canvas;
app.State.do_action(
new app.Actions.Bundle_action('desaturate_tool', 'Desaturate Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
//decrease memory
this.tmpCanvas.width = 1;
this.tmpCanvas.height = 1;
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
}
desaturate_general(type, mouse, size, anti_aliasing) {
var ctx = this.tmpCanvasCtx;
var mouse_x = Math.round(mouse.x) - config.layer.x;
var mouse_y = Math.round(mouse.y) - config.layer.y;
//adapt to origin size
mouse_x = this.adaptSize(mouse_x, 'width');
mouse_y = this.adaptSize(mouse_y, 'height');
var size_w = this.adaptSize(size, 'width');
var size_h = this.adaptSize(size, 'height');
//find center
var center_x = mouse_x - Math.round(size_w / 2);
var center_y = mouse_y - Math.round(size_h / 2);
//convert float coords to integers
center_x = Math.round(center_x);
center_y = Math.round(center_y);
mouse_x = Math.round(mouse_x);
mouse_y = Math.round(mouse_y);
var imageData = ctx.getImageData(center_x, center_y, size_w, size_h);
var filtered = ImageFilters.GrayScale(imageData); //add effect
this.Helper.image_round(this.tmpCanvasCtx, mouse_x, mouse_y, size_w, size_h, filtered, anti_aliasing);
}
}
export default Desaturate_class;
+207
View File
@@ -0,0 +1,207 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
class Erase_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'erase';
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
this.started = false;
}
load() {
this.default_events();
}
default_dragMove(event, is_touch) {
if (config.TOOL.name != this.name)
return;
this.mousemove(event, is_touch);
//mouse cursor
var mouse = this.get_mouse_info(event);
var params = this.getParams();
if (params.circle == true)
this.show_mouse_cursor(mouse.x, mouse.y, params.size, 'circle');
else
this.show_mouse_cursor(mouse.x, mouse.y, params.size, 'rect');
}
on_params_update() {
var params = this.getParams();
var strict_element = document.querySelector('.attributes #strict');
if (params.circle == false) {
//hide strict controls
strict_element.style.display = 'none';
}
else {
//show strict controls
strict_element.style.display = 'block';
}
}
mousedown(e) {
this.started = false;
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.click_valid == false) {
return;
}
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
if (config.layer.is_vector == true) {
alertify.error('Layer is vector, convert it to raster to apply this tool.');
return;
}
if (config.layer.rotate || 0 > 0) {
alertify.error('Erase on rotate object is disabled. Please rasterize first.');
return;
}
this.started = true;
//get canvas from layer
this.tmpCanvas = document.createElement('canvas');
this.tmpCanvasCtx = this.tmpCanvas.getContext("2d");
this.tmpCanvas.width = config.layer.width_original;
this.tmpCanvas.height = config.layer.height_original;
this.tmpCanvasCtx.drawImage(config.layer.link, 0, 0);
this.tmpCanvasCtx.scale(
config.layer.width_original / config.layer.width,
config.layer.height_original / config.layer.height
);
//do erase
this.erase_general(this.tmpCanvasCtx, 'click', mouse, params.size, params.strict, params.circle);
//register tmp canvas for faster redraw
config.layer.link_canvas = this.tmpCanvas;
config.need_render = true;
}
mousemove(e, is_touch) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
if (this.started == false) {
return;
}
if (mouse.click_x == mouse.x && mouse.click_y == mouse.y) {
//same coordinates
return;
}
//do erase
this.erase_general(this.tmpCanvasCtx, 'move', mouse, params.size, params.strict, params.circle, is_touch);
//draw draft preview
config.need_render = true;
}
mouseup(e) {
if (this.started == false) {
return;
}
delete config.layer.link_canvas;
app.State.do_action(
new app.Actions.Bundle_action('erase_tool', 'Erase Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
//decrease memory
this.tmpCanvas.width = 1;
this.tmpCanvas.height = 1;
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
}
erase_general(ctx, type, mouse, size, strict, is_circle, is_touch) {
var mouse_x = Math.round(mouse.x) - config.layer.x;
var mouse_y = Math.round(mouse.y) - config.layer.y;
var alpha = config.ALPHA;
var mouse_last_x = parseInt(mouse.last_x) - config.layer.x;
var mouse_last_y = parseInt(mouse.last_y) - config.layer.y;
ctx.beginPath();
ctx.lineWidth = size;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
if (alpha < 255)
ctx.strokeStyle = "rgba(255, 255, 255, " + alpha / 255 / 10 + ")";
else
ctx.strokeStyle = "rgba(255, 255, 255, 1)";
if (is_circle == false) {
//rectangle
var size_half = Math.ceil(size / 2);
if (size == 1) {
//single cell mode
mouse_x = Math.floor(mouse.x) - config.layer.x;
mouse_y = Math.floor(mouse.y) - config.layer.y;
size_half = 0;
}
ctx.save();
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = "rgba(255, 255, 255, " + alpha / 255 + ")";
ctx.fillRect(mouse_x - size_half, mouse_y - size_half, size, size);
ctx.restore();
}
else {
//circle
ctx.save();
if (strict == false) {
var radgrad = ctx.createRadialGradient(
mouse_x, mouse_y, size / 8,
mouse_x, mouse_y, size / 2);
if (type == 'click')
radgrad.addColorStop(0, "rgba(255, 255, 255, " + alpha / 255 + ")");
else if (type == 'move')
radgrad.addColorStop(0, "rgba(255, 255, 255, " + alpha / 255 / 2 + ")");
radgrad.addColorStop(1, "rgba(255, 255, 255, 0)");
}
//set Composite
ctx.globalCompositeOperation = 'destination-out';
if (strict == true)
ctx.fillStyle = "rgba(255, 255, 255, " + alpha / 255 + ")";
else
ctx.fillStyle = radgrad;
ctx.beginPath();
ctx.arc(mouse_x, mouse_y, size / 2, 0, Math.PI * 2, true);
ctx.fill();
ctx.restore();
}
//extra work if mouse moving fast - fill gaps
if (type == 'move' && is_circle == true && mouse_last_x != false && mouse_last_y != false && is_touch !== true) {
ctx.save();
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath();
ctx.moveTo(mouse_last_x, mouse_last_y);
ctx.lineTo(mouse_x, mouse_y);
ctx.stroke();
ctx.restore();
}
}
}
export default Erase_class;
+231
View File
@@ -0,0 +1,231 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import Helper_class from './../libs/helpers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
class Fill_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.ctx = ctx;
this.name = 'fill';
this.working = false;
}
dragStart(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
_this.mousedown(event);
}
load() {
var _this = this;
//mouse events
document.addEventListener('mousedown', function (event) {
_this.dragStart(event);
});
// collect touch events
document.addEventListener('touchstart', function (event) {
_this.dragStart(event);
});
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false) {
return;
}
if (config.layer.rotate || 0 > 0) {
alertify.error('Erase on rotate object is disabled. Please rasterize first.');
return;
}
this.fill(mouse);
}
async fill(mouse) {
var params = this.getParams();
if(this.working == true){
return;
}
if (config.layer.type != 'image' && config.layer.type !== null) {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
if (config.layer.is_vector == true) {
alertify.error('Layer is vector, convert it to raster to apply this tool.');
return;
}
if (config.ALPHA == 0) {
alertify.error('Color alpha value can not be zero.');
return;
}
//get canvas from layer
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
if (config.layer.type !== null) {
canvas.width = config.layer.width_original;
canvas.height = config.layer.height_original;
ctx.drawImage(config.layer.link, 0, 0);
}
else {
canvas.width = config.WIDTH;
canvas.height = config.HEIGHT;
}
var mouse_x = Math.round(mouse.x) - config.layer.x;
var mouse_y = Math.round(mouse.y) - config.layer.y;
//adapt to origin size
mouse_x = this.adaptSize(mouse_x, 'width');
mouse_y = this.adaptSize(mouse_y, 'height');
//convert float coords to integers
mouse_x = Math.round(mouse_x);
mouse_y = Math.round(mouse_y);
var color_to = this.Helper.hexToRgb(config.COLOR);
color_to.a = config.ALPHA;
//change
this.working = true;
this.fill_general(ctx, config.WIDTH, config.HEIGHT,
mouse_x, mouse_y, color_to, params.power, params.anti_aliasing, params.contiguous);
if (config.layer.type != null) {
//update
app.State.do_action(
new app.Actions.Bundle_action('fill_tool', 'Fill Tool', [
new app.Actions.Update_layer_image_action(canvas)
])
);
}
else {
//create new
var params = [];
params.type = 'image';
params.name = 'Fill';
params.data = canvas.toDataURL("image/png");
params.x = parseInt(canvas.dataset.x) || 0;
params.y = parseInt(canvas.dataset.y) || 0;
params.width = canvas.width;
params.height = canvas.height;
app.State.do_action(
new app.Actions.Bundle_action('fill_tool', 'Fill Tool', [
new app.Actions.Insert_layer_action(params)
])
);
}
//prevent crash bug on touch screen - hard to explain and debug
await new Promise(r => setTimeout(r, 10));
this.working = false;
}
fill_general(context, W, H, x, y, color_to, sensitivity, anti_aliasing, contiguous = false) {
sensitivity = sensitivity * 255 / 100; //convert to 0-255 interval
x = parseInt(x);
y = parseInt(y);
var canvasTemp = document.createElement('canvas');
canvasTemp.width = W;
canvasTemp.height = H;
var ctxTemp = canvasTemp.getContext("2d");
ctxTemp.rect(0, 0, W, H);
ctxTemp.fillStyle = "rgba(255, 255, 255, 0)";
ctxTemp.fill();
var img_tmp = ctxTemp.getImageData(0, 0, W, H);
var imgData_tmp = img_tmp.data;
var img = context.getImageData(0, 0, W, H);
var imgData = img.data;
var k = ((y * (img.width * 4)) + (x * 4));
var dx = [0, -1, +1, 0];
var dy = [-1, 0, 0, +1];
var color_from = {
r: imgData[k + 0],
g: imgData[k + 1],
b: imgData[k + 2],
a: imgData[k + 3]
};
if (color_from.r == color_to.r && color_from.g == color_to.g
&& color_from.b == color_to.b && color_from.a == color_to.a) {
return false;
}
if (contiguous == false) {
//check only nearest pixels
var stack = [];
stack.push([x, y]);
while (stack.length > 0) {
var curPoint = stack.pop();
for (var i = 0; i < 4; i++) {
var nextPointX = curPoint[0] + dx[i];
var nextPointY = curPoint[1] + dy[i];
if (nextPointX < 0 || nextPointY < 0 || nextPointX >= W || nextPointY >= H)
continue;
var k = (nextPointY * W + nextPointX) * 4;
if (imgData_tmp[k + 3] != 0)
continue; //already parsed
//check
if (Math.abs(imgData[k + 0] - color_from.r) <= sensitivity &&
Math.abs(imgData[k + 1] - color_from.g) <= sensitivity &&
Math.abs(imgData[k + 2] - color_from.b) <= sensitivity &&
Math.abs(imgData[k + 3] - color_from.a) <= sensitivity) {
//fill pixel
imgData_tmp[k] = color_to.r; //r
imgData_tmp[k + 1] = color_to.g; //g
imgData_tmp[k + 2] = color_to.b; //b
imgData_tmp[k + 3] = color_to.a; //a
stack.push([nextPointX, nextPointY]);
}
}
}
}
else {
//global mode - contiguous
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
//imgData[i] + 0.7152 * imgData[i + 1] + 0.0722 * imgData[i + 2]);
for (var j = 0; j < 4; j++) {
var k = i + j;
if (Math.abs(imgData[k] - color_from.r) <= sensitivity
&& Math.abs(imgData[k + 1] - color_from.g) <= sensitivity
&& Math.abs(imgData[k + 2] - color_from.b) <= sensitivity
&& Math.abs(imgData[k + 3] - color_from.a) <= sensitivity) {
imgData_tmp[k] = color_to.r; //r
imgData_tmp[k + 1] = color_to.g; //g
imgData_tmp[k + 2] = color_to.b; //b
imgData_tmp[k + 3] = color_to.a; //a
}
}
}
}
ctxTemp.putImageData(img_tmp, 0, 0);
if (anti_aliasing == true) {
context.filter = 'blur(1px)';
}
context.drawImage(canvasTemp, 0, 0);
}
}
export default Fill_class;
+184
View File
@@ -0,0 +1,184 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import Helper_class from './../libs/helpers.js';
class Gradient_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.ctx = ctx;
this.name = 'gradient';
this.layer = {};
}
load() {
this.default_events();
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.click_valid == false)
return;
var name = this.name;
var is_vector = false;
if (params.radial == true) {
name = 'Radial gradient';
is_vector = true;
}
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
name: this.Helper.ucfirst(name) + ' #' + this.Base_layers.auto_increment,
params: this.clone(this.getParams()),
status: 'draft',
render_function: [this.name, 'render'],
x: mouse.x,
y: mouse.y,
rotate: null,
is_vector: is_vector,
color: null,
data: {
center_x: mouse.x,
center_y: mouse.y,
},
};
app.State.do_action(
new app.Actions.Bundle_action('new_gradient_layer', 'New Gradient Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
var width = mouse.x - this.layer.x;
var height = mouse.y - this.layer.y;
if (params.radial == true) {
config.layer.x = this.layer.data.center_x - width;
config.layer.y = this.layer.data.center_y - height;
config.layer.width = width * 2;
config.layer.height = height * 2;
}
else {
config.layer.width = width;
config.layer.height = height;
}
this.Base_layers.render();
}
mouseup(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.click_valid == false) {
config.layer.status = null;
return;
}
var width = mouse.x - this.layer.x;
var height = mouse.y - this.layer.y;
if (width == 0 && height == 0) {
//same coordinates - cancel
app.State.scrap_last_action();
return;
}
let new_settings = {};
if (params.radial == true) {
new_settings = {
x: this.layer.data.center_x - width,
y: this.layer.data.center_y - height,
width: width * 2,
height: height * 2
}
}
else {
new_settings = {
width,
height
}
}
new_settings.status = null;
app.State.do_action(
new app.Actions.Update_layer_action(config.layer.id, new_settings),
{ merge_with_history: 'new_gradient_layer' }
);
this.Base_layers.render();
}
render(ctx, layer) {
if (layer.width == 0 && layer.height == 0)
return;
var params = layer.params;
var power = params.radial_power;
if(power > 99){
power = 99;
}
var alpha = params.alpha / 100 * 255;
if(power > 255){
power = 255;
}
var color1 = params.color_1;
var color2 = params.color_2;
var radial = params.radial;
var color2_rgb = this.Helper.hexToRgb(color2);
var width = layer.x + layer.width - 1;
var height = layer.y + layer.height - 1;
if (radial == false) {
//linear
ctx.beginPath();
ctx.rect(0, 0, config.WIDTH, config.HEIGHT);
var grd = ctx.createLinearGradient(
layer.x, layer.y,
width, height);
grd.addColorStop(0, color1);
grd.addColorStop(1, "rgba(" + color2_rgb.r + ", " + color2_rgb.g + ", "
+ color2_rgb.b + ", " + alpha / 255 + ")");
ctx.fillStyle = grd;
ctx.fill();
}
else {
//radial
var dist_x = layer.width;
var dist_y = layer.height;
var center_x = layer.x + Math.round(layer.width / 2);
var center_y = layer.y + Math.round(layer.height / 2);
var distance = Math.sqrt((dist_x * dist_x) + (dist_y * dist_y));
var radgrad = ctx.createRadialGradient(
center_x, center_y, distance * power / 100,
center_x, center_y, distance);
radgrad.addColorStop(0, color1);
radgrad.addColorStop(1, "rgba(" + color2_rgb.r + ", " + color2_rgb.g + ", "
+ color2_rgb.b + ", " + alpha / 255 + ")");
ctx.fillStyle = radgrad;
ctx.fillRect(0, 0, config.WIDTH, config.HEIGHT);
}
}
}
export default Gradient_class;
+214
View File
@@ -0,0 +1,214 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
class Magic_erase_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'magic_erase';
this.working = false;
}
dragStart(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
_this.mousedown(event);
}
load() {
var _this = this;
//mouse events
document.addEventListener('mousedown', function (event) {
_this.dragStart(event);
});
// collect touch events
document.addEventListener('touchstart', function (event) {
_this.dragStart(event);
});
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false) {
return;
}
if (config.layer.rotate || 0 > 0) {
alertify.error('Erase on rotate object is disabled. Please rasterize first.');
return;
}
this.magic_erase(mouse);
}
async magic_erase(mouse) {
var params = this.getParams();
if(this.working == true){
return;
}
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
if (config.layer.is_vector == true) {
alertify.error('Layer is vector, convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
canvas.width = config.layer.width_original;
canvas.height = config.layer.height_original;
ctx.drawImage(config.layer.link, 0, 0);
var mouse_x = Math.round(mouse.x) - config.layer.x;
var mouse_y = Math.round(mouse.y) - config.layer.y;
//adapt to origin size
mouse_x = this.adaptSize(mouse_x, 'width');
mouse_y = this.adaptSize(mouse_y, 'height');
//convert float coords to integers
mouse_x = Math.round(mouse_x);
mouse_y = Math.round(mouse_y);
//change
this.working = true;
this.magic_erase_general(ctx, config.WIDTH, config.HEIGHT,
mouse_x, mouse_y, params.power, params.anti_aliasing, params.contiguous);
app.State.do_action(
new app.Actions.Bundle_action('magic_erase_tool', 'Magic Eraser Tool', [
new app.Actions.Update_layer_image_action(canvas)
])
);
//prevent crash bug on touch screen - hard to explain and debug
await new Promise(r => setTimeout(r, 10));
this.working = false;
}
/**
* apply magic erase
*
* @param {ctx} context
* @param {int} W
* @param {int} H
* @param {int} x
* @param {int} y
* @param {int} sensitivity max 100
* @param {Boolean} anti_aliasing
*/
magic_erase_general(context, W, H, x, y, sensitivity, anti_aliasing, contiguous = false) {
sensitivity = sensitivity * 255 / 100; //convert to 0-255 interval
x = parseInt(x);
y = parseInt(y);
var canvasTemp = document.createElement('canvas');
canvasTemp.width = W;
canvasTemp.height = H;
var ctxTemp = canvasTemp.getContext("2d");
ctxTemp.rect(0, 0, W, H);
ctxTemp.fillStyle = "rgba(255, 255, 255, 0)";
ctxTemp.fill();
var img_tmp = ctxTemp.getImageData(0, 0, W, H);
var imgData_tmp = img_tmp.data;
var img = context.getImageData(0, 0, W, H);
var imgData = img.data;
var k = ((y * (img.width * 4)) + (x * 4));
var dx = [0, -1, +1, 0];
var dy = [-1, 0, 0, +1];
var color_to = {
r: 255,
g: 255,
b: 255,
a: 255
};
var color_from = {
r: imgData[k + 0],
g: imgData[k + 1],
b: imgData[k + 2],
a: imgData[k + 3]
};
if (color_from.r == color_to.r &&
color_from.g == color_to.g &&
color_from.b == color_to.b &&
color_from.a == 0) {
return false;
}
if (contiguous == false) {
//check only nearest pixels
var stack = [];
stack.push([x, y]);
while (stack.length > 0) {
var curPoint = stack.pop();
for (var i = 0; i < 4; i++) {
var nextPointX = curPoint[0] + dx[i];
var nextPointY = curPoint[1] + dy[i];
if (nextPointX < 0 || nextPointY < 0 || nextPointX >= W || nextPointY >= H)
continue;
var k = (nextPointY * W + nextPointX) * 4;
if (imgData_tmp[k + 3] != 0)
continue; //already parsed
if (Math.abs(imgData[k] - color_from.r) <= sensitivity
&& Math.abs(imgData[k + 1] - color_from.g) <= sensitivity
&& Math.abs(imgData[k + 2] - color_from.b) <= sensitivity
&& Math.abs(imgData[k + 3] - color_from.a) <= sensitivity) {
//erase
imgData_tmp[k] = color_to.r; //r
imgData_tmp[k + 1] = color_to.g; //g
imgData_tmp[k + 2] = color_to.b; //b
imgData_tmp[k + 3] = color_to.a; //a
stack.push([nextPointX, nextPointY]);
}
}
}
}
else {
//global mode - contiguous
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
//imgData[i] + 0.7152 * imgData[i + 1] + 0.0722 * imgData[i + 2]);
for (var j = 0; j < 4; j++) {
var k = i + j;
if (Math.abs(imgData[k] - color_from.r) <= sensitivity
&& Math.abs(imgData[k + 1] - color_from.g) <= sensitivity
&& Math.abs(imgData[k + 2] - color_from.b) <= sensitivity
&& Math.abs(imgData[k + 3] - color_from.a) <= sensitivity) {
imgData_tmp[k] = color_to.r; //r
imgData_tmp[k + 1] = color_to.g; //g
imgData_tmp[k + 2] = color_to.b; //b
imgData_tmp[k + 3] = color_to.a; //a
}
}
}
}
//destination-out + blur = anti-aliasing
ctxTemp.putImageData(img_tmp, 0, 0);
context.globalCompositeOperation = "destination-out";
if (anti_aliasing == true) {
context.filter = 'blur(1px)';
}
context.drawImage(canvasTemp, 0, 0);
}
}
export default Magic_erase_class;
+164
View File
@@ -0,0 +1,164 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import File_open_class from './../modules/file/open.js';
import Tools_settings_class from './../modules/tools/settings.js';
import Dialog_class from './../libs/popup.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
class Media_class extends Base_tools_class {
constructor(ctx) {
super();
this.File_open = new File_open_class();
this.Tools_settings = new Tools_settings_class();
this.POP = new Dialog_class();
this.name = 'media';
this.cache = [];
this.page = 1;
this.per_page = 50;
}
load() {
//nothing
}
render(ctx, layer) {
//nothing
}
on_activate() {
this.search();
}
/**
* Image search api
*
* @param {string} query
* @param {array} data
* @param pages
*/
search(query = '', data = [], pages = null) {
var _this = this;
var html = '';
var html_paging = '';
var key = config.pixabay_key;
key = key.split("").reverse().join("");
var safe_search = this.Tools_settings.get_setting('safe_search');
if (data.length > 0) {
for (var i in data) {
html += '<div class="item">';
html += ' <img class="displayBlock pointer" alt="" src="' + data[i].previewURL + '" data-url="' + data[i].webformatURL + '" />';
html += '</div>';
}
//fix for last line
html += '<div class="item"></div>';
html += '<div class="item"></div>';
html += '<div class="item"></div>';
html += '<div class="item"></div>';
//paging
html_paging += '<div class="media-paging" id="media_paging">';
html_paging += '<button type="button" data-value="1" title="Previous">&lt;</button> ';
for(var i = 1; i <= Math.min(10, pages); i++) {
var selected = '';
if(this.page == i){
var selected = 'selected';
}
html_paging += '<button type="button" class="'+selected+'" data-value="'+i+'">'+i+'</button> ';
}
html_paging += '<button type="button" data-value="'+Math.min(this.page + 1, pages)+'" title="Next">&gt;</button> ';
html_paging += '</div>';
}
else{
this.page = 1;
}
var settings = {
title: 'Search',
//comment: 'Source: <a class="text_muted" href="https://pixabay.com/">pixabay.com</a>.',
className: 'wide',
params: [
{name: "query", title: "Keyword:", value: query},
],
on_load: function (params, popup) {
var node = document.createElement("div");
node.classList.add('flex-container');
node.innerHTML = html + html_paging;
popup.el.querySelector('.dialog_content').appendChild(node);
//events
var targets = popup.el.querySelectorAll('.item img');
for (var i = 0; i < targets.length; i++) {
targets[i].addEventListener('click', function (event) {
//we have click
var data = {
url: this.dataset.url,
};
_this.File_open.file_open_url_handler(data);
_this.POP.hide();
new app.Actions.Activate_tool_action('select', true).do();
});
}
var targets = popup.el.querySelectorAll('#media_paging button');
for (var i = 0; i < targets.length; i++) {
targets[i].addEventListener('click', function (event) {
//we have click
_this.page = parseInt(this.dataset.value);
_this.POP.save();
});
}
},
on_finish: function (params) {
if (params.query == '')
return;
var URL = "https://pixabay.com/api/?key=" + key
+ "&page=" + _this.page
+ "&per_page=" + _this.per_page
+ "&safesearch=" + safe_search
+ "&q=" + encodeURIComponent(params.query);
if (_this.cache[URL] != undefined) {
//using cache
setTimeout(function () {
//only call same function after all handlers finishes
var data = _this.cache[URL];
if (parseInt(data.totalHits) == 0) {
alertify.error('Your search did not match any images.');
}
var pages = Math.ceil(data.totalHits / _this.per_page);
_this.search(params.query, data.hits, pages);
}, 100);
}
else {
//query to service
$.getJSON(URL, function (data) {
_this.cache[URL] = data;
if (parseInt(data.totalHits) == 0) {
alertify.error('Your search did not match any images.');
}
var pages = Math.ceil(data.totalHits / _this.per_page);
_this.search(params.query, data.hits, pages);
})
.fail(function () {
alertify.error('Error connecting to service.');
});
}
},
};
this.POP.show(settings);
document.getElementById("pop_data_query").select();
}
}
export default Media_class;
+306
View File
@@ -0,0 +1,306 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
class Pencil_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.name = 'pencil';
this.layer = {};
this.params_hash = false;
this.pressure_supported = false;
this.pointer_pressure = 0; // has range [0 - 1]
}
load() {
var _this = this;
//pointer events
document.addEventListener('pointerdown', function (event) {
_this.pointerdown(event);
});
document.addEventListener('pointermove', function (event) {
_this.pointermove(event);
});
this.default_events();
}
dragMove(event) {
if (config.TOOL.name != this.name)
return;
this.mousemove(event);
}
pointerdown(e) {
// Devices that don't actually support pen pressure can give 0.5 as a false reading.
// It is highly unlikely a real pen will read exactly 0.5 at the start of a stroke.
if (e.pressure && e.pressure !== 0 && e.pressure !== 0.5 && e.pressure <= 1) {
this.pressure_supported = true;
this.pointer_pressure = e.pressure;
} else {
this.pressure_supported = false;
}
}
pointermove(e) {
// Pressure of exactly 1 seems to be an input error, sometimes I see it when lifting the pen
// off the screen when pressure reading should be near 0.
if (this.pressure_supported && e.pressure < 1) {
this.pointer_pressure = e.pressure;
}
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false)
return;
var params_hash = this.get_params_hash();
var opacity = Math.round(config.ALPHA / 255 * 100);
if (config.layer.type != this.name || params_hash != this.params_hash) {
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
data: [],
opacity: opacity,
params: this.clone(this.getParams()),
status: 'draft',
render_function: [this.name, 'render'],
x: 0,
y: 0,
width: config.WIDTH,
height: config.HEIGHT,
hide_selection_if_active: true,
rotate: null,
is_vector: true,
color: config.COLOR
};
app.State.do_action(
new app.Actions.Bundle_action('new_pencil_layer', 'New Pencil Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
this.params_hash = params_hash;
}
else {
//continue adding layer data, just register break
const new_data = JSON.parse(JSON.stringify(config.layer.data));
new_data.push(null);
app.State.do_action(
new app.Actions.Bundle_action('update_pencil_layer', 'Update Pencil Layer', [
new app.Actions.Update_layer_action(config.layer.id, {
data: new_data
})
])
);
}
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
//detect line size
var size = params.size;
var new_size = size;
if (params.pressure == true && this.pressure_supported) {
new_size = size * this.pointer_pressure * 2;
}
//more data
config.layer.data.push([
Math.ceil(mouse.x - config.layer.x),
Math.ceil(mouse.y - config.layer.y),
new_size
]);
this.Base_layers.render();
}
mouseup(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.click_valid == false) {
config.layer.status = null;
return;
}
//detect line size
var size = params.size;
var new_size = size;
if (params.pressure == true && this.pressure_supported) {
new_size = size * this.pointer_pressure * 2;
}
//more data
config.layer.data.push([
Math.ceil(mouse.x - config.layer.x),
Math.ceil(mouse.y - config.layer.y),
new_size
]);
this.check_dimensions();
config.layer.status = null;
this.Base_layers.render();
}
render(ctx, layer) {
this.render_aliased(ctx, layer);
}
/**
* draw without antialiasing, sharp, ugly mode.
*
* @param {object} ctx
* @param {object} layer
*/
render_aliased(ctx, layer) {
if (layer.data.length == 0)
return;
var params = layer.params;
var data = layer.data;
var n = data.length;
var size = params.size;
//set styles
ctx.fillStyle = layer.color;
ctx.strokeStyle = layer.color;
ctx.translate(layer.x, layer.y);
//draw
ctx.beginPath();
ctx.moveTo(data[0][0], data[0][1]);
for (var i = 1; i < n; i++) {
if (data[i] === null) {
//break
ctx.beginPath();
}
else {
//line
size = data[i][2];
if(size == undefined){
size = 1;
}
if (data[i - 1] == null) {
//exception - point
ctx.fillRect(
data[i][0] - Math.floor(size / 2) - 1,
data[i][1] - Math.floor(size / 2) - 1,
size,
size
);
}
else {
//lines
ctx.beginPath();
this.draw_simple_line(
ctx,
data[i - 1][0],
data[i - 1][1],
data[i][0],
data[i][1],
size
);
}
}
}
if (n == 1 || data[1] == null) {
//point
ctx.beginPath();
ctx.fillRect(
data[0][0] - Math.floor(size / 2) - 1,
data[0][1] - Math.floor(size / 2) - 1,
size,
size
);
}
ctx.translate(-layer.x, -layer.y);
}
/**
* draws line without aliasing
*
* @param {object} ctx
* @param {int} from_x
* @param {int} from_y
* @param {int} to_x
* @param {int} to_y
* @param {int} size
*/
draw_simple_line(ctx, from_x, from_y, to_x, to_y, size) {
var dist_x = from_x - to_x;
var dist_y = from_y - to_y;
var distance = Math.sqrt((dist_x * dist_x) + (dist_y * dist_y));
var radiance = Math.atan2(dist_y, dist_x);
for (var j = 0; j < distance; j++) {
var x_tmp = Math.round(to_x + Math.cos(radiance) * j) - Math.floor(size / 2) - 1;
var y_tmp = Math.round(to_y + Math.sin(radiance) * j) - Math.floor(size / 2) - 1;
ctx.fillRect(x_tmp, y_tmp, size, size);
}
}
/**
* recalculate layer x, y, width and height values.
*/
check_dimensions() {
if(config.layer.data.length == 0)
return;
//find bounds
var data = JSON.parse(JSON.stringify(config.layer.data)); // Deep copy for history
var min_x = data[0][0];
var min_y = data[0][1];
var max_x = data[0][0];
var max_y = data[0][1];
for(var i in data){
if(data[i] === null)
continue;
min_x = Math.min(min_x, data[i][0]);
min_y = Math.min(min_y, data[i][1]);
max_x = Math.max(max_x, data[i][0]);
max_y = Math.max(max_y, data[i][1]);
}
//move current data
for(var i in data){
if(data[i] === null)
continue;
data[i][0] = data[i][0] - min_x;
data[i][1] = data[i][1] - min_y;
}
//change layers bounds
app.State.do_action(
new app.Actions.Update_layer_action(config.layer.id, {
x: config.layer.x + min_x,
y: config.layer.y + min_y,
width: max_x - min_x,
height: max_y - min_y,
data
}),
{
merge_with_history: ['new_pencil_layer', 'update_pencil_layer']
}
);
}
}
export default Pencil_class;
+111
View File
@@ -0,0 +1,111 @@
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import Helper_class from './../libs/helpers.js';
import Base_gui_class from './../core/base-gui.js';
class Pick_color_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.Base_gui = new Base_gui_class();
this.ctx = ctx;
this.name = 'pick_color';
}
dragStart(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
_this.mousedown(event);
}
dragMove(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
_this.mousemove(event);
}
load() {
var _this = this;
//mouse events
document.addEventListener('mousedown', function (event) {
_this.dragStart(event);
});
document.addEventListener('mousemove', function (event) {
_this.dragMove(event);
});
document.addEventListener('mouseup', function (event) {
var mouse = _this.get_mouse_info(event);
if (config.TOOL.name != _this.name || mouse.click_valid == false)
return;
_this.copy_color_to_clipboard();
});
// collect touch events
document.addEventListener('touchstart', function (event) {
_this.dragStart(event);
});
document.addEventListener('touchmove', function (event) {
_this.dragMove(event);
});
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false) {
return;
}
this.pick_color(mouse);
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
if (mouse.is_drag == false || mouse.click_valid == false) {
return;
}
this.pick_color(mouse);
}
pick_color(mouse) {
var params = this.getParams();
//get canvas from layer
if (params.global == false) {
//active layer
var canvas = this.Base_layers.convert_layer_to_canvas(config.layer.id, null, false);
var ctx = canvas.getContext("2d");
}
else {
//global
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
canvas.width = config.WIDTH;
canvas.height = config.HEIGHT;
this.Base_layers.convert_layers_to_canvas(ctx, null, false);
}
//find color
var c = ctx.getImageData(mouse.x, mouse.y, 1, 1).data;
var hex = this.Helper.rgbToHex(c[0], c[1], c[2]);
const newColorDefinition = { hex };
if (c[3] > 0) {
//set alpha
newColorDefinition.a = c[3];
}
this.Base_gui.GUI_colors.set_color(newColorDefinition);
}
copy_color_to_clipboard() {
navigator.clipboard.writeText(config.COLOR);
}
}
export default Pick_color_class;
+577
View File
@@ -0,0 +1,577 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import Base_selection_class from './../core/base-selection.js';
import Helper_class from './../libs/helpers.js';
import Dialog_class from './../libs/popup.js';
class Select_tool_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.POP = new Dialog_class();
this.Helper = new Helper_class();
this.ctx = ctx;
this.name = 'select';
this.saved = false;
this.mousedown_dimensions = { x: null, y: null, width: null, height: null };
this.keyboard_move_start_position = null;
this.moving = false;
this.resizing = false;
this.snap_line_info = {x: null, y: null};
this.rotate_initial = null;
var sel_config = {
enable_background: false,
enable_borders: true,
enable_controls: true,
keep_ratio: true,
enable_rotation: true,
enable_move: true,
data_function: function () {
return config.layer;
},
};
this.Base_selection = new Base_selection_class(ctx, sel_config, this.name);
}
load() {
var _this = this;
//mouse events
document.addEventListener('mousedown', function (e) {
_this.dragStart(e);
});
document.addEventListener('mousemove', function (e) {
_this.dragMove(e);
});
document.addEventListener('mouseup', function (e) {
_this.dragEnd(e);
});
// collect touch events
document.addEventListener('touchstart', function (e) {
_this.dragStart(e);
});
document.addEventListener('touchmove', function (e) {
_this.dragMove(e);
});
document.addEventListener('touchend', function (e) {
_this.dragEnd(e);
});
//keyboard actions
document.addEventListener('keydown', (event) => {
if (config.TOOL.name != this.name)
return;
if (this.POP.get_active_instances() > 0) {
return;
}
if (this.Helper.is_input(event.target))
return;
var k = event.key;
if (k == "ArrowUp") {
this.move(0, -1, event);
}
else if (k == "ArrowDown") {
this.move(0, 1, event);
}
else if (k == "ArrowRight") {
this.move(1, 0, event);
}
else if (k == "ArrowLeft") {
this.move(-1, 0, event);
}
if (k == "Delete") {
if (config.TOOL.name == this.name) {
app.State.do_action(
new app.Actions.Delete_layer_action(config.layer.id)
);
}
}
});
document.addEventListener('keyup', (event) => {
if (config.TOOL.name != this.name)
return;
if (this.POP.active == true)
return;
if (this.Helper.is_input(event.target))
return;
var k = event.key;
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(k)) {
if (this.keyboard_move_start_position) {
let x = config.layer.x;
let y = config.layer.y;
config.layer.x = this.keyboard_move_start_position.x;
config.layer.y = this.keyboard_move_start_position.y;
app.State.do_action(
new app.Actions.Update_layer_action(config.layer.id, { x, y })
);
this.keyboard_move_start_position = null;
}
}
});
}
dragStart(event) {
var mouse = this.get_mouse_info(event);
if (config.TOOL.name != this.name)
return;
if (mouse.click_valid == false) {
return;
}
this.mousedown(event);
}
dragMove(event) {
var mouse = this.get_mouse_info(event);
if (config.TOOL.name != this.name)
return;
if (mouse.click_valid == false) {
return;
}
this.mousemove(event);
}
dragEnd(event) {
var mouse = this.get_mouse_info(event);
if (config.TOOL.name != this.name)
return;
if (mouse.click_valid == false) {
return;
}
this.mouseup(event);
this.Base_layers.render();
}
async mousedown(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false || config.mouse_lock === true) {
return;
}
this.rotate_initial = config.layer.rotate;
if (this.Base_selection.mouse_lock != null) {
this.resizing = true;
this.Base_selection.find_settings().keep_ratio = config.layer.type === 'image';
if (config.layer.type === 'text' && config.layer.params && config.layer.params.boundary === 'dynamic') {
config.layer.params.boundary = 'box';
}
}
else {
this.moving = true;
await this.auto_select_object(e);
this.Base_selection.find_settings().keep_ratio = config.layer.type === 'image';
this.saved = false;
}
this.mousedown_dimensions = {
x: Math.round(config.layer.x),
y: Math.round(config.layer.y),
width: Math.round(config.layer.width),
height: Math.round(config.layer.height)
};
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
if (mouse.is_drag == false || mouse.click_valid == false || config.mouse_lock === true) {
return;
}
if (this.resizing) {
//also handle rotation
let rotate = this.Base_selection.current_angle
if(config.layer.rotate != rotate && rotate !== null){
config.layer.rotate = rotate;
}
return;
}
else if (this.moving) {
//move object
config.layer.x = Math.round(mouse.x - mouse.click_x + this.mousedown_dimensions.x);
config.layer.y = Math.round(mouse.y - mouse.click_y + this.mousedown_dimensions.y);
//apply snap
var snap_info = this.calc_snap(e, config.layer.x, config.layer.y);
if(snap_info != null){
if(snap_info.x != null) {
config.layer.x = snap_info.x;
}
if(snap_info.y != null) {
config.layer.y = snap_info.y;
}
}
config.need_render = true;
}
}
mouseup(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false || config.mouse_lock === true) {
return;
}
if (this.resizing) {
let x = config.layer.x;
let y = config.layer.y;
let width = config.layer.width;
let height = config.layer.height;
//reset values
config.layer.x = this.mousedown_dimensions.x;
config.layer.y = this.mousedown_dimensions.y;
config.layer.width = this.mousedown_dimensions.width;
config.layer.height = this.mousedown_dimensions.height;
if (
this.mousedown_dimensions.x !== x || this.mousedown_dimensions.y !== y ||
this.mousedown_dimensions.width !== width || this.mousedown_dimensions.height !== height
) {
app.State.do_action(
new app.Actions.Bundle_action('resize_layer', 'Resize Layer', [
new app.Actions.Update_layer_action(config.layer.id, {
x, y, width, height
})
])
);
}
//also handle rotation
let rotate = this.Base_selection.current_angle;
if(this.rotate_initial != rotate && rotate !== null){
//save state
config.layer.rotate = this.rotate_initial;
app.State.do_action(
new app.Actions.Bundle_action('resize_layer', 'Resize Layer', [
new app.Actions.Update_layer_action(config.layer.id, {
rotate
})
])
);
}
}
else if (this.moving) {
var new_x = Math.round(mouse.x - mouse.click_x + this.mousedown_dimensions.x);
var new_y = Math.round(mouse.y - mouse.click_y + this.mousedown_dimensions.y);
config.layer.x = this.mousedown_dimensions.x;
config.layer.y = this.mousedown_dimensions.y;
if(mouse.x - mouse.click_x || mouse.y - mouse.click_y) {
var snap_info = this.calc_snap(e, new_x, new_y);
if (snap_info != null) {
if (snap_info.x != null) {
new_x = snap_info.x;
}
if (snap_info.y != null) {
new_y = snap_info.y;
}
}
}
if (this.mousedown_dimensions.x !== new_x || this.mousedown_dimensions.y !== new_y) {
app.State.do_action(
new app.Actions.Bundle_action('move_layer', 'Move Layer', [
new app.Actions.Update_layer_action(config.layer.id, {
x: new_x,
y: new_y
})
])
);
}
}
this.moving = false;
this.resizing = false;
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
var mouse = this.get_mouse_info(event);
//maybe related tool have additional overlay render handlers?
if(config.layer.render_function != null) {
var render_class = config.layer.render_function[0];
var render_function = 'select';
if (
typeof this.Base_gui.GUI_tools.tools_modules[render_class].object[
render_function
] != "undefined"
) {
this.Base_gui.GUI_tools.tools_modules[render_class].object[
render_function
](this.ctx);
}
}
if (mouse.is_drag == false)
return;
this.render_overlay_parent(ctx);
}
/**
* calculates current object snap coordinates and returns it. One of coordinates can be null.
*
* @param event
* @param pos_x
* @param pos_y
* @returns object|null
*/
calc_snap(event, pos_x, pos_y) {
var snap_position = { x: null, y: null };
var params = this.getParams();
if(config.SNAP === false || event.shiftKey == true){
this.snap_line_info = {x: null, y: null};
return null;
}
//settings
var sensitivity = 0.01;
var max_distance = (config.WIDTH + config.HEIGHT) / 2 * sensitivity / config.ZOOM;
//collect snap positions
var snap_positions = this.get_snap_positions(config.layer.id);
//find closest snap positions
var min_group = {
x: {
start: null,
center: null,
end: null,
},
y: {
start: null,
center: null,
end: null,
},
};
var min_group_distance = {
x: {
start: null,
center: null,
end: null,
},
y: {
start: null,
center: null,
end: null,
},
};
//x
for(var i in snap_positions.x){
var distance = Math.abs(pos_x - snap_positions.x[i]);
if(distance < max_distance && (distance < min_group_distance.x.start || min_group_distance.x.start === null)){
min_group_distance.x.start = distance;
min_group.x.start = snap_positions.x[i];
}
var distance = Math.abs(pos_x + config.layer.width/2 - snap_positions.x[i]);
if(distance < max_distance && (distance < min_group_distance.x.center || min_group_distance.x.center === null)){
min_group_distance.x.center = distance;
min_group.x.center = snap_positions.x[i];
}
var distance = Math.abs(pos_x + config.layer.width - snap_positions.x[i]);
if(distance < max_distance && (distance < min_group_distance.x.end || min_group_distance.x.end === null)){
min_group_distance.x.end = distance;
min_group.x.end = snap_positions.x[i];
}
}
//y
for(var i in snap_positions.y){
var distance = Math.abs(pos_y - snap_positions.y[i]);
if(distance < max_distance && (distance < min_group_distance.y.start || min_group_distance.y.start === null)){
min_group_distance.y.start = distance;
min_group.y.start = snap_positions.y[i];
}
var distance = Math.abs(pos_y + config.layer.height/2 - snap_positions.y[i]);
if(distance < max_distance && (distance < min_group_distance.y.center || min_group_distance.y.center === null)){
min_group_distance.y.center = distance;
min_group.y.center = snap_positions.y[i];
}
var distance = Math.abs(pos_y + config.layer.height - snap_positions.y[i]);
if(distance < max_distance && (distance < min_group_distance.y.end || min_group_distance.y.end === null)){
min_group_distance.y.end = distance;
min_group.y.end = snap_positions.y[i];
}
}
//find best begin, center, end
var min_distance = {
x: null,
y: null,
};
//x
if(min_group_distance.x.start != null)
min_distance.x = min_group_distance.x.start;
if(min_group_distance.x.center != null && (min_group_distance.x.center < min_distance.x || min_distance.x === null))
min_distance.x = min_group_distance.x.center;
if(min_group_distance.x.end != null && (min_group_distance.x.end < min_distance.x || min_distance.x === null))
min_distance.x = min_group_distance.x.end;
//y
if(min_group_distance.y.start != null)
min_distance.y = min_group_distance.y.start;
if(min_group_distance.y.center != null && (min_group_distance.y.center < min_distance.y || min_distance.y === null))
min_distance.y = min_group_distance.y.center;
if(min_group_distance.y.end != null && (min_group_distance.y.end < min_distance.y || min_distance.y === null))
min_distance.y = min_group_distance.y.end;
//apply snap
var success = false;
//x
if(min_group.x.center != null && min_group_distance.x.center == min_distance.x) {
snap_position.x = Math.round(min_group.x.center - config.layer.width / 2);
success = true;
this.snap_line_info.x = {
start_x: min_group.x.center,
start_y: 0,
end_x: min_group.x.center,
end_y: config.HEIGHT
};
}
else if(min_group.x.start != null && min_group_distance.x.start == min_distance.x) {
snap_position.x = Math.round(min_group.x.start);
success = true;
this.snap_line_info.x = {
start_x: min_group.x.start,
start_y: 0,
end_x: min_group.x.start,
end_y: config.HEIGHT,
};
}
else if(min_group.x.end != null && min_group_distance.x.end == min_distance.x) {
snap_position.x = Math.round(min_group.x.end - config.layer.width);
success = true;
this.snap_line_info.x = {
start_x: min_group.x.end,
start_y: 0,
end_x: min_group.x.end,
end_y: config.HEIGHT
};
}
else{
this.snap_line_info.x = null;
}
//y
if(min_group.y.center != null && min_group_distance.y.center == min_distance.y) {
snap_position.y = Math.round(min_group.y.center - config.layer.height / 2);
success = true;
this.snap_line_info.y = {
start_x: 0,
start_y: min_group.y.center,
end_x: config.WIDTH,
end_y: min_group.y.center,
};
}
else if(min_group.y.start != null && min_group_distance.y.start == min_distance.y) {
snap_position.y = Math.round(min_group.y.start);
success = true;
this.snap_line_info.y = {
start_x: 0,
start_y: min_group.y.start,
end_x: config.WIDTH,
end_y: min_group.y.start,
};
}
else if(min_group.y.end != null && min_group_distance.y.end == min_distance.y) {
snap_position.y = Math.round(min_group.y.end - config.layer.height);
success = true;
this.snap_line_info.y = {
start_x: 0,
start_y: min_group.y.end,
end_x: config.WIDTH,
end_y: min_group.y.end,
};
}
else{
this.snap_line_info.y = null;
}
if(success) {
return snap_position;
}
return null;
}
move(direction_x, direction_y, event) {
if (!this.keyboard_move_start_position) {
this.keyboard_move_start_position = {
x: config.layer.x,
y: config.layer.y
}
}
var power = 10;
if (event.ctrlKey == true || event.metaKey)
power = 50;
if (event.shiftKey == true)
power = 1;
config.layer.x += direction_x * power;
config.layer.y += direction_y * power;
config.need_render = true;
}
async auto_select_object(e) {
var params = this.getParams();
if (params.auto_select == false)
return;
var layers_sorted = this.Base_layers.get_sorted_layers();
//render main canvas
for (var i = 0; i < layers_sorted.length; i++) {
var value = layers_sorted[i];
var canvas = this.Base_layers.convert_layer_to_canvas(value.id, null, false);
if (this.check_hit_region(e, canvas.getContext("2d"), value) == true) {
await app.State.do_action(
new app.Actions.Select_layer_action(value.id)
);
break;
}
}
}
check_hit_region(e, ctx, layer) {
var mouse = this.get_mouse_info(e);
if(layer.type == 'image' && Math.abs(layer.width * layer.height / 1000000) > 5){
//too big to check using getImageData - use simple way
if (mouse.x > layer.x && mouse.x < layer.x + layer.width &&
mouse.y > layer.y && mouse.y < layer.y + layer.height) {
//hit
return true;
}
return false;
}
var data = ctx.getImageData(mouse.x, mouse.y, 1, 1).data;
var blank = [0, 0, 0, 0];
if (config.TRANSPARENCY == false) {
blank = [0, 0, 0, 0];
}
if (data[0] != blank[0] || data[1] != blank[1] || data[2] != blank[2]
|| data[3] != blank[3]) {
//hit
return true;
}
return false;
}
}
export default Select_tool_class;
+348
View File
@@ -0,0 +1,348 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import Base_selection_class from './../core/base-selection.js';
import GUI_tools_class from './../core/gui/gui-tools.js';
import Helper_class from './../libs/helpers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
var instance = null;
class Selection_class extends Base_tools_class {
constructor(ctx) {
super();
//singleton
if (instance) {
return instance;
}
instance = this;
var _this = this;
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.ctx = ctx;
this.name = 'selection';
this.type = null;
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
this.selection_coords_from = null;
this.selection = {
x: null,
y: null,
width: null,
height: null,
};
var sel_config = {
enable_background: true,
enable_borders: true,
enable_controls: false,
enable_rotation: false,
enable_move: false,
data_function: function () {
return _this.selection;
},
};
this.mousedown_selection = null;
this.Base_selection = new Base_selection_class(ctx, sel_config, this.name);
this.GUI_tools = new GUI_tools_class();
}
load() {
var _this = this;
//mouse events
document.addEventListener('mousedown', function (event) {
_this.dragStart(event);
});
document.addEventListener('mousemove', function (event) {
_this.dragMove(event);
});
document.addEventListener('mouseup', function (event) {
_this.dragEnd(event);
});
// collect touch events
document.addEventListener('touchstart', function (event) {
_this.dragStart(event);
});
document.addEventListener('touchmove', function (event) {
_this.dragMove(event);
});
document.addEventListener('touchend', function (event) {
_this.dragEnd(event);
});
document.addEventListener('keydown', (e) => {
var code = e.keyCode;
if (this.Helper.is_input(e.target))
return;
if (code == 27) {
//escape
app.State.do_action(new app.Actions.Bundle_action('clear_selection', 'Clear Selection', this.on_leave()));
}
if (code == 46) {
//delete
if (config.TOOL.name == this.name) {
this.delete_selection();
}
}
if (code == 65 && (e.ctrlKey == true || e.metaKey)) {
//A
e.preventDefault();
this.select_all();
}
}, false);
}
dragStart(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
_this.mousedown(event);
}
dragMove(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
_this.mousemove(event);
}
dragEnd(event) {
var _this = this;
if (config.TOOL.name != _this.name)
return;
_this.mouseup(event);
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
var layer = config.layer;
if (this.Base_selection.is_drag == false || mouse.click_valid == false)
return;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
this.mousedown_selection = JSON.parse(JSON.stringify(this.selection));
if (this.selection.width != null && this.selection.height != null
&& mouse.x > this.selection.x
&& mouse.x < this.selection.x + this.selection.width
&& mouse.y > this.selection.y
&& mouse.y < this.selection.y + this.selection.height
&& layer.width == layer.width_original && layer.height == layer.height_original
) {
//move
this.type = 'move';
if (this.tmpCanvas == null) {
this.init_tmp_canvas();
//register tmp canvas for faster redraw
config.layer.link_canvas = this.tmpCanvas;
config.need_render = true;
}
}
else {
//create new selection
this.selection = {
x: mouse.x,
y: mouse.y,
width: 0,
height: 0,
};
this.type = 'create';
this.selection_coords_from = {x: mouse.x, y: mouse.y};
}
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
if (this.Base_selection.is_drag == false || mouse.is_drag == false)
return;
if (e.type == 'mousedown' && (mouse.click_valid == false) || config.layer.type != 'image') {
return;
}
if (this.selection_coords_from === null) {
return;
}
if (this.type == 'create') {
//create new selection
this.selection.width = mouse.x - mouse.click_x;
this.selection.height = mouse.y - mouse.click_y;
config.need_render = true;
}
}
mouseup(e) {
var mouse = this.get_mouse_info(e);
if (!this.Base_selection.is_drag) {
return;
}
if ((e.type == 'mousedown' && mouse.click_valid == false) || config.layer.type != 'image') {
return;
}
if (this.type === 'move') {
return; // Translate appears to not work at the moment
}
var width = mouse.x - this.selection.x;
var height = mouse.y - this.selection.y;
if (width == 0 || height == 0) {
//cancel selection
app.State.do_action(
new app.Actions.Bundle_action('clear_selection', 'Clear Selection', this.on_leave())
);
return;
}
if (this.selection.width != null && this.selection.height != null) {
//make sure coords not negative
var details = this.selection;
var x = details.x;
var y = details.y;
if (details.width < 0) {
x = x + details.width;
this.selection_coords_from.x = x;
}
if (details.height < 0) {
y = y + details.height;
this.selection_coords_from.y = y;
}
this.selection = {
x: x,
y: y,
width: Math.abs(details.width),
height: Math.abs(details.height),
};
app.State.do_action(
new app.Actions.Set_selection_action(this.selection.x, this.selection.y, this.selection.width, this.selection.height, this.mousedown_selection)
);
}
}
select_all() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
let actions = [];
if (config.TOOL.name != this.name) {
actions.push(
new app.Actions.Activate_tool_action(this.name)
);
}
actions.push(
new app.Actions.Set_selection_action(0, 0, config.WIDTH, config.HEIGHT, this.selection)
);
app.State.do_action(
new app.Actions.Bundle_action('select_all', 'Select All', actions)
);
}
render(ctx, layer) {
//nothing
}
save_translate() {
if (this.tmpCanvas == null)
return;
delete config.layer.link_canvas;
app.State.do_action(
new app.Actions.Bundle_action('selection_tool', 'Selection Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
this.reset_tmp_canvas();
config.need_render = true;
}
delete_selection() {
var selection = this.selection;
var layer = config.layer;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
if (selection == null) {
alertify.error('Nothing is selected.');
return;
}
this.init_tmp_canvas();
var mouse_x = selection.x - layer.x;
var mouse_y = selection.y - layer.y;
//adapt to origin size
mouse_x = this.adaptSize(mouse_x, 'width');
mouse_y = this.adaptSize(mouse_y, 'height');
selection.width = this.adaptSize(selection.width, 'width');
selection.height = this.adaptSize(selection.height, 'height');
//do erase
this.tmpCanvasCtx.clearRect(mouse_x, mouse_y, selection.width, selection.height);
app.State.do_action(
new app.Actions.Bundle_action('delete_selection', 'Delete Selection', [
new app.Actions.Update_layer_image_action(this.tmpCanvas),
new app.Actions.Reset_selection_action(this.selection)
])
);
this.reset_tmp_canvas();
delete config.layer.link_canvas;
this.reset_tmp_canvas();
}
init_tmp_canvas() {
this.tmpCanvas = document.createElement('canvas');
this.tmpCanvasCtx = this.tmpCanvas.getContext("2d");
this.tmpCanvas.width = config.layer.width_original;
this.tmpCanvas.height = config.layer.height_original;
this.tmpCanvasCtx.drawImage(config.layer.link, 0, 0);
}
on_leave() {
let actions = [
new app.Actions.Reset_selection_action(this.selection)
];
delete config.layer.link_canvas;
this.reset_tmp_canvas();
return actions;
}
clear_selection() {
app.State.do_action(
new app.Actions.Bundle_action('clear_selection', 'Clear Selection', this.on_leave())
);
}
reset_tmp_canvas() {
if (this.tmpCanvas == null)
return;
this.tmpCanvas.width = 1;
this.tmpCanvas.height = 1;
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
}
}
;
export default Selection_class;
+137
View File
@@ -0,0 +1,137 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import Dialog_class from './../libs/popup.js';
import GUI_tools_class from './../core/gui/gui-tools.js';
var instance = null;
class Shape_class extends Base_tools_class {
constructor(ctx) {
super();
//singleton
if (instance) {
return instance;
}
instance = this;
this.Base_layers = new Base_layers_class();
this.GUI_tools = new GUI_tools_class();
this.POP = new Dialog_class();
this.ctx = ctx;
this.name = 'shape';
this.layer = {};
this.preview_width = 150;
this.preview_height = 120;
this.set_events();
}
set_events() {
document.addEventListener('keydown', (event) => {
var code = event.keyCode;
if (this.Helper.is_input(event.target))
return;
if (code == 72) {
//H
this.show_shapes();
}
}, false);
}
load() {
}
on_activate() {
this.show_shapes();
}
async show_shapes(){
var _this = this;
var html = '';
var data = this.get_shapes();
for (var i in data) {
html += '<div class="item">';
html += ' <canvas id="c_' + data[i].key + '" width="' + this.preview_width + '" height="'
+ this.preview_height + '" class="effectsPreview" data-key="'
+ data[i].key + '"></canvas>';
html += '<div class="preview-item-title">' + data[i].title + '</div>';
html += '</div>';
}
for (var i = 0; i < 4; i++) {
html += '<div class="item"></div>';
}
var settings = {
title: 'Shapes',
className: 'wide',
on_load: function (params, popup) {
var node = document.createElement("div");
node.classList.add('flex-container');
node.innerHTML = html;
popup.el.querySelector('.dialog_content').appendChild(node);
//events
var targets = popup.el.querySelectorAll('.item canvas');
for (var i = 0; i < targets.length; i++) {
targets[i].addEventListener('click', function (event) {
//we have click
_this.GUI_tools.activate_tool(this.dataset.key);
_this.POP.hide();
});
}
},
};
this.POP.show(settings);
//sleep, lets wait till DOM is finished
await new Promise(r => setTimeout(r, 10));
//draw demo thumbs
for (var i in data) {
var function_name = 'demo';
var canvas = document.getElementById('c_'+data[i].key);
var ctx = canvas.getContext("2d");
if(typeof data[i].object[function_name] == "undefined")
continue;
data[i].object[function_name](ctx, 20, 20, this.preview_width - 40, this.preview_height - 40, null);
}
}
render(ctx, layer) {
}
get_shapes(){
var list = [];
for (var i in this.Base_gui.GUI_tools.tools_modules) {
var object = this.Base_gui.GUI_tools.tools_modules[i];
if (object.full_key.indexOf("shapes/") == -1 )
continue;
list.push(object);
}
list.sort(function(a, b) {
var nameA = a.title.toUpperCase();
var nameB = b.title.toUpperCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
return 0;
});
return list;
}
}
export default Shape_class;
+212
View File
@@ -0,0 +1,212 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Arrow_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'arrow';
this.layer = {};
this.best_ratio = 1;
this.snap_line_info = {x: null, y: null};
this.mouse_click = {x: null, y: null};
}
load() {
this.default_events();
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false) {
return;
}
var mouse_x = mouse.x;
var mouse_y = mouse.y;
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
this.mouse_click.x = mouse_x;
this.mouse_click.y = mouse_y;
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
params: this.clone(this.getParams()),
status: 'draft',
render_function: [this.name, 'render'],
x: Math.round(mouse_x),
y: Math.round(mouse_y),
rotate: null,
is_vector: true,
color: config.COLOR
};
app.State.do_action(
new app.Actions.Bundle_action('new_line_layer', 'New Line Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(this.mouse_click.x);
var click_y = Math.round(this.mouse_click.y);
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
var width = mouse_x - this.layer.x;
var height = mouse_y - this.layer.y;
if (e.ctrlKey == true || e.metaKey) {
//one direction only
if (Math.abs(width) < Math.abs(height))
width = 0;
else
height = 0;
}
//more data
config.layer.width = width;
config.layer.height = height;
this.Base_layers.render();
}
mouseup(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false) {
config.layer.status = null;
return;
}
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(this.mouse_click.x);
var click_y = Math.round(this.mouse_click.y);
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
this.snap_line_info = {x: null, y: null};
var width = mouse_x - this.layer.x;
var height = mouse_y - this.layer.y;
if (width == 0 && height == 0) {
//same coordinates - cancel
app.State.scrap_last_action();
return;
}
if (e.ctrlKey == true || e.metaKey) {
//one direction only
if (Math.abs(width) < Math.abs(height))
width = 0;
else
height = 0;
}
//more data
app.State.do_action(
new app.Actions.Update_layer_action(config.layer.id, {
width,
height,
status: null
}),
{ merge_with_history: 'new_line_layer' }
);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
ctx.fillStyle = '#aaa';
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
this.arrow(ctx, x, y, x + width, y + height, 15);
}
render(ctx, layer) {
if (layer.width == 0 && layer.height == 0)
return;
var params = layer.params;
//set styles
ctx.fillStyle = layer.color;
ctx.strokeStyle = layer.color;
ctx.lineWidth = params.size;
ctx.lineCap = 'round';
var width = layer.x + layer.width;
var height = layer.y + layer.height;
var headlen = params.size * 7;
if (headlen < 15)
headlen = 15;
this.arrow(ctx,
layer.x, layer.y,
width, height,
headlen);
}
arrow(ctx, fromx, fromy, tox, toy, headlen) {
var dx = tox - fromx;
var dy = toy - fromy;
var angle = Math.atan2(dy, dx);
ctx.beginPath();
ctx.moveTo(fromx, fromy);
ctx.lineTo(tox, toy);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(tox - headlen * Math.cos(angle - Math.PI / 6), toy - headlen * Math.sin(angle - Math.PI / 6));
ctx.lineTo(tox, toy);
ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 6), toy - headlen * Math.sin(angle + Math.PI / 6));
ctx.stroke();
}
}
export default Arrow_class;
@@ -0,0 +1,482 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
class Bezier_Curve_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.ctx = ctx;
this.name = 'bezier_curve';
this.layer = {};
this.best_ratio = 1;
this.snap_line_info = {x: null, y: null};
this.params_hash = false;
this.selected_obj_positions = {};
this.mouse_lock = null;
this.selected_object_drag_type = null;
this.old_data = null;
this.events();
}
load() {
var _this = this;
this.default_events();
document.addEventListener('keydown', function (event) {
if (config.TOOL.name != _this.name) {
return;
}
var code = event.code;
if (code == "Escape") {
//escape
}
});
}
/**
* events for handling helping lines only
*/
events() {
document.addEventListener('mousedown', (e) => {
this.selected_object_actions(e);
});
document.addEventListener('mousemove', (e) => {
this.selected_object_actions(e);
});
document.addEventListener('mouseup', (e) => {
this.selected_object_actions(e);
});
// touch
document.addEventListener('touchstart', (event) => {
this.selected_object_actions(event);
});
document.addEventListener('touchmove', (event) => {
this.selected_object_actions(event);
}, {passive: false});
document.addEventListener('touchend', (event) => {
this.selected_object_actions(event);
});
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false) {
return;
}
var params_hash = this.get_params_hash();
var mouse_x = mouse.x;
var mouse_y = mouse.y;
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
const data_clone = JSON.parse(JSON.stringify(config.layer.data));
if (config.layer.type != this.name || params_hash != this.params_hash
|| (data_clone != null && data_clone.cp2.x !== null)) {
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
data: {
start: {x: mouse_x, y: mouse_y},
cp1: {x: null, y: null},
cp2: {x: null, y: null},
end: {x: null, y: null}
},
params: this.clone(this.getParams()),
render_function: [this.name, 'render'],
x: 0,
y: 0,
width: null,
height: null,
hide_selection_if_active: true,
rotate: null,
is_vector: true,
color: config.COLOR,
status: 'draft',
};
app.State.do_action(
new app.Actions.Bundle_action('new_bezier_layer', 'New Bezier Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
this.params_hash = params_hash;
}
else {
//add more data
config.layer.data.end.x = mouse_x;
config.layer.data.end.y = mouse_y;
}
this.Base_layers.render();
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(mouse.click_x);
var click_y = Math.round(mouse.click_y);
if (mouse.click_valid == false) {
return;
}
if (mouse.is_drag == false) {
return;
}
if (e.ctrlKey == true || e.metaKey) {
//one direction only
var width = mouse_x - click_x;
var height = mouse_y - click_y;
if (Math.abs(width) > Math.abs(height))
mouse_y = click_y;
else
mouse_x = click_x;
}
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
//add more data
if(config.layer.data.end.x === null){
//still first step
config.layer.data.cp1.x = mouse_x;
config.layer.data.cp1.y = mouse_y;
}
else{
config.layer.data.cp2.x = mouse_x;
config.layer.data.cp2.y = mouse_y;
}
this.Base_layers.render();
}
mouseup(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false) {
return;
}
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(mouse.click_x);
var click_y = Math.round(mouse.click_y);
if (e.ctrlKey == true || e.metaKey) {
//one direction only
var width = mouse_x - click_x;
var height = mouse_y - click_y;
if (Math.abs(width) > Math.abs(height))
mouse_y = click_y;
else
mouse_x = click_x;
}
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
this.snap_line_info = {x: null, y: null};
//add more data
if(config.layer.data.end.x === null){
//still first step
config.layer.data.cp1.x = mouse_x;
config.layer.data.cp1.y = mouse_y;
}
else{
config.layer.data.cp2.x = mouse_x;
config.layer.data.cp2.y = mouse_y;
config.layer.status = null;
}
this.Base_layers.render();
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
//also draw control lines
if(config.layer.type == this.name){
var bezier = config.layer.data;
this.selected_obj_positions = {};
var x = config.layer.x;
var y = config.layer.y;
//draw corners
if (bezier.start.x != null) {
this.Helper.draw_special_line(
this.ctx,
x + bezier.start.x,
y + bezier.start.y,
x + bezier.cp1.x,
y + bezier.cp1.y
);
if(config.TOOL.name == 'select') {
this.selected_obj_positions.cp1_start = this.Helper.draw_control_point(
this.ctx,
x + bezier.start.x,
y + bezier.start.y
);
this.selected_obj_positions.cp1_end = this.Helper.draw_control_point(
this.ctx,
x + bezier.cp1.x,
y + bezier.cp1.y
);
}
}
if (bezier.end.x != null && bezier.cp2.x != null) {
this.Helper.draw_special_line(
this.ctx,
x + bezier.end.x,
y + bezier.end.y,
x + bezier.cp2.x,
y + bezier.cp2.y
);
if(config.TOOL.name == 'select') {
this.selected_obj_positions.cp2_start = this.Helper.draw_control_point(
this.ctx,
x + bezier.end.x,
y + bezier.end.y
);
this.selected_obj_positions.cp2_end = this.Helper.draw_control_point(
this.ctx,
x + bezier.cp2.x,
y + bezier.cp2.y
);
}
}
}
}
select(ctx) {
this.render_overlay(ctx);
}
demo(ctx, x, y, width, height) {
var data = {
start: {x: x, y: y},
cp1: {x: x + width, y: y},
cp2: {x: x, y: y + height},
end: {x: x + width, y: y + height}
};
this.draw_bezier(ctx, 0, 0, data, 2, '#555');
}
render(ctx, layer) {
var params = layer.params;
this.draw_bezier(ctx, layer.x, layer.y, layer.data, params.size, layer.color);
}
draw_bezier(ctx, x, y, data, lineWidth, color) {
if(data.end.x == null || data.cp2.x == null){
return;
}
//set styles
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.lineWidth = lineWidth;
ctx.lineCap = 'round';
//draw bezier
ctx.beginPath();
ctx.moveTo(x + data.start.x, y + data.start.y);
ctx.bezierCurveTo(
x + data.cp1.x, y + data.cp1.y,
x + data.cp2.x, y + data.cp2.y,
x + data.end.x, y + data.end.y
);
ctx.stroke();
}
selected_object_actions(e) {
if(config.TOOL.name != 'select' || config.layer.type != this.name || config.layer.status == 'draft'){
return;
}
var ctx = this.Base_layers.ctx;
var mouse = this.get_mouse_info(e);
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(mouse.click_x);
var click_y = Math.round(mouse.click_y);
const mainWrapper = document.getElementById('main_wrapper');
//simplify checks
var event_type = e.type;
if(event_type == 'touchstart') event_type = 'mousedown';
if(event_type == 'touchmove') event_type = 'mousemove';
if(event_type == 'touchend') event_type = 'mouseup';
if (event_type == 'mouseup') {
//reset
config.mouse_lock = null;
if (mainWrapper.style.cursor != 'default') {
mainWrapper.style.cursor = 'default';
}
}
if (event_type == 'mousedown' && config.mouse.valid == false) {
return;
}
if (event_type == 'mousemove' && this.mouse_lock == 'move_point' && mouse.is_drag) {
mainWrapper.style.cursor = "move";
if (e.buttons == 1 || typeof e.buttons == "undefined") {
var type = this.selected_object_drag_type;
var bezier = config.layer.data;
// Do transformations
var dx = Math.round(mouse.x - mouse.click_x) - config.layer.x;
var dy = Math.round(mouse.y - mouse.click_y) - config.layer.y;
// Set values
if(type == 'cp1_start') {
bezier.start.x = mouse.click_x + dx;
bezier.start.y = mouse.click_y + dy;
if (e.ctrlKey == true || e.metaKey) {
//one direction only
var width = mouse_x - bezier.cp1.x;
var height = mouse_y - bezier.cp1.y;
if (Math.abs(width) > Math.abs(height))
bezier.start.y = bezier.cp1.y;
else
bezier.start.x = bezier.cp1.x;
}
}
else if(type == 'cp1_end') {
bezier.cp1.x = mouse.click_x + dx;
bezier.cp1.y = mouse.click_y + dy;
if (e.ctrlKey == true || e.metaKey) {
//one direction only
var width = mouse_x -bezier.start.x;
var height = mouse_y - bezier.start.y;
if (Math.abs(width) > Math.abs(height))
bezier.cp1.y = bezier.start.y;
else
bezier.cp1.x = bezier.start.x;
}
}
else if(type == 'cp2_start') {
bezier.end.x = mouse.click_x+ dx;
bezier.end.y = mouse.click_y + dy;
if (e.ctrlKey == true || e.metaKey) {
//one direction only
var width = mouse_x - bezier.cp2.x;
var height = mouse_y - bezier.cp2.y;
if (Math.abs(width) > Math.abs(height))
bezier.end.y = bezier.cp2.y;
else
bezier.end.x = bezier.cp2.x;
}
}
else if(type == 'cp2_end') {
bezier.cp2.x = mouse.click_x + dx;
bezier.cp2.y = mouse.click_y + dy;
if (e.ctrlKey == true || e.metaKey) {
//one direction only
var width = mouse_x - bezier.end.x;
var height = mouse_y - bezier.end.y;
if (Math.abs(width) > Math.abs(height))
bezier.cp2.y = bezier.end.y;
else
bezier.cp2.x = bezier.end.x;
}
}
config.need_render = true;
}
return;
}
if (event_type == 'mouseup' && this.mouse_lock == 'move_point') {
this.mouse_lock = null;
var type = this.selected_object_drag_type;
var bezier = config.layer.data;
//reset sate
config.layer.data = this.old_data;
//save state
app.State.do_action(
new app.Actions.Bundle_action('change_layer_details', 'Change Layer Details', [
new app.Actions.Update_layer_action(config.layer.id, {
data: bezier,
})
])
);
config.need_render = true;
}
if (!mouse.is_drag && ['mousedown', 'mouseup'].includes(event_type)) {
return;
}
if (!this.mouse_lock) {
for (let current_drag_type in this.selected_obj_positions) {
const position = this.selected_obj_positions[current_drag_type];
if (position && this.ctx.isPointInPath(position, mouse.x, mouse.y)) {
// match
if (event_type == 'mousedown') {
if (e.buttons == 1 || typeof e.buttons == "undefined") {
this.mouse_lock = 'move_point';
this.selected_object_drag_type = current_drag_type;
}
config.mouse_lock = true;
this.old_data = JSON.parse(JSON.stringify(config.layer.data));
}
if (event_type == 'mousemove') {
mainWrapper.style.cursor = 'move';
}
}
}
}
}
}
export default Bezier_Curve_class;
+99
View File
@@ -0,0 +1,99 @@
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Callout_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'callout';
this.layer = {};
this.best_ratio = 1.3;
this.snap_line_info = {x: null, y: null};
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
ctx.fillStyle = '#aaa';
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
var width_all = width + x * 2;
width = height * this.best_ratio;
x = (width_all - width) / 2;
ctx.save();
ctx.translate(x + width / 2, y + height / 2);
this.draw_shape(ctx, -width / 2, -height / 2, width, height);
ctx.restore();
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
ctx.restore();
}
draw_shape(ctx, x, y, width, height, coords) {
ctx.lineJoin = "round";
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + width, y);
ctx.lineTo(x + width, y + height * 0.6);
ctx.lineTo(x + width / 2 + width / 10, y + height * 0.6);
ctx.lineTo(x + width / 8, y + height);
ctx.lineTo(x + width / 2 - width / 10, y + height * 0.6);
ctx.lineTo(x, y + height * 0.6);
ctx.lineTo(x, y);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
}
export default Callout_class;
+82
View File
@@ -0,0 +1,82 @@
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Cog_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'cog';
this.layer = {};
this.best_ratio = 1;
this.snap_line_info = {x: null, y: null};
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
ctx.fillStyle = '#777';
ctx.lineWidth = 1;
ctx.save();
ctx.translate(x + width / 2, y + height / 2);
this.draw_shape(ctx, -width / 2, -height / 2, width, height);
ctx.restore();
}
render(ctx, layer) {
var params = layer.params;
ctx.save();
//set styles
ctx.fillStyle = params.fill_color;
ctx.lineWidth = 1;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
ctx.restore();
}
draw_shape(ctx, x, y, width, height, coords) {
ctx.lineJoin = "round";
ctx.beginPath();
//better dont do this, there will be issues with border size
ctx.scale(width/512, height/500);
ctx.translate(-256, -252);
//SVG path
var p = new Path2D("M190.883 502.932c-4.517 0-9.082-.991-13.368-3.055l-63.216-30.438c-13.348-6.426-20.255-21.479-16.422-35.794 3.684-13.757 8.609-29.81 14.376-46.879a195.425 195.425 0 0 1-15.733-19.711c-17.979 1.837-34.736 3.07-48.937 3.594-14.773.515-27.899-9.536-31.195-23.975L.776 278.273c-3.297-14.444 4.167-29.229 17.748-35.156 13.056-5.697 28.669-11.851 45.59-17.977a193.78 193.78 0 0 1 5.601-24.614c-12.655-12.922-24.061-25.246-33.297-35.989-9.643-11.217-9.939-27.761-.706-39.339l43.744-54.854c9.239-11.584 25.453-14.963 38.552-8.043 12.556 6.636 27.096 15.004 42.466 24.433a194.25 194.25 0 0 1 22.746-10.969c2.209-17.923 4.735-34.522 7.381-48.468 2.757-14.532 15.506-25.079 30.315-25.079h70.161c14.815 0 27.569 10.567 30.325 25.126 2.646 13.983 5.17 30.564 7.377 48.422a193.854 193.854 0 0 1 22.75 10.971c15.42-9.466 29.975-17.843 42.506-24.458 13.079-6.901 29.275-3.512 38.509 8.066l43.743 54.855c9.237 11.582 8.928 28.142-.738 39.374-9.254 10.756-20.646 23.066-33.263 35.957a193.79 193.79 0 0 1 5.601 24.62c16.986 6.145 32.615 12.304 45.634 17.992h.001c13.553 5.923 20.997 20.699 17.701 35.137l-15.615 68.4c-3.299 14.446-16.455 24.532-31.247 23.972-14.229-.531-30.97-1.762-48.889-3.588a195.251 195.251 0 0 1-15.728 19.703c5.791 17.122 10.723 33.189 14.394 46.921 3.819 14.291-3.093 29.324-16.436 35.748l-63.214 30.438c-13.351 6.428-29.426 2.438-38.224-9.484-8.455-11.455-17.931-25.313-27.679-40.466-8.425.548-16.745.548-25.176 0-9.772 15.201-19.257 29.075-27.702 40.508-5.964 8.075-15.283 12.499-24.824 12.5zm-61.851-61.915l61.516 29.619c15.437-20.988 29.097-42.937 36.43-54.579 26.665 3.104 31.829 3.053 58.035.001 6.932 10.997 20.8 33.291 36.445 54.576l61.515-29.619c-6.794-25.207-15.471-49.669-19.957-62.54 19.028-18.834 22.066-22.637 36.219-45.367 13.048 1.451 39.007 4.495 65.388 5.533l15.195-66.562c-24.034-10.441-48.695-18.946-61.337-23.387-2.824-26.58-3.888-31.341-12.882-56.619 9.27-9.299 27.886-27.753 45.083-47.657l-42.566-53.381c-22.622 12.001-44 25.528-56.513 33.37-22.495-14.328-26.889-16.481-52.31-25.228-1.474-12.904-4.292-38.972-9.156-64.958H221.86c-4.53 24.145-7.144 47.395-9.144 64.955-25.185 8.667-29.587 10.755-52.309 25.223-11.055-6.923-33.256-21.009-56.521-33.362l-42.568 53.379c16.896 19.57 35.133 37.669 45.088 47.647-8.943 25.131-10.043 29.878-12.885 56.613-12.366 4.348-37.104 12.879-61.339 23.397l15.192 66.562c25.642-.998 50.721-3.907 65.381-5.542 14.147 22.727 17.192 26.54 36.221 45.377-4.265 12.257-13.059 37.034-19.944 62.549zm351.667-168.554l.009.004-.009-.004zM256 347.486c-50.446 0-91.486-41.041-91.486-91.486s41.041-91.486 91.486-91.486c50.445 0 91.486 41.041 91.486 91.486S306.445 347.486 256 347.486zm0-150.972c-32.801 0-59.486 26.686-59.486 59.486S223.2 315.486 256 315.486 315.486 288.8 315.486 256 288.801 196.514 256 196.514z");
ctx.closePath();
ctx.fill(p);
}
}
export default Cog_class;
+100
View File
@@ -0,0 +1,100 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Cylinder_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'cylinder';
this.layer = {};
this.best_ratio = 0.7;
this.snap_line_info = {x: null, y: null};
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
ctx.fillStyle = '#aaa';
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
var width_all = width + x * 2;
width = height * this.best_ratio;
x = (width_all - width) / 2;
ctx.save();
ctx.translate(x + width / 2, y + height / 2);
this.draw_shape(ctx, -width / 2, -height / 2, width, height);
ctx.restore();
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
ctx.restore();
}
draw_shape(ctx, x, y, width, height, coords) {
ctx.lineJoin = "round";
ctx.beginPath();
ctx.scale(1, 1.20);
ctx.translate(-width / 2, -height / 2);
var dh = height/3;
ctx.moveTo(0, dh);
ctx.bezierCurveTo(0,dh+dh, width,dh+dh, width,dh);
ctx.bezierCurveTo(width,dh-dh, 0,dh-dh, 0,dh);
ctx.lineTo(0, height-dh);
ctx.bezierCurveTo(0,height-dh+dh, width,height-dh+dh, width,height-dh);
ctx.lineTo(width, dh);
ctx.fill();
ctx.stroke();
}
}
export default Cylinder_class;
+262
View File
@@ -0,0 +1,262 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Ellipse_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'ellipse';
this.layer = {};
this.best_ratio = 1;
this.snap_line_info = {x: null, y: null};
this.mouse_click = {x: null, y: null};
}
load() {
this.default_events();
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.click_valid == false)
return;
var mouse_x = mouse.x;
var mouse_y = mouse.y;
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
this.mouse_click.x = mouse_x;
this.mouse_click.y = mouse_y;
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
params: this.clone(this.getParams()),
status: 'draft',
render_function: [this.name, 'render'],
x: mouse_x,
y: mouse_y,
color: null,
is_vector: true,
};
if (params.circle == true) {
//disable rotate
this.layer.rotate = null;
}
app.State.do_action(
new app.Actions.Bundle_action('new_ellipse_layer', 'New Ellipse Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(this.mouse_click.x);
var click_y = Math.round(this.mouse_click.y);
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
var x = Math.min(mouse_x, click_x);
var y = Math.min(mouse_y, click_y);
var width = Math.abs(mouse_x - click_x);
var height = Math.abs(mouse_y - click_y);
if (params.circle == true || e.ctrlKey == true || e.metaKey) {
if (width < height) {
width = height;
}
else {
height = width;
}
if (mouse_x < click_x) {
x = click_x - width;
}
if (mouse_y < click_y) {
y = click_y - height;
}
}
//more data
config.layer.x = x;
config.layer.y = y;
config.layer.width = width;
config.layer.height = height;
this.Base_layers.render();
}
mouseup(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.click_valid == false) {
config.layer.status = null;
return;
}
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(this.mouse_click.x);
var click_y = Math.round(this.mouse_click.y);
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
this.snap_line_info = {x: null, y: null};
var x = Math.min(mouse_x, click_x);
var y = Math.min(mouse_y, click_y);
var width = Math.abs(mouse_x - click_x);
var height = Math.abs(mouse_y - click_y);
if (params.circle == true || e.ctrlKey == true || e.metaKey) {
if (width < height) {
width = height;
}
else {
height = width;
}
if (mouse_x < click_x) {
x = click_x - width;
}
if (mouse_y < click_y) {
y = click_y - height;
}
}
if (width == 0 && height == 0) {
//same coordinates - cancel
app.State.scrap_last_action();
return;
}
//more data
app.State.do_action(
new app.Actions.Update_layer_action(config.layer.id, {
x: x,
y: y,
width: width,
height: height,
status: null
}),
{ merge_with_history: 'new_ellipse_layer' }
);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
x = parseInt(x);
y = parseInt(y);
width = parseInt(width);
height = parseInt(height);
ctx.fillStyle = '#aaa';
ctx.strokeStyle = '#555';
ctx.lineWidth = 3;
this.ellipse(
ctx,
x,
y,
width,
height,
true,
true
);
}
render(ctx, layer) {
var params = layer.params;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
var dist_x = layer.width;
var dist_y = layer.height;
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.ellipse(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, params.border, params.fill);
ctx.restore();
}
ellipse(ctx, x, y, w, h, stroke, fill) {
var kappa = .5522848,
ox = (w / 2) * kappa, // control point offset horizontal
oy = (h / 2) * kappa, // control point offset vertical
xe = x + w, // x-end
ye = y + h, // y-end
xm = x + w / 2, // x-middle
ym = y + h / 2; // y-middle
ctx.beginPath();
ctx.moveTo(x, ym);
ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
ctx.closePath();
if ( stroke == true)
ctx.stroke();
if (fill == true)
ctx.fill();
}
}
export default Ellipse_class;
+106
View File
@@ -0,0 +1,106 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Heart_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'heart';
this.layer = {};
this.best_ratio = 1.2;
this.snap_line_info = {x: null, y: null};
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
ctx.fillStyle = '#aaa';
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
ctx.save();
ctx.translate(x + width / 2, y + height / 2);
this.draw_shape(ctx, -width / 2, -height / 2, width, height);
ctx.restore();
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
ctx.restore();
}
draw_shape(ctx, x, y, width, height, coords) {
ctx.lineJoin = "round";
ctx.beginPath();
ctx.scale(1.071, 1.1);
ctx.translate(-width / 2, -height / 1.85);
ctx.moveTo(width/2, height/5);
ctx.bezierCurveTo(5 * width / 14, 0,
0, height / 15,
width / 28, 2 * height / 5);
ctx.bezierCurveTo(width / 14, 2 * height / 3,
3 * width / 7, 5 * height / 6,
width / 2, height);
ctx.bezierCurveTo(4 * width / 7, 5 * height / 6,
13 * width / 14, 2 * height / 3,
27 * width / 28, 2 * height / 5);
ctx.bezierCurveTo(width, height / 15,
9 * width / 14, 0,
width / 2, height / 5);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
}
export default Heart_class;
+114
View File
@@ -0,0 +1,114 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Hexagon_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'hexagon';
this.layer = {};
this.best_ratio = 1.1547005;
this.snap_line_info = {x: null, y: null};
this.coords = [
[75, 6.698729810778069],
[100, 50],
[75, 93.30127018922192],
[24.99999999999999, 93.30127018922192],
[0, 50.00000000000001],
[24.99999999999998, 6.698729810778076],
[75, 6.698729810778069],
[75, 6.698729810778069],
];
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
ctx.fillStyle = '#aaa';
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
this.draw_shape(ctx, x, y - 5, width, height, this.coords);
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, this.coords);
ctx.restore();
}
draw_shape(ctx, x, y, width, height, coords) {
ctx.lineJoin = "round";
ctx.beginPath();
ctx.scale(1, this.best_ratio);
for(var i in coords){
if(coords[i] === null){
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.beginPath();
continue;
}
//coords in 100x100 box
var pos_x = x + coords[i][0] * width / 100;
var pos_y = y + coords[i][1] * height / 100;
if(i == '0')
ctx.moveTo(pos_x, pos_y);
else
ctx.lineTo(pos_x, pos_y);
}
ctx.closePath();
ctx.fill();
ctx.stroke();
}
}
export default Hexagon_class;
+110
View File
@@ -0,0 +1,110 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Human_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'human';
this.layer = {};
this.best_ratio = 0.35;
this.snap_line_info = {x: null, y: null};
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
ctx.fillStyle = '#aaa';
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
var width_all = width + x * 2;
width = height * this.best_ratio;
x = (width_all - width) / 2;
ctx.save();
ctx.translate(x + width / 2, y + height / 2);
this.draw_shape(ctx, -width / 2, -height / 2, width, height);
ctx.restore();
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
ctx.restore();
}
draw_shape(ctx, x, y, width, height) {
ctx.lineJoin = "round";
ctx.beginPath();
ctx.translate(-width / 2, -height / 2);
var radius = Math.sqrt(width * height) * 0.28;
var neck_height = height * 0.07;
var leg_height = height * 0.3;
if(radius * 2 + neck_height + leg_height > height){
radius = (height - leg_height - neck_height) / 2;
}
ctx.arc(width / 2, radius, radius, 0, 2 * Math.PI);
//body
ctx.moveTo(width / 2, radius * 2);
ctx.lineTo(width / 2, height - leg_height);
//arm
ctx.moveTo(0, radius*2 + neck_height);
ctx.lineTo(width, radius*2 + neck_height);
//left leg
ctx.moveTo(width / 2, height - leg_height);
ctx.lineTo(0, height);
//right leg
ctx.moveTo(width / 2, height - leg_height);
ctx.lineTo(width, height);
ctx.fill();
ctx.stroke();
}
}
export default Human_class;
+195
View File
@@ -0,0 +1,195 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Line_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'line';
this.layer = {};
this.best_ratio = 1;
this.snap_line_info = {x: null, y: null};
this.mouse_click = {x: null, y: null};
}
load() {
this.default_events();
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false)
return;
var mouse_x = mouse.x;
var mouse_y = mouse.y;
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
this.mouse_click.x = mouse_x;
this.mouse_click.y = mouse_y;
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
params: this.clone(this.getParams()),
status: 'draft',
render_function: [this.name, 'render'],
x: mouse_x,
y: mouse_y,
rotate: null,
is_vector: true,
color: config.COLOR
};
app.State.do_action(
new app.Actions.Bundle_action('new_line_layer', 'New Line Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(this.mouse_click.x);
var click_y = Math.round(this.mouse_click.y);
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
var width = mouse_x - this.layer.x;
var height = mouse_y - this.layer.y;
if (e.ctrlKey == true || e.metaKey) {
//one direction only
if (Math.abs(width) < Math.abs(height))
width = 0;
else
height = 0;
}
//more data
config.layer.width = width;
config.layer.height = height;
this.Base_layers.render();
}
mouseup(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false) {
config.layer.status = null;
return;
}
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(this.mouse_click.x);
var click_y = Math.round(this.mouse_click.y);
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
this.snap_line_info = {x: null, y: null};
var width = mouse_x - this.layer.x;
var height = mouse_y - this.layer.y;
if (width == 0 && height == 0) {
//same coordinates - cancel
app.State.scrap_last_action();
return;
}
if (e.ctrlKey == true || e.metaKey) {
//one direction only
if (Math.abs(width) < Math.abs(height))
width = 0;
else
height = 0;
}
//more data
app.State.do_action(
new app.Actions.Update_layer_action(config.layer.id, {
width,
height,
status: null
}),
{ merge_with_history: 'new_line_layer' }
);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
var coords = [
[0, 0],
[100, 100],
];
this.draw_shape(ctx, x, y, width, height, coords);
}
render(ctx, layer) {
if (layer.width == 0 && layer.height == 0)
return;
var params = layer.params;
//set styles
ctx.fillStyle = layer.color;
ctx.strokeStyle = layer.color;
ctx.lineWidth = params.size;
ctx.lineCap = 'round';
var width = layer.x + layer.width;
var height = layer.y + layer.height;
//draw line
ctx.beginPath();
ctx.moveTo(layer.x, layer.y);
ctx.lineTo(width, height);
ctx.stroke();
}
}
export default Line_class;
+120
View File
@@ -0,0 +1,120 @@
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Moon_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'moon';
this.layer = {};
this.best_ratio = 0.8;
this.snap_line_info = {x: null, y: null};
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
ctx.fillStyle = '#aaa';
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
var width_all = width + x * 2;
width = height * this.best_ratio;
x = (width_all - width) / 2;
ctx.save();
ctx.translate(x + width / 2, y + height / 2);
this.draw_shape(ctx, -width / 2, -height / 2, width, height, true, true);
ctx.restore();
}
render(ctx, layer) {
var params = layer.params;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, params.fill, params.border);
ctx.restore();
}
draw_shape(ctx, x, y, width, height, fill, stroke) {
var left = parseInt(x);
var top = parseInt(y);
ctx.beginPath();
ctx.moveTo(left + width * 0.512, top + height / 2);
ctx.bezierCurveTo(
left + width * 51.2 / 100, top + height * 28.4 / 100,
left + width * 71.5 / 100, top + height * 10.1 / 100,
left + width * 100 / 100, top + height * 3.1 / 100
);
ctx.bezierCurveTo(
left + width * 92 / 100, top + height * 1.1 / 100,
left + width * 83.4 / 100, top + height * 0 / 100,
left + width * 74.4 / 100, top + height * 0 / 100
);
ctx.bezierCurveTo(
left + width * 33.3 / 100, top + height * 0 / 100,
left + width * 0 / 100, top + height * 22.4 / 100,
left + width * 0 / 100, top + height * 50 / 100
);
ctx.bezierCurveTo(
left + width * 0 / 100, top + height * 77.6 / 100,
left + width * 33.3 / 100, top + height * 100 / 100,
left + width * 74.4 / 100, top + height * 100 / 100
);
ctx.bezierCurveTo(
left + width * 83.4 / 100, top + height * 100 / 100,
left + width * 92 / 100, top + height * 98.9 / 100,
left + width * 100 / 100, top + height * 96.9 / 100
);
ctx.bezierCurveTo(
left + width * 71.5 / 100, top + height * 89.9 / 100,
left + width * 51.2 / 100, top + height * 71.6 / 100,
left + width * 51.2 / 100, top + height * 50 / 100
);
ctx.closePath();
if (fill) {
ctx.fill();
}
if (stroke) {
ctx.stroke();
}
}
}
export default Moon_class;
@@ -0,0 +1,75 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Parallelogram_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'parallelogram';
this.layer = {};
this.best_ratio = 2;
this.snap_line_info = {x: null, y: null};
this.coords = [
[25, 0],
[100, 0],
[75, 100],
[0, 100],
[25, 0],
];
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
this.draw_shape(ctx, x, y, width, height, this.coords);
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, this.coords, false);
ctx.restore();
}
}
export default Parallelogram_class;
+112
View File
@@ -0,0 +1,112 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Pentagon_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'pentagon';
this.layer = {};
this.best_ratio = 1.051;
this.snap_line_info = {x: null, y: null};
this.coords = [
[100.40599536364314, 38.90073974812779],
[81.15261837150108, 98.1565411518722],
[18.84738162849893, 98.1565411518722],
[-0.40599536364314304, 38.90073974812779],
[49.99999999999999, 2.2786404499999975],
[100.40599536364314, 38.900739748127776],
[100.40599536364314, 38.90073974812779],
];
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
ctx.fillStyle = '#aaa';
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
this.draw_shape(ctx, x, y, width, height, this.coords);
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, this.coords);
ctx.restore();
}
draw_shape(ctx, x, y, width, height, coords) {
ctx.lineJoin = "round";
ctx.beginPath();
ctx.scale(1, 1.051);
for(var i in coords){
if(coords[i] === null){
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.beginPath();
continue;
}
//coords in 100x100 box
var pos_x = x + coords[i][0] * width / 100;
var pos_y = y + coords[i][1] * height / 100;
if(i == '0')
ctx.moveTo(pos_x, pos_y);
else
ctx.lineTo(pos_x, pos_y);
}
ctx.closePath();
ctx.fill();
ctx.stroke();
}
}
export default Pentagon_class;
+85
View File
@@ -0,0 +1,85 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
class Plus_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.ctx = ctx;
this.name = 'plus';
this.layer = {};
this.best_ratio = 1;
this.snap_line_info = {x: null, y: null};
this.coords = [
[35, 0],
[65, 0],
[65, 35],
[100, 35],
[100, 65],
[65, 65],
[65, 100],
[35, 100],
[35, 65],
[0, 65],
[0, 35],
[35, 35],
[35, 0],
];
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
this.draw_shape(ctx, x, y, width, height, this.coords);
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, this.coords, false);
ctx.restore();
}
}
export default Plus_class;
+369
View File
@@ -0,0 +1,369 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
class Polygon_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.ctx = ctx;
this.name = 'polygon';
this.layer = {};
this.best_ratio = 1;
this.snap_line_info = {x: null, y: null};
this.params_hash = false;
this.selected_obj_positions = {};
this.mouse_lock = null;
this.selected_object_drag_type = null;
this.old_data = null;
this.events();
}
load() {
var _this = this;
this.default_events();
document.addEventListener('keydown', function (event) {
var code = event.code;
if (config.TOOL.name == _this.name && code == "Escape") {
//escape
config.layer.status = null;
}
});
}
/**
* events for handling helping lines only
*/
events() {
document.addEventListener('mousedown', (e) => {
this.selected_object_actions(e);
});
document.addEventListener('mousemove', (e) => {
this.selected_object_actions(e);
});
document.addEventListener('mouseup', (e) => {
this.selected_object_actions(e);
});
// touch
document.addEventListener('touchstart', (event) => {
this.selected_object_actions(event);
});
document.addEventListener('touchmove', (event) => {
this.selected_object_actions(event);
}, {passive: false});
document.addEventListener('touchend', (event) => {
this.selected_object_actions(event);
});
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false) {
return;
}
var params_hash = this.get_params_hash();
var mouse_x = mouse.x;
var mouse_y = mouse.y;
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
if (config.layer.type != this.name || params_hash != this.params_hash
|| (config.layer.data != null && config.layer.status != 'draft')) {
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
data: [
{x: mouse_x, y: mouse_y}
],
params: this.clone(this.getParams()),
render_function: [this.name, 'render'],
x: 0,
y: 0,
width: null,
height: null,
hide_selection_if_active: true,
rotate: null,
is_vector: true,
color: null,
status: 'draft',
};
app.State.do_action(
new app.Actions.Bundle_action('new_polygon_layer', 'New Polygon Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
this.params_hash = params_hash;
}
else {
//add more data
config.layer.data.push(
{x: mouse_x, y: mouse_y}
);
}
this.Base_layers.render();
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
if (mouse.click_valid == false) {
return;
}
if (mouse.is_drag == false) {
return;
}
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
//add more data
config.layer.data[config.layer.data.length - 1] = {x: mouse_x, y: mouse_y};
this.Base_layers.render();
}
mouseup(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false) {
return;
}
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
this.snap_line_info = {x: null, y: null};
//add more data
config.layer.data[config.layer.data.length - 1] = {x: mouse_x, y: mouse_y};
this.Base_layers.render();
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
if(config.TOOL.name != 'select'){
return;
}
//also draw control lines
if(config.layer.type == this.name){
var data = config.layer.data;
this.selected_obj_positions = {};
//draw corners
for(var i in data) {
var point = data[i];
this.selected_obj_positions[i] = this.Helper.draw_control_point(
this.ctx,
config.layer.x + point.x,
config.layer.y + point.y
);
}
}
}
select(ctx) {
this.render_overlay(ctx);
}
demo(ctx, x, y, width, height) {
ctx.fillStyle = '#aaa';
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
var width_all = width + x * 2;
width = height * this.best_ratio;
x = (width_all - width) / 2;
var data = [
{x: 0, y: 0},
{x: width, y: 0},
{x: width * 1.1, y: height * 2 / 3},
{x: width / 2, y: height / 3},
{x: -1 * width * 0.2, y: height},
];
ctx.save();
ctx.translate(x + width / 2, y + height / 2);
this.draw_polygon(ctx, -width / 2, -height / 2, width, height, data);
ctx.restore();
}
render(ctx, layer) {
var params = layer.params;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_polygon(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, layer.data);
ctx.restore();
}
draw_polygon(ctx, x, y, width, height, data) {
if(data.length == 0){
return;
}
//draw
ctx.beginPath();
for(var i in data) {
if(i == 0){
ctx.moveTo(x + data[i].x, y + data[i].y);
}
else{
ctx.lineTo(x + data[i].x, y + data[i].y);
}
}
ctx.closePath();
ctx.fill()
ctx.stroke();
}
selected_object_actions(e) {
if(config.TOOL.name != 'select' || config.layer.type != this.name){
return;
}
var ctx = this.Base_layers.ctx;
var mouse = this.get_mouse_info(e);
const mainWrapper = document.getElementById('main_wrapper');
//simplify checks
var event_type = e.type;
if(event_type == 'touchstart') event_type = 'mousedown';
if(event_type == 'touchmove') event_type = 'mousemove';
if(event_type == 'touchend') event_type = 'mouseup';
if (event_type == 'mouseup') {
//reset
config.mouse_lock = null;
if (mainWrapper.style.cursor != 'default') {
mainWrapper.style.cursor = 'default';
}
}
if (event_type == 'mousedown' && config.mouse.valid == false) {
return;
}
if (event_type == 'mousemove' && this.mouse_lock == 'move_point' && mouse.is_drag) {
mainWrapper.style.cursor = "move";
if (e.buttons == 1 || typeof e.buttons == "undefined") {
var type = this.selected_object_drag_type;
var bezier = config.layer.data;
// Do transformations
var dx = Math.round(mouse.x - mouse.click_x) - config.layer.x;
var dy = Math.round(mouse.y - mouse.click_y) - config.layer.y;
// Set values
config.layer.data[type] = {
x: mouse.click_x + dx,
y: mouse.click_y + dy
};
config.need_render = true;
}
return;
}
if (event_type == 'mouseup' && this.mouse_lock == 'move_point') {
this.mouse_lock = null;
var bezier = config.layer.data;
//reset sate
config.layer.data = this.old_data;
//save state
app.State.do_action(
new app.Actions.Bundle_action('change_layer_details', 'Change Layer Details', [
new app.Actions.Update_layer_action(config.layer.id, {
data: bezier,
})
])
);
config.need_render = true;
}
if (!mouse.is_drag && ['mousedown', 'mouseup'].includes(event_type)) {
return;
}
if (!this.mouse_lock) {
for (let current_drag_type in this.selected_obj_positions) {
const position = this.selected_obj_positions[current_drag_type];
if (position && this.ctx.isPointInPath(position, mouse.x, mouse.y)) {
// match
if (event_type == 'mousedown') {
if (e.buttons == 1 || typeof e.buttons == "undefined") {
this.mouse_lock = 'move_point';
this.selected_object_drag_type = current_drag_type;
}
config.mouse_lock = true;
this.old_data = JSON.parse(JSON.stringify(config.layer.data));
}
if (event_type == 'mousemove') {
mainWrapper.style.cursor = 'move';
}
}
}
}
}
}
export default Polygon_class;
+302
View File
@@ -0,0 +1,302 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Rectangle_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'rectangle';
this.layer = {};
this.best_ratio = 1;
this.snap_line_info = {x: null, y: null};
this.mouse_click = {x: null, y: null};
}
load() {
this.default_events();
}
mousedown(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false)
return;
var mouse_x = mouse.x;
var mouse_y = mouse.y;
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
this.mouse_click.x = mouse_x;
this.mouse_click.y = mouse_y;
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
params: this.clone(this.getParams()),
status: 'draft',
render_function: [this.name, 'render'],
x: Math.round(mouse_x),
y: Math.round(mouse_y),
color: null,
is_vector: true
};
app.State.do_action(
new app.Actions.Bundle_action('new_rectangle_layer', 'New Rectangle Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(this.mouse_click.x);
var click_y = Math.round(this.mouse_click.y);
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
var x = Math.min(mouse_x, click_x);
var y = Math.min(mouse_y, click_y);
var width = Math.abs(mouse_x - click_x);
var height = Math.abs(mouse_y - click_y);
if (params.square == true || e.ctrlKey == true || e.metaKey) {
if (width < height) {
width = height;
}
else {
height = width;
}
if (mouse_x < click_x) {
x = click_x - width;
}
if (mouse_y < click_y) {
y = click_y - height;
}
}
//more data
config.layer.x = x;
config.layer.y = y;
config.layer.width = width;
config.layer.height = height;
this.Base_layers.render();
}
mouseup(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.click_valid == false) {
config.layer.status = null;
return;
}
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(this.mouse_click.x);
var click_y = Math.round(this.mouse_click.y);
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
this.snap_line_info = {x: null, y: null};
var x = Math.min(mouse_x, click_x);
var y = Math.min(mouse_y, click_y);
var width = Math.abs(mouse_x - click_x);
var height = Math.abs(mouse_y - click_y);
if (params.square == true || e.ctrlKey == true || e.metaKey) {
if (width < height) {
width = height;
}
else {
height = width;
}
if (mouse_x < click_x) {
x = click_x - width;
}
if (mouse_y < click_y) {
y = click_y - height;
}
}
if (width == 0 && height == 0) {
//same coordinates - cancel
app.State.scrap_last_action();
return;
}
//more data
app.State.do_action(
new app.Actions.Update_layer_action(config.layer.id, {
x,
y,
width,
height,
status: null
}),
{ merge_with_history: 'new_rectangle_layer' }
);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
var coords = [
[0, 0],
[100, 0],
[100, 100],
[0, 100],
[0, 0],
];
this.draw_shape(ctx, x, y, width, height, coords);
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
var stroke = params.border;
var rotateSupport = true;
var radius = params.radius;
if(radius == undefined)
radius = 0;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
if (rotateSupport == false) {
this.roundRect(ctx, layer.x, layer.y, layer.width, layer.height, radius, fill, stroke);
}
else {
//rotate
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.roundRect(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, radius, fill, stroke);
}
ctx.restore();
}
/**
* Draws a rounded rectangle on canvas.
*
* @param {CanvasRenderingContext2D} ctx
* @param {Number} x
* @param {Number} y
* @param {Number} width
* @param {Number} height
* @param {Number} radius
* @param {Boolean} fill
*/
roundRect(ctx, x, y, width, height, radius, fill, stroke) {
x = parseInt(x);
y = parseInt(y);
width = parseInt(width);
height = parseInt(height);
if(width < 0){
width = Math.abs(width);
x = x - width;
}
if(height < 0){
height = Math.abs(height);
y = y - height;
}
var smaller_dimension = Math.min(width, height);
radius = parseInt(radius);
if (typeof fill == 'undefined') {
fill = false;
}
if (typeof radius === 'undefined') {
radius = 0;
}
radius = Math.min(radius, width / 2, height / 2);
radius = Math.floor(radius);
// Odd dimensions must draw offset half a pixel
if (width % 2 == 1 && config.layer.status != 'draft') {
x -= 0.5;
}
if (height % 2 == 1 && config.layer.status != 'draft') {
y -= 0.5;
}
var stroke_offset = !fill && ctx.lineWidth % 2 == 1 && width > 1 && height > 1 ? 0.5 : 0;
if (smaller_dimension < 2) fill = true;
radius = {tl: radius, tr: radius, br: radius, bl: radius};
ctx.beginPath();
ctx.moveTo(x + radius.tl + stroke_offset, y + stroke_offset);
ctx.lineTo(x + width - radius.tr - stroke_offset, y + stroke_offset);
ctx.quadraticCurveTo(x + width - stroke_offset, y + stroke_offset, x + width - stroke_offset, y + radius.tr + stroke_offset);
ctx.lineTo(x + width - stroke_offset, y + height - radius.br - stroke_offset);
ctx.quadraticCurveTo(x + width - stroke_offset, y + height - stroke_offset, x + width - radius.br - stroke_offset, y + height - stroke_offset);
ctx.lineTo(x + radius.bl + stroke_offset, y + height - stroke_offset);
ctx.quadraticCurveTo(x + stroke_offset, y + height - stroke_offset, x + stroke_offset, y + height - radius.bl - stroke_offset);
ctx.lineTo(x + stroke_offset, y + radius.tl + stroke_offset);
ctx.quadraticCurveTo(x + stroke_offset, y + stroke_offset, x + radius.tl + stroke_offset, y + stroke_offset);
ctx.closePath();
if (fill) {
ctx.fill();
}
if (stroke) {
ctx.stroke();
}
}
}
export default Rectangle_class;
@@ -0,0 +1,74 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Right_Triangle_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'right_triangle';
this.layer = {};
this.best_ratio = 1;
this.snap_line_info = {x: null, y: null};
this.coords = [
[0, 0],
[100, 100],
[0, 100],
[0, 0],
];
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
this.draw_shape(ctx, x, y, width, height, this.coords);
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, this.coords, false);
ctx.restore();
}
}
export default Right_Triangle_class;
+82
View File
@@ -0,0 +1,82 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Romb_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'romb';
this.layer = {};
this.best_ratio = 0.8;
this.snap_line_info = {x: null, y: null};
this.coords_demo = [
[50, 0],
[80, 50],
[50, 100],
[20, 50],
[50, 0],
];
this.coords = [
[50, 0],
[100, 50],
[50, 100],
[0, 50],
[50, 0],
];
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
this.draw_shape(ctx, x, y, width, height, this.coords_demo);
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, this.coords, false);
ctx.restore();
}
}
export default Romb_class;
+113
View File
@@ -0,0 +1,113 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Star_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'star';
this.layer = {};
this.best_ratio = 1;
this.coords = [];
this.snap_line_info = {x: null, y: null};
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
generate_coords(spikes, innerRadius) {
//settings
innerRadius = parseInt(innerRadius) / 2;
innerRadius = Math.min(Math.max(innerRadius, 0), 100);
spikes = parseInt(spikes);
spikes = Math.max(spikes, 3);
var outerRadius = 50;
if(spikes == 5){
outerRadius = 53;
}
var cx = 50;
var cy = 50;
if(spikes == 5){
cy = 55;
}
var rot = Math.PI / 2 * 3;
var x = cx;
var y = cy;
var step = Math.PI / spikes;
this.coords = [];
this.coords.push([cx, cy - outerRadius]);
for (var i = 0; i < spikes; i++) {
x = cx + Math.cos(rot) * outerRadius;
y = cy + Math.sin(rot) * outerRadius;
this.coords.push([x, y]);
rot += step;
x = cx + Math.cos(rot) * innerRadius;
y = cy + Math.sin(rot) * innerRadius;
this.coords.push([x, y]);
rot += step;
}
this.coords.push([cx, cy - outerRadius]);
}
demo(ctx, x, y, width, height) {
this.generate_coords(5, 40);
this.draw_shape(ctx, x, y, width, height, this.coords);
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
this.generate_coords(params.corners, params.inner_radius);
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, this.coords, false);
ctx.restore();
}
}
export default Star_class;
+115
View File
@@ -0,0 +1,115 @@
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Tear_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'tear';
this.layer = {};
this.best_ratio = 0.7;
this.snap_line_info = {x: null, y: null};
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
ctx.fillStyle = '#aaa';
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
var width_all = width + x * 2;
width = height * this.best_ratio;
x = (width_all - width) / 2;
ctx.save();
ctx.translate(x + width / 2, y + height / 2);
this.draw_shape(ctx, -width / 2, -height / 2, width, height, true, true);
ctx.restore();
}
render(ctx, layer) {
var params = layer.params;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, params.fill, params.border);
ctx.restore();
}
draw_shape(ctx, x, y, width, height, fill, stroke) {
var left = parseInt(x);
var top = parseInt(y);
//settings
var curve_height = 29 / 100;
var curve_start_x = 28 / 100;
var curve_end_x = 1 - curve_start_x;
var curve_cdx = 70;
var curve_cdy = 58;
ctx.beginPath();
ctx.moveTo(left + width * 0.5, top);
ctx.quadraticCurveTo(
left + width * 0.5, top + height * 13 / 100,
left + width * curve_end_x, top + height * curve_height
);
ctx.bezierCurveTo(
left + width * (50 + curve_cdx) / 100, top + height * curve_cdy / 100,
left + width * 100 / 100, top + height * 100 / 100,
left + width * 0.5, top + height
);
ctx.bezierCurveTo(
left + width * 0 / 100, top + height * 100 / 100,
left + width * (50 - curve_cdx) / 100, top + height * curve_cdy / 100,
left + width * curve_start_x, top + height * curve_height
);
ctx.quadraticCurveTo(
left + width * 0.5, top + height * 13 / 100,
left + width * 0.5, top
);
ctx.closePath();
if (fill) {
ctx.fill();
}
if (stroke) {
ctx.stroke();
}
}
}
export default Tear_class;
+75
View File
@@ -0,0 +1,75 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Trapezoid_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'trapezoid';
this.layer = {};
this.best_ratio = 2;
this.snap_line_info = {x: null, y: null};
this.coords = [
[20, 0],
[80, 0],
[100, 100],
[0, 100],
[20, 0],
];
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
this.draw_shape(ctx, x, y, width, height, this.coords);
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, this.coords, false);
ctx.restore();
}
}
export default Trapezoid_class;
+74
View File
@@ -0,0 +1,74 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Base_layers_class from './../../core/base-layers.js';
class Triangle_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.ctx = ctx;
this.name = 'triangle';
this.layer = {};
this.best_ratio = 2 / Math.sqrt(3);
this.snap_line_info = {x: null, y: null};
this.coords = [
[50, 0],
[100, 100],
[0, 100],
[50, 0],
];
}
load() {
this.default_events();
}
mousedown(e) {
this.shape_mousedown(e);
}
mousemove(e) {
this.shape_mousemove(e);
}
mouseup(e) {
this.shape_mouseup(e);
}
render_overlay(ctx){
var ctx = this.Base_layers.ctx;
this.render_overlay_parent(ctx);
}
demo(ctx, x, y, width, height) {
this.draw_shape(ctx, x, y, width, height, this.coords);
}
render(ctx, layer) {
var params = layer.params;
var fill = params.fill;
ctx.save();
//set styles
ctx.strokeStyle = 'transparent';
ctx.fillStyle = 'transparent';
if(params.border)
ctx.strokeStyle = params.border_color;
if(params.fill)
ctx.fillStyle = params.fill_color;
ctx.lineWidth = params.border_size;
//draw with rotation support
ctx.translate(layer.x + layer.width / 2, layer.y + layer.height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
this.draw_shape(ctx, -layer.width / 2, -layer.height / 2, layer.width, layer.height, this.coords, false);
ctx.restore();
}
}
export default Triangle_class;
+139
View File
@@ -0,0 +1,139 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
import ImageFilters from './../libs/imagefilters.js';
import Helper_class from './../libs/helpers.js';
class Sharpen_class extends Base_tools_class {
constructor(ctx) {
super();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.ctx = ctx;
this.name = 'sharpen';
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
this.started = false;
}
load() {
this.default_events();
}
default_dragMove(event) {
if (config.TOOL.name != this.name)
return;
this.mousemove(event);
//mouse cursor
var mouse = this.get_mouse_info(event);
var params = this.getParams();
this.show_mouse_cursor(mouse.x, mouse.y, params.size, 'circle');
}
mousedown(e) {
this.started = false;
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.click_valid == false) {
return;
}
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
if (config.layer.rotate || 0 > 0) {
alertify.error('Erase on rotate object is disabled. Please rasterize first.');
return;
}
this.started = true;
//get canvas from layer
this.tmpCanvas = document.createElement('canvas');
this.tmpCanvasCtx = this.tmpCanvas.getContext("2d");
this.tmpCanvas.width = config.layer.width_original;
this.tmpCanvas.height = config.layer.height_original;
this.tmpCanvasCtx.drawImage(config.layer.link, 0, 0);
//do sharpen
this.sharpen_general('click', mouse, params.size);
//register tmp canvas for faster redraw
config.layer.link_canvas = this.tmpCanvas;
config.need_render = true;
}
mousemove(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
if (this.started == false) {
return;
}
//do sharpen
this.sharpen_general('move', mouse, params.size);
//draw draft preview
config.need_render = true;
}
mouseup(e) {
if (this.started == false) {
return;
}
delete config.layer.link_canvas;
app.State.do_action(
new app.Actions.Bundle_action('sharpen_tool', 'Sharpen Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
//decrease memory
this.tmpCanvas.width = 1;
this.tmpCanvas.height = 1;
this.tmpCanvas = null;
this.tmpCanvasCtx = null;
}
sharpen_general(type, mouse, size) {
var ctx = this.tmpCanvasCtx;
var mouse_x = Math.round(mouse.x) - config.layer.x;
var mouse_y = Math.round(mouse.y) - config.layer.y;
//adapt to origin size
mouse_x = this.adaptSize(mouse_x, 'width');
mouse_y = this.adaptSize(mouse_y, 'height');
var size_w = this.adaptSize(size, 'width');
var size_h = this.adaptSize(size, 'height');
//find center
var center_x = mouse_x - Math.round(size_w / 2);
var center_y = mouse_y - Math.round(size_h / 2);
//convert float coords to integers
mouse_x = Math.round(mouse_x);
mouse_y = Math.round(mouse_y);
center_x = Math.round(center_x);
center_y = Math.round(center_y);
var power = 0.5;
if (type == 'move') {
power = power / 10;
}
var imageData = ctx.getImageData(center_x, center_y, size_w, size_h);
var filtered = ImageFilters.Sharpen(imageData, power); //add effect
this.Helper.image_round(this.tmpCanvasCtx, mouse_x, mouse_y, size_w, size_h, filtered);
}
}
export default Sharpen_class;
File diff suppressed because it is too large Load Diff