Personal C Sharp                                                         By  famsoft.org Home How To Start Examples-Desktop Examples-Web PC# Methods Reference-Desktop Reference-Web
```                                 DEMONSTRATIVE EXAMPLES
======================

EXAMPLES ON DRAWING
===================

Before we see the examples, we need to explain some drawing basics.

CARTESIAN AND POLAR COORDINATES:

As you already know, when we define location of an object we base it on the location of
object's center relative to form's center instead of basing it on the location of top
left corner of the object relative to top left corner of the form. This makes drawing
simpler and more scientific.

When we have been working with "Controls", (j,k,i,o) used to mean (Horizontal position,
Vertical position, Width and height) respectively. Here we'll use the same symbols except
that we need to increase precision, so we'll use the float var's (jf,kf,lf,of) instead.
Again, (lf) replaces (if) since (if) is a C# keyword.

|                                  |   /
-ve jf  |  +ve jf                          |  /
+ve kf  |  +ve kf                          | /
|                                  |/)kf (Angle)
--------------+------------- jf     -------------+-------------
|                                  |
-ve jf  |  +ve jf                          |
-ve kf  |  -ve kf                          |
|                                  |
kb = false                         kb = true;
(Cartesian)                         (Polar)

For drawings, we can define locations using Cartesian or polar coordinates. Whenever you
like to use polar coordinates to define a location, supply:

jf=Radius, kf=Angle between radius and horizontal axis, kb=true (indicating that you are
using Polar Coordinates)

Angles increase in the positive direction when rotations are anti-clockwise which is the
standard in mathematics. To simplify things further, angles are measured in degrees (0:360)
=========================================================================================

EXAMPLE 1: We need to plot the X,Y axes then:

a) Draw, using Cartesian coordinates a square with sides=100 pixels each and center location
on the negative side of the X-axis, 150 pixels away from the origin.

b) Then, draw, using Polar coordinates, a circle with diameter=100 and center at the same
location as the square.

Use a different color for each drawing.
=========================================================================================

public class a : pcs {                        // Always remember to make your ".cs" file
// name match the class name.
public override void init() {
base.init();                              // Always remember, this should be the last
}                                           // statement in method init()
public override void run() {
jf=-250;kf=0;lf=250;of=0;gm("cld");       // Draw line bet. points (-250,0) & (250,0)
jf=0;kf=150;lf=0;of=-150;gm("cld");       // Draw line bet. points (0,150) & (0,-150)

os="X";jf=265;kf=0;gm("ctd");             // Draw letter "X" beside X-axis
os="Y";jf=0;kf=165;gm("ctd");             // Draw letter "Y" above Y-axis

cls="r0";gm("sps");                       // Set pen color at solid red.
jf=-150;kf=0;lf=of=100;gm("crd");         // Location=(-150,0), width=height=100,
// Create rect & draw.
cls="b0";gm("sps");                       // Set pen color at solid blue.
// width=height=100,Create ellipse & draw
}
}
=========================================================================================
TUTORIAL: Some drawings have been demonstrated when we created graphical menues in
the past section which covered "Controls". The only new items here are the drawing of a
line and the use of polar coordinates.

In general, here are the variables which are required for creating shapes:

jf,kf: If Cartesian Coordinates are used (kb=false), they mean Horizontal, vertical
position of object's center relative to form's center where the origin point is.
If Polar coordinates are used (kb=true), (jf) is the length of a line which
connects objects center with form's center and kf is the angle between that line
and the positive X-axis.
lf,of: The width and height of the object respectively.

EXCEPTION: If the object was a "line", (jf,kf)=position of line's start point and
(lf,of)=position of line's end point.

kb:    Used to identify polar coordinates. When we set (kb=true), we mean that (jf,kf)
we are supplying represent radius and angle instead of horiz, vert positions.

jd,kd: Arches require two more parameters and we use jd,kd for them. jd is the start
angle and kd is the extent angle.

SHEAR AND ROTATION: There are two additional features which we can get when creating a
shape object. We can get the shape object sheared horizontally and/or vertically. We can
also get it rotated at any angle we choose.

id,od: Horizontal and Vertical Shear Factors.

IMPORTANT REMARK: You already know that all "i,j,k" based variables (GUV's) are always
reset at the completion of any method. This is why when you like your object to be drawn
at the center you can just assign no values to (jf,kf) since you are certain that their

Now, you see that method gm() is using more var's than just the (i,j,k) based GUV's. You
may be worried about var's like (od) and (ad) We assure you that method gm() resets them
too. Here are all the var's method gm() resets in addition to the (i,j,k) based GUV's:

This should be enough for this example. Explanation of how to set pen and brush colors
is left for another example.
=========================================================================================
```
```EXAMPLE 2: If you are wondering why we need the polar coordinate system, some applications can
be significantly simplified by using this system. Here is a figure which could be hard to draw
using Cartesian coordinates.

=========================================================================================
public class a : pcs {

public override void init() {
base.init();
}
public override void run() {
lf=of=200;gm("ced");                      // Draw a circle, diam=200 pixels
for (float a=0;a<360;a+=60) {             // make a=angle values between 0,300
kb=true;jf=100;kf=a;lf=of=200;          // using polar coord's draw arches with center
jd=(double)a+120;kd=120;                // at radius=100, angle=a, start angle=a+120
}
}
}
=========================================================================================
```
```TUTORIAL:

What are we drawing on?
-----------------------

What we have is the form surface on which we can install controls. We can also paint its
background with any color or tile it with a background image. We can also draw on it
except that we don't like to do so for a reason which we'll explain shortly.

On top of the form is a transparent bitmap image which we draw on. Since the bitmap
image is transparent, everything the form contains shows through it and combines with the
drawings.

In order to maximize simplicity, avoid some errors and insure that your program runs once
only, we include all program code into method run()

Whenever the Form's size is minimized, then restored back to original size, the .NET software
redraws for us all controls and background paint or image on the form, but it does not redraw
anything else which we could have drawn directly on the form using method run(). PC# redraws
the bitmap image with all the drawings on its surface, so we get everything to show up again.

A second advantage of using the bitmap image is that it allows us to save our drawings
into a file with ease and reliability. We'll see an example on that shortly.

One more advantage, is the compatibility between drawing on the form and drawing on
client's browser which we'll see when we get into web developing.

Finally, this way has simplified miximg class (pcs)'s drawings with class (pcs3)'s We'll
see that when we get into the chapter of "Using the Windows Presentation Foundation".

=========================================================================================
EXAMPLE 3: Now, we'll see a demonstration of what you can do with "Shear" and "Rotation".
This is the easiest way to draw a diamond shaped object or a parallelogram.
=========================================================================================
public class a : pcs {

public override void init() {
base.init();
}
public override void run() {

lf=260;of=100;id=-1;od=0;gm("crd");        // Draw a rect with horiz shear factor=-1
cls="b05";gm("sps");                       // Create solid blue pen
lf=of=60;ad=45;gm("crf");                  // Create, fill a square, rotated 45 degrs
cls="r0";gm("sps");                        // Change color to red
lf=of=200;id=0;od=-1;gm("ced");            // Draw a circle with vert shear factor=-1
}
}
=========================================================================================
TUTORIAL:

Shear Factors (id,od):
----------------------

To learn the effect of shearing, assume we have a Rectangle and like to convert it into
a Parallelogram by rotating and stretching either its vertical sides or its horizontal
sides by an angle (a)

The table below shows the possible assignments of the shear factors and the resulting Shape
in each case:

id       od      Transformation result
------   ------  ------------------------------------------------
tan(a)   0       The vertical sides rotate anti-clockwise by (a)
-tan(a)   0       The vertical sides rotate clockwise by (a)
0        tan(a)  The horizontal sides rotate clockwise by (a)
0       -tan(a)  The horizontal sides rotate anti-clockwise by (a)

A combination of shearing, and rotation can make you able to get the necessary
=========================================================================================
```
```EXAMPLE 4: Now, let us get into business, we need to draw both sides of an ace of diamond
card using gradient paint to decorate its back side.
=========================================================================================
public class a : pcs {

public override void init() {
base.init();
}
public override void run() {

//----------------------------- Card's Front side --------------------------------
jf=-150;kf=0;lf=224;of=300;gm("crd");   // Draw card outline rectangle
cls="r0";gm("sps");                     // Set color to pure red, solid pen/brush
fns="trp24";                            // Set font:TimesRoman, plain size=24
os="A";jf=-242;kf=130;gm("ctf");        // Draw the text "A" at top left
os="A";jf=-58;kf=-130;ad=180;gm("ctf"); // Draw same rotated 180 degrees
// at bottom right corner.
jf=-150;kf=0;lf=of=30;ad=45;gm("crf");  // Fill a 30X30 square at card's center
// rotated 45 degrees.
jf=-242;kf=110;lf=of=15;ad=45;gm("crf");// Same but smaller at top left corner
jf=-58;kf=-110;lf=of=15;ad=45;gm("crf");// and at bottom right corner

//----------------------------- Card's Back side --------------------------------
cls="S9";gm("sps");                     // set color back to black solid
jf=+150;kf=0;lf=224;of=300;gm("crd");   // Draw card outline rectangle

cls="r0";i=5;gm("sps");                 // Set pen color to red, size=5, solid
jf=150;kf=0;lf=209;of=285;gm("crd");    // Draw a rectangular frame.

jf=150;kf=0;lf=199;of=275;gm("cr");     // Create inner rectangle without drawing

// from blue to white at an angle of 90 deg
// covering (gpp)'s bounding area.
gm("grf");                              // Render (gpp) and fill with color

JF=new float[]{150,50,250,150};         // Create Gen Path with points defined in
KF=new float[]{138,-138,-138,138};      // JF[],KF[], Point Count=4. Don't draw it.
oi=4;gm("cp");
// new (gpp) object and changing colors at
// opposite direction
gm("grf");                              // Render (gpp) and fill with color
}
}
=========================================================================================
TUTORIAL: The only new items in this example are the gradient paint and the GraphicsPath
object. The rest should be easy to understand. Next example will be entirely on the
GraphicsPath. So we are only going to discuss "Gradient Paint".

To set gradient paint, you need to create the object without drawing, then call method
gm() with your wanted color assigned to (cls) at the correct mode for the gradient paint
type you choose. At the end, you fill the object with the paint.

The color code string (cls) should contain two color codes, the start color followed with
the end color.  When the gradient paint is applied, color will be changing gradually from
the start color to the end one over the object's surface.

There are two types of gradient paint:
(1) Linear: You select this type by calling gm("spl"). In addition to supplying the color
code string, you can also supply the angle at which you like the gradient paint to be

(2) Radial: You select this type by calling gm("spr"). The start color will be applied to
the center of the object and will be gradually changing to the end color as it comes
closer to object's outlines.
=========================================================================================
```
```EXAMPLE 5: Drawing the diamond shape has been easy. How about drawing an "ace of hearts"?
=========================================================================================
public class a : pcs {
public override void init() {
base.init();
}
public override void run() {
cls="S9";gm("sps");                    // Set color to black.
lf=224;of=300;gm("crd");               // Draw card outline rectangle
cls="r0";gm("sps");                    // Set color to pure red.
fns="trp24";                           // Set font:TimesRoman,plain, size=24
os="A";jf=-92;kf=135;gm("ctf");        // Draw the text "A" at top left corner
os="A";jf=92;kf=-135;ad=180;gm("ctf"); // Draw same rotated 180 deg at bottom right

createHeart();                         // Run local method to create the heart (gpp)
// It comes large in size, centered into form
GraphicsPath gpp1=(GraphicsPath)gpp.Clone();            // Make a copy of (gpp)
GraphicsPath gpp2=(GraphicsPath)gpp.Clone();            // Make a second copy of (gpp)

//--------------------------- Drawing the center heart ------------------------------
jd=kd=0.125;kf=9;gm("stu");            // Create a unit Affine Transform to scale
// (gpp) to 1/8th without location change
gm("gtf");                             // Xfrm then render-fill (gpp)

//----------------------------- Drawing Corner hearts -------------------------------
gpp=gpp1;                              // Make (gpp) refer to 1st copy of object
jd=kd=0.05;jf=-97;kf=123;gm("stu");    // Create Affine Xfrms to scale to 5% and move
gm("gtf");                             // it to corner then render & fill (gpp)

gpp=gpp2;                              // Make (gpp) refer to 1st copy of object
gm("gtf");                             // Do the same at other corner except that the
}                                        // Xform also rotates it 180 deg's this time

//--------------------------- Creating the Heart object -----------------------------

void createHeart(){                      // This method creates a large size heart
JF[0]=0;KF[0]=0;OF[0]=0.1f;            // symbol located at Form's center, using
JF[1]=10;KF[1]=30;OF[1]=0.1f;          // the "GraphicsPath".
JF[2]=30;KF[2]=60;OF[2]=0.1f;
JF[3]=60;KF[3]=70;OF[3]=0.1f;          // To get the data, you sketch the heart on
JF[4]=100;KF[4]=60;OF[4]=0.1f;         // paper first and obtain the x,y coordinates
JF[5]=130;KF[5]=30;OF[5]=0.1f;         // of points on it's outline.
JF[6]=150;KF[6]=0;OF[6]=0.1f;
JF[7]=150;KF[7]=-30;OF[7]=0.1f;        // Get as many points as you can for the curved
JF[8]=135;KF[8]=-60;OF[8]=0.01f;       // sections and only the start & end points for
JF[9]=100;KF[9]=-100;OF[9]=0.01f;      // "straight line" sections.
JF[10]=0;KF[10]=-230;OF[10]=0.01f;
JF[11]=-100;KF[11]=-100;OF[11]=0.01f;  // JF[], KF[] are the x,y coordinates for the
JF[12]=-135;KF[12]=-60;OF[12]=0.01f;   // points. OF[] = tension. For a straight lines
JF[13]=-150;KF[13]=-30;OF[13]=0.1f;    // OF[]=0  At sections where you like the curve
JF[14]=-150;KF[14]=0;OF[14]=0.1f;      // not to bend too much or in other words to
JF[15]=-130;KF[15]=30;OF[15]=0.1f;     // be close to a straight line, assign small
JF[16]=-100;KF[16]=60;OF[16]=0.1f;     // value to the tension.
JF[17]=-60;KF[17]=70;OF[17]=0.1f;
JF[18]=-30;KF[18]=60;OF[18]=0.1f;
JF[19]=-10;KF[19]=30;OF[19]=0.1f;
JF[20]=0;KF[20]=0;OF[20]=0;            // Make sure to make your start point also your
// end point since it's a closed figure.

oi=21;gm("cp");                        // total rows=21, create path
}
}
=========================================================================================
TUTORIAL: The comments supplied with the code actually explained it all. However, we
need to clear a point here. All shape objects we are using (including text objects) are
GraphicsPath objects. So what was new in this example?

We needed to unify all shape objects in order to simplify working with them. The
GraphicsPath is an object which you can add any shape object to. So, we have used this
feature to unify them all. Whenever you call method gm() to create any shape, the method
creates a new GraphicsPath, adds the shape you wanted to it and makes (gpp) a reference
to that object. The new object represents your shape since it is the only item it
contains.

In this example, we needed to add some lines and curves to make a closed figure. So, we
used the same GraphicsPath feature to do the job. So all shape objects, simple or complex
are represented by the same object type.

REMARK: Starting at PC# version 4.40, you have another way to generate (gpp) for the Heart
sketch. You can scan the sketch and save it into file then supply the name of the file to
method gm("bg") to create (gpp) for you. Example 11 of the "Imaging" chapter shows you how
to do this job.

Affine transform will be discussed in details at a later example.
=========================================================================================
```
```EXAMPLE 6: In this example, we are going to see how we can give our drawings 3-D look
using 4 different methods.

=========================================================================================
public class a : pcs {                     // Always remember, class name = file name
public override void init() {
base.init();                           // Should be last statement in init()
}
public override void run() {

cls="b7";cm("fcb");                    // Make Foem's background color light blue
jf=-300;lf=of=150;gm("ce");            // Create a circle
gm("grf");                             // Render-fill the circle object.

jf=-100;lf=of=150;gm("ce");            // Create another circle next to previous one
gm("grf");                             // Render-fill the circle object.

//-------------------------- Special Effects - Reflection ---------------------------
lf=of=150;gm("ce");                    // Create a circle at center
jf=100;of=5;cls="b0";ks="r";gm("grs"); // Render with sp effects-reflection at locatn
// (100,0), brightness factor=5,blue color

//----------------------------- Special Effects - Depth -----------------------------
lf=of=150;gm("ce");                    // Create a circle at center
// Render with sp effects-depth at location
// (300,0), 3D angle=30, Depth=20 pixels
//------------------------------ Displaying the Text --------------------------------
ss="                        ";         // String of spaces
fns="trb16";cls="S9";gm("sps");kf=-110;// Font, Color and location = (0,-110)
gm("ctf");                             // Create text object and draw-fill.
kf=-130;
gm("ctf");                             // Draw-fill second line.
}
}
=========================================================================================
TUTORIAL:
(1) To create the gradient paint, we create the shape object first at the location we want
then make the brush which fits the object. When we use the special effects, we create the
object at Form's center then call gm("grs") and supply where we want the object to be.
So, if we want to display more than one copy of the object with special effects at
different locations, we'll still need only one object to create.

(2) The special effects are very useful, so we are going to have two more demonstration
examples on them.
=========================================================================================
```
```EXAMPLE 7: Here is an example on Special Effects - Depth. It shows how to display text
in large size letters with 3-D look.
=========================================================================================
public class a : pcs {                     // Always remember, class name = file name
public override void init() {
base.init();                           // Should be last statement in init()
}
public override void run() {

cm("fwc");float wf=of;                 // Get Form's Client width and height,
cm("fhc");float hf=of;                 // Store them for future use
cls="b2";gm("sps");                    // Prepare light blue paint
lf=wf;of=hf;gm("crf");                 // Fill form's background with it.
fns="trp260";                          // Set the font to TimesRoman, plain,260
xs="PC#";                              // The string to be drawn with sp effects.
float pos=-250;                        // Horiz position of each char to be drawn
char[] C=xs.ToCharArray();             // Scan the string
for (int i=0;i< xs.Length;i++) {       // Read the string characters one by one
os=""+C[i];gm("ct");                 // Get the shape object for each char
cls="s9s0";jf=pos;kf=0;id=20;        // cls=brightest-darkest colors, location,depth
pos+=200;                            // Make each 2 chars 200 pixels apart
}
}
}
=========================================================================================

After method gm("grs") draws the 3D object, the present GraphicsPath object (gpp) which it
ends with is for the front surface of the drawing. After you study the chapter of imaging,
you'll know that this means that you can color the front surface of the drawing with any
special color like "gold" or "chrome".

Here is a new version of example 7 which draws a golden "PC#" string (It requires PC#
version 4.40 or later):

public class a : pcs {                     // Always remember, class name = file name
public override void init() {
j=825;k=425;dm("s");                   // Resize Form
base.init();                           // Should be last statement in init()
}
public override void run() {

cm("fwc");float wf=of;                 // Get Form's Client width and height,
cm("fhc");float hf=of;                 // Store them for future use
cls="b6";gm("sps");                    // Prepare light blue paint
lf=wf;of=hf;gm("crf");                 // Fill form's background with it.
fns="trp260";                          // Set the font to TimesRoman, plain,260
xs="PC#";                              // The string to be drawn with sp effects.
float pos=-250;                        // Horiz position of each char to be drawn
char[] C=xs.ToCharArray();             // Scan the string
for (int n=0;n< xs.Length;n++) {       // Read the string characters one by one
os=""+C[n];gm("ct");                 // Get the shape object for each char
cls="o8Y6";jf=pos;kf=0;id=20;        // cls=brightest-darkest colors, location,depth
gm("gc");                            // Set the front surface of character as clip area
lf=of=400;fls="images\\gold.bmp";gm("blf");
// Start a new bip using "gold.bmp" image scaled to be
jf=pos;gm("br");                     // slightly larger than (gpp) and draw it over (gpp)
gm("gcn");                           // Cancel previous setup of Clip area
pos+=200;                            // Make each 2 chars 200 pixels apart
}
}
}
=========================================================================================
```
```EXAMPLE 8: And here is an example on Special Effects - Reflection. It shows how to draw
a piece of jewelry.
=========================================================================================
public class a : pcs {                     // Always remember, class name = file name
public override void init() {
base.init();                           // Should be last statement in init()
}
public override void run() {
lf=of=170;gm("ce");                    // Create the circular gold plate
cls="o5y5";gm("spl");                  // Prepare linear gradient brush for it
gm("grf");                             // then render-fill the gold plate.
lf=6;of=50;gm("c=");                   // Create hexagon shape object at center.
cls="r0";ks="r";gm("grs");             // Draw the object using sp effects-refl at
jf=45;cls="b0";ks="r";gm("grs");       // center in red, then repeat 6 times using
jf=-45;cls="b0";ks="r";gm("grs");      // different colors and different locations
jf=22;kf=40;cls="g0";ks="r";gm("grs");
jf=-22;kf=40;cls="m0";ks="r";gm("grs");
jf=22;kf=-40;cls="m0";ks="r";gm("grs");
jf=-22;kf=-40;cls="g0";ks="r";gm("grs");
lf=of=25;gm("ce");                     // Create a circle at center (pearl)
for (int x=0;x<20;x++) {               // Draw it 20 times using sp effects-reflection
jf=80;kf=18*x;kb=true;               // at locations around the plate. Polar coord's
cls="p0";ks="r";gm("grs");           // are used for specifying locations
}
}
}
=========================================================================================
graphics related objects are created and used.

The present object:
-------------------

For simplicity and memory management, PC# works with only one object of each type at a
time. When you call gm("cr") to create a rectangle for example, (gpp), the "present
GraphicsPath object reference" will become a reference of the GraphicsPath object which
contains the rectangle. Any operation you do next will be performed on the rectangle since
it becomes the one and only GraphicsPath object available.

This situation stays on until you create another shape, then the (gpp) becomes a reference
to the new GraphicsPath object and the object which contains the rectangle will have no
reference, so C#'s garbage collection thread will dispose it and free the resources it
occupies.

Now, you may like to say "If I like to operate on the rectangle again, how can I do it?
The answer is that you should create another reference to the rect object before you
create the new object. Here is a code example:

lf=of=100;gm("cr");                        // Create a square
// Do some operations on the square
GraphicsPath square = gpp;                 // Create another reference to the square object
lf=of=150;gm("ce");                        // Now create a circle
// Do some operations on the circle
gpp=square;                                // make (gpp) a reference to the old object
// Do more operations on the square

Objects available for graphics:
-------------------------------

At any moment only one of each of the following objects are available:

grp: Present Graphics object. This object represents the surface on which all graphics are
drawn. At the start it is the "default graphical output device" (bio) which is a
transparent bitmap object of the same size as the form. Your program can change the
device (grp) refers to at any moment by calling gm("sdx") where x can be 'b' meaning
"present bitmap object" or 'd' to return back to the default.
gra: The Form's graphics object. It is the underlayment where all controls are on.
Anything drawn to this surface will show through the transparent object above it,
represented by (grp) Each time you draw an object on the default device bitmap (bio),
the default bitmap object is automatically redrawn to (gra) This auto-display feature
can be stopped by gm("dn") and you can then redraw the object whenever you want
with gm("d")
gpp: GraphicsPath object. Before your program creates any of this object type, gpp refers
to a null object.
bip: Bitmap object. You can either create a fully transparent new bitmap object using
method gm("bn") or create one from an image file. You can make (bip) your graphical
output device so that you can draw on its surface, then you can return to the default
graphical device and draw (bip) on it at any location you like. Drawing shadows
normally requires this operation. Before your program creates any of this object type,
bip refers to a null object.
bio: A fully transparent Bitmap object which is the default graphical device. See "grp"
and "gra" for more. You do not create this object.
spp: Solid Pen object. Before your program creates any of this object type, (spp) is
for for a black pen of width=1 pixel.
sbp: Solid Brush object. Before your program creates any of this object type, (sbp) is
for a black brush of width=1 pixel.
lgp: Linear gradient paint brush object. Before your program creates any of this object
type, lgp refers to a null object.
type, rgp refers to a null object.
tbp: Texture paint brush object. Before your program creates any of this object type,
tbp refers to a null object.
utp: Unit's Affine transform. A general transform for shapes and bitmap transformation.
Before your program creates any of this object type, utp refers to null object.
pdp: PrintDocument object. You create it when you initialize a new Print job.
Before your program creates any of this object type, pdp refers to null object.
=========================================================================================
```
```==================================================================================================

REMARK: We can draw an object with both special effects applied to it (Depth and Reflection)
Here is an example for drawing a hexagon with both special effects:

public class a : pcs {
public override void init() {
base.init();
}
public override void run() {
//------------------------------ Draw a hexagon with Sp. Effects-Depth ------------------------
lf=6;of=150;gm("c=");                     // Create a hexagon at center
cls="p0P3";id=30;ad=10;ks="d";gm("grs");  // Apply sp effects-depth, 3D angle=10, Depth=30

//-------------- Draw a hexagon with Sp. Effects-Reflection on its front surface --------------
gm("gx");om("tf");float j1f=of;           // Get X-Coordinate of last GPP object, assign to j1f
gm("gy");om("tf");float k1f=of;           // Get its Y-Coordinate also and assign it to k1f
lf=6;of=150;gm("c=");                     // Create same hexagon again at center
jf=j1f;kf=k1f;of=5;cls="p0";ks="r";gm("grs");
}                                           // Apply Sp Effects-Reflection and draw it above front
}                                             // surface of the first drawing.
===================================================================================================

================

===================================================================================================
EXAMPLE 9: Here is an example on drawing shadows and the use of Affine transform.
===================================================================================================
public class a : pcs {                     // Always remember, class name = file name
public override void init() {
base.init();                           // Should be last statement in init()
}
public override void run() {
gm("dn");                              // Turn off auto-display

//----------------------- Drawing a pattern on the background -----------------------
cls="y0";gm("sps");                    // Set color to yellow.
for(int i=-180;i<181;i+=4) {           // Fill drawing area with horizontal yellow
kf=i;of=2;lf=360;gm("crf");          // lines, 2 pixels thick to use as background
}
xs="Windows.NET Programming At Its Best ";
xs+=xs;                                // String to be drawn twice.

//--------------------------- Drawing the string's shadow ---------------------------
fns="crb24";                           // Set font to bold courier, size 24.
lf=of=320;gm("bn");                    // Create 320 X 320 Bitmap.
gm("sdb");                             // Set output device to (bip) to draw on it.
cls="S92";gm("sps");                   // Set color to (20% opaque) black
char[]XC=xs.ToCharArray();
for (x=0;x< xs.Length;x++) {           // Scan arch string, assign its char's one by
os=""+XC[x];                         // one to (os) then create (gpp) representing
gm("ct");                            // the char if drawn at at center.
lf=150;of=180-5*x;kb=true;          // Modify (utp) to xfrm char's to points of
gm("stu");                           // and making a full circle.
gm("gtf");                           // Xfrm & fill (gpp) of each char.
}
gm("sdd");                             // Return output device to its default setting.
k=2;gm("bb");                          // Blur (bip) to make it look like a shadow.
jf=0;kf=-10;gm("br");                  // Draw the bitmap with the shadow on it off
// center by 10 pixels (down)
//------------------------------- Drawing the string --------------------------------
cls="b0";gm("sps");                    // Set color to pure blue
for (x=0;x< xs.Length;x++) {           // Draw the same circle of chars directly on
os=""+XC[x];                         // the default graphical output device,
gm("ct");                            // exactly at its center
lf=150;of=180-5*x;kb=true;
gm("stu");
gm("gtf");
}
//-------------------------- Drawing the large "PC#" shadow -------------------------
gm("stu");                             // Reset (utp)
fns="trb84";                           // Change font to a larger Times Roman font.
lf=of=200;gm("bn");                    // Create 200 X 200 Bitmap object
gm("sdb");                             // Set output device to (bip)
cls="S92";gm("sps");                   // Set Color to black with 20% opacity.
os="PC#";gm("ctf");                    // Draw-fill the string on the bitmap.
k=3;gm("bb");                          // Blur the shadow on (bip).
jf=0;kf=-15;gm("br");                  // Draw shadow shifted 15 pixels down.

//--------------------------- Drawing the large "PC#" body --------------------------
os="PC#";gm("ct");                     // Create text object
cls="y39o09";id=10;                    // cls=brightest-darkest colors, depth=10
ad=60;ks="d";gm("grs");                // 3D angle=60,render with sp effects-depth

gm("d");                               // Display graphics.
}
}
===================================================================================================
TUTORIAL:

Why have we turned auto-display off?
------------------------------------

The program runs slow and flickers a lot, so it seemed to be more covenient to keep
display off until the drawing is complete.

Why did we need the yellow background pattern?
----------------------------------------------

To improve shadows' look. There are three items combination which makes Shadows look real:
(1) Drawing something rough on the background.
(2) Drawing them in partially transparent gray color.
(3) Blurring them.

AFFINE TRANSFORM:
-----------------

Affine transform is a mapping between the current coordinate system and a new coordinate
system which you like to switch to.  It can be represented by a matrix which contains the
scaling, shearing, rotation and translation parameters.

The order of operations is important.  For example, rotating an object around  form's
center then moving it to a new location is not the same as if you move the object first
then rotate it.

With PC# you need not to worry.  You call method gm("stu") with all the parameters at once
and  everything will be done at the right order. Mode "stu" is for "unit transform setup"
which is a general Affine transform for shapes, bitmaps, brushes, etc.

Before you decide which parameters to use, think about it this way: You are allowed to do
the following to your object at this exact order:

(1) Scale it by (jd,kd) while maintaining its center at the same location.
(2) Shear it by (id,od) while still maintaining its center at the same location.
(3) Rotate it by (ad) around its own center, so its center still keeps its position.
(4) Move it, so its center moves horizontally and vertically by the pixel amounts (lf,of)

REMARK: Everthing is assumed to be done using PC#'s logic (which is the standard in mathematics)
This means that rotating by a positive angle means rotating anti-clockwise and moving
vertically by a positive amount means moving up.

Affine transform parameters:
----------------------------

jf,kf      The original coordinates of object's center relative to form's center (before
the transform is applied to the object)  If you create all your objects
centered at Form's center, you'll have no need to assign values to jf,kf.

lf,of      The (x,y) displacements of the object.  In case the original object center
was at (0,0), (lf,of) simply mean the new location of the object center.
REMARK: If you supply (kb=true) meaning that you're using polar coordinates,
(lf,of) become the amounts to change radius and angle with.

jd,kd      The scale factors. If equal to (2,0.5), the object will be enlarged to double
the size horizontally and reduced to half the size vertically.  If either
value is negative, the object will be inverted.  For example (jd=-1) produces a
mirror image of the original object with the vertical axis acting as the mirror.

id,od      The shear factors. The effects of shearing have been discussed before.

ad         The rotation angle in degrees.  You need to understand that the rotation is
going to be around object's center which is point (jf,kf) not around form's
center.

ob         (ob=true) means apply transforms at normal order. If present unit transform (utp)
contains a previous transform, apply original transform first and this one next.

ib         (ib=true) means apply transforms at reverse order. If present unit transform (utp)
contains a previous transform, apply this transform first and the original one next.

REMARK: if both (ib,ob) are false, the new transform will replace the original one.

Transformation order:
---------------------

As stated before, PC# takes care of applying each of the required transformations at the
same order.  The order is as follows:   Scale - Shear - Rotate - Translate.
If you are adding a transform to another, you need to care about the application order.
Parameters (ib) and (ob) set the application order.
===================================================================================================
```
```EXAMPLE 10: This example shows how to make the object you are drawing look elevated up
or pushed down.
=========================================================================================
public class a : pcs {                     // Always remember, class name = file name
public override void init() {
base.init();                           // Should be last statement in init()
}
public override void run() {
//-------------------- Painting background --------------------------
cm("fwc");float wf=of;                 // Get Form's Client width and height,
cm("fhc");float hf=of;                 // Store them for future use
cls="s4";gm("sps");                    // Prepare light gray paint
lf=wf;of=hf;gm("crf");                 // Fill form's background with it

xs="Personal C Sharp";                 // Prepare string to be drawn
fns="trb84";                           // Set font to TimesRoman, bold, 84

//-------------------- "Elevated up" String --------------------------
y=40;                                  // Vert pos where String is to be drawn
cls="s9";gm("sps");                    // Set color to white
os=xs;kf=y+2;gm("ctf");                // Draw text slightly above (y)
cls="S9";gm("sps");                    // Set color to black
os=xs;kf=y-2;gm("ctf");                // Draw text slightly under (y)
cls="s4";gm("sps");                    // Set color to wanted foreground color
os=xs;kf=y;gm("ctf");                  // Draw text exactly at (y)
//-------------------- "Pushed down" String --------------------------
y=-40;                                 // Vert pos where String is to be drawn
cls="S9";gm("sps");                    // Set color to black
os=xs;kf=y+2;gm("ctf");                // Draw text slightly above (y)
cls="s9";gm("sps");                    // Set color to white
os=xs;kf=y-2;gm("ctf");                // Draw text slightly under (y)
cls="s4";gm("sps");                    // Set color to wanted foreground color
os=xs;kf=y;gm("ctf");                  // Draw text exactly at (y)
}
}
=========================================================================================
TUTORIAL:

Easy example. All we needed to do, was to display the string 3 times one time in black
color, one time in white color and one time in the wanted color between the first two.

The white colored string looks like a reflection and the black colored string looks like
a shadow. Since we normally expect the light source to be at the top, when we see a
shadow at the bottom and a reflection at the top, we feel that the object is elevated up
and when we see a shadow at the top and a reflection at the bottom, we feel that the
object is pushed down.
=========================================================================================
```
```EXAMPLE 11: Now, let us see how we can save our drawing into file. We are going to save
the piece of jewelry created in example 8 into a "jpeg" format file named
"x.jpg".
=========================================================================================
public class a : pcs {                     // Always remember, class name = file name
public override void init() {
base.init();                           // Should be last statement in init()
}
public override void run() {
j=k=200;cm("fs");                      // Resize form to fit object
cls="s7";gm("sps");                    // Prepare light gray paint
lf=of=200;gm("crf");                   // Fill form's background with it.

//------------------------- Drawing the object -------------------------
lf=of=170;gm("ce");                    // Create the circular gold plate
cls="o5y5";gm("spl");                  // Prepare linear gradient brush for it
gm("grf");                             // then render-fill the gold plate.
lf=6;of=50;gm("c=");                   // Create hexagon shape object at center.
cls="r0";ks="r";gm("grs");             // Draw the object using sp effects-refl at
jf=45;cls="b0";ks="r";gm("grs");       // center in red, then repeat 6 times using
jf=-45;cls="b0";ks="r";gm("grs");      // different colors and different locations
jf=22;kf=40;cls="g0";ks="r";gm("grs");
jf=-22;kf=40;cls="m0";ks="r";gm("grs");
jf=22;kf=-40;cls="m0";ks="r";gm("grs");
jf=-22;kf=-40;cls="g0";ks="r";gm("grs");
lf=of=25;gm("ce");                     // Create a circle at center (pearl)
for (int x=0;x<20;x++) {               // Draw it 20 times using sp effects-reflection
jf=80;kf=18*x;kb=true;               // at locations around the plate. Polar coord's
cls="p0";ks="r";gm("grs");           // are used for specifying locations
}
//---------------------------- Saving (bio) ---------------------------

bip=bio;fls="x.jpg";gm("bsj");         // Make (bip) refer to same object (bio) refers
// to, then save it in "jpeg" format
}
}
=========================================================================================
TUTORIAL:

The code remained the same, we have only added some code at the top to reduce form's
size (which automatically reduces the default graphical device bitmap (bio) to the same
new size. We have then painted the background of (bio) with light gray paint. After
drawing the object we saved it into a jpg file.

As you know (bio) is transparent, we depend on the Form underneath it in adding background
to our drawings. This works fine as long as we are only interested in drawing them to the
screen. If we want to save (bio), we must add the missing background.

The jpg format is great in memory saving. The bmp format uses a lot more memory but looks
better. Reducing form's size is essential in order to reduce the file size.
=========================================================================================
```
```EXAMPLE 12: Now, let us read the file back and draw the image.
=========================================================================================
public class a : pcs {                     // Always remember, class name = file name
public override void init() {
base.init();                           // Should be last statement in init()
}
public override void run() {
fls="x.jpg";gm("blf");                 // Create a new bitmap object and load the
// file into it at full scale.
gm("br");                              // Render (bip) to default graph. output device
}
}
=========================================================================================
```
```EXAMPLE 13: Let us now write a program to show how to handle a mixture of Controls and
drawings and how to make them control each others. We are going to create two combo boxes
and a button in between on the form. On the default graphical output bitmap (bio) above,
we are going to draw 4 different shapes. The user selects a shape and a color by the
combo boxes then click the button to start the execution. When this happens, the selected
shape will be filled with selected color.

Additionally, the jewel object which we have saved in a file will be used as the
background image for the button. The button itself will be transparent so the user will
be clicking on the drawing.

Make sure that the jewel file "x.jpg" is available. If not, run Example 11 to generate it.

=========================================================================================
public class a : pcs {

public override void init() {
bli=1;                                 // Initialize at block 1
base.init();
}
public override void setup() {

cs="bt0";i=100;o=100;cls="s79s70";ib=true;ims="x.jpg";cm("i");
// Create bt0 with background image
cs="ch0";cus="Select a Shape";j=-250;i=190;o=40;cls="b0s3";fns="trb16";
CIS=new string[] {"Triangle","Square","Circle","Pentagon"};
cm("i");                      // Combo box ch0 with choices of shapes
cs="ch1";cus="Select a Color";j=250;i=190;o=40;cls="b0s3";fns="trb16";
CIS=new string[] {"Red","Green","Blue","Black"};
cm("i");                      // Combo box ch1 with choices of colors
}

public override void update() {

//  Var's: s=Shape object number
//         c=Color order number

if ("bt0".Equals(cs)) {                // if bt0 clicked
cs="ch0";cm("gu");                   // Get ch0 update (selected index)
s=cui;                               // Assign selected shape index to (s)
cs="ch1";cm("gu");                   // Get ch1 update
c=cui;                               // Assign selected color index to (c)
bli=2;um("b");                       // Goto block 2 for execution
}
}
public override void run() {
if (blp==1) {                          // Initialization block

jf=-250;kf=90;lf=3;of=150;gm("c=d"); // Draw the triangle
jf=250;kf=100;lf=of=120;gm("crd");   // Draw the square
jf=-250;kf=-100;lf=of=130;gm("ced"); // Draw the circle
jf=250;kf=-100;lf=5;of=150;gm("c=d");// Draw the pentagon
}
if (blp==2) {                          // Execution block

cls="r0 g0 b0 S9 ".Substring(3*c,2); // Get (cls) for selected color
gm("sps");                           // Create solid pen/brush for the color

if (s==0) {                          // If item 0 was selected, draw-fill triangle
jf=-250;kf=90;lf=3;of=150;gm("c=f");
}
else if (s==1) {                     // If item 1 was selected, draw-fill square
jf=250;kf=100;lf=of=120;gm("crf");
}
else if (s==2) {                     // If item 2 was selected, draw-fill circle
jf=-250;kf=-100;lf=of=130;gm("cef");
}
else if (s==3) {                     // If item 3 was selected, draw-fill pentagon
jf=250;kf=-100;lf=5;of=150;gm("c=f");
}
}
}
}
=========================================================================================
TUTORIAL:

Drawing the button:
-------------------

We wanted both the foreground and the background colors of the button to be transparent.
This is not completely allowed for controls. Foreground colors are always fully opaque
even if we set them to be transparent. Background colors for most controls cannot be
transparent either. Fortunately the button is one of few controls which can be of
transparent background. The Label and Panel controls share this ability with the button.

In method setup(), we made the assignments (ib=true;cls="s79s70") for the button. This
means that we have requested the button to be "Flat" and to use for both foreground and
background the color "s7" which is used for form's background and also for image's
background. We selected the foreground to be fully opaque and the background to be fully
transparent.

Why did we want the button to be flat? This is because the standard botton has a three
dimensional border which will prevent it from being invisible. The flat button also has a
border except that its border is a rectangle drawn around it using the button's foreground
color. So, we can make a flat button invisible by making its foreground color invisible.

We can't make the foreground color transparent as mentioned before, but we can make it match
the form's background color which will do the same job and this is what we have done.

The background color can be made transparent by making its opacity digit "0". It also can be
made invisible by selecting the color which matches the image's background color. We have done
the two together which has actually been more than necessary.

Making a drawing clickable:
---------------------------

Using the same technique you can draw any object and make it clickable. This time we used
an object which was saved into a file, but this is not necessary. You can draw the object
on the present bitmap object (bip) and make (bip) the background image of a transparent
button of equal size to its bounding rectangle. To set (bip) as the background image of any
control assign "b" to (ims) and call cm("sg").  You can also do the same during setup using
method cm("i")

What could happen if an object is drawn on the top of a control?
----------------------------------------------------------------

This example opens the road for a new discussion. The controls and the drawings in the
example have been apart from each other, so there has been no visibility problem for
either one. What could happen if an object was drawn on the top of a control?

What we have is the form with all the controls installed into it, and a transparent
bitmap object above it containing the drawings. So, theoritically, the object drawn on the
bitmap should cover the control making it invisible. In reality, this is not allowed.
Although the object can hide the form's body, it cannot hide the controls it contains.
The only way you can prevent a control from showing through your object, is by making the
control invisible.

The "text screen" is made of controls which cover the entire form, so if you turn it on,
you'll have no chance to see any graphics. However, method tm() can make the text screen
temporarely invisible allowing graphics to appear. To allow graphics to be visible, call
tm("vg") To allow text to return back to visiblity call tm("vt")
=========================================================================================
```
```                                     PRINTING
==========

Printing has been simplified considerably. The printing job starts by calling gm("po") to open
a new printing operation.  Then, you can do any of the following:

(1) Obtain (get) Printer's default setup values using modes starting with "pg".
(2) Change (set) Printer's default setup values using modes starting with "ps".
(3) Print text or image using those default values. The printing modes start with "pr".

At the end, you call gm("pc") to close the printing operation.

The changes which you make to the default setup values in (2) are temporary. Their effect ends
when you close the printing operation.

OBTAINING PRINTER'S SETUP VALUES:
=================================

The modes of method gm() which can get the printer's setup values for you are made to be easy to
remember. They are divided into two categories:

(1) Selection Lists:
--------------------

There are normally more than one printer accessable to your computer. One of them is considered
to be the default printer. You may like to use the default printer or you may like to obtain
the installed printers list in order to select the one which suites your printing job.

After you select the printer, there will be more selectios to be made. Each printer supports a
variety of resolutions. You may like to use the default resolution or you may like to obtain the
resolution list in order to pick one.

Similarly, each printer may have more than one paper tray (called paper source), you may like to
use the default one or to obtain a list of their names in order to select one. Finally, you may
like to use the default paper size or you may like to obtain the paper size list in order to pick
one.

The mode strings for getting printer selection lists are as follows:

pgP: Get Printer selection list.
pgR: Get Resolution selection list.
pgS: Get Paper Source selection list.
pgZ: Get Paper Size selection list.

The output of each of these modes is assigned to OS[]. Each row of OS[] is assigned one list item
preceded with a colon then with the item's index in the original collection. The item's index
may not match its row number in array OS[]. This is because method gm() purges the collections
and usually eliminates some invalid items before adding each list to OS[].

Here is a sample output which you get by calling gm("pgP") then displaying each row of the
returned array OS[]:

0:Lexmark 1020 Color Jetprinter
1:HP Photosmart D5100 series
2:HP DeskJet 612C

Here is a sample output which you get by calling gm("pgR") then displaying each row of the
returned array OS[]:

4:300
5:600
6:1200

Here is a sample output which you get by calling gm("pgS") then displaying each row of the
returned array OS[]:

0:Automatically Select
1:Main tray
2:Photo Tray
3:CD/DVD Tray

Here is a sample output which you get by calling gm("pgZ) then displaying each row of the
returned array OS[]:

0:Letter
1:Legal
2:Executive
3:A4
4:A5
5:B5 (JIS)
6:Envelope #10
7:Envelope DL

Some of these lists can be very long. The process of obtaining them is slow and may take several
minutes.

Using these lists is unified. You assign the index of the item which you like to select to (i)
and call the matching "Printer Setup" mode. For example if you like to select the second
printer on the Printer Selection list use the code {i=1;gm("psP");} and if you like to select a
resolution of 600, use {i=5;gm("psR");}

(2) Default Values:
-------------------

If you like to know which is the default printer, the default resolution for that printer, the
default paper source or the default paper size, use the same modes with replacing upper case
letters with lower case ones. Here are the modes:

pgp: Get Default printer name.
pgr: Get Default resolution.
pgs: Get Default paper source.
pgz: Get Default paper size.

There are modes also to obtain necessary measurments for printing. All measurments are in one
hundredth of an inch unit. They are:

pgx: Get left margin width.
pgy: Get top  margin width.
pgw: Get Printable Page's width.
pgh: Get Printable Page's height.

pgt: Get Total number of lines per page when a specific font is used.

The outputs of all these modes come assigned to (os) Here is a sample output for each mode:

pgp: os="HP Photosmart D5100 series";
pgr: os="600";
pgs: os="Automatically Select";
pgz: os="Letter";

pgx: os="100";
pgy: os="100";
pgw: os="824";
pgh: os="1076.333";

pgt: os="47";

CHANGING PRINTER'S SETUP VALUES:
================================

The changes which you can make here to the default values are temporary. They are effective
within one print operation only. The original default values will return after closing the
current operation by calling gm("pc")

Here are all the modes which you can use to change default values:

Requesting Special features:
----------------------------

psc   USE: Set "Color"-"B/W" setup.      IN:ib=flag, ib=true means "in color"
psl   USE: Set "Landscape" flag          IN:ib=true means "use Landscape"
psf   USE: Set "Print to File" option.   IN:ib=true means "Print to file"
pst   USE: Set Collate option.           IN:ib=true means "Collate output"

Setting Margins, number of copies and page range:
-------------------------------------------------

psm   USE: Set Margins (in 1/100th of an inch)  IN:j,k,i,o=left, right, top, bottom
psn   USE: Set Number of Copies.                IN:o=number of copies wanted.
psr   USE: Set Page range.                      IN:(j,k)=(From,To) page numbers

Setting Printer name and document name:
---------------------------------------

psp   USE: Set Printer Name.             IN:os=Printer Name
psd   USE: Set Document Name.            IN:os=Document Name

Setting values which have been selected from lists:
---------------------------------------------------

psP   USE: Set def Printer call gm("pgP") first. IN:i=index of OS[] where desired printer name is
psR   USE: Set Resolution. Call gm("pgR") first. IN:i=index of OS[] where desired resolution is.
psS   USE: Set Resolution. Call gm("pgS") first. IN:i=index of OS[] where desired PaperSource is.
psZ   USE: Set Resolution. Call gm("pgZ") first. IN:i=index of OS[] where desired PaperSize is.

Setup using Printer Information and Page setup dialog boxes:
------------------------------------------------------------

psi   USE: Show Print Information dialog to allow user selection.
psg   USE: Show Page Setup dialog to allow user selection.

Notice that the default printer can be set either by its name using mode "psp" or by its order
number in the installed printers list using mode "psP".

EXAMPLE:
--------

This example demonstrates how selection lists are obtained and how to select an item from each
list and set printer defaults accordingly. So compile it, run it and practice with it to
familiarize yourself with your printers and their setup values.

Since nothing is going to be printed following the setup, this example will do nothing more than
helping you to learn. All the changes you make will become ineffective once the printing
operation is closed.

--------------------------------------------------------------------------------------------
public class a : pcs {

public override void run() {

cm("fe");
j=78;k=20;dm("cs");              // Recize Console to 78 char's/line - 20 lines/page
cls="s0";dm("ccb");              // Paint background with light gray.

gm("po");                        // Open new printing operation

//------------------------------ Selecting a Printer -----------------------------------
tm("c");                                // Clear Screen
cls="r0";os="Installed Printers: ";tm();os="";tm();
gm("pgP");cls="S9";                     // Get installed printer list.
for (x=0;x< OS.Length;x++) {            // Scan list
os=OS[x];tm();                        // and display each item.
}
os="";tm();cls="b0";                    // Skip a line, change color
os="Select a printer: ";tm("i");        // Instruct user to select a printer.
om("ti");i=o;gm("psP");                 // Convert to int, assign to (i) and call gm("psP")

//----------------------------- Selecting a Resolution ---------------------------------
tm("c");                                // Clear Screen
cls="r0";os="Available Resolutions: ";tm();os="";tm();
gm("pgR");cls="S9";                     // Get resolution list.
for (x=0;x< OS.Length;x++) {            // Scan list
os=OS[x];tm();                        // and display each item.
}
os="";tm();cls="b0";                    // Skip a line, change color
os="Select a resolution: ";tm("i");     // Instruct user to select a resolution.
om("ti");i=o;gm("psR");                 // Convert to int, assign to (i) and call gm("psR")

//----------------------------- Selecting Paper Source ---------------------------------
tm("c");                                // Clear Screen
cls="r0";os="Available Paper Sources: ";tm();os="";tm();
gm("pgS");cls="S9";                     // Get paper sources list.
for (x=0;x< OS.Length;x++) {            // Scan list
os=OS[x];tm();                        // and display each item.
}
os="";tm();cls="b0";                    // Skip a line, change color
os="Select a paper source: ";tm("i");   // Instruct user to select a paper source.
om("ti");i=o;gm("psS");                 // Convert to int, assign to (i) and call gm("psS")

//------------------------------ Selecting Paper Size ----------------------------------
tm("c");                                // Clear Screen
cls="r0";os="Available Paper Sizes: ";tm();os="";tm();
gm("pgZ");cls="S9";                     // Get paper sizes list.
for (x=0;x< OS.Length;x++) {            // Scan list
os=OS[x];tm();                        // and display each item.
}
os="";tm();cls="b0";                    // Skip a line, change color
os="Select a paper size: ";tm("i");     // Instruct user to select a paper size.
om("ti");i=o;gm("psZ");                 // Convert to int, assign to (i) and call gm("psZ")

//-------------------------- Display modified default settings -------------------------
tm("c");                               // Clear Screen
cls="r0";os="Current default values: ";tm();os="";tm();
cls="S9";
gm("pgx");os="Left Margin ........... : "+os;tm();
gm("pgy");os="Top  Margin ........... : "+os;tm();
gm("pgw");os="Printable area's Width  : "+os;tm();
gm("pgh");os="Printable area's Height : "+os;tm();
gm("pgp");os="Name of selected Printer: "+os;tm();
gm("pgr");os="Selected resolution ... : "+os;tm();
gm("pgs");os="Selected paper source . : "+os;tm();
gm("pgz");os="Selected paper size ... : "+os;tm();
fns="cr12";
gm("pgt");os="Number of lines / page when font code 'cr12' is used: "+os;tm();
gm("pc");                              // Close printing operation
}
}
--------------------------------------------------------------------------------------------

PRINTING:
=========

There are 3 modes for printing:

prb   USE: Print the present Bitmap object (bip)
IN : jf,kf=Location of (bip)'s center relative to paper's center
lf,of(optional)=size to scale to.
Default:full size, putting into consideration the resolutions of (bip) in pixels/inch
and the printer resolution in dots/inch.
REMARK: jf,kf,lf,of are in (1/100)th of an inch unit.

prt   USE: Print contents of Text Array OS[]
IN:OS[]=Text Array, oi=Number of text rows,  fns=Font code

prf   USE: Print a Text File.
IN: fls=Text File Name   fns=font code

The last two modes can print as many pages as it takes to complete the job while the first mode
which prints (bip) is expected to print one page only.

==============================================================================================
EXAMPLE 14: We like to do the following:

(a) Print a number of text lines on a page. Use the "Print setup" dialog to select the
number of copies to be printed.

(b) Display some text lines on the "text screen" then print them. Select the number of
copies programatically.

(c) Print a text file. Use the "Page Setup" dialog to select orientation manually.

(d) Create a bitmap object loading an image file into it then print it. Set the
orientation programmatically in this case.

(e) Finally obtain the number of printable lines per page when a specific font is used.
==============================================================================================
public class a : pcs {                     // Always remember, class name = file name
public override void init() {
tia=toa="t";                           // Select "text screen" display
base.init();                           // Should be last statement in init()
}
public override void run() {
//----------------------------- (a) Printing Text Lines -----------------------------
gm("po");                              // Open a new printing operation
gm("psi");                             // Show Print Information Dialog
OS[0]="Text Line 1";                   // Populate string array OS[] with
OS[1]="Text Line 2";                   // two lines of text
oi=2;                                  // Number of lines
fns="trp12";gm("prt");                 // Print OS[]'s text using supplied font
gm("pc");                              // Close printing operation
//-------------------------- (b) Printing Text Screen's Text -------------------------
os="Text Line 1";tm();                 // Display tow lines of text
os="Text Line 2";tm();                 // on the "text screen"
gm("po");                              // Open a new printing operation
o=2;gm("psn");                         // Select "number of copies=2"
tm("g");                               // Get text screen's text lines in (OS[]) and
// number of lines in (oi)
fns="crp14";gm("prt");                 // Print OS[]'s text using supplied font
gm("pc");                              // Close printing operation
//----------------------------- (c) Printing a Text File -----------------------------
gm("po");                              // Open a new printing operation
gm("psg");                             // Show Print Page Dialog
fns="trp10";fls="x.txt";gm("prf");     // Print the file using supplied font
gm("pc");                              // Close printing operation
//---------------------------- (d) Printing Bitmap Object ----------------------------
tm("vg");                              // Change visibility from text to graphics
fls="images\\pix.jpg";gm("blf");       // Create new Bitmap & load image file into it
gm("br");                              // Display the Bitmap object for inspection
gm("po");                              // Open a new printing operation
ib=true;gm("psl");                     // Select "landscape" orientation.
kf=200;gm("prb");                      // Print the Bitmap object with it's center
// 2 inches above paper's center.
gm("pc");                              // Close printing operation
//------------------------ (e) Obtaining Number Of Lines/Page ------------------------
tm("vt");                              // Return visibility to text
gm("po");                              // Open a new printing operation
fns="trb12";gm("pgt");                 // Get total number of lines/page
os="Number of lines per page="+os;tm();// Display result.
gm("pc");                              // Close printing operation
}
}
===============================================================================================
TUTORIAL: Here are few Remarks:

(1) Text printing starts at page's margins horizontally and vertically. Bitmap printing
is done the ordinary PC#'s way; you supply the coordinates of the center of the Bitmap
object relative to page PrintableArea's center measured in (1/100)th of an inch units and
assigned to (jf,kf) Printable area is all the area which can be printed of the page
regardless to set margins.

(2) Method tm() can print the "text screen" contents simply by calling tm("p") It allows
specifying the font to print with, but it does not allow special printer setups. This is
why we did not use it.

(3) As you must have noticed, all text is printed in black and white using one font. There is
no single formula for how color and font informations are inserted into text. The "RTF"
formula which is used by the TextScreen is just one of them.

If you like to print your text in variety of colors and fonts, you have the following two
choices:

a) Display the text graphically using method gm() then make (bip) a second reference to
(bio)'s object and print it using mode "prb".

b) Display the text on the "text screen" first, save it to an "RTF" file using method
tm("fsr") then print it using Microsoft's "Word" or "WordPad".

(4) In order to know how to force the printer to move to a new page whenever you find it necessary,
click on "Reference-Desktop" and read the section titled "Saving Text Screen's Rich Text into
an RTF file and printing it".
```
```===============================================================================================

Scaling the image to be printed:
================================

Mode "prb" prints the present Bitmap object (bip) It allows us to set the position of the bitmap
center relative to the center of the printable area of the paper in 1/100th of an inch unit. We
do that by assigning the (x,y) components of (bip)'s position to (jf,kf) If we make no
assignments to (jf,kf) the image will be centered into the page.

Mode "prb" also allows us to set the width and height of the printed image by assigning them to
(lf,of) They are also in 1/100th of an inch unit. Now, how can we calculate the necessary values
of (lf,of)?

Suppose that we like to match the width of the displayed image in inches with the width of the
printed image (See the next Remark), how can we do it? Method gm("bR") can find for us (bip)'s
resolution in Pixels/Inch over both the horizontal and vertical directions. So we can easily
calculate the width in inches as follows:

Width in inches = (Width in Pixels) / X-Resolution.

Then all we need to do is to multiply this number by 100 and assign it to (lf) Similarly, we can
calculate (of) Except that all this is unnecessary since it's the default. All you need to do
if you like to match inch for inch is to supply (lf=of=0;) and method gm("prb") will do the
calculations for you.

If you like to assign values to (lf,of), we recommend assigning value to one of them only and
assigning zero to the other one. This is because the method will replace the zero in this case
with the value which makes the width to height ratio matches the width to height ratio of (bip)
This should save you time and guarantee that the printed image will look like the original.

REMARK:
-------

The actual width of the image on your monitor may not match the calculated width in inches. This
is because the width you see depends also on the monitor itself. The resolution amounts in
pixels per inch which we use to calculate the image dimensions in inches come from the .NET class
System.Drawing.Image.

Printing large size images:
---------------------------

Computer screens and print papers do not match in size. Standard paper size is 8.5" X 11" which
means that paper's height exceeds its width. For computer screens, the opposite is true.

Fortunately, we don't need to be concerned of the screen height since we don't draw on the Form
directly, we draw on (bio) which can be of any height we choose and we can use vertical
scrollbars.

On the horizontal direction, we normally don't like to exceed the screen's width or install
horizontal scrollbars although we can. The reason is that whatever exceeds the screen in width
will normally not fit into standard size paper when printed.

Whenever we have a large image to draw, the best to do is to create (bio) with same width as the
form's and a height which makes the height/width ratio of the paper matches the height/width
ratio of (bio)

The outside dimensions of the "letter" size paper is 8.5 X 11 If you subtract 1 inch from all
sides as margins, the net size would be 6.5 X 9. These are the numbers which you may like to use.
In the next example, we are going to be using the "Printable Area" size which can be as large as
8.25 X 10.75.

===============================================================================================
EXAMPLE 15: Create (bio) with full screen width and make it match printer's paper in height/width
ratio. Draw something which can occupy (bio) in full then print it.
===============================================================================================

public class a : pcs {                     // Always remember, class name = file name

// Var's used: w,h = Width and height of printable area of page in 1/100th of an inch unit.
//             x,y = Calculated width and height of screen in pixels.
//               c = Used in color selection.

public override void run() {
//----------------------------- (a) Printing Text Lines -----------------------------
gm("dn");                              // Stop display temporarely.
gm("po");                              // Open a new printing operation
gm("pgw");om("ti");w=o;                // Get paper PrintableArea's width, convert to (int)
gm("pgh");om("ti");h=o;                // Get paper PrintableArea's height, convert to (int)

cm("fwc");om("ff");om("ti");x=o;       // Get screen width, convert to (int)
of=x*h/w;om("ff");om("ti");y=o;        // Calculate how much screen height would be
// to be proportional to paper.
of=y;cm("fbv");                        // Request vert scrollbar & (bio) height=y
cls="s9";gm("ec");                     // Paint (bio)'s background (necessary for scrollbar
for (int k=-y/2;k< y/2;k+=100) {       // Operation) Scan (bio) vertically and
for (int j=-x/2;j< x/2;j+=100) {     // horizontally and draw a colored circle each 100
c=(j+x/2)/100;if (c >7) c=0;       // pixels. (c) is used to select one color for each
cls="r0b0y0g0p0c0m0S9".Substring (c*2,2);gm("sps");
jf=j+25;kf=k;lf=of=25;gm("cef");   // circle.
}
}
gm("dn");                              // Resume display.
lf=w;of=h;bip=bio;gm("prb");           // Assign (bio) to (bip) and print it so that it
// covers entire printable area of page.
gm("pc");                              // Close printing operation.
}
}

==============================================================================================
```
```
DRAWING ON THE TEXT SCREEN
==========================

The Text Screen is a very versatile display media. In addition to allowing both your
program and the user to display text and images, file them and print them, it also
allows the two to edit its contents. It can send any of its contents to the Clipboard and
can also receive the Clipboard's content.

This feature can easily be used to send your drawings to the Text Screen and insert them
wherever you like there. Let us have an example.
=========================================================================================
Example 16: Let us draw the same piece of jewelry which we have used in other examples on
the text screen. In order to show that the Text Screen can contain combinations of text,
pictures and dynamically generated drawings, we are going to display two text lines, one
above the drawing and one below it.
=========================================================================================
public class a:pcs {
public override void init() {
tia=toa="t";                           // Use text screen for text input/output
base.init();                           // Initialize pcs
}
public override void run() {
cls="r0";fns="trb18";os="Drawing on the Text Screen";tm();os="";tm();
// Display title
cls="b0";fns="trb12";os="This text line comes before the drawing.";tm();
// Display first line on Text Screen
ib=true;cm("fv");                      // Make form invisible temporarely
j=k=200;ib=true;cm("fs");              // Resize form to fit object
cls="s7";gm("ec");                     // Color background to match Text Screen.

//--------------------- Drawing the object (No change in code) --------------------
lf=of=170;gm("ce");                    // Create the circular gold plate
cls="o5y5";gm("spl");                  // Prepare linear gradient brush for it
gm("grf");                             // then render-fill the gold plate.
lf=6;of=50;gm("c=");                   // Create hexagon shape object at center.
cls="r0";ks="r";gm("grs");             // Draw the object using sp effects-refl at
jf=45;cls="b0";ks="r";gm("grs");       // center in red, then repeat 6 times using
jf=-45;cls="b0";ks="r";gm("grs");      // different colors and different locations
jf=22;kf=40;cls="g0";ks="r";gm("grs");
jf=-22;kf=40;cls="m0";ks="r";gm("grs");
jf=22;kf=-40;cls="m0";ks="r";gm("grs");
jf=-22;kf=-40;cls="g0";ks="r";gm("grs");
lf=of=25;gm("ce");                     // Create a circle at center (pearl)
for (int x=0;x<20;x++) {               // Draw it 20 times using sp effects-reflection
jf=80;kf=18*x;kb=true;               // at locations around the plate. Polar coord's
cls="p0";ks="r";gm("grs");           // are used for specifying locations
}
//------------------------ Displaying (bio) on Text Screen -----------------------
imp=bio;sm("csi");                     // Set (bio) into Clipboard
cm("fsd");                             // Return form to regular size.
cm("fv");                              // Return visibility to Form.
os="               ";tm("d");          // Move cursor to wanted display position
tm("ep");                              // Text Screen edit-paste
os="";tm();                            // Move to next line
cls="b0";os="And this one comes after the drawing.";tm();
}                                        // Display second line on Text Screen.
}
=========================================================================================
TUTORIAL: While preparing the drawing, We wanted to reduce the form size to the size of
the drawn object in order to eliminate the space around it. But since the form size also
sets the Text Screen size, we had to resize the form back to original size after the
drawing was complete. Also, we kept the Text Screen invisible during drawing for this
same reason.

The object was copied to the Clipboard while the form was small and pasted on the Text
Screen after it has been returned to the original size. As you know each time the form
is resized using cm("fs"), the Bitmap object (bio) with all the drawings on its surface
are also resized by the same amounts horizontally and vertically. This means that if we
have not changed visibility to the Text Screen we could have seen a larger elliptical
piece of jewelry which would still be of no concern to us since we made our copy to the
clipboard before the resizing took place.

Displaying image files on the Text Screen is much easier, you assign to (ims) the image
file name and call tm("dg") to display the image at the cursor.

```
```
===========================================================================================

CLIP AREA
=========

(This section requires version 1.55 or higher)

The GraphicsPath object (gpp) can be set as a clip area. A clip area is an area on the
graphical output device which disallows drawing anything outside its borders.

A negative clip area does the opposite. Drawings can appear only outside a negative clip area.

To set (gpp) as a clip area call gm("gc")  To set (gpp) as a negative clip area call gm("gc-")
To add another (gpp) object to current clip area call gm("gc+")  To modify current clip area
by intersecting (gpp) with it, call gm("gc&")  To modify it by xoring (gpp) with it call
gm("gc^") and to reset the clip area call gm("gcn")

=========================================================================================
Example 17: Create a large elliptical shape and make it a negative clip area, Create the
graphical shape object of the text "PC#" and xor it with the clip area then paint the
entire form with an image. See what you'll get.
=========================================================================================

public class a : pcs {
public override void init() {
base.init();
}
public override void run() {
lf=700;of=300;gm("ce");                // Create an ellipse.
gm("gc-");                             // Make it a negative clip area.

fns="trb260";os="PC#";kf=-20;gm("ct"); // Create (gpp) for the "PC#" text
gm("gc^");                             // Xor (gpp) with the clip area

lf=60;of=30;fls="images\\icon.bmp";gm("blf");
gm("spt");                             // Create a texture brush using icon.bmp image file
lf=760;of=360;gm("cr");                // Create a form size rectangle
gm("grf");                             // Draw-fill the rectangle using texture brush
}
}
```
```==============================================================================================

HANDLING MOUSE AND KEYBOARD EVENTS
==================================
(For versions 1.75 and higher)

In the chapter of "Handling Controls", we have handled the events of several controls, but we
did not learn how to determine where in the form the mouse has been clicked or which keyboard
key has been pressed. There are important applications which require handling mouse and keyboard
events, most of them involve graphics. This is why we have included the study in this chapter.

The mouse and keyboard controls:
--------------------------------

In order to simplify handling the two devices, method cm() treats them as controls. Actually we
look at the mouse as 4 different controls. This is because we are interested in handling four
mouse events which are "mouse up", "mouse down", "mouse moved and "mouse clicked". We use the
following keynames for all the controls involved:

"md0" : Mouse Pressing (Down)
"mu0" : Mouse Releasing (Up)
"mv0" : Mouse Moving (Up or Down)
"mc0" : Mouse Clicking (Down then UP)
"ky0" : Key Pressing.

We can have only one of each of these controls in a class and we must use the keynames shown.

Installing mouse and keyboard controls:
---------------------------------------

This is done into method setup() as usual except that there are no parameters to specify.
Using the keynames listed above is mandatory.

Handling mouse and keyboard events:
-----------------------------------

When a mouse key is pressed, released or clicked, method update() is called with the correct
keyname for the event assigned to (cs) Additionally, method update receives the following:

oxf,oyf: Coordinates of the point where the mouse was at, relative to original Form's center.
You need to adjust them with gm("brc") Since user may resize Form.

ob     : (ob=false) means that mouse left key is the source of the event. (ob=true) means that
right key is the source.

When a keyboard key is pressed, method update is called with (cs="ky0") and

oc     : Standard ASCII Character representing pressed key (Examples: A, a, 1 and [Enter]) or
code representing a special character (Examples: [Insert], [Delete] and [UpArrow])

ob     : Character type flag. (ob=true) Means special character.

We'll be discussing keyboard events in more details later.

----------------------------

When you design your software, you assume that the Form size is either the PC# default size or
the size you have requested using method dm("s") or cm("fs")  The mouse coordinates which come
assigned to (oxf,oyf) are relative to the center of the Form when the clicking took place. The
Form size at that moment may have not been the same as the original size since the user may have
resized the Form.

For this reason, you should always call method gm("brc") to make the adjustment necessary to
(oxf,oyf) before using them.

There are two methods which adjust the mouse coordinates. Method gm("brc") is to be used when
your drawings are on (bio) and method gm("bdc") which you will use in the next example is to be
used when your drawings are on (bip)

-----------------------

The form will be divided into 3 regions. The user will press any key on the keyboard and release
it. The character of the key will be assigned to (xs) to memorize it.

If the mouse is clicked into region-1, the character will be displayed using TimesRoman large
font at the point where the mouse has been clicked.

If the mouse is clicked into region-2, the character will be displayed using Wingdings font.
Wingdings font contains drawings which are mapped to characters.

If the mouse is clicked where a color pot is, the color will be switched to the color of that
pot. Any drawing at any region thereafter will be in that new color.

If the mouse is pushed down at one point within region-3, then dragged to a second point, a
series of tiny straight lines will be drawn as the mouse moves between the two points which
combine to show the exact path of the mouse pointer.

If at any time the user likes to erase one region's content, he can do that by pushing
the key which represents the region number.

=========================================================================================
Example 18: Divide the form into 3 regions. demonstrate how to handle a KeyPress event to
know which char key is pushed and how to handle MousePress event to know where to display
the char. Use two different fonts for the demonstration.

Additionally show how to handle the MouseUp, MouseDown and MouseMove events and use that
to draw with the mouse.
=========================================================================================

public class a : pcs {

float xf,yf,j1f,k1f,wf,hf;
bool IsMouseDown;

// Var's Used: xf,yf  : Used to store coordinates of the point where mouse is clicked.
//             j1f,k1f: Used to store coordinates of the point where mouse is pressed.
//             wf,hf  : Client Size width and height.
//             xs     : Stores the key pressed in a string form.
//             IsMouseDown: A flag which indicates that mouse is down when set.

public override void init() {
base.init();
}

public override void setup() {
cs="mc0";cm("i");
cs="mu0";cm("i");
cs="md0";cm("i");
cs="mv0";cm("i");
cs="ky0";cm("i");
}

public override void update() {
//--------------------------------- Mouse Click event ------------------------------------
if (cs=="mc0") {                   // If mouse was clicked:
xf=oxf;yf=oyf;                   // Save mouse-click coordinates temporarely
// If clicked one of the three color pots, set color accordingly.
if (xf>(wf/8-20) && xf<(wf/8+20) && yf>(-3*hf/8-20) && yf<(-3*hf/8+20)) cls="r0";
if (xf>(wf/4-20) && xf<(wf/4+20) && yf>(-3*hf/8-20) && yf<(-3*hf/8+20)) cls="g0";
if (xf>(3*wf/8-20) && xf<(3*wf/8+20) && yf>(-3*hf/8-20) && yf<(-3*hf/8+20)) cls="b0";
i=3;gm("sps");                   // Create 3-pixel wide pen of requested color

if (xf<0 && yf>0) {              // If the point clicked was in region-1:
fns="trb132";                  // Use a large TimesRoman font.
os=xs;                         // Assign char of last key pressed to (os)
jf=xf;kf=yf;gm("ctd");         // Draw the char at the point which was clicked.
}
else if (xf<0 && yf<0) {         // Else if point clicked was in region-2:
fns="wn80";                    // Use Wingdings font which contains symbols & drawings
os=xs;                         // Assign char of last key pressed to (os)
jf=xf;kf=yf;gm("ctd");         // Draw the char at the point which was clicked.
}
}

//--------------------------------- Mouse Down event ------------------------------------
if (cs=="md0") {                   // If mouse was pressed down:
cs="";j1f=oxf;k1f=oyf;           // Save mouse-down coordinates temporarely.
IsMouseDown=true;                // Set Mouse down flag.
}

//---------------------------------- Mouse Up event -------------------------------------
if (cs=="mu0") {                   // If mouse was released up:
IsMouseDown=false;               // Reset Mouse Down flag.
}

//--------------------------------- Mouse Move event ------------------------------------
if (cs=="mv0" && IsMouseDown) {    // If mouse moved while pressed down (dragged):
um("c");                         // See Remark.
if (oxf>0 && j1f>0 && oyf>(45-3*hf/8) && k1f>(45-3*hf/8)) {
// If coordinates are in region-3 above color pots:
cs="";lf=oxf;of=oyf;           // Draw a line ending at current coord's
jf=j1f;kf=k1f;                 // and starting at previous coord's.
gm("cld");
j1f=oxf;k1f=oyf;               // Replace previous coord's with current ones.
}
}

//---------------------------------- Key Press event ------------------------------------
if (cs=="ky0") {                   // If keyboard key was pressed:
xs=""+oc;                        // Make (xs)=the pressed char in a string form.
if ("123".IndexOf(oc)>-1) Start();
// If "1", "2" or "3" was the key pressed, clear and
}                                  // initialize the region of same number.
}

//--------------------------------- Program Start-Up --------------------------------------
public override void run() {
cm("fwc");wf=of;cm("fhc");hf=of;   // Get client width and height and store them into wf,hf
oc='1';Start();oc='2';Start();oc='3';Start();
// Initialize all 3 regions.
}

void Start() {

//-------------------------------- Initialize region-1 ----------------------------------

if (oc=='1') {                                   // If initialize region-1 requested:
cls="p7";gm("sps");                            // Prepare light pink color brush.
jf=-wf/4;kf=hf/4;lf=wf/2;of=hf/2;gm("crf");    // Color region-1 background with it.

cls="r0";gm("sps");fns="trb16";                // Draw title in red.
os="CLICK A CHARACTER";jf=-wf/4;kf=0.45f*hf;gm("ctf");
cls="b0";gm("sps");                            // Draw next line in blue.

}
//-------------------------------- Initialize region-2 ----------------------------------

else if (oc=='2') {                             // If initialize region-2 requested:
cls="g7";gm("sps");                           // Prepare light green color brush.
jf=-wf/4;kf=-hf/4;lf=wf/2;of=hf/2;gm("crf");  // Color region-2 background with it.

cls="r0";gm("sps");fns="trb16";               // Draw title in red.
os="CLICK A DRAWING";jf=-wf/4;kf=-0.05f*hf;gm("ctf");
cls="b0";gm("sps");                           // Draw next line in blue.
}

//-------------------------------- Initialize region-3 ----------------------------------

else if (oc=='3') {                             // If initialize region-3 requested:
cls="o7";gm("sps");                           // Prepare light orange color brush.
jf=wf/4;kf=0;lf=wf/2;of=hf;gm("crf");         // Color region-3 background with it.

cls="r0";gm("sps");fns="trb16";               // Prepare red pen.
os="SKETCH PAD";jf=wf/4;kf=0.45f*hf;gm("ctf");// Draw title at center of region-3
cls="b0";gm("sps");                           // Draw next two lines in blue.
os="Drag mouse pointer between two points to draw a line";jf=wf/4;kf=0.40f*hf;gm("ctf");
os="To draw in color, Click into the correct color pot";jf=wf/4;kf=0.35f*hf;gm("ctf");

//--------------------------------- Create Color Pots ---------------------------------

cls="r0";gm("sps");                     // Prepare red brush
jf=wf/8;kf=-3*hf/8;lf=of=40;gm("cef");  // Draw the red color paint.

cls="g0";gm("sps");                     // Repeat for the green color paint.
jf=wf/4;kf=-3*hf/8;lf=of=40;gm("cef");

cls="b0";gm("sps");                     // Repeat for the blue colr paint
jf=wf*3/8;kf=-3*hf/8;lf=of=40;gm("cef");

cls="S9";i=4;gm("sps");                 // Drawing black containers around color paint
jf=wf/8;kf=-3*hf/8;lf=of=42;gm("ced");  // Red pot
jf=wf/4;kf=-3*hf/8;lf=of=42;gm("ced");  // Green pot
jf=wf*3/8;kf=-3*hf/8;lf=of=42;gm("ced");// Blue pots

}
}
}
===============================================================================================```
```==============================================================================================

Graphical Dialog Boxes
======================

The sections of "Handling Controls" and "Drawing" have become mixed-up lately. This is because
there are very interesting applications which include graphics with mouse and keyboard event
handling. One important application is Graphical Dialog Boxes.

You need to review example 12 of "Handling Controls" where the modes "bd", "bdf", "bdd", "bdn"
and "bdc" of method gm() have been discussed.

Mode "bdf" and "bdd" allow us to display the present bitmap object (bip) without blending with
(bio) and without being affected by the scrollbars. Mode "bdn" allows us to end the display of
(bip) leaving (bio) unchanged. This is exactly what a dialog box is required to do. So, we have
just invented a way to draw a graphical dialog box. All we need to do is to draw the dialog box
on (bip) and use those modes to launch the dialog box and to make it disappear.

You can use your imagination and skill to make the most beautiful dialog boxes ever made. You
have all the tools and everything is very easy. In addition to their beauty, the graphical
dialog boxes run faster than our custom dialog boxes.

===============================================================================================
Example 19: Create a graphical dialog box. Make it launchable at Form's center by pressing
[ENTER]. Install scrollbars on Form to show how to make the dialog box scroll independant. Make
the dialog box disappear when its button is clicked.
===============================================================================================

public class a:pcs {

Bitmap bi1;bool DialogOn;

// Var's used: bi1      : Stores (bip) which contains the dialog box.
//             xs       : String keyed-in by user into text field.
//             DialogOn : A flag which indicates that the dialog is on display when set.

public override void setup() {
cs="mc0";cm("i");
cs="ky0";cm("i");
}

public override void update() {
//---------------------------------- Key Press event ------------------------------------
if (cs=="ky0") {                    // If keyboard key was pressed:
if (ob) return;                   // Return if special function key.
if ((int)oc==13 && !DialogOn) {   // If [ENTER] pressed & Dialog box was not displayed:

bip=bi1;ds="c";gm("bdd");       // Display its bitmap at center of Form.
DialogOn=true;                  // Turn "On-Display" flag on.
xs="";return;                   // Initialize (xs) and exit.
}
// If any other key pressed:
if(xs.Length<15) xs+=oc;          // Add the pressed char to (xs), limit (xs) to 15 char's
bip=(Bitmap)bi1.Clone();          // Make a copy of empty dialog box's bitmap
gm("sdb");                        // Make it the graphical output device.
cls="b0";gm("sps");fns="trb16";   // Set color and font.
os=xs;kf=-30;gm("ctf");           // Draw text at center of textfield.
gm("sdd");                        // Switch back to default graphical output device.
ds="c";gm("bdd");                 // Draw bitmap at Form's center
}

//--------------------------------- Mouse Click event ------------------------------------
else if (cs=="mc0") {               // If mouse was clicked:
um("c");                          // See Remark (needed temporarely)
gm("bdc");                        // Make received (oxf,oyf) relative to (bip)'s center.

// If clicked at button, stop dialog box display. Display keyed-in string then erase it.
if (oxf>(0-25) && oxf<(0+25) && oyf>(-71-25) && oyf<(-71+25)) {
gm("bdn");DialogOn=false;       // Stop (bip)'s display.
os="Received "+xs;cm("d");      // Display message in a dialog box.
xs="";                          // Erase (xs)
}
}
}

public override void run() {
j=k=350;cm("fs");                   // Resize form to (350,350) pixels
lf=450;cm("fbh");                   // Request horiz scrollbar & (bio) width of 450 pxls
of=600;cm("fbv");                   // Request vert scrollbar & (bio) height of 600 pxls
cls="g7";gm("ec");                  // Paint (bio)'s background.
DrawDialog();                       // Draw Dialog Box on a bitmap.
bip=bi1;ds="c";gm("bdd");           // Display bitmap at center of Form
DialogOn=true;                      // Turn (on-Display) flag on.
}

void DrawDialog() {

lf=210;of=210;gm("bn");gm("sdb");   // Create a bitmap with enough size for the drawings.
// and make it the graphical output device.
// Draw the blue circular dialog box with red frame.
cls="b0";gm("sps");                 // Create blue brush
lf=of=200;gm("cef");                // Draw the dialog box's body
cls="r0";i=3;gm("sps");             // Create 3-pixels wide pen.
lf=of=200;gm("ced");                // Draw a red circle around dialog box.

// Draw the white textfield using 3D-depth effect.
lf=160;of=25;gm("cr");              // Create textfield rectangle. Apply 3D-Depth to it.

// Draw the button using 3D-Reflection effect.
lf=6;of=50;ad=90;gm("c=");          // Create Hexagon rotated 90 degrees.
kf=-71;cls="p0";ks="r";gm("grs");   // Draw it, applying 3D-Reflection effect.

// Draw "Type your name then click the button" elevated up.
fns="esb28";                        // Set font. Display text using 3 shades as usual.
cls="s9";gm("sps");d=1;DisplayText();
cls="S9";gm("sps");d=-2;DisplayText();
cls="s4";gm("sps");d=0;DisplayText();

gm("sdd");                          // Switch graphical output device back to default
bi1=bip;                            // Save bitmap with drawing on.
}
void DisplayText() {                  // Display text  IN:d=vertical location adjustment
kf=70+d;os="Type";gm("ctf");gm("grf");
kf=10+d;os="then click the button";gm("ctf");gm("grf");
}
}
===============================================================================================```
```                          DRAWING IN THREE DIMENSIONAL PLANES
===================================
(Requires PC# v1.90)

INTRODUCTION:
=============

You have already used two of our 3D effects which are labeled "Reflection" and "Depth". The
two have been used extensively by class "pasp" in making web pages. The advantage in using
these drawings over the internet is that you allow the user to interact with the creation
process of the drawings while your software remains at the server unseen to your competitors.

We have also, demonstrated that by using AJAX, you can speed up the interaction with the user
and we have also developed a means to allow remote method invocation meaning that the client
can use server methods remotely.

The "Windows Presentation Foundation" (WPF) which came with the .NET v3 contains some namespaces
for 3D drawing. They contain very interesting classes which allow viwing 3D objects from
different distances and angles using different lights, transforming 3D objects and animating
them. The WPF classes can also be accessed by the internet Explorer which means that they can
be viewed over the internet, but there are some difficulties:

(1) The classes do not create 3D objects for you, they only work on them. They expect you to
supply them with the the 3D coordinates of all the vertices and other information of a

(2) Unfortunately, those classes are not based on the original powerful DotNET graphics
formula which class (pcs) uses internally. They are based on a new Graphics formula which
has some limitations. However, we could successfully overcome most of this difficulty.

(3) Apparently, your code will be exposed to others when placed on the web. This is still to
be investigated.

We are currently working on the development of a software which simplifies the use of the WPF
3D Graphics. To start with, we need to make you able to create 3D objects in a simple manner
which can supply their vertices coordinates and all other data to the WPF classes automatically.

The new 3D drawing addition will be available into both (pcs) and (pasp) classes so you can
create 3D objects to draw on desktop or to put on the internet. Additionally, a copy will be
added to the new class (pcs3) which handles WPF. This copy will have the ability to supply the
WPF with data concerning created 3D objects.

The three dimensional axes and planes:
======================================

Y
|
|
-|-
| | |<--- Object
| | |
| +------------- X
|/  |
/   |
/|   |
/  ---
/
Z

The 3D axes are:

(1) x: The horizontal axis on the screen. It points to the right side.
(2) y: The vertical axis on the screen. It points to the up side.
(3) z: This axis represents the depth it's perpendicular to the screen's surface and points
to the outsde direction.

The x,y,z coordinates are considered to be growing positively when they grow in the direction
their axes point to. The angle between each two axes is 90 degrees. Each two axes form a plane.
So there are 3 planes "xy" plane, "yz" plane and "xz" plane.

Axes can be drawn in any directions as long as they make 90 degrees with each others and abide
with the right hand rule. This rule says that if your right hand's index finger points to the
positive x-axis and the middle finger points to the positive y-axis, your thumb will be pointing
to the positive z-axis.

When we use polar coordinates to draw on the xy plane, we define the angle to be between the
radius and the poitive x-axis. So, let us call the x-axis the base axis of the xy plane. We
need to select a base axis for each of the three planes. Our selection is:

(1) xy plane: Base=x.
(2) yz plane: Base=z.
(3) zx plane: base=z.

When you request drawing a point on one plane, method gm() will consider the value assigned to
(jf) to be the location of the point in the direction of the base axis of that plane and the
value assigned to (kf) to be the point position in the other axis's direction.

How to draw on one of the 3D planes:
====================================

It's made very easy, you call method gm() to create and draw or fill an object as usual. The
only difference is that you add (cns) to your parameters. (cns) is the container. It's assigned
a string like (cns="xy") This means draw the object on the "xy" plane at z=0 which means on the
screen plane.

This may sound like 2D drawing but it is not. When you draw a rectangle, it will show it as a
parallelogram since the x-axis is not horizontal in this case. If you want the regular 2D
drawing, keep (cns="")

If you like to draw on a plane which is parallel to the "xy" plane but at a depth of 100, (this
means 100 pixels away from the screen plane) make the assignment (cns="xy100")  If you like to
draw  on a plane which is parallel to the "xz" plane, but at a height of (-200) which means
with (y=-200), make the assignment (cns="xz-200")

You need to know here that all the numbers you supply are besed on 2D drawing. You don't get
into making adjustments to your drawing lengths or angles to make them look three dimensional.
Method gm() takes care of that. This includes the 3rd dimension number which you assign to
(cns). If you supply (cns="xy100"), method gm() will figure out how this number should be
modified to fit 3D drawing before it applies it to your drawing.

==============================================================================================
Example 20: Draw a 200 X 150 X 100 rectangular box and draw the string "PC#" on all its three
visible sides.
==============================================================================================

public class a : pcs {

public override void run() {

cns="xy50";                                 // Container: Parallel to xy plane at z=50
cls="r0";gm("sps");                         // Create red pen
lf=200;of=150;gm("crd");                    // Draw the front rect at z=1/2 of box's depth.
fns="trb50";os="PC#";gm("ctf");             // Draw the string on the same plane.

cns="xz75";                                 // Container: Parallel to xz plane at y=75
cls="G2";gm("sps");                         // Create green pen
lf=100;of=200;gm("crd");                    // Draw the top rect at y=1/2 of box's height.
fns="trb35";os="PC#";gm("ctf");             // Draw the string on the same plane.

cns="yz-100";                               // Container: Parallel to yz plane at x=-100
cls="b0";gm("sps");                         // Create blue pen
lf=100;of=150;gm("crd");                    // Draw the side rect at x=1/2 of box's width.
fns="trb25";os="PC#";gm("ctf");             // Draw the string on the same plane.

}
}
----------------------------------------------------------------------------------------------

Notice that you have supplied the real dimensions of the box. You did not care about how these
numbers are modified to make the box look three dimensional. Method gm() takes care of that.

==============================================================================================
```
```
Doing more complicated drawings on 3D planes:
=============================================

Mostly anything which You can draw on 2D space can be drawn on any of the three planes. We
are going to try here to duplicate previous drawing examples on the three surfaces of a
rectangular block. We are going to duplicate 6 examples, so we need two blocks.

==============================================================================================
Example 22: Modify the code of examples number 1, 2, 3, 7, 10 and 17 to make them draw on the
surfaces of two 3D rectangular blocks.
==============================================================================================

public class a : pcs {

public override void init() {
base.init();
}

public override void run() {

//================================= Drawing at the left side ================================

lf=-200;gm("stu");                          // Create necessary transform (utp) to move left

//-------------------------------- Drawing on the xy plane --------------------------------
cns="xy100";                                // Container: parallel to xy plane with z=100
cls="r0";gm("sps");                         // Create red brush and use it to draw a
lf=200;of=150;gm("cr");gm("gtd");           // 200X150 rect after moving it to left side.

//---------- Example 3's code modified to move drawings to left -----------
cls="S9";gm("sps");                         // Restore original black pen/brush
lf=130;of=50;id=-1;od=0;gm("cr");gm("gtd"); // Draw a rect with horiz shear factor=-1
cls="b05";gm("sps");                        // Create solid blue pen
lf=of=30;ad=45;gm("cr");gm("gtf");          // Create, fill a square, rotated 45 degrees
cls="r0";gm("sps");                         // Change color to red
lf=of=100;id=0;od=-1;gm("ce");gm("gtd");    // Draw a circle with vert shear factor=-1

//-------------------------------- Drawing on the xz plane --------------------------------
cns="xz75";                                 // Container: parallel to xz plane with y=75
cls="G2";gm("sps");                         // Create green brush and use it to draw a
lf=200;of=200;gm("cr");gm("gtd");           // 200X200 rect after moving it to left side.

//---------- Example 1's code modified to move drawings to left -----------
cls="S9";gm("sps");                         // Restore original black pen/brush
jf=-70;kf=0;lf=70;of=0;gm("cl");gm("gtd");  // Draw line bet. points (-70,0) & (70,0)
jf=0;kf=65;lf=0;of=-65;gm("cl");gm("gtd");  // Draw line bet. points (0,65) & (0,-65)

os="X";jf=80;kf=0;gm("ct");gm("gtd");       // Draw letter "X" beside X-axis
os="Y";jf=0;kf=75;gm("ct");gm("gtd");       // Draw letter "Y" above Y-axis

cls="r0";gm("sps");                         // Set pen color=red,solid. Use it to draw a
jf=-55;kf=0;lf=of=50;gm("cr");gm("gtd");    // rect at Location=(-55,0), width=height=50,

cls="b0";gm("sps");                         // Set pen color=blue,solid. Use it to draw a
jf=55;kf=180;kb=true;lf=of=50;gm("ce");gm("gtd");
//-------------------------------- Drawing on the yz plane --------------------------------
cns="yz-100";                               // Container: parallel to yz plane with x=-100
cls="b0";gm("sps");                         // Create blue brush and use it to draw a
lf=200;of=150;gm("cr");gm("gtd");           // 200X150 rect after moving it to left side.

//---------- Example 2's code modified to move drawings to left -----------
lf=of=120;gm("ce");gm("gtd");               // Draw a circle, diam=120 pixels
for (float a=0;a<360;a+=60) {               // Make a=angle values between 0,300;
kb=true;jf=60;kf=a;lf=of=120;             // using polar coord's draw arches with center
jd=(double)a+120;kd=120;                  // at radius=60, angle=a, start angle=a+120
gm("ca");gm("gtd");                       // Extent angle=120
}

//================================= Drawing at the right side ===============================

lf=200;gm("stu");                           // Create necessary transform (utp) to move right

//-------------------------------- Drawing on the xy plane --------------------------------
cns="xy100";                                // Container: parallel to xy plane with z=100
cls="b2";gm("sps");                         // Create red brush and use it to draw a
lf=200;of=150;gm("cr");gm("gtf");           // 200X150 rect after moving it to right side.

//---------- Example 7's code modified to move drawings to right -----------
os="PC#";fns="trb62";gm("ct");              // Create text then draw it with 3D-effects,
cls="s9s0";id=10;od=30;ks="d";jf=200;gm("grs");
// depth=10, angle=30. draw at right side.
//-------------------------------- Drawing on the xz plane --------------------------------
cns="xz75";                                 // Container: parallel to xz plane with y=75
cls="G2";gm("sps");                         // Create green brush and use it to draw a
lf=200;of=200;gm("cr");gm("gtd");           // 200X200 rect after moving it to right side.

//---------- Example 17's code modified to move drawings to right -----------
lf=190;of=120;gm("ce");gm("gtd");           // Create an ellipse.
gm("gc-");                                  // Make it a negative clip area.

fns="trb80";os="PC#";kf=-0;gm("ct");gm("gtd");
// Create (gpp) for the "PC#" text
gm("gc^");                                  // Xor (gpp) with the clip area

lf=30;of=15;fls="images\\icon.bmp";gm("blf");
gm("spt");                                  // Create a texture brush using icon.bmp file
lf=200;of=200;gm("cr");gm("gtf");           // Use it to paint entire surface.

//-------------------------------- Drawing on the yz plane --------------------------------
cns="yz-100";                               // Container: parallel to yz plane with x=-100
cls="s4";gm("sps");                         // Create grey brush and use it to draw a
lf=200;of=150;gm("cr");gm("gtf");           // 200X150 rect after moving it to right side.

//---------- Example 10's code modified to move drawings to right -----------
fns="trb32";kf=d;xs="FAMSOFT";              // Set font and string to draw

// Draw string at 3 vertical levels using white,black and grey colors.
cls="s9";gm("sps");os=xs;kf=2;gm("ct");gm("gtf");
cls="S9";gm("sps");os=xs;kf=-2;gm("ct");gm("gtf");
cls="s4";gm("sps");os=xs;kf=0;gm("ct");gm("gtf");
}
}
==============================================================================================
```
```===============================================================================================

The equally sided object:
=========================

As you must know, method gm() can draw a polygon with equal sides and equal angles between the
sides at modes "c=d" and "c=f". The circle which passes by all vertices of the object
determines the size. You supply the diameter of this circle assigned to (of) and the number of
sides assigned to (lf)

The method starts drawing the object by setting its first point on the +ve Y-axis. So each
object it draws must have a top vertex on the Y-axis.

The equally sided object creation mode is more general than you may already know. The polygon
with the least number of sides is the triangle which you get with the assignment (lf=3) If you
make the assignments (lf=1) or (lf=2) you get a vertical line which is the diameter of the
enclosing circle and when you make the assignment (lf=0), you get the enclosing circle itself.

Drawing Cylinders:
==================

You have already seen how to draw a rectangular block. The next 3D object to draw is a cylinder
A cylinder is defined by its base and its height. The base can be of any shape and the height
is always perpendicular to the base. The cylinder which method gm() can draw has an equally
sided object base sitting on one of the 3 planes (xy, xz or yz) and a height which is parallel
to the third axis (the axis which the base plane does not include)

Since equally sided objects can be of any number of sides and can also be a line or a circle,
our cylinder base can be any of them. However, since one objective of our 3D drawing is to
supply coordinates of points and other data to the WPF's 3D drawing classes, a circle which is
created with (lf=0) cannot be useful. We can still create a cylinder with circular base as
we'll see soon. Here are information about cylinders of different bases:

(1) A cylinder with line base:
------------------------------

Normally, the base of a cylinder cannot be a line, but since this is one option we have when
we create the base using method gm(), we are making it available. A cylinder with a line base
is actually a rectangle. The line which method gm() draws is always vertical as described before
and since it can be drawn on any of the 3 standard planes, the word "vertical" in this case needs
to be replaced with "perpendicular to the base axis of its plane".

(2) A cylinder with a square base:
----------------------------------

This is actually another way to draw a rectangular block with a square base.

(3) A cylinder with a circular base:
------------------------------------

The WPF classes require a mesh pattern data which is made of straight line triangles, so we
can't supply data for an object with a curved line. However, since we can draw a base with any
number of sides, we can approximate a circle with an equally sided polygon of large number
of sides. A polygon with 40 sides or more can hardly be distinguished from a circle while its
mesh pattern data can be supplied to the WPF classes.

Parameters necessary for creating a cylinder:
---------------------------------------------

The cylinder can be drawn by calling gm("cCd") The second letter in the mode string is an upper
case one. You must keep (cns="") since you are not doing 2D drawing on a 3D plane surface, you
are drawing a 3D object which occupies all 3D planes. However the plane which the cylinder base
is parallel to must be specified. You assign it to (js) and it can be either (js="xy"), (js="xz")
or (js="yz")  Remember that the order of the 2 letters is not important. This means that js="xy"
and js="yx" are same assignment.

The cylinder object is created so that its center coincides with form's center, then it's moved
to the postion which you choose on the form. You assign the coordinates of that position to
(jf,kf)

A three dimensional drawing is usually made of visible lines and hidden ones. The hidden ones
are either not displayed or displayed with dashed lines or in different color. We do both. We
display them (if you select so) in dashed lines and in a color which you specify.

To display hidden lines, make the assignment (ib=true). You should also specify a combined
color code for visible and hidden lines. For example (cls="b0y0") will cause visible lines to
show up as blue solid lines and hidden lines to show up as yellow dashed lines.

jf,kf : location of cylinder center relative to Form's center.

lf    : Number of base's sides. (lf=1) or (lf=2) draws a cylinder with line base which is
a rectangle.
of    : Diameter of enclosing circle of the base.

id    : Cylinder height.

js    : Plane which base is parallel to. Can be "xy", "xz" or "yz".

cls   : Combined color code for "visible lines" and "hidden lines".

ib    : "Show hidden lines" flag. Hidden lines will be displayed when (ib=true)

==============================================================================================
Example 23: Draw 5 cylinders with variety of bases on different planes. Base shapes are: line,
triangle, hexagon, square and circle (polygon with 40 sides) Allow hidden lines to show up in
one of the cylinders.
==============================================================================================

public class a : pcs {

public override void run() {

jf=-300;lf=1;of=100;id=100;js="yz";cls="b0b0";gm("cCd");
jf=-160;lf=3;of=100;id=100;js="xz";cls="r0r0";gm("cCd");
lf=6;of=100;id=100;js="xy";ib=true;cls="S9s0";gm("cCd");
jf=150;lf=4;of=100;id=100;js="xy";cls="G2G2";gm("cCd");
jf=300;lf=40;of=100;id=100;js="xz";cls="b0b0";gm("cCd");
}
}
==============================================================================================
```
```===============================================================================================

Drawing Pyramids and Cones:
===========================

The next 3D object to draw is a Pyramid with a general base. The base is also an equally sided
polygon which can approximate a circle when the number of its sides are 40 or more.

All parameters are the same except that a Pyramid (or a Cone) can be drawn with its base either
up or down. When the pyramid's base is up, it's fully exposed. When it's down, approximately
half of it is hidden. "Up and down" are meaningful only when we are talking about a pyramid with
its base on the XZ plane and height parallel to the Y-axis, but the amount of base exposure
applies to all cases.

In order to differentiate between the two positions of a pyramid, we consider that when you
assign a positive number to (id) which is the Pyramid's height, you want the pyramid to appear
with its base at its bottom. If you assign a negative number to (id), the pyramid will appear
with its base at the top and the base will be fully exposed.

Same as with the Cylinders, you can draw a Pyramid with a "single line" base. In this case the
Pyramid will turn into plane triangle. Here are all parameters:

jf,kf : location of Pyramid's center relative to Form's center.

lf    : Number of base's sides. (lf=2) draws a pyramid with line base which is a plane triangle.

of    : Diameter of enclosing circle of the base.

id    : Pyramid height. Negative height means pyramid is upside down (see above)

js    : Plane on which base sits. Can be "xy", "xz" or "yz".

cls   : Combined color code for "visible lines" and "hidden lines".

ib    : "Show hidden lines" flag. Hidden lines will be displayed when (ib=true)

==============================================================================================
Example 24: Repeat Example 23 replacing cylinders with pyramids. Assign negative heights to
some pyramids.
==============================================================================================

public class a : pcs {

public override void run() {

jf=-300;lf=1;of=100;id=100;js="yz";cls="b0b0";gm("cPd");
jf=-150;kf=20;lf=3;of=100;id=100;js="xz";ib=true;cls="r0y0";gm("cPd");
jf=-20;kf=30;lf=6;of=100;id=-100;js="xy";ib=true;cls="S9s0";gm("cPd");
jf=160;kf=25;lf=4;of=100;id=100;js="xz";cls="G2G2";gm("cPd");
jf=300;lf=40;of=100;id=-100;js="xz";cls="b0b0";gm("cPd");
}
}
==============================================================================================
```
```==================================================================================================

Drawing Charts
==============
Requires last update of PC# Version 4.35 or higher
----------------------------------------

We like to make a general method which can be called by your applications to draw charts. Your
applications must be able to supply the method with as few data as possible concerning the
wanted charts and expect the method to figure out the rest by itself.

Your application will receive the chart drawn on (bio) It can either save it into an image file
(See Example 11) or display it on the Text Screen (See Example 16) The method will resize the
Form to the chart size and your application should return it back to original size. The method
will also make the form invisible while drawing the chart and make it back visible when done.

Drawing charts requires plenty of calculations. They also require the use of plenty of variable
names. Since we don't like using long names, we must design our program carefully, set a formula
for selecting variable names and make sure everything is well organized.

The Chart's Grid:
=================

Our charts are divided both horizontally and vertically in the same manner your ruler is divided.
The side of your ruler which measures centimeters contains 2 sets of divisions. Small divisions
which measure millimeters and large marked divisions which measure centimeters. Each Marked division
(MrkDiv) contains 10 Small divisions (SmDiv)'s  The marks on the MrkDiv's of your ruler are the
numbers (0:30)

The marks at the Marked divisions can be specified by a user supplied strings for the horizontal
and vertical marks (js,ks) The strings should contain all wanted marks in order seperated with
commas. If the user supplies (js="") or (ks="") the method will figure-out which marks to put by
itself.

Selecting MrkDiv's is not a very easy job when done programmatically. Let us have an Example. If
the data of all points at the vertical side have been (101.60,100.01,107.5,109.00,108.44) and you
like to have 10 marked divisions, where should you set your marks? Using common sense, you most
likely will set them at (100,101,102,103,104,105,106,107,108,109) since the people who'll use the
chart like to see easy to read numbers there. Let us see how to do this job programmatically.

Data required by the Chart Making Method:
=========================================

JD[],KD[]= Two arrays which contain the (x,y) values of the data points which make the chart.
Lengths of the two arrays Must be equal and equal to the number of data points.
i,o      = Small Division (SmDiv) Size in Pixels (default 10).
id,od    = Small Division Size in Data.
j,k      = Marked division (MrkDiv) size in small divisions (default 5).
js,ks    = Marks Seperated with commas. To use Data instead at either dir, keep js="" or ks=""
hs,vs    = Horiz-Axis & Vert-Axis Descriptions. Phrases which tells what each Axis represents.
os       = Chart Description. A phrase which the method can put for you under the chart.

REMARKS:
========

(1) As you can see, there are defaults for all data except JD[], KD[], id & od.

(2) The chart creation method is made purely in PC#, It's listed below, However, it can also be
accessed with gm("cc") of class (pcs)

(3) All i,j,k & o based GUV's, hs and vs are reset at end of method. Notice that (hs,vs) are two
of the variables which class (pcs) pre-defines for you.

//================================== The Method's Listing ======================================
void MakeChart() {
// IN : JD[],KD[]=Horiz,Vert data. Length of arrays Must be same and = No of data points
//      i,o  = Smallest Division Size in Pixels (default 10 pixels each).
//      id,od= Smallest Division Size in Data.
//      j,k  = Marked division size in smallest divisions (default 5 each).
//      js,ks= Marks Seperated with commas. To use Data instead, keep js="" or ks=""
//      hs,vs= Horiz-Axis & Vert-Axis Descriptions.
//      os   = Chart Description.

// OUT: Chart drawn on (bio)

//--------------------------------- Declaring variables -----------------------------------
// Most var's are made of 3 char's as follows:

// 1st char (Item to be measured)    2nd char (Measured in)    3rd char (For X or Y direction)
// ==============================    ======================    ===============================
// s = Small division (SmDiv)        p = Pixels                x = For X-Direction
// m = Large Marked div (MrkDiv)     d = Data                  y = For Y-Direction
// c = Entire Chart                  s = Small divisions
// f = First Small division          m = Marked divisions
// F = First Marked division
// n = Min Data (Smallest data)
// x = Max data (Largest Data)

// All items measured in data are of type "double" and all other items are of type "int".

if(i<=0) i=10;if(o<=0) o=10;                    // Default of SmDiv sz in pixels.
if(j<=0) j=5;if(k<=0) k=5;                      // Default of MrkDiv sz in SmDiv's.
if(id==0 || od==0) {                            // If (id or od) was missing, give error msg.
os="Error: Small Division's size in data at both directions must be specified.";tm();return;
}
int msx=j,msy=k,spx=i,spy=o;                    // MrkDivv size in SmDiv's, SmDiv size in pixls
int cmx=0,cmy=0,csx=0,csy=0,cpx=0,cpy=0;        // Chart sz in MrkDiv's, in SmDiv's & in pixels
double sdx=id,sdy=od,cdx=0,cdy=0;               // SmDiv, Chart and Chart sizes in data
double ndx,ndy,xdx,xdy;                         // Min data & Max data
double mdx=0,mdy=0;                             // MrkDiv size in data
double fdx=0,fdy=0,ldx=0,ldy=0,Fdx=0,Fdy=0;     // First SmDiv, Last SmDiv and First MrkDiv in data
int xshift=0,yshift=0;                          // Shift of Fdy from fdy in SmDiv's
string j1s=js,k1s=ks;                           // Comma seperated marks or "" meaning use data
string h1s=hs,v1s=vs,o1s=os;                    // X-Axis, Y-Axis and Chart descriptions
//------------------------------- Configuring Chart Constants ---------------------------------
//----------------------- Y-Direction ------------------------
//------ Finding min & max data (ndy,xdy) ------
for(c=1;c< KD.Length;c++) {                     // Scan all data and find
if (KD[c]< ndy) ndy=KD[c];                    // the lowest
if (KD[c]>xdy) xdy=KD[c];                     // and highest data
}

//------ Computing (first & last) data in chart and chart size ------
// Chart should cover data range of (xdy-ndy) or slightly more. Both first and last data should
// fall at multiples of (sdy) Chart should start at first SmDiv (fdy) and end at last SmDiv (ldy)
// REMARK: Notice that we have reduced precision from double to float when comparing some numbers.
//         This is because sometimes comparing equal double numbers gives wrong results.
if(ndy>=0) {                                    // If min SmDiv was +ve:
for(jd=0;jd< ndy+sdy;jd+=sdy) if ((float)jd>=(float)ndy) break;
if(jd==ndy) fdy=jd;else fdy=jd-sdy;           // First SmDiv shld be multiple of (sdy) and equal
}                                               // or just under min data.
else {                                          // Else if was -ve:
for(jd=0;jd> ndy-sdy;jd-=sdy) if ((float)jd<=(float)ndy) break;
fdy=jd;                                       // First SmDiv shld be multiple of (sdy) and equal
}                                               // or just under min data.
for(jd=fdy;jd< xdy+sdy;jd+=sdy) {if ((float)jd>=(float)xdy) break;}
ldy=jd;                                         // Last SmDiv shld be multiple of (sdy) and equal
// or just above max data.
cdy=ldy-fdy;                                    // Chart Size in data = last data - first data
csy=(int)(cdy/sdy+0.5);                         // Chart size in SmDiv's
cpy=csy*spy;                                    // Chart Size in Pixels

//------ Computing Number of Marked divisions and at which SmDiv the first one sits ------
cmy=(int)(csy/msy+0.5);                          // No. of marks=(SmDiv's/chart)/(SmDiv's/MrkDiv)
mdy=msy*sdy;                                    // MrkDiv sz in dt=
//              Its sz in SmDiv's*SmDiv sz in dt

if((ndy+mdy)>=0) {                              // If min MrkDiv was +ve:
for(jd=0;jd< ndy+mdy;jd+=mdy) if ((float)jd>=(float)ndy) break;
Fdy=jd;                                       // Frst mark should be multple of (mdy) and equal
}                                               // or just above min data.
else {                                          // Else If was -ve:
for(jd=0;jd>(ndy-mdy);jd-=mdy) if ((float)jd<=(float)ndy) break;
Fdy=jd+mdy;                                   // Frst mark should be multple of (mdy) and equal
}                                               // or just under min data.
yshift=(int)((Fdy-fdy)/sdy+0.5);                // Number of SmDiv's to skip before 1st mark

if(k1s.Length>0) {                              // If chart will be marked by (ks), No. of marks
os=k1s;oc=',';om("s");KS=OS;                  // = length of OS[] after converting (ks) to OS[]
}

//----------------------- X-Direction ------------------------
//------ Finding min & max data (ndx,xdx) ------
xdx=ndx=JD[0];                                  // Calculate them by considering=first data
for(j=1;j< JD.Length;j++) {                     // then scan all data and find actual values
if (JD[j]< ndx) ndx=JD[j];
if (JD[j]>xdx) xdx=JD[j];
}
//------ Computing (first & last) data in chart and chart size ------
if(ndx>=0) {
for(jd=0;jd< ndx+sdx;jd+=sdx) if ((float)jd>=(float)ndx) break;
if(jd==ndx) fdx=jd;else fdx=jd-sdx;           // See Y-Direction section for comments
}
else {
for(jd=0;jd> ndx-sdx;jd-=sdx) if ((float)jd<=(float)ndx) break;
fdx=jd;
}
for(jd=fdx;jd< xdx+sdx;jd+=sdx) {if ((float)jd>=(float)xdx) break;}
ldx=jd;

cdx=ldx-fdx;                                    // See Y-Direction section for comments
csx=(int)(cdx/sdx+0.5);
cpx=csx*spx;

//------ Computing Number of Marked divisions and at which SmDiv the first one sits ------
cmx=(int)(csx/msx+0.5);

mdx=msx*sdx;

if((ndx+mdx)>=0) {
for(jd=0;jd< ndx+mdx;jd+=mdx) if ((float)jd>=(float)ndx) break;
Fdx=jd;
}
else {
for(jd=0;jd>(ndx-mdx);jd-=mdx) if ((float)jd<=(float)ndx) break;
Fdx=jd+mdx;
}
xshift=(int)((Fdx-fdx)/sdx+0.5);

if(j1s.Length>0) {
os=j1s;oc=',';om("s");JS=OS;
}

//------------------------------ Preparation for drawing the chart ----------------------------
ib=true;cm("fv");                               // Make form invisible temporarely
j=cpx+100+(int)(xshift*spx);k=cpy+100+(int)(yshift*spy);ib=true;cm("fs");
// Resize form to fit chart
cls="s7";gm("ec");                              // Color background to match Text Screen.
cls="S9";gm("sps");                             // Prepare black pen
lf=cpx;of=cpy;gm("crd");                        // Draw chart outlines

//-------------------------------- Drawing the chart's grid -----------------------------------
//------ Drawing Axes and Chart Descriptions ------
cls="S9";gm("sps");fns="trb16";                 // Prepare black pen & Large font
kf=-45-cpy/2;jf=0;os=o1s;gm("ctf");             // Draw Chart Description
fns="trb14";cls="G3";gm("sps");                 // Create green pen & Medium font
kf=-30-cpy/2;jf=0;os=h1s;gm("ctf");             // Draw X-Axis Description

//------ Drawing SmDiv's border lines ------
cls="r0";gm("sps");                             // Prepare red pen
for(int c=1;c< csy;c++) {                       // Draw all horizontal grid lines
jf=-cpx/2;lf=cpx/2;kf=of=c*spy-cpy/2;gm("cld");
}
for(int c=1;c< csx;c++) {                       // Draw all vertical grid lines
kf=-cpy/2;of=cpy/2;jf=lf=c*spx-cpx/2;gm("cld");
}

//------ Drawing MrkDiv's border lines ------
cls="b0";gm("sps");                             // Prepare blue pen
for(int c=0;c< (msy*cmy);c+=msy) {              // Draw 1 horz grid line each (msy) SmDiv's
jf=-cpx/2;lf=cpx/2;kf=of=c*spy-cpy/2+spy*(float)yshift;gm("cld");
}                                               // starting at the pixel amount of (yshift)

for(int c=0;c< (msx*cmx);c+=msx) {              // Draw one Vert grid line each (msx) SmDiv's
kf=-cpy/2;of=cpy/2;jf=lf=c*spx-cpx/2+spx*(float)xshift;gm("cld");
}
//------ Drawing Marks at MrkDiv's border lines ------
for(int c=0;c< cmy;c++) {                       // Scan all vert marks of the chart
if(k1s.Length>(c+1) && yshift>0) os=KS[c+1];  // If marks suppld && shift exists, shift one Mark
else if(k1s.Length>(c) && yshift==0) os=KS[c];// If marks suppld && no shift, assign corrct mark
else {od=Fdy+mdy*c;om("fd");od=0;}            // Else os=(MrkDiv no * Its sz in data
// + Data represented by First mark. Reset (od)
jf=-cpx/2-15;kf=msy*c*spy-cpy/2+spy*(float)yshift;gm("ctf");
jf=cpx/2+15;kf=msy*c*spy-cpy/2+spy*(float)yshift;gm("ctf");
// Drw mrk at the 2 sids 15 pxls away frm v mrgns
if(os=="0") {                                 // If data=0:
cls="G3";i=2;gm("sps");                     // Redraw horz MrkDiv line using 2-Pxls green pen
jf=-cpx/2;lf=cpx/2;kf=of=msy*c*spy-cpy/2+spy*(float)yshift;gm("cld");
cls="b0";gm("sps");                         // Restore blue pen
}
}

for(int c=0;c< cmx;c++) {                       // Repeat for horiz marks
if(j1s.Length>(c+1) && xshift>0) os=JS[c+1];
else if(j1s.Length>(c) && xshift==0) os=JS[c];
else {od=Fdx+mdx*c;om("fd");od=0;}

kf=-cpy/2-15;jf=msx*c*spx-cpx/2+spx*xshift;gm("ctf");
kf=+cpy/2+15;jf=msx*c*spx-cpx/2+spx*xshift;gm("ctf");

if(os=="0") {
cls="G3";i=2;gm("sps");
kf=-cpy/2;of=cpy/2;jf=lf=msy*c*spx-cpx/2+spx*(float)xshift;gm("cld");
cls="b0";gm("sps");
}
}

//------------------------------------ Drawing the chart ------------------------------------
cls="S9";i=2;gm("sps");                         // Prepare 2-pixel wide black pen
double xscale=spx/sdx,yscale=spy/sdy;           // Pxls/Dta Scale = SmDiv sz (in pxls/in data)
for(int d=0;d< JD.Length-1;d++) {               // Scan all data points
jf=(float)((JD[d]-fdx)*xscale-cpx/2);lf=(float)((JD[d+1]-fdx)*xscale-cpx/2);
kf=(float)((KD[d]-fdy)*yscale-cpy/2);of=(float)((KD[d+1]-fdy)*yscale-cpy/2);
gm("cld");                                    // Draw line bet each two adjacent points
}

//--------------------------------------- Finish Drawing -------------------------------------
cm("fv");                                       // Return visibility to Form.
um("c");um("co");hs=vs="";                      // Reset All GUV's
}
====================================================================================================

REMARK:
=======

Notice that when we compared numbers of type "double" we reduced their precision to "float" before
the comparison was made. The reason is that the math processor sometimes after doing a math operation
on a number of type "double", gives a result like "2.50000001" or "2.4999999" when the result should
be "2.5". Therefore when the result is compared against "2.5" the comparison fails.

Reducing the numbers' precision to "float" is only one way to fix this problem. A better way to fix
it would be by cleaning the numbers before the comparison is done. This can be done by converting the
"double" number to "string" and back with (om("fd");om("td");)  Method om("fd") cleans the numbers in
addition to converting them to "string" types.

The original reason for adding the "cleaning of numbers" process to method om("fd") was that it was
expected to be called immediately before displaying or printing numbers and we can't accept displayed
or printed numbers to look bulky. Look at this code:

od=2.4999999;om("fd");tm();                    // Displays "2.5"

=======================

The next example shows how to use the method we have just developed to draw 4 charts:

(1) A chart to represent a simple function in the form "y=f(x)". The sine wave function "y=sin(x)"
is the one we'll draw.

(2) A chart to illustrate the two parametric equations which represent a specific shape. The shape
represented will be an ellipse. The two parametric equations are "x=10*cos(t)" and "y=5*sin(t)".

(3) A chart for statistical data. The data we are going to use is for Microsoft's stock prices of
4 weeks. The prices are for the 5 business days of each week. Monday of the first week was a
holiday, so the data is for 19 days. Here they are:

--------------------------------------------------------------------
MON          TUE           WED            THU          FRI
--------------------------------------------------------------------
Jan 21,35.90  Jan 22,35.66  Jan 23,35.79  Jan 24,36.54
Jan 27,35.76  Jan 28,36.00  Jan 29,36.39  Jan 30,36.59  Jan 31,37.56
Feb 3,36.21   Feb 4,36.08   Feb 5,35.55   Feb 6,35.91   Feb 7,36.29
Feb 10,36.53  Feb 11,36.89  Feb 12,37.19  Feb 13,37.33  Feb 14,37.34
--------------------------------------------------------------------

(4) The same chart of (3) with the assumption that the user wants to specify his own marks
in the horizontal direction.

===============================================================================================
Example 25: Show how to use the "Chart Making" method which you have just developed to draw the
4 charts described above. Use an "old fashioned menu" with 4 choices for drawing the 4 charts.
===============================================================================================

public class a : pcs {

public override void init() {
tia=toa="t";
j=760;k=480;dm("s");
bli=0;
base.init();
}
public override void run() {
if (blp==0) {
fns="crb10";
cls="r0";fns="crb12";tm("c");            // Set font,clor, clear screen.
os="";sm("dd");string dts=os;            // Get today's date, assign to (dts)
fm(".");cls="G3";fns="crb10";            // Get current directory
os="                           Date: "+dts+"  Directory: "+os;tm();
os="";tm();                              // Display them in green then Skip one line
cls="S9";fns="crb10";                    // Change color to black
os="  (S) Drawing Sine wave graph using angles between (0-360)";tm();
os="  (P) Drawing the Parametric equations x=10*cos(t) & y=5*sin(t)";tm();
os="  (M) Drawing Microsoft's stock price chart using data for marking.";tm();
os="  (Y) Drawing Microsoft's stock price chart using supplied marking.";tm();
os="  (E) Exit Program.";tm();
os="";tm();

cls="b0";os="Selection :";bli=1;tm("i");return;
}

// ---------------------------- Main Menu Selection Processing------------------------------
else if (blp==1) {
os=os.ToUpper();                         // Convert selection to upper case
x="SPMYE".IndexOf(os);                   // Get selection index
if (os=="" || x<0){                      // If unexpected char entered
if (x==0) {bli=10;um("b");return;}
else if (x==1) {bli=20;um("b");return;}
else if (x==2) {bli=30;um("b");return;}
else if (x==3) {bli=40;um("b");return;}
else if (x==4) sm("e");                  // If selection was "E or e" exit program
}

//------ Drawing a sine wave chart using angles in the range (0-360) degrees ------
else if (blp==10) {
JD=new double[361];KD=new double[361];   // Dimension arrays for data range of (0-360)
for(c=0;c<=360;c++) {                    // Scan all data ranges
JD[c]=c;                               // Horiz data is all angles in order
js="sin";od=c;um("mt");                // Calculate the sine of each angle
KD[c]=od;                              // and store as vertical data
}
i=o=8;id=5;od=0.1;                       // SmDiv sz in pxl's=8 and in data=5(horiz) & 0.1(vert
j=4;k=2;js=ks="";                        // MrkDiv sz in SmDiv's=4(horiz) & 2(vert)
hs="Angles in degrees (x)";vs="Sin(x)";  // Horiz-Axis, Vert-Axis and Chart Descriptions
os="This chart represents the function y=sin(x)";
gm("cc");                                // Draw chart on (bio)
bli=90;um("b");return;                   // Transfer the drawing to the TextScreen.
}

//------------- Drawing an Ellipse with its Parametric Equations --------------
else if (blp==20) {
JD=new double[360];KD=new double[360];   // Dimension arrays for data range of (0-359)
for(c=0;c<360;c++) {                     // Scan 0-360 degrees angles range
q=1+(int)(c/90);                       // Order of the quadrant the angle is in (1:4)

a=c;                                   // If in 1st quad (default) keep angle as is
if(q==2) a=180-c;                      // If in 2nd quad Subtract angle from 180
if (q==3) {a=c-180;}                   // If in 3rd quad Subtract 180 from angle
if(q==4) a=360-c;                      // If in 4th quad Subtract angle from 360

js="cos";od=a;um("mt");                // Get cos(angle)
if(q==2 || q==3) od=-od;               // Negate if in 2nd or 3rd quad
JD[c]=10*od;                           // Calculate the X-Value

js="sin";od=a;um("mt");                // Get sin(angle)
if(q==3 || q==4) od=-od;               // Negate if in 3rd or 4th quad
KD[c]=5*od;                            // Calculate the Y-Value
}
id=od=0.5;                               // Only JD[], KD[], id, od & os specified. Defaults are
os="Chart for the Parametric functios x=10*cos(t) and y=5*sin(t)";
gm("cc");                                // acceptable for all othe param's. Draw chart on (bio)
bli=90;um("b");return;                   // Transfer the drawing to the TextScreen.
}

//------------- Drawing statistical data with data marking --------------
else if (blp==30) {
JD=new double[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};

KD=new double[] {35.90,35.66,35.79,36.54,35.76,36.00,36.39,36.59,37.56
,36.21,36.08,35.55,35.91,36.29,36.53,36.89,37.19,37.33,37.34};

i=o=20;id=1;od=0.25;                     // SmDiv's sz=20 in Pix's & 1 data(hor), 0.25 data(ver)
j=5;k=4;js=ks="";                        // MrkDiv sz=5 SmDiv's(hor) & 4 (vert)
hs="Business Days";vs="Stock Price in \$";// Horiz-Axis, Vert-Axis and Chart Descriptions
os="Microsoft's stock prices for the dates Jan 21 to Feb 15";
gm("cc");                                // Draw chart on (bio)
bli=90;um("b");return;                   // Transfer the drawing to the TextScreen.
}

//------------- Drawing statistical data with user selected marking --------------
else if (blp==40) {
JD=new double[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};

KD=new double[] {35.90,35.66,35.79,36.54,35.76,36.00,36.39,36.59,37.56
,36.21,36.08,35.55,35.91,36.29,36.53,36.89,37.19,37.33,37.34};

i=o=20;id=1;od=0.25;j=5;k=4;ks="";       // Data and grid sizes are Same as before
js="1st WK,2nd WK,3rd WK,4th WK";        // Horizontal markings string
hs="Business Days";vs="Stock Price in \$";// Horiz-Axis, Vert-Axis and Chart Descriptions
os="Microsoft's stock prices for the dates Jan 21 to Feb 15";
gm("cc");                                // Draw chart on (bio)
bli=90;um("b");return;                   // Transfer the drawing to the TextScreen.
}

//---------- Finishing the chart drawing operation to the TextScreen -----------
else if (blp==90) {
imp=bio;sm("csi");                       // Set (bio) into Clipboard
j=760;k=480;cm("fs");                   // Return form to regular size
tm("ep");                                // Text Screen edit-paste
os="";tm();                              // Move to next line