Рубрики

drawing

Tutorial for drawing with an alternative method

By default, the control’s drawing code displays a square and the text PolyCtl. In this step, you will change the code to display something more interesting. The following tasks are involved:


Drawing 2D stuff

As you learnt in the previous tutorials, SFML’s window module provides an easy way to open an OpenGL window and handle its events, but it doesn’t help when it comes to drawing something. The only option which is left to you is to use the powerful, yet complex and low level OpenGL API.

Fortunately, SFML provides a graphics module which will help you draw 2D entities in a much simpler way than with OpenGL.

The drawing window

To draw the entities provided by the graphics module, you must use a specialized window class: sf::RenderWindow . This class is derived from sf::Window , and inherits all its functions. Everything that you’ve learnt about sf::Window (creation, event handling, controlling the framerate, mixing with OpenGL, etc.) is applicable to sf::RenderWindow as well.

On top of that, sf::RenderWindow adds high-level functions to help you draw things easily. In this tutorial we’ll focus on two of these functions: clear and draw . They are as simple as their name implies: clear clears the whole window with the chosen color, and draw draws whatever object you pass to it.

Here is what a typical main loop looks like with a render window:

#include int main() < // create the window sf::RenderWindow window(sf::VideoMode(800, 600), "My window"); // run the program as long as the window is open while (window.isOpen()) < // check all the window's events that were triggered since the last iteration of the loop sf::Event event; while (window.pollEvent(event)) < // "close requested" event: we close the window if (event.type == sf::Event::Closed) window.close(); >// clear the window with black color window.clear(sf::Color::Black); // draw everything here. // window.draw(. ); // end the current frame window.display(); > return 0; > 

Calling clear before drawing anything is mandatory, otherwise the contents from previous frames will be present behind anything you draw. The only exception is when you cover the entire window with what you draw, so that no pixel is not drawn to. In this case you can avoid calling clear (although it won’t have a noticeable impact on performance).

Calling display is also mandatory, it takes what was drawn since the last call to display and displays it on the window. Indeed, things are not drawn directly to the window, but to a hidden buffer. This buffer is then copied to the window when you call display — this is called double-buffering.

This clear/draw/display cycle is the only good way to draw things. Don’t try other strategies, such as keeping pixels from the previous frame, “erasing” pixels, or drawing once and calling display multiple times. You’ll get strange results due to double-buffering.
Modern graphics hardware and APIs are really made for repeated clear/draw/display cycles where everything is completely refreshed at each iteration of the main loop. Don’t be scared to draw 1000 sprites 60 times per second, you’re far below the millions of triangles that your computer can handle.


What can I draw now?

Now that you have a main loop which is ready to draw, let’s see what, and how, you can actually draw there.

SFML provides four kinds of drawable entities: three of them are ready to be used (sprites, text and shapes), the last one is the building block that will help you create your own drawable entities (vertex arrays).

Although they share some common properties, each of these entities come with their own nuances and are therefore explained in dedicated tutorials:

  • Sprite tutorial
  • Text tutorial
  • Shape tutorial
  • Vertex array tutorial

How to Draw the Head from Any Angle Part 1

In this tutorial I’ll attempt to summarize Andrew Loomis’s approach to drawing the head. It’s a great method for drawing the head from various angles.

If you like the video, please share with your friends.

For more video tutorials visit www.proko.com and subscribe to the newsletter

My videos summarize this book. The full version is a great resource.

The Basic Forms

To draw the head from any angle you must first understand its basic structure. Look past all the distracting details and visualize the underlying forms. This ability to simplify can be applied to the features of the face, but when starting the drawing you could look even further. Ignore even the features and simplify to the most basic form of the head. I use a method taught by Andrew Loomis in his book, “Drawing the Head & Hands”.

The head deconstructed into its basic forms, is a sphere as the cranium and a block as the jaw and cheek bones.

A Sphere as the Cranium

The sides of the head are flat, so we can slice off a piece from both sides of the ball. From profile, this plane will be a perfect circle, but when drawing it from any other angle, it will appear to be an oval because of perspective. Divide this oval into quadrants. The vertical line represents the beginning of the jaw. The horizontal line represents the brow line. The top and bottom of the oval help you find the hair line and the bottom of the nose.

A Block as the Jaw and Cheek Bones

Attach the shape of the jaw. The top will start at the brow line and the back will start at the center of the oval. This is a 3-D volume with a front plane, side planes, and bottom plane (bottom plane is seen from some angles).

Constructing From Any Angle

Step 1 – Determine the angle of the ball

The angle of the head is established at the very beginning of the drawing with the ball. All three axes must be addressed:

X Axis – The up and down tilt is established by the angles of the horizontal and vertical lines in the oval. Also, on extreme up tilts and down tilts, the thirds will be foreshortened because of perspective.

Y Axis – The direction the head is turning (left or right) is established by the width of the oval. As the head turns towards you, you can see more of the front of the face and less of the side, so the oval representing the side will get narrower. Similarly, when the head turns away from you, more of the side plane is revealed and the oval will appear wider.

Z Axis – The twist is established by the angle of the center line, the angle of the oval and the placement of the oval on the ball.

Step 2 – Find the thirds

