A Polygon Editor for 2D with a full Menu and Load and Save
// ********************************************************************************
// joined pde-file of folder polygonEditor1
// ********************************************************************************
// ********************************************************************************
// tab: polygonEditor1.pde main file (file name is folder name)
//https://discourse.processing.org/t/multiple-polygons-loaded-user-selects-polygon-of-interest-user-edits-selected-polygon-nodes-user-saves-polygon/41774
// constants ---------------------------------------------------------------------------------
// states for the program (constants)
// The states correspond to the Main Menu (Splash Screen, Menu State, then the entries of the Menu):
final int SPLASH_SCREEN = 0; // Yellow start Screen
final int MENU = 1; // MAIN MENU
final int AddPoints = 6; // add points with mouse click (for empty canvas)
final int EDITOR = 2; // drag points, delete points, show as shape
final int STATE_MOVE = 7; // move entire shape
final int LOAD = 5; // load
final int SAVE = 4; // save
final int HELP = 3; // help
int stateProgram = SPLASH_SCREEN; // current state
// Texts for main menu buttons
final String[] textsForMenu = {
"Add points",
"Editor: Move and delete points",
"Move entire polygon",
"Load ",
"Save ",
"Help Screen",
"Delete All"
};
// colors
final color BLACK = color(0);
final color WHITE = color(255);
final color RED = color(255, 0, 0);
final color GREEN = color(0, 255, 0);
final color BLUE = color(0, 0, 255);
final color PINK = color(255, 0, 255);
final color YELLOW = color(255, 255, 0);
final color TURQUOISE = color(0, 255, 255);
final color YELLOW2 = (#FFF80A);
final color BROWN = #B9780D;
// -------------------------------------------------------------------------------
// Variables and objects
// The Polygon
ArrayList<PVector> polygon = new ArrayList();
// Toolbox save / load
ClassSaveLoadTools classSaveLoadTools;
// Menu
Menu mainMenu;
float burgerX= 20, burgerY= 20;
// Help
String helpTextEditor = "Burger Icon goes back to Menu\nEsc goes back to Menu\n1 Toggle Shape\nBackspace delete last point\nx Toggle Help";
String helpTextStateHelp = "Polygon Editor\nEsc goes back to Menu normally";
boolean showHelp = true;
boolean showAsShape = false; // for state Editor
// dragging points
boolean mouseDragForStateEditor = false; // for State Editor
int selectedPointForStateEditor = -1; // for State Editor
// dragging polygon
boolean mouseDragForStateMove = false; // for state STATE_MOVE
// -------------------------------------------------------------------------------------
void settings() {
size( 1200, 900 );
}
void setup() {
surface.setTitle("Polygon Editor");
//font
PFont pf = createFont("ARIAL", 14);
textFont(pf);
// load and save Toolbox
classSaveLoadTools = new ClassSaveLoadTools();
// Main Menu
mainMenu = new Menu();
} // setup
void draw() {
switch(stateProgram) {
case SPLASH_SCREEN:
drawForStateSplashScreen();
break;
case MENU:
showMenu();
break;
case AddPoints:
drawForStateAddPoints();
break;
case EDITOR:
drawForStateEditor();
break;
case STATE_MOVE:
// move entire shape
drawForStateMove();
break;
case LOAD:
// wait for Load Dialog
classSaveLoadTools.waitForLoadDialog();
break;
case SAVE:
// wait for Save Dialog
classSaveLoadTools.waitForSaveDialog();
break;
case HELP:
drawForStateHelp();
break;
default:
// ERROR
println("draw(): Error 31223 with "+stateProgram);
exit();
break;
} //switch
//
} // draw
//
// ********************************************************************************
// tab: cMenu.pde
// this is the Main Menu on the center
class Menu {
final int myButtonsWidth = 430;
final int x1 = int(width/2.0- myButtonsWidth/2.0);
final int y1 = 110;
final color bkColor = color(133);
boolean open=true;
// number of buttons in Pane
int upperBound = textsForMenu.length;
Button[] myButtons=new Button[upperBound]; // make a button of type class Button
// constr
Menu() {
// make buttons
for (int i=0; i < upperBound; i++) {
// xi, yi, wi, hi,
// visible, text from elementsForMenu, id from i
myButtons[i] = new Button(
x1+10, (y1+30) + i * 75,
myButtonsWidth, 50,
true,
textsForMenu[i],
i);
}
}// constr
void display() {
strokeWeight(1);
textSize(14);
fill(255);
textAlign(LEFT, TOP);
textSize(24);
for (Button btn : myButtons) {
btn.draw();
}//for
}//method
int myMousePressed() {
int result = -1;
for (Button btn : myButtons) {
if (btn.myMousePressed()) {
result = btn.id;
}//if
}//for
return result;
}//method
//
} // class
//
// ********************************************************************************
// tab: cMenuButton.pde
class Button {
// only used for Menu
int x, y,
w, h,
id;
boolean vis;
String text;
// constr
Button(int x, int y,
int w, int h,
boolean vis,
String text_,
int id) {
//
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.vis = vis;
this.text = text_;
this.id = id;
} // constr
void draw() {
if ( vis ) {
strokeWeight(1);
fill(0, 0, 200);
if (over()) stroke(200, 0, 200);
else stroke(0, 200, 200);
rect(x, y,
w, h);
noStroke();
fill(200);
textAlign(LEFT, TOP);
text(text,
x + 4, y + 3,
w-10, 900);
}
}
boolean over() {
return
mouseX > x &
mouseX < x + w &
mouseY > y &
mouseY < y + h;
}
boolean myMousePressed() {
//called from main mousePressed
return
over();
}
//
} // class
//
// ********************************************************************************
// tab: cSaveLoad.pde
// Save and load class.
// Remark: the two 'callback' functions MUST BE OUTSIDE THE CLASS.
// Class with 3 blocks with 2 functions each, for save and load.
class ClassSaveLoadTools {
// editor path and file extension
final String pathFolder = "Polys";
final String fileExtension = ".csv";
// Paths (returned by 'callback' functions after using save and load)
String savePath="";
String loadPath="";
// --------------------------------------------------------------------
// constr
ClassSaveLoadTools() {
loadPath = "";
} // constr
// --------------------------------------------------------------------
// the two init functions
void initSave() {
// init save process
// reset
savePath="";
// make date time stamp (the expression nf(n,2) means leading zero: 2 becomes 02)
String dateTimeStamp = year()
+ nf(month(), 2)
+ nf(day(), 2)
+ "-"
+ nf(hour(), 2)
+ nf(minute(), 2)
+ nf(second(), 2);
// prepare fileDescription which occurs in the dialogue
File fileDescription = new File( sketchPath()
+ "//"
+ pathFolder
+ "//"
+ dateTimeStamp
+ fileExtension);
// open the dialog
selectOutput("Select a file to write to", "fileSelectedSave", fileDescription);
// set state to wait
stateProgram=SAVE;
}
void initLoad() {
// init load process
// reset
loadPath="";
// prepare fileDescription which occurs in the dialogue
File fileDescription = new File( sketchPath()+"//"+pathFolder+"//"+"*" + fileExtension );
// open the dialog
selectInput("Select a file to load", "fileSelectedLoad", fileDescription);
// set state to wait
stateProgram=LOAD;
}
// ----------------------------------------------------
// waiting
void waitForSaveDialog() {
if (!savePath.equals("")) {
// waiting is over
saveIt();
// go back
stateProgram=EDITOR;
}
}
void waitForLoadDialog() {
if (!loadPath.equals("")) {
// waiting is over
loadIt();
// go back
stateProgram=MENU; // stateProgram=EDITOR;
}
}
// ----------------------------------------------------
// executing save and load [AFTER the 2 dialogs]
void saveIt() {
// save
// get the data from the ArrayList
String[] sArray = new String[0];
for (PVector pv : polygon ) {
sArray = (String[]) append ( sArray, pv.x+","+pv.y );
}
// check if file extension (fileExtension, e.g. .txt) is there
int len = savePath.length();
if (len<4 || !savePath.substring( len-4 ).equals(fileExtension)) {
// file Extension is not present, we have to add it
savePath += fileExtension; // add the file Extension
}
// save
println("Saved: " + savePath);
saveStrings (savePath, sArray);
}
void loadIt() {
// load
String[] linesInFile = loadStrings(loadPath);
println("loading "
+ linesInFile.length
+ " lines.");
for (int i=0; i<linesInFile.length; i++) {
if (linesInFile[i].trim().equals(""))
continue;
if (linesInFile[i].trim().substring(0, 2).equals("//")) {
print("Skipped line (which is commented out with //) #"+i+": ");
println(linesInFile[i]);
continue;
}
String[] elementsInLine = split(linesInFile[i], ",");
if (elementsInLine.length<2) {
print("Error in line #"+i+": ");
println(linesInFile[i]);
continue;
}
PVector newPv = new PVector ( float( elementsInLine[0]), float(elementsInLine[1]));
polygon.add(newPv);
}//for
}
//
}//class
//
// ********************************************************************************
// tab: InputsKeyb.pde
void keyPressed() {
switch(stateProgram) {
case SPLASH_SCREEN:
stateProgram = MENU; // leave state
key=0; // kill ESC
break;
case MENU:
// just in case the user has no mouse
if (key==' ') {
stateProgram=EDITOR; // leave state
} else if (key==ENTER||key==RETURN) {
// ignore
} else if (key=='0') {
// dump
for (PVector pv : polygon) {
println(pv.x
+","
+pv.y);
}
}
key=0; // kill ESC
break;
case STATE_MOVE:
case HELP:
key=0; // kill ESC
stateProgram=MENU; // leave state
break;
case EDITOR:
if (key==DELETE&&mouseDragForStateEditor&&selectedPointForStateEditor!=-1) {
polygon.remove(selectedPointForStateEditor);
mouseDragForStateEditor=false;
selectedPointForStateEditor=-1;
} else if (key==ESC) {
key=0; // kill ESC
stateProgram=MENU; // leave state
} else if (key=='1') {
showAsShape =
!showAsShape;
} else if (key=='x') {
showHelp =
! showHelp;
} else if (key==BACKSPACE) {
if (polygon.size()>0)
polygon.remove(polygon.size()-1);
}
break;
case AddPoints:
if (key==ESC) {
key=0; // kill ESC
stateProgram=MENU; // leave state
} else if (key==BACKSPACE) {
if (polygon.size()>0)
polygon.remove(polygon.size()-1);
}
break;
default:
// ERROR
println("keyPressed(): Error 227 with "+stateProgram);
exit();
break;
}//switch
//
}//func
//
// ********************************************************************************
// tab: InputsMouse.pde
void mousePressed() {
switch(stateProgram) {
case SPLASH_SCREEN:
stateProgram=MENU; // leave state
key=0; // kill ESC
break;
case HELP:
stateProgram = MENU;
key=0; // kill ESC
break;
case EDITOR:
mousePressedForStateEDITOR();
break;
case MENU:
mousePressedForStateMENU();
break;
case AddPoints:
polygon.add(new PVector(mouseX, mouseY));
break;
case STATE_MOVE:
//
mouseDragForStateMove=true;
break;
default:
println("Error 129");
exit();
break;
}//switch
}
void mouseReleased() {
mouseDragForStateEditor=false;
mouseDragForStateMove =false;
}//func
// ---------------------------------------------------------------------------------------------
void mousePressedForStateMENU() {
// Eval buttons of Menu.
// Numbers in switch() must match the order in class Menu.
int result = mainMenu.myMousePressed();
switch(result) {
case -1:
// ignore
break;
case 0:
stateProgram=AddPoints;
break;
case 1:
// Editor
stateProgram = EDITOR;
break;
case 2:
// MOVE
stateProgram=STATE_MOVE;
break;
case 3:
// load
classSaveLoadTools.initLoad();
break;
case 4:
// SAVE dialog
classSaveLoadTools.initSave();
break;
case 5:
stateProgram=HELP;
break;
case 6:
// delete all
polygon.clear();
break;
default:
println("mousePressedForStateMENU(): Error 836 with "
+result
+" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
exit();
break;
}//switch
}//func
void mousePressedForStateEDITOR() {
// mouse inputs
if (mouseButton == RIGHT ) {
// ....
return;
}
if (mouseButton == CENTER) {
// ....
return;
}
// ---
// technically not necessary
if (mouseButton != LEFT ) {
return;
}
// ---
// LEFT mouse button only from here on
// are we already dragging?
if (mouseDragForStateEditor) {
return; // leave
}
// Burger Menu
if (dist(mouseX, mouseY, burgerX, burgerY) < 44) {
stateProgram=MENU;
return;
}
// search point to start dragging
for (int i=0; i<polygon.size(); i++) {
strokeWeight(10);
fill(255);
float x = polygon.get(i).x;
float y = polygon.get(i).y;
if (dist(mouseX, mouseY, x, y) < 22) {
selectedPointForStateEditor = i;
mouseDragForStateEditor=true;
return;
}//if
}//for
// Hide Help Text
if (dist(mouseX, mouseY, 22, 100) < 22) {
showHelp =
! showHelp;
}//if
//
}//func
//
// ********************************************************************************
// tab: StateAddPoints.pde
void drawForStateAddPoints() {
background(0);
// title
showTitle("Add points", "Click in the canvas");
showPoly();
showPolySize();
showStatusBar("Click Mouse to add points. Backspace to delete last point. Esc for Menu.");
}
// ********************************************************************************
// tab: StateEditor.pde
// for state Editor
void drawForStateEditor() {
// draw For State "Editor"
background(0);
showStatusBar("Burger Symbol for Menu. Press 1 for Shape on/off. Backspace to delete the last point. Press x to toggle help.");
if (showAsShape) {
fill(111);
beginShape();
for (PVector pv : polygon) {
vertex(pv.x, pv.y);
}
endShape();
}//if
showPoly();
if (mouseDragForStateEditor) {
polygon.get(selectedPointForStateEditor).x=mouseX;
polygon.get(selectedPointForStateEditor).y=mouseY;
PVector pv = polygon.get(selectedPointForStateEditor);
showStatusBar("Drag. DEL to delete. Pos: "
+pv.x + ", "+pv.y);
}
//-------------------
showTitle("Points Editor", "Drag Points (drag and press delete key to delete the point)");
// burger menu
burgerMenuSign();
// Message
showPolySize();
// small help text, top left
fill(255);
if (showHelp) {
text(helpTextEditor,
22, 100,
310, 900);
} else {
if (dist(mouseX, mouseY, 22, 100) < 22) {
fill(0, 255, 0);
}
text("x",
22, 100);
}
//
} //func
//
// ********************************************************************************
// tab: StateHelp.pde
void drawForStateHelp() {
background(0);
// title
showTitle("Help for the Program", "Polygon Editor");
// help text
text(helpTextStateHelp, 122, 160,
610, 900);
}
// ********************************************************************************
// tab: StateMenu.pde
void showMenu() {
background (0);
// title
showTitle("Main Menu", "Requires Sub folder Polys in Sketch's folder.");
// The core
mainMenu.display();
// number of points
showPolySize();
// status bar
showStatusBar("Main Menu: Choose an Option. Press Space Bar to go to Editor. ");
}
//
// ********************************************************************************
// tab: StateMove.pde
void drawForStateMove() {
// move entire shape
background(0);
if (mouseDragForStateMove) {
for (PVector pv : polygon) {
pv.x+=mouseX-pmouseX;
pv.y+=mouseY-pmouseY;
}
}
// title
showTitle("Move entire polygon", "Click anywhere in the canvas and drag");
showPoly();
showPolySize();
showStatusBar("Use Mouse to move. Esc for Menu.");
}
// ********************************************************************************
// tab: StateSplash.pde
void drawForStateSplashScreen() {
// show splash screen
background(0);
showStatusBar("Click mouse.");
// yellow rectangle
final int rectWidth = 600;
fill(YELLOW);
noStroke();
rect(100, 260, rectWidth, 400);
// frame for the rect
int border = 4;
noFill();
stroke(0);
rect(100+border, 260+border,
rectWidth-2*border, 400-2*border);
// text in the rect
fill(0);
textSize(32);
textAlign(LEFT);
text("The Polygon Editor", rectWidth/2+70, height/2-30,
300, 900);
textSize(14);
text("Edit Polygons. \nRequires Sub-folder 'Polys' in Sketch's folder.",
rectWidth/2+70, height/2+40,
300, 900);
textSize(32);
}
//
// ********************************************************************************
// tab: Tools.pde
// General Tools
void showTitle(String headline, String subText ) {
// title
int xpos= width/2;
int ypos=32;
textSize(29);
fill(WHITE);
textAlign(CENTER);
text(headline,
xpos, ypos);
textSize(14);
text(subText,
xpos, ypos+33);
textAlign(LEFT);
}
void burgerMenuSign() {
// Burger Menu Sign
fill(255);
textAlign(LEFT, TOP);
if (dist(mouseX, mouseY, burgerX, burgerY) < 44)
stroke(0, 255, 0); // green
else
stroke(255); // white
strokeWeight(3);
float factorY = 9;
for (int i=0; i<3; i++) {
line(burgerX+6, burgerY+8+i*factorY,
burgerX+6+23, burgerY+8+i*factorY );
}//for
//reset
strokeWeight(1);
}
void showStatusBar(String s1) {
fill(111);
noStroke();
rect(0, height-20,
width, 22);
fill(WHITE);
textSize(14);
textAlign(LEFT);
text(s1, 6, height-4);
}
void showPolySize() {
textSize(14);
fill(255);
textAlign(LEFT);
text ("Number of Points:\n"
+polygon.size(),
width-141, height-210);
}
void showPoly() {
textSize(14);
noFill();
for (int i=0; i<polygon.size(); i++) {
strokeWeight(10);
fill(255);
float x = polygon.get(i).x;
float y = polygon.get(i).y;
point(x, y);
ellipse(x, y, 4, 4);
fill(2550, 0, 0);
textSize(24);
text(i, x+10, y);
}
}
// ********************************************************************************
// tab: ToolsSaveLoad.pde
// the two 'callback' functions MUST BE OUTSIDE THE CLASS
// this tab is an addition to the class
void fileSelectedSave(File selection) {
// the 'callback' function
if (selection == null) {
// Window was closed or the user hit cancel
// go back
stateProgram=MENU;
} else {
// User selected selection.getAbsolutePath()
classSaveLoadTools.savePath=selection.getAbsolutePath();
}
}
void fileSelectedLoad(File selection) {
// the 'callback' function
if (selection == null) {
// Window was closed or the user hit cancel
// go back
stateProgram=MENU;
} else {
// User selected selection.getAbsolutePath()
classSaveLoadTools.loadPath=selection.getAbsolutePath();
}
}
//
// End of joined file. ********************************************************************************