Edit: Fantasia Painter (full version), is now available here: http://fantasia.nokola.com
Edit 2: The Fantasia Lite version below, has been updated to include the new Rainbow brush (source code included too).
Here’s what I built tonight – a drawing tool that utilizes procedural brushes to enable almost everyone to create fluffy rabbits like this:
Note that it took me about 2 minutes to “build the rabbit”. I’m sure someone else can do better. Supposedly, it lives in the hole on the right.
Here’s the live app:
As usual: Download source code
Note that this whole thing is inspired by http://mrdoob.com/120/Harmony (HTML5 – doesn’t work in IE8 as I write this) which was originally inspired by http://www.zefrank.com/scribbler/gallery/index_ran.html. By the way, make sure to check the funny videos “How To Dance Properly” on Ze’s page: http://www.zefrank.com/invite/swfs/index2.html
I talked way back (March or something) with Mr. Doob and he released his Harmony code under the MIT license. His latest source code is now released under GPL though. I didn’t look at it since I wanted my sources to be MS-PL license-able.
What Is A Procedural Brush?
A typical “brush” will just draw whatever it is supposed to draw (e.g. connect dots with lines). It just needs the current and previous mouse position.
A procedural brush is “smarter”, since it takes more variables into account.
Fantasia’s brushes make use of the following data to modify the drawing output:
- mouse position
- velocity (how fast the mouse moved)
- time between 2 consecutive dots
- distance between dots
- slope (angle) of the line between the last and current dot
- the last N previous dots (N varies between 2 and 100000)
The Fantasia brushes modify these outputs:
- Color (e.g. color can be changed based on angle which can produce cool output – I haven’t published this brush yet)
- Opacity (the faster you move, the less visible the trace is, such as in the OldPen sample
- Stroke width (again can be controlled by speed, or maybe by slope (angle), which produces a nice caligraphy effect)
- Drawing other random lines
Here is the source code of few of the brushes:
The Line Brush – same as a “regular” brush, just connects dots with straight lines:
public override void Stroke(double x, double y, double dx, double dy, double timeMsec, double distance)
{
_surface.Render(new Line() { X1 = _prevX, Y1 = _prevY, X2 = x, Y2 = y, Stroke = _brush }, null);
}
The Old Pen brush, uses velocity = distance / time, to modify the color intensity:
public override void Stroke(double x, double y, double dx, double dy, double timeMsec, double distance)
{
_surface.Render(new Line()
{
X1 = _prevX,
Y1 = _prevY,
X2 = x,
Y2 = y,
Stroke = new SolidColorBrush(
new Color()
{
A = (byte)(255 - distance / timeMsec * 100),
R = (byte) (_color.R * (distance / timeMsec * 100)),
G = (byte) (_color.G * (distance / timeMsec * 100)),
B = (byte) (_color.B * (distance / timeMsec * 100)),
})
}, null);
}
The History brush, most complex so far and used for the sketch and Furs:
public override void Stroke(double x, double y, double dx, double dy, double timeMsec, double distance)
{
_points.Add(new Point(x, y));
if ((Memory > 0) && (_points.Count > Memory)) _points.RemoveAt(0);
_surface.Render(new Line() { X1 = _prevX, Y1 = _prevY, X2 = x, Y2 = y, Stroke = _solidBrush }, null);
foreach (Point p in _points)
{
double cdx = p.X - x;
double cdy = p.Y - y;
double dist2 = cdx * cdx + cdy * cdy;
if ((dist2 < _activationDistance2) && (_random.NextDouble() > (dist2 / (_activationDistance2 * ActivationChance))))
{
_surface.Render(new Line() { X1 = x + cdx * ReachSource, Y1 = y + cdy * ReachSource, X2 = p.X - cdx * ReachDest, Y2 = p.Y - cdy * ReachDest, Stroke = _stroke }, null);
}
}
}
The history brush can also do internal highlights if the _stroke is set to a LinearGradientBrush.
There is also a prototyping Test brush: I’m trying to “smoothen” the line as it is getting drawn. It’s using binaries by Charlez Petzold: http://www.charlespetzold.com/blog/2009/01/Canonical-Splines-in-WPF-and-Silverlight.html
Hope you like it! Please comment!