Document 182245

.
Journeyman
'-_
11
Fast Bezier
C
esin
Windows
Michael Bertrand
ezier curves are widely used
in computer graphics. PostScript uses these curves as
building blocks, for example;
defining even circles in terms of
Beziers. Many modern PC and Mac illustration programs implement Bezier
curves in a direct way, allowing users
to create and interactively edit the
curves to create complex images.
These curves possess several properties that have led to their widespread adoption in computer graphics
applications:
B
• Because they are defined in terms
of a few points, Bezier curves
can be identified with these points
in the graphics database.
• Efficient algorithms generate the
entire curve from the defining
points.
• The defining points intuitively
describe the curve.
I
Four defining points-A
Bezier curve
is defined by four points called controll, handle1, handle2, and control2
(abbreviated here as ctrll, hand1,
hand2, and ctrl2). The curve begins at
ctrll and proceeds toward hand1; at
the other end, we can think of it as
starting from ctrl2 and proceeding toward hand2. In each case, the curve
gradually pulls away from the associated handle in its movement toward
Here's how to draw Bezier curves quickly enough to
rubber-band them on-screen.
the other handle/control.
The
handles are points of attraction for
the curve; the curve starts from ctrll
toward hand 1, but gradually pulls toward hand2 as the attractive force of
hand1 diminishes and the attractive
force of hand2 increases. The further
away hand1 is from ctrll, the longer
the curve will be pulled toward ctrll
before breaking toward hand2.
Bezier curves take several forms, as
shown in the screen shot given in Figure 1. Vectors connecting each control point to its handle are shown as
well as the Bezier curve they define.
This example is about as complex as
four-point Beziers get; more complex
Bezier curves require a series of Beziers
joined together in a continuous path.
four original defining points. The calculated points can then be connected
by line segments to give the impression of a smooth curve. The de
Casteljau algorithm breaks a Bezier
curve into two separate pieces, left
and right, each of which is itself a
Bezier curve.
The key to the efficiency of this algorithm is the astounding simplicity
of the math, which involves taking
simple averages to calculate de
Casteljau construction points: First
average the original defining points
(the q's shown below are the averages), then average the averages (r's),
then take a final average (sO):
ctrll
- pO
de Casteljau Construction-Beziers
must be drawn quickly to be dragged,
or rubber-banded, on the screen,
since the curves are being drawn and
erased constantly with each mouse
movement. We also need fast Bezierdrawing routines to render a complex
image comprising perhaps hundreds
of individual Beziers in a reasonable
time.
The de Casteljau algorithm is a fast
integer-based method for calculating
points along a Bezier curve, given the
handl
- pl
qO
rO
sa
ql
hand2
- p2
ctr12
- p3
rl
q2
That is: qO = (pO + p1)/2, rO = (qO +
q1)/2, and so on. The actual calculations will be with coordinates, not
points, but it helps to think in terms
of points, keeping in mind that the
"average" of two points is the point
midway between the two. It turns out
that sO is the midpoint of the Bezier
Feb/March 1992
25
;
C: FAST BEllER CURVES
1=1
original Bezier curve.
3. (p3, q2, r1, sO)are (ctrll, hand1,
hand2, ctrl2) of a sub-Bezier coinciding with the right half of the
original Bezier curve.
Bezier
Help
~ Left Button dOI,WI, drag, button up fur 1st hendle.
Left Button do •...
en. drag, button up for 2nd handle and Bez
Right dick to dear ,.·!indo,
..
...·}
x
x
After the first step illustrated here, we
have 3 points on the curve:
The original
The midpoint
The original
• FIGURE 2-The
de Caster au Construction
~~~~------------------~
p1
rO
r1
p2
q2
pO
curve. What's more, (pO, qO, rO, sO)
are (ctrl, hand, hand, ctrl) for a Bezier
coinciding with the left half of the
original Bezier; and (p3, q2, rl, sO)
are (ctrl, hand, hand, ctrl) for another
Bezier coinciding with the right half
of the original Bezier.
The procedure can be repeated:
the midpoint of the left sub-Bezier is
1/4 of the way along the original
curve; and the midpoint of the right
sub-Bezieris 3/4 of the way along the
original curve. The subdivision process can be repeated indefinitely to
generate 2, 4, 8, 16,...2n sub-Beziers
and 3,5,9,17, ...2"+1 points along
the original curve. Like other subdivision processes, the de Casteljau algo26
PC TECHNIQUES
p3
rithm lends itself to recursive implementation. Averaging entails dividing
by 2, which can be done quickly as a
shift right operation because we are
dealing with integers.
The de Casteljau construction is illustrated in Figure 2, where the p's
are the controls and handles of the
original Bezier curve. qO is midway
between pO and p l , rO is midway between qO and q l , and so on. Observe
that:
1. sOis the midpoint of the original
Bezier curve.
2. (pO, qO, rO, sO)are (ctrll, hand1,
hand2, ctrl2) of a sub-Bezier coinCiding with the left half of the
point.
original
point.
pO
Bezier
p3
curve
Applying the procedure again to
the left and right sub-Beziers generates
their midpoints, giving five points on
the original Bezier. Subdividing these
four sub-Beziers then gives us nine
points on the original curve, and so
on. Stopping at four subdivision levels
and 17 points produces smooth
curves at VGA resolution. The number of subdivisions, or recursive
depth, is BEZ_DEPTH in the program .
NUM_BEZPTS is the number of
points generated along the Bezier
curve, and is used to allocate an array
to hold the Bezier points. Therefore,
make sure that:
NUM BEZPTS
..........
control
of the
control
>-
2""
DEPI'
+ 1
Increasing BEZ_DEPTH results in
more line segments in the Bezier
curve, hence a smoother curve-at
least up to a point. We reach diminishing returns in increasing BEZ_DEPTH
too much, since the accumulated error of repeated averaging eventually
throws the calculations off by one or
more pixels. Remember that
BEZ_DEPTH is an exponent, so increasing BEZ_DEPTH by 1 doubles
the number of-segments.
Since the recursion proceeds to the
maximum depth down the far left
branch, the first curve point actually
generated is the point immediately
following pO = ctr11 along the Bezier.
The remaining points are also generated in order (from pO = ctrll to p3 =
ctrI2), a nice side effect of the recursive implementation. Another point
is collected into an array every time
the recursion reaches its finest subdivision level, and the points are in order!
Writing tools in Windows-We
want to show off our fast Bezier-drawing through an interactive Bezier
Tool. If the curve rubber-bands well
on the screen, then we can claim to
have a good algorithm. Interactive
tools in Windows are constructed
with the concept of "system state."
The Window procedure passes mouse
messages to Bez'Toolf), which maintains a key static variable, iState,
which takes four values summarized
in Table 1.
BezToolO's action depends on
iState, which in turn depends on the
sequence of mouse messages that
have recently streamed into the tool.
Until the first WM_LBUITONDOWN
message is received, iState remains
NOT_STARTED because nothing has
been done. The first WM_LBUITONDOWN triggers a state transition to
DRAG_HANDl. In this state, the tool
responds to WM_MOUSEMOVEs by
rubber-banding the first handle in
XOR mode. WM_LBUITONUP then
causes a state transition to WAIT_FOR_CTRL2. Nothing happens until
another WM_LBUITONDOWN is received, which changes iState to
DRAG_HAND2; in this state,
WM_MOUSEMOVE messages cause
rubber-banding of both the second
handle and the Bezier curve as a
whole. WM_LBUITONUP now
causes a final state transition back to
NOT_STARTED. The final handle and
Bezier are frozen, and the tool is
again ready to start another Bezier.
BezToolO calls DrawHandleO and
DrawBezO to draw the figures (which
in turn call Windows' GDI calls
Move'Tof), Line'I'ot), and Polylinetj).
Each mouse move causes two calls to
these routines. The first call draws
over the figure exactly where it had
been drawn the first time. Since we
are in XOR drawing mode, drawing
over the original figure erases it. The
second call then draws at the new location. Static variables must be used if
the user points are to be remembered
for the next pass through the tool so
previous figures can be erased.
Windows and graphics programming-Windows
is a natural medium
for this kind of programming. Mouse
events are sent to our window procedure automatically, enabling us to
build interactive mouse-driven tools.
The GDI system provides line drawing, including the R2_NOTXORPEN
ROP code which allows us to draw in
XOR mode.
Windows has a built-in coordinate
system and mapping modes so we
can change our working range of
numbers. The Beziers would not dis-
play nearly as nicely were we restricted
to screen coordinates of about 500x500
pixels. By setting the MM_ISOTROPIC
mapping mode and adjusting the
Window Extent and Viewport Extent
in Bez'Toolt), we can expand the range
to [-15,000, +15,000] which minimizes
the negative side effectsof calculations
with small integers. •
Editor's note: To rebuild BEZ.EXE, you'll
need several files in addition to BEZ.C,
including some with no ASCII representation. These files are present in a file
called BEZ.ZIP, contained within the
listings archive (or this issue, either on a
Disk Subscription disk or from one of the
online services and BBS systems that
carry our listings.
References
Foley, James D., Andries van Dam,
Steven K. Feiner, and John F. Hughes,
Computer Graphics: Principles and
Practice, Addison-Wesley (2nd ed.,
1990), pp 507ff.
Michael Bertrand teaches Mathematics
and programming at Madison Area
Technical College, Madison, WI 53704.
COMPUTER
LANGUAGE
5.0 presents
C Bug # 651
'#[email protected]
rwJ.l;/·
struct
{ int a[3]. b: } w[] = { { L 2. 3 }. 2 }:
Do you see any problems with this declaration? Chances are your compiler
will not report any difficulties and yet, you may be surprised to learn how
many elements are in w[] (hint: it's not 1).
If you need help, give us a call; refer to bug #651.
PC-lint will catch this and many other
C bugs. Unlike your compiler, PC-lint
looks across all modules of your application for bugs and inconsistencies.
New - Optional Strong Type Checking
and variables possibly not initialized.
More than 330 error messages. More
than 105 options for complete customization. Suppress error messages,
locally or globally, by symbol name, by
message number, by filename, etc.
Check for portability problems. Alter
size of scalars. Adjust format of error
messages. Automatically generate ANSI
prototypes for your K&R functions.
AUn: Power users with huge programs.
PC-lint 386 uses DOS Extender
Technology to access the full storage
and flat model speed of your 386. Now
fully compatible with Windows 3.0
and DOS 5.0
PC-lint 386 --$239
PC-lint DOS - OS/2 - $139
Mainframe & Mini Programmers
Fiexelsmt in obfuscated source
form, is available for Unix, OS-9,
VAXIVMS, QNX, IBM VM/MVS,
etc. Requires only K&R C to compile but supports ANSI. Callfor
pricing.
3207 Hogarth Lane, Collegeville, PA 19426
CALL TODAY (215) 584-4261 Or FAX (215) 584-4266
30 Day Money-back Guarantee.
PA add 6% sales lax.
PC·linl and FlexeLint are trademarks of Gimpel Software
Feb/March 1992
27
C: FAST BEZIER CURVES
A TABLE l-IState's
Four Possible Values
NOT_STARTED
:
tool
DRAG_HANOI
:
dragging
has
not
been
WAlTJORJTRL2
:
waiting
for
DRAG_HAND2
:
draggi
ng handl
CW_USEDEFAULT,
ShowWindow(hWnd,
started
contro12
e2
to
and
:
Program
to
vely.
User
i nteraeti
handle;
the
handle.
Demonstrates
Bezier
calculation
of
Copyright
draw
(c)
draws
<wi ndows . h)
Iii ncl ude
"bez.
curves
and
handl
e by
fi rst
the
1991.
Iii ne 1 ude
while
curve
Bezier
on.
reeei
Bezier
their
together
de
algorithm
nq , then
with
the
for
fast
ves
app
Conti
*1
window
client
NULl);
area;
send
*1
WM_PAINT
and
unti
di spatch
them
1 GetMessage()
to
appropri
returns
ate
wi n
NULL when
it
*1
WM_OUIT.
NULL,
NULL,
NULl)
1* process
ateMessage(&msg);
Di spatchMessage(&msg);
/* pass
char
input
message
to
from
window
*/
keyboard
*1
function
}
return(msg.wParam)
A.
que
nues
(GetMessage(&msg,
Transl
second
second
points.
Michael
hInstance,
(
handles
draggi
rubber-bands
Casteljau
the
1* update
1* Read msgs from
Bezi er
.•. LISTING I-BEZ.C
BEZ.C
display
/*
NULL,
be entered
functi
/*
nCmdShow);
UpdateWindow(hWnd);
handlel
NULL,
;
*1
Bertrand.
1ong
FAR PASCAL WndProc (HWND hWnd,
uns i gned
WORD wParam,
h"
i Message,
LONG I Param)
1*
HPEN
hRedPen;
/*
red
pen
int
LogPerDevice;
/* Illogical
WORD
cxCl i ent;
WORD
cyCl i ent;
/* size of
/* size of
1* current
POINT
BelPts[NUM_BEZPTS]
POINT
*PtrBezPts;
char
Instrl[]
"*
Instr2[]
int
IN:
client
area
(x ) ,
client
area
(y).
along
Bezier
BezPts[]
curve
array
down,
drag,
button
up for
1st
handle.";
down,
drag,
button
up for
2nd
handle
-
*/
*/
app's
messages
come
hWnd, iMessage,wParam,lParam
: standard
Windows
proc
parameters
HOC
hOC;
PAINTSTRUCT
ps;
/* must
FARPROC
lpProcAbout;
generate
our
1* needed
when
own handle
recei
1* pointer
to
to
DC to
ve WM_PAINT
"AboutBez"
draw
*/
message
*1
*1
function
(i Message)
(
"* Right
click
to
clear
hlnstance,
LPSTR lpszCmdLine,
and
Create
/*
Bez.";
window.";
hRedPen
wi ndow
int
and
set
break;
1* WM_CREATE */
case
nCmdShow)
di spach
message
cyClient
standard
static
char
szAppName
static
char
static
char
as
*1
global.
l , RGB(255,
0, 0»;
a rea
size
into
-
LOWORD(lParam);
g I oba 1 s when
wi ndow
-
HIWORD(IParam);
res i zed.
*1
1* WM_SIZE */
break;
parms
store
CreatePen(PS_SOLID,
WM_SIZE:
cxClient
loop.
hInstance,hPrevInstance,lpszCmdLine,nCmdShow:
WinMain
and
-
1* Get c 1 i ent
Regi ster
once
hRedPen
HANDLE hPrevlnstance,
/*
IN:
all
ca se WM_CREATE;
PASCAL WinMain(HANDLE
USE:
procedure:
*/
*/
*/
*1
instance
window
here.
Button
Instr3[]
Application's
unit
switch
Button
"* Left
char
device
~
Left
char
per
*/
axes).
/* array
of pts
/* pointer
into
;
USE:
*1
handles.
units
(both
HANDLE hInst;
for
*/
case
[]
WMJOMMAND:
1f
(wParam
-
-
"Bezier";
szlconName[]
-
"BezIeon";
(
szMenuName[]
-
"Bezhenu":
/* "About"
call
HWND
hWnd;
MSG
msg;
1* for
registering
window
menu
item
Dialog80x
-
by
user
:
*1
function.
MakeProcInstance(AboutBez,
(hlnst,
"AboutBez",
FreeProclnstance(
*1
chosen
"AboutBez"
I pProcAbout
/* handle to WinMain's
window
*/
1* message di spached to wi ndow *1
WNDCLASS we;
10M ABOUTl
hlnst);
hWnd,
lpProcAbout);
I pProcAbout);
)
1* Save
so
hlnst
instance
can
-
use
handle
for
in
"About"
global
box.
*1
case
hlnstance;
/*
/* Register
if
application
window
WfLPAINT:
Repa i nt
hOC -
*1
class.
1* WM_COMMAND*1
brea k;
var
dialog
Se 1 ectObj
(!hPrevInstance)
I
wc.cbClsExtra
- 0;
EndPaint(hWnd,
wc. cbWndExtra
- 0;
break;
wc. hI nstance
-
*/
wi ndow' s messages
wc.hleon
-
LoadIcon(hlnstance,
-
LoadCursor(NULL,
wc. hbrBackground
-
GetStoekObject
wc. I pszMenuName
-
slMenuName;
wc. 1 pszCI assName
-
szAppName;
case
szlconName);
GetStockObj
ect (ANS I_VARJONTl
);
0,
30,
Instr3,
lstrlen(lnstr3));
&ps);
1* WM_PAINT */
WfLLBUTTONDOWN:
ca se WM_RBUTTONDOWN:
IDC_ARROW);
(WHITCBRUSH);
1* menu resource
in RC fil
1* name used inca 11 to
CreateWindow()
*1
e
*1
case
WM_MOUSEMOVE:
case
WM_LBUTTONUP:
1* Mouse events
BezTool(hWnd,
passed
on
iMessage,
to
BezTool
()
for
processing.
*/
lParam);
1* WM_LBUTTONDOWN...
break;
*/
(!RegisterClass(&wc»
return
(FALSE)
case
;
WM_DESTROY:
/* Destroy
window
De 1eteObject
1* Initialize
hWnd -
CreateWi
specific
instance.
ndow( szAppName,
PC TECHNIQUES
WS_OVERLAPPEOWINDOW,
CW_USEDEFAULT,
CW_USEOEFAULT,
:
0);
1* WM_DESTROY */
break;
defaul
& delete
(hRedPen)
Pas tOui tfiessage(
*1
szAppName,
CW_USEDEFAULT,
28
*/
wi ndow.
hInstance;
we. hCursor
if
of
0,
WndProc;
get
1 eft
TextOut(hDC,
CS_HREDRAW
-
to
upper
Textout(hDC,
-
wc. I pfnWndProc
CS_VREORAW;
at
&ps);
0, Ins t r l . lstrlen(lnstr)));
D, 15, Instr2,
l s tr l ent Ins tr z j ) :"
we. style
/* fn
ions
ect (hOC,
TextOut(hDC,
(
instruct
BeginPaint(hWnd,
t:
pen
when
application
terminated.
*1
return(
}
DefWi ndowProc(
hWnd,
1* switch(iMessage)
return(Ol)
i Message,
wParam,
1 Param));
SetROPZ(hDC,
RLNOTXORPEN);
DrawBez(
ctrll,
*1
(HWND hWnd,
unsi gned
i Message,
case
IN:
hWnd
NOTE:
mouse
event
handle
to
to
: mouse
event
1 Param
: mouse
coords
is
the
handles
and
Bezier
BezTool
current
state
iState.
iState's
the
as
tool's
()
of
the
(x
--
1 award,
Bezier
by
remembers
called
tool
value,
action
input
is
y --
drawing
this
the
the
is
are
next
last
time
in
time
thru.
also
i State)
and
the
Bezier
key
the
draws.
static
and
statics
The
handle
so
hand1.x
-
inPt.x;
hand1.y
-
inPt.y;
DrawHandle(hDC,
determines
points,
case
DrawBez(hDC,
HOC
hOC;
WORD
maxClient;
POINT
inPt;
POINT
pts[ZJ;
1* must generate
our own handle
1* larger
of (cxClient,
cyClientl
1* incoming
point
*1
1* to
get
Bez
control
1* user-entered
static
POINT
static
POINT
int
/flogical
DC to
(lst):
*1
handle
( Znd )':
*1
*1
*1
Bez
ctrlZ,
inPt.x;
handZ.y
-
inPt.y;
state:
DRAG_HANOI.
etc.
1* draw
in
*f
f* erase
handZ);
handl.
handZ,
ctrlZ,
handZ,
*f
new handle
f* draw
handZ);
handl,
*f
*1
ct r l Z) :
f* get
ctrll.
XOR
old
new
*f
ct r l Z) :
break;
}
case
1* BezTool()'s
new
f* DRAG_HANOI *f
f* switch(iState)
*f
*1
handZ;
iState;
-
f* WM_MOUSEMOVE *f
break;
control
f* draw
ctrl!,
handZ.x
DrawBez(hDC,
unit
handl);
ctrlZ,
DrawHandle(hDC,
units/dev.
handle
draw
ct r l l , handI;
1* user-entered
static
LogPerDevice,
to
ctrl!,
RZ_NOTXORPEN);
DrawHandle(hDC,
*1
f* draw in XOR *f
/* erase old */
1* get new handl e * f
DRAG_HANDZ:
SetROPZ(hDC,
thru.
handll;
1* DRAG_HANOI *f
break;
BezTool()
ctrl!.
RZ_NOTXORPEN);
DrawHandle(hDC.
variable
tool,
control
as
processes
WM_LBUTTONUP
user
DRAG_HANOI:
SetROPZ (hOC,
which
the
thru
maintained
case
hi word)
tool
as
maintained
set
time
user,
them
repeatedly
as
WM_MOUSEMOVE:
switch(
(WM_LBUTTONDOWN, etc.)
interactive
*f
(
WM_RBUTTONDOWN, WM_LBUTTONDOWN, WM_MOUSEMOV
E,
messages.
curve.
window
iMessage
This
draw
XOR
LONG 1 Param)
1*
Process
in
ctrlZ);
f* WM_LBUTTONDOWN *1
break;
voi d NEAR PASCAL BezTool
handZ,
*f
break;
}
f* draw
handl,
f* NOCSTARTED
1* switch(iState)
*f
;
USE:
hOC,
*1
WM_LBUTTONUP:
switch(iState)
(
hOC -
case
GetDC(hWnd);
DRAG_HANOI:
i State
1* Set
in
extents
range
and
[-15000,
SetMapMode(hDC,
-
so will
*1
+I5000J.
30000,
>
(cxClient
SetViewportOrg(hDC,
cxClient»
RZ_COPYPEN);
? cxClient
-maxCl
1,
: cyClient;
cyClient»
iState
1);
-
will
Illogical
need
units
later
when
pts[OJ.x
-
pts[OJ.y
pt sl I'l .x
-
pts[1].y
-
0;
-
1;
DPtoLP(hDC,
pts,
2);
LogPerDevice
-
(pts[1].x
1* Incomi ng poi nt
in
LOWORD(lParam);
inPt.y
-
HIWORD(lParam);
RZJOPYPEN);
DrawHandle(hDC,
hOC,
&i nPt,
swi tch(
i Message)
little
3x3
boxes
in
*f
DrawHandle().
pts[O].x)
coordi
nates.
(pts[l]
.x
pts[O].x)
(pts[OJ
.x
pts[1].x)
*1
if
*f
coordinates.
( t State
-
area
case
if
not
in
middle
of
Bez.
*f
hWnd,
NULL,
:
ReleaseDC(hWnd,
Application's
IN:
hDlg
handle
iMessage
message
auxiliary
1 Param
unused
TRUE if
processed
"About"
box
-
ctrl1.x
-
inPt.x;
hand1.y
-
ctrl1.y
-
inPt.y;
break;
f* NOT_STARTED *1
1* starting
drag *1
1* store user poi nt
in statics
user
on
lOOK,
message,
clicks
IDCANCEl)
FALSE otherwise.
OK button
*f
close.
f* initialize
dialog
box
*f
(TRUE);
OK box
(wParam
f* recei
selected;
--
lOOK
IDCANCEL
II
wParam
ved
if
-
a command
system
menu
*f
close
command
*1
IOCANCEl)
(
*/
EndDi al og( hDl g,
return
TRUE);
(TRUE) ;
f* exit
dialog
f* did
proccess
f* did
not
box
*/
message
*f
}
handZ.x
-
ctrlZ.x
-
inPt.x;
1* starti
1* store
handZ.y
-
ctr12.y
-
inPt.y;
in
-
when
WM_COMMAND:
if
WAITJOR_CTRLZ:
i State
(act
appropriate
only
WM_INITDIALOG:
f* lOOK if
NOT_STARTED:
hand1.x
info
(i Message)
case
DRAG_HANOI;
message
Return
system
function.
box
type
Closes
case
case
LONG 1 Param)
box
dialog
NOTE:
return
-
hDlg,
dialog
to
RET:
i State)
i State
"About"
wParam
switch
(
case
hOC);
USE:
or
WM_LBUTTONDOWN:
swi tch(
iMessage,
*f
*f
TRUE);
f* WM_RBUTTONDOWN * f
brea k;
unsigned
handl,
handle
COPY mode
;
NOT_STARTED)
Inva 1 i dateRect(
hand2,
1* draw in
ctr l z i ;
hand2);
WORD wParam,
1);
client
ctrl!,
BOOL FAR PASCAL AbouJ;Bez(HWND
WM_RBUTTONDOWN:
f* Erase
ctr12,
f* switch(iState)
*f
f* WM_LBUTTONUP *f
f* switch(iMessage)
*1
(
case
*f
*1
f* DRAG_HANDZ *f
break;
unit
final
handle
COPY mode
f*
logical
DPtoLP(
device
/* COpy pen for
final
break;
devi ce
-
to
draw
>
inPt.x
f* Convert
per
in
NOT_STARTED;
DrawBez(hDC,
1* Calculate
1* COPY pen for
1* draw
hand l ) :
DRAG_HANDZ:
SetROPZ(hDC,
ient);
ctrl!,
f* DRAG_HANOI *f
break;
case
cyClient)
maxClient,
WAIT_FOR_CTRLZ;
DrawHandle(hDC,
30000);
SetviewportExt(hDC,
-
SetROPZ(hDC.
be working
MM_ISOTROPIC);
SetWindowExt(hDC,
maxClient
origin
DRAG_HANDZ:
drag *1
user poi nt
ng
statics
*f
break;
}
return
f* switch
1* WM_CDMMAND *f
(iMessage)
*f
(FALSE);
process
Feb/March
message
*f
1992
29
C: FAST BEZIER CURVES
void
NEAR PASCAL DrawBez(HDC hOC, POINT ctrll,
POINT handl.
POINT hand2, POINT ctr12)
1*
USE:
Draw Bezier
IN:
ctrll,handl.hand2,ctr12:
curve
given
Bez as a polygon.
and handle
control
NOTE: Set up, then call
routine,
generate
points
the
control
points
for
depth
*1
1* depth ~
means we are at the finest
grab point into global
array and return,
Bezier
the recursive
de Casteljau
Windows' Polyline()
displays
BEl_DEPTH - recursive
on poi nts:
sa;
°
points,
and handle
SubDivideBez(),
along the Bez.
1* de Castel j au constructi
POINT qO, ql, q2, rO, rl,
subdivision
level:
breaking
off recursion.
*1
if (!depth)
of de Casteljau.
(
Initial
POINT ctrll
loaded here,
then recursive
routine
calculates
and loads the remaining
2 BECDEPTH - (NUM_BElPTS - 1) de Casteljau
pts. *1
*PtrBezPts++
h
~ p3:
return;
(
PtrBezPts
- BezPts:
*PtrBezPts++
- ctrll:
1* calc pts *1
1* init
1* first
*1
case
1* Calculate
previous
*1
fast
SubDi vi deBez( ctrll,
Polyline(hDC,
ptr to start
of array
control
po i rrt special
handl,
BezPts,
hand2,
ctr12,
NUM_BEZPTS):
BEl_DEPTH):
1* call
q2.x
depth)
Calculate
de Casteljau
construction
points
in two.
pO,pl.p2,p3:
control/handle/handle/control
IN:
subdivide
depth:
current
the
de Casteljau
NOTE: Calculates
recursive
Bezi er can be subdi v i ded into
depth
construction
2 pa rts
(l eft,
recursive
calls
to this
routine.
Recursion
depth, decremented
once for each recursion
and break
for
Bez
Bez to
points
note
s as averages
shi ft ri ght
- (p2.x
»
+ p3.x)
- (p2.y
+ p3.y)
»
I:
3 q' s . *1
rO.y - (qO.y
rl.y - (ql.y
+ ql.y)
+ q2.y)
»
»
1:
1:
I;
q2.y
1* sa is midway between 2 r's and is in middle of original
sO.x - (rO.x + rl.x)
»1:
sO.y - (rO.y + rl.y)
» I:
of algorithm.
then
po i nt
of
is
by 2. *1
1* r' s are mi dway between
rO.x - (qO.x + ql.x)
»1:
rl.x - (q l .« + q2.xl »1;
1*
USE:
division
construction
midway paints):
1* q's are midway between 4 incoming control
and handl e points.
qO.x - (pO.x + pl.x)
» I: qO.y - (pO.y + pl.y) » 1:
ql.x - (pl.x + p2.x) » I: ql.y - (pLy + p2.y) »1;
Windows to draw *1
NEAR PASCAL SubDivideBez(POINT
pO, POINT pl ,
POINT p2, POINT p3, int
void
de Casteljau
points
(ie.,
*1
Bez.
so the
ri ght)
by
1* Decrement depth:
subdivide
incoming
1 eft,
then ri ght. * 1
SubDivideBez(pO,
qo , rO, sO, --depth):
is broken off when
level.
becomes O.
This is the finest
level of subdivision:
the right-most
point on
the small subdivided
Bezier
is also a point on the original
Bezier,
so we load it into global
array BezPts[]
(thru PtrBezPts
which points
into the array).
*1
r l , q2,
SubDivideBez(sO,
void
p3,
Bez into
2 parts:
depth):
NEAR PASCAL DrawHandle(HDC
POINT q)
hOC, POINT p,
1*
Draws handl e on screen
IN:
hOC:
handle
from p to q with
to display
Victor Image Processing Library
powerful
Handles
are
drawn with
hRedPen.
context
p,q : handle start
and end points
NOTE: Don't CreatePen
or delete--these
only.
Announcing version 2:
Use Victor to develop
USE:
little
are
3x3 pixel
done globally
boxes
at each
Each pixel
is LogPerDevice
logical
units:
logical
units
used for the boxes since we are in M~tISOTROP1C mapping
image applications
once
end.
must be
mode.
*1
Work with images of any size -- use
conventional, expanded, and
extended memory
int
int
Now your applications
can support 8-bit color
and gray scale images of any size because
Victor gives you complete
control over conventional,
expanded,
and extended memory.
Display on Super
VGA
Display images on EGA/VGA and super VGA up
to 1024 x 768 256 colors.
and Turbo C environments.
PCX/TlFF/GlF/BIN
Handle images from any source, or create
translation
programs between the popular. file
formats.
Gray scale and color images
1* Init
Catenary
Systems
470 Belleview
for scanner and laser printer.
VI~NMC/COD
St Louis MO 63119
Circle 76 on reader service card
30
PC TECHNIQUES
pen,
select
- SelectObject(hDC,
- q.x
red
#1
pen.
hRedPen):
LineTo(hDC,
«
+ (LogPerDevice
y coord
I pixel
(314) 962-7833
*1
of 1 t t t l e box *1
q.x,
q.Yl:
Remember
1):
below q.y.
*1
LogPerDevi ce :
1* Ora" little
box:
3x3 pixels.
*1
MoveTo(hDC, xLeft,
y): LineTo(hDC,
xRight,
MoveTo(hDC, xLeft,
y): LineTo(hDC,
xRight,
MoveTo(hDC, xLeft,
y): LineTo(hDC,
xRight,
Give YOllr applications support
to order
y coord
1* Draw handle.
*1
MoveTo(hDC, p.x, p.y):
y - q.y
ScanJet and LaserJet support
Cali (314) 962-7833
origPen
xRight
You can offer device control for gray scale
scanning -- AND print halftones at any size.
Victor supports Microsoft C, QuickC, and
TurboC, includes demonstration
and prototyping
sofrware,
and full documentation
..
Victor Library version 2, $195
i*
box at end of handle
box *1
1* Set left
and right
coords around q.x (3 pixels).
Windows 1i nes do not draw 1as t pi xe 1. *1
xLeft
~ q. x - LogPerDevi ce:
Powerful image processing
for all images -your sofrware can have features like: zoom,
resize, brighten,
contrast,
sharpen,
outline,
linearize, matrix conv, colorize, & more.
code available.
y:
/* Save original
Image-based applications can
be developed in MSC, QuickC,
Load & save
Source
xRi ght:
1* DC's orinal
pen * /
1* left
coord of little
1* right
coord of little
HPEN ori gPen:
int
xLeft :
1* Re-select
original
pen.
SelectObject(hDC,
origPen):
*1
y):
y);
y):
y += LogPerDevice:
y += LogPerDevice:
y += LogPerDevice:
*1