# -*- coding: utf-8 -*-

__author__ = "Zasimov Alexey"
__email__ = "zasimov-a@yandex-team.ru"

"""
В машинной графике есть несколько видов координат:
    - мировые - относятся к проблемной области;
    - экранные - координаты пикселей;
    - нормальные - экранные координаты от 0 до 1;
    - оконные - видимая часть мировых координат;
    - порт - регион на экране, где рисуется окно.
    
Порт описывается экранными координатами.

Задача - указываем координаты в мировых координатах.
А они уже отображаются на экранные координаты.

Отображение оконных координат в экранные выполняется так:
    - приведение к нулю (origin);
    - S-преобразование;
    - T-преобразование.
    
Этот модуль умеет преобразовывать координаты из мировых в экранные.

Важные символы: window, viewport, WVMapper

WVMapper - это контекст, знающей об окне и области вывода. Этот объект
также умеет реализовывать операцию отсечения.

"""

from plotter.share.math.epsilon import epsilon
from math import fabs


# Константы для удобства работы с координатами, которые представлены обычными
# кортежами.
# Для моих модулей это общепринято. 
# Такие же константы есть в math.xy.
X = 0
Y = 1

MIN = 0
MAX = 1


# Две вспомогательные функции, чтобы не тянуть модуль vector.

def add_vector(v, s):
    return v[X] + s[X], v[Y] + s[Y]


def sub_vector(v, s):
    return v[X] - s[X], v[Y] - s[Y]


def viewport(minv, maxv):
    """
    Создает порт.
    """
    assert abs(minv[X] - maxv[X]) > epsilon
    assert abs(minv[Y] - maxv[Y]) > epsilon
    return minv, maxv


def iviewport(minv, maxv):
    """
    Создает инвертированный порт.

    Порт инвертируется для того, чтобы 0 находился в нижнем левом углу.
    """
    assert abs(minv[X] - maxv[X]) > epsilon
    assert abs(minv[Y] - maxv[Y]) > epsilon
    return (minv[X], maxv[Y]), (maxv[X], minv[Y])


def window(minw, maxw):
    """
    Создает окно.
    """
    # FIXME: возможно нужно убрать это отсюда =)
    assert fabs(minw[X] - maxw[X]) > epsilon
    assert fabs(minw[Y] - maxw[Y]) > epsilon
    return minw, maxw


def origin(w, p):
    return sub_vector(p, w[0])


def K(w, v):
    return (v[MAX][X] - v[MIN][X]) / (w[MAX][X] - w[MIN][X]), (v[MAX][Y] - v[MIN][Y]) / (w[MAX][Y] - w[MIN][Y])


def S(p, k):
    return p[X] * k[X], p[Y] * k[Y]


def T(p, v):
    return add_vector(p, v[MIN])


def scale(p, w, k):
    o = origin(w, p)
    s = S(o, k)
    return s


def wv_mapping(p, w, v, k):
    s = scale(p, w, k)
    return T(s, v)


class WVMapper:
    """
    Отображатель оконных координат в экранные.
    """

    def __init__(self, w, v):
        self.w = w
        self.v = v
        self.k = K(w, v)

    def scale(self, x, y):
        return scale((x, y), self.w, self.k)

    def __call__(self, x, y):
        return wv_mapping((x, y), self.w, self.v, self.k)


if __name__ == "__main__":
    w = window((10, 10), (20, 20))
    v = viewport((0, 0), (100, 100))
    mapper = WVMapper(w, v)

    p1 = (11, 11)
    p2 = (12, 13)

    print(mapper(*p1))
    print(mapper(*p2))