After establishing the angle of the ball, divide the face into thirds. The distance between the hairline and brow-line should be the same as the distance between the brow-line and bottom of the nose. Add that same distance to find the chin. Notice how the hairline and nose-line align with the top and bottom bottom of the oval when wrapped around the face. Imagine the head as a box. The thirds must be wrapped around the side plane and front plane.

Step 3 – Add the jaw

A common mistake at this point is to make the jaw too long in comparison to the ball. Make sure to measure your thirds correctly and that they relate correctly to the ball. Notice how the shape of the jaw changes from various angles.

Step 4 – Add the features

ith this basic structure properly established, it becomes much easier to add the features in the right place. Check back for more information about specific features in a later post.

Practice inventing the head from all possible angles. Get a sketchbook and fill an entire page with little heads. When drawing without reference photos, you quickly realize where your weaknesses are because you can’t rely on copying. You can only work with what you know.


Adding a Method to Calculate the Polygon Points

Add a method, called CalcPoints , that will calculate the coordinates of the points that make up the perimeter of the polygon. These calculations will be based on the RECT variable that is passed into the function.

To add the CalcPoints method

  1. Add the declaration of CalcPoints to the IPolyCtl public section of the CPolyCtl class in PolyCtl.h:
void CalcPoints(const RECT& rc); 

The last part of the public section of the CPolyCtl class will look like this:

 void FinalRelease() < >public: void CalcPoints(const RECT& rc); 
void CPolyCtl::CalcPoints(const RECT& rc) < const double pi = 3.14159265358979; POINT ptCenter; double dblRadiusx = (rc.right - rc.left) / 2; double dblRadiusy = (rc.bottom - rc.top) / 2; double dblAngle = 3 * pi / 2; // Start at the top double dblDiff = 2 * pi / m_nSides; // Angle each side will make ptCenter.x = (rc.left + rc.right) / 2; ptCenter.y = (rc.top + rc.bottom) / 2; // Calculate the points for each side for (int i = 0; i < m_nSides; i++) < m_arrPoint[i].x = (long)(dblRadiusx * cos(dblAngle) + ptCenter.x + 0.5); m_arrPoint[i].y = (long)(dblRadiusy * sin(dblAngle) + ptCenter.y + 0.5); dblAngle += dblDiff; >> 

Initializing the Fill Color

Initialize m_clrFillColor with a default color.

To initialize the fill color

  1. Use green as the default color by adding this line to the CPolyCtl constructor in PolyCtl.h:
m_clrFillColor = RGB(0, 0xFF, 0); 

The constructor now looks like this:

CPolyCtl()

Building and Testing the Control

Rebuild the control. Make sure the PolyCtl.htm file is closed if it is still open, and then click Build Polygon on the Build menu. You could view the control once again from the PolyCtl.htm page, but this time use the ActiveX Control Test Container.

To use the ActiveX Control Test Container

  1. Build and start the ActiveX Control Test Container. The TSTCON Sample: ActiveX Control Test Container can be found on GitHub.

Note For errors involving ATL::CW2AEX , in Script.Cpp, replace line TRACE( “XActiveScriptSite::GetItemInfo( %s )n”, pszNameT ); with TRACE( “XActiveScriptSite::GetItemInfo( %s )n”, pszNameT.m_psz ); , and line TRACE( “Source Text: %sn”, COLE2CT( bstrSourceLineText ) ); with TRACE( “Source Text: %sn”, bstrSourceLineText ); .
For errors involving HMONITOR , open StdAfx.h in the TCProps project and replace:

#ifndef WINVER #define WINVER 0x0400 #endif 

with

#ifndef WINVER #define WINVER 0x0500 #define _WIN32_WINNT 0x0500 #endif 

Try changing the number of sides by following the next procedure. To modify properties on a dual interface from within Test Container, use Invoke Methods.

To modify a control’s property from within the Test Container

  1. In Test Container, click Invoke Methods on the Control menu. The Invoke Method dialog box is displayed.
  2. Select the PropPut version of the Sides property from the Method Name drop-down list box.
  3. Type 5 in the Parameter Value box, click Set Value, and click Invoke.

Note that the control does not change. Although you changed the number of sides internally by setting the m_nSides variable, this did not cause the control to repaint. If you switch to another application and then switch back to Test Container, you will find that the control has repainted and has the correct number of sides.

To correct this problem, add a call to the FireViewChange function, defined in IViewObjectExImpl , after you set the number of sides. If the control is running in its own window, FireViewChange will call the InvalidateRect method directly. If the control is running windowless, the InvalidateRect method will be called on the container’s site interface. This forces the control to repaint itself.

To add a call to FireViewChange

  1. Update PolyCtl.cpp by adding the call to FireViewChange to the put_Sides method. When you have finished, the put_Sides method should look like this:
STDMETHODIMP CPolyCtl::put_Sides(short newVal) < if (2 < newVal && newVal < 101) < m_nSides = newVal; FireViewChange(); return S_OK; >else < return Error(_T("Shape must have between 3 and 100 sides")); >> 

After adding FireViewChange , rebuild and try the control again in the ActiveX Control Test Container. This time when you change the number of sides and click Invoke , you should see the control change immediately.

In the next step, you will add an event.

Colin Wynn
the authorColin Wynn

Leave a Reply