Browse Source

initial

master
Victor Giers 3 years ago
commit
f591210c2e
100 changed files with 7521 additions and 0 deletions
  1. 26
    0
      README.md
  2. 43
    0
      build/application.linux-arm64/data/save.csv
  3. BIN
      build/application.linux-arm64/lib/core.jar
  4. BIN
      build/application.linux-arm64/lib/gluegen-rt-natives-linux-aarch64.jar
  5. BIN
      build/application.linux-arm64/lib/gluegen-rt.jar
  6. BIN
      build/application.linux-arm64/lib/jogl-all-natives-linux-aarch64.jar
  7. BIN
      build/application.linux-arm64/lib/jogl-all.jar
  8. BIN
      build/application.linux-arm64/lib/ontology.jar
  9. 5
    0
      build/application.linux-arm64/ontology
  10. 78
    0
      build/application.linux-arm64/source/buttons.pde
  11. 60
    0
      build/application.linux-arm64/source/links.pde
  12. 129
    0
      build/application.linux-arm64/source/nodes.pde
  13. 577
    0
      build/application.linux-arm64/source/ontology.java
  14. 83
    0
      build/application.linux-arm64/source/ontology.pde
  15. 74
    0
      build/application.linux-arm64/source/save_load.pde
  16. 124
    0
      build/application.linux-arm64/source/ui.pde
  17. 43
    0
      build/application.linux-armv6hf/data/save.csv
  18. BIN
      build/application.linux-armv6hf/lib/core.jar
  19. BIN
      build/application.linux-armv6hf/lib/gluegen-rt-natives-linux-armv6hf.jar
  20. BIN
      build/application.linux-armv6hf/lib/gluegen-rt.jar
  21. BIN
      build/application.linux-armv6hf/lib/jogl-all-natives-linux-armv6hf.jar
  22. BIN
      build/application.linux-armv6hf/lib/jogl-all.jar
  23. BIN
      build/application.linux-armv6hf/lib/ontology.jar
  24. 5
    0
      build/application.linux-armv6hf/ontology
  25. 78
    0
      build/application.linux-armv6hf/source/buttons.pde
  26. 60
    0
      build/application.linux-armv6hf/source/links.pde
  27. 129
    0
      build/application.linux-armv6hf/source/nodes.pde
  28. 577
    0
      build/application.linux-armv6hf/source/ontology.java
  29. 83
    0
      build/application.linux-armv6hf/source/ontology.pde
  30. 74
    0
      build/application.linux-armv6hf/source/save_load.pde
  31. 124
    0
      build/application.linux-armv6hf/source/ui.pde
  32. 43
    0
      build/application.linux32/data/save.csv
  33. BIN
      build/application.linux32/lib/core.jar
  34. BIN
      build/application.linux32/lib/gluegen-rt-natives-linux-i586.jar
  35. BIN
      build/application.linux32/lib/gluegen-rt.jar
  36. BIN
      build/application.linux32/lib/jogl-all-natives-linux-i586.jar
  37. BIN
      build/application.linux32/lib/jogl-all.jar
  38. BIN
      build/application.linux32/lib/ontology.jar
  39. 5
    0
      build/application.linux32/ontology
  40. 78
    0
      build/application.linux32/source/buttons.pde
  41. 60
    0
      build/application.linux32/source/links.pde
  42. 129
    0
      build/application.linux32/source/nodes.pde
  43. 577
    0
      build/application.linux32/source/ontology.java
  44. 83
    0
      build/application.linux32/source/ontology.pde
  45. 74
    0
      build/application.linux32/source/save_load.pde
  46. 124
    0
      build/application.linux32/source/ui.pde
  47. 43
    0
      build/application.linux64/data/save.csv
  48. BIN
      build/application.linux64/lib/core.jar
  49. BIN
      build/application.linux64/lib/gluegen-rt-natives-linux-amd64.jar
  50. BIN
      build/application.linux64/lib/gluegen-rt.jar
  51. BIN
      build/application.linux64/lib/jogl-all-natives-linux-amd64.jar
  52. BIN
      build/application.linux64/lib/jogl-all.jar
  53. BIN
      build/application.linux64/lib/ontology.jar
  54. 5
    0
      build/application.linux64/ontology
  55. 78
    0
      build/application.linux64/source/buttons.pde
  56. 60
    0
      build/application.linux64/source/links.pde
  57. 129
    0
      build/application.linux64/source/nodes.pde
  58. 577
    0
      build/application.linux64/source/ontology.java
  59. 83
    0
      build/application.linux64/source/ontology.pde
  60. 74
    0
      build/application.linux64/source/save_load.pde
  61. 124
    0
      build/application.linux64/source/ui.pde
  62. 43
    0
      build/application.windows32/data/save.csv
  63. BIN
      build/application.windows32/lib/core.jar
  64. BIN
      build/application.windows32/lib/gluegen-rt-natives-windows-i586.jar
  65. BIN
      build/application.windows32/lib/gluegen-rt.jar
  66. BIN
      build/application.windows32/lib/jogl-all-natives-windows-i586.jar
  67. BIN
      build/application.windows32/lib/jogl-all.jar
  68. BIN
      build/application.windows32/lib/ontology.jar
  69. BIN
      build/application.windows32/ontology.exe
  70. 78
    0
      build/application.windows32/source/buttons.pde
  71. 60
    0
      build/application.windows32/source/links.pde
  72. 129
    0
      build/application.windows32/source/nodes.pde
  73. 577
    0
      build/application.windows32/source/ontology.java
  74. 83
    0
      build/application.windows32/source/ontology.pde
  75. 74
    0
      build/application.windows32/source/save_load.pde
  76. 124
    0
      build/application.windows32/source/ui.pde
  77. 43
    0
      build/application.windows64/data/save.csv
  78. BIN
      build/application.windows64/lib/core.jar
  79. BIN
      build/application.windows64/lib/gluegen-rt-natives-windows-amd64.jar
  80. BIN
      build/application.windows64/lib/gluegen-rt.jar
  81. BIN
      build/application.windows64/lib/jogl-all-natives-windows-amd64.jar
  82. BIN
      build/application.windows64/lib/jogl-all.jar
  83. BIN
      build/application.windows64/lib/ontology.jar
  84. BIN
      build/application.windows64/ontology.exe
  85. 78
    0
      build/application.windows64/source/buttons.pde
  86. 60
    0
      build/application.windows64/source/links.pde
  87. 129
    0
      build/application.windows64/source/nodes.pde
  88. 577
    0
      build/application.windows64/source/ontology.java
  89. 83
    0
      build/application.windows64/source/ontology.pde
  90. 74
    0
      build/application.windows64/source/save_load.pde
  91. 124
    0
      build/application.windows64/source/ui.pde
  92. 78
    0
      buttons.pde
  93. 43
    0
      data/save.csv
  94. BIN
      examples/example_bright.png
  95. BIN
      examples/gui_dark.png
  96. 60
    0
      links.pde
  97. 129
    0
      nodes.pde
  98. 83
    0
      ontology.pde
  99. 74
    0
      save_load.pde
  100. 0
    0
      ui.pde

+ 26
- 0
README.md View File

@@ -0,0 +1,26 @@
# Simple Ontology
## Define some truth !
**Create custom node network graphs aka ontologies.**
Tool to make ontological network topologies with.
Has saving and reopening functionality and exports images.
Smallest alternative to protégé, ONTOLIS or OntoStudio.

**How to use**
Add nodes by clicking on "Add Node".
Select a node by clicking on it and type on your keyboard to give it a title / rename it.
Link nodes by dragging "wires" out of the inlets / outlets and plug them into another nodes inlets / outlets (inspired from Blenders node based shading and compositing system).
You can only connect black with red / red with black. Black is inlet, red is outlet. You can not connect inlet with inlet or outlet with outlet.

You need Java 1.8 to run the builds.


**Ideas**
* Subtitles and image-import for each node
* Seperately render UI from workspace
* Database
* Make workspace dragable / movable (like Google maps)
* draw only when necessary (more events) (ditch Processing?)
* Test if it runs on windows
* Even more testing!

Pretty sure I'm gonna leave it as is for the time being though.

