===============================================================================================
USING THE WINDOWS PRESENTATION FOUNDATION (WPF)
===============================================
We have now two seperate classes. Class (pcs) is for version 2. You can use it with either
version 2 or version 3. Additionally, we have class (pcs3) which can be accessed with version 3
only. The two classes are identical in everything except Controls and Drawing. All version 3
additions will be in class (pcs3) only.
Changes made to Controls:
=========================
The (WPF) controls are more similar to the "Web Controls" than to the "Desktop" Controls. For
this reason, class (pcs3) is using almost identical software to the one used in class (pasp)
for this purpose. Class (pcs3) does not do the layout discretely, it does it in the same manner
as class (pasp) does.
-------------------------------------------------------------------
If you have not studied the "Web Examples" yet, you must study at
least the first 4 examples of the "WPDI" before you get further.
-------------------------------------------------------------------
Class (pasp) inserts each group of controls into a table, then inserts the tables into bigger
tables, and this continues until it ends with the root table "tb0" containing the whole page.
Class (pcs3) uses the same idea except that it replaces tables with panels called "Grids" which
are very similar to tables. The root grid "gr0" contains everything in the page.
Some of the good ideas used in class (pasp) like having a "Master page" and combining controls
with graphics are going to be used also in class (pcs3)
Although we have done the best we can to absorb the new changes internally into class (pcs3), so
you can use the same PC# methods as if nothing has been changed, there are still some few changes
which you need to be taking care of. As a sample, this is how example 1 of the chapter
"Using Controls"is to be modified in order to run with class (pcs3) and the WPF:
----------------------------------------------------------------------------------------------
public class a : pcs3 {
public override void init() {
base.init();
}
public override void setup() { // All installations are done within this method
//---------------------------------- Page Contents -----------------------------------
cns=""; // gr0 has no container since it's the root
cs="gr0";cls="S9g8";cm("i"); // gr0 installation parameters
//----------------------------------- gr0 Contents -----------------------------------
cns="gr0";
cs="tf0";j=k=0;oyd=0.5;ds="c";lf=200;cls="b0y0";fns="trbi20";cm("i");
cs="bt0";cis="My Color Changes";j=0;k=1;oyd=0.5;ds="c";lf=160;of=30;fns="trb16";
cls="S9y0";cm("i");
}
public override void update() { // This method is called whenever an event takes place
if ("bt0".Equals(cs)) { // If button "bt0" has generated this event
cs="tf0";cm("gu"); // Get latest update of text field "tf0" in (cus)
if (cus.Length<1) return;
os=cus.Substring(0,1);om("u");// Get 1st char of (cus) and covert it to U/C
n="RGB".IndexOf(os); // See if it was R,G,B or none of them
if (n<0) cls="S9y0"; // If none of them select yello background color
else if (n==0) cls="S9r0"; // If was "R" select red background color
else if (n==1) cls="S9g0"; // If was "G" select green background color
else if (n==2) cls="S9b0"; // If was "B" select blue background color
cs="bt0";cm("sC"); // Call method cm() to set the color for "bt0"
cs="tf0";cus="";cm("su"); // Erase tf0's text.
cs="tf0";cm("sx"); // Turn the focus state of "tf0" on.
}
}
}
----------------------------------------------------------------------------------------------
Compiling:
==========
You may compile the class with either the Visual Studio or tool (pcp) If you receive an error
when you compile with our tool, select "PC# Reference-Desktop" and read the section titled
"The refs.txt file" to learn how to correct the problem.
----------------------------------------------------------------------------------------------
As you can see, the code is mostly the same. You are still using the same methods, the same
architecture, the same color and font codes. So, as far as you are concerned almost nothing has
changed. The only difference, you may have noticed is using grids instead of tables.
Using grids is similar only. Some rules are different since they require more complicated setup.
We have done our best to simplify their setup and ended with dividing controls into 3 categories
each require different actions. Let us list all setup parameters before we get further:
j,k : Index of first cell which the control occupies horizontally and vertically. This has
been explained in WPDI so we are not going to say more about these two parameters here.
i,o : Column and row span. These two items have also been discussed thoroughly in WPDI.
lf,of : Width and height of the control.
oxd,oyd: They are new. When you like to install two controls into one grid panel horizontally,
and the first contol is twice as wide as the second one, you need to make the
assignments (oxd=0.67;) for the first one and (oxd=0.33;) for the second one. In
general, for each control in the container:
oxd = (Its CellWidth / ContainerWidth) oyd = (Its CellHeight / ContainerHeight)
When we have been installing web controls into tables, the space allocation job has
been done automatically. Unfortunately, Grid panels are less helpful with this feature.
This is why we need these two parameters.
ds : Direction string. Can be (c/n/s/e/w/ne/nw/se/sw/fh/fv) It tells how the control will be
aligned inside its cell. (c) means center, Next 8 directional symbols are obvious.
(fh) and (fv) mean that the control should fill its cell horizontally / vertically.
Default values:
---------------
As you know, when you keep a parameter unassigned, its value will be zero if numeric or "" if
string. Here is what leaving each of the parameters unassigned mean:
j,k : Means that the control starts at the first cell (horizontally, vertically)
i,o : Means that the control ocupies one cell (horizontally, vertically)
oxd,oyd : Means that the control occupies the full width or the full height of its container.
ds : Means mount the control at the center of its cell.
Let us now discuss each control category requirements:
(1) Grids:
----------
As you can see in the code above, the root grid is installed first. Then you may need to install
more grids into the root grid, then install additional grids into each of the new grids,... this
may continue forming a tree of grids.
The root grid requires no parameters other than the color if you like it to be identifiable. This
is because,it is the only control in its container.
Branch grids require j, k, i, o and either (lf,of) or (oxd,oyd) You may also supply both if you
don't like the grid to occupy its entire cell (meaning that you like to keep a margin around it)
(2) Image Controls:
-------------------
The simplist way to mount an Image Control is to divide the space of its container by installing
additional grids into it first, then installing the image control into one of the new grids.
Since it will be the only control in its container, it will require no parameters other than
(lf,of) if you like to change the size of its image.
(3) All other controls:
-----------------------
Supplying j, k, i, o, oxd and oyd is necessary for all other controls (unless you are accepting
defaults) Assigning values to (lf, of) is optional.
Creating a master page:
=======================
As you have noticed with web controls, master pages offer a great help. So, let us have one
which contains an icon, a title and a settable page number to be displayed. It will also
contain the two public variables (PageWidth) and (PageHeight) which allow each page to set
their useable page width and height if default values have not been acceptable.
The master page class will extend class (pcs3) and each example class will extend the master
page class.
You can create a hierarchy of master pages and each may contain fields and methods which can be
accessed by your application classes.
---------------------------------------------------------------------------------------------
public class ms1:pcs3 {
public int PageNumber=0;
public int PageWidth=560;
public int PageHeight=300;
public override void setup() {
base.setup();
//---------------------------------- Page Contents -----------------------------------
cns=""; // gr0 Container="" since it's
// mounted directly into the page.
cs="gr0";cls="S9s9";cm("i"); // gr0 installation parameters
//----------------------------------- gr0 Contents -----------------------------------
cns="gr0"; // Container=gr0 for next grids
cs="gr1";j=0;k=0;lf=PageWidth-150;of=60;ds="w";cls="S9g7";fns="tr12";jd=5;cm("i");
cs="gr2";j=1;k=0;lf=150;of=60;ds="e";cls="S9g7";fns="crb10";jd=5;cm("i");
cs="gr3";j=0;k=1;i=2;lf=PageWidth;of=PageHeight;ds="c";cls="S9y7";fns="crb10";cm("i");
//----------------------------------- gr2 Contents ----------------------------------
cns="gr1"; // gr1 is divided into 4 col,3 rows
cs="lb11";j=0;k=0;oyd=0.6;ds="c";cis="Windows Presentation Foundation Examples";
fns="trb16";cls="r0g7";cm("i");
cs="lb12";j=0;k=1;oyd=0.4;ds="c";cis="Example Number "+PageNumber;
fns="trb12";cls="b0g7";cm("i");
//----------------------------------- gr2 Contents ----------------------------------
cns="gr2"; // gr1 is divided into 4 col,3 rows
cs="im01";j=0;k=0;ds="e";ims="images\\icon.bmp";cm("i");
}
public class helper:pcs {
}
}
----------------------------------------------------------------------------------------------
The master page makes a good exercise on mounting controls. Here are some remarks:
(1) We needed to create two grids one for common data at the top and one to be used by the child
pages. Since we wanted to mount an image, we had to create a third grid for it.
(2) For the grids we must supply either (lf,of) or (oxd,oyd) In this case, we supplied (lf,of)
(3) (i,o) have been left unassigned for all except for the grid which will be used by child
classes (gr3) since it occupies 2 cells horizontally.
(4) For (lb11,lb12), (oxd) has not been assigned since they occupy their entire cell horizonty.
----------------------------------------------------------------------------------------------
Accessing class (pcs) graphics:
-------------------------------
Version 2 has been great for graphics and we have spent plenty of time creating software using
the ".NET" version 2 and both classes (pcs) and (pasp), so we must make use of them here. We
have developed an easy way to access version 2 graphics which requires the following:
(1) You install an Image Control which will receive (bio) with your drawing on.
(2) You create a nested class which extends class (pcs).
(3) You start the nested class by calling gm("wo") meaning "Open a new WPF operation".
(4) You write the same code into the nested class just as you have been.
(5) You end the nested class with calling gm("wc") meaning "Close the WPF operation. This will
modify the resulting (bio) with all the graphics on its surface to meet WPF requirements.
(5) From method run() of the top class you execute the nested class then call cm("su") supplying
it with the image control's keyname. Method cm() at this mode updates an image control by
changing its image. The method expects the new image file name to be assigned to (ims) This
time we assign "b" to (ims) which means to the method that the image is (bio)
---------------------------------------------------------------------------------------------
Example 1: Create a master page then extend it with a class which runs two examples of the
version 2 kind. One is example 1 of "Handling controls" and the other is example 8 of "Drawing".
---------------------------------------------------------------------------------------------
//assembly ms1.exe; // Referenced object.
public class a : ms1 { // using pcs3 // Class a extends master class.
public override void init() {
base.init();
}
public override void setup() {
PageNumber=1;base.setup(); // Call Master Class's setup(), set Page No.
//----------------------------------- gr3 Contents -----------------------------------
cns="gr3"; // gr3 is divided into 2 columns
cs="gr4";cis="g1";j=0;k=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";cm("i");
cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";cm("i");
//----------------------------------- gr4 Contents -----------------------------------
cns="gr4";
cs="tf0";j=k=0;oyd=0.5;ds="c";lf=200;cls="b0y0";fns="trbi20";cm("i");
cs="bt0";cis="My Color Changes";j=0;k=1;oyd=0.5;ds="c";lf=160;of=30;
fns="trb16";cls="S9y0";cm("i");
//----------------------------------- gr5 Contents -----------------------------------
cns="gr5";
cs="im1";ds="c";ims="";lf=200;of=200;cm("i");
} // This is the control which will receive (bio) and scale it
//-------------- Method update is entirely used by color change section ----------------
public override void update() { // This method is called whenever an event takes place
if ("bt0".Equals(cs)) { // If button "bt0" has generated this event
cs="tf0";cm("gu"); // Get latest update of text field "tf0" in (cus)
if (cus.Length<1) return;
os=cus.Substring(0,1);om("u");// Get 1st char of (cus) and covert it to U/C
n="RGB".IndexOf(os); // See if it was R,G,B or none of them
if (n<0) cls="S9y0"; // If none of them select yello background color
else if (n==0) cls="S9r0"; // If was "R" select red background color
else if (n==1) cls="S9g0"; // If was "G" select green background color
else if (n==2) cls="S9b0"; // If was "B" select blue background color
cs="bt0";cm("sC"); // Call method cm() to set the color for "bt0"
cs="tf0";cus="";cm("su"); // Erase tf0's text.
cs="tf0";cm("sx"); // Turn the focus state of "tf0" on.
}
}
//------------------- Method run is entirely used by the Drawing section ---------------
public override void run() {
N no=new N(); // Inistantiate nested class.
no.run(); // Run nested class instance.
cs="im1";ims="b";cm("su"); // Update Image control with (bio) image.
}
//--------------------------------- The nested class -----------------------------------
public class N : pcs { // Class (N) extends (pcs)
public override void run() {
gm("bwo"); // Open a new WPF operation
j=k=220;cm("fs"); // Reduce size to fit object.
cls="y7";gm("ec"); // Make background color matchs page's color.
//********************** No change upto next asterisk line *************************
lf=of=170;gm("ce"); // Create the circular gold plate
cls="o5y5";gm("spl"); // Prepare linear gradient brush for it
gm("grf"); // then render-fill the gold plate.
lf=6;of=50;gm("c="); // Create hexagon shape object at center.
cls="r0";ks="r";gm("grs"); // Draw the object using sp effects-refl at
jf=45;cls="b0";ks="r";gm("grs"); // center in red, then repeat 6 times using
jf=-45;cls="b0";ks="r";gm("grs"); // different colors and different locations
jf=22;kf=40;cls="g0";ks="r";gm("grs");
jf=-22;kf=40;cls="m0";ks="r";gm("grs");
jf=22;kf=-40;cls="m0";ks="r";gm("grs");
jf=-22;kf=-40;cls="g0";ks="r";gm("grs");
lf=of=25;gm("ce"); // Create a circle at center (pearl)
for (int x=0;x<20;x++) { // Draw it 20 times using sp effects-reflection
jf=80;kf=18*x;kb=true; // at locations around the plate. Polar coord's
cls="p0";ks="r";gm("grs"); // are used for specifying locations
}
//***********************************************************************************
gm("bwc"); // Close WPF operation
}
}
}
==============================================================================================
Compiling:
==========
You may compile the class with either the Visual Studio or tool (pcp) If you receive an error
when you compile with our tool, select "PC# Reference-Desktop" and read the section titled
"The refs.txt file" to learn how to correct the problem.
Notice how the class declaration statement contains the word "pcs3" following a comment symbol.
public class a : ms1 { // using pcs3 // Class a extends master class.
When you compile a class using one PC# tool, the tool checks to see if your class extends class
(pcs3) If it does, it reads the file "system\\Refs.txt" which is located into your working
directory and adds all the locations it contains.
This time your class extends class (ms1), but since (ms1) extends (pcs3), you want the tool to
do the same. The tool is made to merely look for the availability of the word "pcs3" at the
class declaration line in order to conclude whether to add the locations or not. So including
the word "pcs3" into any commented phrase on the same line can make it do the same.
----------------------------------------------------------------------------------------------
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 Slider Control:
===================
The WPF controls can be accessed with method cm() using the same code which you have been using
to work with Form-based controls. The only difference is in the layout method which is similar
to the one class (pasp) uses to mount web controls.
One control which has been always neglected is the "Slider Control". It was assumed to be
unnecessary, but we need it now since it can help with 3D graphics. The slider control's
installation parameters (not including the mounting parameters which have already been
discussed) are:
cis : The control's label. Will be discussed in details next.
lf,of : Width or Height of the control. This also sets the orientation of your wanted slider
control. If you want a horizontal one, specify (lf) and keep (of=0) If you want a
vertical one, specify (of) and keep (lf=0)
js : Minimum and Maximum values. They are the two values you receive from the slider
control when it's at its two extreme positions.
cus : Contains the initial value where you like the pointer to start at.
Handling the Slider Control's event:
------------------------------------
Each time the slider control is activated, method update() is called with the slider's keyname
and the value which its pointer is at assigned to (od)
Control's Labels:
=================
We normally like to place a label near any control in order to identify it. For this reason,
PC# allows you to specify a label to almost all controls. You assign the label to (cis) and
assign to (os) a direction code which can be (n/s/e/w) which determines where the label
should be relative to the control. The positions are as follows:
n: (Default) Place label above control.
s: Place label under control.
e: Place label to the right of control.
w: Place label to the left of control.
Labels are placed very close to their controls. You can seperate them from their controls by
any distance by adding spaces or line feeds before or after the label's text depending on the
direction code which you have specified. Here are two examples:
cis="Label\n";os="n"; // Place the label above control seperating the two with a linefeed.
cis=" Label"; os="e"; // Place the label right of control seperating the two with space.
In class (pasp), labels have been doing great jobs. HTML has been possible to use with them
so we have been able to use them to add several lines of text, tables, hyperlinks with variety
of colors and fonts. Here we don't have all this luxury, but we can still insert linefeeds
into a label's text to make it a multi-line one as we'll do with the slider's label in the next
example.
The Labels are displayed using the same font which is assigned to the control and the color
which is assigned to the control's foreground.
Using WPF 2D Graphics:
======================
Just like we made method cm() usable without changes for the WPF, we like to make method gm()
also usable with minimum changes.
We already know that we can import version 2 graphics to our WPF classes, but it should be
better if we can use the WPF itself to do the job. In fact, if you intend to place your WPF
classes on the web, you can't use class (pcs)
To receive version 2 graphics, we used an Image control, we'll do the same with the WPF
2D graphics. This requires setting the graphical output device to the Image control which you
like to draw into. You do that by assigning the keyname of the control to (cs) and calling
gm("sdi") When your drawing is complete, you call gm("fdi") which means "Finish drawing to the
image control device". Whatever you draw will not show up until this method is called.
Method gm()'s new parameters:
-----------------------------
The WPF does not use type "float" which has been used with version 2 graphics, it uses type
double instead. Therefore, we have also switched method gm()'s input parameters to their
equivalent type double ones. This means that:
jd,kd replace jf,kf
id,od replace lf,of
Some of the values required but not extensively used, have been assigned to variables of type
"double" in Version 2. As an example, (jd,kd), (id,od) have been used to supply scaling and
shearing factors when setting up an Affine Transform object. Those valuse are now assigned to
(jf,kf), (lf,of) instead. In other words, when we use the WPF for drawing, we use an opposite
logic in selecting variable types.
=============================================================================================
Example 2: Create a slider control which changes values in the range (+5:-5) Set its initial
value at zero. Create a text field and make it constantly show the value which the slider's
pointer is at.
Also create an Image control and use it to display an image and several shapes. Show how
to fill a shape with a texture brush also.
=============================================================================================
//assembly ms1.exe;
public class a : ms1 { // using pcs3
public override void init() {
base.init();
}
public override void setup() {
PageNumber=2;base.setup(); // Execute master class's setup() supplying it with page no.
//----------------------------------- gr3 Contents -----------------------------------
cns="gr3"; // gr3 is divided into 2 col's.
cs="gr4";cis="g1";j=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";cm("i");
cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";cm("i");
//----------------------------------- gr4 Contents -----------------------------------
cns="gr4"; // gr4 is divided into 2 rows.
cs="tf0";cis="Display";os="s";k=0;ds="c";oyd=0.5;lf=60;cus="0.00";cls="b0y0";fns="trb22";
jd=3;cm("i"); // jd=3 means set a 3 pixels border around control.
cs="sl0";cis="Slide pointer and watch Display\n -5 0 +5";os="n";
k=1;oyd=0.5;js="-5,5";cus="0";lf=200;ds="c";cls="S9b2";fns="crb12";cm("i");
//----------------------------------- gr5 Contents -----------------------------------
cns="gr5"; // gr5 is not divided.
cs="im1";cis="\n2D Drawings";os="s";ds="c";ims="";lf=200;of=200;fns="trb16";cm("i");
}
//------------------------------- Handling slider events ------------------------------
public override void update() {
if ("sl0".Equals(cs)) { // If Slider "sl0" has generated this event
ib=true;om("fd"); // Convert (od) to string reducing decimal digits to two.
cus=os;cs="tf0";cm("su"); // and set it into text field control.
}
}
//----------------------------- Drawing into Image control ----------------------------
public override void run() {
cs="im1";gm("sdi"); // Make Image Control "im1" the graphical output device.
kd=-35;id=od=80;fls="images\\flower.jpg";gm("cid");
// Display an image 35 pixels under center.
fls="images\\flower.jpg";gm("blf");gm("spt");
// Create a new texture brush using the same image.
kd=30;id=3;od=90;gm("c=f"); // Create equally sided triangle and fill it with the brush
// Place it 30 pixels above center.
cls="y0";id=3;gm("sps"); // Create a 3 pixels wide pen of yellow color.
id=6;od=100;gm("c=d"); // Create Hexagon and draw it at center.
cls="r0";id=1;gm("sps"); // Create a new pen/brush of solid red color.
id=od=150;gm("crd"); // Create a new Rectangle and draw it at center.
cls="b0";id=1;gm("sps"); // Create a new pen/brush of solid blue color.
id=od=150;gm("ced"); // Create a circle and draw it at center.
gm("fdi"); // Finish drawing into the specified Image control.
}
}
==============================================================================================
|
 |
===============================================================================================
Are we really drawing in pixels?
================================
No. We are just pretending we are! All drawings are relative, but we are trying to make our
drawings compatible with class (pcs)'s drawings. We try to think in pixels, just like we used
to do with class (pcs)
Some people like to use small numbers like 1 or 2 for the widths and heights of the objects they
draw. This is not very good since it teachs us to neglect accuracy and also because the default
width of a pen is still one (it used to be one pixel in class pcs) However, the variable which
you assign the pen's width to when you create a pen is now (id) which is of type double, so it
can be assigned a fraction.
More about the WPF 2D Graphics:
===============================
There is almost no difference between the PC# methods which operates on Version 2 graphics
and the ones which operate on WPF 2D graphics other than the types of used variables.
Drawing lines, Shapes, images and text:
---------------------------------------
jd,kd : Location of object's center relative to graphical output device's center.
id,od : Width and height of shape.
lf,of : Shear factors if necessary
ad : Rotation angle in degrees if necessary.
For lines, (jd,kd) are coord's of start point and (id,od) are coordinates of end point. For
text, (jd,kd) represent location of text center. Font code (fns) and text to draw (os) are also
required. For images, you need to supply their center location in (jd,kd) and their desired
width and height assigned to (id,od) in addition to the file name or URL assigned to (fls)
All modes are the same.
Affine Transform:
-----------------
jd,kd : Present location of object's center relative to graphical output device's center.
jf,kf : Scale factors.
lf,of : Shear factors.
id,od : Translation. Required displacement of object.
ad : Rotation angel.
You set the unit transform by calling gm("stu") To apply it to a shape create the shape without
drawing then call gm("gtd") to draw it or gm("gtf") to fill it after being transformed.
As explained before, we use the transformation order: Scale - Shear - Rotate - Translate. So,
you must plan all your object transformations accordingly. Note that the first 3 operations are
performed around object's center without changing the center's location.
3D Effects:
-----------
Both the "Reflection" and "Depth" 3D effects are available without changes other than variable
types which are:
REFLECTION: jd,kd: Desired location of object center relative to Graphical device's center
cls : Wanted base color
of : Brightness factor.
DEPTH : id : Depth.
ad : 3D inclination angle relative to negative X-axis.
cls : Combined code of brightest-Darkest colors.
Shapes must be created at CENTER prior to applying 3D effects to them.
==============================================================================================
Example 3: Rewrite examples 7 and 8 of the "Drawing" chapter to get them to run using the
Windows Presentation Foundation (WPF) software.
==============================================================================================
//assembly ms1.exe;
public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3"
public override void init() {
base.init();
}
public override void setup() {
PageNumber=3;base.setup(); // Execute master class's setup() supplying it with page no.
//----------------------------------- gr3 Contents -----------------------------------
cns="gr3"; // gr3 is divided into 2 col's.
cs="gr4";cis="g1";j=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";cm("i");
cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";cm("i");
//----------------------------------- gr4 Contents -----------------------------------
cns="gr4"; // gr4 is not divided.
cs="im1";cis="\n3D Effects, Reflection";os="s";ds="c";ims="";lf=200;of=200;fns="trb16";cm("i");
//----------------------------------- gr5 Contents -----------------------------------
cns="gr5"; // gr5 is not divided.
cs="im2";cis="\n3D Effects, Depth";os="s";ds="c";ims="";lf=200;of=200;fns="trb16";cm("i");
}
public override void run() {
//-------------------------------- Drawing the Jewel ------------------------------------
cs="im1";gm("sdi"); // Set (im1) as graphical output device.
cls="y0";gm("sps"); // Create yello brush
id=od=195;gm("crf"); // and paint background with it.
//-------------------------------- Drawing the object -----------------------------------
id=od=170;gm("ce"); // Create the circular gold plate
cls="o5y5";gm("spl"); // Prepare linear gradient brush for it
gm("grf"); // then render-fill the gold plate.
id=6;od=50;gm("c="); // Create hexagon shape object at center.
of=1;cls="r0";ks="r";gm("grs"); // Draw the object using sp effects-refl at
of=1;jd=45;cls="b0";ks="r";gm("grs"); // center in red, then repeat 6 times using
of=1;jd=-45;cls="b0";ks="r";gm("grs"); // different colors and different locations
of=1;jd=22;kd=40;cls="g0";ks="r";gm("grs");
of=1;jd=-22;kd=40;cls="m0";ks="r";gm("grs");
of=1;jd=22;kd=-40;cls="m0";ks="r";gm("grs");
of=1;jd=-22;kd=-40;cls="g0";ks="r";gm("grs");
id=od=25;gm("ce"); // Create a circle at center (pearl)
for (int x=0;x<20;x++) { // Draw it 20 times using sp effects-reflection
jd=80;kd=18*x;kb=true; // at locations around the plate. Polar coord's
of=1;cls="p0";ks="r";gm("grs"); // are used for specifying locations
}
gm("fdi"); // Finish drawing to (im1)
//-------------------------------- Drawing the 3D Text ---------------------------------
cs="im2";gm("sdi"); // Set (im2) as graphical output device.
cls="b0";gm("sps"); // Create blue brush
id=od=195;gm("crf"); // and paint background with it.
os="PC#";fns="trb100";gm("ct"); // Create text geometry.
ks="d";ad=30;id=10;cls="s9s0";gm("grs"); // Draw text using 3D effects-depth
gm("fdi"); // Finish drawing to (im2)
}
}
==============================================================================================
|
 |
