Multiple polygons loaded, user selects polygon of interest, user edits selected polygon nodes, user saves polygon

I want to load several polygons, user selects a polygon of interest, selected polygon nodes are visually drawn, user optionally drags polygon node to a new position, adjusted polygon is then saved.

I would appreciate a working example - thank you in advance.

A quick look around failed to find a previous post that would help me.

1 Like

This is doable.

Is this some sort of homework?

Please show your own attempt then we can help you with more specific questions.

Hey, and welcome to the forum! Great to have you here!

2 Likes

Do you mean svg files or obj files?

Do you want to load a fixed file or do you want to let the user choose one/ multiple file(s) in a Dialog Field?

Do you mean you have multiple files loaded?
How does the use choose? Menue or mouse?

Saved with a fixed name or with a select new file name dialog for the user?

It can be SVG loaded files -or- plain old X/Y list from text files.

The main point is:-

  1. Multiple Polygon/Polyline/Line objects

  2. User Selects object of interest

  3. Logic flag decides action (Move -or- Edit [even delete])
    if Move then allow user to drag selected object to new position
    if Edit then paint square/circle on each X/Y node, allow user to select node and drag
    optionally dblclick node and collect/set X/Y values via editbox
    if delete then delete node -or- object subject to logic flag above

  4. Save all objects back to file(s) using original load file info -or- prompt for new save name

I am a retired pensioner keeping my mind active during the long sleepless nights :slight_smile:

1 Like

Sounds interesting.

Show your code and some questions.

There are a lot of examples out there.

See Examples / Processing.org

But I think there are programs out there to achieve this

1 Like

If you hope to get any pleasure out of programming and keep your mind active you need to have a go yourself.

There are several design decisions that must be made before you start codeing.

  1. How is the data stored on disc. You are expecting the file to hold data for multiple polygons so a simple CSV format will not be enough. I suggest you look to text files using JSON format.

  2. What data structures does the sketch need to store the polygon data. I suggest that you use objection orientation (OO) i.e. classes and objects.

  3. What functionality does the sketch need (user requirements)

The value of any example code depends on the readers programming knowledge and skill.

3 Likes

Good question.

Do you already have the polygons? In which format?

Or do you expect the sketch to create them from scratch?

When you have a folder of 6 csv you can just load all files in the folder and see the entire folder as a project.

Then you can use a folder select dialog to load the entire folder in the canvas.

Do you plan plain native processing/ java flavor?

1 Like

Hello @andyhill.

Start simple and build on that.

We (some young programming enthusiasts and I) accomplished this building on code one step at a time.

This was our first step that I am willing to share:

ArrayList<PVector> polygon = new ArrayList<PVector>();
boolean overBox = false;

void setup()
  {
  size(500, 500);
  }
  
void draw()
  {
  background(255);

  noFill();
  for(int i=0; i<polygon.size(); i++)
    {
    strokeWeight(10);
    float x = polygon.get(i).x;
    float y = polygon.get(i).y;
    point(x, y); 
    fill(2550, 0, 0);
    textSize(24);
    text(i, x+10, y);
    }  
  }
  
void mouseClicked()
  {
  polygon.add(new PVector(mouseX, mouseY));
  }

Once the concept of PVectors, ArrayList of objects and interactivity with the mouse was understood we generated PShapes with vertices from the data.

This example helped with mouse cursor over a point and then it could be removed from ArrayList:

The next step was saving that data; if you can save data you can also recall data.

Multiple polygons were a challenge and they the initial solution was mulitple ArrayLists at first. That is as far as we got…

These are programming novices so I did not yet venture into advanced concepts… not yet.

:)

1 Like

Menue:
New form(s)
Move form
Edit points / delete points
Delete forms
Load
Save
Help
Open folder
Quit

1 Like

See also Some Graphical Editors

1 Like

Thank you for your reply

Example

This examples comes with a Menu.

Needs a folder Polys in the Sketch Folder.

You might want to DISTRIBUTE he code over several tabs as indicated.
(it has been copied together (Concatenated) from multiple tabs automatically)

Example for file content

610.0,259.0
822.0,363.0
786.0,465.0
461.0,615.0
301.0,581.0

only ONE polygon


// ********************************************************************************
//         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)
final int SPLASH_SCREEN         = 0;
final int MENU                  = 1;
final int EDITOR                = 2;
final int HELP                  = 3;
final int SAVE                  = 4;
final int LOAD                  = 5;
final int AddPoints             = 6;
int stateProgram = SPLASH_SCREEN; // current state

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

ArrayList<PVector> polygon = new ArrayList();

// Toolbox save / load
ClassSaveLoadTools classSaveLoadTools;

// Menu
Menu mainMenu;

String helpText1= "Polygon Editor\nEsc goes back to Menu normally";

boolean showHelp = true;

float burgerx1= 20;
float burgery1= 20;

// dragging points
boolean mouseDrag = false;
int selectedPoint;

// -------------------------------------------------------------------------------------

void settings() {
  size( 1200, 900 );
}