+ 43
- 0
build/application.linux-arm64/data/save.csv View File

@@ -0,0 +1,43 @@































Biological Process,395,537,32,33
Physiological Process,292,455,34
Cellular Process,483,455,34
Cellular Physiological Process,391,375,36,35
Cell Cycle,305,296,40,41
Cell Division,544,265,37
Cytokinesis,543,165,38
Cytokinesis after Meiosis,399,54
M Phase of Meiotic Cell Cycle,310,144,38
M Phase,239,225,39
Meiotic Cell Cycle,392,225,39


BIN
build/application.linux-arm64/lib/core.jar View File


BIN
build/application.linux-arm64/lib/gluegen-rt-natives-linux-aarch64.jar View File


BIN
build/application.linux-arm64/lib/gluegen-rt.jar View File


BIN
build/application.linux-arm64/lib/jogl-all-natives-linux-aarch64.jar View File


BIN
build/application.linux-arm64/lib/jogl-all.jar View File


BIN
build/application.linux-arm64/lib/ontology.jar View File


+ 5
- 0
build/application.linux-arm64/ontology View File

@@ -0,0 +1,5 @@
#!/bin/sh

APPDIR=$(readlink -f "$0")
APPDIR=$(dirname "$APPDIR")
java -Xms64m -Xmx12000m -Djna.nosys=true -Djava.library.path="$APPDIR:$APPDIR/lib" -cp "$APPDIR:$APPDIR/lib/ontology.jar:$APPDIR/lib/core.jar:$APPDIR/lib/jogl-all.jar:$APPDIR/lib/gluegen-rt.jar:$APPDIR/lib/jogl-all-natives-linux-aarch64.jar:$APPDIR/lib/gluegen-rt-natives-linux-aarch64.jar" ontology "$@"

+ 78
- 0
build/application.linux-arm64/source/buttons.pde View File

@@ -0,0 +1,78 @@
int buttonCount = 6;
int i_buttonId;

void initButtons() {
buttons[0] = new Button(10, 10, "Add Node");
buttons[1] = new Button(104, 10, "Delete Node");
buttons[2] = new Button(10, 50, "Dark/Bright Mode");
buttons[3] = new Button(10, 90, "Save");
buttons[4] = new Button(10, 130, "Open");
buttons[5] = new Button(10, 170, "Export Image");
/* buttons[0] = new Button(10, 10, "Node Hinzufügen");
buttons[1] = new Button(155, 10, "Löschen");
buttons[2] = new Button(10, 50, "Farben umkehren");
buttons[3] = new Button(10, 90, "Speichern");
buttons[4] = new Button(10, 130, "Öffnen");
buttons[5] = new Button(10, 170, "Bild Exportieren");*/
}

void buttonFunctions(int functionID) {
switch (functionID) {
case(0):
addNode(int(random(50, width-50)), int(random(30, height-150)), "");
i_selectedNode = nodeCount-1;
break;
case(1):
deleteSelectedNode();
break;
case(2):
darkMode = !darkMode;
break;
case(3):
saveCSV(dataPath("save.csv"));
selectOutput("Where to save .csv file to?", "saveCSVFile");
break;
case(4):
selectInput("Select csv File", "loadCSVFile");
break;
case(5):
selectOutput("Where to export .png image file to?", "savePNGFile");
break;
default:
break;
}
}


Button buttons[] = new Button[buttonCount];

class Button {
int id, x, y, w, h;
String label;

boolean hover() {
return (mouseX > x && mouseX < x+w && mouseY > y && mouseY < y+h) ? true : false;
}

void click() {
buttonFunctions(id);
}
Button(int x_, int y_, String label_) {
id = i_buttonId;
i_buttonId++;
x = x_;
y = y_ ;
label = label_;
w = int(textWidth(label))+18;
h = 32;
}

void display() {
stroke(0);
strokeWeight(1);
fill(255, 127);
rect(x, y, w, h, 4);
fill(0);
text(label, x+9, y+h/2+5);
}
}

+ 60
- 0
build/application.linux-arm64/source/links.pde View File

@@ -0,0 +1,60 @@
int linkCount;

boolean dragLink; //state of dragging a link
int dragOriginLet = 0; //dragging from inlet: -1. dragging from outlet: 1.
int dragOriginId = -1; //which nodes out- / inlet did user click on?

void Link(int parentNode, int targetNode) {
/**
* Check if user is adding or removing a link
*/
int exists = -1;
for (int i = 0; i < linkCount; i++) {
if (links[i].parentNode == parentNode && links[i].targetNode == targetNode) {
exists = i;
break;
}
}
if (exists != -1) {
/**
* !active state is pretty much the same as deleted state that the node-class comes with
*/
links[exists].active = !links[exists].active;
} else {
links[linkCount] = new Link(parentNode, targetNode);
linkCount++;
}
}

class Link {
int parentNode, targetNode;
boolean active;
PVector link = new PVector(0, 0);

Link(int parentNode_, int targetNode_) {
parentNode = parentNode_;
targetNode = targetNode_;
active = true;
}

/**
* This could be an event that only gets called when nodes are moved or resized.
*/
void update() {
link.x = nodes[targetNode].inletCenter.x - nodes[parentNode].outletCenter.x;
link.y = nodes[targetNode].inletCenter.y - nodes[parentNode].outletCenter.y;
}

void display() {
stroke(darkMode? 255 : 0);
strokeWeight(2);
pushMatrix();
translate(nodes[parentNode].outletCenter.x, nodes[parentNode].outletCenter.y);
line(0, 0, link.x, link.y);
translate(-nodes[parentNode].outletCenter.x+(nodes[parentNode].outletCenter.x+nodes[targetNode].inletCenter.x)/2, -nodes[parentNode].outletCenter.y+(nodes[parentNode].outletCenter.y+nodes[targetNode].inletCenter.y)/2);
rotate(link.heading());
fill(darkMode? 0 : 255);
triangle(0, textSize/2, textSize, 0, 0, -textSize/2);
popMatrix();
}
}

+ 129
- 0
build/application.linux-arm64/source/nodes.pde View File

@@ -0,0 +1,129 @@
int nodeCount;
int i_selectedNode = -1;
int padding = textSize/3*2; //Padding between text and surrounding box
int letTolerance = 10; //Widens the clickable areas of in/outlets a bit

void addNode(int x, int y, String title) {
nodes[nodeCount] = new Node(x, y, title);
nodeCount++;
}

void deselect() {
i_selectedNode = -1;
}

void deleteSelectedNode() { //Seperate from class for garbage collector reasons
if (i_selectedNode != -1) {
for (int i = 0; i < linkCount; i++) {
if (links[i].targetNode == i_selectedNode || links[i].parentNode == i_selectedNode) {
links[i].active = false;
}
}
nodes[i_selectedNode].deleted = true;
i_selectedNode = -1;
}
}

