123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- 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);
- }
- }
- }
|