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.
For simplicity, we'll be refering to first unit cylinder as "unit cylinder number 1" and to its
bottom surface as "unit cylinder number 0".
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 25: 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 27.
==============================================================================================
BEST SELLERS FROM AMAZON.COM
Books, C Sharp Books, .NET Computers Electronics Industrial & Scientific Items MP3 Downloads DVD Camera & Photo Cell Phones & Services Magazine Subscriptions Office Products On Demand Videos
|
 |
===============================================================================================
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.
At this time, the default density is the only setting available.
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 26: 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 27: 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 25 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 27, 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 25 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 26,27 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
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.
dd : Displacement of sub-assembly's center from main-assembly's center. Notice that this
displacement is expressed in polar coordinates. The radius is (dd) and the angle is the
first number in (ks)
ib : 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.
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 26.
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 27 to a section of another assembly. So make sure this file is available into
your working directory. If it was not, run example 27 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 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 28: 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 27 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 27
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 29: Recreate the right section drawing of example 28 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 27
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;dd=30;gm("3RA");
o=1;i=2;ks="270,90,0,20";js="0,50,360,100";ad=90;dd=30;gm("3RA");
o=1;i=3;ks="135,90,0,20";js="0,50,360,100";ad=90;dd=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;dd=30;gm("3RA");
o=1;i=4;ks="90,37,0,70";js="0,50,360,100";ad=90;dd=30;gm("3RA");
o=1;i=4;ks="180,37,0,70";js="0,50,360,100";ad=90;dd=30;gm("3RA");
o=1;i=4;ks="270,37,0,70";js="0,50,360,100";ad=90;dd=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)
//--------------------- 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 30: 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".
==============================================================================================
BEST SELLERS FROM AMAZON.COM
Books, C Sharp Books, .NET Computers Electronics Industrial & Scientific Items MP3 Downloads DVD Camera & Photo Cell Phones & Services Magazine Subscriptions Office Products On Demand Videos
|
 |
|