Geometric primitives

In this chapter we will deal with 2D graphics. 2D graphics can be created in the following ways:

  • Drawing geometric primitives such as lines, circles, and other curves and shapes like triangles and rectangles. This is the most natural way of creating graphics by programming. Generative art and creative coding projects are often based on this graphics method. We will consider this in the rest of the chapter.
  • Drawing images lets you add more realism to the graphics, and this is considered in Chapter 4, Images and Textures.
  • Setting the contents of the screen directly, pixel-by-pixel, is the most powerful way of generating graphics. But it is harder to use for simple things like drawing curves. So, such method is normally used together with both of the previous methods. A somewhat fast technique for drawing a screen pixel-by-pixel consists of filling an array with pixels colors, loading it in an image, and drawing the image on the screen (see its description in the Creating images section in Chapter 4, Images and Textures). The fastest, but a little bit harder technique, is using fragment shaders (see its explanation in the A simple fragment shader example section in Chapter 8, Using Shaders).

openFrameworks has the following functions for drawing primitives:

  • ofLine( x1, y1, x2, y2 ): This function draws a line segment connecting points (x1, y1) and (x2, y2)
  • ofRect( x, y, w, h ): This function draws a rectangle with the top-left corner (x, y), width w, and height h
  • ofTriangle( x1, y1, x2, y2, x3, y3 ): This function draws a triangle with vertices (x1, y1), (x2, y2), and (x3, y3)
  • ofCircle( x, y, r ): This function draws a circle with center (x, y) and radius r

Tip

openFrameworks has no special function for changing the color of a separate pixel. To do so, you can draw the pixel (x, y) as a rectangle with width and height equal to 1 pixel; that is, ofRect( x, y, 1, 1 ). This is a very slow method, but we sometimes use it for educational and debugging purposes.

All the coordinates in these functions are float type. Although the coordinates (x, y) of a particular pixel on the screen are integer values, openFrameworks uses float numbers for drawing geometric primitives. This is because a video card can draw objects with the float coordinates using modeling, as if the line goes between pixels. So the resultant picture of drawing with float coordinates is smoother than with integer coordinates.

Using these functions, it is possible to create simple drawings.

The simplest example of a flower

Let's consider the example that draws a circle, line, and two triangles, which forms the simplest kind of flower.

Note

This is example 02-2D/02-FlowerSimplest.

This example project is based on the openFrameworks emptyExample project. Fill the body of the testApp::draw() function with the following code:

ofBackground( 255, 255, 255 );              //Set white background
ofSetColor( 0, 0, 0 );                      //Set black color

ofCircle( 300, 100,  40 );                     //Blossom
ofLine( 300, 100,  300, 400 );                 //Stem
ofTriangle( 300, 270,  300, 300,  200, 220 );  //Left leaf
ofTriangle( 300, 270,  300, 300,  400, 220 );  //Right leaf

On running this code, you will see the following picture of the "flower":

The simplest example of a flower

Controlling the drawing of primitives

There are a number of functions for controlling the parameters for drawing primitives.

  • ofSetColor( r, g, b ): This function sets the color of drawing primitives, where r, g, and b are integer values corresponding to red, green, and blue components of the color in the range 0 to 255. After calling ofSetColor(), all the primitives will be drawn using this color until another ofSetColor() calling. We will discuss colors in more detail in the Colors section.
  • ofFill() and ofNoFill(): These functions enable and disable filling shapes like circles, rectangles, and triangles. After calling ofFill() or ofNoFill(), all the primitives will be drawn filled or unfilled until the next function is called. By default, the shapes are rendered filled with color. Add the line ofNoFill(); before ofCircle(...); in the previous example and you will see all the shapes unfilled, as follows:
    Controlling the drawing of primitives
  • ofSetLineWidth( lineWidth ): This function sets the width of the rendered lines to the lineWidth value, which has type float. The default value is 1.0, and calling this function with larger values will result in thick lines. It only affects drawing unfilled shapes. The line thickness is changed up to some limit depending on the video card. Normally, this limit is not less than 8.0.

    Tip

    Add the line ofSetLineWidth( 7 ); before the line drawing in the previous example, and you will see the flower with a thick vertical line, whereas all the filled shapes will remain unchanged. Note that we use the value 7; this is an odd number, so it gives symmetrical line thickening.

    Note that this method for obtaining thick lines is simple but not perfect, because adjacent lines are drawn quite crudely. For obtaining smooth thick lines, you should draw these as filled shapes.

  • ofSetCircleResolution( res ): This function sets the circle resolution; that is, the number of line segments used for drawing circles to res. The default value is 20, but with such settings only small circles look good. For bigger circles, it is recommended to increase the circle resolution; for example, to 40 or 60. Add the line ofSetCircleResolution( 40 ); before ofCircle(...); in the previous example and you will see a smoother circle. Note that a large res value can decrease the performance of the project, so if you need to draw many small circles, consider using smaller res values.
  • ofEnableSmoothing() and ofDisableSmoothing(): These functions enable and disable line smoothing. Such settings can be controlled by your video card. In our example, calling these functions will not have any effect.

Tip

Performance considerations

The functions discussed work well for drawings containing not more than a 1000 primitives. When you draw more primitives, the project's performance can decrease (it depends on your video card). The reason is that each command such as ofSetColor() or ofLine() is sent to drawing separately, which takes time. So, for drawing 10,000, 100,000, or even 1 million primitives, you should use advanced methods, which draw many primitives at once. In openFrameworks, you can use the ofMesh and ofVboMesh classes for this (for details, see the Using ofMesh section in Chapter 7, Drawing in 3D).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset