So, I'm now partly sold on the power of a Visitor approach to rendering. Consider this snippet:
if (clicked) diagram.accept(new DropShadowVisitor(g, 5, -5);
else diagram.accept(new DrawVisitor(g));
What this is doing is drawing a diagram normally unless the mouse is clicked, when it draws with a drop shadow. I see that the beauty of this is the ability to manipulate functionality as a block (just as in languages where you can pass around functions...).
However, I should point out that the approach has its tricky rapids as well as such smooth sailing. The image below is a spot-the-difference (click for bigger):
On the left is the version drawn by a naive first try at the drop visitor. Its methods look like this, the visitText(Text text) method:
g.setColor(Color.LIGHT_GRAY);
g.drawString(text.text, text.x, text.y);
g.setColor(Color.BLACK);
g.drawString(text.text, text.x + dx, text.y + dy);
The problem with this code is subtle - the elements are visited once each, and the shadow is rendered at the same time as the element. The change that had to be made was to have a boolean "drawingShadow" and to visit the elements twice
this.drawingShadow = true;
for (DiagramElement element : diagram.children) { element.accept(this); }
this.drawingShadow = false;
for (DiagramElement element : diagram.children) { element.accept(this); }
The shadow has to be drawn first - BUT first for the whole diagram, not just first for each element. So the new text method is :
if (this.drawingShadow) {
g.setColor(Color.LIGHT_GRAY);
g.drawString(text.text, text.x, text.y);
} else {
g.setColor(Color.BLACK);
g.drawString(text.text, text.x + dx, text.y + dy);
}
Comments