class Node {
String title;
int id;
int inlets = 2;
int outlets = 2;
int x, y;
int h = textSize+2*padding;
int w = textSize+2*padding;
PVector dragMouseStartPoint = new PVector(0, 0);
PVector outletCenter = new PVector(0, 0);
PVector inletCenter = new PVector(0, 0);
boolean deleted;
boolean drag;
boolean hover() {
if (mouseX > x-w/2 && mouseX < x+w/2 && mouseY > y-h/2 && mouseY < y+h/2) {
return true;
} else {
return false;
}
}
boolean hoverOutlet() {
if (mouseX > x-(textSize+padding)/2-letTolerance && mouseX < x+(textSize+padding)/2+letTolerance && mouseY > y-padding-textSize/2-(textSize+padding)/2-letTolerance && mouseY < y-padding-textSize/2) {
return true;
} else {
return false;
}
}
boolean hoverInlet() {
if (mouseX > x-(textSize+padding)/2-letTolerance && mouseX < x+(textSize+padding)/2+letTolerance && mouseY > y+padding+textSize/2 && mouseY < y+padding+textSize/2+(textSize+padding)/2+letTolerance) {
return true;
} else {
return false;
}
}

Node(int x_, int y_, String title_) {
id = nodeCount;
title = title_;
x = x_;
y = y_;
setVectors();
refreshSize();
}

void setVectors() {
outletCenter.x = x;
outletCenter.y = y-(padding+textSize/2)*1.3;
inletCenter.x = x;
inletCenter.y = y+(padding+textSize/2)*1.3;
}

/**
* Called when changing nodes title or when zooming via mouse-wheel
*/
void refreshSize() {
w = int(textWidth(title))+2*padding;
h = textSize+2*padding;
}

/**
* Update-function is really only necessary when dragging the node
*/
void update() {
if (drag) {
x -= dragMouseStartPoint.x - mouseX;
dragMouseStartPoint.x = mouseX;
y -= dragMouseStartPoint.y - mouseY;
dragMouseStartPoint.y = mouseY;
setVectors();
}
}

void display() {
/**
* Selected nodes border
*/
if (i_selectedNode == id) {
noStroke();
fill(150, 100);
rect(x, y, w+20, h+20, 4);
}

/**
* Inlet & Outlet
*/
stroke(255, darkMode? 255 : 0);
strokeWeight(1);
fill(255, 0, 0);
ellipse(x, y-padding-textSize/2, textSize+padding, textSize+padding);
fill(0);
ellipse(x, y+padding+textSize/2, textSize+padding, textSize+padding);

/**
* Box & Title
*/
stroke(127);
strokeWeight(2);
fill(50);
rect(x, y, w, h, 4);
fill(255);
text(title, x, y+textSize/3);
}
}

+ 577
- 0
build/application.linux-arm64/source/ontology.java View File

@@ -0,0 +1,577 @@
import processing.core.*;
import processing.data.*;
import processing.event.*;
import processing.opengl.*;

import java.util.HashMap;
import java.util.ArrayList;
import java.io.File;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;

