diff --git a/.coverage b/.coverage deleted file mode 100644 index 09e0f7502..000000000 --- a/.coverage +++ /dev/null @@ -1 +0,0 @@ -!coverage.py: This is a private format, don't read it directly!{"lines": {"/home/jwright/Downloads/cadquery/cadquery/cq_directive.py": [], "/home/jwright/Downloads/cadquery/cadquery/cq.py": [2049, 2052, 2053, 2054, 2056, 2057, 2058, 2060, 18, 20, 21, 22, 23, 24, 27, 2076, 2078, 33, 34, 35, 36, 2085, 2086, 39, 40, 2089, 43, 2093, 49, 51, 2102, 2103, 2104, 58, 59, 60, 2109, 62, 2111, 65, 2126, 2127, 2128, 81, 2130, 2131, 84, 2133, 2134, 2140, 2141, 2142, 2143, 96, 2145, 2147, 2149, 102, 2151, 104, 106, 107, 108, 110, 112, 2166, 2168, 2170, 2171, 2172, 2173, 2174, 2178, 363, 2180, 133, 2182, 2183, 136, 2185, 138, 2187, 140, 141, 2255, 143, 145, 149, 150, 151, 153, 154, 156, 2207, 368, 2211, 2213, 2215, 2217, 2218, 2220, 174, 175, 176, 178, 182, 184, 185, 2235, 188, 189, 190, 2239, 192, 193, 195, 374, 2246, 2247, 2249, 2251, 2252, 205, 2254, 207, 2257, 211, 213, 222, 224, 2427, 2276, 2277, 2279, 2282, 2087, 238, 239, 240, 2088, 243, 244, 246, 383, 253, 255, 2307, 2308, 2309, 2310, 2433, 2312, 265, 2314, 2316, 2318, 2333, 2336, 2339, 2340, 2341, 2342, 2344, 2346, 306, 2075, 308, 309, 310, 311, 2361, 314, 2363, 2364, 2366, 2368, 322, 329, 330, 332, 333, 335, 337, 341, 344, 345, 349, 350, 353, 355, 356, 357, 360, 361, 2411, 364, 365, 2414, 2415, 2416, 2417, 2418, 2419, 2420, 2422, 375, 2424, 378, 379, 2428, 382, 2431, 384, 385, 2434, 388, 390, 396, 398, 404, 406, 411, 413, 2471, 2472, 2474, 427, 428, 432, 2482, 2483, 2485, 2486, 2488, 2489, 2491, 2494, 2497, 2498, 2500, 454, 455, 456, 457, 458, 461, 462, 464, 466, 78, 79, 2524, 479, 80, 482, 483, 485, 486, 488, 490, 1447, 82, 2132, 519, 521, 2480, 551, 553, 582, 97, 584, 605, 607, 103, 839, 631, 633, 651, 653, 669, 671, 2502, 681, 683, 692, 694, 720, 722, 723, 724, 725, 727, 729, 2070, 742, 743, 745, 758, 2071, 766, 769, 131, 2072, 2523, 805, 807, 808, 811, 812, 813, 815, 137, 2527, 836, 838, 481, 842, 843, 844, 846, 369, 874, 876, 877, 880, 882, 883, 886, 910, 912, 914, 936, 937, 938, 939, 943, 947, 948, 949, 951, 952, 953, 955, 968, 969, 971, 972, 974, 975, 976, 977, 979, 981, 994, 995, 996, 997, 998, 999, 1001, 1022, 1024, 1025, 1026, 1027, 1031, 1032, 172, 1034, 1036, 1050, 1053, 1054, 1055, 1056, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1067, 1069, 1089, 1090, 1091, 1092, 1094, 1096, 1123, 1124, 1125, 1127, 2236, 2237, 1137, 1139, 1141, 1143, 1144, 1146, 1149, 2241, 1160, 1161, 1163, 1170, 1172, 1179, 1181, 1191, 1192, 1194, 1204, 1205, 1208, 2359, 1223, 1224, 1227, 2253, 1242, 1243, 1244, 1246, 2259, 1278, 1279, 1281, 1282, 1284, 1286, 1287, 1289, 1291, 1307, 1308, 1309, 1311, 1313, 1314, 1316, 1318, 1338, 1341, 1343, 1345, 1346, 1347, 1350, 1352, 1354, 2091, 1372, 1373, 1374, 1376, 1390, 1391, 1392, 1394, 1401, 1403, 1404, 1406, 1421, 1423, 1433, 1434, 1435, 1439, 1446, 241, 1448, 1449, 1451, 1472, 1475, 1476, 1478, 1480, 1481, 1482, 1486, 1487, 1488, 1490, 1492, 1524, 1525, 1527, 1529, 1530, 1532, 1534, 1535, 1536, 1538, 1540, 1542, 1560, 1561, 1564, 1567, 1568, 1570, 1572, 263, 2313, 1598, 1601, 1602, 1603, 1604, 1605, 1607, 1608, 1609, 1610, 1612, 1613, 1616, 1619, 1648, 1649, 1650, 1651, 1653, 1655, 1666, 1668, 1669, 1670, 1671, 1672, 1673, 1675, 1677, 1692, 1695, 1698, 1699, 1701, 1704, 1706, 1707, 1709, 1711, 1725, 1728, 1730, 1732, 1741, 1742, 1743, 1745, 1747, 1757, 1758, 1762, 1763, 1764, 1765, 1767, 1769, 1770, 1773, 1803, 1804, 1806, 1812, 1814, 1817, 1818, 1819, 1821, 1825, 2107, 2356, 2357, 1856, 1857, 1859, 1862, 1865, 1866, 1867, 1868, 1869, 1870, 1872, 1876, 2362, 63, 1902, 1903, 1905, 1911, 1913, 1914, 1916, 1919, 320, 1940, 1942, 1945, 1954, 1955, 1956, 1957, 1958, 1959, 1963, 1964, 1966, 1967, 1968, 1970, 1995, 1997, 1998, 2000, 2001, 2002, 2004, 2027, 2030, 2034, 2035, 2037, 2041, 2044, 2045, 2047], "/home/jwright/Downloads/cadquery/cadquery/freecad_impl/__init__.py": [18, 19, 22, 24, 25, 29, 34, 36, 37, 38, 39, 40, 41, 42, 44, 45, 101, 102, 103, 104, 105, 106, 107, 108, 109], "/home/jwright/Downloads/cadquery/cadquery/freecad_impl/shapes.py": [1024, 514, 1027, 517, 519, 428, 523, 547, 526, 528, 88, 857, 537, 539, 90, 717, 603, 549, 550, 945, 552, 605, 49, 50, 51, 52, 565, 55, 568, 569, 571, 572, 61, 693, 352, 68, 69, 582, 71, 584, 74, 587, 588, 590, 79, 592, 580, 82, 83, 84, 85, 87, 600, 89, 602, 91, 604, 93, 94, 95, 96, 609, 98, 699, 104, 105, 618, 619, 621, 110, 623, 624, 113, 114, 627, 116, 617, 72, 636, 819, 640, 643, 134, 961, 567, 862, 651, 654, 655, 659, 662, 664, 669, 160, 673, 795, 677, 909, 682, 455, 172, 173, 175, 807, 178, 181, 694, 184, 628, 187, 188, 701, 702, 191, 799, 708, 710, 711, 204, 825, 206, 719, 720, 209, 210, 211, 212, 213, 729, 219, 794, 806, 208, 741, 742, 977, 233, 561, 809, 750, 752, 59, 243, 244, 245, 189, 247, 680, 249, 762, 931, 255, 256, 769, 258, 260, 897, 812, 898, 813, 276, 279, 280, 793, 282, 283, 796, 285, 286, 901, 288, 289, 802, 291, 292, 294, 295, 808, 297, 298, 300, 301, 303, 816, 904, 306, 307, 309, 822, 823, 564, 991, 317, 318, 821, 320, 321, 323, 324, 325, 327, 329, 330, 761, 332, 333, 335, 1017, 340, 343, 856, 241, 859, 860, 349, 350, 351, 864, 353, 500, 355, 486, 97, 487, 483, 368, 369, 370, 372, 373, 376, 890, 379, 892, 381, 800, 895, 385, 386, 387, 388, 389, 906, 392, 818, 394, 395, 908, 397, 401, 404, 921, 407, 920, 409, 855, 413, 926, 928, 240, 930, 419, 420, 933, 753, 424, 426, 427, 940, 429, 942, 925, 944, 433, 947, 949, 951, 952, 441, 954, 801, 444, 562, 672, 962, 331, 964, 803, 417, 456, 457, 459, 972, 418, 974, 975, 465, 466, 979, 468, 469, 471, 472, 475, 817, 442, 992, 995, 485, 998, 81, 1000, 489, 1004, 594, 1007, 1009, 499, 595, 1012, 501, 503, 596, 1018, 1019, 767, 1021, 511], "/home/jwright/Downloads/cadquery/cadquery/__init__.py": [2, 3, 4, 5, 10, 11, 15, 16, 17, 18, 21], "/home/jwright/Downloads/cadquery/cadquery/selectors.py": [18, 20, 21, 22, 25, 30, 31, 41, 43, 44, 46, 47, 49, 50, 52, 53, 55, 71, 72, 73, 74, 76, 77, 83, 85, 98, 99, 100, 101, 102, 104, 106, 107, 108, 110, 113, 114, 115, 117, 118, 119, 120, 121, 122, 124, 125, 127, 129, 133, 134, 135, 136, 138, 142, 148, 149, 152, 154, 156, 157, 158, 160, 161, 162, 164, 166, 184, 186, 187, 189, 207, 209, 210, 212, 230, 232, 233, 234, 235, 238, 257, 258, 259, 261, 262, 263, 264, 265, 266, 268, 293, 294, 295, 296, 297, 298, 299, 301, 302, 310, 311, 313, 316, 318, 322, 323, 324, 325, 327, 328, 329, 331, 334, 337, 338, 340, 342, 345, 346, 348, 350, 354, 355, 356, 358, 362, 363, 364, 366, 368, 370, 405, 406, 408, 409, 410, 411, 412, 413, 414, 417, 418, 419, 420, 421, 422, 423, 425, 426, 427, 429, 430, 431, 432, 434, 439, 442, 443, 447, 448, 452, 454, 455, 457, 458, 459, 460, 461, 462, 463, 464, 465, 469, 474], "/home/jwright/Downloads/cadquery/cadquery/cqgi.py": [4, 5, 6, 7, 8, 10, 12, 23, 24, 27, 36, 38, 43, 44, 45, 46, 51, 53, 64, 66, 67, 68, 70, 71, 72, 74, 83, 95, 96, 98, 99, 101, 102, 103, 104, 105, 106, 107, 110, 111, 112, 113, 114, 116, 117, 118, 119, 120, 121, 122, 124, 125, 126, 128, 129, 131, 132, 133, 135, 136, 139, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 165, 166, 167, 168, 171, 175, 176, 177, 179, 180, 182, 183, 184, 185, 188, 189, 192, 193, 196, 197, 200, 201, 204, 214, 215, 218, 221, 224, 227, 230, 232, 234, 235, 237, 238, 240, 241, 242, 243, 244, 245, 246, 247, 249, 251, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 267, 268, 271, 275, 280, 285, 286, 287, 288, 290, 295, 297, 301, 303, 309, 315, 316, 318, 323, 324, 325, 326, 328, 332, 333, 336, 340, 341, 344, 349, 351, 362, 365, 368, 372, 377, 378, 379, 381, 382, 384, 385, 386, 388, 389, 390, 391, 393, 394, 395, 397, 398, 400, 403, 404, 405, 407, 411, 412, 416, 417, 418, 420, 421, 422, 423, 425, 428, 430, 431, 433, 434, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 453, 455, 456, 459, 462, 463, 464, 466, 467, 472], "/home/jwright/Downloads/cadquery/cadquery/freecad_impl/importers.py": [2, 3, 5, 6, 7, 8, 9, 10, 12, 13, 15, 16, 17, 20, 28, 29, 33, 39, 41, 44, 45, 46, 48, 53], "/home/jwright/Downloads/cadquery/cadquery/freecad_impl/exporters.py": [1, 3, 4, 6, 9, 10, 15, 16, 17, 18, 19, 20, 23, 24, 25, 28, 34, 46, 47, 49, 51, 53, 55, 56, 59, 60, 61, 62, 63, 64, 65, 66, 71, 74, 76, 77, 78, 79, 83, 84, 86, 91, 92, 93, 95, 96, 99, 103, 105, 107, 108, 111, 115, 116, 118, 121, 122, 124, 125, 127, 128, 130, 131, 132, 133, 136, 137, 138, 139, 140, 141, 142, 143, 144, 147, 148, 149, 150, 151, 152, 153, 154, 157, 164, 165, 167, 168, 169, 170, 172, 173, 174, 177, 179, 180, 186, 187, 188, 189, 190, 191, 195, 210, 211, 212, 214, 215, 216, 217, 218, 220, 221, 223, 225, 226, 227, 232, 237, 239, 243, 245, 246, 247, 248, 251, 252, 254, 257, 258, 259, 260, 263, 266, 269, 270, 271, 273, 274, 275, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 294, 297, 304, 305, 306, 307, 353, 389, 391], "/home/jwright/Downloads/cadquery/cadquery/freecad_impl/geom.py": [515, 516, 517, 522, 523, 524, 529, 18, 20, 21, 22, 23, 26, 540, 542, 544, 546, 547, 548, 550, 632, 42, 44, 45, 46, 47, 560, 49, 50, 563, 564, 565, 567, 568, 569, 58, 572, 573, 575, 581, 70, 583, 72, 73, 74, 75, 76, 77, 78, 79, 592, 593, 594, 595, 597, 87, 89, 91, 93, 95, 608, 97, 99, 101, 614, 103, 616, 105, 618, 107, 109, 110, 113, 114, 116, 117, 630, 119, 120, 633, 122, 123, 125, 638, 127, 128, 130, 635, 132, 645, 134, 136, 144, 146, 147, 149, 152, 155, 158, 161, 162, 164, 167, 170, 173, 174, 177, 181, 182, 183, 184, 188, 189, 191, 192, 195, 586, 631, 206, 208, 209, 463, 634, 570, 238, 637, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 254, 255, 260, 261, 262, 263, 264, 266, 267, 268, 269, 270, 272, 273, 643, 278, 279, 280, 281, 282, 284, 285, 133, 48, 290, 291, 296, 297, 302, 303, 51, 308, 309, 52, 314, 315, 590, 629, 53, 320, 321, 326, 327, 55, 332, 345, 346, 348, 349, 350, 352, 354, 356, 358, 360, 362, 364, 365, 367, 609, 389, 391, 495, 69, 418, 419, 582, 423, 424, 71, 428, 430, 584, 611, 585, 445, 446, 587, 454, 588, 461, 462, 589, 464, 466, 467, 469, 591, 483, 485, 488, 489, 490, 491, 494, 613, 497, 499], "/home/jwright/Downloads/cadquery/cadquery/plugins/__init__.py": [18], "/home/jwright/Downloads/cadquery/cadquery/contrib/__init__.py": []}} \ No newline at end of file diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..77ac8c6d1 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,7 @@ +[run] +branch = True + +[report] +exclude_lines = + # Ignore stub body + \.\.\. diff --git a/.travis.yml b/.travis.yml index 368c9f2db..f4e29af2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,7 @@ matrix: os: linux script: - black . --diff --check + - mypy cadquery before_install: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then @@ -54,7 +55,8 @@ before_install: - conda config --set always_yes yes --set changeps1 no; - conda env create -f environment.yml - source ~/miniconda/bin/activate cadquery -- conda install -c conda-forge -c defaults -c cadquery python=$PYTHON_VERSION +- conda install -c conda-forge -c defaults -c cadquery python=$PYTHON_VERSION mypy +- pip install git+https://github.com/CadQuery/OCP-stubs.git install: - python setup.py install diff --git a/cadquery/occ_impl/exporters.py b/cadquery/occ_impl/exporters.py index 159a77543..2abe6baf6 100644 --- a/cadquery/occ_impl/exporters.py +++ b/cadquery/occ_impl/exporters.py @@ -6,10 +6,7 @@ import os import sys -if sys.version_info.major == 2: - import cStringIO as StringIO -else: - import io as StringIO +import io as StringIO from .shapes import Shape, Compound, TOLERANCE from .geom import BoundBox @@ -20,10 +17,7 @@ from OCP.HLRAlgo import HLRAlgo_Projector from OCP.GCPnts import GCPnts_QuasiUniformDeflection -try: - import xml.etree.cElementTree as ET -except ImportError: - import xml.etree.ElementTree as ET +import xml.etree.cElementTree as ET DISCRETIZATION_TOLERANCE = 1e-3 DEFAULT_DIR = gp_Dir(-1.75, 1.1, 5) diff --git a/cadquery/occ_impl/geom.py b/cadquery/occ_impl/geom.py index d42c7334b..3feaa2925 100644 --- a/cadquery/occ_impl/geom.py +++ b/cadquery/occ_impl/geom.py @@ -1,9 +1,12 @@ import math +from typing import overload, Sequence, Union, Tuple, Type, Optional + from OCP.gp import gp_Vec, gp_Ax1, gp_Ax3, gp_Pnt, gp_Dir, gp_Trsf, gp_GTrsf, gp, gp_XYZ from OCP.Bnd import Bnd_Box from OCP.BRepBndLib import BRepBndLib from OCP.BRepMesh import BRepMesh_IncrementalMesh +from OCP.TopoDS import TopoDS_Shape TOL = 1e-2 @@ -23,6 +26,32 @@ class Vector(object): * two float values: x,y """ + _wrapped: gp_Vec + + @overload + def __init__(self, x: float, y: float, z: float) -> None: + ... + + @overload + def __init__(self, x: float, y: float) -> None: + ... + + @overload + def __init__(self, v: "Vector") -> None: + ... + + @overload + def __init__(self, v: Sequence[float]) -> None: + ... + + @overload + def __init__(self, v: Union[gp_Vec, gp_Pnt, gp_Dir, gp_XYZ]) -> None: + ... + + @overload + def __init__(self) -> None: + ... + def __init__(self, *args): if len(args) == 3: fV = gp_Vec(*args) @@ -51,74 +80,73 @@ def __init__(self, *args): self._wrapped = fV @property - def x(self): + def x(self) -> float: return self.wrapped.X() @x.setter - def x(self, value): + def x(self, value: float) -> None: self.wrapped.SetX(value) @property - def y(self): + def y(self) -> float: return self.wrapped.Y() @y.setter - def y(self, value): + def y(self, value: float) -> None: self.wrapped.SetY(value) @property - def z(self): + def z(self) -> float: return self.wrapped.Z() @z.setter - def z(self, value): + def z(self, value: float) -> None: self.wrapped.SetZ(value) @property - def Length(self): + def Length(self) -> float: return self.wrapped.Magnitude() @property - def wrapped(self): + def wrapped(self) -> gp_Vec: return self._wrapped - def toTuple(self): + def toTuple(self) -> Tuple[float, float, float]: return (self.x, self.y, self.z) - # TODO: is it possible to create a dynamic proxy without all this code? - def cross(self, v): + def cross(self, v: "Vector") -> "Vector": return Vector(self.wrapped.Crossed(v.wrapped)) - def dot(self, v): + def dot(self, v: "Vector") -> float: return self.wrapped.Dot(v.wrapped) - def sub(self, v): + def sub(self, v: "Vector") -> "Vector": return Vector(self.wrapped.Subtracted(v.wrapped)) - def __sub__(self, v): + def __sub__(self, v: "Vector") -> "Vector": return self.sub(v) - def add(self, v): + def add(self, v: "Vector") -> "Vector": return Vector(self.wrapped.Added(v.wrapped)) - def __add__(self, v): + def __add__(self, v: "Vector") -> "Vector": return self.add(v) - def multiply(self, scale): + def multiply(self, scale: float) -> "Vector": """Return a copy multiplied by the provided scalar""" return Vector(self.wrapped.Multiplied(scale)) - def __mul__(self, scale): + def __mul__(self, scale: float) -> "Vector": return self.multiply(scale) - def __truediv__(self, denom): + def __truediv__(self, denom: float) -> "Vector": return self.multiply(1.0 / denom) - def normalized(self): + def normalized(self) -> "Vector": """Return a normalized version of this vector""" return Vector(self.wrapped.Normalized()) - def Center(self): + def Center(self) -> "Vector": """Return the vector itself The center of myself is myself. @@ -128,7 +156,7 @@ def Center(self): """ return self - def getAngle(self, v): + def getAngle(self, v: "Vector") -> float: return self.wrapped.Angle(v.wrapped) def distanceToLine(self): @@ -140,7 +168,7 @@ def projectToLine(self): def distanceToPlane(self): raise NotImplementedError("Have not needed this yet, but OCCT supports it!") - def projectToPlane(self, plane): + def projectToPlane(self, plane: "Plane") -> "Vector": """ Vector is projected onto the plane provided as input. @@ -153,36 +181,30 @@ def projectToPlane(self, plane): return self - normal * (((self - base).dot(normal)) / normal.Length ** 2) - def __neg__(self): + def __neg__(self) -> "Vector": return self * -1 - def __abs__(self): + def __abs__(self) -> float: return self.Length - def __repr__(self): + def __repr__(self) -> str: return "Vector: " + str((self.x, self.y, self.z)) - def __str__(self): + def __str__(self) -> str: return "Vector: " + str((self.x, self.y, self.z)) - def __eq__(self, other): + def __eq__(self, other: "Vector") -> bool: # type: ignore[override] return self.wrapped.IsEqual(other.wrapped, 0.00001, 0.00001) - """ - is not implemented in OCC - def __ne__(self, other): - return self.wrapped.__ne__(other) - """ - - def toPnt(self): + def toPnt(self) -> gp_Pnt: return gp_Pnt(self.wrapped.XYZ()) - def toDir(self): + def toDir(self) -> gp_Dir: return gp_Dir(self.wrapped.XYZ()) - def transform(self, T): + def transform(self, T: "Matrix") -> "Vector": # to gp_Pnt to obey cq transformation convention (in OCP.vectors do not translate) pnt = self.toPnt() @@ -209,6 +231,20 @@ class Matrix: since this is a transform matrix. """ + wrapped: gp_GTrsf + + @overload + def __init__(self) -> None: + ... + + @overload + def __init__(self, matrix: Union[gp_GTrsf, gp_Trsf]) -> None: + ... + + @overload + def __init__(self, matrix: Sequence[Sequence[float]]) -> None: + ... + def __init__(self, matrix=None): if matrix is None: @@ -246,29 +282,37 @@ def __init__(self, matrix=None): else: raise TypeError("Invalid param to matrix constructor: {}".format(matrix)) - def rotateX(self, angle): + def rotateX(self, angle: float): self._rotate(gp.OX_s(), angle) - def rotateY(self, angle): + def rotateY(self, angle: float): self._rotate(gp.OY_s(), angle) - def rotateZ(self, angle): + def rotateZ(self, angle: float): self._rotate(gp.OZ_s(), angle) - def _rotate(self, direction, angle): + def _rotate(self, direction: gp_Ax1, angle: float): new = gp_Trsf() new.SetRotation(direction, angle) self.wrapped = self.wrapped * gp_GTrsf(new) - def inverse(self): + def inverse(self) -> "Matrix": return Matrix(self.wrapped.Inverted()) + @overload + def multiply(self, other: Vector) -> Vector: + ... + + @overload + def multiply(self, other: "Matrix") -> "Matrix": + ... + def multiply(self, other): if isinstance(other, Vector): @@ -276,7 +320,7 @@ def multiply(self, other): return Matrix(self.wrapped.Multiplied(other.wrapped)) - def transposed_list(self): + def transposed_list(self) -> Sequence[float]: """Needed by the cqparts gltf exporter """ @@ -287,7 +331,7 @@ def transposed_list(self): return [data[j][i] for i in range(4) for j in range(4)] - def __getitem__(self, rc): + def __getitem__(self, rc: Tuple[int, int]) -> float: """Provide Matrix[r, c] syntax for accessing individual values. The row and column parameters start at zero, which is consistent with most python libraries, but is counter to gp_GTrsf(), which is 1-indexed. @@ -319,12 +363,21 @@ class Plane(object): created automatically from faces. """ + xDir: Vector + yDir: Vector + zDir: Vector + _origin: Vector + + lcs: gp_Ax3 + rG: Matrix + fG: Matrix + # equality tolerances _eq_tolerance_origin = 1e-6 _eq_tolerance_dot = 1e-6 @classmethod - def named(cls, stdName, origin=(0, 0, 0)): + def named(cls: Type["Plane"], stdName: str, origin=(0, 0, 0)) -> "Plane": """Create a predefined Plane based on the conventional names. :param stdName: one of (XY|YZ|ZX|XZ|YX|ZY|front|back|left|right|top|bottom) @@ -492,8 +545,6 @@ def __ne__(self, other): def origin(self): return self._origin - # TODO is this property rly needed -- why not handle this in the constructor - @origin.setter def origin(self, value): self._origin = Vector(value) @@ -523,50 +574,6 @@ def setOrigin2d(self, x, y): """ self.origin = self.toWorldCoords((x, y)) - def isWireInside(self, baseWire, testWire): - """Determine if testWire is inside baseWire - - Determine if testWire is inside baseWire, after both wires are projected - into the current plane. - - :param baseWire: a reference wire - :type baseWire: a FreeCAD wire - :param testWire: another wire - :type testWire: a FreeCAD wire - :return: True if testWire is inside baseWire, otherwise False - - If either wire does not lie in the current plane, it is projected into - the plane first. - - *WARNING*: This method is not 100% reliable. It uses bounding box - tests, but needs more work to check for cases when curves are complex. - - Future Enhancements: - * Discretizing points along each curve to provide a more reliable - test. - """ - - pass - - """ - # TODO: also use a set of points along the wire to test as well. - # TODO: would it be more efficient to create objects in the local - # coordinate system, and then transform to global - # coordinates upon extrusion? - - tBaseWire = baseWire.transformGeometry(self.fG) - tTestWire = testWire.transformGeometry(self.fG) - - # These bounding boxes will have z=0, since we transformed them into the - # space of the plane. - bb = tBaseWire.BoundingBox() - tb = tTestWire.BoundingBox() - - # findOutsideBox actually inspects both ways, here we only want to - # know if one is inside the other - return bb == BoundBox.findOutsideBox2D(bb, tb) - """ - def toLocalCoords(self, obj): """Project the provided coordinates onto this plane @@ -650,54 +657,6 @@ def rotated(self, rotate=(0, 0, 0)): return Plane(self.origin, newXdir, newZdir) - def rotateShapes(self, listOfShapes, rotationMatrix): - """Rotate the listOfShapes by the supplied rotationMatrix - - @param listOfShapes is a list of shape objects - @param rotationMatrix is a geom.Matrix object. - returns a list of shape objects rotated according to the rotationMatrix. - """ - # Compute rotation matrix (global --> local --> rotate --> global). - # rm = self.plane.fG.multiply(matrix).multiply(self.plane.rG) - # rm = self.computeTransform(rotationMatrix) - - # There might be a better way, but to do this rotation takes 3 steps: - # - transform geometry to local coordinates - # - then rotate about x - # - then transform back to global coordinates. - - # TODO why is it here? - - raise NotImplementedError - - """ - resultWires = [] - for w in listOfShapes: - mirrored = w.transformGeometry(rotationMatrix.wrapped) - - # If the first vertex of the second wire is not coincident with the - # first or last vertices of the first wire we have to fix the wire - # so that it will mirror correctly. - if ((mirrored.wrapped.Vertexes[0].X == w.wrapped.Vertexes[0].X and - mirrored.wrapped.Vertexes[0].Y == w.wrapped.Vertexes[0].Y and - mirrored.wrapped.Vertexes[0].Z == w.wrapped.Vertexes[0].Z) or - (mirrored.wrapped.Vertexes[0].X == w.wrapped.Vertexes[-1].X and - mirrored.wrapped.Vertexes[0].Y == w.wrapped.Vertexes[-1].Y and - mirrored.wrapped.Vertexes[0].Z == w.wrapped.Vertexes[-1].Z)): - - resultWires.append(mirrored) - else: - # Make sure that our mirrored edges meet up and are ordered - # properly. - aEdges = w.wrapped.Edges - aEdges.extend(mirrored.wrapped.Edges) - comp = FreeCADPart.Compound(aEdges) - mirroredWire = comp.connectEdgesToWires(False).Wires[0] - - resultWires.append(cadquery.Shape.cast(mirroredWire)) - - return resultWires""" - def mirrorInPlane(self, listOfShapes, axis="X"): local_coord_system = gp_Ax3( @@ -756,7 +715,6 @@ def _calcTransforms(self): inverseT.SetTransformation(local_coord_system, global_coord_system) inverse.wrapped = gp_GTrsf(inverseT) - # TODO verify if this is OK self.lcs = local_coord_system self.rG = inverse self.fG = forward @@ -765,7 +723,21 @@ def _calcTransforms(self): class BoundBox(object): """A BoundingBox for an object or set of objects. Wraps the OCP one""" - def __init__(self, bb): + wrapped: Bnd_Box + + xmin: float + xmax: float + xlen: float + + ymin: float + ymax: float + ylen: float + + zmin: float + zmax: float + zlen: float + + def __init__(self, bb: Bnd_Box) -> None: self.wrapped = bb XMin, YMin, ZMin, XMax, YMax, ZMax = bb.Get() @@ -783,7 +755,11 @@ def __init__(self, bb): self.DiagonalLength = self.wrapped.SquareExtent() ** 0.5 - def add(self, obj, tol=1e-8): + def add( + self, + obj: Union[Tuple[float, float, float], Vector, "BoundBox"], + tol: float = 1e-8, + ) -> "BoundBox": """Returns a modified (expanded) bounding box obj can be one of several things: @@ -809,7 +785,7 @@ def add(self, obj, tol=1e-8): return BoundBox(tmp) @staticmethod - def findOutsideBox2D(bb1, bb2): + def findOutsideBox2D(bb1: "BoundBox", bb2: "BoundBox") -> Optional["BoundBox"]: """Compares bounding boxes Compares bounding boxes. Returns none if neither is inside the other. @@ -821,25 +797,30 @@ def findOutsideBox2D(bb1, bb2): """ if ( - bb1.XMin < bb2.XMin - and bb1.XMax > bb2.XMax - and bb1.YMin < bb2.YMin - and bb1.YMax > bb2.YMax + bb1.xmin < bb2.xmin + and bb1.xmax > bb2.xmax + and bb1.ymin < bb2.ymin + and bb1.ymax > bb2.ymax ): return bb1 if ( - bb2.XMin < bb1.XMin - and bb2.XMax > bb1.XMax - and bb2.YMin < bb1.YMin - and bb2.YMax > bb1.YMax + bb2.xmin < bb1.xmin + and bb2.xmax > bb1.xmax + and bb2.ymin < bb1.ymin + and bb2.ymax > bb1.ymax ): return bb2 return None @classmethod - def _fromTopoDS(cls, shape, tol=None, optimal=True): + def _fromTopoDS( + cls: Type["BoundBox"], + shape: TopoDS_Shape, + tol: Optional[float] = None, + optimal: bool = True, + ): """ Constructs a bounding box from a TopoDS_Shape """ @@ -858,7 +839,7 @@ def _fromTopoDS(cls, shape, tol=None, optimal=True): return cls(bbox) - def isInside(self, b2): + def isInside(self, b2: "BoundBox") -> bool: """Is the provided bounding box inside this one?""" if ( b2.xmin > self.xmin diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 000000000..ef5416ed8 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,11 @@ +[mypy] +ignore_missing_imports = False + +[mypy-ezdxf.*] +ignore_missing_imports = True + +[mypy-pyparsing.*] +ignore_missing_imports = True + +[mypy-IPython.*] +ignore_missing_imports = True