===============================================================================================
PLAYING VIDEO
=============
The new method vm():
====================
Method gm() of class (pcs3) which has been written initially for classes (pcs) and (pasp) has
been modified to fit the WPF 2D Graphics and will be used for this purpose. The new method vm()
will handle the new features of the WPF like video and 3D Drawing.
Playing video files:
====================
The image control which has proven to be a useful graphical output device which can display
all shapes and images can also show movies. So, to play a movie, you need to create an image
control and call method gm("sdi") with its keyname to start and to call gm("fdi") to finish at
the end.
After setting the graphical output device, you call vm("mvo") to open a new video play operation,
then you load the movie file by supplying its URL or its local file path (assigned to fls) to
method vm("mvl") You can then call vm("mvp") to play the movie and call vm("mvc") to close and
execute the video play operation. Here are the most useful video modes:
mvo : Open a new video play operation.
IN: id,od (optional) width,height of movie display rectangle.
mvl : Load video file/URL (assigned to fls)
mvp : Play
mvu : Pause
mvs : Stop
mvv : Set volume. IN: id=wanted volume (range is 0:1)
mvb : Set Balance. IN: id=wanted balance (range is -1:1)
mvc : Execute and Close current video play operation.
===========================================================================================
Example 4: Show how to play a video file and how to set the play volume and balance. Show
also how to stop it and pause it.
===========================================================================================
We used the video file "bear.wmv" which is a short movie file sample that comes with Windows
Vista. You need to search for it into your computer or search for any file with same
extension, then to copy the file to your working directory before running this example.
If you'll be using a different file, modify the line marked with **** with the new file name.
-------------------------------------------------------------------------------------------
//assembly ms1.exe;
public class a : ms1 { // using pcs3
public override void init() {
base.init();
}
public override void setup() {
PageNumber=4;base.setup(); // Execute master class's setup() supplying it with page no.
//----------------------------------- gr3 Contents -----------------------------------
cns="gr3"; // gr3 is divided into 2 col's.
cs="gr4";cis="g1";j=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";cm("i");
cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";cm("i");
//----------------------------------- gr4 Contents -----------------------------------
cns="gr4"; // gr4 is divided into 3 col's and 3 rows.
cs="sl0";cis="Volume Control\n0 5 10";os="n";k=0;i=3;oyd=0.33;
js="0,10";cus="5";lf=232;ds="c";cls="S9b2";fns="crb12";cm("i");
cs="sl1";cis="Balance Control\nL 0 R";os="n";k=1;i=3;oyd=0.33;
js="-5,5";cus="0";lf=232;ds="c";cls="S9b2";fns="crb12";cm("i");
cs="bt0";cis="Play";j=0;k=2;ds="c";oxd=0.33;oyd=0.34;cls="s9G1";fns="trb22";jd=3;cm("i");
cs="bt1";cis="Pause";j=1;k=2;ds="c";oxd=0.33;oyd=0.34;cls="s9Y1";fns="trb22";jd=3;cm("i");
cs="bt2";cis="Stop";j=2;k=2;ds="c";oxd=0.34;oyd=0.34;cls="s9r0";fns="trb22";jd=3;cm("i");
//----------------------------------- gr5 Contents -----------------------------------
cns="gr5"; // gr5 is not divided.
cs="im1";cis="\nVideo";os="s";ds="c";ims="";lf=200;of=200;fns="trb16";cm("i");
}
public override void update() {
if ("sl0".Equals(cs)) { // If volume control activated:
id=od/10;vm("mvv"); // Adjust value to be (0:1) then set volume
}
else if ("sl1".Equals(cs)) { // If balance control activated:
id=od/5;vm("mvb"); // Adjust value to be (-1:1) then set balance
}
else if ("bt0".Equals(cs)) { // If Play button clicked:
vm("mvp"); // Play movie.
}
else if ("bt1".Equals(cs)) { // If Pause button clicked:
vm("mvu"); // Pause play.
}
else if ("bt2".Equals(cs)) { // If Stop button clicked:
vm("mvs"); // Stop play.
}
}
public override void run() {
cs="im1";gm("sdi"); // Set (im1) as graphical output device.
vm("mvo"); // Open a new video play operation.
fls="bear.wmv";vm("mvl"); // **** Load movie file
id=1;vm("mvv"); // Set volume.
id=0;vm("mvb"); // Set balance
vm("mvc"); // Execute and close video play operation.
gm("fdi"); // Finish drawing to (im1)
}
}
==============================================================================================
|
 |
