Personal C Sharp                                                         By  famsoft.org
HomeHow To StartExamples-DesktopExamples-WebPC# MethodsReference-DesktopReference-Web


=============================================================================================== USING THE WINDOWS PRESENTATION FOUNDATION (WPF) =============================================== We have now two seperate classes. Class (pcs) is for version 2. You can use it with either version 2 or version 3. Additionally, we have class (pcs3) which can be accessed with version 3 only. The two classes are identical in everything except Controls and Drawing. All version 3 additions will be in class (pcs3) only. Changes made to Controls: ========================= The (WPF) controls are more similar to the "Web Controls" than to the "Desktop" Controls. For this reason, class (pcs3) is using almost identical software to the one used in class (pasp) for this purpose. Class (pcs3) does not do the layout discretely, it does it in the same manner as class (pasp) does. ------------------------------------------------------------------- If you have not studied the "Web Examples" yet, you must study at least the first 4 examples of the "WPDI" before you get further. ------------------------------------------------------------------- Class (pasp) inserts each group of controls into a table, then inserts the tables into bigger tables, and this continues until it ends with the root table "tb0" containing the whole page. Class (pcs3) uses the same idea except that it replaces tables with panels called "Grids" which are very similar to tables. The root grid "gr0" contains everything in the page. Some of the good ideas used in class (pasp) like having a "Master page" and combining controls with graphics are going to be used also in class (pcs3) Although we have done the best we can to absorb the new changes internally into class (pcs3), so you can use the same PC# methods as if nothing has been changed, there are still some few changes which you need to be taking care of. As a sample, this is how example 1 of the chapter "Using Controls"is to be modified in order to run with class (pcs3) and the WPF: ---------------------------------------------------------------------------------------------- public class a : pcs3 { public override void init() { base.init(); } public override void setup() { // All installations are done within this method //---------------------------------- Page Contents ----------------------------------- cns=""; // gr0 has no container since it's the root cs="gr0";cls="S9g8";cm("i"); // gr0 installation parameters //----------------------------------- gr0 Contents ----------------------------------- cns="gr0"; cs="tf0";j=k=0;oyd=0.5;ds="c";lf=200;cls="b0y0";fns="trbi20";cm("i"); cs="bt0";cis="My Color Changes";j=0;k=1;oyd=0.5;ds="c";lf=160;of=30;fns="trb16"; cls="S9y0";cm("i"); } public override void update() { // This method is called whenever an event takes place if ("bt0".Equals(cs)) { // If button "bt0" has generated this event cs="tf0";cm("gu"); // Get latest update of text field "tf0" in (cus) if (cus.Length<1) return; os=cus.Substring(0,1);om("u");// Get 1st char of (cus) and covert it to U/C n="RGB".IndexOf(os); // See if it was R,G,B or none of them if (n<0) cls="S9y0"; // If none of them select yello background color else if (n==0) cls="S9r0"; // If was "R" select red background color else if (n==1) cls="S9g0"; // If was "G" select green background color else if (n==2) cls="S9b0"; // If was "B" select blue background color cs="bt0";cm("sC"); // Call method cm() to set the color for "bt0" cs="tf0";cus="";cm("su"); // Erase tf0's text. cs="tf0";cm("sx"); // Turn the focus state of "tf0" on. } } } ---------------------------------------------------------------------------------------------- Compiling: ========== You may compile the class with either the Visual Studio or tool (pcp) If you receive an error when you compile with our tool, select "PC# Reference-Desktop" and read the section titled "The refs.txt file" to learn how to correct the problem. ---------------------------------------------------------------------------------------------- As you can see, the code is mostly the same. You are still using the same methods, the same architecture, the same color and font codes. So, as far as you are concerned almost nothing has changed. The only difference, you may have noticed is using grids instead of tables. Using grids is similar only. Some rules are different since they require more complicated setup. We have done our best to simplify their setup and ended with dividing controls into 3 categories each require different actions. Let us list all setup parameters before we get further: j,k : Index of first cell which the control occupies horizontally and vertically. This has been explained in WPDI so we are not going to say more about these two parameters here. i,o : Column and row span. These two items have also been discussed thoroughly in WPDI. lf,of : Width and height of the control. oxd,oyd: They are new. When you like to install two controls into one grid panel horizontally, and the first contol is twice as wide as the second one, you need to make the assignments (oxd=0.67;) for the first one and (oxd=0.33;) for the second one. In general, for each control in the container: oxd = (Its CellWidth / ContainerWidth) oyd = (Its CellHeight / ContainerHeight) When we have been installing web controls into tables, the space allocation job has been done automatically. Unfortunately, Grid panels are less helpful with this feature. This is why we need these two parameters. ds : Direction string. Can be (c/n/s/e/w/ne/nw/se/sw/fh/fv) It tells how the control will be aligned inside its cell. (c) means center, Next 8 directional symbols are obvious. (fh) and (fv) mean that the control should fill its cell horizontally / vertically. Default values: --------------- As you know, when you keep a parameter unassigned, its value will be zero if numeric or "" if string. Here is what leaving each of the parameters unassigned mean: j,k : Means that the control starts at the first cell (horizontally, vertically) i,o : Means that the control ocupies one cell (horizontally, vertically) oxd,oyd : Means that the control occupies the full width or the full height of its container. ds : Means mount the control at the center of its cell. Let us now discuss each control category requirements: (1) Grids: ---------- As you can see in the code above, the root grid is installed first. Then you may need to install more grids into the root grid, then install additional grids into each of the new grids,... this may continue forming a tree of grids. The root grid requires no parameters other than the color if you like it to be identifiable. This is because,it is the only control in its container. Branch grids require j, k, i, o and either (lf,of) or (oxd,oyd) You may also supply both if you don't like the grid to occupy its entire cell (meaning that you like to keep a margin around it) (2) Image Controls: ------------------- The simplist way to mount an Image Control is to divide the space of its container by installing additional grids into it first, then installing the image control into one of the new grids. Since it will be the only control in its container, it will require no parameters other than (lf,of) if you like to change the size of its image. (3) All other controls: ----------------------- Supplying j, k, i, o, oxd and oyd is necessary for all other controls (unless you are accepting defaults) Assigning values to (lf, of) is optional. Creating a master page: ======================= As you have noticed with web controls, master pages offer a great help. So, let us have one which contains an icon, a title and a settable page number to be displayed. It will also contain the three public variables (PageWidth), (PageHeight) and (PageColor) which allow each page to set its page width, its page height and its background color if default values have not been acceptable. The master page class will extend class (pcs3) and each example class will extend the master page class. You can create a hierarchy of master pages and each may contain fields and methods which can be accessed by your application classes. --------------------------------------------------------------------------------------------- public class ms1:pcs3 { public int PageNumber=0; public int PageWidth=560; public int PageHeight=300; public string PageColor="S9y7"; public override void setup() { base.setup(); //---------------------------------- Page Contents ----------------------------------- cns=""; // gr0 Container="" since it's // mounted directly into the page. cs="gr0";cls="S9s9";cm("i"); // gr0 installation parameters //----------------------------------- gr0 Contents ----------------------------------- cns="gr0"; // Container=gr0 for next grids cs="gr1";j=0;k=0;lf=PageWidth-150;of=60;ds="w";cls="S9g7";fns="tr12";jd=5;cm("i"); cs="gr2";j=1;k=0;lf=150;of=60;ds="e";cls="S9g7";fns="crb10";jd=5;cm("i"); cs="gr3";j=0;k=1;i=2;lf=PageWidth;of=PageHeight;ds="c";cls=PageColor;fns="crb10";cm("i"); //----------------------------------- gr2 Contents ---------------------------------- cns="gr1"; // gr1 is divided into 4 col,3 rows cs="lb11";j=0;k=0;oyd=0.6;ds="c";cis="Windows Presentation Foundation Examples"; fns="trb16";cls="r0g7";cm("i"); cs="lb12";j=0;k=1;oyd=0.4;ds="c";cis="Example Number "+PageNumber; fns="trb12";cls="b0g7";cm("i"); //----------------------------------- gr2 Contents ---------------------------------- cns="gr2"; // gr1 is divided into 4 col,3 rows cs="im01";j=0;k=0;ds="e";ims="images\\icon.bmp";cm("i"); } public class helper:pcs { } } ---------------------------------------------------------------------------------------------- The master page makes a good exercise on mounting controls. Here are some remarks: (1) We needed to create two grids one for common data at the top and one to be used by the child pages. Since we wanted to mount an image, we had to create a third grid for it. (2) For the grids we must supply either (lf,of) or (oxd,oyd) In this case, we supplied (lf,of) (3) (i,o) have been left unassigned for all except for the grid which will be used by child classes (gr3) since it occupies 2 cells horizontally. (4) For (lb11,lb12), (oxd) has not been assigned since they occupy their entire cell horizonty. ---------------------------------------------------------------------------------------------- Accessing class (pcs) graphics: ------------------------------- Version 2 has been great for graphics and we have spent plenty of time creating software using the ".NET" version 2 and both classes (pcs) and (pasp), so we must make use of them here. We have developed an easy way to access version 2 graphics which requires the following: (1) You install an Image Control which will receive (bio) with your drawing on. (2) You create a nested class which extends class (pcs). (3) You start the nested class by calling gm("wo") meaning "Open a new WPF operation". (4) You write the same code into the nested class just as you have been. (5) You end the nested class with calling gm("wc") meaning "Close the WPF operation. This will modify the resulting (bio) with all the graphics on its surface to meet WPF requirements. (6) From method run() of the top class you execute the nested class then call cm("su") supplying it with the image control's keyname. Method cm() at this mode updates an image control by changing its image. The method expects the new image file name to be assigned to (ims) This time we assign "b" to (ims) which means to the method that the image is (bio) --------------------------------------------------------------------------------------------- Example 1: Create a master page then extend it with a class which runs two examples of the version 2 kind. One is example 1 of "Handling controls" and the other is example 8 of "Drawing". --------------------------------------------------------------------------------------------- //assembly ms1.exe; // Referenced object. public class a : ms1 { // using pcs3 // Class a extends master class. public override void init() { base.init(); } public override void setup() { PageNumber=1;base.setup(); // Call Master Class's setup(), set Page No. //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr3 is divided into 2 columns cs="gr4";cis="g1";j=0;k=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";cm("i"); cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";cm("i"); //----------------------------------- gr4 Contents ----------------------------------- cns="gr4"; cs="tf0";j=k=0;oyd=0.5;ds="c";lf=200;cls="b0y0";fns="trbi20";cm("i"); cs="bt0";cis="My Color Changes";j=0;k=1;oyd=0.5;ds="c";lf=160;of=30; fns="trb16";cls="S9y0";cm("i"); //----------------------------------- gr5 Contents ----------------------------------- cns="gr5"; cs="im1";ds="c";ims="";lf=200;of=200;cm("i"); } // This is the control which will receive (bio) and scale it //-------------- Method update is entirely used by color change section ---------------- public override void update() { // This method is called whenever an event takes place if ("bt0".Equals(cs)) { // If button "bt0" has generated this event cs="tf0";cm("gu"); // Get latest update of text field "tf0" in (cus) if (cus.Length<1) return; os=cus.Substring(0,1);om("u");// Get 1st char of (cus) and covert it to U/C n="RGB".IndexOf(os); // See if it was R,G,B or none of them if (n<0) cls="S9y0"; // If none of them select yello background color else if (n==0) cls="S9r0"; // If was "R" select red background color else if (n==1) cls="S9g0"; // If was "G" select green background color else if (n==2) cls="S9b0"; // If was "B" select blue background color cs="bt0";cm("sC"); // Call method cm() to set the color for "bt0" cs="tf0";cus="";cm("su"); // Erase tf0's text. cs="tf0";cm("sx"); // Turn the focus state of "tf0" on. } } //------------------- Method run is entirely used by the Drawing section --------------- public override void run() { N no=new N(); // Inistantiate nested class. no.run(); // Run nested class instance. cs="im1";ims="b";cm("su"); // Update Image control with (bio) image. } //--------------------------------- The nested class ----------------------------------- public class N : pcs { // Class (N) extends (pcs) public override void run() { gm("bwo"); // Open a new WPF operation j=k=220;cm("fs"); // Reduce size to fit object. cls="y7";gm("ec"); // Make background color matchs page's color. //********************** No change upto next asterisk line ************************* 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 } //*********************************************************************************** gm("bwc"); // Close WPF operation } } } ============================================================================================== Compiling: ========== You may compile the class with either the Visual Studio or tool (pcp) If you receive an error when you compile with our tool, select "PC# Reference-Desktop" and read the section titled "The refs.txt file" to learn how to correct the problem. Notice how the class declaration statement contains the word "pcs3" following a comment symbol. public class a : ms1 { // using pcs3 // Class a extends master class. When you compile a class using one PC# tool, the tool checks to see if your class extends class (pcs3) If it does, it reads the file "system\\Refs.txt" which is located into your working directory and adds all the locations it contains. This time your class extends class (ms1), but since (ms1) extends (pcs3), you want the tool to do the same. The tool is made to merely look for the availability of the word "pcs3" at the class declaration line in order to conclude whether to add the locations or not. So including the word "pcs3" into any commented phrase on the same line can make it do the same. ----------------------------------------------------------------------------------------------


=============================================================================================== The Slider Control: =================== The WPF controls can be accessed with method cm() using the same code which you have been using to work with Form-based controls. The only difference is in the layout method which is similar to the one class (pasp) uses to mount web controls. One control which has been always neglected is the "Slider Control". It was assumed to be unnecessary, but we need it now since it can help with 3D graphics. The slider control's installation parameters (not including the mounting parameters which have already been discussed) are: cis : The control's label. Will be discussed in details next. lf,of : Width or Height of the control. This also sets the orientation of your wanted slider control. If you want a horizontal one, specify (lf) and keep (of=0) If you want a vertical one, specify (of) and keep (lf=0) js : Minimum and Maximum values. They are the two values you receive from the slider control when it's at its two extreme positions. cus : Contains the initial value where you like the pointer to start at. Handling the Slider Control's event: ------------------------------------ Each time the slider control is activated, method update() is called with the slider's keyname and the value which its pointer is at assigned to (od) Control's Labels: ================= We normally like to place a label near any control in order to identify it. For this reason, PC# allows you to specify a label to almost all controls. You assign the label to (cis) and assign to (os) a direction code which can be (n/s/e/w) which determines where the label should be relative to the control. The positions are as follows: n: (Default) Place label above control. s: Place label under control. e: Place label to the right of control. w: Place label to the left of control. Labels are placed very close to their controls. You can seperate them from their controls by any distance by adding spaces or line feeds before or after the label's text depending on the direction code which you have specified. Here are two examples: cis="Label\n";os="n"; // Place the label above control seperating the two with a linefeed. cis=" Label"; os="e"; // Place the label right of control seperating the two with space. In class (pasp), labels have been doing great jobs. HTML has been possible to use with them so we have been able to use them to add several lines of text, tables, hyperlinks with variety of colors and fonts. Here we don't have all this luxury, but we can still insert linefeeds into a label's text to make it a multi-line one as we'll do with the slider's label in the next example. The Labels are displayed using the same font which is assigned to the control and the color which is assigned to the control's foreground. Using WPF 2D Graphics: ====================== Just like we made method cm() usable without changes for the WPF, we like to make method gm() also usable with minimum changes. We already know that we can import version 2 graphics to our WPF classes, but it should be better if we can use the WPF itself to do the job. To receive version 2 graphics, we used an Image control, we'll do the same with the WPF 2D graphics. This requires setting the graphical output device to the Image control which you like to draw into. You do that by assigning the keyname of the control to (cs) and calling gm("sdi") When your drawing is complete, you call gm("fdi") which means "Finish drawing to the image control device". Whatever you draw will not show up until this method is called. Method gm()'s new parameters: ----------------------------- The WPF does not use type "float" which has been used with version 2 graphics, it uses type double instead. Therefore, we have also switched method gm()'s input parameters to their equivalent type double ones. This means that: jd,kd replace jf,kf id,od replace lf,of Some of the values required but not extensively used, have been assigned to variables of type "double" in Version 2. As an example, (jd,kd), (id,od) have been used to supply scaling and shearing factors when setting up an Affine Transform object. Those valuse are now assigned to (jf,kf), (lf,of) instead. In other words, when we use the WPF for drawing, we use an opposite logic in selecting variable types. ============================================================================================= Example 2: Create a slider control which changes values in the range (+5:-5) Set its initial value at zero. Create a text field and make it constantly show the value which the slider's pointer is at. Also create an Image control and use it to display an image and several shapes. Show how to fill a shape with a texture brush also. ============================================================================================= //assembly ms1.exe; public class a : ms1 { // using pcs3 public override void init() { base.init(); } public override void setup() { PageNumber=2;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr3 is divided into 2 col's. cs="gr4";cis="g1";j=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";cm("i"); cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";cm("i"); //----------------------------------- gr4 Contents ----------------------------------- cns="gr4"; // gr4 is divided into 2 rows. cs="tf0";cis="Display";os="s";k=0;ds="c";oyd=0.5;lf=60;cus="0.00";cls="b0y0";fns="trb22"; jd=3;cm("i"); // jd=3 means set a 3 pixels border around control. cs="sl0";cis="Slide pointer and watch Display\n -5 0 +5";os="n"; k=1;oyd=0.5;js="-5,5";cus="0";lf=200;ds="c";cls="S9b2";fns="crb12";cm("i"); //----------------------------------- gr5 Contents ----------------------------------- cns="gr5"; // gr5 is not divided. cs="im1";cis="\n2D Drawings";os="s";ds="c";ims="";lf=200;of=200;fns="trb16";cm("i"); } //------------------------------- Handling slider events ------------------------------ public override void update() { if ("sl0".Equals(cs)) { // If Slider "sl0" has generated this event ib=true;om("fd"); // Convert (od) to string reducing decimal digits to two. cus=os;cs="tf0";cm("su"); // and set it into text field control. } } //----------------------------- Drawing into Image control ---------------------------- public override void run() { cs="im1";gm("sdi"); // Make Image Control "im1" the graphical output device. kd=-35;id=od=80;fls="images\\flower.jpg";gm("cid"); // Display an image 35 pixels under center. fls="images\\flower.jpg";gm("blf");gm("spt"); // Create a new texture brush using the same image. kd=30;id=3;od=90;gm("c=f"); // Create equally sided triangle and fill it with the brush // Place it 30 pixels above center. cls="y0";id=3;gm("sps"); // Create a 3 pixels wide pen of yellow color. id=6;od=100;gm("c=d"); // Create Hexagon and draw it at center. cls="r0";id=1;gm("sps"); // Create a new pen/brush of solid red color. id=od=150;gm("crd"); // Create a new Rectangle and draw it at center. cls="b0";id=1;gm("sps"); // Create a new pen/brush of solid blue color. id=od=150;gm("ced"); // Create a circle and draw it at center. gm("fdi"); // Finish drawing into the specified Image control. } } ==============================================================================================


=============================================================================================== Are we really drawing in pixels? ================================ No. We are just pretending we are! All drawings are relative, but we are trying to make our drawings compatible with class (pcs)'s drawings. We try to think in pixels, just like we used to do with class (pcs) Some people like to use small numbers like 1 or 2 for the widths and heights of the objects they draw. This is not very good since it teaches us to neglect accuracy and also because the default width of a pen is still one (it used to be one pixel in class pcs) However, the variable which you assign the pen's width to when you create a pen is now (id) which is of type double, so it can be assigned a fraction. More about the WPF 2D Graphics: =============================== There is almost no difference between the PC# methods which operates on Version 2 graphics and the ones which operate on WPF 2D graphics other than the types of used variables. Drawing lines, Shapes, images and text: --------------------------------------- jd,kd : Location of object's center relative to graphical output device's center. id,od : Width and height of shape. lf,of : Shear factors if necessary ad : Rotation angle in degrees if necessary. For lines, (jd,kd) are coord's of start point and (id,od) are coordinates of end point. For text, (jd,kd) represent location of text center. Font code (fns) and text to draw (os) are also required. For images, you need to supply their center location in (jd,kd) and their desired width and height assigned to (id,od) in addition to the file name or URL assigned to (fls) All modes are the same. Affine Transform: ----------------- jd,kd : Present location of object's center relative to graphical output device's center. jf,kf : Scale factors. lf,of : Shear factors. id,od : Translation. Required displacement of object. ad : Rotation angle. You set the unit transform by calling gm("stu") To apply it to a shape create the shape without drawing then call gm("gtd") to draw it or gm("gtf") to fill it after being transformed. As explained before, we use the transformation order: Scale - Shear - Rotate - Translate. So, you must plan all your object transformations accordingly. Note that the first 3 operations are performed around object's center without changing the center's location. 3D Effects: ----------- Both the "Reflection" and "Depth" 3D effects are available without changes other than variable types which are: REFLECTION: jd,kd: Desired location of object center relative to Graphical device's center cls : Wanted base color of : Brightness factor. DEPTH : id : Depth. ad : 3D inclination angle relative to negative X-axis. cls : Combined code of brightest-Darkest colors. Shapes must be created at CENTER prior to applying 3D effects to them. ============================================================================================== Example 3: Rewrite examples 7 and 8 of the "Drawing" chapter to get them to run using the Windows Presentation Foundation (WPF) software. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=3;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr3 is divided into 2 col's. cs="gr4";cis="g1";j=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";cm("i"); cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";cm("i"); //----------------------------------- gr4 Contents ----------------------------------- cns="gr4"; // gr4 is not divided. cs="im1";cis="\n3D Effects, Reflection";os="s";ds="c";ims="";lf=200;of=200;fns="trb16";cm("i"); //----------------------------------- gr5 Contents ----------------------------------- cns="gr5"; // gr5 is not divided. cs="im2";cis="\n3D Effects, Depth";os="s";ds="c";ims="";lf=200;of=200;fns="trb16";cm("i"); } public override void run() { //-------------------------------- Drawing the Jewel ------------------------------------ cs="im1";gm("sdi"); // Set (im1) as graphical output device. cls="y0";gm("sps"); // Create yello brush id=od=195;gm("crf"); // and paint background with it. //-------------------------------- Drawing the object ----------------------------------- id=od=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. id=6;od=50;gm("c="); // Create hexagon shape object at center. of=1;cls="r0";ks="r";gm("grs"); // Draw the object using sp effects-refl at of=1;jd=45;cls="b0";ks="r";gm("grs"); // center in red, then repeat 6 times using of=1;jd=-45;cls="b0";ks="r";gm("grs"); // different colors and different locations of=1;jd=22;kd=40;cls="g0";ks="r";gm("grs"); of=1;jd=-22;kd=40;cls="m0";ks="r";gm("grs"); of=1;jd=22;kd=-40;cls="m0";ks="r";gm("grs"); of=1;jd=-22;kd=-40;cls="g0";ks="r";gm("grs"); id=od=25;gm("ce"); // Create a circle at center (pearl) for (int x=0;x<20;x++) { // Draw it 20 times using sp effects-reflection jd=80;kd=18*x;kb=true; // at locations around the plate. Polar coord's of=1;cls="p0";ks="r";gm("grs"); // are used for specifying locations } gm("fdi"); // Finish drawing to (im1) //-------------------------------- Drawing the 3D Text --------------------------------- cs="im2";gm("sdi"); // Set (im2) as graphical output device. cls="b0";gm("sps"); // Create blue brush id=od=195;gm("crf"); // and paint background with it. os="PC#";fns="trb100";gm("ct"); // Create text geometry. ks="d";ad=30;id=10;cls="s9s0";gm("grs"); // Draw text using 3D effects-depth gm("fdi"); // Finish drawing to (im2) } } ==============================================================================================


=============================================================================================== PLAYING VIDEO ============= The new method vm(): ==================== Method gm() of class (pcs3) which has been written initially for classes (pcs) and (pasp) has been modified to fit the WPF 2D Graphics and will be used for this purpose. The new method vm() will handle the new features of the WPF like video and 3D Drawing. Playing video files: ==================== The image control which has proven to be a useful graphical output device which can display all shapes and images can also show movies. So, to play a movie, you need to create an image control and call method gm("sdi") with its keyname to start and to call gm("fdi") to finish at the end. After setting the graphical output device, you call vm("mvo") to open a new video play operation, then you load the movie file by supplying its URL or its local file path (assigned to fls) to method vm("mvl") You can then call vm("mvp") to play the movie and call vm("mvc") to close and execute the video play operation. Here are the most useful video modes: mvo : Open a new video play operation. IN: id,od (optional) width,height of movie display rectangle. mvl : Load video file/URL (assigned to fls) mvp : Play mvu : Pause mvs : Stop mvv : Set volume. IN: id=wanted volume (range is 0:1) mvb : Set Balance. IN: id=wanted balance (range is -1:1) mvc : Execute and Close current video play operation. =========================================================================================== Example 4: Show how to play a video file and how to set the play volume and balance. Show also how to stop it and pause it. =========================================================================================== We used the video file "bear.wmv" which is a short movie file sample that comes with Windows Vista. You need to search for it into your computer or search for any file with same extension, then to copy the file to your working directory before running this example. If you'll be using a different file, modify the line marked with **** with the new file name. ------------------------------------------------------------------------------------------- //assembly ms1.exe; public class a : ms1 { // using pcs3 public override void init() { base.init(); } public override void setup() { PageNumber=4;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr3 is divided into 2 col's. cs="gr4";cis="g1";j=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";cm("i"); cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";cm("i"); //----------------------------------- gr4 Contents ----------------------------------- cns="gr4"; // gr4 is divided into 3 col's and 3 rows. cs="sl0";cis="Volume Control\n0 5 10";os="n";k=0;i=3;oyd=0.33; js="0,10";cus="5";lf=232;ds="c";cls="S9b2";fns="crb12";cm("i"); cs="sl1";cis="Balance Control\nL 0 R";os="n";k=1;i=3;oyd=0.33; js="-5,5";cus="0";lf=232;ds="c";cls="S9b2";fns="crb12";cm("i"); cs="bt0";cis="Play";j=0;k=2;ds="c";oxd=0.33;oyd=0.34;cls="s9G2";fns="trb22";jd=3;cm("i"); cs="bt1";cis="Pause";j=1;k=2;ds="c";oxd=0.33;oyd=0.34;cls="s9o2";fns="trb22";jd=3;cm("i"); cs="bt2";cis="Stop";j=2;k=2;ds="c";oxd=0.34;oyd=0.34;cls="s9r0";fns="trb22";jd=3;cm("i"); //----------------------------------- gr5 Contents ----------------------------------- cns="gr5"; // gr5 is not divided. cs="im1";cis="\nVideo";os="s";ds="c";ims="";lf=200;of=200;fns="trb16";cm("i"); } public override void update() { if ("sl0".Equals(cs)) { // If volume control activated: id=od/10;vm("mvv"); // Adjust value to be (0:1) then set volume } else if ("sl1".Equals(cs)) { // If balance control activated: id=od/5;vm("mvb"); // Adjust value to be (-1:1) then set balance } else if ("bt0".Equals(cs)) { // If Play button clicked: vm("mvp"); // Play movie. } else if ("bt1".Equals(cs)) { // If Pause button clicked: vm("mvu"); // Pause play. } else if ("bt2".Equals(cs)) { // If Stop button clicked: vm("mvs"); // Stop play. } } public override void run() { cs="im1";gm("sdi"); // Set (im1) as graphical output device. vm("mvo"); // Open a new video play operation. fls="bear.wmv";vm("mvl"); // **** Load movie file id=1;vm("mvv"); // Set volume. id=0;vm("mvb"); // Set balance vm("mvc"); // Execute and close video play operation. gm("fdi"); // Finish drawing to (im1) } } ==============================================================================================