void setup() {
  surface.setTitle("Polygon Editor");

  //font
  PFont pf = createFont("ARIAL", 14);
  textFont(pf);

  // load
  classSaveLoadTools = new ClassSaveLoadTools();

  // Main Menu
  mainMenu = new Menu();
} // setup

void draw() {

  switch(stateProgram) {

  case SPLASH_SCREEN:
    drawForStateSplashScreen();
    break;

  case EDITOR:
    drawForStateEditor();
    break;

  case HELP:
    drawForStateHelp();
    break;

  case SAVE:
    // wait for Save Dialog
    classSaveLoadTools.waitForSaveDialog();
    break;

  case LOAD:
    // wait for Load Dialog
    classSaveLoadTools.waitForLoadDialog();
    break;

  case MENU:
    showMenu();
    break;

  case AddPoints:
    drawForStateAddPoints();
    break;

  default:
    stateProgram = EDITOR; // ERROR - RESET
    break;
  } //switch
  //
} // draw
//

// ********************************************************************************
// tab: cMenu.pde


// this is the Main Menu on the center
class Menu {

  final int x1 = int(width/2.0-100);
  final int y1 = 210;

  final color bkColor = color(133);

  boolean open=true;

  final String[] textsForMenu = {
    "Add points",
    "Editor ",
    "Load ",
    "Save ",
    "Help Screen",
    "Delete All"
  };

  // 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, seli, visible, text from elementsForMenu, id from i
      myButtons[i] = new Button(x1+10, (y1+30) + i * 75,
        330, 50,
        false, 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() {

    boolean returnValue=false;
    int result = -1;

    for (Button btn : myButtons) {
      if (btn.myMousePressed()) {
        result = btn.id;
        returnValue = true;
      }//if
    }//for

    if (returnValue) {
      for (Button btn : myButtons) {
        btn.selected=false;
      }
    }

    return result;
  }//method
  //
} // class
//

// ********************************************************************************
// tab: cMenuButton.pde

class Button {

  // only used for Pane

  int x, y,
    w, h,
    id;
  boolean selected, vis;
  String text;

  Button(int x, int y,
    int w, int h,
    boolean sel, boolean vis,
    String atext,
    int id) {
    //
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.selected  = sel;
    this.vis  = vis;
    this.text = atext;
    this.id  = id;
  }

  void  draw() {
    if ( this.vis ) {
      strokeWeight(1);

      if (this.selected)    fill(0, 200, 0);
      else                  fill(0, 0, 200);

      if (this.over())      stroke(200, 0, 200);
      else                  stroke(0, 200, 200);

      rect(this.x, this.y,
        this.w, this.h);
      noStroke();
      fill(200);

      textAlign(LEFT, TOP);
      text(text,
        x + 4, y + 3,
        w-10, 900);
    }
  }

  boolean over() {
    return
      mouseX > this.x &
      mouseX < this.x + this.w &
      mouseY > this.y &
      mouseY < this.y + this.h;
  }

  boolean myMousePressed() {
    //called from main mousePressed
    return
      over();
  }
  //
} // class
//

// ********************************************************************************
// tab: cSaveLoad.pde


// Save and load
// the two 'callback' functions MUST BE OUTSIDE THE CLASS
// 3 blocks with 2 functions each, for save and load

class ClassSaveLoadTools {

  // editor path and file extension
  final String pathFolder    = "Polys";
  final String defaultFile   = "list1.txt";
  final String fileExtension = ".txt";

  // Paths (returned by 'callback' functions after using save and load)
  String savePath="";
  String loadPath="";

  // --------------------------------------------------------------------

  // constr
  ClassSaveLoadTools() {
    loadPath = sketchPath("") +
      "//"
      +pathFolder
      +"//"
      +defaultFile;
    println("Loaded: "
      +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=EDITOR;
    }
  }

  // ----------------------------------------------------
  // executing save and load [AFTER the 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() {

    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 ( int(  elementsInLine[0]), int(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) {
      //
    } else if (key=='0'||key=='1'||key=='2') {
      //
    }
    key=0;  //  kill ESC
    break;

  case HELP:
    key=0;  //  kill ESC
    stateProgram=MENU; // leave state
    break;

  case EDITOR:
    if (key==DELETE&&mouseDrag&&selectedPoint!=-1) {
      polygon.remove(selectedPoint);
      mouseDrag=false;
      selectedPoint=-1;
    } else if (key==ESC) {
      key=0;  //  kill ESC
      stateProgram=MENU; // leave state
    }
    break;

  case AddPoints:
    if (key==ESC) {
      key=0;  //  kill ESC
      stateProgram=MENU; // leave state
    }
    break;

  default:
    stateProgram=SPLASH_SCREEN;
    break;
  }//switch
}

// ********************************************************************************
// 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;

  default:
    println("Error 129");
    exit();
    break;
  }//switch
}

