Now that spatial queries are implemented, the Presentation Layer needs to access the Visuals stored in the R-Tree. The Presentation Layer consists of separate classes that handle the distinct visual layers. These are the Nodes, the Connectors, and the Labels. Additional layers are used for overlays and prototype Visuals, but these are not of importance right now.
BrainSharper supports dragging the whole canvas at once. Zooming is not yet supported, but will be soon.
Right now, the Presentation Layer receives the events that are dispatched by the domain and realizes them by creating or modifying (WPF) control objects. Obviously, this is a push-model: The domain pushes the events to the user interface.
To change the Presentation Layer from a stupid “I present you all Visuals” Canvas to one that manages the only the Visuals inside the Viewing Area, the following components need to be created:
Additionally, I decided to split up the layer classes into a controlling part, that handles all domain events, and a viewing part, that displays the controls.
All in all, the Presentation Layer is going to morph towards a more pull-oriented nature. It watches the domain events to get notified when something changes. And whenever the view area changes, it queries the missing Visuals directly from the database.
Below is a rough visualization that covers only the node layer:
Some details of the implementation:
The Visual Presenter queries the Visuals from the R-Tree table. Obviously, this takes some time depending on the number of visuals that intersect with the Viewing Area. On my computer, the queries run in sub-millisecond time, but to avoid lags on slower computers, I decided to decouple the queries from the main thread. This should improve the scrolling experience, but may cause new Visuals to pop in while the user is dragging the document.
Another advantage of introducing a Worker Thread, is that the Visual Collector can also use it. I’ve decided to implement the Visual Collector in a similar way like the Presenter: As soon the Viewing Area has changed and the user is inactive for some time, the Visual Collector runs a background task to find out what Visuals should be displayed inside the viewing area. It then instructs all layers to hide Visuals that are not visible. Of course, the Layer Views could decide autonomously to hide the visuals that are not inside the viewing area, but I wanted that the Presenter and the Collector use the same rectangle information for the intersection tests. This guarantees that the two mechanisms don’t contradict in their decisions. Another advantage is that the Presenter and the Collector are both independent of the user interface framework.
The next step for BrainSharper is to extend its capability to handle very large documents. It should be possible to edit documents that contain thousands of nodes and connectors without noticing a drop in rendering performance. While this “optimization” may look like a premature one, I fear that continuing without it would have dramatic consequences on the internal design, which can be very hard to fix later on. Exceptionally, and only when an optimization directly concerns scalability, my personal guidelines allow me to optimize prematurely, so here it goes:
In its document editor, BrainSharper uses event sourcing to build up the domain model. There are two derived models, or as I call them now “views”, these are a) the domain model and b) the compensating changesets. When a new event is created, the event and both views are written to an SQLite database in one single transaction.
SQLite has this great feature, the R-Tree, which implements a data structure that is suitable for queries over multi-dimensional ranges. So the idea is to maintain one separate R-Tree table to optimize queries for the presentation layer. Maintaining an R-Tree table has the following consequences:
Well, and that is only half of the complexity. A lot more is required to implement the caching behavior for the visuals that are shown on the screen and specifically the logic that decides what rectangles are being queried from the R-Tree. I may cover that in another post.
Be warned: this is a rant, and I may not have solutions for the problems outlined.
Right now I am trying to use WPF 4 to implement a concept mapping editor. I was seriously prepared to handle some unexpected WPF issues by my own. But some details just pissed me off:
The first obvious problem was the font quality (again). By default, WPF uses Cleartype to render fonts; obviously a custom implementation, because WPF just ignored that I switched Cleartype off, system wide.
(screenshots of a WPF performance tool, the one below is zoomed 2 times)
This is – by far – the worst font quality rendering I’ve ever seen on any platform I can remember. (note that the top screenshot above may actually look even worse if you don’t have the same pixel format like my monitor, and the maximized one is just for reference to see how Cleartype works).
Cleartype can be switched off by changing a simple property (a recent feature introduced in WPF 4), but the anti-aliased results without font hinting and Cleartype were too blurry and also a lot worse than any other anti-aliased font rendering I’ve seen before. The only acceptable setting was to draw the fonts with font aligning (hinting) and gray scale anti-aliasing. The result looked slightly odd, but the readability itself was a lot better now.
For my concept mapping editor I render drop shadows at each node to increase the plasticity a bit. Everything went fine until I implemented a gesture that allowed to move the entire canvas around. Performance was dropping below 20 frames per seconds for a small number of nodes (about 40), which was very unexpected given the hardware GPU acceleration available. I first verified that everything was indeed rendered in hardware and found out that a lot of temporary render targets were created.
Drawing a WPF visual with a drop shadow works by first drawing the visual to an intermediate render target and then drawing the contents of render target together and the shadow on the screen. But for whatever reason, these intermediate render targets seem to be very, very slow. Switching the shadows off got me back to 80 frames per seconds.
Now there are two primary ways to optimize drop shadow rendering in WPF.
And now the funny thing which really reflects back on the bad implementation quality of WPF. Switching from hardware rendering to software rendering actually improved the framerate, not enough, but considerable. Can’t be, I thought. What a mess.
My solution here is just to turn off all drop shadows while the user is moving the canvas. Simple but effective.
Update: Using intermediate render targets seems to be a lot faster in software. You can find out about the number of render targets your app uses with Perforator’s HW IRTs Per Frame History Graph.
To actually find out why the performance problem existed, I used the WPF Performance Suite by Microsoft, but initially were blocked by two strange bugs (here and here).
Now, after all this bad experience, and with the plan to port all the UI code to Mac OS X, I am reconsidering to use Silverlight or Moonlight. If this does not turn out well, I need to accept that there is currently no decent user interface library available for Windows, except.. yes, may be Qt again, but this will be another story.
Now that BrainSharper’s nodes and connectors are working quite fine, I am thinking about how labels should be implemented.
BTW: A screenshot of BrainSharper in action:
The node in the center is the selected node. By tapping [-], it can be deleted (including all the related connectors) and by pressing and holding [+] it can be connected to an existing node. If there is no node at the time the mouse button is released, a new node is created and connected.
So back to the labels. The most pragmatic way to implement labels as a separate user interface and database object. A label indeed looks similar to a node, but has some different behavior, specifically when it comes to automatic layout and the relation to its connecting lines.
![]()
If BrainSharper should support the creation of multi-way connectors, and allow further parameterization (for example by making them bendable), two new connector types are needed:
And together with the node to node connector, three different connectors are required to implement all connecting paths.
Premature optimization may be evil, but this cries out loud for a removal of duplication. Right now a connector refers to the From and To node it is connecting with (using GUIDs). To remove the duplication, all connectors need to be treated in a generic way, and all other objects to which connectors can refer to, should be treated as generic nodes.
What BrainSharper needs to support are typed nodes and typed connectors. So our new label just got promoted to a typed node and the three connector types collapse into a generic one.
One consequence is that the attribute schemes of the nodes and connectors need to be treated differently. And there must be a clear distinction between attributes that are generic (like the time of creation) and attributes that are specific to a type (like an absolute position of a node, which a label does not have).
One further realization is that this model allows for plugging in different node and connector types. Actually this is exactly what I had in mind when I was initially planning BrainSharper, but it was too complex to implement upfront, and now, unexpectedly, this feature needs to added very early in the development process.
I truly hope that making nodes and connectors generic does not cause a lot of accidental complexity. I plan to implement the type reference of each node and connector by another GUID that does refer to the (yet fixed) meta information the concrete instance requires (like its attributes, user interface representation and layouting algorithm).
Update: I recognized that for supporting generic nodes and connectors, only the domain model needs to be changed. The events that exist so far, don’t need to be changed, because they represent the user’s intention. This means that graphs that are drawn with the current version should be usable by a future, more generic version of BrainSharper. And because multi-way connectors are not on my todo list of the first product version (a minimum viable product), I decided against going generic right now.
With BrainSharper getting mature, the domain model is starting to show dependencies among the different objects. For example, a node can connect to other nodes, and before a node is deleted, all its connections need to be deleted.
The following ideas came to my mind:
Obviously, the second solution seems to be the only way to move forward.
Programming a solution to this specific problem is not a big task, but what about dependencies in general, what if they get more complex? For example, BrainSharper needs to support labels at the connectors and adding connections to the labels may also be allowed. And what if multiple nodes were deleted at once?
Consequently, there is a more generic, recursive dependency problem to solve. I guess this cries for a small DSL, which describes the dependencies among the domain model and specifies the events that need to be created to enforce the constraints.
Update: Instead of a DSL, I’ve implemented a dependency resolution algorithm which recognizes all destructive events in a change set and prefixes them with the destructive events of the dependent objects. The destructions had to be topologically sorted to remove duplicates and keep the order consistent. This ensures that the (re)creation events of the compensating change set were ordered properly. Not a perfect solution yet, but generic enough to be extended with a few lines of code.