#
# Copyright (c) 2003, 2004 Art Haas
#
# This file is part of PythonCAD.
# 
# PythonCAD is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# 
# PythonCAD 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 General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with PythonCAD; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# mirror operations
#

import math

import Generic.point
import Generic.segment
import Generic.circle
import Generic.arc
import Generic.leader
import Generic.polyline
import Generic.hcline
import Generic.vcline
import Generic.acline
import Generic.cline

def _test_point(layer, x, y):
    _p = layer.find('point', x, y)
    if _p is None:
        _p = Generic.point.Point(x, y)
        layer.addObject(_p)
    return _p

def _horizontal_mirror(image, hy, objlist):
    _active_layer = image.getActiveLayer()
    _mobjs = []
    for _obj in objlist:
        if isinstance(_obj, Generic.point.Point):
            _x, _y = _obj.getCoords()
            _ym = _y + (2.0 * (hy - _y))
            _mp = _test_point(_active_layer, _x, _ym)
            _mobjs.append(_mp)
        elif isinstance(_obj, Generic.segment.Segment):
            _p1, _p2 = _obj.getEndpoints()
            _p1x, _p1y = _p1.getCoords()
            _p2x, _p2y = _p2.getCoords()            
            if (abs(hy - _p1y) > 1e-10 or
                abs(hy - _p2y) > 1e-10):
                _y1m = _p1y + (2.0 * (hy - _p1y))
                _mp1 = _test_point(_active_layer, _p1x, _y1m)
                _y2m = _p2y + (2.0 * (hy - _p2y))
                _mp2 = _test_point(_active_layer, _p2x, _y2m)
                _ms = _obj.clone()
                _ms.setP1(_mp1)
                _ms.setP2(_mp2)
                _ms.reset()
                _mobjs.append(_ms)
        elif isinstance(_obj, Generic.arc.Arc):
            _x, _y = _obj.getCenter().getCoords()
            _ym = _y + (2.0 * (hy - _y))
            _mcp = _test_point(_active_layer, _x, _ym)
            _ma = _obj.clone()
            _ma.setCenter(_mcp)
            _ma.setStartAngle(360.0 - _obj.getEndAngle())
            _ma.setEndAngle(360.0 - _obj.getStartAngle())
            _ma.reset()
            _mobjs.append(_ma)
        elif isinstance(_obj, Generic.circle.Circle):
            _x, _y = _obj.getCenter().getCoords()
            if abs(hy - _y) > 1e-10:
                _ym = _y + (2.0 * (hy - _y))
                _mcp = _test_point(_active_layer, _x, _ym)
                _mc = _obj.clone()
                _mc.setCenter(_mcp)
                _mc.reset()
                _mobjs.append(_mc)
        elif isinstance(_obj, Generic.hcline.HCLine):
            _x, _y = _obj.getLocation().getCoords()
            if abs(hy - _y) > 1e-10:
                _ym = _y + (2.0 * (hy - _y))
                _mp = _test_point(_active_layer, _x, _ym)
                _mh = _obj.clone()
                _mh.setLocation(_mp)
                _mh.reset()
                _mobjs.append(_mh)
        elif isinstance(_obj, Generic.vcline.VCLine):
            pass
        elif isinstance(_obj, Generic.acline.ACLine):
            _angle = _obj.getAngle()
            if abs(abs(_angle) - 90.0) > 1e-10: # not vertical
                _x, _y = _obj.getLocation().getCoords()
                _ym = _y + (2.0 * (hy - _y))
                _mp = _test_point(_active_layer, _x, _ym)
                _ma = _obj.clone()
                if abs(_angle) > 1e-10: # not horizontal
                    _ma.setAngle(-1.0 * _angle)
                _ma.setLocation(_mp)
                _ma.reset()
                _mobjs.append(_ma)
        elif isinstance(_obj, Generic.cline.CLine):
            _p1, _p2 = _obj.getKeypoints()
            _x1, _y1 = _p1.getCoords()
            _x2, _y2 = _p2.getCoords()
            if abs(_x2 - _x1) > 1e-10: # not vertical
                _ym = _y1 + (2.0 * (hy - _y1))
                _mp1 = _test_point(_active_layer, _x1, _ym)
                _ym = _y2 + (2.0 * (hy - _y2))
                _mp2 = _test_point(_active_layer, _x2, _ym)
                _mc = Generic.cline.CLine(_mp1, _mp2)
                _mobjs.append(_mc)
        elif isinstance(_obj, Generic.leader.Leader):
            _p1, _p2, _p3 = _obj.getPoints()
            _p1x, _p1y = _p1.getCoords()
            _p2x, _p2y = _p2.getCoords()
            _p3x, _p3y = _p3.getCoords()
            if (abs(_p1y - hy) > 1e-10 or
                abs(_p2y - hy) > 1e-10 or
                abs(_p3y - hy) > 1e-10):
                _ym = _p1y + (2.0 * (hy - _p1y))
                _mp1 = _test_point(_active_layer, _p1x, _ym)
                _ym = _p2y + (2.0 * (hy - _p2y))
                _mp2 = _test_point(_active_layer, _p2x, _ym)
                _ym = _p3y + (2.0 * (hy - _p3y))
                _mp3 = _test_point(_active_layer, _p3x, _ym)
                _ml = _obj.clone()
                _ml.setP1(_mp1)
                _ml.setP2(_mp2)
                _ml.setP3(_mp3)
                _mobjs.append(_ml)
        elif isinstance(_obj, Generic.polyline.Polyline):
            _mpl = _obj.clone()
            for _i in range(len(_mpl)):
                _pt = _mpl.getPoint(_i)
                _x, _y = _pt.getCoords()
                _ym = _y + (2.0 * (hy - _y))
                _mp = _test_point(_active_layer, _x, _ym)
                _mpl.setPoint(_i, _mp)
            _mobjs.append(_mpl)
        else:
            print "skipping obj: " + `_obj`
    for _mobj in _mobjs:
        if not isinstance(_mobj, Generic.point.Point):
            _active_layer.addObject(_mobj)
    return _mobjs

