Friday, August 12, 2011

Notes on Dependency Objects

For the last several days ( yes – days! ) I have been trying to understand Dependency Objects as they relate to 3D graphics. I have been reading Ptezold’s 3D Programming for Windows as well as Applications = Code + Markup. I have also searched the internet for tutorials, the best of which was at: http://www.wpftutorial.net/DependencyProperties.html. I think the problem is that most discussions were overly complicated and perhaps not specific enough to what I was trying to do.

So here is my summary of what I learned relative to what I, myself, am trying to do right now! FYI – my current project is something that I call WoodCAD. It does a lot of 3D graphics and makes very heavy use of Dependency Objects.

 

Introduction to Dependency Objects

To a first approximation, just think of Dependency Objects as a way to define a property. They are fancy, but from the outside at least, they just look like a simple property. Once you have your head around that, understand that dependency properties also offer the following:

  • Its data is stored not in the class that holds it, but rather in some sort of mysterious dictionary that you don’t really have to understand completely in order to use. Have some faith that the data is there somewhere.
  • Dependency properties can only be used by a class that is descended from the Dependency Object class. That’s because its parent class provides a couple of methods that you need to use dependency properties
  • Dependency Properties have a default value that is used if no other value has been explicitly assigned. Note that this is one value per class that does not need to be set in the instance can use the default value.
  • You can specify a method that gets called whenever its value changes. This is the coolest thing about them and the main reason for using them here.
  • You can specify a Coerce method that is applied to the value before you try to use it. A good example would be trimming a string to fit within a particular size. This is totally optional and I am not currently using it.
  • You can specify a Validate method that tests the value and throws and error if it does not pass. The Validate method gets the new value after it has been processed by the Coerce method. Again, this is completely optional and I am not currently using it.
  • · The last thing is that if the Dependency Property you are defining is itself a Dependency Object, then the Changed Method gets triggered if you change the entire object, or if you change any dependency objects within the Dependency Object. This is what hung me up and I will give an example so that you will wonder how it was ever confusing to me. See the section named Cascading Dependency Objects.

 

Setting up a simple Dependency Object

Let’s make up a Dependency Object named Length that will be a property of our class named MyClass.

Start with the property definition – sometimes called the CLR definition:

public double Length {
set {SetValue(LengthProperty, value);}
get {return (int)GetValue(LengthProperty);}
}

See – It’s just a simple property.

Get and Set Value are methods defined by Dependency Object from which the class you are working in must be extended. Length Property is defined as follows:

public static readonly DependencyProperty LengthProperty =
DependencyProperty.Register(
"Length",
typeof(double),
typeof(MyClass),
new PropertyMetadata(new Point3DCollection(), LengthPropertyChanged)
);

It seems to come from a kind of Property Factory. But no matter. You get it by registering it. It is an object instance in its own right and you feed it to the Get and Set Value methods.

The first parameter in the Register method is a string that must be the name of the property. Next is the type to be held in the Dependency Object. After that is the type of the class that is using it. Next is Property Metadata (more on that in the next paragraph.) I could also have included an optional Validation callback here, but did not.

Now, about Property Metadata: Property Metadata allows us to specify the default value (an empty Point 3D Collection) as well as the call back method. A third parameter of Property Metadata that we did not use here is the Coerce method which is a private static that returns object and expects a sender (Dependency Object) and a value (object) as parameters.

And more about the Validate Method: Note that this is an optional parameter of the Dependency Property Register method, rather than the Property Metadata constructor. This is a private static bool that expects a single object parameter holding the proposed new value. If this returns false, an exception is thrown.

This leaves the most interesting thing: the Length Property Changed method. First of all, this has to be static in order to access it from a static method. Fortunately, the callback sends us the object, so we can call the instance version of the method as follows:

static void LengthPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) {
(obj as MyClass).LengthPropertyChanged(args);
}

And Length Property Changed is just a regular old method that returns void. It is just that it gets called automatically whenever the Length changes.

Cascading Dependency Objects

This turns out to be incredibly easy to do. If the Dependency Object is of a type that is itself a Dependency Object, then the Change event gets triggered in either of two ways:

  • If you reassign a new object instance to the Dependency Object, this triggers the changed method because the entire object has changed.
  • If you change something within the dependency object, that triggers the changed method as well.

The example I tried was using a Point 3D Collection. Note that this is extended from Dependency Object. I used exactly the same patterns as for in the Length example above, except of course that the types are Point 3D Collection instead of double.

I found that the changed method was triggered if I assigned a completely new Point 3D Collection to the object. One would certainly expect that.

I also found that if I just added a point to the object, that too caused the changed method to run.

Note that changing an X, Y, or Z value on a particular point would not trigger the changed method because the Point 3D object is not a dependency object.

Setting up a Read-Only Dependency

Petzold’s book talks about Read Only Dependency Objects. An example of this is the Geometry property of the Shape Base class in my program. This is generated completely within each instance of the class and external programs simply read it. The constructor for the object initialized the value for the Geometry, but once this has been done, it is never changed. The collections within it are sometimes changed, but object holding the geometry is never changed. Therefore, there is no changed method defined for geometry; all the change methods are associated with the various parameters that can force an update to the geometry.

