1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916
|
#
# Copyright (c) 2002, 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
#
#
# tool stuff
#
import math
import types
import array
from PythonCAD.Generic import util
from PythonCAD.Generic import color
from PythonCAD.Generic import linetype
from PythonCAD.Generic import style
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
from PythonCAD.Generic.text import TextStyle, TextBlock
from PythonCAD.Generic import dimension
from PythonCAD.Generic.layer import Layer
from PythonCAD.Generic import tangent
class Tool(object):
"""A generic tool object.
This class is meant to be a base class for tools. A Tool
instance the following attributes:
list: A list the tool can use to store objects
handlers: A dictionary used to store functions
A Tool object has the following methods:
{set/get/del/has}Handler(): Store/retrive/delete/test a handler for an event.
clearHandlers(): Unset all the handlers in the tool.
reset(): Restore the tool to a a default state.
initialize(): Retore the tool to its original state.
{set/get}Filter(): Set/Get a filtering procedure for object testing.
{set/get}Location(): Store/retrieve an image-based coordinate pair.
{set/get}CurrentPoint(): Store/retrieve a screen-based coordinate pair.
clearCurrentPoint(): Set the screen-based coordinate to None.
create(): Instantiate the object the tool is designed to create.
"""
def __init__(self):
"""Instantiate a Tool.
t = Tool()
"""
super(Tool, self).__init__()
self.__objlist = []
self.__objtype = None
self.__fproc = None
self.__handlers = {}
self.__location = None
self.__curpoint = None
self.__points = []
self.__xpts = array.array('d')
self.__ypts = array.array('d')
self.__shift = None
def __len__(self):
"""Return the number of objects in the list via len().
"""
return len(self.__objlist)
def __iter__(self):
"""Make the Tool iterable.
"""
return iter(self.__objlist)
def getList(self):
"""Return the Tool's object list.
getList()
"""
return self.__objlist
list = property(getList, None, None, "Tool object list.")
def reset(self):
"""Restore the Tool to its initial state.
reset()
This function purges the Tool's object list and handler dictionary.
"""
del self.__objlist[:]
self.__handlers.clear()
self.__location = None
self.__curpoint = None
del self.__points[:]
del self.__xpts[:]
del self.__ypts[:]
self.__shift = None
def initialize(self):
self.reset()
def setHandler(self, key, func):
"""Set a handler for the Tool.
setHandler(key, func)
There are two arguments for this function:
key: A string used to identify a particular action
func: A function object
There are no restrictions on what the function 'func' does,
the argument count, etc. Any call to setHandler() with
a key that is already stored replaces the old 'func' argument
with the new one. The function argument may be None, and
the key argument must be a string.
"""
if key is None:
raise ValueError, "Key value cannot be None."
if not isinstance(key, str):
raise TypeError, "Invalid key type: " + `type(key)`
if func and not isinstance(func, types.FunctionType):
raise TypeError, "Invalid function type: " + `type(func)`
self.__handlers[key] = func
def getHandler(self, key):
"""Return the function for a particular key.
getHandler(key)
Given argument 'key', the function associated with it is
returned. A KeyError is raised if the argument 'key' had
not be used to store a function.
"""
if not isinstance(key, str):
raise TypeError, "Invalid key type: " + `type(key)`
if not self.__handlers.has_key(key):
raise KeyError, "Invalid key '%s'" % key
return self.__handlers[key]
def delHandler(self, key):
"""Delete the handler associated with a particular key
delHandler(key)
The argument 'key' should be a string.
"""
if self.__handlers.has_key(key):
del self.__handlers[key]
def hasHandler(self, key):
"""Check if there is a handler stored for a given key.
hasHandler(key)
The 'key' argument must be a string. The function returns 1
if there is a handler for that key, 0 otherwise.
"""
_k = key
if not isinstance(_k, str):
raise TypeError, "Invalid key type: " + `type(key)`
return self.__handlers.has_key(_k)
def clearHandlers(self):
"""Unset all the handlers for the tool.
clearHandlers()
This function does not alter the Tool's object list.
"""
self.__handlers.clear()
def setObjtype(self, objtype):
"""Store the type of objects on which the tool operates.
setObjtype(objtype)
Argument 'objtype' can be a single type, a tuple of types, or 'None'.
"""
if not isinstance(objtype, (tuple, types.NoneType, types.TypeType)):
raise TypeError, "Invalid objtype: " + `type(objtype)`
if objtype is not None:
if isinstance(objtype, tuple):
for _obj in objtype:
if not isinstance(_obj, types.TypeType):
raise TypeError, "Invalid objtype: " + `type(_obj)`
self.__objtype = objtype
def getObjtype(self):
"""Return the type of object on which the tool operates.
getObjtype()
This method returns the value set in a setObjtype(), or None if
no object types have been specified.
"""
return self.__objtype
def setFilter(self, proc):
"""Store a procedure used to examine selected objects.
setFilter(proc)
Argument 'proc' must be a callable procedure.
"""
if not callable(proc):
raise TypeError, "Invalid filter procedure: " + `type(proc)`
self.__fproc = proc
def getFilter(self):
"""Return a stored procedure.
getFilter()
This method returns the procedure stored vai setFilter() or None.
"""
return self.__fproc
def pushObject(self, obj):
"""Add an object to the Tool's object list.
pushObject(obj)
"""
self.__objlist.append(obj)
def popObject(self):
"""Remove the last object on the Tool's object list.
popObject()
If the object list is empty, this function returns None.
"""
_obj = None
if len(self.__objlist):
_obj = self.__objlist.pop()
return _obj
def delObjects(self):
"""Remove all objects from the Tool's object list.
delObjects()
This function does not alter the Tool's handlers.
"""
del self.__objlist[:]
def getObject(self, idx):
"""Access an object in the tool.
getObject(idx)
The argument 'idx' is the index into the list of
stored objects.
"""
return self.__objlist[idx]
def setLocation(self, x, y):
"""Store an x/y location in the tool.
setLocation(x,y)
Store an x-y coordinate in the tool. Both arguments
should be floats
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__location = (_x,_y)
def getLocation(self):
"""Return the stored location in the tool
getLocation()
"""
return self.__location
def clearLocation(self):
"""Reset the location to an empty value.
clearLocation()
"""
self.__location = None
def setCurrentPoint(self, x, y):
"""Set the tool's current point.
setCurrentPoint(x,y)
Store an x-y coordinate in the tool. Both arguments
should be int.
"""
_x = x
if not isinstance(_x, int):
_x = int(x)
_y = y
if not isinstance(_y, int):
_y = int(y)
self.__curpoint = (_x, _y)
def getCurrentPoint(self):
"""Return the tool's current point value.
getCurrentPoint()
"""
return self.__curpoint
def clearCurrentPoint(self):
"""Reset the current point to an empty value
clearCurrentPoint()
"""
self.__curpoint = None
def create(self, image):
"""Create an object the tool is designed to construct.
create(image)
The argument 'image' is an image in which the newly created object
will be added. In the Tool class, this method does nothing. It is
meant to be overriden in classed using the Tool class as a base
class.
"""
pass # override
class PointTool(Tool):
"""A specialized tool for drawing Point objects.
The PointTool class is derived from the Tool class, so it
shares the methods and attributes of that class. The PointTool
class has the following additional methods:
{get/set}Point(): Get/Set a x/y coordinate in the tool.
"""
def __init__(self):
super(PointTool, self).__init__()
self.__point = None
def setPoint(self, x, y):
"""Store an x/y coordinate in the tool
setPoint(x, y)
Arguments 'x' and 'y' should be floats.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__point = (_x, _y)
def getPoint(self):
"""Get the stored x/y coordinates from the tool.
getPoint()
This method returns a tuple containing the values passed in
with the setPoint() method, or None if that method has not
been invoked.
"""
return self.__point
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends Tool::reset().
"""
super(PointTool, self).reset()
self.__point = None
def create(self, image):
"""Create a new Point and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
if self.__point is not None:
_active_layer = image.getActiveLayer()
_x, _y = self.__point
_p = Point(_x, _y)
_active_layer.addObject(_p)
self.reset()
class SegmentTool(Tool):
"""A Specialized tool for drawing Segment objects.
The SegmentTool class is derived from the Tool class, so
it shares the attributes and methods of that class. The
SegmentTool class has the following additional methods:
{get/set}FirstPoint(): Get/Set the first point of the Segment.
{get/set}SecondPoint(): Get/Set the second point of the Segment.
"""
def __init__(self):
super(SegmentTool, self).__init__()
self.__first_point = None
self.__second_point = None
def setFirstPoint(self, x, y):
"""Store the first point of the Segment.
setFirstPoint(x, y)
Arguments 'x' and 'y' should be floats.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__first_point = (_x, _y)
def getFirstPoint(self):
"""Get the first point of the Segment.
getFirstPoint()
This method returns a tuple holding the coordinates stored
by invoking the setFirstPoint() method, or None if that method
has not been invoked.
"""
return self.__first_point
def setSecondPoint(self, x, y):
"""Store the second point of the Segment.
setSecondPoint(x, y)
Arguments 'x' and 'y' should be floats. If the
tool has not had the first point set with setFirstPoint(),
a ValueError exception is raised.
"""
if self.__first_point is None:
raise ValueError, "SegmentTool first point is not set."
_x = util.get_float(x)
_y = util.get_float(y)
self.__second_point = (_x, _y)
def getSecondPoint(self):
"""Get the second point of the Segment.
getSecondPoint()
This method returns a tuple holding the coordinates stored
by invoking the setSecondPoint() method, or None if that method
has not been invoked.
"""
return self.__second_point
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends Tool::reset().
"""
super(SegmentTool, self).reset()
self.__first_point = None
self.__second_point = None
def create(self, image):
"""Create a new Segment and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
if (self.__first_point is not None and
self.__second_point is not None):
_active_layer = image.getActiveLayer()
_x1, _y1 = self.__first_point
_x2, _y2 = self.__second_point
_pts = _active_layer.find('point', _x1, _y1)
if len(_pts) == 0:
_p1 = Point(_x1, _y1)
_active_layer.addObject(_p1)
else:
_p1 = _pts[0]
_pts = _active_layer.find('point', _x2, _y2)
if len(_pts) == 0:
_p2 = Point(_x2, _y2)
_active_layer.addObject(_p2)
else:
_p2 = _pts[0]
_s = image.getOption('LINE_STYLE')
_seg = Segment(_p1, _p2, _s)
_l = image.getOption('LINE_TYPE')
if _l != _s.getLinetype():
_seg.setLinetype(_l)
_c = image.getOption('LINE_COLOR')
if _c != _s.getColor():
_seg.setColor(_c)
_t = image.getOption('LINE_THICKNESS')
if abs(_t - _s.getThickness()) > 1e-10:
_seg.setThickness(_t)
_active_layer.addObject(_seg)
self.reset()
class RectangleTool(SegmentTool):
"""A Specialized tool for drawing rectangles.
The RectangleTool is derived from the SegmentTool, so it
shares all the methods and attributes of that class. A
RectangleTool creates four Segments in the shape of
a rectangle in the image.
"""
def __init__(self):
super(RectangleTool, self).__init__()
def create(self, image):
"""Create Segments and add them to the image.
create(image)
This method overrides the SegmentTool::create() method.
"""
_p1 = self.getFirstPoint()
_p2 = self.getSecondPoint()
if _p1 is not None and _p2 is not None:
_x1, _y1 = _p1
_x2, _y2 = _p2
_active_layer = image.getActiveLayer()
_pts = _active_layer.find('point', _x1, _y1)
if len(_pts) == 0:
_p1 = Point(_x1, _y1)
_active_layer.addObject(_p1)
else:
_p1 = _pts[0]
_pts = _active_layer.find('point', _x1, _y2)
if len(_pts) == 0:
_p2 = Point(_x1, _y2)
_active_layer.addObject(_p2)
else:
_p2 = _pts[0]
_pts = _active_layer.find('point', _x2, _y2)
if len(_pts) == 0:
_p3 = Point(_x2, _y2)
_active_layer.addObject(_p3)
else:
_p3 = _pts[0]
_pts = _active_layer.find('point', _x2, _y1)
if len(_pts) == 0:
_p4 = Point(_x2, _y1)
_active_layer.addObject(_p4)
else:
_p4 = _pts[0]
_s = image.getOption('LINE_STYLE')
_l = image.getOption('LINE_TYPE')
if _l == _s.getLinetype():
_l = None
_c = image.getOption('LINE_COLOR')
if _c == _s.getColor():
_c = None
_t = image.getOption('LINE_THICKNESS')
if abs(_t - _s.getThickness()) < 1e-10:
_t = None
_seg = Segment(_p1, _p2, _s, linetype=_l, color=_c, thickness=_t)
_active_layer.addObject(_seg)
_seg = Segment(_p2, _p3, _s, linetype=_l, color=_c, thickness=_t)
_active_layer.addObject(_seg)
_seg = Segment(_p3, _p4, _s, linetype=_l, color=_c, thickness=_t)
_active_layer.addObject(_seg)
_seg = Segment(_p4, _p1, _s, linetype=_l, color=_c, thickness=_t)
_active_layer.addObject(_seg)
self.reset()
class CircleTool(Tool):
"""A Specialized tool for drawing Circle objects.
The CircleTool is derived from the Tool class, so it shares
all the methods and attributes of that class. The CircleTool
class has the following addtional methods:
{set/get}Center(): Set/Get the center point location of the circle.
{set/get}Radius(): Set/Get the radius of the circle.
"""
def __init__(self):
super(CircleTool, self).__init__()
self.__center = None
self.__radius = None
def setCenter(self, x, y):
"""Set the center point location of the circle.
setCenter(x, y)
The arguments 'x' and 'y' give the location for the center
of the circle.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__center = (_x, _y)
def getCenter(self):
"""Get the center point location of the circle.
getCenter()
This method returns the coordinates stored with the setCenter()
method, or None if that method has not been called.
"""
return self.__center
def setRadius(self, radius):
"""Set the radius of the circle.
setRadius(radius)
The argument 'radius' must be a float value greater than 0.0
"""
_r = util.get_float(radius)
if not _r > 0.0:
raise ValueError, "Invalid radius: %g" % _r
self.__radius = _r
def getRadius(self):
"""Get the radius of the circle.
getRadius()
This method returns the value specified from the setRadius()
call, or None if that method has not been invoked.
"""
return self.__radius
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends Tool::reset().
"""
super(CircleTool, self).reset()
self.__center = None
self.__radius = None
def create(self, image):
"""Create a new Circle and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
if (self.__center is not None and
self.__radius is not None):
_active_layer = image.getActiveLayer()
_x, _y = self.__center
_r = self.__radius
_pts = _active_layer.find('point', _x, _y)
if len(_pts) == 0:
_cp = Point(_x, _y)
_active_layer.addObject(_cp)
else:
_cp = _pts[0]
_s = image.getOption('LINE_STYLE')
_circle = Circle(_cp, _r, _s)
_l = image.getOption('LINE_TYPE')
if _l != _s.getLinetype():
_circle.setLinetype(_l)
_c = image.getOption('LINE_COLOR')
if _c != _s.getColor():
_circle.setColor(_c)
_t = image.getOption('LINE_THICKNESS')
if abs(_t - _s.getThickness()) > 1e-10:
_circle.setThickness(_t)
_active_layer.addObject(_circle)
self.reset()
class TwoPointCircleTool(CircleTool):
"""A specialized class for drawing Circles between two points.
The TwoPointCircleTool class is derived from the CircleTool
class, so it shares all the methods and attributes of that
class. The TwoPointCircleTool class has the following addtional
methods:
{set/get}FirstPoint(): Set/Get the first point used to define the circle.
{set/get}SecondPoint(): Set/Get the second point used to define the circle.
"""
def __init__(self):
super(TwoPointCircleTool, self).__init__()
self.__first_point = None
self.__second_point = None
def setFirstPoint(self, x, y):
"""Set the first point used to define the location of the circle.
setFirstPoint(x, y)
Arguments 'x' and 'y' give the location of a point.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__first_point = (_x, _y)
def getFirstPoint(self):
"""Get the first point used to define the location of the circle.
getFirstPoint()
This method returns a tuple holding the values used when the
setFirstPoint() method was called, or None if that method has
not yet been used.
"""
return self.__first_point
def setSecondPoint(self, x, y):
"""Set the second point used to define the location of the circle.
setSecondPoint(x, y)
Arguments 'x' and 'y' give the location of a point. Invoking
this method before the setFirstPoint() method will raise a
ValueError.
"""
if self.__first_point is None:
raise ValueError, "First point is not set"
_x = util.get_float(x)
_y = util.get_float(y)
_x1, _y1 = self.__first_point
_xc = (_x + _x1)/2.0
_yc = (_y + _y1)/2.0
_radius = math.hypot((_x - _x1), (_y - _y1))/2.0
self.setCenter(_xc, _yc)
self.setRadius(_radius)
self.__second_point = (_x, _y)
def getSecondPoint(self):
"""Get the second point used to define the location of the circle.
getSecondPoint()
This method returns a tuple holding the values used when the
setSecondPoint() method was called, or None if that method has
not yet been used.
"""
return self.__second_point
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends CircleTool::reset().
"""
super(TwoPointCircleTool, self).reset()
self.__first_point = None
self.__second_point = None
class ArcTool(CircleTool):
"""A specialized tool for drawing Arc objects.
The ArcTool is Derived from the CircleTool class, so it shares
all the attributes and methods of that class. The ArcTool class
has the following addtional methods:
{set/get}StartAngle(): Set/Get the start angle of the arc
{set/get}EndAngle(): Set/Get the end angle of the arc.
"""
def __init__(self):
super(ArcTool, self).__init__()
self.__start_angle = None
self.__end_angle = None
def setStartAngle(self, angle):
"""Set the start angle of the arc.
setStartAngle(angle)
The argument 'angle' should be a float value between 0.0 and 360.0
"""
_angle = util.make_c_angle(angle)
self.__start_angle = _angle
def getStartAngle(self):
"""Return the start angle of the arc.
getStartAngle()
This method returns the value defined in the previous setStartAngle()
call, or None if that method has not been called.
"""
return self.__start_angle
def setEndAngle(self, angle):
"""Set the start angle of the arc.
setStartAngle(angle)
The argument 'angle' should be a float value between 0.0 and 360.0
"""
_angle = util.make_c_angle(angle)
self.__end_angle = _angle
def getEndAngle(self):
"""Return the end angle of the arc.
getEndAngle()
This method returns the value defined in the previous setEndAngle()
call, or None if that method has not been called.
"""
return self.__end_angle
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends CircleTool::reset().
"""
super(ArcTool, self).reset()
self.__start_angle = None
self.__end_angle = None
def create(self, image):
"""Create a new Arc and add it to the image.
create(image)
This method overrides the CircleTool::create() method.
"""
_center = self.getCenter()
_radius = self.getRadius()
_sa = self.__start_angle
_ea = self.__end_angle
if (_center is not None and
_radius is not None and
_sa is not None and
_ea is not None):
_active_layer = image.getActiveLayer()
_x, _y = _center
_pts = _active_layer.find('point', _x, _y)
if len(_pts) == 0:
_cp = Point(_x, _y)
_active_layer.addObject(_cp)
else:
_cp = _pts[0]
_s = image.getOption('LINE_STYLE')
_arc = Arc(_cp, _radius, _sa, _ea, _s)
_l = image.getOption('LINE_TYPE')
if _l != _s.getLinetype():
_arc.setLinetype(_l)
_c = image.getOption('LINE_COLOR')
if _c != _s.getColor():
_arc.setColor(_c)
_t = image.getOption('LINE_THICKNESS')
if abs(_t - _s.getThickness()) > 1e-10:
_arc.setThickness(_t)
for _ep in _arc.getEndpoints():
_ex, _ey = _ep
_pts = _active_layer.find('point', _ex, _ey)
if len(_pts) == 0:
_lp = Point(_ex, _ey)
_active_layer.addObject(_lp)
_active_layer.addObject(_arc)
self.reset()
class LeaderTool(Tool):
"""A specialized tool for drawing Leader objects.
The LeaderTool class is derived from the Tool class, so it
shares the methods and attributes of that class. The LeaderTool
class has the following addtional methods:
{set/get}FirstPoint(): Set/Get the first point of the Leader.
{set/get}MidPoint(): Set/Get the second point of the Leader.
{set/get}FinalPoint(): Set/Get the final point of the Leader.
"""
def __init__(self):
super(LeaderTool, self).__init__()
self.__start_point = None
self.__mid_point = None
self.__end_point = None
def setFirstPoint(self, x, y):
"""Set the first point used to define the Leader.
setFirstPoint(x, y)
Arguments 'x' and 'y' give the location of a point.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__start_point = (_x, _y)
def getFirstPoint(self):
"""Get the first point used to define the Leader.
getFirstPoint()
This method returns a tuple holding the values used when the
setFirstPoint() method was called, or None if that method has
not yet been used.
"""
return self.__start_point
def setMidPoint(self, x, y):
"""Set the second point used to define the Leader.
setMidPoint(x, y)
Arguments 'x' and 'y' give the location of a point. If the
first point has not been set this method raises a ValueError.
"""
if self.__start_point is None:
raise ValueError, "First point not set in LeaderTool."
_x = util.get_float(x)
_y = util.get_float(y)
self.__mid_point = (_x, _y)
def getMidPoint(self):
"""Get the second point used to define the Leader.
getMidPoint()
This method returns a tuple holding the values used when the
setMidPoint() method was called, or None if that method has
not yet been used.
"""
return self.__mid_point
def setFinalPoint(self, x, y):
"""Set the first point used to final point of the Leader.
setFinalPoint(x, y)
Arguments 'x' and 'y' give the location of a point. This method
raises an error if the first point or second point have not been
set.
"""
if self.__start_point is None:
raise ValueError, "First point not set in LeaderTool."
if self.__mid_point is None:
raise ValueError, "Second point not set in LeaderTool."
_x = util.get_float(x)
_y = util.get_float(y)
self.__end_point = (_x, _y)
def getFinalPoint(self):
"""Get the third point used to define the Leader.
getFinalPoint()
This method returns a tuple holding the values used when the
setFinalPoint() method was called, or None if that method has
not yet been used.
"""
return self.__end_point
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends Tool::reset().
"""
super(LeaderTool, self).reset()
self.__start_point = None
self.__mid_point = None
self.__end_point = None
def create(self, image):
"""Create a new Leader and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
if (self.__start_point is not None and
self.__mid_point is not None and
self.__end_point is not None):
_active_layer = image.getActiveLayer()
_x1, _y1 = self.__start_point
_x2, _y2 = self.__mid_point
_x3, _y3 = self.__end_point
_pts = _active_layer.find('point', _x1, _y1)
if len(_pts) == 0:
_p1 = Point(_x1, _y1)
_active_layer.addObject(_p1)
else:
_p1 = _pts[0]
_pts = _active_layer.find('point', _x2, _y2)
if len(_pts) == 0:
_p2 = Point(_x2, _y2)
_active_layer.addObject(_p2)
else:
_p2 = _pts[0]
_pts = _active_layer.find('point', _x3, _y3)
if len(_pts) == 0:
_p3 = Point(_x3, _y3)
_active_layer.addObject(_p3)
else:
_p3 = _pts[0]
_size = image.getOption('LEADER_ARROW_SIZE')
_s = image.getOption('LINE_STYLE')
_leader = Leader(_p1, _p2, _p3, _size, _s)
_l = image.getOption('LINE_TYPE')
if _l != _s.getLinetype():
_leader.setLinetype(_l)
_c = image.getOption('LINE_COLOR')
if _c != _s.getColor():
_leader.setColor(_c)
_t = image.getOption('LINE_THICKNESS')
if abs(_t - _s.getThickness()) > 1e-10:
_leader.setThickness(_t)
_active_layer.addObject(_leader)
self.reset()
class PolylineTool(Tool):
"""A specialized tool for drawing Polyline objects.
The PolylineTool class is derived from the Tool class, so it
shares all the attributes and methods of that class. The PolylineTool
class has the following addtional methods:
storePoint(): Store a point used to define the Polyline.
getPoint(): Retrieve a point used to define the Polyline.
getLastPoint(): Retrieve the last point used to define the Polyline.
getPoints(): Get the list of points that define the Polyline.
"""
def __init__(self):
super(PolylineTool, self).__init__()
self.__points = []
def __len__(self):
return len(self.__points)
def storePoint(self, x, y):
"""Store a point that will define a Polyline.
storePoint(x, y)
The arguments 'x' and 'y' should be float values. There is
no limit as to how long a Polyline should be, so each invocation
of this method appends the values to the list of stored points.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__points.append((_x, _y))
def getPoint(self, i):
"""Retrieve a point used to define a Polyline.
getPoint(i)
Argument 'i' represents the index in the list of points that
defines the polyline. Negative indicies will get points from
last-to-first. Using an invalid index will raise an error.
This method returns a tuple holding the x/y coordinates.
"""
return self.__points[i]
def getPoints(self):
"""Get all the points that define the Polyline.
getPoints()
This method returns a list of tuples holding the x/y coordinates
of all the points that define the Polyline.
"""
return self.__points[:]
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends Tool::reset().
"""
super(PolylineTool, self).reset()
del self.__points[:]
def create(self, image):
"""Create a new Polyline and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
if len(self.__points):
_pts = []
_active_layer = image.getActiveLayer()
for _pt in self.__points:
_x, _y = _pt
_lpts = _active_layer.find('point', _x, _y)
if len(_lpts) == 0:
_p = Point(_x, _y)
_active_layer.addObject(_p)
_pts.append(_p)
else:
_pts.append(_lpts[0])
_s = image.getOption('LINE_STYLE')
_pline = Polyline(_pts, _s)
_l = image.getOption('LINE_TYPE')
if _l != _s.getLinetype():
_pline.setLinetype(_l)
_c = image.getOption('LINE_COLOR')
if _c != _s.getColor():
_pline.setColor(_c)
_t = image.getOption('LINE_THICKNESS')
if abs(_t - _s.getThickness()) > 1e-10:
_pline.setThickness(_t)
_active_layer.addObject(_pline)
self.reset()
class PolygonTool(Tool):
"""A specialized to for creating Polygons from Segments.
The PolygonTool will create an uniformly sized polygon from Segment
entities. The minimum number of sides is three, creating an equilateral
triangle. There is no maximum number of sides, though realistically any
polygon with more than 20 or so sides is unlikely to be drawn. As
the PolygonTool is derived from the Tool class, it shares all the attributes
and method of that class. The PolygonTool has the following additional
methods:
{get/set}SideCount(): Get/Set the number of sides in the polygon.
{get/set}External() Get/Set if the polygon is drawn inside or outside a circle.
{get/set}Center(): Get/Set the center location of the polygon.
getCoords(): Get the coordinates of the polygon corners.
"""
def __init__(self):
super(PolygonTool, self).__init__()
self.__nsides = None
self.__increment = None
self.__external = False
self.__center = None
self.__xpts = array.array("d")
self.__ypts = array.array("d")
def setSideCount(self, count):
"""Set the number of sides of the polygon to create.
setSideCount(count)
Argument "count" should be an integer value greater than 2.
"""
_count = count
if not isinstance(_count, int):
_count = int(count)
if _count < 3:
raise ValueError, "Invalid count: %d" % _count
self.__nsides = _count
self.__increment = (360.0/float(_count)) * (math.pi/180.0)
for _i in range(_count):
self.__xpts.insert(_i, 0.0)
self.__ypts.insert(_i, 0.0)
def getSideCount(self):
"""Get the number of sides of the polygon to be created.
getSideCount()
A ValueError exception is raised if the side count has not been
set with setSideCount()
"""
if self.__nsides is None:
raise ValueError, "No side count defined."
return self.__nsides
def setExternal(self):
"""Create the polygon on the outside of a reference circle.
setExternal()
By default the polygon is drawing completely contained within a
circle. Invoking this method will created the polygon so that all
sides are outside the circle.
"""
self.__external = True
def getExternal(self):
"""Test if the polygon will be created outside a circle.
getExternal()
If the setExternal() method has been called, this method will
return True. By default this method will return False.
"""
return self.__external
def setCenter(self, x, y):
"""Define the center of the polygon.
setCenter(x, y)
Arguments 'x' and 'y' should be float values.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__center = (_x, _y)
def getCenter(self):
"""Retrieve the center of the polygon to be created.
getCenter()
This method returns a tuple holding two float values containing
the 'x' and 'y' coordinates of the polygon center. A ValueError
is raised if the center has not been set with a prior call to setCenter().
"""
if self.__center is None:
raise ValueError, "Center is undefined."
return self.__center
def getCoord(self, i):
"""Get one of the coordinates of the polygon corners.
getCoord(i)
Argument "i" should be an integer value such that:
0 <= i <= number of polygon sides
"""
_x = self.__xpts[i]
_y = self.__ypts[i]
return _x, _y
def setLocation(self, x, y):
"""Set the tool location.
setLocation(x, y)
This method extends Tool::setLocation() and calculates the polygon
points.
"""
super(PolygonTool, self).setLocation(x, y)
_x, _y = self.getLocation()
_count = self.__nsides
_inc = self.__increment
if self.__external:
_offset = _inc/2.0
else:
_offset = 0.0
_cx, _cy = self.__center
_xsep = _x - _cx
_ysep = _y - _cy
_angle = math.atan2(_ysep, _xsep) + _offset
_rad = math.hypot(_xsep, _ysep)/math.cos(_offset)
_xp = self.__xpts
_yp = self.__ypts
for _i in range(_count):
_xp[_i] = _cx + (_rad * math.cos(_angle))
_yp[_i] = _cy + (_rad * math.sin(_angle))
_angle = _angle + _inc
def create(self, image):
"""Create a Polygon from Segments and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
if len(self.__xpts):
_active_layer = image.getActiveLayer()
_s = image.getOption('LINE_STYLE')
_l = image.getOption('LINE_TYPE')
if _l == _s.getLinetype():
_l = None
_c = image.getOption('LINE_COLOR')
if _c == _s.getColor():
_c = None
_t = image.getOption('LINE_THICKNESS')
if abs(_t - _s.getThickness()) < 1e-10:
_t = None
_count = self.__nsides
_xp = self.__xpts
_yp = self.__ypts
_x = _xp[0]
_y = _yp[0]
#
# find starting point ...
#
_pts = _active_layer.find('point', _x, _y)
if len(_pts) == 0:
_p0 = Point(_x, _y)
_active_layer.addObject(_p0)
else:
_p0 = _pts[0]
#
# make segments for all the points ...
#
_p1 = _p0
for _i in range(1, _count):
_x = _xp[_i]
_y = _yp[_i]
_pts = _active_layer.find('point', _x, _y)
if len(_pts) == 0:
_pi = Point(_x, _y)
_active_layer.addObject(_pi)
else:
_pi = _pts[0]
_seg = Segment(_p1, _pi, _s, linetype=_l, color=_c, thickness=_t)
_active_layer.addObject(_seg)
_p1 = _pi
#
# now add closing segment ...
#
_seg = Segment(_p1, _p0, _s, linetype=_l, color=_c, thickness=_t)
_active_layer.addObject(_seg)
self.reset()
def reset(self):
"""Restore the PolygonTool to its original state.
reset()
This method extends Tool::reset()
"""
super(PolygonTool, self).reset()
# self.__nsides = None
# self.__increment = None
# self.__external = False # make this adjustable?
self.__center = None
for _i in range(self.__nsides):
self.__xpts[_i] = 0.0
self.__ypts[_i] = 0.0
class HCLineTool(PointTool):
"""A specialized tool for drawing HCLine objects.
The HCLineTool class is derived from the PointTool class
so it shares all the attributes and methods of that class.
There are no additional methods for this class.
"""
def __init__(self):
super(HCLineTool, self).__init__()
def create(self, image):
"""Create a new HCLine and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
_p = self.getPoint()
if _p is not None:
_active_layer = image.getActiveLayer()
_x, _y = _p
_pts = _active_layer.find('point', _x, _y)
if len(_pts) == 0:
_pt = Point(_x, _y)
_active_layer.addObject(_pt)
else:
_pt = _pts[0]
_hcl = HCLine(_pt)
_active_layer.addObject(_hcl)
self.reset()
class VCLineTool(PointTool):
"""A specialized tool for drawing VCLine objects.
The VCLineTool class is derived from the PointTool class
so it shares all the attributes and methods of that class.
There are no additional methods for this class.
"""
def __init__(self):
super(VCLineTool, self).__init__()
def create(self, image):
"""Create a new VCLine and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
_p = self.getPoint()
if _p is not None:
_active_layer = image.getActiveLayer()
_x, _y = _p
_pts = _active_layer.find('point', _x, _y)
if len(_pts) == 0:
_pt = Point(_x, _y)
_active_layer.addObject(_pt)
else:
_pt = _pts[0]
_vcl = VCLine(_pt)
_active_layer.addObject(_vcl)
self.reset()
class ACLineTool(PointTool):
"""A specialized tool for drawing ACLine objects.
The ACLineTool class is derived from the PointTool class
so it shares all the attributes and methods of that class.
The ACLineTool class has the following addtional methods:
{set/get}Angle(): Set/Get the angle of the ACLine.
"""
def __init__(self):
super(ACLineTool, self).__init__()
self.__angle = None
def setLocation(self, x, y):
"""Set the location of the Tool.
setLocation(x, y)
This method extends the Tool::setLocation() method.
"""
super(ACLineTool, self).setLocation(x, y)
_loc = self.getLocation()
if _loc is None:
return
_x, _y = _loc
_x1, _y1 = self.getPoint()
if abs(_y - _y1) < 1e-10: # horizontal
self.__angle = 0.0
elif abs(_x - _x1) < 1e-10: # vertical
self.__angle = 90.0
else:
_slope = 180.0/math.pi * math.atan2((_y - _y1), (_x - _x1))
self.__angle = util.make_angle(_slope)
def setAngle(self, angle):
"""Set the angle for the ACLine.
setAngle(angle)
The argument 'angle' should be a float where -90.0 < angle < 90.0
"""
_angle = util.make_angle(angle)
self.__angle = _angle
def getAngle(self):
"""Get the angle for the ACLine.
getAngle()
This method returns a float.
"""
return self.__angle
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends PointTool::reset().
"""
super(ACLineTool, self).reset()
self.__angle = None
def create(self, image):
"""Create a new ACLine and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
_p = self.getPoint()
if (_p is not None and
self.__angle is not None):
_active_layer = image.getActiveLayer()
_x, _y = _p
_pts = _active_layer.find('point', _x, _y)
if len(_pts) == 0:
_pt = Point(_x, _y)
_active_layer.addObject(_pt)
else:
_pt = _pts[0]
_acl = ACLine(_pt, self.__angle)
_active_layer.addObject(_acl)
self.reset()
class CLineTool(SegmentTool):
"""A specialized tool for drawing CLine objects.
The CLineTool class is derived from the SegmentTool class,
so it shares all the attributes and methods of that class.
There are no extra methods for the CLineTool class.
"""
def __init__(self):
super(CLineTool, self).__init__()
def create(self, image):
"""Create a new CLine and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
_p1 = self.getFirstPoint()
_p2 = self.getSecondPoint()
if _p1 is not None and _p2 is not None:
_active_layer = image.getActiveLayer()
_x1, _y1 = _p1
_x2, _y2 = _p2
_pts = _active_layer.find('point', _x1, _y1)
if len(_pts) == 0:
_p1 = Point(_x1, _y1)
_active_layer.addObject(_p1)
else:
_p1 = _pts[0]
_pts = _active_layer.find('point', _x2, _y2)
if len(_pts) == 0:
_p2 = Point(_x2, _y2)
_active_layer.addObject(_p2)
else:
_p2 = _pts[0]
_cline = CLine(_p1, _p2)
_active_layer.addObject(_cline)
self.reset()
class CCircleTool(CircleTool):
"""A specialized tool for drawing CCircle objects.
The CCircleTool class is derived from the CircleTool class,
so it shares all the attributes and methods of that class.
There are no additional methods for the CCircleTool class.
"""
def __init__(self):
super(CCircleTool, self).__init__()
def create(self, image):
"""Create a new CCircle and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
_active_layer = image.getActiveLayer()
_x, _y = self.getCenter()
_radius = self.getRadius()
_pts = _active_layer.find('point', _x, _y)
if len(_pts) == 0:
_cp = Point(_x, _y)
_active_layer.addObject(_cp)
else:
_cp = _pts[0]
_ccircle = CCircle(_cp, _radius)
_active_layer.addObject(_ccircle)
self.reset()
class TwoPointCCircleTool(TwoPointCircleTool):
"""A specialized tool for drawing CCircle objects between two points.
The TwoPointCCircleTool class is derived from the TwoPointCircleTool
class, so it shares all the attributes and methods of that class.
There are no additional methods for the TwoPointCCircleTool class.
"""
def __init__(self):
super(TwoPointCCircleTool, self).__init__()
def create(self, image):
"""Create a new CCircle and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
_center = self.getCenter()
_radius = self.getRadius()
if _center is not None and _radius is not None:
_active_layer = image.getActiveLayer()
_x, _y = _center
_pts = _active_layer.find('point', _x, _y)
if len(_pts) == 0:
_cp = Point(_x, _y)
_active_layer.addObject(_cp)
else:
_cp = _pts[0]
_ccircle = CCircle(_cp, _radius)
_active_layer.addObject(_ccircle)
self.reset()
class ParallelOffsetTool(Tool):
"""A specialized tool for creating parallel construction lines.
The ParallelOffsetTool will create a construction line parallel
to another construction line a fixed distance from the original
construction line. The type of the new construction line will match
that of the original.
The ParallelOffsetTool is derived from the Tool class, so it shares
all the attributes and methods of that class. The ParallelOffsetTool
has the following addtional methods:
{set/get}Offset(): Set/Get the distance between the construction lines.
{set/get}ConstructionLine(): Set/Get the original construction line
{set/get}ReferencePoint(): Set/Get the point to define where the new
construction line will go.
"""
def __init__(self):
super(ParallelOffsetTool, self).__init__()
self.__refpt = None
self.__offset = None
self.__conline = None
def setOffset(self, offset):
"""Store the displacement in the tool.
setOffset(offset)
Argument 'offset' must be a float.
"""
_offset = util.get_float(offset)
self.__offset = _offset
def getOffset(self):
"""Return the stored offset from the tool.
getOffset()
This method will raise a ValueError exception if the offset has
not been set with setOffset()
"""
_offset = self.__offset
if _offset is None:
raise ValueError, "Offset is not defined."
return _offset
def setConstructionLine(self, conline):
"""Store the reference construction line in the tool.
setConstructionLine(conline)
Argument 'conline' must be a VCLine, HCLine, ACLine, or CLine object.
"""
if not isinstance(conline, (HCLine, VCLine, ACLine, CLine)):
raise TypeError, "Invalid Construction line: " + `type(conline)`
self.__conline = conline
def getConstructionLine(self):
"""Retrieve the stored construction line from the tool.
getConstructionLine()
A ValueError exception is raised if the construction line has not been
set with the setConstructionLine() method.
"""
_conline = self.__conline
if _conline is None:
raise ValueError, "Construction line is not defined."
return _conline
def setReferencePoint(self, x, y):
"""Store the reference point for positioning the new construction line.
setReferencePoint(x, y)
Arguments 'x' and 'y' give the coordinates of a reference point
used to determine where the new construction line will be placed.
Both arguments should be floats.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__refpt = (_x, _y)
def getReferencePoint(self):
"""Retreive the reference point from the tool.
getReferencePoint()
This method returns a tuple containing the values stored from
the setReferencePoint() call. This method will raise a ValueError
exception if the reference point has not been set.
"""
_refpt = self.__refpt
if _refpt is None:
raise ValueError, "No reference point defined."
return _refpt
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends Tool::reset().
"""
super(ParallelOffsetTool, self).reset()
self.__refpt = None
self.__offset = None
self.__conline = None
def create(self, image):
"""Create a parallel construction line in an image.
create(image)
This method overrides the Tool::create() method.
"""
_offset = self.__offset
_conline = self.__conline
_refpt = self.__refpt
if (_offset is not None and
_conline is not None and
_refpt is not None):
_active_layer = image.getActiveLayer()
_rx, _ry = _refpt
_lp1 = _lp2 = _ncl = None
if isinstance(_conline, HCLine):
_x, _y = _conline.getLocation().getCoords()
if _ry > _y:
_yn = _y + _offset
else:
_yn = _y - _offset
if len(_active_layer.find('hcline', _yn)) == 0:
_pts = _active_layer.find('point', _x, _yn)
if len(_pts) == 0:
_lp1 = Point(_x, _yn)
else:
_lp1 = _pts[0]
_ncl = HCLine(_lp1)
elif isinstance(_conline, VCLine):
_x, _y = _conline.getLocation().getCoords()
if _rx > _x:
_xn = _x + _offset
else:
_xn = _x - _offset
if len(_active_layer.find('vcline', _xn)) == 0:
_pts = _active_layer.find('point', _xn, _y)
if len(_pts) == 0:
_lp1 = Point(_xn, _y)
else:
_lp1 = _pts[0]
_ncl = VCLine(_lp1)
elif isinstance(_conline, ACLine):
_x, _y = _conline.getLocation().getCoords()
_angle = _conline.getAngle()
if abs(_angle) < 1e-10: # horizontal
_dx = 0.0
_dy = _offset
elif abs(abs(_angle) - 90.0) < 1e-10: # vertical
_dx = _offset
_dy = 0.0
else:
_slope = math.tan(_angle * (math.pi/180.0))
_yint = _y - (_slope * _x)
#
# p1 => (_x, _y)
# p2 => (0, _yint)
#
# redefine angle from p1 to p2 ...
_angle = math.atan2((_yint - _y), -_x)
if _angle < 0.0:
_angle = _angle + (2.0 * math.pi)
_sqlen = math.hypot(_x, (_y - _yint))
_sn = ((_y - _ry) * (0.0 - _x)) - ((_x - _rx) * (_yint - _y))
_s = _sn/_sqlen
if _s < 0.0:
_perp = _angle + (math.pi/2.0)
else:
_perp = _angle - (math.pi/2.0)
_dx = _offset * math.cos(_perp)
_dy = _offset * math.sin(_perp)
_angle = _conline.getAngle() # reset variable
_xn = _x + _dx
_yn = _y + _dy
if len(_active_layer.find('acline', _xn, _yn, _angle)) == 0:
_pts = _active_layer.find('point', _xn, _yn)
if len(_pts) == 0:
_lp1 = Point(_xn, _yn)
else:
_lp1 = _pts[0]
_ncl = ACLine(_lp1, _angle)
elif isinstance(_conline, CLine):
_p1, _p2 = _conline.getKeypoints()
_x1, _y1 = _p1.getCoords()
_x2, _y2 = _p2.getCoords()
if abs(_x2 - _x1) < 1e-10: # vertical
_dx = _offset
_dy = 0.0
elif abs(_y2 - _y1) < 1e-10: # horizontal
_dx = 0.0
_dy = _offset
else:
_angle = math.atan2((_y2 - _y1), (_x2 - _x1))
if _angle < 0.0:
_angle = _angle + (2.0 * math.pi)
_sqlen = math.hypot((_x2 - _x1), (_y2 - _y1))
_sn = ((_y1 - _ry) * (_x2 - _x1)) - ((_x1 - _rx) * (_y2 - _y1))
_s = _sn/_sqlen
if _s < 0.0:
_perp = _angle + (math.pi/2.0)
else:
_perp = _angle - (math.pi/2.0)
_dx = math.cos(_perp) * _offset
_dy = math.sin(_perp) * _offset
_x1n = _x1 + _dx
_y1n = _y1 + _dy
_x2n = _x2 + _dx
_y2n = _y2 + _dy
if len(_active_layer.find('cline', _x1n, _y1n, _x2n, _y2n)) == 0:
_pts = _active_layer.find('point', _x1n, _y1n)
if len(_pts) == 0:
_lp1 = Point(_x1n, _y1n)
else:
_lp1 = _pts[0]
_pts = _active_layer.find('point', _x2n, _y2n)
if len(_pts) == 0:
_lp2 = Point(_x2n, _y2n)
else:
_lp2 = _pts[0]
_ncl = CLine(_lp1, _lp2)
else:
raise TypeError, "Invalid Construction line type: " + `type(_conline)`
if _ncl is not None:
if _lp1 is not None and _lp1.getParent() is None:
_active_layer.addObject(_lp1)
if _lp2 is not None and _lp2.getParent() is None:
_active_layer.addObject(_lp2)
_active_layer.addObject(_ncl)
self.reset()
class TangentCircleTool(Tool):
"""A specialized class for creating tangent construction circles.
This class is meant to be a base class for tools that create tangent
construction circles. It is derived from the tool class so it shares
all the attributes and methods of that class. This class has the
following additional methods:
{set/get}Center(): Set/Get the center of the tangent circle.
{set/get}Radius(): Set/Get the radius of the tangent circle.
{set/get}PixelRect(): Set/Get the screen rectangle for drawing the circle.
"""
def __init__(self):
super(TangentCircleTool, self).__init__()
self.__center = None
self.__radius = None
self.__rect = None
def setCenter(self, x, y):
"""Store the tangent circle center point in the tool.
setCenter(x, y)
Arguments 'x' and 'y' should be floats.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__center = (_x, _y)
def getCenter(self):
"""Return the center of the tangent circle.
getCenter()
This method returns a tuple holding two floats, the first
is the 'x' coordinate of the center, the second is the 'y'
coordinate. If the tool has not yet been invoked with a
setLocation() call, this method returns None.
"""
return self.__center
def setRadius(self, radius):
"""Store the radius in the tool.
setRadius(radius)
Argument 'radius' should be a float.
"""
_radius = util.get_float(radius)
self.__radius = _radius
def getRadius(self):
"""Return the center of the tangent circle.
getRadius()
This method returns a float giving the radius of the tangent
circle, or None if the radius is not set.
"""
return self.__radius
def setPixelRect(self, xmin, ymin, width, height):
"""Store the screen coordinates used to draw the circle.
setPixelRect(xmin, ymin, width, height)
All the arguments should be integer values.
"""
_xmin = xmin
if not isinstance(_xmin, int):
_xmin = int(xmin)
_ymin = ymin
if not isinstance(_ymin, int):
_ymin = int(ymin)
_width = width
if not isinstance(_width, int):
_width = int(_width)
_height = height
if not isinstance(_height, int):
_height = int(height)
self.__rect = (_xmin, _ymin, _width, _height)
def getPixelRect(self):
"""Return the screen boundary of the circle to draw
getPixelRect(self)
This method will return a tuple holding four integer values:
xmin, ymin, width, height
If the rectangle has not been set by calling setPixelRect(), then
this method will return None.
"""
return self.__rect
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends Tool::reset().
"""
super(TangentCircleTool, self).reset()
self.__center = None
self.__radius = None
self.__rect = None
def create(self, image):
"""Create a new CCircle and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
_active_layer = image.getActiveLayer()
_x, _y = self.__center
_radius = self.__radius
_pts = _active_layer.find('point', _x, _y)
if len(_pts) == 0:
_cp = Point(_x, _y)
_active_layer.addObject(_cp)
else:
_cp = _pts[0]
_ccircle = CCircle(_cp, _radius)
_active_layer.addObject(_ccircle)
self.reset()
class TangentCCircleTool(TangentCircleTool):
"""A specialized tool for creating tangent construction circles.
The TangentCCircleTool will create a construction circle tangent
to a construction line or a construction circle.
The TangentCCircleTool is derived from the TangentCircleTool class, so
it shares all the attributes and methods of that class. The TangentCCircleTool
has the following addtional methods:
{set/get}ConstructionObject(): Set/Get the reference construction object.
"""
def __init__(self):
super(TangentCCircleTool, self).__init__()
self.__conobj = None
def setConstructionLine(self, conobj):
"""Store the reference construction object in the tool.
setConstructionLine(conobj)
Argument 'conobj' must be a VCLine, HCLine, ACLine, CLine,
or CCircle object.
"""
if not isinstance(conobj, (HCLine, VCLine, ACLine, CLine, CCircle)):
raise TypeError, "Invalid Construction entity type: " + `type(conobj)`
self.__conobj = conobj
def getConstructionLine(self):
"""Retrieve the stored construction line from the tool.
getConstructionLine()
A ValueError exception is raised if the construction line has not been
set with the setConstructionLine() method.
"""
_conobj = self.__conobj
if _conobj is None:
raise ValueError, "Construction object is not defined."
return _conobj
def setLocation(self, x, y):
"""Store an x/y coordinate pair in the tool.
setLocation(x, y)
Arguments 'x' and 'y' should be floats. This method extends
the TangentCircleTool::setLocation() methods.
"""
super(TangentCCircleTool, self).setLocation(x, y)
_tx, _ty = self.getLocation()
_conobj = self.__conobj
_cx = _cy = _radius = None
if isinstance(_conobj, HCLine):
_x, _y = _conobj.getLocation().getCoords()
_cx = _tx
_cy = (_ty + _y)/2.0
_radius = abs(_ty - _y)/2.0
elif isinstance(_conobj, VCLine):
_x, _y = _conobj.getLocation().getCoords()
_cx = (_tx + _x)/2.0
_cy = _ty
_radius = abs(_tx - _x)/2.0
elif isinstance(_conobj, (ACLine, CLine)):
_px, _py = _conobj.getProjection(_tx, _ty)
_cx = (_tx + _px)/2.0
_cy = (_ty + _py)/2.0
_radius = math.hypot((_tx - _px), (_ty - _py))/2.0
elif isinstance(_conobj, CCircle):
_ccx, _ccy = _conobj.getCenter().getCoords()
_rad = _conobj.getRadius()
_sep = math.hypot((_tx - _ccx), (_ty - _ccy))
if _sep < 1e-10:
return
_angle = math.atan2((_ty - _ccy), (_tx - _ccx))
_px = _ccx + (_rad * math.cos(_angle))
_py = _ccy + (_rad * math.sin(_angle))
_cx = (_tx + _px)/2.0
_cy = (_ty + _py)/2.0
_radius = math.hypot((_tx - _px), (_ty - _py))/2.0
else:
raise TypeError, "Invalid construction entity type: " + `type(_conobj)`
self.setCenter(_cx, _cy)
self.setRadius(_radius)
class TwoPointTangentCCircleTool(TangentCircleTool):
"""A specialized tool for creating tangent construction circles.
The TwoPointTangentCCircleTool will create a construction circle tangent
to two construction lines or a construction line and a construction
circle if such a tangent circle can be created.
The TwoPointTangentCCircleTool is derived from the TangentCircleTool
class, so it shares all the attributes and methods of that class. This
class also has the following addtional methods:
{set/get}FirstConObject(): Set/Get the first construction object.
{set/get}SecondConObject(): Set/Get the second constuction object.
"""
def __init__(self):
super(TwoPointTangentCCircleTool, self).__init__()
self.__cobj1 = None
self.__cobj2 = None
def setFirstConObject(self, conobj):
"""Store the first reference construction object in the tool.
setFirstConObject(conobj)
Argument 'conobj' must be a VCLine, HCLine, ACLine, CLine, or CCircle object.
"""
if not isinstance(conobj, (HCLine, VCLine, ACLine, CLine, CCircle)):
raise TypeError, "Invalid Construction entity type: " + `type(conobj)`
self.__cobj1 = conobj
def getFirstConObject(self):
"""Retreive the first construction object from the tool.
getFirstConObject()
"""
return self.__cobj1
def setSecondConObject(self, conobj):
"""Store the second reference construction object in the tool.
setSecondConObject(conobj)
Argument 'conobj' must be a VCLine, HCLine, ACLine, or a CLine object.
Drawing a tangent circle against two CCircle objects is not yet supported.
A ValueError exception will be raised if this method is called before the
first construction object has been set.
"""
if self.__cobj1 is None:
raise ValueError, "First construction object not set."
if not isinstance(conobj, (HCLine, VCLine, ACLine, CLine)):
raise TypeError, "Invalid Construction line type: " + `type(conobj)`
self.__cobj2 = conobj
def getSecondConObject(self):
"""Retreive the second construction object from the tool.
getSecondConObject()
"""
return self.__cobj2
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends the TangentCircleTool::reset() method.
"""
super(TwoPointTangentCCircleTool, self).reset()
self.__cobj1 = None
self.__cobj2 = None
def setLocation(self, x, y):
"""Store an x/y coordinate pair in the tool.
setLocation(x, y)
Arguments 'x' and 'y' should be floats. This method extends
the TangentCircleTool::setLocation() methods.
"""
super(TwoPointTangentCCircleTool, self).setLocation(x, y)
_tx, _ty = self.getLocation()
_obja = self.__cobj1
_objb = self.__cobj2
_tandata = tangent.calc_tangent_circle(_obja, _objb, _tx, _ty)
if _tandata is not None:
_cx, _cy, _radius = _tandata
self.setCenter(_cx, _cy)
self.setRadius(_radius)
class CCircleTangentLineTool(Tool):
"""A specialized class for creating tangent lines to construction circles.
This class is a specialized class that handles creating construction
lines around circles. There can be at most four possible tangent lines.
There are two tangent lines if the circles overlap, and no tangent
lines if one circle is inside another. As this tool is derived from
the Tool class it shares all the attributes and methods of that
class. The CCircleTangentLineTool class has the following additional
methods:
{get/set}FirstCCircle(): Get/Set the first CCircle in the tool.
{get/set}SecondCCircle(): Get/Set the second CCircle in the tool.
"""
def __init__(self):
super(CCircleTangentLineTool, self).__init__()
self.__circ1 = None
self.__circ2 = None
self.__tanpts = []
def setFirstCCircle(self, ccircle):
"""Store the first construction circle in the tool.
setFirstCCircle(ccircle)
Argument 'ccircle' must be a CCircle object.
"""
if not isinstance(ccircle, CCircle):
raise TypeError, "Invalid CCircle type: " + `type(ccircle)`
self.__circ1 = ccircle
def getFirstCCircle(self):
"""Retreive the first construction circle from the tool.
getFirstCCircle()
"""
return self.__circ1
def setSecondCCircle(self, ccircle):
"""Store the second construction circle in the tool.
setSecondCCircle(ccircle)
Argument 'ccircle' must be a CCircle object. A ValueError exception will
be raised if this method is called before the first construction circle
has been set.
"""
if self.__circ1 is None:
raise ValueError, "First construction circle not set."
if not isinstance(ccircle, CCircle):
raise TypeError, "Invalid CCircle type: " + `type(ccircle)`
self.__circ2 = ccircle
#
# calculate the tangent points if they exist
#
_cc1 = self.__circ1
_cc2 = self.__circ2
_cx1, _cy1 = _cc1.getCenter().getCoords()
_r1 = _cc1.getRadius()
_cx2, _cy2 = _cc2.getCenter().getCoords()
_r2 = _cc2.getRadius()
_sep = math.hypot((_cx2 - _cx1), (_cy2 - _cy1))
_angle = math.atan2((_cy2 - _cy1), (_cx2 - _cx1))
_sine = math.sin(_angle)
_cosine = math.cos(_angle)
#
# tangent points are calculated as if the first circle
# center is (0, 0) and both circle centers on the x-axis,
# so the points need to be transformed to the correct coordinates
#
_tanpts = self.__tanpts
del _tanpts[:] # make sure it is empty ...
_tansets = tangent.calc_two_circle_tangents(_r1, _r2, _sep)
for _set in _tansets:
_x1, _y1, _x2, _y2 = _set
_tx1 = ((_x1 * _cosine) - (_y1 * _sine)) + _cx1
_ty1 = ((_x1 * _sine) + (_y1 * _cosine)) + _cy1
_tx2 = ((_x2 * _cosine) - (_y2 * _sine)) + _cx1
_ty2 = ((_x2 * _sine) + (_y2 * _cosine)) + _cy1
_tanpts.append((_tx1, _ty1, _tx2, _ty2))
def getSecondCCircle(self):
"""Retreive the second construction circle from the tool.
getSecondCCircle()
"""
return self.__circ2
def hasTangentPoints(self):
"""Test if tangent points were found for the two circles.
hasTangentPoints()
"""
_val = False
if len(self.__tanpts):
_val = True
return _val
def getTangentPoints(self):
"""Return the tangent points calculated for two-circle tangency.
getTangentPoints()
This method returns a list of tuples holding four float values:
(x1, y1, x2, y2)
A tangent line can be drawn between the two circles from (x1, y1) to (x2, y2).
"""
return self.__tanpts[:]
def create(self, image):
"""Create the tangent line for two circles.
create(image)
"""
_x, _y = self.getLocation()
_tanpts = self.__tanpts
if not len(_tanpts):
raise ValueError, "No tangent points defined."
_minsep = _px1 = _py1 = _px2 = _py2 = None
for _set in _tanpts:
_x1, _y1, _x2, _y2 = _set
_sqlen = pow((_x2 - _x1), 2) + pow((_y2 - _y1), 2)
_rn = ((_x - _x1) * (_x2 - _x1)) + ((_y - _y1) * (_y2 - _y1))
_r = _rn/_sqlen
_px = _x1 + _r * (_x2 - _x1)
_py = _y1 + _r * (_y2 - _y1)
_sep = math.hypot((_px - _x), (_py - _y))
if _minsep is None or _sep < _minsep:
_minsep = _sep
_px1 = _x1
_py1 = _y1
_px2 = _x2
_py2 = _y2
_active_layer = image.getActiveLayer()
_pts = _active_layer.find('point', _px1, _py1)
if len(_pts) == 0:
_p1 = Point(_px1, _py1)
_active_layer.addObject(_p1)
else:
_p1 = _pts[0]
_pts = _active_layer.find('point', _px2, _py2)
if len(_pts) == 0:
_p2 = Point(_px2, _py2)
_active_layer.addObject(_p2)
else:
_p2 = _pts[0]
_active_layer.addObject(CLine(_p1, _p2))
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends Tool::reset().
"""
super(CCircleTangentLineTool, self).reset()
self.__circ1 = None
self.__circ2 = None
del self.__tanpts[:]
class DimensionTool(Tool):
"""A base class for tools creating Dimension objects.
The DimensionTool class is meant to be a base class for classes
that will create Dimension objects. The DimensionTool class is
derived from the Tool class, so it shares all the attributes and
methods of that class. The DimensionTool class has the following
additional methods:
setDimension(): Store a dimension object in the tool
getDimension(): Retrieve a stored dimension object.
setDimPrefs(): Apply the current dimension preferences on a stored dimension.
"""
def __init__(self):
super(DimensionTool, self).__init__()
self.__dim = None
def _setDimension(self, dim):
"""Store a dimension in the tool.
setDimension(dim)
The argument 'dim' must be a Dimension object.
"""
if not isinstance(dim, dimension.Dimension):
raise TypeError, "Invalid Dimension type: " + `type(dim)`
self.__dim = dim
def getDimension(self):
"""Retrieve the stored dimension object from the tool.
getDimension()
This method returns the stored Dimension or None.
"""
return self.__dim
def setDimPrefs(self, image):
"""Apply the current dimension options to the stored dimension.
setDimPrefs(image)
The argument 'image' is an image option in which the current dimension
preferences are retrieved.
"""
_dim = self.__dim
if _dim is None:
raise ValueError, "No dimension stored in tool."
_pds = _dim.getPrimaryDimstring()
_pds.mute()
_pds.setFamily(image.getOption('DIM_PRIMARY_FONT_FAMILY'))
_pds.setWeight(image.getOption('DIM_PRIMARY_FONT_WEIGHT'))
_pds.setStyle(image.getOption('DIM_PRIMARY_FONT_STYLE'))
_pds.setColor(image.getOption('DIM_PRIMARY_FONT_COLOR'))
_pds.setSize(image.getOption('DIM_PRIMARY_TEXT_SIZE'))
_pds.setAlignment(image.getOption('DIM_PRIMARY_TEXT_ALIGNMENT'))
_pds.setPrecision(image.getOption('DIM_PRIMARY_PRECISION'))
_pds.setUnits(image.getOption('DIM_PRIMARY_UNITS'))
_pds.setPrintZero(image.getOption('DIM_PRIMARY_LEADING_ZERO'))
_pds.setPrintDecimal(image.getOption('DIM_PRIMARY_TRAILING_DECIMAL'))
_pds.unmute()
_sds = _dim.getSecondaryDimstring()
_sds.mute()
_sds.setFamily(image.getOption('DIM_SECONDARY_FONT_FAMILY'))
_sds.setWeight(image.getOption('DIM_SECONDARY_FONT_WEIGHT'))
_sds.setStyle(image.getOption('DIM_SECONDARY_FONT_STYLE'))
_sds.setColor(image.getOption('DIM_SECONDARY_FONT_COLOR'))
_sds.setSize(image.getOption('DIM_SECONDARY_TEXT_SIZE'))
_sds.setAlignment(image.getOption('DIM_SECONDARY_TEXT_ALIGNMENT'))
_sds.setPrecision(image.getOption('DIM_SECONDARY_PRECISION'))
_sds.setUnits(image.getOption('DIM_SECONDARY_UNITS'))
_sds.setPrintZero(image.getOption('DIM_SECONDARY_LEADING_ZERO'))
_sds.setPrintDecimal(image.getOption('DIM_SECONDARY_TRAILING_DECIMAL'))
_sds.unmute()
_dim.setOffset(image.getOption('DIM_OFFSET'))
_dim.setExtension(image.getOption('DIM_EXTENSION'))
_dim.setColor(image.getOption('DIM_COLOR'))
_dim.setPosition(image.getOption('DIM_POSITION'))
_dim.setEndpointType(image.getOption('DIM_ENDPOINT'))
_dim.setEndpointSize(image.getOption('DIM_ENDPOINT_SIZE'))
_dim.setDualDimMode(image.getOption('DIM_DUAL_MODE'))
def reset(self):
"""Restore the tool to its initial state.
reset()
This method sets the DimensionTool dimension to None.
"""
super(DimensionTool, self).reset()
self.__dim = None
class LinearDimensionTool(DimensionTool):
"""A specialized tool for drawing LinearDimension objects.
The LinearDimensionTool is derived from the DimensionTool and
Tool, class, so it shares all the attributes and methods of those classes.
The LinearDimensionTool class has the following addtional methods:
{set/get}FirstPoint(): Set/Get the first point in the LinearDimension
{set/get}SecondPoint(): Set/Get the second point in the LinearDimension.
{set/get}DimPosition(): Set/Get the location of the dimension text.
"""
def __init__(self):
super(LinearDimensionTool, self).__init__()
self.__p1 = None
self.__p2 = None
self.__position = None
def setFirstPoint(self, p):
"""Store the first point for the LinearDimension.
setFirstPoint(p):
The argument 'p' must be a Point instance.
"""
if not isinstance (p, Point):
raise TypeError, "Invalid Point: " + `type(p)`
if p.getParent() is None:
raise ValueError, "Point not found in a Layer."
self.__p1 = p
def getFirstPoint(self):
"""Return the first point for the LinearDimension.
getFirstPoint()
This method returns a tuple of two objects: the first object
is a Layer, the second object is a Point.
"""
return self.__p1
def setSecondPoint(self, p):
"""Store the second point for the LinearDimension.
setSecondPoint(p):
The argument 'p' must be a Point instance.
"""
if self.__p1 is None:
raise ValueError, "First point not set for LinearDimension."
if not isinstance (p, Point):
raise TypeError, "Invalid Point: " + `type(p)`
if p.getParent() is None:
raise ValueError, "Point not found in a Layer."
self.__p2 = p
def getSecondPoint(self):
"""Return the second point for the LinearDimension.
getSecondPoint()
This method returns a tuple of two objects: the first object
is a Layer, the second object is a Point.
"""
return self.__p2
def setDimPosition(self, x, y):
"""Store the point where the dimension text will be located.
setDimPosition(x, y)
Arguments 'x' and 'y' should be float values.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__position = (_x, _y)
def getDimPosition(self):
"""Retrieve where the dimension text should be placed.
getDimPosition()
This method returns a tuple containing the x/y coodindates defined
by the setDimPosition() call, or None if that method has not been invoked.
"""
return self.__position
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends the reset() methods of its base classes.
"""
super(LinearDimensionTool, self).reset()
self.__p1 = None
self.__p2 = None
self.__position = None
def makeDimension(self, image):
"""Create a LinearDimension based on the stored tool values.
makeDimension(image)
The argument 'image' is an image object where the dimension will be used.
"""
_p1 = self.__p1
_p2 = self.__p2
_x, _y = self.__position
if (_p1 is not None and
_p2 is not None and
_x is not None and
_y is not None):
_ds = image.getOption('DIM_STYLE')
_ldim = dimension.LinearDimension(_p1, _p2, _x, _y, _ds)
self._setDimension(_ldim)
self.setDimPrefs(image)
def create(self, image):
"""Create a new LinearDimension and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
_ldim = self.getDimension()
if _ldim is not None:
_pds = _ldim.getPrimaryDimstring()
_pds.mute()
try:
_pds.setPrefix(image.getOption('DIM_PRIMARY_PREFIX'))
_pds.setSuffix(image.getOption('DIM_PRIMARY_SUFFIX'))
finally:
_pds.unmute()
_sds = _ldim.getSecondaryDimstring()
_sds.mute()
try:
_sds.setPrefix(image.getOption('DIM_SECONDARY_PREFIX'))
_sds.setSuffix(image.getOption('DIM_SECONDARY_SUFFIX'))
finally:
_sds.unmute()
_ldim.calcDimValues()
image.addObject(_ldim)
self.reset()
class HorizontalDimensionTool(LinearDimensionTool):
"""A specialized tool for drawing HorizontalDimension objects.
The HorizontalDimensionTool is derived from the LinearDimensionTool
and the Tool classes, so it shares all the attributes and methods of
those class.
There are no additional methods for the HorizontalDimension class.
"""
def __init__(self):
super(HorizontalDimensionTool, self).__init__()
def makeDimension(self, image):
"""Create a HorizontalDimension based on the stored tool values.
makeDimension(image)
The argument 'image' is an image object where the dimension willbe used.
"""
_p1 = self.getFirstPoint()
_p2 = self.getSecondPoint()
_x, _y = self.getDimPosition()
if (_p1 is not None and
_p2 is not None and
_x is not None and
_y is not None):
_ds = image.getOption('DIM_STYLE')
_hdim = dimension.HorizontalDimension(_p1, _p2, _x, _y, _ds)
self._setDimension(_hdim)
self.setDimPrefs(image)
def create(self, image):
"""Create a new HorizontalDimension and add it to the image.
create(image)
This method overrides the LinearDimensionTool::create() method.
"""
_hdim = self.getDimension()
if _hdim is not None:
_pds = _hdim.getPrimaryDimstring()
_pds.mute()
try:
_pds.setPrefix(image.getOption('DIM_PRIMARY_PREFIX'))
_pds.setSuffix(image.getOption('DIM_PRIMARY_SUFFIX'))
finally:
_pds.unmute()
_sds = _hdim.getSecondaryDimstring()
_sds.mute()
try:
_sds.setPrefix(image.getOption('DIM_SECONDARY_PREFIX'))
_sds.setSuffix(image.getOption('DIM_SECONDARY_SUFFIX'))
finally:
_sds.unmute()
_hdim.calcDimValues()
image.addObject(_hdim)
self.reset()
class VerticalDimensionTool(LinearDimensionTool):
"""A specialized tool for drawing VerticalDimension objects.
The VerticalalDimensionTool is derived from the LinearDimensionTool
and the Tool classes, so it shares all the attributes and methods of
those class.
There are no additional methods for the VerticalalDimension class.
"""
def __init__(self):
super(VerticalDimensionTool, self).__init__()
def makeDimension(self, image):
"""Create a VerticalDimension based on the stored tool values.
makeDimension(image)
The argument 'image' is an image object where the dimension will be used.
"""
_p1 = self.getFirstPoint()
_p2 = self.getSecondPoint()
_x, _y = self.getDimPosition()
if (_p1 is not None and
_p2 is not None and
_x is not None and
_y is not None):
_ds = image.getOption('DIM_STYLE')
_vdim = dimension.VerticalDimension(_p1, _p2, _x, _y, _ds)
self._setDimension(_vdim)
self.setDimPrefs(image)
def create(self, image):
"""Create a new VerticalDimension and add it to the image.
create(image)
This method overrides the LinearDimensionTool::create() method.
"""
_vdim = self.getDimension()
if _vdim is not None:
_pds = _vdim.getPrimaryDimstring()
_pds.mute()
try:
_pds.setPrefix(image.getOption('DIM_PRIMARY_PREFIX'))
_pds.setSuffix(image.getOption('DIM_PRIMARY_SUFFIX'))
finally:
_pds.unmute()
_sds = _vdim.getSecondaryDimstring()
_sds.mute()
try:
_sds.setPrefix(image.getOption('DIM_SECONDARY_PREFIX'))
_sds.setSuffix(image.getOption('DIM_SECONDARY_SUFFIX'))
finally:
_sds.unmute()
_vdim.calcDimValues()
image.addObject(_vdim)
self.reset()
class RadialDimensionTool(DimensionTool):
"""A specialized tool for drawing RadialDimension objects.
The RadialDimensionTool class is derived from the DimensionTool class
and Tool class, so it shares all the attributes and methods of those
classes. The RadialDimensionTool class has the following additional
methods:
{set/get}DimObject(): Set/Get the circular object being dimensioned.
{set/get}DimPosition(): Set/Get the location of the dimension text.
"""
def __init__(self):
super(RadialDimensionTool, self).__init__()
self.__cobj = None
self.__position = None
def setDimObject(self, cobj):
"""Store the Circle or Arc that the RadialDimension will describe.
setDimObject(cobj):
The argument 'cobj' must be either a Circle or Arc instance.
"""
if not isinstance (cobj, (Circle, Arc)):
raise TypeError, "Invalid Circle or Arc: " + `type(cobj)`
if cobj.getParent() is None:
raise ValueError, "Circle/Arc not found in a Layer."
self.__cobj = cobj
def getDimObject(self):
"""Return the object the RadialDimension will define.
getDimObject()
This method returns a tuple of two objects: the first object
is a Layer, the second object is either a Circle or an Arc
"""
return self.__cobj.getParent(), self.__cobj
def setDimPosition(self, x, y):
"""Store the point where the dimension text will be located.
setDimPosition(x, y)
Arguments 'x' and 'y' should be float values.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__position = (_x, _y)
def getDimPosition(self):
"""Retrieve where the dimension text should be placed.
getDimPosition()
This method returns a tuple containing the x/y coodindates defined
by the setDimPosition() call, or None if that method has not been
invoked.
"""
return self.__position
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends the reset() methods of its base classes.
"""
super(RadialDimensionTool, self).reset()
self.__cobj = None
self.__position = None
def makeDimension(self, image):
"""Create a RadialDimension based on the stored tool values.
makeDimension(image)
The argument 'image' is an image object where the dimension will
be used.
"""
_cobj = self.__cobj
_x, _y = self.__position
if (_cobj is not None and
_x is not None and
_y is not None):
_ds = image.getOption('DIM_STYLE')
_rdim = dimension.RadialDimension(_cobj, _x, _y, _ds)
self._setDimension(_rdim)
self.setDimPrefs(image)
def create(self, image):
"""Create a new RadialDimension and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
_rdim = self.getDimension()
if _rdim is not None:
_pds = _rdim.getPrimaryDimstring()
_pds.mute()
try:
_pds.setPrefix(image.getOption('RADIAL_DIM_PRIMARY_PREFIX'))
_pds.setSuffix(image.getOption('RADIAL_DIM_PRIMARY_SUFFIX'))
finally:
_pds.unmute()
_sds = _rdim.getSecondaryDimstring()
_sds.mute()
try:
_sds.setPrefix(image.getOption('RADIAL_DIM_SECONDARY_PREFIX'))
_sds.setSuffix(image.getOption('RADIAL_DIM_SECONDARY_SUFFIX'))
finally:
_sds.unmute()
_rdim.setDiaMode(image.getOption('RADIAL_DIM_DIA_MODE'))
_rdim.calcDimValues()
image.addObject(_rdim)
self.reset()
class AngularDimensionTool(LinearDimensionTool):
"""A specialized tool for drawing AngularDimension objects.
The AngularDimensionTool class is derived from the LinearDimensionTool
class, so it shares all the attributes and methods of that class. The
AngularDimensionTool class has the following additional methods:
{set/get}VertexPoint(): Set/Get the vertex point used by the dimension
"""
def __init__(self):
super(AngularDimensionTool, self).__init__()
self.__vp = None
def setVertexPoint(self, p):
"""Store the vertex point for the AngularDimension.
setVertexPoint(p):
The argument 'p' must be a Point instance.
"""
if not isinstance (p, Point):
raise TypeError, "Invalid Point: " + `type(p)`
if p.getParent() is None:
raise ValueError, "Point not found in layer."
self.__vp = p
def getVertexPoint(self):
"""Return the vertex point for the AngularDimension.
getVertexPoint()
This method returns a tuple of two objects: the first object
is a Layer, the second object is a Point.
"""
return self.__vp
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends LinearDimensionTool::reset().
"""
super(AngularDimensionTool, self).reset()
self.__vp = None
def makeDimension(self, image):
"""Create an AngularDimension based on the stored tool values.
makeDimension(image)
The argument 'image' is an image object where the dimension will be used.
"""
_vp = self.__vp
_p1 = self.getFirstPoint()
_p2 = self.getSecondPoint()
_x, _y = self.getDimPosition()
if (_vp is not None and
_p1 is not None and
_p2 is not None and
_x is not None and
_y is not None):
_ds = image.getOption('DIM_STYLE')
_adim = dimension.AngularDimension(_vp, _p1, _p2, _x, _y, _ds)
self._setDimension(_adim)
self.setDimPrefs(image)
def create(self, image):
"""Create a new AngularDimension and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
_adim = self.getDimension()
if _adim is not None:
_pds = _adim.getPrimaryDimstring()
_pds.mute()
try:
_pds.setPrefix(image.getOption('ANGULAR_DIM_PRIMARY_PREFIX'))
_pds.setSuffix(image.getOption('ANGULAR_DIM_PRIMARY_SUFFIX'))
finally:
_pds.unmute()
_sds = _adim.getSecondaryDimstring()
_sds.mute()
try:
_sds.setPrefix(image.getOption('ANGULAR_DIM_SECONDARY_PREFIX'))
_sds.setSuffix(image.getOption('ANGULAR_DIM_SECONDARY_SUFFIX'))
finally:
_sds.unmute()
_adim.calcDimValues()
image.addObject(_adim)
self.reset()
#
# printing/plotting tools
#
class PlotTool(Tool):
"""A tool for defining plot regions
"""
def __init__(self):
super(PlotTool, self).__init__()
self.__c1 = None
self.__c2 = None
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends Tool::reset().
"""
super(PlotTool, self).reset()
self.__c1 = None
self.__c2 = None
def setFirstCorner(self, x, y):
"""Store the first corner of the plot region.
setFirstCorner(x, y)
Arguments 'x' and 'y' should be floats.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__c1 = (_x, _y)
def getFirstCorner(self):
"""Return the first corner of the plot region.
getFirstCorner()
"""
if self.__c1 is None:
raise ValueError, "First corner not defined"
return self.__c1
def setSecondCorner(self, x, y):
"""Store the second corner of the plot region.
setSecondCorner(x, y)
Arguments 'x' and 'y' should be floats.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__c2 = (_x, _y)
def getSecondCorner(self):
"""Return the second corner of the plot region.
getSecondCorner()
"""
if self.__c2 is None:
raise ValueError, "Second corner not defined"
return self.__c2
#
# entity modification tools
#
class RegionTool(Tool):
"""A base class for a tool that can store a defined region.
The RegionTool class is designed to be a base class for tools that
need to store a rectangular region. The RegionTool class is derived
from the Tool class, so it shares all the attributes and methods of
that classs. The RegionTool class has the following additional methods:
{set/get}Region(): Set/Get a stored rectangular region
"""
def __init__(self):
super(RegionTool, self).__init__()
self.__region = None
def setRegion(self, xmin, ymin, xmax, ymax):
"""Store a rectangular region in the RegionTool.
setRegion(xmin, ymin, xmax, ymax)
xmin: The minimum x-coordinate value
ymin: The minimum y-coordinate value
xmax: The maximum x-coordinate value
ymax: The maximum y-coordinate value
All the arguments should be floats. If xmax < xmin or ymax < ymin
a ValueError exception is raised.
"""
_xmin = util.get_float(xmin)
_ymin = util.get_float(ymin)
_xmax = util.get_float(xmax)
if _xmax < _xmin:
raise ValueError, "Invalid values: xmax < xmin"
_ymax = util.get_float(ymax)
if _ymax < _ymin:
raise ValueError, "Invalid values: ymax < ymin"
self.__region = (_xmin, _ymin, _xmax, _ymax)
def getRegion(self):
"""Retrieve the stored rectangular region in the tool.
getRegion()
This method returns a tuple containing four float values:
(xmin, ymin, xmax, ymax)
"""
return self.__region
def reset(self):
"""Restore the tool to its initial state.
reset()
This method resets the RegionTool region to None.
"""
super(RegionTool, self).reset()
self.__region = None
class MoveTool(RegionTool):
"""A specialized class for moving objects.
The MoveTool class is derived from the Tool and RegionTool classes,
so it shares all the attributes and methods of those classes. The
MoveTool class has the following additional methods:
{set/get}Distance(): Set/Get the values to move objects.
"""
def __init__(self):
super(MoveTool, self).__init__()
self.__distance = None
def setDistance(self, x, y):
"""Store the distance to move objects in the tool.
setDistance(x, y)
Arguments 'x' and 'y' should be floats, and represent the amount
to move objects in the x-coordinate and y-coordinate axes.
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__distance = (_x, _y)
def getDistance(self):
"""Get the displacement stored in the tool.
getDistance()
This method returns a tuple containing two float values.
(xdisp, ydisp)
If this method is called before the setDistance() method is
used, a ValueError exception is raised.
"""
if self.__distance is None:
raise ValueError, "No distance stored in tool."
return self.__distance
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends the reset() method of its base classes
"""
super(MoveTool, self).reset()
self.__distance = None
class HorizontalMoveTool(MoveTool):
"""A specialized class for moving objects horizontally.
The HorizontalMoveTool is derived from the MoveTool class, so
it shares all the attributes and methods of that class.
There are no additional methods for this class.
"""
def __init__(self):
super(HorizontalMoveTool, self).__init__()
def setDistance(self, x, y):
"""Store the distance to move objects in the tool.
setDistance(x, y)
This method extends the MoveTool::setDistance() method by
enforcing a y-axis displacement of 0.0
"""
_x = util.get_float(x)
super(HorizontalMoveTool, self).setDistance(_x, 0.0)
class VerticalMoveTool(MoveTool):
"""A specialized class for moving objects vertically.
The VerticalMoveTool is derived from the MoveTool class, so
it shares all the attributes and methods of that class.
There are no additional methods for this class.
"""
def __init__(self):
super(VerticalMoveTool, self).__init__()
def setDistance(self, x, y):
"""Store the distance to move objects in the tool.
setDistance(x, y)
This method extends the MoveTool::setDistance() method by
enforcing an x-axis displacement of 0.0
"""
_y = util.get_float(y)
super(VerticalMoveTool, self).setDistance(0.0, _y)
class StretchTool(MoveTool):
"""A specialized class for stretching objects
The StretchTool class is derived from the MoveTool class, so
it shares all the attributes and methods of that class.
There are no additional methods for this class.
"""
def __init__(self):
super(StretchTool, self).__init__()
class HorizontalStretchTool(HorizontalMoveTool):
"""A specialized class for stretching objects horizontally.
The HorizontalStretchTool class is derived from the HorizontalMoveTool
class, so it shares all the attributes and methods of that class.
There are no additional methods for this class.
"""
def __init__(self):
super(HorizontalStretchTool, self).__init__()
class VerticalStretchTool(VerticalMoveTool):
"""A specialized class for stretching objects horizontally.
The VerticalStretchTool class is derived from the VerticalMoveTool
class, so it shares all the attributes and methods of that class.
There are no additional methods for this class.
"""
def __init__(self):
super(VerticalStretchTool, self).__init__()
class DeleteTool(RegionTool):
"""A specialized class for deleting objects.
The DeleteTool class is derived from the Tool and RegionTool classes,
so it shares all the attributes and methods of those classes.
There are no additional methods for this class.
"""
def __init__(self):
super(DeleteTool, self).__init__()
class SplitTool(RegionTool):
"""A specialized class for splitting objects.
The SplitTool class is derived from the Tool and RegionTool classes,
so it shares all the attributes and methods of those classes.
There are no additional methods for this class.
"""
def __init__(self):
super(SplitTool, self).__init__()
class MirrorTool(RegionTool):
"""A specialized class for mirroring objects.
The MirrorTool class is derived from the Tool and RegionTool classes,
so it shares all the attributes and methods of those classes. The
MirrorTool class has the following additional methods:
{set/get}MirrorLine(): Set/Get the construction line used for mirroring
"""
def __init__(self):
super(MirrorTool, self).__init__()
self.__mirrorline = None
def setMirrorLine(self, mline):
"""Store the mirroring construction line in the tool.
setMirrorLine(mline)
The argument 'mline' must be a construction line, otherwise
a TypeError exception is raised.
"""
if not isinstance(mline, (HCLine, VCLine, ACLine, CLine)):
raise TypeError, "Invalid mirroring line type: " + `type(mline)`
self.__mirrorline = mline
def getMirrorLine(self):
"""Retrieve the mirroring construction line from the tool.
getMirrorLine()
If the mirroring construction line has not been set, a ValueError
exception is raised.
"""
if self.__mirrorline is None:
raise ValueError, "No mirror line set."
return self.__mirrorline
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends the RegionTool::reset() method.
"""
super(MirrorTool, self).reset()
self.__mirrorline = None
class RotateTool(RegionTool):
"""A specialized class for rotating objects.
The RotateTool class is derived from the Tool and RegionTool classes,
so it shares all the attributes and methods of those classes. The
Rotateool class has the following additional methods:
{set/get}RotationPoint(): Set/Get the point objects are rotated around
{set/get}Angle(): Set/Get the angle of rotation
"""
def __init__(self):
super(RotateTool, self).__init__()
self.__rp = None
self.__angle = None
def setRotationPoint(self, x, y):
"""Set the coordinates the entities will rotate around
setRotationPoint(x, y)
Arguments 'x' and 'y' should be floats
"""
_x = util.get_float(x)
_y = util.get_float(y)
self.__rp = (_x, _y)
def getRotationPoint(self):
"""Get the point objects will rotate around
getRotationPoint()
This method returns a tuple of two floats or None if the rotation
point has not be defined with setRotationPoint()
"""
return self.__rp
def setAngle(self, angle):
"""Set the angle of rotation.
setAngle(angle)
Argument 'angle' should be a float value. The value is normalized
so that abs(angle) < 360.0.
"""
_a = util.get_float(angle)
self.__angle = math.fmod(_a, 360.0)
def getAngle(self):
"""Get the angle of rotation.
getAngle()
This method returns a float or None if the angle has not been
set with setAngle()
"""
return self.__angle
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends Tool::reset().
"""
super(RotateTool, self).reset()
self.__rp = None
self.__angle = None
class GraphicObjectTool(RegionTool):
"""A specialized class for changing attributes of GraphicObject instances.
The GraphicObjectTool class is derived from the RegionTool class,
so it shares all the attributes and methods of that class. The
GraphicObjectTool class has the following additional methods:
{set/get}Attribute(): Set/Get the desired attribute
{set/get}Value(): Set/Get the new entity color.
"""
def __init__(self):
super(RegionTool, self).__init__()
self.__attr = None
self.__value = None
def setAttribute(self, attr):
"""Define which attribute the tool is modifying.
setAttribute(attr)
Argument 'attr' should be either 'setStyle', 'setLinetype', 'setColor',
or 'setThickness'
"""
if not isinstance(attr, str):
raise TypeError, "Invalid attribute type: " + `type(attr)`
if attr not in ('setStyle', 'setLinetype', 'setColor', 'setThickness'):
raise ValueError, "Invalid attribute: " + attr
self.__attr = attr
def getAttribute(self):
"""Return the specified attribute.
getAttribute()
If called before invoking setAttribute(), this method raises a ValueError.
"""
if self.__attr is None:
raise ValueError, "Tool attribute not defined."
return self.__attr
def setValue(self, val):
"""Store the new value of the entity attribute.
setValue(val)
Argument 'val' depends on the type of attribute defined for the
tool. If no attribute is defined this method raises a ValueError.
Invoking this method with 'None' as an argument sets the tool
to restore the default attribute value.
"""
if self.__attr is None:
raise ValueError, "Tool attribute not defined."
_a = self.__attr
_val = None
if val is not None:
if _a == 'setStyle':
if not isinstance(val, style.Style):
raise TypeError, "Invalid Style: " + `type(val)`
_val = val
elif _a == 'setLinetype':
if not isinstance(val, linetype.Linetype):
raise TypeError, "Invalid Linetype: " + `type(val)`
_val = val
elif _a == 'setColor':
if not isinstance(val, color.Color):
raise TypeError, "Invalid Color: " + `type(val)`
_val = val
elif _a == 'setThickness':
_val = util.get_float(val)
if _val < 0.0:
raise ValueError, "Invalid thickness: %g" % _val
else:
raise ValueError, "Unexpected attribute: " + _a
self.__value = _val
def getValue(self):
"""Get the stored attribute value.
getValue()
This method returns the value stored in setValue() or None.
"""
return self.__value
class TextTool(RegionTool):
"""A specialized class for entering text.
The TextTool class is derived from the Tool class, so it shares
the attributes and methods of that class. The TextTool class also
has the following additional methods:
{set/get}Text(): Set/Get the text string in the tool.
hasText(): Test if the tool has stored a text string
{set/get}TextLocation(): Set/Get where the text is to be placed.
{set/get}TextBlock(): Set/Get a TextBlock instance in the Tool
{set/get}Bounds(): Set/Get the width and height of the text
{set/get}PixelSize(): Set/Get the a rectangular region bounding the text.
{set/get}Layout(): Set/Get the formatted text string display.
{set/get/test}Attribute(): Set/Get/Test a TextBlock attribute
{set/get}Value(): Set/Get the attribute value.
"""
def __init__(self):
super(TextTool, self).__init__()
self.__text = None
self.__location = None
self.__tblock = None
self.__attr = None
self.__value = None
self.__bounds = None
self.__pixel_size = None
self.__layout = None
def setText(self, text):
"""Store some text in the tool.
setText(text)
The argument 'text' should be a unicode object.
"""
_text = text
if not isinstance(_text, unicode):
_text = unicode(text)
self.__text = _text
def getText(self):
"""Retrieve the stored text from the TextTool.
getText()
If no text has been stored, this method raises a ValueError exception.
"""
if self.__text is None:
raise ValueError, "No text stored in TextTool."
return self.__text
def hasText(self):
"""Test if the tool has stored a text string.
hasText()
"""
return self.__text is not None
def setTextLocation(self, x, y):
"""Store the location where the text will be placed.
setTextLocation(x, y)
The arguments 'x' and 'y' should be float values.
"""
_x, _y = util.make_coords(x, y)
self.__location = (_x, _y)
def getTextLocation(self):
"""Retrieve the location where the text will be placed.
getTextLocation()
This method returns a tuple holding two floats:
(x, y)
A ValueError exception is raised if this method is called prior to
setting the text location with setTextLocation().
"""
if self.__location is None:
raise ValueError, "No text location defined."
return self.__location
def testAttribute(self, attr):
"""Test that the given attribute is valid.
testAttribute(attr)
Argument 'attr' should be one of the following: 'setAngle',
'setAlignment', 'setFamily', 'setStyle', 'setWeight', 'setColor',
or 'setSize'
"""
if not isinstance(attr, str):
raise TypeError, "Invalid attribute type: " + `type(attr)`
return attr in ('setAngle', 'setAlignment', 'setFamily',
'setStyle', 'setWeight', 'setColor', 'setSize')
def setAttribute(self, attr):
"""Define which attribute the tool is modifying.
setAttribute(attr)
Argument 'attr' should be one of the following: 'setAngle',
'setAlignment', 'setFamily', 'setStyle', 'setWeight', 'setColor',
or 'setSize'
"""
if not self.testAttribute(attr):
raise ValueError, "Invalid attribute: " + attr
self.__attr = attr
def getAttribute(self):
"""Return the specified attribute.
getAttribute()
If called before invoking setAttribute(), this method raises a ValueError.
"""
if self.__attr is None:
raise ValueError, "Tool attribute not defined."
return self.__attr
def testValue(self, val):
"""Test that the given value is valid for the preset attribute.
testValue(val)
Argument 'val' depends on what attribute has been set with via setAttribute().
"""
_a = self.__attr
if _a == 'setAngle':
_val = util.getFloat(val)
elif _a == 'setAlignment':
if not isinstance(val, int):
raise TypeError, "Invalid alignment type: " + `type(val)`
if (val != TextStyle.ALIGN_LEFT and
val != TextStyle.ALIGN_CENTER and
val != TextStyle.ALIGN_RIGHT):
raise ValueError, "Invalid alignment value: %d" % val
_val = val
elif _a == 'setColor':
if not isinstance(val, color.Color):
raise TypeError, "Invalid Color: " + `type(val)`
_val = val
elif _a == 'setFamily':
if not isinstance(val, types.StringTypes):
raise TypeError, "Invalid family type: " + `type(val)`
_val = val
elif _a == 'setStyle':
if not isinstance(val, int):
raise TypeError, "Invalid style type: " + `type(val)`
if (val != TextStyle.FONT_NORMAL and
val != TextStyle.FONT_OBLIQUE and
val != TextStyle.FONT_ITALIC):
raise ValueError, "Invalid style value: %d" % val
_val = val
elif _a == 'setWeight':
if not isinstance(val, int):
raise TypeError, "Invalid weight type: " + `type(val)`
if (val != TextStyle.WEIGHT_NORMAL and
val != TextStyle.WEIGHT_LIGHT and
val != TextStyle.WEIGHT_BOLD and
val != TextStyle.WEIGHT_HEAVY):
raise ValueError, "Invalid weight value: %d" % val
_val = val
elif _a == 'setSize':
_val = util.get_float(val)
if _val < 0.0:
raise ValueError, "Invalid size: %g" % _val
else:
raise ValueError, "Unexpected attribute: " + _a
return _val
def setValue(self, val):
"""Store the new value of the entity attribute.
setValue(val)
Argument 'val' depends on the type of attribute defined for the
tool. If no attribute is defined this method raises a ValueError.
Invoking this method with 'None' as an argument sets the tool
to restore the default attribute value.
"""
if self.__attr is None:
raise ValueError, "Tool attribute not defined."
_val = None
if val is not None:
_val = self.testValue(val)
self.__value = _val
def getValue(self):
"""Get the stored attribute value.
getValue()
This method returns the value stored in setValue() or None.
"""
return self.__value
def getBounds(self):
"""Return the width and height of the TextBlock.
getBounds()
"""
if self.__bounds is None:
raise ValueError, "TextBlock bounds not defined."
return self.__bounds
def setBounds(self, width, height):
"""Set the width and height of the TextBlock.
setBounds(width, height):
Arguments 'width' and 'height' should be positive float values.
"""
_w = util.get_float(width)
if _w < 0.0:
raise ValueError, "Invalid width: %g" % _w
_h = util.get_float(height)
if _h < 0.0:
raise ValueError, "Invalid height: %g" % _h
self.__bounds = (_w, _h)
def setPixelSize(self, width, height):
"""Store a screen-size rectangular boundary for the text.
setPixelSize(width, height)
Arguments 'width' and 'height' should be positive integer values.
This method is somewhat GTK specific ...
"""
_width = width
if not isinstance(_width, int):
_width = int(width)
if _width < 0:
raise ValueError, "Invalid width: %d" % _width
_height = height
if not isinstance(_height, int):
_height = int(height)
if _height < 0:
raise ValueError, "Invalid height: %d" % _height
self.__pixel_size = (_width, _height)
def getPixelSize(self):
"""Retrieve the stored rectangular region of text.
getPixelSize()
A ValueError exception is raised if this method is called before
the size has been set by setPixelSize()
"""
if self.__pixel_size is None:
raise ValueError, "Pixel size is not defined."
return self.__pixel_size
def setLayout(self, layout):
"""Store a formatted layout string for the text.
setLayout()
This method is very GTK/Pango specific ...
"""
self.__layout = layout
def getLayout(self):
"""Retrieve the formatted layout for the text string.
getLayout()
This method is very GTK/Pango specific ...
"""
return self.__layout
def setTextBlock(self, tblock):
"""Store a TextBlock instance within the Tool.
setTextBlock(tblock)
Argument 'tblock' must be a TextBlock.
"""
if not isinstance(tblock, TextBlock):
raise TypeError, "Invalid TextBlock: " + `type(tblock)`
self.__tblock = tblock
def getTextBlock(self):
"""Retrieve a stored TextBlock within the Tool.
getTextBlock()
This method may return None if no TextBlock has been stored
via setTextBlock().
"""
return self.__tblock
def create(self, image):
"""Create a new TextBlock and add it to the image.
create(image)
This method overrides the Tool::create() method.
"""
_tb = self.__tblock
if _tb is None:
_text = self.getText()
_x, _y = self.getTextLocation()
_ts = image.getOption('TEXT_STYLE')
_tb = TextBlock(_x, _y, _text, _ts)
_f = image.getOption('FONT_FAMILY')
if _f != _ts.getFamily():
_tb.setFamily(_f)
_s = image.getOption('FONT_STYLE')
if _s != _ts.getStyle():
_tb.setStyle(_s)
_w = image.getOption('FONT_WEIGHT')
if _w != _ts.getWeight():
_tb.setWeight(_w)
_c = image.getOption('FONT_COLOR')
if _c != _ts.getColor():
_tb.setColor(_c)
_sz = image.getOption('TEXT_SIZE')
if abs(_sz - _ts.getSize()) > 1e-10:
_tb.setSize(_sz)
_a = image.getOption('TEXT_ANGLE')
if abs(_a - _ts.getAngle()) > 1e-10:
_tb.setAngle(_a)
_al = image.getOption('TEXT_ALIGNMENT')
if _al != _ts.getAlignment():
_tb.setAlignment(_al)
image.addObject(_tb)
self.reset()
def reset(self):
"""Restore the tool to its initial state.
reset()
This method extends Tool::reset().
"""
super(TextTool, self).reset()
self.__text = None
self.__location = None
self.__tblock = None
self.__bounds = None
self.__pixel_size = None
self.__layout = None
class EditDimensionTool(RegionTool):
"""A specialized class for changing attributes of Dimension instances.
The EditDimensionTool class is derived from the RegionTool class,
so it shares all the attributes and methods of that class. The
EditDimensionTool class has the following additional methods:
{set/get}Attribute(): Set/Get the desired attribute
{set/get}Value(): Set/Get the new entity color.
"""
def __init__(self):
super(RegionTool, self).__init__()
self.__attr = None
self.__value = None
def setAttribute(self, attr):
"""Define which attribute the tool is modifying.
setAttribute(attr)
Argument 'attr' should be one of the following:
'setEndpointType', 'setEndpointSize', 'setDualDimMode', 'setDualModeOffset',
'setOffset', 'setExtension', 'setColor', 'setThickness', 'setScale'
"""
if not isinstance(attr, str):
raise TypeError, "Invalid attribute type: " + `type(attr)`
if attr not in ('setEndpointType', 'setEndpointSize',
'setDualDimMode', 'setDualModeOffset',
'setOffset', 'setExtension', 'setColor',
'setThickness', 'setScale'):
raise ValueError, "Invalid attribute: " + attr
self.__attr = attr
def getAttribute(self):
"""Return the specified attribute.
getAttribute()
If called before invoking setAttribute(), this method raises a ValueError.
"""
if self.__attr is None:
raise ValueError, "Tool attribute not defined."
return self.__attr
def setValue(self, val):
"""Store the new value of the entity attribute.
setValue(val)
Argument 'val' depends on the type of attribute defined for the
tool. If no attribute is defined this method raises a ValueError.
Invoking this method with 'None' as an argument sets the tool
to restore the default attribute value.
"""
if self.__attr is None:
raise ValueError, "Tool attribute not defined."
_a = self.__attr
_val = None
if val is not None:
if _a == 'setEndpointType':
if (val != dimension.Dimension.DIM_ENDPT_NONE and
val != dimension.Dimension.DIM_ENDPT_ARROW and
val != dimension.Dimension.DIM_ENDPT_FILLED_ARROW and
val != dimension.Dimension.DIM_ENDPT_SLASH and
val != dimension.Dimension.DIM_ENDPT_CIRCLE):
raise ValueError, "Invalid endpoint value: " + str(val)
_val = val
elif _a == 'setEndpointSize':
_val = util.get_float(val)
if _val < 0.0:
raise ValueError, "Invalid endpoint size: %g" % _val
_val = val
elif _a == 'setDualDimMode':
util.test_boolean(val)
_val = val
elif _a == 'setDualModeOffset':
_val = util.get_float(val)
if _val < 0.0:
raise ValueError, "Invalid offset length: %g" % _val
_val = val
elif _a == 'setOffset':
_val = util.get_float(val)
if _val < 0.0:
raise ValueError, "Invalid offset length: %g" % _val
_val = val
elif _a == 'setExtension':
_val = util.get_float(val)
if _val < 0.0:
raise ValueError, "Invalid extension length: %g" % _val
_val = val
elif _a == 'setColor':
if not isinstance(val, color.Color):
raise TypeError, "Invalid Color: " + `type(val)`
_val = val
elif _a == 'setThickness':
_val = util.get_float(val)
if _val < 0.0:
raise ValueError, "Invalid thickness: %g" % _val
elif _a == 'setScale':
_val = util.get_float(val)
if _val < 0.0:
raise ValueError, "Invalid scale: %g" % _val
else:
raise ValueError, "Unexpected attribute: " + _a
self.__value = _val
def getValue(self):
"""Get the stored attribute value.
getValue()
This method returns the value stored in setValue() or None.
"""
return self.__value
class EditDimStringTool(TextTool):
"""A specialized class for modifying DimString instances in Dimensions.
The EditDimStringTool class is derived from the TextTool class, so it
shares the attributes and methods of that class. The TextTool class has
the following additional methods:
{set/get}Primary(): Set/Get the DimString on which the tool operates.
"""
def __init__(self):
super(EditDimStringTool, self).__init__()
self.__pds = True
def setPrimary(self, flag=True):
"""Set the tool to operate on the primary DimString
setPrimary([flag])
Optional argument 'flag' should be a boolean. By default the
tool will operate on the primary DimString of a Dimension. If
argument 'flag' is False, the tool will operate on the secondary
DimString.
"""
util.test_boolean(flag)
self.__pds = flag
def getPrimary(self):
"""Test if the tool operates on the primary DimString
getPrimary()
This method returns a boolean
"""
return self.__pds
def testAttribute(self, attr):
"""Test that the attribute is valid for the DimString entity.
testAttribute(attr)
Argument 'attr' must be one of the following: 'setPrefix', 'setSuffix',
'setUnits', 'setPrecision', 'setPrintZero', 'setPringDecimal', 'setFamily',
'setStyle', 'setWeight', 'setSize', 'setAlignment', 'setColor', or
'setAngle'.
"""
if not isinstance(attr, str):
raise TypeError, "Invalid attribute type: " + `type(attr)`
_res = attr in ('setPrefix', 'setSuffix', 'setUnits', 'setPrecision',
'setPrintZero', 'setPrintDecimal')
if _res:
return _res
return super(EditDimStringTool, self).testAttribute(attr)
def testValue(self, val):
"""Test that the value is valid for a given DimString attribute.
testValue(val)
Argument 'val' depends on the attribute set for the EditDimString instance.
"""
_a = self.getAttribute()
if _a == 'setPrefix' or _a == 'setSuffix':
if not isinstance(val, types.StringTypes):
raise TypeError, "Invalid %s type: %s " % (_a, `type(val)`)
_val = val
if not isinstance(_val, unicode):
_val = unicode(val)
elif _a == 'setPrecision':
if not isinstance(val, int):
raise TypeError, "Invalid precision type: " + `type(val)`
_val = val
elif _a == 'setPrintZero' or _a == 'setPrintDecimal':
try:
util.test_boolean(val)
except TypeError:
raise TypeError, "Invalid %s type: %s " % (_a, `type(val)`)
_val = val
elif _a == 'setUnits':
_val = val # FIXME: needs error checking ...
else:
_val = super(EditDimStringTool, self).testValue(val)
return _val
def setText(self, txt):
pass
def getText(self):
pass
def hasText(self):
pass
def setTextLocation(self, x, y):
pass
def getTextLocation(self):
pass
def setBounds(self, width, height):
pass
def getBounds(self):
pass
def setPixelSize(self, width, height):
pass
def getPixelSize(self):
pass
def setLayout(self, layout):
pass
def getLayout(self):
pass
|