public class ontology extends PApplet {

Node nodes[] = new Node[1000];
Link links[] = new Link[6000];

int textSize = 14;
boolean darkMode = true;

public void setup() {
surface.setResizable(true);

textSize(textSize);

initButtons();
/**
* When saving, a copy of the save-file is stored locally which is opened when starting the program
*/
try {
loadCSV(dataPath("save.csv"));
}
catch(Exception e) {
println("No File found, starting blank");
}
}

public void draw() {
background(darkMode? 0 : 255);

/**
* Update and display links
*/
for (int i = 0; i < linkCount; i++) {
if (links[i].active) {
links[i].update();
links[i].display();
}
}

/**
* Update and display nodes
*/
for (int i = 0; i < nodeCount; i++) {
if (!nodes[i].deleted) {
nodes[i].update();
nodes[i].display();
}
}

/**
* Update and display ui, hide via exporting-flag for easy image-saving
* Not all buttons always show, that's why this isn't in a loop
*/
if (!exporting) {
textSize(14);
rectMode(CORNER);
textAlign(CORNER);
buttons[0].display();
if (i_selectedNode != -1) buttons[1].display();
buttons[2].display();
buttons[3].display();
buttons[4].display();
buttons[5].display();
rectMode(CENTER);
textAlign(CENTER);
textSize(textSize);
} else {
save(savePNGpath);
exporting = false;
}

/**
* Pull "wire" out of in/outlet before creating a link (see mouseReleased in ui)
*/
if (dragLink) {
stroke(darkMode? 255:0);
strokeWeight(2);
if (dragOriginLet == -1) {
line(nodes[dragOriginId].inletCenter.x, nodes[dragOriginId].inletCenter.y, mouseX, mouseY);
} else if (dragOriginLet == 1) {
line(nodes[dragOriginId].outletCenter.x, nodes[dragOriginId].outletCenter.y, mouseX, mouseY);
}
}
}
int buttonCount = 6;
int i_buttonId;

public void initButtons() {
buttons[0] = new Button(10, 10, "Add Node");
buttons[1] = new Button(104, 10, "Delete Node");
buttons[2] = new Button(10, 50, "Dark/Bright Mode");
buttons[3] = new Button(10, 90, "Save");
buttons[4] = new Button(10, 130, "Open");
buttons[5] = new Button(10, 170, "Export Image");
/* buttons[0] = new Button(10, 10, "Node Hinzufügen");
buttons[1] = new Button(155, 10, "Löschen");
buttons[2] = new Button(10, 50, "Farben umkehren");
buttons[3] = new Button(10, 90, "Speichern");
buttons[4] = new Button(10, 130, "Öffnen");
buttons[5] = new Button(10, 170, "Bild Exportieren");*/
}

public void buttonFunctions(int functionID) {
switch (functionID) {
case(0):
addNode(PApplet.parseInt(random(50, width-50)), PApplet.parseInt(random(30, height-150)), "");
i_selectedNode = nodeCount-1;
break;
case(1):
deleteSelectedNode();
break;
case(2):
darkMode = !darkMode;
break;
case(3):
saveCSV(dataPath("save.csv"));
selectOutput("Where to save .csv file to?", "saveCSVFile");
break;
case(4):
selectInput("Select csv File", "loadCSVFile");
break;
case(5):
selectOutput("Where to export .png image file to?", "savePNGFile");
break;
default:
break;
}
}


Button buttons[] = new Button[buttonCount];

class Button {
int id, x, y, w, h;
String label;

public boolean hover() {
return (mouseX > x && mouseX < x+w && mouseY > y && mouseY < y+h) ? true : false;
}

public void click() {
buttonFunctions(id);
}
Button(int x_, int y_, String label_) {
id = i_buttonId;
i_buttonId++;
x = x_;
y = y_ ;
label = label_;
w = PApplet.parseInt(textWidth(label))+18;
h = 32;
}

public void display() {
stroke(0);
strokeWeight(1);
fill(255, 127);
rect(x, y, w, h, 4);
fill(0);
text(label, x+9, y+h/2+5);
}
}
int linkCount;

boolean dragLink; //state of dragging a link
int dragOriginLet = 0; //dragging from inlet: -1. dragging from outlet: 1.
int dragOriginId = -1; //which nodes out- / inlet did user click on?

public void Link(int parentNode, int targetNode) {
/**
* Check if user is adding or removing a link
*/
int exists = -1;
for (int i = 0; i < linkCount; i++) {
if (links[i].parentNode == parentNode && links[i].targetNode == targetNode) {
exists = i;
break;
}
}
if (exists != -1) {
/**
* !active state is pretty much the same as deleted state that the node-class comes with
*/
links[exists].active = !links[exists].active;
} else {
links[linkCount] = new Link(parentNode, targetNode);
linkCount++;
}
}

class Link {
int parentNode, targetNode;
boolean active;
PVector link = new PVector(0, 0);

Link(int parentNode_, int targetNode_) {
parentNode = parentNode_;
targetNode = targetNode_;
active = true;
}

/**
* This could be an event that only gets called when nodes are moved or resized.
*/
public void update() {
link.x = nodes[targetNode].inletCenter.x - nodes[parentNode].outletCenter.x;
link.y = nodes[targetNode].inletCenter.y - nodes[parentNode].outletCenter.y;
}

public void display() {
stroke(darkMode? 255 : 0);
strokeWeight(2);
pushMatrix();
translate(nodes[parentNode].outletCenter.x, nodes[parentNode].outletCenter.y);
line(0, 0, link.x, link.y);
translate(-nodes[parentNode].outletCenter.x+(nodes[parentNode].outletCenter.x+nodes[targetNode].inletCenter.x)/2, -nodes[parentNode].outletCenter.y+(nodes[parentNode].outletCenter.y+nodes[targetNode].inletCenter.y)/2);
rotate(link.heading());
fill(darkMode? 0 : 255);
triangle(0, textSize/2, textSize, 0, 0, -textSize/2);
popMatrix();
}
}
int nodeCount;
int i_selectedNode = -1;
int padding = textSize/3*2; //Padding between text and surrounding box
int letTolerance = 10; //Widens the clickable areas of in/outlets a bit

public void addNode(int x, int y, String title) {
nodes[nodeCount] = new Node(x, y, title);
nodeCount++;
}

public void deselect() {
i_selectedNode = -1;
}

public void deleteSelectedNode() { //Seperate from class for garbage collector reasons
if (i_selectedNode != -1) {
for (int i = 0; i < linkCount; i++) {
if (links[i].targetNode == i_selectedNode || links[i].parentNode == i_selectedNode) {
links[i].active = false;
}
}
nodes[i_selectedNode].deleted = true;
i_selectedNode = -1;
}
}

class Node {
String title;
int id;
int inlets = 2;
int outlets = 2;
int x, y;
int h = textSize+2*padding;
int w = textSize+2*padding;
PVector dragMouseStartPoint = new PVector(0, 0);
PVector outletCenter = new PVector(0, 0);
PVector inletCenter = new PVector(0, 0);
boolean deleted;
boolean drag;
public boolean hover() {
if (mouseX > x-w/2 && mouseX < x+w/2 && mouseY > y-h/2 && mouseY < y+h/2) {
return true;
} else {
return false;
}
}
public boolean hoverOutlet() {
if (mouseX > x-(textSize+padding)/2-letTolerance && mouseX < x+(textSize+padding)/2+letTolerance && mouseY > y-padding-textSize/2-(textSize+padding)/2-letTolerance && mouseY < y-padding-textSize/2) {
return true;
} else {
return false;
}
}
public boolean hoverInlet() {
if (mouseX > x-(textSize+padding)/2-letTolerance && mouseX < x+(textSize+padding)/2+letTolerance && mouseY > y+padding+textSize/2 && mouseY < y+padding+textSize/2+(textSize+padding)/2+letTolerance) {
return true;
} else {
return false;
}
}

Node(int x_, int y_, String title_) {
id = nodeCount;
title = title_;
x = x_;
y = y_;
setVectors();
refreshSize();
}

public void setVectors() {
outletCenter.x = x;
outletCenter.y = y-(padding+textSize/2)*1.3f;
inletCenter.x = x;
inletCenter.y = y+(padding+textSize/2)*1.3f;
}

/**
* Called when changing nodes title or when zooming via mouse-wheel
*/
public void refreshSize() {
w = PApplet.parseInt(textWidth(title))+2*padding;
h = textSize+2*padding;
}

/**
* Update-function is really only necessary when dragging the node
*/
public void update() {
if (drag) {
x -= dragMouseStartPoint.x - mouseX;
dragMouseStartPoint.x = mouseX;
y -= dragMouseStartPoint.y - mouseY;
dragMouseStartPoint.y = mouseY;
setVectors();
}
}

public void display() {
/**
* Selected nodes border
*/
if (i_selectedNode == id) {
noStroke();
fill(150, 100);
rect(x, y, w+20, h+20, 4);
}

/**
* Inlet & Outlet
*/
stroke(255, darkMode? 255 : 0);
strokeWeight(1);
fill(255, 0, 0);
ellipse(x, y-padding-textSize/2, textSize+padding, textSize+padding);
fill(0);
ellipse(x, y+padding+textSize/2, textSize+padding, textSize+padding);

/**
* Box & Title
*/
stroke(127);
strokeWeight(2);
fill(50);
rect(x, y, w, h, 4);
fill(255);
text(title, x, y+textSize/3);
}
}

/**
* Saving and loading is a messy hack!
* You'll get a blank line in your save file for each deleted object.
* That's because of my lack of skills of dodging the garbage collector.
* I didn't loose data to corruption yet.
* Don't touch the save files if you don't want to mess them up!
**/

boolean exporting;
String savePNGpath = dataPath("save.png");

public void savePNGFile(File selection) {
savePNGpath = selection.getPath();
exporting = true;
}

public void saveCSVFile(File selection) {
saveCSV(selection.getPath());
}

public void loadCSVFile(File selection) {
loadCSV(selection.getPath());
}

public void loadCSV(String path) {
for (int i = 0; i < nodeCount; i++) {
//nodes[i].deleted = true;
i_selectedNode = i;
deleteSelectedNode();
}
nodeCount = 0;

for (int i = 0; i < linkCount; i++) {
links[i].active = false;
}
linkCount = 0;

String[] loadString = loadStrings(path);
for (int i = 0; i < loadString.length-1; i++) {
//This is ultra shitty
if (loadString[i].length() == 0) {
addNode(-1000, -1000, " ");
i_selectedNode = nodeCount - 1;
deleteSelectedNode();
//nodes[nodeCount-1].deleted = true;
} else {
String nodeData[] = split(loadString[i], ',');
addNode(PApplet.parseInt(nodeData[1]), PApplet.parseInt(nodeData[2]), nodeData[0]);
}
}
for (int i = 0; i < loadString.length-1; i++) {
String nodeData[] = split(loadString[i], ',');
for (int j = 3; j < nodeData.length; j++) {
Link(i, PApplet.parseInt(nodeData[j]));
}
}
}

public void saveCSV(String path) {
String saveString = "";
for (int i = 0; i < nodeCount; i++) {
if (!nodes[i].deleted) {
saveString += nodes[i].title + "," + nodes[i].x + "," + nodes[i].y;
for (int j = 0; j < linkCount; j++) {
if (links[j].parentNode == i && links[j].active) {
saveString += "," + str(links[j].targetNode);
}
}
}
saveString += "\n";
}
String[] saveArray = split(saveString, '\n');
saveStrings(path, saveArray);
}

/**
* Keys only get caught for changing a nodes title.
* Previous title is remembered in savedTitle and restored when hitting Escape
*/
String savedTitle;
public void keyPressed() {
if (key == ESC) {
key = 0;
}
if (i_selectedNode != -1) {
if (keyCode == BACKSPACE) {
if (nodes[i_selectedNode].title.length() > 0) {
nodes[i_selectedNode].title = nodes[i_selectedNode].title.substring(0, nodes[i_selectedNode].title.length()-1);
nodes[i_selectedNode].refreshSize();
}
} else if (keyCode == DELETE) {
deleteSelectedNode();
} else if (keyCode == ESC) {
nodes[i_selectedNode].title = savedTitle;
nodes[i_selectedNode].refreshSize();
deselect();
} else if (keyCode == RETURN || keyCode == ENTER) {
deselect();
} else if (keyCode != SHIFT && keyCode != RETURN && keyCode != ENTER && keyCode != CONTROL) {
nodes[i_selectedNode].title = nodes[i_selectedNode].title + key;
nodes[i_selectedNode].refreshSize();
}
}
}


public void mousePressed() {
if (mouseButton == LEFT) {
/**
* Either press a button...
*/
boolean buttonClicked = false;
for (int i = 0; i < buttons.length; i++) {
if (buttons[i].hover()) {
buttons[i].click();
//Not able to break out of here without using a flag
buttonClicked = true;
}
}

if (!buttonClicked) {
for (int i = 0; i < nodeCount; i++) {
/**
* ...or select / drag a node...
*/
if (nodes[i].hover()) {
i_selectedNode = i;
savedTitle = nodes[i].title;
nodes[i].drag = true;
nodes[i].dragMouseStartPoint.x = mouseX;
nodes[i].dragMouseStartPoint.y = mouseY;
break;
}
/**
* ...or pull a new link out of an inlet or outlet.
*/
else if (nodes[i].hoverInlet()) {
dragLink = true;
dragOriginLet = -1;
dragOriginId = i;
} else if (nodes[i].hoverOutlet()) {
dragLink = true;
dragOriginLet = 1;
dragOriginId = i;
}

//deselecting the shit out of this for reasons I don't remember, no care to improve right now
deselect();
}
}
}
}

/**
* Create links
*/
public void mouseReleased() {
for (int i = 0; i < nodeCount; i++) {
nodes[i].drag = false;
if (dragLink && dragOriginId != i) {
if (nodes[i].hoverInlet() && dragOriginLet == 1) {

Link(dragOriginId, i);
}
if (nodes[i].hoverOutlet() && dragOriginLet == -1) {

Link(i, dragOriginId);
}
}
}

/**
* Reset drag&drop states
*/
dragLink = false;
dragOriginLet = 0;
dragOriginId = -1;
}


/**
* Scaling is tied to textSize. Having the UI on a seperate PGraphics would be better, but it's good enough for now
*/
int zoomDelta = 3;
public void mouseWheel(MouseEvent event) {
float zoom = event.getAmount();
if (zoom < 0) {
textSize += zoomDelta;
} else if (zoom > 0) {
if (textSize - zoomDelta > 0) {
textSize -= zoomDelta;
}
}
padding = textSize/3*2;
for (int i = 0; i < nodeCount; i++) {
nodes[i].refreshSize();
nodes[i].setVectors();
}
}

public void settings() { size(800, 600); }
static public void main(String[] passedArgs) {
String[] appletArgs = new String[] { "ontology" };
if (passedArgs != null) {
PApplet.main(concat(appletArgs, passedArgs));
} else {
PApplet.main(appletArgs);
}
}
}