Why then should geometry even be a Dependency Object? The reason is that other 3D objects in the hierarchy are using this and watching it. If it changes, they have to run their own changed methods.

Here is the code that defines this read only Dependency Object:

public MeshGeometry3D Geometry {
protected set { SetValue(GeometryKey, value); }
get { return (MeshGeometry3D)GetValue(GeometryProperty); }
}

static DependencyPropertyKey GeometryKey =
DependencyProperty.RegisterReadOnly("Geometry",
typeof(MeshGeometry3D),
typeof(V3G_Shape_Base),
new PropertyMetadata(new MeshGeometry3D()));
public static readonly DependencyProperty GeometryProperty =
GeometryKey.DependencyProperty;

I got into trouble because I tried to use this pattern for a Point 3D Collection. It did not work. It turns out that I was making changes to the Collection, so this was not appropriate.

 

Applying Dependency Objects to Controls

The examples I found on this tend to involve their use with Controls. While these examples are interesting, they can also be a bit confusing since their emphasis is different. Here are some of the differences I found:

  • The control examples tend to use Framework Property Metadata instead of just Property Metadata. This metadata has lots more information than the simple Property Metadata that I am using, including something called Inherits.
  • Controls have a hierarchy that allows them to inherit values from higher level controls. For example, if a text block in a button has a font size property and the button has a font size property and the form has a font size, the font size that is actually used is selected from the most local control that actually has the property set. This is very important as regards controls and the Framework Property Metadata has some control over this, but I did not figure out whether this directly from Dependency Objects or from some fancy code somewhere in the classes between the controls and the Dependency Object class from which they are extended.

Thursday, August 11, 2011

NH Humane Society Event

Barbara and I went to the Humane Society Event. It was a disappointment for a number of reasons.

  • The event was very disorganized. It took a long time to hand them our tickets and check in. It took even longer to give them more money for things purchased at the silent auction and check out.
  • Parking was a mess.
  • We went with the idea that we would be fed. The event did start at 6:00 PM.  We were fooled. They did have people going around with hors d’oeuvres, but both of us were starving by the time we went into the dining room. And then there was no dinner.
  • It was boring with a long auction that we had to listen to before the comedians came on.
  • The comedians were OK, but not as funny as last year.

Don’t get me wrong – the event was not horrible and I had an OK time there. We bought bear candle holders at the silent auction and also a snake pin. BUT…

Bottom line is that it will not be necessary to attend this event next year.

Tuesday, August 9, 2011

Bike Ride Andover to Lebanon

Yesterday and today (Aug 8th & 9th), Jim Lerner and I rode our bikes from East Andover to Lebanon and back. I used my mountain bike and Jim used his hybrid.

At 10:00 AM, we started from the home of Alex and Myra Burnhard who have a lovely home there on a hill. We rode 1.4 miles down the hill to mile marker 43 on the trail. We had a nice, leisurely ride to Tewksbury pond where we stopped to eat the lunch Myra had made for us. We to there at about 1:15 and ate lunch by the side of the lake.

Barbara had agreed to meet us in Lebanon at around 3:00 PM. I had tried to phone her without success several times to tell her that we would be late. At 1:45, I decided to hurry to Lebanon so that she would not be waiting alone for us. I told the others to relax and take their time.

I finally got through to her on a bad connection and told her that I would be late. She headed to TJ Max in West Lebanon. I finally made it to Lebanon at 3:05 after doing the last 8.5 mile stretch in 35 minutes. I called Barbara and she got there at around 3:30. We then waited for the others until 4 or so. It turns out that Myra had had a leaky tire that required regular re-pumping.

We than all went to the Norwich Inn which turned out to be lovely. We there met Suzanne, a friend of Jim’s who joined us on the patio for drinks. Later we had a nice dinner (Suzanne had left) and retired to our lovely rooms.

The next morning, Barbara and I went to the King Arthur Flour store where we purchased a multi-tier spring form pan and some cake pan insulating straps. We met Jim for breakfast at the inn and had a hearty mean. Alex and Myra had eaten elsewhere at a spot where they loved the coffee.

We then all drove to Lebanon. Jim and I started out at 10:10. Alex had developed a sore the day before and decided not to ride, so Barbara drove the two of them home. Jim and I stopped in Danbury for a sandwich and finally made it back to chez Alex and Myra at 3:00 PM. His speedometer says that we had been riding for just under 4 hours.

By this time, we were both suffering from sore butts – mostly I think due to the very bumpy stretch from Danbury to Caanan or so. But all in all it was a pleasant excursion. It is a great ride and the Norwich Inn is a lovely getaway.

Sunday, August 7, 2011

Unsuccessful Pottery Firing

