diff --git a/index.html b/index.html index ac7e4b6db..f268672c2 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ GroundForge : A web based toolbox to design bobbin lace grounds with matching diagrams. - + diff --git a/js/index.js b/js/index.js index 41b583718..660ecbb2d 100644 --- a/js/index.js +++ b/js/index.js @@ -45,8 +45,6 @@ function load() { palette: colors, onAnimationEnd: function() {setHref(document.getElementById("dlThread"),'threads')} }) - var p = document.getElementById("pairs" ); p.scrollTop = p.scrollTop==0 ? 180 : p.scrollTop - var t = document.getElementById("threads"); t.scrollTop = t.scrollTop==0 ? 140 : t.scrollTop } function onChangeColor(el) { if (el.value == 'FFFFFF') { diff --git a/js/show-graph.js b/js/show-graph.js index cb1439904..78e362a40 100644 --- a/js/show-graph.js +++ b/js/show-graph.js @@ -1,27 +1,15 @@ fullyTransparant = 0 // global to allow override while testing var diagram = {} -diagram.startThread = function(svgDefs){ +diagram.start = function(svgDefs, id, shape){ svgDefs.append('svg:marker') - .attr('id', "start-thread") + .attr('id', id) .attr('viewBox', '-7 -7 14 14') .attr('markerWidth', 12) .attr('markerHeight', 12 ) .attr('orient', 'auto') .attr('markerUnits', 'userSpaceOnUse') .append('svg:path') - .attr('d', d3.svg.symbol().type("square")) - .attr('fill', "#000").style('opacity',0.5) -} -diagram.startPair = function(svgDefs){ - svgDefs.append('svg:marker') - .attr('id', "start-pair") - .attr('viewBox', '-7 -7 14 14') - .attr('markerWidth', 10) - .attr('markerHeight', 12 ) - .attr('orient', 'auto') - .attr('markerUnits', 'userSpaceOnUse') - .append('svg:path') - .attr('d', d3.svg.symbol().type("diamond")) + .attr('d', shape) .attr('fill', "#000").style('opacity',0.5) } diagram.twistMark = function(svgDefs){ @@ -67,40 +55,27 @@ diagram.markers = function(svgDefs,id,color){ diagram.shape = {} diagram.shape.stitch = "M 6,0 A 6,6 0 0 1 0,6 6,6 0 0 1 -6,0 6,6 0 0 1 0,-6 6,6 0 0 1 6,0 Z" // larger circle diagram.shape.pin = "M 4,0 A 4,4 0 0 1 0,4 4,4 0 0 1 -4,0 4,4 0 0 1 0,-4 4,4 0 0 1 4,0 Z" // smaller circle +diagram.shape.square = "M -6,-6 6,-6 6,6 -6,6 Z" +diagram.shape.diamond = "M -5,0 0,8 5,0 0,-8 Z" diagram.shape.bobbin = "m 0,40 c -3.40759,0 -6.01351,3.60204 -1.63269,3.60204 l 0,19.82157 c -3.67432,-0.008 -1.7251,5.087 -1.32784,7.27458 0.76065,4.18864 1.01701,8.40176 0.3478,12.58551 -1.68869,10.55725 -2.31894,21.67593 1.25552,31.9161 0.2088,0.59819 0.68935,2.7631 1.40054,2.7636 0.71159,0 1.19169,-2.16521 1.40057,-2.7636 C 5.01838,104.95964 4.38954,93.84095 2.70085,83.2837 2.03164,79.09995 2.28656,74.88683 3.04721,70.69819 3.44447,68.51061 5.61865,63.44146 1.71951,63.42361 l 0,-19.82157 C 5.86853,43.60204 3.4855,39.99659 0,40 L 0,0" -diagram.transform = function(d) { - return "translate(" + d.x + "," + d.y + ")" -} diagram.markLinks = function(links) { links .style('marker-start', function(d) { if (d.start != "white") return 'url(#start-'+d.start+')' }) .style('marker-end', function(d) { if (d.end != "white") return 'url(#end-'+d.end+')' }) .style('marker-mid', function(d,i) { if (d.mid) return 'url(#twist-1)' }) } -diagram.path = function(d) { - var sX = d.source.x - var sY = d.source.y - var tX = d.target.x - var tY = d.target.y - var dX = (tX - sX) - var dY = (tY - sY) - var mid = d.left ? ("S" + (sX - dY/3 + dX/3) + "," + (sY + dX/3 + dY/3)) : - d.right ? ("S" + (sX + dY/3 + dX/3) + "," + (sY + dY/3 - dX/3)) : - " " - if (d.end && d.end == "white") - return "M"+ sX + "," + sY + mid + " " + (tX - dX/4) + "," + (tY - dY/4) - if (d.start && d.start == "white") - return "M"+ (sX + dX/4) + "," + (sY + dY/4) + mid + " " + tX + "," + tY - if (d.mid) - return "M"+ sX + "," + sY + " " + (sX + dX/2) + "," + (sY + dY/2) + " " + tX + "," + tY - return "M"+ sX + "," + sY + " " + tX + "," + tY -} -diagram.showGraph = function(args) { - var colorpicker = (args.threadColor == undefined ? undefined : d3.select(args.threadColor)[0][0]) +diagram.showGraph = function(args) { args.width = args.width ? args.width : 744 args.height = args.height? args.height : 1052 + var m = args.links.length + for (i = 0; i < m; ++i) { + var link = args.links[i] + link.source = args.nodes[link.source]; + link.target = args.nodes[link.target]; + } + // document creation var svgRoot = d3.select(args.container).append("svg") .attr("width", args.width) @@ -110,8 +85,8 @@ diagram.showGraph = function(args) { // marker definitions var defs = svgRoot.append('svg:defs') - diagram.startThread(defs) - diagram.startPair(defs) + diagram.start(defs, "start-thread", diagram.shape.square) + diagram.start(defs, "start-pair", diagram.shape.diamond) diagram.twistMark(defs) diagram.markers(defs, 'green','#0f0') diagram.markers(defs, 'red','#f00') @@ -121,7 +96,8 @@ diagram.showGraph = function(args) { // zoom functionality var container = svgRoot.append('svg:g') - .call(d3.behavior.zoom().scale(args.scale).on("zoom", redraw)) + //.call(d3.zoom().scaleTo(args.scale).on("zoom", redraw)) + //https://github.com/d3/d3/blob/master/CHANGES.md#zooming-d3-zoom .append('svg:g') .attr("transform", args.transform) @@ -141,40 +117,27 @@ diagram.showGraph = function(args) { var isIE = document.documentURI == undefined // wrong feature check var isMobileMac = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream - var link = container.selectAll(".path").data(args.links).enter().append("svg:path") - .attr("class", function(d) { return d.thread ? "link thread" + d.thread : "link"}) + var links = container.selectAll(".path").data(args.links).enter().append("svg:path") + .attr("class", function(d) { return d.thread ? "link thread" + d.thread : "link" }) .attr("id",function(d,i) { return "link_" + i; }) .style('opacity', function(d) { return d.border || d.toPin ? fullyTransparant : 1}) .style('stroke', '#000') .style('fill', 'none') - if (!isIE) diagram.markLinks(link) + if (!isIE) diagram.markLinks(links) - var sh // an inline assignment prevent two lookups - var node = container.selectAll(".node").data(args.nodes).enter().append("svg:path") + var nodes = container.selectAll(".node").data(args.nodes).enter().append("svg:path") .attr("d", function(d) { return (d.bobbin ? diagram.shape.bobbin : d.pin ? diagram.shape.pin : diagram.shape.stitch)}) .attr("class", function(d) { return "node " + (d.startOf ? "threadStart" : d.thread ? ("thread"+d.thread) : "")}) .style('opacity', function(d) { return d.bobbin || d.pin ? 1 : fullyTransparant}) .style('fill', '#000000') .style('stroke', function(d) { return d.pin ? 'none' : '#000000'}) - node.append("svg:title").text(function(d) { return d.title ? d.title : "" }) - var threadStarts = container.selectAll(".threadStart") - - // configure layout - - var force = d3.layout.force() - .nodes(args.nodes) - .links(args.links) - .size([args.width, args.height]) - .charge(-20) - .linkDistance(10) - .linkStrength(10) - .start() - .alpha(0.01) + nodes.append("svg:title").text(function(d) { return d.title ? d.title : "" }) + var threadStarts = container.selectAll(".threadStart") if ( args.palette ) { var colors = args.palette.split(',') - for(i=threadStarts[0].length ; i >= 0 ; i--) { + for(i=threadStarts.size() ; i >= 0 ; i--) { var n = (i - 1 + colors.length) % colors.length container.selectAll(".thread"+i) .style('stroke', colors[n]) @@ -184,24 +147,84 @@ diagram.showGraph = function(args) { // event listeners - threadStarts.on('click', function (d) { - if (d3.event.defaultPrevented) return - container.selectAll("."+d.startOf) - .style('stroke', '#'+colorpicker.value) - .style('fill', function(d) { return d.bobbin ? '#'+colorpicker.value : 'none' }) - }) - + var colorpicker = (args.threadColor == undefined ? undefined : d3.select(args.threadColor).node()) + if (colorpicker) { + threadStarts.on('click', function (d) { + if (d3.event.defaultPrevented) return + container.selectAll("."+d.startOf) + .style('stroke', '#'+colorpicker.value) + .style('fill', function(d) { return d.bobbin ? '#'+colorpicker.value : 'none' }) + }) + } + nodes.call(d3.drag() + .on("start", dragstarted) + .on("drag", dragged) + .on("end", dragended)) + + var drawPath = function(d) { + var sX = d.source.x + var sY = d.source.y + var tX = d.target.x + var tY = d.target.y + var dX = (tX - sX) + var dY = (tY - sY) + var mid = d.left ? ("S" + (sX - dY/3 + dX/3) + "," + (sY + dX/3 + dY/3)) : + d.right ? ("S" + (sX + dY/3 + dX/3) + "," + (sY + dY/3 - dX/3)) : + " " + if (d.end && d.end == "white") + return "M"+ sX + "," + sY + mid + " " + (tX - dX/4) + "," + (tY - dY/4) + if (d.start && d.start == "white") + return "M"+ (sX + dX/4) + "," + (sY + dY/4) + mid + " " + tX + "," + tY + if (d.mid) + return "M"+ sX + "," + sY + " " + (sX + dX/2) + "," + (sY + dY/2) + " " + tX + "," + tY + return "M"+ sX + "," + sY + " " + tX + "," + tY + } + var moveNode = function(d) { + d.x = d.x ? d.x : 0 + d.y = d.y ? d.y : 0 + return "translate(" + d.x + "," + d.y + ")" + } // a higher speed for IE as marks only appear when the animation is finished var mod = isMobileMac ? 10 : isIE ? 3 : 2 var step = 0 - // layout simulation step - force.on("tick", function() { - if ( ((step++)%mod) != 0) return - link.attr("d", diagram.path) - node.attr("transform", diagram.transform) - }) - force.on("end", function(){ - if (isIE) diagram.markLinks(link) - if (args.onAnimationEnd) args.onAnimationEnd() - }) + var simTicked = function() { + //if ( ((step++)%mod) != 0) return // skip rendering + //step++ + nodes.attr("transform", moveNode) + links.attr("d", drawPath) + } + var simEnded = function(){ + if (isIE) diagram.markLinks(links) + if (args.onAnimationEnd) args.onAnimationEnd() + } +// var force = d3.layout.force() +// .nodes(args.nodes) +// .links(args.links) +// .size([args.width, args.height]) +// .charge(-20) +// .linkDistance(10) +// .linkStrength(10) +// .start() +// .alpha(0.01) +// Above calibration with the v3 API resulted in a relative quick compact animation. + var sim = d3.forceSimulation(args.nodes) + .force("charge", d3.forceManyBody().strength(-2)) + .force("link", d3.forceLink(links).strength(1).distance(5).iterations(10)) + .on("tick", simTicked) + .on("end", simEnded) + + function dragstarted(d) { + if (!d3.event.active) sim.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + function dragged(d) { + d.fx = d3.event.x; + d.fy = d3.event.y; + } + function dragended(d) { + if (!d3.event.active) sim.alphaTarget(0.3); + d.fx = null; + d.fy = null; + } }