How do i create a flight path using i3 processing

Hello everyone, I am trying to use i3 Processing to create a flight path.

Currently i am able to use i3 processing for accurate positioning of my device. But after looking through the reference, i saw the " line() " function and was thinking maybe i was able to use that to capture and display the path my device have taken. But i am not very sure if the ’ line() " function is the correct function to use for my scenario.

If there are other better functions for my situation, please leave it at the comments below :slight_smile:

If the line() function can be used for my situation, could someone explain on how to use that function for this situation of mine?

Your help is really much appreciated. :slight_smile:

line() might be good for displaying your data but if you want some way of saving all the previous locations for further use, I suggest using an ArrayList of PVectors. If you use line() there’s no way of retrieving what you put into it later. Here some code that might get you started:

// create the ArrayList
ArrayList<PVector> positions = new ArrayList<PVector>;

// draw would look something like this
void draw() {
  background(0);
  stroke(255);
  for (int i = 1; i < positions.size(); i++) {
    PVector previous = positions.get(i - 1);
    PVector current = positions.get(i);
    line(previous.x, previous.y, current.x, current.y);
  }
}

// then whenever you get a new position do this
positions.add(new PVector(deviceX, deviceY));

If you need to have a z coordinate as well that’s fairly easy to add I just recommend checking out the PVector documentation. If you want to save the data after processing closes I suggest looking into the Table class and using that instead of an ArrayList.

What is i3 Processing? Funny because I use Processing and i3, but you are talking about something else, right? :slight_smile:

@ArduinoKer===

same question than @hamoid = are you doing tile window manager???

I was curious about this too. I could be wrong but I think it’s the way some people read the the processing logo.

1 Like

ha! that’s cool :slight_smile: thanks for the clarification!

i do not understand what is " tile window manager " is that a project.?

It’s something for linux. It’s not relevant if you don’t know what it is. Do you have a question about that line?

Hello, thank you for the reply, i have read through the pVector code, but i am a beginner in this software you see. So i am still a little confused about the code. My current code for positioning is done, but after looking at the code you have advised me to use for a start, i do not know how to integrate your advised coding together with my positioning one that i currently have.

This is my coding for positioning for reference.

import oscP5.*;
import processing.serial.*;
import java.lang.Math.*;

/////////////////////////////////////////////////////////////
//////////////////////  parameters //////////////////////////
/////////////////////////////////////////////////////////////

boolean serial = true;          // set to true to use Serial, false to use OSC messages.

int oscPort = 8888;               // change this to your UDP port
String serialPort = "COM3";      // change this to your COM port 

int positionHistoryLength = 10;

// other parameters and variables

OscP5 oscP5;
Serial  myPort;

int     lf = 10;       //ASCII linefeed
String  inString;      //String for testing serial communication

// some variables for plotting the map
int offset_x = 30;
int offset_y = 30;
float pixel_per_mm = 0.5;
int border = 500;
int thick_mark = 500;
int device_size = 15;

// creates an empty list of pozyx devices
PozyxDevice[] pozyxDevices = {}; 


class PozyxDevice{
  private int ID;
  private int[] pos_x = new int [positionHistoryLength];
  private int[] pos_y = new int [positionHistoryLength];
  private int[] pos_z = new int [positionHistoryLength];
  
  public PozyxDevice(int ID){this.ID = ID;}
  
  public void addPosition(int x, int y, int z){
    System.arraycopy(pos_x, 0, pos_x, 1, positionHistoryLength - 1);
    System.arraycopy(pos_y, 0, pos_y, 1, positionHistoryLength - 1);
    System.arraycopy(pos_z, 0, pos_z, 1, positionHistoryLength - 1);
    
    pos_x[0] = x;
    pos_y[0] = y;
    pos_z[0] = z;
  }
  
  public int[] getCurrentPosition(){
    int[] position ={pos_x[0], pos_y[0], pos_z[0]};
    return position;
  }
}

void setup(){
  size(1000, 700, P3D);
  surface.setResizable(true);
  stroke(0, 0, 0);
  colorMode(RGB, 256); 
  // sets up the input
  if(serial){
    try{
      myPort = new Serial(this, serialPort, 115200);
      myPort.clear();
      myPort.bufferUntil(lf);
    }catch(Exception e){
      println("Cannot open serial port.");
    }
  }else{
    try{
      oscP5 = new OscP5(this, oscPort);
    }catch(Exception e){
      println("Cannot open UDP port");
    }
  }
}

