Skip to content

Commit

Permalink
added builtin ruler
Browse files Browse the repository at this point in the history
  • Loading branch information
mdrasmus committed Dec 19, 2012
1 parent c82a73d commit be5cdd2
Show file tree
Hide file tree
Showing 3 changed files with 276 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README
Expand Up @@ -10,7 +10,7 @@ Summary
SUMMON is a python extension module that provides rapid prototyping of 2D
visualizations. By heavily relying on the python scripting language, SUMMON
allows the user to rapidly prototype a custom visualization for their data,
without the overhead of a designing a graphical user interface or
without the overhead of designing a graphical user interface or
recompiling native code. By simplifying the task of designing a
visualization, users can spend more time on understanding their data.

Expand Down
14 changes: 13 additions & 1 deletion lib/summon/__init__.py
Expand Up @@ -1331,7 +1331,6 @@ class Menu (object):

def __init__(self):
self.menuid = summon_core.new_menu()
#print "menu", self.menuid
self.items = []

# NOTE: I tried the __del__ function, but had trouble with destroyMenu on
Expand Down Expand Up @@ -1467,6 +1466,8 @@ class SummonMenu (Menu):

def __init__(self, win):
Menu.__init__(self)
self._win = win
self._ruler = None

# window options
self.window_menu = Menu()
Expand Down Expand Up @@ -1505,10 +1506,21 @@ def __init__(self, win):
self.misc.add_entry("toggle crosshair (ctrl+x)", win.toggle_crosshair)
self.misc.add_entry("toggle aliasing (ctrl+l)", win.toggle_aliasing)
self.misc.add_entry("inspect", lambda: inspector.inspect_window(win))
self.misc.add_entry("ruler", self.toggle_ruler)
self.add_submenu("Misc", self.misc)

self.add_entry("close (q)", win.close)

def toggle_ruler(self):

from summon import hud

if self._ruler is None:
self._ruler = hud.RulerHud(self._win)
else:
self._ruler.remove()
self._ruler = None



#=============================================================================
Expand Down
262 changes: 262 additions & 0 deletions lib/summon/hud.py
Expand Up @@ -6,6 +6,8 @@
"""

import math

import summon
from summon import shapes
from summon.core import *
Expand Down Expand Up @@ -142,3 +144,263 @@ def on_resize(self, width, height):
self.draw(width, height))


#=============================================================================

def get_ruler_auto_size(screenwidth, worldwidth, pixels=50):
"""get most appropriate unit for zoom level"""

if worldwidth == 0.0:
return 1.0
order = min(math.ceil(math.log10(worldwidth * pixels /
float(screenwidth))), 30)
return 10**order


def get_unit_suffix(unit):
"""get the sufffix for a unit"""

order = math.log10(max(unit, 1e-300))

if order < -1:
unitstr = "e" + str(order)
unit2 = unit
elif order < 3:
unitstr = ""
unit2 = 1
elif 3 <= order < 6:
unitstr = "K"
unit2 = 1000
elif 6 <= order < 9:
unitstr = "M"
unit2 = 1e6
elif 9 <= order < 12:
unitstr = "G"
unit2 = 1e9
elif 12 <= order < 15:
unitstr = "T"
unit2 = 1e12
elif 15 <= order:
unitstr = "e" + str(order)
unit2 = unit

return unit2, unitstr


class RulerHud (object):
def __init__(self, win, height=40, width=40, top=True, left=True,
minunit=1,
bgcolor=None, fgcolor=None, fgcolor2=None):
self.height = height
self.width = width
self.minunit = minunit
self.sides = set()
if top:
self.sides.add("top")
if left:
self.sides.add("left")

self.bgcolor = bgcolor if bgcolor else win.get_bgcolor()
self.fgcolor = fgcolor if fgcolor else (
1-self.bgcolor[0], 1-self.bgcolor[1], 1-self.bgcolor[2])

mix = .5; mix2 = 1 - mix
self.fgcolor2 = fgcolor2 if fgcolor2 else (
mix*self.fgcolor[0] + mix2*.5,
mix*self.fgcolor[1] + mix2*.5,
mix*self.fgcolor[2] + mix2*.5, .2)
self.visible = True

self.g = group()
self.add(win)


def add(self, win):
"""Add ruler to a summon.Window 'win'"""

self.win = win

# install callbacks
self.win.add_resize_listener(self.on_resize)
self.win.add_view_change_listener(self.on_scroll)

self.win.screen.add_group(self.g)
self.update()


def remove(self):
"""Remove a ruler from its window"""

if self.win:
# uninstall callbacks
self.win.remove_resize_listener(self.on_resize)
self.win.remove_view_change_listener(self.on_scroll)

self.g.remove_self()
self.g.clear()
self.win = None


def show(self):
"""Shows the ruler"""
self.g.set_visible(True)
self.visible = True

def hide(self):
"""Hides the ruler"""
self.g.set_visible(False)
self.visible = False

def toggle(self):
"""Toggles the ruler's visibility"""
self.visible = not self.visible
self.g.set_visible(self.visible)