def _vertical_mirror(image, vx, objlist):
    _active_layer = image.getActiveLayer()
    _mobjs = []
    for _obj in objlist:
        if isinstance(_obj, Generic.point.Point):
            _x, _y = _obj.getCoords()
            _xm = _x + (2.0 * (vx - _x))
            _mp = _test_point(_active_layer, _xm, _y)
            _mobjs.append(_mp)
        elif isinstance(_obj, Generic.segment.Segment):
            _p1, _p2 = _obj.getEndpoints()
            _p1x, _p1y = _p1.getCoords()
            _p2x, _p2y = _p2.getCoords()            
            if (abs(vx - _p1x) > 1e-10 or
                abs(vx - _p2x) > 1e-10):
                _x1m = _p1x + (2.0 * (vx - _p1x))
                _mp1 = _test_point(_active_layer, _x1m, _p1y)
                _x2m = _p2x + (2.0 * (vx - _p2x))
                _mp2 = _test_point(_active_layer, _x2m, _p2y)
                _ms = _obj.clone()
                _ms.setP1(_mp1)
                _ms.setP2(_mp2)
                _mobjs.append(_ms)
        elif isinstance(_obj, Generic.arc.Arc):
            _x, _y = _obj.getCenter().getCoords()
            _xm = _x + (2.0 * (vx - _x))
            _mcp = _test_point(_active_layer, _xm, _y)
            _ma = _obj.clone()
            _ma.setCenter(_mcp)
            _ma.setStartAngle(180.0 - _obj.getEndAngle())
            _ma.setEndAngle(180.0 - _obj.getStartAngle())
            _mobjs.append(_ma)
        elif isinstance(_obj, Generic.circle.Circle):
            _x, _y = _obj.getCenter().getCoords()
            if abs(vx - _x) > 1e-10:
                _xm = _x + (2.0 * (vx - _x))
                _mcp = _test_point(_active_layer, _xm, _y)
                _mc = _obj.clone()
                _mc.setCenter(_mcp)
                _mobjs.append(_mc)
        elif isinstance(_obj, Generic.hcline.HCLine):
            pass
        elif isinstance(_obj, Generic.vcline.VCLine):
            _x, _y = _obj.getLocation().getCoords()
            if abs(vx - _x) > 1e-10:
                _xm = _x + (2.0 * (vx - _x))
                _mp = _test_point(_active_layer, _xm, _y)
                _mv = _obj.clone()
                _mv.setLocation(_mp)
                _mobjs.append(_mv)
        elif isinstance(_obj, Generic.acline.ACLine):
            _angle = _obj.getAngle()
            if abs(_angle) > 1e-10: # not horizontal
                _x, _y = _obj.getLocation().getCoords()
                _xm = _x + (2.0 * (vx - _x))
                _mp = _test_point(_active_layer, _xm, _y)
                _ma = _obj.clone()
                if abs(abs(_angle) - 90.0) > 1e-10: # not vertical
                    _ma.setAngle(-1.0 * _angle)
                _ma.setLocation(_mp)
                _mobjs.append(_ma)
        elif isinstance(_obj, Generic.cline.CLine):
            _p1, _p2 = _obj.getKeypoints()
            _x1, _y1 = _p1.getCoords()
            _x2, _y2 = _p2.getCoords()
            if abs(_y2 - _y1) > 1e-10: # not horizontal
                _xm = _x1 + (2.0 * (vx - _x1))
                _mp1 = _test_point(_active_layer, _xm, _y1)
                _xm = _x2 + (2.0 * (vx - _x2))
                _mp2 = _test_point(_active_layer, _xm, _y2)
                _mc = Generic.cline.CLine(_mp1, _mp2)
                _mobjs.append(_mc)
        elif isinstance(_obj, Generic.leader.Leader):
            _p1, _p2, _p3 = _obj.getPoints()
            _p1x, _p1y = _p1.getCoords()
            _p2x, _p2y = _p2.getCoords()
            _p3x, _p3y = _p3.getCoords()
            if (abs(_p1x - vx) > 1e-10 or
                abs(_p2x - vx) > 1e-10 or
                abs(_p3x - vx) > 1e-10):
                _xm = _p1x + (2.0 * (vx - _p1x))
                _mp1 = _test_point(_active_layer, _xm, _p1y)
                _xm = _p2x + (2.0 * (vx - _p2x))
                _mp2 = _test_point(_active_layer, _xm, _p2y)
                _xm = _p3x + (2.0 * (vx - _p3x))
                _mp3 = _test_point(_active_layer, _xm, _p3y)
                _ml = _obj.clone()
                _ml.setP1(_mp1)
                _ml.setP2(_mp2)
                _ml.setP3(_mp3)
                _mobjs.append(_ml)
        elif isinstance(_obj, Generic.polyline.Polyline):
            _mpl = _obj.clone()
            for _i in range(len(_mpl)):
                _pt = _mpl.getPoint(_i)
                _x, _y = _pt.getCoords()
                _xm = _x + (2.0 * (vx - _x))
                _mp = _test_point(_active_layer, _xm, _y)
                _mpl.setPoint(_i, _mp)
            _mobjs.append(_mpl)
        else:
            print "skipping obj: " + `_obj`
    for _mobj in _mobjs:
        if not isinstance(_mobj, Generic.point.Point):
            _active_layer.addObject(_mobj)
    return _mobjs

