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


DRAWING II 3D ASSEMBLY FILES ================= (for versions 3.2 and later only) The best way to draw complex 3D objects with class (pcs) is by using 3D assembly files. They are common for 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. What are assembly files? ------------------------ An assembly file is a table file which contains the data necessary to draw a complex 3D object. So, we advise you to skip to the "Filing" chapter, learn about "Table Files" before you continue. The assembly file contains two header rows followed with the column titles and the template rows. Data starts at row number 4. Here is a sample from file "doughnut.scu" which you'll be creating in the next example: Color Code: s8S8 Material: d Unit Cylinders: 40 Sectors: 40 First Hollow Unit Cylinder: 0 0 1 2 3 4 5 6 7 8 9 10 11 ======= ======= ======= ======= ======= ======= ======= ======= ======= ======= ======= ======= 50.00 50.00 50.00 50.00 50.00 50.00 50.00 50.00 50.00 50.00 50.00 50.00 56.24 56.24 56.24 56.24 56.24 56.24 56.24 56.24 56.24 56.24 56.24 56.24 58.72 58.72 58.72 58.72 58.72 58.72 58.72 58.72 58.72 58.72 58.72 58.72 60.54 60.54 60.54 60.54 60.54 60.54 60.54 60.54 60.54 60.54 60.54 60.54 62.00 62.00 62.00 62.00 62.00 62.00 62.00 62.00 62.00 62.00 62.00 62.00 The header rows are used to store setup constants for the assembly. To understand these constants let us assume that the 3D figure was a circular cylinder sitting on its round base and you sliced it into round discs each of them is one pixel thick. These discs are what we call unit cylinders. Now, let us assume that you took one disc and drew lines from its center to its circumference each one degree, so you ended by dividing the unit cylinder into 360 triangular sections which we call "sectors". Let us now assume that you cut each of the sector lines (the radii) by a specific amount in order to turn the round disc into a shape which you know and that you saved the radii lengths into file. You repeated your doing to all unit cylinders and then put them back atop each others to reconstruct the original cylinder. The new 3D figure you end with can now be drawn or manufactured by anyone who has a copy of your file. Radii data start at row number 4, immediately following the file template. The first row is for the sectors of unit cylinder number zero which is the bottom unit cylinder. The last row is for the top unit cylinder. Columns represent sectors. So if you have divided each unit cylinder into 360 sectors, the file should contain 360 columns. The sample data above shows only the first 12 columns. You decide how many unit cylinders and how many sectors the 3D object should be divided into. Objects which contain more details like a statue of a person require more divisions or in other words require higher density drawing. In the sample data above, the object was divided into 40 unit cylinders and each unit cylinder was divided into 40 sectors. The color code and surface material are optional since when you draw the object, you may supply new values to overwrite the values read from file. Furthermore, material type is usable only by the WPF which can be accessed by class (pcs3) only, we have no use for it here. Also the color codes required to draw the object using the WPF may not be the same as the ones to use when you draw it here. HOLLOW 3D ASSEMBLIES: When you create a 3D assembly file for a drinking cup, you need to supply data for both the outer and inner surfaces. But as you know, some unit cylinders at the bottom must stay solid, this is why a file which represents a hollow object must contain the constant "First Hollow Unit Cylinder" at header row number 2. If the object was fully solid, we set "First Hollow Unit Cylinder=-1" as an indication. We assume that the top unit cylinder of a hollow object is hollow. If you need to draw a hollow object which is covered at the top draw a lid above it or zero all inner surface data of its top unit cylinder(s) Data of the inner surface immediately follows data of the outer surface. They must be equal in number. A hollow unit cylinder whose inner surface data are all zeros is treated by the drawing method as solid. Discussing some technical details: ---------------------------------- We have mentioned that each unit cylinder is one pixel thick and each sector is bound horizontally by two radii one before it and one after it as we move counter-clockwise starting by the positive Z-axis. Remember that we have defined the Z-axis as the base axis (or the polar axis) of the "xz" plane. Since the sector has two surfaces, top and bottom, then there are 4 radii around each sector. Which of the four radii should we set into the file to represent the sector? We use the last top radius to represent a sector. Horizontally, we should be getting all data. This is because it's a closed loop. If we assume that we have 360 sectors, the first radius of sector zero will not be listed as sector zero's data, but will be listed as sector 359's data. So no data is missing. Vertically, there is a problem. Data concerning the bottom surface of the object will not show into the file. To fix that, we add the bottom surface data at the beginning. So first row of data is not for first unit cylinder, it's for the bottom surface of the first unit cylinder. This means that if number of unit cylinders was 40, we expect to see 41 rows of data for the outer surface which could be followed with another 41 rows of data for the inner surface if the object was hollow. If the object was divided into 40 unit cylinders, unit cylinders number 0,1 and 40 will mean (in order) bottom surface of 1st unit cylinder, first unit cylinder and top unit cylinder. Comparing class (pcs) with class (pcs3) regarding 3D assemblies: ---------------------------------------------------------------- Class (pcs3) uses the WPF which is a sophisticated software which computes the shading necessary for each point on the object based on the light types, postions and intensities. Class (pcs) uses simple shading developed by Personal C Sharp which is enough to make the 3D figure show up but not with plenty of calculations. However, the drawings you make using class (pcs) can be also made by class (pasp) and picked up by any person accross the web using any standard browser. Drawing is done at the server. Only the final image of the 3D figure is what the client can see. The graphics module of class (pcs) contains many tools which class (pcs3)'s graphics module lacks, especially when you like to use images to create your 3D drawings. Our ability to draw objects temporarely on the bitmap object (bip) then draw (bip) on the default graphical output device can also help. Additionally, mixing 2D drawing and text with 3D drawing is much easier in class (pcs) Class (pcs) runs many times lighter than class (pcs3) This is because we dont keep any graphical objects after being drawn on the form when we use this class. The WPF must keep all objects all the time since its main objective is animation. In conclusion, the best option is to use class (pcs) to prepare the 3D assembly files and work on them, then finally use class (pcs3) to draw them and perform any animation, transformation or slow motion necessary. Working with 3D Assemblies: =========================== We can divide the jobs necessary into four: (1) Creating a 3D assembly file. (2) Modifying the 3D assembly file data. (3) Drawing the 3D assembly file content. (4) Adding 3D assemblies together to create a bigger 3D scene. Creating a 3D assembly file: ---------------------------- Assuming that you have already studied table files, you should have no problem creating a 3D assembly file and reading it back. As you know, a table file is not a table file until the template record is written. This means that the top 4 records of a 3D assembly file must be available before you can open it as a table file. The top 4 records can be written using NotePad or programmatically by opening the file as a SAF. You don't need to do it either way. Method gm("3wc") creates a new 3D assembly file for you and writes the assembly constants, column titles and the template into it. It requires the following parameters: fls= Assembly File Name (or full path if not in current folder) o = Number of unit cylinders which you like to divide the object into. lf = Number of sectors which you like to divide each unit cylinder into. js = (Optional) Material code. Necessary only when file is read by class (pcs3) Leave it blank. cls= (Optional) Dual color code. You may keep it blank. ib = Hollow Assembly flag. Assign it (true) if you want a hollow assemby and will write all data concerning its inner surface before attempting to read the file back and draw the assembly. i = Order of first hollow unit cylinder. If you supply (i=4), it means that the 4 bottom unit cylinders (0:3) will stay solid. If (ib=false), (i) will be assigned (-1) automatically. After calling this method, you can open the file as a table file and write your data into it. Similarly, method gm("3rc") Opens an existing 3D assembly file, reads and returns the assembly constants to you. It requires only the file name assigned to (fls) and returns the following: oyf= Number of unit cylinders. oxf= Number of sectors per unit cylinder. os = Material code. cls= Color code. oi = Order of first hollow cylinder. Normally, you don't need to call this method unless you like to modify the file. If you like to read the file and draw the object, call method gm("3rd") which starts by calling method gm("3rc") internally. Drawing the 3D assembly file content: ------------------------------------- Method gm("3rd") reads any 3D Assembly file and draws the object represented by its data. It expects the following parameters: fls = Assembly file path. cls = (Brightest Shade) - (Darkest Shade) dual color code. (jf,kf)= Wanted position of assembly's center relative to form's center. (jd,kd)= Horizontal and vertical scale factors. ad = Rotation angle around vertical axis of the assembly. jb = If you make the assignment (jb=true) the reverse shading process of top surface will be eliminated. ib = If you make the assignment (ib=true) the assembly will be drawn upside down. So, top unit cylinder will be at the bottom and unit cylinder number zero will be at the top. The object's entire body is drawn with linear gradient paint brush using the dual color code which you have supplied. Additionally, the top layer of the object is shaded with a reverse color code. This improves the look af objects which has a flat top surface like a cylinder or a rectangular block. For other objects which has a thin upper layer like spheres and rings, it is always better not to do that last shading. If you like to eliminate top layer reverse shading, make the assignment (jb=true) The parameters show that your ability to postion the 3D assembly object into the page is limited. You can set its center position anywhere into the form with (jf,kf) This is not a 3D positioning, it's based on the form's 2D space. You can scale it by (jd,kd). (kd) sets the height of a unit cylinder in pixels (default=1), (jd) is the scale factor to multiply the data with (default=1) (ad) is the rotation angle. It sets which sector will be facing you when the assembly is drawn. The default is (ad=0) which means that sectors count start at the +ve Z-axis. This also means that sector (0) will be the sector facing you. If you like to do any shearing or different rotation, you can draw it first on (bip), apply the wanted shear or rotation to (bip) then draw it. Writing data into 3D assembly file: =================================== Using table files to write data is too simple to require help from our side. However, in order to save you time we have few methods which can create the most popular assemblies for you if you supply them with the necessary parameters. Creating a cylinder: -------------------- A cylinder is the simplest 3D assembly. Since its unit cylinders are circular, radii of all sectors of each unit cylinder are equal. So each data row is made of same number (which is the radius length) repeated at each column. If the cylinder was straight, we mean not sloped, all unit cylinders will be identical, therefore the entire assembly data will be a repitition of same number. Since we must be able to create sloped and / or hollow cylinders, things can be a little more complicated. So, a method will be helpful. Method gm("3cc") can create a cylinder for you if you supply it with the following parameters: fls= Name of the file which will store cylinder's constants and data. o = Number of unit cylinders. lf = Numbers of sectors per unit cylinder. of = Diameter of enclosing circle for the cross section at the cylinder's center. cls= (Optional) dual color code. ib = "Hollow cylinder" flag. (ib=true) means that your cylinder will be hollow. i = Order of first hollow unit cylinder. id = Thickness in pixels. Meaningful only if (ib=true) jd = Bottom diameter to center diameter ratio. Notice that the thickness is one of the required parameters for a hollow cylinder. This means that this method expects the inner surface of the cylinder you want to be also a cylinder of equal shape and smaller size. In general the inner surface of a 3D assembly does not have to be of the same kind as the outer. Method gm("3cc") starts by calling method gm("3wc") internally in order to create the new file and write the constants and template record. About the next example: ----------------------- We are going to be drawing a glass full with orange juice and a doughnut on a plate by its side. The cup, the plate and the juice inside the cup can all be drawn using method gm("3cc") The cup and the plate are both hollow cylinders while the orange juice is a solid one. The side thickness of the cup is 2 pixels and the botton thickness is 4. For the plate, both numbers are 6. Since the cup is made of glass, it should be partially transparent. So we used the color code "s81S01" to draw it with. For the orange juice we used the code "y0o0". This means that we expect the juice to show through the cup. Notice that we don't have to save these codes into the files since we overwrite them when we draw. CREATING THE 3D ASSEMBLY FILE OF A DOUGHNUT: -------------------------------------------- Y ^ | of=100 | o =40 |+y - -------|------- | / \ | a/ \b o --+------+------+---------> X | \ / | \ / - -------|------- |-y |<--- of ---->| The doughnut is a hollow object. Its outer surface in the +ve X-direction starts with point (+y) and takes the path (b) to the end point (-y) Its inner surface starts with point (+y) and takes the path (a) to point (-y) We are considering that the diameter of the enclosing circle at the center of the doughnut vertically which is assigned to (of) to be the distance between the centers of the two small circles which we see in a vertical cross section of the doughnut which is sketched above. This simplifies calculations. The height of the doughnut or in other words the number of unit cylinders the doughnut is made of (o) is equal to the diameter of each of the two small circles. We are going to be scanning the doughnut vertically from point (-y) to point (+y) which translates to (from unit cyl #0 to #40) and find the value of (x) at each point. For the outer surface, (x) is made of two components: (1) Half of (of) This component is the same for all points. (2) The part which path (b) adds. The amount of this component depends on the value of (y) Since the path is a circle, its value comes from the equation: X^2 + Y^2 = R^2 where (R) is the radius of the small circle which is 20 pixels. So, for the outer surface: x=0.5*of + (20^2-y^2)^0.5 For the inner surface, it should be: x=0.5*of - (20^2-y^2)^0.5 We are using the symbol (^) to denote exponent here. Horizontally, all sector data should be the same since the object is circular. This same data is itself the value of (x) calculated above. ============================================================================================== Example 1: Draw a glass cup full with orange juice and a plate with a doughnut by its side. ============================================================================================== public class a : pcs { public override void init() { base.init(); } public override void run() { cm("fsf"); // Size form to fill the screen gm("dn"); // Halt display //------------------------------- Creating Assembly Files -------------------------------- //----- Creating the cup ----- // The cup is a sloped hollow cylinder. We'll use method gm("3cc") to create it. The first // hollow u cyl from the bottom is #4 and glass thickness at the sides is 2 pixels. // Since cup is made of glass, a partially transparent color is going to be used when we // draw it. We can accept default color for now. 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.85d;gm("3cc"); //----- Creating the plate ----- // The plate is also a wide sloped hollow cylinder which can be done by method gm("3cc") // The plate's thickness is the same at both bottom and sides. fls="plate.scu";lf=40;of=150;o=20;jd=0.5d;ib=true;i=6;id=6;gm("3cc"); //----- Creating the Doughnut assembly ----- // We have no method for this one. So we'll do it here. We'll consider the enclosing circle // diameter to be the diameter of the center circle of the doughnut // 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 --------------------------------- // We are going to draw the orange juice before we draw the cup around it and the plate // before we draw the doughnut above it. We like to halt the display until it's complete in // order to speed up the operation. Display has been halted at the top. fls="juice.scu";jf=240;kd=2.5;kf=-13;jd=1.9d;cls="y0o0";gm("3rd"); fls="cup.scu";jf=240;kd=3;jd=2d;cls="s81S01";gm("3rd"); fls="plate.scu";jf=-125;kf=-65;kd=2.5;jd=2d;cls="y3Y3";gm("3rd"); fls="doughnut.scu";jf=-115;kf=-50;kd=2.5;jd=2;cls="o0O5";jb=true;gm("3rd"); gm("dn"); // Resume Display. } } ============================================================================================== Explaining what we have done mathematically: -------------------------------------------- Earlier, when we have been explaining how to draw the doughnut, We have said: "Horizontally, all sector data should be the same since the object is circular. This same data is itself the value of (x) calculated above". This may have not been detailed enough to satisfy someone who likes mathematical analysis. Actually, this is important to explain in details since most of what we're going to do next depends on it. We draw 3D assemblies which are not too complicated (like a doughnut) using one vertical cross section for the entire assembly and one horizontal cross section for each unit cylinder it contains. The vertical cross section is in the "xy" plane at (z=0) and the horizontal cross sections are in planes which are parallel to the "xz" plane. At the vertical cross section, We have found that the value of (x) of the outer surface at any point is: x=0.5*of + (20^2-y^2)^0.5 Regarding the horizontal cross sections, all what we can tell from this, is the (x,z) coordinates of only one point. At (z=0), (x) is equal to the value obtained from the formula above. Additionally, we know that the shape of the horizontal cross section of each unit cylinder is a circle, but we dont know how much the radius of each circle is. This means that its equation is: (x^2 + z^2 = a^2) where (a) is an unknown constant, Let us assume that the value of (x) for one unit cylinder was found to be (25) using the vertical formula. This tells us that point (x=25, z=0) is a point on the horiz. cross section's circle. Substituting into the circle's equation, we get: 25^2 + 0 = a^2 So: a=25 and the equation is now: (x^2 + z^2 = 25^2) If you'll be using Polar coordinates horizontally, the circle's equation is (r=a) which means that all column data for this unit cylinder in the file should be (25.00) If you'll be using Cartesian coordinates horizontally, you have two unknowns (x,z) and two equations: (x^2 + z^2 = 25^2) and (x/z = tan (ad)) Where (ad) is the angle at each sector. So, you should have no problem getting (x,z) of all points, except that you need at the end to calculate the radius at each point [=(x^2 + z^2)^0.5] since the radius is what you store into the file. Obviously, since the shape is a circle, the radius of all points will be found to be (25) We'll have more discussion of this subject following the code of example 3. ==============================================================================================


=============================================================================================== THE STARTUP ASSEMBLY: ===================== A solid nonsloped circular cylinder is the simplest 3D assembly since all its sector data are the same. Therefore, when creating a new 3D assembly, we normally start by creating a cylinder then modifying its data as the new shape requires. This is why we call the 3D Assembly creation job "Computer Sculpture". We start with the cylinder and sculpt it into the wanted figure. Notice the extension "scu" of the 3D Assembly files. RAINBOW COLOR SHADING: ====================== The simple shading method which we use in class (pcs) to present a 3D assembly is good enough when the Assembly contains few details. The WPF should do a better job with assemblies which contain more details. However, There is another way which makes you able to see the details of an assembly without shading. This way is to draw each unit cylinder in a different color. This way is good to use for testing assemblies which are in the developing stage. Which colors should we use? We can use the colors for an additional purpose which is identifying each unit cylinder's number while on the screen so that we can better evaluate the job we have done. For this reason we use the standard color code in selecting which color to draw each unit cylinder with. We base the color selection on the first digit in each unit cylinder's number. The standard color code is: 0=Black 1=Brown 2=Red 3=Orange 4=Yellow 5=Green 6=Blue 7=Violet 8=Gray 9=White We have elected to replace "Black" with "Cyan" to represent the digit "0" since we like to use the black color for drawing text and some other uses. It should be useful to draw a color code chart at the bottom of your drawing which shows each color and the digit it represents. This chart is made for you. You get it by calling method gm("3cd") supplying it with the location on the form where you like the chart to be drawn. To instruct the 3D Assembly's drawing method to use this "Rainbow Coloring" when drawing the assembly, make the assignment (cls="r") Drawing 3D assemblies with class (pcs): -------------------------------------- Despite the non-realistic look of the rainbow color shading, it's unbeatable in 3D assembly presentation. You can't miss any detail with it. This is why we'll be using it to draw high density assemblies in class (pcs) The drawing method allows you to rotate the assembly by any angle around (y) axis when you draw it and it also allows you to draw it after flipping it upside down. This means that you can inspect the 3D assembly from all directions. HIGH DENSITY 3D ASSEMBLIES: =========================== In order to draw more complicated 3D figures, we must use high density assemblies. This causes processing time to be of concern. So we need to take some measures in order to reduce processing time. Use of 3D data Arrays and unifying some 3D Assembly parameters are some of these measures. Using an "Array of 3D Assembly's Data" (A3D): -------------------------------------------- When we open a table file, all file data is copied to the string array TBS[][], we can then read each row of data with method fm("r") This method reads the row, seperates the data it contains and loads them into the string array OS[]. The reason we keep data in a string form is to be general since a table file can contain data of any type. The data of a 3D assembly is always numeric. So, we need to avoid having to convert data from a string form to numeric form and back each time we work on an assembly. This is done by using (A3D) which is an array of type "double". We start by loading file data into (A3D), we do all the work necessary on (A3D) then we transfer (A3D)'s data back to file when finished. The 3D Assembly Data Array (A3D) is a 4-dimensional array of type double in the form: A3D[Array Number(0:89)][Unit Cylinder Number][Surface Number (0=Outer, 1=Inner)][Sector Number] Setting 3D Assembly's density: ------------------------------ When you have been using 3D Assembly files to create, modify or draw an assembly you used to supply the number of sectors per unit cylinder (lf) and the diameter of enclosing circle (of) These two numbers are no longer necessary when you use A3D's to work on high density assemblies. In order to simplify things further and reduce processing time, all high density assemblies used in one project must be of same density. The default density is defined as follows: (1) No. of sectors per u cylinder = 360. (2) Diameter of enclosing circle = 100 Pixels. (3) Height of each unit cylinder = 1 Pixel. Your question now is expected to be "Why should we assume that these numbers belong to same density?" The answer is that they all mean that our drawing unit is approximately 1 pixel. If we assume that our assembly starts with the startup assembly, the arched side of each sector is equal to (Circumference of cross section / 360) = 2*PI*R/360. Where (R) is the enclosing circle radius. So, if we want that arched part of the sector to be 1 pixel in length: R = 360/2*PI = 57 (Approximately) This should mean that the diameter of enclosing circle should be (114 pixels) However, since we expect the radius to be reduced by different amounts in order to create the desired shape, we may consider (50) to be an average radius to end with. This is why we set the default of (100) for the enclosing circle diameter. REMARK: This applies to data in file and in 3D arrays only. When we draw the assembly, we can scale it horizontally and vertically to any size which we want. Working with different densities: --------------------------------- The variable (dna) sets the density. Its default value is (1.0) For simple 3D figures like the ones used in the previous example, we can work with lower density. In this case, we should assign a fraction to (dna) into method init() You must use one single density for each project. This is because you can't add or replace part of an assembly with another assembly if the densities of the two are not the same. In general: No. of sectors per u cylinder = dna*360 For the next few examples, we'll be using default density. However, we have made a version of the previous example to fit the web in which we used A3D's at lower density. See page 6 of WPDV (To see that web example, return to our home page, click on "WPD V" then on "Page 6") NEW METHODS TO SOLVE MATH PROBLEMS: =================================== These methods can save you time and make you avoid some errors in performing math operations. You may need to use them when you create or modify 3D Assemblies. Method gm(): ------------ CTP USE: Convert Cartesian coordinates of one point to Polar. IN : jf,kf= X,Y OUT: of,ad=Radius, Angle. CTC USE: Convert Polar coordinates of one point to Cartesian. IN : jf,kf= Radius, Angle. OUT: oxf,oyf= X,Y MTO USE: Calculate (x,y) after moving center point to origin and rotating axes by (-ad) to coinside with main axes. REMARK: This is the reverse process of moving origin to a new location and rotating axes by the angle (ad) IN : jf,kf = Original X,Y lf,of=center point coord's relative to origin. ad= Angle which source axes make with main axes. OUT: oxf,oyf = Resulting X,Y. Method um(): ------------ These methods are used internally by class (pcs) You can also make use of them. mta: USE: Mathematically calculate (asin, acos or atan) with quadrant adjustment. IN: od=function value, js = function (asin/acos/atan), ib=true means (X is -ve) in case of (asin and atan) or (Y is -ve) in case of (acos) OUT: od=Angle in degrees (0:360), adjusted for the quadrant. mh: USE: Mathematically calculate Hyperbolic functions. IN: od=Number to apply function to, js = function or constant name, could be: "sinh", "cosh", "tanh" or "e" OUT: od mlc: USE: Apply law of cosines to calculate length of one side of a triangle using lengths of the other two and the angle between them. If you make the assignment (ib=true) it will return the angle between the two sides if you supply the 3rd side's length. IN : jd,kd = Lengths of other two sides. ad=Angle between them. or: ib=true, jd,kd = Lengths of the two sides, id = 3rd side's length OUT: od = Length wanted or ad = Angle wanted (if ib=true) mq2: USE: Find the real roots (if any) of the second degree equation: a*x^2 + b*x + c = 0 IN : jd=Constant (a), kd=Constant (b), id=Constant (c) OUT: OD[] Containing all real roots found. oi=Number of real roots found. mq3: USE: Find the real roots of the third degree equation: a*x^3 + b*x^2 + c*x + d = 0 IN : jd=Constant (a), kd=Constant (b), id=Constant (c), od=Constant (d) OUT: OD[] Containing all real roots found. oi=Number of real roots found. HOW TO WORK ON HIGH DENSITY 3D ASSEMBLIES: ========================================== (1) The first step is to load the data of the assembly which you like to work on from file into a 3D data array (A3D) You do so by calling method gm("3LF") supplying it with file name and the A3D number which you like to load the data into. Any number in the range (0:89) can be used. If you intend to use other assemblies to modify your assembly with, load them into A3D's in the same manner. (2) You may then modify the A3D of the assembly by yourself or by using one of our methods which can either replace a section of the assembly with different data or add new data to existing one. In either case, you need to specify a section of the assembly where modification will take place. We'll see how to do this next. (3) When you have finished you need to save modified A3D into original file or a new one using method gm("3SF") (4) You can then draw the file at any scale using class (pcs) or (pcs3)'s drawing methods. Specifying the area of the assembly to be worked on: ---------------------------------------------------- You specify the area of the main assembly which is to be modified with 4 numbers. You include the 4 numbers into (ks) seperated with commas. The first two numbers set the location of area's center and the other two set area's width and height. Area's center point is defined by its sector number and unit cylinder number. Since we are using default density, there is one sector each one degree. So, sector number also means the angle which the sector makes with the positive Z-Axis. Unit cylinder number also means vertical position in pixels relative to assembly's bottom. The width is in pixels. To understand what the area width means, you need to know how we add a sub-assembly to a main one. Let us consider the next example in which we'll add a small "half a sphere" to a large one. The flat area of the small sphere sits over a flat area of the large sphere of the same size. This means that the first step is to create a flat area in the large sphere by cutting and removing part of it. The thickness of the part to be removed must be carefully selected so that the two flat areas are equal. This flat area's diameter is equal to the diameter of the small sphere and it's the width which you supply into (ks) The last item which you include into (ks) is the the height of the area to be flattened which is also equal to the diameter of the small sphere. Available methods which modify an A3D: -------------------------------------- 3LF USE: Load Assembly file's data into an A3D. IN : fls=File name, o=A3D number OUT: A3d[o][][][] 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. IN : o = A3D number. oc = A code which describes the curvature of the area to be modified. Can be F/S/E meaning: (Flat area) / (Section of a Sphere) / (Section of an Ellipsoid) ks = Center location, width and height of area to be modified as described above. dd = Thickness to be added to flat area. (dd) can be +ve or -ve. 3AS USE: Modify A3D data by adding half a sphere to it. IN : o, oc, ks (Same as in mode "3AF") 3AE USE: Modify A3D data by adding half an Ellipsoid to it. IN : o, oc, ks, dd (Same as in mode "3AF") 3AM USE: Modify A3D data by adding a sub-assembly which you specify its specs vertically and horizontally in the two method GetX() and GetRadius() IN : o, oc, ks, dd (Same as mode "3AF") 3SF USE: Save A3D data into file. IN : fls=File name, o=A3D number. ib = Hollow Assembly flag. Assign it (true) if you want a hollow assemby. i = Order of first hollow unit cylinder. How to write methods GetX() and GetRadius(): -------------------------------------------- Mode "3AM" requires that you override these two methods. If you have looked at example 21 of the "Windows Presentation Foundation" you must be familliar of them. The methods you write here are more general. This is because in Example 21 of the WPF, you have been making a complete assembly. Here you are modifying one limited area of an assembly which can also be set to be the whole assembly. Additionally, the new version of these methods are made to handle hollow objects. They are declared void and they return the array OD[] containing outer and inner surface data. For the next few examples, we'll be drawing solid objects only. So each method should return a single value which we assign to OD[0]. Before you prepare the method, you need to supply to method gm("3AM") few parameters: o : A3D number which contains the main assembly. oc : A code which describes type of curvature of the main assembly which helps method gm("3AM") in preparing the flat area which will receive your additive. ks : Contains the center location where your additive will sit and the width and height of your additive in pixels The parameters of method GetX() are (y) which is relative to center location (in ks), (hw) which means "Half Width" and (hh) which means "Half Height". (hw,hh) come from the last two numbers which you include into (ks) The parameters of method GetRadius() are (angle) which is your sub-assembly angle, (r0) which comes from the vertical formula. It's relative to the center location (in ks) and (y) which is the same as for method GetX() ============================================================================================== Example 2: Create a cylinder and a sphere. Add a flat area with positive thickness to an area of the cylinder then add a small "half a sphere" to the flat area. Additionally, add "half an Ellipsoid" to another area of the cylinder. Use your own methods to create this addition. Add a flat area to the sphere with negative thickness, then add the same small "half a sphere" to it. Additionally add a larger "half a sphere" to a different area using your own methods. ============================================================================================== public class a : pcs { string addition=""; public override void init() { base.init(); } public override void run() { j=600;k=470;cm("fs"); // Size form to fill the screen cls="y7";gm("ec"); //---------------------------------- Creating Cylinder ----------------------------------- fls="cylinder.scu";lf=360;of=100;o=100;gm("3cc"); //----------------------------------- Creating Sphere ------------------------------------ // Sphere's constants: fls="sphere.scu";int o1=100; // File name, Number of unit cylinders float o1f=100,l1f=360; // Diameter of encl circle at center, no of sides string r1s; // Radius // Creating Sphere file and writing constants: o=o1;lf=l1f;gm("3wc"); // Create file, Write header fs="tf0";fm("o"); // Open as table file // Outer Surface: for (int u=0;u< o1+1;u++) { // Scanning (o+1) u cyl's and writing their data: OS=new string[(int)l1f]; // Initiating field data array OS[] od=o1f*o1f/4-(u-50)*(u-50);um("ms"); // Get x from: x^2=(of/2)^2 - (u-50)^2 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 } fm("c"); // Close file. //--------------------------- Modifying and Drawing Cylinder ---------------------------- fls="cylinder.scu";o=1;gm("3LF"); // Load file data into A3D number 1. o=1;ks="0,50,50,50";oc='C';dd=10;gm("3AF"); // Add a flat area with 10 pixel thickness o=1;ks="359,50,26,26";oc='F';gm("3AS"); // Add a small sphere to the flat area. o=1;ks="280,50,50,70";js="";oc='S';addition="ellipsoid";gm("3AM"); // Add an Ellipsoid whose specs are in local methods fls="modified.scu";o=1;gm("3SF"); // Save A3D into file "modified.scu". fls="modified.scu";jf=-150;kf=15;kd=jd=2;cls="r";ad=0;gm("3rd"); // Draw file // Draw assembly using (2,2) scale factors. //---------------------------- Modifying and Drawing Sphere ----------------------------- fls="sphere.scu";o=1;gm("3LF"); // Load file data into A3D number 1. o=1;ks="0,50,50,50";oc='C';dd=-10;gm("3AF"); // Add a flat area with -10 pixel thickness o=1;ks="359,50,26,26";oc='F';gm("3AS"); // Add a small sphere to the flat area. o=1;ks="280,50,50,50";oc='S';addition="sphere";gm("3AM"); // Add a sphere whose specs are in local methods fls="modified.scu";o=1;gm("3SF"); // Save A3D into file "modified.scu". fls="modified.scu";jf=150;kf=15;kd=jd=2;cls="r";ad=0;gm("3rd"); // Draw file // Draw assembly using (2,2) scale factors. kf=-196;gm("3cd"); // Display color code chart. } //------------------------------------ Method GetX() -------------------------------------- public override void GetX(double y,double hw,double hh) { // Sphere: if (addition=="sphere") { // If this trip was for the sphere addition: od=hw*hw-y*y;um("ms"); // Calculate x^2 = r^2 - y^2 OD[0]=od; // Assign (x) to OD[0]. } // Ellipsoid: // Ellipse Equation is x^2/a^2 + y^2/b^2 = 1. So: x^2 = a^2 (1 - y^2/b^2) a=hw, b=35. else if (addition=="ellipsoid") { // If this trip was for the sphere addition: od=hw*hw*(1-y*y/(35*35));um("ms"); // Use formula to get (x) OD[0]=od; // and return it into OD[0]. } else OD[0]=hw; // This default value is not expected to be used. } //----------------------------------- Method Radius() ------------------------------------- public override void GetRadius(double angle,double r0,double y) { OD[0]=r0; // Return radius for all points. } } ============================================================================================= REMARKS: ======== (1) You needed to create two items, a sphere and an ellipsoid using your code in methods GETX() and GetRadius() So you used the local variable (addition) which you assign a value to before calling method gm("3AM") This insures that when your methods are called by class (pcs) the correct section of your code runs. (2) We have intentionally used method gm("3AM") in this example to let you know how to use this method. In the future, whenever you need to add a sphere or an ellipsoid, don't waste your time; use methods gm("3AS") and gm("3AE") instead. Use method gm("3AM") Whenever you have something more complicated to be added. (3) We have not assigned a value to (dd) when we created the sphere and the ellipsoid. If you have made the assignment (dd=-10), the two figures could have been nested deeper into their main assemblies. Method gm() insures clean coupling between the sub and main assemblies regardless to the requested depth. When you nest the sub-assembly deeper, you expect its exposed surface to be smaller. This does not mean that you set a smaller width or height into (ks) They should be always equal to the sphere's diameter or the ellipsoid's major and minor axes. Method gm() takes care of this problem internally. (4) As you must have noticed in the last example, all working areas of the main assemblies which we have flattened or added a sub-assembly to are in vertical planes. This will remain as a restriction for some time. Notice that this restriction applies only to the assembly's A3D and file. After the file is made, you can use class (pcs3) to rotate the assembly around any axis as you like. ==============================================================================================


=============================================================================================== REPLACING SECTIONS OF A 3D ASSEMBLY: ==================================== In the previous example, we have added a sub-assembly to the main assembly. This means that the original sector data at one area of the main assembly have been modified by either adding to them or reducing them by certain amounts. Now we are going to be replacing the original data with new data. If we set the height of area to be modified at its maximum height of 100 and set the angle range to 360 degrees, we expect the original assembly to be completely replaced with the new one. As mentioned before, a nonsloped cylinder is considered to be the simplest 3D Assembly. So it should be the best to use as the startup assembly which we replace with any figure which we want. You may be wondering why should'nt we create the new assembly to start with instead of starting with a cylinder then modifying it into a different assembly. The reason is that we're not expecting the new figure to be created with a single equation, we are interested in creating more complex assemblies which require several steps of replacements and additions. A3D's are going to be used for this purpose. The time spent in creating the startup figure and in saving the resulting figure into file are not going to add much to the overall processing time. Parameters necessary for data replacement modes: ------------------------------------------------ Parameters are mostly the same with few changes: o : A3D number as usual oc : Not necessary for the replacement. ks : Center location is the same. The width of the area to be modified means the width of the new figure. If the new figure was another cylinder or a sphere, the width will be its diameter. The height also means the height of the new figure except that the rest of the original assembly above and below the replaced area will remain unchanged. ad : Angle in degrees which specifies the arch of the main-assembly section to be replaced. If you intend to replace the full assembly horizontally assign (360) to (ad) Available modes which modify an A3D by replacing sections: ---------------------------------------------------------- 3RS USE: Modify A3D data by replacing an area with a section of a sphere. IN : o, ks (See above) 3RE USE: Modify A3D data by replacing an area with a section of an ellipsoid. IN : o, ks (See above) 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() IN : o, ks, js 3RQ IN : Modify A3D by specifying Vertical and Horizontal equations. Will be discused later. 3RA IN : Modify A3D using another A3D of a different 3D assembly. Will be discused later. About the next example: ----------------------- The "egg" is an odd shaped object which innovative people have been always looking for ways to draw. One way to draw an egg was invented by a person named "Moss". You draw Moss's egg with 4 different arches. Look at the sketch which we have included with the screen shot of the next example to see how it's drawn. (R) is the radius of the bottom section of the egg. It's equal to half of the egg's width which is the width you include into (ks) Moss expects you to draw the lines you see in the sketch and draw 4 arches as follows: (1) A bottom arch with center at (0,0) and radius=R. (2) 2 side arches with centers at (R,0) & (-R,0) and radius=2R. (3) One small arch with center at (0,R) and radius=(2R - R*(2)^0.5) The bottom arch is for the points whose (Y-value) is negative. The middle two arches are for points whose Y-values are in the Range (0:R*(2)^0.5) The top arch is for the rest of the points. The first equation (for bottom arch) should be: x^2 + y^2 = R^2 The second equation (for right side arch) should be: (x+R)^2 + y^2 = (2R)^2 The third equation (for top arch) should be: x^2 + (y-R)^2 = (2R - R*(2)^0.5)^2 You can simplify the last equation more if you like, but it's not realy necessary since computers are going to do the work and as you know computers don't complain. ============================================================================================== Example 3: Draw Moss's Egg in 3D. ============================================================================================== public class a : pcs { public override void init() { base.init(); } public override void run() { j=600;k=470;cm("fs"); // Size form at (600,470) cls="y7";gm("ec"); //------------------------------------ Main Program -------------------------------------- fls="cylinder.scu";lf=360;of=100;o=100;gm("3cc");// Create the startup assembly fls="cylinder.scu";o=1;gm("3LF"); // Load file data into A3D number 1. // Replace entire assembly. Set new assembly's width at 60 pixels. o=1;ks="0,50,60,100";ad=360;gm("3RM"); fls="egg.scu";o=1;gm("3SF"); // Save A3D into file "egg.scu". fls="egg.scu";jf=150;kf=15;kd=jd=2;cls="r";gm("3rd"); // Draw file kf=-196;gm("3cd"); // Display color code chart. } //------------------------------------ Method GetX() -------------------------------------- public override void GetX(double y,double hw,double hh) { if(y<0) { // If (y) was negative: od=hw*hw-y*y;um("ms"); // Draw bottom chart. } else if(y< hw*Math.Sqrt(2)) { // If (y) was at range of middle arches. od=hw*hw*4-y*y;um("ms"); // Use middle arch equation to get (x+R) od=od-hw; // Get x } else { od=2;um("ms"); // Calculate (2)^0.5 double RootTwo=od; // and assign result to (RootTwo) od=2*hw-hw*RootTwo; // Make od = 2R - R*(2)^0.5 od=od*od-(y-hw)*(y-hw); // Modify (od) to: od = od^2 - (y-R)^2 um("ms"); // Modify it again to: od = (od)^0.5 } OD[0]=od;oi=1; // Return x } //----------------------------------- Method Radius() ------------------------------------- public override void GetRadius(double angle,double r0,double y) { OD[0]=r0;oi=1; // Return radius for all points. } } ============================================================================================= COMMENT: ======== If you remember the comment following example 1 under the title "Explaining what we have done mathematically", we have mentioned in that comment that the vertical cross section of a 3D assembly must be in the "xy" plane at (z=0) Sorry, we can't keep this restriction any more. The intersection of the "xy" plane and the "xz" plane form a line which is the X-axis (positive and negative) The +ve part of the X-axis is at sector 90 and the negative part is at sector 270 (Remember that sector zero is at the +ve Z-axis) This means that our vertical cross section represents sectors 90 and 270 vertically. We have mentioned in the previous comment that the value of (x) of a particular unit cylinder which is obtained with the vertical formula means to the horizontal cross section the value of (x) at (z=0) If expressed in the polar coordinates system, it should mean the radius at the angle of 90. If the horizontal cross section was a circle or if we are modifying the entire assembly horizontally (ad=360) as we did in this example, we would have no problem, but we cannot keep this restriction in some cases. The first number in (ks) is the center point of the portion to be modified of the main assembly horizontally. That portion's size is expressed in degrees and it's assigned to (ad) If (ad=20) and first number in (ks) was (180), this means that only the section between sectors 170 and 190 will be modified. This means that we cannot make use of the vertical cross section formula which gives us the radius at sector 90. We can make use of a vertical cross section which represent a sector within the range to be modified and prefereably sector 180. So, we need a more general definition of the vertical cross section. It should be in a vertical plane, should contain the Y-axis but it does not have to be in the "xy" plane. The angle between the "yz" plane and the vertical cross section should be equal to the first number into (ks) ==============================================================================================


=============================================================================================== EXTENDING OUR USE OF EQUATIONS (For versions 3.3 and later): ============================================================ In example 3, you have seen how to use more than one equation to replace one assembly or a section of one assembly with a new data. Both the main assembly and the addition have been fully solid. We need to see now how we can use equations to create assemblies which contain hollow unit cylinders. The doughnut which we have created in example 1 has been also a hollow object, except that we have used a special code to create it and wrote the data directly into the file. Now, we are going to write it into the common methods GetX() and GetRadius() which are accessed by method gm("3RM") in a more general manner. Upgrading methods GetX() and GetRadius(): ----------------------------------------- If you have looked at examples 2, 3 before this new version was available, you need to look at them again since the methods GetX() and GetRadius() have been upgraded. The two methods no longer return a single value of "double" type. They are now declared "void" and they output the "general use" array of type "double" OD[] containing data of multiple "outer" and "inner" surfaces which each radius of a unit cylinder may go through. In general, a 3D assembly may contain more than one "outer" and one "inner" surfaces, Our A3D's are set to handle upto 5 of each. But let us keep things simple for a while. In the next example we are going to create an assembly which contains one outer and one inner surface using the equations of its vertical and horizontal cross sections. Available objects which PC# can create for you: ----------------------------------------------- Before we show you how to create more objects by equations using your own methods, we'll show you the objects which class (pcs) can create for you by equations also if you supply the necessary parameters. You already know that method gm("3RS") and method gm("3RE") can create a sphere and an ellipsoid for you. The sphere's vertical cross section is a circle while the ellipsoid's vertical cross section is an ellipse. Horizontally, both cross sections of the sphere and the ellipsoid are circles. An alternative method to specify the 3D assembly which you want is by specifying the shapes of its vertical and horizontal cross sections. To do so, call method gm("3RQ") which replaces a section of an assembly with new data using equations of vertical and horizontal cross sections of specified shapes. When you call method gm("3RQ"), you assign a two character code to (os) which indicates the vertical and horizontal cross sections' shapes. For the sphere, that code would be (os="cc") meaning (Circle-Circle) and for the ellipsoid, it would be (os="ec") meaning (Ellipse-Circle) Here are all the available codes which you can assign to (os): Vertical Cross Section: ----------------------- CODE SHAPE NAME EQUATION REQUIRED PARAMETERS ==== ========== ======== =================== r Rectangle x = hw hw = Half width of rectangle t Trapezoid x = hw + y*hw*(1-jd)/hh hw,hh=1/2 width, height. jd=bottom/center ratio c Circle x^2 + y^2 = hw^2 hw = Radius of circle = Eq Sided Obj n/a hh=Radius of enclosing circle, jd=Number of sides e Ellipse (x/hw)^2 + (y/hh)^2 = 1 hw,hh = 1/2(width,height), kd=unclipped height. h Hyperbola (x/hw)^2 - (y/hh)^2 = 1 hw,hh = Constants. p Parabola y = jd * x^2 jd = Constant s SuperEllipse (x/hw)^4 + (y/hh)^4 = 1 hw,hh = Half width, Half Height. H Heart Symbol (x^2+y^2-1)^3-x^2*y^3=0 jd=Scale factor, Default=40. m GetX() Your own equation(s) You decide. Use (jd,kd) or use local var's d Delete data x=0 None. It deletes existing data. $ Sketch n/a ims=Image file name which contains vert CS sketch. . None n/a None. Means do not modify vertical cross section. REMARKS: ======== (1) You may select an ellipse whose top and bottom are clipped by assigning its "unclipped" height to (kd) If you want a complete ellipse, keep (kd) unassigned. (2) You may modify vertical and horizontal cross sections independantly by using the code '.'. As an example, you may replace a section with an ellipsoid in one step with the assignment os="ec" or in two steps with the assignment os="e." in the first trip and the assignment os=".c" in the second trip. (3) When you use code '$' to replace vertical or horizontal equations with artist sketch, you must do the job in two steps. This means that you may make the assignment os="$." or os=".$" only. You'll get an error message if you make any other assignments to (os) Horizontal Cross Sections: -------------------------- CODE SHAPE NAME EQUATION REQUIRED PARAMETERS ==== ========== ======== =================== r Rectangle n/a rr = Half Width, id = Depth c Circle r=rr rr = Circle's Radius = Eq Sided Obj n/a rr=Radius of enclosing circle, id=Number of sides e Ellipse (x/rr)^2+(z/id*rr)^2 = 1 rr=X-radius, id=Ratio of (Z-radius)/(X-radius) R Rose Curve r = rr * sin(id*angle) rr=Petal length, id = Number of petals. g Gear Curve r = rr * (1+0.1*tanh(t)) rr=Gear radius, id = Number of teeth. where t=10*sin(id*angle) m GetRadius() Your own equation(s) You decide. Use (id,od) or use local var's. d Delete data r=0 None. It deletes existing data. $ Sketch n/a ims=Image file name which contains horiz CS sketch . None n/a None. Means do not modify horizontal cross section REMARK: ======= If you like to see the resulting 3D figures when any combination of the vertical CS equations and horizontal CS equations above are used, return to the our home page then click on [WPD V] then on [page 7]. Parameters of method gm("3RQ"): ------------------------------- o : A3D number of main-assembly. os : 2-char code which specifies the shapes of vertical and horizontal cross sections. ks : Center location (in sectors and u cyl's), width and height of area to be replaced. The width in this case is the raw width (or radius of enclosing circle) of replacement assembly and the height is the number of unit cylinders it should contain. If you like to replace the entire assembly vertically, make it equal to main-assembly's height. ad : Angle in degrees which specifies the arch of the main-assembly section to be replaced. If you intend to replace the full assembly horizontally assign (360) to (ad) jd,kd: Additional parameters which vertical cross section's equation may require. id,od: Additional parameters which horizontal cross section's equation may require. Replacing an assembly or a section of an assembly with another assembly: ------------------------------------------------------------------------ Method gm("3RA") can replace a section of the main-assembly with a sub-assembly. You must load the sub-assembly file into an A3D, just as you did with the main assembly. Here are the parameters in this case: o : A3D number of main-assembly. i : A3D number of sub-assembly. ks : Center location of area to be replaced (in sectors and u cyl's), width and height of area to be replaced. If you like to replace the entire assembly vertically, make the height equal to main-assembly's height. The width in (ks) is unused in this mode. ad : Angle in degrees which specifies the arch of the main-assembly section to be replaced. If you intend to replace the full assembly horizontally assign (360) to (ad) js : Center location of area to be copied (in sectors and u cyl's), width and height of area to be copied. Notice that the width in (js) is the angle in degrees which bounds the area to be copied. If you like to copy the entire assembly, assign (360) to the width and make the height equal to sub-assembly's height. ld : Displacement of sub-assembly's center from main-assembly's center. Notice that this displacement is expressed in polar coordinates. The radius is (ld) and the angle is the first number in (ks) kb : Lock-on flag. (ib=true) causes main-assembly surface to lock on sub-assembly surface from all directions leaving only portions of sub-assembly which extend beyond it to be exposed. ib : Hollow Assembly flag. ib=true means "hollow assembly wanted" and (dd,j) must be assigned. dd : Thickness of hollow assembly (Necessary when ib=true) j : Number of first hollow unit cylinder (Necessary when ib=true) REMARKS: ======== (1) The two assemblies must be of same density. (2) Notice that the width of (ks) is not used. Area of main-assembly bounded by the angle in (ad) receives the sub-assembly. Rest of main-assembly is unaffected. If you like to replace full assembly horizontally, make the assignment (ad=360) (3) The source and destination area heights do not have to be equal. The process will scale the copied area to the size of the destination area if necessary. When scaling is done, aspect ratio of the sub-assembly is guaranteed to stay unchanged. (4) If you keep (dd) unassigned, sub-assembly's center will coinside with main-assembly's center horizontally. (5) If you select (dd) so that portion of sub-assembly extends beyond main-assembly and make the assignment (ib=true), main-assembly surface will close on sub-assembly generating shapes which are similar to the ones of example 2. About the next example: ======================= Using methods gm("3RQ") and gm("3RA"): -------------------------------------- There will be plenty of practice on using gm("3RQ") to replace multiple sections of a main-assembly with sub-assembly's whose vertical and horizontal cross sections' equations are available. Additionally, method gm("3RA") will be used to transfer the "egg.scu" assembly which has been created in Example 3 to a section of another assembly. So make sure this file is available into your working directory. If it was not, run example 3 to generate it. Using method gm("3RM") to create a partially hollow assembly: ------------------------------------------------------------- There are many equations which can draw figures which look like the "heart" symbol, but one of them, although not very easy to work with, is the closest to the actual symbol. This equation is: (x^2 + y^2 - 1)^3 - x^2*y^3 = 0 As you know, within method GetX() you are supplied with values of (y) and expected to return the values of (x) which correspond to them. Let us try to simplify the equation by using the substitution (c = y^2 - 1) It should bring us an equation of the sixth degree in (x) This sounds like a problem, but fortunately, we can use another substitution which is (u = x^2) to turn it into an equation of the third degree in (u) which we can solve. Now, the equation is: (u+c)^3 - y^3 * u = 0 If you work on this equation further, you should be able to end with: u^3 + 3c*u^2 +(3c^2-y^3)u + c^3 = 0 and we have a method which can solve this equation. It's method um("mq3") The required parameters for this equation should be: jd = 1; kd = 3*c; id = 3*c^2 - y^3; od = c^3 As you must know an equation of 3rd degree should have upto 3 roots. Some of the roots could be imaginary. The method returns all the real roots it could find assigned to array OD[]. It neglects imaginary roots. The integer (oi) is assigned the number of returned roots. Or in other words the number of occupied rows of array OD[]. After we get the roots, which represent all possible values of (u) that verify the equation, we will then need to calculate the value of (x) which corresponds to each value of (u) Since (x) is equal to the square root of (u), all negative values of (u) must be neglected since they bring imaginary values of (x) Each square root operation brings an equal positive and negative values of (x) As we always do, we'll consider +ve values of (x) only then scan each unit unit cylinder over 360 degrees to get all sector data. Now, looking at the sketch below, you can tell that we should end with one positive value of (x) at a unit cylinder like (A) and two positive values of (x) at a unit cylinder like (B) So, if you do everything correctly, this is what you return into OD[]. Method gm() takes care of identifying which value is for outer surface and which one is for inner one. This equation draws heart of dimensions about (1.25 X 1.25) pixels. So we must scale it up. For this purpose, we assign a scale factor to (jd) When the method is called, we reduce the received Y-value with it, apply the equation then increase the result with the same scale. The last problem which you may be thinking of, is that the order of the first hollow unit cylinder is expected to be supplied by you when you save the A3D into file. How can you know which one? This is not a problem. You can make the assignments (ib=true;i=0) which means that the object is hollow top to bottom. The solid part will be cosidered to be hollow, but with inner surface radii of zeros which is the same as solid. Y ^ | | -- | -- / \ | / \ B -----/----\-|-/----\----- / \|/ \ * * * | | | * | * \ | / A -----\------|------/----- \ | / \ | / \ | / \ | / \ | / \|/ * | ============================================================================================== Example 4: Show how to use method gm("3RQ") to replace sections of the startup assemblies with variety of shapes. Use also method gm("3RA") to include the assembly which we have created in Example 3 into one new assembly. Create and draw an object which looks like the heart symbol vertically and with elliptical cross section horizontally. ============================================================================================== REMARK: File "egg.scu" must be available into your working directory. If not, run example 3 to generate it. public class a : pcs { public override void init() { base.init(); } public override void run() { j=700;k=470;cm("fs"); // Size form at (700,470) cls="y7";gm("ec"); fls="cylinder.scu";lf=360;of=100;o=100;gm("3cc"); // Create the startup assembly //------------------------------------ Left side assembly ------------------------------ fls="cylinder.scu";o=1;gm("3LF"); // Load file data into A3D number 1. // Divide assembly into 4 sections with vert sizes of 4,16,60,20 (top to bottom) // Replace each with a different sub-assembly. o=1;ks="0,98,60,4";ad=360;id=6;os="rR";gm("3RQ"); // 1st equation (Rect-Rose with 6 petals) o=1;ks="0,88,6,16";ad=360;os="rc";gm("3RQ"); // 2nd equation (Rect-Circular cylinder) o=1;ks="0,50,60,60";ad=360;os="cc";gm("3RQ"); // 3rd equation (Sphere) o=1;ks="0,10,60,20";ad=360;id=10;os="r=";gm("3RQ");// 4th eq (Rect-polygn with 10 sides base) fls="modified.scu";o=1;i=0;gm("3SF"); // Save A3D into file "modified.scu". fls="modified.scu";jf=-230;kf=15;kd=jd=2;cls="r";gm("3rd"); // Draw file at left //---------------------------------- Right side assembly ------------------------------- fls="cylinder.scu";o=1;gm("3LF"); // Load main file data into A3D number 1. fls="egg.scu";o=2;gm("3LF"); // Load sub file data into A3D number 2. // Divide assembly into 4 sections with vert sizes of 20,8,62,10 (top to bottom) // Replace first with the "egg.scu" assembly and the other 3 with a variety of assemblies. o=1;i=2;ks="0,90,0,20";js="0,50,360,100";ad=360;gm("3RA"); o=1;ks="0,76,70,8";ad=360;os="rc";gm("3RQ"); // Equation (Rect-Circle) o=1;ks="0,41,20,62";ad=360;id=6;os="h=";gm("3RQ"); // Eq (Hyperbola-Polygon with 6 sids base) o=1;ks="0,5,40,10";ad=360;id=6;os="r=";gm("3RQ"); // Eq (Rectangle-Polygon with 6 sids base) fls="modified.scu";o=1;i=0;gm("3SF"); // Save A3D into file "modified.scu". fls="modified.scu";jf=230;kf=15;kd=jd=2;cls="r";ad=0;gm("3rd"); // Draw file at right //----------------------------------- Middle Assembly ---------------------------------- fls="cylinder.scu";o=1;gm("3LF"); // Load file data into A3D number 1. // Area to be replaced: Center angle=270, Center height=50, Width=60 pixels, Height=100 // Bounding angle=360 dgrees // Vertical CS: Scale factor: 36 Horizontal CS: Z-Rad/X-Rad=0.5 // Use Local equation for vertical cross section and use ellipse equation for Horiz CS. o=1;ks="270,50,60,100";ad=360;jd=36;id=0.5;os="me";gm("3RQ"); fls="modified.scu";o=1;i=0;ib=true;gm("3SF"); // Save A3D into file "modified.scu". fls="modified.scu";jf=00;kf=-15;kd=jd=2;cls="r";ad=0;gm("3rd"); // Draw file kf=-196;gm("3cd"); // Display color code chart. } //------------------------------------ Method GetX() -------------------------------------- public override void GetX(double y,double hw,double hh) { double j1d=jd; // Copy (jd) to persist it. // This method should return OD[], but this array is also used by method um("mq3") which // will be used within this method. So we'll define a new array, save return data into it // then copy it to array OD[] at the end. int o1i=0; // Array index double[] O1D=new double[10]; // Define an array of type double. for(int i1=0;i1<10;i1++) O1D[i1]=0; // Zero all rows. // Equation: (x^2+y^2-1)^3-x^2y^3=0 Substituting: u=x^2, c=y^2-1 we end with: // u^3 + 3c*u^2 +(3c^2-y^3)u + c^3 = 0 y=y/j1d; // Reduce current (y) to the equation size double c=y*y-1; // Substitution (discussed above) jd=1;kd=3*c;id=3*c*c-y*y*y;od=c*c*c; // Equation's a,b,c,d constants. um("mq3"); // Call method to get equation's roots. for (int i1=0;i1< oi;i1++) { // Scan returned OD[] from method: if(OD[i1]<=0) continue; // Neglect -ve values O1D[o1i]=j1d*Math.Sqrt(OD[i1]); // Get root. Multiply by scale as discussed. o1i++; // Add result to O1D[]; increment its index. } OD=O1D;oi=o1i; // Assign O1D[] to OD[] } } ============================================================================================= COMMENTS: ========= (1) The 3D heart symbol with an Elliptical cross section which you have created with: o=1;ks="270,50,60,100";ad=360;jd=36;id=0.5;os="me";gm("3RQ"); may have not been the one you're expecting. If so, try one with a rectangular cross section. Replace line above with: o=1;ks="270,50,60,100";ad=360;jd=36;id=20;os="mr";gm("3RQ"); (2) The heart symbol equation is included into class (pcs)'s vertical equations collection. So, you did not really have to use your own methods to generate it. Its equation code is (H) =============================================================================================


=============================================================================================== Graphics quality: ----------------- You may not like the "jpeg" screen shot image of last example, but when you try running it directly on your computer you'll discover that its quality is excellent. The little egg which sits on the table looks fantastic even if you reduce its scale to half of what it is now. Placing sub-assembly away from center: -------------------------------------- By default, when you use method gm("3RA") to replace a section of the main-assembly with a sub- assembly, the sub-assembly will be centered around the main-assembly's center. This is the position of best graphics quality. Why? As you know, the 3D figure is defined by the sector data which are stored into the 3D assembly's file. The sector data are lengths of radii which start from the center of each unit cylinder of the main-assembly and end at the figure's surface. The more the data the better the figure's drawing quality. When you set the sub-assembly at the center, it will be defined with 360 pieces of data which is the maximum (assuming default density) As you move the sub-assembly away from the center, the number of its defining data is reduced. Despite the graphics quality problem, we must place sub-assemblies wherever they should be. So, method gm("3RA") allows us to place the sub assembly's center horizontally at a point which is defined by its polar coordinates (dd, a) relative to main assembly's center, where (a) is the first number which you assign to (ks) ============================================================================================== Example 5: Recreate the right section drawing of example 4 after making the following changes: a) Instead of placing one egg on table's center place 2 eggs and a cup at different locations of the table. b) Replace the hyperboloid stand of the table with 4 legs. Apply woodwork to the table legs so that they look like professionally made furniture. c) Place a carpet under the table. ============================================================================================== REMARK: File "egg.scu" must be available into your working directory. If not, run example 3 to generate it. public class a : pcs { public override void init() { base.init(); } public override void run() { j=360;k=470;cm("fs"); // Size form at (360,470) cls="y7";gm("ec"); // Paint background fls="cylinder.scu";lf=360;of=100;o=100;gm("3cc"); // Create the startup assembly //------------------------------- Startup of main-assembly ------------------------------- fls="cylinder.scu";o=1;gm("3LF"); // Load startup assy data into A3D no 1. //------------------------------ Preparing 3D assembly parts ----------------------------- // Loading file "egg.scu" into A3D no 2: fls="egg.scu";o=2;gm("3LF"); // Load "egg.scu" file data into A3D no 2. // MAKING A CUP: Modify startup assembly by replacing it with a sloped cylinder with // Bottom/Center slope of (0.75) Sloped cylinder "vert-horiz" shapes are "Trapezoid-Circle". fls="cylinder.scu";o=3;gm("3LF"); // Load startup assy data into A3D no 3. o=3;ks="0,50,50,100";ad=360;jd=0.75;os="tc";gm("3RQ"); // MAKING A TABLE LEG: Divide leg vertically into 4 sections as follows (Starting at Top and // defined by Vertical-Horizontal Cross sections): // Top 16 pixels: (Rect-Hexagon) // Next 60 pixels: (Ellipse-Circle), Ellipse height=64 pixels, clipped to 60. // Next 4 pixels: (Ellipse-Circle), Ellipse height=6 pixels, clipped to 4. // Next 20 pixels: (Trapezoid-Circle) Ratio of (width at bottom) to (Width at center)=0.5 fls="cylinder.scu";o=4;gm("3LF"); // Load startup assy data into A3D no 4. o=4;ks="0,92,10,16";ad=360;id=6;os="r=";gm("3RQ"); // Eq (Rectangle-Polygon with 6 sids base) o=4;ks="0,54,8,60";ad=360;kd=64;os="ec";gm("3RQ"); // Eq (Ellipse-Circle) o=4;ks="0,22,6,4";ad=360;kd=6;os="ec";gm("3RQ"); // Eq (Ellipse-Circle) o=4;ks="0,10,6,20";ad=360;jd=0.5;os="tc";gm("3RQ");// Eq (Trapezoid-Circle) //--------------------- Replacing sections of main-assembly with parts -------------------- //----- (1) Replacing top 20 unit cylinders with 2 eggs and the cup ----- o=1;ks="0,90,0,20";ad=360;os="dd";gm("3RQ"); // Delete top 20 pixels of Main-assembly // Replace the 2 (90 sectors) areas of top 20 u cyl's of main assembly with A3D #2 (the egg) // one area is centered around sector (0) and the other is centered around sector (270) // Also, replace a third (90 sectors) area which is centered around sector (135) with the // A3D no 3 (the cup). o=1;i=2;ks="0,90,0,20";js="0,50,360,100";ad=90;ld=30;gm("3RA"); o=1;i=2;ks="270,90,0,20";js="0,50,360,100";ad=90;ld=30;gm("3RA"); o=1;i=3;ks="135,90,0,20";js="0,50,360,100";ad=90;ld=20;gm("3RA");//place cup on table //----- (2) Replacing next 8 unit cylinders with table top ----- o=1;ks="0,76,70,8";ad=360;os="rc";gm("3RQ"); // table top Equation (Rect-Circle) //----- (3) Replacing next 70 unit cylinders with table legs ----- // Replace the 4 (90 sectors) areas of this section with A3D # 4 (table leg) The 4 legs // are positioned at a radius of (30) and angles of (0,90,180 and 270) o=1;i=4;ks="0,37,0,70";js="0,50,360,100";ad=90;ld=30;gm("3RA"); o=1;i=4;ks="90,37,0,70";js="0,50,360,100";ad=90;ld=30;gm("3RA"); o=1;i=4;ks="180,37,0,70";js="0,50,360,100";ad=90;ld=30;gm("3RA"); o=1;i=4;ks="270,37,0,70";js="0,50,360,100";ad=90;ld=30;gm("3RA"); //----- (4) Replacing bottom 2 unit cylinders with Carpet ----- o=1;ks="0,1,120,2";ad=360;id=4;os="r=";gm("3RQ"); // Eq (Rectangle-Polygon with 4 sids base) fls="cylinder.scu";lf=360;of=100;o=100;gm("3cc"); // Create the startup assembly //--------------------- Saving final assembly into file and drawing it -------------------- fls="modified.scu";o=1;i=0;ib=true;gm("3SF"); // Save A3D into file "modified.scu". fls="modified.scu";jf=0;kf=45;kd=jd=2.2;cls="r";ad=0;gm("3rd"); // Draw file kf=-196;gm("3cd"); // Display color code chart. } } ============================================================================================= We have replaced the 20 unit cylinders above the table top with two eggs and a cup. We have also replaced the 70 unit cylinders below the table top with four legs. We have used different techniques when we did the two replacements. In the first replacement, we started by deleting all data of the 20 unit cylinders. Deleting data does not mean eliminating them. It means replacing them with zeros. We have then replaced three portions, each of them is 90 degrees wide with the 3 parts. The method places each part at the center of its assigned portion leaving empty space around it at both sides. The 3 portions cover only 270 degrees of the 360. So if we have not had zeroed all data to start with, parts of the original startup cylinder could have appeared. Notice that we could have accomplished the same without deleting data to start with if we have made the portions to be replaced 120 degrees wide instead of 90. In the second replacement, we have replaced four 90 degrees portions with the four table legs. This has covered the entire startup cylinder body. This is why we have had no need for deletion. ==============================================================================================


=============================================================================================== DRAWING USING ARTIST SKETCH: ============================ If you like to draw a special shape which you can't find an equation for, you may draw a sketch for the vertical cross section, horizontal cross section or both. You can process only one sketch at a time. This means that method gm() expects you to assign either "$." or ".$" to (os) Data assigned to the parameter (ks) should be the same when processing the two sketches. Preparing the sketch: --------------------- You must draw the sketch using black ink on a white sheet of paper. Draw only the outlines of either a horizontal or a vertical cross section. Do your best to make your drawing lines clear, continuous and of uniform thickness. Scan the sketch and save it into an image file (prefereably a "bmp" file) Leave a very small margin around image at all 4 directions. Required parameters: -------------------- Method gm("3RQ") is the one to be used. Parameters required are same as normal except that you need to add (ims) to them. Here is the parameter list: o : A3D number ims : Sketch's image file path. os : Can be "$." or ".$" only. ad : Angle in degrees which specifies the arch of the main-assembly section to be replaced. If you intend to replace the full assembly horizontally assign (360) to (ad) ks : Center of the portion to be replaced with the sketch horizontally and vertically are the first and second parameters. Third parameter is unused. Fourth parameter is the number of unit cylinders to be replaced. Make sure to assign same data to (ks) when supplying vertical and horizontal cross sections. About the next example: ----------------------- We have made a hand sketch for the vertical and horizontal cross sections of a vase. the file names of the two sketch images are "vase.bmp" and "base.bmp" respectively. Both files are saved into sudirectory "images" of your working directory. We are going to create two 3D assemblies for the vase. The first one uses the verical cross section's sketch and a standard circular base. The second one uses both the vertical cross section and horizontal cross section sketches. =============================================================================================== Example 6: Create and draw a 3D assembly of a vase with circular cross section horizontally using file "vase.bmp" for the vertical cross section. Also create and draw another 3D assembly for the vase using the two files "vase.bmp" and "base.bmp" which contain its vertical and horizontal sketches. =============================================================================================== public class a : pcs { public override void init() { base.init(); } public override void run() { j=700;k=470;cm("fs"); // Size form at (700,470) //------------------------------- Drawing Title and labels ------------------------------ cls="r0";gm("sps");fns="trb16"; // Set color font then draw title. os="3D Drawing using Artist Sketch";kf=196;gm("ctf"); os="Vertical CS Sketch Sketch-Circle Sketch-Sketch Horizontal CS Sketch"; cls="S9";gm("sps");fns="crb12";kf=-90;gm("ctf"); // Set color font then draw labels. //----------------------------- Creating Startup assembly ------------------------------- fls="cylinder.scu";lf=360;of=100;o=100;gm("3cc"); // Create the startup assembly fls="cylinder.scu";o=1;gm("3LF"); // Load startup assy data into A3D no 1. //------------------------------- Drawing sketch images --------------------------------- fls="images\\vase.bmp";lf=0;of=150;gm("blf"); // Create a new (bip) from Vert CS file. jf=-260;gm("br"); // Draw the vertical CS file at left. fls="images\\base.bmp";lf=0;of=150;gm("blf"); // Create a new (bip) from horiz CS file. jf=242;gm("br"); // Draw the horiz. CS file at right. //-------------------------- First Assembly: Sketch-Circle ------------------------------ // First trip: Processing vertical cross section's sketch: o=1;ims="images\\vase.bmp";ks="270,50,30,100";ad=360;os="$.";gm("3RQ"); // Second trip: Requesting a circular horizontal cross section: o=1;ks="270,50,100,100";ad=360;os=".c";gm("3RQ"); // Apply Equations (n/c-Circle) // Save and draw resulting assembly: fls="modified.scu";o=1;gm("3SF"); // Save A3D into file "modified.scu". fls="modified.scu";jf=-100;kd=jd=1;cls="r";ad=0;gm("3rd"); // Draw file at left //-------------------------- Second Assembly: Sketch-Sketch ----------------------------- // First trip: Processing vertical cross section's sketch: o=1;ims="images\\vase.bmp";ks="270,50,30,100";ad=360;os="$.";gm("3RQ"); // Second trip: Processing horizontal cross section's sketch: o=1;ims="images\\base.bmp";ks="270,50,0,100";ad=360;os=".$";gm("3RQ"); // Save and draw resulting assembly: fls="modified.scu";o=1;gm("3SF"); // Save A3D into file "modified.scu". fls="modified.scu";jf=50;kd=jd=1;cls="r";ad=0;gm("3rd"); // Draw file at right kf=-196;gm("3cd"); // Display color code chart. } } ============================================================================================== A better idea for a sketch: --------------------------- You can use method gm() to draw the shape which represents the vertical or horizontal cross section on the presnt bitmap object then save it into file. This should do better than sketching since the shape outlines are guaranteed to be continuous and of even thickness in this case. You may use the graphical path to draw and save a complicated cross section. Example 5 of "Drawing I" shows you one way to create a closed graphical path. You need to research the graphical path class since there are some usable tools which we have not shown in that example. Here is an example which shows how to create a sketch file of a square which could have been used as the horizontal cross section in the previous example: lf=of=305;gm("bn"); // Create a new (bip) slightly larger than the sketch. gm("sdb"); // Make it the graphical output device. cls="s9";gm("ec"); // Paint it white. cls="S9";gm("sps"); // Create a black pen. lf=of=300;gm("crd"); // Draw a square. gm("sdd"); // Return graphical output device to default. fls="base.bmp";gm("bsb");// Save (bip) into file "base.bmp". ==============================================================================================