Problem Description
Here's program illustrating how to rotate
a figure defined by an array of points without losing accuracy.
Background & Techniques
Joe W. wrote and sent a program illustrating a problem he was having with
distortions introduced as a figure was rotated. The problem is
that points with integer coordinates will generally have non-integer
coordinates after they are rotated by an arbitrary angle. The
trick is to maintain the coordinate values in floating point format and only
round them to integer values when canvas drawing routines require integers.
|
 |
 |
 |
|
Original |
Integer rotate 360°in 10° steps |
Real rotate 360°
in 10° steps |
The program below allows the user to draw an arbitrary figure in either
integer or real format, copy it to a TPaintbox descendent of the the other
format, and rotate both to observe the result.
Two TPaintbox descendent classes, TIntegerRotate and TRealRotate handle the mechanics of capturing data as the mouse is used
to drag draw (moving the mouse with the left button held down) any design
you choose. Each of the classes keeps two copies of the data, one
retains the original data for comparison (FDataOrig) and one to hold
the rotated points (FData).
The original code used a TList control to hold pointers to
TPoint records which are dynamically allocated as they are added.
Current practices and compiler features make the explicit use of pointers
largely unnecessary. In TRealRotate I replaced the Fdata
and FDateOrig Tlists with dynamic arrays.
Compare the two AddItem methods to see the difference.
For some reason, the original AddItem also checked to make sure that
no two matching points were added. That seemed like a bug to me, so I
eliminated that code also.
There are few other points related to creating visual controls that might
be worth mentioning:
- As longtime viewers know, I am not a fan of adding registering user
written components
to Delphi since maintaining their presence across new computers, Windows versions, and
Delphi versions is not likely to happen. Installed visual components
have the advantage though of allowing properties to be set through mouse
movements and the Object Inspector at design time. My solution
is to use a standard component (a TPaintBox in this case) as a
prototype which is passed to my control's Create procedure to provide
the visual aspects.
You can see how that works in this program for the two rotate classes.
- Properties can be defined in classes to allow users indirect access to
some of the data fields. Properties can protect the fields by taking
read access through a function (the Read parameter of the property
definition) for retrieving a value, and through a procedure (the Write
parameter) when the user is allowed to modify a private variable.
You can see examples here for the FShowOriginal Boolean variable
which the user accesses via the ShowOriginal property and the FNumberPoints variable accessed via the
Count property.
- Indexed access to a list or array is also available via an indexed
property, for example the Strings property of a TStringlist or
the Items property of a TListBox. . In our case,
the RData property provide access to the rotated point coordinates.
Check the Copy buttons on the main form to see this in use. By adding the
Default parameter to the definition, the instance name can be
used as a synonym for the indexed property. For example, if we define MyReal:TRealData;
then MyReal[i]
references the same point as RData[i].
Running/Exploring the Program
Suggestions for Further Explorations
???
|
Original Date: October 28, 2007 |
Modified:
November 01, 2007 |
|