def _reflect_point(t1, t2, t3, yint, x, y):
    #
    # reflect point in coordinate frame with
    # origin at mirror line y-intersection, then
    # convert reflected point back to global
    # coordinates
    #
    _nx = (t1 * x) + (t3 * (y - yint))
    _ny = (t3 * x) + (t2 * (y - yint)) + yint
    return _nx, _ny

def _angled_mirror(image, slope, yint, objlist):
    _active_layer = image.getActiveLayer()
    _angle = math.atan(slope)
    #
    # linear algebra reflection matrix coefficients
    #
    if abs(abs(_angle) - 45.0) < 1e-10:
        _t1 = 0.0
        _t2 = 0.0
        _t3 = 1.0
    else:
        _cosine = math.cos(_angle)
        _sine = math.sin(_angle)
        _t1 = (2.0 * _cosine * _cosine) - 1.0
        _t2 = (2.0 * _sine * _sine) - 1.0
        _t3 = 2.0 * _cosine * _sine
    _mobjs = []
    for _obj in objlist:
        if isinstance(_obj, Generic.point.Point):
            _x, _y = _obj.getCoords()
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _np = _test_point(_active_layer, _rx, _ry)
            _mobjs.append(_np)
        elif isinstance(_obj, Generic.segment.Segment):
            _p1, _p2 = _obj.getEndpoints()
            _x, _y = _p1.getCoords()
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _np1 = _test_point(_active_layer, _rx, _ry)
            _x, _y = _p2.getCoords()
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _np2 = _test_point(_active_layer, _rx, _ry)
            _ns = _obj.clone()
            _ns.setP1(_np1)
            _ns.setP2(_np2)
            _mobjs.append(_ns)
        elif isinstance(_obj, Generic.arc.Arc):
            _x, _y = _obj.getCenter().getCoords()
            _rcx, _rcy = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _mcp = _test_point(_active_layer, _rcx, _rcy)
            _ep1, _ep2 = _obj.getEndpoints()
            _x, _y = _ep1
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _rtd = 180.0/math.pi
            _ea = _rtd * math.atan2((_ry - _rcy), (_rx - _rcx))
            if _ea < 0.0:
                _ea = _ea + 360.0
            _x, _y = _ep2
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _sa = _rtd * math.atan2((_ry - _rcy), (_rx - _rcx))
            if _sa < 0.0:
                _sa = _sa + 360.0
            _ma = _obj.clone()
            _ma.setCenter(_mcp)
            _ma.setStartAngle(_sa)
            _ma.setEndAngle(_ea)
            _mobjs.append(_ma)
        elif isinstance(_obj, Generic.circle.Circle):
            _x, _y = _obj.getCenter().getCoords()
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _ncp = _test_point(_active_layer, _rx, _ry)
            _nc = _obj.clone()
            _nc.setCenter(_ncp)
            _mobjs.append(_nc)
        elif isinstance(_obj, Generic.hcline.HCLine):
            _x, _y = _obj.getLocation().getCoords()
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _mp = _test_point(_active_layer, _rx, _ry)
            if abs(abs(_angle) - 45.0) < 1e-10:
                _mh = Generic.vcline.VCLine(_mp)
            else:
                _xi = (_y - yint)/slope
                _mang = 180.0/math.pi * math.atan2((_ry - _y), (_rx - _xi))
                if _mang < 0.0:
                    _mang = _mang + 360.0
                _mh = Generic.acline.ACLine(_mp, _mang)
            _mobjs.append(_mh)
        elif isinstance(_obj, Generic.vcline.VCLine):
            _x, _y = _obj.getLocation().getCoords()
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _mp = _test_point(_active_layer, _rx, _ry)
            if abs(abs(_angle) - 45.0) < 1e-10:
                _mv = Generic.hcline.HCLine(_mp)
            else:
                _yi = (slope * _x) + yint 
                _mang = 180.0/math.pi * math.atan2((_ry - _yi), (_rx - _x))
                if _mang < 0.0:
                    _mang = _mang + 360.0
                _mv = Generic.acline.ACLine(_mp, _mang)
            _mobjs.append(_mv)
        elif isinstance(_obj, Generic.acline.ACLine):
            _at45 = False
            if abs(abs(_angle) - 45.0) < 1e-10:
                _at45 = True
            _x, _y = _obj.getLocation().getCoords()
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _mp = _test_point(_active_layer, _rx, _ry)
            _acl_angle = _obj.getAngle()
            if _at45 and abs(_acl_angle) < 1e-10:
                _ma = Generic.vcline.VCLine(_mp)
            elif _at45 and abs(abs(_acl_angle) - 90.0) < 1e-10:
                _ma = Generic.hcline.HCLine(_mp)
            else:
                if abs(_acl_angle) < 1e-10: # horizontal
                    _xi = (_y - _yint)/slope
                    _yi = _y
                elif abs(abs(_acl_angle) - 90.0) < 1e-10: # vertical
                    _xi = _x
                    _yi = (slope * _x) + _yint
                else:
                    _asl = math.tan((math.pi/180.0) * _acl_angle)
                    if abs(_asl - slope) < 1e-10: # parallel
                        _mang = _acl_angle
                    else:
                        _ayi = _y - (_asl * _x)                        
                        _xi = (_ayi - yint)/(slope - _asl)
                        _yi = (slope * _xi) + yint
                        _mang = 180.0/math.pi * math.atan2((_ry - _yi), (_rx - _xi))
                        if _mang < 0.0:
                            _mang = _mang + 360.0
                _ma = Generic.acline.ACLine(_mp, _mang)
            _mobjs.append(_ma)
        elif isinstance(_obj, Generic.cline.CLine):
            _p1, _p2 = _obj.getKeypoints()
            _x, _y = _p1.getCoords()
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _mp1 = _test_point(_active_layer, _rx, _ry)
            _x, _y = _p2.getCoords()
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _mp2 = _test_point(_active_layer, _rx, _ry)
            _nc = Generic.cline.CLine(_mp1, _mp2)
            _mobjs.append(_nc)
        elif isinstance(_obj, Generic.leader.Leader):
            _p1, _p2, _p3 = _obj.getPoints()
            _x, _y = _p1.getCoords()
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _mp1 = _test_point(_active_layer, _rx, _ry)
            _x, _y = _p2.getCoords()
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _mp2 = _test_point(_active_layer, _rx, _ry)
            _x, _y = _p3.getCoords()
            _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
            _mp3 = _test_point(_active_layer, _rx, _ry)
            _ml = _obj.clone()
            _ml.setP1(_mp1)
            _ml.setP2(_mp2)
            _ml.setP3(_mp3)
            _mobjs.append(_ml)
        elif isinstance(_obj, Generic.polyline.Polyline):
            _mpts = []
            for _i in range(len(_obj)):
                _x, _y = _obj.getPoint(_i).getCoords()
                _rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
                _mp = _test_point(_active_layer, _rx, _ry)
                _mpts.append(_mp)
            _s = _obj.getStyle()
            _l = _obj.getLinetype()
            _c = _obj.getColor()
            _t = _obj.getThickness()
            _mpl = Generic.polyline.Polyline(_mpts, _s, _l, _c, _t)
            _mobjs.append(_mpl)
        else:
            print "Skipping object: " + `_obj`
    for _mobj in _mobjs:
        if not isinstance(_mobj, Generic.point.Point):
            _active_layer.addObject(_mobj)
    return _mobjs

