62 lines
1.8 KiB
Python
62 lines
1.8 KiB
Python
#!/usr/bin/env python
|
|
|
|
from decimal import Decimal
|
|
|
|
from leather.scales.base import Scale
|
|
from leather.ticks.score import ScoreTicker
|
|
|
|
|
|
class Linear(Scale):
|
|
"""
|
|
A scale that linearly maps values from a domain to a range.
|
|
|
|
:param domain_min:
|
|
The minimum value of the input domain.
|
|
:param domain_max:
|
|
The maximum value of the input domain.
|
|
"""
|
|
def __init__(self, domain_min, domain_max):
|
|
if domain_min > domain_max:
|
|
raise ValueError('Inverted domains are not currently supported.')
|
|
elif domain_min == domain_max:
|
|
# Default to unit scale
|
|
self._data_min = Decimal(0)
|
|
self._data_max = Decimal(1)
|
|
else:
|
|
self._data_min = Decimal(domain_min)
|
|
self._data_max = Decimal(domain_max)
|
|
|
|
self._ticker = ScoreTicker(self._data_min, self._data_max)
|
|
|
|
def contains(self, v):
|
|
"""
|
|
Return :code:`True` if a given value is contained within this scale's
|
|
domain.
|
|
"""
|
|
return self._data_min <= v <= self._data_max
|
|
|
|
def project(self, value, range_min, range_max):
|
|
"""
|
|
Project a value in this scale's domain to a target range.
|
|
"""
|
|
value = Decimal(value)
|
|
range_min = Decimal(range_min)
|
|
range_max = Decimal(range_max)
|
|
|
|
pos = (value - self._ticker.min) / (self._ticker.max - self._ticker.min)
|
|
|
|
return ((range_max - range_min) * pos) + range_min
|
|
|
|
def project_interval(self, value, range_min, range_max):
|
|
"""
|
|
Project a value in this scale's domain to an interval in the target
|
|
range. This is used for places :class:`.Bars` and :class:`.Columns`.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def ticks(self):
|
|
"""
|
|
Generate a series of ticks for this scale.
|
|
"""
|
|
return self._ticker.ticks
|