Source code elsie/boxtree/lazy.py

 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
from typing import Tuple


class LazyValue:
    """
    Value that will be resolved after layout is computed.
    """

    def __init__(self, fn):
        self.fn = fn

    def map(self, fn) -> "LazyValue":
        """Lazily maps the result of this value."""
        return LazyValue(lambda: fn(self.eval()))

    def add(self, value: float) -> "LazyValue":
        """Lazily adds the given number to this value."""
        return self.map(lambda v: v + value)

    def eval(self):
        """Evaluates the actual value."""
        return self.fn()


class LazyPoint:
    """
    2D point that will be resolved after layout is computed.
    """

    def __init__(self, x, y):
        assert isinstance(x, LazyValue)
        assert isinstance(y, LazyValue)
        self.x = x
        self.y = y

    def add(self, x: float, y: float) -> "LazyPoint":
        """
        Creates a new point moved by the given (x, y) offset.

        Parameters
        ----------
        x: float
            x offset
        y: float
            y offset
        """
        return LazyPoint(self.x.map(lambda v: v + x), self.y.map(lambda v: v + y))

    def eval(self) -> Tuple[float, float]:
        """Evaluates the actual point."""
        return self.x.eval(), self.y.eval()


def unpack_point(obj, box):
    if isinstance(obj, LazyPoint):
        return obj.x, obj.y
    if (isinstance(obj, tuple) or isinstance(obj, list)) and len(obj) == 2:
        return box.x(obj[0]), box.y(obj[1])
    raise Exception("Invalid point: {!r}".format(obj))


def eval_value(obj):
    if isinstance(obj, LazyValue):
        return obj.eval()
    else:
        return obj


def eval_pair(pair):
    return eval_value(pair[0]), eval_value(pair[1])