==============================================================================================
Playing Audio:
==============
Method um() contains modes to play audio files of type "wave". This is possible to use in both
classes (pcs) and (pcs3) Here is an example which you can try:
public override void run() {
um("awo"); // Open a new audio-wave file play operation
fls="c:\\windows\\media\\chimes.wav";um("awl");// Load audio file.
kb=true;um("awp"); // Play file continuously.
}
New option for drawing with 3D Effects:
=======================================
When you use the 3D effects-depth you can select a new style which makes you feel that the
drawing grows in size as it moves forward. You can get that style by adding (ib=true) to your
parametrs. See next example.
New variables which you need to know about:
===========================================
As you must know, one of the objectives of Personal C. Sharp is to minimize the number of
variables and methods which you use in order to make your job easy and to reduce the chance
for errors.
However, handling the new 3D classes of the WPF has made it necessary to add two more variables
which are (ld) and (dd), both are of type "double". This addition applies to class (pcs3) only.
(jd,kd,ld) will be used as the 3D coordinates of a point or a vector.
(id,od,dd) will be used as (width,height,depth) of a 3D object.
Please make sure not to use those variable names as your local variables. Here is a list of
all the variables which have been borrowed from your pre-defined "one char + type" list:
cs: Control Keyname.
fs: File Keyname.
ds: Direction code.
ls: Replaces the GUV (is) since (is) is a C# keyword.
ad: Angle.
ld: 3rd dimension (z) (Class pcs3 only)
dd: Depth. (Class pcs3 only)
We hope we'll not need more. Please make sure not to use any of these names as local var's.
Drawing a Rectangular Block:
============================
If you look at the chapter on "Drawing", you'll see examples which show how to create a
Rectangular block by assembling its outer surfaces together and how to draw on those surfaces.
You have also seen examples on how to draw cylinders and pyramids of general bases.
One objective in developing those drawings is to have 3D objects which can automatically
supply the WPF 3D drawing classes with their mesh pattern data. Now it's time to rewrite the
methods which create those 3D objects using WPF's software so we can use them.
The Rectangular block has been added to the cylinder and pyramid as a 3D object to request.
You request drawing a rectangular block by calling gm("cRd") Here are all necessary parameters:
jd,kd : location of Rectangular Block's center relative to Form's center.
id,od,dd: Width,Height,Depth.
cls : Combined color code for "visible lines" and "hidden lines".
ib : "Show hidden lines" flag. Hidden lines will be displayed when (ib=true)
==============================================================================================
Example 5: Show how to use the new 3D effects feature. Also show how to draw a rectangular
block of dimensions 40 X 80 X 50. Make hidden lines appear.
==============================================================================================
//assembly ms1.exe;
public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3"
public override void init() {
base.init();
}
public override void setup() {
PageNumber=5;base.setup(); // Execute master class's setup() supplying it with page no.
//----------------------------------- gr3 Contents -----------------------------------
cns="gr3"; // gr3 is divided into 2 col's.
cs="gr4";cis="g1";j=0;oxd=0.5;ds="w";cls="S9y7";fns="tr12";cm("i");
cs="gr5";j=1;k=0;oxd=0.5;ds="e";cls="S9y7";fns="crb10";cm("i");
//----------------------------------- gr4 Contents -----------------------------------
cns="gr4"; // gr4 is not divided.
cs="im1";cis="\n3D Effects, Depth";os="s";ds="c";ims="";lf=250;of=250;fns="trb16";cm("i");
//----------------------------------- gr5 Contents -----------------------------------
cns="gr5"; // gr5 is not divided.
cs="im2";cis="\nRectangular Block";os="s";ds="c";ims="";lf=250;of=250;fns="trb16";cm("i");
}
public override void run() {
//------------------------------- 3D Effecte - Depth ----------------------------------
cs="im1";gm("sdi"); // Set (im1) as graphical output device.
cls="b6";gm("sps"); // Create light blue brush
id=od=250;gm("crf"); // Paint background with it.
jd=20;kd=-30;os="PC#";fns="trb80";gm("ct"); // Create text geometry.
ks="d";ad=30;id=100;ib=true;cls="s9s0";gm("grs");
// Draw text using 3D effects-depth with scaling
gm("fdi"); // Finish drawing to (im1)
//--------------------------- Drawing the Rectangular Block ---------------------------
cs="im2";gm("sdi"); // Set (im2) as graphical output device.
cls="b6";gm("sps"); // Create light blue brush
id=od=250;gm("crf"); // Paint background with it.
id=40;od=80;dd=50;ib=true;cls="S9s0";gm("cRd");
// Create 40X80X50 Rect Block, show hidden lines
cns="yz-20"; // Container: Parallel to yz plane at x=-20
cls="r0";gm("sps"); // Create red pen
fns="trb20";os="PC#";gm("ctf"); // Draw the string on that plane.
gm("fdi"); // Finish drawing to (im2)
}
}
==============================================================================================
|
 |
