Personal C Sharp                                                         By  famsoft.org
HomeHow To StartExamples-DesktopExamples-WebPC# MethodsReference-DesktopReference-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. kf jf (Radius) | | / -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. jf=150;kf=180;kb=true;lf=of=100;gm("ced");// Radius=150,angle=180,polar coor, // 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. ad: Rotation angle. 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 values are zeros to start with. 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: o=0;of=0;od=0;ob=false;ad=0; 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 gm("cad"); // Extent angle=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 parallelogram for your application. =========================================================================================


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 cls="b0s9";ad=-90;gm("spl"); // Set color to linear gradient changing // 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"); cls="b0s9";ad=90;gm("spl"); // Create linear gradient color covering the // 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 applied at into (ad) (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 jd=kd=0.05;jf=88;kf=-109;ad=180;gm("stu"); 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 //----------------------------- Radial Gradient Paint ------------------------------ jf=-300;lf=of=150;gm("ce"); // Create a circle cls="s9g0";gm("spr"); // Create radial gradient brush for the circle gm("grf"); // Render-fill the circle object. //----------------------------- Linear Gradient Paint ------------------------------ jf=-100;lf=of=150;gm("ce"); // Create another circle next to previous one cls="s9r0";ad=0;gm("spl"); // Create linear gradient brush for the circle 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 cls="s9s0";jf=300;id=20;ad=30;ks="d";gm("grs"); // 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) os="Gradient Paint"+ss+"Gradient Paint"+ss+"Special Effects"+ss+"Special Effects"; gm("ctf"); // Create text object and draw-fill. kf=-130; os="Radial"+ss+" Linear"+ss+" Reflection"+ss+" Depth"; 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 ad=30;ks="d";gm("grs"); // ad=3D angle, ks="d" means select "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 ad=30;ks="d";gm("grs"); // ad=3D angle, ks="d" means select "depth" gm("gc"); 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 } } } ========================================================================================= TUTORIAL: There is nothing new about this example, however we'll explain here how 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. rgp: Radial gradient paint brush object. Before your program creates any of this 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. =================================================================================================== DRAWING SHADOWS: ================ =================================================================================================== 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 ad=(90-5*x); // rad=150 and different angles starting at 180 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; ad=90-5*x; 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. gm("sdd"); // Return to default output device setting. 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. Adjusting mouse coordinates: ---------------------------- 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) About the next example: ----------------------- 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: kf,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: gm("brc"); // Adjust received coordinates 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: gm("brc"); // Adjust received coordinates 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. gm("brc"); // Adjust received coordinates 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. os="Press a key then click here to display its character";jf=-wf/4;kf=0.40f*hf;gm("ctf"); } //-------------------------------- 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. os="Press a key then click here to display its drawing";jf=-wf/4;kf=-0.10f*hf;gm("ctf"); } //-------------------------------- 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 } cls="S9";i=3;gm("sps"); // Start with 3 pixels wide pen of black color. } } ===============================================================================================


============================================================================================== 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. id=4;kf=-30;cls="s0s9";ad=45;ks="d";gm("grs"); // 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=40+d;os="your name";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 3D object which you know to start with. (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"); // circle, radius=55,angle=180,polar coor, //-------------------------------- 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) ------ xdy=ndy=KD[0]; // Start with both=first data 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 jf=-40-cpx/2;kf=0;os=v1s;ad=90;gm("ctf"); // Draw Y-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. About the next example: ======================= 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() { // ------------------------------------ Main Menu --------------------------------------- if (blp==0) { fns="crb10"; cls="r0";fns="crb12";tm("c"); // Set font,clor, clear screen. os=" GRAPH MAKING MENU";tm(); 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; // Get user selection and jump to block 1. } // ---------------------------- 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 bli=0;um("b");return; // return to block 0. } // else jump to the block which executes selection 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 cls="b0";os="Hit [ENTER] to return to menu:";bli=0;tm("i");return; } } } ==================================================================================================