In this article, you will see how you can bring the goodness of Processing on WPF.

Contents
- Introduction
- Drawing/Drawer Space Paradigm
- ProcessingContext
- Mini Language
- In Depth, Matrix Power
- Conclusion
- History
Introduction
Why do visual artists and data visualization experts like processing so much when WPF can do better?
That’s the question I wanted to answer when I bought a book about Processing… and here is what I found.
- Processing is only about drawing, it makes things simpler for its audience by limiting its reach (even if theoretically you can do everything, it would be too cumbersome to create a whole application with it)
- Processing makes the difference, out of the box, between drawing space and drawer space.
I can’t do anything about the first point. C# is about general purpose development and it must stay like that.
However, for the second point, an idea sparks in my mind: What if we could draw a geometry in WPF with this drawing/drawer space paradigm?
Drawing/Drawer Space Paradigm
With the Drawing/Drawer space paradigm, the way to draw a line of 45 degree with length 10 on a sheet, is to rotate the sheet 45 degree, and then trace a straight line of length 10.
In the Drawing only space paradigm, you would calculate the coordinate of the 2nd point of the line with trigonometry rules: end.X = COS(45) * 10, end.Y = SIN(45) * 10.
Then trace the line to this point.
On a more complex drawing, what does it mean?
To draw that cross with the Drawing only space paradigm, you’ll need to specify all coordinates.

In plain xaml, it is something like that.
<Path x:Name="path" Data="M 0 0 L 5 0 L 5 -5 L 10 -5 L 10 0 L 15 0
L 15 5 L 10 5 L 10 10 L 5 10 L 5 5 L 0 5 Z"
Stretch="Uniform"
Fill="LightBlue"
Stroke="Black" ></Path>
On the other hand, with the Drawing/Drawer space paradigm, you only specify how to draw it, by moving your pencil and rotating the sheet.

Here, I specify only the moves of my hand and sheet to draw that.
I move 5 forward, then rotate the sheet of –90 degree, then move 5 forward, then rotate 90, then move 5 forward, then rotate 90, etc.
In XAML:
<Path local:GeometryProperties.Data="L 5 ROT -90 L 5 ROT 90 L 5 ROT 90
L 5 ROT -90 L 5 ROT 90 L 5 ROT 90
L 5 ROT -90 L 5 ROT 90 L 5 ROT 90
L 5 ROT -90 L 5 ROT 90 L 5 ROT 90 F Z"
Stretch="Uniform"
Fill="LightBlue"
Stroke="Black" ></Path>
When you do a rotation, you don’t rotate the figure, you are rotating the sheet on which you draw… what I call the drawer space, as opposed to the sheet space (or drawing space).
Let’s now take a look at a harder example:
With the drawing only space paradigm, to do the following geometry, you need to specify the coordinates of each ellipse. That means using your forgotten trigonometry.

Can you draw the same thing without trigonometry? Sure you can.
With the drawing/drawer space paradigm, you can say:
- Save basis, Move 10, Draw Ellipse, Load basis, Rotate 45
- Repeat 7 times
You can see I show you the drawer’s basis when I Draw Ellipse… as you can see, the Drawer’s basis is rotated.

In this new mini language that is efficiently coded like that: (Push and Pop save and load the current/latest basis):
<Path x:Name="path" local:GeometryProperties.Data="F E 20
ROT 45 PUSH M 100 E 20 POP
ROT 45 PUSH M 100 E 20 POP
ROT 45 PUSH M 100 E 20 POP
ROT 45 PUSH M 100 E 20 POP
ROT 45 PUSH M 100 E 20 POP
ROT 45 PUSH M 100 E 20 POP
ROT 45 PUSH M 100 E 20 POP
ROT 45 PUSH M 100 E 20 POP"
Stretch="Uniform"
Fill="LightBlue"
Stroke="Black" ></Path>
How easy is that?
ProcessingContext
I called the most important class, with the name of what seed this idea in my mind.