+ 83
- 0
build/application.linux-arm64/source/ontology.pde View File

@@ -0,0 +1,83 @@
Node nodes[] = new Node[1000];
Link links[] = new Link[6000];

int textSize = 14;
boolean darkMode = true;

void setup() {
size(800, 600);
surface.setResizable(true);

textSize(textSize);

initButtons();
/**
* When saving, a copy of the save-file is stored locally which is opened when starting the program
*/
try {
loadCSV(dataPath("save.csv"));
}
catch(Exception e) {
println("No File found, starting blank");
}
}

void draw() {
background(darkMode? 0 : 255);

/**
* Update and display links
*/
for (int i = 0; i < linkCount; i++) {
if (links[i].active) {
links[i].update();
links[i].display();
}
}

/**
* Update and display nodes
*/
for (int i = 0; i < nodeCount; i++) {
if (!nodes[i].deleted) {
nodes[i].update();
nodes[i].display();
}
}

/**
* Update and display ui, hide via exporting-flag for easy image-saving
* Not all buttons always show, that's why this isn't in a loop
*/
if (!exporting) {
textSize(14);
rectMode(CORNER);
textAlign(CORNER);
buttons[0].display();
if (i_selectedNode != -1) buttons[1].display();
buttons[2].display();
buttons[3].display();
buttons[4].display();
buttons[5].display();
rectMode(CENTER);
textAlign(CENTER);
textSize(textSize);
} else {
save(savePNGpath);
exporting = false;
}

/**
* Pull "wire" out of in/outlet before creating a link (see mouseReleased in ui)
*/
if (dragLink) {
stroke(darkMode? 255:0);
strokeWeight(2);
if (dragOriginLet == -1) {
line(nodes[dragOriginId].inletCenter.x, nodes[dragOriginId].inletCenter.y, mouseX, mouseY);
} else if (dragOriginLet == 1) {
line(nodes[dragOriginId].outletCenter.x, nodes[dragOriginId].outletCenter.y, mouseX, mouseY);
}
}
}

+ 74
- 0
build/application.linux-arm64/source/save_load.pde View File

@@ -0,0 +1,74 @@
/**
* Saving and loading is a messy hack!
* You'll get a blank line in your save file for each deleted object.
* That's because of my lack of skills of dodging the garbage collector.
* I didn't loose data to corruption yet.
* Don't touch the save files if you don't want to mess them up!
**/

boolean exporting;
String savePNGpath = dataPath("save.png");

void savePNGFile(File selection) {
savePNGpath = selection.getPath();
exporting = true;
}

void saveCSVFile(File selection) {
saveCSV(selection.getPath());
}

void loadCSVFile(File selection) {
loadCSV(selection.getPath());
}

void loadCSV(String path) {
for (int i = 0; i < nodeCount; i++) {
//nodes[i].deleted = true;
i_selectedNode = i;
deleteSelectedNode();
}
nodeCount = 0;

for (int i = 0; i < linkCount; i++) {
links[i].active = false;
}
linkCount = 0;

String[] loadString = loadStrings(path);
for (int i = 0; i < loadString.length-1; i++) {
//This is ultra shitty
if (loadString[i].length() == 0) {
addNode(-1000, -1000, " ");
i_selectedNode = nodeCount - 1;
deleteSelectedNode();
//nodes[nodeCount-1].deleted = true;
} else {
String nodeData[] = split(loadString[i], ',');
addNode(int(nodeData[1]), int(nodeData[2]), nodeData[0]);
}
}
for (int i = 0; i < loadString.length-1; i++) {
String nodeData[] = split(loadString[i], ',');
for (int j = 3; j < nodeData.length; j++) {
Link(i, int(nodeData[j]));
}
}
}

void saveCSV(String path) {
String saveString = "";
for (int i = 0; i < nodeCount; i++) {
if (!nodes[i].deleted) {
saveString += nodes[i].title + "," + nodes[i].x + "," + nodes[i].y;
for (int j = 0; j < linkCount; j++) {
if (links[j].parentNode == i && links[j].active) {
saveString += "," + str(links[j].targetNode);
}
}
}
saveString += "\n";
}
String[] saveArray = split(saveString, '\n');
saveStrings(path, saveArray);
}

+ 124
- 0
build/application.linux-arm64/source/ui.pde View File

@@ -0,0 +1,124 @@
/**
* Keys only get caught for changing a nodes title.
* Previous title is remembered in savedTitle and restored when hitting Escape
*/
String savedTitle;
void keyPressed() {
if (key == ESC) {
key = 0;
}
if (i_selectedNode != -1) {
if (keyCode == BACKSPACE) {
if (nodes[i_selectedNode].title.length() > 0) {
nodes[i_selectedNode].title = nodes[i_selectedNode].title.substring(0, nodes[i_selectedNode].title.length()-1);
nodes[i_selectedNode].refreshSize();
}
} else if (keyCode == DELETE) {
deleteSelectedNode();
} else if (keyCode == ESC) {
nodes[i_selectedNode].title = savedTitle;
nodes[i_selectedNode].refreshSize();
deselect();
} else if (keyCode == RETURN || keyCode == ENTER) {
deselect();
} else if (keyCode != SHIFT && keyCode != RETURN && keyCode != ENTER && keyCode != CONTROL) {
nodes[i_selectedNode].title = nodes[i_selectedNode].title + key;
nodes[i_selectedNode].refreshSize();
}
}
}


void mousePressed() {
if (mouseButton == LEFT) {
/**
* Either press a button...
*/
boolean buttonClicked = false;
for (int i = 0; i < buttons.length; i++) {
if (buttons[i].hover()) {
buttons[i].click();
//Not able to break out of here without using a flag
buttonClicked = true;
}
}

if (!buttonClicked) {
for (int i = 0; i < nodeCount; i++) {
/**
* ...or select / drag a node...
*/
if (nodes[i].hover()) {
i_selectedNode = i;
savedTitle = nodes[i].title;
nodes[i].drag = true;
nodes[i].dragMouseStartPoint.x = mouseX;
nodes[i].dragMouseStartPoint.y = mouseY;
break;
}
/**
* ...or pull a new link out of an inlet or outlet.
*/
else if (nodes[i].hoverInlet()) {
dragLink = true;
dragOriginLet = -1;
dragOriginId = i;
} else if (nodes[i].hoverOutlet()) {
dragLink = true;
dragOriginLet = 1;
dragOriginId = i;
}

//deselecting the shit out of this for reasons I don't remember, no care to improve right now
deselect();
}
}
}
}

/**
* Create links
*/
void mouseReleased() {
for (int i = 0; i < nodeCount; i++) {
nodes[i].drag = false;
if (dragLink && dragOriginId != i) {
if (nodes[i].hoverInlet() && dragOriginLet == 1) {

Link(dragOriginId, i);
}
if (nodes[i].hoverOutlet() && dragOriginLet == -1) {

Link(i, dragOriginId);
}
}
}

/**
* Reset drag&drop states
*/
dragLink = false;
dragOriginLet = 0;
dragOriginId = -1;
}


/**
* Scaling is tied to textSize. Having the UI on a seperate PGraphics would be better, but it's good enough for now
*/
int zoomDelta = 3;
void mouseWheel(MouseEvent event) {
float zoom = event.getAmount();
if (zoom < 0) {
textSize += zoomDelta;
} else if (zoom > 0) {
if (textSize - zoomDelta > 0) {
textSize -= zoomDelta;
}
}
padding = textSize/3*2;
for (int i = 0; i < nodeCount; i++) {
nodes[i].refreshSize();
nodes[i].setVectors();
}
}

