# # Copyright (c) 2002, 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 # # # miscellaneous utility functions # from math import fmod, pi import types from PythonCAD.Generic import tolerance def get_float(val): _v = val if not isinstance(_v, float): if not isinstance(_v, (int, long)): raise TypeError, "Invalid non-numeric type: " + `type(_v)` _v = float(val) return _v def test_boolean(val): if hasattr(types, 'BooleanType'): if not isinstance(val, types.BooleanType): raise TypeError, "Invalid non-boolean type: " + `type(val)` else: if val is not True and val is not False: raise TypeError, "Invalid non-boolean type: " + `type(val)` def tuple_to_two_floats(t): if not isinstance(t, tuple): raise TypeError, "Argument must be a tuple: " + `type(t)` if len(t) != 2: raise ValueError, "Tuple must hold exactly two objects: " + str(t) _obj1, _obj2 = t _x = get_float(_obj1) _y = get_float(_obj2) return _x, _y def tuple_to_three_floats(t): if not isinstance(t, tuple): raise TypeError, "Argument must be a tuple: " + `type(t)` if len(t) != 3: raise ValueError, "Tuple must hold exactly three objects: " + str(t) _obj1, _obj2, _obj3 = t _x = get_float(_obj1) _y = get_float(_obj2) _z = get_float(_obj3) return _x, _y, _z def make_angle(angle): """Return an angle value such that -90 <= angle <= 90. make_angle(angle) The argument angle should be a float. Additionally the argument is expected to be in degrees, not radians. """ _angle = get_float(angle) if _angle < -90.0 or _angle > 90.0: _fa = fmod(_angle, 360.0) if abs(_fa) < 1e-10: _angle = 0.0 elif _fa > 0.0: if _fa > 270.0: _angle = _fa - 360.0 elif _fa > 90.0: _angle = _fa - 180.0 else: _angle = _fa else: if _fa < -270.0: _angle = _fa + 360.0 elif _fa < -90.0: _angle = _fa + 180.0 else: _angle = _fa return _angle def make_c_angle(angle): """Return an angle value such that 0 <= angle <= 360. make_c_angle(angle) The argument angle should be a float. """ _a = get_float(angle) if _a < 0.0: _a = fmod(_a, 360.0) + 360.0 elif _a > 360.0: _a = fmod(_a, 360.0) return _a def make_coords(x, y): """Check and convert x/y values to float values. make_coords(x, y) This routine is used to ensure the values are float values. """ _x = get_float(x) _y = get_float(y) return _x, _y def make_region(xmin, ymin, xmax, ymax): """Return a validated region defined by (xmin, ymin) to (xmax, ymax). make_region(xmin, ymin, xmax, ymax) This routine is used to ensure the values are floats and that xmin < xmax and ymin < ymax. """ _xmin = get_float(xmin) _ymin = get_float(ymin) _xmax = get_float(xmax) if _xmax < _xmin: raise ValueError, "Invalid values: xmax < xmin" _ymax = get_float(ymax) if _ymax < _ymin: raise ValueError, "Invalid values: ymax < ymin" return _xmin, _ymin, _xmax, _ymax def degrees(value): """Convert a value from radians to degrees. degrees(value) In Python 2.3 this is available as the math.degrees() function, but the value isn't scaled from -360.0 <= angle <= 360.0 """ _value = get_float(value) return fmod(_value, 360.0) def radians(value): """Convert a value from degrees to radians. radians(value) In Python 2.3 this is available ad the math.radians() function, but the value isn't scaled from -2*pi <= angle <= 2*pi """ _value = get_float(value) return fmod(_value, (2.0 * pi)) # # map x/y coordinates to a (x1, y1)->(x2, y2) segment # def map_coords(x, y, x1, y1, x2, y2, tol=tolerance.TOL): """ map_coords(x, y, x1, y1, x2, y2[, tol]) """ _x = get_float(x) _y = get_float(y) _x1 = get_float(x1) _y1 = get_float(y1) _x2 = get_float(x2) _y2 = get_float(y2) _t = tolerance.toltest(tol) if ((_x < min(_x1, _x2) - _t) or (_y < min(_y1, _y2) - _t) or (_x > max(_x1, _x2) + _t) or (_y > max(_y1, _y2) + _t)): return None _sqlen = pow((_x2 - _x1), 2) + pow((_y2 - _y1), 2) if _sqlen < 1e-10: # coincident points return None _r = ((_x - _x1)*(_x2 - _x1) + (_y - _y1)*(_y2 - _y1))/_sqlen if _r < 0.0: _r = 0.0 if _r > 1.0: _r = 1.0 _px = _x1 + _r * (_x2 - _x1) _py = _y1 + _r * (_y2 - _y1) if abs(_px - _x) < _t and abs(_py - _y) < _t: return _px, _py return None # # test if line segments are visible within a rectangular region # def in_region(x1, y1, x2, y2, xmin, ymin, xmax, ymax): """Test if a segment from (x1, y1)->(x2, y2) is in region. in_region(x1, y1, x2, y2, xmin, ymin, xmax, ymax) """ _x1 = get_float(x1) _y1 = get_float(y1) _x2 = get_float(x2) _y2 = get_float(y2) _xmin = get_float(xmin) _ymin = get_float(ymin) _xmax = get_float(xmax) if _xmax < _xmin: raise ValueError, "Illegal values: xmax < xmin" _ymax = get_float(ymax) if _ymax < _ymin: raise ValueError, "Illegal values: ymax < ymin" if not ((_x1 < _xmin) or (_x1 > _xmax) or (_y1 < _ymin) or (_y1 > _ymax)): return True if not ((_x2 < _xmin) or (_x2 > _xmax) or (_y2 < _ymin) or (_y2 > _ymax)): return True # # simple horizontal/vertical testing # if abs(_y2 - _y1) < 1e-10: # horizontal if not ((_y1 < _ymin) or (_y1 > _ymax)): if min(_x1, _x2) < _xmin and max(_x1, _x2) > _xmax: return True if abs(_x2 - _x1) < 1e-10: # vertical if not ((_x1 < _xmin) or (_x1 > _xmax)): if min(_y1, _y2) < _ymin and max(_y1, _y2) > _ymax: return True # # see if segment intersects an imaginary segment # from (xmin, ymax) to (xmax, ymin) # # p1 = (xmin, ymax) # p2 = (xmax, ymin) # p3 = (x1, y1) # p4 = (x2, y2) # _d = ((_xmax - _xmin)*(_y2 - _y1)) - ((_ymin - _ymax)*(_x2 - _x1)) if abs(_d) > 1e-10: _n = ((_ymax - _y1)*(_x2 - _x1)) - ((_xmin - _x1)*(_y2 - _y1)) _r = _n/_d if 0.0 < _r < 1.0: return True # # see if segment intersects an imaginary segment # from (xmin, ymin) to (xmax, ymax) # # p1 = (xmin, ymin) # p2 = (xmax, ymax) # p3 = (x1, y1) # p4 = (x2, y2) # _d = ((_xmax - _xmin)*(_y2 - _y1)) - ((_ymax - _ymin)*(_x2 - _x1)) if abs(_d) > 1e-10: _n = ((_ymin - _y1)*(_x2 - _x1)) - ((_xmin - _x1)*(_y2 - _y1)) _r = _n/_d if 0.0 < _r < 1.0: return True return False