void draw(){
  background(126,161,172);
  fill(0,0,0);
  text("(c) Pozyx Labs", width-100, 20);      
  drawMap();
}
  
void drawMap(){
  
  int plane_width =  width - 2 * offset_x;
  int plane_height = height - 2 * offset_y;
  // draw the plane
  stroke(0);
  fill(255);
  
  rect(offset_x, offset_y, plane_width, plane_height);
  
  calculateAspectRatio();
  
  pushMatrix();  
    
  translate(offset_x + (border * pixel_per_mm), height - offset_y - (border * pixel_per_mm));
  rotateX(radians(180));
  fill(0);
  
  // draw the grid
  strokeWeight(1);
  stroke(200);
  for(int i = 0; i < (int) plane_width/pixel_per_mm/thick_mark ; i++)
    line(i * thick_mark * pixel_per_mm, - thick_mark * pixel_per_mm, i * thick_mark * pixel_per_mm, plane_height - thick_mark * pixel_per_mm);
    
  stroke(100);
  for(int i = 0; i < (int) plane_height/pixel_per_mm/thick_mark - 1 ; i++)
    line(-(thick_mark * pixel_per_mm), i * thick_mark * pixel_per_mm, plane_width-(thick_mark * pixel_per_mm),  (i* thick_mark * pixel_per_mm));
  
  drawDevices();
    
  stroke(0);
  fill(0);
  drawArrow(0, 0, 50, 0.);
  drawArrow(0, 0, 50, 90.);
  pushMatrix();
  rotateX(radians(180));
  text("X", 55, 5);  
  text("Y", -3, -55);
  popMatrix();  
  
  popMatrix();  
}

void calculateAspectRatio(){
  float plane_width =  width - 2 * offset_x;
  float plane_height = height - 2 * offset_y;
  int max_width_mm = 0;
  int max_height_mm = 0;
  for (PozyxDevice pozyxDevice : pozyxDevices){
    int[] pos = pozyxDevice.getCurrentPosition();
    max_width_mm = max(max_width_mm, pos[0]);
    max_height_mm = max(max_height_mm, pos[1]);
  }
  max_width_mm += 2*border; 
  max_height_mm += 2*border; 
  pixel_per_mm = min(pixel_per_mm, plane_width / max_width_mm, plane_height / max_height_mm);
}

void drawDevices(){
  for(PozyxDevice pozyxDevice : pozyxDevices){
    drawDevice(pozyxDevice);
  }
}

void drawDevice(PozyxDevice device){  
  stroke(0, 0, 0);
  fill(0, 0, 0);
  int[] current_position = device.getCurrentPosition();
  ellipse(pixel_per_mm * current_position[0] - device_size/2, pixel_per_mm * current_position[1] + device_size/2, device_size, device_size);
  
  pushMatrix();
  rotateX(radians(180));
  fill(0);
  textSize(11);
  text("0x" + hex(device.ID, 4), pixel_per_mm * current_position[0] - 3 * device_size / 2, - pixel_per_mm * current_position[1] + device_size);
  textSize(12);
  popMatrix();
}

void addPosition(int ID, int x, int y, int z){
  for(PozyxDevice pozyxDevice : pozyxDevices){
    // ID in device list already
    if(pozyxDevice.ID == ID){
      pozyxDevice.addPosition(x, y, z);
      return;
    }
  }
  // ID not in device list
  PozyxDevice newPozyx = new PozyxDevice(ID);
  newPozyx.addPosition(x, y, z);
  pozyxDevices = (PozyxDevice[]) append(pozyxDevices, newPozyx);
}

void drawArrow(int center_x, int center_y, int len, float angle){
  pushMatrix();
  translate(center_x, center_y);
  rotate(radians(angle));
  strokeWeight(2);
  line(0,0,len, 0);
  line(len, 0, len - 8, -8);
  line(len, 0, len - 8, 8);
  popMatrix();
}

void serialEvent(Serial p) {
  // expected string: POS,network_id,posx,posy,posz
  inString = (myPort.readString());
  print(inString);  
  try {
    // parse the data
    String[] dataStrings = split(inString, ',');
    
    if (dataStrings[0].equals("POS") || dataStrings[0].equals("ANCHOR")){
      int id = Integer.parseInt(split(dataStrings[1], 'x')[1], 16);
      addPosition(id, int(dataStrings[2]), int(dataStrings[3]), int(dataStrings[4]));
    }
  }catch (Exception e) {
      println("Error while reading serial data.");
  }
}