+ 43
- 0
build/application.linux-armv6hf/data/save.csv View File

@@ -0,0 +1,43 @@































Biological Process,395,537,32,33
Physiological Process,292,455,34
Cellular Process,483,455,34
Cellular Physiological Process,391,375,36,35
Cell Cycle,305,296,40,41
Cell Division,544,265,37
Cytokinesis,543,165,38
Cytokinesis after Meiosis,399,54
M Phase of Meiotic Cell Cycle,310,144,38
M Phase,239,225,39
Meiotic Cell Cycle,392,225,39


BIN
build/application.linux-armv6hf/lib/core.jar View File


BIN
build/application.linux-armv6hf/lib/gluegen-rt-natives-linux-armv6hf.jar View File


BIN
build/application.linux-armv6hf/lib/gluegen-rt.jar View File


BIN
build/application.linux-armv6hf/lib/jogl-all-natives-linux-armv6hf.jar View File


BIN
build/application.linux-armv6hf/lib/jogl-all.jar View File


BIN
build/application.linux-armv6hf/lib/ontology.jar View File


+ 5
- 0
build/application.linux-armv6hf/ontology View File

@@ -0,0 +1,5 @@
#!/bin/sh

APPDIR=$(readlink -f "$0")
APPDIR=$(dirname "$APPDIR")
java -Djna.nosys=true -Djava.library.path="$APPDIR:$APPDIR/lib" -cp "$APPDIR:$APPDIR/lib/ontology.jar:$APPDIR/lib/core.jar:$APPDIR/lib/jogl-all.jar:$APPDIR/lib/gluegen-rt.jar:$APPDIR/lib/jogl-all-natives-linux-armv6hf.jar:$APPDIR/lib/gluegen-rt-natives-linux-armv6hf.jar" ontology "$@"

+ 78
- 0
build/application.linux-armv6hf/source/buttons.pde View File

@@ -0,0 +1,78 @@
int buttonCount = 6;
int i_buttonId;

void initButtons() {
buttons[0] = new Button(10, 10, "Add Node");
buttons[1] = new Button(104, 10, "Delete Node");
buttons[2] = new Button(10, 50, "Dark/Bright Mode");
buttons[3] = new Button(10, 90, "Save");
buttons[4] = new Button(10, 130, "Open");
buttons[5] = new Button(10, 170, "Export Image");
/* buttons[0] = new Button(10, 10, "Node Hinzufügen");
buttons[1] = new Button(155, 10, "Löschen");
buttons[2] = new Button(10, 50, "Farben umkehren");
buttons[3] = new Button(10, 90, "Speichern");
buttons[4] = new Button(10, 130, "Öffnen");
buttons[5] = new Button(10, 170, "Bild Exportieren");*/
}

void buttonFunctions(int functionID) {
switch (functionID) {
case(0):
addNode(int(random(50, width-50)), int(random(30, height-150)), "");
i_selectedNode = nodeCount-1;
break;
case(1):
deleteSelectedNode();
break;
case(2):
darkMode = !darkMode;
break;
case(3):
saveCSV(dataPath("save.csv"));
selectOutput("Where to save .csv file to?", "saveCSVFile");
break;
case(4):
selectInput("Select csv File", "loadCSVFile");
break;
case(5):
selectOutput("Where to export .png image file to?", "savePNGFile");
break;
default:
break;
}
}


Button buttons[] = new Button[buttonCount];

class Button {
int id, x, y, w, h;
String label;

boolean hover() {
return (mouseX > x && mouseX < x+w && mouseY > y && mouseY < y+h) ? true : false;
}

void click() {
buttonFunctions(id);
}
Button(int x_, int y_, String label_) {
id = i_buttonId;
i_buttonId++;
x = x_;
y = y_ ;
label = label_;
w = int(textWidth(label))+18;
h = 32;
}

void display() {
stroke(0);
strokeWeight(1);
fill(255, 127);
rect(x, y, w, h, 4);
fill(0);
text(label, x+9, y+h/2+5);
}
}

+ 60
- 0
build/application.linux-armv6hf/source/links.pde View File

@@ -0,0 +1,60 @@
int linkCount;

boolean dragLink; //state of dragging a link
int dragOriginLet = 0; //dragging from inlet: -1. dragging from outlet: 1.
int dragOriginId = -1; //which nodes out- / inlet did user click on?

void Link(int parentNode, int targetNode) {
/**
* Check if user is adding or removing a link
*/
int exists = -1;
for (int i = 0; i < linkCount; i++) {
if (links[i].parentNode == parentNode && links[i].targetNode == targetNode) {
exists = i;
break;
}
}
if (exists != -1) {
/**
* !active state is pretty much the same as deleted state that the node-class comes with
*/
links[exists].active = !links[exists].active;
} else {
links[linkCount] = new Link(parentNode, targetNode);
linkCount++;
}
}

class Link {
int parentNode, targetNode;
boolean active;
PVector link = new PVector(0, 0);

Link(int parentNode_, int targetNode_) {
parentNode = parentNode_;
targetNode = targetNode_;
active = true;
}

/**
* This could be an event that only gets called when nodes are moved or resized.
*/
void update() {
link.x = nodes[targetNode].inletCenter.x - nodes[parentNode].outletCenter.x;
link.y = nodes[targetNode].inletCenter.y - nodes[parentNode].outletCenter.y;
}

void display() {
stroke(darkMode? 255 : 0);
strokeWeight(2);
pushMatrix();
translate(nodes[parentNode].outletCenter.x, nodes[parentNode].outletCenter.y);
line(0, 0, link.x, link.y);
translate(-nodes[parentNode].outletCenter.x+(nodes[parentNode].outletCenter.x+nodes[targetNode].inletCenter.x)/2, -nodes[parentNode].outletCenter.y+(nodes[parentNode].outletCenter.y+nodes[targetNode].inletCenter.y)/2);
rotate(link.heading());
fill(darkMode? 0 : 255);
triangle(0, textSize/2, textSize, 0, 0, -textSize/2);
popMatrix();
}
}

+ 129
- 0
build/application.linux-armv6hf/source/nodes.pde View File

@@ -0,0 +1,129 @@
int nodeCount;
int i_selectedNode = -1;
int padding = textSize/3*2; //Padding between text and surrounding box
int letTolerance = 10; //Widens the clickable areas of in/outlets a bit

void addNode(int x, int y, String title) {
nodes[nodeCount] = new Node(x, y, title);
nodeCount++;
}

void deselect() {
i_selectedNode = -1;
}

void deleteSelectedNode() { //Seperate from class for garbage collector reasons
if (i_selectedNode != -1) {
for (int i = 0; i < linkCount; i++) {
if (links[i].targetNode == i_selectedNode || links[i].parentNode == i_selectedNode) {
links[i].active = false;
}
}
nodes[i_selectedNode].deleted = true;
i_selectedNode = -1;
}
}