===============================================================================================
3-D DRAWING using the WPF
Level I
=========================
(Requires .NET v3.5 and PC# v3.0)
The New WPF 3D Module:
=====================
What makes the new 3D module of the WPF interesting is the following new features:
(1) It allows viewing the 3D object from any direction and at any distance. What you see on the
screen is actually the picture which a camera picks up. the type, position and direction
of that camera are settable.
(2) The color and shades which you see depend on the lights around the object. You can choose
from a variety of light types placed at whatever positions you like.
(3) The color which the object appears at depends also on the type of material it's made of,
how reflective it is to light in general and to each light color in particular.
Data required by the WPF 3D module:
==================================
The WPF 3D module can help you viewing a 3D object under variety of conditions, transforming the
object and animating it if you like, but it does not help you in creating the object. It
requires that you supply it with data regarding an object which you know to start with. This
data includes the 3D location of (at least) each vertex of the object and a mesh pattern data.
1 0
+----------------+
| /|
| / |
| / |
| / |
| / |
| / |
| / |
| / |
| / |
| / |
| / |
| / |
| / |
| / |
| / |
|/ |
+----------------+
2 3
If the object was a rectangle like this one, the data required would be the 3D coordinates of
the 4 points (0,1,2,3) and an index array which can be (0,1,2,0,2,3) what these 6 numbers
mean is that the object is made of 2 triangles, if we list the points which make each triangle
moving counter-clockwise, the two lists would be (0,1,2) and (0,2,3)
This seems to be easy when it is in 2D plane. If the rectangle, has been in 3D space making a
different angles with each of the three 3D planes, things could have been harder. If it was a
more complicated shape, the difficulty could have been higher.
We have developed a software which allows drawing rectangular blocks, cyliders and pyramids
with general bases. They can be drawn using class (pcs)'s graphics module and can also be drawn
using the WPF's one. Additionally they can automatically supply the WPF 3D module with their
points and triangle indices data (the mesh pattern)
Personal C Sharp simplification:
================================
The most difficult part is the mesh pattern data described above which is now "no work" if you
limit your 3D objects to Rectangular blocks, cylinders and pyramids which we know that you can't
do. Fortunately, this difficulty is limited to Level I and will be eliminated when you reach
Level II.
The data is supplied to PC# software in the form of 4 arrays as follows:
PXD[] An array of type "double" which contains the X component of each point's coordinates.
PYD[] An array of type "double" which contains the Y component of each point's coordinates.
PZD[] An array of type "double" which contains the Z component of each point's coordinates.
IX[] An array of type "int" which contains the indices of the points of all triangles which
make the shape in the order described above.
If you like to use one of our 3D objects, for example a pyramid with hexagonal base sitting on
a plane which is parallel to the YZ plane with enclosing circle diameter of 100 and a height of
150, you would use the code:
id=6;od=100;dd=150;js="yz";gm("cPv");
This will create the required 4 arrays for you. The last char in the mode string 'v' means
create vertex arrays only (Don't draw or fill the generated object)
If you are wondering how large these arrays are, in the next example we'll create the vertex
arrays of a hexagonal cylinder, circular cylinder and a pyramid with a 10 sides base. The total
of all numbers to be supplied exceeds 1,000.
The PC# defaults:
-----------------
The basic 3 setups which you need to do after supplying the "mesh" data are "lights setup",
"object's color and material setup" and "camera setup". We are not going to get into details
regarding them now. However the default settings which PC# will set for you if you don't supply
data for the 3 items are:
(1) A combination of 3 lights will be available. One directional light at the top of the object,
one directional light coming from the upper, left corner (at z=maximum) direction toward
the center and one ambient light.
(2) A perspective camera sitting at the upper, right (at z=maximum) corner and directed toward
the center where the object is expected to be. The camera has a field of view of 60 degrees.
(3) The object is assumed to be made of light diffusing material and colored with our default
brush which is black for the foreground and white for the background.
How to draw 3D objects:
-----------------------
(1) Call method gm("cRv"), gm("cCv") or gm("cPv") to create a rectangular block, a cylinder or
a pyramid in order to generate the point arrays PXD[], PYD[], PZD[] and IX[]. If you like
to draw a different object, you need to assign data to these arrays by yourself.
(2) If you accept all defaults and like the object to be centered into the Graphical Output
device, all you need to do is to call vm("cof") to create the object and fill it. If you
want to create the object only, call vm("co") Then you can render it later and fill it with
vm("grf") or transform it and fill it with vm("gtf")
If you don't like the object's center to be at the origin, assign values to (jd, kd and ld)
If you have more than one object to draw, repeat the two steps for each and assign different
values to (jd,kd,ld) in (2) in order to seperate them.
This is just the beginning. There are plenty of items to discuss, but let us have an example
before we get further.
==============================================================================================
Example 6: Draw a pyramid with 10 sides base which is parallel to the xz plane, a cylinder with
circular base which is parallel to the yz plane and a cylinder with hexagonal base which is
parallel to the xy plane. Use different color for each.
==============================================================================================
//assembly ms1.exe;
public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3"
public override void init() {
base.init();
}
public override void setup() {
PageNumber=6;base.setup(); // Execute master class's setup() supplying it with page no.
//----------------------------------- gr3 Contents -----------------------------------
cns="gr3"; // gr4 is not divided.
cs="im1";cis="\n3D Graphics";os="s";ds="c";lf=560;of=250;fns="trb16";cm("i");
}
public override void run() {
cs="im1";gm("sdi"); // Set (im1) as graphical output device.
id=10;od=150;dd=150;js="zx";gm("cPv"); // Create the pyramid with 10 sides
cls="S9g4";js="d";vm("cof"); // Use green color for background and
// create-fill diffuse material object
id=40;od=100;dd=100;js="zy";gm("cCv"); // Repeat with the round (40 sides) base
jd=160;kd=30;ld=-50;cls="S9b4";js="d";vm("cof"); // cylinder. use blue color.
id=6;od=170;dd=170;js="yx";gm("cCv"); // Repeat with the hex cylinder using
jd=-300;kd=-125;ld=50;cls="s9r4";js="d";vm("cof");// red color for background.
gm("fdi"); // Finish Image control drawing operation.
}
}
==============================================================================================
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
|
 |
===============================================================================================
If you don't like the screen shot of last example, don't get disappointed. The poor graphics
are caused by sending the images using "jpg" compressed formula through the internet. If you
run the code on your computer all colors and shades will look fine. The edge lines of the
hexagonal cylinder and pyramid will look more distinctive and the circular cylinder will look
smoother.
How could edges appear for the hex cylinder and pyramid but not for the circular cylinder?
------------------------------------------------------------------------------------------
We have designed the default light positions and directions so that object edges become
distinctive. Despite that the circular cylinder which is actually made of a 40 sides polygon
base shows with no recognizable edges. What did we do to make this possible?
Each vertex of a cylinder is shared among 3 planes, the base plane and two side planes. If we
want the edges to show, we need to list each vertex location in arrays PXD[],PYD[],PZD[] 3 times
so that the points which make the triangles of each plane are defined seperately in array IX[].
If we like the side planes to mix making a continuous round surface, we list each vertex twice
only. Once for the base and once for the other two planes together.
Whenever you create a cylinder or a pyramid with a base which is made of 25 sides or more, we
assume that you want a circular object, so we list each vertex twice. Otherwise, we list each
vertex 3 times.
Why do we need two combined color codes for 3D objects?
-------------------------------------------------------
Whenever we use two combined color codes for an object we usually mean that the first one is
for the foreground and the second one is for the background. This applies to 3D objects too.
However, when we set the color of a 3D object's surface, we are expected to specify the color
of the back of that surface too. This color is normally never seen except if the object is only
a surface with no volume like a triangle or a rectangle or if the camera is inside the object!
We use the foreground color specified in the combined color code to paint the background of that
back surface with.
We said that if the camera was inside an object which has volume like a cylinder or a cube, it
will see everything painted in the color which is set as the color of the back side. How is
that possible? You can believe it if you assume that a 3D object is considered to be a hollow
shell which is painted inside and outside with different colors.
Can we reverse one of the triangles which define an object's surface so it appears in the back
surface color? Yes. All we need to do, is to reverse the indices sequence for that triangle
in array IX[] so its point listings become in clockwise order instead of counter-clockwise.
About the next example:
-----------------------
Can we have an example to illustrate that? Yes, in the next example we'll move the camera using
a slider control so we can see a triangle and a rectangle at both their front and back sides.
We are also going to make the camera penetrate inside a cylinder to look inside it. Additionally
we'll modify array IX[] for the rectangle so that the points of one of its composing triangles
become listed in a clockwise order causing that triangle to appear in reverse side color.
We are going to be using the green color for the outside of all objects and the red color for
the back (or the inside) of all objects.
The triangle and rectangle are actually a pyramid and a cylinder of a single line base. The
line will be vertical alongside the Y axis. We'll then move the triangle along the X-axis to
point (-150) and also move the rectangle along the X-axis to point (+150)
==============================================================================================
Example 7: Create a triangle at the left, a rectangle at the right and a cylinder at the center
paint them all green with reverse side in red. Move the camera using a slider control on a line
which is on the xz plane parallel to the x-axis at z=190. The camera will be moving between the
two extreme points where (x=-250) and (x=250) Set the z position of the cylinder so that its
outer-most point extends in the z direction to the point z=200. This means that when the camera
is at its center point it will be inside the cylinder.
==============================================================================================
//assembly ms1.exe;
public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3"
public override void init() {
base.init();
}
public override void setup() {
PageNumber=7;base.setup(); // Execute master class's setup() supplying it with page no.
//----------------------------------- gr3 Contents -----------------------------------
cns="gr3"; // gr3 is divided into 2 rows.
cs="gr4";k=0;oyd=0.8;ds="n";cls="S9y7";fns="tr12";cm("i");
cs="gr5";j=0;k=1;oyd=0.2;ds="c";cls="S9y7";fns="crb10";cm("i");
//----------------------------------- gr4 Contents -----------------------------------
cns="gr4"; // gr4 is not divided.
cs="im1";os="s";ds="c";lf=560;of=250;fns="trb16";cm("i");
//----------------------------------- gr5 Contents -----------------------------------
cns="gr5"; // gr5 is not divided.
cs="sl0";cis=" Camera X Position\n-250 -200 -150 -100 ";
cis+="-50 0 +50 +100 +150 +200 +250";os="n";
js="-5,5";cus="0";lf=500;ds="c";cls="S9b2";fns="crb12";cm("i");
}
public override void update() {
if ("sl0".Equals(cs)) { // If Slider "sl0" has generated this event
jd=od*50;kd=0;ld=190;vm("scp"); // Apply received value to camera's X postion.
}
}
public override void run() {
cs="im1";gm("sdi"); // Set (im1) as the graphical output device.
cls="b0";id=2;gm("sps");
id=558;od=235;gm("crd");
id=1;od=150;dd=100;js="xy";gm("cPv"); // Get the vertex data for the triangle
jd=-150;cls="r0g0";js="d";vm("cof"); // Draw the triangle at point (-150,0,0)
id=40;od=100;dd=200;js="xy";gm("cCv"); // Get the vertex data for the circular cylinder
ld=100;cls="r0g0";js="d";vm("cof"); // Draw it at point (0,0,100)
id=1;od=150;dd=100;js="yx";gm("cCv"); // Get the vertex data for the rectangle
// The 2nd triangle indices occupy the 3 rows (IX[3]:IX[5]) Exchanging values of first
// and second rows changes point order from counter-clockwise to clockwise.
n=IX[3];IX[3]=IX[4];IX[4]=n; //Exchange contents of IX[3] and IX[4]
jd=150;cls="r0g0";js="d";vm("cof"); // Draw rectangle at point (150,0,0)
//---- Camera setup ----
os="0,0,190"; // Camera position = Point (0,0,190)
js="0,0,-1"; // Look direction = from +ve (Z) to -ve (Z)
ks="0,1,0"; // Up Direction = +ve (Y)
id=120; // Field of view angle=120 degrees.
vm("ccp"); // Create Perspective camera.
gm("fdi"); // Finish Image Control drawing operation.
}
}
----------------------------------------------------------------------------------------------
Notice the following when you run this program:
(1) When the camera is at the center, it's inside the cylinder so the entire view becomes red
and the rear end of the cylinder becomes visible as a far away circle.
(2) When you move the camera to the negative side and start passing the point (-50), you
suddenly see the cylinder in green and also the triangle appears in green. The camera is
now outside the cylinder.
(3) As you continue moving the camera in the negative direction, the green triangle shrinks
horizontally, turning into a vertical line and at point (-150) it totally disappears.
(4) When you pass (-150) the triangle reappears in red and it keeps growing in size until the
camera reachs the end position in the negative direction.
(5) If you return to the center then move to point (+50), the cylinder will become green and
the rectangle shows up as two triangles, one green and one red.
(6) The rectangle keeps shrinking in size to a vertical line and disappear at point (+150)
(7) Whe you pass this point, the rectangle starts showing up, but the colors of its 2 triangles
become reversed.
(8) As you move further in the poitive direction, the rectangle grows in size until the other
camera extreme point is reached.
==============================================================================================
|
 |
