Frontend Part

delete_edge
Ugo Finnendahl 5 years ago
parent 4a7b82b2f8
commit 0d773e28b7
  1. 102
      frontend/assets/css/src/style.scss
  2. 1
      frontend/assets/css/style.min.css
  3. BIN
      frontend/assets/fonts/Roboto-Regular.eot
  4. 11080
      frontend/assets/fonts/Roboto-Regular.svg
  5. BIN
      frontend/assets/fonts/Roboto-Regular.ttf
  6. BIN
      frontend/assets/fonts/Roboto-Regular.woff
  7. BIN
      frontend/assets/fonts/Roboto-Regular.woff2
  8. 12
      frontend/assets/fonts/roboto.css
  9. 133
      frontend/assets/imgs/drawing.svg
  10. 47
      frontend/assets/imgs/link.svg
  11. 44
      frontend/assets/imgs/plus.svg
  12. 44
      frontend/assets/imgs/share.svg
  13. 436
      frontend/assets/js/canvas.js
  14. 61
      frontend/index.html
  15. 2
      main.py
  16. 13
      templates/index.html

@ -0,0 +1,102 @@
$button-color: rgb(147, 146, 146);
$main-color: rgb(240, 141, 25);
*{
margin:0;
padding: 0;
box-sizing: border-box;
}
body{
font-family: Roboto;
}
main{
position: relative;
height: 100vh;
#infobar{
position: absolute;
top:0;
width: 100%;
padding: 0.4em;
background-color: rgba(232, 9, 9, 0.4);
border: 4px solid rgba(232, 9, 9, 0.8);
text-align: center;
font-weight: bold;
font-size: 1.8em;
z-index: 1;
transform: translateY(-100%);
transition: transform 1s;
&.active{
transform: translateY(0%);
}
}
.canvas{
background-color: lightgrey;
position: relative;
height: 100%;
input{
position: absolute;
border: none;
padding: 0.6em 1em;
width: 50px;
transform: translateX(-50%) translateY(-50%);
}
}
nav, footer{
display: flex;
position: absolute;
top: 1em;
left: 1em;
opacity: 0.9;
button{
display: block;
background-color: $button-color;
border: none;
border-radius: 1em;
padding: 1em 1em;
min-width: 8em;
margin: 0.4em;
color: black;
border: solid 2px darken($button-color, 50);
box-shadow: 2px 2px 8px rgba(0,0,0,0.5);
&:focus {
outline: none;
}
&::-moz-focus-inner {
border: 0;
}
&:hover{
background-color: lighten($button-color, 10);
border-color: darken($button-color, 100);
cursor: pointer;
}
&.active{
background-color: $main-color;
}
h1{
text-align: center;
font-size: 1.4em;
font-weight: normal;
white-space: nowrap
}
img{
display: block;
margin: 0 auto 0.5em auto;
width: 2em;
}
}
}
footer{
top: auto;
left: auto;
bottom: 1em;
right: 1em;
}
}

@ -0,0 +1 @@
*{margin:0;padding:0;box-sizing:border-box}body{font-family:Roboto}main{position:relative;height:100vh}main #infobar{position:absolute;top:0;width:100%;padding:0.4em;background-color:rgba(232,9,9,0.4);border:4px solid rgba(232,9,9,0.8);text-align:center;font-weight:bold;font-size:1.8em;z-index:1;transform:translateY(-100%);transition:transform 1s}main #infobar.active{transform:translateY(0%)}main .canvas{background-color:lightgrey;position:relative;height:100%}main .canvas input{position:absolute;border:none;padding:0.6em 1em;width:50px;transform:translateX(-50%) translateY(-50%)}main nav,main footer{display:flex;position:absolute;top:1em;left:1em;opacity:0.9}main nav button,main footer button{display:block;background-color:#939292;border:none;border-radius:1em;padding:1em 1em;min-width:8em;margin:0.4em;color:black;border:solid 2px #131313;box-shadow:2px 2px 8px rgba(0,0,0,0.5)}main nav button:focus,main footer button:focus{outline:none}main nav button::-moz-focus-inner,main footer button::-moz-focus-inner{border:0}main nav button:hover,main footer button:hover{background-color:#acacac;border-color:#000;cursor:pointer}main nav button.active,main footer button.active{background-color:#f08d19}main nav button h1,main footer button h1{text-align:center;font-size:1.4em;font-weight:normal;white-space:nowrap}main nav button img,main footer button img{display:block;margin:0 auto 0.5em auto;width:2em}main footer{top:auto;left:auto;bottom:1em;right:1em}

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 774 KiB

