From f9953c7802e8e369ccb66ba18dabaf07c687da94 Mon Sep 17 00:00:00 2001 From: Julien Samama Date: Wed, 26 Sep 2018 17:21:43 +0200 Subject: [PATCH] Feature request #70 resizing nodes * 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 | 22 ++++++++++++++------- app/js/classes/data.js | 23 ++++++++++++++++++++++ app/js/classes/node.js | 44 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 83 insertions(+), 10 deletions(-) diff --git a/app/css/style.css b/app/css/style.css index f134e13..3b967fc 100644 --- a/app/css/style.css +++ b/app/css/style.css @@ -323,6 +323,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 0db3eb3..432e84a 100644 --- a/app/index.html +++ b/app/index.html @@ -131,7 +131,7 @@
- +
diff --git a/app/js/classes/app.js b/app/js/classes/app.js index 64dc220..d3a5df4 100644 --- a/app/js/classes/app.js +++ b/app/js/classes/app.js @@ -894,18 +894,26 @@ var App = function(name, version) { 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 + x: fromX + normal.x * distFrom * scale, + y: fromY + normal.y * distFrom * scale }; var to = { - x: toX - normal.x * dist * scale, - y: toY - normal.y * dist * scale + x: toX - normal.x * distTo * scale, + y: toY - normal.y * distTo * scale }; self.context.strokeStyle = diff --git a/app/js/classes/data.js b/app/js/classes/data.js index f04d1f6..036a80a 100644 --- a/app/js/classes/data.js +++ b/app/js/classes/data.js @@ -156,6 +156,10 @@ var data = { if (obj == null) obj = {}; 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) obj = {}; obj.colorID = Number( @@ -268,6 +272,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); } @@ -292,6 +302,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() }); } @@ -309,6 +320,12 @@ var data = { "," + 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; @@ -346,6 +363,12 @@ var data = { '" y="' + content[i].position.y + '">\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 2803c0e..e42218e 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,28 @@ 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 +327,7 @@ var Node = function() dragging = false; groupDragging = false; moved = false; + resizing = false; app.updateArrowsThrottled(); });