===============================================================================================
Material Types:
===============
In all the past examples we have been selecting "Diffuse" material for all 3D objects. This has
been done by making the assignment (js="d") when creating the objects. Actually, this assignment
has not been necessary since it's the default. In general, there are 3 types of material which
we can select:
d Diffuse Material. This is the default and most common. It means a material which diffuses
light like fabrics, wood, regular wall paints,..etc.
e Emissive Material. Make this selection when you like the object to shine.
s Specular Material. You need to practice with this selection to see if it can be of use to
you.
It seems that you can't use selection (e) or selection (s) alone. You must use them as top layers
with a layer of type (d) underneeth. If you don't do that, material of type (e) appears white
regardless to the color which you specify and material of type (s) appears transparent!
In the next example, we are going to be creating 3D objects and drawing on them. This makes it
perfect for practicing with material type selections.
Generalizing the graphical output device:
=========================================
The image control has been successfully generalized so that it can now do the following:
(1) Draw all 2D Graphics including text.
(2) Draw Images.
(3) Play movie.
(4) Display all the WPF 3D graphics.
You can also mix more than one of the drawing tasks above into one image control.
You make an image control the graphical output device by assigning its keyname to (cs) and
calling gm("sdi") You can then draw any combination of the 4 drawing types listed above, then
call gm("fdi") to finish the Image control drawing operation. The 2D drawings will be done first
followed with the 3D drawings unless you make the assignment (jb=true) before calling gm("fdi")
See remark (2) for more details.
The Visual Brush:
=================
The second Graphical Output Device after the Image Control is the Visual brush. You set it by
calling gm("sdb") then you can draw either geometric shapes and images or 3D objects. When
done, you call gm("fdb") to finish the brush drawing operation. After that you can apply the
brush to either a 2D or a 3D object's surface to paint it with the drawings.
You may repeat the operation more than once to paint the same surface with variety of drawings.
REMARKS:
-------
(1) Any object which you draw on the visual brush must have a set size which is smaller than or
equal to the size of the surface which will receive the drawings.
(2) There are two categories of graphics, "2D graphics, images and video" make one category and
3D graphics make another category. The objects used for each of the two categories are
not the same. This should not be of concern to you unless drawings from the 2 different
categories are going to be covering each other. In this case, you need to specify which one
should be on the top. The default is the 3D drawings. If you like a drawing of the first
category to be on the top while the image control is the graphical output device, make the
assignment (jb=true) before calling gm("fdi)
With the Visual Brush, you cannot draw graphics of both categories at the same time. However,
you can start a visual brush drawing operation, draw graphics of one category on the brush,
finish the operation and paint the target object with the brush, then you can repeat the
whole operation to paint the same target again with drawings of different category.
Drawing on a 3D object's surface:
=================================
In the "Drawing" chapter, you have seen examples on how to draw on 3D planes. Can we do the same
using the WPF 3D module. Yes we can. The process is not exactly the same, but the results are the
same.
In the past examples, you have been drawing each 3D object in two steps:
(1) Obtaining the mesh data for an object by using method gm("cRv"), gm("cCv") or gm("cPv) to
get the data for a Recatangular block, Cylinder or a Pyramid (respectively)
(2) Calling method vm("cof") supplying it with the color assigned to (cls) and the material type
code assigned to (js) to create the 3D object.
The typical line of code for step (2) has been:
cls="r0g0";js="d";vm("cof"); // Draw front surface with green solid brush and back
// surface with solid red brush. Use diffuse material.
The brush type was not specified since we wanted solid brush which is the default. If we want
to specify a different brush, we should assign to (ks) one of the codes "s", "l", "r", "t" or
"v" meaning "solid", "linear gradient", "radial gradient", "texture" or "visual" brush.
Now, instead of painting the entire object with one solid color we like to paint each surface
independantly. The visual brush (described above) is used for the drawing. This means that we
need to make the following changes:
(1) Instead of drawing the entire object at once we need to draw each surface independantly and
assemble them together to make the 3D object. This is not new. We are going to use the same
technique which we have used to draw 3D objects using class (pcs) You need to review
examples (20,21) of the "Drawing" chapter to know how.
To draw a Rectangular block, we are not going to call gm("cRv") any more. We are going
to be calling gm("crv") while specifying the container plane and depth assigned to (cns)
This will be repeated 3 times, once for each visible side of the rectangular block.
(2) We are going to be using the visual brush. So, we are going to set it temporarely as
the Graphical Output device, draw on it then assign it to the object by making the
assignment (ks="v") when we create the 3D object.
(3) The WPF 3D module requires two more arrays of type double to contain the "Texture Coordinates"
which are necessary when any brush other than the solid color one is used. This is no work
to you. The 2 arrays (TXD[], TYD[]) will be generated automatically with all other mesh data
arrays when you call gm("crv")
===============================================================================================
Example 8: Draw a 140 X 150 X 100 rectangular block by assembling its three visible sides. Draw
the text "PC#" on one side, a Hexagon on another side and an image on the third side. In order to
demonstrate our ability to mix different drawings into one image control, start by drawing a
frame in 2D space to set the rectangular block into.
===============================================================================================
//assembly ms1.exe;
public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3"
public override void init() {
base.init();
}
public override void setup() {
PageNumber=8;base.setup(); // Execute master class's setup() supplying it with page no.
//------------------------------------- gr3 Contents -------------------------------------
cns="gr3"; // gr4 is not divided.
cs="im1";cis="\n3D Graphics";os="s";ds="c";lf=560;of=250;fns="trb16";cm("i");
}
public override void run() {
cs="im1";gm("sdi"); // Set (im1) as the graphical output device.
//----------------------------- Drawing a frame in 2D space -----------------------------
cls="g0";id=3;gm("sps"); // Create a 3 pixels wide pen of solid green color.
id=247;od=247;gm("crd"); // Create a new Rectangle and draw it at center.
//-------------------------------- Drawing the front side -------------------------------
cns="xy50"; // Container: Parallel to xy plane at z=50 (1/2 of depth)
id=140;od=150;gm("crv"); // Get vertex data of front rect.
js="d";cls="s9r4";ks="s";vm("cof"); // Create-fill the 3D object using solid red brush
gm("sdb"); // Set the present visual brush as the gr output device
cls="y0";id=1;gm("sps"); // Create solid yellow color brush
fns="trb40";os="PC#";gm("ctf"); // Draw the text "PC#" on the Visual brush
gm("fdb"); // Finish v brush drawing operation.
js="d";cls="s9r4";ks="v";vm("cof"); // Create object in 3D space painting it with visual br
//--------------------------------- Drawing the top side --------------------------------
cns="xz75"; // Container: Parallel to xz plane at y=75
id=100;od=140;gm("crv"); // Get vertex data of top rect.
js="d";cls="s9b4";vm("cof"); // Create-fill the 3D object using solid blue brush
gm("sdb"); // Set the present visual brush as the gr output device
cls="s9";id=1;gm("sps"); // Create solid white color brush.
id=6;od=80;gm("c=f"); // Draw-fill a hexagon on the visual brush.
gm("fdb"); // Finish v brush drawing operation.
js="d";cls="s9r4";ks="v";vm("cof"); // Create object in 3D space painting it with visual br
//--------------------------------- Drawing the right side ------------------------------
cns="yz70"; // Container: Parallel to yz plane at x=70
id=100;od=150;gm("crv"); // Get vertex data of right side rect.
js="d";cls="s9g4";vm("cof"); // Create-fill the 3D object using solid green brush
gm("sdb"); // Set the present visual brush as the gr output device
fls="images\\flower.jpg"; // Name of Image file to be drawn
id=100;od=150;gm("cid"); // Draw image on v brush after scaling it to fit rect
gm("fdb"); // Finish v brush drawing operation.
js="d";cls="s9r4";ks="v";vm("cof"); // Create object in 3D space painting it with visual br
gm("fdi"); // Finish drawing to (im1)
}
}
----------------------------------------------------------------------------------------------
COMMENTS:
---------
(1) When we have been assembling a rectangular block using class (pcs), the depth of the block
side which was parallel to plane (yz) has been negative, but here it's postive. This is
because with class (pcs), we have been drawing the left side, but here we are drawing the
right side of the rectangular block.
(2) In this example We have drawn the visible sides of the 3D object only. In general, the right
thing to do is to draw all 6 sides. If we have been doing transformation or animation, the
object's back side could have appeared.
(3) When we talk about the front and back sides of the object, we must keep in mind that the
object center coincides with the graphical output device's center. All vertex data are based
on that. This means that if the side you have requested to draw was parallel to the xy plane
with a negative depth (z<0), the side you get will be a back side, painted with back side
color and anything you draw on it will not be visible unless you move the camera to view the
back side.
==============================================================================================
|
 |
===============================================================================================
Drawing a large number of 3D objects into one page:
===================================================
There are more than one way to do so. You may create a large number of grids, one for each item
and create image controls into each. Each image control can display one item. You can also use
the method which we have used in example 6 in which we have created only one image control and
set the 3D coordinates of each item using (jd,kd,ld) to seperate them. The first method requires
plenty of code and the second one is hard to do.
Using the visual brush as a graphical output device can be the best choice. It can allow us to
position the items easily in 2D space and the entire job can be done in a loop which requires
minimum code.
==============================================================================================
Example 9: Draw 9 cylinders with variety of sides into one page. The sides should be in the
range (3:11) Draw a frame around each item and display a description label within the frame.
Also display a title for the page.
==============================================================================================
//assembly ms1.exe;
public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3"
public override void init() {
base.init();
}
public override void setup() {
PageNumber=9;PageHeight=630;base.setup(); // Execute master class's setup() supplying it
// with page no. and height.
//------------------------------------- gr3 Contents -------------------------------------
cns="gr3"; // gr3 is not divided.
cs="im1";cis="3D Graphics";os="s";ds="c";lf=560;of=620;fns="trb16";cm("i");
}
public override void run() {
cs="im1";gm("sdi"); // Set (im1) as the graphical output device.
cls="y7";gm("sps"); // Create a new pen/brush of background color.
kd=305;id=557;od=50;gm("crf"); // Create a new Rectangle to house title.
cls="r0";id=3;gm("sps"); // Create a new pen/brush of solid red color.
kd=300;os="CYLINDERS";fns="trb20";gm("ctf");// Display title
for (int n=0;n<9;n++) { // Repeat the following 9 times:
cls="S9";id=2;gm("sps");fns="trb12"; // Set color & font for frame and item lables.
jd=185*(n%3)-185;kd=-185*(int)(n/3)+105; // Set position of each item's label.
os="Number of sides="+(n+3);gm("ctf"); // Display label.
jd=185*(n%3)-185;kd=-185*(int)(n/3)+185; // Set position of each square's center point
id=od=180;gm("crd"); // Draw square.
gm("sdb"); // Set visual brush as graphical output device.
id=n+3;od=100;dd=100;js="yz";gm("cCv"); // Get vertex data for each cylinder.
cls="r0b4";js="d";vm("cof"); // Create 3D object and display it in blue color.
gm("fdb"); // Finish drawing to brush
gm("grf"); // Paint the square with the visual brush.
}
gm("fdi"); // Finish drawing to (im1)
}
}
----------------------------------------------------------------------------------------------
Everything seems to be fine except that the graphics quality of the WPF 3D module is not as high
as we like it to be. As you can see, outer edges of all 3D objects are rough. This problem
becomes more noticeable when the objects are small in size.
Notice the scrollbars which automatically show up when we set the page height or page width to
values which are beyond what the window can display without the bars.
==============================================================================================
|
 |
