d-bl / GroundForge

Bobbin Lace Diagrams : threads from pairs
https://d-bl.github.io/GroundForge/
GNU General Public License v3.0
12 stars 7 forks source link

Diagram layout #119

Closed jo-pol closed 5 years ago

jo-pol commented 6 years ago

Intro

Demo with a prototype, pair diagram and thread diagram. The initial layout of the latter two is more or less like the prototype and subsequently reshaped with an animated process.

Force configuration

Lacking a graph-math background, the current configuration of the D3js-v4 forces is calibrated with trial and error. Only the forceCenter arguments are clear. The render method (discussed with the invisible components) needs to double these values for the overall image size. https://github.com/d-bl/GroundForge/blob/22d0a185e322a8b64c749c0f44be5f7ce36d0982/docs/js/tiles.js#L110-L115

Spring system

A wikipedia article specifies at least two systems, Hookes law combined with some repulsion and the graph-theoretic distance. Without a mathematical background on the subject I have no idea what D3js supports of the shelve. But D3-in-depth writes somewhere: "We can define our own force functions..." StackOverflow might have a clue for some effects.

@veronika suggests a spring system for the flat torus so it doesn't look funny at the edges the way the planar algorithm does

Fixed positions

It is possible to fix and release a node by setting or removing fx and or fy attributes as shown with this demo

Invisible components

The diagrams have invisible components which you can show with a local copy of the page. Add a parameter to the render call. Something like render(...., 744, 1052, 0.1), the higher the value of the additional parameter, the darker these elements appear. https://github.com/d-bl/GroundForge/blob/1fa825d942740acefcdfa5e5f08877c36d3f2f19/docs/js/tiles.js#L76 The replace call causes a smaller scale for the thread diagram resulting in a more similar size as for the pair diagram. As a penalty the target to toggle the thread color becomes harder to hit.

Types of links

The first line of the force configurations deals with three types of links. In the order of appearance:

Without proper foot sides, the resulting fringes will also be considered as starting pins and cause alternating connections. Strong links sew those thread diagrams into tubes. But weak links cause overlapping starting points, making it harder to select a thread to change its color. This overlap can be prevented with .force("collide", d3.forceCollide(7)), but this can put the thread starts in the wrong order what was the whole purpose of the invisible links.

Below two examples with invisible components shown grey. An exaggerated tube example on the left. On the right the tube is prevented with workarounds that omit too long invisible links. The diagram has wings with not used bobbins: another bug. Depending on edge stitches (see for example variant A and B), the edge can form a loose plait. It might be time to completely rewrite the transformation from a pair diagram to a thread diagram. invisible wings

Performance on mobile devices

Performance of the diagrams is a disaster on mobile devices. Although the thread diagram requires the most animation power, its animation is started automatically because the initial state makes no sense, see #87 for details. The device dependent render frequency does improve experience. As it takes a while before the first step shows up, it might be caused by constructing the data that is fed into D3js.

jo-pol commented 6 years ago

graph-theoretic distance

This wikipedia article introduces the concept "graph-theoretic distance between nodes".

No education on the subject that I'm aware of. Looking at the minimal cycles in our directed 2in-2out-graphs (without crossing edges) rendered with force D3js graphs: edges in a cycle get shorter when the neighbouring cycle has less edges. So the neighbouring cycle size per edge gives the ratio's of edge size within a cycle. Don't know what to fill in for edges on the foot sides that are part of only one cycle. Otherwise these ratios seem to provide enough information to render the graph.

With the matrices that defines our graphs it doesn't require rocket science to find a node that is about near the centre. Thus we can start rendering in the centre of the canvas.

Without the need for further iterations we seem to get running times that grow linear with the number of nodes and/or cycle sizes rather than exponential or logarithmic. That might be a false assumption but I don't see a need for the animated evolution to get at the final layout as happens with the force graphs.

Searching the web a bit further for (JavaScript/ScalaJS?) libraries that can do the rendering, I get at geodesic distance and subsequently at this d3js example but the latter doesn't seem to fit the bill: the graph is wrapped around a globe rather than the torus Veronika Irvine describes. Ah, my naive search is probably mixing up vertice length with the number of vertices between arbitrary nodes in the graph.

jo-pol commented 6 years ago

JavaScript based hooks to implement another layout are explained in the readme

For a scala based implementation, the line below suggest to develop an alternative for nudgeNodes that calculates the node position. Executing this test class produces SVG documents in target/test/pattern allowing a visual check of the results. But we need a better API for the purpose.

https://github.com/d-bl/GroundForge/blob/d0d89ef56603a67a97e34fe80e774c6c3601b975/src/test/scala/dibl/TesselaceThumbs.scala#L61

jo-pol commented 6 years ago

The line cited in the previous message could be changed into

val diagram = NewPairDiagram.create(config)
val nudgedNodes = diagram.nodes.map(node => node.withLocation(node.x, node.y))
val svg = D3jsSVG.render(Diagram(nudgedNodes, diagram.links), width = 200, height = 200)

Of course this example actually doesn't change the positions of the nodes.