#
# Copyright (c) 2003, 2004, 2005, 2006 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
from PythonCAD.Generic.point import Point
from PythonCAD.Generic.segment import Segment
from PythonCAD.Generic.circle import Circle
from PythonCAD.Generic.arc import Arc
from PythonCAD.Generic.leader import Leader
from PythonCAD.Generic.polyline import Polyline
from PythonCAD.Generic.hcline import HCLine
from PythonCAD.Generic.vcline import VCLine
from PythonCAD.Generic.acline import ACLine
from PythonCAD.Generic.cline import CLine
from PythonCAD.Generic.ccircle import CCircle
def _test_point(layer, x, y):
_pts = layer.find('point', x, y)
if len(_pts) == 0:
_p = Point(x, y)
layer.addObject(_p)
else:
_p = _pts.pop()
_max = _p.countUsers()
for _pt in _pts:
_count = _pt.countUsers()
if _count > _max:
_max = _count
_p = _pt
return _p
def _horizontal_mirror(hy, objlist):
for _obj in objlist:
_layer = _obj.getParent()
if _layer is None:
continue
_mobj = None
if isinstance(_obj, Point):
_x, _y = _obj.getCoords()
_ym = _y + (2.0 * (hy - _y))
_mp = _test_point(_layer, _x, _ym)
elif isinstance(_obj, 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(_layer, _p1x, _y1m)
_y2m = _p2y + (2.0 * (hy - _p2y))
_mp2 = _test_point(_layer, _p2x, _y2m)
_mobj = _obj.clone()
_mobj.setP1(_mp1)
_mobj.setP2(_mp2)
elif isinstance(_obj, Arc):
_x, _y = _obj.getCenter().getCoords()
_ym = _y + (2.0 * (hy - _y))
_mp = _test_point(_layer, _x, _ym)
_mobj = _obj.clone()
_mobj.setCenter(_mp)
_mobj.setStartAngle(360.0 - _obj.getEndAngle())
_mobj.setEndAngle(360.0 - _obj.getStartAngle())
elif isinstance(_obj, (Circle, CCircle)):
_x, _y = _obj.getCenter().getCoords()
if abs(hy - _y) > 1e-10:
_ym = _y + (2.0 * (hy - _y))
_mp = _test_point(_layer, _x, _ym)
_mobj = _obj.clone()
_mobj.setCenter(_mp)
elif isinstance(_obj, HCLine):
_x, _y = _obj.getLocation().getCoords()
if abs(hy - _y) > 1e-10:
_ym = _y + (2.0 * (hy - _y))
_mp = _test_point(_layer, _x, _ym)
_mobj = _obj.clone()
_mobj.setLocation(_mp)
elif isinstance(_obj, VCLine):
pass
elif isinstance(_obj, 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(_layer, _x, _ym)
_mobj = _obj.clone()
if abs(_angle) > 1e-10: # not horizontal
_mobj.setAngle(-1.0 * _angle)
_mobj.setLocation(_mp)
elif isinstance(_obj, 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(_layer, _x1, _ym)
_ym = _y2 + (2.0 * (hy - _y2))
_mp2 = _test_point(_layer, _x2, _ym)
_mobj = CLine(_mp1, _mp2)
elif isinstance(_obj, 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(_layer, _p1x, _ym)
_ym = _p2y + (2.0 * (hy - _p2y))
_mp2 = _test_point(_layer, _p2x, _ym)
_ym = _p3y + (2.0 * (hy - _p3y))
_mp3 = _test_point(_layer, _p3x, _ym)
_mobj = _obj.clone()
_mobj.setP1(_mp1)
_mobj.setP2(_mp2)
_mobj.setP3(_mp3)
elif isinstance(_obj, Polyline):
_mobj = _obj.clone()
for _i in range(len(_mobj)):
_pt = _mobj.getPoint(_i)
_x, _y = _pt.getCoords()
_ym = _y + (2.0 * (hy - _y))
_mp = _test_point(_layer, _x, _ym)
_mobj.setPoint(_i, _mp)
else:
print "skipping obj: " + `_obj`
if _mobj is not None:
_layer.addObject(_mobj)
def _vertical_mirror(vx, objlist):
for _obj in objlist:
_layer = _obj.getParent()
if _layer is None:
continue
_mobj = None
if isinstance(_obj, Point):
_x, _y = _obj.getCoords()
_xm = _x + (2.0 * (vx - _x))
_mp = _test_point(_layer, _xm, _y)
elif isinstance(_obj, 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(_layer, _x1m, _p1y)
_x2m = _p2x + (2.0 * (vx - _p2x))
_mp2 = _test_point(_layer, _x2m, _p2y)
_mobj = _obj.clone()
_mobj.setP1(_mp1)
_mobj.setP2(_mp2)
elif isinstance(_obj, Arc):
_x, _y = _obj.getCenter().getCoords()
_xm = _x + (2.0 * (vx - _x))
_mp = _test_point(_layer, _xm, _y)
_mobj = _obj.clone()
_mobj.setCenter(_mp)
_mobj.setStartAngle(180.0 - _obj.getEndAngle())
_mobj.setEndAngle(180.0 - _obj.getStartAngle())
elif isinstance(_obj, (Circle, CCircle)):
_x, _y = _obj.getCenter().getCoords()
if abs(vx - _x) > 1e-10:
_xm = _x + (2.0 * (vx - _x))
_mp = _test_point(_layer, _xm, _y)
_mobj = _obj.clone()
_mobj.setCenter(_mp)
elif isinstance(_obj, HCLine):
pass
elif isinstance(_obj, VCLine):
_x, _y = _obj.getLocation().getCoords()
if abs(vx - _x) > 1e-10:
_xm = _x + (2.0 * (vx - _x))
_mp = _test_point(_layer, _xm, _y)
_mobj = _obj.clone()
_mobj.setLocation(_mp)
elif isinstance(_obj, 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(_layer, _xm, _y)
_mobj = _obj.clone()
if abs(abs(_angle) - 90.0) > 1e-10: # not vertical
_mobj.setAngle(-1.0 * _angle)
_mobj.setLocation(_mp)
elif isinstance(_obj, 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(_layer, _xm, _y1)
_xm = _x2 + (2.0 * (vx - _x2))
_mp2 = _test_point(_layer, _xm, _y2)
_mobj = CLine(_mp1, _mp2)
elif isinstance(_obj, 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(_layer, _xm, _p1y)
_xm = _p2x + (2.0 * (vx - _p2x))
_mp2 = _test_point(_layer, _xm, _p2y)
_xm = _p3x + (2.0 * (vx - _p3x))
_mp3 = _test_point(_layer, _xm, _p3y)
_mobj = _obj.clone()
_mobj.setP1(_mp1)
_mobj.setP2(_mp2)
_mobj.setP3(_mp3)
elif isinstance(_obj, Polyline):
_mobj = _obj.clone()
for _i in range(len(_mobj)):
_pt = _mobj.getPoint(_i)
_x, _y = _pt.getCoords()
_xm = _x + (2.0 * (vx - _x))
_mp = _test_point(_layer, _xm, _y)
_mobj.setPoint(_i, _mp)
else:
print "skipping obj: " + `_obj`
if _mobj is not None:
_layer.addObject(_mobj)
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(slope, yint, objlist):
_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
for _obj in objlist:
_layer = _obj.getParent()
if _layer is None:
continue
_mobj = None
if isinstance(_obj, Point):
_x, _y = _obj.getCoords()
_rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
_np = _test_point(_layer, _rx, _ry)
elif isinstance(_obj, Segment):
_p1, _p2 = _obj.getEndpoints()
_x, _y = _p1.getCoords()
_rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
_mp1 = _test_point(_layer, _rx, _ry)
_x, _y = _p2.getCoords()
_rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
_mp2 = _test_point(_layer, _rx, _ry)
_mobj = _obj.clone()
_mobj.setP1(_mp1)
_mobj.setP2(_mp2)
elif isinstance(_obj, Arc):
_x, _y = _obj.getCenter().getCoords()
_rcx, _rcy = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
_mp = _test_point(_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
_mobj = _obj.clone()
_mobj.setCenter(_mp)
_mobj.setStartAngle(_sa)
_mobj.setEndAngle(_ea)
elif isinstance(_obj, (Circle, CCircle)):
_x, _y = _obj.getCenter().getCoords()
_rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
_mp = _test_point(_layer, _rx, _ry)
_mobj = _obj.clone()
_mobj.setCenter(_mp)
elif isinstance(_obj, HCLine):
_x, _y = _obj.getLocation().getCoords()
_rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
_mp = _test_point(_layer, _rx, _ry)
if abs(abs(_angle) - 45.0) < 1e-10:
_mobj = 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
_mobj = ACLine(_mp, _mang)
elif isinstance(_obj, VCLine):
_x, _y = _obj.getLocation().getCoords()
_rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
_mp = _test_point(_layer, _rx, _ry)
if abs(abs(_angle) - 45.0) < 1e-10:
_mobj = 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
_mobj = ACLine(_mp, _mang)
elif isinstance(_obj, 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(_layer, _rx, _ry)
_acl_angle = _obj.getAngle()
if _at45 and abs(_acl_angle) < 1e-10:
_mobj = VCLine(_mp)
elif _at45 and abs(abs(_acl_angle) - 90.0) < 1e-10:
_mobj = 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
_mobj = ACLine(_mp, _mang)
elif isinstance(_obj, CLine):
_p1, _p2 = _obj.getKeypoints()
_x, _y = _p1.getCoords()
_rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
_mp1 = _test_point(_layer, _rx, _ry)
_x, _y = _p2.getCoords()
_rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
_mp2 = _test_point(_layer, _rx, _ry)
_mobj = CLine(_mp1, _mp2)
elif isinstance(_obj, Leader):
_p1, _p2, _p3 = _obj.getPoints()
_x, _y = _p1.getCoords()
_rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
_mp1 = _test_point(_layer, _rx, _ry)
_x, _y = _p2.getCoords()
_rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
_mp2 = _test_point(_layer, _rx, _ry)
_x, _y = _p3.getCoords()
_rx, _ry = _reflect_point(_t1, _t2, _t3, yint, _x, _y)
_mp3 = _test_point(_layer, _rx, _ry)
_mobj = _obj.clone()
_mobj.setP1(_mp1)
_mobj.setP2(_mp2)
_mobj.setP3(_mp3)
elif isinstance(_obj, 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(_layer, _rx, _ry)
_mpts.append(_mp)
_s = _obj.getStyle()
_mobj = Polyline(_mpts, _s)
_l = _obj.getLinetype()
if _l != _s.getLinetype():
_mobj.setLinetype(_l)
_c = _obj.getColor()
if _c != _s.getColor():
_mobj.setColor(_c)
_t = _obj.getThickness()
if abs(_t - _s.getThickness()) > 1e-10:
_mobj.setThickness(_t)
else:
print "Skipping object: " + `_obj`
if _mobj is not None:
_layer.addObject(_mobj)
def mirror_objects(mline, objlist):
if not isinstance(mline, (HCLine, VCLine, ACLine, CLine)):
raise TypeError, "Invalid mirror line type: " + `type(mline)`
if not isinstance(objlist, (list, tuple)):
raise TypeError, "Invalid object list: " + `type(objlist)`
if isinstance(mline, HCLine):
_x, _y = mline.getLocation().getCoords()
_horizontal_mirror(_y, objlist)
elif isinstance(mline, VCLine):
_x, _y = mline.getLocation().getCoords()
_vertical_mirror(_x, objlist)
elif isinstance(mline, ACLine):
_x, _y = mline.getLocation().getCoords()
_angle = mline.getAngle()
if abs(_angle) < 1e-10: # horizontal:
_horizontal_mirror(_y, objlist)
elif abs(abs(_angle) - 90.0) < 1e-10: # vertical
_vertical_mirror(_x, objlist)
else:
_slope = math.tan(_angle * (math.pi/180.0))
_yint = _y - (_slope * _x)
_angled_mirror(_slope, _yint, objlist)
elif isinstance(mline, CLine):
_p1, _p2 = mline.getKeypoints()
_p1x, _p1y = _p1.getCoords()
_p2x, _p2y = _p2.getCoords()
if abs(_p2y - _p1y) < 1e-10: # horizontal
_horizontal_mirror(_p1y, objlist)
elif abs(_p2x - _p1x) < 1e-10: # vertical
_vertical_mirror(_p1x, objlist)
else:
_slope = (_p2y - _p1y)/(_p2x - _p1x)
_yint = _p1y - (_slope * _p1x)
_angled_mirror(_slope, _yint, objlist)
else:
raise TypeError, "Unexpected mirror line type: " + `type(mline)`