def mirror_objects(image, mline, objlist):
    _mobjs = []
    if isinstance(mline, Generic.hcline.HCLine):
        _x, _y = mline.getLocation().getCoords()
        _mobjs.extend(_horizontal_mirror(image, _y, objlist))
    elif isinstance(mline, Generic.vcline.VCLine):
        _x, _y = mline.getLocation().getCoords()
        _mobjs.extend(_vertical_mirror(image, _x, objlist))
    elif isinstance(mline, Generic.acline.ACLine):
        _x, _y = mline.getLocation().getCoords()
        _angle = mline.getAngle()
        if abs(_angle) < 1e-10: # horizontal:
            _mobjs.extend(_horizontal_mirror(image, _y, objlist))
        elif abs(abs(_angle) - 90.0) < 1e-10: # vertical
            _mobjs.extend(_vertical_mirror(image, _x, objlist))
        else:
            _slope = math.tan(_angle * (math.pi/180.0))
            _yint = _y - (_slope * _x)
            _mobjs.extend(_angled_mirror(image, _slope, _yint, objlist))
    elif isinstance(mline, Generic.cline.CLine):
        _p1, _p2 = mline.getKeypoints()
        _p1x, _p1y = _p1.getCoords()
        _p2x, _p2y = _p2.getCoords()
        if abs(_p2y - _p1y) < 1e-10: # horizontal
            _mobjs.extend(_horizontal_mirror(image, _p1y, objlist))
        elif abs(_p2x - _p1x) < 1e-10: # vertical
            _mobjs.extend(_vertical_mirror(image, _p1x, objlist))
        else:
            _slope = (_p2y - _p1y)/(_p2x - _p1x)
            _yint = _p1y - (_slope * _p1x)
            _mobjs.extend(_angled_mirror(image, _slope, _yint, objlist))
    else:
        raise TypeError, "Unexpected mirror line type: " + `mline`
    return _mobjs
    