def on_resize(self, w, h):
"""Callback for window resize"""
self.update()

def on_scroll(self):
"""Callback for window scroll"""
self.update()

def update(self):
"""Update the display of a ruler"""

self.g.clear()

g = group()
if "top" in self.sides:
g.append(self.draw_horizontal_ruler())
if "left" in self.sides:
g.append(self.draw_vertical_ruler())
if "top" in self.sides and "left" in self.sides:
g.append(self.draw_corner())
self.g.append(g)


def draw_horizontal_ruler(self):
g = group()

# get window dimensions
screen_width, screen_height = self.win.get_size()
x1, y1, x2, y2 = self.win.get_visible()
world_width = x2 - x1

# layout ruler
left = 0
right = screen_width
top = screen_height
bot = screen_height - self.height
xscale = world_width / float(screen_width)

step = get_ruler_auto_size(screen_width, world_width)
unit, unitstr = get_unit_suffix(step)

# ruler background
g.append(group(
color(*self.bgcolor), quads(left, top, right, top,
right, bot, left, bot),
color(*self.fgcolor2),
lines(left, bot, right, bot)))

# make mini hashes
if step >= self.minunit*10 and world_width / step < 1000:
step2 = step // 10
l = []
worldi = (x1 // step2) * step2
while worldi < x2:
screeni = (worldi - x1) / xscale
l.extend([screeni, top, screeni, bot])
worldi += step2
g.append(lines(color(*self.fgcolor2), *l))

# make main hashes
if step >= self.minunit and world_width / step < 1000:
l = []; t = group(color(*self.fgcolor))
worldi = (x1 // step) * step
while worldi < x2:
screeni = (worldi - x1) / xscale
l.extend([screeni, top, screeni, bot])
t.append(
text("%s%s" % (int(worldi // unit), unitstr),
screeni - step/xscale, top, screeni+1, bot,
"middle", "right"))
worldi += step
g.append(group(lines(color(*self.fgcolor), *l), t))

return g



def draw_vertical_ruler(self):
g = group()

# get window dimensions
screen_width, screen_height = self.win.get_size()
x1, y1, x2, y2 = self.win.get_visible()
world_height = y2 - y1

# layout ruler
left = 0
right = self.width
top = screen_height
bot = 0
yscale = world_height / float(screen_height)

step = get_ruler_auto_size(screen_height, world_height)
unit, unitstr = get_unit_suffix(step)

# ruler background
g.append(group(
color(*self.bgcolor), quads(left, top, right, top,
right, bot, left, bot),
color(*self.fgcolor2),
lines(right, top, right, bot)))

# make mini hashes
if step >= 10 and world_height / step < 1000:
step2 = step // 10
l = []
worldi = (y1 // step2) * step2
while worldi < y2:
screeni = (worldi - y1) / yscale
l.extend([left, screeni, right, screeni])
worldi += step2
g.append(lines(color(*self.fgcolor2), *l))

# make main hashes
if step >= 1 and world_height / step < 1000:
l = []; t = group(color(*self.fgcolor))
worldi = (y1 // step) * step
while worldi < y2:
screeni = (worldi - y1) / yscale
l.extend([right, screeni, left, screeni])
t.append(
text("%s%s" % (int(worldi // unit), unitstr),
left, screeni + 100, right, screeni,
"center", "bottom"))
worldi += step
g.append(group(lines(color(*self.fgcolor), *l), t))

return g


def draw_corner(self):

# get window dimensions
screen_width, screen_height = self.win.get_size()

# layout ruler
left = 0
right = self.width
top = screen_height
bot = top - self.height

# ruler background
return group(quads(color(*self.bgcolor),
left, top, right, top,
right, bot, left, bot),
lines(color(*self.fgcolor2), right, top, right, bot,
right, bot, left, bot))


0 comments on commit be5cdd2

Please sign in to comment.