void oscEvent(OscMessage theOscMessage) {
  // osc message received
  if (theOscMessage.addrPattern().equals("/position") || theOscMessage.addrPattern().equals("/anchor")){
    try{
      addPosition(theOscMessage.get(0).intValue(), theOscMessage.get(1).intValue(), theOscMessage.get(2).intValue(), 0);
    }catch(Exception e){
      println("Error while receiving OSC position");
    }
  }
}

void keyPressed() {
  // resets the scale to the current area taken by the devices. Useful if you moved too much outside of the anchor area.
  pixel_per_mm = 0.5;
  println("Resetting area size");  
}

Looks like you’re already storing the location in PozyxDevice so you don’t need to use an ArrayList, sorry for that misdirection. You’ll have to change the access modifiers for pos_x, pos_y, and pos_z or better yet create a get method in the PozyxDevice class that gives you all the positions not just the current one. Then create a method to display the paths. Not sure if this would be bug free but it’s where I’d start:

void drawPaths() {
  // set stroke here
  for (PozyxDevice p : pozyxDevices) {
    for (int i = 1; i < p.pos_x.length; i++) {
      // notice this loop starts at 1 otherwise you'll get an array out of bounds exception
      // this assumes that pos_x, pos_y, and pos_z are all the same length
      line(p.pos_x[i - 1], p.pos_y[i - 1], p.pos_z[i - 1], p.pos_x[i], p.pos_y[i], p.pos_z[i]);
    }
  }
}

You might also check out the PeasyCam library for processing it makes working in 3D much easier.

I do not really fully understand what the sentence " You’ll have to change the access modifiers for pos_x, pos_y, and pos_z or better yet create a get method in the PozyxDevice class that gives you all the positions not just the current one " mean.

what does " create a get method " mean.? Does it mean to create a function.?

Access modifiers are public, private, and protected you can learn more about them here. Because those arrays are private you can’t use them in code outside of the class unless you change it. The easy thing would be to change them to public like:

// in PozyxDevice class
public int[] pos_x = new int [positionHistoryLength];
public int[] pos_y = new int [positionHistoryLength];
public int[] pos_z = new int [positionHistoryLength];

But you might also look into getter and setter methods. (Methods are essentially the same as functions just they’re inside of a class.) It’s generally better to getters and setters when you can. Here is an article on that if you’re interested.

B/c class PozyxDevice is a nested member of the sketch’s PApplet top subclass, access modifiers don’t matter! :upside_down_face:

1 Like

You’re right, sorry I made that much more complicated than it had to be. Disregard the whole access modifiers thing.

ohh, its okay no worries :):). I still appreciate you being here helping me it really means a lot to me.

Just that now i am back to square one. i still don’t really understand the concept, which is causing me to not be able to create the flight path. Sorry for the trouble caused

i’ve tried to code the flight path with the startup code advised by you. The flight path seems more or less correct but its a little off.

As you can see from the image, 0x0000 is my “device” which is supposed to move around and create a flight path. but on the right of the image you can see scribbles at one area. That flight path is correct because my “device” is stationary. however it is at a random empty field instead of following my " 0x0000" device. Is there anyway to maybe set an offset or something.? Because the path created is correct and its with respect to my " device " just that it is drawing at the wrong place of the graph.

1 Like

I noticed in your code you use translate() and rotate() and multiplying a variable with pixel_per_mm in a lot of the draw methods. You may need to apply some combination of those to drawPaths() function to get it to render in the correct location. I suggest looking at the transformations in drawDevice() and trying to match those.

1 Like

Hey figraham, thank you so so much. Now as you can see my program is working like how i wanted it to work, and i really couldn’t have done it without you. Thank you from the bottom of my heart. This is actually for my school project, and you have no idea how much you have actually helped me. Really, thank you! no words can express my gratitude…

This is actually alright for my project, but i was thinking is it possible to make the flight path stay.? Like right now it appears for a while and it disappears. Is it possible to make it stay when the software is running, until it gets stopped. If its too troublesome for you its okay. You’ve really helped me a lot. Thank you very much :slight_smile:

No problem glad you got it working. If you want it to stay longer you could just increase the value this variable. If you want it to stay on screen forever you could convert the pos_x, pos_y, and pos_z arrays in PozyxDevice to ArrayLists. Here’s a good video about ArrayLists that might be helpful.

1 Like