@ -0,0 +1,12 @@
@font-face {
font-family: 'Roboto-Regular';
src: url('Roboto-Regular.eot');
src: url('Roboto-Regular.eot?#iefix') format('embedded-opentype'),
url('Roboto-Regular.svg#Roboto-Regular') format('svg'),
url('Roboto-Regular.ttf') format('truetype'),
url('Roboto-Regular.woff') format('woff'),
url('Roboto-Regular.woff2') format('woff2');
font-weight: normal;
font-style: normal;
}

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="180.62222mm"
height="180.62224mm"
viewBox="0 0 180.62222 180.62224"
version="1.1"
id="svg8"
sodipodi:docname="drawing.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="-120.94346"
inkscape:cy="367.00053"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1146"
inkscape:window-x="1920"
inkscape:window-y="363"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-6.4507914,-19.968648)">
<path
id="path10"
d="m 176.22372,30.817938 c -6.9963,-6.9963 -16.29806,-10.84929 -26.19237,-10.84929 -9.89431,0 -19.19607,3.85299 -26.19237,10.84929 -6.9963,6.9963 -10.8493,16.29806 -10.8493,26.19238 0,4.31188 1.89243,15.232918 7.37251,22.18503 L 65.207588,134.21865 c -7.158128,-7.16836 -17.403247,-7.71111 -21.715127,-7.71111 -9.89432,0 -19.19608,3.853 -26.19238,10.8493 -6.9963,6.9963 -10.8492896,16.29806 -10.8492896,26.19237 0,9.89431 3.8529896,19.19607 10.8492896,26.19237 6.9963,6.9963 16.29806,10.8493 26.19238,10.8493 9.89431,0 19.19607,-3.853 26.19237,-10.8493 6.9963,-6.9963 10.84929,-16.29806 10.84929,-26.19237 0,-4.31188 -0.73173,-8.50939 -2.13596,-12.45471 L 137.57664,91.916018 c 3.94532,1.40423 8.14283,2.13596 12.45471,2.13596 9.89431,0 19.19607,-3.85299 26.19237,-10.84929 6.9963,-6.9963 10.8493,-16.29806 10.8493,-26.19237 0,-9.89432 -3.853,-19.19608 -10.8493,-26.19238 z M 62.200701,182.25746 c -4.99676,4.99814 -11.64166,7.75008 -18.70824,7.75008 -7.06658,0 -13.71148,-2.75194 -18.70825,-7.75008 -4.99815,-4.99677 -7.75009,-11.64167 -7.75009,-18.70825 0,-7.06658 2.75194,-13.71148 7.75009,-18.70825 4.99677,-4.99814 11.64167,-7.75008 18.70825,-7.75008 7.06658,0 13.71148,2.75194 18.70824,7.75008 4.99815,4.99677 7.75009,11.64167 7.75009,18.70825 0,7.06658 -2.75194,13.71148 -7.75009,18.70825 z m 16.19746,-31.16296 c -1.25801,-2.75678 -3.853218,-6.32319 -5.572099,-8.03223 -0.746882,-0.74261 -0.800749,0.35265 0,0 L 129.5472,86.773972 c 1.0542,1.40422 -6.79035,-4.99349 -5.70822,-3.571284 3.40182,4.470894 11.46481,7.859582 13.73766,8.71333 z M 168.7396,75.718558 c -4.99677,4.99815 -11.64167,7.75009 -18.70825,7.75009 -7.06658,0 -13.71148,-2.75194 -18.70825,-7.75009 -4.99814,-4.99676 -7.75008,-11.64166 -7.75008,-18.70824 0,-7.06658 2.75194,-13.71148 7.75008,-18.70825 4.99677,-4.99815 11.64167,-7.75009 18.70825,-7.75009 7.06658,0 13.71148,2.75194 18.70825,7.75009 4.99814,4.99677 7.75008,11.64167 7.75008,18.70825 0,7.06658 -2.75194,13.71148 -7.75008,18.70824 z"
inkscape:connector-curvature="0"
style="stroke-width:0.35277778"
sodipodi:nodetypes="ssssccsssssssccsssscscscscsccsccscccscscscsc" />
<g
id="g104"
transform="matrix(0.26458333,0,0,0.26458333,-30.050368,-170.4192)">
<g
id="g46">
<g
id="g44">
<path
id="path40"
d="m 418.5,418.5 c 95.6,-95.6 95.6,-251.2 0,-346.8 -95.6,-95.6 -251.2,-95.6 -346.8,0 -95.6,95.6 -95.6,251.2 0,346.8 95.6,95.6 251.2,95.6 346.8,0 z M 89,89 c 86.1,-86.1 226.1,-86.1 312.2,0 86.1,86.1 86.1,226.1 0,312.2 -86.1,86.1 -226.1,86.1 -312.2,0 C 2.9,315.1 3,175.1 89,89 Z"
inkscape:connector-curvature="0" />
<path
id="path42"
d="m 245.1,336.9 c 3.4,0 6.4,-1.4 8.7,-3.6 2.2,-2.2 3.6,-5.3 3.6,-8.7 v -67.3 h 67.3 c 3.4,0 6.4,-1.4 8.7,-3.6 2.2,-2.2 3.6,-5.3 3.6,-8.7 0,-6.8 -5.5,-12.3 -12.2,-12.2 h -67.3 v -67.3 c 0,-6.8 -5.5,-12.3 -12.2,-12.2 -6.8,0 -12.3,5.5 -12.2,12.2 v 67.3 h -67.3 c -6.8,0 -12.3,5.5 -12.2,12.2 0,6.8 5.5,12.3 12.2,12.2 h 67.3 v 67.3 c -0.3,6.9 5.2,12.4 12,12.4 z"
inkscape:connector-curvature="0" />
</g>
</g>
<g
id="g48">
</g>
<g
id="g50">
</g>
<g
id="g52">
</g>
<g
id="g54">
</g>
<g
id="g56">
</g>
<g
id="g58">
</g>
<g
id="g60">
</g>
<g
id="g62">
</g>
<g
id="g64">
</g>
<g
id="g66">
</g>
<g
id="g68">
</g>
<g
id="g70">
</g>
<g
id="g72">
</g>
<g
id="g74">
</g>
<g
id="g76">
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 482.8 482.8" style="enable-background:new 0 0 482.8 482.8;" xml:space="preserve">
<g>
<g>
<path d="M255.2,209.3c-5.3,5.3-5.3,13.8,0,19.1c21.9,21.9,21.9,57.5,0,79.4l-115,115c-21.9,21.9-57.5,21.9-79.4,0l-17.3-17.3
c-21.9-21.9-21.9-57.5,0-79.4l115-115c5.3-5.3,5.3-13.8,0-19.1s-13.8-5.3-19.1,0l-115,115C8.7,322.7,0,343.6,0,365.8
c0,22.2,8.6,43.1,24.4,58.8l17.3,17.3c16.2,16.2,37.5,24.3,58.8,24.3s42.6-8.1,58.8-24.3l115-115c32.4-32.4,32.4-85.2,0-117.6
C269.1,204,260.5,204,255.2,209.3z"/>
<path d="M458.5,58.2l-17.3-17.3c-32.4-32.4-85.2-32.4-117.6,0l-115,115c-32.4,32.4-32.4,85.2,0,117.6c5.3,5.3,13.8,5.3,19.1,0
s5.3-13.8,0-19.1c-21.9-21.9-21.9-57.5,0-79.4l115-115c21.9-21.9,57.5-21.9,79.4,0l17.3,17.3c21.9,21.9,21.9,57.5,0,79.4l-115,115
c-5.3,5.3-5.3,13.8,0,19.1c2.6,2.6,6.1,4,9.5,4s6.9-1.3,9.5-4l115-115c15.7-15.7,24.4-36.6,24.4-58.8
C482.8,94.8,474.2,73.9,458.5,58.2z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 490.2 490.2" style="enable-background:new 0 0 490.2 490.2;" xml:space="preserve">
<g>
<g>
<path d="M418.5,418.5c95.6-95.6,95.6-251.2,0-346.8s-251.2-95.6-346.8,0s-95.6,251.2,0,346.8S322.9,514.1,418.5,418.5z M89,89
c86.1-86.1,226.1-86.1,312.2,0s86.1,226.1,0,312.2s-226.1,86.1-312.2,0S3,175.1,89,89z"/>
<path d="M245.1,336.9c3.4,0,6.4-1.4,8.7-3.6c2.2-2.2,3.6-5.3,3.6-8.7v-67.3h67.3c3.4,0,6.4-1.4,8.7-3.6c2.2-2.2,3.6-5.3,3.6-8.7
c0-6.8-5.5-12.3-12.2-12.2h-67.3v-67.3c0-6.8-5.5-12.3-12.2-12.2c-6.8,0-12.3,5.5-12.2,12.2v67.3h-67.3c-6.8,0-12.3,5.5-12.2,12.2
c0,6.8,5.5,12.3,12.2,12.2h67.3v67.3C232.8,331.4,238.3,336.9,245.1,336.9z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 481.6 481.6" style="enable-background:new 0 0 481.6 481.6;" xml:space="preserve">
<g>
<path d="M381.6,309.4c-27.7,0-52.4,13.2-68.2,33.6l-132.3-73.9c3.1-8.9,4.8-18.5,4.8-28.4c0-10-1.7-19.5-4.9-28.5l132.2-73.8
c15.7,20.5,40.5,33.8,68.3,33.8c47.4,0,86.1-38.6,86.1-86.1S429,0,381.5,0s-86.1,38.6-86.1,86.1c0,10,1.7,19.6,4.9,28.5
l-132.1,73.8c-15.7-20.6-40.5-33.8-68.3-33.8c-47.4,0-86.1,38.6-86.1,86.1s38.7,86.1,86.2,86.1c27.8,0,52.6-13.3,68.4-33.9
l132.2,73.9c-3.2,9-5,18.7-5,28.7c0,47.4,38.6,86.1,86.1,86.1s86.1-38.6,86.1-86.1S429.1,309.4,381.6,309.4z M381.6,27.1
c32.6,0,59.1,26.5,59.1,59.1s-26.5,59.1-59.1,59.1s-59.1-26.5-59.1-59.1S349.1,27.1,381.6,27.1z M100,299.8
c-32.6,0-59.1-26.5-59.1-59.1s26.5-59.1,59.1-59.1s59.1,26.5,59.1,59.1S132.5,299.8,100,299.8z M381.6,454.5
c-32.6,0-59.1-26.5-59.1-59.1c0-32.6,26.5-59.1,59.1-59.1s59.1,26.5,59.1,59.1C440.7,428,414.2,454.5,381.6,454.5z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,436 @@
const canvas = document.getElementById("canvas");
// Layouts
var width = canvas.offsetWidth;
var height = canvas.offsetHeight;
const node_size = 55;
var stage = new Konva.Stage({
container: canvas,
width: width,
height: height
});
const node_layout = {
radius: node_size,
fill: 'white',
stroke: 'black',
strokeWidth: 2,
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: {
x: 2,
y: 2
},
shadowOpacity: 0.3,
};
const node_text_layout = {
x: 1 - node_size,
y: 2 - node_size,
text: name,
fontSize: 22,
width: 2 * node_size-1,
height: 2 * node_size-1,
fontFamily: 'Roboto',
fill: 'black',
verticalAlign: 'middle',
align: 'center'
};
const edge_layout = {
stroke: 'black',
strokeWidth: 3,
}
const edge_text_layout = {
fontSize: 22,
width: 2 * node_size,
height: 2 * node_size,
fontFamily: 'Roboto',
fill: 'black',
verticalAlign: 'middle',
align: 'center'
};
//-----------------------------------------------------------------------------
//-------------------------------Init------------------------------------------
//-----------------------------------------------------------------------------
var edge_layer = new Konva.Layer();
var node_layer = new Konva.Layer();
var konva_edges = [];
var konva_nodes = [];
var graph_copy = {};
var state = "idle";
// selected nodes
var selected = [];
var path_elements = [];
// (additions) auto layout
var auto_layout = false;
function initialisation(){
stage.add(edge_layer);
stage.add(node_layer);
// avoid flickering
edge_layer.hide();
// draw initial graph
for (id in nodes) {
draw_new_node(nodes[id], node_size + Math.random() * (width - 2 * node_size), node_size + Math.random() * (height - 2 * node_size));
}
for (id in edges) {
draw_new_edge(edges[id]);
}
// (additions) auto layout
if ( sessionStorage.getItem("auto_layout") !== null) {
// Restore the contents of the text field
auto_layout = (sessionStorage.getItem("auto_layout") == 'true');
if (auto_layout){
document.querySelector("[data-activeon=auto_layout]").classList.add("active");
}
}
// Precalculate a good graph presentation
for (var i = 0; i < 1000; i++) {
force_directed_graph();
}
node_layer.draw();
edge_layer.show();
}
initialisation();
//-----------------------------------------------------------------------------
//------------------------------utils------------------------------------------
//-----------------------------------------------------------------------------
function sort(array) {
return array.sort(function(a, b) {
return a - b;
})
}
//-----------------------------------------------------------------------------
//---------------------------draw methods--------------------------------------
//-----------------------------------------------------------------------------
function draw_new_node(name, x, y) {
const pos = {
x: x,
y: y
};
let group = new Konva.Group({
...pos,
draggable: true,
dragBoundFunc: function(pos) {
return {
x: sort([node_size, pos.x, width - node_size])[1],
y: sort([node_size, pos.y, height - node_size])[1],
};
},
id: name
});
let box = new Konva.Circle(node_layout);
let simpleText = new Konva.Text({...node_text_layout, text: name});
group.add(box);
group.add(simpleText);
group.on("click", on_node_click);
group.on('dragstart', function() {
group.attrs.dragging = true;
});
group.on('dragend', function() {
group.attrs.dragging = false;
});
group.on('mouseover', function() {
document.body.style.cursor = 'pointer';
});
group.on('mouseout', function() {
document.body.style.cursor = 'default';
});
graph_copy[name] = [];
konva_nodes.push(group);
node_layer.add(group);
node_layer.draw();
}
function draw_new_edge(edge) {
let start_node = stage.findOne('#' + edge["start"]);
let end_node = stage.findOne('#' + edge["end"]);
let weight = edge["weight"];
let group = new Konva.Group({
start_node: start_node,
end_node: end_node,
weight: weight,
id: edge["start"]+edge["end"]
});
let line = new Konva.Line(edge_layout);
let simpleText = new Konva.Text({...edge_text_layout, text: weight});
graph_copy[edge["start"]].push(edge["end"]);
graph_copy[edge["end"]].push(edge["start"]);
group.add(line);
group.add(simpleText);
konva_edges.push(group);
update_edge(group);
edge_layer.add(group);
edge_layer.draw();
return group;
}
function update_edge(konva_edge) {
let line = konva_edge.children[0];
let text = konva_edge.children[1];
let start_node = konva_edge.attrs.start_node;
let end_node = konva_edge.attrs.end_node;
let size = 15+text.text().length*15;
let line_length = Math.sqrt((start_node.getX() - end_node.getX()) ** 2 + (start_node.getY() - end_node.getY()) ** 2) - size;
line.points([start_node.getX(), start_node.getY(), end_node.getX(), end_node.getY()]);
line.dash([line_length / 2, size, line_length / 2]);
text.x((start_node.getX() + end_node.getX()) / 2 - node_size);
text.y((start_node.getY() + end_node.getY()) / 2 - node_size),
edge_layer.draw();
}
function reset_node_style(){
for (let id in konva_nodes) {
konva_nodes[id].children[0].strokeWidth(node_layout.strokeWidth);
konva_nodes[id].children[0].stroke("black");
}
}
function reset_edge_style(){
for (let id in konva_edges) {
konva_edges[id].children[0].strokeWidth(edge_layout.strokeWidth);
konva_edges[id].children[0].stroke("black");
}
}
function draw_selected_nodes() {
reset_node_style();
reset_edge_style();
for (let id in selected) {
selected[id].children[0].strokeWidth(node_layout.strokeWidth + 1);
selected[id].children[0].stroke("rgb(240, 141, 25)");
}
node_layer.draw();
}
function draw_path(path){
let path_elements = [];
for (let id in path){
path_elements.push(stage.findOne("#"+path[id]));
if (id == 0) {
continue
}
let current_edge = stage.findOne("#"+path[id-1]+path[id]);
if (current_edge){
path_elements.push(current_edge);
}else{
path_elements.push(stage.findOne("#"+path[id]+path[id-1]));
}
}
reset_node_style();
reset_edge_style();
for (let id in path_elements) {
path_elements[id].children[0].strokeWidth(node_layout.strokeWidth + 1);
path_elements[id].children[0].stroke("rgb(240, 141, 25)");
}
}
function on_node_click(e) {
e.cancelBubble = true;
// let mousePos = stage.getPointerPosition();
let idx = selected.indexOf(this);
if (idx >= 0) {
selected.splice(idx, 1);
} else if (selected.length < 2) {
selected.push(this);
} else {
selected.shift();
selected.push(this);
}
draw_selected_nodes();
};
function create_node(){
if (state != "create_node"){
state = "create_node";
// let mousePos = stage.getPointerPosition();
let pos = {x: width/2, y: height/2};
create_input(pos.x, pos.y).then(function(text) {
draw_new_node(text, pos.x, pos.y);
state = "idle";
}, function(){state = "idle"});
}
}
function create_edge() {
if (selected.length < 2) {
show_error("Select at least 2 nodes.");
return;
}
let x = (selected[0].getX() + selected[1].getX()) / 2;
let y = (selected[0].getY() + selected[1].getY()) / 2;
create_input(x, y).then(function(text) {
draw_new_edge({
"start": selected[0].children[1].text(),
"end": selected[1].children[1].text(),
"weight": text
});
selected = [];
draw_selected_nodes();
},function(){});
}
function create_input(x, y) {
return new Promise(function(resolve, reject) {
var input = document.createElement("input");
input.setAttribute("type", "text");
input.style.left = x + "px";
input.style.top = y + "px";
canvas.appendChild(input);
input.focus();
stage.listening(false);
window.addEventListener("keyup", function(event) {
if (event.key === "Escape") {
input.remove();
stage.listening(true);
reject();
}
});
input.addEventListener("keyup", function(event) {
if (event.key === "Enter") {
resolve(input.value);
input.remove();
stage.listening(true);
}
});
});
}
function find_path(){
draw_path(elem);
}
function update_edges(edges) {
for (id in edges) {
update_edge(edges[id]);
}
}
node_layer.on('beforeDraw', function() {
update_edges(konva_edges);
});
// adapt the stage on any window resize
function fitStageIntoParentContainer() {
width = canvas.offsetWidth;
height = canvas.offsetHeight;
stage.width(canvas.offsetWidth);
stage.height(canvas.offsetHeight);
stage.draw();
}
window.addEventListener('resize', fitStageIntoParentContainer);
//-----------------------------------------------------------------------------
//---------------------(additions) auto layout---------------------------------
//-----------------------------------------------------------------------------
function toggle_auto_layout() {
auto_layout = !auto_layout;
document.querySelector("[data-activeon=auto_layout]").classList.toggle("active");
sessionStorage.setItem("auto_layout", auto_layout);
}
function normalize(vec) {
let l = Math.sqrt(vec[0]**2+vec[1]**2);
return [vec[0]/l,vec[1]/l];
}
function in_edges(start_node, end_node){
let idx = graph_copy[start_node.attrs.id].indexOf(end_node.attrs.id);
if (idx < 0){
return false;
}
return true;
}
function force_directed_graph(spring_length = 300, step = 0.004){
for (var idx in konva_nodes){
if (konva_nodes[idx].attrs.dragging){
continue
}
var x1 = konva_nodes[idx].getX();
var y1 = konva_nodes[idx].getY();
var rep = [0,0];
var attr = [0,0];
for (let jdx in konva_nodes){
if (idx == jdx){
continue;
}
let x2 = konva_nodes[jdx].getX();
let y2 = konva_nodes[jdx].getY();
let dir = normalize([x2-x1,y2-y1]);
let dist = ((x1-x2)**2+(y1-y2)**2);
if (in_edges(konva_nodes[idx], konva_nodes[jdx])){
let f = (dist/spring_length);
attr[0] += f*dir[0];
attr[1] += f*dir[1];
}
let f = (spring_length**2/Math.sqrt(dist));
rep[0] -= f*dir[0];
rep[1] -= f*dir[1];
}
let new_x = x1+step*(rep[0]+attr[0]);
let new_y = y1+step*(rep[1]+attr[1]);
if (new_x < 2*node_size) {
new_x += 10*step*((2*node_size-new_x));
} else if (new_x > width-2*node_size) {
new_x += 10*step*((width-2*node_size-new_x));
}
if (new_y < 2*node_size) {
new_y += 10*step*((2*node_size-new_y));
} else if (new_y > height-2*node_size) {
new_y += 10*step*((height-2*node_size-new_y));
}
konva_nodes[idx].x(new_x);
konva_nodes[idx].y(new_y);
}
}
var anim = new Konva.Animation(function(frame) {
if (auto_layout) {
const spring_length = 300;
const step = 0.00025*frame.timeDiff;
force_directed_graph(spring_length, step)
}
}, node_layer);
anim.start();
function show_error(msg){
let infobar = document.querySelector("#infobar");
infobar.innerHTML = msg;
infobar.classList.add("active");
window.setTimeout(function(){ infobar.classList.remove("active"); }, 4000);
}
// bug: this avoids glitches
window.onfocus = function () {auto_layout = true;};
window.onblur = function () {auto_layout = false;};

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="de" dir="ltr">
<head>
<meta charset="utf-8">
<title>Assignment</title>
<link rel="stylesheet" href="{{ url_for('static', filename='fonts/roboto.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.min.css') }}">
<script src="https://unpkg.com/konva@4.0.7/konva.min.js"></script>
</head>
<body>
<main>
<div id="infobar">You have to select at least 2 nodes</div>
<div id="canvas" class="canvas"></div>
<nav>
<button type="button" onclick="create_node()">
<img src="{{ url_for('static', filename='imgs/plus.svg') }}">
<h1>Create</h1>
</button>
<button type="button" onclick="create_edge()">
<img src="{{ url_for('static', filename='imgs/drawing.svg') }}">
<h1>Connect</h1>
</button>
<button type="button" onclick="find_path()">
<img src="{{ url_for('static', filename='imgs/share.svg') }}">
<h1>Find Path</h1>
</button>
</nav>
<footer>
<button type="button" data-activeon="auto_layout" onclick="toggle_auto_layout()">
<h1>Automatic Layout</h1>
</button>
</footer>
</main>
<script>
var nodes = ["Aachen", "Berlin", "Chemnitz", "Dresden", "Essen"]
var edges = [{
"start": "Aachen",
"end": "Berlin",
"weight": 800
},{
"start": "Dresden",
"end": "Berlin",
"weight": 800
},{
"start": "Essen",
"end": "Berlin",
"weight": 800
}, {
"start": "Chemnitz",
"end": "Dresden",
"weight": 5
},
{
"start": "Chemnitz",
"end": "Berlin",
"weight": 5
}]
</script>
<script src="{{ url_for('static', filename='js/canvas.js')}}"></script>
</body>
</html>

@ -2,6 +2,8 @@ from flask import Flask, jsonify, request, render_template
import numpy as np import numpy as np
app = Flask(__name__) app = Flask(__name__)
app.template_folder = "frontend/"
app.static_folder = "frontend/assets/"
class Graph(object): class Graph(object):

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="de" dir="ltr">
<head>
<meta charset="utf-8">
<title>Assignment</title>
</head>
<body>
<p><b>nodes:</b> {{ nodes }}</p>
<p><b>edges:</b> {{ edges }}</p>
<div ></div>
<script src="{{ url_for('static', filename='js/canvas.js')}}"></script>
</body>
</html>
Loading…
Cancel
Save