===============================================================================================
Operating on PC# internal 3D objects and arrays:
================================================
Some of the objects and arrays which are used by PC# to access the WPF classes are declared
public in order to allow you to operate on them whenever you find it necessary. Here is a list
of them:
igp = Image control which is currently the Graphical output device.
vbp = Visual Brush which is currently the Graphical output device.
gmp = Last GeometryModel3D object created.
gmo = Last 2D Geometry object created.
pcp = Present PerspectiveCamera
ocp = Present OrthographicCamera
mcp = Present MatrixCamera
alp = Present AmbientLight
dlp = Present DirectionalLight
plp = Present PointLight
Mesh data:
----------
PXD[],PYD[],PZD[] Array of type "double" which contain the X,y & z coordinates of each point
of the 3D object.
NXD[],NYD[],NZD[] Arrays of type "double" which contains the Vector3D components of the Normal
property of each point.
IX[] An array of type "int" which contains the indices of the points of all triangles which
make the shape in an order which we have discussed before.
TXD[],TYD[] Arrays of type "double" which contains Texture X & Y Coordinates.
REMARKS:
--------
(1) Whenever you supply the vertex data for a 3D object, you may like to include point normals
with your data. The Normal of a point on a 3D object is the vector which is perpendicular to the
surface at that point and points to outside the object.
Before you supply this data, you should create the type double arrays NXD[], NYD[] and NZD[]
which are defined by class pcs3 as arrays of the same lengths as arrays PXD[], PYD[] and PZD[].
Then you should load them with the (x,y & z) components of each vector which represents the
normal at each point in the point arrays at the same order.
PC# does not use the Normal arrays when it supplies data for a Rectangular Block, a Cylinder or
a Pyramid. It uses a different method which has been explained before.
(2) When you supply your own vertex data for an object, remember not to make the mistake of
redeclaring the vertex arrays. We mean by that:
DO : IX=new int[50];
DO NOT: int[] IX=new int[50];
since obviously array IX[] of the second line is not the one which class (pcs3) can use.
(3) You can get all the references listed above for public objects by calling vm("O") with no
parameters, except the Light objects which require supplying the method with their keynames.
Comparing class (pcs) with class (pcs3):
========================================
Working with the WPF graphics is made to be very similar to working with class pcs's graphics.
With class pcs, all shapes are represented by one object of type GraphicsPath which is (gpp)
Similarly, with class pcs3, all 2D shapes are represented by one object of type Geometry which
is (gmo) and all 3D shapes are represented by one object of type GeometryModel3D which is (gmp)
With class pcs, you can create a shape by calling method gm("cx"), gm("cxd") or gm("cxf) where x
is a character which represents the type of shape wanted. It can be (r,e,=,p,i,t,R,C,P) meaning
(Rect, ellipse, equally sided object, general path, image, text, Rectangular Block, Cylinder or
Pyramid) respectively.
If the mode string ends with "d", the object's outlines are drawn to the graphical output device
using the present Pen object. If the mode string ends with "f", the object is filled with the
present brush. If the third character was omitted, the object will be created only without
display.
Class pcs3 functions identically regarding 2D graphics. With 3D graphics it can either create
objects only with vm("co") or create and fill them with vm("cof") It cannot draw outlines.
With class pcs, you can work on the shape object after being created and (gpp) become its
reference. Calling gm("grd") or gm("grf") draws or fills the object. Methods gm("gt"), gm("gtd")
and gm("gtf") applies present unit transform (utp) to the object then it can either draw its
outlines or fill it depending on the 3rd character of the mode string.
With class pcs3, you can also work on the shape object after being created and (gmo) or (gmp)
become a reference of its underlaying Geometry. The same modes of method gm() are available when
working with 2D objects. With 3D objects, methods vm("grf"), vm("gt") and vm("gtf") are
available.
3D TRANSFORMS:
==============
The transformation technique which we have used with 2D graphics has made transformation very
easy and more importantly error free. So, we are going to do the same with 3D transformation.
To remind you with it, you need to plan your transformation job as follows (in the same order):
(1) If you need to scale the object, assign the (x,y and z) scale factors to (js) seperated
with commas. Remember that the object will be scaled up or down while its center remains
at same position.
(2) If you need to rotate the object, assign the rotation axis' 3D vector components to (ks)
seperated with commas and the rotation angle in degrees to (ad)
(3) If you like to move object's center in all 3 directions by the amounts (x1,y1,z1), assign
these 3 values to (os) seperated with commas.
(4) Call vm("st") to set the transform.
(5) After that, you can transform (gmp) at any time by calling vm("gt") or you can transform it
and draw it at the same time by gm("gtf") All this should be preceded with vm("co") in
order to create (gmp) for your 3D object.
As you must have noticed, we have not included "shearing" in the process. We may do that at a
later time.
==============================================================================================
Example 10: Create a circular cylinder and draw it. Scale the cylinder to half in all three
directions and draw it again. Rotate it by 90 degrees around Z-axis and draw it for the third
time.
==============================================================================================
//assembly ms1.exe;
public class a : ms1 { // using pcs3 // Make sure to keep the commented word "pcs3"
public override void init() {
base.init();
}
public override void setup() {
PageNumber=10;base.setup(); // Execute master class's setup() supplying it with page no.
//----------------------------------- gr3 Contents -----------------------------------
cns="gr3"; // gr3 is not divided.
cs="im1";cis="\n3D TRANSFORMS";os="s";ds="c";lf=560;of=250;fns="trb16";cm("i");
}
public override void run() {
cs="im1";gm("sdi"); // Set (im1) as graphical output device.
id=40;od=100;dd=100;js="zy";gm("cCv"); // Obtain vertex data for a cylinder.
//---------------------------------- Original Object ------------------------------------
cls="S9";gm("sps"); // Create black pen.
jd=-95;kd=-165;id=od=180; // Position for first text line
os="Original";gm("ctf"); // Draw the text.
jd=-185;id=od=180;gm("crd"); // Draw the containing square.
gm("sdb"); // Set visual brush as graphical output device.
cls="S9b5";js="d";vm("cof"); // Create and draw-fill original object in blue.
gm("fdb"); // Finish with brush as graphical out device.
gm("grf"); // Paint first square with visual brush.
//--------------------------------- Scaled down Object ----------------------------------
cls="S9";gm("sps"); // Recreate black pen.
jd=90;kd=-165;id=od=180; // Position for second text line
os="Scaled to half";gm("ctf"); // Draw text.
id=od=180;gm("crd"); // Draw containing square.
gm("sdb"); // Set visual brush as graphical output device.
cls="S9g5";js="d";vm("co"); // Recreate original object in green w/o drawing.
js="0.5,0.5,0.5";vm("st"); // Set transform to scale object by 0.5
vm("gtf"); // Transform and draw-fill object
gm("fdb"); // Finish with brush as graphical out device.
gm("grf"); // Paint second square with visual brush
//----------------------------------- Rotated Object ------------------------------------
cls="S9";gm("sps"); // Recreate black pen.
jd=280;kd=-165;id=od=180; // Position for third text line
os="Rotated 90 degrees";gm("ctf"); // Draw text.
jd=185;id=od=180;gm("crd"); // Draw containing square.
gm("sdb"); // Set visual brush as graphical output device.
cls="S9r5";js="d";vm("co"); // Recreate original object in red w/o drawing.
ks="0,0,1";ad=90;;vm("st"); // Set transform to rotate obj 90 degrees around
vm("gtf"); // Z-axis. Transform and draw-fill object.
gm("fdb"); // Finish with brush as graphical out device.
gm("grf"); // Paint third square with visual brush.
gm("fdi"); // Finish Image control drawing operation.
}
}
----------------------------------------------------------------------------------------------
COMPILING: Use tool "pcp" or "pcpr" as usual. Make sure to keep the commented statement
"//assembly ms1.exe;" at the top and also to keep the commented word "pcs3" at the class
declaration line since both are required by the compiling tools. For more details read the
text titled "Compiling" which follows example 1.
COMMENTS:
---------
Since you already know about objects involved, we can explain this example more technically.
(1) We called method gm("cCv") to create the necessary arrays once only at the top. This is
because these arrays do not get erased until you request the vertex data of a new object
by calling gm("cxv") where x can be "R", "C", "P", "r" or "=". In this example, we needed
the vertex data for one object only.
(2) Why have we been drawing the label which goes with each square before drawing the square
itself? When the label text was drawn the present 2D object (gmo) has been created as the
text's geometry object. When the square was drawn, (gmo) has been overwritten to become the
square's geometry object.
Immediately after (gmo) became the square's object, we set the visual brush as the graphical
output device, drew on it, then painted (gmo) with it. This is why the last (gmo) which
was available immediately before using the brush must be for the square.
(3) The same idea applies to 3D. Each time we call vm("co") to create a 3D object, (gmp) is
recreated to point to that object. So, when we set the transform then call vm("gtf"), we
know that we are applying the transform to last (gmp) which means to the last created 3D
object.
(4) Here is one question which you may like to ask "Both (igp) and (vbp) accept 2D and 3D
drawings. If we have drawn 2D figures on the visual brush the 2D Geometry object (gmo)
could have changed. So how could we guarantee that the geometry object which has been
painted with the visual brush at the end belongs to the square?
The answer is that when you call gm("sdb") to make (vbp) the graphical output device, the
first thing method gm() does is creating a second reference to both (gmo) and (gmp) to
persist the two objects, and when you call gm ("fdb") to finish the brush drawing operation,
the original values of (gmo) and (gmp) are restored.
==============================================================================================
|
 |
===============================================================================================
Camera and Lights
=================
The first time we introduced a short course in mathematics was in the chapter of "Imaging" when
we discussed Matrices. The WPF contains classes which include methods which perform Vectors and
Quaternions mathematical operations in addition to 3D Matrices. However, writing WPF application
software requires only a small knowledge of them. This is what we are going to start with here.
We may get into more details later if we find that necessary.
VECTORS:
========
A vector is an amount which has a magnitude and direction but it does not have a specific
location. This means that all vectors in a 3D space which have same magnitude and parallel to
each others are the same. And since they are the same, we can simplify defining each group of
equal vectors by using the one of the group which starts at the origin and ends at a point in
the 3D space.
A vector is usually represented with an arrow of the same length as the vector's magnitude and
pointing to the same direction as the vector's direction. If the tail of that arrow starts at
the origin and the tip ends at point (x,y,z), we call that vector(x,y,z)
Normalized Vectors (or unit vectors):
-------------------------------------
Vectors are used in the WPF 3D module to represent items which have magnitude and direction
like "directional lights" and also items which have direction only like "Camera Up direction".
Vectors which represent direction only have a magnitude of 1. They are called unit vectors or
normalized vectors.
You can normalize a vector be dividing it by its magnitude. The magnitude of vector (x,y,z) is
(x^2 + y^2 + z^2)^0.5 [We mean by this the square root of (x square + y square + z square)]
The X, Y and Z axes are normalized vectors. Their values are (1,0,0), (0,1,0) and (0,0,1)
respectively.
Normally, you don't have to normalize vectors which mean "Direction" to the WPF since the WPF
will do this job for you.
COMPLEX NUMBERS:
================
The complex number (n), in general can be represented by two components, real component and
imaginary one as follows:
n = a + bi where i=(-1)^0.5
Mathematicians have found that representing a point in 2D space with a complex number could
simplify some calculations especially "rotation". This, of course is in addition to many other
applications in physics and other siences which have been simplified with the use of complex
numbers.
In 2D space, we use the real part of the number (a) to represent the (x) value of the point and
the imaginary part (b) to represent the (y) value.
QUATERNIONS:
============
If the real and imaginary parts of a complex number can represent a point in 2D space, how can
we represent a point in 3D space? the answer is by using 4 components. One real and 3 imaginary
components. The Quaternion can be expressed as follows:
q = w + xi + yj + zk
Quaternions are used in the WPF for rotation transformation. We see no need for more about them
at this point.
CAMERA TYPES:
=============
There are three types of cameras: Perspective, Orthographic and Matrix.
(1) The Perspective Camera:
---------------------------
The Perspective camera is the standard camera which everybody uses. It makes the nearby sides of
the object appear to be larger than the far sides which is what everybody expects. To create a
perspective camera, call vm("ccp") with the following parameters:
os : Camera position. Assign the (x,y,z) coordinates of the camera position to (os) seperated
with commas. When the camera is far away from the object, you expect the object to appear
smaller.
js : Look Direction. Assign the x,y,z components of the "Look" direction vector to (js)
seperated with commas. Normally, you set the object at the center. So, if you position the
camera at point (x,y,z) you like to make your look direction vector (-x,-y,-z)
ks : Up Direction. Assign the x,y,z components of the camera's upward direction vector to (ks)
seperated with commas. Normally we like that direction to be the positive Y-axis. So we
make the assignment: ks="0,1,0".
id : Field of View. This is the angle of the view cone of the camera. Whenever it's narrow, only
a small area of the scene appears; whenever it's wide more area appears and objects look
smaller.
(2) The Orthographic Camera:
----------------------------
When we like to draw more precisive 3D drawings, like the ones used for Engineering, we normally
don't like far sides of the object to appear smaller than the near ones since everything must be
done with measurments. In order to generate this kind of drawings, the Orthographic camera is
the one to select. It does not have a view cone. It actually has a view cylinder which must be
as wide as the object in order to view it at 1:1 scale.
To create an Orthographic camera call vm("cco") with the following parameters:
os : Camera Position. See above.
js : Look Direction. See above.
ks : Up Direction. See above.
id : Width.
(3) The Matrix Camera:
----------------------
Normally, the Perspective and Orthographic cameras are all the Camera types which you may need.
However, the WPF allows you to set the properties of a camera by defining its view and
projection matrices. PC# allows you to use this type of camera to view the 3D scene which it
generates for you, but it does not create the matrix camera itself.
So, if you like to use a Matrix camera, do the following:
(1) Create the view and projection transforms.
(2) Create a matrix camera using the two transforms and make (mcp) its reference.
(3) call vm("ccm") with no parameters.
LIGHTS:
=======
There are three major types of light:
(1) Ambient Light:
------------------
This is a diffused light which is available everywhere but does not have a specific direction.
It increases brightness of a 3D object while it cannot produce shadows or make object edges
distinctive. For this reason, we normally don't use this light type alone. We mix it with other
light types. It can be used as the brightness control of a 3D scene.
(2) Directional Light:
----------------------
This is a light which is made of parallel rays with a specific intensity and direction. It can
generate shadows and can cause sharp edges of the 3D object to appear. We may use more than one
of this light type in order to illuminate the object from different directions.
(3) Point Light:
----------------
This is "Light bulb" light. You may use any number of them in a 3D scene. The light rays of this
type go evenly in all directions around the point where the light source is at.
LIGHT PARAMETERS:
-----------------
"Color" is a common parameter which you must specify for all types of light. The WPF 3D module
applies some laws of physics regarding light reflection. An object looks dark unless it can
reflect light and the color it appears at is the color of the light which it reflects.
If we assume that the object's color was red and the light was green, the object will appear
black no matter how strong the light is since the object cannot reflect green light.
The only property which you supply when creating ambient light is the color code (cls) Point
light requires additionally, the 3 coordinates of its location point assigned to (jd,kd,ld)
Directional light requires (cls) and the 3 components of the vector specifying light direction
assigned to (jd,kd,ld) also.
HOW ARE LIGHTS CREATED IN PC#:
------------------------------
As you know PC# keeps only one object of each type at any moment. This made it possible to refer
to each object by one unique name. For example (btp) is always the reference name of the last
button object which you have called a PC# method to operate on. There are two advantages in
that:
(1) Memory Management: It regulates the size of the resources used by your program in order to
guard against a crash.
(2) Simplicity and error control: You can guarantee the name of the object which you like to
operate on. This guards against errors and makes your job simpler, easier and more pleasant.
If you have studied the "Pc# reference-Desktop" you already know how to use the "Present object"
and what to do if you must keep more than one object at a time.
With some objects like "Controls", keeping more than one object during the execution of your
class is more than just a rare necessity. This is because you may like to create 3 buttons in
one class and the user may click any of them at any time during execution, so the 3 objects must
be always available. We use keynames to identify each object of this kind and PC# keeps all the
object locations in its archives.
Whenever you like to do an operation on button "bt0" for example, you supply method cm() with the
keyname "bt0" assigned to (cs) The first thing the method does is looking through the archives
to find the object of this button and making (btp) its reference. After the execution of this
operation and before calling the method to operate on a new button, you can guarantee that (btp)
is a reference to your button's object.
If you like (btp) to be the reference of any button but you have no need to perform an operation,
call cm("O") supplying it with the button's keyname.
Let us now return to the main subject. You have a need for only one Camera in a 3D scene, so PC#
has assigned 3 names for the 3 Camera objects which are (pcp), (ocp) and (mcp) When we come to
"Lights", you may like to have more than one directional light or point light. So, we need to
use the same technique which we use with controls.
To create a light or perform any operation on a light, you need to supply its keyname assigned
to (cs) The first 2 chars for the keyname of each light type are as follows:
al: for Ambient light.
dl: for Directional light.
pl: for Point light.
So, "al0","dl5" and "pl7" are valid keynames for lights. The 3 present objects for the 3 light
types are (alp), (dlp) and (plp) We can install upto 10 different lights in a 3D scene.
EXAMPLES:
---------
cs="al0";cls="s0";vm("cla"); // Create ambient light, gray in color.
cs="pl5";jd=kd=ld=100;vm("slp"); // Set the position of a point light at (100,100,100)
cs="dl5";jd=kd=ld=-100;vm("sld");// Set the direction of a directional light at (-100,-100,-100)
About the next example:
=======================
The next example is very useful since it teachs you all about camera types, locations and
directions. It also teaches you light types, how to create them and how to modify them after
being created. It shows you how to move the Camera in a circle above the object while constantly
pointing at the object in order to view it from all directions.
This example is also an application on using check box and radio button groups. They work
identically as they do in class (pcs) so, if you need more explanation regarding them, review
example 3 of the chapter "Using Controls".
==============================================================================================
Example 11: Create an eight sides cylinder. View it with a camera located at max (x,y,z) corner.
Illuminate it with 4 lights. One directional light pointing to it from front top left corner,
one directional light pointing to it from rear top right corner, one point light at top-center
and an ambient light. Show the effect of switching between perspective and orthographic camera
types. Also show the effect of eliminating one or more of created lights. Show how to dim and
brighten the scene by adjusting the intensity of the ambient light and how to see object from
all directions by rotating the camera over 360 degrees using slider controls.
==============================================================================================
//assembly ms1.exe;
public class a : ms1 { // using pcs3
public override void init() {
base.init();
}
public override void setup() {
PageNumber=11;base.setup(); // Execute master class's setup() supplying it with page no.
//----------------------------------- gr3 Contents -----------------------------------
cns="gr3"; // gr3 is divided into 3 col's.
cs="gr4";j=0;oxd=0.30;ds="w";cls="S9y7";fns="tr12";cm("i");
cs="gr5";j=1;k=0;oxd=0.40;ds="c";cls="S9y7";fns="crb10";cm("i");
cs="gr6";j=2;k=0;oxd=0.30;ds="e";cls="S9y7";fns="crb10";cm("i");
//----------------------------------- gr4 Contents -----------------------------------
cns="gr4"; // gr4 is divided into 6 rows.
cs="lb0";cis="LIGHT SELECTION";k=0;ds="c";oyd=0.1;cls="r0y7";fns="trb12";cm("i");
cs="cb00";cis="Front Directional Light";k=1;ds="w";cus="1";
oyd=0.1;cls="S9s9";fns="trb12";cm("i");
cs="cb01";cis="Rear Directional Light";k=2;ds="w";cus="1";
oyd=0.1;cls="S9s9";fns="trb12";cm("i");
cs="cb02";cis="Top Point Light";k=3;ds="w";cus="1";oyd=0.1;cls="S9s9";fns="trb12";cm("i");
cs="bt0";cis="Select Lights";k=4;ds="c";oyd=0.2;cls="r0g7";fns="trb12";cm("i");
cs="sl0";cis="Ambient Light Intensity\n 0 5 10";os="n";k=5;oyd=0.4;
js="0,10";cus="5";lf=160;ds="s";cls="S9b2";fns="crb12";cm("i");
//----------------------------------- gr5 Contents -----------------------------------
cns="gr5"; // gr5 is not divided.
cs="im1";cis="\nCamera and Light Effects";os="s";ds="c";ims="";lf=of=220;fns="trb16";cm("i");
//----------------------------------- gr6 Contents -----------------------------------
cns="gr6"; // gr6 is divided into 6 rows.
cs="lb1";cis="CAMERA SELECTION";k=0;ds="c";oyd=0.1;cls="roy7";fns="trb12";cm("i");
cs="rb00";cis="Perspective Camera";k=1;ds="w";cus="1";oyd=0.1;cls="S9s9";fns="trb12";cm("i");
cs="rb01";cis="Orthographic Camera";k=2;ds="w";oyd=0.1;cls="S9s9";fns="trb12";cm("i");
cs="bt1";cis="Select Camera";k=4;ds="c";oyd=0.2;cls="r0g7";fns="trb12";cm("i");
cs="sl1";cis=" Camera Position\n-180 0 +180";os="n";k=5;oyd=0.4;
js="0,10";cus="5";lf=160;ds="s";cls="S9b2";fns="crb12";cm("i");
}
public override void update() {
//------------------------------- Ambient Light adjustment ------------------------------
if ("sl0".Equals(cs)) { // If Ambient Light control slider activated:
if (od<5) { // If pointer was at the darker half of the control:
cls="S"+(9-(int)(od*9/5)); // Set the corresponding color code.
}
else { // Else, if pointer was at lighter half of the control:
od-=5; // Adjust reading to fit the color code
cls="s"+((int)(od*9/5)); // Set the corresponding color code.
}
cs="al0";vm("slc"); // Set ambient light color accordingly.
}
//------------------------------------ Camera Rotation ----------------------------------
else if ("sl1".Equals(cs)) { // If Camera rotation slider activated:
// The object is considered to be at the center of a (220 X 220 X 220) room. The Camera is
// at its ceiling which is parallel to the xz plane at a height of (y=110), It rotates
// around ceiling center at a radius of 110. Its initial position is at point (110,0)
// making a startup angle of zero with the Z-axis projection on the ceiling plane.
// The slider's range was set as (0:10) so we should multiply its readings by (36) to
// adjust them to (-180:180) range. The Camera's z and x coordinates at any angle should
// be [radius*cos(angle)] and [radius*sin(angle)] respectively. In order to make camera
// always point at center, its "Look direction" Vector3D components are obtained by
// negating its position coordinates.
double angle=od*36; // Obtain rotation angle which corresponds to (od)
od=angle;js="cos";um("mt");zd=-110*od;
od=angle;js="sin";um("mt");xd=-110*od;
jd=xd;kd=110;ld=zd;vm("scp"); // Apply received values to camera's postion.
jd=-xd;kd=-110;ld=-zd;vm("scl"); // and to its look direction.
}
//------------------------------------ Lights Selection ---------------------------------
else if ("bt0".Equals(cs)) { // If "Lights Selection" button clicked:
cs="cb0*";cm("gu"); // Get update values of CheckBox group into CUS[]
for (n=0;n<3;n++) { // Scan CUS[] rows (which correspond to CheckBox states)
if(CUS[n]=="1") cls="s0"; // If this row contains "1". set color at initial color.
else cls="S9"; // Else, make it black (meaning eliminate this light)
if (n==0) cs="dl0"; // First row sets our front directional light.
else if (n==1) cs="dl1"; // Second row sets our back directional light.
else cs="pl0"; // Third row sets top point light.
vm("slc"); // Set lights as this row requires.
} // Repeat for all rows of array CUS[]
}
//------------------------------------ Camera Selection ---------------------------------
else if ("bt1".Equals(cs)) { // If "Camera Selection" button clicked:
cs="rb0*";cm("gu"); // Get index of selected radio button in (cui)
os="0,110,110"; // Set values which are common for the 2 camera types
js="0,-110,-110";ks="0,1,0"; // which are: position, look direction and up direction.
if (cui==0) { // If Perspective camera was selected:
id=90; // Set its "Field of view angle" at 90 degrees.
vm("ccp"); // and recreate a Perspective camera.
}
else { // Else, if Orthographic camera was selected:
id=220; // Set its "Width" property at (220) which is enough to
vm("cco"); // view full object and recreate an orthographic camera.
}
}
}
public override void run() {
cs="im1";gm("sdi"); // Set (im1) as graphical output device.
cls="g0";id=3;gm("sps"); // Create a new pen/brush of solid green color.
id=217;od=217;gm("crd"); // Create a new Rectangle and draw it at center.
id=8;od=150;dd=-150;js="yz";gm("cCv");
// Get vertex data for Cylinder with 8 sides base.
cls="S9r5";js="d";vm("cof"); // Create and draw object in red using diffuse material.
//---- Startup Camera ----
os="0,110,110"; // Camera position = Point (0,110,110)
js="0,-110,-110"; // Look direction = from camera position to center.
ks="0,1,0"; // Up Direction = +ve (Y)
id=90; // Field of view angle=90 degrees.
vm("ccp"); // Create Perspective camera.
//---- Startup Lights ----
// "dl0" illuminates object's front side. "dl1" illuminates its back side. "pl0" illuminates
// its top and "al0" acts as a brightness control.
cs="dl0";cls="s0";jd=110;kd=ld=-110;vm("cld");// dir light from top-left-out corner to center
cs="dl1";cls="s0";jd=-110;kd=ld=110;vm("cld");// dir light from top-right-in corner to center
cs="pl0";cls="s0";jd=ld=0;kd=110;vm("clp"); // point light at top center.
cs="al0";cls="s0";vm("cla"); // ambient light.
gm("fdi"); // Finish drawing to (im1)
}
}
----------------------------------------------------------------------------------------------
As you must have expected moving the slider (sl1) from end to end rotates object by 360 degrees.
This is because rotating the camera around object means that the object's image in the camera is
rotating by the same angle in the opposite direction.
COMMENTS:
=========
(1) When we created slider (sl1) we requested a range of (0:10), then we multiplied each reading
which we received from the slider in method update() by 36 since actual range is (-180:180)
Why did'nt we set range at (-180:180) or (0:360) to start with? If we did we could have
ended with a slider which contains 360 tick marks instead of 10. A slider with 10 tick marks
looks better.
(2) In real life, objects can be bright enough to cause blindness while on the screen or on
paper they are as bright as they can be when they appear in white color. Since we must show
a clear difference between bright and dark spots in a 3D scene, we make gray the average
shade. Most lights are created with color code "s0" which is for gray color.
==============================================================================================
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
|
 |
===============================================================================================
ANIMATION
=========
If you have studied the web examples of WPDIII and WPDIV, you know how to use JavaScript and
class (pasp) to do several types of animations. The WPF includes an entire namespace for the
purpose of animation. We like to simplify and implement the most usable ones and most
importantly, make it easy for you to include multiple animations into one page. Our animated
objects must be able to run together into one graphical output device or several graphical
output devices in the same page. Both 2D and 3D objects must be possible to animate.
What can we animate?
--------------------
We can apply the following animations to a 2D object:
(1) We can move the 2D object horizontally and vertically according to a preset formula.
(2) We can scale the 2D object horizontally and vertically also according to a preset formula.
(3) And We can rotate the 2D object according to a preset formula.
Similarly, we can apply the following animations to a 3D object:
(1) We can move the 3D along the x,y and z axes according to a preset formula.
(2) We can scale the 3D object on the x,y and z directions also according to a preset formula.
(3) And We can rotate the 3D object according to a preset formula.
What are the preset formulae?
-----------------------------
Whenever we call method vm() to animate a specific property of an object, we supply a time
schedule which describes what the value of that property should be at each selected moment. For
example, if we like to apply horizontal movement animation to an object, we call vm("apx")
which is for animating the x-position of the object supplying it with a time schedule like this:
O[0]=0;OS[0]="0"; // At start (meaning after zero seconds), Keep object at center (ie x=0)
O[1]=2;OS[1]="100"; // After 2 seconds, object should be at position (x=100)
O[2]=4;OS[2]="-100"; // After 4 seconds, object should be at position (x=-100)
This formula is made of 3 steps. We are telling The WPF where the object should be at start,
after 2 seconds and after 4 seconds. the object is not going to jump from (position 0) to
(position 100) or from (position 100) to (position -100), it will move smoothly between the
three points. The movement-time relation is linear.
Arrays O[] and OS[] are used to specify the seconds and the property values. The reason we use
OS[] for this job is that different properties may require values of different types. For
example, rotation of a 3D object with changing both axis and angle, requires 4 numbers, the first
three specify the axis vector and the fourth one specifies the angle. We supply the 4 numbers
seperated with commas into a row of array OS[], like: OS[4]="1,0,0,45".
What to do after the time schedule has been executed?
-----------------------------------------------------
The default is to repeat the process in reverse direction, then repeat the entire process
indefinitely. However you can overwrite the defaults with:
jb=true: Means Do Not "Auto Reverse".
j=repititions: If (j) was not assigned zero, Animation will perform a limited number of
repititions then stops. The number of repititions is the value assigned to (j)
Setting the animation time unit:
--------------------------------
We have told you that the amounts you assign to O[] are times in seconds. This is true although
there is more to it. The default time unit is a second, but you can change that if you want.
If you set it at (0.5 seconds), O[0]=3 will mean O[0]=1.5 seconds in this case.
To change the animation time unit to any value, assign that value to (id) and call vm("atu")
When you set the begin time and the duration of two animation steps, you are usually interested
in how the number of the two steps compare rather than what the numbers are. You may like the
two steps to be equal in duration for example or one of them to be a multiple of the other. This
is why it's easier and more convenient to set the time in units instead of setting it discretely.
The default time unit is one second, but you may set it at any value which may contain fractions.
How to write the Animation program:
===================================
[A] Identify the object with a keyname:
---------------------------------------
Since we may need to animate more than one 2D or 3D object in one page, we need to keep the
objects which we intend to animate referenced all the time. So whatever applies to "Lights"
applies to them. We need to use keynames to identify those objects. The first two char's of
a 2D object's keyname is "g2" and the first two char's of 3D object's keyname is "g3".
So, to create a square which you intend to animate later, you may use this code:
cs="g20";id=od=100;gm("crd");// Create rect and draw it. Identify its Geometry with "g20"
and to create a circular 3D cylinder which you intend to animate, you may use this code:
id=40;od=90;dd=60;js="zy";gm("cCv"); // Obtain vertex data for a cylinder.
cs="g30";cls="s9s9";js="d";vm("cof");// Create & fill it. Identify it with the keyname "g30"
[B] Animate object's properties:
--------------------------------
You may then make any number of calls to method vm() at the following modes:
"apx": Animate x-position using supplied formula.
"apy": Animate y-position using supplied formula.
"apz": (For 3D objects only) Animate z-position using supplied formula.
"asx": Animate x-Scale factor using supplied formula.
"asy": Animate y-Scale factor using supplied formula.
"asz": (For 3D objects only) Animate z-Scale factor using supplied formula.
"ara": Animate rotation angle only using supplied formula.
"arx": Animate rotation axis and angle only using supplied formula.
You should precede your call with the following assignments:
(1) The same keyname which you used when object was created assigned to (cs)
(2) The preset formula as described before. Notice that rotation of a 2D object requires
assigning angles only to OS[], while rotation of a 3D object may require assigning angles
alone or vectors of rotation axes and the angles.
Rows of array OS[] are assigned one number for all animation options except when you rotate
both axis and angle of a 3D object. You assign OS[]'s row 4 numbers seperated with commas
in this case. When you animate the angle alone of a 3D object, you need to assign the
rotation axis vector components to (ks) while assigning angles only to each row of OS[].
(3) If you like to prevent "auto-reverse", supply (jb=true)
(4) If you don't like the animation to repeat forever, assign the number of wanted repetitions
to (j)
[C] Set animation trigger:
--------------------------
If you like the animation to start as soon as the page is loaded, call vm("at") with no
parameters.
If you like it to start when button "bt0" is clicked, receive the event in method update() and
start the animation there.
How to make object's color change during animation?
===================================================
You don't need to animate the color. You can set lights of different colors around the object
so whenever the object moves, its color changes because of light reflections. We'll demonstrate
this in the next example.
How to animate properties of an object by yourself?
===================================================
The WPF animation namespace allows you to animate some properties which we have no animation
methods for. If you must animate any of these properties you need to do the animation job by
yourself. If you like to animate such properties of an object which PC# has created for you,
you need to know the following:
(1) (gmo) is the reference of the (Geometry) object of the last 2D object which PC# has created
for you.
(2) (gmp) is the reference of the (GeometryModel3D) object of the last 3D object which PC# has
created for you.
(3) (srp) is the reference of the (Storyboard) object declared in class (pcs3)
All 3 reference names are declared public so you can operate on them. Let us assume that you
like to make a 2D object swing between the 2 X-positions (x=5,x=-5), here is what to do:
(1) Create a translate transform, assign a name to it and register it with:
this.RegisterName(name,ref);
where ref= Translate transform object reference and "name" is the name you assigned to it.
(2) Create an animation object of the type you like (most likely will be DoubleAnimation in
this case) and set it to change values according to your desired formula.
(3) Set the "AutoReverse" and "RepeatBehavior" of the animation object.
(4) Set the TargetName of the animation to the same name which you used in (1) as follows:
Storyboard.SetTargetName(a,name); Where (a) is the Animation object ref.
(5) Set the TargetProperty of the animation to the XProperty of the TranslateTransform:
Storyboard.SetTarg
| |