/usr/share/pyshared/gaphas/freehand.py is in python-gaphas 0.7.2-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | """
Cairo context using Steve Hanov's freehand drawing code.
# Crazyline. By Steve Hanov, 2008
# Released to the public domain.
# The idea is to draw a curve, setting two control points at random
# close to each side of the line. The longer the line, the sloppier
# it's drawn.
See: http://stevehanov.ca/blog/index.php?id=33 and
http://stevehanov.ca/blog/index.php?id=93
"""
from math import sqrt
from random import Random
from painter import Context
class FreeHandCairoContext(object):
KAPPA = 0.5522847498
def __init__(self, cr, sloppiness=0.5):
"""
Create context with given sloppiness. Range [0..2.0] gives acceptable
results.
* Draftsman: 0.0
* Artist: 0.25
* Cartoonist: 0.5
* Child: 1.0
* Drunk: 2.0
"""
self.cr = cr
self.sloppiness = sloppiness # In range 0.0 .. 2.0
def __getattr__(self, key):
return getattr(self.cr, key)
def line_to(self, x, y):
cr = self.cr
sloppiness = self.sloppiness
from_x, from_y = cr.get_current_point()
# calculate the length of the line.
length = sqrt( (x-from_x)*(x-from_x) + (y-from_y)*(y-from_y))
# This offset determines how sloppy the line is drawn. It depends on
# the length, but maxes out at 20.
offset = length/10 * sloppiness
if offset > 20: offset = 20
dev_x, dev_y = cr.user_to_device(x, y)
rand = Random((from_x, from_y, dev_x, dev_y, length, offset)).random
# Overshoot the destination a little, as one might if drawing with a pen.
to_x = x + sloppiness * rand() * offset/4
to_y = y + sloppiness * rand() * offset/4
# t1 and t2 are coordinates of a line shifted under or to the right of
# our original.
t1_x = from_x + offset
t1_y = from_y + offset
t2_x = to_x + offset
t2_y = to_y + offset
# create a control point at random along our shifted line.
r = rand()
control1_x = t1_x + r * (t2_x-t1_x)
control1_y = t1_y + r * (t2_y-t1_y)
# now make t1 and t2 the coordinates of our line shifted above
# and to the left of the original.
t1_x = from_x - offset
t2_x = to_x - offset
t1_y = from_y - offset
t2_y = to_y - offset
# create a second control point at random along the shifted line.
r = rand()
control2_x = t1_x + r * (t2_x-t1_x)
control2_y = t1_y + r * (t2_y-t1_y)
# draw the line!
cr.curve_to(control1_x, control1_y, control2_x, control2_y, to_x, to_y)
def rel_line_to(self, dx, dy):
cr = self.cr
from_x, from_y = cr.get_current_point()
self.line_to(from_x + dx, from_y + dy)
def curve_to(self, x1, y1, x2, y2, x3, y3):
cr = self.cr
from_x, from_y = cr.get_current_point()
dev_x, dev_y = cr.user_to_device(x3, y3)
rand = Random((from_x, from_y, dev_x, dev_y, x1, y1, x2, y2, x3, y3)).random
r = rand()
c1_x = from_x + r * (x1-from_x)
c1_y = from_y + r * (y1-from_y)
r = rand()
c2_x = x3 + r * (x2-x3)
c2_y = y3 + r * (y2-y3)
cr.curve_to(c1_x, c1_y, c2_x, c2_y, x3, y3)
def rel_curve_to(self, dx1, dy1, dx2, dy2, dx3, dy3):
cr = self.cr
from_x, from_y = cr.get_current_point()
self.curve_to(from_x+dx1, from_y+dy1, from_x+dx2, from_y+dy2, from_x+dx3, from_y+dy3)
def corner_to(self, cx, cy, x, y):
cr = self.cr
from_x, from_y = cr.get_current_point()
# calculate radius of the circle.
radius1 = Math.sqrt( (cx-from_x)*(cx-from_x) +
(cy-from_y)*(cy-from_y));
radius2 = Math.sqrt( (cx-x)*(cx-x) +
(cy-y)*(cy-y));
dev_x, dev_y = cr.user_to_device(x, y)
rand = Random((cx, cy, dev_x, dev_y, radius1, radius2)).random
# place first control point
c1_x = from_x + self.KAPPA * (cx - from_x) + rand() * sloppiness * radius1 / 2
c1_y = from_y + self.KAPPA * (cy - from_y) + rand() * sloppiness * radius1 / 2
# place second control point
c2_x = x + self.KAPPA * (cx - x) + rand() * sloppiness * radius2 / 1.5
c2_y = y + self.KAPPA * (cy - y) + rand() * sloppiness * radius2 / 1.5
cr.curve_to(c1_x, c1_y, c2_x, c2_y, x3, y3)
def rectangle(self, x, y, width, height):
x1 = x + width
y1 = y + height
self.move_to(x, y)
self.line_to(x1, y)
self.line_to(x1, y1)
self.line_to(x, y1)
if self.sloppiness > 0.1:
self.line_to(x, y)
else:
self.close_path()
class FreeHandPainter(object):
def __init__(self, subpainter, sloppiness=1.0, view=None):
self.subpainter = subpainter
self.view = view
self.sloppiness = sloppiness
def set_view(self, view):
self.view = view
self.subpainter.set_view(view)
def paint(self, context):
subcontext = Context(cairo=FreeHandCairoContext(context.cairo, self.sloppiness), items=context.items, area=context.area)
self.subpainter.paint(subcontext)
# vi:sw=4:et:ai
|