The important part is that each method on ProcessingContext
modifies the GeometryGroup
we pass in the constructor:
public ProcessingContext(GeometryGroup group)
{
_Path = new PathGeometry();
_Geometries = group;
_Geometries.Children.Add(_Path);
}
Origin
is the origin of the drawer’s space, and _CurrentTransform
, how to transform a point in drawing coordinate into the drawer coordinate. (I’ll talk about that in depth later.)
Mini-Language
Almost each operation is mapped to the mini language.
[GeometryCommand("S")]
public void Scale(double factor)
{
this.PushTransform(new ScaleTransform(factor, factor)
{
CenterX = _Origin.X,
CenterY = _Origin.Y
});
}
This means that “S 2.0” will be interpreted by Scale(2.0);
You can execute the mini language with ProcessingContext.Execute(string data)
.
Then, coding an attached property to use on Shape
was relatively easy.
public class GeometryProperties
{
public static string GetData(DependencyObject obj)
{
return (string)obj.GetValue(DataProperty);
}
public static void SetData(DependencyObject obj, string value)
{
obj.SetValue(DataProperty, value);
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.RegisterAttached("Data", typeof(string),
typeof(GeometryProperties), new PropertyMetadata(null, OnDataChanged));
static void OnDataChanged(DependencyObject source, DependencyPropertyChangedEventArgs args)
{
var path = source as Path;
GeometryGroup pathGeometry = source as GeometryGroup;
if(path != null)
{
pathGeometry = new GeometryGroup();
path.Data = pathGeometry;
}
if(pathGeometry != null)
{
var processing = new ProcessingContext(pathGeometry);
processing.Execute(args.NewValue as string);
}
}
}
So here is a summary of the mini language (you can see a sample in the source code), all parameters are relative to the drawer’s basis.
M x y? | Move space from origin |
L x y? stroked? | Trace a line, and move basis |
ROT deg | Rotate basis from origin |
S x y? | Scale basis from origin |
PUSH | Save current basis |
POP | Load last pushed basis |
BASIS size | Draw the current basis |
BEZ x y xControl yControl, stroked | Draw a bezier line and move basis to x y |
E xRadius yRadius? | Draw ellipse |
R xRadius yRadius? xcornRadius? ycornRadius? | Draw rectangle with rounded borders |
C | Figures will be closed |
NC | Figures will not be closed |
F | Figures will be filled |
NF | Figures will not be filled |
Z | New figure |
In Depth, Matrix Power
As you have seen, the ProcessingContext
class holds a _CurrentTransform
, this transform can transform any point back and forth between drawing space and drawer space. Each transform of basis will change _CurrentTransform
which is internally nothing more than a Transform
(which is inside hold a Matrix
).
[GeometryCommand("ROT")]
public void Rotate(double degree)
{
var rotation = new RotateTransform(degree)
{
CenterX = _Origin.X,
CenterY = _Origin.Y
};
this.PushTransform(rotation);
}
private void PushTransform(Transform transform)
{
Append(transform.Value);
_Origin = _CurrentTransform.Transform(new Point(0, 0));
}
private void Append(Matrix matrix)
{
var currentMatrix = _CurrentTransform.Value;
currentMatrix.Append(matrix);
_CurrentTransform = new MatrixTransform(currentMatrix);
}
With this _CurrentTransform
, I can safely transform every parameter from drawer’s space to drawing space, create a segment, and then translate the basis.
[GeometryCommand("L")]
public LineSegment Line(double x, double y, bool stroked)
{
var nextPoint = _CurrentTransform.Transform(new Point(x, y));
var line = new LineSegment(nextPoint, stroked);
this.CurrentFigure.Segments.Add(line);
Translate(x, y);
return line;
}
private void Translate(double x, double y)
{
var t = _CurrentTransform.Transform(new Point(x, y));
PushTransform(new TranslateTransform(t.X - _Origin.X, t.Y - _Origin.Y));
}
So what Push
, and Pop
are doing? Well, they are just saving the basis…
[GeometryCommand("PUSH")]
public void PushMatrix()
{
_Matrixes.Push(_CurrentTransform.Value);
}
[GeometryCommand("POP")]
public void PopMatrix()
{
_CurrentTransform = new MatrixTransform(_Matrixes.Pop());
_Origin = _CurrentTransform.Transform(new Point(0, 0));
}
That’s all I have to say about the implementation… I only convert coordinates from one system to another, and default transformations centered on the drawer’s origin…
One more example for you:
[GeometryCommand("BEZ")]
public BezierSegment Bezier(int x, int y, int controlX, int controlY, bool stroked = true)
{
var nextPoint = _CurrentTransform.Transform(new Point(x, y));
var controlPoint = _CurrentTransform.Transform(new Point(controlX, controlY));
var bezier = new BezierSegment(_Origin, controlPoint, nextPoint, stroked);
this.CurrentFigure.Segments.Add(bezier);
Translate(x, y);
return bezier;
}
The code is really lean, using the right abstraction for the task makes things so much simple.
Conclusion
Processing is sure a great way to create powerful visuals… but its only power, for a developer that knows a general purpose language, is its paradigm for drawing stuff.
I understood how Processing worked internally when I read an article about cameras in OpenGL… (if the link is dead, check Google cache) this is when I got the “Ahah” moment of understanding a matrix as a basis, then everything popped in my mind: aaah processing ? that’s how they do it ! (and the second “Ahah” moment : aah that’s why matrices are cool!)
OpenGL and Processing seems far enough but the dot connected, and I could bring the goodness of Processing on WPF.
History
- 15th August, 2013: Initial version