class Node {
String title;
int id;
int inlets = 2;
int outlets = 2;
int x, y;
int h = textSize+2*padding;
int w = textSize+2*padding;
PVector dragMouseStartPoint = new PVector(0, 0);
PVector outletCenter = new PVector(0, 0);
PVector inletCenter = new PVector(0, 0);
boolean deleted;
boolean drag;
boolean hover() {
if (mouseX > x-w/2 && mouseX < x+w/2 && mouseY > y-h/2 && mouseY < y+h/2) {
return true;
} else {
return false;
}
}
boolean hoverOutlet() {
if (mouseX > x-(textSize+padding)/2-letTolerance && mouseX < x+(textSize+padding)/2+letTolerance && mouseY > y-padding-textSize/2-(textSize+padding)/2-letTolerance && mouseY < y-padding-textSize/2) {
return true;
} else {
return false;
}
}
boolean hoverInlet() {
if (mouseX > x-(textSize+padding)/2-letTolerance && mouseX < x+(textSize+padding)/2+letTolerance && mouseY > y+padding+textSize/2 && mouseY < y+padding+textSize/2+(textSize+padding)/2+letTolerance) {
return true;
} else {
return false;
}
}

Node(int x_, int y_, String title_) {
id = nodeCount;
title = title_;
x = x_;
y = y_;
setVectors();
refreshSize();
}

void setVectors() {
outletCenter.x = x;
outletCenter.y = y-(padding+textSize/2)*1.3;
inletCenter.x = x;
inletCenter.y = y+(padding+textSize/2)*1.3;
}

/**
* Called when changing nodes title or when zooming via mouse-wheel
*/
void refreshSize() {
w = int(textWidth(title))+2*padding;
h = textSize+2*padding;
}

/**
* Update-function is really only necessary when dragging the node
*/
void update() {
if (drag) {
x -= dragMouseStartPoint.x - mouseX;
dragMouseStartPoint.x = mouseX;
y -= dragMouseStartPoint.y - mouseY;
dragMouseStartPoint.y = mouseY;
setVectors();
}
}

void display() {
/**
* Selected nodes border
*/
if (i_selectedNode == id) {
noStroke();
fill(150, 100);
rect(x, y, w+20, h+20, 4);
}

/**
* Inlet & Outlet
*/
stroke(255, darkMode? 255 : 0);
strokeWeight(1);
fill(255, 0, 0);
ellipse(x, y-padding-textSize/2, textSize+padding, textSize+padding);
fill(0);
ellipse(x, y+padding+textSize/2, textSize+padding, textSize+padding);

/**
* Box & Title
*/
stroke(127);
strokeWeight(2);
fill(50);
rect(x, y, w, h, 4);
fill(255);
text(title, x, y+textSize/3);
}
}

+ 577
- 0
build/application.linux-armv6hf/source/ontology.java View File

@@ -0,0 +1,577 @@
import processing.core.*;
import processing.data.*;
import processing.event.*;
import processing.opengl.*;

import java.util.HashMap;
import java.util.ArrayList;
import java.io.File;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;

