Skip to content

Commit

Permalink
Smart scales.
Browse files Browse the repository at this point in the history
  • Loading branch information
onyxfish committed May 18, 2016
1 parent 140b1e3 commit 997c4b6
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 62 deletions.
33 changes: 18 additions & 15 deletions leather/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,48 @@ class Axis(Renderable):
"""
A horizontal or vertical chart axis.
"""
def __init__(self, scale, orient='bottom', ticks=5, tick_width='1px', tick_size=4, tick_color='#eee', label_color='#9c9c9c', zero_color='#a8a8a8'):
self.scale = scale
self.orient = orient
def __init__(self, orient='bottom', ticks=5, tick_width='1px', tick_size=4, tick_color='#eee', label_color='#9c9c9c', zero_color='#a8a8a8'):
self.ticks = ticks
self.tick_width = tick_width
self.tick_size = tick_size
self.tick_color = tick_color
self.label_color = label_color
self.zero_color = zero_color

def to_svg(self, width, height):
def to_svg(self, width, height, scale, orient):
"""
Render this axis to SVG elements.
"""
group = ET.Element('g')
group.set('class', 'axis ' + orient)

if self.orient == 'left':
if orient == 'left':
label_x = -(self.tick_size * 2)
x1 = -self.tick_size
x2 = width
project_range = [height, 0]
elif self.orient == 'bottom':
elif orient == 'bottom':
label_y = height + (self.tick_size * 2)
y1 = 0
y2 = height + self.tick_size
project_range = [0, width]

for value in self.scale.ticks(self.ticks):
projected_value = self.scale.project(value, project_range)
for value in scale.ticks(self.ticks):
tick_group = ET.Element('g')
tick_group.set('class', 'tick')

projected_value = scale.project(value, project_range)

if value == 0:
tick_color = self.zero_color
else:
tick_color = self.tick_color

if self.orient == 'left':
if orient == 'left':
y1 = projected_value
y2 = projected_value

elif self.orient == 'bottom':
elif orient == 'bottom':
x1 = projected_value
x2 = projected_value

Expand All @@ -61,15 +63,14 @@ def to_svg(self, width, height):
y2=six.text_type(y2),
stroke=tick_color
)

tick.set('stroke-width', self.tick_width)

if self.orient == 'left':
if orient == 'left':
x = label_x
y = projected_value
dy = '0.32em'
text_anchor = 'end'
elif self.orient == 'bottom':
elif orient == 'bottom':
x = projected_value
y = label_y
dy = '1em'
Expand All @@ -85,7 +86,9 @@ def to_svg(self, width, height):
label.set('text-anchor', text_anchor)
label.text = six.text_type(value)

group.append(tick)
group.append(label)
tick_group.append(tick)
tick_group.append(label)

group.append(tick_group)

return group
14 changes: 4 additions & 10 deletions leather/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,20 +111,14 @@ def _validate_dimension(self, dimension):
"""
scale = self._scales[dimension]
axis = self._axes[dimension]
data_type = self._types[dimension]

if not axis:
if not scale:
scale = Scale.infer(self._layers, dimension, data_type)
scale = Scale.infer(self._layers, dimension, self._types[dimension])
else:
scale = scale

if dimension == X:
orient = 'bottom'
elif dimension == Y:
orient = 'left'

axis = Axis(scale, orient)
axis = Axis()
# Verify data are within bounds
else:
pass
Expand Down Expand Up @@ -163,8 +157,8 @@ def to_svg_group(self, width, height, margin=None):
x_scale, x_axis = self._validate_dimension(X)
y_scale, y_axis = self._validate_dimension(Y)

axes_group.append(x_axis.to_svg(canvas_width, canvas_height))
axes_group.append(y_axis.to_svg(canvas_width, canvas_height))
axes_group.append(x_axis.to_svg(canvas_width, canvas_height, x_scale, 'bottom'))
axes_group.append(y_axis.to_svg(canvas_width, canvas_height, y_scale, 'left'))

# Series
series_group = ET.Element('g')
Expand Down
2 changes: 1 addition & 1 deletion leather/scales/linear.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ def ticks(self, count):
"""
Return a sequence of :code:`count` ticks based on this scale.
"""
i = int((self.max - self.min) / count)
i = int((self.max - self.min) / (count - 1))

return range(self.min, self.max + i, i)
77 changes: 43 additions & 34 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,42 +64,51 @@
#
# grid.to_svg('test.svg', 1200, 600)

data = [[
(0, 3),
(4, 5),
(7, 9),
(8, 4)
], [
(0, 4),
(1, 3),
(2, 3),
(10, 7),
(15, 5)
], [
(0, 4),
(5, 5),
(6, 6),
(7, 7),
(8, 8)
], [
(4, 4),
(6, 3),
(7, 5),
(8, 6),
(12, 10)
]]

lattice = leather.Lattice(data, leather.Lines())

lattice.to_svg('test.svg', 1200, 600)
# data = [[
# (0, 3),
# (4, 5),
# (7, 9),
# (8, 4)
# ], [
# (0, 4),
# (1, 3),
# (2, 3),
# (10, 7),
# (15, 5)
# ], [
# (0, 4),
# (5, 5),
# (6, 6),
# (7, 7),
# (8, 8)
# ], [
# (4, 4),
# (6, 3),
# (7, 5),
# (8, 6),
# (12, 10)
# ]]
#
# lattice = leather.Lattice(data, leather.Lines())
#
# lattice.to_svg('test.svg', 1200, 600)

# data = [
# (3, 'foo'),
# (5, 'bing'),
# (9, 'baz'),
# (4, 'blurg')
# ]
data = [
(3, 1),
(5, 3),
(9, 12),
(4, 15)
]
#
# chart = leather.Chart()
# chart.add_bars(data)
# chart.to_svg('test.svg')

chart = leather.Chart()
chart.set_x_scale(leather.Linear(0, 20))
chart.set_x_axis(leather.Axis(ticks=5))
chart.set_y_scale(leather.Linear(0, 20))
chart.set_y_axis(leather.Axis(ticks=5))
chart.add_dots(data)

chart.to_svg('test.svg')
22 changes: 20 additions & 2 deletions tests/test_chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,41 @@ def setUp(self):
(9, 10)
]

def test_simple(self):
def test_single_series(self):
chart = leather.Chart()
chart.add_dots(self.data1)

svg = self.render_chart(chart)

self.assertElementCount(svg, '.axis', 2)
self.assertElementCount(svg, '.series', 1)
self.assertElementCount(svg, '.dots', 1)
self.assertElementCount(svg, 'circle', 4)

def test_multiple(self):
def test_multiple_series(self):
chart = leather.Chart()
chart.add_dots(self.data1)
chart.add_dots(self.data2)

svg = self.render_chart(chart)

self.assertElementCount(svg, '.axis', 2)
self.assertElementCount(svg, '.series', 2)
self.assertElementCount(svg, '.dots', 2)
self.assertElementCount(svg, 'circle', 9)

def test_custom_scale(self):
chart = leather.Chart()
chart.set_x_scale(leather.Linear(0, 20))
chart.set_x_axis(leather.Axis(ticks=5))
chart.set_y_scale(leather.Linear(0, 20))
chart.set_y_axis(leather.Axis(ticks=5))
chart.add_dots(self.data1)

svg = self.render_chart(chart)

self.assertElementCount(svg, '.axis', 2)
self.assertElementCount(svg, '.tick', 10)
self.assertElementCount(svg, '.series', 1)
self.assertElementCount(svg, '.dots', 1)
self.assertElementCount(svg, 'circle', 4)

0 comments on commit 997c4b6

Please sign in to comment.