I suspended work on this project due to various domestic and spare-time commitments, not least getting new releases out of the Chemistry Add-In for Word. Over the past year and a half or so I have been picking away at it, experimenting with various Windows Presentation Foundation features. In the meantime, the tree that I was barking up was in danger of being cut down. There was a lot of loose talk about Microsoft abandoning WPF altogether in favour of hardcore, C++ based technologies.
Well, that hasn't happened, thank God. I hate C++. It's nothing more than a glorified assembler with object oriented features bolted on. All C# has in common with it is syntax. C# is an example of how to do an object oriented language properly, combining the elegance of Java with the pragmatism of Visual Basic. Microsoft's renewed commitment to WPF is to be welcomed unconditionally. And this has spurred me on to finish what I started.
Managing Chemistry in WPF
When you draw a chemical structure, you draw atoms and bonds. Bonds link pairs of atoms, and atoms have a finite number of bonds, depending on the chemical element. Bonds are represented by straight lines, atoms by upper and lowercase letters. Except for carbon atoms, which for the sake of clarity are vertices at line junctions. Hydrogen atoms are generally omitted: chemists can infer their presence. Structural formulas are a graphical language in their own right, that require a practiced eye to read.The editor uses the only plausible WPF component for arranging graphical objects precisely, the Canvas. Canvases provide attached properties for precisely locating an object in the x-y plane.
Primitives: Atom and Bond objects
Atoms
I decided to implement Atom and Bond objects that derived from Shape, indirectly through a custom ChemicalShape object. Why Shapes instead of DrawingVisuals? Well, Shapes provide better interactivity and also all the WPF goodies we take for granted such as styling and templates. All you have to do to specify a Shape is to define its geometry:protected override System.Windows.Media.Geometry DefiningGeometry
{
get
{
Point point1 = new Point(this.Position.X, this.Position.Y);
System.Windows.Media.Geometry g = new EllipseGeometry(point1, AtomRadius, AtomRadius);
return g;
}
}
Hardly mind-wrenching stuff: draw a circle at the point where the atom resides. Atoms have a Position property of type Point, and varying this moves the Atom around the Canvas. As stated, Atoms normally reside at vertices. We want to highlight them only when we pass the mouse over them. This is easily done by defining their Fill property as Transparent, but changing this to the system's highlight colour when the mouse passes over them. This can be done in a XAML Style. One of the advantages of this approach is that you can easily simulate 'gravity': snapping the mouse pointer to the Atom's Position when you are roughly in the area of it. WPF's hit testing will allow you to detect whether the current Mouse pointer is within the Atom's geometry, even if you can only infer its existence as a vertex.
Bonds
Atoms can have an arbitrary number of Bonds linking them together. Bonds are defined by four main characteristics: the StartAtom and EndAtom, the Order (single, double, triple) and the Stereo (none, hatch, wedge). I also created StartPoint and EndPoint dependency properties. Why do this when the start and end atoms can define the Bond's placement? Well, dependency properties allow data binding, so when the Atom's position changes, so does the StartPoint or EndPoint. You can then trap these changes in a callback procedure.Bond shapes can be a bit of a pain to draw. Single bonds are easy, but double and triple bonds present more of a challenge. Double bonds can also lie either side of the center line, especially if they are part of a ring.
Wedge and hatch bonds present even more of a challenge. I created a Turtle class (no prizes for guessing what this does) that allows easy placement of points on the Canvas. The Turtle paces out the defining geometry of the bond's shape:
//use a turtle to pace out the wedge shape
Turtle ttl = new Turtle(wedgestartpoint, angle);
ttl.AddPoint();
ttl.Forward(bondlength);
ttl.Clockwise(BasicGeometry.DegToRadians(90));
ttl.Forward(WEDGE_WIDTH / 2);
ttl.AddPoint();
ttl.Anticlockwise(BasicGeometry.DegToRadians(180));
ttl.Forward(WEDGE_WIDTH);
ttl.AddPoint();
The Turtle stores all its points as a List<Point>. This makes it easy to create lines and paths.Fancy bond patterns
Drawing hatch patterns within a wedge shape for a bond is a horrible problem for conventional Windows GDI+ programming. WPF makes it much easier. I defined a read-only dependency property (yes, such things do exist) called Angle. I then bound a LinearGradientBrush to the Angle:<LinearGradientBrush x:Key="HashBrushBase" MappingMode="Absolute" StartPoint="50,0" EndPoint="50,5" SpreadMethod="Repeat">
<LinearGradientBrush.Transform>
<RotateTransform Angle="{Binding RelativeSource={RelativeSource
AncestorType={x:Type chemistry:Bond}}, Path=Angle}" />
</LinearGradientBrush.Transform>
<GradientStop Color="Black" Offset="0" />
<GradientStop Color="Black" Offset="0.25" />
<GradientStop Color="Transparent" Offset="0.25" /> <GradientStop Color="Transparent" Offset="0.5" /> </LinearGradientBrush>
This performs remarkably quickly: molecules can be rotated in real time without any perceptible delay to rendering of hatch or wedge bonds:
Next installment: how the objects are organised within the editor.

No comments:
Post a Comment