Processing Help -Draw line at given angle

Ha Ha!

Off screen buffer, you say… You know, it is like a door opening into a huge new world when you find a new command. I remember (in basic) when I found select case instead of a ton of if/then statements. Now that I have this off-screen buffer thing, it seems that not only will it address my mapping issue, but I could also draw graphs from robot data and pop them up when ever I want. This one is going to be handy… I hope this works, I have a lot of test coding to do. --If it does work, it will be like a double rainbow.

I’m seeing it

And I recognise the problem. The off screen buffer seems promising. More so than redrawing umpteen vectos with every draw(). I thought along similar lines: save the built up image (on disk or in memory somewhere) before the next draw() wipes it again. The buffer is basically that in revered order. Save first, then display.

Seems to work

I gave it a try and it seems to work. I modified the sketch you posted earlier (just draw lines to buffer and then the buffer to screen. Nothing really fancy) and it drew the route I took from a local bar to home last Saturday! Amazing!

serial_map_ctc.png

Here's the Picaxe part:
main:
  random w0
  sertxd("r:", #b0, CR)
  sertxd("t:", #b1, CR)
  goto main

And here's the sketch (Sorry for a long post. I guess there's no way to add attachments to replies):
import processing.serial.*;

PGraphics buffer;
PImage img;
Serial PicPort;
String myString = null;
int cr = 13; // strings are terminated by ascii 13: carriage return aka newline
float oldangle = 0;
float x0 = 0;
float y0 = 0;
float x1 = 0;
float y1 = 0;
float angle = 0;
float travel = 0;

void setup() {
  size(600,400);
  //                 parent comport           baud  parity databits stopbit
  PicPort = new Serial(this, Serial.list()[0], 4800, 'N',   8,       1.0);
 
  // Create an off-screen buffer.
  buffer = createGraphics(600, 400, JAVA2D);
  buffer.beginDraw();
  buffer.background(32);
  buffer.stroke(192);
  buffer.rectMode(CENTER);
  buffer.translate(width / 2, height / 2);
  buffer.ellipse(0,0 , 10,10);
  buffer.endDraw();
  img = buffer.get(0, 0, buffer.width, buffer.height);
}

void draw() {
  background(255);
  updateBuffer();
  image(img, -width/2, -height/2);
}

void updateBuffer() {
  translate(width / 2, height / 2);
  //rect(0,0 , 10,10);
  while (PicPort.available() > 0) {
    myString = PicPort.readStringUntil(cr);
    if (myString != null) {
      // println(myString);
      // examples of myString: "r:259", "t:18", "t:0"
      String telemetry [] = split(myString, ":");
      if (telemetry.length == 2) {
        String qualifier = telemetry[0];
        float value = float(telemetry[1]);  //  WHY won't this convert to int ?!?!
        int valint = int(value);

        //     println (qualifier +"="+ valint);


        if (qualifier.equals("r")) {
          // rotation
          angle = oldangle + value / 360 * TWO_PI; // input ranges 0-359, result is in radians
          //      println ("rotated: " + value + " degrees" +"   "+ angle +" radians");
        }
        if (qualifier.equals("t")) {
          // travel
          travel = value / 50 * width * .01;  // input ranges 0-50, result is in 1% increments of screen width
          //     println ("traveled: " + value + " mickeys" +"    "+ travel +" pixels");

          //     background(192);
          x1 = x0 + travel * cos(angle);
          y1 = y0 + travel * sin(angle);
          buffer.beginDraw();
          buffer.stroke(192);
          buffer.rectMode(CENTER);
          buffer.translate(width / 2, height / 2);
          buffer.strokeWeight(2);
          buffer.line(x0,y0 , x1, y1);
          buffer.endDraw();
          oldangle = angle;
          x0 = x1;
          y0 = y1;
        }
      }

    }
  }
  img = buffer.get(0, 0, buffer.width, buffer.height);
}

You beat me to it!

Nice work nuumio,

I have been running kids all around this afternoon and havn’t gotten around to writing any test code and then I get home and bam! --there is your stuff. Thanks bunches.

No problem

No problem. Glad I could help. I took a deeper look how that serial lib works in Processing and found out it supports events. So instead of checking if there’s something available in serial port every now and then (or every draw() call like in my previous post) you can use serialEvent method call to catch an event when there’s something available. Could be useful in your app if you don’t already use it (check also bufferUntil). Here’s something I put together (sorry again for a long post):
import processing.serial.*;

PGraphics buffer;
PImage img;
Serial PicPort;
String myString = null;
int cr = 13; // strings are terminated by ascii 13: carriage return aka newline
float oldangle = 0;
float x0 = 0;
float y0 = 0;
float x1 = 0;
float y1 = 0;
float angle = 0;
float travel = 0;

void setup() {
  size(600,400);
  //                 parent comport           baud  parity databits stopbit
  PicPort = new Serial(this, Serial.list()[0], 4800, ‘N’,   8,       1.0);
  PicPort.bufferUntil(cr); // Buffer until CR is found (before calling serialEvent)
 
  // Create an off-screen buffer and initial image.
  buffer = createGraphics(600, 400, JAVA2D);
  buffer.beginDraw();
  buffer.background(32);
  buffer.stroke(192);
  buffer.rectMode(CENTER);
  buffer.translate(width / 2, height / 2);
  buffer.ellipse(0,0 , 10,10);
  buffer.endDraw();
  img = buffer.get(0, 0, buffer.width, buffer.height);
}

void draw() {
  translate(width / 2, height / 2);
  background(255);
  image(img, -width/2, -height/2);
}

// This gets called whenever there’s data available
void serialEvent(Serial p) {
  myString = p.readStringUntil(cr);
  if (myString != null) {
    // println(myString);
    // examples of myString: “r:259”, “t:18”, “t:0”
    String telemetry [] = split(myString, “:”);
    if (telemetry.length == 2) {
      String qualifier = telemetry[0];
      float value = float(telemetry[1]);  //  WHY won’t this convert to int ?!?!
      int valint = int(value);

      //     println (qualifier +"="+ valint);


      if (qualifier.equals(“r”)) {
        // rotation
        angle = oldangle + value / 360 * TWO_PI; // input ranges 0-359, result is in radians
        //      println (“rotated: " + value + " degrees” +"   “+ angle +” radians");
      }
      if (qualifier.equals(“t”)) {
        // travel
        travel = value / 50 * width * .01;  // input ranges 0-50, result is in 1% increments of screen width
        //     println (“traveled: " + value + " mickeys” +"    “+ travel +” pixels");

        //     background(192);
        x1 = x0 + travel * cos(angle);
        y1 = y0 + travel * sin(angle);
        buffer.beginDraw();
        buffer.stroke(192);
        buffer.rectMode(CENTER);
        buffer.translate(width / 2, height / 2);
        buffer.strokeWeight(2);
        buffer.line(x0,y0 , x1, y1);
        buffer.endDraw();
        oldangle = angle;
        x0 = x1;
        y0 = y1;
      }
    }

  }
  img = buffer.get(0, 0, buffer.width, buffer.height);
}

I noticed that sometimes the sketch freezes like there’s nothing coming out from serial port even when there is. I’m not sure if it’s a problem with sketch itself or the underlying library. Serial library seems to use RXTX and I remember having some problems with it a few years back (although it could have been my mistake then too).

 

This one confused me…

I understand the use of translate but maybe not it this instance. In addition, I am very confused by image(img, -width/2,-height/2);

Why are there negitives infront of the width and height? And how does translate affect this? Could I get a little on this one?

Wait, I think I got this one…

So we start with a “cut it in half” translate at the beginning so we start in the middle. When we are drawing the map, we stay with width/2 and height/2 translate because we started in the middle and we want all future calculations of lines to reflect the fact that we started in the middle. The -width/2 thing is the fact that the image is going to be posted from the top left corner and if we used 0,0 after the translate (w/2 h/2), the 0,0 would “translate” to the center and the picture would be down and to the right. --How’s that for a run-on sentence?

If I am right so far, we could use this translate to start our picture at any point we want… --This would be a good start-up routine to be able to click a point on the map as to where is robot is in the room, and where it is pointing. The direction could also come from a compas --Anyone want to paypal me some dough for a compas? At any rate, this should be good.

I got the above correct, right?

You got me on this one

I really didn’t read (AND understand) rik’s original code too carefully. I just thought that the origin is in the center by default in Processing but now I noticed that it’s caused by the “left over” translate from the original code.  If you remove the translate call from the beginning of updateBuffer method then you can use image(img,0,0) in draw(). I even copied the “now useless” translate to draw method in the version where I use serialEvent. Stupid me.

Using buffer.translate() could be useful to setup the starting point or you can just put the coordinates to x0 and y0. I might event take a look at setting the starting point thing (am I becoming a Processing-junkie already).

I hope my post makes sense. I only had one cup of coffee so far.

 

Yet another long post

So I did that set starting point feature. Code is below. Bugs and weird comments expected because I’m feeling really tired. Click mouse button to set starting point and click it again to set direction.

I used mouseMoved and mouseClicked events to get starting point and direction. Variable state is used to keep track of what’s going on. Serial port initialization is moved from setup() to mouseClicked() so it only starts reading the port when starting point and direction are set.

import processing.serial.*;

// States
final int STATE_GETTING_START_POINT = 0;
final int STATE_GETTING_DIRECTION   = 1;
final int STATE_TRACKING            = 2;

int state = STATE_GETTING_START_POINT;

PGraphics buffer;
PImage img;
Serial PicPort;
String myString = null;
int cr = 13; // strings are terminated by ascii 13: carriage return aka newline
float oldangle = 0;
float x0 = 0;
float y0 = 0;
float x1 = 0;
float y1 = 0;
float angle = 0;
float travel = 0;

void setup() {
  size(600,400);
 
  // Create an off-screen buffer and initial image.
  buffer = createGraphics(600, 400, JAVA2D);
  buffer.beginDraw();
  buffer.background(32);
  buffer.endDraw();
  img = buffer.get(0, 0, buffer.width, buffer.height);
}

void draw() {
  background(255);
  image(img,0,0);
}

// This gets called whenever there’s data available
void serialEvent(Serial p) {
  myString = p.readStringUntil(cr);
  if (myString != null) {
    // println(myString);
    // examples of myString: “r:259”, “t:18”, “t:0”
    String telemetry [] = split(myString, “:”);
    if (telemetry.length == 2) {
      String qualifier = telemetry[0];
      float value = float(telemetry[1]);  //  WHY won’t this convert to int ?!?!
      int valint = int(value);

      //     println (qualifier +"="+ valint);


      if (qualifier.equals(“r”)) {
        // rotation
        angle = oldangle + value / 360 * TWO_PI; // input ranges 0-359, result is in radians
        //      println (“rotated: " + value + " degrees” +"   “+ angle +” radians");
      }
      if (qualifier.equals(“t”)) {
        // travel
        travel = value / 50 * width * .01;  // input ranges 0-50, result is in 1% increments of screen width
        //     println (“traveled: " + value + " mickeys” +"    “+ travel +” pixels");

        //     background(192);
        x1 = x0 + travel * cos(angle);
        y1 = y0 + travel * sin(angle);
        buffer.beginDraw();
        buffer.stroke(192);
        buffer.rectMode(CENTER);
        buffer.translate(width / 2, height / 2);
        buffer.strokeWeight(2);
        buffer.line(x0,y0 , x1, y1);
        buffer.endDraw();
        oldangle = angle;
        x0 = x1;
        y0 = y1;
      }
    }

  }
  img = buffer.get(0, 0, buffer.width, buffer.height);
}

void storeStartingPoint() {
  // Mouse coordinates are originated from upper left corner.
  // Translate starting point by width / 2 and height / 2
  x0 = mouseX - width / 2;
  y0 = mouseY - height / 2;
  //println (“x0=” + x0 + “, y0=” + y0);
}

void storeDirectionPoint() {
  // Mouse coordinates are originated from upper left corner.
  // Translate starting point by width / 2 and height / 2
  x1 = mouseX - width / 2;
  y1 = mouseY - height / 2;
  //println (“x0=” + x0 + “, y0=” + y0);
}

void drawStartingPointAndDirectionToBuffer() {
      // Draw starting point and direction to buffer
      buffer.beginDraw();
      buffer.background(32); // Clear
      buffer.stroke(192);
      buffer.rectMode(CENTER);
      buffer.translate(width / 2, height / 2);
      buffer.ellipse(x0,y0 , 10,10);
      // We want to draw direction only if state is STATE_GETTING_DIRECTION
      if( state == STATE_GETTING_DIRECTION ) {
        buffer.line (x0, y0, x1, y1);
      }
      buffer.endDraw();
      img = buffer.get(0, 0, buffer.width, buffer.height);
}

// mouseMoved event is used to draw nice animation while
// setting starting point and direction
void mouseMoved() {
  switch(state) {
    case STATE_GETTING_START_POINT:
      storeStartingPoint();
      // Draw starting point to buffer
      drawStartingPointAndDirectionToBuffer();

      break;
    case STATE_GETTING_DIRECTION:
      storeDirectionPoint();

      // Draw starting point and direction to buffer
      drawStartingPointAndDirectionToBuffer();

      break;
    case STATE_TRACKING:
      // Nothing to do here.
      break;
    default:
      // Should not happen!
      break;
  }
}

// mouseClicked event is used to set starting point and direction.
// We also change the state here
void mouseClicked() {
  println (“mouseX=” + mouseX + “, mouseY=” + mouseY);
  switch(state) {
    case STATE_GETTING_START_POINT:
      storeStartingPoint();
     
      state = STATE_GETTING_DIRECTION;
      break;
    case STATE_GETTING_DIRECTION:
      storeDirectionPoint();

      // Calculate x and y differences and angle
      float dx = x1 - x0;
      float dy = y1 - y0;
      angle = atan2(dy, dx);
      println (“angle=” + degrees(angle));
     
      // Change state before calling drawStartingPointAndDirectionToBuffer
      // so direction line is not drawn anymore
      state = STATE_TRACKING;
     
      // Draw starting point to buffer
      drawStartingPointAndDirectionToBuffer();

      // Start reading serial port
      //                 parent comport           baud  parity databits stopbit
      PicPort = new Serial(this, Serial.list()[0], 4800, ‘N’,   8,       1.0);
      PicPort.bufferUntil(cr); // Buffer until CR is found (before calling serialEvent)
     
      break;
    case STATE_TRACKING:
      // Nothing to do here
      break;
    default:
      // Should not happen!
      break;
  }
}