SlideShare ist ein Scribd-Unternehmen logo
1 von 15
Downloaden Sie, um offline zu lesen
A graphic library and an application for simple curve
                    manipolation


                                   Michele Segata


                      Facolt` di Scienze MM.FF.NN.
                            a
                 Course of Principles of Computer Graphics

                         michele.segata@gmail.com




                                        Abstract
    In this report I am going to describe the implementation of a simple curve manipolation
application, developed as a fulfilment of the second project for the course of Principles of
Computer Graphics. The report is organized as follows: Section 1 introduces the problem
and what the application should implement, Section 2 describes the architecture of the pro-
gram, Section 3 explains some foundamental parts of the source code and finally Section 4
illustrates how to use the application together with some screenshots and examples.
1     Introduction

The project I had to develop was a simple application for the manipulation of some type of
parametric curves we have studied during the course. The program should allow a user to
manipulate the following kind of curves:


    • Hermite spline curves;


    • Bezier curves;


    • Interpolating spline curves;


    • B-Spline curves;


    • Lagrangian curves.


    The application had to be interactive, giving to the user the possibility to:


    • switch from a curve type to another:


    • add curves:


    • add control points to a curve:


    • edit curve parameters such as continuity (for composite curves) or the K value for B-
      Splines:


    • manipulate control points, so dragging one or more of them together to modify the aspect
      of a curve.


   Moreover I had to implement the possibility to put an image in background and let the user
try to draw over it using the tool, so to determine which kind of curve is more suited for the
purpose. Finally the program had to be able to receive a file in input, read a list of points
coordinates and draw the five types of curve using those points.
  A screenshot of the final application can be seen in Figure 1: on the left I put the control
menu, while in the center you can see and manipulate the curve.


                                                2
Figure 1: A screenshot of the final curve manipulation application

    In the following Section I will describe the architecture of the application.


2     Architecture
The architecture of the application is extremely simple and it is shown in Figure 2: the Figure
shows the rendering cycle with the operations that are performed within it.
    The first task to be done is to retrieve current mouse state: to easily achieve this goal I
have developed a routine which retrieves current mouse position, pressed button and keyboard
modifiers (such as the shift key). Mouse state is then passed to the function that renders the
scenegraph: it needs the state because the scenegraph that I have developed performs also
picking, so it uses the previous and the current state of the mouse to determine mouse changes,
such as movements or clicks.
    The scenegraph rendering function needs also the application state, which contains informa-
tion such as the kind of curve the user has selected, in order to show it and hide the others.
    As depicted by Figure 2, the function performs four main steps. The first one is the menu
rendering: the graphic library I have developed allows to generate buttons with text, so it is
easy to create the menu shown in Figure 1 by inserting the buttons in the scenegraph as normal
objects to be rendered and tell the scenegraph that on them picking has to be performed.
    Picking is indeed the next step, which is obtained by inverting the current transformation
matrix. To understand why, have a look at Figure 3. What the scenegraph knows about the
button object is its original center and its dimension: button position on screen, after applying
transformation matrix, is not known. I could have saved it, but it is just a waste of space.
Instead, by applying the inverse of the transformation matrix to the mouse cursor, I can get
its position w.r.t. button position saved in memory. Clearly, inverted transformation matrices
are precomputed at the beginning of the program to avoid waste of CPU resources during
rendering. To perform matrix inversion I have used my own library in which I have developed

                                                 3
MOUSE       write    MOUSE
                         STATE               MANAGER




                                  read
                                               S
                                               C             MENU
                                               E           RENDERING
                                               N
                                               E
                                               G
                                               R             MENU
                                               A            PICKING
                                               P
                      APPLICATION              H
                         STATE                                           read
                                     read                    CURVE
                                               R
                                               E           RENDERING
                                               N                          CURVES
                             write             D                           STATE
                                               E
                                               R            CURVE
                         EVENT                              PICKING
                        HANDLER                I                       write
                                               N
                                               G


                                                                       write




                      Main rendering cycle

                            Figure 2: Architecture of the application


some mathematical utilities, to easily perform matrix/vector operations, linear systems solving,
etc. . .
    If the picking procedure discovers an event, such as a click, it invokes a callback which handles
the event: the handler will modify the state of the application, for example setting the Bezier
curve as hidden and the Hermite as visible. At the successive cycle, the rendering function will
use the updated state.
    The following step is the curve rendering: from the curve state, the rendering function gets
information such as control points, continuity, etc. . . and draws the curve on the screen. For
curve drawing I have implemented a dedicated set of procedures in my graphic library.
    After rendering, picking on curve points is performed: the latter operation will modify the
state of the curves, in particular changing the position of the control points. It is possible to
drag a single point or more that one together: by clicking on some points with the shift key
pressed, they will be inserted in a structure and when mouse is dragged all of them will be
moved.


3    Implementation
The application has been developed entirely in C and using only the OpenGL graphic library
and the library I have personally developed: no other high level tools had been used. Why?


                                                       4
(-3, 2)                 (3, 2)                               (0, 6)                  (6, 6)

                         BUTTON                                                      BUTTON
                         OBJECT                                                       IMAGE
                                                 Transformation and                                           Unknown
                                                     rendering                                                 by the
                     CENTER = (0, 0)                                             CENTER = (3, 4)
                                                                                                             application
                                                                                                                logic
           (-3, -2)                (3, -2)                              (0, 2)                      (6, 2)

                 Object in memory                                                Object on screen




                     (-2, -1)                                                    (1, 3)


                                               Inverse transformation
                 Mouse position w.r.t.                                           Mouse on screen
                    button object



                                              Figure 3: Picking procedure


Because this is a course on the principles of computer graphics and, in my opinion, the best
way to learn the principles is by trying to implement them after having studied the concepts.
That is why I have developed the library with curves, 3D geometry, mathematic functionalities,
scenegraph, picking and so on.
    I am now going to describe some parts of the source code. I will not describe the entire
library, because it would take hundreds of pages: anyhow, the source code is documented.
    I will comment the scenegraph rendering source code, since it is the core of the entire ap-
plication. Listing 1 shows the structure of a node. The first fields (in id, in p, down id, down p
and up id) are used by the scenegraph to save information about picking states: for example, if
the mouse is inside a button, the field in p will point to the node containing that button. Then
we have the field type, which clearly tells the rendering procedure the kind of node: it could be
an empty node, a mesh, a transformation matrix, a curve or a text. The data about the node
is then inserted into the union node. We can assign an id to the node, which is particularly
useful for event handling to let the application determine which button have been clicked, for
example. inv matrix contains the inverse of the transformation matrix if the node is of this
kind. This field contains the inverse of all the matrices multiplied together from the root down
to the current node.
    Then clearly we have the children of the node: their count is saved into nchildren, while the
field children is the array containing them. The field picking determines wheter picking has to
be performed and visible if the node has to be rendered or not. The last two fields are used by
the scenegraph to memorize the mouse coordinates unprojected from the window coordinates
to the object coordinates.
struct g r a p h n o d e t {
    int                                      in id ;
    struct g r a p h n o d e t               ∗ in p ;
    int                                      down id ;
    struct g r a p h n o d e t               ∗ down p ;
    int                                      up id ;
    node type t                              type ;
    int                                      id ;
    union {
         mesh t                              ∗ object ;
         floating t                          ∗∗ m a t r i x ;
         curve t                             ∗ curve ;
         text t                              ∗ text ;


                                                                  5
} node ;
      floating t                          ∗∗ i n v m a t r i x ;
      int                                 nchildren ;
      struct g r a p h n o d e t          ∗∗ c h i l d r e n ;
      int                                 picking ;
      int                                 visible ;
      GLdouble                            un mouse [ 4 ] ;
      GLdouble                            pun mouse [ 4 ] ;
};

                                Listing 1: Source code of the scenegraph node

    The scenegraph rendering function code is quite long so I will describe it taking only some
pieces out of it. First of all, the scenegraph rendering function is recursive: since we are working
with a graph, visiting it in a recursive way is straightforward. Its prototype is shown in Listing 2.
void s c e n e g r a p h r e n d e r (
    graph node t ∗ root ,
    g r a p h n o d e t ∗g ,
    f l o a t i n g t ∗∗tm ,
    void ( ∗ r e n d e r c a l l b a c k ) ( /∗ . . . ∗/ ) ,
    void ∗ c a l l b a c k d a t a ,
    void ( ∗ e v e n t h a n d l e r ) ( g r a p h n o d e t ∗ , f l o a t i n g t ∗∗tm , g r a p h n o d e t ∗ , void ∗ ,
              pick type t ) ,
    void ∗ h a n d l e r d a t a ,
    mouse t ∗ms ,
    mouse t ∗pms ,
    selection t ∗ sel ,
    f l o a t i n g t ∗∗ inv m
);

                              Listing 2: Scenegraph rendering function prototype

    The first two parameters indicate the root of the scenegraph and the node that is going to be
processed. The third indicates the current transformation matrix. render callback, if specified,
is invoked before processing a node and callback data is a pointer to user defined data passed
to the render callback. event handler is a pointer to the callback to be invoked when a picking
event is raised: it clearly will specify the node that is interested in the event and the type of
event: handler data is a pointer to user defined data for the event handler. ms and pms are
pointers to the current mouse state and the mouse state in the previous frame. sel is a pointer
to a structure that contains the set of points selected for dragging. Finally, inv m indicates which
is the inverse for the current transformation matrix.
    The first thing the function controls is if the node to be processed is NULL or if it is not
visible: if so, the function ends. By doing this, if a node is not visible, also its children won’t
be displayed.
i f ( g == NULL)
      return ;

if   ( ! g−>v i s i b l e )
       return ;

  Then it performs different operations based on node type. If the node is a transformation
matrix, then it multiplies the current transformation matrix with the one contained in the node.
i f ( g−>t y p e == NODE MATRIX) {
      ntm = c r e a t e m a t r i x t ( T MATRIX SIZE , T MATRIX SIZE ) ;
      m a t r i x m u l t i p l y ( tm , T MATRIX SIZE , T MATRIX SIZE , g−>node . matrix , 0 ,
            T MATRIX SIZE , ntm−>m a t r i x ) ;


                                                               6
}

     If the node is a mesh to be rendered instead, the function renders it.
i f ( g−>t y p e == NODE OBJECT) {

       r e n d e r m e s h ( g−>node . o b j e c t , tm , 0 , 0 ) ;

    If needed, picking is also performed. The first thing to do is to unproject the mouse and
multiply the result for the inverse of the transformation matrix. Then1 I control if the mouse is
inside or outside the object.
       i f ( g−>p i c k i n g ) {

              pt = PICK NONE ;

              u n p r o j e c t m o u s e ( tm , g−>un mouse , ms , inv m ) ;

               i f ( g−>node . o b j e c t −>p r o p e r t i e s . t y p e == PLANE) {

                    l = g−>node . o b j e c t −>p r o p e r t i e s . b a r i c e n t e r [ 0 ] − g−>node . o b j e c t −>
                        p r o p e r t i e s . edge / 2 ;
                    r = l + g−>node . o b j e c t −>p r o p e r t i e s . edge ;
                    t = g−>node . o b j e c t −>p r o p e r t i e s . b a r i c e n t e r [ 1 ] + g−>node . o b j e c t −>
                        properties . height / 2;
                    b = t − g−>node . o b j e c t −>p r o p e r t i e s . h e i g h t ;

                    i f ( ( g−>un mouse [ 0 ] >= l ) && ( g−>un mouse [ 0 ] <= r ) && ( g−>un mouse [ 1 ]
                           <= t ) && ( g−>un mouse [ 1 ] >= b ) && ( g−>un mouse [ 2 ] > Z LIMIT ) )
                          c p t = PICK MOUSEIN ;
                    else
                          c p t = PICK MOUSEOUT;

              }

    Then we can have different situations: if the mouse is inside and we have not yet signaled
the event, then we invoke the mouseout event for the object that was previously ”mousein” (if
existing), then we signal the mousein event for the current object.
               i f ( event handler ) {

                    switch ( c p t ) {

                    case PICK MOUSEIN :
                        // have we a l r e a d y s i g n a l e d mouse i n f o r t h i s o b j e c t ? a r e we s t i l l
                                inside that object ?
                        i f ( r o o t −>i n i d != g−>i d ) {

                                 // s i g n a l t h e mouse o u t t o o t h e r o b j e c t i f needed
                                 i f ( r o o t −>i n i d != −1) {
                                        e v e n t h a n d l e r ( r o o t , tm , r o o t −>i n p , h a n d l e r d a t a ,
                                               PICK MOUSEOUT) ;
                                 }

                                 e v e n t h a n d l e r ( r o o t , tm , g , h a n d l e r d a t a , c p t ) ;
                                 // remember t h a t now t h e mouse i s i n s i d e c u r r e n t o b j e c t
                                 r o o t −>i n i d = g−>i d ;
                                 r o o t −>i n p = g ;
                          }

    1 For   semplicity I have implemented only the picking for rectangles


                                                                   7
If no mouse button is down at the moment but it was down for an object in the previous
picking control, then we signal the mouseup event.
                   // c h e c k f o r c l i c k
                   i f ( ms−>b == −1) {
                          i f ( r o o t −>down id == g−>i d )
                                  e v e n t h a n d l e r ( r o o t , tm , g , h a n d l e r d a t a , PICK MOUSEUP) ;
                          r o o t −>down id = −1;
                          r o o t −>down p = NULL;
                   }

    If instead the mouse button is down, we save this information into the root node.
                   i f ( ms−>b == 0 ) {
                         i f ( r o o t −>down id == −1) {
                               r o o t −>down id = g−>i d ;
                               r o o t −>down p = g ;
                         }
                   }
                   break ;

   If we have discovered a mouseout, we signal it only if the mouse was previously inside the
object.
              case PICK MOUSEOUT:

                   // i f we were i n s i d e c u r r e n t o b j e c t and we a r e g o i n g o u t t h e n we
                         s i g n a l t h e mouse o u t
                   i f ( r o o t −>i n i d == g−>i d ) {
                         e v e n t h a n d l e r ( r o o t , tm , g , h a n d l e r d a t a , c p t ) ;
                         // remember t h a t now mouse i s o u t s i d e c u r r e n t o b j e c t
                         r o o t −>i n i d = −1;
                         r o o t −>i n p = NULL;
                   }
                   i f ( ms−>b == −1 && r o o t −>down id == g−>i d ) {
                         r o o t −>down id = −1;
                         r o o t −>down p = NULL;
                   }
                   break ;

              default :
                  break ;

              }

         }

     }
}

    We now move on and analyze the case in which the node is a curve. Here we have five
subcases, one per curve type: since they are all quite similar I will go through only the Hermite.
To draw it we cycle through all the composite curves and we check for continuity: if set to C0,
then each curve has its own derivatives, otherwise the derivative on the first point is set as the
derivative on the second point of the previous curve. The single hermite curve is draw by using
the curve hermite spline procedure. Moreover, to give a graphic feedback to let the user know
which composite curve is currently selected, I painted the latter in yellow.
    Then the manage curve function is used to drag the points that have been selected: its code
is shown in Listing 3. The function checks first of all if the user has released the mouse button


                                                         8
and if the shift key is not pressed: in that case the function removes all the points from the
selection. Then, if there is at least one point selected and the mouse button is pressed, it moves
all the selected point by an amount which is determined by the difference between the previous
mouse state and the current.
    After that, the hermite control function, draws control points and derivatives and performs
picking: picking on points is easy and it is obtained by measuring the distance between point
and mouse cursor with usual euclidean distance. If the function detects that a control point
is clicked, it is added to the selection so that the manage curve function can move it in case of
dragging.

i f ( g−>t y p e == NODE CURVE) {

     c u r v e t ∗ c = g−>node . c u r v e ;
     i n t j , base , n p o i n t s ;
     f l o a t i n g t ∗∗ c u r v e p o i n t s ;
     f l o a t i n g t tmp [ 3 ] ;
     floating t color [ 4 ] ;

    switch ( c−>t y p e ) {

    case CURVE HERMITE:

           f o r ( i = 0 ; i < c−>n c o m p o s i t e ; i ++) {

                  i f ( i == c−>s e l e c t e d c o m p o s i t e )
                        v e c t o r s e t 4 ( c o l o r , CCS R , CCS G , CCS B , CCS A) ;
                  else
                        v e c t o r s e t 4 ( c o l o r , CCU R, CCU G, CCU B, CCU A) ;

                  switch ( c−>c o n t i n u i t y ) {

                  case 0 :
                      c u r v e h e r m i t e s p l i n e ( c−>p o i n t s [ i ] . c , c−>p o i n t s [ i + 1 ] . c , c−>
                             d e r i v a t i v e s [ i ∗ 2 ] . d e r i v , c−>d e r i v a t i v e s [ i ∗ 2 + 1 ] . d e r i v , tm
                             , color ) ;
                      break ;

                  case 1 :
                      i f ( i == 0 )
                            c u r v e h e r m i t e s p l i n e ( c−>p o i n t s [ i ] . c , c−>p o i n t s [ i +   1 ] . c , c−>
                                   d e r i v a t i v e s [ i ∗ 2 ] . d e r i v , c−>d e r i v a t i v e s [ i ∗ 2   + 1 ] . deriv
                                   , tm , c o l o r ) ;
                      else
                            c u r v e h e r m i t e s p l i n e ( c−>p o i n t s [ i ] . c , c−>p o i n t s [ i +   1 ] . c , c−>
                                   d e r i v a t i v e s [ i ∗ 2 − 1 ] . d e r i v , c−>d e r i v a t i v e s [ 2   ∗ i + 1].
                                   d e r i v , tm , c o l o r ) ;
                      break ;

                  }

           }

           manage curve ( g , ms , pms , s e l ) ;

           h e r m i t e c o n t r o l ( g , ms , pms , s e l , tm ) ;

           break ;

    case CURVE BEZIER :



                                                               9
[...]

     default :
         break ;
     }

     i f ( g−>p i c k i n g ) {
           u n p r o j e c t m o u s e ( tm , g−>un mouse , ms , inv m ) ;
           u n p r o j e c t m o u s e ( tm , g−>pun mouse , pms , inv m ) ;
     }
}




   Finally, the mouse cursor is unprojected and saved into the node and will be used at the next
rendering cycle. The reason why I unproject the mouse after the rendering is that for retrieving
the pixel the mouse is over I use the OpenGL glReadPixels function: as the name suggests, it
reads a pixel from the viewport (in particular using the depth buffer), but clearly the pixel must
firstly be rendered.

void manage curve ( g r a p h n o d e t ∗g , mouse t ∗ms , mouse t ∗pms , s e l e c t i o n t ∗ s e l ) {

     i f ( ms−>b == −1 && pms−>b == 0 && ! ( ms−>k & KEY SHIFT) ) {

           unselect selection ( sel ) ;
           selection empty ( sel ) ;

     }

     i f ( s e l e c t i o n s i z e ( s e l ) != 0 && ms−>b == 0 ) {

           m o v e s e l e c t i o n ( s e l , g−>pun mouse [ 0 ] − g−>un mouse [ 0 ] , g−>pun mouse [ 1 ] − g
                −>un mouse [ 1 ] ) ;

     }

}


                                       Listing 3: manage curve function



    As said before, the last type of nodes the scenegraph handles is text: in this case, I have
developed a function that prints a text on the screen. Its parameters are center position, the size
of the character, a char map and the text to print. The char map is an empty mesh containing
a texture from which retrieve the images of the characters: it can be considered as the font to
be used. The one I have used is shown in Figure 4.

i f ( g−>t y p e == NODE TEXT) {

     p r i n t c e n t e r e d s t r i n g f m t ( g−>node . t e x t −>x , g−>node . t e x t −>y , g−>node . t e x t −>
             s i z e , g−>node . t e x t −>char map , tm , ”%s ” , g−>node . t e x t −>t e x t ) ;

}




                                                           10
Figure 4: The texture used to print text on the screen

   The last part of the scenegraph rendering function is the recursive invocation: for each child
we invoke the function on it. If the current node is a transformation matrix, then the new
matrix is passed to the procedure, as well as its inverse.
     f o r ( c h i l d = 0 ; c h i l d < g−>n c h i l d r e n ; c h i l d ++)
           i f ( g−>t y p e == NODE MATRIX)
                   s c e n e g r a p h r e n d e r ( r o o t , g−>c h i l d r e n [ c h i l d ] , ntm−>matrix ,
                           r e n d e r c a l l b a c k , c a l l b a c k d a t a , e v e n t h a n d l e r , h a n d l e r d a t a , ms ,
                          pms , s e l , g−>i n v m a t r i x ) ;
           else
                   s c e n e g r a p h r e n d e r ( r o o t , g−>c h i l d r e n [ c h i l d ] , tm , r e n d e r c a l l b a c k ,
                           c a l l b a c k d a t a , e v e n t h a n d l e r , h a n d l e r d a t a , ms , pms , s e l , inv m ) ;

     i f ( g−>t y p e == NODE MATRIX)
           f r e e m a t r i x t ( ntm ) ;

}




4     Usage and examples
The project comes with the source code and a makefile that can be used to build it on Mac
OS X, Windows and Linux. By typing the make command, the application curvemanipulator is
generated. It is possible to run it in three different ways:
    • normal way: type ./curvemanipulator;
    • with background image: type ./curvemanipulator picture.bmp and the application will
      show the image in background so that the user can try to draw on it using the program.
      The image must be a bmp file and if your graphic card is quite old it must be a square
      with a size that is equal to a power of two (e.g. 512x512);
    • with sampled points: type ./curvemanipulator points.txt <points per composite curve>
      [curve to draw] and the application will use the points in the file as points for the curves.


                                                                  11
The second parameter specifies how many points per curve should be used (excepts for
      the hermite spline), while the third, if specified, tells the application to draw only a single
      type of curve: 0 will draw only the Hermite, 1 the Bezier, 2 the Interpolating Spline, 3
      the B-Spline and 4 the Lagrangian. This is useful if you want to draw a curve with a high
      number of points to reduce the computational time. The file with sampled points must
      contain a point per line with comma separated x and y coordinates.




   The first case is shown in Figure 1. As said before, on the left you have the controls and in
center the curve. Let’s see how to use it. The upper part lets you chose between the different
type of curves and the currently selected curve is printed under the buttons.

   Then you have the ”Add curve” button, which lets you insert a new curve, to be added to
the set of composite curves. As soon as you click it, the text ”Selected mode: Add curve” will
appear on top of the screen, telling you that the modality is active and will remain like that
until you click another button or you press the ESC key. In this way you can insert multiple
curves with successive clicks on the screen.

    If you have a composite curve, then you can select a single piece by using the + and - buttons
under the ”Curve:” text: as soon as you change the selected piece, the number will change and
the curve will be drawn in yellow. After that you can add a point to the currently selected
curve by clicking the ”Add point” button: this action will show in the upper part of the screen
the ”Selected mode: Add point” and from now on you will be able to add multiple points by
clicking on the screen2 .

   At any time, you can drag any point you see on the screen, in order to modify the aspect of
the curve. If you want to drag more than one point at a time, just keep the shift key pressed
and select the points by clicking on them: after that, drag a point to modify them all.

    If you use Hermite, Bezier or Interpolating Spline, then you can set the type of continuity
between composite curves by clicking the buttons ”C0” and ”C1”: in particular, if you have
selected curve number 3, then the continuity you have chosen will be set between curves 3 and
4. For a better understanding of which derivatives belongs to which curve, the derivative of the
first point is colored in green, while the derivative on last point is colored in light blue.

    Finally, if you use a B-Spline, you will be able to change the K value for each curve by
selecting it and then use the + and - buttons under the ”B-Spline K:” text.

   If you specify a background image then a button is added to the menu, to let the user hide
and show it. The result is shown in Figure 5 and in Figure 6, where I tryed to draw over an A4
Avant designed by Audi.




  2 Since   Hermite splines have only two points per curve, the program will let you only add curves in that case


                                                        12
Figure 5: The application running with the background image visible




            Figure 6: The application running with the background image hidden


    The result of the last modality is shown in Figure 7: the sampling file contained the points
of the a race circuit shown in Figure 8.


                                              13
Figure 7: The application running with the sampling option and displaying a B-Spline curve




                        Figure 8: The race circuit that I have sampled


A      Appendix - Brief graphic library description
In the appendix I give a very brief description of what kind functionalities the graphic library
provides, by listing the header files.

    • gl 2d geometry.h: contains functionalities to create 2D figure meshes, such as polygons or


                                              14
circles and to generate the convex hull of a given set of points via the Graham algorithm;
• gl 3d geometry.h: contains functionalities to create 3D solid meshes, like cubes, regular
  pyramids, cones, prysms cylinders and spheres. The sphere can be drawn with different
  resolutions (i.e. slices and stacks). For each solid, there is the possibility to automatically
  calculate normals for lights. I have developed three algorithms, one which calculates face
  normals, one which calculates vertex normals by averaging adjacent face normals (useful
  if you want to see a smooth surface) and one which uses both and selects between vertex
  normals and face normals by angle discrimination;
• gl curve.h: contains functionalities to draw the curves I have mentioned in this report;
• gl material.h: contains some simple functionalities to load textures into a mesh, manage
  properties of materials and a nice procedure to map a texture onto a sphere;
• gl matrix.h: contains a bunch of matematical functionalities, to create matrices, vectors,
  copy and duplicate them, perform operations such as sums, subtractions and multiplica-
  tions, perform dot products, normalize vectors, determination of CW and CCW direction
  between two vectors, matrix determinant, matrix inverse and linear system solving;
• gl mempool.h: contains functionalities to get pre-allocated matrices and vectors when
  needed, without allocating and deallocating and wasting CPU resources. This is useful for
  example for the rendering function, where temporary vectors and matrices are needed;
• gl mesh.h: contains functionalities to create meshes and faces for meshes, as well as cal-
  culation of normals;
• gl mouse.h: contains functionalities for retrieving current mouse state;
• gl physic law.h: contains functionalities useful for physics simulation: you can evaluate,
  integrate and derivate polynoms, compute motion law and gravity force;
• gl physic.h: contains functionalities for simple physics, that is sphere to plane collision
  and sphere to sphere collision;
• gl printf.h: contains functionalities for drawing texts, in a printf way;
• gl rendering.h: contains only a function to perform rendering of a mesh. The function
  is quite complicated, because it performs OpenGL attributes activation and deactivation
  given mesh properties;
• gl scenegraph.h: contains the functionalities to manage a scenegraph, that is create it,
  add nodes, add children, render the graph, etc. . . ;
• gl selection.h: contains functionalities to manage multiple object selection, so add an
  object to the selection, retrieve current selected objects, etc. . . ;
• gl tga.h: contains a function which loads a tga image from a file;
• gl transformation.h: contains functionalities to performs transformations, such as rota-
  tions or translations;
• gl util.h: contains functionalities for dinamic arrays, elapsed time calculation (in mi-
  croseconds, useful for physics) and a modified version of the quick sort contained in the
  standard C library.



                                             15

Weitere ähnliche Inhalte

Ähnlich wie A graphic library and an application for simple curve manipolation

[IJET-V1I5P13] Authors: Ayesha Shaikh, Antara Kanade,Mabel Fernandes, Shubhan...
[IJET-V1I5P13] Authors: Ayesha Shaikh, Antara Kanade,Mabel Fernandes, Shubhan...[IJET-V1I5P13] Authors: Ayesha Shaikh, Antara Kanade,Mabel Fernandes, Shubhan...
[IJET-V1I5P13] Authors: Ayesha Shaikh, Antara Kanade,Mabel Fernandes, Shubhan...
IJET - International Journal of Engineering and Techniques
 
MAE 593 Final ProjectReport
MAE 593 Final ProjectReportMAE 593 Final ProjectReport
MAE 593 Final ProjectReport
nsapre
 
Empirical Analysis of Invariance of Transform Coefficients under Rotation
Empirical Analysis of Invariance of Transform Coefficients under RotationEmpirical Analysis of Invariance of Transform Coefficients under Rotation
Empirical Analysis of Invariance of Transform Coefficients under Rotation
IJERD Editor
 
Advanced Image Reconstruction Algorithms in MRIfor ISMRMversion finalll
Advanced Image Reconstruction Algorithms in MRIfor ISMRMversion finalllAdvanced Image Reconstruction Algorithms in MRIfor ISMRMversion finalll
Advanced Image Reconstruction Algorithms in MRIfor ISMRMversion finalll
Muddassar Abbasi
 
Camera interval timer with infrared remote control
Camera interval timer with infrared remote controlCamera interval timer with infrared remote control
Camera interval timer with infrared remote control
RSComponentsTCC
 

Ähnlich wie A graphic library and an application for simple curve manipolation (20)

[IJET-V1I5P13] Authors: Ayesha Shaikh, Antara Kanade,Mabel Fernandes, Shubhan...
[IJET-V1I5P13] Authors: Ayesha Shaikh, Antara Kanade,Mabel Fernandes, Shubhan...[IJET-V1I5P13] Authors: Ayesha Shaikh, Antara Kanade,Mabel Fernandes, Shubhan...
[IJET-V1I5P13] Authors: Ayesha Shaikh, Antara Kanade,Mabel Fernandes, Shubhan...
 
SAP Transport System; Step-by-step guide from concept to practical
SAP Transport System; Step-by-step guide from concept to practicalSAP Transport System; Step-by-step guide from concept to practical
SAP Transport System; Step-by-step guide from concept to practical
 
Block Image Encryption using Wavelet
Block Image Encryption using WaveletBlock Image Encryption using Wavelet
Block Image Encryption using Wavelet
 
MAE 593 Final ProjectReport
MAE 593 Final ProjectReportMAE 593 Final ProjectReport
MAE 593 Final ProjectReport
 
Animation ppt
Animation pptAnimation ppt
Animation ppt
 
Empirical Analysis of Invariance of Transform Coefficients under Rotation
Empirical Analysis of Invariance of Transform Coefficients under RotationEmpirical Analysis of Invariance of Transform Coefficients under Rotation
Empirical Analysis of Invariance of Transform Coefficients under Rotation
 
Comparative Analysis of Hand Gesture Recognition Techniques
Comparative Analysis of Hand Gesture Recognition TechniquesComparative Analysis of Hand Gesture Recognition Techniques
Comparative Analysis of Hand Gesture Recognition Techniques
 
3 d graphics with opengl part 2
3 d graphics with opengl  part 23 d graphics with opengl  part 2
3 d graphics with opengl part 2
 
Advanced Image Reconstruction Algorithms in MRIfor ISMRMversion finalll
Advanced Image Reconstruction Algorithms in MRIfor ISMRMversion finalllAdvanced Image Reconstruction Algorithms in MRIfor ISMRMversion finalll
Advanced Image Reconstruction Algorithms in MRIfor ISMRMversion finalll
 
Automatic Image Registration Using 2D-DWT
Automatic Image Registration Using 2D-DWTAutomatic Image Registration Using 2D-DWT
Automatic Image Registration Using 2D-DWT
 
Ax03303120316
Ax03303120316Ax03303120316
Ax03303120316
 
Camera interval timer with infrared remote control
Camera interval timer with infrared remote controlCamera interval timer with infrared remote control
Camera interval timer with infrared remote control
 
Infoception 8:going modal
Infoception 8:going modalInfoception 8:going modal
Infoception 8:going modal
 
Pointer Events Working Group update / TPAC 2023 / Patrick H. Lauke
Pointer Events Working Group update / TPAC 2023 / Patrick H. LaukePointer Events Working Group update / TPAC 2023 / Patrick H. Lauke
Pointer Events Working Group update / TPAC 2023 / Patrick H. Lauke
 
998-isvc16
998-isvc16998-isvc16
998-isvc16
 
"FingerPrint Recognition Using Principle Component Analysis(PCA)”
"FingerPrint Recognition Using Principle Component Analysis(PCA)”"FingerPrint Recognition Using Principle Component Analysis(PCA)”
"FingerPrint Recognition Using Principle Component Analysis(PCA)”
 
Isvc08
Isvc08Isvc08
Isvc08
 
Fx570 ms 991ms_e
Fx570 ms 991ms_eFx570 ms 991ms_e
Fx570 ms 991ms_e
 
K25047051
K25047051K25047051
K25047051
 
K25047051
K25047051K25047051
K25047051
 

Mehr von graphitech

Mehr von graphitech (20)

Rescue Mission
Rescue MissionRescue Mission
Rescue Mission
 
Mashup - Sustainability
Mashup - SustainabilityMashup - Sustainability
Mashup - Sustainability
 
Mashup - Sustainability
Mashup - SustainabilityMashup - Sustainability
Mashup - Sustainability
 
Multiple Screens
Multiple ScreensMultiple Screens
Multiple Screens
 
Multiple Screens
Multiple ScreensMultiple Screens
Multiple Screens
 
Graph Matching
Graph MatchingGraph Matching
Graph Matching
 
Shape Analysis
Shape AnalysisShape Analysis
Shape Analysis
 
Human Interaction Library
Human Interaction LibraryHuman Interaction Library
Human Interaction Library
 
WebCams Mapping on Nasa World Wind
WebCams Mapping on Nasa World WindWebCams Mapping on Nasa World Wind
WebCams Mapping on Nasa World Wind
 
Street Builder
Street BuilderStreet Builder
Street Builder
 
Street Builder
Street BuilderStreet Builder
Street Builder
 
Live Video in World Wind
Live Video in World WindLive Video in World Wind
Live Video in World Wind
 
Live Video in World Wind
Live Video in World WindLive Video in World Wind
Live Video in World Wind
 
Terrain Modification
Terrain ModificationTerrain Modification
Terrain Modification
 
YARCA (Yet Another Raycasting Application) Project
YARCA (Yet Another Raycasting Application) ProjectYARCA (Yet Another Raycasting Application) Project
YARCA (Yet Another Raycasting Application) Project
 
YARCA (Yet Another Raycasting Application) Project
YARCA (Yet Another Raycasting Application) ProjectYARCA (Yet Another Raycasting Application) Project
YARCA (Yet Another Raycasting Application) Project
 
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
 
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
 
Arcanoid 3D
Arcanoid 3DArcanoid 3D
Arcanoid 3D
 
Arcanoid 3D
Arcanoid 3DArcanoid 3D
Arcanoid 3D
 

A graphic library and an application for simple curve manipolation

  • 1. A graphic library and an application for simple curve manipolation Michele Segata Facolt` di Scienze MM.FF.NN. a Course of Principles of Computer Graphics michele.segata@gmail.com Abstract In this report I am going to describe the implementation of a simple curve manipolation application, developed as a fulfilment of the second project for the course of Principles of Computer Graphics. The report is organized as follows: Section 1 introduces the problem and what the application should implement, Section 2 describes the architecture of the pro- gram, Section 3 explains some foundamental parts of the source code and finally Section 4 illustrates how to use the application together with some screenshots and examples.
  • 2. 1 Introduction The project I had to develop was a simple application for the manipulation of some type of parametric curves we have studied during the course. The program should allow a user to manipulate the following kind of curves: • Hermite spline curves; • Bezier curves; • Interpolating spline curves; • B-Spline curves; • Lagrangian curves. The application had to be interactive, giving to the user the possibility to: • switch from a curve type to another: • add curves: • add control points to a curve: • edit curve parameters such as continuity (for composite curves) or the K value for B- Splines: • manipulate control points, so dragging one or more of them together to modify the aspect of a curve. Moreover I had to implement the possibility to put an image in background and let the user try to draw over it using the tool, so to determine which kind of curve is more suited for the purpose. Finally the program had to be able to receive a file in input, read a list of points coordinates and draw the five types of curve using those points. A screenshot of the final application can be seen in Figure 1: on the left I put the control menu, while in the center you can see and manipulate the curve. 2
  • 3. Figure 1: A screenshot of the final curve manipulation application In the following Section I will describe the architecture of the application. 2 Architecture The architecture of the application is extremely simple and it is shown in Figure 2: the Figure shows the rendering cycle with the operations that are performed within it. The first task to be done is to retrieve current mouse state: to easily achieve this goal I have developed a routine which retrieves current mouse position, pressed button and keyboard modifiers (such as the shift key). Mouse state is then passed to the function that renders the scenegraph: it needs the state because the scenegraph that I have developed performs also picking, so it uses the previous and the current state of the mouse to determine mouse changes, such as movements or clicks. The scenegraph rendering function needs also the application state, which contains informa- tion such as the kind of curve the user has selected, in order to show it and hide the others. As depicted by Figure 2, the function performs four main steps. The first one is the menu rendering: the graphic library I have developed allows to generate buttons with text, so it is easy to create the menu shown in Figure 1 by inserting the buttons in the scenegraph as normal objects to be rendered and tell the scenegraph that on them picking has to be performed. Picking is indeed the next step, which is obtained by inverting the current transformation matrix. To understand why, have a look at Figure 3. What the scenegraph knows about the button object is its original center and its dimension: button position on screen, after applying transformation matrix, is not known. I could have saved it, but it is just a waste of space. Instead, by applying the inverse of the transformation matrix to the mouse cursor, I can get its position w.r.t. button position saved in memory. Clearly, inverted transformation matrices are precomputed at the beginning of the program to avoid waste of CPU resources during rendering. To perform matrix inversion I have used my own library in which I have developed 3
  • 4. MOUSE write MOUSE STATE MANAGER read S C MENU E RENDERING N E G R MENU A PICKING P APPLICATION H STATE read read CURVE R E RENDERING N CURVES write D STATE E R CURVE EVENT PICKING HANDLER I write N G write Main rendering cycle Figure 2: Architecture of the application some mathematical utilities, to easily perform matrix/vector operations, linear systems solving, etc. . . If the picking procedure discovers an event, such as a click, it invokes a callback which handles the event: the handler will modify the state of the application, for example setting the Bezier curve as hidden and the Hermite as visible. At the successive cycle, the rendering function will use the updated state. The following step is the curve rendering: from the curve state, the rendering function gets information such as control points, continuity, etc. . . and draws the curve on the screen. For curve drawing I have implemented a dedicated set of procedures in my graphic library. After rendering, picking on curve points is performed: the latter operation will modify the state of the curves, in particular changing the position of the control points. It is possible to drag a single point or more that one together: by clicking on some points with the shift key pressed, they will be inserted in a structure and when mouse is dragged all of them will be moved. 3 Implementation The application has been developed entirely in C and using only the OpenGL graphic library and the library I have personally developed: no other high level tools had been used. Why? 4
  • 5. (-3, 2) (3, 2) (0, 6) (6, 6) BUTTON BUTTON OBJECT IMAGE Transformation and Unknown rendering by the CENTER = (0, 0) CENTER = (3, 4) application logic (-3, -2) (3, -2) (0, 2) (6, 2) Object in memory Object on screen (-2, -1) (1, 3) Inverse transformation Mouse position w.r.t. Mouse on screen button object Figure 3: Picking procedure Because this is a course on the principles of computer graphics and, in my opinion, the best way to learn the principles is by trying to implement them after having studied the concepts. That is why I have developed the library with curves, 3D geometry, mathematic functionalities, scenegraph, picking and so on. I am now going to describe some parts of the source code. I will not describe the entire library, because it would take hundreds of pages: anyhow, the source code is documented. I will comment the scenegraph rendering source code, since it is the core of the entire ap- plication. Listing 1 shows the structure of a node. The first fields (in id, in p, down id, down p and up id) are used by the scenegraph to save information about picking states: for example, if the mouse is inside a button, the field in p will point to the node containing that button. Then we have the field type, which clearly tells the rendering procedure the kind of node: it could be an empty node, a mesh, a transformation matrix, a curve or a text. The data about the node is then inserted into the union node. We can assign an id to the node, which is particularly useful for event handling to let the application determine which button have been clicked, for example. inv matrix contains the inverse of the transformation matrix if the node is of this kind. This field contains the inverse of all the matrices multiplied together from the root down to the current node. Then clearly we have the children of the node: their count is saved into nchildren, while the field children is the array containing them. The field picking determines wheter picking has to be performed and visible if the node has to be rendered or not. The last two fields are used by the scenegraph to memorize the mouse coordinates unprojected from the window coordinates to the object coordinates. struct g r a p h n o d e t { int in id ; struct g r a p h n o d e t ∗ in p ; int down id ; struct g r a p h n o d e t ∗ down p ; int up id ; node type t type ; int id ; union { mesh t ∗ object ; floating t ∗∗ m a t r i x ; curve t ∗ curve ; text t ∗ text ; 5
  • 6. } node ; floating t ∗∗ i n v m a t r i x ; int nchildren ; struct g r a p h n o d e t ∗∗ c h i l d r e n ; int picking ; int visible ; GLdouble un mouse [ 4 ] ; GLdouble pun mouse [ 4 ] ; }; Listing 1: Source code of the scenegraph node The scenegraph rendering function code is quite long so I will describe it taking only some pieces out of it. First of all, the scenegraph rendering function is recursive: since we are working with a graph, visiting it in a recursive way is straightforward. Its prototype is shown in Listing 2. void s c e n e g r a p h r e n d e r ( graph node t ∗ root , g r a p h n o d e t ∗g , f l o a t i n g t ∗∗tm , void ( ∗ r e n d e r c a l l b a c k ) ( /∗ . . . ∗/ ) , void ∗ c a l l b a c k d a t a , void ( ∗ e v e n t h a n d l e r ) ( g r a p h n o d e t ∗ , f l o a t i n g t ∗∗tm , g r a p h n o d e t ∗ , void ∗ , pick type t ) , void ∗ h a n d l e r d a t a , mouse t ∗ms , mouse t ∗pms , selection t ∗ sel , f l o a t i n g t ∗∗ inv m ); Listing 2: Scenegraph rendering function prototype The first two parameters indicate the root of the scenegraph and the node that is going to be processed. The third indicates the current transformation matrix. render callback, if specified, is invoked before processing a node and callback data is a pointer to user defined data passed to the render callback. event handler is a pointer to the callback to be invoked when a picking event is raised: it clearly will specify the node that is interested in the event and the type of event: handler data is a pointer to user defined data for the event handler. ms and pms are pointers to the current mouse state and the mouse state in the previous frame. sel is a pointer to a structure that contains the set of points selected for dragging. Finally, inv m indicates which is the inverse for the current transformation matrix. The first thing the function controls is if the node to be processed is NULL or if it is not visible: if so, the function ends. By doing this, if a node is not visible, also its children won’t be displayed. i f ( g == NULL) return ; if ( ! g−>v i s i b l e ) return ; Then it performs different operations based on node type. If the node is a transformation matrix, then it multiplies the current transformation matrix with the one contained in the node. i f ( g−>t y p e == NODE MATRIX) { ntm = c r e a t e m a t r i x t ( T MATRIX SIZE , T MATRIX SIZE ) ; m a t r i x m u l t i p l y ( tm , T MATRIX SIZE , T MATRIX SIZE , g−>node . matrix , 0 , T MATRIX SIZE , ntm−>m a t r i x ) ; 6
  • 7. } If the node is a mesh to be rendered instead, the function renders it. i f ( g−>t y p e == NODE OBJECT) { r e n d e r m e s h ( g−>node . o b j e c t , tm , 0 , 0 ) ; If needed, picking is also performed. The first thing to do is to unproject the mouse and multiply the result for the inverse of the transformation matrix. Then1 I control if the mouse is inside or outside the object. i f ( g−>p i c k i n g ) { pt = PICK NONE ; u n p r o j e c t m o u s e ( tm , g−>un mouse , ms , inv m ) ; i f ( g−>node . o b j e c t −>p r o p e r t i e s . t y p e == PLANE) { l = g−>node . o b j e c t −>p r o p e r t i e s . b a r i c e n t e r [ 0 ] − g−>node . o b j e c t −> p r o p e r t i e s . edge / 2 ; r = l + g−>node . o b j e c t −>p r o p e r t i e s . edge ; t = g−>node . o b j e c t −>p r o p e r t i e s . b a r i c e n t e r [ 1 ] + g−>node . o b j e c t −> properties . height / 2; b = t − g−>node . o b j e c t −>p r o p e r t i e s . h e i g h t ; i f ( ( g−>un mouse [ 0 ] >= l ) && ( g−>un mouse [ 0 ] <= r ) && ( g−>un mouse [ 1 ] <= t ) && ( g−>un mouse [ 1 ] >= b ) && ( g−>un mouse [ 2 ] > Z LIMIT ) ) c p t = PICK MOUSEIN ; else c p t = PICK MOUSEOUT; } Then we can have different situations: if the mouse is inside and we have not yet signaled the event, then we invoke the mouseout event for the object that was previously ”mousein” (if existing), then we signal the mousein event for the current object. i f ( event handler ) { switch ( c p t ) { case PICK MOUSEIN : // have we a l r e a d y s i g n a l e d mouse i n f o r t h i s o b j e c t ? a r e we s t i l l inside that object ? i f ( r o o t −>i n i d != g−>i d ) { // s i g n a l t h e mouse o u t t o o t h e r o b j e c t i f needed i f ( r o o t −>i n i d != −1) { e v e n t h a n d l e r ( r o o t , tm , r o o t −>i n p , h a n d l e r d a t a , PICK MOUSEOUT) ; } e v e n t h a n d l e r ( r o o t , tm , g , h a n d l e r d a t a , c p t ) ; // remember t h a t now t h e mouse i s i n s i d e c u r r e n t o b j e c t r o o t −>i n i d = g−>i d ; r o o t −>i n p = g ; } 1 For semplicity I have implemented only the picking for rectangles 7
  • 8. If no mouse button is down at the moment but it was down for an object in the previous picking control, then we signal the mouseup event. // c h e c k f o r c l i c k i f ( ms−>b == −1) { i f ( r o o t −>down id == g−>i d ) e v e n t h a n d l e r ( r o o t , tm , g , h a n d l e r d a t a , PICK MOUSEUP) ; r o o t −>down id = −1; r o o t −>down p = NULL; } If instead the mouse button is down, we save this information into the root node. i f ( ms−>b == 0 ) { i f ( r o o t −>down id == −1) { r o o t −>down id = g−>i d ; r o o t −>down p = g ; } } break ; If we have discovered a mouseout, we signal it only if the mouse was previously inside the object. case PICK MOUSEOUT: // i f we were i n s i d e c u r r e n t o b j e c t and we a r e g o i n g o u t t h e n we s i g n a l t h e mouse o u t i f ( r o o t −>i n i d == g−>i d ) { e v e n t h a n d l e r ( r o o t , tm , g , h a n d l e r d a t a , c p t ) ; // remember t h a t now mouse i s o u t s i d e c u r r e n t o b j e c t r o o t −>i n i d = −1; r o o t −>i n p = NULL; } i f ( ms−>b == −1 && r o o t −>down id == g−>i d ) { r o o t −>down id = −1; r o o t −>down p = NULL; } break ; default : break ; } } } } We now move on and analyze the case in which the node is a curve. Here we have five subcases, one per curve type: since they are all quite similar I will go through only the Hermite. To draw it we cycle through all the composite curves and we check for continuity: if set to C0, then each curve has its own derivatives, otherwise the derivative on the first point is set as the derivative on the second point of the previous curve. The single hermite curve is draw by using the curve hermite spline procedure. Moreover, to give a graphic feedback to let the user know which composite curve is currently selected, I painted the latter in yellow. Then the manage curve function is used to drag the points that have been selected: its code is shown in Listing 3. The function checks first of all if the user has released the mouse button 8
  • 9. and if the shift key is not pressed: in that case the function removes all the points from the selection. Then, if there is at least one point selected and the mouse button is pressed, it moves all the selected point by an amount which is determined by the difference between the previous mouse state and the current. After that, the hermite control function, draws control points and derivatives and performs picking: picking on points is easy and it is obtained by measuring the distance between point and mouse cursor with usual euclidean distance. If the function detects that a control point is clicked, it is added to the selection so that the manage curve function can move it in case of dragging. i f ( g−>t y p e == NODE CURVE) { c u r v e t ∗ c = g−>node . c u r v e ; i n t j , base , n p o i n t s ; f l o a t i n g t ∗∗ c u r v e p o i n t s ; f l o a t i n g t tmp [ 3 ] ; floating t color [ 4 ] ; switch ( c−>t y p e ) { case CURVE HERMITE: f o r ( i = 0 ; i < c−>n c o m p o s i t e ; i ++) { i f ( i == c−>s e l e c t e d c o m p o s i t e ) v e c t o r s e t 4 ( c o l o r , CCS R , CCS G , CCS B , CCS A) ; else v e c t o r s e t 4 ( c o l o r , CCU R, CCU G, CCU B, CCU A) ; switch ( c−>c o n t i n u i t y ) { case 0 : c u r v e h e r m i t e s p l i n e ( c−>p o i n t s [ i ] . c , c−>p o i n t s [ i + 1 ] . c , c−> d e r i v a t i v e s [ i ∗ 2 ] . d e r i v , c−>d e r i v a t i v e s [ i ∗ 2 + 1 ] . d e r i v , tm , color ) ; break ; case 1 : i f ( i == 0 ) c u r v e h e r m i t e s p l i n e ( c−>p o i n t s [ i ] . c , c−>p o i n t s [ i + 1 ] . c , c−> d e r i v a t i v e s [ i ∗ 2 ] . d e r i v , c−>d e r i v a t i v e s [ i ∗ 2 + 1 ] . deriv , tm , c o l o r ) ; else c u r v e h e r m i t e s p l i n e ( c−>p o i n t s [ i ] . c , c−>p o i n t s [ i + 1 ] . c , c−> d e r i v a t i v e s [ i ∗ 2 − 1 ] . d e r i v , c−>d e r i v a t i v e s [ 2 ∗ i + 1]. d e r i v , tm , c o l o r ) ; break ; } } manage curve ( g , ms , pms , s e l ) ; h e r m i t e c o n t r o l ( g , ms , pms , s e l , tm ) ; break ; case CURVE BEZIER : 9
  • 10. [...] default : break ; } i f ( g−>p i c k i n g ) { u n p r o j e c t m o u s e ( tm , g−>un mouse , ms , inv m ) ; u n p r o j e c t m o u s e ( tm , g−>pun mouse , pms , inv m ) ; } } Finally, the mouse cursor is unprojected and saved into the node and will be used at the next rendering cycle. The reason why I unproject the mouse after the rendering is that for retrieving the pixel the mouse is over I use the OpenGL glReadPixels function: as the name suggests, it reads a pixel from the viewport (in particular using the depth buffer), but clearly the pixel must firstly be rendered. void manage curve ( g r a p h n o d e t ∗g , mouse t ∗ms , mouse t ∗pms , s e l e c t i o n t ∗ s e l ) { i f ( ms−>b == −1 && pms−>b == 0 && ! ( ms−>k & KEY SHIFT) ) { unselect selection ( sel ) ; selection empty ( sel ) ; } i f ( s e l e c t i o n s i z e ( s e l ) != 0 && ms−>b == 0 ) { m o v e s e l e c t i o n ( s e l , g−>pun mouse [ 0 ] − g−>un mouse [ 0 ] , g−>pun mouse [ 1 ] − g −>un mouse [ 1 ] ) ; } } Listing 3: manage curve function As said before, the last type of nodes the scenegraph handles is text: in this case, I have developed a function that prints a text on the screen. Its parameters are center position, the size of the character, a char map and the text to print. The char map is an empty mesh containing a texture from which retrieve the images of the characters: it can be considered as the font to be used. The one I have used is shown in Figure 4. i f ( g−>t y p e == NODE TEXT) { p r i n t c e n t e r e d s t r i n g f m t ( g−>node . t e x t −>x , g−>node . t e x t −>y , g−>node . t e x t −> s i z e , g−>node . t e x t −>char map , tm , ”%s ” , g−>node . t e x t −>t e x t ) ; } 10
  • 11. Figure 4: The texture used to print text on the screen The last part of the scenegraph rendering function is the recursive invocation: for each child we invoke the function on it. If the current node is a transformation matrix, then the new matrix is passed to the procedure, as well as its inverse. f o r ( c h i l d = 0 ; c h i l d < g−>n c h i l d r e n ; c h i l d ++) i f ( g−>t y p e == NODE MATRIX) s c e n e g r a p h r e n d e r ( r o o t , g−>c h i l d r e n [ c h i l d ] , ntm−>matrix , r e n d e r c a l l b a c k , c a l l b a c k d a t a , e v e n t h a n d l e r , h a n d l e r d a t a , ms , pms , s e l , g−>i n v m a t r i x ) ; else s c e n e g r a p h r e n d e r ( r o o t , g−>c h i l d r e n [ c h i l d ] , tm , r e n d e r c a l l b a c k , c a l l b a c k d a t a , e v e n t h a n d l e r , h a n d l e r d a t a , ms , pms , s e l , inv m ) ; i f ( g−>t y p e == NODE MATRIX) f r e e m a t r i x t ( ntm ) ; } 4 Usage and examples The project comes with the source code and a makefile that can be used to build it on Mac OS X, Windows and Linux. By typing the make command, the application curvemanipulator is generated. It is possible to run it in three different ways: • normal way: type ./curvemanipulator; • with background image: type ./curvemanipulator picture.bmp and the application will show the image in background so that the user can try to draw on it using the program. The image must be a bmp file and if your graphic card is quite old it must be a square with a size that is equal to a power of two (e.g. 512x512); • with sampled points: type ./curvemanipulator points.txt <points per composite curve> [curve to draw] and the application will use the points in the file as points for the curves. 11
  • 12. The second parameter specifies how many points per curve should be used (excepts for the hermite spline), while the third, if specified, tells the application to draw only a single type of curve: 0 will draw only the Hermite, 1 the Bezier, 2 the Interpolating Spline, 3 the B-Spline and 4 the Lagrangian. This is useful if you want to draw a curve with a high number of points to reduce the computational time. The file with sampled points must contain a point per line with comma separated x and y coordinates. The first case is shown in Figure 1. As said before, on the left you have the controls and in center the curve. Let’s see how to use it. The upper part lets you chose between the different type of curves and the currently selected curve is printed under the buttons. Then you have the ”Add curve” button, which lets you insert a new curve, to be added to the set of composite curves. As soon as you click it, the text ”Selected mode: Add curve” will appear on top of the screen, telling you that the modality is active and will remain like that until you click another button or you press the ESC key. In this way you can insert multiple curves with successive clicks on the screen. If you have a composite curve, then you can select a single piece by using the + and - buttons under the ”Curve:” text: as soon as you change the selected piece, the number will change and the curve will be drawn in yellow. After that you can add a point to the currently selected curve by clicking the ”Add point” button: this action will show in the upper part of the screen the ”Selected mode: Add point” and from now on you will be able to add multiple points by clicking on the screen2 . At any time, you can drag any point you see on the screen, in order to modify the aspect of the curve. If you want to drag more than one point at a time, just keep the shift key pressed and select the points by clicking on them: after that, drag a point to modify them all. If you use Hermite, Bezier or Interpolating Spline, then you can set the type of continuity between composite curves by clicking the buttons ”C0” and ”C1”: in particular, if you have selected curve number 3, then the continuity you have chosen will be set between curves 3 and 4. For a better understanding of which derivatives belongs to which curve, the derivative of the first point is colored in green, while the derivative on last point is colored in light blue. Finally, if you use a B-Spline, you will be able to change the K value for each curve by selecting it and then use the + and - buttons under the ”B-Spline K:” text. If you specify a background image then a button is added to the menu, to let the user hide and show it. The result is shown in Figure 5 and in Figure 6, where I tryed to draw over an A4 Avant designed by Audi. 2 Since Hermite splines have only two points per curve, the program will let you only add curves in that case 12
  • 13. Figure 5: The application running with the background image visible Figure 6: The application running with the background image hidden The result of the last modality is shown in Figure 7: the sampling file contained the points of the a race circuit shown in Figure 8. 13
  • 14. Figure 7: The application running with the sampling option and displaying a B-Spline curve Figure 8: The race circuit that I have sampled A Appendix - Brief graphic library description In the appendix I give a very brief description of what kind functionalities the graphic library provides, by listing the header files. • gl 2d geometry.h: contains functionalities to create 2D figure meshes, such as polygons or 14
  • 15. circles and to generate the convex hull of a given set of points via the Graham algorithm; • gl 3d geometry.h: contains functionalities to create 3D solid meshes, like cubes, regular pyramids, cones, prysms cylinders and spheres. The sphere can be drawn with different resolutions (i.e. slices and stacks). For each solid, there is the possibility to automatically calculate normals for lights. I have developed three algorithms, one which calculates face normals, one which calculates vertex normals by averaging adjacent face normals (useful if you want to see a smooth surface) and one which uses both and selects between vertex normals and face normals by angle discrimination; • gl curve.h: contains functionalities to draw the curves I have mentioned in this report; • gl material.h: contains some simple functionalities to load textures into a mesh, manage properties of materials and a nice procedure to map a texture onto a sphere; • gl matrix.h: contains a bunch of matematical functionalities, to create matrices, vectors, copy and duplicate them, perform operations such as sums, subtractions and multiplica- tions, perform dot products, normalize vectors, determination of CW and CCW direction between two vectors, matrix determinant, matrix inverse and linear system solving; • gl mempool.h: contains functionalities to get pre-allocated matrices and vectors when needed, without allocating and deallocating and wasting CPU resources. This is useful for example for the rendering function, where temporary vectors and matrices are needed; • gl mesh.h: contains functionalities to create meshes and faces for meshes, as well as cal- culation of normals; • gl mouse.h: contains functionalities for retrieving current mouse state; • gl physic law.h: contains functionalities useful for physics simulation: you can evaluate, integrate and derivate polynoms, compute motion law and gravity force; • gl physic.h: contains functionalities for simple physics, that is sphere to plane collision and sphere to sphere collision; • gl printf.h: contains functionalities for drawing texts, in a printf way; • gl rendering.h: contains only a function to perform rendering of a mesh. The function is quite complicated, because it performs OpenGL attributes activation and deactivation given mesh properties; • gl scenegraph.h: contains the functionalities to manage a scenegraph, that is create it, add nodes, add children, render the graph, etc. . . ; • gl selection.h: contains functionalities to manage multiple object selection, so add an object to the selection, retrieve current selected objects, etc. . . ; • gl tga.h: contains a function which loads a tga image from a file; • gl transformation.h: contains functionalities to performs transformations, such as rota- tions or translations; • gl util.h: contains functionalities for dinamic arrays, elapsed time calculation (in mi- croseconds, useful for physics) and a modified version of the quick sort contained in the standard C library. 15