public class ontology extends PApplet {

Node nodes[] = new Node[1000];
Link links[] = new Link[6000];

int textSize = 14;
boolean darkMode = true;

public void setup() {
surface.setResizable(true);

textSize(textSize);

initButtons();
/**
* When saving, a copy of the save-file is stored locally which is opened when starting the program
*/
try {
loadCSV(dataPath("save.csv"));
}
catch(Exception e) {
println("No File found, starting blank");
}
}

public void draw() {
background(darkMode? 0 : 255);

/**
* Update and display links
*/
for (int i = 0; i < linkCount; i++) {
if (links[i].active) {
links[i].update();
links[i].display();
}
}

/**
* Update and display nodes
*/
for (int i = 0; i < nodeCount; i++) {
if (!nodes[i].deleted) {
nodes[i].update();
nodes[i].display();
}
}

/**
* Update and display ui, hide via exporting-flag for easy image-saving
* Not all buttons always show, that's why this isn't in a loop
*/
if (!exporting) {
textSize(14);
rectMode(CORNER);
textAlign(CORNER);
buttons[0].display();
if (i_selectedNode != -1) buttons[1].display();
buttons[2].display();
buttons[3].display();
buttons[4].display();
buttons[5].display();
rectMode(CENTER);
textAlign(CENTER);
textSize(textSize);
} else {
save(savePNGpath);
exporting = false;
}

/**
* Pull "wire" out of in/outlet before creating a link (see mouseReleased in ui)
*/
if (dragLink) {
stroke(darkMode? 255:0);
strokeWeight(2);
if (dragOriginLet == -1) {
line(nodes[dragOriginId].inletCenter.x, nodes[dragOriginId].inletCenter.y, mouseX, mouseY);
} else if (dragOriginLet == 1) {
line(nodes[dragOriginId].outletCenter.x, nodes[dragOriginId].outletCenter.y, mouseX, mouseY);
}
}
}
int buttonCount = 6;
int i_buttonId;

public void initButtons() {
buttons[0] = new Button(10, 10, "Add Node");
buttons[1] = new Button(104, 10, "Delete Node");
buttons[2] = new Button(10, 50, "Dark/Bright Mode");
buttons[3] = new Button(10, 90, "Save");
buttons[4] = new Button(10, 130, "Open");
buttons[5] = new Button(10, 170, "Export Image");
/* buttons[0] = new Button(10, 10, "Node Hinzufügen");
buttons[1] = new Button(155, 10, "Löschen");
buttons[2] = new Button(10, 50, "Farben umkehren");
buttons[3] = new Button(10, 90, "Speichern");
buttons[4] = new Button(10, 130, "Öffnen");
buttons[5] = new Button(10, 170, "Bild Exportieren");*/
}

public void buttonFunctions(int functionID) {
switch (functionID) {
case(0):
addNode(PApplet.parseInt(random(50, width-50)), PApplet.parseInt(random(30, height-150)), "");
i_selectedNode = nodeCount-1;
break;
case(1):
deleteSelectedNode();
break;
case(2):
darkMode = !darkMode;
break;
case(3):
saveCSV(dataPath("save.csv"));
selectOutput("Where to save .csv file to?", "saveCSVFile");
break;
case(4):
selectInput("Select csv File", "loadCSVFile");
break;
case(5):
selectOutput("Where to export .png image file to?", "savePNGFile");
break;
default:
break;
}
}


Button buttons[] = new Button[buttonCount];

class Button {
int id, x, y, w, h;
String label;

public boolean hover() {
return (mouseX > x && mouseX < x+w && mouseY > y && mouseY < y+h) ? true : false;
}

public void click() {
buttonFunctions(id);
}
Button(int x_, int y_, String label_) {
id = i_buttonId;
i_buttonId++;
x = x_;
y = y_ ;
label = label_;
w = PApplet.parseInt(textWidth(label))+18;
h = 32;
}

public void display() {
stroke(0);
strokeWeight(1);
fill(255, 127);
rect(x, y, w, h, 4);
fill(0);
text(label, x+9, y+h/2+5);
}
}
int linkCount;

boolean dragLink; //state of dragging a link
int dragOriginLet = 0; //dragging from inlet: -1. dragging from outlet: 1.
int dragOriginId = -1; //which nodes out- / inlet did user click on?

public void Link(int parentNode, int targetNode) {
/**
* Check if user is adding or removing a link
*/
int exists = -1;
for (int i = 0; i < linkCount; i++) {
if (links[i].parentNode == parentNode && links[i].targetNode == targetNode) {
exists = i;
break;
}
}
if (exists != -1) {
/**
* !active state is pretty much the same as deleted state that the node-class comes with
*/
links[exists].active = !links[exists].active;
} else {
links[linkCount] = new Link(parentNode, targetNode);
linkCount++;
}
}

class Link {
int parentNode, targetNode;
boolean active;
PVector link = new PVector(0, 0);

Link(int parentNode_, int targetNode_) {
parentNode = parentNode_;
targetNode = targetNode_;
active = true;
}

/**
* This could be an event that only gets called when nodes are moved or resized.
*/
public void update() {
link.x = nodes[targetNode].inletCenter.x - nodes[parentNode].outletCenter.x;
link.y = nodes[targetNode].inletCenter.y - nodes[parentNode].outletCenter.y;
}

public void display() {
stroke(darkMode? 255 : 0);
strokeWeight(2);
pushMatrix();
translate(nodes[parentNode].outletCenter.x, nodes[parentNode].outletCenter.y);
line(0, 0, link.x, link.y);
translate(-nodes[parentNode].outletCenter.x+(nodes[parentNode].outletCenter.x+nodes[targetNode].inletCenter.x)/2, -nodes[parentNode].outletCenter.y+(nodes[parentNode].outletCenter.y+nodes[targetNode].inletCenter.y)/2);
rotate(link.heading());
fill(darkMode? 0 : 255);
triangle(0, textSize/2, textSize, 0, 0, -textSize/2);
popMatrix();
}
}
int nodeCount;
int i_selectedNode = -1;
int padding = textSize/3*2; //Padding between text and surrounding box
int letTolerance = 10; //Widens the clickable areas of in/outlets a bit

public void addNode(int x, int y, String title) {
nodes[nodeCount] = new Node(x, y, title);
nodeCount++;
}

public void deselect() {
i_selectedNode = -1;
}

public void deleteSelectedNode() { //Seperate from class for garbage collector reasons
if (i_selectedNode != -1) {
for (int i = 0; i < linkCount; i++) {
if (links[i].targetNode == i_selectedNode || links[i].parentNode == i_selectedNode) {
links[i].active = false;
}
}
nodes[i_selectedNode].deleted = true;
i_selectedNode = -1;
}
}

class Node {
String title;
int id;
int inlets = 2;
int outlets = 2;
int x, y;
int h = textSize+2*padding;
int w = textSize+2*padding;
PVector dragMouseStartPoint = new PVector(0, 0);
PVector outletCenter = new PVector(0, 0);
PVector inletCenter = new PVector(0, 0);
boolean deleted;
boolean drag;
public boolean hover() {
if (mouseX > x-w/2 && mouseX < x+w/2 && mouseY > y-h/2 && mouseY < y+h/2) {
return true;
} else {
return false;
}
}
public boolean hoverOutlet() {
if (mouseX > x-(textSize+padding)/2-letTolerance && mouseX < x+(textSize+padding)/2+letTolerance && mouseY > y-padding-textSize/2-(textSize+padding)/2-letTolerance && mouseY < y-padding-textSize/2) {
return true;
} else {
return false;
}
}
public boolean hoverInlet() {
if (mouseX > x-(textSize+padding)/2-letTolerance && mouseX < x+(textSize+padding)/2+letTolerance && mouseY > y+padding+textSize/2 && mouseY < y+padding+textSize/2+(textSize+padding)/2+letTolerance) {
return true;
} else {
return false;
}
}

Node(int x_, int y_, String title_) {
id = nodeCount;
title = title_;
x = x_;
y = y_;
setVectors();
refreshSize();
}

public void setVectors() {
outletCenter.x = x;
outletCenter.y = y-(padding+textSize/2)*1.3f;
inletCenter.x = x;
inletCenter.y = y+(padding+textSize/2)*1.3f;
}

/**
* Called when changing nodes title or when zooming via mouse-wheel
*/
public void refreshSize() {
w = PApplet.parseInt(textWidth(title))+2*padding;
h = textSize+2*padding;
}

/**
* Update-function is really only necessary when dragging the node
*/
public void update() {
if (drag) {
x -= dragMouseStartPoint.x - mouseX;
dragMouseStartPoint.x = mouseX;
y -= dragMouseStartPoint.y - mouseY;
dragMouseStartPoint.y = mouseY;
setVectors();
}
}

public void display() {
/**
* Selected nodes border
*/
if (i_selectedNode == id) {
noStroke();
fill(150, 100);
rect(x, y, w+20, h+20, 4);
}

/**
* Inlet & Outlet
*/
stroke(255, darkMode? 255 : 0);
strokeWeight(1);
fill(255, 0, 0);
ellipse(x, y-padding-textSize/2, textSize+padding, textSize+padding);
fill(0);
ellipse(x, y+padding+textSize/2, textSize+padding, textSize+padding);

/**
* Box & Title
*/
stroke(127);
strokeWeight(2);
fill(50);
rect(x, y, w, h, 4);
fill(255);
text(title, x, y+textSize/3);
}
}

/**
* Saving and loading is a messy hack!
* You'll get a blank line in your save file for each deleted object.
* That's because of my lack of skills of dodging the garbage collector.
* I didn't loose data to corruption yet.
* Don't touch the save files if you don't want to mess them up!
**/

boolean exporting;
String savePNGpath = dataPath("save.png");

public void savePNGFile(File selection) {
savePNGpath = selection.getPath();
exporting = true;
}

public void saveCSVFile(File selection) {
saveCSV(selection.getPath());
}

public void loadCSVFile(File selection) {
loadCSV(selection.getPath());
}

public void loadCSV(String path) {
for (int i = 0; i < nodeCount; i++) {
//nodes[i].deleted = true;
i_selectedNode = i;
deleteSelectedNode();
}
nodeCount = 0;

for (int i = 0; i < linkCount; i++) {
links[i].active = false;
}
linkCount = 0;

String[] loadString = loadStrings(path);
for (int i = 0; i < loadString.length-1; i++) {
//This is ultra shitty
if (loadString[i].length() == 0) {
addNode(-1000, -1000, " ");
i_selectedNode = nodeCount - 1;
deleteSelectedNode();
//nodes[nodeCount-1].deleted = true;
} else {
String nodeData[] = split(loadString[i], ',');
addNode(PApplet.parseInt(nodeData[1]), PApplet.parseInt(nodeData[2]), nodeData[0]);
}
}
for (int i = 0; i < loadString.length-1; i++) {
String nodeData[] = split(loadString[i], ',');
for (int j = 3; j < nodeData.length; j++) {
Link(i, PApplet.parseInt(nodeData[j]));
}
}
}

public void saveCSV(String path) {
String saveString = "";
for (int i = 0; i < nodeCount; i++) {
if (!nodes[i].deleted) {
saveString += nodes[i].title + "," + nodes[i].x + "," + nodes[i].y;
for (int j = 0; j < linkCount; j++) {
if (links[j].parentNode == i && links[j].active) {
saveString += "," + str(links[j].targetNode);
}
}
}
saveString += "\n";
}
String[] saveArray = split(saveString, '\n');
saveStrings(path, saveArray);
}

/**
* Keys only get caught for changing a nodes title.
* Previous title is remembered in savedTitle and restored when hitting Escape
*/
String savedTitle;
public void keyPressed() {
if (key == ESC) {
key = 0;
}
if (i_selectedNode != -1) {
if (keyCode == BACKSPACE) {
if (nodes[i_selectedNode].title.length() > 0) {
nodes[i_selectedNode].title = nodes[i_selectedNode].title.substring(0, nodes[i_selectedNode].title.length()-1);
nodes[i_selectedNode].refreshSize();
}
} else if (keyCode == DELETE) {
deleteSelectedNode();
} else if (keyCode == ESC) {
nodes[i_selectedNode].title = savedTitle;
nodes[i_selectedNode].refreshSize();
deselect();
} else if (keyCode == RETURN || keyCode == ENTER) {
deselect();
} else if (keyCode != SHIFT && keyCode != RETURN && keyCode != ENTER && keyCode != CONTROL) {
nodes[i_selectedNode].title = nodes[i_selectedNode].title + key;
nodes[i_selectedNode].refreshSize();
}
}
}


public void mousePressed() {
if (mouseButton == LEFT) {
/**
* Either press a button...
*/
boolean buttonClicked = false;
for (int i = 0; i < buttons.length; i++) {
if (buttons[i].hover()) {
buttons[i].click();
//Not able to break out of here without using a flag
buttonClicked = true;
}
}

if (!buttonClicked) {
for (int i = 0; i < nodeCount; i++) {
/**
* ...or select / drag a node...
*/
if (nodes[i].hover()) {
i_selectedNode = i;
savedTitle = nodes[i].title;
nodes[i].drag = true;
nodes[i].dragMouseStartPoint.x = mouseX;
nodes[i].dragMouseStartPoint.y = mouseY;
break;
}
/**
* ...or pull a new link out of an inlet or outlet.
*/
else if (nodes[i].hoverInlet()) {
dragLink = true;
dragOriginLet = -1;
dragOriginId = i;
} else if (nodes[i].hoverOutlet()) {
dragLink = true;
dragOriginLet = 1;
dragOriginId = i;
}

//deselecting the shit out of this for reasons I don't remember, no care to improve right now
deselect();
}
}
}
}

/**
* Create links
*/
public void mouseReleased() {
for (int i = 0; i < nodeCount; i++) {
nodes[i].drag = false;
if (dragLink && dragOriginId != i) {
if (nodes[i].hoverInlet() && dragOriginLet == 1) {

Link(dragOriginId, i);
}
if (nodes[i].hoverOutlet() && dragOriginLet == -1) {

Link(i, dragOriginId);
}
}
}

/**
* Reset drag&drop states
*/
dragLink = false;
dragOriginLet = 0;
dragOriginId = -1;
}


/**
* Scaling is tied to textSize. Having the UI on a seperate PGraphics would be better, but it's good enough for now
*/
int zoomDelta = 3;
public void mouseWheel(MouseEvent event) {