From 40569c8c714dfcad107839abf22261ed1de21cad Mon Sep 17 00:00:00 2001 From: viliusle Date: Thu, 22 Oct 2020 22:59:15 +0300 Subject: [PATCH] #143 - line stabiliser for brush --- images/test-collection.json | 34 +----- src/js/config.js | 2 +- src/js/tools/brush.js | 209 +++++++++++++++++++++++++++++------- 3 files changed, 171 insertions(+), 74 deletions(-) diff --git a/images/test-collection.json b/images/test-collection.json index f05775c..03ddca3 100644 --- a/images/test-collection.json +++ b/images/test-collection.json @@ -93,24 +93,7 @@ 7 ], [ - 123, - 117, - 8 - ], - [ - 107, - 118, - 9 - ], - [ - 101, - 118, - 10 - ], - [ - 99, - 118, - 9 + null ], [ 97, @@ -276,20 +259,7 @@ 90 ], [ - 329, - 94 - ], - [ - 324, - 94 - ], - [ - 323, - 94 - ], - [ - 322, - 88 + null ], [ 320, diff --git a/src/js/config.js b/src/js/config.js index 7930ee8..0458647 100644 --- a/src/js/config.js +++ b/src/js/config.js @@ -44,7 +44,7 @@ config.TOOLS = [ title: 'Brush', attributes: { size: 4, - smart_brush: true, + pressure: false, }, }, { diff --git a/src/js/tools/brush.js b/src/js/tools/brush.js index 9783711..6eca5e0 100644 --- a/src/js/tools/brush.js +++ b/src/js/tools/brush.js @@ -118,6 +118,11 @@ class Brush_class extends Base_tools_class { this.Base_layers.insert(this.layer); this.params_hash = params_hash; } + + config.layer.data.push({ + pressure: this.pressure_supported, + data: [], + }); } mousemove(e) { @@ -131,22 +136,26 @@ class Brush_class extends Base_tools_class { var max_speed = 20; var power = 2; //how speed affects size var params = this.getParams(); + var n = config.layer.data.length; + var last_group = config.layer.data[n-1]; //detect line size var size = params.size; var new_size = size; - if (this.pressure_supported) { - new_size = size * this.pointer_pressure * 2; - } - else if (params.smart_brush == true) { - new_size = size + size / max_speed * mouse.speed_average * power; - new_size = Math.max(new_size, size / 4); - new_size = Math.round(new_size); + if (params.pressure == true) { + if (this.pressure_supported) { + new_size = size * this.pointer_pressure * 2; + } + else { + new_size = size + size / max_speed * mouse.speed_average * power; + new_size = Math.max(new_size, size / 4); + new_size = Math.round(new_size); + } } - //more data - config.layer.data.push([mouse.x - config.layer.x, mouse.y - config.layer.y, new_size]); + last_group.data.push([mouse.x - config.layer.x, mouse.y - config.layer.y, new_size]); + config.layer.status = 'draft'; this.Base_layers.render(); } @@ -159,13 +168,16 @@ class Brush_class extends Base_tools_class { //more data var params = this.getParams(); + var n = config.layer.data.length; + var last_group = config.layer.data[n-1]; var size = params.size; var new_size = size; + if (this.pressure_supported) { new_size = size * this.pointer_pressure * 2; } - config.layer.data.push([mouse.x - config.layer.x, mouse.y - config.layer.y, new_size]); - config.layer.data.push(null); + + last_group.data.push([mouse.x - config.layer.x, mouse.y - config.layer.y, new_size]); config.layer.status = null; this.Base_layers.render(); } @@ -182,49 +194,164 @@ class Brush_class extends Base_tools_class { ctx.strokeStyle = layer.color; ctx.lineWidth = params.size; ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; ctx.translate(layer.x, layer.y); - //draw var data = layer.data; - var n = data.length; - var size = params.size; - 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 - ctx.lineWidth = data[i][2]; - if (data[i - 1] == null && data[i + 1] == null) { - //exception - point - ctx.arc(data[i][0], data[i][1], size / 2, 0, 2 * Math.PI, false); - ctx.fill(); + //check for legacy format + if(data.length > 0 && typeof data[0].data == "undefined"){ + //convert + var legacy = JSON.parse(JSON.stringify(data)); + data = []; + data.push({ + pressure: true, + data: [], + }); + var group_index = 0; + for(var i in legacy){ + if(legacy[i][0] === null){ + data.push({ + pressure: false, + data: [], + }); + group_index++; } - else if (data[i - 1] != null) { - //lines - ctx.lineWidth = data[i][2]; - ctx.beginPath(); - ctx.moveTo(data[i - 1][0], data[i - 1][1]); - ctx.lineTo(data[i][0], data[i][1]); - ctx.stroke(); + else { + data[group_index].data.push([legacy[i][0], legacy[i][1], legacy[i][2]]); } } } - if (n == 1 || data[1] == null) { - //point - ctx.beginPath(); - ctx.arc(data[0][0], data[0][1], size / 2, 0, 2 * Math.PI, false); - ctx.fill(); + + var n = data.length; + for (var k = 0; k < n; k++) { + var group = data[k]; //data from mouse down till mouse release + var group_data = group.data; + var group_n = group_data.length; + + if (group.pressure == false) { + //stabilized lines method does not support multiple line sizes + this.render_stabilized(ctx, group_data); + } + else { + 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 (n == 1 || 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); } -}; + /** + * 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 tempdata1 = [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; + tempdata1.push([c, d]); + } + + var tempdata2 = [tempdata1[0]]; + for (var i = 1; i < tempdata1.length - 1; i = i+1) { + c = (tempdata1[i][0] + tempdata1[i + 1][0]) / 2; + d = (tempdata1[i][1] + tempdata1[i + 1][1]) / 2; + tempdata2.push([c, d]); + } + + var tempdata = [tempdata2[0]]; + for (var i = 1; i < tempdata2.length - 1; i = i+1) { + c = (tempdata2[i][0] + tempdata2[i + 1][0]) / 2; + d = (tempdata2[i][1] + tempdata2[i + 1][1]) / 2; + tempdata.push([c, d]); + } + + //draw + for (var i = 1; i < tempdata.length - 2; i = i+1) { + c = (tempdata[i][0] + tempdata[i + 1][0]) / 2; + d = (tempdata[i][1] + tempdata[i + 1][1]) / 2; + ctx.quadraticCurveTo(tempdata[i][0], tempdata[i][1], c, d); + } + + // For the last 2 points + ctx.quadraticCurveTo( + tempdata[i][0], + tempdata[i][1], + tempdata[i+1][0], + tempdata[i+1][1] + ); + ctx.stroke(); + } + +} export default Brush_class;