Common resources for graphical widgets
PtWidget --> PtBasic --> PtGraphic
For more information, see the diagram of the widget hierarchy.
None - not normally instantiated.
<photon/PtGraphic.h>
The PtGraphic superclass provides the common resources used by all graphic widgets. Graphical widgets provide attributes for color, fills, patterns, line thickness, joins, and much more.
When you want to incorporate simple, static drawings in your interface, use the widgets subclassed to PtGraphic.
Don't call the Pg... drawing primitives directly, as they won't be redrawn when widgets are damaged and repaired. If you need to draw something that can't be done with these widgets, do your drawing inside a PtRaw widget. For more information, see the Raw Drawing and Animation chapter of the Photon Programmer's Guide. |
Each graphical widget draws a single graphical primitive. The provided primitives are:
You can build up a drawing by creating one widget for each of the graphical primitives you need. You should create the widgets from back to front, so that their stacking order is correct and they consequently appear correct when drawn to the screen. You should also place the widgets in a container widget (such as PtPane).
All the vector graphics widgets are defined by an origin and a set of coordinates.
The origin is an offset from the graphic widget's origin, which is used as the origin of the coordinate space for the graphics primitive.
The set of coordinates is implemented as an array of PhPoint_t structures. Each coordinate specifies a vertex or control point of the primitive. Here's a code fragment to illustrate:
PhPoint_t points[]= { { 0, 0}, {40,40}, {40, 5}, { 0, 0}}; PtSetArg(&argt[0], Pt_ARG_POINTS, points, 4 ) ; PtCreateWidget( PtPolygon, parent, 1, argt ) ;
Each of the coordinates defining the primitive are relative to the graphics widget's origin.
The graphics widget's origin is specified by the Pt_ARG_ORIGIN resource. This resource takes a PhPoint_t as a value. This point is the origin of the primitive's coordinate space, in pixels, relative to the widget's origin.
The set of points is specified using the array resource Pt_ARG_POINTS. The number of points required in the array - and the interpretation of those points - depends on the type of graphics primitive being defined.
When drawing the points for the primitive or the curve, the widget uses its associated line drawing attributes to determine the color and drawing style to use. These line drawing attributes are specified using the following resources defined on the PtGraphic widget class:
For explanations of the values you may specify for these resources, see the documentation for PgSetStrokeCap() and PgSetStrokeJoin().
Most of the graphics primitives can also be configured to draw a closed curve. When this is done, the interior of the primitive may also be filled using the fill color and style associated with the widget to determine. These fill attributes are specified using the following resources defined by the PtBasic widget class:
To create a drawing composed of several graphical widgets, you should first create a container-class widget to place the widgets in. Normally, you set the border width and margins of the container to zero.
At this point, you may create the graphics primitives and position them within the container widget. You may choose to position and size the graphics widgets in one of several ways, but the simplest is to place them all at position (0,0), which is adequate for most purposes.
The origin resource, Pt_ARG_ORIGIN, provides a reference point or datum for all the primitives added to the container-class widget. This resource defines a coordinate space for each graphic allowing maximum flexibility for positioning and sizing primitives.
For example, the origin lets you create a library of symbols defined in their own coordinate space. You can then use the origin to place the symbol anywhere in the drawing, and the widget itself doesn't need to be positioned. The only thing you have to do then is scale the symbol itself.
If you know the overall dimensions of the drawing, you may want to explicitly set the dimensions of the graphics widgets as you create them. You might also want to set the dimensions if you want to have a standard symbol clipped to obtain a new shape. The figure below illustrates how a five-pointed star will be drawn when Pt_ARG_ORIGIN is set to (50,50) and the dimensions of the widget are fixed at 101 x 101 pixels. The star will be constructed from this set of points:
{(95, -31), (-58, 81), (0, -100), (58, 81), (-95, -31)}
A five-pointed star before clipping.
Note the resulting bounding box of the widget as well as the origin of the polygon's coordinate space, which is fixed at position (50,50) of the widget's canvas.
The star after clipping.
If you don't need special clipping, however, you should use the graphics widget's resize policy or another geometry management mechanism to determine the widget's size. The default resize policy for graphics is Pt_RESIZE_...AS_REQUIRED for both the width and height. To fix the dimensions of the widget as shown in the case above, you have to override the default resize policy. For more information, see the Pt_ARG_RESIZE_FLAGS resource in the description of PtWidget.
Occasionally you'll want to group together a number of graphical primitives and define them as a group or unit. All the primitives within the group are defined in terms of their common origin, and the unit may be repositioned or resized without affecting the group's components. You can do this simply by creating a container-class widget and placing the widgets for the graphical primitives within it.
Using this technique, you can create a complex drawing composed of a number of sub-drawings. There's no limit to the number of drawings that can be nested in this way. The only limiting factor here is the number of resources consumed. Recall that each container widget requires a Photon region, which is a Photon server resource that consumes server memory. In a system with constrained memory, many deeply nested drawings may consume excess amounts of server resources. |
The advantage of vector graphics is they use less space to define and are easy to scale. They may be scaled independently in the x and y directions.
To rescale any of your graphics primitives, you must multiply each control point and the origin for the primitive by x and y scale factors. The Pt_CB_RESCALE callback makes this easier.
This callback is invoked whenever the primitive's size is redefined, whether by a PtSetResources() call or directly by the parent.
The callback should be attached to an application function that scales each point of the widget and its origin.
You can thus rescale an entire drawing by creating a resize function for the container that implements the drawing. The resize function repositions each of the children and multiplies their dimensions by the scale factors for x and y. Graphics primitives will automatically be scaled because their Pt_CB_RESCALE callbacks will be invoked.
If a container is used to create sub-drawings, the resize function must be called for each container in order for the rescaling to be propagated through all the sub-drawings. This can be handled by attaching the same resize callback that was used for the drawing as the resize callback for each sub-drawing.
To scale drawings correctly in this manner, follow these two steps:
A resize function is required for each container used for a drawing or sub-drawing.
This resize function determines the x and y scale factors to apply to the drawing. It looks at the new extents for the drawing widget to determine the ratio of the new width and height compared to the widget's current width and height. It can use these as the scale factor, or it may choose one of these values - presumably the smaller of the two - to use as the scale factor for both width and height in order to preserve the aspect ratio of the drawing (or sub-drawing).
The resize function then passes over each of its children and scales the x and y members of the Pt_ARG_POS and Pt_ARG_DIM resources by the x and y scale factors. In order for children to be resized correctly, you must change their resize policy to Pt_NEVER for both the width and height. You can do this in your resize function or after the graphics are realized.
The code below shows a resize function that performs the actions indicated above. The function takes a PhDim_t pointer as client_data indicating the old extents of the widget. These dimensions are initialized when the widget is realized and updated whenever it's resized.
void resize(PtWidget_t *wgt, void *client_data, PtCallbackInfo_t *info) { PhPoint_t *pos, newpos; PhDim_t *dim, newdim; PtArg_t arg[2]; PhDim_t *old_extents = (PhDim_t *)client_data; PhRect_t *extents = (PhRect_t *)info->cbdata; int width = extents->lr.x - extents->ul.x + 1; int height = extents->lr.y - extents->ul.y + 1; double xs = width; double ys = height; PtWidget_t *child = PtWidgetChildFront(wgt); if (old_extents) { xs = xs / old_extents->w; ys = ys / old_extents->h; while (child != NULL) { PtSetArg(&arg[0], Pt_ARG_POS, &pos, 0); PtSetArg(&arg[1], Pt_ARG_DIM, &dim, 0); PtGetResources(child, 2, arg); if (dim && pos) { newpos = *pos; newpos.x *= xs; newpos.y *= ys; newdim = *dim; newdim.w *= xs; newdim.h *= ys; PtSetArg(&arg[0], Pt_ARG_DIM, &newdim, 0); PtSetArg(&arg[1], Pt_ARG_POS, &newpos, 0); PtSetResources(child, 2, arg); } child = PtWidgetBrotherBehind(child); } } }
Calling PtAddCallback() after the widget is created attaches the resize function to the resize callback. When the widget is created, a closure is created for it and the address of closure is associated with the callback in the call to PtAddCallback(). This closure is a dynamically allocated data structure associated with one of more of a widget's callbacks; the closure contains state information for use by those callbacks. In this case, the closure is a PhDim_t that maintains the widget's current dimensions. The closure is initialized by a Pt_CB_REALIZED callback that sets the dimensions to those of the widget.
Any sub-drawings created using container-class widgets are also attached to the same resize callback function and require a closure of their own. The resize callback will be invoked on the sub-drawing when the parent resizes the sub-drawing within the resize callback invoked on the parent.
A scaling function is required for all the graphics primitives. This function calculates x and y scale factors based on the extents passed in the call data. It then scales the Pt_ARG_ORIGIN resource and each point in the Pt_ARG_POINTS array by the scale factors. the scaling function doesn't try to modify the scale factor to preserve a particular aspect ratio because this has already been done by its parent's resize function. The scaling function is attached to the Pt_CB_RESCALE callback of each graphics widget after the widget is created.
The scaling function must be called with new scale factors in both the x and y directions. The simplest way to get the scale factors is to get the container widget's old and new dimensions. The scale factors can then be expressed as the ratio between the old and new dimensions.
The sample callback function below shows a simple function for rescaling graphics primitives. As with the resize function given previously, the widget's current dimensions are obtained from the closure passed to the callback function as client_data.
void rescale(PtWidget_t *wgt, void *client_data, PtCallbackInfo_t *info) { PhDim_t *old_extents = (PhDim_t *)client_data; PhArea_t *area = (PhArea_t *)info->cbdata; PhDim_t *dim; PhPoint_t *points; PhPoint_t neworg, *org; short *npoints; PtArg_t arg[3]; PtSetArg(&arg[0], Pt_ARG_DIM, &dim, 0); PtSetArg(&arg[1], Pt_ARG_POINTS, &points, (ulong_t)&npoints); PtSetArg(&arg[2], Pt_ARG_ORIGIN, &org, 0); PtGetResources(wgt, 3, arg); if (old_extents && dim && points) { double xs = dim->w; double ys = dim->h; PhPoint_t *newpoints = (PhPoint_t *)alloca(*npoints * sizeof(PhPoint_t)); int i; xs = xs / old_extents->w; ys = ys / old_extents->h; neworg.x = org->x * xs; neworg.y = org->y * ys; for (i = 0; i < *npoints; i++) { newpoints[i].x = points[i].x * xs; newpoints[i].y = points[i].y * ys; } PtSetArg(&arg[0], Pt_ARG_POINTS, newpoints, *npoints); PtSetArg(&arg[1], Pt_ARG_ORIGIN, &neworg, 0); PtSetResources(wgt, 2, arg); } }
Taken together with the resizing function given previously, this can be used to create a rescalable picture as in the example below. This example creates a drawing using a PtGroup and populates it with three ellipses and an arc to create a simple happy face.
main(int argc, char *argv[]) { PtAppContext_t app; PtArg_t args[8]; int nargs; PtWidget_t *window, *container_widget, *widget; PhPoint_t face[] = { {0,0}, {100,100} }; PhPoint_t eye[] = { {0,0}, {6,10} }; PhPoint_t mouth[] = { {0,0}, {79,49} }; PhPoint_t org; PhDim_t *dim; void resize(PtWidget_t *, void *, PtCallbackInfo_t *); void rescale(PtWidget_t *, void *, PtCallbackInfo_t *); void realized(PtWidget_t *, void *, PtCallbackInfo_t *); if ((window = PtAppInit(&app, &argc, argv, 0, NULL)) == NULL) exit(EXIT_FAILURE); /* Create a container for the drawing */ nargs = 0; PtSetArg(&args[nargs], Pt_ARG_GROUP_ORIENTATION, Pt_GROUP_ASIS, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_ANCHOR_FLAGS, Pt_LEFT_ANCHORED_LEFT|Pt_RIGHT_ANCHORED_RIGHT| Pt_TOP_ANCHORED_TOP|Pt_BOTTOM_ANCHORED_BOTTOM, Pt_IS_ANCHORED); nargs++; container_widget = PtCreateWidget(PtGroup, window, nargs, args); dim = (PhDim_t *)malloc(sizeof(PhDim_t)); PtAddCallback(container_widget, Pt_CB_REALIZED, realized, dim); PtAddCallback(container_widget, Pt_CB_RESIZE, resize, dim); nargs = 0; dim = (PhDim_t *)malloc(sizeof(PhDim_t)); PtSetArg(&args[nargs], Pt_ARG_POINTS, face, sizeof(face)/sizeof(face[0])); nargs++; PtSetArg(&args[nargs], Pt_ARG_FILL_COLOR, Pg_YELLOW, 0); nargs++; widget = PtCreateWidget(PtEllipse, container_widget, nargs, args); PtAddCallback(widget, Pt_CB_REALIZED, realized, dim); PtAddCallback(widget, Pt_CB_RESCALE, rescale, dim); nargs = 0; dim = (PhDim_t *)malloc(sizeof(PhDim_t)); org.x = 10; org.y = 30; PtSetArg(&args[nargs], Pt_ARG_POINTS, mouth, sizeof(mouth)/sizeof(mouth[0])); nargs++; PtSetArg(&args[nargs], Pt_ARG_ORIGIN, &org, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_ARC_START, 2300, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_ARC_END, 3100, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_ARC_TYPE, Pt_ARC_CHORD, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_FILL_COLOR, Pg_BLACK, 0); nargs++; widget = PtCreateWidget(PtArc, container_widget, nargs, args); PtAddCallback(widget, Pt_CB_REALIZED, realized, dim); PtAddCallback(widget, Pt_CB_RESCALE, rescale, dim); nargs = 0; dim = (PhDim_t *)malloc(sizeof(PhDim_t)); org.x = 27; org.y = 30; PtSetArg(&args[nargs], Pt_ARG_POINTS, eye, sizeof(eye)/sizeof(eye[0])); nargs++; PtSetArg(&args[nargs], Pt_ARG_ORIGIN, &org, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_FILL_COLOR, Pg_BLACK, 0); nargs++; widget = PtCreateWidget(PtEllipse, container_widget, nargs, args); PtAddCallback(widget, Pt_CB_REALIZED, realized, dim); PtAddCallback(widget, Pt_CB_RESCALE, rescale, dim); dim = (PhDim_t *)malloc(sizeof(PhDim_t)); nargs = 0; org.x = 67; PtSetArg(&args[nargs], Pt_ARG_POINTS, eye, sizeof(eye)/sizeof(eye[0])); nargs++; PtSetArg(&args[nargs], Pt_ARG_ORIGIN, &org, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_FILL_COLOR, Pg_BLACK, 0); nargs++; widget = PtCreateWidget(PtEllipse, container_widget, nargs, args); PtAddCallback(widget, Pt_CB_REALIZED, realized, dim); PtAddCallback(widget, Pt_CB_RESCALE, rescale, dim); PtRealizeWidget(window); PtMainLoop(); } void realized(PtWidget_t *wgt, void *client_data, PtCallbackInfo_t *info) { PhDim_t *dim; PhDim_t *size = (PhDim_t *)client_data; PtArg_t arg[2]; info = info; PtSetArg(&arg[0], Pt_ARG_DIM, &dim, 0); PtGetResources(wgt, 1, arg); if (size && dim) *size = *dim; }
Remember to consider the appearance of the drawing if the x and y dimensions are scaled independently in this way. The drawing may look odd if its original aspect ratio isn't preserved. This will be especially true if the drawing contains arcs - circular arcs will become elliptical if the aspect ratio isn't preserved.
You can preserve the aspect ratio of the original drawing by choosing the same scale factor for the x and y dimensions. The scale factor is best determined by:
To do this, calculate the two scale factors and use the minimum of the two:
xs = new.x / old.x; ys = new.x / old.x; s = min(xs, ys);
Then scale the old dimensions by the value s to get the desired new dimensions:
desired.x = s * old.x; desired.y = s * old.x;
Now call the scaling function with the scale factors expressed by the two dimensions, old, and desired.
You can easily modify the resize and scaling callback functions given above to preserve the aspect ratio of all or part of a drawing. To do so, change the closure to a structure that contains the widget dimensions and change the callback functions accordingly.
The closure should also have a flag indicating whether or not the aspect ratio is to be preserved. You could then add the following lines after the calculation of the xs and ys scale factors:
if (old_extents->preserve_aspect) xs = xs < ys ? xs : ys;
The aspect ratio of the entire drawing in our happy face example can be preserved by setting the preserve_aspect flag on the closure for the PtGroup widget alone.
Scaling a graphic in the manner indicated above can introduce small round-off errors in the calculation every time the scaling is performed. After many successive recalculations, the graphic may no longer appear correct.
For this reason, it may be a good idea to keep the original coordinates of the graphical object around so that scaling can be performed from them. Keep the array of control points and the origin for the graphic in normalized integer coordinates. When you must scale the graphic, simply calculate a new origin and coordinates by:
You'll need a way of obtaining the original coordinates inside your callbacks. The simplest way is to have an object associated with each of your graphics widgets. Define a structure, say Graphic_t, for these objects that maintains all the original coordinate data for the graphics widget as well as any additional information you might like to keep about the graphics primitive. This should consist of the graphic control points, the origin, and a position and dimensions for the widget. When you create the graphics widget, store the pointer to this object in the Pt_ARG_USER_DATA resource of the graphics widget.
The rescaling function for graphics widgets must obtain the Graphic_t object associated with the widget and must calculate a new origin and points based on the widget's new dimensions and the original coordinate data.
The resizing function should obtain the Graphic_t object for each child of the container and calculate a new position and dimension for that child based on its original coordinate data and the container's scaling factor.
The following example illustrates how our happy face can be implemented using this strategy:
#include <Pt.h> typedef struct graphic_str { PhPoint_t pos; PhDim_t dim; PhPoint_t org; PhPoint_t *points; int n; } Graphic; static Graphic *widget_to_graphic(PtWidget_t *w) { PtArg_t arg[1]; void *data; Graphic *graphic; PtSetArg(&arg[0], Pt_ARG_USER_DATA, &data, 0); PtGetResources(w, 1, arg); graphic = data ? *(Graphic **)data : NULL; return graphic; } int rescale_cb(PtWidget_t *w, void *client_data, PtCallbackInfo_t *info) { PtArg_t args[2]; PhDim_t factor; PhDim_t *dim; PhRect_t rect; Graphic *self = widget_to_graphic(w); PhPoint_t org; PhPoint_t *points; int n; if (!self) return ( Pt_CONTINUE ); if (!self->points) return ( Pt_CONTINUE ); points = (PhPoint_t *)alloca(self->n * sizeof(PhPoint_t)); if (!points) return ( Pt_CONTINUE ); PtBasicWidgetCanvas(PtWidgetParent(w), &rect); PtSetArg(&args[0], Pt_ARG_DIM, &dim, 0); PtGetResources(w, 1, args); factor = *dim; for (n = 0; n < self->n; n++) { points[n].x = ( self->points[n].x * factor.w ) / 100 + rect.ul.x; points[n].y = ( self->points[n].y * factor.h ) / 100 + rect.ul.y; } org.x = ( self->org.x * factor.w ) / 100 + rect.ul.x; org.y = ( self->org.y * factor.h ) / 100 + rect.ul.y; PtSetArg(&args[0], Pt_ARG_POINTS, points, self->n); PtSetArg(&args[0], Pt_ARG_ORIGIN, &org, 0); PtSetResources(w, 2, args); return ( Pt_CONTINUE ); } int resize_cb(PtWidget_t *w, void *client_data, PtCallbackInfo_t *info) { PtArg_t args[2]; PhPoint_t pos; PhDim_t dim; PhDim_t *size; PhDim_t factor, nf; PhRect_t rect, prect; PhRect_t *extents = (PhRect_t *)info->cbdata; Graphic *self = widget_to_graphic(w); PtWidget_t *child = PtWidgetChildFront(w); if (!self) return ( Pt_CONTINUE ); PtBasicWidgetCanvas(PtWidgetParent(w), &prect); PtBasicWidgetCanvas(w, &rect); PtSetArg(&args[0], Pt_ARG_DIM, &size, 0); PtGetResources(w, 1, args); nf = *size; factor.w = extents->lr.x - extents->ul.x + 1; factor.h = extents->lr.y - extents->ul.y + 1; pos = self->pos; dim = self->dim; pos.x = ( pos.x * factor.w ) / 100 + prect.ul.x; pos.y = ( pos.y * factor.h ) / 100 + prect.ul.y; /* this is superfluous */ dim.w = ( dim.w * factor.w ) / 100; dim.h = ( dim.h * factor.h ) / 100; PtSetArg(&args[0], Pt_ARG_POS, &pos, 0); PtSetResources(w, 1, args); while (child != NULL) { Graphic *g = widget_to_graphic(child); if (g) { pos = g->pos; dim = g->dim; pos.x = ( pos.x * factor.w ) / 100 + rect.ul.x; pos.y = ( pos.y * factor.h ) / 100 + rect.ul.y; dim.w = ( dim.w * factor.w ) / 100; dim.h = ( dim.h * factor.h ) / 100; PtSetArg(&args[0], Pt_ARG_POS, &pos, 0); PtSetArg(&args[1], Pt_ARG_DIM, &dim, 0); PtSetResources(child, 2, args); } child = PtWidgetBrotherBehind(child); } return ( Pt_CONTINUE ); } int main(int argc, char *argv[]) { PtAppContext_t app; PtArg_t args[8]; int nargs; PhPoint_t dim; PhRect_t offsets = {0, 0, 0, 0}; PtWidget_t *window, *drawing, *widget; static PhPoint_t face_pts[] = { {0,0}, {100,100} }; static PhPoint_t eye_pts[] = { {0,0}, {100,100} }; static PhPoint_t mouth_pts[] = { {0,0}, {100,100} }; Graphic *graphic; Graphic smiley = {{0,0}, {100,100}, {0,0}, NULL, 0}; Graphic face = {{0,0}, {100,100}, {0,0}, &face_pts, sizeof(face_pts)/sizeof(face_pts[0])}; Graphic left_eye = {{27,30}, {7,11}, {0,0}, &eye_pts, sizeof(eye_pts)/sizeof(eye_pts[0])}; Graphic right_eye = {{67,30}, {7,11}, {0,0}, &eye_pts, sizeof(eye_pts)/sizeof(eye_pts[0])}; Graphic mouth = {{10,30}, {80,50}, {0,0}, &mouth_pts, sizeof(mouth_pts)/sizeof(mouth_pts[0])}; dim.x = 200; dim.y = 200; PtSetArg(&args[0], Pt_ARG_DIM, &dim, 0); if ((window = PtAppInit(&app, &argc, argv, 0, NULL)) == NULL) { return EXIT_FAILURE; } graphic = &smiley; nargs = 0; PtSetArg(&args[nargs], Pt_ARG_ANCHOR_FLAGS, Pt_LEFT_ANCHORED_LEFT|Pt_RIGHT_ANCHORED_RIGHT| Pt_TOP_ANCHORED_TOP|Pt_BOTTOM_ANCHORED_BOTTOM, Pt_IS_ANCHORED); nargs++; PtSetArg(&args[nargs], Pt_ARG_ANCHOR_OFFSETS, &offsets, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_GROUP_ORIENTATION, Pt_GROUP_ASIS, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_USER_DATA, &graphic, sizeof (Graphic *)); nargs++; PtSetArg(&args[nargs], Pt_ARG_POS, &smiley.pos, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_DIM, &smiley.dim, 0); nargs++; drawing = PtCreateWidget(PtGroup, window, nargs, args); PtAddCallback(drawing, Pt_CB_RESIZE, resize_cb, NULL); graphic = &face; nargs = 0; PtSetArg(&args[nargs], Pt_ARG_FILL_COLOR, Pg_YELLOW, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_USER_DATA, &graphic, sizeof (Graphic *)); nargs++; PtSetArg(&args[nargs], Pt_ARG_POS, &face.pos, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_DIM, &face.dim, 0); nargs++; widget = PtCreateWidget(PtEllipse, drawing, nargs, args); PtAddCallback(widget, Pt_CB_RESCALE, rescale_cb, NULL); graphic = &mouth; nargs = 0; PtSetArg(&args[nargs], Pt_ARG_ARC_START, 2300, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_ARC_END, 3100, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_ARC_TYPE, Pt_ARC_CHORD, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_FILL_COLOR, Pg_BLACK, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_USER_DATA, &graphic, sizeof (Graphic *)); nargs++; PtSetArg(&args[nargs], Pt_ARG_POS, &mouth.pos, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_DIM, &mouth.dim, 0); nargs++; widget = PtCreateWidget(PtArc, drawing, nargs, args); PtAddCallback(widget, Pt_CB_RESCALE, rescale_cb, NULL); graphic = &left_eye; nargs = 0; PtSetArg(&args[nargs], Pt_ARG_USER_DATA, &graphic, sizeof (Graphic *)); nargs++; PtSetArg(&args[nargs], Pt_ARG_FILL_COLOR, Pg_BLACK, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_POS, &left_eye.pos, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_DIM, &left_eye.dim, 0); nargs++; widget = PtCreateWidget(PtEllipse, drawing, nargs, args); PtAddCallback(widget, Pt_CB_RESCALE, rescale_cb, NULL); graphic = &right_eye; nargs = 0; PtSetArg(&args[nargs], Pt_ARG_USER_DATA, &graphic, sizeof (Graphic *)); nargs++; PtSetArg(&args[nargs], Pt_ARG_FILL_COLOR, Pg_BLACK, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_POS, &right_eye.pos, 0); nargs++; PtSetArg(&args[nargs], Pt_ARG_DIM, &right_eye.dim, 0); nargs++; widget = PtCreateWidget(PtEllipse, drawing, nargs, args); PtAddCallback(widget, Pt_CB_RESCALE, rescale_cb, NULL); PtRealizeWidget(window); PtMainLoop(); return EXIT_SUCCESS; }
It's important to realize the effect that the position, origin, and positional coordinates have when rescaling the widget. This will affect how you choose to use these resources when you create the graphics widgets and how you scale these resources in your resizing and scaling functions.
The sample resizing function shown here modifies the position of each of its children, and the sample scaling function modifies the origin and all the points that define the primitive. This makes the positions of all sub-drawing and primitives scale along with the drawing itself.
Since the widget's origin and position allow independent control over the widget's position in addition to its size, you have maximum flexibility in rescaling drawings. To rescale the drawing's elements without altering their positions, scale the points that define the primitive and leave the position and origin alone.
In general, you should alter the widget's position to affect the position of the bounding box for the primitive that it will be clipped to. Use the origin to translate the primitive so that part of it will be clipped.
You should always scale graphics based on their original set of points. If you don't use the original points, the aspect will be lost after scaling. |
Resource | C type | Pt type | Default |
---|---|---|---|
Pt_ARG_DASH_LIST | char, short | Array | NULL |
Pt_ARG_DASH_SCALE | long | Scalar | 0 |
Pt_ARG_GRAPHIC_FLAGS | char | Flag | 0 |
Pt_ARG_LINE_CAP | unsigned short | Scalar | Pg_BUTT_CAP |
Pt_ARG_LINE_JOIN | unsigned short | Scalar | Pg_MITER_JOIN |
Pt_ARG_LINE_WIDTH | long | Scalar | 0 |
Pt_ARG_ORIGIN | PhPoint_t | Struct | 0,0 |
Pt_ARG_POINTS | PhPoint_t *, short | Array | NULL |
Pt_CB_RESCALE | PtCallback_t * | Link | NULL |
C type | Pt type | Default |
---|---|---|
char, short | Array | NULL |
An array of bytes that describes the on and off bits for stroke operations.
C type | Pt type | Default |
---|---|---|
long | Scalar | 0 |
A scaling factor that's applied to each of the bits in the dash list to determine the number of pixels for each dash. For information on setting this factor, see PgSetStrokeDash() in the Photon Library Reference.
C type | Pt type | Default |
---|---|---|
char | Flag | 0 |
Possible values:
The default setting of this resource is 0; that is, no flags have been set.
C type | Pt type | Default |
---|---|---|
unsigned short | Scalar | Pg_BUTT_CAP |
Defines how the ends of thick lines are drawn; see PgSetStrokeCap(). Possible values:
C type | Pt type | Default |
---|---|---|
unsigned short | Scalar | Pg_MITER_JOIN |
Defines how thick lines are connected; see PgSetStrokeJoin(). Possible values:
C type | Pt type | Default |
---|---|---|
long | Scalar | 0 |
The line width for any graphically based widget.
C type | Pt type | Default |
---|---|---|
PhPoint_t | Struct | 0,0 |
An offset from the upper left corner of the widget's canvas. The graphic will be rendered with its origin at:
(widget position) + (Pt_ARG_ORIGIN)
C type | Pt type | Default |
---|---|---|
PhPoint_t *, short | Array | NULL |
An array of points (PhPoint_t structures) describing the graphic. The number of points required in the array - and the interpretation of those points - depends on the type of graphics primitive being defined.
C type | Pt type | Default |
---|---|---|
PtCallback_t * | Link | NULL |
A list of callbacks that the widget invokes if its Pt_ARG_DIM or Pt_ARG_AREA resources are modified. You can use this callback to rescale the widget.
Each callback is passed a PtCallbackInfo_t structure that contains at least the following members:
These callbacks should return Pt_CONTINUE.
If the widget modifies an inherited resource, the "Default override" column indicates the new value. This modification affects any subclasses of the widget.