summaryrefslogtreecommitdiff
path: root/src/python/mathutil.py
blob: b27758e0aa250a4c38119cfd7167b61e3a3091d5 (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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# libavg - Media Playback Engine.
# Copyright (C) 2003-2014 Ulrich von Zadow
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Current versions can be found at www.libavg.de
#
# Original author of this file is Martin Heistermann <mh at sponc dot de>
#

# TODO: Some of this stuff is duplicated - either in Point2D or in MathHelper.h/.cpp.
# Clean that up.

import math
from libavg import Point2D

def getAngle(p1, p2):
    vec = p2 - p1
    res = math.atan2(vec.y, vec.x)
    if res < 0:
        res += math.pi * 2
    return res

def getDistance (p, q):
    return math.sqrt((p.x-q.x)**2 + (p.y-q.y)**2)

def getDistSquared (p, q):
    return (p.x-q.x)**2 + (p.y-q.y)**2

def getScaleToSize ((width, height), (max_width, max_height)):
    if width < max_width:
        height = height * (float(max_width) / width)
        width = max_width
    elif height > max_height:
        width = width * (float(max_height) / height)
        height = max_height
    return getScaledDim((width, height), (max_width, max_height))

def getScaledDim (size, max = None, min = None):
    width, height = size
    if width == 0 or height == 0:
        return size

    if max:
        max = Point2D(max)
        assert (max.x > 0 and max.y > 0)
        if width > max.x:
            height = height * (max.x / width)
            width = max.x
        if height > max.y:
            width = width * (max.y / height)
            height = max.y

    if min:
        min = Point2D(min)
        assert (min.x > 0 and min.y > 0)
        if width < min.x:
            height = height * (min.x / width)
            width = min.x
        if height < min.y:
            width = width * (min.y / height)
            height = min.y

    return Point2D(width, height)


class EquationNotSolvable (Exception):
    pass
class EquationSingular (Exception):
    pass

def gauss_jordan(m, eps = 1.0/(10**10)):
    """Puts given matrix (2D array) into the Reduced Row Echelon Form.
         Returns True if successful, False if 'm' is singular.
         NOTE: make sure all the matrix items support fractions! Int matrix will NOT work!
         Written by Jarno Elonen in April 2005, released into Public Domain
         http://elonen.iki.fi/code/misc-notes/affine-fit/index.html"""
    (h, w) = (len(m), len(m[0]))
    for y in range(0,h):
        maxrow = y
        for y2 in range(y+1, h):        # Find max pivot
            if abs(m[y2][y]) > abs(m[maxrow][y]):
                maxrow = y2
        (m[y], m[maxrow]) = (m[maxrow], m[y])
        if abs(m[y][y]) <= eps:         # Singular?
            raise EquationSingular
        for y2 in range(y+1, h):        # Eliminate column y
            c = m[y2][y] / m[y][y]
            for x in range(y, w):
                m[y2][x] -= m[y][x] * c
    for y in range(h-1, 0-1, -1): # Backsubstitute
        c    = m[y][y]
        for y2 in range(0,y):
            for x in range(w-1, y-1, -1):
                m[y2][x] -=    m[y][x] * m[y2][y] / c
        m[y][y] /= c
        for x in range(h, w):             # Normalize row y
            m[y][x] /= c
    return m


def solveEquationMatrix(_matrix, eps = 1.0/(10**10)):
    matrix=[]
    for coefficients, res in _matrix:
        newrow = map(float, coefficients + (res,))
        matrix.append(newrow)
    matrix = gauss_jordan (matrix)
    res=[]
    for col in xrange(len(matrix[0])-1):
        rows = filter(lambda row: row[col] >= eps, matrix)
        if len(rows)!=1:
            raise EquationNotSolvable
        res.append (rows[0][-1])

    return res


def getOffsetForMovedPivot(oldPivot, newPivot, angle):
    oldPos = Point2D(0,0).getRotated(angle, oldPivot)
    newPos = Point2D(0,0).getRotated(angle, newPivot)
    return oldPos - newPos

def isNaN(x):
    return (not(x<=0) and not(x>=0))

def sgn (x):
    if x<0:
        return -1
    elif x==0:
        return 0
    else:
        return 1

class MovingAverage:
    """
    Moving average implementation.
    Example:
    ma = MovingAverage(20)
    print ma(2)
    print ma(3)
    print ma(10)
    """
    def __init__(self, points):
        self.__points = points
        self.__values = []

    def __appendValue(self, value):
        self.__values = (self.__values + [value])[-self.__points:]

    def __getAverage(self):
        sum = reduce(lambda a,b:a+b, self.__values)
        return float(sum) / len(self.__values)

    def __call__(self, value):
        self.__appendValue(value)
        return self.__getAverage()