GPIGuide - Line and Arc Primitives: Difference between revisions
Created page with "{{DISPLAYTITLE:Line and Arc Primitives}} Line and arc primitives are graphics building blocks for creating pictures that consist of objects such as polygons, circles, fillets, ellipses, and other geometric figures. ==Related Topics== * Presentation spaces and device contexts * Color and mix attributes * Area primitives ==About Line and Arc Primitives== Simple drawing applications use line and arc primitives as drawing tools. They are useful, for example, in spreadshee..." |
No edit summary |
||
Line 1: | Line 1: | ||
{{ | {{IBM-Reprint}} | ||
{{GPIGuide}} | |||
Line and arc primitives are graphics building blocks for creating pictures that consist of objects such as polygons, circles, fillets, ellipses, and other geometric figures. | Line and arc primitives are graphics building blocks for creating pictures that consist of objects such as polygons, circles, fillets, ellipses, and other geometric figures. | ||
Revision as of 03:53, 14 May 2025
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
Line and arc primitives are graphics building blocks for creating pictures that consist of objects such as polygons, circles, fillets, ellipses, and other geometric figures.
Related Topics
- Presentation spaces and device contexts
- Color and mix attributes
- Area primitives
About Line and Arc Primitives
Simple drawing applications use line and arc primitives as drawing tools. They are useful, for example, in spreadsheet applications for constructing pie charts, bar charts, and graphs. Computer-aided-design (CAD) applications combine line and arc primitives to draw complex pictures such as schematic diagrams for electrical wiring, blueprints for building sites, and cross-sectional views of machinery.
Line and arc primitives are two families of primitives that contain many variations of straight lines and curved lines, respectively. They are governed by the attributes found in the LINEBUNDLE data structure.
An application draws line and arc primitives by first calling GpiMove or GpiSetCurrentPosition, either of which sets the current position to a specified starting point. GpiMove ignores the AM_PRESERVE mode, whereas GpiSetCurrentPosition saves the current position if the AM_PRESERVE mode is set. The more sophisticated function, GpiPolyLineDisjoint, contains its own starting point and does not need to be preceded by GpiMove or GpiSetCurrentPosition. Prior to calling a line or arc function, the current position can be determined with GpiQueryCurrentPosition.
Attributes of Line and Arc Primitives
The attributes of line and arc primitives contained in LINEBUNDLE are:
- Line width
- Geometric width
- Line type
- Line end
- Line join
- Line color
- Line mix
The line end and line join attributes apply only to lines drawn in a path.
When an application creates a presentation space, the line and arc attributes are set to the default values shown in the following table.
Line Attribute Default Values
Attribute | Default Value | Function that Redefines Attribute |
---|---|---|
Width | 1.0 | GpiSetLineWidth |
Geometric width | None | GpiSetLineWidthGeom |
Type | LINETYPE_SOLID | GpiSetLineType |
Color | CLR_NEUTRAL | GpiSetAttrs (LBB_COLOR) |
Mix attribute | FM_OVERPAINT | GpiSetAttrs (LBB_MIX_MODE) |
Line Width and Geometric Width
The operating system defines two types of lines: cosmetic and geometric, representing two separate concepts.
Cosmetic lines represent the mathematical ideal of a line being an entity with only one dimension—length. When cosmetic lines are drawn, they are usually one pel wide. The line width attribute provides a visual method of distinguishing different types of lines. For example, on a map, roads might be drawn thicker than railroad tracks. The line width attribute defines a multiplier to be applied to the normal (or default) line width. Your application can determine the cosmetic line width by calling GpiQueryLineWidth.
There are certain devices that do not define the thickness of cosmetic lines in terms of a number of pels. The PM interface interprets the specified width for these devices as shown in the following table:
Identifier | Description |
---|---|
LINEWIDTH_DEFAULT | The default width for the device. |
LINEWIDTH_NORMAL | The line width equivalent to a multiplier of 1. Identical to LINEWIDTH_DEFAULT unless changed with GpiSetDefAttrs. |
LINEWIDTH_THICK | The line width equivalent to a multiplier greater than 1. |
Your application can set the cosmetic line width with either a multiplier or an identifier value using GpiSetLineWidth. Since the line represents a widthless ideal, the actual drawn width is a fixed value. The line width remains unchanged when you increase the size of, or zoom in on, a graphics object. The cosmetic line width is not subject to transformations.
In contrast, geometric lines represent an area whose thickness is equal to the specified line width. Geometric lines are subject to changes in scale through transformations, in the same manner as geometric figures. The width of a geometric line does not have a default value. Width is treated as a line attribute for programming convenience, but it is actually a geometric property. A line width is set with GpiSetLineWidthGeom. If a width is already specified, that width can be determined with GpiQueryLineWidthGeom.
To use geometric line width, the lines are assembled into a path. When you use a path to define wide lines, you can specify:
- Geometric width for the lines, using the geometric line width attribute
- Style of the line ends, using the line end attribute
- Style of the junctions of the lines, using the line join attribute
These three attributes apply only to geometric lines drawn using paths.
Line Types
Line type, also called line style, defines the way a line or arc is drawn: as a solid line, a series of dashes, a series of dots, or a combination of dashes and dots. The line type for all subsequent line primitives is selected using GpiSetLineType. For example, if you change the line type to LINETYPE_DOT, any line or arc primitive subsequently drawn is drawn as a dotted line. The following table illustrates the nine standard line types provided by PM. You cannot define other line types for PM applications.
Standard Line Types
Type | Identifier | Long Value |
---|---|---|
Dotted line | LINETYPE_DOT | 1L |
Short-dashed line | LINETYPE_SHORTDASH | 2L |
Dash-dot line | LINETYPE_DASHDOT | 3L |
Double-dotted line | LINETYPE_DOUBLEDOT | 4L |
Long-dashed line | LINETYPE_LONGDASH | 5L |
Dash-double-dot line | LINETYPE_DASHDOUBLEDOT | 6L |
Solid line | LINETYPE_SOLID | 7L |
Invisible line | LINETYPE_INVISIBLE | 8L |
Alternate pels on | LINETYPE_ALTERNATE | 9L |
The default line type (LINETYPE_DEFAULT) is identical to the LINETYPE_SOLID type and has a long value of 0L. The error line type (LINETYPE_ERROR) has a long value of -1L. Your application can determine the current line type using GpiQueryLineType.
Line Color and Mix Attributes
The color attribute defines the color used to draw a primitive or an object. The mix attribute determines how the color of a primitive or an object is combined with the color of the drawing surface or any other objects on the surface.
The line color defines the color used to draw the output from any of the operating system's line functions. When a presentation space is created, the line color initial default is black. Unlike certain other primitives, line and arc primitives do not have a background color. The current color of the drawing surface automatically plays a greater role in the appearance of line and arc primitives than in those primitives that do have a background color. Specifically, when an application draws a dotted or dashed line, the color that appears between the dots or dashes is the current drawing-surface color.
Line and arc primitives have only a color attribute for the actual line. The mix attribute controls the combination of line color with drawing surface color. When a presentation space is created, the line mix attribute initial default is FM_OVERPAINT. The overpaint mix attribute specifies that the line color is not modified by the color of the drawing surface. If the line mix attribute is changed, the line color is mixed with colors that are already on the drawing surface.
To specify a new color or mix attribute, use GpiSetAttrs. This function accepts as input the type of primitive (e.g., PRIM_LINE), a list of attributes to be changed, a list of attributes to be set to their default values, and the values for the attributes to be changed. GpiSetAttrs is useful for specifying colors and mix attributes just for a specific data structure (e.g., the LINEBUNDLE structure) and provides some protection against invalid colors.
To determine the current line color and mix attribute, use GpiQueryAttrs. This function accepts as input the primitive type and the attributes in question and returns an array of values for the specifically queried attributes.
To reset the default line color and mix attribute, use GpiSetDefAttrs. This function accepts as input the type of primitive (e.g., PRIM_LINE), the attributes to be changed, and the values that will become the new default values. Changing default values is important when working with segments, but changing them during a series of drawing functions is not recommended.
The line color and mix attribute can also be specified with GpiSetColor and GpiSetMix, respectively. However, these functions specify the color and mix attribute for all primitive BUNDLE data structures that have a component for foreground color and foreground mix attribute. The queries GpiQueryColor and GpiQueryMix determine the color and mix attribute as specified by GpiSetColor and GpiSetMix. If the line color or mix attribute is specified individually, these queries can return a value inconsistent with the current line color or mix attribute.
Line Primitive Family
The following table describes the variants of the basic line primitive and the functions that draw them.
Functions that Draw Straight Lines
Variants | Function | Description |
---|---|---|
Lines | GpiLine | Draws a single line from the current position to a specified point. |
Polylines | GpiPolyLine | Draws a series of connected lines, from the current position through successive points. |
Series of lines | GpiPolyLineDisjoint | Draws a series of unconnected lines. |
Boxes | GpiBox | Draws a rectangular box with one corner at the current position. |
When the operating system draws a line, it includes the pels at the starting and ending points of the line. The algorithm used to draw the rest of the line depends on the device driver. For example, a driver for a raster device might use a modified Bresenham algorithm, but a driver for a vector device, such as a plotter, simply connects the starting and ending points. In all cases, the result is a line primitive that looks the same from device to device.
Lines
GpiLine draws a line from the current position to a specified end point. After drawing the line, the current position is at the end point specified by GpiLine. To draw a single point, you can use GpiLine with an end point identical to the current position. The current position can be determined using GpiQueryCurrentPosition.
Polylines
GpiPolyLine draws a sequence of connected lines, starting at the current position and passing through a series of specified coordinate positions. After drawing the series of lines, the current position is at the end point of the last line specified by GpiPolyLine. If you are drawing a graph with five connected lines, you can use GpiPolyLine once rather than GpiLine five times. GpiPolyLine accepts as input a number of points and an array of point coordinates.
GpiPolyLineDisjoint eliminates the need to use a series of GpiMove and GpiLine functions to draw multiple unconnected lines. GpiPolyLineDisjoint accepts as input an even number, the number of points, and an array of point coordinates. The first point in a point-pair is the starting point; the second, the end point of that line segment. Upon completion, the end point of the final line becomes the current position.
Boxes
GpiBox draws a rectangular box with one corner at the current position and the diagonally-opposite corner at a specified position. The sides of the box are parallel to the x- and y-axes. Like GpiPolyLine, GpiBox lets you draw a number of connected lines using a single function rather than four separate GpiLine functions. The current position is unchanged by GpiBox.
Note: The start and end position of any closed shape are always the same. Therefore, the current position is unchanged after drawing a closed figure.
In addition to the corner position, GpiBox accepts as input an option for rounded corners and for filled interior. The PM programming interface rounds the corners of a box by drawing an elliptical section in place of the square corner. Two GpiBox parameters, IHRound and IVRound, represent the horizontal and vertical length of the full axis of the ellipse used to round each corner. If the two values are equal, a quarter-circle is used for the rounding. If the two values are 0, no rounding is performed.
Since GpiBox can be used to define a closed figure, it also accepts as input a long value signifying a filled interior. The long value lControl can be:
- DRO_OUTLINE: Draw the box only.
- DRO_FILL: Fill the box interior only.
- DRO_OUTLINEFILL: Draw the box and fill its interior.
The pattern that fills the interior and other drawing options are controlled by the AREABUNDLE data structure. GpiFullArc is the only other line and arc primitive that can be used to define a closed figure.
Attributes of Arc Primitives
The arc primitive shares width, type, and color and mix attributes with line primitives. For example, if you use GpiSetLineType to change the line type to LINETYPE_DASHDOT, all subsequent arcs are drawn with a dash-dot line. In addition to the line attributes defined in the LINEBUNDLE data structure, arc primitives in the simple-arc family are influenced by the values in the ARCPARAMS data structure. Arc primitives in the multiple-arc family have a different method of construction and are not influenced by ARCPARAMS.
In terms of geometrical pictures, the simple arcs contain full or partial:
- Circles: Closed curves whose center is equidistant from every point on the curve.
- Ellipses: Closed curves defined by two fixed points such that the sum of the distances from any point on the curve to the two fixed points is constant.
Multiple arcs contain:
- Fillets: Curves that are tangential to the two lines defined by three control points.
- Splines: Curves that, given four control points, are tangent to the first and last of three intersecting lines.
There are three simple arc operations that begin with a unit circle that lies at the origin of world coordinate space. This unit circle defines the current arc on which subsequent full, partial, and 3-point arcs are based. Your application can define the following attributes of the current arc with GpiSetArcParams:
- Shape
- Orientation
- Size
- Drawing direction
GpiSetArcParams accepts as input an ARCPARAMS data structure that has four parameters (p, q, r, and s). Of the four:
- p scales in the x-direction
- q scales in the y-direction
- r and s are shear components
The values were derived from the major and minor axis of an ellipse. An application can determine the current arc parameters with GpiSetDefArcParams, which copies the current arc parameters to their corresponding fields in the supplied ARCPARAMS structure. The application can set the arc parameters with GpiSetArcParams. This function accepts as input a copy of the ARCPARAMS structure that contains the new arc parameters. The default values of p, q, r, and s, unless changed by GpiSetArcParams, define a unit circle:
- p and q are 1
- r and s are 0
The arc parameters define a transformation that is applied to each point on the perimeter of the unit circle. For any point (x,y) on the perimeter of the unit circle, there exists a new point (x',y'), as determined by the following two linear equations:
x' = p * x + r * y y' = s * x + q * y
These parameters form a 2-by-2 matrix:
[ p r ] [ s q ]
that scales and shears simple arcs. This transformation matrix is not related to the general transformation functions that move objects through coordinate spaces. It is a special-purpose matrix that transforms the shape and size of the imaginary unit circle. The transformed unit circle, also called the current arc, is then used to define the shape and size of the simple arc functions in world coordinates.
After an arc has been described in world coordinates, it can be transformed with the transformation functions, just as any other primitive can. A transformation is orthogonal when:
(p * r) + (s * q) = 0
If orthogonal, the line from the origin (0,0) to the point (p,s) is either:
- The radius of a circle
- Half the major or minor axis of an ellipse
and, the line from the origin to the point (r,q) is either:
- The radius of a circle
- Half the minor or major axis of an ellipse
An orthogonal transformation does not guarantee that the shape of an object as defined by an application will be the same as the shape of the object on the output device. For example, if the page units in the application are PU_PELS, and the pels on the device are rectangular (but not square), calling GpiFullArc produces an ellipse—not a circle—even when the arc parameters are set to their default values.
The product of the 2-by-2 matrix multiplication influences the direction of any arc based on the current arc, except a 3-point arc. The following table illustrates this directional influence:
If... | GpiFullArc and GpiPartialArc... |
---|---|
(p * q) is greater than (r * s) | Draw the ellipse counterclockwise. |
(p * q) is less than (r * s) | Draw the ellipse clockwise. |
(p * q) equals (r * s) | Draw a straight line rather than an ellipse. |
Simple-Arc Primitive Family
The following table describes the variants of a simple-arc primitive and the functions that draw them. All are defined, to some extent, by the current arc parameters. As with line primitives, an application draws simple arcs by first using GpiMove or GpiSetCurrentPosition to set the current position.
Functions that Draw Simple Arcs
Variants | Function | Description |
---|---|---|
Full arcs | GpiFullArc | Draws a circle or an ellipse. |
Partial arcs | GpiPartialArc | Draws a straight line followed by a section of a circle or ellipse. |
3-point arcs | GpiPointArc | Draws an arc through three points. |
Full Arcs
GpiFullArc draws a complete circle or ellipse with its center at the current position. The current position remains unchanged. Whether GpiFullArc draws a circle or an ellipse depends on the current arc parameters. When the current arc is a circle, GpiFullArc draws a circle; when it is an ellipse, GpiFullArc draws an ellipse.
Defining an Ellipse
To define an ellipse as the current arc, use GpiSetArcParams where the values (p,s) and (r,q) are the world coordinates of the end points of the major and minor axes of the ellipse. For example, current arc parameters of (18,0) and (0,10) define an ellipse with a major axis of 36 coordinate units and a minor axis of 20 coordinate units.
For maximum accuracy, create the axes of an ellipse so that they are at right-angles to each other. You can check this by ensuring that the following equation is always true:
p * r + s * q = 0
You can also define a tilted ellipse as the current arc. None of the current arc parameters for a tilted ellipse will be 0, though you should still ensure that the axes of the ellipse are at right-angles to each other.
Defining a Circle
To define a circle as the current arc, (r,q) and (p,s) can be any such value that they lie on a circle. For example, if (r,q) and (p,s) are set to (-4,3) and (3,4), the current arc is a circle with a radius of 5 coordinate units. For simplicity, a circle can be defined by specifying current arc parameters where p is the radius of the circle, and p = q, r = 0, and s = 0. For example, to define a circle centered on the origin and with a radius of 10 world-coordinate units, use GpiSetArcParams with the values (0,10) (10,0).
The default values of the current arc parameters are (0,1) (1,0), which define a circle with a radius of one world-coordinate unit (a unit circle).
GpiFullArc accepts as input a multiplier value, so that the size of the full arc is increased or decreased in relation to the current arc. For example, if the current arc parameters define an ellipse whose major axis is 20 coordinate units and whose minor axis is 8 coordinate units, a multiplier of 2 in GpiFullArc creates an ellipse whose major axis is 40 coordinate units and whose minor axis is 16 coordinate units.
Because the arc parameters are integers, when a fraction is required, for example when rotating an ellipse, greater precision can be obtained by scaling up the required arc parameter values as much as possible, then using a multiplier smaller than 1 to scale the ellipse back down to the required size.
With the default arc parameters defining a circle of 1 world coordinate unit, you can draw a circle of any size by allowing the arc parameters to default, then specifying the radius of the circle with the GpiFullArc multiplier. For example, to draw a circle with a radius of 12 coordinate units, call GpiFullArc with a multiplier of 12 and allow GpiSetArcParams to default.
Since GpiFullArc, like GpiBox, can be used to define a closed figure, it also accepts as input a long value signifying a filled interior. The long value, IControl, can be:
- DRO_OUTLINE: Draw the arc only.
- DRO_FILL: Fill the arc interior only.
- DRO_OUTLINEFILL: Draw the arc and fill its interior.
The pattern that fills the interior and other drawing options are controlled by the AREABUNDLE data structure. See Area and Polygon Primitives for detailed information on using areas.
Partial Arcs
A partial arc is a section of a full arc defined by the current arc parameters. To draw a partial arc, use GpiPartialArc, which draws two separate figures. The first figure is a straight line from the current position to the starting point of a partial arc, and the second figure is the partial arc itself. When the arc has been drawn, the new current position is at the end point of the partial arc.
GpiPartialArc accepts as input the center of the current full arc, of which the partial arc is a part, specified in world coordinates. You can also specify a multiplier value to increase or decrease the size of the partial arc in relation to the current full arc.
You must also specify two positive fixed values: a start angle and a sweep angle. If the current full arc is a circle, the start angle is measured counterclockwise from the x-axis of the circle. The intersection of the start angle with the full arc, adjusted by the multiplier value, defines the starting point of the partial arc. The sweep angle continues the counterclockwise measurement, beginning where the start angle left off. The intersection of the sweep angle with the full arc, adjusted by the multiplier value, defines the partial arc.
If the current arc is not a circle, the start and sweep angle are skewed to the same degree that the ellipse is a skewed circle. You can join the end point of the partial arc and the current position with GpiLine. This line is drawn automatically if you define the partial arc within a GpiBeginArea and GpiEndArea bracket.
3-Point Arcs
GpiPointArc draws an arc from the current position through an intermediate point to an end point. When the arc is drawn, the current position is at the end point of the arc. You specify both the intermediate point and the end point, and these values determine both the size of the arc and the direction in which it is drawn. The shape and the orientation of a 3-point arc are determined by the current arc parameters.
If the current arc parameters define an ellipse, that ellipse is scaled up or down to fit the three points of the arc. When you want the three points of the arc to be points on a circle, allow the current arc parameters to default so they define a unit circle. You do not need to use the values p and q to specify the radius of the circle, because the radius is determined by the relative positions of the three points of the arc.
Multiple-Arc Primitive Family
The following table describes the variants of the multiple-arc primitive and the functions that draw them. As with line primitives, an application draws multiple arcs by first using GpiMove or GpiSetCurrentPosition to set the current position. The multiple arcs are the most sophisticated of the arc primitives, and their construction does not depend on the current arc parameters.
Functions that Draw Multiple Arcs
Variants | Function | Description |
---|---|---|
Fillets | GpiPolyFillet | Draws one or more fillets. |
Fillets | GpiPolyFilletSharp | Draws one or more fillets with varying degrees of sharpness. |
Splines | GpiPolySpline | Draws one or more splines. |
Fillets
GpiPolyFillet constructs a fillet (a curved line) made up of one or more arcs, each of which touches a different straight line. You specify the end points of these straight lines with GpiPolyFillet. The lines are not drawn but are used to construct the curve.
The fillet starts at the current position and finishes at the end point of the last line. On the way from the start point to the end point, the fillet is tangential to all intermediate lines at their midpoints. When the fillet is drawn, the current position is at the end point of the last construction line.
When you supply only two points, the construction lines of the fillet are drawn from the current position to the first point, and from the first point to the second point. The fillet is drawn from the current position to the second point and is tangential to the construction lines at those points.
GpiPolyFilletSharp creates a fillet on a series of connected construction lines. The first fillet in the series is built using two construction lines: one drawn from the current position to point 1 (the control point), and one drawn from point 1 to point 2 (the end point). The fillet is drawn from the current position to the end point and is tangential to the two construction lines at those points.
GpiPolyFilletSharp also accepts as input a sharpness value. Sharpness is a measure of the distance between the fillet and the control point. The sharpness of the fillet is calculated as the ratio of the distance from the midpoint of the notional line between the start and end points to the point where the fillet crosses this line, divided by the distance from this crossing point to the control point. The sharpness value defines the type of arc as shown in the following table:
A sharpness value of... | Defines... |
---|---|
Greater than 1.0 | A hyperbola |
Equal to 1.0 | A parabola |
Less than 1.0 | An ellipse |
Subsequent fillets start from the end point of the previous fillet and are constructed using the next two lines in the sequence in exactly the same way. For each fillet, you define one control point, one end point, and one sharpness value. Upon completion, the current position is at the end point of the final construction line in the sequence.
There might be discontinuity of gradient between multiple fillets drawn with GpiPolyFilletSharp. To avoid this, ensure that points B and C of one fillet are on the same construction line as points A and B of the next fillet in the sequence. Discontinuity of gradient between fillets does not occur when the fillets are drawn with GpiPolyFillet.
Splines
GpiPolySpline creates a succession of one or more Bezier splines. The spline is a curve, but its construction method is different from that of the fillet. As input to this function, you supply three construction points for each spline. The first spline starts from the current position and ends at the third specified point. The two intermediate points are control points for the curve. Subsequent splines start at the end point of the previous spline, have two intermediary control points, and end at the third control point.
To avoid discontinuity of gradient between the end of one spline and the start of the next, ensure that the last two construction points of the first spline and the first two construction points of the second spline are positioned along a single construction line.
Using Line and Arc Primitives
This section explains how to:
- Draw a straight line
- Create a "rubber-banding" effect with straight lines or arcs
- Draw a circle, ellipse, fillet, or spline
Drawing a Straight Line
To draw a straight line, set the current position with GpiMove or GpiSetCurrentPosition. Set the end point of the line by filling in a POINTL structure, then draw the line with GpiLine.
#include <os2.h> BOOL DrawLine(HPS hps, LONG xStart, LONG yStart, LONG xEnd, LONG yEnd) { POINTL ptl; /* Point structure */ ptl.x = xStart; /* Loads starting x-coordinate */ ptl.y = yStart; /* Loads starting y-coordinate */ GpiMove(hps, &ptl); /* Sets current position */ ptl.x = xEnd; /* Loads ending x-coordinate */ ptl.y = yEnd; /* Loads ending y-coordinate */ if (GpiLine(hps, &ptl) == GPI_OK) { return TRUE; /* Draw Line */ } /* if */ else return FALSE; } /* DrawLine */
The second argument of GpiMove is the address of a POINTL structure that contains coordinates of the line's starting point. The second argument of GpiLine is the address of another POINTL structure that contains the coordinates of the last point on the line.
Creating a Rubber-Banding Effect with a Straight Line
When lines are drawn with a rubber-banding effect, the original line (if one exists) is erased, and a new line is drawn in its place. This process typically takes place each time the mouse is dragged and continues until the mouse button is released. The quickest way to erase the original line (having ensured that it was drawn using mix attribute FM_XOR) is to redraw it using mix attribute FM_XOR.
#define INCL_WININPUT #define INCL_GPITRANSFORMS #define INCL_GPIPRIMITIVES #include <os2.h> HPS hps; /* Presentation-space handle */ LONG curr_color; MRESULT EXPENTRY wpGeneric(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { static POINTL ptlStart; /* Starting point of line */ static POINTL ptlNew; /* Ending point of line */ static POINTL ptlPrev; /* Previous end point of line */ static BOOL fDraw; /* Line-drawing flag */ switch (msg) { case WM_BUTTON1DOWN: /* User begins drawing */ GpiSetColor(hps, CLR_GREEN); ptlStart.x = (LONG)(LOUSHORT(mp1)); ptlStart.y = (LONG)(HIUSHORT(mp1)); GpiConvert(hps, CVTC_DEVICE, CVTC_WORLD, 1L, &ptlStart); ptlPrev.x = ptlStart.x; ptlPrev.y = ptlStart.y; GpiMove(hps, &ptlStart); fDraw = TRUE; return ((MRESULT)TRUE); case WM_MOUSEMOVE: /* User draws line */ if (fDraw) { ptlNew.x = (LONG)(LOUSHORT(mp1)); ptlNew.y = (LONG)(HIUSHORT(mp1)); GpiConvert(hps, CVTC_DEVICE, CVTC_WORLD, 1L, &ptlNew); curr_color = GpiQueryColor(hps); GpiSetMix(hps, FM_XOR); if ((ptlStart.x != ptlPrev.x) || (ptlStart.y != ptlPrev.y)) { GpiMove(hps, &ptlStart); GpiLine(hps, &ptlPrev); } /* if */ if ((ptlStart.x != ptlNew.x) || (ptlStart.y != ptlNew.y)) { GpiMove(hps, &ptlStart); GpiLine(hps, &ptlNew); ptlPrev.x = ptlNew.x; ptlPrev.y = ptlNew.y; } /* if */ GpiSetMix(hps, FM_OVERPAINT); } /* if */ return ((MRESULT)TRUE); case WM_BUTTON1UP: /* User stops drawing */ fDraw = FALSE; return ((MRESULT)TRUE); } /* switch */ } /* wpGeneric */
Drawing a Circle
When drawing a circle, all transformations between the world, model, page, and device spaces must maintain square units. This means your application should select metric, English, or arbitrary page units instead of pels. On most devices, pels are rectangular, but not necessarily square. The scaling factors should be equal. If the transformations maintain square units and the arc parameters are set to their default values, GpiFullArc produces a circle.
In the example below, if the page units are PU_LOENGLISH and the default transformations are set, a circle with a radius of 1/2 inch is drawn.
#define INCL_GPIPRIMITIVES #include <os2.h> HPS hps; /* Presentation-space handle */ MRESULT EXPENTRY wpClient(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { ARCPARAMS arcp; /* Structure for arc parameters */ POINTL ptlPos; /* Structure for current position */ FIXED fxMult; /* Multiplier for circle */ arcp.lP = 1; arcp.lQ = 1; arcp.lR = 0; arcp.lS = 0; GpiSetArcParams(hps, &arcp); /* Sets parameters to default */ ptlPos.x = 100; /* Loads x-coordinate */ ptlPos.y = 100; /* Loads y-coordinate */ GpiMove(hps, &ptlPos); /* Sets current position */ fxMult = MAKEFIXED(50, 0); /* Sets multiplier */ GpiFullArc(hps, DRO_OUTLINE, fxMult); /* Draws circle */ } /* wpClient */
The second argument to GpiFullArc, DRO_OUTLINE, specifies that the operating system should draw only the outline of the circle rather than filling the interior with the current fill pattern. The third argument, fxMult, specifies that the operating system should multiply the size of the circle by 50 units. Because the page units are PU_LOENGLISH and the default transformations are set, 50 units is equivalent to 1/2 inch.
Drawing an Ellipse
If you set the world, model, page, and device transformations to maintain square units, you can use the arc parameters to transform the shape of the unit circle to an ellipse and draw this with GpiFullArc. The example below alters the arc parameter, p, by doubling its value, making the ellipse twice as wide horizontally as it is vertically.
If the page units are PU_LOENGLISH and the default transformations are set, an ellipse with a 2-inch major axis (parallel to the x-axis) and a 1-inch minor axis (parallel to the y-axis) is drawn.
#define INCL_GPIPRIMITIVES #include <os2.h> HPS hps; /* Presentation-space handle */ MRESULT EXPENTRY wpClient(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { POINTL ptlPos; /* Structure for current position */ FIXED fxMult; /* Multiplier for ellipse */ ARCPARAMS arcp; /* Structure for arc parameters */ arcp.lP = 2; arcp.lQ = 1; arcp.lR = 0; arcp.lS = 0; GpiSetArcParams(hps, &arcp); /* Sets parameters to default */ ptlPos.x = 200; /* Loads x-coordinate */ ptlPos.y = 100; /* Loads y-coordinate */ GpiMove(hps, &ptlPos); /* Sets current position */ fxMult = MAKEFIXED(50, 0); /* Sets multiplier */ GpiFullArc(hps, DRO_OUTLINE, fxMult); /* Draws ellipse */ } /* wpClient */
Because the arc-parameter fields, lP and lQ, are set to 2 and 1, the operating system creates an ellipse with a major axis that is twice as long as the minor axis.
Drawing a Pie Slice
To use GpiPartialArc to draw a closed shape bounded by a chord and an arc, follow these steps:
- Set the current line type to LINETYPE_INVISIBLE with GpiSetLineType.
- Call GpiPartialArc with the start angle equal to angle B and the sweep angle equal to 0. This moves the current position to a point on the current arc, defining one end of the chord.
- Select a visible line type with GpiSetLineType.
- Call GpiPartialArc with the start angle equal to angle A and the sweep angle equal to angle B - angle A. Angle B must be greater than angle A. The center point is the same on both GpiPartialArc calls.
To fill this partial arc with the current area-fill pattern, bracket the GpiPartialArc call of step 4 with GpiBeginArea and GpiEndArea. Do not call GpiBeginArea before step 2.
The following example shows how to draw a pie slice:
#define INCL_GPIPRIMITIVES #include <os2.h> void Figure_516() { HPS hps; POINTL ptlCenter = {2L, 2L}; /* Coordinates of the center point */ FIXED fxAngleA = 20L; /* Angle A in degrees */ FIXED fxAngleB = 130L; /* Angle B in degrees */ GpiSetLineType(hps, LINETYPE_INVISIBLE); /* Set position to start drawing the arc at angle B */ GpiPartialArc(hps, &ptlCenter, MAKEFIXED(1, 0), fxAngleB, 0L); GpiSetLineType(hps, LINETYPE_SOLID); GpiBeginArea(hps, BA_BOUNDARY | BA_ALTERNATE); /* Fill the area */ GpiPartialArc(hps, &ptlCenter, MAKEFIXED(1, 0), fxAngleA, fxAngleB - fxAngleA); GpiEndArea(hps); /* Cancel area-fill */ }
Drawing a Fillet
A fillet is tangential to two lines. The curve of the fillet is always tangential to a line drawn between its start and control points and a line drawn between its end and control points.
The following example shows how to draw a single curve using the current position and two control points:
#include <os2.h> HPS hps; /* Presentation-space handle */ MRESULT EXPENTRY wpClient(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { POINTL aptl[2]; /* Structure for control points */ aptl[0].x = 50; /* Loads x-coord. of first control point */ aptl[0].y = 50; /* Loads y-coord. of first control point */ GpiMove(hps, aptl); /* Sets current position */ aptl[0].x = 75; /* Loads x-coord. of second control point */ aptl[0].y = 75; /* Loads y-coord. of second control point */ aptl[1].x = 100; /* Loads x-coord. of third control point */ aptl[1].y = 50; /* Loads y-coord. of third control point */ GpiPolyFillet(hps, 2, aptl); /* Draws fillet */ } /* wpClient */
When you draw a sharp fillet, the sharpness value controls the shape of the curve. The following example uses a sharpness value of 3, which creates a hyperbolic curve:
#include <os2.h> HPS hps; /* Presentation-space handle */ MRESULT EXPENTRY wpClient(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { POINTL aptl[2]; /* Structure for control points */ FIXED fxSharpness; /* Sharpness value */ aptl[0].x = 50; /* Loads x-coord. of first control point */ aptl[0].y = 50; /* Loads y-coord. of first control point */ GpiMove(hps, aptl); /* Sets current position */ aptl[0].x = 75; /* Loads x-coord. of second control point */ aptl[0].y = 75; /* Loads y-coord. of second control point */ aptl[1].x = 100; /* Loads x-coord. of third control point */ aptl[1].y = 50; /* Loads y-coord. of third control point */ fxSharpness = MAKEFIXED(3, 0); /* Sets sharpness value */ GpiPolyFilletSharp(hps, 2L, aptl, &fxSharpness); /* Draws fillet */ } /* wpClient */
Drawing a Spline
When you use GpiPolySpline to draw a spline, each curve is tangential to the first and last of three connected lines. The following example shows how to draw a spline:
#include <os2.h> HPS hps; /* Presentation-space handle */ MRESULT EXPENTRY wpClient(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { POINTL aptl[3]; /* Structure for control points */ aptl[0].x = 50; /* Loads x-coord. of first control point */ aptl[0].y = 100; /* Loads y-coord. of first control point */ GpiMove(hps, aptl); /* Sets current position */ aptl[0].x = 75; /* Loads x-coord. of second control point */ aptl[0].y = 200; /* Loads y-coord. of second control point */ aptl[1].x = 100; /* Loads x-coord. of third control point */ aptl[1].y = 0; /* Loads y-coord. of third control point */ aptl[2].x = 125; /* Loads x-coord. of fourth control point */ aptl[2].y = 100; /* Loads y-coord. of fourth control point */ GpiPolySpline(hps, 3L, aptl); /* Draws spline */ } /* wpClient */