void mouseReleased() {
  mouseDrag=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:
    // load
    classSaveLoadTools.initLoad();
    break;

  case 3:
    // SAVE dialog
    classSaveLoadTools.initSave();
    break;

  case 4:
    stateProgram=HELP;
    break;

  case 5:
    // delete all
    polygon.clear();
    break;

  default:
    println("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

  if (dist(mouseX, mouseY, burgerx1, burgery1) < 44) {
    stateProgram=MENU;
    return;
  }

  // are we already dragging?
  if (mouseDrag) {
    return; // leave
  }

  // 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) {
      selectedPoint = i;
      mouseDrag=true;
      return;
    }
  }
  //
}//func
//

// ********************************************************************************
// tab: StateAdd.pde


void drawForStateAddPoints() {
  background(0);

  // title
  int xpos= width/2;
  int ypos=32;
  textSize(29);
  fill(WHITE);
  textAlign(CENTER);
  text("Add points",
    xpos, ypos);
  textSize(14);

  textSize(14);
  noFill();
  for (int i=0; i<polygon.size(); i++) {
    strokeWeight(10);
    float x = polygon.get(i).x;
    float y = polygon.get(i).y;
    fill(255);
    point(x, y);
    ellipse(x, y, 4, 4);

    fill(2550, 0, 0);
    textSize(24);
    text(i, x+10, y);
  }
}

// ********************************************************************************
// tab: StateEditor.pde


// for state Editor

void drawForStateEditor() {
  // draw For State "Editor"

  background(0);

  showStatusBar("Burger Symbol for Menu");

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

  if (mouseDrag) {
    polygon.get(selectedPoint).x=mouseX;
    polygon.get(selectedPoint).y=mouseY;
  }

  fill(WHITE);

  //-------------------

  // title
  int xpos= width/2;
  int ypos=32;
  textSize(29);
  fill(WHITE);
  textAlign(CENTER);
  text("Editor ",
    xpos, ypos);
  textSize(14);
  text("Drag Points (drag and press delete key to delete the point)",
    xpos, ypos+33);
  textAlign(LEFT);

  // burger menu
  burgerMenuSign();

  // Message
  fill(WHITE);
  text ("Number of Points:\n"
    +polygon.size(),
    width-141, height-210);

  // small help text, top left
  if (showHelp) {
    fill(255);
    text(helpText1,
      22, 100,
      310, 900);
  } else {
    fill(255);
    text("x",
      22, 100,
      310, 900);
  }
  //
} //func
//

// ********************************************************************************
// tab: StateHelp.pde



void drawForStateHelp() {
  background(0);

  // title
  fill(WHITE);
  int xpos= width/2;
  int ypos=32;
  textSize(29);
  fill(WHITE);
  textAlign(CENTER);
  text("Help for the Program",
    xpos, ypos);

  textSize(14);
  text("Polygon Editor",
    xpos, ypos+33);
  textAlign(LEFT);

  // help text
  text(helpText1, 122, 160,
    610, 900);
}

// ********************************************************************************
// tab: StateMenu.pde


void showMenu() {

  background (0);

  // title
  int xpos= width/2;
  int ypos= 32;
  textSize(29);
  fill(WHITE);
  textAlign(CENTER);
  text("Main Menu. Needs a folder Polys in the Sketch Folder.",
    xpos, ypos);
  textAlign(LEFT);

  // The core
  mainMenu.display();

  showStatusBar("Main Menu: Choose an Option. Press Space Bar to go to Editor. ");
}
//

// ********************************************************************************
// tab: StateSplash.pde


void drawForStateSplashScreen() {

  // show splash screen
  background(0);

  showStatusBar("Press any key");

  // 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", rectWidth/2+90, height/2+40,
    300, 900);
  textSize(32);
}
//

// ********************************************************************************
// tab: Tools.pde


// General Tools

void burgerMenuSign() {
  // Burger Menu Sign

  fill(255);
  textAlign(LEFT, TOP);

  if (dist(mouseX, mouseY, burgerx1, burgery1) < 44)
    stroke(0, 255, 0); // green
  else
    stroke(255); // white

  strokeWeight(3);

  float factorY = 9;
  for (int i=0; i<3; i++) {
    line(burgerx1+6, burgery1+8+i*factorY,
      burgerx1+6+23, burgery1+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);
}

//--------------------------------------------------------

// ********************************************************************************
// 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. ********************************************************************************

1 Like

Thank you very much Chrisir, I will study your code :smiley:

1 Like

I put your code HERE

<div id='drawDiv'></div>
<script type="text/javascript">
  new p5()
  HERE
</script>

How would I execute it ?

It’s processing code (since this was the forum section you chose)

Use the IDE

1 Like

I am not using any IDE.

I tried it in https://editor.p5js.org/

SyntaxError: Unexpected identifier ‘int’
Did you just try to use p5.js’s int() function? If so, you may want to move it into your sketch’s setup() function.

Sorry

Ours won’t work in p5.js

Either translate it or get the free IDE

Thanks for the tip, do you have an IDE url ?

Sure. Download Processing / Processing.org

1 Like