summaryrefslogtreecommitdiff
path: root/tools.py
blob: a884db37b5a8fd8844e0f31a47cbb62f8014e6ac (plain)
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
import numpy as np
import time
import datetime
import sys
import math

def ufloat_to_str(x):
    msd = -int(math.floor(math.log10(x.std_dev())))
    return '%.*f +/- %.*f' % (msd, x.nominal_value, msd, x.std_dev())

def progress(seq):
    "Print progress while iterating over `seq`."
    n = len(seq)
    print '[' + ' '*21 + ']\r[',
    sys.stdout.flush()
    update_interval = max(n // 10, 1)
    for i, item in enumerate(seq):
        if i % update_interval == 0:
            print '.',
            sys.stdout.flush()
        yield item
    print ']'
    sys.stdout.flush()

def debugger_hook(type, value, tb):
    if hasattr(sys, 'ps1') or not sys.stderr.isatty():
       # we are in interactive mode or we don't have a tty-like
       # device, so we call the default hook
       sys.__excepthook__(type, value, tb)
    else:
       import traceback, pdb
       # we are NOT in interactive mode, print the exception...
       traceback.print_exception(type, value, tb)
       print
       # ...then start the debugger in post-mortem mode.
       pdb.pm()

def enable_debug_on_crash():
    "Start the PDB console when an uncaught exception propagates to the top."
    sys.excepthook = debugger_hook

# allow profile decorator to exist, but do nothing if not running under
# kernprof
try:
    profile_if_possible = profile
except NameError:
    profile_if_possible = lambda x: x

def timeit(func):
    "A decorator to print the time elapsed in a function call."
    def f(*args, **kwargs):
        t0 = time.time()
        retval = func(*args, **kwargs)
        elapsed = time.time() - t0
        print '%s elapsed in %s().' % (datetime.timedelta(seconds=elapsed), func.__name__)
        return retval
    return f

def read_csv(filename):
    """Return an array of comma-separated values from `filename`."""
    f = open(filename)

    points = []
    for line in f:
        try:
            points.append([float(s) for s in line.split(',')])
        except ValueError:
            pass

    f.close()

    return np.array(points)

def offset(points, x):
    """
    Return the set of points obtained by offsetting the edges of the profile
    created by `points` by an amount `x`.

    Args:
        - points: array
            Array of points which define the 2-D profile to be offset.
        - x: float
            Distance to offset the profile; a positive `x` value will offset
            the profile in the direction of the profile path rotated 90 degrees
            clockwise.
    """
    points = np.asarray(points)
    points = np.array([points[0] - (points[1] - points[0])] + list(points) + [points[-1] - (points[-2] - points[-1])])
    
    offset_points = []
    for i in range(1,len(points)-1):
        v1 = np.cross(points[i]-points[i-1], (0,0,1))[:2]
        v1 /= np.linalg.norm(v1)
        v1 *= x

        a = points[i-1] + v1
        b = points[i] + v1

        v2 = np.cross(points[i+1]-points[i], (0,0,1))[:2]
        v2 /= np.linalg.norm(v2)
        v2 *= x

        c = points[i] + v2
        d = points[i+1] + v2

        m = np.empty((2,2))
        m[:,0] = b-a
        m[:,1] = c-d

        try:
            j = np.linalg.solve(m, c-a)[0]
        except np.linalg.linalg.LinAlgError as e:
            offset_points.append(b)
            continue

        offset_points.append((a + j*(b-a)))

    return np.array(offset_points)