============================================================================================== Playing Audio: ============== Method um() contains modes to play audio files of type "wave". This is possible to use in both classes (pcs) and (pcs3) Here is an example which you can try: public override void run() { um("awo"); // Open a new audio-wave file play operation fls="c:\\windows\\media\\chimes.wav";um("awl");// Load audio file. kb=true;um("awp"); // Play file continuously. } New option for drawing with 3D Effects: ======================================= When you use the 3D effects-depth you can select a new style which makes you feel that the drawing grows in size as it moves forward. You can get that style by adding (ib=true) to your parametrs. See next example. New variables which you need to know about: =========================================== As you must know, one of the objectives of Personal C. Sharp is to minimize the number of variables and methods which you use in order to make your job easy and to reduce the chance for errors. However, handling the new 3D classes of the WPF has made it necessary to add two more variables which are (ld) and (dd), both are of type "double". This addition applies to class (pcs3) only. (jd,kd,ld) will be used as the 3D coordinates of a point or a vector. (id,od,dd) will be used as (width,height,depth) of a 3D object. Please make sure not to use those variable names as your local variables. Here is a list of all the variables which have been borrowed from your pre-defined "one char + type" list: cs: Control Keyname. fs: File Keyname. ds: Direction code. ls: Replaces the GUV (is) since (is) is a C# keyword. ad: Angle. ld: 3rd dimension (z) (Class pcs3 only) dd: Depth. (Class pcs3 only) We hope we'll not need more. Please make sure not to use any of these names as local var's. Drawing a Rectangular Block: ============================ If you look at the chapter on "Drawing", you'll see examples which show how to create a Rectangular block by assembling its outer surfaces together and how to draw on those surfaces. You have also seen examples on how to draw cylinders and pyramids of general bases. One objective in developing those drawings is to have 3D objects which can automatically supply the WPF 3D drawing classes with their mesh pattern data. Now it's time to rewrite the methods which create those 3D objects using WPF's software so we can use them. The Rectangular block has been added to the cylinder and pyramid as a 3D object to request. You request drawing a rectangular block by calling gm("cRd") Here are all necessary parameters: jd,kd : location of Rectangular Block's center relative to Form's center. id,od,dd: Width,Height,Depth. cls : Combined color code for "visible lines" and "hidden lines". ib : "Show hidden lines" flag. Hidden lines will be displayed when (ib=true) ============================================================================================== Example 5: Show how to use the new 3D effects feature. Also show how to draw a rectangular block of dimensions 40 X 80 X 50. Make hidden lines appear. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=5;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr3 is divided into 2 col's. cs="gr4";cis="g1";j=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";cm("i"); cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";cm("i"); //----------------------------------- gr4 Contents ----------------------------------- cns="gr4"; // gr4 is not divided. cs="im1";cis="\n3D Effects, Depth";os="s";ds="c";ims="";lf=250;of=250;fns="trb16";cm("i"); //----------------------------------- gr5 Contents ----------------------------------- cns="gr5"; // gr5 is not divided. cs="im2";cis="\nRectangular Block";os="s";ds="c";ims="";lf=250;of=250;fns="trb16";cm("i"); } public override void run() { //------------------------------- 3D Effecte - Depth ---------------------------------- cs="im1";gm("sdi"); // Set (im1) as graphical output device. cls="b6";gm("sps"); // Create light blue brush id=od=250;gm("crf"); // Paint background with it. jd=20;kd=-30;os="PC#";fns="trb80";gm("ct"); // Create text geometry. ks="d";ad=30;id=100;ib=true;cls="s9s0";gm("grs"); // Draw text using 3D effects-depth with scaling gm("fdi"); // Finish drawing to (im1) //--------------------------- Drawing the Rectangular Block --------------------------- cs="im2";gm("sdi"); // Set (im2) as graphical output device. cls="b6";gm("sps"); // Create light blue brush id=od=250;gm("crf"); // Paint background with it. id=40;od=80;dd=50;ib=true;cls="S9s0";gm("cRd"); // Create 40X80X50 Rect Block, show hidden lines cns="yz-20"; // Container: Parallel to yz plane at x=-20 cls="r0";gm("sps"); // Create red pen fns="trb20";os="PC#";gm("ctf"); // Draw the string on that plane. gm("fdi"); // Finish drawing to (im2) } } ==============================================================================================


=============================================================================================== 3-D DRAWING using the WPF Level I ========================= (Requires .NET v3.5 and PC# v3.0) The New WPF 3D Module: ===================== What makes the new 3D module of the WPF interesting are the following new features: (1) It allows viewing the 3D object from any direction and at any distance. What you see on the screen is actually the picture which a camera picks up. the type, position and direction of that camera are settable. (2) The color and shades which you see depend on the lights around the object. You can choose from a variety of light types placed at whatever positions you like. (3) The color which the object appears at depends also on the type of material it's made of, how reflective it is to light in general and to each light color in particular. Data required by the WPF 3D module: ================================== The WPF 3D module can help you viewing a 3D object under variety of conditions, transforming the object and animating it if you like, but it does not help you in creating the object. It requires that you supply it with data regarding an object which you know to start with. This data includes the 3D location of (at least) each vertex of the object and a mesh pattern data. 1 0 +----------------+ | /| | / | | / | | / | | / | | / | | / | | / | | / | | / | | / | | / | | / | | / | | / | |/ | +----------------+ 2 3 If the object was a rectangle like this one, the data required would be the 3D coordinates of the 4 points (0,1,2,3) and an index array whose contents can be (0,1,2,0,2,3) what these 6 numbers mean is that the object is made of 2 triangles, if we list the points which make each triangle moving counter-clockwise, the two lists would be (0,1,2) and (0,2,3) This seems to be easy when it is in 2D space. If the rectangle, has been in 3D space making different angles with each of the three 3D planes, things could have been harder. If it was a more complicated shape, the difficulty could have been higher. We have developed a software which allows drawing rectangular blocks, cyliders and pyramids with general bases. They can be drawn using class (pcs)'s graphics module and can also be drawn using the WPF's one. Additionally they can automatically supply the WPF 3D module with their points and triangle indices data (the mesh pattern) Personal C Sharp simplification: ================================ The most difficult part is the mesh pattern data described above which is now "no work" if you limit your 3D objects to Rectangular blocks, cylinders and pyramids which we know that you can't do. Fortunately, this difficulty is limited to Level I and will be eliminated when you reach Level II. The data is supplied to PC# software in the form of 4 arrays as follows: PXD[] An array of type "double" which contains the X component of each point's coordinates. PYD[] An array of type "double" which contains the Y component of each point's coordinates. PZD[] An array of type "double" which contains the Z component of each point's coordinates. IX[] An array of type "int" which contains the indices of the points of all triangles which make the shape in the order described above. If you like to use one of our 3D objects, for example a pyramid with hexagonal base sitting on a plane which is parallel to the YZ plane with enclosing circle diameter of 100 and a height of 150, you would use the code: id=6;od=100;dd=150;js="yz";gm("cPv"); This will create the required 4 arrays for you. The last char in the mode string 'v' means create vertex arrays only (Don't draw or fill the generated object) If you are wondering how large these arrays are, in the next example we'll create the vertex arrays of a hexagonal cylinder, circular cylinder and a pyramid with a 10 sides base. The total of all numbers to be supplied exceeds 1,000. The PC# defaults: ----------------- The basic 3 setups which you need to do after supplying the "mesh" data are "lights setup", "object's color and material setup" and "camera setup". We are not going to get into details regarding them now. However the default settings which PC# will set for you if you don't supply data for the 3 items are: (1) A combination of 3 lights will be available. One directional light at the top of the object, one directional light coming from the upper, left corner (at z=maximum) direction toward the center and one ambient light. (2) A perspective camera sitting at the upper, right (at z=maximum) corner and directed toward the center where the object is expected to be. The camera has a field of view of 60 degrees. (3) The object is assumed to be made of light diffusing material and colored with our default brush which is black for the foreground and white for the background. How to draw 3D objects: ----------------------- (1) Call method gm("cRv"), gm("cCv") or gm("cPv") to create a rectangular block, a cylinder or a pyramid in order to generate the point arrays PXD[], PYD[], PZD[] and IX[]. If you like to draw a different object, you need to assign data to these arrays by yourself. (2) If you accept all defaults and like the object to be centered into the Graphical Output device, all you need to do is to call vm("cof") to create the object and fill it. If you want to create the object only, call vm("co") Then you can render it later and fill it with vm("grf") or transform it and fill it with vm("gtf") If you don't like the object's center to be at the origin, assign values to (jd, kd and ld) If you have more than one object to draw, repeat the two steps for each and assign different values to (jd,kd,ld) in (2) in order to seperate them. This is just the beginning. There are plenty of items to discuss, but let us have an example before we get further. ============================================================================================== Example 6: Draw a pyramid with 10 sides base which is parallel to the xz plane, a cylinder with circular base which is parallel to the yz plane and a cylinder with hexagonal base which is parallel to the xy plane. Use different color for each. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=6;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr4 is not divided. cs="im1";cis="\n3D Graphics";os="s";ds="c";lf=560;of=250;fns="trb16";cm("i"); } public override void run() { cs="im1";gm("sdi"); // Set (im1) as graphical output device. id=10;od=150;dd=150;js="zx";gm("cPv"); // Create the pyramid with 10 sides cls="S9g4";js="d";vm("cof"); // Use green color for background and // create-fill diffuse material object id=40;od=100;dd=100;js="zy";gm("cCv"); // Repeat with the round (40 sides) base jd=160;kd=30;ld=-50;cls="S9b4";js="d";vm("cof"); // cylinder. use blue color. id=6;od=170;dd=170;js="yx";gm("cCv"); // Repeat with the hex cylinder using jd=-300;kd=-125;ld=50;cls="s9r4";js="d";vm("cof");// red color for background. gm("fdi"); // Finish Image control drawing operation. } } ==============================================================================================


=============================================================================================== If you don't like the screen shot of last example, don't get disappointed. The poor graphics are caused by sending the images using "jpg" compressed formula through the internet. If you run the code on your computer all colors and shades will look fine. The edge lines of the hexagonal cylinder and pyramid will look more distinctive and the circular cylinder will look smoother. How could edges appear for the hex cylinder and pyramid but not for the circular cylinder? ------------------------------------------------------------------------------------------ We have designed the default light positions and directions so that object edges become distinctive. Despite that the circular cylinder which is actually made of a 40 sides polygon base shows with no recognizable edges. What did we do to make this possible? Each vertex of a cylinder is shared among 3 planes, the base plane and two side planes. If we want the edges to show, we need to list each vertex location in arrays PXD[],PYD[],PZD[] 3 times so that the points which make the triangles of each plane are defined seperately in array IX[]. If we like the side planes to mix making a continuous round surface, we list each vertex twice only. Once for the base and once for the other two planes together. Whenever you create a cylinder or a pyramid with a base which is made of 25 sides or more, we assume that you want a circular object, so we list each vertex twice. Otherwise, we list each vertex 3 times. Why do we need two combined color codes for 3D objects? ------------------------------------------------------- Whenever we use two combined color codes for an object we usually mean that the first one is for the foreground and the second one is for the background. This applies to 3D objects too. However, when we set the color of a 3D object's surface, we are expected to specify the color of the back of that surface too. This color is normally never seen except if the object is only a surface with no volume like a triangle or a rectangle or if the camera is inside the object! We use the foreground color specified in the combined color code to paint the background of that back surface with. We said that if the camera was inside an object which has volume like a cylinder or a cube, it will see everything painted in the color which is set as the color of the back side. How is that possible? You can believe it if you assume that a 3D object is considered to be a hollow shell which is painted inside and outside with different colors. Can we reverse one of the triangles which define an object's surface so it appears in the back surface color? Yes. All we need to do, is to reverse the indices sequence for that triangle in array IX[] so its point listings become in clockwise order instead of counter-clockwise. About the next example: ----------------------- Can we have an example to illustrate that? Yes, in the next example we'll move the camera using a slider control so we can see a triangle and a rectangle at both their front and back sides. We are also going to make the camera penetrate inside a cylinder to look inside it. Additionally we'll modify array IX[] for the rectangle so that the points of one of its composing triangles become listed in a clockwise order causing that triangle to appear in reverse side color. We are going to be using the green color for the outside of all objects and the red color for the back (or the inside) of all objects. The triangle and rectangle are actually a pyramid and a cylinder of a single line base. The line will be vertical alongside the Y axis. We'll then move the triangle along the X-axis to point (-150) and also move the rectangle along the X-axis to point (+150) ============================================================================================== Example 7: Create a triangle at the left, a rectangle at the right and a cylinder at the center paint them all green with reverse side in red. Move the camera using a slider control on a line which is on the xz plane parallel to the x-axis at z=190. The camera will be moving between the two extreme points where (x=-250) and (x=250) Set the z position of the cylinder so that its outer-most point extends in the z direction to the point z=200. This means that when the camera is at its center point it will be inside the cylinder. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=7;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr3 is divided into 2 rows. cs="gr4";k=0;oyd=0.8;ds="n";cls="S9y7";fns="tr12";cm("i"); cs="gr5";j=0;k=1;oyd=0.2;ds="c";cls="S9y7";fns="crb10";cm("i"); //----------------------------------- gr4 Contents ----------------------------------- cns="gr4"; // gr4 is not divided. cs="im1";os="s";ds="c";lf=560;of=250;fns="trb16";cm("i"); //----------------------------------- gr5 Contents ----------------------------------- cns="gr5"; // gr5 is not divided. cs="sl0";cis=" Camera X Position\n-250 -200 -150 -100 "; cis+="-50 0 +50 +100 +150 +200 +250";os="n"; js="-5,5";cus="0";lf=500;ds="c";cls="S9b2";fns="crb12";cm("i"); } public override void update() { if ("sl0".Equals(cs)) { // If Slider "sl0" has generated this event jd=od*50;kd=0;ld=190;vm("scp"); // Apply received value to camera's X postion. } } public override void run() { cs="im1";gm("sdi"); // Set (im1) as the graphical output device. cls="b0";id=2;gm("sps"); id=558;od=235;gm("crd"); id=1;od=150;dd=100;js="xy";gm("cPv"); // Get the vertex data for the triangle jd=-150;cls="r0g0";js="d";vm("cof"); // Draw the triangle at point (-150,0,0) id=40;od=100;dd=200;js="xy";gm("cCv"); // Get the vertex data for the circular cylinder ld=100;cls="r0g0";js="d";vm("cof"); // Draw it at point (0,0,100) id=1;od=150;dd=100;js="yx";gm("cCv"); // Get the vertex data for the rectangle // The 2nd triangle indices occupy the 3 rows (IX[3]:IX[5]) Exchanging values of first // and second rows changes point order from counter-clockwise to clockwise. n=IX[3];IX[3]=IX[4];IX[4]=n; //Exchange contents of IX[3] and IX[4] jd=150;cls="r0g0";js="d";vm("cof"); // Draw rectangle at point (150,0,0) //---- Camera setup ---- os="0,0,190"; // Camera position = Point (0,0,190) js="0,0,-1"; // Look direction = from +ve (Z) to -ve (Z) ks="0,1,0"; // Up Direction = +ve (Y) id=120; // Field of view angle=120 degrees. vm("ccp"); // Create Perspective camera. gm("fdi"); // Finish Image Control drawing operation. } } ---------------------------------------------------------------------------------------------- Notice the following when you run this program: (1) When the camera is at the center, it's inside the cylinder so the entire view becomes red and the rear end of the cylinder becomes visible as a far away circle. (2) When you move the camera to the negative side and start passing the point (-50), you suddenly see the cylinder in green and also the triangle appears in green. The camera is now outside the cylinder. (3) As you continue moving the camera in the negative direction, the green triangle shrinks horizontally, turning into a vertical line and at point (-150) it totally disappears. (4) When you pass (-150) the triangle reappears in red and it keeps growing in size until the camera reachs the end position in the negative direction. (5) If you return to the center then move to point (+50), the cylinder will become green and the rectangle shows up as two triangles, one green and one red. (6) The rectangle keeps shrinking in size to a vertical line and disappear at point (+150) (7) Whe you pass this point, the rectangle starts showing up, but the colors of its 2 triangles become reversed. (8) As you move further in the poitive direction, the rectangle grows in size until the other camera extreme point is reached. ==============================================================================================


=============================================================================================== Material Types: =============== In all the past examples we have been selecting "Diffuse" material for all 3D objects. This has been done by making the assignment (js="d") when creating the objects. Actually, this assignment has not been necessary since it's the default. In general, there are 3 types of material which we can select: d Diffuse Material. This is the default and most common. It means a material which diffuses light like fabrics, wood, regular wall paints,..etc. e Emissive Material. Make this selection when you like the object to shine. s Specular Material. You need to practice with this selection to see if it can be of use to you. It seems that you can't use selection (e) or selection (s) alone. You must use them as top layers with a layer of type (d) underneeth. If you don't do that, material of type (e) appears white regardless to the color which you specify and material of type (s) appears transparent! In the next example, we are going to be creating 3D objects and drawing on them. This makes it perfect for practicing with material type selections. Color Types: ============ We have mentioned before that we supply a dual color code when we create a 3D object and that the first code is for the back surface which is usually invisible except if the object to be created was a surface of no volume. Now, we need to get deeper into this subject. First of all, if the object was transparent or partially transparent, the back surface color will be effective since it will be seen through the transparent front surface color. So, in this case the back surface color must also be transparent. Additionally, all this discussion was about the default color brush type which is the solid one. A brush can be "Solid", "Linear Gradient", "Radial Gradient", "Texture" or "Visual". We select the brush type by assigning "s","l","r","t" or "v" to (ks) when we call method gm("co") or gm("cof") to create the object. No assignment means that we have selected (ks="s") When we select linear or radial brush type, the dual color code represents the start and end colors of the gradient paint which both front and back surfaces will be painted with. Selection "t" means that we like to paint object's surface with an image and selection "v" means that we like to use the visual brush which we have prepared in advance with pictures, drawings and/or videos. We'll be discussing the visual brush shortly. Generalizing the graphical output device: ========================================= The image control has been successfully generalized so that it can now do the following: (1) Draw all 2D Graphics including text. (2) Draw Images. (3) Play movie. (4) Display all the WPF 3D graphics. You can also mix more than one of the drawing tasks above into one image control. You make an image control the graphical output device by assigning its keyname to (cs) and calling gm("sdi") You can then draw any combination of the 4 drawing types listed above, then call gm("fdi") to finish the Image control drawing operation. The 2D drawings will be done first followed with the 3D drawings unless you make the assignment (jb=true) before calling gm("fdi") See remark (2) for more details. The Visual Brush: ================= The second Graphical Output Device after the Image Control is the Visual brush. You set it by calling gm("sdb") then you can draw either geometric shapes and images or 3D objects. When done, you call gm("fdb") to finish the brush drawing operation. After that you can apply the brush to either a 2D or a 3D object's surface to paint it with the drawings. You may repeat the operation more than once to paint the same surface with variety of drawings. REMARKS: ------- (1) Any object which you draw on the visual brush must have a set size which is smaller than or equal to the size of the surface which will receive the drawings. (2) There are two categories of graphics, "2D graphics, images and video" make one category and 3D graphics make another category. The objects used for each of the two categories are not the same. This should not be of concern to you unless drawings from the 2 different categories are going to be covering each other. In this case, you need to specify which one should be on the top. The default is the 3D drawings. If you like a drawing of the first category to be on the top while the image control is the graphical output device, make the assignment (jb=true) before calling gm("fdi) With the Visual Brush, you cannot draw graphics of both categories at the same time. However, you can start a visual brush drawing operation, draw graphics of one category on the brush, finish the operation and paint the target object with the brush, then you can repeat the whole operation to paint the same target again with drawings of different category. Drawing on a 3D object's surface: ================================= In the "Drawing" chapter, you have seen examples on how to draw on 3D planes. Can we do the same using the WPF 3D module. Yes we can. The process is not exactly the same, but the results are the same. In the past examples, you have been drawing each 3D object in two steps: (1) Obtaining the mesh data for an object by using method gm("cRv"), gm("cCv") or gm("cPv) to get the data for a Recatangular block, Cylinder or a Pyramid (respectively) (2) Calling method vm("cof") supplying it with the color assigned to (cls) and the material type code assigned to (js) to create the 3D object. The typical line of code for step (2) has been: cls="r0g0";js="d";vm("cof"); // Draw front surface with green solid brush and back // surface with solid red brush. Use diffuse material. The brush type was not specified since we wanted solid brush which is the default. If we want to specify a different brush, we should assign to (ks) one of the codes "s", "l", "r", "t" or "v" meaning "solid", "linear gradient", "radial gradient", "texture" or "visual" brush. Now, instead of painting the entire object with one solid color we like to paint each surface independantly. The visual brush (described above) is used for the drawing. This means that we need to make the following changes: (1) Instead of drawing the entire object at once we need to draw each surface independantly and assemble them together to make the 3D object. This is not new. We are going to use the same technique which we have used to draw 3D objects using class (pcs) You need to review examples (20,21) of the "Drawing" chapter to know how. To draw a Rectangular block, we are not going to call gm("cRv") any more. We are going to be calling gm("crv") while specifying the container plane and depth assigned to (cns) This will be repeated 3 times, once for each visible side of the rectangular block. (2) We are going to be using the visual brush. So, we are going to set it temporarely as the Graphical Output device, draw on it then assign it to the object by making the assignment (ks="v") when we create the 3D object. (3) The WPF 3D module requires two more arrays of type double to contain the "Texture Coordinates" which are necessary when any brush other than the solid color one is used. This is no work to you. The 2 arrays (TXD[], TYD[]) will be generated automatically with all other mesh data arrays when you call gm("crv") =============================================================================================== Example 8: Draw a 140 X 150 X 100 rectangular block by assembling its three visible sides. Draw the text "PC#" on one side, a Hexagon on another side and an image on the third side. In order to demonstrate our ability to mix different drawings into one image control, start by drawing a frame in 2D space to set the rectangular block into. =============================================================================================== //assembly ms1.exe; public class b : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=8;base.setup(); // Execute master class's setup() supplying it with page no. //------------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="im1";cis="\n3D Graphics";os="s";ds="c";lf=560;of=250;fns="trb16";cm("i"); } public override void run() { cs="im1";gm("sdi"); // Set (im1) as the graphical output device. //----------------------------- Drawing a frame in 2D space ----------------------------- cls="g0";id=3;gm("sps"); // Create a 3 pixels wide pen of solid green color. id=247;od=247;gm("crd"); // Create a new Rectangle and draw it at center. //-------------------------------- Drawing the front side ------------------------------- cns="xy50"; // Container: Parallel to xy plane at z=50 (1/2 of depth) id=140;od=150;gm("crv"); // Get vertex data of front rect. js="d";cls="s9r4";ks="s";vm("cof"); // Create-fill the 3D object using solid red brush gm("sdb"); // Set the present visual brush as the gr output device cls="y0";id=1;gm("sps"); // Create solid yellow color brush fns="trb40";os="PC#";gm("ctf"); // Draw the text "PC#" on the Visual brush gm("fdb"); // Finish v brush drawing operation. ks="v";vm("cof"); // Draw object again painting it with visual br //--------------------------------- Drawing the top side -------------------------------- cns="xz75"; // Container: Parallel to xz plane at y=75 id=100;od=140;gm("crv"); // Get vertex data of top rect. js="d";cls="s9b4";vm("cof"); // Create-fill the 3D object using solid blue brush gm("sdb"); // Set the present visual brush as the gr output device cls="s9";id=1;gm("sps"); // Create solid white color brush. id=6;od=80;gm("c=f"); // Draw-fill a hexagon on the visual brush. gm("fdb"); // Finish v brush drawing operation. ks="v";vm("cof"); // Draw object again painting it with visual br //--------------------------------- Drawing the right side ------------------------------ cns="yz70"; // Container: Parallel to yz plane at x=70 id=100;od=150;gm("crv"); // Get vertex data of right side rect. js="d";cls="s9g4";vm("cof"); // Create-fill the 3D object using solid green brush gm("sdb"); // Set the present visual brush as the gr output device fls="images\\flower.jpg"; // Name of Image file to be drawn id=100;od=150;gm("cid"); // Draw image on v brush after scaling it to fit rect gm("fdb"); // Finish v brush drawing operation. ks="v";vm("cof"); // Draw object again painting it with visual br gm("fdi"); // Finish drawing to (im1) } } ---------------------------------------------------------------------------------------------- COMMENTS: --------- (1) When we have been assembling a rectangular block using class (pcs), the depth of the block side which was parallel to plane (yz) has been negative, but here it's postive. This is because with class (pcs), we have been drawing the left side, but here we are drawing the right side of the rectangular block. (2) In this example We have drawn the visible sides of the 3D object only. In general, the right thing to do is to draw all 6 sides. If we have been doing transformation or animation, the object's back side could have appeared. (3) When we talk about the front and back sides of the object, we must keep in mind that the object center coincides with the graphical output device's center. All vertex data are based on that. This means that if the side you have requested to draw was parallel to the xy plane with a negative depth (z<0), the side you get will be a back side, painted with back side color and anything you draw on it will not be visible unless you move the camera to view the back side. ==============================================================================================


=============================================================================================== Drawing a large number of 3D objects into one page: =================================================== There are more than one way to do so. You may create a large number of grids, one for each item and create image controls into each. Each image control can display one item. You can also use the method which we have used in example 6 in which we have created only one image control and set the 3D coordinates of each item using (jd,kd,ld) to seperate them. The first method requires plenty of code and the second one is hard to do. Using the visual brush as a graphical output device can be the best choice. It can allow us to position the items easily in 2D space and the entire job can be done in a loop which requires minimum code. ============================================================================================== Example 9: Draw 9 cylinders with variety of sides into one page. The sides should be in the range (3:11) Draw a frame around each item and display a description label within the frame. Also display a title for the page. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=9;PageHeight=630;base.setup(); // Execute master class's setup() supplying it // with page no. and height. //------------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="im1";cis="3D Graphics";os="s";ds="c";lf=560;of=620;fns="trb16";cm("i"); } public override void run() { cs="im1";gm("sdi"); // Set (im1) as the graphical output device. cls="y7";gm("sps"); // Create a new pen/brush of background color. kd=305;id=557;od=50;gm("crf"); // Create a new Rectangle to house title. cls="r0";id=3;gm("sps"); // Create a new pen/brush of solid red color. kd=300;os="CYLINDERS";fns="trb20";gm("ctf");// Display title for (int n=0;n<9;n++) { // Repeat the following 9 times: cls="S9";id=2;gm("sps");fns="trb12"; // Set color & font for frame and item lables. jd=185*(n%3)-185;kd=-185*(int)(n/3)+105; // Set position of each item's label. os="Number of sides="+(n+3);gm("ctf"); // Display label. jd=185*(n%3)-185;kd=-185*(int)(n/3)+185; // Set position of each square's center point id=od=180;gm("crd"); // Draw square. gm("sdb"); // Set visual brush as graphical output device. id=n+3;od=100;dd=100;js="yz";gm("cCv"); // Get vertex data for each cylinder. cls="r0b4";js="d";vm("cof"); // Create 3D object and display it in blue color. gm("fdb"); // Finish drawing to brush gm("grf"); // Paint the square with the visual brush. } gm("fdi"); // Finish drawing to (im1) } } ---------------------------------------------------------------------------------------------- Notice the scrollbars which automatically show up when we set the page height or page width to values which are beyond what the window can display without the bars. ==============================================================================================


=============================================================================================== Operating on PC# internal 3D objects and arrays: ================================================ Some of the objects and arrays which are used by PC# to access the WPF classes are declared public in order to allow you to operate on them whenever you find it necessary. Here is a list of them: igp = Image control which is currently the Graphical output device. vbp = Visual Brush which is currently the Graphical output device. gmp = Last GeometryModel3D object created. gmo = Last 2D Geometry object created. pcp = Present PerspectiveCamera ocp = Present OrthographicCamera mcp = Present MatrixCamera alp = Present AmbientLight dlp = Present DirectionalLight plp = Present PointLight Mesh data: ---------- PXD[],PYD[],PZD[] Array of type "double" which contain the X,y & z coordinates of each point of the 3D object. NXD[],NYD[],NZD[] Arrays of type "double" which contains the Vector3D components of the Normal property of each point. IX[] An array of type "int" which contains the indices of the points of all triangles which make the shape in an order which we have discussed before. TXD[],TYD[] Arrays of type "double" which contains Texture X & Y Coordinates. REMARKS: -------- (1) Whenever you supply the vertex data for a 3D object, you may like to include point normals with your data. The Normal of a point on a 3D object is the vector which is perpendicular to the surface at that point and points to outside the object. Before you supply this data, you should create the type double arrays NXD[], NYD[] and NZD[] which are defined by class pcs3 as arrays of the same lengths as arrays PXD[], PYD[] and PZD[]. Then you should load them with the (x,y & z) components of each vector which represents the normal at each point in the point arrays at the same order. PC# does not use the Normal arrays when it supplies data for a Rectangular Block, a Cylinder or a Pyramid. It uses a different method which has been explained before. (2) When you supply your own vertex data for an object, remember not to make the mistake of redeclaring the vertex arrays. We mean by that: DO : IX=new int[50]; DO NOT: int[] IX=new int[50]; since obviously array IX[] of the second line is not the one which class (pcs3) can use. (3) You can get all the references listed above for public objects by calling vm("O") with no parameters, except the Light objects which require supplying the method with their keynames. Comparing class (pcs) with class (pcs3): ======================================== Working with the WPF graphics is made to be very similar to working with class pcs's graphics. With class pcs, all shapes are represented by one object of type GraphicsPath which is (gpp) Similarly, with class pcs3, all 2D shapes are represented by one object of type Geometry which is (gmo) and all 3D shapes are represented by one object of type GeometryModel3D which is (gmp) With class pcs, you can create a shape by calling method gm("cx"), gm("cxd") or gm("cxf) where x is a character which represents the type of shape wanted. It can be (r,e,=,p,i,t,R,C,P) meaning (Rect, ellipse, equally sided object, general path, image, text, Rectangular Block, Cylinder or Pyramid) respectively. If the mode string ends with "d", the object's outlines are drawn to the graphical output device using the present Pen object. If the mode string ends with "f", the object is filled with the present brush. If the third character was omitted, the object will be created only without display. Class pcs3 functions identically regarding 2D graphics. With 3D graphics it can either create objects only with vm("co") or create and fill them with vm("cof") It cannot draw outlines. With class pcs, you can work on the shape object after being created and (gpp) become its reference. Calling gm("grd") or gm("grf") draws or fills the object. Methods gm("gt"), gm("gtd") and gm("gtf") apply present unit transform (utp) to the object then it can either draw its outlines or fill it depending on the 3rd character of the mode string. With class pcs3, you can also work on the shape object after being created and (gmo) or (gmp) becomes a reference of its underlaying Geometry. The same modes of method gm() are available when working with 2D objects. With 3D objects, methods vm("grf"), vm("gt") and vm("gtf") are available. 3D TRANSFORMS: ============== The transformation technique which we have used with 2D graphics has made transformation very easy and more importantly error free. So, we are going to do the same with 3D transformation. To remind you with it, you need to plan your transformation job as follows (in the same order): (1) If you need to scale the object, assign the (x,y and z) scale factors to (js) seperated with commas. Remember that the object will be scaled up or down while its center remains at same position. (2) If you need to rotate the object, assign the rotation axis' 3D vector components to (ks) seperated with commas and the rotation angle in degrees to (ad) Remember that rotation will be done around object's own center. (3) If you like to move object's center in all 3 directions by the amounts (x1,y1,z1), assign these 3 values to (os) seperated with commas. (4) Call vm("st") to set the transform supplying it additionally with current coordinates of object's center assigned to (jd,kd,ld) (5) After that, you can transform (gmp) at any time by calling vm("gt") or you can transform it and draw it at the same time by gm("gtf") All this should be preceded with vm("co") in order to create (gmp) for your 3D object. As you must have noticed, we have not included "shearing" in the process. We may do that at a later time. ============================================================================================== Example 10: Create a circular cylinder and draw it. Scale the cylinder to half in all three directions and draw it again. Rotate it by 90 degrees around Z-axis and draw it for the third time. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=10;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr3 is not divided. cs="im1";cis="\n3D TRANSFORMS";os="s";ds="c";lf=560;of=250;fns="trb16";cm("i"); } public override void run() { cs="im1";gm("sdi"); // Set (im1) as graphical output device. id=40;od=100;dd=100;js="zy";gm("cCv"); // Obtain vertex data for a cylinder. //---------------------------------- Original Object ------------------------------------ cls="S9";gm("sps"); // Create black pen. jd=-95;kd=-165;id=od=180; // Position for first text line os="Original";gm("ctf"); // Draw the text. jd=-185;id=od=180;gm("crd"); // Draw the containing square. gm("sdb"); // Set visual brush as graphical output device. cls="S9b5";js="d";vm("cof"); // Create and draw-fill original object in blue. gm("fdb"); // Finish with brush as graphical out device. gm("grf"); // Paint first square with visual brush. //--------------------------------- Scaled down Object ---------------------------------- cls="S9";gm("sps"); // Recreate black pen. jd=90;kd=-165;id=od=180; // Position for second text line os="Scaled to half";gm("ctf"); // Draw text. id=od=180;gm("crd"); // Draw containing square. gm("sdb"); // Set visual brush as graphical output device. cls="S9g5";js="d";vm("co"); // Recreate original object in green w/o drawing. js="0.5,0.5,0.5";vm("st"); // Set transform to scale object by 0.5 vm("gtf"); // Transform and draw-fill object gm("fdb"); // Finish with brush as graphical out device. gm("grf"); // Paint second square with visual brush //----------------------------------- Rotated Object ------------------------------------ cls="S9";gm("sps"); // Recreate black pen. jd=280;kd=-165;id=od=180; // Position for third text line os="Rotated 90 degrees";gm("ctf"); // Draw text. jd=185;id=od=180;gm("crd"); // Draw containing square. gm("sdb"); // Set visual brush as graphical output device. cls="S9r5";js="d";vm("co"); // Recreate original object in red w/o drawing. ks="0,0,1";ad=90;;vm("st"); // Set transform to rotate obj 90 degrees around vm("gtf"); // Z-axis. Transform and draw-fill object. gm("fdb"); // Finish with brush as graphical out device. gm("grf"); // Paint third square with visual brush. gm("fdi"); // Finish Image control drawing operation. } } ---------------------------------------------------------------------------------------------- COMPILING: Use tool "pcp" or "pcpr" as usual. Make sure to keep the commented statement "//assembly ms1.exe;" at the top and also to keep the commented word "pcs3" at the class declaration line since both are required by the compiling tools. For more details read the text titled "Compiling" which follows example 1. COMMENTS: --------- Since you already know about objects involved, we can explain this example more technically. (1) We called method gm("cCv") to create the necessary arrays once only at the top. This is because these arrays do not get erased until you request the vertex data of a new object by calling gm("cxv") where x can be "R", "C", "P", "r" or "=". In this example, we needed the vertex data for one object only. (2) Why have we been drawing the label which goes with each square before drawing the square itself? When the label text was drawn the present 2D object (gmo) has been created as the text's geometry object. When the square was drawn, (gmo) has been overwritten to become the square's geometry object. Immediately after (gmo) became the square's object, we set the visual brush as the graphical output device, drew on it, then painted (gmo) with it. This is why the last (gmo) which was available immediately before using the brush must be for the square. (3) The same idea applies to 3D. Each time we call vm("co") to create a 3D object, (gmp) is recreated to point to that object. So, when we set the transform then call vm("gtf"), we know that we are applying the transform to last (gmp) which means to the last created 3D object. (4) Here is one question which you may like to ask "Both (igp) and (vbp) accept 2D and 3D drawings. If we have drawn 2D figures on the visual brush the 2D Geometry object (gmo) could have changed. So how could we guarantee that the geometry object which has been painted with the visual brush at the end belongs to the square? The answer is that when you call gm("sdb") to make (vbp) the graphical output device, the first thing method gm() does is creating a second reference to both (gmo) and (gmp) to persist the two objects, and when you call gm ("fdb") to finish the brush drawing operation, the original values of (gmo) and (gmp) are restored. ==============================================================================================


=============================================================================================== Camera and Lights ================= The first time we introduced a short course in mathematics was in the chapter of "Imaging" when we discussed Matrices. The WPF contains classes which include methods that perform Vectors and Quaternions mathematical operations in addition to 3D Matrices. However, writing WPF application software requires only a small knowledge of them. This is what we are going to start with here. We may get into more details later if we find that necessary. VECTORS: ======== A vector is an amount which has a magnitude and direction but it does not have a specific location. This means that all vectors in a 3D space which have same magnitude and parallel to each others are the same. And since they are the same, we can simplify defining each group of equal vectors by using the one of the group which starts at the origin and ends at a point in the 3D space. A vector is usually represented with an arrow of the same length as the vector's magnitude and pointing to the same direction as the vector's direction. If the tail of that arrow starts at the origin and the tip ends at point (x,y,z), we call that vector(x,y,z) Normalized Vectors (or unit vectors): ------------------------------------- Vectors which are used in the WPF 3D module represent direction only. They are called normalized vectors or unit vectors. You can normalize a vector by dividing it by its magnitude. The magnitude of vector (x,y,z) is (x^2 + y^2 + z^2)^0.5 [We mean by this the square root of (x squared + y squared + z squared)] You don't have to normalize vectors before supplying them to the WPF since the WPF will do this job for you if you don't. The X, Y and Z axes's normalized vectors are (1,0,0), (0,1,0) and (0,0,1) respectively. COMPLEX NUMBERS: ================ The complex number (n), in general can be represented by two components, real component and imaginary one as follows: n = a + bi where i=(-1)^0.5 Mathematicians have found that representing a point in 2D space with a complex number could simplify some calculations especially "rotation". This, of course is in addition to many other applications in physics and other fields of sience which have been simplified with the use of complex numbers. In 2D space, we use the real part of the number (a) to represent the (x) value of the point and the imaginary part (b) to represent the (y) value. QUATERNIONS: ============ If the real and imaginary parts of a complex number can represent a point in 2D space, how can we represent a point in 3D space? the answer is by using 4 components. One real and 3 imaginary components. The Quaternion can be expressed as follows: q = w + xi + yj + zk Quaternions are used in the WPF for rotation transformation. We see no need for more about them at this point. CAMERA TYPES: ============= There are three types of cameras: Perspective, Orthographic and Matrix. (1) The Perspective Camera: --------------------------- The Perspective camera is the standard camera which everybody uses. It makes the nearby sides of the object appear to be larger than the far sides which is what everybody expects. To create a perspective camera, call vm("ccp") with the following parameters: os : Camera position. Assign the (x,y,z) coordinates of the camera position to (os) seperated with commas. When the camera is far away from the object, you expect the object to appear smaller. js : Look Direction. Assign the x,y,z components of the "Look" direction vector to (js) seperated with commas. Normally, you set the object at the center. So, if you position the camera at point (x,y,z) you like to make your look direction vector (-x,-y,-z) ks : Up Direction. Assign the x,y,z components of the camera's upward direction vector to (ks) seperated with commas. Normally we like that direction to be the positive Y-axis. So we make the assignment: ks="0,1,0". id : Field of View. This is the angle of the view cone of the camera. Whenever it's narrow, only a small area of the scene appears; whenever it's wide more area appears and objects look smaller. (2) The Orthographic Camera: ---------------------------- When we like to draw more precisive 3D drawings, like the ones used in Engineering, we normally don't like far sides of the object to appear smaller than the near ones since everything must be done with measurments. In order to generate this kind of drawings, the Orthographic camera is the one to select. It does not have a view cone. It actually has a view cylinder which must be as wide as the object in order to view it at 1:1 scale. To create an Orthographic camera call vm("cco") with the following parameters: os : Camera Position. See above. js : Look Direction. See above. ks : Up Direction. See above. id : Width. (3) The Matrix Camera: ---------------------- Normally, the Perspective and Orthographic cameras are all the Camera types which you may need. However, the WPF allows you to set the properties of a camera by defining its view and projection matrices. PC# allows you to use this type of camera to view the 3D scene which it generates for you, but it does not create the matrix camera itself. So, if you like to use a Matrix camera, do the following: (1) Create the view and projection transforms. (2) Create a matrix camera using the two transforms and make (mcp) its reference. (3) call vm("ccm") with no parameters. LIGHTS: ======= There are three major types of light: (1) Ambient Light: ------------------ This is a diffused light which is available everywhere but does not have a specific direction. It increases brightness of a 3D object while it cannot produce shadows or make object edges distinctive. For this reason, we normally don't use this light type alone. We mix it with other light types. It can be used as the brightness control of a 3D scene. (2) Directional Light: ---------------------- This is a light which is made of parallel rays with a specific intensity and direction. It can generate shadows and can cause sharp edges of the 3D object to appear. We may use more than one of this light type in order to illuminate the object from different directions. (3) Point Light: ---------------- This is "Light bulb" light. You may use any number of them in a 3D scene. The light rays of this type go evenly in all directions around the point where the light source is. LIGHT PARAMETERS: ----------------- "Color" is a common parameter which you must specify for all types of light. The WPF 3D module applies some laws of physics regarding light reflection. An object looks dark unless it can reflect light and the color it appears at is the color of the light which it reflects. If we assume that the object's color was red and the light was green, the object will appear black no matter how strong the light is since the object cannot reflect green light. The only property which you supply when creating ambient light is the color code (cls) Point light requires additionally, the 3 coordinates of its location point assigned to (jd,kd,ld) Directional light requires (cls) and the 3 components of the vector specifying light direction assigned to (jd,kd,ld) also. HOW ARE LIGHTS CREATED IN PC#: ------------------------------ As you know PC# keeps only one object of each type at any moment. This made it possible to refer to each object by one unique name. For example (btp) is always the reference name of the last button object which you have called a PC# method to operate on. There are two advantages in that: (1) Memory Management: It regulates the size of the resources used by your program in order to guard against a crash. (2) Simplicity and error control: You can guarantee the name of the object which you like to operate on. This guards against errors and makes your job simpler, easier and more pleasant. If you have studied the "Pc# reference-Desktop" you already know how to use the "Present object" and what to do if you must keep more than one object at a time. With some objects like "Controls", keeping more than one object during the execution of your class is more than just a rare necessity. This is because you may like to create 3 buttons in one class and the user may click any of them at any time during execution, so the 3 objects must be always available. We use keynames to identify each object of this kind and PC# keeps all the object locations in its archives. Whenever you like to do an operation on button "bt0" for example, you supply method cm() with the keyname "bt0" assigned to (cs) The first thing the method does is looking through the archives to find the object of this button and making (btp) its reference. After the execution of this operation and before calling the method to operate on a new button, you can guarantee that (btp) is a reference to your button's object. If you like (btp) to be the reference of any button but you have no need to perform an operation, call cm("O") supplying it with the button's keyname. Let us now return to the main subject. You have a need for only one Camera in a 3D scene, so PC# has assigned 3 names for the 3 Camera objects which are (pcp), (ocp) and (mcp) When we come to "Lights", you may like to have more than one directional light or point light. So, we need to use the same technique which we use with controls. To create a light or perform any operation on a light, you need to supply its keyname assigned to (cs) The first 2 chars for the keyname of each light type are as follows: al: for Ambient light. dl: for Directional light. pl: for Point light. So, "al0","dl5" and "pl7" are valid keynames for lights. The 3 present objects for the 3 light types are (alp), (dlp) and (plp) We can install upto 10 different lights in a 3D scene. EXAMPLES: --------- cs="al0";cls="s0";vm("cla"); // Create ambient light, gray in color. cs="pl5";jd=kd=ld=100;vm("slp"); // Set the position of a point light at (100,100,100) cs="dl5";jd=kd=ld=-100;vm("sld");// Set the direction of a directional light at (-100,-100,-100) About the next example: ======================= The next example is very useful since it teaches you all about camera types, locations and directions. It also teaches you light types, how to create them and how to modify them after being created. It shows you how to move the Camera in a circle above the object while constantly pointing at the object in order to view it from all directions. This example is also an application on using check box and radio button groups. They work identically as they do in class (pcs) so, if you need more explanation regarding them, review example 3 of the chapter "Using Controls". ============================================================================================== Example 11: Create an eight sides cylinder. View it with a camera located at max (x,y,z) corner. Illuminate it with 4 lights. One directional light pointing to it from front top left corner, one directional light pointing to it from rear top right corner, one point light at top-center and an ambient light. Show the effect of switching between perspective and orthographic camera types. Also show the effect of eliminating one or more of created lights. Show how to dim and brighten the scene by adjusting the intensity of the ambient light and how to see object from all directions by rotating the camera over 360 degrees using slider controls. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 public override void init() { base.init(); } public override void setup() { PageNumber=11;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr3 is divided into 3 col's. cs="gr4";j=0;oxd=0.30;ds="w";cls="S9y7";fns="tr12";cm("i"); cs="gr5";j=1;k=0;oxd=0.40;ds="c";cls="S9y7";fns="crb10";cm("i"); cs="gr6";j=2;k=0;oxd=0.30;ds="e";cls="S9y7";fns="crb10";cm("i"); //----------------------------------- gr4 Contents ----------------------------------- cns="gr4"; // gr4 is divided into 6 rows. cs="lb0";cis="LIGHT SELECTION";k=0;ds="c";oyd=0.1;cls="r0y7";fns="trb12";cm("i"); cs="cb00";cis="Front Directional Light";k=1;ds="w";cus="1"; oyd=0.1;cls="S9s9";fns="trb12";cm("i"); cs="cb01";cis="Rear Directional Light";k=2;ds="w";cus="1"; oyd=0.1;cls="S9s9";fns="trb12";cm("i"); cs="cb02";cis="Top Point Light";k=3;ds="w";cus="1";oyd=0.1;cls="S9s9";fns="trb12";cm("i"); cs="bt0";cis="Select Lights";k=4;ds="c";oyd=0.2;cls="r0g7";fns="trb12";cm("i"); cs="sl0";cis="Ambient Light Intensity\n 0 5 10";os="n";k=5;oyd=0.4; js="0,10";cus="5";lf=160;ds="s";cls="S9b2";fns="crb12";cm("i"); //----------------------------------- gr5 Contents ----------------------------------- cns="gr5"; // gr5 is not divided. cs="im1";cis="\nCamera and Light Effects";os="s";ds="c";ims="";lf=of=220;fns="trb16";cm("i"); //----------------------------------- gr6 Contents ----------------------------------- cns="gr6"; // gr6 is divided into 6 rows. cs="lb1";cis="CAMERA SELECTION";k=0;ds="c";oyd=0.1;cls="roy7";fns="trb12";cm("i"); cs="rb00";cis="Perspective Camera";k=1;ds="w";cus="1";oyd=0.1;cls="S9s9";fns="trb12";cm("i"); cs="rb01";cis="Orthographic Camera";k=2;ds="w";oyd=0.1;cls="S9s9";fns="trb12";cm("i"); cs="bt1";cis="Select Camera";k=4;ds="c";oyd=0.2;cls="r0g7";fns="trb12";cm("i"); cs="sl1";cis=" Camera Position\n-180 0 +180";os="n";k=5;oyd=0.4; js="0,10";cus="5";lf=160;ds="s";cls="S9b2";fns="crb12";cm("i"); } public override void update() { //------------------------------- Ambient Light adjustment ------------------------------ if ("sl0".Equals(cs)) { // If Ambient Light control slider activated: if (od<5) { // If pointer was at the darker half of the control: cls="S"+(9-(int)(od*9/5)); // Set the corresponding color code. } else { // Else, if pointer was at lighter half of the control: od-=5; // Adjust reading to fit the color code cls="s"+((int)(od*9/5)); // Set the corresponding color code. } cs="al0";vm("slc"); // Set ambient light color accordingly. } //------------------------------------ Camera Rotation ---------------------------------- else if ("sl1".Equals(cs)) { // If Camera rotation slider activated: // The object is considered to be at the center of a (220 X 220 X 220) room. The Camera is // at its ceiling which is parallel to the xz plane at a height of (y=110), It rotates // around ceiling center at a radius of 110. Its initial position is at point (110,0) // making a startup angle of zero with the Z-axis projection on the ceiling plane. // The slider's range was set as (0:10) so we should multiply its readings by (36) to // adjust them to (-180:180) range. The Camera's z and x coordinates at any angle should // be [radius*cos(angle)] and [radius*sin(angle)] respectively. In order to make camera // always point at center, its "Look direction" Vector3D components are obtained by // negating its position coordinates. double angle=od*36; // Obtain rotation angle which corresponds to (od) od=angle;js="cos";um("mt");zd=-110*od; od=angle;js="sin";um("mt");xd=-110*od; jd=xd;kd=110;ld=zd;vm("scp"); // Apply received values to camera's postion. jd=-xd;kd=-110;ld=-zd;vm("scl"); // and to its look direction. } //------------------------------------ Lights Selection --------------------------------- else if ("bt0".Equals(cs)) { // If "Lights Selection" button clicked: cs="cb0*";cm("gu"); // Get update values of CheckBox group into CUS[] for (n=0;n<3;n++) { // Scan CUS[] rows (which correspond to CheckBox states) if(CUS[n]=="1") cls="s0"; // If this row contains "1". set color at initial color. else cls="S9"; // Else, make it black (meaning eliminate this light) if (n==0) cs="dl0"; // First row sets our front directional light. else if (n==1) cs="dl1"; // Second row sets our back directional light. else cs="pl0"; // Third row sets top point light. vm("slc"); // Set lights as this row requires. } // Repeat for all rows of array CUS[] } //------------------------------------ Camera Selection --------------------------------- else if ("bt1".Equals(cs)) { // If "Camera Selection" button clicked: cs="rb0*";cm("gu"); // Get index of selected radio button in (cui) os="0,110,110"; // Set values which are common for the 2 camera types js="0,-110,-110";ks="0,1,0"; // which are: position, look direction and up direction. if (cui==0) { // If Perspective camera was selected: id=90; // Set its "Field of view angle" at 90 degrees. vm("ccp"); // and recreate a Perspective camera. } else { // Else, if Orthographic camera was selected: id=220; // Set its "Width" property at (220) which is enough to vm("cco"); // view full object and recreate an orthographic camera. } } } public override void run() { cs="im1";gm("sdi"); // Set (im1) as graphical output device. cls="g0";id=3;gm("sps"); // Create a new pen/brush of solid green color. id=217;od=217;gm("crd"); // Create a new Rectangle and draw it at center. id=8;od=150;dd=-150;js="yz";gm("cCv"); // Get vertex data for Cylinder with 8 sides base. cls="S9r5";js="d";vm("cof"); // Create and draw object in red using diffuse material. //---- Startup Camera ---- os="0,110,110"; // Camera position = Point (0,110,110) js="0,-110,-110"; // Look direction = from camera position to center. ks="0,1,0"; // Up Direction = +ve (Y) id=90; // Field of view angle=90 degrees. vm("ccp"); // Create Perspective camera. //---- Startup Lights ---- // "dl0" illuminates object's front side. "dl1" illuminates its back side. "pl0" illuminates // its top and "al0" acts as a brightness control. cs="dl0";cls="s0";jd=110;kd=ld=-110;vm("cld");// dir light from top-left-out corner to center cs="dl1";cls="s0";jd=-110;kd=ld=110;vm("cld");// dir light from top-right-in corner to center cs="pl0";cls="s0";jd=ld=0;kd=110;vm("clp"); // point light at top center. cs="al0";cls="s0";vm("cla"); // ambient light. gm("fdi"); // Finish drawing to (im1) } } ---------------------------------------------------------------------------------------------- As you must have expected moving the slider (sl1) from end to end rotates object by 360 degrees. This is because rotating the camera around object means that the object's image in the camera is rotating by the same angle in the opposite direction. COMMENTS: ========= (1) When we created slider (sl1) we requested a range of (0:10), then we multiplied each reading which we received from the slider in method update() by 36 since actual range is (-180:180) Why did'nt we set range at (-180:180) or (0:360) to start with? If we did we could have ended with a slider which contains 360 tick marks instead of 10. A slider with 10 tick marks looks better. (2) In real life, objects can be bright enough to cause blindness while on the screen or on paper they are as bright as they can be when they appear in white color. Since we must show a clear difference between bright and dark spots in a 3D scene, we make gray the average shade. Most lights are created with color code "s0" which is for gray color. ==============================================================================================


=============================================================================================== ANIMATION ========= If you have studied the web examples of WPDIII and WPDIV, you know how to use JavaScript and class (pasp) to do several types of animations. The WPF includes an entire namespace for the purpose of animation. We like to simplify and implement the most usable ones and most importantly, make it easy for you to include multiple animations into one page. Our animated objects must be able to run together into one graphical output device or several graphical output devices in the same page. Both 2D and 3D objects must be possible to animate. What can we animate? -------------------- We can apply the following animations to a 2D object: (1) We can move the 2D object horizontally and vertically according to a preset formula. (2) We can scale the 2D object horizontally and vertically also according to a preset formula. (3) And We can rotate the 2D object according to a preset formula. Similarly, we can apply the following animations to a 3D object: (1) We can move the 3D along the x,y and z axes according to a preset formula. (2) We can scale the 3D object on the x,y and z directions also according to a preset formula. (3) And We can rotate the 3D object according to a preset formula. What are the preset formulae? ----------------------------- Whenever we call method vm() to animate a specific property of an object, we supply a time schedule which describes what the value of that property should be at each selected moment. For example, if we like to apply horizontal movement animation to an object, we call vm("apx") which is for animating the x-position of the object supplying it with a time schedule like this: O[0]=0;OS[0]="0"; // At start (meaning after zero seconds), Keep object at center (ie x=0) O[1]=2;OS[1]="100"; // After 2 seconds, object should be at position (x=100) O[2]=4;OS[2]="-100"; // After 4 seconds, object should be at position (x=-100) This formula is made of 3 steps. We are telling The WPF where the object should be at start, after 2 seconds and after 4 seconds. the object is not going to jump from (position 0) to (position 100) or from (position 100) to (position -100), it will move smoothly between the three points. The movement-time relation is linear. Arrays O[] and OS[] are used to specify the seconds and the property values. The reason we use OS[] for this job is that different properties may require values of different types. For example, rotation of a 3D object with changing both axis and angle, requires 4 numbers, the first three specify the axis vector and the fourth one specifies the angle. We supply the 4 numbers seperated with commas into a row of array OS[], like: OS[4]="1,0,0,45". What to do after the time schedule has been executed? ----------------------------------------------------- The default is to repeat the process in reverse direction, then repeat the entire process indefinitely. However you can overwrite the defaults with: jb=true: Means Do Not "Auto Reverse". j=repititions: If (j) was not assigned zero, Animation will perform a limited number of repititions then stops. The number of repititions is the value assigned to (j) Setting the animation time unit: -------------------------------- We have told you that the amounts you assign to O[] are times in seconds. This is true although there is more to it. The default time unit is a second, but you can change that if you want. If you set it at (0.5 seconds), O[0]=3 will mean O[0]=1.5 seconds in this case. To change the animation time unit to any value, assign that value to (id) and call vm("atu") When you set the begin time and the duration of two animation steps, you are usually interested in how the number of the two steps compare rather than what the numbers are. You may like the two steps to be equal in duration for example or one of them to be a multiple of the other. This is why it's easier and more convenient to set the time in units instead of setting it discretely. The default time unit is one second, but you may set it at any value which may contain fractions. How to write the Animation program: =================================== [A] Identify the object with a keyname: --------------------------------------- Since we may need to animate more than one 2D or 3D object in one page, we need to keep the objects which we intend to animate referenced all the time. So whatever applies to "Lights" applies to them. We need to use keynames to identify those objects. The first two char's of a 2D object's keyname is "g2" and the first two char's of 3D object's keyname is "g3". So, to create a square which you intend to animate later, you may use this code: cs="g20";id=od=100;gm("crd");// Create rect and draw it. Identify its Geometry with "g20" and to create a circular 3D cylinder which you intend to animate, you may use this code: id=40;od=90;dd=60;js="zy";gm("cCv"); // Obtain vertex data for a cylinder. cs="g30";cls="s9s9";js="d";vm("cof");// Create & fill it. Identify it with the keyname "g30" [B] Animate object's properties: -------------------------------- You may then make any number of calls to method vm() at the following modes: "apx": Animate x-position using supplied formula. "apy": Animate y-position using supplied formula. "apz": (For 3D objects only) Animate z-position using supplied formula. "asx": Animate x-Scale factor using supplied formula. "asy": Animate y-Scale factor using supplied formula. "asz": (For 3D objects only) Animate z-Scale factor using supplied formula. "ara": Animate rotation angle only using supplied formula. "arx": Animate rotation axis and angle only using supplied formula. You should precede your call with the following assignments: (1) The same keyname which you used when object was created assigned to (cs) (2) The preset formula as described before. Notice that rotation of a 2D object requires assigning angles only to OS[], while rotation of a 3D object may require assigning angles alone or vectors of rotation axes and the angles. Rows of array OS[] are assigned one number for all animation options except when you rotate both axis and angle of a 3D object. You assign OS[]'s row 4 numbers seperated with commas in this case. When you animate the angle alone of a 3D object, you need to assign the rotation axis vector components to (ks) while assigning angles only to each row of OS[]. (3) If you like to prevent "auto-reverse", supply (jb=true) (4) If you don't like the animation to repeat forever, assign the number of wanted repetitions to (j) [C] Set animation trigger: -------------------------- If you like the animation to start as soon as the page is loaded, call vm("at") with no parameters. If you like it to start when button "bt0" is clicked, receive the event in method update() and start the animation there. How to make object's color change during animation? =================================================== You don't need to animate the color. You can set lights of different colors around the object so whenever the object moves, its color changes because of light reflections. We'll demonstrate this in the next example. How to animate properties of an object by yourself? =================================================== The WPF animation namespace allows you to animate some properties which we have no animation methods for. If you must animate any of these properties you need to do the animation job by yourself. If you like to animate such properties of an object which PC# has created for you, you need to know the following: (1) (gmo) is the reference of the (Geometry) object of the last 2D object which you have operated on using a PC# method. (2) (gmp) is the reference of the (GeometryModel3D) object of the last 3D object which you have operated on using a PC# method. (3) (srp) is the reference of the (Storyboard) object declared in class (pcs3) All 3 reference names are declared public so you can operate on them using standard C#. Let us assume that you like to make a 2D object which PC# has created for you swing between the 2 X-positions (x=5,x=-5) using standard C#, here is what to do: (1) Create a translate transform, assign a name to it and register it with: this.RegisterName(name,ref); where ref= Translate transform object reference and "name" is the name you assigned to it. (2) Create an animation object of the type you like (most likely will be DoubleAnimation in this case) and set it to change values according to your desired formula. (3) Set the "AutoReverse" and "RepeatBehavior" of the animation object. (4) Set the TargetName of the animation to the same name which you used in (1) as follows: Storyboard.SetTargetName(a,name); Where (a) is the Animation object ref. (5) Set the TargetProperty of the animation to the XProperty of the TranslateTransform: Storyboard.SetTargetProperty(a,new PropertyPath(TranslateTransform.XProperty)); (6) Add your animation to PC#'s Storyboard object with: srp.Children.Add(a); (7) Apply the transform to the Geometry object with: gmo.Transform = ref;// ref is a reference to your TranslateTransform (8) Call vm("at") to start the animation process. ============================================================================================== Example 12: Apply position, scale and rotation animation to a 3D object. Install directional lights of different colors around it and display it into an image control cell. Create two additional 2D objects. Apply scale animation to one and a combination of scaling and rotation to the other. Make the two share a second image control cell. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=12;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr3 is divided into 2 col's. cs="gr4";cis="g1";j=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";cm("i"); cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";cm("i"); //----------------------------------- gr4 Contents ----------------------------------- cns="gr4"; // gr4 is not divided. cs="im1";cis="\nSingle 3D Animation";os="s";ds="c";ims="";lf=250;of=250;fns="trb16";cm("i"); //----------------------------------- gr5 Contents ----------------------------------- cns="gr5"; // gr5 is not divided. cs="im2";cis="\nMultiple 2D Animations";os="s";ds="c";ims="";lf=250;of=250;fns="trb16";cm("i"); } public override void run() { //--------------------------------- The 3D Object ----------------------------------- cs="im1";gm("sdi"); // Set (im1) as graphical output device. //---- Creating Lights ---- // Create directional lights of different colors at the 4 top corners in addition to a top // light and an ambient one. cs="dl0";cls="y0";jd=kd=ld=110;vm("cld"); cs="dl1";cls="g0";jd=kd=110;ld=-110;vm("cld"); cs="dl2";cls="b0";kd=110;jd=ld=-110;vm("cld"); cs="dl3";cls="c0";kd=ld=110;jd=-110;vm("cld"); cs="pl0";cls="r0";jd=ld=0;kd=100;vm("clp"); cs="al0";cls="s0";vm("cla"); //---- Creating the object ---- id=40;od=100;dd=100;js="zy";gm("cCv"); // Obtain vertex data for a cylinder. cs="g30";cls="s9s9";js="d";vm("cof"); // Create and draw-fill original object in white. //---- Setting the animation formula ---- O[0]=0;OS[0]="0"; O[1]=3;OS[1]="-50"; O[2]=6;OS[2]="150"; cs="g30";vm("apx"); O[0]=0;OS[0]="0"; O[1]=3;OS[1]="30"; O[2]=6;OS[2]="-180"; cs="g30";vm("apy"); O[0]=0;OS[0]="1"; O[1]=3;OS[1]="1.5"; O[2]=6;OS[2]="0.5"; cs="g30";vm("asx"); O[0]=0;OS[0]="0,1,0,90";O[1]=3;OS[1]="0,1,0,-90";O[2]=6;OS[2]="0,0,1,-90";cs="g30";vm("arx"); cs="";vm("at"); // Make "PageLoaded" event triggers animation (def) gm("fdi"); // Finish drawing to (im1) //--------------------------------- The 2D Objects ---------------------------------- cs="im2";gm("sdi"); // Set (im2) as graphical output device. //---- First 2D Object ---- cls="r0";gm("sps"); // Prepare solid red brush cs="g20";id=od=60;gm("crf"); // Create & fill a square. Identify it with "g20". // Apply scale and rotation animation O[0]=0;OS[0]="1"; O[1]=3;OS[1]="1.5";O[2]=6;OS[2]="0.5"; cs="g20";vm("asx"); O[0]=0;OS[0]="30";O[1]=3;OS[1]="-30";O[2]=6;OS[2]="-30"; cs="g20";vm("ara"); cs="";vm("at"); // Make "PageLoaded" event triggers animation (def) //---- Second 2D Object ---- cls="b0";id=3;gm("sps"); // Prepare solid blue brush cs="g21";id=od=100;gm("ced"); // Create & draw a circle. Identify it with "g21". // Apply scale animation at both x,y directions. O[0]=0;OS[0]="1"; O[1]=3;OS[1]="1.5";O[2]=6;OS[2]="0.5"; cs="g21";vm("asx"); O[0]=0;OS[0]="1"; O[1]=3;OS[1]="0.5";O[2]=6;OS[2]="1.5"; cs="g21";vm("asy"); cs="";vm("at"); // Make "PageLoaded" event triggers animation (def) gm("fdi"); // Finish drawing to (im2) } } ---------------------------------------------------------------------------------------------- COMPILING: Use tool "pcp" or "pcpr" as usual. Make sure to keep the commented statement "//assembly ms1.exe;" at the top and also to keep the commented word "pcs3" at the class declaration line since both are required by the compiling tools. For more details read the text titled "Compiling" which follows example 1. ==============================================================================================


=============================================================================================== More Graphical Output Devices: ============================== Mixing 2D drawings with 3D drawings is very important since you normally like to insert your 3D drawings into 2D frames and to write some text around them. This is why we have decided to use the Image Control and the Visual Brush as Graphical Output Devices. The two devices have done their jobs fine so far, but recently, when we started to write software which involves mouse clicks and slow motion of 3D objects, we have encountered some problems. The team who have developed the WPF software have probably not expected us to use Image Controls or Visual Brushes for this purpose. So, we need to add two more Graphical Output Devices: (1) The Graphics Path object which is called Path in the WPF, despite that we are still calling its object (gpp) This device can handle 2D drawings only. (2) The Viewport3D object which is a control made specifically for 3D drawings. As usual, we are not limited to one of each. They are assigned keynames just like the Image control. The keynames for (gpp) can be "gp0",...., "gp89" and the keynames for (vpp) can be "vp0",...., "vp89". We can use any number of each into one page. The Image Control and the brush are still the best. The new controls which are restricted to one type of drawings are good only when we like the drawings to be clickable and / or movable. Which objects can be assigned keynames? ======================================= With class (pcs), only Controls and files are assigned keynames. With class (pcs3), we have explained that we need to use this technique with most Graphical Output Devices and Lights since we may need more of one kind of them and must keep them persisted since they are used more than one time during execution. We have also mentioned that you must use keynames to identify 2D and 3D objects which you need to animate. The WPF uses the same technique internally. They also require names to be registered for objects which are used for the same purposes. Therefore, we have expanded the use of keynames as follows: (1) Whenever you create one of the following objects, you may supply a keyname. In this case the keyname will be associated with the object in PC# archives and also will be registered into WPF as a name for the object. This allows you to call a PC# method at any time to perform setups on the object and to perform a job which requires WPF registeration like animation. Notice that the PC# setup methods can be called with or without an object keyname. If no keyname is assigned, operation will be done on the last accessed object of its type (the present object) If a keyname is supplied, the object which is associated with the keyname will be retrieved from archives and made to be the present object before the operation is performed. The objects are: gmp = Present GeometryModel3D object. gmo = Present 2D Geometry object. mvp = Present ModelVisual3D object (Will be discussed later) pcp = Present PerspectiveCamera ocp = Present OrthographicCamera mcp = Present MatrixCamera alp = Present AmbientLight dlp = Present DirectionalLight plp = Present PointLight and the first two char's of the objects' keynames should be: al: alp dl: dlp pl: plp pc: pcp oc: ocp mc: mcp g2: gmo g3: gmp mv:mvp (2) If you have created an object without supplying a keyname, and later you wanted to give it a keyname and to register it also with the WPF, assign the keyname to (cs) and call vm("r") Make sure that the Present object name refers to your object before the call. If another object has been registered under the same keyname, it will be replaced with the new object. Making 3D objects clickable: ============================ Whenever you set an Image Control or a Viewport3D Control as the Graphical Output Device, the control automatically becomes a recepient of the "MouseDown" event. This means that whenever the user clicks at any point within the Graphical output device, the (x,y) coordinates of the point are sent to method update assigned to (oxd,oyd) respectively. You receive the "MouseDown" event into method update with the condition [if (cs=="md0")] just as you do with class (pcs) The first thing to do after receiving the mouse coordinates is to call method vm("gh") which will give you (gmp) of the 3D object which has been hit and will give you also its keyname assigned to (os) If the right mouse button was the one clicked, you'll receive (ob=true) in addition to (oxd,oyd) Slow motion of 3D objects: ========================== We already know that whenever the user clicks on a 3D drawing we can obtain the (gmp) of the clicked object. What is a good application to use that for? Imagin if you draw a door or a window and whenever the user clicks on it, it opens and whenever the user clicks it with the right button it closes. We believe this can make a good application, especially if the opening and closing are done in a slow motion. The 3D object which you like to move must have a registered keyname. This means that you should have supplied a keyname when you created it using vm("co") or vm("cof") The method which handles slow motion is method vm("gm") It can apply translate, scale and or rotate transforms to the object in a slow motion all in one step, just like method vm("st") It expects the following parameters: js=Scale factors seperated with commas. ks=Rotation axis vector components seperated with commas. os=x, y and z displacement components seperated with commas. ad=Rotation angle in degrees. cs=3D object Keyname. jd,kd,ld=Present center coordinates. i=Begin time (in animation time units) Default:zero meaning "start immediately". o=Duration (in animation time units) Default:1. The animation time unit is 1 second by default. You can change the animation time unit value by assigning the new value to (id) and calling vm("atu") In the next example we are going to set the animation time unit at 2 seconds. The operation is repeatable. This means that each time the object is clicked it keeps moving, rotating or being scaled in the same direction. At any time the object can be returned back to original state by calling vm("gmb") You may like this to happen when the user right-clicks the object. ============================================================================================== Example 13: Make the 3 objects of example 6 clickable. Use Viewport3D graphical output device this time and perform the following each time one object is clicked: (1) First object changes color each time it's clicked and its original red color can be restored by right-clicking it. (2) Second object slowly rotates counter-clockwise by 90 degrees each time it's clicked and its original position can be restored with a right click. (3) Third object is scaled down repeatedly to half its size in a slow motion each time it's clicked. Its original size can be restored with a right click. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" string colors="g5 b5 y5 m5 c5 o5 p5 s0 "; // Color codes to be assigned to 1st object int color=0; // Index of a color code in the string (colors) public override void init() { id=2;vm("atu"); // Set animation time unit at 2 seconds. base.init(); } public override void setup() { PageNumber=13;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";cis="Click to Change Color Click to Rotate Click to Scale";os="s"; ds="nw";lf=560;of=250;fns="crb14";cm("i"); // Vewport3D to be used as Graphical out device } public override void update() { if (cs=="md0") { // If mouse is clicked: bool o1b=ob; // Save received mouse button indicator temp. vm("gh"); // Get (gmp) hit using received mouse coord's cs=os; // Assign returned 3D object keyname to (cs) //--------------------------------- First Object ------------------------------------- if (cs=="g30") { // If Object hit was first one: if (!o1b) { // If clicked mouse button was left one: cls="S9"+colors.Substring(color*3,2); // Assign the color code at index to (cls) js="d";vm("sm"); // Set material (including color) of object. color++;if (color>7) color=0; // increment color code index. Cycle back } // when end is reached. else { // Else if right mouse button was clicked: cls="S9r5";js="d";vm("sm"); // Return object color to red. } } //--------------------------------- Second Object ------------------------------------ else if (cs=="g31") { // If Object hit was second one: if (!o1b) { // If clicked mouse button was left one: ks="0,1,0";ad=90;js=os="";vm("gm"); // Rotate object 90 degrees around Y-axis. } else vm("gmb"); // Else if right mouse button was clicked: } // Return object back to original position. //--------------------------------- Third Object ------------------------------------- else if (cs=="g32") { // If Object hit was third one: if (!o1b) { // If clicked mouse button was left one: jd=160;kd=30;ld=-50; // Supply present position coordinates and js="0.5,0.5,0.5";ks=os="";vm("gm"); // scale factors. Execute slow object scaling. } else vm("gmb"); // Else if right mouse button was clicked: } // Return object back to original size. os=cs="";oxd=oyd=0; // Reset received values } } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. //--------------------------------- Create the 3 Objects ------------------------------- id=6;od=170;dd=170;js="yx";gm("cCv"); // Get vertex coord's for a hex cylinder with // enclosing circle diam of 170, height of 170 cs="g30";jd=-300;kd=-125;ld=50; // Create cylinder, centered at (-300,-125,50) cls="s9r4";js="d";vm("cof"); // using red diffuse material. Fill cylinder. id=10;od=150;dd=150;js="zx";gm("cPv"); // Get vertex data for a pyramid with 10 sides cs="g31";cls="S9g4";js="d";vm("cof"); // Create it and fill it. Use green D material id=40;od=100;dd=100;js="zy";gm("cCv"); // Get vertex data of cylinder with round base cs="g32";jd=160;kd=30;ld=-50; // Create cylinder, centered at (160,30,-50) cls="S9b4";js="d";vm("cof"); // using blue diffuse material. Fill cylinder. gm("fdv"); // Finish Viewport3D drawing operation. } } -------------------------------------------------------------------------------------------- Notice that we have supplied the object original coordinates (assigned to jd,kd,ld) when we wanted to scale the third object. This is how we could guarantee that the object will be scaled without changing its center location. We have not supplied the original coordinates of the second object since it was centered at the origin which is the default. In general, rotating a 3D object requires 3 conponents: (1) Rotation center coordinates. (2) Rotation axis vector. (3) Rotation angle. COMPILING: Use tool "pcp" or "pcpr" as usual. Make sure to keep the commented statement "//assembly ms1.exe;" at the top and also to keep the commented word "pcs3" at the class declaration line since both are required by the compiling tools. For more details read the text titled "Compiling" which follows example 1. ==============================================================================================


=============================================================================================== COMPLEX SEQUENCED MOVMENTS: (Version 3.01 and later) =========================== Slow motion does not have to be a single one. In fact, you can create a very interesting applications when you apply multiple, sequenced slow motions to a group of related 3D objects which make a 3D assembly. Let us start with an example. Let us assume that you wanted to send two items as a gift to somebody. So you purchased a box kit which is actually a single piece of cardboard, like the one sketched below. To make the box, you bent the two sides arround the dotted lines by 90 degrees. You followd that by bending the front and rear sides around the dotted lines by 90 degrees also. Then you dropped the two items into the box and finally closed the box by bending the top side by 90 degrees. +---------+ | | | Top | | | | | +.........+ | | | Rear | | | | | +---------+.........+---------+ . | . . | . / \ | Left . Bottom . Right | / \ / \ | Side . . Side | / \ ----- | . . | ----- Gift 1 +---------+.........+---------+ Gift 2 | | | Front | | | | | +---------+ Now, we like to draw the box kit and show the entire process of making the box, inserting the gifts into it then closing it in a slow motion. Putting into consideration that each slow motion takes one time unit (default value of time unit is 1 second) to complete, the timing is going to be as follows: (1) AT START: Both left and right sides will make 90 degrees rotations at the same time. (2) AFTER 1 TIME UNIT: Both front and rear sides will make 90 degrees rotations at the same time. (3) AFTER 2 TIME UNITS: Both Gift 1 and Gift 2 will move up into the air to a height which exceeds the box's height. (4) AFTER 3 TIME UNITS: Both Gift items will move in a plane which is parallel to the XZ plane toward the box and will stop when they are above its opening. (5) AFTER 4 TIME UNITS: Both Gift items will move down inside the box and stop when they reach its bottom. (6) AFTER 5 TIME UNITS: The box's top will make 90 degrees rotation to close the box. Those have been the required actions, now let us see how to perform them: Drawing the Box Kit: -------------------- The easiest way is to draw 6 identical squares, all at the location where the box's bottom should be, then move each side to where it should be using transformation. This is done within method run() The bottom is the only side which comes at its correct place without transformation. Drawing the Gift Items: ----------------------- The two items share the same vertex data but each has a unique color and sits at a unique location relative to the box. Triggering the movement operation: ---------------------------------- We like the entire operation to start when we click on any of the 3D objects. After the operation has been completed and the box is closed, we don't like additional clicks to mess things up. To accomplish this, we use the boolean variable (started) The initial value of this variable is (false) This value satisfies the condition set to start the operation. After the operation has started, the value of (started) is changed to (true), so additional clicks become ineffective. Setting the Begin time (i) and Duration (o) of each step: --------------------------------------------------------- As explained before, we set the two timing parameters in animation time units and we set the animation time unit itself (default=1 second) using vm("atu") If we do not specify the begin time (i), the motion step will start immediately and if we do not specify the duration, the step will take one animation time unit to complete. In a sequenced movements operation, we must set the begin time of each step since each step should start after the step before has been completed. Time tracking: -------------- The required timing shown above, indicates that there are always two objects which are required to move at the same time during all steps except step (6) when the box is closed. We can handle this by using two time tracking variables of type "int", each starts at zero and each handles half of the sequenced operations as follows: (1) First time tracking variable starts when left side moves and counts one time unit when it completes its 90 degrees turn. Then it tracks the front object movement and counts 2 time units when it's completed. Next to that, it tracks the left gift item's 3 movements and reachs 5 time units when the object is inside the box. (2) Second time tracking variable acts similarly by tracking the right side, then the rear (and top) sides and finally the right gift item and its value reachs 5 time units also when the right gift item is inside the box. (3) At the end, either tracking variable can be used to track the final operation which is closing the box. ============================================================================================== Example 14: Draw the box kit which has been explained above, Make the box, then insert two gift items into it and close it in a slow motion. Make the operation start when the user clicks on any drawn 3D object and make sure that additional clicks will do nothing. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" bool started=false; // A flag which insures that operations runs // One time only. public override void init() { id=2;vm("atu"); // Set animation time unit at 2 seconds. base.init(); } public override void setup() { PageNumber=14;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";cis="Click on any object to start the operation";os="s"; ds="nw";lf=560;of=250;fns="crb14";cm("i"); // Vewport3D to be used as Graphical out device } public override void update() { if (cs=="md0" && !started) { // If mouse is clicked for the first time: started=true; // Set flag so more clicks will do nothing. int t1=0,t2=0; // Create & Initialize 2 time tracking var's. //--------------------------------- Making the box ----------------------------------- // There are 2 sets of sequenced operations which will run in synch with each other. // Each operation completes in 1 Time Unit. So time tracking var's are incremented after // each operation and (i) of following operation is assigned last adjusted time tracking // so it begins when original operation is complete. // Rear side (cs="g34") and Top (cs="g35") must turn at same time as one piece, this is why // time has not been incremented after "g34" rotation operation was completed. i=t1;cs="g31";jd=-50;kd=-50;ks="0,0,-1";ad=90;vm("gm");t1++; // Left Side i=t2;cs="g32";jd=50;kd=-50;ks="0,0,1";ad=90;vm("gm");t2++; // Right Side i=t1;cs="g33";ld=50;kd=-50;ks="-1,0,0";ad=90;vm("gm");t1++; // Front Side i=t2;cs="g34";ld=-50;kd=-50;ks="1,0,0";ad=90;vm("gm"); // Rear Side i=t2;cs="g35";ld=-50;kd=-50;ks="1,0,0";ad=90;vm("gm");t2++; // Top. Runs together with g34 //-------------------------- Inserting Gift Items into Box --------------------------- i=t1;cs="g36";os="0,125,0";vm("gm");t1++; // Move Left item up i=t1;cs="g36";os="140,0,-140";vm("gm");t1++;// Move it sideway to become above box opening i=t1;cs="g36";os="0,-125,0";vm("gm");t1++; // Move it down to sit inside box. i=t2;cs="g37";os="0,125,0";vm("gm");t2++; // Move Right item up i=t2;cs="g37";os="-135,0,135";vm("gm");t2++;// Move it sideway to become above box opening i=t2;cs="g37";os="0,-125,0";vm("gm");t2++; // Move it down to sit inside box. //--------------------------------- Closing the Box ---------------------------------- i=t2;cs="g35";ld=-50;kd=50;ks="1,0,0";ad=90;vm("gm");t2++; os=cs="";oxd=oyd=0; // Reset received values } } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. //--------------------------------- Drawing the Box Kit ------------------------------- // We are going to create the 3D objects of all 6 sides of the box at same location which is // where the box's bottom should be (in a plane which is parallel to XZ, at y=-50), then we // are going to transform each side to where it should be in the kit. cns="xz-50"; // Draw into a parallel plane to xz at y=-50 id=od=100;gm("crv"); // Get vertex data of a square drawn there. for (int n=0;n<6;n++) { // Create 6 identical 3D objects using same cs="g3"+n;cls="c0p0";js="d";vm("co"); // vertex data. Use keynames: g30:g35. } // Transform each side to where it should be and draw it. cs="g30";vm("grf"); // Bottom: Keep at same place. os="-100,0,0";vm("st");cs="g31";vm("gtf"); // Left side: move center by (-100,0,0) os="100,0,0";vm("st");cs="g32";vm("gtf"); // Right side: move center by (100,0,0) os="0,0,100";vm("st");cs="g33";vm("gtf"); // Front side: move center by (0,0,100) os="0,0,-100";vm("st");cs="g34";vm("gtf"); // Rear side: move center by (0,0,-100) os="0,0,-200";vm("st");cs="g35";vm("gtf"); // Top: move center by (0,0,-200) //---------------------------- Drawing the two Gift Items ----------------------------- // Both objects use same vertex data, but positioned differently. The left side item with // "g36" keyname is positioned at (-150,-35,150) The right side item with "g37" keyname is // positioned at (150,-35,-150) id=40;od=30;dd=30;js="xz";gm("cPv"); // Get vertex data for a circular pyramid cs="g36";jd=-150;kd=-35;ld=150;cls="b5b5";vm("cof"); cs="g37";jd=150;kd=-35;ld=-150;cls="r5r5";vm("cof"); gm("fdv"); // Finish Viewport3D drawing operation. } } ---------------------------------------------------------------------------------------------- COMPILING: Use tool "pcp" or "pcpr" as usual. Make sure to keep the commented statement "//assembly ms1.exe;" at the top and also to keep the commented word "pcs3" at the class declaration line since both are required by the compiling tools. For more details read the text titled "Compiling" which follows example 1. COMMENTS: ========= [A] What could have happened if we have eliminated method update() and added all the code of method update() to the code in method run()? The answer: The animation could have started as soon as the page was loaded and everything else could have been the same. [B] There are more than one chance to draw a 3D object, for example: (1) You may draw it immediately after creating it by calling vm("cof") (2) You may create it then draw it later by calling vm("grf") (3) You may draw it while applying a transform to it by calling vm("gtf") (4) You may draw it while moving it by calling vm("gmf") In the previous example, we have used vm("grf") to draw the box bottom, vm("gtf") to draw all other box sides and vm("cof") to draw the gift items. We could have drawn any of them using any of the 4 means while achieving the same results. [C] You may draw the object first, then apply necessary operations to it (like transformation, animation or slow motion) or you may perform the operations before you draw it. [D] When you draw a 3D object, you don't draw it to the Graphical Output Device directly. You draw it to an object of type "ModelVisual3D" and when you call method gm("fdv") or gm("fdi") to finish the drawing, the ModelVisual3D object is drawn to the Graphical Output device. [E] Setting rotation axis: ---------------------- Look at the code section "Making the box". Some rotations have been done around +ve axes and others have been done around -ve ones. How can we easily determine which axis and which axis direction to assign to each rotation? The easiest way is to use your right hand again. This time, seperate the thumb from the other 4 fingers. While keeping the other 4 fingers attached together, fold them loosely to make an arch. Position your arm so that the arch points to the direction of the rotation which you like to perform. Your thumb will be pointing to the direction of the axis which the rotation should be performed around. ==============================================================================================


=============================================================================================== Here is a nice exercise which you can do. EXERCISE: --------- Make the user able to click on the box after being closed in order to reverse the process. This means that the box will open up, both gift items will return to their initial positions and all box sides will move back to their original positions. The user must then be able to restart the process by clicking at any object as usual. Try to modify last example's code in order to get this done then compare your program with this: //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" bool started=false,finished=false; // Flags which set which of the two processes // to run. //************************************** NO CHANGE **************************************** public override void init() { id=2;vm("atu"); base.init(); } public override void setup() { PageNumber=14;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";cis="Click on any object to start the operation";os="s"; ds="nw";lf=560;of=250;fns="crb14";cm("i"); // Vewport3D to be used as Graphical out device } public override void update() { //------------------------------ (1) BOX ASSEMBLY PROCESS ------------------------------- if (cs=="md0" && !started) { // If mouse is clicked for the first time: started=true; // Set flag so more clicks will do nothing. int t1=0,t2=0; // Create & Initialize 2 time tracking var's. //--------------------------------- Making the box ----------------------------------- // There are 2 sets of sequenced operations which will run in synch with each other. // Each operation completes in 1 Time Unit. So time tracking var's are incremented after // each operation and (i) of following operation is assigned last adjusted time tracking // so it begins when original operation is complete. // Rear side (cs="g34") and Top (cs="g35") must turn at same time as one piece, this is why // time has not been incremented after "g34" rotation operation was completed. i=t1;cs="g31";jd=-50;kd=-50;ks="0,0,-1";ad=90;vm("gm");t1++; // Left Side i=t2;cs="g32";jd=50;kd=-50;ks="0,0,1";ad=90;vm("gm");t2++; // Right Side i=t1;cs="g33";ld=50;kd=-50;ks="-1,0,0";ad=90;vm("gm");t1++; // Front Side i=t2;cs="g34";ld=-50;kd=-50;ks="1,0,0";ad=90;vm("gm"); // Rear Side i=t2;cs="g35";ld=-50;kd=-50;ks="1,0,0";ad=90;vm("gm");t2++; // Top. Runs together with g34 //-------------------------- Inserting Gift Items into Box --------------------------- i=t1;cs="g36";os="0,125,0";vm("gm");t1++; // Move Left item up i=t1;cs="g36";os="140,0,-140";vm("gm");t1++;// Move it sideway to become above box opening i=t1;cs="g36";os="0,-125,0";vm("gm");t1++; // Move it down to sit inside box. i=t2;cs="g37";os="0,125,0";vm("gm");t2++; // Move Right item up i=t2;cs="g37";os="-135,0,135";vm("gm");t2++;// Move it sideway to become above box opening i=t2;cs="g37";os="0,-125,0";vm("gm");t2++; // Move it down to sit inside box. //--------------------------------- Closing the Box ---------------------------------- i=t2;cs="g35";ld=-50;kd=50;ks="1,0,0";ad=90;vm("gm");t2++; os=cs="";oxd=oyd=0; // Reset received values //************************************************************************************** finished=true; // Set reverse process flag to allow its start } //----------------------------- (2) BOX DISASSEMBLY PROCESS ----------------------------- if (cs=="md0" && finished) { // If mouse is clicked for the first time: finished=false; // Set flag so more clicks will do nothing. int t1=1,t2=0; // t1 starts at 1 to be even with t2 after box // is opened. //--------------------------------- Opening the Box ---------------------------------- i=t2;cs="g35";ld=-50;kd=50;ks="-1,0,0";ad=90;vm("gm");t2++; //----------------------------- Removing gifts from box ------------------------------ i=t1;cs="g36";os="0,125,0";vm("gm");t1++; // Move left item up until it clears box's top i=t1;cs="g36";os="-140,0,140";vm("gm");t1++;// Move it sideway to the left i=t1;cs="g36";os="0,-125,0";vm("gm");t1++; // Move it down to initial position. i=t2;cs="g37";os="0,125,0";vm("gm");t2++; // Do the same to right item. i=t2;cs="g37";os="135,0,-135";vm("gm");t2++; i=t2;cs="g37";os="0,-125,0";vm("gm");t2++; //------------------------------ Disassembling the box -------------------------------- i=t1;cs="g33";ld=50;kd=-50;ks="1,0,0";ad=90;vm("gm");t1++; // Front Side i=t2;cs="g34";ld=-50;kd=-50;ks="-1,0,0";ad=90;vm("gm"); // Rear Side i=t2;cs="g35";ld=-50;kd=-50;ks="-1,0,0";ad=90;vm("gm");t2++;// Top. Runs together with g34 i=t1;cs="g31";jd=-50;kd=-50;ks="0,0,1";ad=90;vm("gm");t1++; // Left Side i=t2;cs="g32";jd=50;kd=-50;ks="0,0,-1";ad=90;vm("gm");t2++; // Right Side os=cs="";oxd=oyd=0; // Reset received values started=false; // Set startup flag to allow a new start. } } public override void run() { //**************************************** NO CHANGE ************************************* cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. //--------------------------------- Drawing the Box Kit ------------------------------- // We are going to create the 3D objects of all 6 sides of the box at same location which is // where the box's bottom should be (in a plane which is parallel to XZ, at y=-50), then we // are going to transform each side to where it should be in the kit. cns="xz-50"; // Draw into a parallel plane to xz at y=-50 id=od=100;gm("crv"); // Get vertex data of a square drawn there. for (int n=0;n<6;n++) { // Create 6 identical 3D objects using same cs="g3"+n;cls="c0p0";js="d";vm("co"); // vertex data. Use keynames: g30:g35. } // Transform each side to where it should be and draw it. cs="g30";vm("grf"); // Bottom: Keep at same place. os="-100,0,0";vm("st");cs="g31";vm("gtf"); // Left side: move center by (-100,0,0) os="100,0,0";vm("st");cs="g32";vm("gtf"); // Right side: move center by (100,0,0) os="0,0,100";vm("st");cs="g33";vm("gtf"); // Front side: move center by (0,0,100) os="0,0,-100";vm("st");cs="g34";vm("gtf"); // Rear side: move center by (0,0,-100) os="0,0,-200";vm("st");cs="g35";vm("gtf"); // Top: move center by (0,0,-200) //---------------------------- Drawing the two Gift Items ----------------------------- // Both objects use same vertex data, but positioned differently. The left side item with // "g36" keyname is positioned at (-150,-35,150) The right side item with "g37" keyname is // positioned at (150,-35,-150) id=40;od=30;dd=30;js="xz";gm("cPv"); // Get vertex data for a circular pyramid cs="g36";jd=-150;kd=-35;ld=150;cls="b5b5";vm("cof"); cs="g37";jd=150;kd=-35;ld=-150;cls="r5r5";vm("cof"); gm("fdv"); // Finish Viewport3D drawing operation. //**************************************************************************************** } } =============================================================================================== 3-D DRAWING using the WPF Level II ========================= 3D Assemblies ============= The ModelVisual3D object (mvp): =============================== If you have read Remark [D] above, you know that we draw 3D objects on an object of type "ModelVisual3D" and after we draw everything we want, that object is drawn on the Graphical output device. Actually, when you call method gm("sdi") or gm("sdv") to set the Graphical Output device an object of this type is created, (mvp) is made its reference and "mv99" is made its keyname except that these are details which you have no need to be concerned of. This object has many properties which are of value to us. Here are some of them: (1) It can be identified when clicked on, just like (gmp) In fact method vm("vh") can get the clicked object for you, make (mvp) its reference and return its keyname assigned to (os) It works the same as method vm("gh") (2) It can store any number of 3D objects together with their preset animation and slow motion. This works automatically. Any time you create and draw an object using vm("cof"), the object is automatically stored into the ModelVisual3D object which (mvp) points to. Note that the object is not stored into (mvp) unless you draw it. (3) You can apply transformation, animation and/or slow motion to (mvp) In this case, the operation will be applied to every 3D object it contains. Methods vm("vt"), vm("vm") can apply transform and slow motion to (mvp) You can also animate (mvp) using the same animation methods. The animation methods know which object to animate since they receives the keyname. (4) The best of it all, any ModelVisual3D object can contain any number of other ModelVisual3D objects and can be made a child of another object of its kind. Method vm("va") is used to add one object to another. It expects the container object keyname to be assigned to (cs) and the child object keyname to be assigned to (ks) See Personal C Sharp Methods to know more. Creating 3D Assemblies: ======================= After reading the properties above, you must know that the ModelVisual3D object qualifies to represent a 3D Assembly which can contain any number of 3D objects and other 3D assemblies. You also know that we can treat the assembly as a unit which we can apply transform, animation or slow motion to. So, this is what we have been looking for. We are no longer limited to single 3D objects, we can draw complex 3D assemblies and can create a bank of assemblies which we can add together to make something even more valuable. Let us call it "3D drawing level two". From now on, We are going to be calling the ModelVisual3D object a 3D Assembly or 3D Visual. Selecting a keyname for a ModelVisual3D object: ----------------------------------------------- Keynames of ModelVisual3D objects start with "mv" and can range between "mv0" and "mv89". How to write the code: ====================== As mentioned before, when you set a new Graphical Output Device a new 3D visual is created and (mvp) is made to be its reference. If you are not going to be creating 3D assemblies as we have been doing in all the past examples, you can just forget about this object and supply no keyname when you finish drawing to the Graphical Output Device. If you are going to be creating a 3D assembly which should contain the 3D objects which you create, you need to create the assembly first by assigning its keyname to (cs) and calling vm("vc") After this is done, the initially created ModelVisual3D object becomes neglected since (mvp) now refers to the new object. Any 3D object which you create and draw from this point on, will be contained by the assembly. This should continue until you create a new assembly. You can apply transform, slow motion or animation to any individual object inside the assembly or apply the operations to all of them in one step by applying it to the assembly object. You can then create a mother assembly and add all created assemblies to it. You can still add more 3D objects to the mother assembly and apply transform, slow motion or animation to the 3D objects or to the entire mother assembly. At the end, if any object inside the mother assembly is animated, you need to call vm("at") to trigger the animation supplying it with the mother assembly's keyname. Then, you need to call gm("fdv") to finish the drawing operation supplying it with the mother assembly's keyname also. If you forget to supply the methods with the keyname, the original 3D visual will be used for the operations which is not what you want. REMARKS: ======== (1) When you set a Viewport3D control as the graphical output device, you must supply the keyname of the Viewport3D control itself. When you finish the drawing operation, you either supply no keyname (if you have created no 3D assemblies) or supply the keyname of the mother 3D assembly which contains all created objects. (2) You must always supply keyname when you call method vm() at modes which start with 'v'. If you forget to supply the keyname, operation is not going to be performed on present assembly object (mvp), it will be performed on the initial (default) object which has been created when Graphical Output Device was set and (mvp) will point to that object after the operation. Dividing operations of previous example into 3 independant operations: ---------------------------------------------------------------------- We are going to be using the code of previous example in the next one. Before we do that, we like to see how we can divide it into 3 operations each one is included into a different method as follows: (1) Method BoxMaking() creates the box kit and applies the slow motions necessary to turn it into a box. (2) Method GiftPacking() creates the two gift items and applies the slow motions necessary to insert them into the box. (3) Method BoxClosing() which applies the slow motion necessary to close the box. In method run, the 3 methods will be called in order. Method update() will be eliminated. Since slow motion does not require trigger, it will run as soon as page is loaded. Here is how the previous example's code will look like: -------------------------------------------------------------------------------------------- //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" int t1=0,t2=0; // Create & Initialize 2 time tracking var's. // One time only. public override void init() { id=2;vm("atu"); // Set animation time unit at 2 seconds. base.init(); } public override void setup() { PageNumber=14;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";cis="";os="s"; ds="nw";lf=560;of=250;fns="crb14";cm("i");// Vewport3D to be used as Graphical out device } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. BoxMaking(); // Run the GiftPacking(); // 3 methods BoxClosing(); // in order. gm("fdv"); // Finish drawing to Graphical Output Device } void BoxMaking() { // Making the box: //--------------------------------- Drawing the Box Kit ------------------------------- cns="xz-50"; // Draw into a parallel plane to xz at y=-50 id=od=100;gm("crv"); // Get vertex data of a square drawn there. for (int n=0;n<6;n++) { // Create 6 identical 3D objects using same cs="g3"+n;cls="c0p0";js="d";vm("co"); // vertex data. Use keynames: g30:g35. } // Transform each side to where it should be and draw it. cs="g30";vm("grf"); // Bottom: Keep at same place. os="-100,0,0";vm("st");cs="g31";vm("gtf"); // Left side: translate center to (-100,0,0) os="100,0,0";vm("st");cs="g32";vm("gtf"); // Right side: translate center to (100,0,0) os="0,0,100";vm("st");cs="g33";vm("gtf"); // Front side: translate center to (0,0,100) os="0,0,-100";vm("st");cs="g34";vm("gtf"); // Rear side: translate center to (0,0,-100) os="0,0,-200";vm("st");cs="g35";vm("gtf"); // Top: translate center to (0,0,-200) //--------------------------------- Making the box ----------------------------------- i=t1;cs="g31";jd=-50;kd=-50;ks="0,0,-1";ad=90;vm("gm");t1++; // Left Side i=t2;cs="g32";jd=50;kd=-50;ks="0,0,1";ad=90;vm("gm");t2++; // Right Side i=t1;cs="g33";ld=50;kd=-50;ks="-1,0,0";ad=90;vm("gm");t1++; // Front Side i=t2;cs="g34";ld=-50;kd=-50;ks="1,0,0";ad=90;vm("gm"); // Rear Side i=t2;cs="g35";ld=-50;kd=-50;ks="1,0,0";ad=90;vm("gm");t2++; // Top. Runs together with g34 } void GiftPacking() { //---------------------------- Drawing the two Gift Items ----------------------------- id=40;od=30;dd=30;js="xz";gm("cPv"); // Get vertex data for a circular pyramid cs="g36";jd=-150;kd=-35;ld=150;cls="b5b5";vm("cof"); cs="g37";jd=150;kd=-35;ld=-150;cls="r5r5";vm("cof"); //-------------------------- Inserting Gift Items into Box --------------------------- i=t1;cs="g36";os="0,125,0";vm("gmf");t1++; // Move Left item up i=t1;cs="g36";os="140,0,-140";vm("gmf");t1++;// Move it to become above box opening i=t1;cs="g36";os="0,-125,0";vm("gmf");t1++; // Move it down to sit inside box. i=t2;cs="g37";os="0,125,0";vm("gmf");t2++; // Move Right item up i=t2;cs="g37";os="-135,0,135";vm("gmf");t2++;// Move it to become above box opening i=t2;cs="g37";os="0,-125,0";vm("gmf");t2++; // Move it down to set inside box. } void BoxClosing() { //--------------------------------- Closing the Box ---------------------------------- i=t2;cs="g35";ld=-50;kd=50;ks="1,0,0";ad=90;vm("gm");t2++; } } -------------------------------------------------------------------------------------------- Copy this code, compile it and run it. You should find out that it does exactly the same as previous example's code except that it runs as soon as the page is loaded. About the next example: ----------------------- We are going to create a ModelVisual3D object at the top of each method. This means that when we run the methods we will end with three 3D assemblies which contain everything necessary to make the box, the two gift items and all necessary slow motions. Next, we are going to create a mother 3D assembly, add the 3 assemblies to it and additionally create and draw a table on the mother assembly which the box will be sitting on. We are going to apply different animations to the mother assembly. Some animations will take place while the child assemblies are running and some will take place after they stop. =========================================================================================== Example 15: Divide the operations performed in example 14 into three. Include each of them into a seperate assembly. Create a mother assembly which contains all three assemblies in addition to the geometry of a hexagonal table to seat the box on. Apply animation to the mother assembly. =========================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" int t1=0,t2=0; // Create & Initialize 2 time tracking var's. public override void init() { id=2;vm("atu"); // Set animation time unit at 2 seconds. base.init(); } public override void setup() { PageNumber=15;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";cis="Using 3D Assemblies";os="s"; ds="nw";lf=560;of=250;fns="trb14";cm("i");// Vewport3D to be used as Graphical out device } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. BoxMaking(); // Visual "mv10" now contains box making oper. GiftPacking(); // Visual "mv11" now contains gift packing op. BoxClosing(); // Visual "mv12" now contains box closing oper. cs="mv0";vm("vc"); // Create the mother 3D Visual "mv0". cs="mv0";ks="mv10";vm("va"); // Add "mv10" to it. cs="mv0";ks="mv11";vm("va"); // Add "mv11" to it. cs="mv0";ks="mv12";vm("va"); // Add "mv12" to it. // Making a hexagonal table id=6;od=200;dd=20;js="xz";gm("cCv"); // Get vertex data for a hex cyl of height=20 cs="g310";kd=-65;cls="s9r4";vm("cof"); // Create the cylinder, position it under box. //------ Animating the entire 3D assembly ("mv0") ------ // Make assembly rotate 30 degrees back and forth while mv10, mv11 and mv12 are running. // Notice that no. of repititions is 3. This is because each repitition is followed with // a reverse one by default, so total duration=6 time units which matches the total duration // of mv10, mv11 and mv12 movements. cs="mv0";j=3;ks="0,1,0";O[0]=1;OS[0]="30";vm("ara"); // After 6 time units, when mv10, mv11 and mv12 stop, make 3 repititions of (180 degrees back // and forth rotations while moving up and down by 50) cs="mv0";i=6;j=3;ks="0,1,0";O[0]=1;OS[0]="180";vm("ara"); cs="mv0";i=6;j=3;O[0]=1;OS[0]="50";vm("apy"); cs="mv0";vm("at"); // Trigger animation of "mv0". cs="mv0";gm("fdv"); // Finish drawing to Graphical Output Device } // using data stored into "mv0". void BoxMaking() { // Making the box: //--------------------------------- Drawing the Box Kit ------------------------------- cs="mv10";vm("vc"); // Create a new 3D Visual //******************************** The rest is unchanged ****************************** cns="xz-50"; // Draw into a parallel plane to xz at y=-50 id=od=100;gm("crv"); // Get vertex data of a square drawn there. for (int n=0;n<6;n++) { // Create 6 identical 3D objects using same cs="g3"+n;cls="c0p0";js="d";vm("co"); // vertex data. Use keynames: g30:g35. } // Transform each side to where it should be and draw it. cs="g30";vm("grf"); // Bottom: Keep at same place. os="-100,0,0";vm("st");cs="g31";vm("gtf"); // Left side: translate center to (-100,0,0) os="100,0,0";vm("st");cs="g32";vm("gtf"); // Right side: translate center to (100,0,0) os="0,0,100";vm("st");cs="g33";vm("gtf"); // Front side: translate center to (0,0,100) os="0,0,-100";vm("st");cs="g34";vm("gtf"); // Rear side: translate center to (0,0,-100) os="0,0,-200";vm("st");cs="g35";vm("gtf"); // Top: translate center to (0,0,-200) //--------------------------------- Making the box ----------------------------------- i=t1;cs="g31";jd=-50;kd=-50;ks="0,0,-1";ad=90;vm("gm");t1++; // Left Side i=t2;cs="g32";jd=50;kd=-50;ks="0,0,1";ad=90;vm("gm");t2++; // Right Side i=t1;cs="g33";ld=50;kd=-50;ks="-1,0,0";ad=90;vm("gm");t1++; // Front Side i=t2;cs="g34";ld=-50;kd=-50;ks="1,0,0";ad=90;vm("gm"); // Rear Side i=t2;cs="g35";ld=-50;kd=-50;ks="1,0,0";ad=90;vm("gm");t2++; // Top. Runs together with g34 //************************************************************************************* } void GiftPacking() { //---------------------------- Drawing the two Gift Items ----------------------------- cs="mv11";vm("vc"); // Create a new 3D Visual //******************************** The rest is unchanged ****************************** id=40;od=30;dd=30;js="xz";gm("cPv"); // Get vertex data for a circular pyramid cs="g36";jd=-150;kd=-35;ld=150;cls="b5b5";vm("cof"); cs="g37";jd=150;kd=-35;ld=-150;cls="r5r5";vm("cof"); //-------------------------- Inserting Gift Items into Box --------------------------- i=t1;cs="g36";os="0,125,0";vm("gmf");t1++; // Move Left item up i=t1;cs="g36";os="140,0,-140";vm("gmf");t1++;// Move it to become above box opening i=t1;cs="g36";os="0,-125,0";vm("gmf");t1++; // Move it down to sit inside box. i=t2;cs="g37";os="0,125,0";vm("gmf");t2++; // Move Right item up i=t2;cs="g37";os="-135,0,135";vm("gmf");t2++;// Move it to become above box opening i=t2;cs="g37";os="0,-125,0";vm("gmf");t2++; // Move it down to set inside box. //************************************************************************************* } void BoxClosing() { //--------------------------------- Closing the Box ---------------------------------- cs="mv12";vm("vc"); // Create a new 3D Visual //******************************** The rest is unchanged ****************************** i=t2;cs="g35";ld=-50;kd=50;ks="1,0,0";ad=90;vm("gm");t2++; } } ==============================================================================================


=============================================================================================== READY TO USE ASSEMBLIES ======================= (For version 3.02 and later) With level I, we have been able to draw Rectangular blocks, Cylinders and Pyramids in addition to drawing a rectangle or any equally sided figure into any of the three dimentional planes. We have been able to do so without the difficulty of having to supply point coordinates, mesh data, texture coordinates or normals. With level II, we are able to draw more sophisticated 3D figures while maintaining the elimination of the need to supply the same data. Level II figures are not simple 3D objects which are represented with the GeometryModel3D (gmp), they are assemblies of 3D objects which are represented with the ModelVisual3D object (mvp) We have developed several tools which handle the creation of 3D assemblies. They are all easy to use and require no vertex data to be supplied, but they require keynames which must be in the range "mv0" to "mv89". CONIC SECTIONS: =============== The three 2D figures "Parabola", "Ellipse" and "Hyperbola" are called "Conic sections". Their equations in 2D plane are: Parabola: x^2 = a*y Where (a) is a constant. Ellipse: x^2/a^2 + y^2/b^2 = 1 Where (a,b) are the major and minor axes. Hyperbola: x^2/a^2 - y^2/b^2 = 1 Where (a,b) are constants. When you "circularize" the conic sections, you come up with 3D figures called "Paraboloid", "Ellipsoid" and "Hyperboloid". The "Sphere" is a special case of the "Ellipsoid". It's an Ellipsoid whose major and minor axes (a,b) are equal. Method vm() at modes "vcp", "vce" and "vch" can create for you assemblies which contain a "Paraboloid", "Ellipsoid" and "Hyperboloid" respectively. However, method vm() is not limited to figures with circular cross sections as these names are for. It can generate for you figures whose cross section can be any equally sided object including a circle as you know. Any conic section object which we create has a base which is on or parallel to the xz plane (this is mandatory. You can then move it or rotate it as you like by applying transforms) The objects are centered at the origin. The base can be any equally sided object starting from a line and ending at a circle. If a line base is selected you'll get a plane Parabola, Ellipse or Hyperbola. You don't have to get a full figure. For example, you can create a dome (half a sphere) or even a smaller slice of a sphere. You select the starting and ending (y) values for the slice you want. If you like to get a full sphere, select a (y) range of (-R,+R) where (R) is the radius. If you like to get the upper dome only, select a range of (0,R) Since the slice you select may not be centered at the origin, you can select any point on the Y-axis to move to the origin. Normally you will select the point at the center of your slice. Here are all the necessary parameters: cs : Keyname of the assembly which you like to add the created figure to. cls : Combination color code (as normal) id : Number of base sides. jd,kd : Constants a,b of the figure's 2D Plane equation (See above) ks : Starting-Y, Ending-Y of selected slice and y-coord. of the point which you like to move to the origin seperated with commas. js : (Optional) Material type code. Default (js="d") meaning "diffuse" material. ============================================================================================== Example 16: Draw a sphere, an Ellipsoid with major axis on the Y-axis and another Ellipsoid with the minor axis on the Y-axis. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=16;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";cis="Sphere Ellipsoid Ellipsoid";os="s"; ds="nw";lf=560;of=250;fns="crb14";cm("i"); // Vewport3D to be used as Graphical out device } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. // Draw a full sphere (From y=-R to y=+R), Radius=100 into "mv10", then move it to the left cs="mv10";cls="b3b3";id=40;jd=100;kd=100;ks="-100,100,0";vm("vce"); cs="mv10";os="-300,-125,50";vm("st");cs="mv10";vm("vt"); // Draw a full Ellipsoid with major axis vertical into "mv11". Keep it at center. cs="mv11";cls="r3r3";id=40;jd=50;kd=100;ks="-100,100,0";vm("vce"); // Draw a full Ellipsoid with minor axis vertical into "mv12". Move it to the right. cs="mv12";cls="g3g3";id=40;jd=70;kd=40;ks="-40,40,0";vm("vce"); cs="mv12";os="135,45,-60";vm("st");cs="mv12";vm("vt"); cs="mv0";vm("vc"); // Create a new 3D Visual. cs="mv0";ks="mv10";vm("va"); // Add all cs="mv0";ks="mv11";vm("va"); // 3 assemblies cs="mv0";ks="mv12";vm("va"); // to it. cs="mv0";gm("fdv"); // Finish drawing to Graphical Output Device } // using data stored into "mv0". } ---------------------------------------------------------------------------------------------- COMPILING: Use tool "pcp" or "pcpr" as usual. Make sure to keep the commented statement "//assembly ms1.exe;" at the top and also to keep the commented word "pcs3" at the class declaration line since both are required by the compiling tools. For more details read the text titled "Compiling" which follows example 1. ==============================================================================================


=============================================================================================== Paraboloids and Hyperboloids: ----------------------------- ============================================================================================== Example 17: Draw two hyperboloids using different constants and also a Paraboloid. Since Paraboloids are not centered at the origin, select a center point for the figure to be moved to the origin. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=17;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";cis="Hyperboloid Hyperboloid Paraboloid";os="s"; ds="nw";lf=560;of=250;fns="crb14";cm("i"); // Vewport3D to be used as Graphical out device } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. // Draw a Hyperboloid From y=-70 to y=+70, with both constants a,b=70 into "mv10", then move // it to the left cs="mv10";cls="b3b3";id=40;jd=70;kd=70;ks="-70,70,0";vm("vch"); cs="mv10";os="-280,-115,50";vm("st");cs="mv10";vm("vt"); // Draw a Hyperboloid From y=-50 to y=+50), with both constants a,b=10 into "mv11" cs="mv11";cls="r3r3";id=40;jd=10;kd=10;ks="-50,50,0";vm("vch"); // Draw a Paraboloid From y=0 to y=+70, with constant a=70 into "mv12", considering the point // of (y=35) to be its center. Then move it to the right. cs="mv12";cls="g5g5";id=40;jd=70;ks="0,70,35";vm("vcp"); cs="mv12";os="135,25,-60";vm("st");cs="mv12";vm("vt"); cs="mv0";vm("vc"); // Create a new 3D visual "mv0" cs="mv0";ks="mv10";vm("va"); // Add all cs="mv0";ks="mv11";vm("va"); // 3 Assemblies cs="mv0";ks="mv12";vm("va"); // to it. cs="mv0";gm("fdv"); // Finish drawing to Graphical Output Device } // using data stored into "mv0". } ---------------------------------------------------------------------------------------------- COMPILING: Use tool "pcp" or "pcpr" as usual. Make sure to keep the commented statement "//assembly ms1.exe;" at the top and also to keep the commented word "pcs3" at the class declaration line since both are required by the compiling tools. For more details read the text titled "Compiling" which follows example 1. ==============================================================================================


=============================================================================================== Making use of conic sections 3D objects: ======================================== With the use of some imagination you should find that they solve many problems. Here are some ideas: (1) A small piece taken from the top of a large sphere can be used to draw seat cushions. (2) Hyperboloids can make good stands for metalic seats and tables. They can also be used in the assembly of decorated funiture. (3) Half a Sphere or Ellipsoid with hexagonal or octagonal base can make a good umbrella. (4) Paraboloids are usable as they are since dish antennas are paraboloids. Flash light reflectors are also Paraboloids. Let us have a demonstration. REMARK: As stated before, we are not drawing in pixels, but we choose the scale which makes our drawings approximately in pixels. ============================================================================================== Example 18: Draw a patio table with a center umbrella and two cushioned seats. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=18;PageHeight=360;base.setup(); // Execute master class's setup() //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";os="s"; ds="nw";lf=560;of=350;fns="crb14";cm("i"); // Vewport3D to be used as Graphical out device } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. //--------------------------------- Producing the Parts --------------------------------- // Making the 2 seat cushions: Pick a slice of large sphere which starts at the top and ends // 20 pixels below the top. Center it by moving its center point (y=90) down to the origin cs="mv10";cls="b4b4";id=40;jd=100;kd=100;ks="80,100,90";vm("vce"); cs="mv11";cls="b4b4";id=40;jd=100;kd=100;ks="80,100,90";vm("vce"); // Making 3 stands for the table and the 2 seats: Create 3 full hyperboloids. for (n=12;n<15;n++) { cs="mv"+n;cls="r4r4";id=40;jd=10;kd=10;ks="-50,50,0";vm("vch"); } // Making the Umbrella: Pick the top half of a large Ellipsoid with 8-Sides base and // vertical minor axis. Center it by moving its center point (y=20) down to the origin cs="mv15";cls="c5c5";id=8;jd=150;kd=40;ks="0,40,20";vm("vce"); //------------------------ Placing Parts where they should be ---------------------------- //---- Placing the 3 stands ---- // Notice that table stand is scaled up vertically to make table higher than seats. cs="mv12";js="1,1.3,1";os="0,-135,0";vm("st"); // Set 3D transform to scale table stand by // 1.3 vertically and move center 135 pixels cs="mv12";vm("vt"); // down. Apply xform to table stand. cs="mv13";os="-120,-150,120";vm("st"); // Set transform to move center left and 150 cs="mv13";vm("vt"); // pixels down. Apply xform to left seat stand cs="mv14";os="120,-150,-120";vm("st"); // Set transform to move center right and 150 cs="mv14";vm("vt"); // pixels down. Apply xform to R seat stand //---- Placing cushions on the seats ---- // A stand's height is 100 pixels, its center is at y=-150, so its top is at -100. Since the // cushion is 20 pixels thick, it's center should be at (y=-90) cs="mv10";os="-120,-90,120";vm("st"); // Set transform to move center left and 90 cs="mv10";vm("vt"); // pixels down. Apply xform to L seat cushion cs="mv11";os="120,-90,-120";vm("st"); // Set transform to move center right and 90 cs="mv11";vm("vt"); // pixels down. Apply xform to R seat cushion //---- Placing the Umbrella at 120 pixels above center ---- cs="mv15";os="0,120,0";vm("st"); // Set transform to move center up 120 pxils cs="mv15";vm("vt"); // Apply xform to Umbrella. //------------------ Creating Mother 3D Assembly and finishing operation ----------------- cs="mv0";vm("vc"); // Create the mother 3D Assembly. for(n=10;n<16;n++) { // Add all child assemblies. cs="mv0";ks="mv"+n;vm("va"); } //---- Drawing the table top and umbrella rod ---- // Table top: id=40;od=200;dd=20;js="xz";gm("cCv"); // Get vertex data for a cyl of height=20 cs="g30";kd=-60;cls="s9r4";vm("cof"); // Create the cylinder, moving it 60 pixels // down to sit above its scaled up stand. // Umbrella rod: id=40;od=10;dd=190;js="xz";gm("cCv"); // Get vertex data for a cyl,190 pixels cs="g31";kd=25;cls="s9s6";vm("cof"); // high 10 pixels thick. Draw the cylinder. cs="mv0";gm("fdv"); // Finish drawing to Graphical Output Device } // using data stored into "mv0". } ============================================================================================== There is something about this example which should make you admire the Windows Presentation foundation software. The umbrella rod is partially covered by the umbrella and partially covered by the table. The WPF software knows that and creates the right drawing regardless to which order you choose in drawing those three items. ===============================================================================================


=============================================================================================== Sloped and Twisted Cylinders: ============================= There are two more tools. They also come as preset 3D Assemblies. Sloped Cylinders: ----------------- You can create a vertical cylinder of any number of base sides whose top and bottoms are not equal in size. This assembly can be used in many applications, the simplest of them is drawing a drinking cup. To create a sloped cylinder, call vm("vcs") with the following parameters: cs : Keyname of 3D Visual object which will contain the cylinder. id : Number of base sides. od : Diameter of enclosing circle of cylinder's top. jd : Diameter of enclosing circle of cylinder's bottom. dd : Cylinder's height. cls: Combined color code of cylinder material. js : (Optional) Material type code. Default (js="d") The Cylinder comes centered at origin with base parallel to xz plane. After creating the cylinder you can place it wherever you like using transforms. Twisted Cylinders: ------------------ You can create a cylinder which is twisted to make a circular arch of any angle and radius. Angles may be positive or negative, but the cylinder is always in the xz plane and the center of rotation is on the Y-axis. To create a twisted cylinder, call vm("vct") with the following parameters: cs : Keyname of 3D Visual object which will contain the cylinder. id : Number of base sides. od : Diameter of enclosing circle of base. jd : Radius of rotation. ad : Rotation angle in degrees. cls: Combined color code of cylinder material. js : (Optional) Material type code. Default (js="d") ============================================================================================== Example 19: Draw a drinking cup with (bottom, top) diameters of (40, 80) and a height of 150. Draw also a circular ring with diameter of 50 and rotation radius of 100. Finally draw a 180 degrees arch of a hexagonal cylinder with enclosing circle diameter of 50 and a rotation radius of 50. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=19;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";cis="Circular Sloped Hexagonal\n"; cis+=" Ring Cylinder Half Ring";os="s"; ds="nw";lf=560;of=250;fns="crb14";cm("i"); // Vewport3D to be used as Graphical out device } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. // Draw a circular 360 degrees ring with rotation radius of 100 then move it to left. cs="mv10";cls="b3b3";id=40;od=50;ad=360;jd=100;vm("vct"); cs="mv10";os="-280,-80,50";vm("st");cs="mv10";vm("vt"); // Draw a vertical cup with bottom and top base diameters of 60, 100 respectively and a // height of 150. cs="mv11";cls="r3r3";id=40;jd=60;od=100;dd=150;vm("vcs"); // Draw a hexagonal cylinder making 180 degrees arch, Arch radius=50 and cylinder's enclosing // circle diameter=50 then move it to the right. cs="mv12";cls="g3g3";id=6;od=50;ad=-180;jd=50;vm("vct"); cs="mv12";os="100,25,-60";vm("st");cs="mv12";vm("vt"); cs="mv0";vm("vc"); // Create a new 3D visual "mv0" cs="mv0";ks="mv10";vm("va"); // Add all cs="mv0";ks="mv11";vm("va"); // 3 Assemblies cs="mv0";ks="mv12";vm("va"); // to it. cs="mv0";gm("fdv"); // Finish drawing to Graphical Output Device } // using data stored into "mv0". } ==============================================================================================


=============================================================================================== Drawing a Circularized version of any function: =============================================== Conic sections have proven to be useful in adding new tools to our 3D drawing toolbox. Therefore we like to extend the advantage by generalizing the process. Instead of being able to draw circularized versions of parabola, ellipse and hyperbola alone, you can now do the same to any 2D shape which you know its equation or can obtain the (x,y) values of its points by any means. There are too many functions which can generate 3D assemblies that you can use as a whole or can use a slice of in your drawings. If you like to draw a special figure which you can't obtain a formula for, all you need to do is to prepare a table containing the (x,y) values of the points which make the figure. You can then use that table within your method to obtain the value of (x) which corresponds to each (y) value which your method receives when called by class pcs3. How does it work? ----------------- You'll include within your class method GetX() which receives the (y) value of a point and returns the corresponding (x) value. The method also receives a second parameter which is the radius of base's enclosing circle (equals to the value assigned to (od) divided by two) Both values are of type "double". Whether you use an equation to compute the (x) values or any other means is upto you. Your method will be called repeatedly by class (pcs3) while creating the 3D figure supplying it with different values of (y) which lie within the Y-range which you have specified in (ks) At the meantime, you'll be using similar code within method run() as you have been doing before except that you'll be calling method vm("vcm") in this case. This indicates that the data of the figure to be drawn should be obtained from your prepared method. Here is the parameter list: cs : Keyname of the assembly which you like to add the created figure to. cls: Combination color code (as normal) id : Number of base sides. ks : Starting-Y, Ending-Y of selected slice and y-coord. of the point which you like to move to the origin seperated with commas. js : (Optional) Material type code. Default (js="d") od : Enclosing circle diameter of base. IMPORTANT: You set the scale: ----------------------------- If the (Y) range supplied to method vm("vcm") was less than 100, the method divides the range into 100 evenly spaced (Y) values and calls method GetX() 100 times in order to get the corresponding (X) value of each. The overall figure size may end to be larger or smaller than the size you want. You can correct this by multiplying the returned value of method GetX() by a scale factor and/or applying a scale transform to the 3D assembly after being created. What to do if there was more than one value of x for one value of y? -------------------------------------------------------------------- We can make use of one value only. Your method should return one value and neglect the rest. ============================================================================================== Example 20: Draw the 3D assemblies which are obtained by circularizing the functions (y=cos x), (y=tan x) and (y=1-sin x) ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=20;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";cis=" y = cos x y = tan x y = 1 - sin x";os="s"; ds="nw";lf=560;of=250;fns="crb14";cm("i"); // Vewport3D to be used as Graphical out device } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. // Create 3D assembly for (y=cos x) from y=-1 to y=1. Scale resulting object down and move // it to the left. cs="mv10";cls="b3b3";id=40;ks="-1,1,0";vm("vcm"); cs="mv10";js="0.75,1.5,0.75";os="-260,-70,50";vm("st");cs="mv10";vm("vt"); // Create 3D assembly for (y=tan x) from y=-1 to y=1. cs="mv11";cls="g3g3";id=40;ks="-1,1,0";vm("vcm"); cs="mv11";js="1,1.5,1";vm("st");cs="mv11";vm("vt"); // Create 3D assembly for (y=1-sin x) from y=0 to y=1. Scale resulting object down and move // it to the right. cs="mv12";cls="r3r3";id=40;ks="0,1,0.5";vm("vcm"); cs="mv12";js="0.75,1.5,0.75";os="135,60,-60";vm("st");cs="mv12";vm("vt"); cs="mv0";vm("vc"); // Create a new 3D visual "mv0" cs="mv0";ks="mv10";vm("va"); // Add all cs="mv0";ks="mv11";vm("va"); // 3 Assemblies cs="mv0";ks="mv12";vm("va"); // to it. cs="mv0";gm("fdv"); // Finish drawing to Graphical Output Device } // using data stored into "mv0". //---------------------------------- The GetX() method ----------------------------------- public override double GetX(double y, double radius) { // IN: cs,y OUT: x if (cs=="mv10") js="acos"; // Use method um("mt") else if (cs=="mv11") js="atan"; // to get the x values else if (cs=="mv12") {y=1-y;js="asin";} // for each object. od=y;um("mt"); return od; // Return the X value. } } ---------------------------------------------------------------------------------------------- COMPILING: Use tool "pcp" or "pcpr" as usual. Make sure to keep the commented statement "//assembly ms1.exe;" at the top and also to keep the commented word "pcs3" at the class declaration line since both are required by the compiling tools. For more details read the text titled "Compiling" which follows example 1. ==============================================================================================


=============================================================================================== Drawing General 3D figures using Level II: ========================================== At Level I, you have been limited to drawing the 3D figures which you can provide vertex data for. Personal C Sharp has helped by automatically supplying the WPF with the vertex data for a general Rectangular Block, Cylinder or Pyramid when it receives from you simple instructions to do so. At Level II, the choices of 3D figures which you can draw have grown considerably after 3D assemblies have been introduced. We have demonstrated so far that you can draw any 3D figure as long as the cross section of that figure in the xz plane is one of PC#'s equally sided shapes which have been introduced years ago when Personal C Sharp first appeared. Now, we are going to show you how to draw any 3D figure which you can imagin. There are no restrictions concerning what you can draw. The objects you draw will be centered at the origin when you start, but you can then relocate them, apply slow motions to them or animate them as you like. Whenever the user clicks on any object, you will be able to receive the event and identify the object which has been clicked. Our 3D General Drawing Module: ============================= Our 3D general drawing module can be described as "Sculpture" in which you start with a cylinder shaped material and reshape it to become the 3D figure which you like to create. Technically, imagine that the original cylinder which has a circular base and a vertical height has been divided into tiny cylinders, each with a height of one pixel which we'll call a unit cylinder. Then, each unit cylinder has been divided into as many as 360 sectors by drawing radii which start at the center and make one degree angles with each others. Now, let us assume that you'll be working on each unit cylinder at a time starting by the one at the bottom. You'll be reducing the radii of the unit cylinder one by one as the 3D figure which you are making requires. After you finish with one unit cylinder, you move to the next one up and repeat procedures. When all are done, you'll end with the 3D figure which you want to create. Drawing general 3D object when both horizontal and vertical equations are available: ==================================================================================== In the previous example, you have been drawing objects using equations for the vertical direction while maintaining the default shape in the horizontal direction which is a circle. Many useful 3D figures can be created this way, but we obviously need more. Mouldings which are bars of wood which carpenters use to decorate around windows and doors can also be created with equations, except that the equations need to be applied in the horizontal direction. In the next example we'll see how to create a bar of moulding whose cross-section is quarter a circle. If you can find equations which can be used to create your 3D figure in both horizontal and vertical directions you would be lucky since your job would be much easier. Applying an equation in the vertical direction: ----------------------------------------------- When you include method GetX() into your class, your method must be able to provide the (X) value for each (Y) value within the range which you have specified when method vm("vcm") was called. Think about each (Y) value as the Y-coordinate of a specific unit cylinder and the (X) value as the radius of that unit cylinder. If you don't include method GetX() into your class, the default equation which will be applied vertically is: (x = radius) where radius is equal to (od/2) It's the unmodified radius of the enclosing circle which you have provided. This means that you'll be getting the standard cylinder vertically. Applying an equation in the horizontal direction: ------------------------------------------------- Each time method GetX() is called, the (x) value returned is the partially modified radius of the current unit cylinder. We call it "partially modified" since it is based only on the equation of the vertical direction, it has not been adjusted yet according to the equation of the horizontal direction. This value is actually the (x) coordinate of the point on the horizontal cross section where (z=0) Next to that, method GetRadius() is called 360 times (if you have selected id=360), once for each sector with the partially modified radius in addition to the sector's angle which ranges between zero and 359 (in this case) and the unit cylinder's Y value. If you don't include method GetRadius() into your code, the default equation which will be applied horizontally is: (r = radius) where (radius) in this case is the partially modified radius which the method receives as a parameter. This means that the unit cylinder's surface will be a circle whose radius is determined only by the equation applied to vertical direction. If you include method GetRadius() into your code, your method should adjust the partially modified radius depending on the angle received using the equation of the horizontal direction. Your method returns the final radius which is used to draw the 3D figure with. 3D figure's density: -------------------- When your code calls method vm("vcm"), it supplies it with two values which set figure density: id : (id) sets the figure's density horizontally since it means the number of sectors each unit cylinder will be divided into. As you know, each sector means one additional piece of data regarding the 3D figure. If you specify a value of (id) which is less than (40), it will be changed to 40 since this is the munimum density allowed (which is one sector each 9 degree) However, if you assign a value to (id) which is higher than 40, your value will be kept unchanged and the 3D figure's density will be higher. ks : This string contains 3 numbers. The first two sets the wanted (Y) range. This means that they set the number of unit cylinders which the 3D figure will be made of. If you assign to (ks) a range which is higher than 100 (for example 300), the figure will be divided into 300 unit cylinders in this case. If you assign a range which is lower than 100, it will be changed to 100 and the figure will be divided into 100 unit cylinders. How to write the code: ---------------------- In addition to methods GetX() and GetRadius(), you'll be using the same code as you did in previous example. You'll be calling method vm("vcm") with same parameters. ============================================================================================== Example 21: (1) Draw the 3D assembly which is controled by the equation (r=R*sin 8a) horizontally and the circle equation (x^2 + y^2 = R^2) vertically. (2) Draw a bar of moulding with a height of 200 and "quarter a circle" cross-section. (3) Draw the 3D assembly which is controled by the equation (r=R*sin 6a) horizontally and the equation (y=tan x) vertically. ============================================================================================== REMARK: ------- Since some values of the function (r=R*sin na) are negative, We have made all the values positive while maintaining the same range by modifying it to [r=0.5*R*(1+sin na)] ---------------------------------------------------------------------------------------------- //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=21;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";cis="HORIZONTAL: r = R*sin 8a r = a r = R*sin 6a \n"; cis+="VERTICAL : x^2 + y^2 = R^2 x = R y = tan x ";os="s"; ds="nw";lf=560;of=250;fns="crb14";cm("i"); // Vewport3D to be used as Graphical out device } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. // Create this 3D assembly using equations GetX() and GetRadius() below then move it to left. cs="mv10";cls="b5b5";od=200;id=360;ks="-150,150,0";vm("vcm"); cs="mv10";os="-260,-130,50";vm("st");cs="mv10";vm("vt"); // Create this 3D assembly using equations GetX() and GetRadius() below then scale it down cs="mv11";cls="r5r5";id=360;od=200;ks="-100,100,0";vm("vcm"); cs="mv11";js="0.75,0.75,0.75";os="100,40,0";vm("st");cs="mv11";vm("vt"); // Create this 3D assembly using equations GetX() and GetRadius() below then scale it down. // and move it to the right. cs="mv12";cls="g5g5";od=200;id=360;ks="-1,1,0";vm("vcm"); cs="mv12";js="0.8,1.6,0.8";os="165,20,-80";vm("st");cs="mv12";vm("vt"); cs="mv0";vm("vc"); // Create a new 3D visual "mv0" cs="mv0";ks="mv10";vm("va"); // Add all cs="mv0";ks="mv11";vm("va"); // 3 Assemblies cs="mv0";ks="mv12";vm("va"); // to it. cs="mv0";gm("fdv"); // Finish drawing to Graphical Output Device } // using data stored into "mv0". //---------------------------------- The GetX() method ----------------------------------- public override double GetX(double y, double radius) { // IN: cs,y OUT: x // "mv10" is vertically a circle. So calculate (x) from the equation (x^2 + y^2 = R^2) if (cs=="mv10") { // If first assembly: od=100*100-y*y;um("ms"); // Calculate square root of (R^2 - y^2) return od; // Return the resulting value. } // "mv12"'s equation vertically is (y = tan x) or (x = atan y) else if (cs=="mv12") { // If third assembly: js="atan";od=y;um("mt"); // Get (atan y) return od; // Return the resulting value. } // "mv11"'s will use default equation else return base.GetX(y,radius); // Else return default value } //------------------------------- The GetRadius() method --------------------------------- public override double GetRadius(double angle,double radius,double y) { // "mv10" equation horizontally is (r = 0.5*R*(1 + sin 8a) if (cs=="mv10") { // If first assembly: od=8*angle;js="sin";um("mt"); // Get sin 8a return (0.5*radius*(1+od)); // Return 0.5*R*(1 + sin 8a) } else if (cs=="mv11") { // If second assembly: if (angle>270) return radius; // return r=R for angles over 270 degrees else return 0; // and r=0 for angles in the range (0:270) } // "mv12" equation horizontally is (r = 0.5*R*(1 + sin 6a) else if (cs=="mv12") { // If third assembly: od=6*angle;js="sin";um("mt"); // Get sin 6a return (0.5*radius*(1+od)); // Return 0.5*R*(1 + sin 6a) } else return radius; // Default: Return r=R } } ---------------------------------------------------------------------------------------------- COMPILING: Use tool "pcp" or "pcpr" as usual. Make sure to keep the commented statement "//assembly ms1.exe;" at the top and also to keep the commented word "pcs3" at the class declaration line since both are required by the compiling tools. For more details read the text titled "Compiling" which follows example 1. COMMENTS: --------- (1) The vertical equation GetX() does a partial modification of the radius while the horizontal equation GetRadius() is the one which does the final job. In fact, you can eliminate GetX() and do both jobs into GetRadius() If you eliminate GetX(), the value of (radius) which equation GetRadius() receives will always be the original radius of enclosing circle (od/2) (2) Why does method GetRadius() receive (y) among its parameters? This value has not been used in the previous example, but it could have been necessary to condition with or even to use in the calculations if the 3D shape was more complicated or if we like to eliminate GetX(). (3) Method GetX() uses Cartesian coordinates while method GetRadius() uses Polar. To illustrate this, consider that you are drawing a sphere which requires drawing a circle in both horizontal and vertical directions. Here are the required methods in this case: public override double GetX(double y,double radius) { // Equation of a circle using Cartesian coord's is: x^2 + y^2 = R^2 od=radius*radius-y*y;um("ms"); // Calculate square root of (R^2 - y^2) return od; // Return the resulting value. } public override double GetRadius(double angle,double radius,double y) { // Equation of a circle using Polar coord's is: r=R return radius; } ==============================================================================================


=============================================================================================== Drawing on the surface of a 3D Assembly ======================================= (For versions 3.06 and higher) When we have been using level I, we have been allowed to use only one color for the outside surface of the entire 3D figure since this is how the WPF 3D module is set. However, we have been able to use the visual brush to draw a picture on a 3D object. Example 8 has shown how to draw on a Rectangular block's surface. This has not been difficult since the drawing surfaces have been flat and also since PC# automatically supplies the "Texture coordinates" for the Rectangular Block surfaces which are required. Drawing on a curved surface using level I is very hard. It requires supplying the "Normals" which are vectors that tells the WPF how surfaces are curved in addition to Texture coordinates, point coordinates and mesh data. PC# has finally developed a software which allows you to draw on any surface of a Level II 3D Assembly which is easy to use, requires none of the data required by level I and runs very fast. How to draw on a general 3D surface? ------------------------------------ You make the visual brush a temporary Graphical Output Device, draw anything you want on it before you call method vm("vcp"), vm("vce"), vm("vch") or vm("vcm") to draw the 3D figure. You also include the wanted size of the image to be drawn and its center location. Here are the three additional required parameters: -------------------------------------------------- lf,of and kf are required only if you have prepared a visual brush in advance which contains drawings that you like to transfer to 3D Assembly's surface. lf,of: Desired Width and height of the image to be drawn on the 3D assembly after being extracted from the visual brush. If no values are assigned to (lf,of), V. brush image will be neglected. kf : Vertical position of image's center relative to center of 3D Assembly's surface. Can we draw on the top and bottom surfaces of a 3D Assembly also? ================================================================= Yes. This is actually the easier one. When a 3D Assembly is created, the top and bottom unit cylinders' 3D objects are archived. You can easily get the keynames used and call method vm("sm") which sets material type and color. The 2-char name of Assembly Top is "at" and the 2-char name of the Assembly Bottom is "ab". The digits used for the keynames are the same as the ones in the keyname which you have assigned to the Assembly when you called its creation method. For example if you have supplied the keyname "cs=mv25" when you created it, then: Assembly Top's Keyname is "at25" and Assembly Bottom Keyname is "ab25". Now you can change the color of the Assembly top to red with: cs="at25";cls="s9r0";vm("sm"); Remember that this color setup method allows you many options for color setting. See "PC# Public Methods" to know them all. The most interesting of them is the "Visual Brush". You can draw anything on it then call: cs="ab25";ks="v";vm("sm"); to display the image on Assembly's bottom. ============================================================================================== Example 22: Draw a variety of drawings and images on the visual brush, then transfer them to Level II 3D Assembly side surfaces. Also draw something on the top and bottom surfaces of one of them. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=22;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";cis="Ellipsoid Ellipsoid Cylinder ";os="s"; ds="nw";lf=560;of=250;fns="crb14";cm("i"); // Vewport3D to be used as Graphical out device } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. //-------------------------------- Drawing the Jewel ------------------------------------ gm("sdb"); // Set v brush as the Graphical output device cls="y3";gm("sps"); // Create yello brush id=od=195;gm("crf"); // and paint background with it. //-------------------------------- Drawing the object ----------------------------------- id=od=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. id=6;od=50;gm("c="); // Create hexagon shape object at center. of=1;cls="r0";ks="r";gm("grs"); // Draw the object using sp effects-refl at of=1;jd=45;cls="b0";ks="r";gm("grs"); // center in red, then repeat 6 times using of=1;jd=-45;cls="b0";ks="r";gm("grs"); // different colors and different locations of=1;jd=22;kd=40;cls="g0";ks="r";gm("grs"); of=1;jd=-22;kd=40;cls="m0";ks="r";gm("grs"); of=1;jd=22;kd=-40;cls="m0";ks="r";gm("grs"); of=1;jd=-22;kd=-40;cls="g0";ks="r";gm("grs"); id=od=25;gm("ce"); // Create a circle at center (pearl) for (int x=0;x<20;x++) { // Draw it 20 times using sp effects-reflection jd=80;kd=18*x;kb=true; // at locations around the plate. Polar coord's of=1;cls="p0";ks="r";gm("grs"); // are used for specifying locations } gm("fdb"); // Finish drawing to v brush VisualBrush vb1=vbp; // Persist V Brush object by making 2nd reference // for later use. // Draw a full Ellipsoid with minor axis vertical into "mv10". Move it to the left. // Notice that we have assigned value to (kf) and rotated the ellipsoid in order to give you // a better view of the drawing. The reason for finding that necessary, is that the camera // views the object at an angle from the top front right corner. Also, moving the object by // (-300,-125,50) causes it to move horizontally from the center to where it should be. cs="mv10";cls="g3y3";id=40;jd=100;kd=70;ks="-70,70,0";kf=20;lf=150;of=90;vm("vce"); cs="mv10";ks="1,0,0";ad=130;vm("st");cs="mv10";vm("vt"); cs="mv10";ks="0,1,0";ad=65;os="-300,-125,50";vm("st");cs="mv10";vm("vt"); // Draw a full Ellipsoid with major axis vertical into "mv11". Keep it at center. gm("sdb"); // Set the visual brush as the gr out device fls="images\\flower.jpg"; // Name of Image file to be drawn id=100;od=150;gm("cid"); // Draw image on v brush after scaling it gm("fdb"); // Finish v brush drawing operation. cs="mv11";cls="b3S9";id=40;jd=50;kd=100;ks="-100,100,0";jf=0;kf=40;lf=100;of=150;vm("vce"); cs="mv11";ks="0,1,0";ad=45;vm("st");cs="mv11";vm("vt"); // Draw a cylinder into "mv12". Move it to the right. gm("sdb"); // Set the visual brush as the gr output device fls="images\\icon.bmp"; // Name of Image file to be drawn id=80;od=40;gm("cid"); // Draw image on v brush after scaling it to fit rect gm("fdb"); // Finish v brush drawing operation. cs="mv12";cls="s4g4";id=40;od=80;ks="-50,50,0";kf=0;lf=80;of=40;vm("vcm"); cs="mv12";ks="0,1,0";ad=20;os="135,45,-60";vm("st");cs="mv12";vm("vt"); cs="mv0";vm("vc"); // Create a new 3D Visual. cs="mv0";ks="mv10";vm("va"); // Add all cs="mv0";ks="mv11";vm("va"); // 3 assemblies cs="mv0";ks="mv12";vm("va"); // to it. vbp=vb1;ks="v";cs="at12";vm("sm"); // Use image of mv10 to set color of top and vbp=vb1;ks="v";cs="ab12";vm("sm"); // bottom unit cylinders of assembly mv12 cs="mv0";gm("fdv"); // Finish drawing to Graphical Output Device } // using data stored into "mv0". } --------------------------------------------------------------------------------------------- COMMENTS: ========= (1) Notice that both method GetX() and GetRadius() have not been included despite that method vm("vcm") has been used to create assembly "mv12". This is because the default output of the two methods has been all that was necessary to create the assembly. ==============================================================================================


=============================================================================================== SAVING 3D ASSEMBLY DATA INTO FILE ================================= As we have mentioned before, using equations to create a 3D Assembly makes the process easy and fast, but this has not been our final goal. We like the length of each radius which defines a sector in a unit cylinder to be stored into an array of type "double" which defines the 3D Assembly. The array can then be saved into a table file and the file can be read back at any time to reconstruct and redraw the 3D Assembly. This should clear our way for creating a software which allows "Sculpture" artists to do their job on their computer screens, then one day somone will invent a machine which can produce the products they make by reading their final 3D assemblys' data files and cutting materials accordingly. The artists are not going to be using equations. They are not going to be modifying file data in the way we do it either. So, someone should write a graphical interface which allows them to use a graphical representation of their tools to do the job. In the meantime, behind the seen, the software should be modifying the file data according to each cut they make. Saving data into file: ====================== To start with, we like to be able to save any Level II 3D assembly data into a "table file". Table files have been explained in the chapter of "Filing". So, you may like to review that chapter before you continue. Saving an assembly after being created using method vm("vcd"), vm("vce"), vm("vch") or vm("vcm") requires the addition of the following parameters: fls: You should assign the 3D Assembly data file name to (fls) We recommend the extension "dta" for the file. oc : You must make the assignment (oc='f') meaning "File the assembly after creating it". The table file created will contain as many rows as the specified Y-range (in ks) requires and as many columns as the specified number of sectors (in id) requires. Each data row of the file contains the sector data of one unit cylinder. The file header contains the 3D Assembly's constants which are color code (cls), material code (js), number of unit cylinders (ks) and number of sectors (id) In the next example, we are going to save the data of the middle Ellipsoid of example 22. Here is a list which shows first 10 rows and first 12 columns of the resulting file: ----------------------------------------------------------------------------------------------- Color Code: b3S9 Material: Unit Cylinders: 200 Sectors: 40 0 1 2 3 4 5 6 7 8 9 10 11 ======= ======= ======= ======= ======= ======= ======= ======= ======= ======= ======= ======= 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 7.05 7.05 7.05 7.05 7.05 7.05 7.05 7.05 7.05 7.05 7.05 7.05 9.95 9.95 9.95 9.95 9.95 9.95 9.95 9.95 9.95 9.95 9.95 9.95 12.16 12.16 12.16 12.16 12.16 12.16 12.16 12.16 12.16 12.16 12.16 12.16 14.00 14.00 14.00 14.00 14.00 14.00 14.00 14.00 14.00 14.00 14.00 14.00 15.61 15.61 15.61 15.61 15.61 15.61 15.61 15.61 15.61 15.61 15.61 15.61 17.06 17.06 17.06 17.06 17.06 17.06 17.06 17.06 17.06 17.06 17.06 17.06 18.38 18.38 18.38 18.38 18.38 18.38 18.38 18.38 18.38 18.38 18.38 18.38 19.60 19.60 19.60 19.60 19.60 19.60 19.60 19.60 19.60 19.60 19.60 19.60 20.73 20.73 20.73 20.73 20.73 20.73 20.73 20.73 20.73 20.73 20.73 20.73 ----------------------------------------------------------------------------------------------- As you can see, all data are rounded to two decimal digits. All data making one row are the same. This is because that particular object was an "Ellipsoid" which has a circular cross section, so all radii of each unit cylinder are equal in length. Reading the 3D assembly data back: ================================== Method vm("vcf") Creates a 3D Assembly by reading the data file which represents the assembly. It requires the following parameters: fls : 3D Assembly data file name. cls : (Optional) If you supply (cls=""), the original color code which was saved into file will be used. The data saved into file is for the 3D Assembly only. It does not include information about any image or 2D drawings which you may have drawn on the assembly's surface when the assembly was saved. If you like to draw the same image or a new one on the surface of the reconstructed assembly, you need to create the visual brush which contains the image as usual and add (kf,lf and of) to the parameters of method vm("vcf") -------------------------------------------------------------------------------------- We have upgraded this example recently to satisfy the requirements of Personal C Sharp version 4.1. The new assembly files can handle hollow assemblies and are common to all Personal C Sharp classes. -------------------------------------------------------------------------------------- =============================================================================================== Example 23: Recreate the Ellipsoid of last example which has been painted with the image of the "flower.jpg" file and save its data this time into file "temp.scu". Read the data back, reconstruct the assembly and draw it back using a different color and a different drawing. =============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { dna=0.112; // Reduce density to 40 sectors/unit cylinder base.init(); } public override void setup() { PageNumber=23;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ------------------------------------- cns="gr3"; // gr3 is not divided. cs="vp1";os="s"; cis="3D Assembly saved into file 3D Assembly read back from file"; ds="nw";lf=560;of=250;fns="trb14";cm("i"); // Vewport3D to be used as Graphical out device } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. //------------------------------- Creating an Ellipsoid -------------------------------- fls="@1";of=100;o=100;gm("3cc"); // Create startup assembly as A3D #1 o=1;ks="0,50,50,100";ad=360;os="ec";gm("3RQ"); // Replace entire assembly with an ellipsoid //--------------- Creating Visual Brush to paint "Original" Assembly with -------------- gm("sdb"); // Set the visual brush as the gr out device fls="images\\flower.jpg"; // Name of Image file to be drawn id=100;od=150;gm("cid"); // Draw image on v brush after scaling it gm("fdb"); // Finish v brush drawing operation. //------------------ Drawing original assembly then saving it into file ---------------- cs="mv10";fls="@1";cls="b3S9";kf=10;lf=50;of=70;gm("3rd"); js="1.5,1.5,1.5";ks="0,1,0";ad=60;os="0,55,120";vm("st"); cs="mv10";vm("vt"); // Position Assembly using transform fls="temp.scu";o=1;gm("3SF"); // Save A3D #1 into file "temp.scu". //-------------- Creating Visual Brush to paint "Read Back" Assembly with -------------- gm("sdb"); // Set the visual brush as the gr out device fls="images\\icon.bmp"; // Name of Image file to be drawn id=100;od=50;gm("cid"); // Draw image on v brush after scaling it gm("fdb"); // Finish v brush drawing operation. //--------- Reading assembly data back, reconstructing and drawing the assembly -------- fls="temp.scu";o=2;gm("3LF"); // Load "temp.scu" data into A3D no 2. cs="mv11";fls="@2";cls="b3c3";kf=10;lf=50;of=25;gm("3rd"); cs="mv11";js="1.75,1.75,1.75";ks="0,1,0";ad=30;os="90,5,-60";vm("st"); cs="mv11";vm("vt"); // Position Assembly using transform cs="mv0";vm("vc"); // Create the mother assembly. cs="mv0";ks="mv10";vm("va"); // Add the cs="mv0";ks="mv11";vm("va"); // 2 assemblies cs="mv0";gm("fdv"); // Finish drawing to Graphical Output Device } } ============================================================================================== Why do we need the 3D Assembly file? ------------------------------------ It's nice to be able to save 3D assemblies into files so we can keep a library of them. We can then use this library to create any 3D scene by adding a number of 3D assemblies to a mother assembly as we have seen in previous examples. But this is not all. 3D Assemblies are costy. In the past examples, we have been drawing objects which do not contain plenty of details, so we have had no speed problem. A hexagonal cylinder could be made of 100 unit cylinders, each of them divided into 6 sectors. This is very light. A circular cylinder or an ellipsoid requires dividing each unit cylinder into 40 sectors which requires more processing time, but still light. In example 21, we needed more details so we divided each unit cylinder into 360 sectors. That could have been the time you started to feel some speed problems. We cannot be limited to what we have so far. We need to advance further and this is going to make the processing time a burden. Here is what we like to do: (1) We like to create hollow 3D assemblies. So far, we have been drawing a drinking cup and an umbrella as if they were statues made of stone. This is quite embarassing. These objects must be made as thin as they should be. Drawing a hollow Assembly costs about twice the processing time which a solid assembly costs to draw. (2) We like to be able to use equations to draw objects as we did in example 21, except that we will not be limited to one equation in each of the horizontal and vertical directions. Multiple equations must be used in each direction in order to create more complex objects. (3) We like to be able to draw objects which require more details like complex machines, animals or people. This requires high density drawing which considerably slows down the operation. (4) We like to be able to modify unit cylinders and sectors of an assembly after the assembly has been created. This requires archiving all 3D objects which also adds to processing time. We are depending on the Assembly file to help with reducing processing time. After an assembly file is made, most work can be done by merely modifying file data without having to access 3D objects. At the end, the assembly can be drawn to the graphical output device with all the modifications made. Assembly files are table files and as you know, table files work very fast since all data is in the RAM, so you don't have to access disk when you write to them. You can also access the archived data array directly without having to call fm("r") or fm("w") to read or write. Compatibility with class (pcs)'s graphics module: ================================================= 3D Assembly files can link all PC# classes. This means that a file made by class (pcs3) using the Windows Presentation Foundation (WPF) can be read by class (pcs) and the object can be drawn on the default graphical output device. Class (pasp) can read the file and send its drawings anywhere accross the web. Although class (pcs3) does better shading and produces more realistic 3D figures when the assembly is drawn, class (pcs) contains more tools which makes it the better in creating and modifying the 3D assembly. So, let us get the older graphics module to the same point the new one has reached before we get further here. So, return back to the chapter of "Drawing" and see the examples starting by Example 1 of "Drawing II". ==============================================================================================


=============================================================================================== A NEW GENERATION OF 3D ASSEMBLIES (For version 4.1 and later) ================================= As promised, we have returned to class (pcs)'s graphics and added the new chapter "Drawing II" where 3D assemblies are created and drawn using the high quality graphics of the .NET version 2. The new 3D assemblies of class (pcs) are superior compared to the ones which we have seen here so far. They are capable of drawing hollow objects, can use built in equations of horizontal and vertical cross sections, can draw from sketches and more capable of creating objects which contain plenty of details. It's now the time to upgrade class (pcs3) to reach the same level. Despite the higher quality of class (pcs)'s graphics, it has the following limitations if compared with the 3D graphics of the Windows Presentation Foundation: (1) SHADING: It does not have the ability to show the 3D objects as well since it lacks the ability to create the shades necessary. We have developed a simple shading method which works fine with simple objects only. We have also used the "Rainbow" coloring method which can show details with great clarity except that it does not keep the natural look of objects. (2) TRANSFORMATION, ANIMATION AND SLOW MOTION: All these features are not available in our 3D drawings of class (pcs) They are left for the WPF to do. Creating the new generation of 3D Assemblies: ============================================= We use class (pcs) to create the 3D Assembly file, then draw the file at class (pcs3) The modes of method gm() which are used in class (pcs) to create and modify 3D Assemblies are: 3rc USE: Read 3D Assembly's constants from its file. 3wc USE: Create a new 3D Assembly file and write these constants into it. 3cc USE: Create 3D Assembly file for a cylinder. 3rd USE: Read 3D Assembly file and draw its content. 3LF USE: Load Assembly file's data into an A3D. 3AF USE: Modify A3D data in order to flatten an area on the assembly's surface and optionally to add thickness to the flat area positively or negatively. 3AS USE: Modify A3D data by adding half a sphere to it. 3AE USE: Modify A3D data by adding half an Ellipsoid to it. 3AM USE: Modify A3D data by adding a sub-assembly which you specify its specs vertically and 3RS USE: Modify A3D data by replacing an area with a section of a sphere. 3RE USE: Modify A3D data by replacing an area with a section of an ellipsoid. 3RM USE: Modify A3D data by replacing an area with a sub-assembly which you specify its specs vertically and horizontally in the two method GetX() and GetRadius() 3RQ USE: Modify A3D data by replacing an area of main assembly with a figure which is defined by its vertical and horizontal cross sections with 2-char code in (os) 3RA USE: Replace a section of main-assembly with a section of sub-assembly. 3SF USE: Save A3D data into file. We have made these modes available in class (pcs3) without a change. Actually, when you call method gm() of class (pcs3) at any of these modes, your call will be executed with an instance of class (pcs) which class (pcs3) keeps. The only exception is method gm("3rd") which draws the assembly using the WPF. Its parameters are as follows: Parameters of class (pcs3)'s drawing method gm("3rd"): ====================================================== cs 3D Assembly keyname. fls 3D Assembly file name. If you like to draw from an A3D instead of a file, make the assignment (fls="@n") where (n) is the A3D number. jd,kd,ld Position of object's center relative to graphical output device's center. cls Dual Color code. If (ks="s"): Second code is the background color of front material and first code is the background color of the back material. If (ks="l" or "r"): The two colors are the start and end colors of the gradient brush. ks Paint brush type. Can be (s/l/r/t/v) meaning (Solid/Linear/Radial/Texture/Visual) js Material type code. Can be (d/e/s) meaning (Diffuse/Emissive/Specular) def="d" If you like to draw on the Assembly's surface: ---------------------------------------------- Apply your drawings to the Visual Brush then add the following parameters to gm("3rd"): lf,of Wanted Width and Height of image on Assembly's surface. kf Vertical position of image relative to assembly's center. REMARK: ======= If the value of either (lf) or (of) was zero, the method assumes that you're not interested in drawing on the surface of the Assembly. Some difficulties: ================== When we tried to draw the 3D Assemblies of the first example in the chapter "Drawing II", we met the following difficulties: (1) The transparent glass cup showed with some unusual shades which we could not find an explanation for. (2) The plate did not look like something we can recognize. It looked like a flat elliptical shade. We could improve the plate look by using a radial gradient brush to paint it with. We also used a non-transparent cup to replace the glass one. If you like to get the transparent cup, try using color code "s61s61". It seems to bring the best results. And some new advantages: ======================== But, if we forget about the difficulties, we can now use 3D transforms to position the objects as we like. We can also apply animation and slow motion to them. When we made the 3D Assembly of a doughnut, we set it in the position which makes the math necessary as simple as possible. Now, we can read the file and set the doughnut at any position we like. This will be demonstrated in the next example. ============================================================================================= Example 24: Duplicate example 1 of "Drawing II" using non-transparent cup and using radial brush to draw the plate. Show how we can reposition the doughnut after being drawn using transforms. ============================================================================================= //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { base.init(); } public override void setup() { PageNumber=24;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr4 is not divided. cs="vp1";cis="\n3D Graphics";os="s";ds="c";lf=560;of=250;fns="trb16";cm("i"); } public override void run() { cs="vp1";gm("sdv"); // Set (im1) as graphical output device. //------------------------------- Creating Assembly Files -------------------------------- //----- Creating the cup ----- // The cup is a sloped hollow cylinder. fls="cup.scu";lf=40;of=100;o=100;jd=0.82d;ib=true;i=4;id=2;gm("3cc"); //----- Creating the orange juice ----- // The orange juice inside the cup is a solid sloped cylinder. fls="juice.scu";lf=40;of=100;o=100;jd=0.82d;gm("3cc"); //----- Creating the plate ----- // The plate is a wide sloped hollow cylinder fls="plate.scu";lf=40;of=150;o=20;jd=0.5d;ib=true;i=6;id=6;gm("3cc"); //----- Creating the Doughnut assembly ----- // Doughnut constants: fls="doughnut.scu";int o1=40; // File name, Number of unit cylinders float o1f=100,l1f=40; // Diameter of encl circle at center, no of sides bool i1b=true;int i1=0; // Assembly is hollow from bottom to top. string r1s; // Radius // Creating doughnut file and writing constants, then opening it as a table file: o=o1;lf=l1f;ib=i1b;i=i1;gm("3wc"); // Write header fs="tf0";fm("o"); // Open as table file //---- Outer Surface ---- for (int u=0;u<=o1;u++) { // Scanning (o+1) u cyl's and writing their data: OS=new string[(int)l1f]; // Initiating field data array OS[] // Notice that the circle equation (X^2 + Y^2 = R^2) requires circle center to be at origin // Since (u=0) means bottom (not center), we should replace (Y) with (u-o1/2) od=20*20-(u-o1/2)*(u-o1/2);um("ms"); // 2nd component of (x) (discussed above) od=0.5*(double)o1f+od; // Adding 1st component to (x) ib=true;om("fd");r1s=os; // Convert to 2 dec digits string, assign to (r1s) for (int s=0;s< l1f;s++) OS[s]=r1s; // Assign (r1s) to all rows of OS[] fm("wn"); // Write each u cyl data as new row into file } //---- Inner Surface ---- for (int u=0;u<=o1;u++) { // Scanning (O+1) u cyl's and writing their data: OS=new string[(int)l1f]; // Initiating OS[] od=20*20-(u-o1/2)*(u-o1/2);um("ms"); // 2nd component of (x) (discussed above) od=(double)(o1f/2)-od; // Adding 1st component to (x) (notice the (-) sign) ib=true;om("fd");r1s=os; // Convert to 2 dec digits string, assign to (r1s) for (int s=0;s< l1f;s++) OS[s]=r1s; // Assigning (r1s) to all rows of OS[] fm("wn"); // Write data following outer surf data. } fm("c"); // Close file. //----------------------------------- Drawing 3D objects --------------------------------- // All drawings start at center. We have assigned values to (jd,kd,ld) to seperate them // then we used transforms to set them exactly where they should be. cs="mv10";fls="juice.scu";jd=46;ld=-48;ks="l";cls="y0o0";gm("3rd"); js="0.94,1.2,0.94";os="0,20,-20";vm("st");cs="mv10";vm("gt"); cs="mv11";fls="cup.scu";jd=45;ld=-45;ks="s";cls="b09s89";gm("3rd"); js="1,1.5,1";os="0,20,-20";vm("st");cs="mv11";vm("gt"); cs="mv12";fls="plate.scu";jd=-60;ld=60;ks="r";cls="y3Y3";gm("3rd"); os="0,-30,0";vm("st");cs="mv12";vm("gt"); cs="mv13";fls="doughnut.scu";jd=20;ld=-20;ks="l";cls="o0O5";gm("3rd"); ks="2,0,1";ad=45;os="-20,0,50";vm("st");cs="mv13";vm("gt"); cs="mv0";vm("vc"); // Create the mother assembly. cs="mv0";ks="mv10";vm("va"); // Add all cs="mv0";ks="mv11";vm("va"); // 4 assemblies cs="mv0";ks="mv12";vm("va"); // to it. cs="mv0";ks="mv13";vm("va"); cs="mv0";gm("fdv"); // Finish drawing to Graphical Output Device } } ============================================================================================== Comparing method gm("3rd") of class (pcs) with same method of class (pcs3): --------------------------------------------------------------------------- (1) Both methods are used for drawing a 3D Assembly. One draws it on (bio) using class (pcs)'s 2D drawing methods and the other draws it on one of class (pcs3)'s graphical output devices using the Windows Presentation Foundation. (2) The first method positions the object's center relative to Form's center using the 2D coordinates (jf,kf). The second one does the positioning using the 3D coordinates (jd,kd,ld) (3) The first method scales the object as it draws it. The scale factors are (jd,kd) The second one does not scale it while drawing. A scale transform can be applied after drawing the object. (4) After the first method has done its job and the 3D Assembly has been drawn, nothing else can be done since all objects have been disposed. With the second method, things are different. The WPF keeps all objects, so you can do any transformation, animation or slow motion after the object has been drawn. Remember that drawings don't show up on the screen until method gm("fdv") is called to finish the drawing operation. =============================================================================================


=============================================================================================== LEVEL II 3D GEOMETRY OBJECTS (For version 4.1 and later) =========================== If you remember how the 3D Assembly idea started, you know that originally, we have been drawing Level I 3D objects which have been represented by the GeometryModel3D (gmp) We have been required to supply point coordinates, mesh data, texture coordinates and normals. For this reason, we have been limited to drawing simple objects in addition to Rectangular blocks, Cylinders and Pyramids. Later, we have developed Level II 3D Assemblies which are represented by the ModelVisual3D object (mvp) which have allowed us to draw more sophisticated 3D figures without the need to supply any of the data mentioned above. The 3D Assemblies can be saved into files which are common to all classes of Personal C Sharp. Which means that they can be used to draw 3D figures using any of the .NET versions starting with version 2 and they can put the drawings on the Web. Now, we have more good news. We are now able to read the 3D Assembly file into the original GeometryModel3D (gmp) Which means that we can create and draw a new kind of GeometryModel3D objects which do not require supplying point coordinates, mesh data, texture coordinates or normals. Let us call them "Level II 3D Geometry objects" or "Level II 3D objects". How to read the 3D Assembly file as a Level II 3D object? --------------------------------------------------------- All you need to do is to either supply a GeometryModel3D keyname when you access method gm("3rd") or supply no keyname. Everything else is the same. See next for more details. Comparing Level II objects with 3D Assemblies: ---------------------------------------------- (1) Both do not require supplying point coordinates, mesh data, texture coordinates or normals. (2) "Rainbow Coloring" can be used with 3D Assemblies only. You can't use them with Level II 3D objects. (3) Level II 3D objects could be lighter and may run faster than 3D Assemblies. (4) Some of the unexplainable shading problems which happen sometimes with 3D Assemblies could be improved if you read the assembly file as a level II 3D object. (5) 3D Assemblies require keynames to identify them ("mv0":"mv89") LevelII 3D objects are treated in this regard as Level I objects. Keynames are not necessary unless you intend to make them clickable, to animate them or to apply slow motion to them. Their keynames are in the range ("g30":"g389") (6) 3D Assemblies require a mother assembly to contain them. Level II objects do not. Working with A3D's instead of files: ==================================== 3D Assembly files are valuable since they allow us to shorten our programs. One program may create an assembly and save it into file then all other programs can use the file without the need to create the same assembly again. In some cases, like when the assembly is for temporary use purpose or when it's created to be used over the internet, files are better be eliminated. For this reason most of the methods which operate on files and require supplying the file name assigned to (fls), accept supplying an A3D number to (fls) instead. In this case, the same operations will be performed on the A3D. If you like to access one of those methods to do an operation on an A3D, make the assignment: (fls="@n";) where 'n' is the A3D number. About the next example: ----------------------- We like to repeat the previous example with the following changes: (1) We'll use A3D's only. No files. (2) Data in the A3D's will be processed as level II 3D objects (3) We'll replace the cup with a Champagne Glass. This should make a nice practice for the people who like math. (4) The orange juice will be replaced with wine. ============================================================================================= Example 25: Repeat previous example using A3D's instead of files. Process all objects as level II 3D objects and replace the cup with a Champagne Glass. ============================================================================================= //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" public override void init() { dna=0.112; base.init(); } public override void setup() { PageNumber=25;base.setup(); // Execute master class's setup() supplying it with page no. //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr3 is not divided. cs="im1";cis="\nLevel II 3D Objects";os="s";ds="c";lf=560;of=250;fns="trb16";cm("i"); } public override void run() { cs="im1";gm("sdi"); // Set (im1) as graphical output device. //------------------------------- Creating Assembly Files -------------------------------- //---------- Creating a Champagne Glass ---------- // Glass constants: int o1=100; // Number of unit cylinders float o1f=100,l1f=40; // Diameter of encl circle at center, no of sides fls="@1";o=o1;gm("3wc"); // Create the new A3D #1 with no. of ucyl's=100 for (int u=0;u<=o1;u++) { // Scanning (o1+1) u cyl's and writing their data: // BOTTOM BASE OF THE GLASS: Hight=10 Pixels // Vertical CS in +ve X-direction is made of a line which intersects Y-axis at 10 pixel and // X-axis at 40 pixels. Its equation should be: y=-(b/c)*x+b where b=10,c=40 So x=4*(10-y) if (u<10) { // If in bottom base's range: od=4*(10-u); // Apply this section's equation. } // MIDDLE BAR: Made of a circular cylinder, 40 Pixels high and 10 Pixels wide // Vertical CS in +ve X-direction is made of a vertical line 5 pixels away from Y-axis. else if (u<50) od=5; // If in middle bar's range apply equation. // TOP SECTION: Made of half a sphere. // Vertical CS equation is: x^2 + (100-u)^2 = 50^2 else { // Else if in top section's range: od=50*50-(100-u)*(100-u);um("ms"); // Apply this section's equation. } // Horizontal CS's of all sections are circles. So all sector data are the same for (int s=0;s< l1f;s++) { // Scan all sectors A3D[1][u][0][s]=od; // Assign (od) to all outer surface data. if (u>=50) A3D[1][u][1][s]=od-2; // and (od-Thickness) to all inner surface data. } } //---------- Creating the drink ---------- // The drink inside the glass is a solid object of same shape as its container. // Drink constants: o1=45; // Number of unit cylinders o1f=96;l1f=40; // Diameter of encl circle at center, no of sides fls="@2";o=o1;gm("3wc"); // Create the new A3D #2 with no. of ucyl's=45 for (int u=0;u<=o1;u++) { // Scanning (O+1) u cyl's and writing their data: od=50*50-(50-u)*(50-u);um("ms"); // Prepare same equation as for the glass top sectn for (int s=0;s< l1f;s++) { // Assign (od-2) to all sectors for outer surface A3D[2][u][0][s]=od-2; // only. } } //---------- Creating the plate ---------- // The plate is a wide sloped hollow cylinder fls="@3";lf=40;of=150;o=20;jd=0.5d;ib=true;i=6;id=6;gm("3cc"); //---------- Creating the Doughnut assembly ---------- // Doughnut constants: o1=40; // Number of unit cylinders o1f=100;l1f=40; // Diameter of encl circle at center, no of sides fls="@4";o=o1;gm("3wc"); // Create the new A3D #4 with no. of ucyl's=40 //---- Outer Surface ---- for (int u=0;u<=o1;u++) { // Scanning (o+1) u cyl's and writing their data: od=20*20-(u-o1/2)*(u-o1/2);um("ms"); // 2nd component of (x) (discussed before) od=0.5*(double)o1f+od; // Adding 1st component to (x) for (int s=0;s< l1f;s++) A3D[4][u][0][s]=od; } // Data at all columns = (od) //---- Inner Surface ---- for (int u=0;u<=o1;u++) { // Scanning (O+1) u cyl's and writing their data: od=20*20-(u-o1/2)*(u-o1/2);um("ms"); // 2nd component of (x) (discussed before) od=(double)(o1f/2)-od; // Adding 1st component to (x) (notice the (-) sign) for (int s=0;s< l1f;s++) A3D[4][u][1][s]=od; } // Data at all columns = (od) //----------------------------------- Drawing 3D objects --------------------------------- // All drawings start at center. We have assigned values to (jd,kd,ld) to seperate them // then we used transforms to set them exactly where they should be. fls="@2";jd=48;ld=-48;ks="l";cls="o3r0";gm("3rd"); js="0.94,1.2,0.94";os="0,50,-20";vm("st");vm("gt"); fls="@1";jd=45;ld=-45;ks="s";cls="s64s64";gm("3rd"); js="1,1.5,1";os="-0,20,-20";vm("st");vm("gt"); fls="@3";jd=-60;ld=60;ks="r";cls="y3Y3";gm("3rd"); os="0,-55,0";vm("st");vm("gt"); fls="@4";jd=-60;ld=60;ks="l";cls="o0O5";gm("3rd"); os="0,-49,0";vm("st");vm("gt"); gm("fdi"); // Finish drawing to Graphical Output Device } } ============================================================================================== Notice that when we created the glass, we made the top part half a sphere in order to simplify calculations. Later we scaled it up vertically to give it the elliptical look which we wanted. ==============================================================================================


=============================================================================================== Making a 3D Scene (For version 4.2 or later) ========================== Most likely, after reading "Comparing Level II objects with 3D Assemblies" you have decided to use the Level II objects since they are lighter and show better on the screen. But there is one problem, the WPF allows only one color to paint each (gmp) object with. Let us return to example 18 in which we drew a patio table with a center umbrella and two cushioned seats. A seat is made of 2 parts, a stand and a cushion. Suppose that you like to create a seat assembly and save it into file so that at any time you can read it back with any of PC# classes and draw it locally or on the web. Should you save it into one file or two? If you save it into one file, you'll have to paint it all with same color. If you save it into two files, you'll have the choice of painting each part with a different color. If you're going to be painting it with "Rainbow" coloring at class (pcs) or class (pasp), you'll probably prefer the one piece assembly since all parts will be painted the same anyway. If you're going to be animating it at class (pcs3), you'll need to have it in one piece except that there is a better choice. You can make the seat in 2 Level II 3D objects, paint them differently and load them all into one 3D Assembly which you can transform or animate as necessary. This new 3D assembly is not an assembly of unit cylinders, it's an assembly of Level II 3D objects. In general, any part of your drawing which you like to set its color independantly, make it clickable or animate it independantly must be represented by a seperate (gmp) or (mvp) object. Additionally any number of objects which you like to animate or apply any other operation to as a group should be added to a container (mvp) type object before the operation is done. About next example: =================== Next example is going to be lengthy but very useful since it shows how to do an entire 3D drawing project. Requirements: ------------- We like to repeat example 18 with the following new features: (1) The umbrella is going to be drawn as a hollow object. (2) We like the user to be able to change the color of any one of the following objects independantly by right clicking on the object: Left Seat Stand, Left Seat Cushion, Right Seat Stand, Right Seat Cushion, Table, Umbrella, Umbrella rod. (3) We like the user to be able to raise the height of either of the two seat assemblies or return it back to normal height by Left clicking the assembly. (4) We like the user to be able to swing the umbrella 45 degrees back and forth by left clicking on it. (5) We like the user to be able to rotate the "table, Umbrella and Umbrella rod" combination by 45 degrees each time he or she left clicks the table or the umbrella rod. (6) We need to save the seat assembly, table assembly and umbrella assembly which we have created into 3 seperate files. The files could be read by other programs in order to regenerate the same 3D scene. (7) We need to create a nested class which extends class (pcs) Within the nested class, the three files will be read and drawn again using the .NET version 2 graphics. Rainbow coloring will be used for this drawing. How to execute required tasks: ------------------------------ (1) Each of the 7 objects whose colors need to be settable by user, will be represented by an independant level II 3D object (gmp) (2) Two 3D Assemblies (mvp) will be created for the two seats. Each of them will contain a set of seat stand and seat cushion. The slow motion operations which raises a seat or return it back will be applied to these two assemblies. (3) The umbrella will be set into a seperate 3D Assembly (mvp) The slow motion operations which swings the umbrella 45 degrees back and forth will be applied to that assembly. (4) One 3D Assembly (mvp) will contain the table and umbrella rod. The 3D Assembly of the umbrella (described in (3)) will be added to this assembly. A slow motion operation will be applied to this assembly in order to rotate the entire group. (5) The seat stand and cushion A3D's will be assembled into a third A3D which will be saved into the file "seat.scu". (6) The table and umbrella rod A3D's will be assembled into a third A3D which will be saved into the file "table.scu". (7) The umbrella's A3D will be saved into file "umbrella.scu". (8) The nested class which extends (pcs) will be similar to the one used in example 1. It'll draw the same 3D scene into a second graphical output device made of the image control (im1) It will use "rainbow" coloring for the entire scene. Selecting density: ------------------ The ellipsoid which is used to make the umbrella is of 8 sides base. If we are to create it using our own math as we did in the previous two examples we would need to have only 8 sectors per unit cylinder. But here we want to use high density general methods like gm("3RQ") and gm("3RA") to create and modify objects. In order for these methods to work correctly with each others, density must be unified for each project. The default density (of 1.00) gives the best quality except that it slows down operations. We have found that a density of (0.25) which means (90 sectors per u cylinder) is the best for this project since it improves speed without a considerable reduction in quality. If you like to use a different density, remember to change (dna) in both the top class and the nested class to the new value since the two classes share the same project. ============================================================================================== Example 26: Draw a patio table with a center umbrella and two seats. Make user able to right click on any part to set its color as he or she desires. Make him or her also able to raise any of the two seats, swing the umbrella or rotate the table-umbrella combination in a slow motion by left clicking. Save the seat, table and umbrella assemblies into files, then read them back from the inside of a nested class to reconstruct and draw the entire seen using class (pcs)'s drawing style. ============================================================================================== //assembly ms1.exe; public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3" string colors="g5 b5 y5 m5 c5 o5 p5 s0 "; // Color codes to be assigned to objects int color=0; // Index of a color code in the string (colors) bool umb=false; // Umbrella "Direction Changed" flag. bool lsb=false,rsb=false; // Left Seat & Right Seat "Raised Up" flags. public override void init() { dna=0.25; // Density: No of sectors/ucyl=360*0.25=90 base.init(); } public override void setup() { PageNumber=26;PageWidth=760;PageHeight=380;base.setup(); // Execute master class's setup() //----------------------------------- gr3 Contents ----------------------------------- cns="gr3"; // gr3 is divided into 2 col's. cs="gr4";j=k=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";cm("i"); cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";cm("i"); //----------------------------------- gr4 Contents ----------------------------------- cns="gr4"; // gr4 is not divided. cs="vp1";cis="Drawings of Class (pcs3)\n";os="s";lf=380;of=370;fns="trb12";cm("i"); //----------------------------------- gr5 Contents ----------------------------------- cns="gr5"; // gr5 is not divided. cs="im1";cis="Drawings of Class (pcs)\n";os="s";ims="";lf=380;of=370;fns="trb12";cm("i"); } public override void update() { if (cs!="md0") return; // If mouse is clicked: bool o1b=ob; // Save received mouse button indicator and double ox1d=oxd,oy1d=oyd; // mouse coordinates temporarely. //----------------- If any object was right clicked (to change its Color) ----------------- if (o1b) { // If clicked mouse button was right one: vm("gh"); // Get (gmp) hit using received mouse coord's cs=os; // Assign returned 3D object keyname to (cs) cls="S9"+colors.Substring(color*3,2); // Assign the color code at index to (cls) js="d";vm("sm"); // Set material (including color) of object. color++;if (color>7) color=0; // increment color code index. Cycle back os=cs="";oxd=oyd=0;return; // Reset received values and exit. } //------------- If any object was left clicked (to perform special operation) ------------- oxd=ox1d;oyd=oy1d;vm("vh"); // Get (mvp) hit using received mouse coord's cs=os; // Assign returned mvp object keyname to (cs) //------ Clicking Left Seat Assembly (to change its height) ------ // Raising seat and returning it back is a "toggle" operation. If (lsb) was reset when user // left clicked the object, seat height is scaled up and (lsb) is set to indicate that the // seat should be returned to normal height when the next click comes. if (cs=="mv10") { // If Object hit was Left Seat's (mvp) if (!lsb) {js="0,1.25,0";os="0,30,0";lsb=true;} else {js="0,0.8,0";os="0,-30,0";lsb=false;} vm("gm"); // Execute height change operation. } //------ Clicking Right Seat Assembly (to change its height) ------ else if (cs=="mv11") { // If Object hit was Right Seat's (mvp) if (!rsb) {js="0,1.25,0";os="0,30,0";rsb=true;} else {js="0,0.8,0";os="0,-30,0";rsb=false;} vm("gm"); // Execute height change operation. } //------ Clicking Umbrella (to change its direction) ------ else if (cs=="mv13") { // If Object hit was Umbrella's (mvp) if (!umb) {ad=-45;umb=true;} // If dir not changed yet, change it, set flag else {ad=45;umb=false;} // Else return it back, reset flag kd=100;ks="1,0,-1";js=os="";vm("gm"); // Execute direction change operation. } //------ Clicking Table Assembly (to rotate it) ------ else if (cs=="mv12") { // If Object hit was Table assembly's (mvp) ks="0,1,0";ad=45;js=os="";vm("gm"); // Rotate it 45 degrees around Y-Axis. } os=cs="";oxd=oyd=0; // Reset received values } public override void run() { cs="vp1";gm("sdv"); // Set (vp1) as graphical output device. //--------------------------- Creating and drawing the Seats ---------------------------- // The seat cushion is a 20 pixel slice taken from the top of a sphere of 150 diameter. // The stand is a hyperboloid of 80 pixels height. The cushion & stand will be of different // colors, so should have different (gmp)'s //------ Making the sphere ------ fls="@1";o=150;gm("3cc"); // Create a startup assembly into A3D #1 o=1;ks="0,75,150,150";ad=360;os="cc";gm("3RQ");// and Make it a sphere //------ Making the Seat Cushion ------ // Create startup assembly of height=20 in A3D #2 and replace it with the top 20 pixels // slice of the sphere. fls="@2";o=20;gm("3cc"); o=2;i=1;ks="0,10,0,20";js="0,140,360,20";ad=360;gm("3RA"); //------ Making the Seat Stand ------ // Create startup assembly of height=80 in A3D #3 and replace it with a hyperboloid. fls="@3";o=80;gm("3cc"); o=3;ks="0,40,0,80";ad=360;jd=kd=10;os="hc";gm("3RQ"); //------ Assembling seat together and filing it to be used by class (pcs)'s drawing ------ // Create startup Assy for full seat. Replace top 20 pxls with cushion and rest with stand. fls="@4";o=100;gm("3cc"); o=4;i=2;ks="0,90,0,20";js="0,10,360,20";ad=360;gm("3RA"); o=4;i=3;ks="0,40,0,80";js="0,40,360,80";ad=360;gm("3RA"); fls="seat.scu";o=4;gm("3SF"); // Save A3D into file "seat.scu". //------ Assembling seat together to be used by (pcs3) drawing. We'll draw the the objects // into (mv10) after positioning at left and into (mv11) after positioning at right. //--- Left Seat (mv10) --- // Create the new (mvp) object. Draw seat cushion at left and seat stand underneath it. cs="mv10";vm("vc"); fls="@2";cs="g30";jd=-100;kd=-110;ld=100;cls="b4b4";gm("3rd"); fls="@3";cs="g31";jd=-100;kd=-160;ld=100;cls="r4r4";gm("3rd"); //--- Right Seat (mv11) --- // Create the new (mvp) object. Draw seat cushion at right and seat stand underneath it. cs="mv11";vm("vc"); fls="@2";cs="g32";jd=100;kd=-110;ld=-100;cls="b4b4";gm("3rd"); fls="@3";cs="g33";jd=100;kd=-160;ld=-100;cls="r4r4";gm("3rd"); //------------------- Creating and drawing the Table and Umbrella rod -------------------- // Table top is a square (an equally sided object of 4 sides) diameter of encl circle=120 // and height=10 pxls. The rest of the table is a hyperboloid stand of height=90 pxls. fls="@5";of=120;o=100;gm("3cc"); // Create Table startup assembly o=5;ks="0,95,120,10";ad=360;id=4;os="r=";gm("3RQ");// Replace top 10 pixels with square cylindr o=5;ks="0,45,0,90";ad=360;jd=15;kd=30;os="hc";gm("3RQ");// Replace rest with stand //------ Umbrella rod ------ fls="@6";of=8;o=120;gm("3cc"); // Create umbrella rod. Diam=8 Height=120 //------ Assembling Table together and filing it to be used by class (pcs)'s drawing ------ // Create Table-Umbrella rod assembly. Replace top 120 with rod and bottm 100 with table. fls="@7";o=220;gm("3cc"); o=7;i=6;ks="0,160,8,120";js="0,60,360,120";ad=360;gm("3RA"); o=7;i=5;ks="0,50,120,100";js="0,50,360,100";ad=360;gm("3RA"); fls="table.scu";o=7;gm("3SF"); // Save A3D into file "table.scu". //------ Assembling Table together to be used by (pcs3) drawing ------ // We'll draw the table and Umbrella rod in different colors. cs="mv12";vm("vc"); // Create the new (mvp) object fls="@5";cs="g34";kd=-95;cls="r4r4";gm("3rd");// Draw table in red fls="@6";cs="g35";kd=15;cls="s9s6";gm("3rd"); // Draw umbrella rod in white. js="1.3,1.3,1.3";os="0,-17,0";vm("st"); // Prepare transform to scale and position cs="mv12";vm("vt"); // table assembly and apply it. //-------------------------- Creating and drawing the Umbrella --------------------------- // The Umbrella is half an ellipsoid. We want it hollow but some u cylinders at top must // stay solid. So, make it all hollow then zero inner surface data of top 6 ucyl's. fls="@8";of=120;o=60;gm("3cc"); // Create a startup assembly and make o=8;ks="0,30,120,60";ad=360;id=8;os="e=";gm("3RQ"); // it an ellipsoid with 8-side base fls="@9";of=120;o=30;gm("3cc"); // Create Umbrella startup assembly and o=9;i=8;ks="0,15,120,30";js="0,45,360,30";ad=360;ib=true;j=0;dd=4;gm("3RA"); // replace it with top half of ellipsoid //--- Zero inner surface data of top 6 unit cylinders --- for(int u=25;u<=30;u++) for(int s=0;s<(360*dna);s++) A3D[9][u][1][s]=0; //------ Save Umbrella into file for (pcs) drawings ------ fls="umbrella.scu";o=9;gm("3SF"); // Save A3D into file "umbrella.scu". //------ Preparing Umbrella for (pcs3) drawing ------ cs="mv13";vm("vc"); // Create the new (mvp) object cs="g36";fls="@9";kd=47;cls="c5c5";gm("3rd"); // Draw Umbrella above center then scale it, js="1.4,1.4,1.4";vm("st");cs="mv13";vm("vt"); cs="mv12";ks="mv13";vm("va"); // Add umbrella's (mvp) to table's (mvp) //----------------- Creating mother Assembly and finishing (pcs3) drawing ---------------- cs="mv0";vm("vc"); // Create the mother assembly. cs="mv0";ks="mv10";vm("va"); // Add all cs="mv0";ks="mv11";vm("va"); // 3 assemblies cs="mv0";ks="mv12";vm("va"); cs="mv0";gm("fdv"); // Finish drawing to Graphical Output Device //-------------------------------- Class (pcs)'s drawing --------------------------------- N no=new N(); // Inistantiate nested class. no.run(); // Run nested class instance. cs="im1";ims="b";cm("su"); // Update Image control with (bio) image. } //------------------------------------- The nested class ----------------------------------- public class N : pcs { // Class (N) extends (pcs) public override void init() { dna=0.25; // Must use same density to use saved files base.init(); } public override void run() { gm("bwo"); // Open a new WPF operation j=380;k=370;cm("fs"); // Reduce size to fit object. cls="y7";gm("ec"); // Make background color matchs page's color. fls="table.scu";o=3;gm("3LF"); // Load table file data into A3D number 3. fls="@3";kf=-10;kd=jd=1.2;cls="r";ad=0;gm("3rd"); // Draw file using scale of 1.2 fls="umbrella.scu";o=4;gm("3LF"); // Load umbrella file data into A3D number 4. fls="@4";kf=75;kd=jd=1.6;cls="r";gm("3rd"); // Draw file using scale of 1.6 fls="seat.scu";o=1;gm("3LF"); // Load seat file data into A3D number 1. fls="@1";jf=-100;kf=-110;kd=jd=0.75;cls="r";ad=0;gm("3rd"); // Draw file at left fls="@1";jf=100;kf=-110;kd=jd=0.75;cls="r";ad=0;gm("3rd"); // Draw file at right gm("bwc"); // Close WPF operation } } } =============================================================================================== CREATING 3D ASSEMBLY FILES USING ASSEMBLY LANGUAGE: =================================================== After you read the chapter of "Boosting PC# speed further by using C and Assembly Sub-Programs", we know that you'll feel that making 3D-Assembly files should have been done in Assembly language in order to maximize speed. We agree and we believe that this can make a good exercise for you. ===============================================================================================