Yesterday, I fired a bunch of old green ware that I had thrown some time ago. I used the small kiln which I had not fired in two years. The kiln sitter did not trip and the kiln got overly hot, ruining (I assume) everything in there and not helping the kiln any either. But, with all failures, I learned a lot:

  • The cone that I put in the kiln sitter melted as it should have and the bar it was holding up dropped, but the switch did not fall. I think this was because it had corroded and gotten a bit stiff. I should have checked this before starting the kiln.
  • When I thought the kiln should have tripped, I should have checked the switch to see if it was stuck. I could have known long before that it was above the required temperature.
  • I should have had a triplet of pyrometric cones that I could have seen through a peep hole, so that I could have checked on the progress.
  • I have a pyrometer. I should have had it connected. Stupid. I need to set that up for next time.
  • I noticed bubbles in the resulting pieces. When I smashed them open, I found that indeed they did have air pockets in them. This could perhaps be an artifact of insufficient wedging. Perhaps also, no amount of wedging would have compensated for the possibility that I introduced the bubbles while trying to rejuvenate the clay after it had sat for too long.

Saturday, August 6, 2011

Hit Testing in 3D Graphics

I did some playing around with Hit Testing using the Visual Tree Helper class. Here are a few things that I learned:

  • The viewport mouse-down event does not fire unless the cursor is over a displayed triangle in the viewport. I am thinking it would be possible to place a large surface at end of the field of view opposite from the camera. There is no reason this could not be almost transparent.
  • It is easy to get the vertices of the triangle that was hit.
  • It is easy to get the point on the surface where the hit occurred.
  • You can the endpoints of a line along the cursor using the following code:

LineRange range;
ViewportInfo.Point2DtoPoint3D(_viewport, ptMouse, out range);

In the viewport mouse-down event, I put the following code:

base.OnMouseLeftButtonDown(args);
Point ptMouse = args.GetPosition(_viewport);

VisualTreeHelper.HitTest(_viewport, null, HitTestDown, new PointHitTestParameters(ptMouse));

I then created the HitTestDown call-back routine:

HitTestResultBehavior HitTestDown(HitTestResult result) {

    RayMeshGeometry3DHitTestResult resultMesh = result as RayMeshGeometry3DHitTestResult;
    ModVis3D vis = resultMesh.VisualHit as ModVis3D;
    string nl = Environment.NewLine;
    int Vertex1 = resultMesh.VertexIndex1;
    int Vertex2 = resultMesh.VertexIndex2;
    int Vertex3 = resultMesh.VertexIndex3;

    string sResult = vis.Generator.Name + nl;
    sResult += Vertex1 + " " + Vertex2 + " " + Vertex3 + nl;
    sResult += resultMesh.VertexWeight1 + " " + resultMesh.VertexWeight2 + " " + resultMesh.VertexWeight3 + nl;
    sResult += resultMesh.PointHit.X + " " + resultMesh.PointHit.Y + " " + resultMesh.PointHit.Z + nl;
    Model3D mod = resultMesh.ModelHit;
    if (mod is Model3DGroup) {
        sResult += "mod is Model3DGroup" + nl;
    } else {
        GeometryModel3D model = mod as GeometryModel3D;
        MeshGeometry3D mesh = model.Geometry as MeshGeometry3D;
        sResult += "Vertex1: ";
        sResult += mesh.Positions[Vertex1].X.ToString() + " ";
        sResult += mesh.Positions[Vertex1].Y.ToString() + " ";
        sResult += mesh.Positions[Vertex1].Z.ToString() + " ";
        sResult += nl;
        sResult += "Vertex2: ";
        sResult += mesh.Positions[Vertex2].X.ToString() + " ";
        sResult += mesh.Positions[Vertex2].Y.ToString() + " ";
        sResult += mesh.Positions[Vertex2].Z.ToString() + " ";
        sResult += nl;
        sResult += "Vertex3: ";
        sResult += mesh.Positions[Vertex3].X.ToString() + " ";
        sResult += mesh.Positions[Vertex3].Y.ToString() + " ";
        sResult += mesh.Positions[Vertex3].Z.ToString() + " ";
        sResult += nl;
       
    }
    SWF.MessageBox.Show(sResult);
    return HitTestResultBehavior.Stop;
}

Tuesday, August 2, 2011

Posting 3D Stuff to WarrenPClark.com

After cleaning stuff up for about a week, I posted what I hope is a workable set of information on 3D photography to WarrenPClark.com. I posted 2 pages of HTML – a default page and Photography.htm. I also posted three files that Photography.htm points to: 3Deifier.exe, BuildingAnOver-UnderStereoViewer.pdf and Viewing3dPhotosOnYourComputer.pdf.

3Deifier evolved from ViewMagic Viewer which it has completely replaced. For now, I have this saved in my dropbox.

The master documents are in dropbox/dev/3Deifier/Documentation.

Here is the list of the Documents I have regarding 3Deifier:

  • Building an Over/Under Stereo Viewer – Similar to ViewMagic™
  • Viewing 3d Photos on your Computer
  • Over Under Viewer.skp – Google Sketchup image of a single periscope
  • Specification.txt – a text document holding a list of critical tasks – not up to date.
  • Specification – Old.doc – an older specification. I think that Viewing 3d Photos on your Computer now makes a better specification than that does. The old one is unnecessarily verbose.