From 564742da259f8da48df3059539df1609bbbf7197 Mon Sep 17 00:00:00 2001 From: Julien Samama Date: Wed, 26 Sep 2018 18:32:58 +0200 Subject: [PATCH] Feature request #70 resizing nodes (nwjs) * Allows node resizing by dragging the bottom right side of a node. It forces it's shape to be a square. I've added this limitation because it would require too much work on the maths of the connection arrows otherwise. * Save to file in json, xml and yarn format. Twine doesn't handle resizing, so the size is not save in the twee and tw2 format. * Loading a file also handle the size if it finds it in json, xml and yarn format. * Had to change a little the math of the arrows because the size was hardcoded. I've tried opening saved files containing the new 'size' option, and old versions of Yarn (the one linked in the build section of the readme) handled it correctly, ignoring the 'size' option and parsing the rest of the file correctly. And old saved files without the new 'size' option are also read correctly, the 'size' option is optional when loading. Note: the dragging box was already here in the html, but it was commented out because it was incomplete. The author who added it was planning to add a 'toggle expand' feature but apparently never did. I reused his 'resize' box and removed the few lines of code related to the 'toggle expand' feature. --- app/css/style.css | 2 ++ app/index.html | 2 +- app/js/classes/app.js | 21 ++++++++++++++++++--- app/js/classes/data.js | 27 ++++++++++++++++++++++++++ app/js/classes/node.js | 43 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 89 insertions(+), 6 deletions(-) diff --git a/app/css/style.css b/app/css/style.css index 3efaf39..8aa19ab 100644 --- a/app/css/style.css +++ b/app/css/style.css @@ -314,6 +314,8 @@ a:hover { color: #000;} position: absolute; width: 200px; height: 200px; + min-width: 200px; + min-height: 200px; background: #fff; border-radius: 2px; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.5); diff --git a/app/index.html b/app/index.html index d44a838..bb43155 100644 --- a/app/index.html +++ b/app/index.html @@ -114,7 +114,7 @@
- +
diff --git a/app/js/classes/app.js b/app/js/classes/app.js index 628e1bb..1172d6b 100644 --- a/app/js/classes/app.js +++ b/app/js/classes/app.js @@ -834,11 +834,26 @@ var App = function(name, version) var distance = Math.sqrt((fromX - toX) * (fromX - toX) + (fromY - toY) * (fromY - toY)); var normal = { x: (toX - fromX) / distance, y: (toY - fromY) / distance }; - var dist = 110 + 160 * (1 - Math.max(Math.abs(normal.x), Math.abs(normal.y))); + const margin = 10; + // Get the size of the 2 nodes. It's a square, so width and height are the same. + let sizeFrom = node.tempWidth / 2 + margin; + let sizeTo = linked.tempWidth / 2 + margin; + // Get the length from the center of the node to it's corner. + let diagonalFrom = Math.sqrt(node.tempWidth * node.tempWidth + node.tempHeight * node.tempHeight) / 2 + margin; + let diagonalTo = Math.sqrt(linked.tempWidth * linked.tempWidth + linked.tempHeight * linked.tempHeight) / 2 + margin; + + let distFrom = sizeFrom + diagonalFrom * (1 - Math.max(Math.abs(normal.x), Math.abs(normal.y))); + let distTo = sizeTo + diagonalTo * (1 - Math.max(Math.abs(normal.x), Math.abs(normal.y))); // get from / to - var from = { x: fromX + normal.x * dist * scale, y: fromY + normal.y * dist * scale }; - var to = { x: toX - normal.x * dist * scale, y: toY - normal.y * dist * scale }; + var from = { + x: fromX + normal.x * distFrom * scale, + y: fromY + normal.y * distFrom * scale + }; + var to = { + x: toX - normal.x * distTo * scale, + y: toY - normal.y * distTo * scale + }; self.context.strokeStyle = "rgba(0, 0, 0, " + (node.tempOpacity * 0.6) + ")"; self.context.fillStyle = "rgba(0, 0, 0, " + (node.tempOpacity * 0.6) + ")"; diff --git a/app/js/classes/data.js b/app/js/classes/data.js index e872999..d93f75c 100644 --- a/app/js/classes/data.js +++ b/app/js/classes/data.js @@ -162,6 +162,14 @@ var data = var xy = lines[i].substr(9, lines[i].length-9).split(','); obj.position = { x: Number(xy[0].trim()), y: Number(xy[1].trim()) } } + else if (lines[i].indexOf("size:") > -1) { + if (obj == null) obj = {}; + let size = lines[i].substr(5, lines[i].length - 5).split(","); + obj.size = { + width: Number(size[0].trim()), + height: Number(size[1].trim()) + }; + } else if (lines[i].indexOf("colorID:") > -1) { if (obj == null) @@ -294,6 +302,12 @@ var data = node.y(object.position.y); avgY += object.position.y; } + if (object.size != undefined && object.size.width != undefined) { + node.width(object.size.width); + } + if (object.size != undefined && object.size.height != undefined) { + node.height(object.size.height); + } if (object.colorID != undefined) node.colorID(object.colorID); } @@ -320,6 +334,7 @@ var data = "tags": nodes[i].tags(), "body": nodes[i].body(), "position": { "x": nodes[i].x(), "y": nodes[i].y() }, + "size": { "width": nodes[i].width(), "height": nodes[i].height() }, "colorID": nodes[i].colorID() }); } @@ -336,6 +351,12 @@ var data = output += "tags: " + content[i].tags + "\n"; output += "colorID: " + content[i].colorID + "\n"; output += "position: " + content[i].position.x + "," + content[i].position.y + "\n"; + output += + "size: " + + content[i].size.width + + "," + + content[i].size.height + + "\n"; output += "---\n"; output += content[i].body; var body = content[i].body @@ -379,6 +400,12 @@ var data = output += "\t\t" + content[i].tags + "\n"; output += "\t\t" + content[i].body + "\n"; output += '\t\t\n'; + output += + '\t\t\n'; output += '\t\t' + content[i].colorID + '\n'; output += "\t\n"; } diff --git a/app/js/classes/node.js b/app/js/classes/node.js index bffd02d..a13d9b5 100644 --- a/app/js/classes/node.js +++ b/app/js/classes/node.js @@ -1,6 +1,4 @@ var globalNodeIndex = 0; -const NodeExpandWidth = 300; -const NodeExpandHeight = 150; const ClipNodeTextLength = 1024; var Node = function() @@ -22,6 +20,7 @@ var Node = function() this.colorID = ko.observable(0); this.checked = false; this.selected = false; + this.resizeHandle = null; // clipped values for display this.clippedTags = ko.computed(function() @@ -84,6 +83,9 @@ var Node = function() app.updateArrowsThrottled(); } ); + + self.resizeHandle = $(self.element).children(".resize")[0]; + self.drag(); $(self.element).on("dblclick", function() @@ -134,6 +136,20 @@ var Node = function() return Math.floor((new WebKitCSSMatrix(self.style.webkitTransform)).m42); } + this.width = function(inWidth) + { + if (inWidth != undefined) + $(self.element).width(Math.floor(inWidth)); + return Math.floor($(self.element).width()); + } + + this.height = function(inHeight) + { + if (inHeight != undefined) + $(self.element).height(Math.floor(inHeight)); + return Math.floor($(self.element).height()); + } + this.resetDoubleClick = function() { self.canDoubleClick = true; @@ -204,6 +220,7 @@ var Node = function() { var dragging = false; var groupDragging = false; + var resizing = false; var offset = [0, 0]; var moved = false; @@ -249,6 +266,27 @@ var Node = function() //app.refresh(); app.updateArrowsThrottled(); } + else if (resizing) + { + const scale = self.getScale(); + const mouseX = (e.pageX / scale); + const mouseY = (e.pageY / scale); + const nodeX = $(self.element).offset().left / scale; + const nodeY = $(self.element).offset().top / scale; + const width = mouseX - nodeX; + const height = mouseY - nodeY; + const size = Math.max(width, height); + // resize + self.width(size); + self.height(size); + + app.updateArrowsThrottled(); + } + }); + + $(self.resizeHandle).on("mousedown", function(e) { + resizing = true; + e.stopPropagation(); }); $(self.element).on("mousedown", function (e) @@ -288,6 +326,7 @@ var Node = function() dragging = false; groupDragging = false; moved = false; + resizing = false; app.updateArrowsThrottled(); });