November 4th, 2016

openFrameworks - Polylines

In this article I'll be going through the tutorials regarding creating basic brushes and freeform shapes in openFrameworks. In a previous post, you may have noticed some screen shots taken from my first foray into creating brushes. It's really amazing what can be done in openFrameworks without having to write a ton of code. So, based on that success, I'm motivated to keep going!

Let's go.

Lines

Here they start out by describing the difference between raster and vector graphics. While I'm not going to go into detail about their differences, the important takeaways include:

  • Vectors can be resized to fit any resolution without distortion. Whereas raster graphics, when enlarged beyond their original resolution, will begin to appear more and more "grainy", or pixelated.
  • Vectors store their graphic data in objects instead of pixel values.
  • Vectors are easier to edit and update once created.

Then, they introduce ofPolyline, which allows us to store and draw path data by storing a series of sequential points and connecting them to draw a line.

  // define a varialbe of type "ofPolyline" - in header file
  ofPolyline straightSegmentPolyline;

  // add vertices to the line - in implementation file setup method
  straightSegmentPolyline.addVertex(100, 100);  
  straightSegmentPolyline.addVertex(150, 150);

  // draw the line - in draw method
  straightSegmentPolyline.draw();

Brushes from PolyLines

As we said, polylines are, essentially, array-like objects that consist of series of points (x,y). Instead of having create variables for both x and y each time we want to create a point on the polyline we can use what is called an ofVec2. This class stores values for X and Y in one variable along with some convenience methods for handling complex maths associated with manipulating these X/Y values. Here's an example of how you might envoke ofVec2.

  ofVec2f mousePos(ofGetMouseX(), ofGetMouseY());

This code initiates a variable called mousePos and establishes it an ofVec2 type. We then pass the current X and Y values captured from mouse movement as arguments. mousePos is now a single variable with these two values stored.

Polylines or polyline objects also contain methods for drawing new points along the line. For example:

  // added to the header - declares a variable called currentPolyline as an ofPolyLine type.
  ofPolyline line;

  // using addVertex to add points to line - x y variables declaration and initiation omitted.
  line.addVertex(x, y);

  // using curveTo to add points to line but creates 20 extra vertices (default) used to create a curved line.
  line.curveTo(x, y);

Once points are added, we only need to call the polyline's draw method to print the line to the screen.

Points

Polylines also expose a means for getting all the vertices along the line.

// will return all vertices (ofVec3 objects)
polyline.getVertices();

Calling getVertices will return a list of ofVec3 objects. ofVec3 is a lot like ofVec2 but offers support for X, Y and Z (depth) values.

We can also get specific points (vertices) by using getPointAtPercent(). Like this:

// takes a float between 0 and 1
polyline.getPointAtPercent(0.5);

Normals

These are some of the techniques we can use to get points on the polyline, but there are other aspects of the polyline we can access. Once we have a point on the line we can get a normal vector) by doing the following:

polyline.getNormalAtIndex(vertexIndex);

This gives us the ability to draw perpendicular lines (or otherwise) along our polyline. But say we want to simply get evenly spaces normals along a given polyline. In this case we use:

polyline.getNormalAtIndexInterpolated(index);

The main difference here is that the argument passed to getNormalAtIndexInterpolated is a floatIndex. This means, we can pass a value like 1.5 and the function will return an ofVec3 point between 1 and 2.

Tangents

Tangents are, effectively, the opposite of normals. They represent a line originating at a given point that runs perpendicular to the normal. We can access or create tangents in the same way we create or access normals. Like so:

polyline.getTangentAtIndex(index);

polyline.getTangentAtIndexInterpolated(floatIndex);

Taking a Screen Shot

As you might have seen in a a previous post, we are capable of capturing the screen and saving it off in standard image formats. This is great for raster graphics. But we're dealing with vector graphics now. These polylines we're making can be saved to PDF format in order to maintain their ability to scale. Here's some code:

void ofApp::setup(){
  // Setup code omitted for clarity...

  isSavingPDF = false;
}

void ofApp::draw(){
  // If isSavingPDF is true (i.e. the s key has been pressed), then
  // anything in between ofBeginSaveScreenAsPDF(...) and ofEndSaveScreenAsPDF()
  // is saved to the file.
  if (isSavingPDF) {
    ofBeginSaveScreenAsPDF("savedScreenshot_" + ofGetTimestampString() + ".pdf");
  }

  // Drawing code omitted for clarity...

  // Finish saving the PDF and reset the isSavingPDF flag to false
  // Ending the PDF tells openFrameworks to resume drawing to the screen.
  if (isSavingPDF) {
    ofEndSaveScreenAsPDF();
    isSavingPDF = false;
  }
}

void ofApp::keyPressed(int key){
  if (key == 's') {
    // isSavingPDF is a flag that lets us know whether or not save a PDF
    isSavingPDF = true;
  }
}

The code above assumes you've declared isSavingPDF in you .h file as a type bool. You can see how this variable is set in the keyPressed method, which then allows us to call the necessary functions that save the PDF file to a defined path. I used this method to snap the pic in this article.

2017 : James Walton : Digital Carpentry