diff --git a/bat/premake_vs2019.bat b/bat/premake_vs2019.bat new file mode 100644 index 0000000..9527030 --- /dev/null +++ b/bat/premake_vs2019.bat @@ -0,0 +1,2 @@ +cd ..\ +premake5 solution_vs2019 \ No newline at end of file diff --git a/doxyfile b/doc/doxyfile similarity index 99% rename from doxyfile rename to doc/doxyfile index dabda97..9a98f6f 100644 --- a/doxyfile +++ b/doc/doxyfile @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = doc/api +OUTPUT_DIRECTORY = api # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and diff --git a/doc/quickstart_guide.docx b/doc/quickstart_guide.docx index ff64fcf..1463e6d 100644 Binary files a/doc/quickstart_guide.docx and b/doc/quickstart_guide.docx differ diff --git a/examples/hello_world/main.cpp b/examples/hello_world/main.cpp index d68db49..690254d 100644 --- a/examples/hello_world/main.cpp +++ b/examples/hello_world/main.cpp @@ -45,7 +45,7 @@ int main(int argc, char** argv) world->SetGravity(gravity); // The fixed time step size. - const float32 timeStep = 1.0f / 60.0f; + const scalar timeStep = 1.0f / 60.0f; // Number of iterations for the velocity constraint solver. const u32 velocityIterations = 8; @@ -59,14 +59,8 @@ int main(int argc, char** argv) // Create a box positioned at the world origin and // aligned with the world frame. - b3BoxHull groundBox; - - // Set the ground box dimensions using a linear scale transform. - b3Transform scale; - scale.position.SetZero(); - scale.rotation = b3Diagonal(10.0f, 1.0f, 10.0f); - groundBox.SetTransform(scale); - + b3BoxHull groundBox(10.0f, 1.0f, 10.0f); + // Create the box physics wrapper. b3HullShape groundShape; groundShape.m_hull = &groundBox; @@ -115,7 +109,7 @@ int main(int argc, char** argv) // Decode the axis and angle of rotation about it from the quaternion. b3Vec3 axis; - float32 angle; + scalar angle; orientation.GetAxisAngle(&axis, &angle); // Visualize the body state in this frame. diff --git a/examples/testbed/data/octopus.ele b/examples/testbed/data/octopus.ele new file mode 100644 index 0000000..47783ec --- /dev/null +++ b/examples/testbed/data/octopus.ele @@ -0,0 +1,17656 @@ +17654 4 0 + 1 3387 3402 3368 4723 + 2 3025 3010 3020 2999 + 3 1541 1529 1528 1521 + 4 1099 958 959 1098 + 5 3630 3649 545 3704 + 6 1371 1515 1516 1511 + 7 2474 2473 2459 2427 + 8 1056 747 1058 751 + 9 507 3382 3407 3413 + 10 3037 2739 3039 3184 + 11 3867 263 265 3816 + 12 2194 471 2203 135 + 13 3419 3424 3670 3673 + 14 866 865 877 878 + 15 3443 3444 558 571 + 16 1600 1598 1213 737 + 17 3995 2903 2904 2908 + 18 793 3894 4294 4404 + 19 389 3920 3909 3954 + 20 2112 152 150 2117 + 21 922 882 920 921 + 22 652 2458 658 659 + 23 2439 2447 682 679 + 24 351 323 316 325 + 25 2309 2311 2297 2313 + 26 3101 3472 3090 4293 + 27 2344 764 2366 2368 + 28 3945 389 396 394 + 29 3909 3879 3960 391 + 30 2925 2950 2934 2936 + 31 3231 982 3052 3048 + 32 2870 3943 2868 2860 + 33 2797 2792 919 890 + 34 2781 3454 3077 3459 + 35 3562 3437 3503 4264 + 36 2661 2880 2862 1658 + 37 3426 3416 3434 3429 + 38 745 2356 778 4034 + 39 2786 3603 4600 4605 + 40 3719 3692 3691 3710 + 41 234 239 230 238 + 42 1263 1265 4085 4395 + 43 3586 3589 444 452 + 44 3342 2856 2854 4754 + 45 3128 3168 1232 4032 + 46 361 373 3733 3724 + 47 2157 2175 1540 1539 + 48 215 1858 143 1873 + 49 563 1357 1358 562 + 50 3524 3532 3521 3522 + 51 819 786 742 1225 + 52 1425 1435 1421 498 + 53 570 3420 3652 3651 + 54 826 1149 1155 1664 + 55 3814 3840 252 253 + 56 292 3934 3474 279 + 57 3423 3419 3444 3424 + 58 2560 2543 2546 2547 + 59 3430 3364 3363 3429 + 60 3779 3959 3961 3912 + 61 284 3526 286 281 + 62 1916 2499 4104 4280 + 63 3800 3803 3802 3962 + 64 2729 2728 2812 2753 + 65 3803 3793 3800 3794 + 66 449 170 1486 175 + 67 2758 2756 2755 2759 + 68 2780 763 2673 2650 + 69 725 814 1138 752 + 70 3871 3849 3867 3816 + 71 3692 3547 3565 3693 + 72 581 2271 588 2267 + 73 1656 3287 4022 4357 + 74 3933 712 4022 4381 + 75 3156 3224 1262 3226 + 76 162 549 1384 1991 + 77 3643 3631 3632 3641 + 78 2939 784 803 804 + 79 179 1913 182 1915 + 80 924 933 929 928 + 81 1648 753 1631 84 + 82 3640 3094 3645 4107 + 83 3028 3136 3144 3163 + 84 1092 1091 1313 1124 + 85 1984 1978 1959 1943 + 86 3672 3639 3673 3664 + 87 2840 3901 3880 3788 + 88 725 1657 1658 1633 + 89 1499 437 439 440 + 90 471 1464 2202 116 + 91 2074 2072 2080 2077 + 92 2601 2593 2611 668 + 93 3999 3981 3992 4516 + 94 1090 983 1261 936 + 95 3369 3620 3375 4380 + 96 1235 3169 1277 1267 + 97 2856 3340 4388 4753 + 98 3514 3491 3403 4723 + 99 1463 474 1472 475 + 100 3444 3443 537 571 + 101 347 786 1215 1561 + 102 315 3214 324 323 + 103 989 4207 4477 4482 + 104 1543 3560 1536 3537 + 105 127 91 2182 2183 + 106 2327 768 2367 2358 + 107 2989 2960 2977 2956 + 108 2812 3227 3242 3243 + 109 1534 1535 1543 1544 + 110 3945 3918 389 398 + 111 2110 39 45 2096 + 112 2001 176 2003 2002 + 113 2577 2280 1620 1616 + 114 3225 3258 982 3222 + 115 3581 3923 3583 2682 + 116 2888 2889 2896 2901 + 117 1113 1110 1109 1738 + 118 3408 3563 3556 3091 + 119 2334 2323 2340 2332 + 120 3693 3570 399 435 + 121 1482 1483 1494 1474 + 122 2195 2203 2161 2202 + 123 3149 3010 3035 3020 + 124 3574 3547 3550 3564 + 125 500 501 558 571 + 126 2475 2476 2478 2520 + 127 3825 3871 265 3816 + 128 3887 295 3908 4462 + 129 3419 3424 555 537 + 130 3398 3361 3396 3390 + 131 548 551 549 154 + 132 841 2415 2412 899 + 133 891 2324 896 895 + 134 2081 2082 2078 2077 + 135 3430 525 509 491 + 136 1602 739 4110 4441 + 137 2170 2172 2173 2167 + 138 3357 3367 3360 3364 + 139 416 402 399 3569 + 140 3675 3683 3720 424 + 141 3077 2782 3529 4588 + 142 3749 3744 3751 3851 + 143 3667 3653 3666 3654 + 144 3984 3798 3746 3745 + 145 3672 543 3652 3651 + 146 3579 3547 3707 3542 + 147 3000 3018 3071 3017 + 148 3707 3579 3542 3543 + 149 11 561 559 497 + 150 3709 3691 3711 3722 + 151 3312 3819 2855 4449 + 152 866 2233 2249 2239 + 153 1048 775 747 1047 + 154 3996 3982 3969 3994 + 155 3444 3419 558 537 + 156 2925 2950 2915 2982 + 157 1366 1375 1387 1367 + 158 3694 3713 3695 435 + 159 2948 2947 2936 2932 + 160 3708 3692 3713 3720 + 161 401 3575 3550 3549 + 162 1948 1885 103 101 + 163 3661 3536 3106 3109 + 164 721 2639 732 720 + 165 2403 901 898 900 + 166 3565 3708 3695 3707 + 167 156 157 2117 2184 + 168 3683 3713 424 435 + 169 267 255 235 262 + 170 2701 2935 2691 2933 + 171 3457 3535 3521 517 + 172 3814 3840 3864 3866 + 173 1901 1972 1964 1897 + 174 3298 3186 3200 3179 + 175 3526 3507 3527 3525 + 176 3356 3946 3919 3915 + 177 3505 526 527 529 + 178 3740 3279 3788 3900 + 179 554 573 572 565 + 180 1704 1719 1258 1259 + 181 3419 3444 3424 537 + 182 4173 4259 4269 4763 + 183 527 526 511 529 + 184 3131 3092 3091 2682 + 185 418 422 424 421 + 186 489 3431 3416 3435 + 187 762 772 879 2359 + 188 285 518 282 519 + 189 2175 2170 2155 2173 + 190 3793 3914 3794 3771 + 191 2433 675 690 689 + 192 3273 632 3272 4666 + 193 3424 3444 3420 537 + 194 638 636 634 647 + 195 3579 3543 3905 3578 + 196 3737 3747 3751 3851 + 197 3372 3389 3628 3624 + 198 1928 212 1914 1929 + 199 3419 3656 3424 3673 + 200 1553 1548 1552 4209 + 201 3552 3557 3611 3612 + 202 508 3381 3425 491 + 203 3657 3656 3407 3413 + 204 3574 416 3694 3569 + 205 2196 2198 2162 2160 + 206 379 380 381 384 + 207 2868 3943 2867 3947 + 208 3396 3361 3404 3359 + 209 2554 2562 2551 2564 + 210 3693 3565 3695 3707 + 211 37 554 556 557 + 212 3220 2824 2836 2833 + 213 1476 1477 462 4141 + 214 1144 4098 4403 4669 + 215 3803 366 362 3902 + 216 518 3507 3527 3511 + 217 651 655 671 656 + 218 3356 3943 3919 3947 + 219 3384 531 532 533 + 220 266 255 253 256 + 221 374 376 3775 3738 + 222 3031 3145 3032 1287 + 223 3694 399 3707 3564 + 224 373 3733 3797 372 + 225 398 3945 396 394 + 226 3746 3745 3989 3772 + 227 4602 1120 4715 4740 + 228 1250 1242 1241 3124 + 229 2573 2576 2592 2567 + 230 834 831 833 2383 + 231 1473 1483 1482 1474 + 232 3589 3600 442 3586 + 233 3919 3918 3915 398 + 234 3513 3510 3512 3394 + 235 2347 2364 2355 2365 + 236 3344 3309 3348 2827 + 237 3849 3847 3867 3816 + 238 3928 3892 306 3889 + 239 3135 3148 3036 3027 + 240 982 1025 1027 980 + 241 3699 3093 3645 4128 + 242 2382 831 2391 2390 + 243 1288 1310 1327 1329 + 244 3793 3803 3800 3962 + 245 1265 987 1263 1255 + 246 2517 2515 19 2513 + 247 398 3945 397 396 + 248 3885 2645 640 3884 + 249 3287 3933 4322 4553 + 250 798 2906 2923 790 + 251 1348 563 1357 1358 + 252 380 2879 392 2877 + 253 356 2951 2953 358 + 254 3883 297 3888 296 + 255 3494 527 3496 529 + 256 274 287 3520 3447 + 257 457 1477 1494 462 + 258 3364 3367 494 493 + 259 3165 3162 3146 1333 + 260 2351 1228 1193 1195 + 261 3194 3191 3192 2674 + 262 3639 3671 3722 3718 + 263 2897 2930 2902 2865 + 264 2960 2963 2958 2957 + 265 713 710 3699 711 + 266 3062 3134 4148 4309 + 267 1188 2130 2137 2124 + 268 2724 2692 4308 4703 + 269 2803 3452 3451 3597 + 270 3982 244 3969 3994 + 271 3101 3930 3473 3406 + 272 3426 508 3416 3381 + 273 3752 3755 3848 3754 + 274 3510 3512 3394 3393 + 275 2163 2160 105 2199 + 276 1389 1382 1381 541 + 277 366 3803 362 3800 + 278 3777 3778 3792 3783 + 279 3102 3098 3769 3768 + 280 349 2951 356 358 + 281 3791 3777 3792 3783 + 282 3778 3994 3974 3980 + 283 416 3574 3547 3550 + 284 853 2396 844 903 + 285 338 2898 2899 2882 + 286 2303 2312 2311 2297 + 287 3639 3667 3666 3654 + 288 2108 2110 2099 2101 + 289 2713 1039 2705 2723 + 290 850 849 840 838 + 291 2416 2398 2408 844 + 292 3009 3149 3027 3014 + 293 818 759 754 2687 + 294 1007 1096 943 977 + 295 3015 3011 3065 3066 + 296 3702 3671 546 3654 + 297 3386 3500 3388 4615 + 298 3692 3693 3695 435 + 299 3712 3691 3711 3710 + 300 254 255 266 256 + 301 779 771 767 778 + 302 307 306 3889 305 + 303 509 3430 3425 3429 + 304 289 290 3445 3447 + 305 324 4316 4492 4595 + 306 3725 3780 3739 3738 + 307 3470 280 3480 3469 + 308 794 1236 1239 1154 + 309 3873 3821 3825 3834 + 310 2858 329 381 2893 + 311 3634 3636 4246 4409 + 312 1260 1719 4042 4043 + 313 633 3273 632 4181 + 314 3970 3778 3987 3971 + 315 3150 3033 3034 3020 + 316 311 3926 3929 3925 + 317 3077 3169 3084 3043 + 318 3736 3728 3983 3777 + 319 3928 3892 309 306 + 320 3104 3661 3650 3655 + 321 3200 3179 4006 4475 + 322 310 3949 3928 312 + 323 1152 1252 1154 4113 + 324 3351 3265 3180 3179 + 325 3228 2735 2738 2802 + 326 1183 4544 4570 4616 + 327 3755 237 3848 3865 + 328 575 577 321 4426 + 329 2672 2657 2686 2685 + 330 1240 1714 1409 1717 + 331 1842 1916 4126 4280 + 332 1085 970 4122 4429 + 333 2350 748 746 4341 + 334 3380 3080 2782 3596 + 335 3819 3807 3806 4176 + 336 3891 3470 3480 3469 + 337 3677 3570 3720 3678 + 338 314 332 4328 4587 + 339 348 1237 4052 4319 + 340 2692 2840 2690 4703 + 341 2684 2681 405 1506 + 342 3493 3392 3496 529 + 343 3419 3656 3436 3407 + 344 3332 3939 3317 4420 + 345 3164 3162 1333 3163 + 346 3787 3878 3281 3740 + 347 462 459 458 4141 + 348 3541 3540 3905 3578 + 349 1479 1473 461 450 + 350 371 373 375 377 + 351 1400 1110 4040 4197 + 352 3015 3018 3009 3149 + 353 4119 3319 4455 4464 + 354 18 1916 4104 4126 + 355 4327 997 4336 4477 + 356 3273 633 3881 4181 + 357 3660 3672 3669 3651 + 358 1401 1391 4066 4402 + 359 3149 3018 3010 3020 + 360 2266 2268 1206 4334 + 361 3702 553 3666 3654 + 362 3107 710 3106 4305 + 363 3833 3830 3807 3804 + 364 2825 2824 3220 4594 + 365 3491 3514 3403 3401 + 366 3110 3095 3280 4618 + 367 3744 230 3855 3726 + 368 1072 1059 1062 4053 + 369 3101 3472 4293 4597 + 370 1697 1700 3584 3583 + 371 2997 3003 4033 4037 + 372 3238 2747 3231 3245 + 373 712 1638 4022 4024 + 374 454 1482 1494 1474 + 375 3398 533 514 3395 + 376 2747 3546 3053 3538 + 377 3644 3131 3646 3717 + 378 553 543 545 546 + 379 3421 3424 3670 3669 + 380 3943 2870 381 385 + 381 2930 2892 2902 2901 + 382 3630 545 3669 3651 + 383 1111 1112 1400 4671 + 384 1581 1590 1763 4152 + 385 1175 1160 1158 4601 + 386 1231 1268 4027 4032 + 387 1477 1475 457 1494 + 388 3312 313 4073 4502 + 389 2843 2959 2842 2958 + 390 3157 3158 3129 4243 + 391 3834 3825 3822 3829 + 392 585 584 592 586 + 393 2763 4276 4632 4653 + 394 1667 1240 1674 1668 + 395 1246 1247 1680 4004 + 396 3481 307 3470 3891 + 397 3457 290 517 283 + 398 296 306 298 3897 + 399 3939 3319 3317 4420 + 400 797 2947 2906 2932 + 401 1738 1400 4040 4497 + 402 2565 2550 701 2555 + 403 3451 2787 3603 4425 + 404 3015 3086 3030 3014 + 405 3139 3136 3141 3163 + 406 3949 3926 3941 3927 + 407 3882 640 2676 4374 + 408 797 799 2932 800 + 409 3380 3620 3370 3369 + 410 793 795 815 1638 + 411 1018 4013 4340 4460 + 412 2890 2661 2880 2862 + 413 2803 3530 4150 4175 + 414 2283 2505 4015 4299 + 415 3908 3887 2988 4652 + 416 2892 2930 2889 2901 + 417 584 297 3883 296 + 418 392 380 381 386 + 419 2896 2892 2900 2899 + 420 2289 1207 4484 4672 + 421 740 786 4239 4537 + 422 2913 643 2907 626 + 423 2283 4015 4296 4299 + 424 2897 2866 2881 2899 + 425 429 1516 420 430 + 426 3500 3387 3386 3388 + 427 3936 2984 1040 1044 + 428 3551 3552 480 481 + 429 3134 3062 4149 4309 + 430 3466 270 3467 268 + 431 4047 1254 4050 4635 + 432 3912 3879 3960 3911 + 433 3036 3012 3013 3145 + 434 3787 3878 3740 3788 + 435 748 2350 1067 4341 + 436 3517 3516 3524 3465 + 437 2798 3224 3226 4042 + 438 3320 3318 3319 586 + 439 1039 3557 2136 2706 + 440 308 3468 3928 3889 + 441 2892 2897 2902 2857 + 442 3795 366 3797 372 + 443 388 389 387 364 + 444 2541 2545 701 2535 + 445 1204 1222 1226 4129 + 446 3500 3386 4607 4615 + 447 1169 1168 1167 1163 + 448 1222 1621 1148 1204 + 449 3094 3300 705 4107 + 450 268 289 273 3447 + 451 1328 1282 1283 1326 + 452 3558 2984 3328 2722 + 453 3619 3587 446 3617 + 454 4205 3049 4443 4450 + 455 455 456 461 450 + 456 309 3888 298 3890 + 457 3100 3097 3103 3096 + 458 3713 3692 3695 435 + 459 704 3100 3103 3096 + 460 2798 2799 3223 4227 + 461 3185 3153 3039 4495 + 462 4062 2729 4103 4127 + 463 1117 1119 1738 4541 + 464 3732 3760 3845 3862 + 465 16 9 18 4126 + 466 1228 4161 4168 4188 + 467 3123 3125 3122 712 + 468 2645 3885 3188 2676 + 469 2481 2494 2476 2493 + 470 1362 1374 1368 1370 + 471 2950 2915 2949 2938 + 472 2276 2506 2488 4007 + 473 893 881 894 2771 + 474 1507 2681 2677 4549 + 475 1619 2579 1185 4182 + 476 1393 1401 4124 4232 + 477 742 819 1225 741 + 478 3273 3158 632 4181 + 479 583 2274 2288 4334 + 480 1376 1340 534 1377 + 481 3533 284 3527 3525 + 482 2684 433 2680 404 + 483 177 2006 168 2007 + 484 797 798 799 800 + 485 1365 1361 1384 1383 + 486 2826 3348 3761 3860 + 487 1333 1309 1332 1327 + 488 3923 2789 3092 4435 + 489 2345 2356 2344 4034 + 490 1650 1652 725 1649 + 491 924 926 943 942 + 492 644 3215 3177 3274 + 493 3187 3207 645 2675 + 494 4013 1018 4340 4345 + 495 1667 1240 1668 4351 + 496 2615 2598 2597 2641 + 497 2783 3485 3450 4385 + 498 2969 2851 2954 2835 + 499 3708 3712 3711 3710 + 500 438 1503 1502 4346 + 501 1516 1510 429 436 + 502 2889 332 338 322 + 503 2344 745 764 774 + 504 3776 3994 3979 3993 + 505 3224 2739 1262 3184 + 506 3743 3728 3799 3727 + 507 3611 3627 3617 3616 + 508 457 1476 462 4141 + 509 335 338 2899 326 + 510 2713 1046 3625 2705 + 511 308 3928 3927 312 + 512 717 718 2618 2627 + 513 2779 922 2770 2773 + 514 3120 3079 3169 3084 + 515 3048 3314 2753 3242 + 516 3130 1258 1271 4405 + 517 81 1937 139 133 + 518 2075 2074 2080 2076 + 519 3267 3292 3268 3301 + 520 338 321 322 319 + 521 1309 1291 1324 1332 + 522 3151 3183 4008 4475 + 523 1767 1142 1756 1143 + 524 1707 1240 1277 1701 + 525 2918 631 2917 4549 + 526 3223 3156 3039 3070 + 527 2900 2898 3344 2827 + 528 983 958 936 959 + 529 3184 3202 3289 4074 + 530 2008 2004 1992 2012 + 531 3188 640 645 4374 + 532 4155 1705 4389 4526 + 533 2964 2968 2916 2990 + 534 1382 1379 1380 1378 + 535 1237 348 345 4319 + 536 1322 1319 1321 1315 + 537 2885 2900 3276 2845 + 538 2889 332 2896 2900 + 539 3040 3058 4230 4418 + 540 1392 1129 1415 4349 + 541 2700 2938 784 785 + 542 392 2879 3948 2877 + 543 1759 1711 4014 4177 + 544 2865 2868 2860 2869 + 545 905 2768 2758 2742 + 546 3979 3776 3993 3986 + 547 2473 686 2468 684 + 548 3472 3471 3090 4293 + 549 75 2173 2167 400 + 550 2841 2959 2955 2958 + 551 835 870 2235 869 + 552 1336 1334 1344 1339 + 553 3412 3382 507 3413 + 554 3728 3736 3790 3777 + 555 680 675 2424 677 + 556 295 299 298 294 + 557 239 3752 3766 3862 + 558 255 266 3809 235 + 559 2964 344 340 2916 + 560 573 1337 572 565 + 561 3426 508 3381 3425 + 562 395 3913 3952 3948 + 563 3778 3789 3783 3980 + 564 3743 3737 234 3765 + 565 889 2776 881 2771 + 566 247 248 245 246 + 567 3799 3747 3748 3742 + 568 776 2657 2673 2670 + 569 3141 3139 3137 3142 + 570 2484 2487 2532 2531 + 571 3840 3866 3841 3836 + 572 2959 2841 2842 2958 + 573 2094 2088 38 36 + 574 3170 3169 3084 3159 + 575 3656 3436 3407 3413 + 576 3799 3737 3747 3742 + 577 1468 1525 1527 1465 + 578 841 2409 2414 2415 + 579 258 3818 260 3844 + 580 3565 3692 3693 3695 + 581 3812 3876 3730 3842 + 582 1379 1376 540 1380 + 583 263 264 267 262 + 584 3983 3970 3984 3798 + 585 419 418 3691 3712 + 586 1469 1459 1471 1451 + 587 4433 3936 4547 4651 + 588 3847 3864 3849 3867 + 589 1472 1467 1461 1462 + 590 3517 3466 287 288 + 591 371 361 3734 375 + 592 393 380 392 390 + 593 3800 3729 3802 3795 + 594 525 3357 3364 493 + 595 3946 3919 3915 398 + 596 3343 3189 3177 3176 + 597 922 2776 889 2771 + 598 2764 3240 3237 2746 + 599 1140 712 4381 4479 + 600 3505 3392 3493 529 + 601 388 389 396 391 + 602 3442 3432 3373 3431 + 603 3261 3254 871 3262 + 604 2733 2745 2735 4348 + 605 920 882 919 921 + 606 161 162 160 159 + 607 3733 373 3797 3739 + 608 1439 1397 1419 2061 + 609 2842 2841 2973 2832 + 610 340 357 350 358 + 611 2610 2462 2602 2604 + 612 2669 888 760 777 + 613 641 2671 3193 863 + 614 852 837 2759 851 + 615 978 941 947 1104 + 616 3187 641 645 863 + 617 4122 1003 4329 4379 + 618 3627 2145 3591 3592 + 619 3436 3412 507 3413 + 620 3801 371 367 368 + 621 2079 2081 2009 2078 + 622 889 888 881 886 + 623 1218 1217 1562 1579 + 624 894 881 2772 2771 + 625 4043 1719 4289 4725 + 626 3725 3797 3739 3724 + 627 739 740 4110 4496 + 628 3803 3800 359 3794 + 629 2792 882 920 2773 + 630 698 2550 701 2535 + 631 4313 4525 4571 4623 + 632 427 551 1384 1383 + 633 3464 3460 3461 3523 + 634 1965 1974 1967 1966 + 635 2416 2408 2419 844 + 636 3852 3098 3264 4123 + 637 3470 280 276 275 + 638 2938 2700 784 2939 + 639 2985 354 2987 2986 + 640 893 922 889 2771 + 641 2004 2006 2079 2001 + 642 2671 3192 861 2674 + 643 3384 3398 514 3395 + 644 2166 1543 1530 3561 + 645 2739 3037 3039 1262 + 646 1308 1300 1295 1164 + 647 862 3195 3192 4248 + 648 1212 1663 1138 1651 + 649 3008 3054 3049 4548 + 650 3953 2867 392 3947 + 651 3234 3233 3195 3253 + 652 41 2510 5 2518 + 653 251 258 3817 3818 + 654 3387 3500 3491 3388 + 655 2305 2231 2230 2293 + 656 2832 317 316 325 + 657 2715 2665 2644 2651 + 658 2925 2940 2927 2982 + 659 285 284 516 513 + 660 754 806 811 810 + 661 2831 3206 2848 2832 + 662 2401 2335 913 2343 + 663 2186 2188 2116 2115 + 664 855 2259 2243 878 + 665 63 2081 2082 2078 + 666 1058 1048 1051 2352 + 667 2174 2157 2154 2158 + 668 2605 2603 2606 2604 + 669 1128 1747 1398 4766 + 670 3196 3234 3247 3248 + 671 3657 3656 3658 3407 + 672 809 2694 788 2721 + 673 675 693 2432 677 + 674 685 2429 694 2428 + 675 2004 168 2008 1992 + 676 2334 885 892 2332 + 677 1473 1479 1483 1474 + 678 722 2642 720 663 + 679 2637 2595 2593 2613 + 680 799 2934 2938 2936 + 681 2259 2254 2256 2255 + 682 2208 2212 2213 2248 + 683 1246 1243 4319 4536 + 684 523 3358 3361 3363 + 685 2656 2693 2685 2687 + 686 2540 2589 2544 2590 + 687 2415 910 2412 899 + 688 1924 181 1934 1912 + 689 3233 868 3229 3241 + 690 1959 1984 1977 1979 + 691 1791 99 1781 1861 + 692 168 186 2008 1992 + 693 831 832 833 2379 + 694 1490 1488 1455 1452 + 695 2011 155 2010 2005 + 696 163 1998 1989 153 + 697 849 2380 2388 2372 + 698 2418 2416 2412 2395 + 699 1972 1964 1897 1903 + 700 1740 1113 1741 4223 + 701 211 228 225 164 + 702 2608 2595 2612 2613 + 703 2409 2371 840 2410 + 704 2403 2406 911 900 + 705 745 2356 765 778 + 706 3770 3977 240 243 + 707 2408 2396 2419 844 + 708 3555 1707 1277 1701 + 709 2090 2083 2091 2085 + 710 2401 913 2321 2343 + 711 710 3093 3700 3703 + 712 1142 1392 1413 1171 + 713 490 506 489 492 + 714 1375 2002 1381 1388 + 715 3104 3106 3655 3109 + 716 3506 3396 3391 3390 + 717 110 118 108 119 + 718 1079 1933 1914 1934 + 719 831 870 835 869 + 720 2401 2321 2400 2342 + 721 2771 2779 2770 2773 + 722 2874 2864 2883 383 + 723 2624 2554 2552 2564 + 724 2628 2638 2633 2634 + 725 893 2779 2771 2778 + 726 905 2732 2766 2742 + 727 2775 2774 917 2806 + 728 3526 3516 3527 3524 + 729 2350 2363 2349 1202 + 730 2439 2425 2437 2509 + 731 3238 3230 2747 2810 + 732 3928 3468 3892 3889 + 733 924 949 943 971 + 734 2052 2086 2092 7 + 735 3691 3719 3721 3722 + 736 2545 2541 698 699 + 737 2245 2244 2257 2234 + 738 3139 3087 3150 3144 + 739 2413 2415 2412 2411 + 740 2403 2405 2413 2394 + 741 1087 974 1086 4172 + 742 655 654 2614 671 + 743 2075 2093 2076 38 + 744 3101 3090 3473 4262 + 745 2603 2605 2602 2604 + 746 954 965 979 952 + 747 2238 2233 2249 2247 + 748 179 182 1932 1915 + 749 2462 2451 2610 2453 + 750 2089 2088 556 36 + 751 2231 2215 2230 2293 + 752 2076 2088 42 38 + 753 1262 3156 1090 1330 + 754 3546 3053 3538 981 + 755 2586 2591 1063 1065 + 756 2156 1541 1529 2150 + 757 1935 65 1941 70 + 758 2056 2055 2087 2058 + 759 2263 2264 2275 2274 + 760 1518 2653 4102 4346 + 761 3692 3708 3713 3695 + 762 1169 1168 1308 1167 + 763 1356 1337 1360 1355 + 764 1310 1332 1327 1329 + 765 2304 2290 2312 2303 + 766 2382 834 833 2383 + 767 1439 1420 1436 1504 + 768 2052 2083 1354 2092 + 769 2302 2227 2223 2301 + 770 3831 3819 3301 4176 + 771 1343 567 1336 1341 + 772 995 996 1331 1330 + 773 2633 2628 2634 2609 + 774 798 797 2906 790 + 775 4323 1669 4343 4562 + 776 3352 3331 3333 3182 + 777 1202 2346 749 2353 + 778 1256 3154 3121 3126 + 779 554 1337 1356 1355 + 780 875 876 2390 2369 + 781 1340 1345 1338 1339 + 782 2396 845 2419 844 + 783 2327 766 896 895 + 784 1389 1382 541 1379 + 785 1384 1361 1364 1362 + 786 1136 1234 3284 1233 + 787 1091 934 1088 4206 + 788 3927 3924 3474 3477 + 789 1760 1279 1761 1666 + 790 995 991 4096 4179 + 791 1688 1679 1684 1671 + 792 1675 1244 4509 4662 + 793 167 2079 2000 2009 + 794 3348 607 3270 4572 + 795 1628 1557 1627 4620 + 796 795 4026 4255 4362 + 797 2577 1195 1224 1194 + 798 1218 1560 1320 1219 + 799 3012 3086 3013 1305 + 800 3387 3386 3546 3402 + 801 15 453 461 32 + 802 1679 1678 1681 1688 + 803 1247 1680 1684 1687 + 804 952 966 947 1261 + 805 3112 710 4291 4305 + 806 1886 1926 1928 1931 + 807 642 644 4498 4685 + 808 639 632 2895 3272 + 809 466 33 2137 34 + 810 2002 539 186 535 + 811 1890 1896 1921 1895 + 812 1143 1171 1147 4157 + 813 553 3639 3666 3654 + 814 747 1048 1056 1058 + 815 913 914 912 2343 + 816 1032 1034 4134 4439 + 817 2401 2402 2343 2399 + 818 2172 2170 2173 2177 + 819 740 739 4341 4496 + 820 3015 3018 3011 3009 + 821 3309 3348 3276 3303 + 822 1275 1234 1257 3168 + 823 2894 2890 3271 1137 + 824 398 396 382 394 + 825 487 445 480 481 + 826 3168 1271 1232 4032 + 827 1560 814 1218 1320 + 828 1636 184 172 4089 + 829 2646 2645 2678 2676 + 830 881 2776 2772 2771 + 831 1500 1501 437 1519 + 832 311 3951 3944 4303 + 833 2681 2684 2680 1506 + 834 822 1646 1041 4089 + 835 3923 2785 3581 3583 + 836 1055 1056 2028 1057 + 837 2764 3237 2731 2746 + 838 1291 1302 1289 1296 + 839 3454 3077 3459 3456 + 840 726 644 3786 4713 + 841 814 1212 1138 1651 + 842 1054 1056 1631 1052 + 843 3936 2873 4433 4582 + 844 162 161 1988 159 + 845 2732 2766 2731 2730 + 846 1569 1641 1570 1568 + 847 3186 3185 3039 4495 + 848 924 943 945 942 + 849 741 1227 748 2354 + 850 3022 3179 3183 4006 + 851 3335 2838 2839 4456 + 852 1678 1679 1681 1687 + 853 2338 2322 2337 2330 + 854 1221 1189 1204 1205 + 855 3304 3242 3243 3308 + 856 3468 3892 3889 3891 + 857 2585 604 2536 2538 + 858 481 445 446 3617 + 859 1396 1437 1397 1438 + 860 2898 2900 3344 327 + 861 3318 3320 3551 464 + 862 3882 3985 645 2676 + 863 4138 1033 4457 4560 + 864 1685 345 4695 4705 + 865 3473 3930 4545 4610 + 866 1102 965 1106 1028 + 867 722 664 674 667 + 868 3018 3149 3010 3035 + 869 190 89 188 88 + 870 725 814 726 1137 + 871 1077 1042 4083 4208 + 872 1192 166 1191 165 + 873 3322 3341 3820 4451 + 874 779 770 762 769 + 875 3288 1249 1140 4022 + 876 1735 1266 4098 4364 + 877 1413 1142 1171 1143 + 878 2575 594 596 609 + 879 601 2546 2582 708 + 880 970 1005 966 971 + 881 3663 3637 3648 4090 + 882 1251 796 1236 1277 + 883 347 786 611 4021 + 884 753 1036 1631 84 + 885 2135 1192 1188 1191 + 886 1230 1609 1918 1184 + 887 1700 1116 1705 1701 + 888 2496 2497 4192 4521 + 889 1896 228 164 1922 + 890 2567 2576 598 2569 + 891 1604 2579 1229 1619 + 892 1930 1077 1919 1081 + 893 2894 2863 3283 1137 + 894 2113 151 2122 174 + 895 1745 1748 1398 4197 + 896 3072 3605 3594 4467 + 897 3330 3887 4119 4486 + 898 3291 3856 3819 3863 + 899 2662 1031 4281 4560 + 900 1276 1177 3169 1736 + 901 714 730 723 729 + 902 1641 1634 1569 1570 + 903 965 954 979 1099 + 904 1416 1405 1121 1120 + 905 2630 2642 663 2641 + 906 1749 4048 4155 4422 + 907 1192 1639 1139 4263 + 908 794 1153 1251 1154 + 909 2891 331 336 2861 + 910 2667 2781 4307 4385 + 911 1183 2579 1606 1197 + 912 1611 1230 1609 1918 + 913 2145 2125 2128 2126 + 914 2911 2908 4498 4761 + 915 974 1012 1009 931 + 916 4417 4269 4744 4763 + 917 3449 2781 2783 3450 + 918 3227 3304 3048 3242 + 919 2380 849 847 2372 + 920 3566 1704 1258 1259 + 921 4336 4327 4477 4609 + 922 1244 1714 1688 1245 + 923 2808 2740 2807 2809 + 924 3084 3079 2679 3078 + 925 1638 795 4255 4479 + 926 793 1639 4438 4684 + 927 65 1959 1977 1979 + 928 3186 3298 3200 3143 + 929 996 1330 1091 1088 + 930 2917 631 4068 4549 + 931 3171 3155 1256 1258 + 932 4075 2802 4387 4418 + 933 3298 3244 3200 3143 + 934 2110 2096 2108 2099 + 935 3146 3165 1333 3167 + 936 1720 1279 1280 1269 + 937 3584 3555 1277 1701 + 938 1025 981 1027 1699 + 939 4152 4177 4268 4749 + 940 1159 1112 1133 1120 + 941 979 954 958 1099 + 942 1682 1316 4319 4536 + 943 2402 2335 2401 2343 + 944 369 257 3874 368 + 945 1980 1945 1953 1979 + 946 2128 2134 2126 2127 + 947 1361 428 1364 1362 + 948 2573 2572 2586 1063 + 949 3120 713 3117 3159 + 950 1609 1183 1918 1184 + 951 1924 181 182 1934 + 952 933 1015 1016 928 + 953 1955 1956 1936 133 + 954 2363 2350 2349 2346 + 955 2552 2624 2564 2563 + 956 2638 2636 2633 2634 + 957 1168 1300 1308 1164 + 958 130 136 77 1953 + 959 3112 3640 4382 4552 + 960 2425 680 677 682 + 961 3083 1696 981 1027 + 962 2626 2621 2632 2620 + 963 794 1251 1236 1154 + 964 284 516 513 291 + 965 1458 1457 1456 1454 + 966 1688 1671 1245 4446 + 967 3656 3419 3436 3673 + 968 2335 914 913 2343 + 969 581 583 588 587 + 970 3165 3135 3148 3145 + 971 976 979 947 977 + 972 2626 2632 2640 2620 + 973 2421 2465 2433 2420 + 974 3150 3166 3033 3020 + 975 2037 2017 2015 2021 + 976 1752 1759 1711 4014 + 977 670 2596 2599 2611 + 978 666 670 2599 2611 + 979 2605 2610 2602 2604 + 980 2125 2145 2144 2126 + 981 1479 1483 3601 1484 + 982 2465 2422 2433 2420 + 983 3526 3532 286 3524 + 984 922 893 889 881 + 985 387 3912 364 365 + 986 3666 3702 3654 3635 + 987 2600 664 665 668 + 988 2142 2123 1481 2141 + 989 2965 2972 356 2963 + 990 221 199 217 188 + 991 2561 2543 2560 2555 + 992 2596 2600 2599 2611 + 993 140 130 136 77 + 994 1940 1939 1938 1941 + 995 1972 1982 1961 1963 + 996 3498 3510 520 3393 + 997 81 1957 1953 1958 + 998 1031 2658 1032 4560 + 999 3996 3982 3973 3981 + 1000 1460 1464 1463 1465 + 1001 3671 3639 3654 3718 + 1002 2513 2507 2527 681 + 1003 170 48 148 174 + 1004 2410 2418 2377 2378 + 1005 2200 127 98 106 + 1006 896 2342 2359 2337 + 1007 665 666 2593 668 + 1008 127 2200 107 106 + 1009 767 745 765 778 + 1010 1122 1093 1104 1105 + 1011 3587 3590 3591 54 + 1012 3513 3508 3394 515 + 1013 336 337 335 338 + 1014 3553 3318 466 464 + 1015 901 914 911 912 + 1016 2402 2393 2343 2399 + 1017 1669 1721 4080 4323 + 1018 2323 2340 896 2337 + 1019 2615 2630 2597 2631 + 1020 2546 604 2585 2548 + 1021 1899 1896 1902 1922 + 1022 221 1887 1908 1888 + 1023 2537 2589 2584 2586 + 1024 389 395 3879 393 + 1025 3318 3320 3553 586 + 1026 1488 478 477 1455 + 1027 3142 3087 3026 3021 + 1028 169 2120 2111 2102 + 1029 1238 794 1675 1236 + 1030 100 1984 1959 1943 + 1031 1964 189 1897 187 + 1032 3517 3524 281 288 + 1033 1961 1982 1960 1963 + 1034 23 29 54 2147 + 1035 50 2061 12 2022 + 1036 2993 3328 3894 4730 + 1037 463 479 1485 483 + 1038 248 246 3770 240 + 1039 3579 401 3543 3578 + 1040 3657 3656 3673 3658 + 1041 3755 3752 3744 3754 + 1042 489 3382 3381 499 + 1043 2096 2098 1478 1482 + 1044 3361 3357 3364 3363 + 1045 59 2476 40 2520 + 1046 58 57 2484 2482 + 1047 616 2207 2213 2209 + 1048 1466 417 1526 1527 + 1049 60 2529 2516 2513 + 1050 1810 1807 1812 124 + 1051 2160 104 417 1526 + 1052 1837 1877 1874 1835 + 1053 3560 1543 406 403 + 1054 3419 3423 3436 3435 + 1055 615 2262 856 630 + 1056 1449 1447 486 1456 + 1057 2498 1851 1846 1845 + 1058 2132 2172 1843 2176 + 1059 1784 1778 1780 1823 + 1060 2440 2435 2441 2442 + 1061 115 137 1949 1950 + 1062 2421 2420 2426 2424 + 1063 654 657 670 673 + 1064 134 1827 110 114 + 1065 2628 2630 715 716 + 1066 3656 3657 3673 3664 + 1067 203 147 1881 144 + 1068 11 2086 3 7 + 1069 2619 2616 2639 2642 + 1070 2489 2501 2484 2503 + 1071 2481 2482 2476 2523 + 1072 653 2534 2452 2614 + 1073 1077 1080 4083 4603 + 1074 416 3579 3694 3578 + 1075 3694 3579 3547 3707 + 1076 416 3694 399 435 + 1077 2093 2084 2088 2094 + 1078 2529 2526 2533 2530 + 1079 615 2246 630 629 + 1080 1834 1850 1841 1838 + 1081 2448 2422 2446 2423 + 1082 2314 638 634 2218 + 1083 870 2245 835 2235 + 1084 1533 1530 1538 1540 + 1085 815 1191 4055 4314 + 1086 1534 1544 1524 400 + 1087 2712 2668 2715 780 + 1088 2966 2974 2834 2952 + 1089 1734 1725 4515 4670 + 1090 3148 3028 3166 3027 + 1091 3589 3585 3586 444 + 1092 3579 416 3550 3543 + 1093 752 1649 84 4697 + 1094 505 20 49 47 + 1095 3630 3652 3669 3668 + 1096 799 2938 784 2936 + 1097 2132 2129 403 2133 + 1098 1543 2166 406 403 + 1099 1854 1855 1849 2500 + 1100 533 515 514 3395 + 1101 1535 1534 1533 1544 + 1102 237 239 3848 236 + 1103 76 2172 2132 2176 + 1104 257 3837 3844 3838 + 1105 2532 2514 2533 2530 + 1106 2527 2513 681 41 + 1107 3172 3056 3134 3138 + 1108 3660 543 553 568 + 1109 2953 2951 350 358 + 1110 250 252 253 256 + 1111 1726 1771 1732 1735 + 1112 3232 3228 2738 2802 + 1113 3436 507 3407 3413 + 1114 1464 410 417 1465 + 1115 3423 3443 558 571 + 1116 521 3398 3361 3396 + 1117 1829 147 203 145 + 1118 399 3693 3707 3564 + 1119 266 3811 3822 3832 + 1120 242 3970 3989 3772 + 1121 3361 3362 532 510 + 1122 75 1544 2173 400 + 1123 376 3780 359 3775 + 1124 399 3694 3695 435 + 1125 3642 3672 3639 3671 + 1126 3675 3697 3698 3677 + 1127 3847 264 263 3816 + 1128 239 232 3766 238 + 1129 3149 3148 3166 3027 + 1130 3682 3631 3704 423 + 1131 3649 3642 3643 3668 + 1132 3456 3459 4150 4175 + 1133 3642 553 545 546 + 1134 3631 3649 3643 3632 + 1135 2930 2901 2697 2929 + 1136 3493 3534 3492 3531 + 1137 374 376 359 372 + 1138 555 3660 568 3658 + 1139 3642 3671 3649 3643 + 1140 474 471 475 473 + 1141 3419 555 558 537 + 1142 3382 3415 3381 3425 + 1143 543 3660 3653 568 + 1144 3442 3433 3432 3431 + 1145 251 3857 3853 370 + 1146 3656 3419 3424 555 + 1147 553 3702 546 3654 + 1148 3631 3649 3630 3704 + 1149 3840 3814 3864 253 + 1150 2779 922 893 2771 + 1151 3147 3036 3145 3031 + 1152 3642 3649 3630 3668 + 1153 3392 3505 3391 3397 + 1154 3854 3846 3858 3753 + 1155 1424 1423 1353 1421 + 1156 3444 3421 3424 3420 + 1157 3630 3649 3652 3668 + 1158 3814 252 3829 253 + 1159 3173 1030 1029 980 + 1160 570 3424 3420 537 + 1161 3834 3809 3822 3832 + 1162 2059 1425 1421 498 + 1163 2050 1353 1421 1434 + 1164 773 776 780 755 + 1165 543 570 558 568 + 1166 3660 3672 543 3653 + 1167 3631 3682 3698 3696 + 1168 1535 1534 1543 1530 + 1169 3574 3694 3547 3564 + 1170 3713 3683 3678 435 + 1171 3848 3749 3751 3851 + 1172 2169 1844 2132 2176 + 1173 545 3649 546 3704 + 1174 3983 3728 3750 3799 + 1175 3702 546 3704 547 + 1176 3691 3709 3711 3710 + 1177 3579 3694 3695 3707 + 1178 3719 3677 3721 3720 + 1179 3887 2971 4139 4368 + 1180 3677 3697 3676 3678 + 1181 2135 1629 1223 1044 + 1182 3380 3045 3080 3066 + 1183 419 3711 547 3635 + 1184 3750 3983 3799 3748 + 1185 1835 1877 1882 1880 + 1186 418 3719 3691 3712 + 1187 3698 3682 3683 422 + 1188 418 3719 3712 3708 + 1189 3987 3970 3971 3798 + 1190 3702 419 547 3635 + 1191 2630 2631 667 716 + 1192 3079 3120 3169 2682 + 1193 3871 3864 3849 3866 + 1194 3691 419 3712 3711 + 1195 3957 3999 3978 3975 + 1196 3574 3568 3570 3674 + 1197 419 418 3712 3713 + 1198 2196 2195 2198 117 + 1199 247 248 3745 3989 + 1200 2159 2198 2161 117 + 1201 3656 555 568 3658 + 1202 3643 3671 3722 3641 + 1203 251 3810 3818 3853 + 1204 3649 3671 546 3704 + 1205 3579 416 3547 3550 + 1206 1090 932 1088 4206 + 1207 3719 3692 3708 3720 + 1208 1532 1522 1521 1531 + 1209 3135 3028 3148 3027 + 1210 3692 3719 3693 3720 + 1211 3987 3778 3789 3792 + 1212 3649 3643 3632 3668 + 1213 3747 3750 3748 3751 + 1214 1778 1818 1785 1780 + 1215 3649 3642 3630 545 + 1216 261 3847 3872 3815 + 1217 3672 3660 543 3651 + 1218 3746 3970 4000 3772 + 1219 3671 3642 3649 546 + 1220 3649 3642 545 546 + 1221 3642 3639 553 546 + 1222 3639 3642 3671 546 + 1223 3970 3746 3989 3772 + 1224 3702 553 546 547 + 1225 3697 3698 3677 3676 + 1226 3719 3691 3712 3710 + 1227 546 545 3704 423 + 1228 3649 3631 3643 3704 + 1229 3149 3009 3033 3035 + 1230 1534 1543 1530 406 + 1231 3691 419 3711 3721 + 1232 3130 1271 3168 4032 + 1233 2157 2175 1539 2174 + 1234 3671 3649 3643 3704 + 1235 3713 418 424 435 + 1236 3709 3691 3722 3718 + 1237 1534 3560 1543 406 + 1238 3671 3702 546 3704 + 1239 3702 3671 3643 3704 + 1240 3755 237 239 3848 + 1241 3139 3136 3144 3166 + 1242 242 3778 3974 240 + 1243 3737 231 3799 3747 + 1244 1356 1350 1357 1358 + 1245 3643 3702 3704 547 + 1246 3721 3677 3676 3641 + 1247 1541 1529 2150 1520 + 1248 3737 3743 234 3751 + 1249 3671 3721 3722 3641 + 1250 3643 3632 3676 3641 + 1251 399 3569 415 3687 + 1252 3083 3582 3583 4019 + 1253 416 3574 3694 3547 + 1254 3550 3547 3542 3540 + 1255 3579 416 3694 3547 + 1256 3087 3150 3144 3034 + 1257 3998 3982 244 3969 + 1258 525 3357 493 3363 + 1259 3708 3712 3713 3695 + 1260 3983 3728 3799 3798 + 1261 3547 3565 3707 3542 + 1262 3569 3683 415 3687 + 1263 2963 2977 2961 2981 + 1264 3433 3412 3436 3435 + 1265 3709 3711 3654 3635 + 1266 3694 399 3695 3707 + 1267 3109 4003 4105 4107 + 1268 3574 416 402 3575 + 1269 3547 3579 3550 3542 + 1270 402 416 401 3575 + 1271 1532 1522 1531 1524 + 1272 1522 1544 1531 1524 + 1273 3547 3565 3693 3707 + 1274 1533 1534 1530 406 + 1275 413 1522 1542 1523 + 1276 3135 3162 3148 3166 + 1277 730 721 732 723 + 1278 3683 3713 3720 424 + 1279 3847 3849 3872 3816 + 1280 3736 247 3745 3989 + 1281 1349 1424 1428 1434 + 1282 3694 3574 3569 3564 + 1283 1332 1333 1327 1329 + 1284 3574 416 3575 3550 + 1285 416 401 3575 3550 + 1286 416 402 401 399 + 1287 3994 302 3979 3973 + 1288 3983 3736 247 3745 + 1289 399 3570 415 435 + 1290 399 3568 3569 3687 + 1291 3708 3692 3565 3695 + 1292 1029 1255 980 4252 + 1293 419 3682 3721 3676 + 1294 2451 2462 671 656 + 1295 2090 2084 2094 2091 + 1296 3828 3817 3818 3832 + 1297 3568 3571 3569 3687 + 1298 2959 353 2955 317 + 1299 3387 3500 3386 4723 + 1300 3979 302 303 301 + 1301 3855 3726 238 3845 + 1302 416 3574 402 3569 + 1303 1397 2063 2062 50 + 1304 1437 503 1397 1439 + 1305 2975 2834 2835 2836 + 1306 3906 402 3571 3569 + 1307 3813 264 233 3815 + 1308 3570 3568 415 3687 + 1309 402 399 3569 415 + 1310 300 2816 4077 4219 + 1311 231 3750 3799 3747 + 1312 3542 3541 3543 3540 + 1313 3574 3575 3571 3567 + 1314 3574 3568 3571 3569 + 1315 3854 261 3872 3815 + 1316 1502 1513 1519 4365 + 1317 2950 2925 2915 2939 + 1318 3784 3759 3731 3760 + 1319 3570 3568 3687 3674 + 1320 3573 3903 3577 3906 + 1321 3694 416 399 3569 + 1322 3568 399 415 3687 + 1323 4383 3519 4415 4471 + 1324 3683 424 415 435 + 1325 3417 3414 3503 4112 + 1326 3957 3998 244 3990 + 1327 2132 76 2176 403 + 1328 3156 2798 3226 4042 + 1329 3683 415 3678 435 + 1330 3568 399 3569 3564 + 1331 2735 3232 2802 4118 + 1332 2176 76 2166 403 + 1333 263 265 267 253 + 1334 490 523 509 494 + 1335 3850 3755 3848 3865 + 1336 104 417 409 116 + 1337 3420 3421 3670 3652 + 1338 3672 3652 3669 3651 + 1339 3046 3047 3143 4075 + 1340 230 3737 3851 236 + 1341 140 139 1958 133 + 1342 1354 1348 2089 1355 + 1343 237 232 239 264 + 1344 3508 3398 3395 3390 + 1345 3848 3850 3865 3847 + 1346 3398 3506 531 3390 + 1347 3912 3960 3972 3911 + 1348 2665 2712 2715 2670 + 1349 3421 3670 3652 3669 + 1350 3814 3871 3824 3816 + 1351 2949 2701 2691 2933 + 1352 264 261 233 3815 + 1353 2700 2934 2938 785 + 1354 570 543 545 3651 + 1355 3045 3380 3080 3044 + 1356 3579 3550 3542 3543 + 1357 91 476 1470 1450 + 1358 417 1466 1526 409 + 1359 3752 3755 239 3848 + 1360 545 3630 3704 3651 + 1361 3022 3046 3200 4075 + 1362 231 3737 234 230 + 1363 3692 3719 3708 3710 + 1364 532 3384 533 514 + 1365 3643 3702 547 3722 + 1366 3693 3570 3720 3564 + 1367 3987 3746 248 3989 + 1368 1437 503 1439 502 + 1369 3766 3855 238 3845 + 1370 4155 4389 4422 4526 + 1371 3736 231 3737 3747 + 1372 2944 2947 2925 2936 + 1373 399 3694 3569 3564 + 1374 3728 3983 3777 3798 + 1375 3575 3571 3567 3576 + 1376 402 3574 3571 3569 + 1377 3547 3693 3550 3564 + 1378 3698 3682 422 3676 + 1379 3547 3694 3707 3564 + 1380 1350 1348 1429 1357 + 1381 349 353 356 2955 + 1382 3983 3799 3748 3742 + 1383 546 3704 547 423 + 1384 3987 3728 3792 3798 + 1385 2964 2983 2961 2981 + 1386 3677 3698 422 3676 + 1387 255 3821 267 235 + 1388 3970 3778 3971 242 + 1389 2976 2851 351 317 + 1390 3971 3996 3982 3969 + 1391 3677 3719 3721 3676 + 1392 3989 242 3772 3990 + 1393 2944 2947 2924 2922 + 1394 2923 798 790 802 + 1395 3412 3416 3436 3435 + 1396 554 563 1358 562 + 1397 3691 419 3721 3676 + 1398 401 3906 3571 3576 + 1399 2927 2940 804 2982 + 1400 3859 3873 3825 3823 + 1401 3982 3999 301 3981 + 1402 2003 2002 540 1380 + 1403 2309 637 2300 2297 + 1404 3999 3998 3982 244 + 1405 3719 3691 3721 3676 + 1406 418 3677 422 3676 + 1407 233 3859 235 262 + 1408 3213 2823 2825 2820 + 1409 3778 242 3770 240 + 1410 2923 2980 2924 2986 + 1411 239 230 3766 236 + 1412 3597 3451 3603 4325 + 1413 237 3865 261 236 + 1414 3728 3777 3792 3798 + 1415 3747 3737 3751 3742 + 1416 3571 3568 3567 3576 + 1417 3750 3799 3747 3748 + 1418 402 401 3906 3571 + 1419 231 3736 3750 3747 + 1420 1542 1522 1524 1523 + 1421 791 341 2978 801 + 1422 3728 3736 3983 3750 + 1423 1424 1425 1421 1434 + 1424 2951 2972 356 2952 + 1425 3744 3749 3754 3851 + 1426 3749 3848 3754 3851 + 1427 230 3744 3754 3851 + 1428 3744 230 3751 3851 + 1429 3847 264 261 263 + 1430 239 237 230 236 + 1431 239 3752 3848 236 + 1432 3750 3984 3746 3745 + 1433 979 982 980 4252 + 1434 3847 263 3867 3816 + 1435 791 354 341 355 + 1436 2972 2965 356 2952 + 1437 3790 3756 3777 3791 + 1438 3744 234 230 3726 + 1439 254 266 3817 3832 + 1440 2140 3589 442 3590 + 1441 3871 3814 3864 3866 + 1442 1532 1541 1521 1537 + 1443 797 2947 798 2906 + 1444 2969 2825 2824 2835 + 1445 2947 2944 798 2906 + 1446 1433 1435 1426 498 + 1447 3749 3744 3754 3742 + 1448 3871 3814 3824 3864 + 1449 230 3737 3751 3851 + 1450 3398 3384 3399 3395 + 1451 1959 67 1943 1986 + 1452 3814 3840 3866 3841 + 1453 3412 3415 3382 3413 + 1454 3729 361 3733 3730 + 1455 1470 476 1446 1450 + 1456 232 3854 237 3753 + 1457 3128 3115 3130 4291 + 1458 3643 3642 3669 3668 + 1459 3346 3199 3345 3217 + 1460 2944 2925 2927 2936 + 1461 3835 3837 3841 3836 + 1462 558 570 537 568 + 1463 512 285 520 519 + 1464 4246 3634 4409 4633 + 1465 3821 3824 3825 3829 + 1466 3824 3871 3864 265 + 1467 3361 3396 3357 3359 + 1468 15 32 2109 14 + 1469 528 3505 3508 3397 + 1470 3444 3421 3420 3652 + 1471 3850 3755 3872 3754 + 1472 1475 1477 457 1476 + 1473 3442 3443 560 3373 + 1474 3871 3814 3866 3841 + 1475 3840 3814 252 3841 + 1476 3432 3442 3373 3374 + 1477 2780 2669 763 2650 + 1478 3481 3470 280 3480 + 1479 1336 1335 1346 1344 + 1480 2965 2972 2974 2952 + 1481 3809 3821 3834 3829 + 1482 788 809 2721 783 + 1483 3506 521 522 530 + 1484 3840 3864 3867 265 + 1485 483 1485 1487 1453 + 1486 3866 3835 3841 3836 + 1487 3824 3829 267 253 + 1488 3765 3766 238 3845 + 1489 3864 3871 3867 265 + 1490 3871 3824 3825 265 + 1491 2864 2866 2874 2876 + 1492 3914 3912 364 3972 + 1493 3367 3374 3358 3360 + 1494 1529 1541 1528 1520 + 1495 3366 525 3383 510 + 1496 3361 3358 3357 3363 + 1497 3433 3442 3443 560 + 1498 3423 3433 3443 560 + 1499 443 463 1485 483 + 1500 1464 474 1463 475 + 1501 3087 3139 3150 3142 + 1502 3415 3416 3412 3382 + 1503 3513 3512 3385 3394 + 1504 560 3423 501 571 + 1505 3506 3398 521 3396 + 1506 490 506 508 489 + 1507 1394 1395 1416 1418 + 1508 1458 485 482 1456 + 1509 1529 414 104 409 + 1510 3711 3691 3721 3722 + 1511 2180 2178 2187 2183 + 1512 3864 3871 3849 3867 + 1513 2648 2647 2708 2714 + 1514 1978 1942 1959 1943 + 1515 490 506 492 494 + 1516 3415 3426 3416 3381 + 1517 2160 2159 417 2199 + 1518 1293 1282 1326 1301 + 1519 1160 1174 1158 4519 + 1520 3423 3433 3436 3435 + 1521 3374 490 3367 494 + 1522 756 2654 2673 2672 + 1523 3357 3367 3364 493 + 1524 3702 547 3722 3635 + 1525 2940 2921 2939 804 + 1526 3374 3373 506 3367 + 1527 882 2792 920 919 + 1528 506 3431 508 489 + 1529 3840 252 3839 3836 + 1530 3690 3405 3684 3088 + 1531 3377 3384 3385 3399 + 1532 252 3840 3841 3836 + 1533 3837 3835 3839 3836 + 1534 135 107 106 2202 + 1535 2078 2082 2073 2077 + 1536 2635 2626 2632 2640 + 1537 3373 3442 3431 500 + 1538 1489 1486 2121 1493 + 1539 3801 3876 3875 3874 + 1540 3384 3398 533 514 + 1541 1447 1449 1446 1450 + 1542 449 170 175 148 + 1543 3727 3728 3777 3792 + 1544 3653 3673 568 3658 + 1545 82 75 2173 2167 + 1546 1543 3560 2132 403 + 1547 1541 2156 1529 414 + 1548 1722 1761 1666 1672 + 1549 3144 3150 3033 3034 + 1550 2156 2197 1529 414 + 1551 3632 3697 3676 3696 + 1552 3657 3653 3673 3664 + 1553 1447 1448 1469 1450 + 1554 3305 3314 3200 3304 + 1555 297 3887 584 3883 + 1556 683 660 662 688 + 1557 3698 3682 3676 3696 + 1558 3667 3653 3657 3664 + 1559 3653 3672 3673 3664 + 1560 3426 3415 3416 3412 + 1561 1544 1524 400 412 + 1562 2430 2455 2466 2464 + 1563 75 400 406 412 + 1564 485 482 1456 1454 + 1565 1467 1460 474 1463 + 1566 3653 3657 3658 3407 + 1567 1859 1863 1870 1867 + 1568 3709 3654 3718 3635 + 1569 657 2605 669 651 + 1570 123 118 119 102 + 1571 2421 2468 2467 2469 + 1572 1534 1535 1533 1530 + 1573 2163 2196 2160 2199 + 1574 1533 1535 1544 1531 + 1575 1524 1542 400 412 + 1576 1522 1532 1542 1524 + 1577 1885 1954 138 103 + 1578 2188 2179 2189 2115 + 1579 1844 2131 2166 2165 + 1580 3656 3419 555 3407 + 1581 2175 82 2173 2171 + 1582 2150 2156 2174 2153 + 1583 1542 1532 75 412 + 1584 1532 141 1522 1542 + 1585 1543 3560 1844 2132 + 1586 3358 3367 3357 493 + 1587 113 1805 123 119 + 1588 476 1470 470 468 + 1589 1810 121 123 124 + 1590 1447 1449 486 1446 + 1591 2205 1467 474 1472 + 1592 3423 3419 3424 3670 + 1593 3424 570 3420 3670 + 1594 3362 3366 3363 3376 + 1595 3513 3510 3498 3512 + 1596 3421 3424 3420 3670 + 1597 2679 3084 3078 3043 + 1598 3086 3001 4333 4445 + 1599 3527 3516 3515 3511 + 1600 3431 508 489 3416 + 1601 152 156 2116 2115 + 1602 474 1460 1464 1463 + 1603 463 1485 1491 1487 + 1604 1797 1865 1798 1799 + 1605 1448 1447 486 1446 + 1606 3945 3918 3915 3920 + 1607 1297 1295 1299 1293 + 1608 2080 2076 2088 1341 + 1609 3600 456 455 450 + 1610 525 508 509 491 + 1611 91 2180 2182 2183 + 1612 1528 1466 1526 1527 + 1613 418 3682 422 421 + 1614 1448 1447 1446 1450 + 1615 508 489 3416 3381 + 1616 3416 489 3382 3381 + 1617 3415 3416 3382 3381 + 1618 3642 3671 3643 3641 + 1619 1805 1807 1813 1810 + 1620 3089 4291 4575 4641 + 1621 1467 1460 1463 1461 + 1622 1467 1463 1472 1461 + 1623 2100 1477 1478 1482 + 1624 509 508 3425 491 + 1625 1467 2205 1460 1464 + 1626 2150 1541 1520 1537 + 1627 1448 1469 1450 1471 + 1628 3736 3750 3747 3745 + 1629 1466 1528 1526 1520 + 1630 1868 1788 1856 1867 + 1631 3656 555 3658 3407 + 1632 474 1467 1463 1472 + 1633 3609 2784 3628 4466 + 1634 129 1778 1818 1785 + 1635 3512 3498 3497 3393 + 1636 76 2172 2176 2165 + 1637 3430 509 3425 491 + 1638 1956 1955 1958 133 + 1639 454 1473 1484 1482 + 1640 485 478 482 1454 + 1641 2114 151 2122 2119 + 1642 1486 1492 1495 1493 + 1643 1459 1470 1446 1450 + 1644 1491 1486 1487 1453 + 1645 1472 1463 475 1451 + 1646 3366 3378 3425 491 + 1647 1466 417 1527 1465 + 1648 3436 501 507 499 + 1649 1826 1827 1828 1786 + 1650 4003 3300 4105 4107 + 1651 2134 2125 466 2137 + 1652 2897 2892 2896 2899 + 1653 2194 2204 2191 2192 + 1654 3378 3430 3425 491 + 1655 443 449 463 483 + 1656 3366 3377 3383 3384 + 1657 3433 3423 3443 3441 + 1658 3419 3436 558 3407 + 1659 2194 2205 2198 2161 + 1660 2186 2188 2179 2178 + 1661 3660 3702 553 3666 + 1662 3164 3162 3028 3166 + 1663 476 1448 1446 1450 + 1664 2098 2100 1478 1482 + 1665 2474 2472 2463 2453 + 1666 3600 1480 456 450 + 1667 2669 2657 2649 2650 + 1668 3506 3398 531 521 + 1669 77 1945 126 70 + 1670 2415 909 910 898 + 1671 3028 3166 3027 3033 + 1672 1470 485 468 192 + 1673 3162 3164 3148 3166 + 1674 273 3523 3447 3463 + 1675 1489 483 1453 2184 + 1676 1485 443 483 1487 + 1677 1475 457 1494 1474 + 1678 881 888 2811 886 + 1679 1492 1477 1478 2121 + 1680 170 1489 449 1486 + 1681 463 1491 1486 1487 + 1682 2107 45 15 14 + 1683 280 308 275 277 + 1684 463 443 1485 1487 + 1685 449 463 1486 1487 + 1686 449 443 463 1487 + 1687 1489 449 1486 1487 + 1688 1393 1394 1120 4589 + 1689 2186 2188 2185 2116 + 1690 2163 2196 2152 2160 + 1691 1449 486 485 1456 + 1692 2113 2112 2122 2119 + 1693 1473 457 1474 460 + 1694 3589 3590 3585 444 + 1695 48 170 169 173 + 1696 389 3879 3910 3954 + 1697 3600 3589 3590 3585 + 1698 3102 3158 726 4243 + 1699 2977 2965 2963 2961 + 1700 3958 644 4016 4069 + 1701 286 282 281 288 + 1702 3943 385 386 3935 + 1703 3927 3934 3950 3474 + 1704 308 3468 3889 3891 + 1705 1414 1418 1411 4287 + 1706 3958 3869 3764 4028 + 1707 2110 2096 2099 2097 + 1708 2861 2870 2893 2859 + 1709 1495 1492 1475 1476 + 1710 2780 2669 760 777 + 1711 295 299 296 298 + 1712 307 308 3481 275 + 1713 289 290 272 283 + 1714 2902 2892 2857 2858 + 1715 1492 1477 1475 1476 + 1716 1459 1472 1469 1450 + 1717 121 132 118 123 + 1718 309 308 312 305 + 1719 922 920 2773 890 + 1720 285 284 513 512 + 1721 3358 3367 493 3404 + 1722 718 717 2618 723 + 1723 361 373 371 372 + 1724 3796 360 3902 378 + 1725 2138 2128 55 2147 + 1726 454 1473 1474 460 + 1727 151 170 2114 2122 + 1728 13 453 3600 2143 + 1729 31 1477 2096 2097 + 1730 1480 453 3600 456 + 1731 3601 3600 3585 3586 + 1732 1480 1479 453 456 + 1733 2100 1480 2142 1481 + 1734 3494 3509 3495 3496 + 1735 3959 3912 3960 365 + 1736 1516 1371 1369 420 + 1737 478 479 477 1455 + 1738 3516 3517 3524 281 + 1739 3495 3493 3492 3531 + 1740 289 272 287 273 + 1741 523 525 3366 510 + 1742 469 3553 467 464 + 1743 920 919 904 921 + 1744 3589 1480 2123 1481 + 1745 3521 3524 274 288 + 1746 2892 337 338 2899 + 1747 3918 3919 3915 3920 + 1748 324 315 4350 4492 + 1749 128 1824 109 111 + 1750 361 366 362 372 + 1751 1984 100 1978 1943 + 1752 1485 463 1491 1496 + 1753 2404 2407 2341 883 + 1754 1309 1288 1327 1326 + 1755 666 2607 2608 2611 + 1756 283 3521 274 288 + 1757 3553 466 467 464 + 1758 3328 3558 2722 2706 + 1759 3470 280 3469 276 + 1760 292 3934 279 293 + 1761 3968 3920 388 397 + 1762 3877 3839 257 3838 + 1763 3460 3464 273 3479 + 1764 3818 260 3843 259 + 1765 3780 3959 376 363 + 1766 1054 1036 1631 4359 + 1767 286 283 291 274 + 1768 527 511 3496 529 + 1769 330 2897 2857 2861 + 1770 2767 3236 3256 3229 + 1771 3945 3946 3915 398 + 1772 3909 3879 3910 3952 + 1773 307 308 275 305 + 1774 381 329 385 2893 + 1775 389 3945 398 394 + 1776 486 485 1456 1454 + 1777 2099 2101 2111 2102 + 1778 2541 2562 2553 2535 + 1779 3913 3919 382 394 + 1780 3460 3481 3475 3480 + 1781 3448 3489 3447 3479 + 1782 3920 388 397 396 + 1783 398 3946 382 3935 + 1784 373 374 359 372 + 1785 1641 1570 1642 1635 + 1786 3018 3017 3010 3020 + 1787 3358 3361 3357 3359 + 1788 3729 3802 3795 3758 + 1789 3734 361 3724 375 + 1790 3918 3968 3920 3954 + 1791 3534 284 517 291 + 1792 3912 387 3960 365 + 1793 2874 379 384 383 + 1794 3919 3918 3953 3947 + 1795 3918 3945 3915 398 + 1796 905 837 2766 827 + 1797 290 3458 3445 3447 + 1798 3879 3909 3910 3954 + 1799 3196 3247 2912 2913 + 1800 388 397 396 378 + 1801 3961 3959 363 365 + 1802 525 3366 3383 491 + 1803 3963 3796 3902 378 + 1804 531 3506 530 3390 + 1805 3672 543 3653 3673 + 1806 3466 3517 281 288 + 1807 3964 3963 3796 3962 + 1808 3734 3723 3724 3857 + 1809 3534 3533 516 3492 + 1810 3526 284 3532 3525 + 1811 2732 2743 2766 2742 + 1812 3945 389 3920 396 + 1813 3945 3920 397 396 + 1814 3494 3505 528 529 + 1815 360 366 3902 378 + 1816 3734 371 367 3842 + 1817 780 773 755 887 + 1818 3779 3780 3959 3912 + 1819 511 3493 3496 529 + 1820 3506 521 530 3404 + 1821 388 3963 397 378 + 1822 3526 3527 3515 3525 + 1823 389 388 387 391 + 1824 387 3909 3960 391 + 1825 389 3879 3909 391 + 1826 388 3909 387 391 + 1827 2237 2212 2261 2260 + 1828 3361 3362 510 3363 + 1829 284 286 3515 281 + 1830 484 157 150 174 + 1831 519 512 3497 3393 + 1832 3510 3498 3512 3393 + 1833 3912 3961 364 365 + 1834 1699 1768 4515 4670 + 1835 3973 3993 3981 3986 + 1836 3532 286 3521 291 + 1837 3377 3378 3366 3376 + 1838 527 3494 528 529 + 1839 3367 506 494 493 + 1840 3457 290 3458 3445 + 1841 3443 3423 560 571 + 1842 2947 797 2936 2932 + 1843 2740 2743 2809 2742 + 1844 1265 1263 4275 4395 + 1845 283 272 291 274 + 1846 2758 918 851 2809 + 1847 511 3493 529 3492 + 1848 526 3505 528 3397 + 1849 2113 2122 150 174 + 1850 3392 3494 3496 529 + 1851 3918 389 3910 3954 + 1852 3507 3509 3495 3525 + 1853 3150 3087 3021 3020 + 1854 528 3513 520 515 + 1855 526 528 533 515 + 1856 3752 3755 3815 3753 + 1857 774 1049 1201 2352 + 1858 3517 287 274 288 + 1859 525 3430 3364 3363 + 1860 525 3364 509 493 + 1861 509 3364 494 493 + 1862 360 388 378 364 + 1863 3416 3426 3430 3429 + 1864 3087 3034 3010 2999 + 1865 3527 518 3515 282 + 1866 845 2416 2419 844 + 1867 3018 3000 3071 2998 + 1868 251 3818 3843 259 + 1869 3960 3909 3972 3911 + 1870 167 2006 2079 2009 + 1871 2775 917 2744 2806 + 1872 2404 884 908 883 + 1873 526 3505 527 528 + 1874 3510 3513 3508 3394 + 1875 3533 3532 3534 3525 + 1876 3377 3362 3366 3383 + 1877 4571 4525 4737 4741 + 1878 3959 376 363 365 + 1879 457 1494 1474 460 + 1880 1485 1491 1487 1453 + 1881 2685 805 2687 2711 + 1882 521 3361 532 510 + 1883 3362 3383 532 510 + 1884 3362 3366 3383 510 + 1885 3367 3358 3357 3360 + 1886 3450 4385 4508 4674 + 1887 3498 520 515 519 + 1888 604 709 2537 2590 + 1889 501 3423 558 571 + 1890 3431 3364 3434 3429 + 1891 508 3426 3434 3429 + 1892 3416 3431 3434 3429 + 1893 3431 3416 3435 3429 + 1894 478 1458 482 1454 + 1895 490 3364 509 3429 + 1896 500 560 501 571 + 1897 3430 3362 3363 3376 + 1898 490 3374 3367 3364 + 1899 3367 3374 3360 3364 + 1900 3362 3377 3366 3376 + 1901 490 3374 506 494 + 1902 570 558 537 571 + 1903 1458 1488 1452 2184 + 1904 3373 3431 506 492 + 1905 3264 3869 3868 3786 + 1906 3431 3373 500 492 + 1907 560 3373 506 492 + 1908 3442 560 500 492 + 1909 3384 3513 3385 514 + 1910 2739 3185 3039 3184 + 1911 531 521 532 510 + 1912 3513 3498 520 515 + 1913 3341 3322 3340 4221 + 1914 525 523 509 510 + 1915 1457 1490 1455 1452 + 1916 3633 3634 3101 4633 + 1917 1491 1485 1490 1453 + 1918 1528 1541 1526 1520 + 1919 2624 2623 2620 2563 + 1920 3458 3489 3445 3447 + 1921 3396 3391 3390 3397 + 1922 2125 2134 466 21 + 1923 449 463 448 1496 + 1924 31 45 1494 460 + 1925 1491 1486 1495 1496 + 1926 1494 454 1474 460 + 1927 278 280 271 269 + 1928 1491 463 1486 1496 + 1929 3002 3086 1174 4445 + 1930 3505 3494 528 3508 + 1931 3639 553 546 3654 + 1932 3494 3510 3508 3509 + 1933 3658 555 507 3407 + 1934 3392 3493 3496 3531 + 1935 528 3510 3513 3508 + 1936 3672 3419 3670 3673 + 1937 3391 530 3390 3397 + 1938 3391 3505 530 3397 + 1939 3148 3036 3027 3014 + 1940 3358 3357 3360 3359 + 1941 3508 3494 3390 3397 + 1942 3505 3506 3391 530 + 1943 284 3533 3534 3525 + 1944 3506 3391 530 3390 + 1945 3513 3498 515 514 + 1946 3505 526 529 530 + 1947 3532 3535 3534 517 + 1948 3498 3513 3512 3497 + 1949 3513 528 3508 515 + 1950 836 857 868 859 + 1951 1529 1466 414 409 + 1952 285 518 3507 3527 + 1953 3533 284 3534 516 + 1954 389 3945 3920 3954 + 1955 3505 3494 3392 529 + 1956 3533 3532 3535 3534 + 1957 3803 3963 3962 3902 + 1958 3507 285 3527 3495 + 1959 285 3533 3527 3495 + 1960 3458 290 3520 3447 + 1961 3532 3521 517 291 + 1962 284 3534 516 291 + 1963 2108 2096 2098 2104 + 1964 3457 3535 3458 3521 + 1965 2712 2710 2714 2711 + 1966 1488 1490 1453 1452 + 1967 1479 1480 3601 450 + 1968 518 3527 3515 3511 + 1969 3498 520 519 3393 + 1970 3235 3236 2751 2756 + 1971 170 148 2122 174 + 1972 284 3526 3532 286 + 1973 270 3464 273 269 + 1974 3481 280 3475 3480 + 1975 444 3586 452 455 + 1976 1475 457 1474 1476 + 1977 516 3534 3492 517 + 1978 3464 3462 3523 3463 + 1979 3516 3526 3527 3515 + 1980 1230 1609 1184 4485 + 1981 3515 518 3511 3497 + 1982 2870 385 3935 2893 + 1983 2870 381 385 2893 + 1984 330 336 335 338 + 1985 3909 3879 3952 3911 + 1986 543 3672 3670 3673 + 1987 373 361 371 375 + 1988 2871 4012 4245 4247 + 1989 521 3361 510 524 + 1990 3876 3812 3875 3874 + 1991 3800 3795 3797 3794 + 1992 710 713 3699 4513 + 1993 799 2931 2948 2932 + 1994 3515 286 282 281 + 1995 1546 1618 2280 4182 + 1996 3920 3909 3954 3972 + 1997 2816 2818 4077 4550 + 1998 3433 3432 3431 3434 + 1999 3735 251 3853 370 + 2000 3398 531 3384 533 + 2001 3793 3800 3796 3962 + 2002 3913 393 394 390 + 2003 2668 805 780 755 + 2004 3505 526 530 3397 + 2005 3533 3495 3492 3531 + 2006 3826 3844 3827 3843 + 2007 3843 369 370 259 + 2008 521 526 522 530 + 2009 776 773 2673 761 + 2010 3837 257 3844 3827 + 2011 506 3431 489 492 + 2012 362 3800 3802 3795 + 2013 361 373 3724 375 + 2014 915 905 904 921 + 2015 531 521 510 524 + 2016 3385 3513 3394 514 + 2017 3729 3757 3802 3758 + 2018 3837 3814 3841 3811 + 2019 3840 3864 3866 3836 + 2020 3835 3877 3839 3836 + 2021 3801 3876 368 3842 + 2022 3735 3734 375 377 + 2023 756 776 759 758 + 2024 3803 362 3800 3802 + 2025 341 2983 2978 2926 + 2026 1319 1560 1561 4133 + 2027 3968 3920 3964 3796 + 2028 3963 3968 3964 3796 + 2029 3505 527 528 529 + 2030 3493 3495 3496 3531 + 2031 3879 3909 3960 3911 + 2032 3495 3533 3496 3531 + 2033 347 1561 4239 4537 + 2034 3817 3826 3828 3818 + 2035 2792 2808 2773 2741 + 2036 3906 3903 3571 3576 + 2037 1808 1948 1812 124 + 2038 2764 907 2726 827 + 2039 1314 348 4027 4052 + 2040 3755 3854 3815 3753 + 2041 362 366 3800 3795 + 2042 361 3733 3734 3724 + 2043 2098 2107 2104 2109 + 2044 3697 3675 3698 3696 + 2045 148 1489 2122 150 + 2046 3852 702 4713 4720 + 2047 1844 1543 2132 2176 + 2048 1191 1645 815 4055 + 2049 348 4052 4718 4733 + 2050 3652 3421 3669 3668 + 2051 547 3643 3722 3641 + 2052 139 140 138 133 + 2053 3568 3574 3571 3567 + 2054 1035 1036 4132 4702 + 2055 489 3416 3382 3435 + 2056 515 3394 514 3395 + 2057 3719 3712 3708 3710 + 2058 3642 3630 3669 3668 + 2059 1055 1054 1057 4359 + 2060 3988 4219 4550 4645 + 2061 1558 1215 1224 4231 + 2062 1489 170 449 148 + 2063 3139 3087 3137 3142 + 2064 1255 4185 4407 4421 + 2065 3144 3150 3166 3033 + 2066 3133 3045 3044 3132 + 2067 523 3361 510 3363 + 2068 2198 2195 2161 117 + 2069 1529 1528 1466 1520 + 2070 3314 3304 3216 3306 + 2071 490 3374 3364 3429 + 2072 3357 525 3364 3363 + 2073 2984 3558 3328 2993 + 2074 1416 1775 1418 1767 + 2075 1232 1231 345 4032 + 2076 3074 3010 3035 2998 + 2077 3652 3630 3669 3651 + 2078 3534 3533 3492 3531 + 2079 2531 59 40 2520 + 2080 3028 3162 3135 3166 + 2081 4385 4279 4386 4564 + 2082 459 175 1493 4141 + 2083 934 1090 1088 4206 + 2084 523 525 493 3363 + 2085 3364 3374 3434 3429 + 2086 487 480 3611 481 + 2087 419 3712 3711 3635 + 2088 3025 3010 2999 2998 + 2089 3430 3426 3425 3429 + 2090 3854 232 233 3846 + 2091 3139 3140 3141 3137 + 2092 3671 3639 546 3654 + 2093 543 570 3652 3651 + 2094 3817 258 3826 3818 + 2095 3165 3162 3135 3146 + 2096 3856 3819 3301 3861 + 2097 4114 4047 4185 4372 + 2098 3854 3813 233 3815 + 2099 2142 2143 2103 2109 + 2100 3677 418 422 424 + 2101 401 3575 3549 3576 + 2102 3139 3087 3140 3137 + 2103 3713 3708 3720 424 + 2104 3983 3736 3777 247 + 2105 2591 2572 2566 2586 + 2106 1857 215 143 206 + 2107 1069 2577 1617 4569 + 2108 1307 1300 1284 1301 + 2109 1960 1981 1987 1942 + 2110 3728 3790 3756 3777 + 2111 3582 3545 3572 3538 + 2112 3681 3131 3714 4128 + 2113 1158 1160 4519 4601 + 2114 3087 3026 3021 2999 + 2115 3850 3847 3849 3872 + 2116 3120 3131 2682 4250 + 2117 3087 3137 3142 3026 + 2118 466 3328 2135 1040 + 2119 3334 3352 3331 3333 + 2120 3104 3661 3647 3650 + 2121 530 526 3390 3397 + 2122 965 976 952 1106 + 2123 1603 1230 1184 4544 + 2124 1110 1115 4197 4629 + 2125 3603 4092 4195 4325 + 2126 3813 3854 233 3846 + 2127 2204 127 2201 2183 + 2128 3847 264 3816 3815 + 2129 1480 1479 456 450 + 2130 3380 3379 3065 3066 + 2131 3443 560 537 571 + 2132 361 371 3785 368 + 2133 3517 3524 274 3522 + 2134 558 3444 537 571 + 2135 3757 3801 3876 3875 + 2136 1031 201 1035 4439 + 2137 3857 251 3843 370 + 2138 1198 2280 824 820 + 2139 3729 3757 3785 3730 + 2140 367 3801 368 3842 + 2141 1682 1686 345 4319 + 2142 2767 836 3235 2751 + 2143 1933 1931 1929 1934 + 2144 1204 1189 2135 1188 + 2145 3557 2136 2706 1040 + 2146 2870 3943 2860 2859 + 2147 2902 2857 2861 2858 + 2148 3968 3920 397 3964 + 2149 1589 1117 4171 4311 + 2150 286 284 3515 282 + 2151 979 4029 4265 4762 + 2152 2261 2224 2213 2225 + 2153 229 1519 441 4087 + 2154 712 3933 4360 4381 + 2155 163 154 1996 1991 + 2156 3491 3499 3368 3365 + 2157 941 978 947 977 + 2158 3037 3039 3022 3023 + 2159 1432 1423 1428 1351 + 2160 1768 4515 4621 4642 + 2161 1700 1116 1276 1705 + 2162 976 965 979 1122 + 2163 3011 3015 3009 3014 + 2164 1146 3132 3174 1145 + 2165 2357 2348 2347 4034 + 2166 3560 1844 2132 4018 + 2167 2567 612 4412 4489 + 2168 405 434 4068 4549 + 2169 4721 2280 4732 4746 + 2170 4116 1741 4744 4763 + 2171 3090 3633 3473 3088 + 2172 1306 1305 4061 4608 + 2173 3083 2747 3053 3538 + 2174 3491 3499 3402 3368 + 2175 1637 1571 1635 4113 + 2176 2800 2736 4348 4391 + 2177 4022 1241 4024 4357 + 2178 3225 2747 3259 4584 + 2179 3038 3040 4237 4418 + 2180 3679 3648 3650 3689 + 2181 3878 3740 3788 3900 + 2182 4274 4234 4286 4436 + 2183 1168 1165 1172 4288 + 2184 3500 3064 2784 4615 + 2185 3923 2785 2682 2679 + 2186 2971 590 4308 4507 + 2187 795 815 1139 4026 + 2188 3472 3101 4427 4597 + 2189 1700 1276 3584 3583 + 2190 1191 1139 4129 4263 + 2191 644 3869 3860 4028 + 2192 1242 3288 1241 3124 + 2193 2497 2499 1830 4192 + 2194 2105 2106 2103 2148 + 2195 4428 4412 4489 4732 + 2196 3370 3380 3369 3375 + 2197 3934 3950 3474 3488 + 2198 3491 3387 3368 4723 + 2199 3937 3092 2783 2682 + 2200 4037 4075 4387 4566 + 2201 2800 2733 2736 4391 + 2202 3906 3577 3904 4256 + 2203 2506 2489 2503 4007 + 2204 3276 3338 3303 4002 + 2205 3169 3079 2682 2679 + 2206 3530 2805 3518 3519 + 2207 1231 1314 348 4027 + 2208 3869 644 3767 4028 + 2209 611 348 4021 4535 + 2210 3077 2781 3459 2786 + 2211 3042 3057 4235 4472 + 2212 1029 1264 4251 4261 + 2213 1180 4686 4717 4739 + 2214 1191 1148 1139 4026 + 2215 3080 3077 3529 2786 + 2216 1116 1276 1114 1582 + 2217 3231 3244 3245 3259 + 2218 3169 3120 3084 3159 + 2219 4092 4307 4385 4386 + 2220 4029 1029 4252 4261 + 2221 1266 1121 1733 4640 + 2222 2823 3213 2825 4594 + 2223 793 3931 4082 4294 + 2224 1020 4085 4714 4729 + 2225 1608 1191 165 4384 + 2226 3514 3502 3403 3401 + 2227 1175 1146 1254 1145 + 2228 632 3102 3774 4181 + 2229 3931 793 3894 4294 + 2230 3332 3330 3887 3329 + 2231 3153 3223 2794 3185 + 2232 2810 2762 3257 3225 + 2233 4013 1018 4207 4327 + 2234 3156 3223 3224 3070 + 2235 2044 1517 2033 1993 + 2236 795 1638 4024 4479 + 2237 3503 3437 4036 4264 + 2238 3633 3405 3406 3088 + 2239 3580 1697 3584 3583 + 2240 821 1198 1317 4199 + 2241 4150 3530 4158 4175 + 2242 3563 3092 3584 3091 + 2243 3623 3598 4401 4408 + 2244 3321 3312 4449 4502 + 2245 3536 3661 3106 3659 + 2246 2785 3582 3581 3583 + 2247 3003 3004 4033 4037 + 2248 1183 1197 4485 4505 + 2249 3083 3582 3572 3538 + 2250 3019 3029 4061 4333 + 2251 3472 3633 3090 3662 + 2252 3379 3066 3428 3594 + 2253 3003 3024 4006 4037 + 2254 644 3773 3767 4028 + 2255 3380 3621 3080 3596 + 2256 3633 3101 3090 3473 + 2257 1919 149 4400 4558 + 2258 1577 1183 1197 4680 + 2259 1276 1700 3169 3583 + 2260 4589 4469 4660 4679 + 2261 1637 1567 1571 4113 + 2262 1155 794 1154 4113 + 2263 1252 1155 1154 4113 + 2264 740 1557 4110 4496 + 2265 2785 3923 3581 4264 + 2266 3120 3131 3584 3091 + 2267 184 825 4664 4724 + 2268 295 3887 3888 3893 + 2269 3322 3820 4388 4682 + 2270 710 3699 3093 3645 + 2271 3623 2782 3596 3593 + 2272 4044 4005 4081 4300 + 2273 3608 3620 3624 3593 + 2274 3554 3175 3715 4405 + 2275 2695 2692 4344 4473 + 2276 2802 3008 4118 4387 + 2277 3454 2781 3077 2783 + 2278 2784 3609 3628 3624 + 2279 3501 3629 3615 3614 + 2280 3633 3101 3473 3406 + 2281 3405 3408 3101 3406 + 2282 3077 3459 3456 3529 + 2283 2886 4038 4162 4183 + 2284 3491 3499 3401 3402 + 2285 825 185 172 4724 + 2286 2504 2282 4120 4299 + 2287 1266 1171 1141 4157 + 2288 3400 3514 3403 4723 + 2289 3080 3623 2782 3596 + 2290 2782 3608 4236 4470 + 2291 3389 3081 3628 3624 + 2292 1196 1185 4424 4480 + 2293 3584 3120 3091 2682 + 2294 1176 3043 1121 1120 + 2295 3380 3379 3428 3375 + 2296 631 2919 2895 4622 + 2297 991 992 4096 4379 + 2298 3526 3524 3462 3522 + 2299 1725 1177 1735 4393 + 2300 1559 1228 1224 4188 + 2301 3379 3427 3428 3375 + 2302 2356 2357 2347 4034 + 2303 1228 1556 1224 4188 + 2304 2653 2652 1034 4346 + 2305 3665 3663 3688 3636 + 2306 3472 3633 3101 3090 + 2307 3598 3595 3599 3529 + 2308 1606 1183 1197 4616 + 2309 1302 1158 1157 1156 + 2310 336 331 330 2861 + 2311 3169 1276 3583 2682 + 2312 3644 3131 3717 3091 + 2313 16 18 4104 4126 + 2314 3049 4186 4387 4566 + 2315 4040 4091 4189 4497 + 2316 3093 3094 705 3645 + 2317 3482 3472 3483 3484 + 2318 1618 2577 1620 4093 + 2319 3372 3370 3371 3624 + 2320 1197 1607 1566 4505 + 2321 1830 1842 2500 4192 + 2322 1270 345 4032 4662 + 2323 2804 3604 3606 3073 + 2324 815 1191 4314 4384 + 2325 3139 3162 3144 3163 + 2326 3679 3637 3105 4090 + 2327 3663 3665 3648 3680 + 2328 4006 3006 4037 4387 + 2329 3587 3619 3590 3585 + 2330 3018 3009 3149 3035 + 2331 3781 3878 3278 3740 + 2332 2816 3957 4550 4645 + 2333 4015 4120 4296 4299 + 2334 2930 2697 2698 2929 + 2335 3083 982 3222 4257 + 2336 1142 1171 1143 4157 + 2337 1033 4138 4184 4560 + 2338 3037 3186 3039 3023 + 2339 592 3285 586 4354 + 2340 3545 3400 3546 4722 + 2341 1045 2713 1046 3625 + 2342 1416 1733 1766 4190 + 2343 1192 1191 1139 4026 + 2344 272 287 274 3447 + 2345 3699 710 3093 3700 + 2346 3409 3437 3562 4036 + 2347 1177 1121 1735 4393 + 2348 3588 3623 2804 3596 + 2349 4494 4427 4506 4597 + 2350 3389 3372 3371 3624 + 2351 2782 3609 2784 3624 + 2352 3081 2784 3628 3624 + 2353 1171 1142 1141 4157 + 2354 995 996 1125 1331 + 2355 2886 2888 2896 2901 + 2356 710 3114 4291 4305 + 2357 633 640 3956 4181 + 2358 3197 641 643 2907 + 2359 1140 3125 4022 4381 + 2360 3514 3400 4369 4722 + 2361 960 4043 4289 4725 + 2362 1036 1031 4138 4702 + 2363 3079 3120 2682 4250 + 2364 2735 3006 4006 4387 + 2365 713 3117 3159 3122 + 2366 2276 2480 2282 4299 + 2367 753 750 4240 4488 + 2368 3637 3663 3648 3680 + 2369 3285 592 712 4354 + 2370 3004 4033 4037 4387 + 2371 3405 3690 3473 3088 + 2372 3405 3633 3662 3088 + 2373 3956 632 3774 4181 + 2374 3957 303 4516 4619 + 2375 3149 3148 3027 3014 + 2376 1312 1136 343 4056 + 2377 1238 1664 4108 4667 + 2378 2628 2630 2631 2622 + 2379 3701 3706 3699 3700 + 2380 3459 3456 3529 4175 + 2381 4251 4029 4252 4261 + 2382 1255 1029 4251 4261 + 2383 1885 1801 1954 1950 + 2384 3158 3273 3881 4181 + 2385 3554 3715 4291 4405 + 2386 3633 3090 3662 3088 + 2387 3688 3634 3685 3636 + 2388 1255 4251 4252 4261 + 2389 184 825 822 4664 + 2390 4301 1141 4568 4590 + 2391 1029 1255 4252 4261 + 2392 3131 713 3699 3120 + 2393 3386 3546 3402 4723 + 2394 3681 3131 3716 3714 + 2395 3405 3686 3633 3101 + 2396 3686 3405 3556 3101 + 2397 1080 1081 4628 4681 + 2398 1760 1764 1763 4152 + 2399 3015 3086 3045 3066 + 2400 2813 4550 4619 4643 + 2401 3134 3056 4148 4472 + 2402 3120 3169 3170 3159 + 2403 1933 1079 1914 1929 + 2404 3105 3679 4090 4331 + 2405 979 4029 4252 4265 + 2406 1247 1685 1686 1684 + 2407 1931 1933 1892 1934 + 2408 3637 3663 4125 4358 + 2409 3405 3473 3406 3088 + 2410 3405 3662 3684 3088 + 2411 3473 3690 3684 3088 + 2412 3665 3680 3685 3689 + 2413 3665 3648 3680 3689 + 2414 3648 3679 3680 3689 + 2415 2286 2284 2288 4334 + 2416 1634 1637 1570 1635 + 2417 3716 3131 3555 3554 + 2418 4350 3213 4492 4595 + 2419 1249 1250 1241 3124 + 2420 822 1198 1187 1918 + 2421 1183 1577 1197 1572 + 2422 3131 3716 3714 3120 + 2423 4276 4257 4632 4653 + 2424 165 1191 815 4384 + 2425 3107 3106 3111 4305 + 2426 3860 644 4699 4720 + 2427 3716 3131 3554 3120 + 2428 1015 933 1010 928 + 2429 3087 3140 3026 2999 + 2430 3131 3555 3554 3120 + 2431 4067 3006 4118 4387 + 2432 3092 3584 3091 2682 + 2433 713 3699 3118 711 + 2434 1276 3584 3583 2682 + 2435 321 3276 580 4002 + 2436 3092 3581 3583 2682 + 2437 3903 3577 3906 4256 + 2438 2789 3923 3092 2682 + 2439 345 1231 4027 4032 + 2440 4499 4517 4571 4719 + 2441 3044 2784 3043 1145 + 2442 1254 1175 3174 1144 + 2443 1168 1165 1173 1172 + 2444 3045 3064 3044 3132 + 2445 3923 2789 2783 2682 + 2446 977 990 4207 4265 + 2447 3456 4150 4158 4175 + 2448 3923 3581 3092 2682 + 2449 1688 1245 4258 4446 + 2450 3460 3480 3469 3461 + 2451 1250 713 1235 3124 + 2452 3608 3609 2782 3624 + 2453 4427 3937 4506 4637 + 2454 3170 1235 3159 3124 + 2455 3279 3967 711 575 + 2456 3622 3380 3621 3080 + 2457 3501 3388 4583 4639 + 2458 3646 3705 4101 4140 + 2459 2902 2858 2865 2860 + 2460 815 1192 4026 4271 + 2461 3808 2853 3805 4002 + 2462 3588 2790 3080 3073 + 2463 3081 3389 3371 3624 + 2464 3690 3688 3685 3689 + 2465 3923 2785 3169 2682 + 2466 1316 1246 4319 4536 + 2467 4098 4178 4527 4669 + 2468 3015 3030 3149 3014 + 2469 3312 3322 3323 3313 + 2470 3906 401 3578 4094 + 2471 3021 3025 3020 2999 + 2472 1706 1116 1672 1701 + 2473 199 90 217 188 + 2474 3164 3165 3162 3148 + 2475 3162 3165 3135 3148 + 2476 3136 3139 3144 3163 + 2477 3162 3028 3144 3163 + 2478 4328 332 4423 4587 + 2479 2310 2266 2288 4334 + 2480 3517 3524 3462 3465 + 2481 3166 3149 3033 3020 + 2482 13 3600 442 2143 + 2483 3315 4045 4106 4183 + 2484 3470 3481 3891 3480 + 2485 3532 3533 3535 3521 + 2486 3757 3729 3876 3730 + 2487 3440 3439 3503 4112 + 2488 4008 2733 4367 4391 + 2489 1112 1118 1133 1120 + 2490 3491 3615 3499 3365 + 2491 4236 4415 4470 4471 + 2492 3923 3169 3583 2682 + 2493 709 604 601 600 + 2494 1930 1079 1076 1078 + 2495 3012 1174 1305 1158 + 2496 603 601 599 600 + 2497 3449 2781 4092 4195 + 2498 3049 3008 2802 4387 + 2499 1277 3169 1278 1267 + 2500 465 487 480 466 + 2501 2892 2889 2888 2901 + 2502 3180 3351 3179 3293 + 2503 3933 793 592 586 + 2504 1569 1637 1571 1635 + 2505 310 3928 309 312 + 2506 3008 4067 4118 4387 + 2507 3557 465 3551 3612 + 2508 2690 2696 3313 590 + 2509 1276 3120 3584 2682 + 2510 1676 1667 1666 1673 + 2511 3169 2785 3504 2679 + 2512 3120 3131 3091 2682 + 2513 3120 1276 3169 2682 + 2514 3278 3279 3740 3900 + 2515 3312 3322 3342 2855 + 2516 2849 3206 3221 4448 + 2517 1681 1679 1688 4446 + 2518 2886 2892 2888 2901 + 2519 1123 963 4043 4725 + 2520 3131 3699 3646 3717 + 2521 3644 3646 3705 3717 + 2522 704 706 3113 3115 + 2523 2823 3213 4594 4648 + 2524 3066 3380 3428 3594 + 2525 4029 1122 4432 4762 + 2526 4230 4237 4418 4548 + 2527 712 3125 1140 4381 + 2528 3820 3094 3291 3300 + 2529 705 3300 4003 4107 + 2530 3274 3309 2826 3761 + 2531 3422 3406 4545 4555 + 2532 2870 2868 381 2860 + 2533 3936 4433 4540 4651 + 2534 3154 3152 3155 1256 + 2535 1110 1109 1738 4040 + 2536 1407 4399 4442 4740 + 2537 3500 3501 3491 3388 + 2538 3380 3370 3379 3375 + 2539 1556 1558 1224 4188 + 2540 498 2059 1442 26 + 2541 991 4096 4179 4379 + 2542 3029 4061 4333 4445 + 2543 3305 3244 3200 3216 + 2544 2868 3943 3947 2860 + 2545 2892 2889 338 322 + 2546 4435 3422 4545 4555 + 2547 669 670 2599 2602 + 2548 3267 3830 3301 3861 + 2549 480 3552 3611 481 + 2550 445 3618 3617 488 + 2551 4130 4013 4345 4503 + 2552 3831 3267 3301 3861 + 2553 3830 3267 3833 3861 + 2554 1207 2268 2288 4334 + 2555 4101 3093 4128 4140 + 2556 3830 3833 3819 3861 + 2557 2699 2941 2942 2943 + 2558 3831 3833 3807 3804 + 2559 2785 3923 4546 4588 + 2560 1214 814 1212 4056 + 2561 3899 3916 408 4039 + 2562 3196 3247 2907 4248 + 2563 3427 3379 3428 3594 + 2564 311 3896 3926 3895 + 2565 297 3887 295 3908 + 2566 2728 2752 2812 3251 + 2567 2889 2930 2896 2901 + 2568 3330 3887 2988 3327 + 2569 584 3883 3319 586 + 2570 3321 3322 3312 3315 + 2571 3852 607 702 4720 + 2572 3940 2872 1655 1654 + 2573 1118 1391 1112 4402 + 2574 3320 3318 3553 464 + 2575 4127 4148 4235 4309 + 2576 3336 2696 3335 2690 + 2577 3094 3300 3312 705 + 2578 34 1188 813 1190 + 2579 1271 1259 1702 4405 + 2580 3605 3000 4234 4755 + 2581 2506 2494 2493 4007 + 2582 793 3933 3886 586 + 2583 3332 3939 3330 3329 + 2584 4385 4092 4386 4674 + 2585 3042 3057 3062 3054 + 2586 3925 3928 3927 3476 + 2587 3312 2855 4682 4753 + 2588 299 3888 296 298 + 2589 3896 2692 2695 3895 + 2590 292 308 3934 312 + 2591 3887 297 584 3908 + 2592 1019 4729 4731 4762 + 2593 3925 3928 3888 3890 + 2594 2802 3232 4075 4387 + 2595 3330 3332 3887 3327 + 2596 641 2903 643 2913 + 2597 2044 1512 204 4213 + 2598 3949 3926 3951 3944 + 2599 577 3805 576 313 + 2600 4143 2289 4484 4672 + 2601 1391 1118 4066 4402 + 2602 3322 3335 3323 3340 + 2603 2781 3603 4092 4195 + 2604 1547 1553 1197 4209 + 2605 3919 398 382 394 + 2606 1677 1676 1668 4108 + 2607 3542 3550 3540 4094 + 2608 4427 4262 4545 4690 + 2609 3933 3285 3886 586 + 2610 3924 3941 3927 3476 + 2611 3949 3896 3926 3927 + 2612 3339 3336 2970 3335 + 2613 3606 3603 2786 4605 + 2614 329 381 385 386 + 2615 3967 3110 3279 711 + 2616 3781 3278 3782 3740 + 2617 1208 2266 1206 4334 + 2618 2845 3276 3338 3275 + 2619 3788 3901 3900 576 + 2620 591 295 574 590 + 2621 3934 3487 3488 279 + 2622 1000 1135 1086 4172 + 2623 438 4134 4153 4346 + 2624 2891 331 337 336 + 2625 469 793 467 586 + 2626 1189 1204 2135 1226 + 2627 2891 2892 2889 2857 + 2628 3661 3104 3536 3655 + 2629 1513 229 2038 4634 + 2630 3662 4180 4246 4633 + 2631 2891 332 2889 337 + 2632 3254 3194 3263 3192 + 2633 3887 584 3319 3908 + 2634 3926 3941 2717 2716 + 2635 3892 309 306 3897 + 2636 3312 3322 3335 3323 + 2637 2718 2725 2717 2716 + 2638 974 995 994 1261 + 2639 3313 4303 4398 4452 + 2640 4132 84 4414 4702 + 2641 3820 3291 3863 3312 + 2642 3322 3820 3863 3312 + 2643 3291 3820 3300 3312 + 2644 3820 3094 3300 3312 + 2645 1266 1175 1147 4190 + 2646 1158 4203 4519 4608 + 2647 1198 1607 1569 1566 + 2648 3627 2145 2144 3591 + 2649 2897 2902 2861 2865 + 2650 481 3611 3617 3616 + 2651 24 2128 2127 22 + 2652 442 2140 3590 54 + 2653 4005 3133 4300 4669 + 2654 710 3093 3703 711 + 2655 2572 2574 2571 600 + 2656 2572 2573 2571 1061 + 2657 1716 1761 1718 4323 + 2658 465 3552 3551 3612 + 2659 3926 311 3951 3944 + 2660 3557 3328 2722 2706 + 2661 1553 1574 1197 4209 + 2662 713 3125 792 712 + 2663 584 585 3883 586 + 2664 252 266 3829 253 + 2665 3552 3320 3551 3612 + 2666 710 705 3645 711 + 2667 3595 2782 3529 4401 + 2668 3468 3928 3892 3890 + 2669 3887 297 295 3208 + 2670 3247 3196 628 4248 + 2671 3221 2849 4594 4648 + 2672 336 337 338 322 + 2673 577 2853 3805 313 + 2674 3322 3321 3312 3342 + 2675 3928 3929 3925 3888 + 2676 3888 3887 3327 3893 + 2677 2897 2902 2857 2861 + 2678 3320 3553 3551 464 + 2679 2817 3957 4516 4619 + 2680 2572 2571 2566 2568 + 2681 1917 1924 166 171 + 2682 1175 1158 1147 4601 + 2683 3552 481 3617 3616 + 2684 3950 3949 3951 3944 + 2685 453 1480 3600 2143 + 2686 2654 2656 2685 2720 + 2687 3958 2911 4759 4761 + 2688 3927 308 3934 3474 + 2689 295 297 3888 3208 + 2690 3246 3247 2912 3176 + 2691 3928 3949 3927 312 + 2692 1515 204 4087 4634 + 2693 713 3123 3122 712 + 2694 3967 3965 3123 712 + 2695 3887 584 3883 3319 + 2696 469 585 592 586 + 2697 3318 3557 3551 3612 + 2698 1258 3175 1259 4405 + 2699 1080 822 1605 4083 + 2700 3949 3896 3895 3951 + 2701 1514 2036 432 4213 + 2702 1300 1308 1307 1284 + 2703 600 599 605 598 + 2704 3901 3967 295 3966 + 2705 2310 2268 2266 4334 + 2706 3328 3553 3894 586 + 2707 1759 1758 4163 4177 + 2708 3103 727 3127 703 + 2709 650 3215 608 649 + 2710 3156 1092 1330 985 + 2711 3186 3182 3202 4074 + 2712 2897 2891 330 2857 + 2713 2705 1039 3936 2723 + 2714 2816 304 4643 4645 + 2715 3093 3094 3095 705 + 2716 3530 2805 3519 4471 + 2717 3500 1266 1146 1735 + 2718 3553 469 467 586 + 2719 4070 324 4316 4492 + 2720 2763 4257 4276 4653 + 2721 3557 3318 3551 464 + 2722 307 308 3889 3891 + 2723 308 307 3481 3891 + 2724 3924 308 3927 3474 + 2725 2660 4068 4281 4549 + 2726 3941 3926 3925 2716 + 2727 3925 3888 309 3890 + 2728 2745 4391 4475 4539 + 2729 576 577 313 589 + 2730 3276 2900 321 2827 + 2731 3276 2853 321 2828 + 2732 2864 330 335 383 + 2733 202 219 200 2017 + 2734 4276 3258 4330 4483 + 2735 3949 3941 3925 3927 + 2736 3179 2745 4475 4539 + 2737 3611 3552 3612 3616 + 2738 457 462 458 4141 + 2739 3549 3542 3543 4094 + 2740 3247 2907 628 626 + 2741 3213 323 2969 2825 + 2742 2901 2697 2929 2703 + 2743 705 706 575 321 + 2744 2889 2891 2857 2861 + 2745 310 3949 312 293 + 2746 1595 737 4058 4290 + 2747 3339 2856 3340 4388 + 2748 3896 2692 3895 2717 + 2749 3328 3318 3319 3317 + 2750 3929 3928 3925 3476 + 2751 3468 3481 3891 3476 + 2752 3896 311 3926 3929 + 2753 3941 3925 3927 3476 + 2754 3928 3949 3925 3927 + 2755 311 3896 3895 3929 + 2756 298 3888 3897 3890 + 2757 3318 3328 3319 586 + 2758 3928 3925 309 3890 + 2759 3320 3318 3551 3612 + 2760 299 311 3895 3888 + 2761 3895 311 3929 3888 + 2762 4127 3134 4149 4309 + 2763 3926 3929 3925 2716 + 2764 3892 3928 309 3890 + 2765 3339 2856 2884 3342 + 2766 3892 309 3897 3890 + 2767 1917 1913 1924 171 + 2768 3941 2718 2717 2716 + 2769 331 329 336 330 + 2770 272 289 287 3447 + 2771 2892 2891 2889 337 + 2772 794 1253 1664 4023 + 2773 3470 307 3889 3891 + 2774 3474 3488 3448 3477 + 2775 297 295 296 298 + 2776 445 3618 446 3617 + 2777 858 3234 3241 859 + 2778 3261 3254 3262 2765 + 2779 3949 310 3928 309 + 2780 299 295 574 294 + 2781 1933 1076 1078 1074 + 2782 299 295 3888 3893 + 2783 4288 1141 4301 4590 + 2784 3898 3883 3888 296 + 2785 4324 4564 4689 4735 + 2786 3557 2723 3610 2706 + 2787 3949 3927 3950 3474 + 2788 3015 3018 3149 3019 + 2789 3343 3246 648 3176 + 2790 3611 487 481 3617 + 2791 3552 465 3551 480 + 2792 3949 3928 3925 309 + 2793 1567 1198 1637 1566 + 2794 826 1317 348 4535 + 2795 3123 3110 712 711 + 2796 3557 3552 465 3612 + 2797 3941 3949 3925 309 + 2798 466 465 467 464 + 2799 3934 292 312 293 + 2800 308 292 3934 3474 + 2801 308 3924 3481 3474 + 2802 3447 3520 3479 3463 + 2803 280 278 271 277 + 2804 292 308 312 277 + 2805 382 3946 392 386 + 2806 330 2866 384 383 + 2807 3898 3883 296 3897 + 2808 3931 4082 4294 4540 + 2809 308 3928 312 305 + 2810 3928 308 3889 305 + 2811 309 3928 306 305 + 2812 3192 3191 861 2674 + 2813 3640 710 3105 4107 + 2814 909 2406 900 883 + 2815 3207 3188 645 2675 + 2816 859 858 628 626 + 2817 2775 917 882 889 + 2818 3892 306 3889 3897 + 2819 3618 3619 446 3617 + 2820 3888 296 298 3897 + 2821 295 299 3888 296 + 2822 297 295 3888 296 + 2823 3888 3898 296 3897 + 2824 306 309 298 3897 + 2825 181 1079 1914 1934 + 2826 34 813 816 56 + 2827 3344 2900 334 327 + 2828 3618 3627 2144 3591 + 2829 1611 1610 1605 1918 + 2830 1303 1302 1158 1157 + 2831 793 466 467 816 + 2832 3253 3196 2907 4248 + 2833 725 2880 3271 3272 + 2834 2003 2081 2082 1342 + 2835 2950 2934 2949 2933 + 2836 3474 280 275 277 + 2837 3488 3487 3448 3477 + 2838 3474 280 3488 3477 + 2839 33 2130 2137 34 + 2840 705 295 712 711 + 2841 1160 1170 4519 4601 + 2842 1659 1660 1656 4012 + 2843 2416 2398 844 843 + 2844 1639 1192 1139 1638 + 2845 3781 3110 3278 3282 + 2846 45 2110 2108 2107 + 2847 3934 3950 3944 293 + 2848 310 311 3944 293 + 2849 33 2134 466 2137 + 2850 592 3965 712 4447 + 2851 2110 2099 2101 2097 + 2852 3585 3587 3586 444 + 2853 1175 1266 1146 1145 + 2854 2577 1225 1224 4084 + 2855 1020 1004 1019 4609 + 2856 1951 1946 1952 1947 + 2857 3481 3468 3891 3480 + 2858 3600 1480 3601 1484 + 2859 4550 3957 4643 4645 + 2860 867 627 871 860 + 2861 3458 3457 3521 3520 + 2862 2348 745 778 4034 + 2863 3945 3918 3920 3954 + 2864 329 331 385 2893 + 2865 3464 3460 3475 3478 + 2866 3460 280 3475 3478 + 2867 3490 3489 273 3445 + 2868 289 3490 273 3445 + 2869 361 3729 3785 3730 + 2870 2891 330 2857 2861 + 2871 2970 3339 3335 3340 + 2872 2891 336 330 2861 + 2873 3523 3520 3447 3463 + 2874 621 3247 628 626 + 2875 3100 704 3103 703 + 2876 837 2757 852 2759 + 2877 2889 2892 2888 2857 + 2878 2274 2264 1199 4334 + 2879 3487 3489 3488 3448 + 2880 4527 4377 4631 4669 + 2881 2808 2744 2806 2807 + 2882 3985 3882 3997 2676 + 2883 648 3246 621 623 + 2884 4499 4417 4709 4734 + 2885 3236 2767 2757 3235 + 2886 1556 1559 1558 4188 + 2887 331 330 2861 2858 + 2888 3885 640 3188 2676 + 2889 332 2888 2896 2900 + 2890 331 330 2858 2866 + 2891 329 331 2858 2866 + 2892 2874 2864 2876 2878 + 2893 2699 2702 2941 2943 + 2894 3191 2671 861 2674 + 2895 2937 2691 2708 2707 + 2896 3287 4022 4357 4430 + 2897 1369 1371 1370 1514 + 2898 3030 3149 3029 3019 + 2899 2733 2745 4348 4391 + 2900 3189 3190 2910 3274 + 2901 2930 2892 2889 2896 + 2902 2897 2892 2902 2896 + 2903 2930 2897 2902 2896 + 2904 2892 2930 2902 2896 + 2905 2889 332 2888 2896 + 2906 3627 3618 3617 3616 + 2907 405 2681 1507 1506 + 2908 2880 2890 2862 1658 + 2909 334 579 333 580 + 2910 788 785 789 811 + 2911 1500 1501 1497 439 + 2912 622 648 650 623 + 2913 3263 3261 871 3262 + 2914 867 627 868 871 + 2915 3246 621 3247 3248 + 2916 3953 2868 2867 3947 + 2917 1510 1515 1516 429 + 2918 3648 3679 3647 4331 + 2919 2037 2020 2017 2021 + 2920 1497 2680 404 1506 + 2921 437 1500 1519 1499 + 2922 3091 3555 4338 4530 + 2923 648 3246 3189 3176 + 2924 2671 3187 3191 863 + 2925 2764 907 2746 2726 + 2926 3581 4264 4369 4722 + 2927 819 1225 2281 817 + 2928 639 2919 2918 2917 + 2929 1600 1591 737 752 + 2930 433 434 404 440 + 2931 2904 2903 3958 2908 + 2932 727 3160 3116 1273 + 2933 314 318 321 322 + 2934 219 218 2037 2042 + 2935 2646 2659 2677 2920 + 2936 2594 2598 2600 2597 + 2937 1196 2280 1616 4182 + 2938 2697 2698 2929 2941 + 2939 623 643 2913 626 + 2940 3958 4016 4028 4069 + 2941 1294 1304 1157 4203 + 2942 2753 3048 3242 3250 + 2943 2346 2349 1202 1626 + 2944 2866 2897 2865 2869 + 2945 782 2694 2721 2719 + 2946 2672 2654 2656 2685 + 2947 639 2661 2919 2917 + 2948 3920 388 3909 3972 + 2949 1369 431 1368 425 + 2950 725 726 3283 1137 + 2951 805 759 2685 2687 + 2952 3195 627 859 628 + 2953 2898 2881 2899 2882 + 2954 2483 2502 2479 4007 + 2955 1212 814 342 4056 + 2956 1003 1001 992 4542 + 2957 3414 3417 3503 4194 + 2958 2684 433 404 440 + 2959 332 2886 2888 2900 + 2960 327 319 326 328 + 2961 3100 3269 702 4572 + 2962 3270 3309 3762 3761 + 2963 3318 3328 466 464 + 2964 2866 2874 384 383 + 2965 3102 3098 3264 3786 + 2966 2881 2864 2883 2882 + 2967 710 704 3128 3115 + 2968 4417 1116 4517 4719 + 2969 2900 314 321 322 + 2970 3089 3112 4291 4305 + 2971 308 3924 3927 3476 + 2972 1503 1502 1499 440 + 2973 338 335 319 326 + 2974 3215 3348 608 649 + 2975 3234 3233 3252 3195 + 2976 3328 3319 586 4730 + 2977 2899 2881 2883 2882 + 2978 3236 2767 3235 2751 + 2979 2903 2912 643 2913 + 2980 3246 621 623 3247 + 2981 3309 3276 3275 3303 + 2982 918 2744 2740 2807 + 2983 379 2877 2878 2875 + 2984 2321 2401 897 880 + 2985 3252 836 2767 868 + 2986 3247 3246 2912 2913 + 2987 3790 3759 3765 249 + 2988 379 380 390 2877 + 2989 775 1048 747 1058 + 2990 1197 2280 1185 4182 + 2991 2900 2898 338 319 + 2992 3233 3252 3195 3253 + 2993 862 3195 628 860 + 2994 3104 3106 3109 4107 + 2995 2908 3189 2910 2911 + 2996 1409 1714 1672 4080 + 2997 327 2898 319 328 + 2998 3344 2898 328 2882 + 2999 335 2899 2883 326 + 3000 3640 3637 4125 4358 + 3001 1998 163 1989 1996 + 3002 2866 381 2876 2869 + 3003 2659 2660 2677 2917 + 3004 433 2684 2680 1499 + 3005 437 229 441 440 + 3006 1510 1515 1519 1509 + 3007 3234 3233 3253 3196 + 3008 627 3254 868 871 + 3009 3997 2671 2683 2676 + 3010 437 229 1499 441 + 3011 3254 3263 3261 871 + 3012 4525 4517 4571 4623 + 3013 648 3343 650 3215 + 3014 1519 437 1499 441 + 3015 648 622 650 3189 + 3016 3247 2913 2907 626 + 3017 3343 648 650 3189 + 3018 3348 644 608 649 + 3019 2903 3995 641 2908 + 3020 321 320 327 319 + 3021 1087 966 4172 4612 + 3022 2900 321 327 319 + 3023 575 577 576 589 + 3024 1513 229 1519 2042 + 3025 3309 3348 3762 3761 + 3026 591 575 576 589 + 3027 3189 2912 2908 2910 + 3028 3190 3274 3275 3302 + 3029 2577 611 823 4746 + 3030 633 2645 640 3188 + 3031 2681 2643 3205 1506 + 3032 1497 433 439 404 + 3033 3102 726 3774 3768 + 3034 3214 315 324 4070 + 3035 3885 2645 3997 2676 + 3036 644 3215 3348 608 + 3037 3562 3092 4427 4435 + 3038 1179 1159 1392 4686 + 3039 4389 1584 4422 4526 + 3040 3102 3158 632 726 + 3041 3277 3177 3309 3274 + 3042 1610 1646 1605 1918 + 3043 3557 3328 3318 464 + 3044 3309 3274 3348 3761 + 3045 3270 2826 3302 3761 + 3046 314 332 337 322 + 3047 3219 3297 3299 3298 + 3048 2460 660 2453 656 + 3049 3186 3224 1262 3184 + 3050 4244 4178 4312 4656 + 3051 2671 3191 861 863 + 3052 3249 3234 3196 3248 + 3053 1972 189 1964 97 + 3054 3247 3189 2912 3176 + 3055 2739 3156 3223 3224 + 3056 3928 308 3927 3476 + 3057 3200 2745 2749 4539 + 3058 3049 3054 4474 4548 + 3059 3117 713 3118 711 + 3060 332 2889 337 322 + 3061 3557 465 466 464 + 3062 1546 2280 1198 1197 + 3063 3190 2910 3274 2909 + 3064 607 320 321 702 + 3065 644 632 642 726 + 3066 1608 1221 4598 4638 + 3067 771 772 2330 2358 + 3068 405 2681 1498 2677 + 3069 4340 4207 4460 4609 + 3070 822 4400 4558 4664 + 3071 3215 3343 3177 3348 + 3072 3177 3343 3309 3348 + 3073 3177 3309 3274 3348 + 3074 3215 3177 3274 3348 + 3075 2643 3204 2675 2676 + 3076 3348 3309 3270 3303 + 3077 3415 3378 3426 3425 + 3078 2661 1652 1648 84 + 3079 1271 1275 3168 1232 + 3080 2130 33 21 34 + 3081 4307 3453 4325 4371 + 3082 793 469 467 592 + 3083 3773 3958 3764 4028 + 3084 2583 2574 2581 2549 + 3085 389 388 396 394 + 3086 405 1507 404 1506 + 3087 1591 739 737 752 + 3088 4407 4694 4750 4758 + 3089 837 905 828 827 + 3090 3234 3249 3256 3248 + 3091 433 1499 439 440 + 3092 2645 3204 2643 2676 + 3093 3189 3343 3177 2911 + 3094 1736 1699 4461 4515 + 3095 4350 3213 4595 4648 + 3096 2894 725 1137 1658 + 3097 1037 1053 1597 1047 + 3098 632 3102 726 3774 + 3099 1117 4468 4688 4700 + 3100 314 318 577 4587 + 3101 797 799 784 2936 + 3102 2681 2684 1498 2677 + 3103 3160 3161 3284 3116 + 3104 590 3313 4413 4606 + 3105 2910 3189 3274 2911 + 3106 1090 955 960 983 + 3107 1411 1159 1415 4349 + 3108 607 644 580 649 + 3109 2135 3328 4404 4651 + 3110 3769 3768 3767 3763 + 3111 2668 2647 2709 2644 + 3112 4527 4630 4631 4649 + 3113 2885 2886 2929 2941 + 3114 3309 3348 3270 3762 + 3115 2900 3344 321 2827 + 3116 201 85 4132 4359 + 3117 3277 3309 3276 3275 + 3118 591 576 590 589 + 3119 4127 3054 4148 4309 + 3120 313 3315 2942 2943 + 3121 3215 579 580 649 + 3122 1198 1317 1197 4319 + 3123 1760 1762 4163 4177 + 3124 3274 3190 2829 3302 + 3125 3896 3926 3895 3951 + 3126 2826 3309 3302 3761 + 3127 1023 1263 4085 4395 + 3128 4249 3052 4565 4573 + 3129 4138 1031 4184 4702 + 3130 1584 1753 1751 4437 + 3131 704 710 706 3115 + 3132 3112 3089 3114 4305 + 3133 3536 3661 3655 3109 + 3134 636 2296 634 2300 + 3135 162 1998 1988 1991 + 3136 826 1198 1197 4319 + 3137 4030 4127 4149 4309 + 3138 992 1003 4096 4379 + 3139 710 3106 3105 4107 + 3140 309 295 298 294 + 3141 3926 311 3895 3951 + 3142 3276 2885 2845 2942 + 3143 3097 3100 3103 703 + 3144 2645 2643 2678 2676 + 3145 3274 3190 2909 2829 + 3146 3274 3348 2826 3860 + 3147 3098 3102 3769 3786 + 3148 3768 3869 3767 3763 + 3149 3157 3158 632 3272 + 3150 3187 2671 3191 2674 + 3151 2645 3885 640 3188 + 3152 3833 3830 3819 3807 + 3153 2884 2852 3342 2854 + 3154 3588 3080 3594 4392 + 3155 2856 2884 3342 2854 + 3156 3227 3304 3242 3243 + 3157 3346 3311 3310 3203 + 3158 1140 795 4255 4362 + 3159 3047 3046 3038 4075 + 3160 1553 1071 4020 4454 + 3161 4045 2886 4106 4183 + 3162 1021 993 4130 4503 + 3163 3113 706 4072 4614 + 3164 3319 592 4455 4464 + 3165 1740 1739 4766 4768 + 3166 2872 1661 1654 4582 + 3167 3098 3852 3264 3097 + 3168 727 704 728 703 + 3169 3152 3154 3155 4495 + 3170 1225 740 1224 4231 + 3171 3097 3100 3099 3096 + 3172 1661 2872 2873 4582 + 3173 3083 2747 2810 3225 + 3174 1317 1198 826 4319 + 3175 1557 740 4341 4496 + 3176 1920 1919 4400 4520 + 3177 4397 963 4677 4712 + 3178 1110 1399 1400 4531 + 3179 3258 3257 4276 4330 + 3180 1244 1689 4222 4554 + 3181 1071 1553 1564 4454 + 3182 2344 745 774 4034 + 3183 1215 1225 1224 4231 + 3184 1851 16 4280 4353 + 3185 1262 1090 935 1330 + 3186 3716 3714 3554 3715 + 3187 3155 3156 1323 1260 + 3188 1754 1584 1755 4014 + 3189 1243 1578 1150 4285 + 3190 3022 3200 3179 4006 + 3191 3679 3648 4090 4331 + 3192 1667 1670 1713 4351 + 3193 1721 1722 1761 1666 + 3194 2684 405 1498 4529 + 3195 3638 3104 3661 3647 + 3196 4387 4075 4418 4566 + 3197 2789 4427 4545 4676 + 3198 2489 2487 2484 2531 + 3199 2056 44 2049 2050 + 3200 2960 2989 2977 2957 + 3201 728 343 339 346 + 3202 631 639 2917 4068 + 3203 3620 3380 3370 3371 + 3204 3998 3957 3999 3978 + 3205 1350 1356 1360 1355 + 3206 1713 1693 1688 4258 + 3207 3200 3265 3232 3201 + 3208 826 794 1155 4113 + 3209 3175 3069 3171 3566 + 3210 2728 2748 2812 2753 + 3211 612 4428 4624 4663 + 3212 763 776 2673 761 + 3213 3152 3171 3155 1256 + 3214 750 753 4233 4488 + 3215 2812 2748 3251 3250 + 3216 3304 3306 3307 3308 + 3217 3230 3238 2747 3231 + 3218 1144 1254 4047 4635 + 3219 3294 3178 3350 3293 + 3220 3227 2812 3052 3243 + 3221 1117 1589 1588 4311 + 3222 4146 1134 4232 4469 + 3223 2794 3186 3185 2795 + 3224 3260 3324 3299 3298 + 3225 1519 1513 4238 4365 + 3226 1279 1721 1666 4323 + 3227 3186 3037 1262 3143 + 3228 3932 1659 1653 4315 + 3229 3638 3679 3105 4331 + 3230 4082 4404 4547 4580 + 3231 3219 3316 3298 3202 + 3232 4300 4527 4627 4669 + 3233 229 1519 4238 4365 + 3234 1161 1170 1157 4203 + 3235 3314 3311 3346 2753 + 3236 3324 3219 3299 3298 + 3237 3298 3186 3202 3224 + 3238 4355 1920 4400 4520 + 3239 3554 3714 4291 4641 + 3240 1033 2661 4138 4457 + 3241 3130 1256 3121 3126 + 3242 934 996 1091 1088 + 3243 3182 3333 3202 3289 + 3244 3297 3219 3316 3298 + 3245 1319 1321 1315 339 + 3246 982 3231 3052 3259 + 3247 1699 1725 4244 4270 + 3248 2669 763 2649 777 + 3249 3153 2739 3223 3185 + 3250 3153 2794 3186 3185 + 3251 3185 3037 3039 3184 + 3252 3640 710 3112 3111 + 3253 1693 1691 1694 4258 + 3254 3195 3197 3193 4248 + 3255 1580 4152 4177 4268 + 3256 1691 1713 1688 4258 + 3257 4424 1196 4480 4585 + 3258 3052 3231 3245 3259 + 3259 1855 1847 4192 4521 + 3260 3196 3195 628 4248 + 3261 3294 3178 3331 3182 + 3262 3352 3294 3331 3182 + 3263 3294 3352 3178 3182 + 3264 2748 2728 2812 3251 + 3265 2135 1629 1044 4580 + 3266 4022 712 4024 4479 + 3267 3083 982 3173 3053 + 3268 3418 3514 4156 4369 + 3269 4278 3041 4300 4463 + 3270 2873 3936 4433 4540 + 3271 3115 3113 3099 3114 + 3272 3082 3052 4249 4573 + 3273 3554 3714 1409 1703 + 3274 2948 789 2946 783 + 3275 3324 3219 3298 3202 + 3276 3297 3316 3290 3202 + 3277 3202 2739 3224 3184 + 3278 4143 1200 4672 4673 + 3279 2739 3223 3185 3184 + 3280 2798 3171 3155 3070 + 3281 3621 3622 3080 4392 + 3282 3024 4006 4037 4075 + 3283 3257 3225 3260 3258 + 3284 1697 3069 3580 3566 + 3285 837 2768 905 2766 + 3286 1036 2661 84 4702 + 3287 2592 4363 4412 4585 + 3288 3160 3121 3126 3168 + 3289 3298 3225 3259 3222 + 3290 1407 1404 4442 4707 + 3291 3316 3297 3298 3202 + 3292 2377 2380 847 2372 + 3293 745 1623 2348 736 + 3294 1576 1230 1184 4485 + 3295 2863 3271 3283 1137 + 3296 3998 3969 3976 3992 + 3297 2812 2752 3242 3250 + 3298 3258 3083 982 3222 + 3299 960 1090 932 936 + 3300 3806 3807 3266 4176 + 3301 3348 3309 3276 2827 + 3302 2947 2944 2948 2936 + 3303 2760 2761 3052 3243 + 3304 3129 3128 3121 1273 + 3305 3621 3080 3596 4392 + 3306 1559 1228 741 1224 + 3307 3156 1262 1090 960 + 3308 3554 1703 1697 3566 + 3309 1064 2588 4053 4347 + 3310 1234 1312 1136 342 + 3311 1025 1030 1027 1029 + 3312 1749 1584 1751 4422 + 3313 1169 1167 1161 1163 + 3314 3153 2739 3185 3039 + 3315 3037 3186 3185 3039 + 3316 3554 1697 3580 3566 + 3317 2739 3153 3223 3039 + 3318 3156 2739 3223 3039 + 3319 2739 3156 3224 3039 + 3320 3638 3106 3105 3111 + 3321 3182 3186 3183 3181 + 3322 3961 3914 3912 364 + 3323 3322 3341 3340 4388 + 3324 2843 2848 2842 2832 + 3325 1698 1708 1699 4461 + 3326 2873 3936 4540 4693 + 3327 2752 2812 3251 3250 + 3328 640 3885 3884 4009 + 3329 2819 4350 4492 4595 + 3330 2966 2953 2990 2967 + 3331 745 1623 774 4034 + 3332 1422 1353 1435 1442 + 3333 3191 3187 861 863 + 3334 1441 1444 1427 1443 + 3335 3346 3199 3201 3198 + 3336 2757 837 2764 2766 + 3337 559 564 562 557 + 3338 3097 3098 3103 3096 + 3339 3113 704 3115 3099 + 3340 2732 905 851 2809 + 3341 1383 1365 2010 1996 + 3342 2283 2501 2506 4007 + 3343 3578 408 4117 4201 + 3344 3352 3294 3178 3350 + 3345 3186 3298 3202 3179 + 3346 3041 4278 4300 4669 + 3347 3082 3173 3051 3050 + 3348 3545 2785 3582 4722 + 3349 3244 3305 3200 3304 + 3350 3046 3022 3200 3179 + 3351 2779 2777 2771 2778 + 3352 2016 438 2013 4134 + 3353 503 1397 1439 50 + 3354 2977 2841 2963 2957 + 3355 2692 3895 4344 4473 + 3356 2976 2842 2973 2832 + 3357 2941 2886 4162 4183 + 3358 1197 1553 4020 4419 + 3359 3346 3314 3200 3217 + 3360 2653 1033 1032 4184 + 3361 2831 2848 2830 2844 + 3362 2761 3227 3052 3243 + 3363 3156 3224 3039 1262 + 3364 2851 2832 2836 325 + 3365 2095 11 559 7 + 3366 2794 2734 3005 3181 + 3367 3311 2748 3314 3251 + 3368 3232 3200 3201 3198 + 3369 3023 3151 3183 3181 + 3370 2493 2276 2488 4007 + 3371 4110 737 4290 4496 + 3372 1357 1352 1358 562 + 3373 301 303 3986 4516 + 3374 3178 3179 3350 3293 + 3375 2667 4385 4386 4564 + 3376 3232 3200 2735 2749 + 3377 3200 3346 3201 3198 + 3378 1041 1221 4089 4598 + 3379 1029 3173 4407 4694 + 3380 1705 4155 4389 4461 + 3381 753 1648 4233 4488 + 3382 1720 1666 1763 4152 + 3383 3179 2733 2745 2750 + 3384 3744 3743 234 3726 + 3385 3221 2847 2846 2844 + 3386 639 2661 2917 4281 + 3387 958 966 1261 936 + 3388 3821 3859 3825 235 + 3389 1129 1159 1127 4349 + 3390 3352 3178 3179 3350 + 3391 3290 3333 3202 3337 + 3392 952 947 983 1261 + 3393 2769 2669 2772 2649 + 3394 2350 746 736 4341 + 3395 2760 3052 3242 3243 + 3396 3605 3427 3594 4467 + 3397 1666 4313 4394 4525 + 3398 2483 2506 2503 4007 + 3399 786 347 1215 4063 + 3400 3052 2812 3242 3243 + 3401 355 2956 357 352 + 3402 2748 3314 3251 3250 + 3403 3172 3134 3060 3050 + 3404 3314 2753 3242 3250 + 3405 633 3273 3881 4317 + 3406 562 1354 3 557 + 3407 4427 3406 4435 4545 + 3408 3314 3304 3307 3308 + 3409 434 2660 2677 4529 + 3410 1348 1337 2089 1355 + 3411 2969 2836 2833 325 + 3412 459 1492 458 4141 + 3413 949 1007 943 971 + 3414 3647 3661 3659 3650 + 3415 3314 3304 3306 3307 + 3416 1757 1754 1710 4437 + 3417 3222 3258 4257 4276 + 3418 3217 3346 3310 3203 + 3419 3186 3153 3185 4495 + 3420 3217 3314 3216 3306 + 3421 3186 3037 3185 3184 + 3422 3199 3346 3201 3217 + 3423 3346 3200 3201 3217 + 3424 3345 3346 3217 3203 + 3425 3314 3217 3310 3307 + 3426 1182 1142 1141 4590 + 3427 3217 3216 3218 3306 + 3428 3324 3219 3202 3217 + 3429 3200 3265 3355 3217 + 3430 3298 3324 3202 3217 + 3431 3298 3202 3179 3217 + 3432 3200 3298 3179 3217 + 3433 3223 2794 3185 2795 + 3434 3182 3352 3179 3217 + 3435 3316 3182 3202 3217 + 3436 3219 3316 3202 3217 + 3437 795 1153 4271 4362 + 3438 2323 2327 2326 2332 + 3439 3156 1262 960 3226 + 3440 3608 3609 3607 4470 + 3441 3030 3148 3149 3014 + 3442 11 497 25 1434 + 3443 3314 3200 3143 2753 + 3444 987 1263 1255 4131 + 3445 3178 3180 3179 3293 + 3446 1758 1764 4163 4177 + 3447 3472 3482 4597 4644 + 3448 3814 3824 3864 253 + 3449 1337 1359 1343 1341 + 3450 354 791 2987 2986 + 3451 737 4058 4290 4496 + 3452 1444 1445 504 1427 + 3453 644 3348 580 649 + 3454 3155 3154 1256 3126 + 3455 3386 3504 4178 4244 + 3456 3161 3157 3160 3129 + 3457 3623 3621 3596 4392 + 3458 3386 3546 3504 4244 + 3459 932 933 1088 4206 + 3460 2483 2479 53 4007 + 3461 4058 737 4110 4496 + 3462 1760 1764 4152 4163 + 3463 3171 3152 3155 3070 + 3464 1159 1392 1415 4349 + 3465 2956 357 352 358 + 3466 3309 3277 3276 2827 + 3467 3240 828 2765 2746 + 3468 3240 2764 828 2746 + 3469 4444 4029 4729 4762 + 3470 2825 2969 2824 2833 + 3471 2972 2951 2974 2952 + 3472 2832 2976 2836 2833 + 3473 2975 2834 2954 2835 + 3474 2976 2975 2836 2833 + 3475 2831 2976 2973 2832 + 3476 2925 2940 2915 2939 + 3477 303 3979 3986 2814 + 3478 1396 1441 1438 1443 + 3479 2834 2824 2835 2836 + 3480 3859 3821 3809 235 + 3481 3726 3732 3760 3845 + 3482 3996 3982 3994 3973 + 3483 3979 3994 3973 3993 + 3484 1726 1771 1772 1769 + 3485 2959 2843 351 317 + 3486 2817 3991 3999 3992 + 3487 2527 2524 2528 41 + 3488 2694 809 782 2721 + 3489 2976 2975 2835 2836 + 3490 1834 1850 1845 1831 + 3491 3791 248 246 3770 + 3492 2307 581 587 593 + 3493 953 975 952 946 + 3494 3991 2817 3957 3978 + 3495 317 351 316 325 + 3496 2963 2841 2958 2957 + 3497 2965 2966 2974 2967 + 3498 2979 2977 2962 2981 + 3499 2976 2975 2972 2973 + 3500 3982 3991 3969 3992 + 3501 797 2947 784 803 + 3502 2959 2960 2955 2958 + 3503 341 354 344 340 + 3504 3821 3873 3859 3813 + 3505 2935 2937 807 2691 + 3506 2837 2843 317 2832 + 3507 3213 324 4492 4595 + 3508 3789 3791 3792 3783 + 3509 2934 2949 2700 2938 + 3510 3798 3792 248 247 + 3511 1444 1426 505 504 + 3512 561 496 559 497 + 3513 1352 1430 1351 1431 + 3514 2065 2069 2060 2067 + 3515 2006 176 177 168 + 3516 3737 231 234 3765 + 3517 2964 2968 2990 2967 + 3518 2052 2053 2086 2085 + 3519 2935 2701 2689 2933 + 3520 2956 2977 350 358 + 3521 915 905 906 904 + 3522 2937 2935 2693 2688 + 3523 3373 3374 506 3434 + 3524 1445 1444 1442 1443 + 3525 3136 3139 3141 3142 + 3526 2242 873 835 2235 + 3527 3834 3822 3828 3832 + 3528 2972 2841 2963 2973 + 3529 801 2924 790 804 + 3530 355 341 357 340 + 3531 791 2978 2987 2986 + 3532 3987 3970 3746 3989 + 3533 831 870 2244 2245 + 3534 542 1340 1338 1377 + 3535 340 357 2990 350 + 3536 3 2086 2091 2085 + 3537 3573 3903 3906 3576 + 3538 3798 3983 247 3745 + 3539 3778 3970 3987 3989 + 3540 3577 3573 3906 3576 + 3541 2226 2231 2217 2230 + 3542 1425 1435 498 1442 + 3543 929 933 1016 928 + 3544 3213 2851 2969 325 + 3545 2320 2239 2387 864 + 3546 3970 3778 242 3989 + 3547 2264 2307 581 2265 + 3548 2222 2213 2231 2228 + 3549 395 391 393 390 + 3550 2851 349 2951 2954 + 3551 353 349 356 351 + 3552 357 356 352 358 + 3553 2831 323 2830 2833 + 3554 354 341 355 340 + 3555 2083 2091 2085 6 + 3556 2985 2979 2962 2981 + 3557 3824 3825 3829 3823 + 3558 2950 2947 784 2936 + 3559 1353 1424 1421 1434 + 3560 3756 3791 3789 3783 + 3561 2701 788 2689 789 + 3562 3756 3790 3759 3791 + 3563 561 1432 1428 1351 + 3564 804 2924 2982 2926 + 3565 3813 3854 3846 3858 + 3566 2468 2467 2469 692 + 3567 1356 1337 573 565 + 3568 2059 1444 1442 2048 + 3569 2972 2841 2955 2958 + 3570 3811 3837 3844 3827 + 3571 355 2989 2985 2981 + 3572 266 255 3829 253 + 3573 2931 2945 2948 2932 + 3574 3164 3136 3028 3163 + 3575 2947 797 784 2936 + 3576 3859 3813 233 3846 + 3577 3785 3801 3874 368 + 3578 414 413 104 409 + 3579 2983 2964 2962 2981 + 3580 3790 3791 246 3770 + 3581 354 341 344 2983 + 3582 341 2991 344 2983 + 3583 798 2944 803 802 + 3584 2980 2978 2962 2924 + 3585 3779 3780 3725 3775 + 3586 250 254 256 260 + 3587 2940 2915 2914 2982 + 3588 3977 3776 3974 240 + 3589 2965 2964 2961 2967 + 3590 3759 3765 3731 3760 + 3591 3756 3777 3791 3783 + 3592 2945 2947 2948 2932 + 3593 757 2686 755 2711 + 3594 3430 525 3364 509 + 3595 2370 2380 2388 2384 + 3596 2948 2931 800 783 + 3597 3790 3756 3759 249 + 3598 340 355 2962 2981 + 3599 2960 356 2955 2958 + 3600 2972 356 2963 2958 + 3601 2841 2842 2958 2957 + 3602 2380 2371 2370 2388 + 3603 2326 2327 2325 2328 + 3604 163 154 155 153 + 3605 471 2203 135 2202 + 3606 264 3813 233 262 + 3607 2239 2320 2243 864 + 3608 2935 2937 2691 2933 + 3609 2949 2701 2700 2938 + 3610 3752 3744 3855 3726 + 3611 2950 2915 2938 2939 + 3612 2934 2950 2949 2938 + 3613 368 3857 3843 370 + 3614 3797 3733 3739 3724 + 3615 3854 3813 3815 3823 + 3616 3728 3756 3727 3777 + 3617 1335 1343 1336 1346 + 3618 357 2964 340 2990 + 3619 3998 3957 244 301 + 3620 2834 2974 2954 2952 + 3621 3766 3855 3845 3862 + 3622 3789 3778 3977 3980 + 3623 3733 3797 3734 3724 + 3624 3734 3857 3842 3853 + 3625 344 2991 2921 2916 + 3626 373 3739 3724 375 + 3627 2803 3598 4001 4137 + 3628 3855 3858 3732 3862 + 3629 3855 3732 3845 3862 + 3630 1364 1362 1368 1370 + 3631 3777 3792 3798 247 + 3632 3756 3784 3727 3777 + 3633 3996 3776 3974 3980 + 3634 3744 3743 3799 3742 + 3635 2992 2991 2914 2926 + 3636 242 248 247 3989 + 3637 3322 3321 3342 3340 + 3638 3728 231 3799 3727 + 3639 264 237 261 263 + 3640 3756 3784 3759 3727 + 3641 3778 3789 3792 3783 + 3642 3765 238 3760 3845 + 3643 2991 344 2921 801 + 3644 3736 3790 3777 247 + 3645 2977 2989 2961 2981 + 3646 3736 3983 3750 3745 + 3647 2974 2951 2954 2952 + 3648 3957 2817 3999 3975 + 3649 2831 2976 2832 2833 + 3650 3737 3743 3751 3742 + 3651 3977 3778 3770 240 + 3652 3756 3784 3777 3783 + 3653 3790 247 245 246 + 3654 3776 241 3974 240 + 3655 248 3746 3745 3989 + 3656 3973 3979 3993 3986 + 3657 3776 3977 3979 243 + 3658 3984 3983 3798 3745 + 3659 3987 3778 3792 3974 + 3660 3982 3999 244 301 + 3661 3973 3982 301 3981 + 3662 2825 2824 2830 2833 + 3663 2700 2934 785 789 + 3664 2084 1354 2089 1355 + 3665 3816 3824 3815 3823 + 3666 244 302 3994 3973 + 3667 3982 244 3994 3973 + 3668 3776 3789 3977 3980 + 3669 3994 3996 3974 3980 + 3670 3776 3996 3994 3980 + 3671 3986 303 2821 2822 + 3672 2821 303 2814 2822 + 3673 4098 4320 4403 4527 + 3674 303 3986 2821 2814 + 3675 1029 980 4029 4252 + 3676 303 3957 301 2816 + 3677 302 3979 303 243 + 3678 2969 3213 2825 2850 + 3679 2825 3213 2820 2850 + 3680 2348 2346 2347 4034 + 3681 489 500 3382 499 + 3682 3752 3743 3744 3726 + 3683 548 427 1384 1373 + 3684 3784 3756 3759 3760 + 3685 237 264 261 233 + 3686 237 232 264 233 + 3687 3854 232 237 233 + 3688 3854 237 261 233 + 3689 356 2972 2955 2958 + 3690 2841 2972 2963 2958 + 3691 807 2701 2700 789 + 3692 3090 3938 3473 4262 + 3693 20 2065 2060 2067 + 3694 922 893 881 894 + 3695 757 818 759 754 + 3696 369 367 368 370 + 3697 1349 1429 559 564 + 3698 784 799 803 785 + 3699 3809 3834 3822 3829 + 3700 258 254 266 3817 + 3701 2686 2712 755 2711 + 3702 371 369 367 368 + 3703 2968 2964 2992 2967 + 3704 3873 3821 3859 3825 + 3705 3755 3850 3848 3754 + 3706 3850 3749 3848 3754 + 3707 264 3847 261 3815 + 3708 261 3854 233 3815 + 3709 756 2654 759 2720 + 3710 266 3809 3817 3832 + 3711 1777 1864 1860 1799 + 3712 3854 232 3846 3753 + 3713 3809 266 3822 3832 + 3714 1609 1230 1576 4485 + 3715 3865 3847 261 3867 + 3716 261 3847 263 3867 + 3717 3759 3765 3760 3845 + 3718 797 799 2936 2932 + 3719 2989 355 357 2981 + 3720 2979 2989 2977 2981 + 3721 2983 2991 2992 2926 + 3722 3811 3814 3822 3829 + 3723 3821 3825 3834 3829 + 3724 3824 3814 3811 3829 + 3725 3876 3812 3874 368 + 3726 886 2811 2666 2663 + 3727 234 3765 238 3760 + 3728 344 341 340 801 + 3729 3784 3731 3732 3760 + 3730 2991 341 344 801 + 3731 3811 252 266 3822 + 3732 3739 3723 3724 3735 + 3733 3723 3725 3739 3724 + 3734 2246 2237 630 629 + 3735 3814 252 3841 3822 + 3736 3841 252 3811 3822 + 3737 3814 3841 3811 3822 + 3738 508 490 489 509 + 3739 266 3809 3822 3829 + 3740 266 255 3809 3829 + 3741 255 267 253 256 + 3742 3733 361 3734 3730 + 3743 252 266 3822 3829 + 3744 3814 252 3822 3829 + 3745 3723 3734 3724 3735 + 3746 2751 852 2756 2759 + 3747 3776 3977 241 240 + 3748 2000 2003 1 2 + 3749 3873 3854 3813 3858 + 3750 2906 2944 2923 2922 + 3751 348 1237 4027 4052 + 3752 3812 369 3874 368 + 3753 367 377 3853 370 + 3754 3846 3766 3845 3862 + 3755 2915 2940 2938 2939 + 3756 232 233 3846 3766 + 3757 3731 3726 3732 3760 + 3758 3743 3726 3731 3760 + 3759 3846 3813 3858 3862 + 3760 234 3743 3765 3760 + 3761 3784 3727 3777 3731 + 3762 3727 3743 3726 3731 + 3763 237 3848 3865 236 + 3764 3754 230 3851 236 + 3765 3725 3797 3724 3775 + 3766 1361 549 1990 1995 + 3767 373 374 375 377 + 3768 2735 3232 2738 2802 + 3769 3733 373 3739 3724 + 3770 395 3913 3910 3952 + 3771 3194 3254 3263 3261 + 3772 3164 3150 3136 3142 + 3773 3394 3385 514 3395 + 3774 376 3780 3775 3738 + 3775 3879 395 3960 391 + 3776 3733 3795 3797 372 + 3777 2550 2565 2557 2555 + 3778 366 3800 3795 3797 + 3779 3876 3801 3730 3842 + 3780 2710 805 2714 2711 + 3781 3839 3877 257 3874 + 3782 852 2757 2756 2759 + 3783 3734 371 375 377 + 3784 3877 3812 3875 3838 + 3785 3822 3826 3828 3827 + 3786 3877 3835 3839 3838 + 3787 3723 3810 3857 3853 + 3788 374 376 3738 375 + 3789 369 367 370 259 + 3790 3360 3357 3364 3434 + 3791 3828 3826 3810 3818 + 3792 3960 387 391 365 + 3793 3810 3826 3857 3843 + 3794 1361 1384 1364 1373 + 3795 3133 3044 3041 3174 + 3796 3757 3729 3785 3758 + 3797 295 3887 4139 4368 + 3798 376 366 359 372 + 3799 2792 920 919 890 + 3800 366 360 359 372 + 3801 3795 3733 3797 3794 + 3802 3822 3811 3829 3827 + 3803 368 367 3842 370 + 3804 3857 3842 3853 370 + 3805 490 489 500 492 + 3806 3377 3362 3399 3376 + 3807 3239 2768 2757 2766 + 3808 893 889 881 2771 + 3809 3913 2879 3952 3948 + 3810 2774 2808 917 2806 + 3811 670 657 674 673 + 3812 3879 395 3910 3952 + 3813 3197 641 2907 2905 + 3814 2792 2791 2797 2743 + 3815 2867 3953 3913 3910 + 3816 768 2360 895 2325 + 3817 388 3963 378 364 + 3818 3879 395 391 393 + 3819 2774 2775 2776 2806 + 3820 915 2732 851 2809 + 3821 2791 2796 2743 2732 + 3822 3913 3909 3910 3952 + 3823 2768 2757 2755 2809 + 3824 257 3826 3844 3827 + 3825 3920 388 396 3909 + 3826 389 3920 396 3909 + 3827 1597 1056 1047 1050 + 3828 387 389 391 393 + 3829 389 3879 391 393 + 3830 396 389 3909 391 + 3831 388 396 3909 391 + 3832 3803 360 366 3902 + 3833 3909 387 3960 3972 + 3834 2668 2712 2715 2709 + 3835 3943 2870 385 3935 + 3836 3953 3919 3913 3910 + 3837 828 3235 2751 2756 + 3838 3802 362 3795 3758 + 3839 2879 3948 2877 2878 + 3840 857 2767 3229 2751 + 3841 2874 379 2877 2878 + 3842 273 3447 3479 3463 + 3843 2870 3356 3946 3935 + 3844 623 622 643 626 + 3845 3931 1654 4540 4582 + 3846 2768 3239 2757 3235 + 3847 3233 2767 3256 3229 + 3848 381 2874 2876 2878 + 3849 3913 395 390 3948 + 3850 3920 3968 388 3972 + 3851 3918 3919 3953 3910 + 3852 3963 3968 397 3964 + 3853 395 389 3879 3910 + 3854 3533 3527 3495 3525 + 3855 389 395 393 3910 + 3856 393 395 3913 3910 + 3857 1645 1191 815 4271 + 3858 2773 2792 2793 890 + 3859 867 3254 3252 2765 + 3860 888 2665 2788 887 + 3861 2791 2796 2797 2743 + 3862 330 2864 335 2881 + 3863 640 3884 3881 3956 + 3864 1390 1179 4739 4769 + 3865 2767 3233 868 3229 + 3866 390 380 3948 2877 + 3867 2775 2776 2806 2663 + 3868 2808 2774 2773 2741 + 3869 186 2002 535 2007 + 3870 818 809 781 810 + 3871 918 2758 2807 2809 + 3872 706 704 3269 702 + 3873 551 427 548 552 + 3874 1386 1385 1372 1383 + 3875 3240 2764 3237 2765 + 3876 2776 2775 922 889 + 3877 2808 2774 2776 2806 + 3878 395 393 3913 390 + 3879 2774 2770 2773 2741 + 3880 2694 2935 806 811 + 3881 3959 3961 3912 365 + 3882 2947 2945 2922 2932 + 3883 3239 2757 2764 2766 + 3884 3229 857 2751 3241 + 3885 1999 1998 1996 1991 + 3886 1993 1361 1990 1995 + 3887 891 885 896 2332 + 3888 852 2757 828 2756 + 3889 846 850 838 842 + 3890 2664 2776 2666 2663 + 3891 1588 4417 4499 4719 + 3892 773 776 2673 2670 + 3893 2757 2768 837 2766 + 3894 1090 934 932 4206 + 3895 2740 918 2807 2809 + 3896 3968 3914 364 3972 + 3897 3961 3912 3972 3911 + 3898 388 3968 364 3972 + 3899 3909 3961 3972 3911 + 3900 905 2758 2809 2742 + 3901 3912 3960 364 3972 + 3902 905 2766 2730 2726 + 3903 2775 2774 922 882 + 3904 2673 763 2649 2650 + 3905 3914 3961 3796 364 + 3906 900 2406 908 883 + 3907 2006 176 2001 2007 + 3908 2758 918 2759 851 + 3909 881 922 894 760 + 3910 922 2775 882 889 + 3911 2808 2774 917 882 + 3912 2709 2715 2644 2651 + 3913 3959 3780 359 363 + 3914 760 888 887 777 + 3915 360 359 364 363 + 3916 2397 2398 902 2399 + 3917 2792 2791 2743 2732 + 3918 776 2686 780 755 + 3919 2944 2978 2927 2924 + 3920 2649 761 887 777 + 3921 431 1362 1368 425 + 3922 1502 1519 1499 441 + 3923 1797 1864 1799 1861 + 3924 176 167 2001 2000 + 3925 521 3506 3396 3404 + 3926 621 3249 3247 3248 + 3927 2327 2367 2325 2328 + 3928 805 2710 754 2687 + 3929 2796 906 2732 2730 + 3930 907 2764 828 827 + 3931 2757 837 852 2764 + 3932 395 390 3948 2877 + 3933 172 822 1041 4089 + 3934 3797 3725 3739 3738 + 3935 3829 255 267 253 + 3936 3813 3859 233 262 + 3937 2774 2775 917 882 + 3938 2657 756 776 2673 + 3939 2072 63 4 2073 + 3940 922 2774 2770 2773 + 3941 3735 3739 3738 375 + 3942 3864 3824 267 253 + 3943 357 2964 2961 2981 + 3944 541 539 536 540 + 3945 2788 2665 2664 2651 + 3946 637 587 2311 2297 + 3947 2647 2712 2714 2711 + 3948 782 781 2655 2720 + 3949 2768 2758 2755 2759 + 3950 834 833 847 848 + 3951 967 970 966 1085 + 3952 776 2686 755 758 + 3953 3826 3818 3844 3843 + 3954 882 915 916 921 + 3955 3826 3810 3818 3843 + 3956 3797 374 3775 3738 + 3957 773 2673 2649 2670 + 3958 837 836 852 828 + 3959 2710 2688 2687 2711 + 3960 2715 2670 780 2644 + 3961 2780 2777 2669 2650 + 3962 919 882 916 921 + 3963 2777 2779 2771 2793 + 3964 2777 2772 2771 2778 + 3965 2661 639 2919 2880 + 3966 2314 2305 2302 2223 + 3967 508 3426 3416 3434 + 3968 2792 2808 882 2773 + 3969 3780 3725 3775 3738 + 3970 1428 1423 1430 1351 + 3971 3374 3360 3364 3434 + 3972 2796 2791 2797 2730 + 3973 3794 3797 3775 3771 + 3974 2407 2406 2341 883 + 3975 251 3810 3857 3843 + 3976 2686 2685 2687 2711 + 3977 3844 3812 3842 3843 + 3978 551 154 1384 1383 + 3979 922 893 894 920 + 3980 3963 3914 3796 364 + 3981 3796 3961 378 364 + 3982 845 2416 844 843 + 3983 734 724 731 729 + 3984 763 2669 2649 2650 + 3985 2665 888 2769 2649 + 3986 427 1386 1384 1373 + 3987 882 922 889 881 + 3988 853 846 2386 2373 + 3989 1385 1365 1383 1388 + 3990 776 756 763 2673 + 3991 776 773 761 758 + 3992 2796 906 2730 2726 + 3993 2673 2657 2672 2650 + 3994 376 374 359 363 + 3995 1386 427 1384 1383 + 3996 922 2779 893 920 + 3997 759 2654 2685 2720 + 3998 2780 763 777 2778 + 3999 155 153 2010 2005 + 4000 2657 2673 2649 2650 + 4001 2771 2779 2773 2793 + 4002 2669 2777 2772 2771 + 4003 551 550 154 552 + 4004 427 1366 552 1372 + 4005 1340 1338 1377 1378 + 4006 2772 2771 2770 2773 + 4007 1512 1515 1511 1514 + 4008 2808 2792 882 916 + 4009 2559 734 2627 2542 + 4010 911 898 900 908 + 4011 1945 130 1946 1944 + 4012 2376 2379 2385 2383 + 4013 2376 832 2389 2372 + 4014 2669 888 2769 760 + 4015 1510 1371 1516 1511 + 4016 2780 2669 2769 760 + 4017 1385 1366 1387 1372 + 4018 2934 799 2948 2936 + 4019 2684 1503 1499 440 + 4020 2673 2657 2649 2670 + 4021 2665 2769 2664 2666 + 4022 2769 2665 2649 2670 + 4023 3234 3249 3196 858 + 4024 2777 2779 2780 2778 + 4025 917 918 2744 916 + 4026 1367 539 1381 541 + 4027 2777 2780 2669 2772 + 4028 2669 2780 2769 2772 + 4029 2792 2771 2773 2793 + 4030 2081 63 2009 2078 + 4031 2808 2774 882 2773 + 4032 2526 2486 2532 2530 + 4033 2780 894 760 2772 + 4034 2769 2780 760 2772 + 4035 1344 1334 1345 1339 + 4036 876 875 2384 2369 + 4037 881 2769 760 2772 + 4038 894 881 760 2772 + 4039 627 868 859 628 + 4040 830 828 2765 829 + 4041 747 1583 1047 737 + 4042 2766 2731 2730 2726 + 4043 219 222 229 202 + 4044 428 1362 426 425 + 4045 1998 163 162 153 + 4046 2412 841 899 839 + 4047 1362 1374 1370 1514 + 4048 2226 2227 2231 2230 + 4049 1366 1385 552 1372 + 4050 707 2547 708 696 + 4051 1281 4323 4343 4562 + 4052 2334 2333 2404 2332 + 4053 837 852 2764 828 + 4054 1374 1369 1368 1370 + 4055 1550 1552 1564 1573 + 4056 427 548 428 1373 + 4057 1515 1510 1516 1511 + 4058 433 2684 1499 440 + 4059 2555 2543 696 2542 + 4060 986 1004 4085 4275 + 4061 229 438 441 440 + 4062 2764 2732 2766 2731 + 4063 2764 837 828 827 + 4064 2764 907 828 2746 + 4065 2420 2422 2426 2424 + 4066 3249 3196 3247 3248 + 4067 1501 1500 437 1499 + 4068 1500 1501 1519 1509 + 4069 1512 1515 1371 1511 + 4070 3254 3195 3192 860 + 4071 868 857 3229 3241 + 4072 828 907 827 829 + 4073 3939 3319 4354 4447 + 4074 837 2764 2766 827 + 4075 2393 2392 2394 2417 + 4076 2764 3239 2766 3240 + 4077 3058 2997 4450 4566 + 4078 3237 3261 3262 2765 + 4079 2937 2935 807 806 + 4080 158 2032 160 159 + 4081 830 3237 2765 2746 + 4082 2053 2052 2086 2092 + 4083 907 830 2746 2726 + 4084 551 548 549 426 + 4085 2732 905 2766 2730 + 4086 906 905 2732 2730 + 4087 2791 2732 2731 2730 + 4088 2731 2764 2746 2726 + 4089 1374 431 1514 432 + 4090 3985 3995 645 2683 + 4091 2730 2731 2746 2726 + 4092 2791 2796 2732 2730 + 4093 905 906 904 827 + 4094 828 830 2765 2746 + 4095 2864 335 2883 383 + 4096 1923 1921 209 224 + 4097 867 830 871 3262 + 4098 3237 830 3262 2746 + 4099 1371 1512 1511 1514 + 4100 1385 1386 1384 1383 + 4101 2876 2869 2878 2875 + 4102 380 392 3948 2877 + 4103 549 161 1990 1995 + 4104 857 858 3241 859 + 4105 2767 836 857 868 + 4106 757 759 805 754 + 4107 983 958 1261 936 + 4108 766 771 2327 772 + 4109 844 845 843 903 + 4110 1374 1361 1363 2033 + 4111 3249 3234 3256 3241 + 4112 852 836 857 2751 + 4113 2767 3236 3229 2751 + 4114 2764 3239 3240 828 + 4115 1503 2684 438 440 + 4116 868 857 3241 859 + 4117 852 2757 2764 828 + 4118 2757 3239 2764 828 + 4119 2756 2757 2755 2759 + 4120 837 905 2758 851 + 4121 3254 867 3252 868 + 4122 3233 3252 2767 868 + 4123 3233 3234 3252 868 + 4124 3195 627 628 860 + 4125 2715 2712 2714 2709 + 4126 437 1501 439 436 + 4127 3254 3252 3195 868 + 4128 627 3254 3195 868 + 4129 4235 4127 4306 4318 + 4130 3252 3234 3195 868 + 4131 2105 2106 2142 2103 + 4132 2556 2559 734 2627 + 4133 2400 2342 2338 2322 + 4134 745 2348 778 736 + 4135 2393 2339 2405 2394 + 4136 768 2360 771 767 + 4137 2354 1202 736 749 + 4138 1502 229 1519 441 + 4139 3192 2671 861 863 + 4140 3247 623 2913 626 + 4141 1158 1174 4135 4519 + 4142 3818 258 260 259 + 4143 2694 2693 2688 2687 + 4144 2689 2721 2946 783 + 4145 781 818 2655 2720 + 4146 784 2700 785 789 + 4147 2934 2700 2933 789 + 4148 2701 2689 2933 789 + 4149 2700 2701 2933 789 + 4150 791 341 801 802 + 4151 2950 2938 784 2939 + 4152 805 757 754 755 + 4153 2712 2647 2714 2709 + 4154 885 896 2332 880 + 4155 809 811 781 810 + 4156 888 2665 2769 2664 + 4157 2937 807 2691 2707 + 4158 2656 2654 2655 2720 + 4159 565 1356 1360 1347 + 4160 2694 2935 2689 2688 + 4161 2657 2649 2670 2686 + 4162 1338 1340 1339 1378 + 4163 2935 2694 806 2693 + 4164 2937 2935 806 2693 + 4165 754 2710 2688 2687 + 4166 2647 2668 805 2648 + 4167 567 566 1335 572 + 4168 760 773 761 887 + 4169 2686 757 755 758 + 4170 1379 1376 1380 1378 + 4171 745 764 774 765 + 4172 2934 2950 2938 2936 + 4173 3854 3755 237 3753 + 4174 805 806 754 2648 + 4175 2710 805 754 2648 + 4176 2065 2068 2069 2067 + 4177 2080 2075 2076 42 + 4178 759 805 754 2687 + 4179 2088 2089 42 36 + 4180 2033 1363 1993 1995 + 4181 773 776 2670 780 + 4182 2237 865 630 629 + 4183 2338 2340 896 2332 + 4184 809 788 811 810 + 4185 798 797 799 803 + 4186 782 2693 2719 2655 + 4187 42 37 38 36 + 4188 2688 2693 781 2655 + 4189 757 818 754 810 + 4190 1353 1433 496 497 + 4191 4 2072 2073 2077 + 4192 2095 559 2092 7 + 4193 624 2210 2262 2252 + 4194 1012 974 1009 972 + 4195 1337 554 1356 573 + 4196 811 806 781 810 + 4197 2654 2672 2686 2685 + 4198 2719 2693 2656 2655 + 4199 2299 2295 2308 2298 + 4200 2084 2083 2088 2089 + 4201 2088 42 38 36 + 4202 37 3 2091 6 + 4203 1802 1803 1811 1826 + 4204 3693 3547 3707 3564 + 4205 63 10 4 2073 + 4206 2938 2950 784 2936 + 4207 2689 2934 2948 2936 + 4208 2694 782 2693 2719 + 4209 2534 2451 653 2452 + 4210 1343 1359 1336 1346 + 4211 1366 550 539 552 + 4212 2947 2950 2925 2936 + 4213 1337 1359 572 565 + 4214 809 788 800 783 + 4215 788 2694 2689 2721 + 4216 2721 782 2719 2655 + 4217 1387 1382 1367 1381 + 4218 2224 2261 614 2260 + 4219 2083 2084 1354 2089 + 4220 1429 1349 2052 1354 + 4221 2693 2694 2719 2946 + 4222 542 569 1334 1345 + 4223 962 955 4289 4397 + 4224 2966 2965 2953 2967 + 4225 1385 1366 1375 1387 + 4226 132 134 1825 108 + 4227 566 567 1335 1343 + 4228 176 2003 2002 540 + 4229 167 2079 2001 2000 + 4230 1362 428 1368 425 + 4231 1385 551 1372 1383 + 4232 1382 1367 1381 541 + 4233 759 818 781 2720 + 4234 1791 1779 1792 1781 + 4235 353 356 2955 352 + 4236 2931 2945 2721 2946 + 4237 567 1335 1336 1344 + 4238 2951 2953 2954 2952 + 4239 2208 617 2248 2206 + 4240 50 1441 2065 20 + 4241 1352 1429 1357 1430 + 4242 1437 1445 1438 502 + 4243 505 1444 2048 43 + 4244 2975 2841 2972 2973 + 4245 1444 1422 1425 1442 + 4246 995 966 994 4179 + 4247 2425 2439 682 2442 + 4248 2055 2095 2087 2051 + 4249 2960 2977 2963 2957 + 4250 1444 1426 1445 1442 + 4251 2246 2254 2251 2262 + 4252 3542 3550 3549 3543 + 4253 2095 2053 2054 2051 + 4254 2916 340 2990 350 + 4255 2953 2916 2990 350 + 4256 2980 2979 2962 2987 + 4257 2437 2447 679 678 + 4258 831 834 835 875 + 4259 2049 2046 2050 2048 + 4260 2989 2960 2958 2957 + 4261 1441 2063 50 2065 + 4262 2080 1342 2077 1341 + 4263 2175 2170 1540 2165 + 4264 3221 2837 317 316 + 4265 3929 3888 3893 4538 + 4266 2989 2979 2977 2957 + 4267 1674 1675 1677 1668 + 4268 2979 2989 2985 2987 + 4269 504 1445 1438 1427 + 4270 2061 1397 2062 50 + 4271 2841 2959 353 2955 + 4272 2694 2689 2721 2946 + 4273 1200 4143 4484 4673 + 4274 2927 2923 790 802 + 4275 354 791 2986 790 + 4276 2945 2931 2721 2932 + 4277 1426 1444 1445 504 + 4278 505 1426 43 26 + 4279 2979 2985 2962 2987 + 4280 2062 2061 50 49 + 4281 2095 1349 11 2054 + 4282 2837 3221 2846 2844 + 4283 2273 2270 2285 2318 + 4284 2837 323 3221 2844 + 4285 323 2831 2830 2844 + 4286 1429 1350 1357 1430 + 4287 2089 554 37 1355 + 4288 2848 2831 2837 2844 + 4289 1397 1420 1439 1419 + 4290 1396 1420 1397 1419 + 4291 2084 2089 37 1355 + 4292 186 177 168 2007 + 4293 2370 2380 2384 2369 + 4294 1356 554 1355 1358 + 4295 2985 354 2962 2987 + 4296 355 2985 2962 2981 + 4297 2989 357 2961 2981 + 4298 344 340 2962 2981 + 4299 357 355 340 2981 + 4300 2989 2979 2985 2981 + 4301 2964 357 340 2981 + 4302 2061 20 49 2060 + 4303 2180 1470 191 192 + 4304 2935 2694 2693 2688 + 4305 2313 1208 2266 1210 + 4306 2215 2221 2217 2216 + 4307 4013 4130 4340 4460 + 4308 1359 2076 42 1341 + 4309 2947 2945 2948 2946 + 4310 1997 1990 1995 1991 + 4311 1366 550 1385 1375 + 4312 660 655 659 656 + 4313 2081 2003 2082 2000 + 4314 894 893 2771 2778 + 4315 894 2780 777 2778 + 4316 893 894 777 2778 + 4317 2382 833 2376 2383 + 4318 561 1349 559 1351 + 4319 2001 2079 2081 2000 + 4320 550 1366 1385 552 + 4321 569 567 1344 1345 + 4322 2665 888 2788 2664 + 4323 2002 1389 1380 1377 + 4324 186 168 2008 2007 + 4325 2780 894 2772 2778 + 4326 840 2409 2410 838 + 4327 2777 2780 2772 2778 + 4328 776 2657 2670 2686 + 4329 1345 1334 1338 1339 + 4330 2249 2259 2239 2243 + 4331 2668 2647 2712 2709 + 4332 1988 1999 1997 1991 + 4333 176 2001 2003 2000 + 4334 913 2401 2321 880 + 4335 3234 3249 858 3241 + 4336 74 100 93 1976 + 4337 1375 1387 1367 1381 + 4338 1375 1385 1387 1381 + 4339 567 569 1342 52 + 4340 2208 2248 614 2260 + 4341 2470 2457 2463 658 + 4342 877 866 2239 864 + 4343 42 2089 556 36 + 4344 1909 1968 1887 198 + 4345 2320 2240 2387 2241 + 4346 1397 503 2063 50 + 4347 2094 2084 37 2091 + 4348 2559 733 734 2542 + 4349 218 2043 2034 2042 + 4350 3037 3186 3183 3143 + 4351 3186 3179 3183 3143 + 4352 2093 2084 2076 2088 + 4353 2088 37 556 36 + 4354 2334 2340 2338 2332 + 4355 846 850 2377 838 + 4356 4251 987 4252 4254 + 4357 1382 1367 541 1379 + 4358 176 167 2006 2001 + 4359 2006 167 2079 2001 + 4360 2075 2076 42 38 + 4361 154 1383 2010 1996 + 4362 2079 2081 2000 2009 + 4363 2396 845 844 903 + 4364 1346 1336 1344 1339 + 4365 167 63 2009 2 + 4366 2468 2473 684 2461 + 4367 965 976 979 952 + 4368 542 1376 534 540 + 4369 1419 2061 2025 2027 + 4370 542 1342 52 2 + 4371 2046 2056 2050 2055 + 4372 554 2089 37 556 + 4373 567 569 1334 1342 + 4374 692 2468 684 2461 + 4375 569 542 1334 1342 + 4376 2267 2310 2266 2288 + 4377 2061 2062 2025 2064 + 4378 2062 2069 2025 2064 + 4379 2052 1349 559 7 + 4380 1445 1438 1427 1443 + 4381 561 496 497 1434 + 4382 2059 1444 1425 1442 + 4383 503 1397 2063 1440 + 4384 554 1337 2089 556 + 4385 497 44 25 2058 + 4386 43 44 47 26 + 4387 2244 2382 2245 2376 + 4388 1359 2076 2089 42 + 4389 2053 2052 2090 2085 + 4390 11 2095 2054 2087 + 4391 1956 1955 1936 1809 + 4392 2086 2095 11 2087 + 4393 1107 976 961 1105 + 4394 2227 2261 2222 2225 + 4395 1337 573 2089 556 + 4396 955 1090 960 936 + 4397 1435 1422 1425 1421 + 4398 831 2382 875 2390 + 4399 2052 2084 2090 2083 + 4400 2721 2931 2946 783 + 4401 958 952 983 1261 + 4402 573 554 563 557 + 4403 2075 27 2072 42 + 4404 1340 1376 1379 1378 + 4405 1357 1352 1430 1351 + 4406 803 801 802 804 + 4407 27 2075 2080 42 + 4408 2088 2093 2094 38 + 4409 1200 2264 4065 4673 + 4410 2068 2047 2048 2045 + 4411 2273 587 593 2298 + 4412 3269 706 4273 4591 + 4413 2272 2273 2287 2285 + 4414 1439 1397 2061 50 + 4415 2214 2215 2216 2291 + 4416 588 2267 2313 2266 + 4417 2310 2311 2313 2266 + 4418 2091 3 2085 6 + 4419 1352 1429 564 562 + 4420 958 939 936 950 + 4421 1354 2083 557 6 + 4422 563 1348 1354 1355 + 4423 2023 2024 2061 2062 + 4424 495 503 1439 50 + 4425 2316 2271 2290 2312 + 4426 503 1441 2063 50 + 4427 2087 2055 25 2058 + 4428 604 2546 2581 2582 + 4429 1824 1821 1823 1786 + 4430 3083 1696 1695 981 + 4431 2169 2132 1843 2168 + 4432 1865 1875 1797 1862 + 4433 58 57 59 61 + 4434 76 2170 2172 2165 + 4435 1426 1444 505 43 + 4436 57 59 61 62 + 4437 1353 2055 497 1434 + 4438 1348 1350 1429 1355 + 4439 2086 2095 2092 7 + 4440 943 978 941 977 + 4441 3969 4000 3976 3992 + 4442 2843 2976 351 317 + 4443 2522 2519 2528 2518 + 4444 2197 2163 2196 2152 + 4445 10 2082 1342 4 + 4446 544 10 1 2 + 4447 3957 3998 3999 301 + 4448 1437 1396 1397 1440 + 4449 3999 3998 244 301 + 4450 3982 244 3973 301 + 4451 1396 1441 1437 1438 + 4452 542 2003 1338 2 + 4453 1444 1422 1442 1443 + 4454 302 244 241 301 + 4455 2056 2049 2046 2050 + 4456 2448 2425 2424 2423 + 4457 2246 2208 2247 2248 + 4458 1433 496 561 1432 + 4459 2243 2240 874 864 + 4460 3186 3224 3143 983 + 4461 1444 1442 2048 43 + 4462 2211 2217 2216 619 + 4463 3971 4000 3772 3976 + 4464 2251 624 2262 2252 + 4465 3224 2739 3039 1262 + 4466 1353 1423 1422 1421 + 4467 1353 2050 2055 1434 + 4468 1423 1424 1428 1421 + 4469 44 2049 2050 2059 + 4470 2059 2050 1421 1434 + 4471 1422 1444 1425 1421 + 4472 2531 2521 2533 2510 + 4473 1445 1442 1438 1443 + 4474 167 2009 1 2 + 4475 11 2054 2057 2087 + 4476 1424 561 1428 1434 + 4477 2504 2276 2480 2282 + 4478 37 2094 38 36 + 4479 2312 2316 2311 2297 + 4480 1600 1601 752 4100 + 4481 569 542 1342 52 + 4482 2605 657 655 651 + 4483 1444 1445 1427 1443 + 4484 2003 2081 1338 2 + 4485 2076 2088 2089 42 + 4486 2094 37 38 2091 + 4487 2056 2050 2055 2058 + 4488 1433 1353 496 1432 + 4489 1878 1865 1876 195 + 4490 1353 1423 1432 1351 + 4491 1353 1433 1435 1432 + 4492 1349 2095 11 559 + 4493 561 1349 11 559 + 4494 2980 2923 2924 2922 + 4495 2081 2082 1342 2 + 4496 2924 2927 790 804 + 4497 347 1216 1315 4063 + 4498 834 831 2382 875 + 4499 2050 2046 2055 2051 + 4500 2801 2733 2800 4367 + 4501 2940 2991 2921 2914 + 4502 2964 2983 2992 2926 + 4503 1864 1800 1777 1860 + 4504 801 790 802 804 + 4505 2336 2335 2400 2338 + 4506 559 11 497 25 + 4507 2076 2093 2088 38 + 4508 2057 11 2087 25 + 4509 2055 2057 2087 25 + 4510 1424 496 561 1434 + 4511 2074 2075 2093 2076 + 4512 2422 675 2433 689 + 4513 1342 542 1338 2 + 4514 1946 1945 1952 1953 + 4515 2238 2237 2247 2248 + 4516 2435 677 2442 2436 + 4517 11 2087 25 2058 + 4518 2072 2075 2074 2080 + 4519 27 2075 2072 2080 + 4520 2090 2084 2093 2094 + 4521 2219 2294 2292 2291 + 4522 2093 2075 2094 38 + 4523 2072 2074 2073 2077 + 4524 2462 652 2604 651 + 4525 2072 2075 42 38 + 4526 1093 978 1104 1105 + 4527 2208 617 618 614 + 4528 2094 2090 2091 2085 + 4529 63 10 2082 2 + 4530 554 1356 573 565 + 4531 3724 3739 3735 375 + 4532 2462 651 671 656 + 4533 614 2248 629 2260 + 4534 1380 1376 1377 1378 + 4535 2003 542 544 2 + 4536 1352 1429 1430 1431 + 4537 2084 2083 1354 6 + 4538 2088 2094 37 36 + 4539 2084 2088 2094 37 + 4540 2088 2084 2089 37 + 4541 1337 554 573 556 + 4542 2089 2088 37 556 + 4543 194 193 191 192 + 4544 2906 2947 2922 2932 + 4545 2082 1342 4 2077 + 4546 569 567 566 52 + 4547 1839 1854 1849 1848 + 4548 2339 2336 2393 2335 + 4549 1352 561 564 1351 + 4550 654 657 655 2602 + 4551 539 1389 536 535 + 4552 2721 2945 2719 2946 + 4553 790 2927 802 804 + 4554 680 693 677 682 + 4555 195 1789 1798 71 + 4556 2082 63 2078 2073 + 4557 3801 3757 3876 3730 + 4558 2201 2204 2183 2192 + 4559 1714 1244 1688 4204 + 4560 1389 1379 536 540 + 4561 1389 1382 1379 1380 + 4562 1294 1162 1304 1303 + 4563 615 616 624 617 + 4564 2237 2246 2248 629 + 4565 2227 2222 2231 2228 + 4566 2253 2207 616 2210 + 4567 850 849 2380 840 + 4568 2370 2390 2241 2369 + 4569 2409 2418 2414 2410 + 4570 1342 1336 2077 1341 + 4571 834 872 835 875 + 4572 2376 2379 2375 2385 + 4573 2409 2371 2380 840 + 4574 2003 176 2000 1 + 4575 1359 1337 2089 1341 + 4576 841 2409 838 839 + 4577 163 154 2010 1996 + 4578 155 2011 2008 1992 + 4579 1321 1311 343 339 + 4580 2467 2465 2469 692 + 4581 2054 1349 2057 2092 + 4582 2212 2237 2238 2248 + 4583 662 686 688 687 + 4584 1825 1804 118 108 + 4585 2515 2448 2446 2447 + 4586 2516 2515 2517 2513 + 4587 3157 2863 3271 3283 + 4588 2782 4236 4415 4470 + 4589 1176 1117 4700 4708 + 4590 892 884 2341 883 + 4591 627 862 628 860 + 4592 914 884 913 897 + 4593 747 775 1058 1201 + 4594 1055 1054 1056 1057 + 4595 405 433 434 404 + 4596 2143 2105 2103 2148 + 4597 2357 2356 778 2355 + 4598 2360 2367 765 2366 + 4599 1998 163 1996 1991 + 4600 1049 2344 764 774 + 4601 176 177 535 2007 + 4602 771 768 767 766 + 4603 229 1502 1499 441 + 4604 1624 1625 1628 1622 + 4605 841 2409 840 838 + 4606 2039 2035 2036 1994 + 4607 3234 3233 3229 3241 + 4608 770 779 762 771 + 4609 3400 3581 4369 4722 + 4610 2327 768 895 2325 + 4611 2415 909 898 899 + 4612 1517 1512 2044 2033 + 4613 29 30 2140 28 + 4614 428 1364 1362 1368 + 4615 82 2175 2157 2171 + 4616 2393 2402 2417 2399 + 4617 885 884 908 897 + 4618 1384 1386 1364 1373 + 4619 207 2039 2036 158 + 4620 2146 23 54 2147 + 4621 3233 3234 868 3241 + 4622 870 831 2244 2383 + 4623 771 768 766 2327 + 4624 2560 2546 2581 2548 + 4625 1231 1268 1318 1314 + 4626 159 2032 1990 1997 + 4627 764 2366 2368 2352 + 4628 2657 756 2673 2672 + 4629 1363 1374 2033 1993 + 4630 2323 2327 2324 2326 + 4631 910 2415 898 899 + 4632 831 875 2391 2390 + 4633 2367 2344 2366 2368 + 4634 901 910 2392 2413 + 4635 2418 845 842 839 + 4636 2324 2327 895 2326 + 4637 2028 2016 2029 2019 + 4638 1900 1970 1962 1897 + 4639 3234 868 3241 859 + 4640 2039 2035 2044 2036 + 4641 437 429 441 436 + 4642 2043 2035 2034 2041 + 4643 1594 1320 1219 1217 + 4644 2935 2701 807 811 + 4645 3195 627 868 859 + 4646 437 433 439 440 + 4647 1374 1512 2044 2036 + 4648 850 838 842 839 + 4649 158 2032 205 160 + 4650 2412 845 899 843 + 4651 1998 162 1988 159 + 4652 884 885 892 2341 + 4653 768 2327 2367 2325 + 4654 219 218 222 220 + 4655 744 778 769 2353 + 4656 2328 2367 2366 2368 + 4657 178 210 179 182 + 4658 2123 2146 3591 2141 + 4659 2845 3276 2828 4165 + 4660 2020 200 2029 2017 + 4661 1003 992 1021 4329 + 4662 901 911 898 900 + 4663 898 909 900 899 + 4664 2405 2403 2406 2394 + 4665 895 2324 2326 2325 + 4666 830 907 828 829 + 4667 2694 788 2689 811 + 4668 1513 2018 1512 1517 + 4669 3884 640 4009 4069 + 4670 726 3157 3271 3283 + 4671 2789 3923 4410 4435 + 4672 631 639 2918 2917 + 4673 632 726 3271 3283 + 4674 3107 706 4218 4378 + 4675 2012 2011 2010 2005 + 4676 3254 627 3195 860 + 4677 868 867 2765 829 + 4678 836 868 2765 829 + 4679 3234 3195 868 859 + 4680 1510 1519 436 1509 + 4681 2036 1374 1514 432 + 4682 2743 2792 2773 2741 + 4683 2036 207 158 205 + 4684 2032 1994 1993 1990 + 4685 1998 1989 153 2005 + 4686 910 2412 899 843 + 4687 918 915 851 2809 + 4688 870 2235 2383 2234 + 4689 2333 2404 2407 2341 + 4690 548 549 162 154 + 4691 616 2224 618 619 + 4692 832 849 2389 2372 + 4693 833 849 847 848 + 4694 1363 1361 1993 1995 + 4695 163 161 549 162 + 4696 724 700 2557 2564 + 4697 1822 1825 118 108 + 4698 981 3546 1027 1699 + 4699 2001 2002 2000 2007 + 4700 2001 176 2002 2007 + 4701 2419 2418 2378 2395 + 4702 2002 186 2008 2007 + 4703 186 155 2008 1992 + 4704 549 163 162 154 + 4705 1988 2032 159 1997 + 4706 875 876 2391 2390 + 4707 717 718 732 723 + 4708 161 549 162 1991 + 4709 3509 3493 3495 3496 + 4710 3150 3087 3142 3021 + 4711 3516 3526 3515 281 + 4712 3526 284 3515 281 + 4713 2006 2004 168 2007 + 4714 850 840 2377 838 + 4715 2002 2011 1388 2007 + 4716 2776 2775 889 886 + 4717 888 2649 887 777 + 4718 154 162 1384 1991 + 4719 907 830 828 2746 + 4720 1363 1361 1365 1383 + 4721 1007 970 966 971 + 4722 2382 2376 2389 2372 + 4723 373 3797 374 372 + 4724 2396 853 2374 903 + 4725 1961 1972 1964 97 + 4726 840 2410 2377 838 + 4727 1952 1954 140 1958 + 4728 2002 176 535 2007 + 4729 1349 2052 1354 2092 + 4730 2002 1389 540 1380 + 4731 539 1389 1381 541 + 4732 2416 2418 845 2419 + 4733 3300 3292 4187 4378 + 4734 853 2374 903 2373 + 4735 2405 2403 2407 2406 + 4736 3440 3414 4112 4194 + 4737 841 2409 2418 2414 + 4738 955 956 962 969 + 4739 2386 832 2375 2372 + 4740 2398 2396 2408 844 + 4741 3161 3284 726 342 + 4742 2335 2402 2401 2400 + 4743 876 2387 2241 2369 + 4744 2014 438 2013 202 + 4745 2405 914 911 908 + 4746 2639 2615 663 2641 + 4747 2374 2378 903 2373 + 4748 838 2409 842 839 + 4749 2342 2321 879 2359 + 4750 909 2415 910 2413 + 4751 2390 2382 2369 2389 + 4752 3169 3120 3170 1277 + 4753 2473 2474 2470 2453 + 4754 2398 2416 2408 2397 + 4755 717 2556 734 2627 + 4756 1381 2002 2008 1388 + 4757 912 911 2417 2399 + 4758 2481 2476 2478 2493 + 4759 2601 2600 665 668 + 4760 2004 2001 2008 2007 + 4761 2379 832 2376 2375 + 4762 2396 2408 2419 2374 + 4763 1985 1974 1967 87 + 4764 731 735 2551 2552 + 4765 96 66 87 64 + 4766 66 1961 1960 1987 + 4767 100 1983 93 1965 + 4768 842 2396 2378 903 + 4769 2375 2386 2372 2373 + 4770 2377 2378 2372 2373 + 4771 657 670 669 2602 + 4772 2605 657 669 2602 + 4773 2470 683 662 688 + 4774 884 900 908 883 + 4775 624 2210 2252 2206 + 4776 2638 2637 665 2593 + 4777 2589 2540 2537 2590 + 4778 2295 638 2308 2319 + 4779 2056 44 2050 2058 + 4780 2593 2611 668 2613 + 4781 870 2245 2235 2234 + 4782 2474 2472 2427 2456 + 4783 200 2020 2029 2019 + 4784 2371 2409 2414 2410 + 4785 740 1228 4088 4161 + 4786 4517 4525 4571 4719 + 4787 2382 831 2244 2245 + 4788 2406 2403 911 2394 + 4789 877 2239 2243 864 + 4790 2382 834 875 2369 + 4791 2379 2386 2385 2383 + 4792 2390 876 2241 2369 + 4793 2396 2419 2378 2374 + 4794 845 2412 899 839 + 4795 846 853 903 2373 + 4796 2377 846 903 2373 + 4797 2396 853 2381 2374 + 4798 2470 683 2457 658 + 4799 845 901 910 843 + 4800 2451 2534 2472 2452 + 4801 2380 2371 2388 2377 + 4802 2388 2371 2410 2377 + 4803 834 872 875 848 + 4804 849 850 847 848 + 4805 2623 2556 2557 2563 + 4806 2371 840 2410 2377 + 4807 2380 850 840 2377 + 4808 2371 2380 840 2377 + 4809 1007 966 943 971 + 4810 3002 1160 3044 3085 + 4811 1717 1409 1703 1702 + 4812 2334 2339 2336 2340 + 4813 718 2618 2627 719 + 4814 700 701 733 696 + 4815 425 549 432 160 + 4816 1745 1398 4040 4197 + 4817 909 2403 2413 898 + 4818 2004 2006 2001 2007 + 4819 1362 549 425 160 + 4820 910 909 2413 898 + 4821 2334 2339 2405 2404 + 4822 910 901 911 898 + 4823 910 2392 2413 2395 + 4824 176 2006 177 2007 + 4825 2393 2339 2335 2405 + 4826 914 2393 2335 2405 + 4827 2405 2403 2404 2407 + 4828 1999 1998 1989 1996 + 4829 2396 2381 2408 2374 + 4830 1989 2012 2010 2005 + 4831 1935 1984 1959 1979 + 4832 1998 1999 1988 1991 + 4833 153 1989 2010 2005 + 4834 2335 914 2405 2404 + 4835 2339 2335 2405 2404 + 4836 2415 909 2406 2411 + 4837 772 2342 896 879 + 4838 733 696 734 2542 + 4839 841 838 899 839 + 4840 845 910 899 843 + 4841 2333 2324 892 2341 + 4842 1716 1692 1683 1715 + 4843 2323 2327 772 896 + 4844 717 724 733 734 + 4845 66 1974 1981 87 + 4846 2413 2392 2394 2395 + 4847 2596 2598 2594 2615 + 4848 2562 700 699 2551 + 4849 2416 2398 2392 2397 + 4850 2398 901 910 2392 + 4851 2362 770 2329 2355 + 4852 2398 2416 910 843 + 4853 2630 2628 2631 716 + 4854 2415 841 909 899 + 4855 674 2600 673 668 + 4856 913 2321 879 880 + 4857 603 601 2582 708 + 4858 2321 2336 2400 2338 + 4859 2336 2401 2321 2400 + 4860 1972 1901 1971 1973 + 4861 2339 2336 2335 2401 + 4862 2336 2335 2401 2400 + 4863 2397 902 2417 2399 + 4864 2546 2560 2547 2548 + 4865 772 2342 879 2359 + 4866 606 2589 2537 2590 + 4867 2335 914 2404 913 + 4868 2404 914 884 913 + 4869 2342 772 896 2359 + 4870 2562 2565 2564 2535 + 4871 2338 2342 896 2337 + 4872 2321 2400 2342 2338 + 4873 769 778 2365 2353 + 4874 2340 2336 2338 2337 + 4875 2332 896 879 880 + 4876 2331 2337 2330 2329 + 4877 762 770 771 2362 + 4878 2340 2338 896 2337 + 4879 2623 2621 2620 2563 + 4880 2559 2618 2627 2558 + 4881 447 445 488 54 + 4882 1926 212 1928 1931 + 4883 766 771 772 896 + 4884 2323 2331 2340 2337 + 4885 2543 701 2555 696 + 4886 2359 2342 2322 2337 + 4887 885 2334 897 880 + 4888 885 766 891 896 + 4889 2336 2334 2340 2338 + 4890 2403 901 2413 898 + 4891 2401 913 897 880 + 4892 766 891 896 895 + 4893 914 2393 911 2343 + 4894 2561 2543 2555 2542 + 4895 2338 2332 879 880 + 4896 769 778 2347 2365 + 4897 724 2559 733 734 + 4898 2571 2570 595 602 + 4899 1188 1192 813 1190 + 4900 999 998 4172 4342 + 4901 640 3955 3884 3956 + 4902 2618 2623 2627 2558 + 4903 2594 664 2615 2597 + 4904 3503 3440 4112 4194 + 4905 3187 3207 3188 645 + 4906 3269 3270 702 4572 + 4907 74 67 65 100 + 4908 1906 95 92 198 + 4909 604 601 707 708 + 4910 2348 744 778 736 + 4911 2565 2562 2557 2555 + 4912 2236 2256 2250 2258 + 4913 228 225 164 1922 + 4914 1926 1886 1927 213 + 4915 1972 1901 189 1971 + 4916 707 604 698 701 + 4917 1960 1963 1981 1942 + 4918 601 604 2546 708 + 4919 461 454 1484 32 + 4920 2556 734 731 729 + 4921 2378 2396 2374 903 + 4922 2642 2630 2597 2641 + 4923 1262 934 996 935 + 4924 976 965 1122 1106 + 4925 652 2462 2606 2458 + 4926 189 95 1906 198 + 4927 2636 2633 2634 2635 + 4928 697 604 2544 2590 + 4929 2402 2397 2417 2399 + 4930 2385 2244 2383 2234 + 4931 181 180 183 182 + 4932 722 721 720 716 + 4933 1901 1970 1971 1973 + 4934 2554 2562 2541 2551 + 4935 1971 1973 1974 1969 + 4936 1480 2100 2098 1482 + 4937 912 902 2343 2399 + 4938 1909 1970 1900 1910 + 4939 2572 2584 2586 1063 + 4940 1324 1292 1287 1286 + 4941 2617 2616 2619 2642 + 4942 910 845 899 839 + 4943 2508 60 2516 2513 + 4944 2637 665 667 716 + 4945 2471 2470 2457 2463 + 4946 2446 2448 680 2447 + 4947 1107 961 1104 1105 + 4948 2459 2427 2455 2466 + 4949 1972 1982 1963 1981 + 4950 845 2396 842 903 + 4951 2377 846 838 903 + 4952 846 845 842 903 + 4953 1737 1741 4116 4763 + 4954 3438 3556 3409 3562 + 4955 3051 3173 3053 1027 + 4956 1262 1090 960 983 + 4957 664 2594 2600 2597 + 4958 1392 1179 4717 4739 + 4959 958 936 959 950 + 4960 2598 2596 2594 2600 + 4961 2598 2594 2615 2597 + 4962 2596 2598 2603 2599 + 4963 2421 2465 2420 2469 + 4964 926 924 943 947 + 4965 1018 1002 1022 4013 + 4966 2053 2095 2086 2085 + 4967 657 654 655 673 + 4968 1483 1484 1482 1494 + 4969 1975 79 1977 1979 + 4970 2630 2600 2597 2631 + 4971 2473 2470 2468 2467 + 4972 2484 57 2485 2486 + 4973 2231 2305 2217 2293 + 4974 1142 1413 1756 1143 + 4975 2448 2422 675 2433 + 4976 1419 2063 1508 2027 + 4977 2222 2227 2231 2223 + 4978 2484 2487 2486 2532 + 4979 19 2515 2447 2513 + 4980 970 1084 1095 1094 + 4981 2421 690 676 694 + 4982 2251 615 617 2248 + 4983 2508 2516 2517 2513 + 4984 2207 2214 2210 2252 + 4985 2486 2526 2532 2531 + 4986 4611 4282 4627 4631 + 4987 495 1419 1508 2027 + 4988 2448 675 680 2447 + 4989 2606 2462 2604 2463 + 4990 2482 59 2476 2523 + 4991 2433 2421 2420 2426 + 4992 855 625 869 2232 + 4993 2422 2433 2420 2426 + 4994 2464 2449 695 676 + 4995 2462 659 651 656 + 4996 2467 684 687 691 + 4997 3442 3433 3443 3441 + 4998 2470 2471 2468 2463 + 4999 2471 2468 2469 2461 + 5000 2465 2421 2467 2469 + 5001 2599 2603 2602 2604 + 5002 2254 2246 2251 2206 + 5003 672 653 2614 671 + 5004 2207 2211 616 2210 + 5005 2470 2474 2463 2453 + 5006 1870 1857 1867 206 + 5007 686 684 688 687 + 5008 2305 2231 2228 2230 + 5009 4294 4082 4404 4651 + 5010 2482 2481 2476 2478 + 5011 2233 2237 2238 2239 + 5012 121 132 1822 118 + 5013 2433 2465 690 692 + 5014 2468 686 2467 684 + 5015 2468 2467 692 684 + 5016 2515 19 2437 2509 + 5017 2253 2208 2261 2213 + 5018 2465 2421 2433 690 + 5019 675 680 693 677 + 5020 1790 1782 1783 1789 + 5021 1863 86 1876 227 + 5022 2509 2437 2443 678 + 5023 57 2487 2484 2482 + 5024 2470 2473 2468 2461 + 5025 2272 2273 2285 2318 + 5026 1208 583 588 2313 + 5027 2487 57 2484 2486 + 5028 58 2481 2478 2488 + 5029 2480 2479 4076 4326 + 5030 2269 2270 2267 2288 + 5031 1574 1553 1552 4209 + 5032 1878 1865 1875 1876 + 5033 59 2482 40 2523 + 5034 3049 3062 3063 4205 + 5035 2269 2271 581 2267 + 5036 2526 2532 2533 2530 + 5037 750 4169 4240 4488 + 5038 1208 583 2313 2266 + 5039 2513 60 681 41 + 5040 2295 2219 2221 2220 + 5041 2274 2264 583 2265 + 5042 2485 57 2526 2486 + 5043 2507 2443 681 678 + 5044 2263 2264 2274 2265 + 5045 3700 3118 3703 4086 + 5046 2494 2276 2495 2493 + 5047 3136 3164 1333 3163 + 5048 4289 3222 960 955 + 5049 948 1005 1008 1006 + 5050 3303 580 4002 4273 + 5051 2317 2306 2290 2318 + 5052 1874 1837 1841 1838 + 5053 2479 2483 2490 2491 + 5054 2508 2516 2514 2517 + 5055 2294 2219 2295 2220 + 5056 2437 2515 2509 2438 + 5057 1762 1752 1759 1711 + 5058 2246 2251 615 2262 + 5059 2269 2287 2274 2265 + 5060 1941 1986 125 126 + 5061 2507 2508 2510 2509 + 5062 1791 1795 71 1861 + 5063 581 2269 2274 2265 + 5064 40 2529 2524 5 + 5065 2487 2486 2532 2531 + 5066 1529 1522 1521 1523 + 5067 1425 2059 1421 1434 + 5068 2511 2444 2527 678 + 5069 2533 2521 2530 2510 + 5070 1176 1118 1120 4708 + 5071 2294 2295 638 2220 + 5072 2217 2305 646 2293 + 5073 854 2254 2262 2250 + 5074 2061 2027 12 2022 + 5075 2145 2125 1038 4167 + 5076 2449 2429 2430 2428 + 5077 587 2307 593 2298 + 5078 80 99 1776 72 + 5079 134 110 108 111 + 5080 2207 2214 2211 2210 + 5081 682 19 681 678 + 5082 2516 2514 2517 2530 + 5083 2784 3169 3043 1121 + 5084 2084 2090 2083 2091 + 5085 2240 2387 874 864 + 5086 222 438 229 202 + 5087 215 203 143 206 + 5088 2458 660 658 659 + 5089 2272 2317 2308 2298 + 5090 2508 2437 2509 2438 + 5091 2062 2061 49 2060 + 5092 653 2460 661 2454 + 5093 1332 1282 1329 1293 + 5094 2443 2437 2441 679 + 5095 497 2055 25 1434 + 5096 414 1466 417 409 + 5097 2517 2508 2513 2507 + 5098 2224 2253 2261 2213 + 5099 2218 2226 2301 635 + 5100 865 2237 2233 629 + 5101 982 3225 3222 983 + 5102 2306 2317 2299 2308 + 5103 680 675 693 689 + 5104 2446 2448 2425 680 + 5105 693 2432 677 694 + 5106 664 722 720 663 + 5107 655 657 659 651 + 5108 664 2615 722 663 + 5109 1279 1281 1280 1269 + 5110 2254 854 2256 2250 + 5111 2249 2239 2320 2243 + 5112 2387 876 874 864 + 5113 2382 875 2390 2369 + 5114 2047 20 2067 47 + 5115 2259 866 2249 2239 + 5116 675 2422 693 689 + 5117 625 866 878 856 + 5118 2550 2560 2555 2548 + 5119 2254 2251 2262 2250 + 5120 2233 2238 2249 2239 + 5121 625 854 2236 2256 + 5122 2605 652 2606 2458 + 5123 2340 2323 896 2332 + 5124 1095 970 967 1085 + 5125 2237 2246 2247 2248 + 5126 2081 1342 1338 2 + 5127 2212 2208 2261 2260 + 5128 2248 2237 629 2260 + 5129 2052 2086 3 2085 + 5130 57 2526 59 62 + 5131 866 865 878 856 + 5132 2207 2251 2252 2206 + 5133 2304 2314 2296 2302 + 5134 2261 2212 2222 2213 + 5135 2210 624 617 2206 + 5136 1811 122 112 113 + 5137 2221 2219 2216 2291 + 5138 2211 616 2210 624 + 5139 2251 2246 2248 2206 + 5140 685 2429 676 694 + 5141 2208 2212 2261 2213 + 5142 2213 2253 2228 2209 + 5143 1426 505 504 26 + 5144 2207 2214 2215 2209 + 5145 2214 2207 2211 2209 + 5146 2246 2208 2248 2206 + 5147 855 625 878 856 + 5148 854 625 615 856 + 5149 849 875 848 2384 + 5150 2207 2253 616 2213 + 5151 625 854 2254 856 + 5152 2236 2259 2256 2255 + 5153 4047 4403 4635 4711 + 5154 2261 2208 614 2260 + 5155 617 615 856 630 + 5156 924 932 936 951 + 5157 2457 2458 2463 658 + 5158 686 2467 684 687 + 5159 2251 615 624 617 + 5160 2219 2221 646 2293 + 5161 2305 2314 2302 2229 + 5162 2253 2208 2213 2228 + 5163 616 2210 624 617 + 5164 615 2246 2262 630 + 5165 2294 2219 2292 2295 + 5166 2470 2471 2457 2461 + 5167 118 1804 110 108 + 5168 2432 677 679 2436 + 5169 865 878 856 630 + 5170 1793 80 1777 72 + 5171 2009 2000 1 2 + 5172 2235 2232 2236 2258 + 5173 2481 58 2482 2480 + 5174 2231 2215 2217 2230 + 5175 2024 2061 2062 2060 + 5176 60 2508 2516 2510 + 5177 2294 2319 2293 2229 + 5178 2207 2253 2213 2209 + 5179 2517 2508 2509 2438 + 5180 2507 2521 2510 2518 + 5181 128 1814 1824 1787 + 5182 2253 2224 616 2213 + 5183 2304 2314 2302 2223 + 5184 2314 2294 2220 2229 + 5185 2526 57 2529 62 + 5186 2294 2219 2220 2229 + 5187 2296 636 2303 2300 + 5188 1349 2054 2057 1434 + 5189 559 11 25 7 + 5190 2221 2219 646 2220 + 5191 2314 2294 638 2220 + 5192 936 969 959 950 + 5193 2515 2517 2509 2438 + 5194 2514 2533 2530 2510 + 5195 2219 2292 2295 2308 + 5196 2306 2290 2303 2300 + 5197 505 43 47 26 + 5198 2292 2272 2317 2308 + 5199 637 636 634 2300 + 5200 613 2213 2228 619 + 5201 3063 3061 4205 4474 + 5202 2306 2316 2290 2318 + 5203 2219 2221 2293 2291 + 5204 2226 2231 2301 635 + 5205 2314 2296 2302 2218 + 5206 2197 2164 2154 2158 + 5207 2253 2207 2206 2209 + 5208 2230 2305 2293 2229 + 5209 2305 2314 2231 2223 + 5210 1122 978 961 1104 + 5211 2294 2219 2293 2291 + 5212 2315 2306 2319 2229 + 5213 2316 637 2290 593 + 5214 2224 2253 616 618 + 5215 2429 2431 2445 2428 + 5216 2444 2443 679 678 + 5217 3131 3681 4125 4128 + 5218 2226 2227 613 2225 + 5219 2246 615 2248 629 + 5220 1117 1109 4487 4511 + 5221 2217 2221 646 2220 + 5222 976 1122 961 1105 + 5223 2215 2217 2230 2216 + 5224 3095 3110 3280 4086 + 5225 2215 2231 2217 2293 + 5226 2215 2294 2293 2291 + 5227 2270 2269 2285 2318 + 5228 2309 2270 587 2311 + 5229 2231 2226 2217 646 + 5230 2305 2231 2217 646 + 5231 613 618 619 620 + 5232 637 2316 587 593 + 5233 646 2217 619 620 + 5234 2312 2271 2310 2311 + 5235 2521 2529 2533 2530 + 5236 1516 420 430 4216 + 5237 2316 637 2311 2297 + 5238 2307 2309 2271 2270 + 5239 2273 2272 2308 2298 + 5240 2309 2307 2271 581 + 5241 2309 2271 2311 2313 + 5242 2047 2048 43 47 + 5243 1139 795 4026 4255 + 5244 2316 2271 2270 2267 + 5245 20 2047 43 47 + 5246 2270 2316 587 2311 + 5247 636 638 2315 2319 + 5248 2441 2440 2442 2445 + 5249 3179 2733 2750 4008 + 5250 725 2890 2880 1658 + 5251 1426 1442 43 26 + 5252 195 86 196 71 + 5253 2309 581 588 587 + 5254 2307 2299 593 2298 + 5255 2271 2316 2270 2312 + 5256 2309 1208 588 2313 + 5257 2295 2299 638 2220 + 5258 2306 2317 2290 2299 + 5259 2305 2231 646 635 + 5260 2314 2304 2296 2315 + 5261 1444 1426 1442 43 + 5262 1444 2059 1427 2070 + 5263 2048 505 43 47 + 5264 1276 1277 1278 4144 + 5265 2055 2057 25 1434 + 5266 2046 2056 2055 2045 + 5267 2269 581 583 2267 + 5268 1200 583 582 578 + 5269 2061 2023 2062 2064 + 5270 2299 2317 593 2298 + 5271 637 2309 588 587 + 5272 832 2386 847 2372 + 5273 2070 2048 2071 47 + 5274 2048 2047 2071 47 + 5275 1574 1548 1549 4209 + 5276 771 768 2327 2358 + 5277 666 2593 668 2613 + 5278 1391 1118 1393 4066 + 5279 3681 3680 3644 4125 + 5280 2502 2483 2479 2491 + 5281 1294 1298 1299 1289 + 5282 2502 2479 2490 2491 + 5283 1207 4065 4484 4673 + 5284 3686 3405 3633 4180 + 5285 3936 1044 4547 4651 + 5286 1550 1575 1151 1551 + 5287 954 953 939 937 + 5288 3082 3050 3052 4136 + 5289 1574 1150 1572 4209 + 5290 1136 1312 343 342 + 5291 1084 970 1007 1094 + 5292 2812 3227 3052 3048 + 5293 3446 3453 4307 4371 + 5294 2790 3072 4726 4752 + 5295 3638 3104 3106 3111 + 5296 1291 3145 1156 1296 + 5297 2526 2529 2516 2530 + 5298 710 3115 3114 4305 + 5299 685 2429 2464 676 + 5300 955 956 936 1024 + 5301 1009 974 931 928 + 5302 953 954 952 937 + 5303 2314 638 2315 634 + 5304 2267 2310 2313 2266 + 5305 347 786 1555 4063 + 5306 1740 4223 4768 4772 + 5307 2236 2232 2255 2258 + 5308 2408 2416 2419 2374 + 5309 2342 2338 896 879 + 5310 2296 2314 2315 634 + 5311 2296 2304 2302 2303 + 5312 2292 2295 2308 2319 + 5313 1905 1899 1898 1902 + 5314 1666 1721 1669 4323 + 5315 2562 700 2557 2555 + 5316 1191 1192 815 4271 + 5317 832 2379 2386 2375 + 5318 2324 2323 2326 2332 + 5319 2244 2382 2376 2383 + 5320 2235 2257 2232 2258 + 5321 1002 1017 1018 1022 + 5322 2543 2547 696 2542 + 5323 2305 2302 2301 635 + 5324 2639 2619 2642 2622 + 5325 2135 1189 1226 4533 + 5326 4564 4279 4604 4689 + 5327 1072 1066 1059 4053 + 5328 3062 3063 4205 4474 + 5329 771 2356 2358 2355 + 5330 2658 434 4153 4529 + 5331 2232 2257 2255 2258 + 5332 2360 771 767 2367 + 5333 814 347 740 339 + 5334 2055 497 25 2058 + 5335 3156 1262 996 1330 + 5336 3057 3138 4235 4472 + 5337 1015 927 1016 928 + 5338 1680 1679 1687 1671 + 5339 1077 1646 1074 4208 + 5340 924 926 946 947 + 5341 1272 1256 1323 1274 + 5342 1153 795 1140 4362 + 5343 3106 710 3105 3111 + 5344 982 1028 1029 980 + 5345 975 952 946 947 + 5346 3634 3685 4180 4246 + 5347 1696 982 955 962 + 5348 1013 924 933 929 + 5349 1012 1009 931 1010 + 5350 3132 1146 3174 1144 + 5351 1748 1745 1746 4197 + 5352 1012 974 1008 930 + 5353 3003 3004 4006 4475 + 5354 603 2583 599 602 + 5355 961 978 947 1104 + 5356 1399 1110 1400 4197 + 5357 932 1013 936 938 + 5358 1680 1679 1684 1687 + 5359 924 1005 948 966 + 5360 1414 1159 1120 1143 + 5361 1256 3130 1258 1271 + 5362 1600 750 1213 4100 + 5363 990 989 4179 4477 + 5364 1365 1995 1996 1991 + 5365 3224 3226 3222 983 + 5366 943 941 947 977 + 5367 1007 949 943 944 + 5368 949 1007 1096 944 + 5369 1096 1007 943 944 + 5370 1337 554 2089 1355 + 5371 3298 3224 3226 3222 + 5372 3298 3224 3222 983 + 5373 3173 3082 3051 3053 + 5374 3083 3225 3258 982 + 5375 1075 1077 1074 4208 + 5376 3225 3259 3222 983 + 5377 1030 1027 1029 980 + 5378 1429 1352 564 1431 + 5379 1013 932 936 1014 + 5380 86 195 227 226 + 5381 3700 3119 3118 4086 + 5382 1189 1204 1226 4129 + 5383 3200 3198 3143 2753 + 5384 1090 3156 935 1330 + 5385 204 432 4213 4216 + 5386 564 563 562 557 + 5387 3228 3232 3198 2802 + 5388 3311 2748 3346 2753 + 5389 786 1215 1224 4063 + 5390 3143 3047 3048 2753 + 5391 2853 3276 4002 4097 + 5392 3224 3298 3143 983 + 5393 4289 4397 4712 4725 + 5394 3298 3260 3326 3222 + 5395 2658 2662 434 4529 + 5396 982 1696 3173 1027 + 5397 1623 745 2348 4034 + 5398 1091 1092 1108 1124 + 5399 1015 933 1009 1010 + 5400 1256 1272 1275 1274 + 5401 3037 3156 3039 1262 + 5402 970 966 994 1261 + 5403 3155 3156 1256 1323 + 5404 1707 1240 1409 1703 + 5405 3931 2872 4582 4693 + 5406 1084 970 1095 1085 + 5407 1090 3156 1260 985 + 5408 1260 1323 1258 985 + 5409 2086 2052 3 7 + 5410 3225 982 3259 983 + 5411 2095 2086 11 7 + 5412 1337 1359 1360 1355 + 5413 1663 1657 1138 1137 + 5414 3224 1262 3226 983 + 5415 3259 3298 3222 983 + 5416 1426 498 1442 26 + 5417 3268 3292 4003 4378 + 5418 3152 3130 3171 1256 + 5419 3227 3231 3052 3048 + 5420 993 4130 4327 4460 + 5421 3160 3129 3121 1273 + 5422 3305 3227 3304 3048 + 5423 1752 1762 1722 1711 + 5424 3213 3221 4594 4648 + 5425 3225 3260 3258 3222 + 5426 947 966 977 1261 + 5427 1116 1700 1276 1701 + 5428 3454 3486 2783 4416 + 5429 3299 3260 3298 3222 + 5430 1082 1089 1108 1124 + 5431 1072 1066 1071 1059 + 5432 996 974 995 1125 + 5433 3156 1092 1090 935 + 5434 4325 4267 4574 4586 + 5435 3662 4246 4409 4633 + 5436 3156 1323 1260 985 + 5437 1274 1275 1271 1232 + 5438 1101 1025 964 1103 + 5439 1132 1084 4013 4207 + 5440 4332 4049 4579 4640 + 5441 934 1262 1090 935 + 5442 1013 924 932 933 + 5443 947 977 983 1261 + 5444 3276 3338 4097 4165 + 5445 996 1262 935 1330 + 5446 934 1090 932 935 + 5447 815 1645 1644 4314 + 5448 812 1192 813 56 + 5449 1282 1283 1326 1301 + 5450 1323 1258 985 1274 + 5451 1025 982 969 959 + 5452 1157 1303 4203 4608 + 5453 3156 1092 935 1330 + 5454 2577 1220 1069 1617 + 5455 1294 1302 1303 1157 + 5456 932 973 1026 1016 + 5457 1512 204 4213 4216 + 5458 771 762 2362 2330 + 5459 936 924 951 940 + 5460 3175 3566 1258 1259 + 5461 1132 1018 1019 4207 + 5462 1721 1692 1716 1715 + 5463 1691 1688 1245 4258 + 5464 932 924 938 951 + 5465 924 932 933 1125 + 5466 974 924 933 1125 + 5467 924 974 932 1125 + 5468 956 957 969 950 + 5469 958 979 1099 959 + 5470 979 982 1099 959 + 5471 968 1101 1098 964 + 5472 1257 1234 1273 3168 + 5473 982 983 969 959 + 5474 1331 1089 998 4115 + 5475 1118 1395 1133 1120 + 5476 932 1013 933 929 + 5477 1641 1643 1570 1568 + 5478 180 212 183 1929 + 5479 954 939 966 958 + 5480 1410 1732 1408 1724 + 5481 979 958 983 959 + 5482 1646 1080 4083 4208 + 5483 826 794 1665 1664 + 5484 1609 1183 1184 4485 + 5485 932 1013 1026 1014 + 5486 1409 3714 1717 1703 + 5487 1658 1657 1662 1633 + 5488 934 996 935 1330 + 5489 727 3128 3129 1273 + 5490 996 1089 1331 1088 + 5491 1056 1053 1052 1050 + 5492 1670 1692 1715 1690 + 5493 703 3102 726 4123 + 5494 3566 1703 1704 1259 + 5495 1703 1697 3566 1704 + 5496 1615 1555 1193 1195 + 5497 1089 1088 1313 1130 + 5498 984 966 1085 4612 + 5499 1322 1319 1219 1321 + 5500 3958 644 4069 4685 + 5501 1300 1295 1164 1297 + 5502 4223 1740 4766 4772 + 5503 1136 814 3284 342 + 5504 1217 1594 1562 1579 + 5505 1710 1584 1705 4226 + 5506 3053 3083 3538 981 + 5507 2567 2576 612 597 + 5508 3553 793 3894 586 + 5509 1275 1256 1274 1271 + 5510 778 2346 2365 2353 + 5511 2357 778 2347 2355 + 5512 814 725 726 342 + 5513 3095 3110 711 4618 + 5514 3554 1703 3566 1259 + 5515 1188 466 34 813 + 5516 3130 3171 1256 1258 + 5517 4356 4427 4506 4637 + 5518 1555 1220 1615 1193 + 5519 721 2636 2622 2633 + 5520 1555 1220 1554 1615 + 5521 1573 1575 1549 1151 + 5522 631 639 633 4666 + 5523 4452 311 4507 4606 + 5524 3285 3886 586 4491 + 5525 3053 4244 4312 4656 + 5526 4178 4244 4649 4656 + 5527 1592 1594 1217 1579 + 5528 2125 2134 2126 2124 + 5529 4517 4417 4623 4734 + 5530 4029 4251 4252 4254 + 5531 794 1637 1647 1154 + 5532 3187 2671 2674 2675 + 5533 2579 1183 1184 1185 + 5534 2575 2576 594 609 + 5535 2580 2577 2576 1069 + 5536 1907 221 1888 216 + 5537 1931 1930 1079 1929 + 5538 130 1945 1946 1953 + 5539 1319 1560 1218 1219 + 5540 1652 1658 1662 1633 + 5541 2280 1196 1616 1070 + 5542 1562 740 4239 4537 + 5543 1071 1073 1616 1619 + 5544 1227 741 2351 1224 + 5545 3623 3529 3593 4401 + 5546 4415 3519 4466 4470 + 5547 1968 1970 1909 1971 + 5548 771 2327 772 2358 + 5549 771 2358 2329 2355 + 5550 213 216 214 208 + 5551 3557 1039 2723 2706 + 5552 2886 2929 2941 4162 + 5553 2359 772 2337 2330 + 5554 2577 2280 1616 1070 + 5555 204 229 4087 4634 + 5556 4413 3313 4452 4606 + 5557 2565 2562 2541 2535 + 5558 762 771 772 2330 + 5559 772 2323 896 2337 + 5560 199 90 188 88 + 5561 1308 1300 1307 1295 + 5562 172 185 4664 4724 + 5563 1079 181 1914 149 + 5564 750 747 1597 751 + 5565 2890 725 2894 1658 + 5566 2345 775 1049 1599 + 5567 3153 2794 3005 3181 + 5568 939 924 966 936 + 5569 4169 751 4233 4488 + 5570 725 1650 1649 84 + 5571 347 814 1561 339 + 5572 1312 1234 343 342 + 5573 1312 1234 1136 1212 + 5574 1689 1244 1245 4705 + 5575 1501 1500 1499 439 + 5576 2403 909 2413 2411 + 5577 1593 1592 1217 1579 + 5578 725 814 752 84 + 5579 4172 998 4214 4342 + 5580 1311 1312 1321 343 + 5581 1624 775 2345 1599 + 5582 1612 1228 1613 1614 + 5583 599 2583 2574 602 + 5584 1215 347 1561 4133 + 5585 2661 725 2880 1658 + 5586 2681 405 1507 2677 + 5587 1362 425 432 160 + 5588 1000 999 4172 4342 + 5589 2030 1054 1036 1631 + 5590 1891 1900 1910 1898 + 5591 2680 433 1497 404 + 5592 2660 1498 2659 2677 + 5593 438 2014 1518 1502 + 5594 772 2327 2330 2358 + 5595 1068 1228 2350 1067 + 5596 2929 2697 2941 4162 + 5597 2684 405 434 404 + 5598 1222 1148 1189 1204 + 5599 3336 2695 2690 4344 + 5600 1723 4155 4389 4422 + 5601 1082 1089 1130 4342 + 5602 2017 2020 2015 2021 + 5603 2323 2331 2337 2358 + 5604 2346 744 1626 749 + 5605 1204 1222 2135 1226 + 5606 4013 4046 4122 4429 + 5607 741 740 1224 743 + 5608 1417 1405 4406 4442 + 5609 2913 2904 2907 2905 + 5610 1656 1660 4035 4059 + 5611 3613 3501 4583 4639 + 5612 2043 218 2034 2037 + 5613 3596 3620 3593 4440 + 5614 3157 632 3271 3272 + 5615 1844 2132 4018 4225 + 5616 1393 1401 4066 4124 + 5617 727 728 343 342 + 5618 632 3955 642 640 + 5619 748 2350 746 2354 + 5620 769 2347 2355 2365 + 5621 70 68 79 1979 + 5622 599 2571 600 605 + 5623 2364 2346 2347 2355 + 5624 4057 2871 4245 4247 + 5625 3115 710 3114 4291 + 5626 1056 1058 1057 751 + 5627 1652 2661 725 84 + 5628 3125 3288 1140 4022 + 5629 2349 2363 2346 1202 + 5630 3246 623 643 2913 + 5631 1048 1058 1201 2352 + 5632 1928 1886 1931 1929 + 5633 1068 2350 2349 1627 + 5634 2697 2698 2941 4162 + 5635 2327 2323 2330 2358 + 5636 1558 1556 1228 1195 + 5637 2573 2576 2571 1061 + 5638 633 3204 2645 3188 + 5639 988 1022 4460 4340 + 5640 1625 1628 1622 1626 + 5641 1125 1082 4214 4342 + 5642 3454 4025 4195 4586 + 5643 771 779 767 2356 + 5644 594 2570 596 595 + 5645 744 2346 778 2353 + 5646 1228 1068 1613 1614 + 5647 2818 3991 4516 4619 + 5648 1626 744 736 749 + 5649 1408 1412 1730 4406 + 5650 4369 3545 3400 3502 + 5651 1562 1594 1563 1579 + 5652 2356 767 765 778 + 5653 704 3115 3099 4577 + 5654 1068 2350 1627 1067 + 5655 1192 812 165 815 + 5656 1561 347 339 4133 + 5657 1924 181 185 178 + 5658 1240 1116 1276 1672 + 5659 1413 1392 1773 1774 + 5660 1770 1410 1732 1408 + 5661 764 2360 765 2366 + 5662 2337 2331 2330 2358 + 5663 1231 1314 346 348 + 5664 768 766 2327 895 + 5665 1315 347 4063 4133 + 5666 3128 710 3115 4291 + 5667 549 161 432 160 + 5668 2480 4076 4299 4326 + 5669 814 3284 726 1137 + 5670 1628 2348 1622 1626 + 5671 1188 2135 2137 34 + 5672 1243 1149 826 4060 + 5673 1086 1000 4172 4342 + 5674 1214 1211 1650 752 + 5675 1228 2351 2350 1067 + 5676 1054 1055 1056 1050 + 5677 1056 1048 1047 1050 + 5678 1054 1056 1052 1050 + 5679 1649 1650 752 84 + 5680 200 201 2029 85 + 5681 1089 1125 998 4342 + 5682 1273 727 343 342 + 5683 1889 1890 1894 1892 + 5684 1227 2363 2351 2350 + 5685 1068 1228 1613 2350 + 5686 2350 2363 1202 2354 + 5687 219 2037 2015 2021 + 5688 2361 2362 2330 2329 + 5689 2574 2575 2571 2570 + 5690 4178 4527 4649 4774 + 5691 770 769 2365 2353 + 5692 2016 2028 2029 85 + 5693 1198 1607 1197 1918 + 5694 1227 2350 748 2354 + 5695 2123 3589 3591 2139 + 5696 1374 1512 1517 2033 + 5697 779 744 778 769 + 5698 1598 1583 1597 737 + 5699 2350 1227 748 1067 + 5700 2578 2575 2570 596 + 5701 1037 1047 1595 1596 + 5702 1214 1211 752 4100 + 5703 2577 1220 1617 1554 + 5704 3437 3411 3503 4036 + 5705 1047 1583 1595 737 + 5706 740 1559 1562 4088 + 5707 2028 1203 2029 85 + 5708 1932 1895 1915 1925 + 5709 1319 1560 1219 1561 + 5710 1203 1055 2028 1057 + 5711 2348 2346 744 1626 + 5712 1241 1249 4024 4035 + 5713 753 1648 1649 84 + 5714 725 1138 1651 752 + 5715 604 601 2546 2582 + 5716 212 180 183 214 + 5717 795 172 815 1644 + 5718 3604 3529 2786 4401 + 5719 347 1315 339 4133 + 5720 2854 3342 2855 4111 + 5721 1000 1135 4172 4612 + 5722 89 1962 88 187 + 5723 2363 1227 1202 2354 + 5724 1227 2363 2350 2354 + 5725 1189 1621 1221 1204 + 5726 2134 33 466 21 + 5727 1228 1613 1220 1615 + 5728 3580 3583 4019 4257 + 5729 709 604 697 2590 + 5730 1225 786 611 787 + 5731 3985 3997 2683 2676 + 5732 2357 2331 2358 2329 + 5733 747 750 1597 737 + 5734 599 610 2571 605 + 5735 2574 2583 2570 602 + 5736 104 105 2199 116 + 5737 2652 2653 1034 1032 + 5738 3041 3132 4178 4669 + 5739 1083 1086 4214 4342 + 5740 1249 4024 4035 4059 + 5741 1055 1054 1203 85 + 5742 725 1652 1651 1633 + 5743 779 767 2356 778 + 5744 745 1623 736 738 + 5745 3342 3321 4449 4502 + 5746 1159 1179 1392 1129 + 5747 1213 1598 1597 737 + 5748 1051 1048 1201 2352 + 5749 3699 3714 4310 4513 + 5750 3284 814 726 342 + 5751 1053 1054 1052 1050 + 5752 1926 1886 213 214 + 5753 201 1032 4134 4439 + 5754 1560 1218 1562 1563 + 5755 2575 2576 2578 1194 + 5756 786 742 1225 740 + 5757 1228 741 2351 740 + 5758 1562 1559 1563 4088 + 5759 1118 1399 1110 4531 + 5760 184 822 172 4089 + 5761 229 1513 2038 2042 + 5762 1054 2030 1035 2031 + 5763 3173 3051 3050 4611 + 5764 3276 2845 3338 4165 + 5765 2886 2887 2703 4162 + 5766 1211 1214 1213 4100 + 5767 1667 1714 1713 4080 + 5768 2659 2677 2918 2917 + 5769 2643 3204 3207 3188 + 5770 632 725 3271 3272 + 5771 740 1602 739 4110 + 5772 1113 1738 4223 4497 + 5773 3103 704 3096 4577 + 5774 3158 3273 632 3272 + 5775 725 2895 2880 3272 + 5776 601 604 2581 2582 + 5777 2030 1054 1631 1052 + 5778 725 632 3271 3283 + 5779 706 3113 4218 4614 + 5780 775 1048 1058 1201 + 5781 1135 1086 4172 4612 + 5782 2132 2129 2133 4225 + 5783 2790 3072 3594 4726 + 5784 704 727 3128 4577 + 5785 2574 2572 2571 2549 + 5786 1583 1037 1597 1047 + 5787 1657 1663 1662 1633 + 5788 753 750 752 84 + 5789 1047 775 1595 1596 + 5790 1663 1657 1651 1633 + 5791 1049 764 1051 2352 + 5792 3093 3699 3700 4128 + 5793 1160 3016 4041 4135 + 5794 1138 814 1651 752 + 5795 814 725 1138 1137 + 5796 2643 2681 3205 2677 + 5797 1512 1513 1517 2038 + 5798 3620 3371 3593 4440 + 5799 2661 1652 725 1658 + 5800 218 219 222 2042 + 5801 1498 2684 2659 2677 + 5802 1519 1501 436 1509 + 5803 1519 437 441 436 + 5804 1515 1510 1511 1509 + 5805 747 1583 1597 1047 + 5806 3312 3300 4187 4378 + 5807 2677 2918 2917 4549 + 5808 2702 3830 3804 4458 + 5809 3716 3091 4151 4530 + 5810 767 771 2356 2367 + 5811 2327 895 2326 2325 + 5812 2367 2360 2325 2328 + 5813 94 95 189 90 + 5814 1317 826 348 4319 + 5815 1575 1549 1151 1551 + 5816 796 345 4509 4617 + 5817 2346 2364 2365 2353 + 5818 183 1931 1932 1929 + 5819 171 1913 178 179 + 5820 770 771 2329 2355 + 5821 2361 2362 2322 2330 + 5822 2580 1061 2588 1060 + 5823 1652 1650 725 1651 + 5824 3039 3155 3023 4495 + 5825 1066 1071 1073 1616 + 5826 2536 2585 2538 2584 + 5827 772 2323 2337 2330 + 5828 1984 1983 1976 1977 + 5829 604 2546 2585 2581 + 5830 2545 2541 2553 2535 + 5831 181 180 182 1929 + 5832 1965 74 1976 79 + 5833 1277 1240 1672 1701 + 5834 100 1983 1965 1976 + 5835 3933 712 1638 4022 + 5836 1889 1891 1893 228 + 5837 1312 1214 1212 4056 + 5838 2562 2565 2541 701 + 5839 826 794 345 1665 + 5840 3448 3478 3479 3477 + 5841 794 1253 345 1664 + 5842 2587 2588 1060 1062 + 5843 2652 2658 4153 4529 + 5844 1225 347 1555 1554 + 5845 2573 2572 2571 2591 + 5846 1933 1079 1076 1934 + 5847 2576 2573 2571 2591 + 5848 1586 1666 4313 4394 + 5849 1221 1189 1205 1191 + 5850 1198 1317 4199 4209 + 5851 3322 2970 3340 4221 + 5852 1243 826 1197 4319 + 5853 2465 2469 692 2461 + 5854 3663 3688 3636 4246 + 5855 795 794 172 826 + 5856 599 610 605 598 + 5857 1893 1907 1888 228 + 5858 1970 1901 1971 1906 + 5859 698 2545 2539 2535 + 5860 445 3618 447 446 + 5861 808 166 1221 1205 + 5862 21 24 2127 22 + 5863 1557 1602 4088 4110 + 5864 3131 3681 3644 4125 + 5865 3483 3484 4748 4767 + 5866 1046 2713 1039 2705 + 5867 3940 2871 2872 4247 + 5868 1897 189 88 187 + 5869 1921 1923 1895 1915 + 5870 2134 2125 2137 2124 + 5871 2146 2123 3591 2139 + 5872 1126 1084 1085 4429 + 5873 1464 2205 471 2202 + 5874 1231 1268 1314 4027 + 5875 2269 2287 2286 4334 + 5876 2134 2130 21 2127 + 5877 3328 466 2722 1040 + 5878 1580 1581 4152 4268 + 5879 1192 815 1139 1638 + 5880 1178 1177 3043 1267 + 5881 2130 33 2134 21 + 5882 4311 1117 4511 4541 + 5883 4134 2652 4153 4346 + 5884 1636 172 1647 1644 + 5885 2574 599 2571 600 + 5886 1574 1575 1150 1551 + 5887 182 181 1914 1934 + 5888 2583 603 599 600 + 5889 4032 796 4291 4617 + 5890 3618 3619 447 446 + 5891 3165 3164 3162 1333 + 5892 3552 3611 481 3616 + 5893 2861 2902 2858 2865 + 5894 1288 1299 1325 1293 + 5895 2100 1480 2098 2142 + 5896 3618 3619 3617 3616 + 5897 189 97 96 88 + 5898 182 1933 1929 1934 + 5899 2545 2553 2539 2535 + 5900 1291 1324 1332 1325 + 5901 487 445 3617 488 + 5902 2146 3591 3592 2139 + 5903 1560 1319 1315 4133 + 5904 1221 1043 1042 1911 + 5905 600 2572 2566 2568 + 5906 2713 1045 1046 1038 + 5907 2566 2589 2537 2586 + 5908 2984 3328 2722 1040 + 5909 2722 3557 2706 1040 + 5910 3119 3110 3118 711 + 5911 2572 2589 2566 2586 + 5912 601 709 600 606 + 5913 3608 3595 2782 4236 + 5914 821 794 795 826 + 5915 1155 794 1664 4023 + 5916 3118 3700 3703 711 + 5917 2101 169 2111 2102 + 5918 33 466 21 24 + 5919 611 821 824 823 + 5920 1927 1893 1888 228 + 5921 3101 3090 4262 4356 + 5922 808 795 172 815 + 5923 181 825 185 172 + 5924 166 1924 808 171 + 5925 4604 4564 4706 4748 + 5926 3552 480 3611 466 + 5927 3557 3552 3611 466 + 5928 3552 465 480 466 + 5929 3552 3557 465 466 + 5930 3318 3328 3553 466 + 5931 466 3557 2722 1040 + 5932 1745 1128 1747 1398 + 5933 1045 2713 3625 3626 + 5934 2652 438 4153 4346 + 5935 1913 1917 166 171 + 5936 2136 1039 2706 1040 + 5937 795 793 815 816 + 5938 2130 1188 2137 34 + 5939 1394 1404 1405 4602 + 5940 1079 1930 1076 1912 + 5941 594 611 597 598 + 5942 3328 3318 3553 586 + 5943 793 3553 467 586 + 5944 1618 1546 2280 824 + 5945 2541 2554 2551 2552 + 5946 2327 766 772 896 + 5947 2653 1034 4102 4346 + 5948 2571 610 2591 2568 + 5949 976 1107 1122 1105 + 5950 1113 1176 1117 1109 + 5951 2583 599 2574 600 + 5952 2572 2571 2591 2566 + 5953 610 597 605 598 + 5954 3640 3699 4513 4575 + 5955 2572 2589 600 2566 + 5956 1198 821 1317 826 + 5957 280 3475 3480 3469 + 5958 1221 1924 1043 1911 + 5959 3050 3172 4030 4136 + 5960 2146 2123 2139 2141 + 5961 1036 1648 1631 84 + 5962 1188 1189 1191 1190 + 5963 2576 2573 598 2569 + 5964 1886 1926 1927 1928 + 5965 1189 1191 4129 4504 + 5966 610 599 2571 598 + 5967 182 1933 1914 1929 + 5968 1666 1722 1672 4166 + 5969 441 429 430 4087 + 5970 3554 3714 1703 4291 + 5971 4320 4352 4527 4611 + 5972 2130 1188 34 1190 + 5973 1924 1917 166 1205 + 5974 1621 1222 1221 1204 + 5975 180 183 1932 1929 + 5976 2573 2592 2588 1065 + 5977 1679 1680 1684 1671 + 5978 1556 1555 1554 1615 + 5979 986 1020 1019 4085 + 5980 166 1192 812 165 + 5981 1148 1621 1189 1204 + 5982 1924 166 808 1205 + 5983 611 1618 2280 823 + 5984 610 2567 612 597 + 5985 1308 1307 1167 1295 + 5986 182 180 1932 1929 + 5987 410 417 1527 116 + 5988 709 600 606 605 + 5989 2585 2550 2548 2538 + 5990 1926 212 183 214 + 5991 1607 1198 1197 1566 + 5992 1121 1417 1733 1765 + 5993 1646 1640 1569 1568 + 5994 1183 1186 1197 1918 + 5995 1893 1891 1907 228 + 5996 2269 2286 2288 4334 + 5997 604 2585 2548 2538 + 5998 1220 1555 1554 1193 + 5999 600 2589 606 605 + 6000 1550 1575 1573 1151 + 6001 786 742 740 743 + 6002 1604 2579 1603 1229 + 6003 1553 1548 1547 1552 + 6004 764 1049 774 2352 + 6005 3895 4337 4398 4413 + 6006 2135 4404 4547 4651 + 6007 1320 1218 1219 1217 + 6008 1047 1037 1599 1596 + 6009 4337 3313 4398 4413 + 6010 808 166 171 165 + 6011 1238 1253 1677 4108 + 6012 2589 2572 2584 2586 + 6013 2608 666 2611 2613 + 6014 1216 347 348 4021 + 6015 1945 130 77 1953 + 6016 1620 2577 1616 1069 + 6017 1079 1914 1920 149 + 6018 1118 1112 4066 4402 + 6019 3688 3634 3636 4246 + 6020 1186 1605 1187 1918 + 6021 1618 611 824 823 + 6022 611 821 823 826 + 6023 710 704 706 728 + 6024 705 3093 3645 711 + 6025 3328 3332 3329 2994 + 6026 212 1928 1931 1929 + 6027 2589 600 2566 2568 + 6028 768 771 2367 2358 + 6029 2323 2337 2330 2358 + 6030 2135 466 2137 34 + 6031 1513 2018 1517 2038 + 6032 599 594 595 598 + 6033 706 3113 3115 4305 + 6034 3896 3949 3926 3951 + 6035 3095 3093 705 711 + 6036 1192 1188 1191 1190 + 6037 713 728 792 796 + 6038 2571 2591 2566 2568 + 6039 2540 2537 2536 2539 + 6040 2643 2681 2677 2678 + 6041 3109 3268 4003 4378 + 6042 2367 2360 2328 2366 + 6043 2889 2892 337 322 + 6044 332 314 2900 322 + 6045 337 2892 338 322 + 6046 332 2900 338 322 + 6047 1202 1626 736 749 + 6048 466 2135 1188 34 + 6049 2678 2643 2675 2676 + 6050 2912 3246 643 2913 + 6051 1887 1909 199 188 + 6052 210 1923 209 224 + 6053 1192 793 1632 813 + 6054 740 1559 1224 4231 + 6055 1079 1914 1929 1920 + 6056 712 4022 4381 4479 + 6057 166 808 1221 165 + 6058 813 1192 816 56 + 6059 166 1221 1191 165 + 6060 1924 1917 1205 1925 + 6061 1646 1080 1605 4083 + 6062 2127 2128 55 22 + 6063 445 487 447 488 + 6064 1276 1117 1114 1582 + 6065 1565 1197 1566 4505 + 6066 2135 466 1188 813 + 6067 1222 1621 1221 1043 + 6068 1636 1198 1637 1647 + 6069 2761 3230 2747 3052 + 6070 3602 3595 3529 4401 + 6071 3110 3117 3118 711 + 6072 2138 2144 2128 2126 + 6073 3156 1260 4042 4043 + 6074 1333 1309 1324 1332 + 6075 998 4172 4214 4465 + 6076 713 710 728 345 + 6077 713 3120 1235 3159 + 6078 466 34 813 816 + 6079 1208 583 2266 4334 + 6080 793 469 592 586 + 6081 1250 1242 1249 1241 + 6082 2135 1192 1632 813 + 6083 3452 3455 3451 3597 + 6084 1892 1932 1934 1912 + 6085 974 966 4465 4532 + 6086 3148 3147 3036 3145 + 6087 1289 1291 1286 1310 + 6088 3529 2782 3593 4401 + 6089 1063 1060 1059 1062 + 6090 1076 1075 1078 1074 + 6091 949 944 925 945 + 6092 3173 1030 3051 4611 + 6093 1682 1247 1686 1684 + 6094 1634 1637 1569 1570 + 6095 184 821 1198 1636 + 6096 3767 3773 3764 4028 + 6097 825 821 172 826 + 6098 3637 3679 3680 4090 + 6099 1174 3086 3012 1305 + 6100 1216 348 4099 4718 + 6101 821 795 172 826 + 6102 212 1931 183 1929 + 6103 180 181 1914 1929 + 6104 181 182 1914 1929 + 6105 1084 1097 1094 4207 + 6106 2585 604 2537 2536 + 6107 1066 1072 1071 1616 + 6108 4363 4585 4593 4721 + 6109 2580 1072 1061 1060 + 6110 597 2567 598 2569 + 6111 2573 2576 2567 2569 + 6112 1675 794 345 796 + 6113 1251 1153 796 1248 + 6114 438 1518 4102 4346 + 6115 1710 1754 1584 4437 + 6116 596 594 595 609 + 6117 1113 1112 1110 1400 + 6118 315 324 4070 4492 + 6119 4026 795 4271 4362 + 6120 1555 1216 1554 4021 + 6121 793 1192 815 56 + 6122 2450 2445 2434 2428 + 6123 2145 2146 3591 3592 + 6124 3087 3021 3020 2999 + 6125 1230 2579 1603 1184 + 6126 3064 2784 4615 4764 + 6127 1221 1608 1191 165 + 6128 2789 3923 2783 4373 + 6129 3936 2984 2722 1040 + 6130 4354 712 4360 4696 + 6131 2573 1061 1060 1065 + 6132 347 1555 1554 4021 + 6133 4583 3389 4615 4764 + 6134 1930 1079 1929 1920 + 6135 1197 1198 4199 4209 + 6136 1890 211 224 164 + 6137 610 2591 2568 2569 + 6138 1071 1553 1552 1564 + 6139 3319 4420 4491 4687 + 6140 1159 1178 1176 1120 + 6141 2592 2573 2567 2569 + 6142 2566 2589 2568 2569 + 6143 1689 1684 1245 4222 + 6144 1295 1294 1304 1163 + 6145 1183 1186 1918 1184 + 6146 1160 1175 3044 3085 + 6147 644 622 643 4498 + 6148 1974 66 64 69 + 6149 3313 2690 590 4413 + 6150 2798 3156 1260 4042 + 6151 1231 1232 346 4160 + 6152 1114 1582 4751 4771 + 6153 1197 1574 1572 4209 + 6154 3558 3328 2993 2994 + 6155 728 713 345 796 + 6156 1666 4313 4525 4571 + 6157 1292 1333 1309 1324 + 6158 1696 3083 3173 1027 + 6159 949 924 943 945 + 6160 949 943 944 945 + 6161 1672 1409 4080 4510 + 6162 3699 710 4513 4575 + 6163 1696 3083 982 3173 + 6164 2577 1618 611 4084 + 6165 3681 3637 3680 4125 + 6166 3991 2817 3999 4516 + 6167 2968 2966 2990 2967 + 6168 3170 1250 1235 3124 + 6169 1240 1668 4351 4667 + 6170 1177 1178 1278 1267 + 6171 1610 1611 1609 1918 + 6172 1577 1243 1578 1149 + 6173 639 4068 4079 4281 + 6174 750 1600 737 4100 + 6175 1574 1606 1197 1572 + 6176 1232 1321 343 4160 + 6177 1116 1276 1589 1117 + 6178 1035 1054 2031 4439 + 6179 1575 1548 1552 1573 + 6180 3169 1177 1278 1267 + 6181 1168 1169 1161 1163 + 6182 3169 3077 3084 2679 + 6183 1473 1483 1484 1482 + 6184 2166 1844 3561 3559 + 6185 3134 3060 3050 4149 + 6186 3006 2735 4118 4387 + 6187 3059 3049 3063 4205 + 6188 2636 2629 2633 2635 + 6189 1198 1186 1187 1918 + 6190 1932 1931 1892 1934 + 6191 3165 3146 3147 3145 + 6192 1253 794 1238 1236 + 6193 584 3319 592 586 + 6194 2631 2628 2637 716 + 6195 1097 1131 1093 4207 + 6196 1895 1890 1932 1892 + 6197 4412 612 4428 4489 + 6198 1084 970 1085 4429 + 6199 438 2013 1034 4102 + 6200 1152 1642 1154 1635 + 6201 3004 2997 3003 4033 + 6202 1153 795 1647 1154 + 6203 706 4218 4591 4665 + 6204 1118 1393 4589 4679 + 6205 1744 1737 1587 4116 + 6206 1330 1092 1313 985 + 6207 1153 794 1251 796 + 6208 1275 1256 1271 1257 + 6209 1092 1090 1260 985 + 6210 1089 1313 1124 1130 + 6211 3117 713 3123 3122 + 6212 1416 1733 1765 1766 + 6213 3079 3169 3084 2679 + 6214 3120 3170 1235 3159 + 6215 3077 3084 2679 3043 + 6216 3159 713 3122 3124 + 6217 2573 2592 1061 2588 + 6218 4417 4453 4709 4734 + 6219 2782 4415 4466 4470 + 6220 1643 1641 1570 1642 + 6221 1311 1312 343 4056 + 6222 1645 795 815 1644 + 6223 172 795 1647 1644 + 6224 710 706 3115 4305 + 6225 1931 1933 1894 1892 + 6226 3577 3904 4094 4490 + 6227 347 1216 1555 4021 + 6228 1968 1970 1971 1906 + 6229 1921 1890 209 224 + 6230 1291 1294 1302 1303 + 6231 3041 3132 3174 1144 + 6232 2144 2145 2128 2126 + 6233 2100 2098 1478 2097 + 6234 181 178 182 172 + 6235 1200 1208 578 4065 + 6236 2871 3940 1655 4012 + 6237 2579 1073 1229 1619 + 6238 1177 3169 1725 1735 + 6239 1917 166 1205 1191 + 6240 1079 1933 1076 1078 + 6241 2506 2276 2504 4007 + 6242 1073 2579 1229 4253 + 6243 1901 189 1971 1906 + 6244 1061 2587 1060 1065 + 6245 1075 1077 1078 1074 + 6246 793 1192 1639 1638 + 6247 1238 1253 1236 4023 + 6248 1183 1577 1572 4570 + 6249 3400 3546 4722 4723 + 6250 1933 182 1914 1934 + 6251 2013 201 4134 4439 + 6252 3204 2643 3205 4549 + 6253 1554 1225 4021 4084 + 6254 2736 2733 4348 4391 + 6255 1043 1221 1042 1041 + 6256 172 1221 1041 4083 + 6257 92 94 1967 69 + 6258 3166 3149 3027 3033 + 6259 1489 170 148 2122 + 6260 91 1470 468 191 + 6261 130 139 136 1953 + 6262 95 94 189 96 + 6263 3618 447 3591 54 + 6264 1924 1932 1934 1925 + 6265 2179 2180 2178 2181 + 6266 1939 1957 1940 1956 + 6267 3135 3148 3147 3036 + 6268 3043 2784 1121 1145 + 6269 2178 2179 2181 192 + 6270 3034 3033 3010 3020 + 6271 1414 1159 1411 4349 + 6272 3614 3372 3365 4639 + 6273 172 1608 815 4314 + 6274 438 2014 2013 4102 + 6275 1762 1760 1761 1666 + 6276 1142 1182 1141 4157 + 6277 814 343 342 4056 + 6278 2781 3603 3459 2786 + 6279 1064 4053 4253 4347 + 6280 1416 1394 1405 1120 + 6281 3010 3033 3035 3020 + 6282 1177 3169 1736 1725 + 6283 1187 1186 4578 4603 + 6284 3503 3514 4194 4264 + 6285 1142 1413 1773 1756 + 6286 3688 3686 3685 4180 + 6287 1281 1279 1280 1586 + 6288 1723 1712 1731 4155 + 6289 201 1031 1032 4439 + 6290 1696 1700 1695 4147 + 6291 4766 1740 4768 4772 + 6292 3887 3319 4486 4652 + 6293 1311 1320 4056 4164 + 6294 1317 821 4199 4535 + 6295 974 966 995 4096 + 6296 2781 3450 4092 4674 + 6297 1187 4355 4400 4520 + 6298 1685 1247 1680 1684 + 6299 3486 3485 4564 4735 + 6300 1272 1323 1331 4115 + 6301 1316 1151 1551 1246 + 6302 1256 1258 1274 1271 + 6303 3062 3042 3054 4148 + 6304 1276 1277 1672 1701 + 6305 1077 1919 4628 4681 + 6306 3634 3685 4151 4633 + 6307 4265 986 4275 4444 + 6308 1518 1502 4238 4365 + 6309 3160 727 3129 1273 + 6310 2920 3204 4317 4576 + 6311 4704 4683 4743 4764 + 6312 3472 3482 3471 4293 + 6313 933 1009 1010 928 + 6314 982 1696 1025 969 + 6315 4356 4262 4427 4637 + 6316 2662 2660 434 4529 + 6317 1027 1699 4244 4270 + 6318 1697 3069 3566 1704 + 6319 1719 3069 1697 1704 + 6320 3714 3716 3554 1409 + 6321 1177 1276 3169 1278 + 6322 1323 1256 1258 1274 + 6323 197 2658 434 4153 + 6324 1771 1726 1732 1769 + 6325 3043 1177 1176 1267 + 6326 1608 808 172 815 + 6327 3050 3060 4030 4149 + 6328 3169 1276 1277 1278 + 6329 2789 3923 4373 4481 + 6330 786 1215 1561 4231 + 6331 3581 3563 3092 3584 + 6332 1240 1276 1277 1672 + 6333 1318 1231 346 4160 + 6334 1654 4433 4540 4582 + 6335 3132 3133 3174 1145 + 6336 3110 3781 3278 4618 + 6337 4413 4452 4507 4606 + 6338 1097 1093 4196 4207 + 6339 1206 1208 4334 4567 + 6340 1279 1721 1761 1666 + 6341 1401 1391 1118 1393 + 6342 1306 3029 4041 4061 + 6343 1276 1700 1705 4771 + 6344 3581 3503 4194 4264 + 6345 794 728 345 796 + 6346 794 826 1155 1664 + 6347 3599 3603 3459 4150 + 6348 713 1250 1235 796 + 6349 1706 1762 1722 1672 + 6350 1266 3500 1121 1735 + 6351 796 1675 1236 1277 + 6352 3220 2823 4594 4648 + 6353 2704 2884 4038 4512 + 6354 1269 1720 1716 1718 + 6355 1577 1578 1197 1572 + 6356 1549 1316 1151 4209 + 6357 1118 1176 1112 1110 + 6358 3500 3169 2784 1121 + 6359 1149 826 1665 1664 + 6360 3483 3484 4644 4748 + 6361 2798 3223 3224 4227 + 6362 1240 1675 1674 1668 + 6363 1726 1772 1406 1769 + 6364 1768 1699 4621 4670 + 6365 3588 3596 3073 4392 + 6366 2798 3224 4042 4227 + 6367 1762 1706 1722 1711 + 6368 3339 3341 2856 4388 + 6369 4469 1746 4660 4679 + 6370 953 954 939 952 + 6371 1640 1641 1569 1568 + 6372 3052 982 3259 4584 + 6373 1637 1198 1569 1566 + 6374 3173 3083 3053 1027 + 6375 4341 1067 4563 4620 + 6376 1279 1720 1280 1666 + 6377 1666 1667 1672 1673 + 6378 969 1101 959 968 + 6379 1198 1567 1197 1566 + 6380 4262 3101 4427 4545 + 6381 1402 1112 1127 1403 + 6382 2504 2276 2282 4299 + 6383 3123 712 4447 4696 + 6384 1281 1279 1669 4343 + 6385 1722 1762 1761 1672 + 6386 4428 2580 4721 4746 + 6387 1159 1118 1176 1112 + 6388 4525 4177 4749 4771 + 6389 4756 4177 4757 4771 + 6390 1159 1133 1402 1127 + 6391 2783 3077 4546 4588 + 6392 1728 1410 1412 1724 + 6393 1265 1264 1029 1255 + 6394 3172 3134 3050 4149 + 6395 1392 1142 1413 1773 + 6396 3603 4195 4267 4325 + 6397 3064 3500 2784 1121 + 6398 1395 1394 1416 1120 + 6399 1401 1118 1399 1393 + 6400 1146 1254 3174 1144 + 6401 3169 2785 1736 1725 + 6402 3098 3852 3097 4123 + 6403 1075 1042 1077 4208 + 6404 3080 3380 3066 3594 + 6405 3145 3147 3031 3032 + 6406 1023 988 1004 4609 + 6407 1667 1670 1669 4080 + 6408 1472 1467 1459 2191 + 6409 3923 2785 4260 4264 + 6410 3965 3319 592 4119 + 6411 3098 3097 3103 4123 + 6412 3312 3321 313 4502 + 6413 1167 1168 1164 1163 + 6414 4686 1392 4717 4739 + 6415 3319 586 4354 4447 + 6416 3908 295 3966 4241 + 6417 3064 1266 1121 1145 + 6418 4433 2873 4540 4582 + 6419 3805 577 576 4426 + 6420 1772 1726 1406 1768 + 6421 3609 3607 4470 4476 + 6422 1686 345 4027 4695 + 6423 2784 3389 4583 4764 + 6424 3982 3991 3992 4516 + 6425 229 2014 1502 4238 + 6426 2996 3154 3151 4495 + 6427 1004 986 1019 4609 + 6428 1761 1281 1269 1718 + 6429 345 1675 796 4509 + 6430 3148 3036 3014 3013 + 6431 1753 1754 1584 1755 + 6432 1280 1279 1666 1586 + 6433 3584 3092 3583 2682 + 6434 1375 1385 1383 1388 + 6435 3806 3312 4073 4449 + 6436 3186 3039 3023 4495 + 6437 1737 1741 4763 4768 + 6438 3504 3500 2679 4646 + 6439 1391 1118 1112 1110 + 6440 1276 1589 1117 1582 + 6441 710 3089 4291 4575 + 6442 1761 1269 1716 1718 + 6443 2588 1060 1062 4053 + 6444 1754 1752 1757 1711 + 6445 1584 1710 1705 1711 + 6446 974 995 1125 4096 + 6447 997 986 4336 4477 + 6448 1118 1120 4647 4679 + 6449 2799 2798 4042 4227 + 6450 1090 3156 960 1260 + 6451 3931 2872 1654 4582 + 6452 793 1632 1639 4263 + 6453 1692 1683 1715 1690 + 6454 1101 959 968 1098 + 6455 1256 3155 1323 1258 + 6456 3940 4322 4592 4625 + 6457 1693 1683 1713 1688 + 6458 223 204 2041 4634 + 6459 1705 1116 1706 1701 + 6460 1737 1741 1587 4116 + 6461 3120 1276 3584 1277 + 6462 1112 1391 1110 4531 + 6463 1112 1159 1133 1402 + 6464 1710 1757 1722 1711 + 6465 2798 3156 3155 1260 + 6466 1117 1114 1582 4171 + 6467 1113 1110 1738 1400 + 6468 1916 4104 4126 4280 + 6469 431 1514 432 4216 + 6470 1686 1682 1684 1687 + 6471 924 974 933 928 + 6472 1172 1182 1141 4288 + 6473 3128 1232 345 4032 + 6474 640 3188 2676 4374 + 6475 4039 408 4054 4201 + 6476 1760 1720 1666 1763 + 6477 3438 3556 3562 3417 + 6478 3156 2798 3224 3226 + 6479 1696 1101 1025 969 + 6480 3101 3634 4151 4633 + 6481 2747 3238 2810 3245 + 6482 1655 3940 1654 4613 + 6483 1116 1276 1672 1701 + 6484 197 2660 2662 4281 + 6485 1276 1116 1589 1582 + 6486 1235 713 796 1277 + 6487 3563 3556 3091 4338 + 6488 1177 3169 1176 1267 + 6489 642 3773 4069 4434 + 6490 3151 4008 4367 4391 + 6491 1693 1678 1683 1688 + 6492 3152 3155 3070 4495 + 6493 3224 3226 4042 4227 + 6494 793 3931 1654 4082 + 6495 1760 1720 1761 1269 + 6496 1279 1720 1760 1269 + 6497 3856 3291 3300 4187 + 6498 3437 3438 3562 3417 + 6499 3556 3408 3409 3562 + 6500 3169 3043 1176 1267 + 6501 3438 3409 3437 3562 + 6502 1175 1254 3174 1145 + 6503 1416 1121 1733 4190 + 6504 1754 1757 1710 1711 + 6505 4417 4453 4734 4736 + 6506 1030 3173 3051 1027 + 6507 962 956 957 969 + 6508 1775 1767 1756 1143 + 6509 1323 3155 1260 1258 + 6510 1691 1693 1694 1713 + 6511 4400 822 4603 4664 + 6512 1399 1400 1398 4197 + 6513 924 929 927 928 + 6514 927 929 1016 928 + 6515 4178 4098 4527 4774 + 6516 924 949 925 945 + 6517 1552 1564 1573 1549 + 6518 1696 982 962 969 + 6519 1082 1089 1124 1130 + 6520 956 939 940 950 + 6521 3500 3064 1266 1121 + 6522 1084 1126 1132 4429 + 6523 1675 1240 1236 1277 + 6524 4171 1117 4229 4487 + 6525 1391 1401 1118 1399 + 6526 965 1122 1106 1028 + 6527 2763 4330 4478 4483 + 6528 1276 1700 3584 1701 + 6529 1030 1025 1028 1029 + 6530 960 1090 1260 4043 + 6531 3546 3053 1027 4244 + 6532 2576 2567 612 4412 + 6533 3988 3981 2813 4516 + 6534 1700 1697 3584 1701 + 6535 4694 4320 4750 4758 + 6536 3908 295 4241 4462 + 6537 1770 1416 1417 1765 + 6538 4543 4607 4615 4704 + 6539 1632 793 1654 4082 + 6540 2908 3995 4498 4761 + 6541 710 3128 345 4032 + 6542 3064 3500 1266 1146 + 6543 1118 1159 1176 1120 + 6544 3128 3130 3168 4032 + 6545 1391 1112 4298 4531 + 6546 1696 1025 981 1027 + 6547 313 2943 2828 4502 + 6548 982 955 962 969 + 6549 953 924 939 937 + 6550 3328 793 2135 4404 + 6551 3546 3053 981 1027 + 6552 1180 1161 1170 1157 + 6553 3053 3083 981 1027 + 6554 3528 3629 3501 3613 + 6555 966 995 4096 4179 + 6556 3321 3315 2704 4183 + 6557 3908 295 592 4455 + 6558 3230 2761 3227 3052 + 6559 3957 303 4619 4643 + 6560 3699 3131 3120 3118 + 6561 3907 3904 4411 4490 + 6562 1678 1693 1681 1688 + 6563 3069 4042 4257 4653 + 6564 3082 3172 3050 4136 + 6565 3064 3500 1146 3132 + 6566 2753 3047 4318 4548 + 6567 2478 2481 2493 2488 + 6568 1294 1304 1163 1157 + 6569 1328 1283 1285 1326 + 6570 3080 3077 2782 3529 + 6571 3603 3599 3459 4600 + 6572 3075 3380 3065 4031 + 6573 2667 3483 4564 4767 + 6574 222 223 220 4634 + 6575 4313 4517 4525 4623 + 6576 3064 3045 3066 3132 + 6577 3023 3022 3183 4006 + 6578 1215 1555 1224 4063 + 6579 1178 1175 3043 1120 + 6580 1736 1114 4756 4757 + 6581 1543 2166 1530 406 + 6582 1833 1839 144 1840 + 6583 39 31 2096 2097 + 6584 822 1187 4520 4603 + 6585 1707 1409 1672 4510 + 6586 1728 1726 1406 1769 + 6587 1418 1416 1767 1766 + 6588 1840 144 8 1848 + 6589 3146 1333 1290 3167 + 6590 2444 2512 2435 2441 + 6591 1168 1308 1167 1164 + 6592 1295 1294 1166 1299 + 6593 4098 4320 4527 4774 + 6594 1294 1295 1164 1163 + 6595 3036 3012 3145 3031 + 6596 82 76 75 2167 + 6597 2487 2482 59 2476 + 6598 1036 1035 4132 4359 + 6599 1874 147 145 1838 + 6600 3619 3587 447 446 + 6601 1167 1308 1295 1164 + 6602 746 748 743 4341 + 6603 1877 1837 1874 1838 + 6604 1813 1810 121 123 + 6605 1359 42 572 1341 + 6606 2431 2429 2466 2464 + 6607 1954 140 137 1949 + 6608 660 2462 2453 656 + 6609 714 735 731 729 + 6610 2489 58 2484 2488 + 6611 1299 1295 1325 1293 + 6612 1974 1965 1967 69 + 6613 2163 105 2151 2199 + 6614 1880 1866 226 1871 + 6615 1534 1533 1544 400 + 6616 417 2159 1527 116 + 6617 1492 1495 448 1476 + 6618 453 1480 2143 2142 + 6619 1302 1291 1156 1296 + 6620 4043 1719 4257 4289 + 6621 2188 2179 2178 2189 + 6622 1804 1805 1825 1806 + 6623 2044 2039 2036 4213 + 6624 95 189 90 198 + 6625 67 1987 1943 1986 + 6626 1808 1955 1956 1809 + 6627 2193 2200 2192 107 + 6628 94 93 87 69 + 6629 1805 1802 1813 1806 + 6630 2638 2628 2637 2634 + 6631 453 15 1484 2109 + 6632 2636 2629 2622 2633 + 6633 666 2599 2602 2611 + 6634 660 683 662 658 + 6635 2474 2451 2472 2453 + 6636 2473 2474 2459 662 + 6637 2631 2600 2601 2611 + 6638 478 156 484 194 + 6639 1955 1948 1936 1809 + 6640 189 95 1971 1906 + 6641 137 1954 1949 1950 + 6642 115 1801 1950 122 + 6643 1983 1974 93 1965 + 6644 2534 2451 2607 2610 + 6645 2455 2427 2430 2466 + 6646 1900 1962 188 1898 + 6647 1889 1896 1890 1899 + 6648 672 666 2607 2608 + 6649 1973 94 96 87 + 6650 478 156 1452 2184 + 6651 65 67 1986 126 + 6652 2418 2414 2410 2412 + 6653 2451 2460 2453 656 + 6654 845 2418 2412 839 + 6655 2600 2601 2611 668 + 6656 2106 2142 2103 2109 + 6657 1982 1901 1973 1985 + 6658 2550 2543 701 2555 + 6659 81 139 138 133 + 6660 2561 2556 2557 2555 + 6661 2611 666 673 2613 + 6662 2600 664 2597 665 + 6663 2600 2597 2631 665 + 6664 2600 2631 2601 665 + 6665 666 670 2611 673 + 6666 2594 2596 670 674 + 6667 1886 1927 1893 1907 + 6668 2596 2594 670 669 + 6669 2628 2638 715 2633 + 6670 2545 697 2544 2590 + 6671 654 672 666 2607 + 6672 1824 134 109 111 + 6673 666 672 2593 2613 + 6674 1795 1789 1798 1792 + 6675 2492 1855 1852 1847 + 6676 2529 2521 2533 2524 + 6677 66 1960 1981 1987 + 6678 1901 1970 189 1906 + 6679 1962 1903 1904 1902 + 6680 1967 94 87 69 + 6681 189 1901 1964 1897 + 6682 95 94 92 69 + 6683 1964 187 1903 1904 + 6684 3013 3012 1305 3145 + 6685 2642 2630 722 716 + 6686 2565 2557 2564 2563 + 6687 3469 3460 3461 270 + 6688 2629 2628 2622 2633 + 6689 1967 1974 1969 1966 + 6690 87 93 64 69 + 6691 1987 1960 1942 1943 + 6692 95 189 97 96 + 6693 1973 92 1967 1969 + 6694 1905 1899 1902 1922 + 6695 1985 1973 1974 1983 + 6696 1585 1280 1590 4145 + 6697 1332 1329 1325 1293 + 6698 1806 121 118 123 + 6699 715 2636 721 2633 + 6700 1974 1967 87 69 + 6701 95 1971 1906 92 + 6702 2636 2638 2637 2634 + 6703 1971 94 1973 92 + 6704 66 1974 87 69 + 6705 2543 2547 707 696 + 6706 139 81 136 1953 + 6707 2636 2638 715 2625 + 6708 2617 2623 2619 2621 + 6709 97 89 88 187 + 6710 209 1886 208 211 + 6711 710 3640 3112 4552 + 6712 1907 190 228 225 + 6713 208 209 211 228 + 6714 66 1960 96 87 + 6715 1972 1982 1901 1973 + 6716 604 2540 2544 2590 + 6717 1909 1887 199 198 + 6718 2637 2638 665 716 + 6719 1891 1910 1899 1898 + 6720 39 2110 2101 2097 + 6721 1985 1974 1981 1983 + 6722 1890 1886 209 211 + 6723 1982 1960 1963 1981 + 6724 1960 1982 1985 1981 + 6725 478 1488 477 2184 + 6726 2886 2888 4106 4328 + 6727 93 1974 64 69 + 6728 67 65 100 64 + 6729 1938 1943 1941 1986 + 6730 1808 1885 1948 101 + 6731 3589 2123 3591 2141 + 6732 209 1886 214 208 + 6733 2640 735 2624 2552 + 6734 1807 1808 1885 1955 + 6735 1808 1948 124 101 + 6736 2610 2607 2602 2611 + 6737 2639 719 720 663 + 6738 2629 2636 2625 2635 + 6739 130 140 1947 1949 + 6740 1937 1940 1956 1809 + 6741 2166 1533 1530 406 + 6742 482 478 484 194 + 6743 1807 1813 1810 1812 + 6744 65 1935 1959 1979 + 6745 1987 1942 1978 1943 + 6746 2203 2200 2193 107 + 6747 1811 122 113 114 + 6748 1938 1959 1943 1986 + 6749 1954 1952 1953 1958 + 6750 3423 3444 3443 3441 + 6751 2186 2179 2181 2184 + 6752 287 268 273 269 + 6753 3135 3165 3147 3145 + 6754 2532 2484 2531 2533 + 6755 134 1827 1825 108 + 6756 3692 3708 3565 3710 + 6757 3148 3135 3147 3145 + 6758 718 2617 714 723 + 6759 586 3319 4687 4730 + 6760 2112 2114 2122 2119 + 6761 1946 130 1947 1944 + 6762 1532 141 1542 75 + 6763 1781 1791 1861 1794 + 6764 1951 1801 1954 1952 + 6765 140 1952 1947 1949 + 6766 1940 1957 1937 1956 + 6767 1891 1893 1907 1910 + 6768 2195 2203 2162 2161 + 6769 2196 2159 2198 2160 + 6770 1802 1805 1811 1884 + 6771 139 81 1953 1958 + 6772 2106 2107 15 2109 + 6773 2599 2600 2602 2611 + 6774 479 1485 1490 1455 + 6775 2096 2108 2099 2104 + 6776 3145 3012 1156 1296 + 6777 1449 485 1470 192 + 6778 715 2642 721 720 + 6779 139 1937 1958 133 + 6780 531 3506 522 530 + 6781 1974 1985 1981 87 + 6782 1933 1931 1079 1929 + 6783 2197 2156 2150 2153 + 6784 1955 1936 138 133 + 6785 1957 1937 1956 1958 + 6786 1955 1957 1956 1958 + 6787 137 140 115 1949 + 6788 1980 65 1935 70 + 6789 1480 2098 2142 2109 + 6790 721 722 667 716 + 6791 666 654 673 668 + 6792 137 140 139 136 + 6793 216 221 199 217 + 6794 1472 1459 475 2191 + 6795 1885 1955 1954 1958 + 6796 485 1449 1458 2181 + 6797 136 81 77 1953 + 6798 2636 2626 2625 2635 + 6799 2439 2435 682 2442 + 6800 680 2446 2447 682 + 6801 416 3579 401 3543 + 6802 2197 2163 2152 2153 + 6803 1941 65 126 70 + 6804 1955 1808 1948 1809 + 6805 672 666 2608 2613 + 6806 2435 2512 2440 2441 + 6807 1803 1801 1884 122 + 6808 152 2185 2116 2117 + 6809 1801 1885 115 1950 + 6810 1948 1808 1812 1809 + 6811 2113 2185 2112 2119 + 6812 68 74 65 100 + 6813 2976 2851 317 2832 + 6814 45 2110 2096 2108 + 6815 2114 151 2119 2118 + 6816 544 176 1 535 + 6817 176 2003 544 1 + 6818 1957 1980 1953 1958 + 6819 1957 1939 1980 1935 + 6820 177 186 535 2007 + 6821 1941 65 1986 126 + 6822 175 170 48 148 + 6823 100 1987 1978 1943 + 6824 67 100 1959 1943 + 6825 2638 2636 715 2633 + 6826 1965 92 1967 69 + 6827 1974 93 1965 69 + 6828 2565 2541 701 2535 + 6829 1939 1941 125 126 + 6830 483 1488 1453 2184 + 6831 2545 2541 699 2551 + 6832 2631 2637 667 716 + 6833 724 2624 2552 2563 + 6834 68 74 100 93 + 6835 2554 2640 2624 2552 + 6836 93 74 1965 69 + 6837 1964 1961 97 187 + 6838 2617 730 718 714 + 6839 1973 1985 1974 1967 + 6840 719 718 732 720 + 6841 98 91 472 468 + 6842 2626 714 2625 2632 + 6843 2642 2639 663 2641 + 6844 477 483 484 2184 + 6845 652 2605 2606 2604 + 6846 665 2638 715 716 + 6847 730 2617 718 732 + 6848 2416 845 2412 843 + 6849 2642 722 720 716 + 6850 67 100 1943 64 + 6851 1983 1981 1978 64 + 6852 910 2416 2412 843 + 6853 730 2629 2619 721 + 6854 1944 79 1975 1979 + 6855 1965 1983 1966 1976 + 6856 2112 1489 2117 2184 + 6857 2636 730 715 721 + 6858 730 2636 2629 721 + 6859 65 1980 1935 1979 + 6860 2595 2601 2608 2609 + 6861 1937 81 139 1958 + 6862 896 2338 2332 879 + 6863 604 709 697 698 + 6864 2541 2562 699 2551 + 6865 66 1961 97 96 + 6866 721 715 720 716 + 6867 1968 1909 1887 1908 + 6868 2543 707 701 696 + 6869 130 1945 77 1944 + 6870 95 90 199 198 + 6871 2562 2554 2541 2553 + 6872 1888 221 213 216 + 6873 1926 1927 1888 213 + 6874 1515 1512 430 204 + 6875 130 77 79 1944 + 6876 1907 221 1908 1888 + 6877 1927 1907 1908 1888 + 6878 1927 1893 1907 1888 + 6879 2591 2592 2566 2569 + 6880 2610 2605 655 651 + 6881 1288 1297 1299 1293 + 6882 2143 2105 2142 2103 + 6883 2550 698 2539 2535 + 6884 604 2540 2537 2536 + 6885 744 769 749 2353 + 6886 1971 95 97 96 + 6887 2589 606 2568 2590 + 6888 709 606 2537 2590 + 6889 2364 1202 2365 2353 + 6890 89 190 1905 225 + 6891 100 1983 1978 64 + 6892 2096 1477 1478 2097 + 6893 2546 2583 2581 2582 + 6894 2556 2623 717 729 + 6895 189 1964 97 88 + 6896 2541 2562 701 699 + 6897 93 100 1965 1976 + 6898 2550 2543 2560 701 + 6899 2543 2550 2560 2555 + 6900 1989 1992 2012 2005 + 6901 698 2541 701 699 + 6902 700 698 701 699 + 6903 697 2545 698 699 + 6904 735 731 2624 2552 + 6905 1669 1279 4323 4343 + 6906 2333 2334 2404 2341 + 6907 2036 2039 1994 158 + 6908 1994 2036 1993 1990 + 6909 4290 1595 4341 4563 + 6910 724 717 723 729 + 6911 1901 1972 189 1964 + 6912 1980 65 70 1979 + 6913 2541 2545 698 701 + 6914 2560 2543 2547 701 + 6915 2547 2543 707 701 + 6916 2560 2547 707 701 + 6917 1888 216 208 228 + 6918 1984 1983 100 1976 + 6919 1961 66 1960 96 + 6920 280 3460 3469 276 + 6921 94 95 1971 96 + 6922 2107 2106 2104 2109 + 6923 1955 1954 1958 133 + 6924 216 1907 190 228 + 6925 188 1962 1904 1898 + 6926 1962 1972 1897 1903 + 6927 190 216 228 217 + 6928 1937 1940 1941 125 + 6929 190 1905 1899 1898 + 6930 1970 1901 189 1897 + 6931 30 29 442 54 + 6932 2106 2108 2107 2104 + 6933 1982 1972 1901 1897 + 6934 95 94 1971 92 + 6935 2100 1480 1481 1482 + 6936 410 2159 1462 116 + 6937 307 3470 276 275 + 6938 2197 2156 1529 2150 + 6939 1017 1023 1020 1018 + 6940 1375 1389 2002 1388 + 6941 362 366 3795 372 + 6942 717 2623 723 729 + 6943 901 910 2413 898 + 6944 1470 2180 2178 192 + 6945 24 488 22 23 + 6946 89 199 188 88 + 6947 39 31 45 2096 + 6948 1905 225 1899 1922 + 6949 30 29 2140 442 + 6950 2195 2203 2196 2162 + 6951 482 478 477 484 + 6952 1477 31 1494 462 + 6953 1811 1826 1827 114 + 6954 456 453 454 461 + 6955 2096 2099 2097 2111 + 6956 3524 286 281 288 + 6957 1961 1972 1963 1964 + 6958 1960 1961 1963 1964 + 6959 1480 1479 3601 1484 + 6960 1494 31 460 462 + 6961 3586 3600 455 450 + 6962 444 3586 455 450 + 6963 2138 2146 2144 2140 + 6964 2105 2106 2107 15 + 6965 2096 2098 2104 2109 + 6966 3524 3517 274 288 + 6967 3511 3512 3497 3393 + 6968 280 3460 3475 3469 + 6969 478 477 484 2184 + 6970 1905 188 1904 1898 + 6971 216 208 228 217 + 6972 2149 2143 2103 2148 + 6973 15 461 1484 32 + 6974 2120 1478 2121 2097 + 6975 3590 3587 444 451 + 6976 3460 3464 3461 270 + 6977 2625 2629 2635 2632 + 6978 2114 2120 2118 2102 + 6979 32 2098 2109 14 + 6980 2098 2100 2142 1481 + 6981 1980 70 1944 1979 + 6982 1974 1973 1967 1969 + 6983 442 3590 452 451 + 6984 3487 3489 3490 3445 + 6985 268 270 273 269 + 6986 3475 3924 3478 3477 + 6987 974 1100 1086 4214 + 6988 2334 2333 892 2341 + 6989 453 454 461 32 + 6990 1164 1300 1297 1301 + 6991 3460 3464 270 269 + 6992 3026 3025 3021 2999 + 6993 2098 2096 1478 2097 + 6994 3490 278 289 271 + 6995 3927 308 312 293 + 6996 276 3460 270 269 + 6997 31 45 460 32 + 6998 1488 478 1455 1452 + 6999 1477 1492 1475 1494 + 7000 276 270 268 269 + 7001 3475 3460 3480 3469 + 7002 444 452 451 455 + 7003 3587 444 451 446 + 7004 89 1909 1962 188 + 7005 45 2096 32 14 + 7006 1457 1456 1454 1455 + 7007 2098 2107 2109 14 + 7008 3162 3164 3028 3163 + 7009 2179 2186 2117 2184 + 7010 1470 1449 2178 2181 + 7011 156 193 2115 192 + 7012 2178 2188 2189 2187 + 7013 2099 2101 2097 2111 + 7014 1449 1457 1458 1452 + 7015 65 67 1959 1986 + 7016 416 401 3550 3543 + 7017 1300 1285 1284 1301 + 7018 2186 156 2117 2184 + 7019 532 531 510 533 + 7020 1532 141 75 2157 + 7021 2630 2642 722 663 + 7022 3568 3570 399 3564 + 7023 471 475 473 472 + 7024 2096 45 2108 14 + 7025 170 175 48 2120 + 7026 443 449 459 148 + 7027 2108 45 2107 14 + 7028 2096 2098 32 14 + 7029 1935 1938 1941 1986 + 7030 2110 169 173 2101 + 7031 1449 1470 1446 2181 + 7032 193 156 2181 194 + 7033 479 478 1454 1455 + 7034 454 1473 1482 1474 + 7035 2185 152 2113 2112 + 7036 1464 410 1462 116 + 7037 459 449 175 148 + 7038 443 459 175 148 + 7039 531 3506 521 522 + 7040 528 520 533 515 + 7041 448 1495 1496 1476 + 7042 176 186 177 168 + 7043 484 148 157 174 + 7044 2170 2175 2155 2157 + 7045 2205 2194 471 2203 + 7046 449 443 1487 148 + 7047 1489 449 1487 148 + 7048 483 1489 1487 148 + 7049 443 483 1487 148 + 7050 3812 3877 3875 3874 + 7051 853 832 2379 2386 + 7052 3877 3812 369 3874 + 7053 2487 2484 2478 2488 + 7054 104 417 1526 409 + 7055 468 194 191 192 + 7056 486 1448 476 1471 + 7057 1333 1292 1290 3167 + 7058 1815 1816 1822 1819 + 7059 1804 1827 1825 1828 + 7060 3865 3848 3851 236 + 7061 1805 1810 123 119 + 7062 2429 2449 2464 676 + 7063 2170 76 2172 2167 + 7064 2205 2194 2191 2192 + 7065 2192 135 107 106 + 7066 127 2200 98 2204 + 7067 2427 686 2455 2430 + 7068 91 127 98 2204 + 7069 2189 2178 2190 192 + 7070 127 2200 2204 2201 + 7071 476 473 472 468 + 7072 1450 1470 2183 2191 + 7073 476 91 1470 468 + 7074 3383 525 532 510 + 7075 1470 485 470 468 + 7076 479 1488 477 1455 + 7077 654 672 2614 671 + 7078 1945 1980 126 70 + 7079 2188 2186 2179 2115 + 7080 489 3431 500 492 + 7081 1447 1448 486 1456 + 7082 482 1458 1456 1454 + 7083 1980 1941 126 70 + 7084 1954 1955 138 133 + 7085 157 484 150 2184 + 7086 478 482 2181 194 + 7087 191 91 2190 192 + 7088 482 485 2181 194 + 7089 127 2200 2201 2182 + 7090 470 485 482 194 + 7091 91 2180 1470 191 + 7092 2187 2201 2182 2183 + 7093 2156 2164 2174 2154 + 7094 417 410 409 116 + 7095 2195 2196 2198 2162 + 7096 1948 1810 1812 124 + 7097 118 113 123 119 + 7098 2200 2192 107 106 + 7099 474 2205 1472 471 + 7100 1460 474 1464 471 + 7101 2205 1460 1464 471 + 7102 2940 2939 803 804 + 7103 195 196 1789 71 + 7104 1951 1801 1803 1950 + 7105 1467 2205 474 471 + 7106 2205 1467 1460 471 + 7107 1460 1467 474 471 + 7108 2460 2459 2456 2454 + 7109 689 693 695 694 + 7110 2196 2162 2152 2160 + 7111 4136 3134 4149 4306 + 7112 2242 2245 2257 2235 + 7113 1801 1802 1803 1884 + 7114 127 91 2190 2182 + 7115 2180 2187 2190 2182 + 7116 2189 2178 2187 2190 + 7117 2462 2470 2463 2453 + 7118 666 654 670 673 + 7119 1780 1784 1823 1786 + 7120 2106 453 2142 2109 + 7121 2159 2160 2150 2153 + 7122 1982 1973 96 87 + 7123 1973 1985 1967 87 + 7124 2426 2421 2467 2449 + 7125 2424 2425 677 2442 + 7126 2423 2446 2437 2438 + 7127 1535 1543 3537 3561 + 7128 1844 1543 2176 2166 + 7129 141 1532 1522 2157 + 7130 141 82 75 2157 + 7131 141 82 2157 2171 + 7132 1544 1533 1531 1538 + 7133 3135 3165 3146 3147 + 7134 407 3560 403 2133 + 7135 3560 2132 403 2133 + 7136 3560 1534 400 406 + 7137 1534 1533 400 406 + 7138 2531 2487 59 2520 + 7139 2176 2172 2177 2165 + 7140 2507 2509 2443 678 + 7141 76 2172 2167 2132 + 7142 2164 2174 2158 142 + 7143 1221 1621 4055 4390 + 7144 1459 1470 1450 2191 + 7145 1608 165 815 4384 + 7146 3206 2819 4594 4648 + 7147 1829 1869 1832 1881 + 7148 1841 1850 1836 1838 + 7149 226 215 1870 206 + 7150 413 414 104 411 + 7151 1531 1532 1524 1538 + 7152 1282 1328 1283 1329 + 7153 3627 2125 2145 4011 + 7154 1869 1858 1832 1881 + 7155 1288 1293 1326 1301 + 7156 1782 1791 73 1781 + 7157 1844 1543 2166 3561 + 7158 1533 2166 1530 1540 + 7159 3560 3544 1536 3537 + 7160 1851 2498 1846 2496 + 7161 76 2176 2166 2165 + 7162 1543 3560 3537 3561 + 7163 2150 2156 1539 2174 + 7164 1543 1535 1530 3561 + 7165 1951 1947 1949 1950 + 7166 400 75 406 403 + 7167 1543 2132 2176 403 + 7168 1526 1466 1520 409 + 7169 2175 2155 2157 2171 + 7170 125 81 126 133 + 7171 1858 215 1857 206 + 7172 730 2636 715 2625 + 7173 1803 1802 1811 1884 + 7174 141 2164 2171 2158 + 7175 1877 145 1835 1882 + 7176 193 2189 2115 192 + 7177 128 1820 1814 1787 + 7178 135 2202 117 116 + 7179 156 193 484 194 + 7180 1801 1951 1954 1950 + 7181 1805 1811 1884 119 + 7182 2159 417 2199 116 + 7183 1528 1529 1466 1527 + 7184 414 413 1542 1523 + 7185 1461 1460 1525 1462 + 7186 1882 215 143 1873 + 7187 422 3683 424 415 + 7188 196 1790 1789 1883 + 7189 2197 1529 414 104 + 7190 1466 1529 414 1523 + 7191 99 1860 1799 1776 + 7192 1877 1836 145 1882 + 7193 2173 2155 2171 2158 + 7194 2197 105 2150 104 + 7195 1529 2197 2150 104 + 7196 105 2160 2150 104 + 7197 1952 1951 1947 1949 + 7198 2640 2632 2624 2620 + 7199 2605 652 669 651 + 7200 1937 1956 1958 133 + 7201 1980 1935 1941 70 + 7202 152 157 150 2117 + 7203 2112 2113 2122 150 + 7204 152 2116 157 2117 + 7205 831 870 2245 835 + 7206 2240 2249 2320 2243 + 7207 2000 167 2009 1 + 7208 831 2382 2244 2383 + 7209 3373 3432 3374 3434 + 7210 3374 3432 3360 3434 + 7211 3431 506 508 3434 + 7212 3432 3373 3431 3434 + 7213 2803 2805 3530 4001 + 7214 854 2262 2236 2250 + 7215 2457 652 2458 658 + 7216 2465 2421 690 2449 + 7217 2421 2465 2467 2449 + 7218 1784 1793 1796 1787 + 7219 675 2432 2424 677 + 7220 2203 2194 2193 2161 + 7221 2472 2460 2456 2454 + 7222 2512 2444 2443 2441 + 7223 554 573 556 557 + 7224 2462 2451 2453 656 + 7225 2619 2629 2621 2622 + 7226 215 1882 1880 1873 + 7227 2163 2197 2164 2151 + 7228 2473 686 2459 2427 + 7229 2450 675 693 2432 + 7230 1802 1805 1804 1806 + 7231 686 661 662 2454 + 7232 1821 1827 1823 1786 + 7233 686 2459 2427 2455 + 7234 686 2473 2468 2427 + 7235 2427 2459 2456 2466 + 7236 2474 2473 2427 2430 + 7237 2457 683 684 658 + 7238 1849 1855 2492 1847 + 7239 1841 1831 1833 1840 + 7240 660 2460 661 656 + 7241 1813 1805 1810 123 + 7242 2474 2470 661 662 + 7243 2474 2473 2470 662 + 7244 854 625 2254 2256 + 7245 2473 2470 662 688 + 7246 686 2473 2459 688 + 7247 2459 2473 662 688 + 7248 686 2459 662 688 + 7249 2473 686 684 688 + 7250 923 924 949 925 + 7251 2448 2446 2425 2423 + 7252 2080 27 42 1341 + 7253 2179 2189 193 192 + 7254 2074 2078 2073 2077 + 7255 2076 2080 42 1341 + 7256 683 2470 2457 688 + 7257 660 683 658 659 + 7258 1343 1359 572 1341 + 7259 42 27 46 1341 + 7260 1342 1336 4 2077 + 7261 2082 4 2073 2077 + 7262 2435 677 682 2442 + 7263 1798 1878 1862 1792 + 7264 2517 2508 2507 2509 + 7265 344 2921 801 804 + 7266 2522 2512 2511 2519 + 7267 1831 1853 1840 146 + 7268 3937 2789 2783 4373 + 7269 40 2524 41 5 + 7270 2527 2444 679 678 + 7271 1840 144 146 8 + 7272 2476 59 40 2523 + 7273 60 19 2513 681 + 7274 2443 2437 679 678 + 7275 2444 2512 2511 2435 + 7276 19 2447 2437 678 + 7277 2511 2527 681 678 + 7278 2508 2513 2507 2527 + 7279 2476 2475 2478 2493 + 7280 2481 58 2494 2488 + 7281 2484 58 2478 2488 + 7282 127 91 98 106 + 7283 2497 1834 2496 1845 + 7284 16 2497 2499 1851 + 7285 2489 2484 2485 2503 + 7286 1829 1832 1870 1873 + 7287 695 687 676 694 + 7288 2489 2487 2506 2488 + 7289 1879 1835 1872 1871 + 7290 2317 2299 2308 2298 + 7291 966 954 958 952 + 7292 2269 2287 2285 2286 + 7293 4015 2505 4120 4299 + 7294 59 58 61 53 + 7295 2446 2439 2425 2437 + 7296 1875 1876 1879 1872 + 7297 581 2274 583 2265 + 7298 2273 2307 587 2298 + 7299 1858 1857 1870 206 + 7300 1865 1878 1875 1862 + 7301 58 57 2482 59 + 7302 638 2299 636 593 + 7303 2619 2639 721 2622 + 7304 2290 2316 593 2318 + 7305 1863 86 1878 1876 + 7306 57 2526 2486 59 + 7307 2476 2475 2525 2523 + 7308 57 2487 2482 59 + 7309 2487 57 2486 59 + 7310 2494 2506 2493 2488 + 7311 2487 2486 2531 59 + 7312 2507 2508 2527 2519 + 7313 2511 2507 2527 2519 + 7314 2425 2424 2423 2437 + 7315 2481 2494 2493 2488 + 7316 2509 2507 681 678 + 7317 2471 2470 2468 2461 + 7318 978 1122 1093 1104 + 7319 2469 2468 692 2461 + 7320 2427 2429 2430 2466 + 7321 1791 71 99 1861 + 7322 1860 1781 1861 1794 + 7323 471 2205 2203 2202 + 7324 1837 1877 1835 1880 + 7325 1836 1841 1833 1840 + 7326 690 689 691 695 + 7327 2164 2174 2154 2158 + 7328 2245 2242 835 2235 + 7329 617 2208 2248 614 + 7330 2261 2208 618 614 + 7331 2082 10 1342 2 + 7332 2296 2314 634 2218 + 7333 2422 2450 675 693 + 7334 2515 2517 19 2509 + 7335 141 82 142 411 + 7336 2450 2422 2426 693 + 7337 686 2459 2455 2454 + 7338 1878 1863 1876 1872 + 7339 2431 685 2464 2434 + 7340 2420 2422 2424 2423 + 7341 677 2435 679 2436 + 7342 2512 2444 2511 2443 + 7343 2235 2257 869 2232 + 7344 2445 2431 2434 2428 + 7345 1827 134 110 108 + 7346 2200 2204 2201 2192 + 7347 1882 1835 1880 1873 + 7348 1853 1829 1833 1840 + 7349 1853 1854 2500 1840 + 7350 144 147 146 8 + 7351 2482 58 59 40 + 7352 1849 2492 1852 1847 + 7353 170 1489 2114 2122 + 7354 1874 1829 1833 1873 + 7355 147 1829 1874 145 + 7356 644 3274 2909 4016 + 7357 1877 1874 145 1838 + 7358 2172 2170 2177 2165 + 7359 1865 1797 1798 1862 + 7360 2197 2163 105 2151 + 7361 1245 1244 4222 4554 + 7362 1533 2170 2166 1540 + 7363 1865 1875 1876 1797 + 7364 2131 1844 2166 3559 + 7365 2176 1844 2166 2165 + 7366 2163 2197 105 2153 + 7367 677 2425 682 2442 + 7368 688 687 691 695 + 7369 2601 665 2593 668 + 7370 3611 3627 3610 4011 + 7371 1856 1788 1859 1867 + 7372 1837 1874 1841 1835 + 7373 86 1788 196 1883 + 7374 1857 143 1883 206 + 7375 3291 3863 3312 4176 + 7376 2527 2511 2519 2528 + 7377 1858 143 1832 1881 + 7378 1864 1860 1799 1861 + 7379 195 196 1859 1792 + 7380 2607 666 2602 2611 + 7381 1802 1811 113 114 + 7382 1829 1839 1833 1840 + 7383 1829 147 1874 1833 + 7384 1505 2166 3561 3559 + 7385 2429 2421 694 2428 + 7386 2195 2196 2199 117 + 7387 1835 1874 1871 1873 + 7388 1882 215 1866 226 + 7389 1853 1854 1849 1830 + 7390 215 226 1870 1871 + 7391 1829 147 1839 203 + 7392 1878 1863 1872 1871 + 7393 2049 2050 2059 1434 + 7394 2200 2201 2193 2192 + 7395 1865 1876 195 227 + 7396 1837 1834 1841 1838 + 7397 1796 1820 1814 1816 + 7398 83 1782 73 1781 + 7399 1869 1829 1832 1840 + 7400 2057 11 25 1434 + 7401 147 1877 1836 145 + 7402 203 147 215 145 + 7403 1874 147 1838 1833 + 7404 147 1829 1839 1833 + 7405 1836 147 145 146 + 7406 1850 1834 1845 1838 + 7407 1850 1877 1836 1838 + 7408 1858 1857 143 1881 + 7409 2446 2439 2447 682 + 7410 2157 2155 2154 2158 + 7411 1869 1839 1858 1881 + 7412 1832 1858 1870 1873 + 7413 2425 680 2424 677 + 7414 2210 2207 2252 2206 + 7415 638 636 2315 634 + 7416 2511 2507 2519 2443 + 7417 129 80 1784 1785 + 7418 1877 1882 1880 1866 + 7419 3444 3442 3443 3441 + 7420 1776 80 1817 78 + 7421 132 121 1822 120 + 7422 2055 2050 2057 1434 + 7423 1788 1868 1858 1857 + 7424 1877 147 1836 1838 + 7425 506 3374 3367 494 + 7426 11 561 497 1434 + 7427 11 2054 1428 1434 + 7428 1800 1820 1777 1817 + 7429 215 1858 1857 143 + 7430 1835 1837 1880 1879 + 7431 1866 1879 1872 1871 + 7432 1876 86 195 227 + 7433 2157 141 2171 2158 + 7434 2155 2157 2171 2158 + 7435 1522 141 2157 2158 + 7436 1789 195 1798 1792 + 7437 1783 1790 1789 1792 + 7438 1811 1802 1826 114 + 7439 1778 129 1784 1785 + 7440 1791 1795 1861 1794 + 7441 1776 1860 1781 1861 + 7442 3276 3348 580 4002 + 7443 1824 1784 1828 1786 + 7444 1804 1826 1827 1828 + 7445 1796 1814 1815 1816 + 7446 1789 1783 71 73 + 7447 129 83 73 72 + 7448 1878 1863 1795 1859 + 7449 2439 2507 2509 2443 + 7450 1779 1783 1791 1792 + 7451 129 1818 72 1785 + 7452 1784 1824 1785 1787 + 7453 1783 1782 1791 73 + 7454 147 1839 203 1881 + 7455 129 128 111 131 + 7456 2449 2421 2429 2428 + 7457 1878 1865 1798 1862 + 7458 1793 1784 1785 1787 + 7459 1782 1779 1783 1791 + 7460 1779 1782 1783 1818 + 7461 2426 2421 2449 2428 + 7462 83 1778 1782 1818 + 7463 2426 2450 693 2428 + 7464 1793 80 1784 1796 + 7465 86 215 226 206 + 7466 1797 1795 1798 1862 + 7467 1807 1805 1813 1806 + 7468 71 1797 99 1861 + 7469 1778 1784 131 1823 + 7470 129 1778 83 1818 + 7471 3805 2853 321 4002 + 7472 1878 1865 195 1798 + 7473 1800 1793 1777 1860 + 7474 1793 72 1794 1785 + 7475 2487 2489 2484 2488 + 7476 2439 2435 2441 679 + 7477 1797 1798 71 99 + 7478 99 1797 1799 1861 + 7479 3444 3423 3424 3441 + 7480 2435 2439 682 679 + 7481 143 203 1832 1881 + 7482 2435 2444 2441 679 + 7483 2205 1467 1472 2191 + 7484 72 1818 1794 1785 + 7485 2519 2508 41 2518 + 7486 1778 129 83 131 + 7487 1818 1781 1785 1780 + 7488 1794 1793 1785 1787 + 7489 3423 3433 3424 3441 + 7490 3421 3444 3424 3441 + 7491 3433 3442 3432 3441 + 7492 3808 3805 321 4002 + 7493 580 3348 3303 4002 + 7494 1820 1796 1800 1816 + 7495 3028 3135 3148 3166 + 7496 3848 3754 3851 236 + 7497 547 546 423 421 + 7498 2186 156 2179 2115 + 7499 156 478 2181 194 + 7500 2179 156 193 2115 + 7501 156 2186 2116 2115 + 7502 265 3864 267 253 + 7503 3723 3734 3735 3853 + 7504 252 3840 265 253 + 7505 3673 3656 568 3658 + 7506 3653 3657 3673 3658 + 7507 555 501 558 568 + 7508 3419 3423 3444 558 + 7509 3444 3423 3443 558 + 7510 501 3423 3436 558 + 7511 3423 3419 3436 558 + 7512 3436 501 558 3407 + 7513 501 3436 507 3407 + 7514 555 3419 558 3407 + 7515 3660 3653 568 3658 + 7516 3737 234 230 3751 + 7517 3672 543 3670 3652 + 7518 3420 3444 3443 537 + 7519 570 3420 3670 3652 + 7520 543 570 3670 3652 + 7521 3420 3630 3652 3651 + 7522 263 267 235 262 + 7523 3642 3672 3670 3669 + 7524 3670 3672 3652 3669 + 7525 3642 3630 545 3669 + 7526 1488 479 1490 1455 + 7527 553 3642 545 3669 + 7528 570 3420 3651 537 + 7529 3672 3642 3639 3669 + 7530 3639 3642 553 3669 + 7531 3660 3639 553 3669 + 7532 543 3660 553 3651 + 7533 543 553 545 3651 + 7534 553 3660 3669 3651 + 7535 545 553 3669 3651 + 7536 555 558 537 568 + 7537 543 3653 3673 568 + 7538 3424 555 537 568 + 7539 570 3424 537 568 + 7540 570 543 3670 568 + 7541 3670 543 3673 568 + 7542 3697 3698 3676 3696 + 7543 3424 570 3670 568 + 7544 3424 3670 3673 568 + 7545 3656 3424 3673 568 + 7546 3424 3656 555 568 + 7547 3660 3653 3658 3666 + 7548 476 1448 1450 1471 + 7549 1525 1460 1527 1465 + 7550 418 419 3682 421 + 7551 3750 3983 3984 3745 + 7552 3746 3798 248 247 + 7553 2159 2150 1526 1520 + 7554 3660 3672 3653 3664 + 7555 3667 3639 3666 3664 + 7556 3653 3667 3666 3664 + 7557 3660 3653 3666 3664 + 7558 3639 3672 3669 3664 + 7559 3660 3639 3669 3664 + 7560 3672 3660 3669 3664 + 7561 3639 3660 553 3664 + 7562 553 3660 3666 3664 + 7563 3639 553 3666 3664 + 7564 422 3682 423 421 + 7565 419 418 547 421 + 7566 3641 547 423 421 + 7567 424 399 415 435 + 7568 3983 3777 3798 247 + 7569 1553 1574 1552 4454 + 7570 3698 3675 3677 422 + 7571 3667 3639 3664 3718 + 7572 3654 3702 3718 3635 + 7573 3682 418 422 3676 + 7574 418 419 3691 3676 + 7575 419 418 3682 3676 + 7576 3719 418 3691 3676 + 7577 418 3719 3677 3676 + 7578 3677 3675 3720 424 + 7579 3675 3677 422 424 + 7580 3719 3677 3720 424 + 7581 3719 418 3677 424 + 7582 418 3719 3708 424 + 7583 3708 3719 3720 424 + 7584 3712 3708 3713 424 + 7585 418 3712 3713 424 + 7586 3712 418 3708 424 + 7587 3748 3747 3751 3742 + 7588 3698 3675 422 424 + 7589 3675 3698 3683 424 + 7590 3683 3698 422 424 + 7591 3746 3798 247 3745 + 7592 3667 3709 3654 3718 + 7593 3639 3667 3654 3718 + 7594 3631 3649 3632 3696 + 7595 3702 3671 3654 3718 + 7596 3671 3702 3643 3718 + 7597 3643 3702 3722 3718 + 7598 3671 3643 3722 3718 + 7599 547 3711 3722 3635 + 7600 3711 3709 3722 3635 + 7601 3722 3709 3718 3635 + 7602 3702 3722 3718 3635 + 7603 2156 2197 2152 2153 + 7604 3631 3632 3641 3696 + 7605 3682 3721 3676 3696 + 7606 3676 3721 3641 3696 + 7607 3632 3676 3641 3696 + 7608 402 3574 3575 3571 + 7609 3631 3682 3696 423 + 7610 3570 3568 399 415 + 7611 3704 3643 547 423 + 7612 547 3643 3641 423 + 7613 3643 3631 3641 423 + 7614 3631 3643 3704 423 + 7615 3641 3631 3696 423 + 7616 3682 419 3721 421 + 7617 3711 419 547 421 + 7618 419 3711 3721 421 + 7619 3722 547 3641 421 + 7620 3721 3722 3641 421 + 7621 3721 3641 3696 421 + 7622 3696 3641 423 421 + 7623 3682 3721 3696 421 + 7624 3682 3696 423 421 + 7625 3711 547 3722 421 + 7626 3721 3711 3722 421 + 7627 3550 401 3549 3543 + 7628 345 4509 4617 4662 + 7629 3570 3574 3674 3678 + 7630 3033 3034 3010 3035 + 7631 3698 3675 3683 3687 + 7632 3568 3675 3687 3674 + 7633 3570 3693 399 3564 + 7634 3574 3568 3569 3564 + 7635 3568 3574 3570 3564 + 7636 3575 3574 3550 3564 + 7637 3575 3550 3549 3564 + 7638 3574 3575 3570 3564 + 7639 3675 3677 3720 3678 + 7640 3697 3675 3674 3678 + 7641 3675 3697 3677 3678 + 7642 3687 3570 3674 3678 + 7643 3683 3675 3720 3678 + 7644 415 3570 3687 3678 + 7645 3683 415 3687 3678 + 7646 3675 3687 3674 3678 + 7647 3675 3683 3687 3678 + 7648 3713 3683 3720 3678 + 7649 3692 3713 3720 3678 + 7650 3693 3692 3720 3678 + 7651 3570 3693 3720 3678 + 7652 1844 2176 2177 2165 + 7653 1292 1324 3145 3167 + 7654 3695 3693 3707 435 + 7655 399 3695 3707 435 + 7656 3693 399 3707 435 + 7657 3692 3713 3678 435 + 7658 3693 3692 3678 435 + 7659 415 3570 3678 435 + 7660 3570 3693 3678 435 + 7661 3008 2737 2802 4118 + 7662 3540 3548 3578 408 + 7663 1747 1745 1398 4040 + 7664 2166 1530 1505 3561 + 7665 1535 1533 1530 3561 + 7666 3381 3382 507 499 + 7667 401 3916 3906 408 + 7668 1110 1738 1400 4040 + 7669 1416 1770 1767 1766 + 7670 3575 3574 3904 3567 + 7671 401 3906 3578 408 + 7672 1847 16 1851 4010 + 7673 3579 416 401 3578 + 7674 401 3899 3916 408 + 7675 3574 3568 3674 3567 + 7676 3007 3151 4367 4391 + 7677 3539 3541 3905 3578 + 7678 3541 3539 3543 3578 + 7679 3543 3539 3905 3578 + 7680 2800 3007 4367 4391 + 7681 2733 3179 2745 4391 + 7682 1237 1685 4695 4705 + 7683 3341 3339 3340 4388 + 7684 3 7 557 6 + 7685 3549 3575 3567 3576 + 7686 402 401 3571 3576 + 7687 401 402 3575 3576 + 7688 3575 402 3571 3576 + 7689 990 4179 4207 4477 + 7690 3577 3904 3567 3576 + 7691 3634 3688 3685 4246 + 7692 1191 4055 4384 4390 + 7693 266 254 256 3832 + 7694 3850 3865 3847 3872 + 7695 3847 3865 261 3872 + 7696 3850 3755 3865 3872 + 7697 3755 3752 239 3753 + 7698 3755 3872 3816 3815 + 7699 1 176 177 535 + 7700 3835 3877 3875 3838 + 7701 3755 3854 237 3872 + 7702 237 3854 261 3872 + 7703 3755 237 3865 3872 + 7704 3865 237 261 3872 + 7705 3755 3854 3872 3815 + 7706 3811 266 256 3832 + 7707 3744 3752 3855 3754 + 7708 3750 3983 3748 3984 + 7709 234 3744 230 3751 + 7710 3743 3744 234 3751 + 7711 3848 3752 3754 236 + 7712 230 3855 3766 236 + 7713 3752 239 3766 236 + 7714 3744 230 3754 236 + 7715 230 3744 3855 236 + 7716 3855 3744 3754 236 + 7717 3752 3855 3754 236 + 7718 3855 3752 3766 236 + 7719 248 3746 247 3745 + 7720 3970 3971 4000 3772 + 7721 3749 3748 3751 3742 + 7722 3971 3996 3969 3976 + 7723 3744 3749 3751 3742 + 7724 3970 3984 3798 3746 + 7725 3987 3970 3798 3746 + 7726 2945 2931 2948 2946 + 7727 263 261 3867 265 + 7728 3743 3744 3751 3742 + 7729 2817 3957 3978 3975 + 7730 3351 3179 3293 3349 + 7731 1170 1158 4519 4601 + 7732 3798 3987 3746 248 + 7733 3792 3987 3798 248 + 7734 3970 3984 3746 4000 + 7735 3789 3987 3792 248 + 7736 3994 3996 3973 3974 + 7737 3971 3969 4000 3976 + 7738 242 248 3770 240 + 7739 248 242 241 240 + 7740 3994 3973 3993 2813 + 7741 3970 3971 3969 4000 + 7742 4000 3746 3772 3976 + 7743 3971 3970 242 3772 + 7744 1911 1924 1912 4220 + 7745 2924 2923 2986 790 + 7746 242 3971 3772 3976 + 7747 244 302 3973 301 + 7748 3776 3994 3993 3980 + 7749 2975 2974 2834 2836 + 7750 304 303 301 2816 + 7751 1441 1396 1437 1440 + 7752 242 3971 3976 241 + 7753 3971 3996 3976 241 + 7754 503 1441 1437 1440 + 7755 323 3213 2969 325 + 7756 2497 2498 1851 2496 + 7757 3772 242 3976 241 + 7758 105 2160 104 2199 + 7759 2978 2983 2962 2981 + 7760 3987 3996 3971 3974 + 7761 2965 2966 2953 2952 + 7762 234 230 3726 238 + 7763 1243 1197 4285 4319 + 7764 3982 3999 3981 3992 + 7765 3999 3998 3976 3992 + 7766 1742 1117 1588 4541 + 7767 3991 3957 2816 2818 + 7768 4226 1584 4389 4526 + 7769 3998 3982 3969 3992 + 7770 3998 3999 3982 3992 + 7771 3772 242 241 3990 + 7772 242 244 241 3990 + 7773 3998 3772 3976 3990 + 7774 3976 3772 241 3990 + 7775 244 3998 3969 3990 + 7776 3969 3998 3976 3990 + 7777 244 3969 3994 3990 + 7778 3996 3976 241 3990 + 7779 3996 3969 3976 3990 + 7780 3969 3996 3994 3990 + 7781 1075 1076 1078 4220 + 7782 3736 3790 247 245 + 7783 231 3736 247 245 + 7784 2951 356 2953 2952 + 7785 356 2965 2963 358 + 7786 230 239 3766 238 + 7787 3736 3728 3790 245 + 7788 3790 3728 3756 245 + 7789 3728 231 3727 245 + 7790 3736 231 3750 245 + 7791 3728 3736 3750 245 + 7792 234 3765 249 238 + 7793 3756 3728 3727 245 + 7794 3750 231 3799 245 + 7795 3728 3750 3799 245 + 7796 231 3728 3799 245 + 7797 3849 3871 3872 3816 + 7798 807 806 2648 2707 + 7799 2710 2937 2708 2707 + 7800 252 266 253 256 + 7801 3840 3864 265 253 + 7802 3824 3814 3829 253 + 7803 232 264 233 262 + 7804 3734 3723 3857 3853 + 7805 559 562 3 557 + 7806 3729 3733 3795 3730 + 7807 1342 2082 2080 2077 + 7808 2088 2076 2089 1341 + 7809 807 2937 806 2707 + 7810 3431 3373 506 3434 + 7811 3067 3011 3074 3035 + 7812 3739 373 374 375 + 7813 1954 137 138 103 + 7814 250 252 3839 257 + 7815 3734 3724 3735 375 + 7816 3864 3824 265 267 + 7817 3824 3871 3825 3816 + 7818 3821 3824 3829 267 + 7819 3018 3011 3009 3035 + 7820 255 3821 3809 267 + 7821 3809 3821 3829 267 + 7822 255 3809 3829 267 + 7823 3871 3867 265 3816 + 7824 3739 3723 3735 3853 + 7825 3872 3847 3816 3815 + 7826 3841 3837 3811 3836 + 7827 252 3841 3811 3836 + 7828 250 3837 3839 3836 + 7829 3837 250 3811 3836 + 7830 252 250 3839 3836 + 7831 3837 250 3839 257 + 7832 3431 508 3416 3434 + 7833 782 2694 2693 781 + 7834 252 3811 266 256 + 7835 3811 252 3836 256 + 7836 250 3811 3836 256 + 7837 252 250 3836 256 + 7838 3855 3752 3726 3753 + 7839 3815 3854 3823 3753 + 7840 254 250 258 260 + 7841 3812 3876 3875 3838 + 7842 2388 2370 2384 2389 + 7843 258 250 257 260 + 7844 250 369 257 260 + 7845 3877 3801 3875 3874 + 7846 176 167 2000 1 + 7847 1999 1989 2010 1996 + 7848 872 873 834 835 + 7849 2052 2053 2054 2092 + 7850 369 3877 257 260 + 7851 3800 3793 3795 3794 + 7852 780 2668 2644 2788 + 7853 3801 3876 3874 368 + 7854 3793 3803 359 3794 + 7855 369 260 367 259 + 7856 3415 3426 3381 3425 + 7857 500 560 571 492 + 7858 508 490 509 3425 + 7859 3426 3378 3430 3425 + 7860 3378 3426 3430 3376 + 7861 560 3442 3373 492 + 7862 3373 3442 500 492 + 7863 1807 1808 1812 124 + 7864 3367 490 3364 494 + 7865 3364 490 509 494 + 7866 523 525 509 493 + 7867 523 509 494 493 + 7868 2198 2195 2162 2161 + 7869 1526 1525 1468 1462 + 7870 1783 1795 1791 1792 + 7871 115 137 1950 103 + 7872 417 104 2199 116 + 7873 2159 2160 417 1526 + 7874 2160 2159 2150 1526 + 7875 1526 2159 1520 1468 + 7876 2160 2150 104 1526 + 7877 1472 474 471 475 + 7878 1528 1526 1525 1527 + 7879 1466 1529 1520 409 + 7880 2150 1526 1520 409 + 7881 2150 104 1526 409 + 7882 1529 2150 1520 409 + 7883 2150 1529 104 409 + 7884 414 1541 413 1523 + 7885 1541 1529 1521 1523 + 7886 1529 1541 414 1523 + 7887 1522 1532 1521 1537 + 7888 1541 413 1523 1537 + 7889 1521 1541 1523 1537 + 7890 1522 1521 1523 1537 + 7891 413 1522 1523 1537 + 7892 75 1544 400 412 + 7893 2170 2166 1540 2165 + 7894 1532 1524 1538 412 + 7895 407 3560 406 403 + 7896 1524 1544 1538 412 + 7897 2163 2195 2196 2199 + 7898 3560 1534 1543 3544 + 7899 1452 2186 2181 2184 + 7900 3150 3164 3136 3166 + 7901 2170 76 2166 2165 + 7902 1533 1532 1531 1538 + 7903 1543 3560 3544 1536 + 7904 2156 1541 2150 1539 + 7905 3136 3028 3144 3166 + 7906 3560 1543 1844 3561 + 7907 1544 1533 2173 400 + 7908 1533 2170 2173 400 + 7909 2173 2170 2167 400 + 7910 1460 1463 1461 1451 + 7911 500 3382 499 3435 + 7912 1544 1531 1524 1538 + 7913 2129 76 2132 403 + 7914 1532 1542 1524 412 + 7915 3382 3412 499 3435 + 7916 508 489 3381 499 + 7917 3433 3423 3436 499 + 7918 560 500 501 499 + 7919 3412 3436 507 499 + 7920 3656 3412 3436 3413 + 7921 3433 3423 3431 3435 + 7922 3416 3412 3382 3435 + 7923 3431 489 500 3435 + 7924 3382 3412 507 499 + 7925 3412 3433 3436 499 + 7926 500 489 501 499 + 7927 3364 3430 509 3429 + 7928 3423 560 501 499 + 7929 3423 501 3436 499 + 7930 486 1449 485 1446 + 7931 485 1449 1470 1446 + 7932 501 555 507 499 + 7933 555 501 507 3407 + 7934 501 555 558 3407 + 7935 3423 3433 560 499 + 7936 500 489 3382 3435 + 7937 3412 3433 499 3435 + 7938 3442 3431 500 3435 + 7939 3442 3433 3431 3435 + 7940 560 500 499 3435 + 7941 3433 560 499 3435 + 7942 3433 3442 560 3435 + 7943 560 3442 500 3435 + 7944 410 1468 1527 1465 + 7945 417 410 1527 1465 + 7946 1467 1460 1461 1462 + 7947 1460 1467 1464 1462 + 7948 1525 1526 1468 1527 + 7949 417 2159 1526 1527 + 7950 1526 2159 1468 1527 + 7951 2205 1467 1468 1462 + 7952 1467 2205 1464 1462 + 7953 1525 1460 1465 1462 + 7954 1460 1464 1465 1462 + 7955 1468 1525 1465 1462 + 7956 2159 2205 1468 1462 + 7957 410 1468 1465 1462 + 7958 1464 410 1465 1462 + 7959 2202 2195 2199 117 + 7960 410 2159 1468 1462 + 7961 2203 2195 107 2202 + 7962 135 2203 107 2202 + 7963 2188 2189 2116 2115 + 7964 1461 1472 1469 1451 + 7965 1457 1449 1458 1456 + 7966 2180 191 2190 192 + 7967 1449 485 1458 1456 + 7968 1486 1489 1487 1493 + 7969 1463 1472 1461 1451 + 7970 1447 1449 1457 1456 + 7971 2178 2180 2190 192 + 7972 1814 132 1815 120 + 7973 486 485 476 470 + 7974 475 476 473 1471 + 7975 1820 1814 1816 120 + 7976 91 476 472 468 + 7977 91 2180 191 2182 + 7978 1814 1815 1816 120 + 7979 1814 1820 109 120 + 7980 3812 3877 3844 3838 + 7981 478 479 483 477 + 7982 1459 476 1450 1471 + 7983 98 91 468 191 + 7984 1448 486 476 470 + 7985 1470 485 1446 470 + 7986 485 486 1446 470 + 7987 476 1470 1446 470 + 7988 1448 476 1446 470 + 7989 486 1448 1446 470 + 7990 2189 2179 193 2115 + 7991 2115 2189 2190 192 + 7992 475 1463 1471 1451 + 7993 1448 1469 1471 1451 + 7994 1469 1459 1450 1471 + 7995 1488 483 477 2184 + 7996 476 1459 473 1471 + 7997 1472 1459 1469 1451 + 7998 1459 1472 475 1451 + 7999 1459 475 473 1451 + 8000 473 475 1471 1451 + 8001 1459 473 1471 1451 + 8002 1488 1489 1453 2184 + 8003 521 531 522 524 + 8004 1492 1477 2121 1493 + 8005 3361 523 510 524 + 8006 2116 156 157 2117 + 8007 1487 1489 2121 1493 + 8008 1477 1478 2121 1493 + 8009 3590 3587 3585 444 + 8010 3600 3589 3585 3586 + 8011 479 1488 1490 1453 + 8012 1488 479 483 1453 + 8013 1485 479 1490 1453 + 8014 479 1485 483 1453 + 8015 1489 483 1487 1453 + 8016 1488 1489 1487 1453 + 8017 2112 1489 2122 2117 + 8018 193 156 484 157 + 8019 156 152 2116 157 + 8020 479 1488 483 477 + 8021 1486 175 2121 1493 + 8022 449 459 175 1493 + 8023 1486 449 175 1493 + 8024 1492 1495 1493 448 + 8025 459 1492 1493 448 + 8026 1473 454 461 460 + 8027 463 449 1486 1496 + 8028 1495 1486 1493 1496 + 8029 1495 1493 448 1496 + 8030 1486 449 1493 1496 + 8031 459 449 448 1496 + 8032 449 459 1493 1496 + 8033 1493 459 448 1496 + 8034 3394 3513 515 514 + 8035 1454 1457 1455 1452 + 8036 478 1454 1455 1452 + 8037 1458 1457 1454 1452 + 8038 478 1458 1454 1452 + 8039 1801 1803 1950 122 + 8040 151 2113 2119 2118 + 8041 1488 478 1452 2184 + 8042 2122 148 150 174 + 8043 2120 170 2114 2118 + 8044 151 2113 2122 2119 + 8045 2145 2146 2144 3591 + 8046 3469 3470 276 270 + 8047 286 3526 3524 281 + 8048 3526 3516 3524 281 + 8049 451 445 447 446 + 8050 442 3589 3586 452 + 8051 3590 444 452 451 + 8052 3464 3460 273 269 + 8053 272 274 3520 3447 + 8054 289 268 287 3447 + 8055 3466 3517 3462 3465 + 8056 393 389 394 392 + 8057 3526 3532 3524 3522 + 8058 3532 3526 3525 3522 + 8059 457 1494 460 462 + 8060 3589 442 3590 452 + 8061 3589 3590 444 452 + 8062 1480 3600 3601 450 + 8063 3498 519 3497 3393 + 8064 3517 3466 3467 3465 + 8065 3460 3469 276 270 + 8066 3587 451 447 446 + 8067 2367 2344 765 2366 + 8068 290 3490 289 3445 + 8069 210 209 211 224 + 8070 1886 213 214 208 + 8071 2105 13 28 2148 + 8072 452 442 451 54 + 8073 3590 3587 451 54 + 8074 1994 2039 2032 158 + 8075 2330 2361 2329 2355 + 8076 452 451 447 54 + 8077 3611 3618 3627 3616 + 8078 442 13 2143 2148 + 8079 371 3801 367 3842 + 8080 1483 1475 1494 1474 + 8081 3461 3469 270 3467 + 8082 3464 3461 270 3467 + 8083 284 3532 517 291 + 8084 1477 457 1476 462 + 8085 286 3524 3521 288 + 8086 381 379 384 2878 + 8087 286 3521 283 288 + 8088 3464 3461 3467 3523 + 8089 3521 3457 274 3522 + 8090 274 3462 3522 3520 + 8091 3478 3464 3479 3463 + 8092 3524 3521 274 3522 + 8093 3524 3517 3462 3522 + 8094 3462 3517 274 3522 + 8095 3943 2870 2868 381 + 8096 3490 289 273 271 + 8097 3464 273 3479 3463 + 8098 3462 3522 3520 3463 + 8099 289 272 273 271 + 8100 3011 3067 3009 3035 + 8101 273 3489 3448 3479 + 8102 385 382 386 3935 + 8103 442 456 452 455 + 8104 3600 442 3586 455 + 8105 3586 442 452 455 + 8106 453 456 442 455 + 8107 453 3600 456 455 + 8108 13 453 442 455 + 8109 453 13 3600 455 + 8110 3600 13 442 455 + 8111 3481 308 3474 275 + 8112 2120 169 2118 2102 + 8113 456 1479 461 450 + 8114 1479 3601 3586 450 + 8115 3601 3600 3586 450 + 8116 1479 1480 453 1484 + 8117 1473 1479 461 1484 + 8118 1479 1473 1483 1484 + 8119 454 1473 461 1484 + 8120 453 15 461 1484 + 8121 1479 453 456 1484 + 8122 456 453 461 1484 + 8123 1479 456 461 1484 + 8124 39 45 32 14 + 8125 2107 15 2109 14 + 8126 39 2110 45 173 + 8127 1477 31 2096 1494 + 8128 31 45 2096 1494 + 8129 1477 2096 1478 1494 + 8130 1478 2096 1482 1494 + 8131 1477 1478 1482 1494 + 8132 512 3509 3497 3393 + 8133 3509 3510 3507 3393 + 8134 3509 3507 3497 3393 + 8135 449 459 448 458 + 8136 457 448 1476 458 + 8137 460 457 462 458 + 8138 459 1492 448 458 + 8139 448 1492 1476 458 + 8140 3322 3820 3312 4451 + 8141 156 2186 1452 2184 + 8142 2284 2286 4484 4567 + 8143 982 3052 4573 4584 + 8144 268 289 287 273 + 8145 3474 3934 3488 279 + 8146 278 280 3487 277 + 8147 308 292 3474 277 + 8148 229 437 1499 440 + 8149 380 379 381 386 + 8150 268 273 3523 3447 + 8151 287 3523 3520 3447 + 8152 778 2356 2347 4034 + 8153 308 3474 275 277 + 8154 270 3467 268 273 + 8155 3953 3918 3910 3954 + 8156 270 3464 3467 273 + 8157 276 280 269 275 + 8158 280 273 271 269 + 8159 280 3481 3474 275 + 8160 307 3481 3470 275 + 8161 3470 3481 280 275 + 8162 3487 278 277 279 + 8163 278 292 277 279 + 8164 278 3487 3490 279 + 8165 394 382 392 386 + 8166 292 3474 277 279 + 8167 280 3487 277 279 + 8168 3487 280 3488 279 + 8169 3474 280 277 279 + 8170 280 3474 3488 279 + 8171 330 329 381 384 + 8172 3946 3919 398 382 + 8173 3946 3919 382 392 + 8174 2870 3943 3356 3935 + 8175 3918 389 398 394 + 8176 3919 3918 398 394 + 8177 3913 393 3910 394 + 8178 393 389 3910 394 + 8179 3919 3913 3910 394 + 8180 3918 3919 3910 394 + 8181 389 3918 3910 394 + 8182 382 3913 394 392 + 8183 3919 3913 382 392 + 8184 3919 3946 3953 392 + 8185 3945 3963 397 3964 + 8186 2861 331 2858 2893 + 8187 2758 2755 2807 2809 + 8188 3919 3953 3913 392 + 8189 3953 2867 3913 392 + 8190 3535 3457 3534 517 + 8191 331 329 2858 2893 + 8192 3879 395 3952 3948 + 8193 2779 922 2773 890 + 8194 2757 3236 3235 2756 + 8195 3234 3233 2767 3256 + 8196 2858 2861 2893 2859 + 8197 2858 329 2866 381 + 8198 382 3946 386 3935 + 8199 828 2757 3235 2756 + 8200 621 623 3247 626 + 8201 2891 331 2861 2893 + 8202 3946 392 386 3935 + 8203 3918 3945 389 3954 + 8204 3946 3919 3953 3947 + 8205 3946 3356 3919 3947 + 8206 392 2867 386 3947 + 8207 2867 3943 386 3947 + 8208 3915 3945 3920 3964 + 8209 3920 3945 397 3964 + 8210 3879 389 3909 3954 + 8211 3946 3953 392 3947 + 8212 3943 3356 3935 3947 + 8213 386 3943 3935 3947 + 8214 392 386 3935 3947 + 8215 3946 392 3935 3947 + 8216 3356 3946 3935 3947 + 8217 273 289 3445 3447 + 8218 3460 280 3478 3448 + 8219 273 3464 3523 3463 + 8220 280 278 3487 3448 + 8221 3489 3487 3490 3448 + 8222 3487 278 3490 3448 + 8223 280 3460 276 3448 + 8224 3490 278 271 3448 + 8225 271 278 269 3448 + 8226 3489 3490 273 3448 + 8227 273 3490 271 3448 + 8228 273 271 269 3448 + 8229 3460 273 269 3448 + 8230 280 276 269 3448 + 8231 278 280 269 3448 + 8232 276 3460 269 3448 + 8233 3968 3963 3914 3796 + 8234 3968 3914 3793 3796 + 8235 285 518 3527 282 + 8236 3533 285 3527 282 + 8237 3914 3968 3954 3972 + 8238 2741 2743 2740 2742 + 8239 3533 3495 3532 3525 + 8240 3034 3087 3020 2999 + 8241 3521 3457 3522 3520 + 8242 3510 3513 3498 520 + 8243 387 364 363 365 + 8244 3793 3803 3962 3902 + 8245 3793 360 3803 3902 + 8246 3963 3796 3962 3902 + 8247 360 3793 3796 3902 + 8248 3796 3793 3962 3902 + 8249 397 3963 3902 378 + 8250 3961 360 3796 378 + 8251 3507 518 512 3497 + 8252 3532 284 3534 3525 + 8253 3527 3507 3495 3525 + 8254 2779 2773 2793 890 + 8255 3467 3461 3465 3523 + 8256 3494 527 528 520 + 8257 3466 268 287 288 + 8258 3505 3494 3508 3397 + 8259 360 3793 3803 359 + 8260 360 3803 366 359 + 8261 3150 3139 3136 3142 + 8262 2792 2797 2793 890 + 8263 3136 3141 3137 3142 + 8264 3136 3164 3028 3166 + 8265 366 3803 3800 359 + 8266 3959 3780 376 3738 + 8267 3963 3796 378 364 + 8268 3963 3968 3914 364 + 8269 917 2808 2744 2806 + 8270 2776 2772 2771 2770 + 8271 387 3912 3960 364 + 8272 388 3968 397 364 + 8273 3963 388 397 364 + 8274 3968 3963 397 364 + 8275 3961 3779 3912 3911 + 8276 516 517 283 291 + 8277 3532 284 286 291 + 8278 285 284 286 282 + 8279 284 3533 3527 282 + 8280 284 3527 3515 282 + 8281 3510 528 3513 520 + 8282 518 3498 519 3497 + 8283 3385 3384 514 3395 + 8284 516 511 3492 513 + 8285 511 3493 3492 513 + 8286 3533 516 3492 513 + 8287 3495 3533 3492 513 + 8288 3533 285 516 513 + 8289 285 3533 3495 513 + 8290 528 527 512 520 + 8291 518 3507 3511 3497 + 8292 285 3533 516 282 + 8293 284 285 516 282 + 8294 3533 284 516 282 + 8295 527 511 513 512 + 8296 3361 521 3396 3404 + 8297 3495 285 513 512 + 8298 511 3493 513 512 + 8299 527 526 528 522 + 8300 3507 285 3495 512 + 8301 3509 3507 3495 512 + 8302 3493 3509 3495 512 + 8303 3509 3493 3496 512 + 8304 3493 511 3496 512 + 8305 3025 3017 3021 3020 + 8306 518 285 3507 512 + 8307 3493 3495 3492 512 + 8308 3492 3495 513 512 + 8309 3493 3492 513 512 + 8310 3362 3398 3399 3395 + 8311 527 3494 3496 520 + 8312 527 511 512 520 + 8313 511 527 3496 520 + 8314 528 3510 3508 520 + 8315 3494 528 3508 520 + 8316 3510 3494 3508 520 + 8317 3494 3505 3392 3397 + 8318 3383 3384 532 514 + 8319 3358 523 3361 524 + 8320 523 3358 493 524 + 8321 494 523 493 524 + 8322 523 531 510 524 + 8323 3398 531 521 532 + 8324 531 3398 3384 532 + 8325 3507 3511 3497 3393 + 8326 3398 3361 3362 532 + 8327 3398 521 3361 532 + 8328 3361 3398 3362 3363 + 8329 493 521 524 3404 + 8330 3358 493 524 3404 + 8331 371 3801 3785 368 + 8332 3361 3358 524 3404 + 8333 521 3361 524 3404 + 8334 3361 3358 3404 3359 + 8335 3396 3506 3391 3359 + 8336 3506 3396 3404 3359 + 8337 3358 3506 3404 3359 + 8338 3377 3383 3384 3399 + 8339 3384 3383 532 3399 + 8340 3398 3384 532 3399 + 8341 3362 3398 532 3399 + 8342 3377 3362 3383 3399 + 8343 3383 3362 532 3399 + 8344 532 533 515 514 + 8345 525 523 3366 3363 + 8346 3357 3358 493 3363 + 8347 3358 523 493 3363 + 8348 3366 523 510 3363 + 8349 3362 3366 510 3363 + 8350 490 509 3425 3429 + 8351 508 490 3425 3429 + 8352 3426 508 3425 3429 + 8353 506 508 3434 3429 + 8354 506 490 508 3429 + 8355 3374 490 506 3429 + 8356 3374 506 3434 3429 + 8357 525 3430 3363 3376 + 8358 3366 525 3363 3376 + 8359 3366 3378 491 3376 + 8360 525 3366 491 3376 + 8361 3430 525 491 3376 + 8362 3378 3430 491 3376 + 8363 528 531 522 533 + 8364 284 3526 3515 3525 + 8365 3494 3392 3496 3397 + 8366 526 528 522 533 + 8367 1191 4055 4314 4384 + 8368 3506 3398 3396 3390 + 8369 3507 3527 3511 3393 + 8370 3385 3394 3399 3395 + 8371 3508 3510 3394 3395 + 8372 511 512 520 519 + 8373 3496 511 520 519 + 8374 511 3496 512 519 + 8375 3509 3507 512 3497 + 8376 3384 3385 3399 3395 + 8377 3508 3394 515 3395 + 8378 285 518 519 3497 + 8379 518 285 512 3497 + 8380 512 285 519 3497 + 8381 3494 3509 3496 3393 + 8382 3494 3510 3509 3393 + 8383 3496 3509 512 3393 + 8384 3496 512 519 3393 + 8385 520 3496 519 3393 + 8386 3494 3496 520 3393 + 8387 3510 3494 520 3393 + 8388 3527 284 3515 3525 + 8389 526 522 530 3390 + 8390 522 531 530 3390 + 8391 515 3508 3395 3390 + 8392 531 522 533 3390 + 8393 3398 531 533 3390 + 8394 3535 3533 3534 3531 + 8395 533 515 3395 3390 + 8396 533 526 515 3390 + 8397 522 526 533 3390 + 8398 3398 533 3395 3390 + 8399 528 3508 515 3397 + 8400 515 3508 3390 3397 + 8401 526 528 515 3397 + 8402 526 515 3390 3397 + 8403 284 286 291 274 + 8404 3532 286 3524 3521 + 8405 290 289 272 3447 + 8406 287 268 3523 3447 + 8407 290 272 3520 3447 + 8408 287 286 274 288 + 8409 284 3532 3534 517 + 8410 3521 286 283 291 + 8411 517 3521 283 291 + 8412 272 287 273 274 + 8413 3535 3532 3521 517 + 8414 3521 3457 517 283 + 8415 518 3515 282 281 + 8416 3517 3516 3515 281 + 8417 516 3534 517 291 + 8418 2043 2039 2035 2040 + 8419 2393 2405 911 2394 + 8420 3521 3457 283 274 + 8421 3524 3462 3465 3523 + 8422 3017 3025 3010 3020 + 8423 3087 3150 3034 3020 + 8424 290 272 283 274 + 8425 3457 290 283 274 + 8426 3757 3801 3785 3730 + 8427 3532 3521 3522 3520 + 8428 3466 3517 287 274 + 8429 3517 3466 3462 274 + 8430 3462 3466 287 274 + 8431 286 283 274 288 + 8432 3943 2858 2860 2859 + 8433 3467 268 273 3523 + 8434 3464 3467 273 3523 + 8435 3462 3466 3465 3523 + 8436 3467 3466 268 3523 + 8437 3466 3467 3465 3523 + 8438 3464 3460 3478 3479 + 8439 3961 3914 3796 3771 + 8440 268 3466 287 3523 + 8441 3466 3462 287 3523 + 8442 308 3934 312 293 + 8443 3523 3462 3520 3463 + 8444 251 258 3818 259 + 8445 3521 3458 3520 3447 + 8446 3457 274 3522 3520 + 8447 292 310 312 293 + 8448 3457 290 274 3520 + 8449 290 3457 3458 3520 + 8450 287 3462 274 3520 + 8451 290 272 274 3520 + 8452 371 3734 3730 3842 + 8453 3857 3734 3842 3843 + 8454 536 1389 540 535 + 8455 3462 287 3523 3520 + 8456 308 3927 3934 293 + 8457 3478 3460 3448 3479 + 8458 3842 367 3853 370 + 8459 3460 273 3448 3479 + 8460 3489 3445 3447 3479 + 8461 3950 3949 3944 293 + 8462 3934 3927 3950 293 + 8463 3949 310 3944 293 + 8464 3489 273 3445 3479 + 8465 3445 273 3447 3479 + 8466 3877 369 257 3874 + 8467 3729 361 3785 3758 + 8468 3810 251 3857 3853 + 8469 361 371 3734 3730 + 8470 371 361 3785 3730 + 8471 361 362 3785 3758 + 8472 3837 3812 3844 3838 + 8473 258 3826 3818 3844 + 8474 3826 258 257 3844 + 8475 257 258 260 3844 + 8476 3877 257 260 3844 + 8477 3780 376 359 363 + 8478 3812 3877 369 3844 + 8479 369 3877 260 3844 + 8480 3876 3812 368 3842 + 8481 2669 2780 763 777 + 8482 373 371 367 377 + 8483 371 3734 367 377 + 8484 3837 3835 3841 3838 + 8485 3734 3733 3730 3842 + 8486 3877 257 3844 3838 + 8487 3835 3837 3839 3838 + 8488 3839 3837 257 3838 + 8489 3803 362 3802 3758 + 8490 3801 3757 3785 3758 + 8491 3801 371 3785 3842 + 8492 3785 371 3730 3842 + 8493 3801 3785 3730 3842 + 8494 361 3729 3733 3758 + 8495 3733 3729 3795 3758 + 8496 373 361 3733 3758 + 8497 3795 362 372 3758 + 8498 3733 3795 372 3758 + 8499 362 361 372 3758 + 8500 361 373 372 3758 + 8501 373 3733 372 3758 + 8502 3961 3914 3771 3972 + 8503 4174 3519 4211 4476 + 8504 3028 3144 3166 3033 + 8505 3164 3165 3148 3166 + 8506 3150 3139 3144 3166 + 8507 3139 3150 3136 3166 + 8508 3149 3009 3027 3033 + 8509 3015 3086 3014 3013 + 8510 3030 3148 3014 3013 + 8511 3938 3473 4262 4610 + 8512 3086 3030 3014 3013 + 8513 3335 2970 4431 4456 + 8514 2812 2760 3052 3242 + 8515 1666 1280 1586 4313 + 8516 2810 3238 2762 3245 + 8517 2817 3957 3999 4516 + 8518 1177 1178 3043 1176 + 8519 982 3082 3173 3053 + 8520 3454 3449 2783 3450 + 8521 4013 4329 4345 4503 + 8522 3339 2856 3342 3340 + 8523 3002 1160 1174 3044 + 8524 1271 1231 1232 4032 + 8525 1160 1175 1174 3044 + 8526 1116 1276 1705 4771 + 8527 3086 3002 1174 3044 + 8528 3075 3380 3379 3065 + 8529 1173 1165 1166 1172 + 8530 3010 3034 3020 2999 + 8531 3009 3015 3149 3014 + 8532 960 3156 4042 4043 + 8533 4148 3134 4235 4309 + 8534 3000 3018 3017 2998 + 8535 3000 3017 3025 2998 + 8536 2781 2787 2667 4307 + 8537 3018 3011 3035 2998 + 8538 3011 3074 3035 2998 + 8539 3017 3018 3010 2998 + 8540 3025 3017 3010 2998 + 8541 3011 3067 3074 2998 + 8542 4204 1244 4554 4662 + 8543 1252 1239 1152 4023 + 8544 3010 3018 3035 2998 + 8545 1392 1774 1390 4568 + 8546 3069 2798 1260 4042 + 8547 2662 2658 1031 4560 + 8548 2733 2800 4367 4391 + 8549 2812 3227 3048 3242 + 8550 3200 3346 3198 2753 + 8551 3346 2754 3198 2753 + 8552 3199 3346 3345 3347 + 8553 3314 2748 2753 3250 + 8554 3314 3346 3200 2753 + 8555 3081 3064 2784 4031 + 8556 2941 2699 2942 4183 + 8557 3230 3227 3231 3052 + 8558 2747 3230 3231 3052 + 8559 2760 2752 2812 3052 + 8560 2748 3311 3314 2753 + 8561 2660 2917 4068 4549 + 8562 1216 347 1555 4063 + 8563 2495 2494 2480 4007 + 8564 3171 3175 1258 4405 + 8565 2702 3804 3806 4458 + 8566 1251 1235 796 1277 + 8567 3188 645 2676 4374 + 8568 3106 710 3111 4305 + 8569 3370 3620 3371 3624 + 8570 702 644 726 4713 + 8571 2283 2501 4007 4326 + 8572 2504 2276 4299 4326 + 8573 3274 644 3860 4028 + 8574 2014 438 1518 4102 + 8575 3595 3608 2782 3593 + 8576 3449 3454 2781 3459 + 8577 1242 1250 3170 3124 + 8578 1585 1280 4145 4737 + 8579 4029 4251 4254 4444 + 8580 3449 2781 3603 3459 + 8581 4185 1264 4407 4716 + 8582 3125 713 792 3122 + 8583 3006 3004 4037 4387 + 8584 3620 3371 3624 3593 + 8585 2783 3937 4373 4416 + 8586 2747 3052 3245 4584 + 8587 1117 1113 1109 1738 + 8588 3274 3860 4016 4028 + 8589 345 1675 4509 4662 + 8590 3680 3644 3636 4151 + 8591 3595 3598 3602 3529 + 8592 642 640 4069 4374 + 8593 1746 1134 4469 4660 + 8594 3322 2970 3335 3340 + 8595 3830 3819 3807 4458 + 8596 3602 3598 3599 3529 + 8597 1736 1177 1725 4393 + 8598 3024 3038 4075 4418 + 8599 1505 407 3559 4018 + 8600 3169 2785 1725 3504 + 8601 3145 3031 1296 1287 + 8602 3500 3169 1725 3504 + 8603 3530 2803 4001 4175 + 8604 2871 1653 4057 4245 + 8605 1732 1771 1769 1770 + 8606 3291 3856 3863 4176 + 8607 3819 3831 3266 4176 + 8608 3092 3581 3584 3583 + 8609 3050 3051 4282 4611 + 8610 3856 3819 3863 4176 + 8611 1118 1393 1120 4589 + 8612 3312 3819 3806 4176 + 8613 1004 1023 4085 4395 + 8614 3486 3937 2783 4416 + 8615 2268 1206 4334 4567 + 8616 4089 172 4302 4638 + 8617 3582 3545 3581 3572 + 8618 1608 815 4314 4384 + 8619 3389 3081 2784 3628 + 8620 3427 3379 3594 4467 + 8621 3688 3665 3685 3689 + 8622 2853 3276 321 4002 + 8623 2782 3623 3529 3593 + 8624 4066 1133 4124 4402 + 8625 3245 3052 3259 4584 + 8626 3083 2747 3225 4584 + 8627 4234 4286 4436 4467 + 8628 1171 1266 1147 4157 + 8629 3231 2747 3052 3245 + 8630 1695 1705 1708 1709 + 8631 3545 2785 3546 3538 + 8632 1695 1708 981 1709 + 8633 2785 3545 3582 3538 + 8634 1696 1695 981 1709 + 8635 1144 4320 4352 4527 + 8636 3563 3408 3556 3562 + 8637 4012 1655 4064 4522 + 8638 3083 3582 1695 3583 + 8639 3204 4317 4576 4659 + 8640 3473 3633 3406 3088 + 8641 3408 3410 3409 3562 + 8642 1553 1546 1197 4020 + 8643 3563 3581 3092 3562 + 8644 3131 3555 3120 3584 + 8645 2684 1503 438 4153 + 8646 1697 3554 3580 3584 + 8647 3288 1249 1241 3124 + 8648 2785 3169 2682 2679 + 8649 957 1101 969 968 + 8650 1696 3083 1695 3583 + 8651 1700 1696 1695 3583 + 8652 4604 4564 4748 4767 + 8653 3044 3002 3085 3043 + 8654 3403 3491 3401 3402 + 8655 1675 1665 1664 4667 + 8656 3633 3405 3101 3406 + 8657 1264 4185 4711 4716 + 8658 3581 3562 3417 3503 + 8659 1154 1637 1635 4113 + 8660 794 1637 1154 4113 + 8661 3562 3437 3417 3503 + 8662 1279 1666 1669 4323 + 8663 3004 3003 4006 4037 + 8664 3411 3921 3439 3503 + 8665 3437 3411 3439 3503 + 8666 4039 3907 4201 4411 + 8667 3484 3483 3446 4767 + 8668 3270 3269 702 4273 + 8669 2567 2592 4363 4412 + 8670 640 632 3956 4181 + 8671 1545 611 4021 4199 + 8672 1733 1121 1765 4640 + 8673 3629 3501 3613 3614 + 8674 3491 3387 3388 3365 + 8675 3387 3491 3368 3365 + 8676 3501 3388 3614 3365 + 8677 3501 3491 3388 3365 + 8678 3491 3501 3615 3365 + 8679 3615 3501 3614 3365 + 8680 3380 3379 3066 3428 + 8681 1392 1413 1415 1774 + 8682 622 3189 643 4498 + 8683 2763 4276 4330 4483 + 8684 3679 3681 3640 3105 + 8685 706 710 3107 4305 + 8686 3681 3637 3640 3105 + 8687 3125 712 4360 4381 + 8688 2942 313 2943 4587 + 8689 2789 3937 4427 4690 + 8690 2804 3588 3596 3073 + 8691 4148 3062 4472 4559 + 8692 1253 1238 1664 4108 + 8693 3285 3933 592 586 + 8694 3620 3609 3608 3624 + 8695 3620 3370 3369 3624 + 8696 3663 3665 3680 3636 + 8697 321 3808 4273 4426 + 8698 3881 633 3956 4181 + 8699 1247 1682 4004 4536 + 8700 1547 1546 1545 4199 + 8701 2782 3608 3624 3593 + 8702 1602 1591 739 4441 + 8703 1403 1133 4066 4402 + 8704 4385 3485 4689 4735 + 8705 3623 2804 3596 3073 + 8706 2804 3623 3080 3073 + 8707 3080 3623 3596 3073 + 8708 2790 3588 3080 3594 + 8709 3380 3622 3428 3594 + 8710 3622 3380 3080 3594 + 8711 3138 3134 4127 4235 + 8712 960 1123 4043 4725 + 8713 3681 3679 3680 3105 + 8714 3637 3681 3680 3105 + 8715 3679 3637 3680 3105 + 8716 3077 2784 4588 4646 + 8717 1170 1158 1157 4203 + 8718 3106 3536 3655 3109 + 8719 633 632 640 4181 + 8720 2886 2888 4045 4106 + 8721 3686 3690 3688 3685 + 8722 3638 3536 3679 3659 + 8723 3661 3638 3647 3659 + 8724 3104 3661 3536 3659 + 8725 3638 3104 3536 3659 + 8726 3104 3638 3661 3659 + 8727 3679 3648 3647 3650 + 8728 3665 3688 3685 3636 + 8729 3679 3647 3659 3650 + 8730 313 3276 321 2828 + 8731 3659 3679 3650 3689 + 8732 1764 1580 1581 4152 + 8733 702 3852 703 4581 + 8734 862 3192 863 4248 + 8735 2785 3545 3546 4722 + 8736 1321 1311 339 4164 + 8737 1560 1219 1561 4164 + 8738 3158 3102 632 4181 + 8739 1558 1559 1224 4188 + 8740 3706 3644 3101 3705 + 8741 3706 3644 3705 3717 + 8742 3699 3701 3646 3717 + 8743 3646 3701 3705 3717 + 8744 3701 3706 3705 3717 + 8745 3706 3701 3699 3717 + 8746 3500 3169 3504 2679 + 8747 3077 3169 2784 2679 + 8748 3555 3563 3584 3091 + 8749 3131 3555 3584 3091 + 8750 3706 3644 3717 3091 + 8751 3644 3706 3101 3091 + 8752 3101 3706 3717 3091 + 8753 3917 4039 4054 4411 + 8754 3405 3556 3101 3091 + 8755 3408 3405 3101 3091 + 8756 3405 3408 3556 3091 + 8757 1705 4226 4389 4526 + 8758 3131 3716 3555 3091 + 8759 1764 1581 1763 4152 + 8760 1847 1851 17 4192 + 8761 3446 2667 4371 4386 + 8762 4048 4155 4422 4526 + 8763 3634 3644 3101 3091 + 8764 1666 1760 1763 4152 + 8765 3131 3681 3716 3091 + 8766 3681 3131 3644 3091 + 8767 1175 3044 3085 3043 + 8768 3078 3084 3085 3043 + 8769 3033 3149 3035 3020 + 8770 3169 3500 2784 2679 + 8771 3486 3937 3092 2783 + 8772 3486 3454 2783 3450 + 8773 3087 3140 3137 3026 + 8774 1178 3043 1176 1120 + 8775 3604 3606 3073 2786 + 8776 3077 3459 3529 2786 + 8777 3454 3449 2781 2783 + 8778 4013 4122 4329 4503 + 8779 3321 2852 2943 4502 + 8780 2853 3805 313 4502 + 8781 645 3882 2676 4374 + 8782 815 1192 1139 4026 + 8783 3486 3485 3454 3450 + 8784 980 982 4029 4252 + 8785 3485 3486 2783 3450 + 8786 2789 3937 2783 2682 + 8787 3937 2789 3092 2682 + 8788 644 3274 4016 4028 + 8789 1235 713 3159 3124 + 8790 3328 3557 466 464 + 8791 1146 1254 4403 4635 + 8792 1492 459 1493 4141 + 8793 2783 3923 2682 2679 + 8794 4179 4013 4207 4482 + 8795 1161 1169 4193 4301 + 8796 979 1122 1028 4029 + 8797 710 3112 3111 4305 + 8798 3459 3602 3529 2786 + 8799 3602 3604 3529 2786 + 8800 2804 3604 3073 2786 + 8801 3080 2804 3073 2786 + 8802 1133 1401 4124 4402 + 8803 3623 3604 2804 2786 + 8804 3623 2804 3080 2786 + 8805 3623 3080 2782 2786 + 8806 2782 3080 3529 2786 + 8807 3623 2782 3529 2786 + 8808 3080 3045 3044 3078 + 8809 1178 1175 1147 4601 + 8810 1094 1097 1096 4207 + 8811 2782 3077 3078 3043 + 8812 3043 1175 1121 1120 + 8813 3077 3080 2782 3078 + 8814 3170 3169 1235 1267 + 8815 3380 3044 2782 3078 + 8816 3080 3380 2782 3078 + 8817 3380 3080 3044 3078 + 8818 3011 3018 3071 2998 + 8819 3149 3018 3020 3019 + 8820 3681 3679 3640 4310 + 8821 4403 4098 4527 4669 + 8822 4184 1031 4281 4702 + 8823 3015 3071 3001 2995 + 8824 3468 3928 3481 3476 + 8825 3557 3558 3328 2706 + 8826 2786 3602 4304 4600 + 8827 1700 1695 1705 4757 + 8828 1265 4251 4714 4728 + 8829 4121 4047 4635 4711 + 8830 3878 3781 3281 3740 + 8831 713 3120 3117 3118 + 8832 713 3699 3120 3118 + 8833 3119 3699 3700 3118 + 8834 4006 4037 4075 4387 + 8835 3232 2802 4118 4387 + 8836 3039 3153 3070 4495 + 8837 2661 639 725 4281 + 8838 3068 3004 3006 4387 + 8839 1775 1418 1143 4287 + 8840 3119 3110 3282 3118 + 8841 1476 457 458 4141 + 8842 2840 2696 2971 590 + 8843 1492 1477 1476 4141 + 8844 705 3279 711 575 + 8845 1675 1238 1677 4108 + 8846 295 705 575 574 + 8847 4427 3472 4644 4706 + 8848 4077 2816 4550 4645 + 8849 3878 3787 3880 3788 + 8850 3312 3296 3295 3741 + 8851 2696 3336 3335 2839 + 8852 2266 583 2288 4334 + 8853 201 197 1035 4702 + 8854 2016 2013 2031 4439 + 8855 3279 3295 3782 3741 + 8856 295 705 712 575 + 8857 3604 3606 2786 4304 + 8858 2822 303 2813 4619 + 8859 3878 3880 3278 3900 + 8860 3819 3831 3301 3861 + 8861 3267 3831 3833 3861 + 8862 3819 3833 3807 3861 + 8863 575 705 576 3108 + 8864 2853 313 321 2828 + 8865 2845 3276 2943 2828 + 8866 2930 2896 2901 2929 + 8867 3055 4205 4443 4450 + 8868 2506 2493 2488 4007 + 8869 750 751 4169 4488 + 8870 591 295 575 574 + 8871 1513 1515 1519 4087 + 8872 429 1515 430 4087 + 8873 3264 726 3786 4713 + 8874 4498 4636 4685 4759 + 8875 3957 3991 4550 4619 + 8876 3335 2696 3323 2690 + 8877 4121 1265 4131 4711 + 8878 3878 3278 3740 3900 + 8879 3880 3878 3788 3900 + 8880 303 2822 4516 4619 + 8881 2822 2813 4516 4619 + 8882 3472 3483 3484 4644 + 8883 4647 1407 4660 4715 + 8884 3281 3781 3782 3740 + 8885 1596 1624 4341 4620 + 8886 3988 2813 4550 4619 + 8887 2817 3991 3957 4619 + 8888 4047 4320 4403 4716 + 8889 1064 1073 1229 4347 + 8890 2840 2696 3335 2839 + 8891 3787 2840 3880 3788 + 8892 3901 3880 3788 3900 + 8893 4178 4377 4527 4669 + 8894 3336 2970 3335 2839 + 8895 3278 3782 3740 3788 + 8896 3278 3279 3782 3788 + 8897 3782 3279 3741 3788 + 8898 3279 3278 3740 3788 + 8899 1735 1725 1734 4727 + 8900 3322 3342 2855 4753 + 8901 1735 1734 4098 4727 + 8902 295 2971 574 590 + 8903 3819 3863 3312 2855 + 8904 2886 2892 2901 2703 + 8905 2692 2718 2725 2717 + 8906 304 2816 300 4219 + 8907 318 705 575 321 + 8908 306 3928 3889 305 + 8909 3901 591 575 576 + 8910 4391 3200 4475 4539 + 8911 3896 3926 2717 2716 + 8912 3926 3896 3929 2716 + 8913 3342 2854 2855 4754 + 8914 2479 2480 53 4007 + 8915 2506 2483 53 4007 + 8916 3304 3314 3242 3308 + 8917 4013 4327 4482 4503 + 8918 2501 2489 2506 4007 + 8919 1234 1257 1273 1233 + 8920 3336 2695 4344 4473 + 8921 1717 1675 4204 4509 + 8922 16 2497 1851 4280 + 8923 2852 2884 3342 2704 + 8924 3321 2852 3342 2704 + 8925 401 3549 3543 4094 + 8926 2500 1842 4010 4280 + 8927 2497 16 2499 4280 + 8928 3202 3182 3289 4074 + 8929 2852 2702 2704 2943 + 8930 4209 1316 4285 4319 + 8931 2697 2886 2929 2703 + 8932 1245 1688 4204 4222 + 8933 1036 1054 1035 4359 + 8934 918 2758 2744 2807 + 8935 2892 2902 2901 2703 + 8936 3887 3908 4368 4462 + 8937 4223 1741 4768 4772 + 8938 3887 2724 3327 3893 + 8939 3115 3113 3114 4305 + 8940 2696 3323 2690 3313 + 8941 3228 2727 2802 4548 + 8942 1073 1185 4253 4424 + 8943 465 3557 3551 464 + 8944 3313 3335 576 590 + 8945 308 307 3889 305 + 8946 3646 4101 4128 4140 + 8947 3887 295 2724 3893 + 8948 4397 963 4654 4677 + 8949 3186 3185 3182 4074 + 8950 3805 3312 576 313 + 8951 2704 2699 2942 2943 + 8952 2696 3335 3323 3313 + 8953 1252 1152 1154 4023 + 8954 2016 201 2013 4439 + 8955 2267 2310 2288 4334 + 8956 3939 3330 3329 3317 + 8957 2639 2642 721 2622 + 8958 1176 1117 1109 4487 + 8959 821 794 1637 1647 + 8960 2268 2310 2267 4334 + 8961 3933 3285 592 712 + 8962 1155 1252 1154 4023 + 8963 3558 3332 3328 2994 + 8964 3452 3455 3597 4267 + 8965 469 3553 585 586 + 8966 3330 3887 3329 3317 + 8967 2872 2871 1661 2873 + 8968 793 1632 1654 1639 + 8969 1719 3069 1260 4042 + 8970 1192 2135 1188 813 + 8971 4106 2888 4210 4328 + 8972 1192 793 816 56 + 8973 793 2135 1632 813 + 8974 1176 1113 1110 1109 + 8975 3286 3287 1656 4357 + 8976 3052 3050 4127 4136 + 8977 1583 1595 737 4058 + 8978 1659 4035 4292 4357 + 8979 3468 308 3928 3476 + 8980 793 3933 592 712 + 8981 3887 2971 2988 3327 + 8982 2971 3887 2724 3327 + 8983 3328 3553 793 3894 + 8984 3924 3481 3475 3476 + 8985 1547 1197 4199 4209 + 8986 3318 3328 3329 3317 + 8987 792 795 712 1140 + 8988 1621 1221 1043 1630 + 8989 467 793 592 712 + 8990 793 3886 3931 3894 + 8991 1317 1198 1197 4209 + 8992 3318 3320 3319 3317 + 8993 3332 3939 3329 3317 + 8994 3887 3329 3317 3208 + 8995 3887 295 3888 3208 + 8996 3329 3328 2994 3317 + 8997 1646 1605 1041 4083 + 8998 1117 4171 4311 4487 + 8999 2279 2282 4015 4120 + 9000 3554 1703 1259 4405 + 9001 3319 3887 3317 3208 + 9002 3898 3319 3317 3208 + 9003 297 3887 3883 3208 + 9004 3883 3898 3888 3208 + 9005 297 3883 3888 3208 + 9006 3883 3887 3319 3208 + 9007 3898 3883 3319 3208 + 9008 309 298 3897 3890 + 9009 3898 3892 3897 3890 + 9010 3888 3898 3897 3890 + 9011 3898 3888 3208 3890 + 9012 466 465 793 467 + 9013 295 297 592 712 + 9014 808 1608 165 815 + 9015 3110 3967 712 711 + 9016 3699 710 3700 711 + 9017 3553 3328 793 467 + 9018 3328 3553 466 467 + 9019 793 3328 2135 467 + 9020 1192 793 813 816 + 9021 3328 466 2135 467 + 9022 3699 3700 3118 711 + 9023 295 297 3908 592 + 9024 3098 3102 3264 4123 + 9025 3110 3967 3123 712 + 9026 297 584 3908 592 + 9027 3090 3101 4293 4356 + 9028 710 713 728 711 + 9029 3501 3528 3613 4583 + 9030 3093 710 3645 711 + 9031 3700 710 3703 711 + 9032 229 1513 1519 4087 + 9033 713 3123 712 711 + 9034 1112 1403 4066 4402 + 9035 2505 2279 4015 4120 + 9036 792 795 793 712 + 9037 3125 713 3122 712 + 9038 3965 3967 3966 712 + 9039 3965 3966 592 712 + 9040 1266 4049 4098 4364 + 9041 1112 1391 1403 4402 + 9042 3967 295 3966 712 + 9043 3966 295 592 712 + 9044 3117 3110 3123 711 + 9045 713 3117 3123 711 + 9046 3967 295 712 575 + 9047 712 705 711 575 + 9048 3967 712 711 575 + 9049 3967 3901 295 575 + 9050 295 3901 591 575 + 9051 3602 3604 2786 4304 + 9052 3047 4075 4237 4548 + 9053 3276 313 2943 2828 + 9054 3312 3321 3315 313 + 9055 2838 3901 3788 576 + 9056 3901 3279 3900 576 + 9057 3279 3788 3900 576 + 9058 3279 3741 3788 576 + 9059 3295 3312 3741 576 + 9060 3312 2838 3741 576 + 9061 3741 2838 3788 576 + 9062 1477 31 462 4141 + 9063 318 2853 577 313 + 9064 3312 3094 705 576 + 9065 3991 2817 4516 4619 + 9066 2813 3988 4516 4619 + 9067 2838 3312 3335 576 + 9068 3093 3646 4128 4140 + 9069 3967 3901 575 576 + 9070 3901 3967 3279 576 + 9071 3279 3967 575 576 + 9072 3279 3295 3741 576 + 9073 3296 3312 3295 576 + 9074 3991 2818 4550 4619 + 9075 3335 3312 3323 576 + 9076 3323 3312 3313 576 + 9077 3335 3323 3313 576 + 9078 3054 3062 4148 4309 + 9079 1415 1413 1143 4287 + 9080 3188 645 2675 2676 + 9081 3943 2870 3356 2859 + 9082 3928 309 312 305 + 9083 2699 2887 2704 4038 + 9084 4104 4126 4280 4353 + 9085 577 318 575 321 + 9086 3153 3152 3070 4495 + 9087 1639 1654 4438 4684 + 9088 607 644 608 702 + 9089 3101 4293 4356 4427 + 9090 2853 318 577 321 + 9091 2853 577 3805 321 + 9092 3276 318 313 321 + 9093 318 2853 313 321 + 9094 3348 3215 580 649 + 9095 579 607 580 649 + 9096 2900 3344 334 321 + 9097 320 334 327 333 + 9098 3924 308 3481 3476 + 9099 3481 308 3891 3476 + 9100 308 3468 3891 3476 + 9101 309 306 298 305 + 9102 2864 335 2881 2883 + 9103 3926 3949 3941 3944 + 9104 3941 3949 309 3944 + 9105 3926 3941 3925 3944 + 9106 3925 3941 309 3944 + 9107 3888 309 298 294 + 9108 299 3888 298 294 + 9109 310 311 299 294 + 9110 309 310 312 294 + 9111 299 311 3888 294 + 9112 311 310 3944 294 + 9113 3888 3925 309 294 + 9114 3926 311 3944 294 + 9115 3949 309 3944 294 + 9116 310 3949 3944 294 + 9117 3949 310 309 294 + 9118 311 3926 3925 294 + 9119 3929 3925 3888 294 + 9120 311 3929 3888 294 + 9121 3929 311 3925 294 + 9122 3925 3926 3944 294 + 9123 309 3925 3944 294 + 9124 3481 3924 3475 3477 + 9125 3924 3481 3474 3477 + 9126 280 3475 3478 3477 + 9127 280 3487 3488 3477 + 9128 280 3478 3448 3477 + 9129 3487 280 3448 3477 + 9130 2857 2861 2858 2859 + 9131 280 3481 3475 3477 + 9132 3481 280 3474 3477 + 9133 3949 3927 312 293 + 9134 3927 3949 3950 293 + 9135 335 330 384 383 + 9136 3234 3233 3256 3229 + 9137 2889 332 2900 338 + 9138 2898 2900 327 319 + 9139 321 2900 322 319 + 9140 2900 338 322 319 + 9141 465 3551 480 464 + 9142 2892 2889 2896 338 + 9143 2896 2889 2900 338 + 9144 2892 2896 2900 338 + 9145 487 445 481 3617 + 9146 2110 39 2096 2097 + 9147 2140 3589 3590 3591 + 9148 3618 3619 3590 3591 + 9149 442 3590 451 54 + 9150 3619 3587 3590 3591 + 9151 3619 3618 447 3591 + 9152 3587 3619 447 3591 + 9153 1199 2264 2286 4567 + 9154 3557 3328 466 2722 + 9155 1039 3936 1040 1044 + 9156 3328 2135 1040 1044 + 9157 1923 1913 179 1915 + 9158 3936 2984 2723 2722 + 9159 1039 3936 2723 2722 + 9160 1118 1115 1176 1110 + 9161 1741 4417 4744 4763 + 9162 1039 3936 2722 1040 + 9163 1039 2722 2706 1040 + 9164 2723 1039 2722 2706 + 9165 1694 1691 4351 4396 + 9166 330 2858 2866 2865 + 9167 2897 330 2866 2865 + 9168 330 2861 2858 2865 + 9169 330 2897 2861 2865 + 9170 3022 3200 4006 4075 + 9171 1395 1416 1766 4190 + 9172 932 934 973 4206 + 9173 3240 3239 3255 3235 + 9174 3195 862 3192 860 + 9175 381 2870 2860 2859 + 9176 2870 381 2893 2859 + 9177 2858 381 2860 2859 + 9178 381 2858 2893 2859 + 9179 330 2866 2864 2881 + 9180 330 2897 2866 2881 + 9181 639 2661 725 2880 + 9182 2864 2866 2876 2881 + 9183 2868 381 2860 2869 + 9184 2858 2865 2860 2869 + 9185 2858 2866 2865 2869 + 9186 2866 2858 381 2869 + 9187 381 2858 2860 2869 + 9188 2900 2892 338 2899 + 9189 2898 2900 338 2899 + 9190 330 2897 2881 2899 + 9191 335 330 2881 2899 + 9192 337 335 338 2899 + 9193 336 330 335 2899 + 9194 337 336 335 2899 + 9195 2892 2891 337 2899 + 9196 337 2891 336 2899 + 9197 2891 2897 330 2899 + 9198 336 2891 330 2899 + 9199 2897 2891 2857 2899 + 9200 2892 2897 2857 2899 + 9201 2891 2892 2857 2899 + 9202 806 807 789 811 + 9203 3906 3577 4094 4490 + 9204 2131 1844 3559 4225 + 9205 2681 2684 405 1498 + 9206 1023 1004 1020 4609 + 9207 3406 3408 4427 4435 + 9208 3773 644 642 4069 + 9209 2680 2684 404 1506 + 9210 2661 1652 1658 1662 + 9211 3194 3263 3192 861 + 9212 3773 3958 4028 4069 + 9213 1500 1497 1499 439 + 9214 437 1501 1499 439 + 9215 650 3343 3189 2911 + 9216 2132 2169 1843 4375 + 9217 2015 219 2021 2042 + 9218 2680 433 1499 439 + 9219 3187 2671 641 863 + 9220 3995 3193 645 2683 + 9221 2680 1500 1497 1499 + 9222 3151 4008 4391 4475 + 9223 632 725 726 3283 + 9224 3246 3249 621 3248 + 9225 433 2680 1497 439 + 9226 1497 2680 1499 439 + 9227 3714 3554 3715 4641 + 9228 3581 3092 3562 4435 + 9229 2660 197 2662 434 + 9230 3091 3101 4151 4530 + 9231 612 2576 4412 4428 + 9232 3556 3101 4338 4530 + 9233 3716 3555 3091 4530 + 9234 3016 1306 4041 4135 + 9235 197 2658 2662 434 + 9236 2684 405 404 1506 + 9237 3204 2643 3207 3205 + 9238 1315 1319 339 4133 + 9239 3193 2671 645 2683 + 9240 1507 2681 3205 1506 + 9241 725 639 632 726 + 9242 2646 2645 2643 2678 + 9243 631 639 2919 2918 + 9244 3271 725 3283 1137 + 9245 639 631 633 632 + 9246 3955 632 642 3774 + 9247 2659 2677 2920 2918 + 9248 726 632 3774 3768 + 9249 3598 3595 4401 4408 + 9250 3277 3270 3275 3302 + 9251 3274 2910 2911 2909 + 9252 2671 3187 641 645 + 9253 3197 641 862 643 + 9254 2671 641 3193 645 + 9255 3247 3246 3248 3176 + 9256 3204 2645 3188 2676 + 9257 3640 3112 3105 3111 + 9258 3189 3246 2912 3176 + 9259 3985 3995 641 645 + 9260 641 3995 3193 645 + 9261 3158 3157 632 726 + 9262 579 607 333 580 + 9263 3233 3252 3253 3255 + 9264 320 334 321 327 + 9265 334 2900 321 327 + 9266 2900 2896 2899 2928 + 9267 324 3212 4316 4595 + 9268 3252 3233 2767 3255 + 9269 2886 2900 2885 2928 + 9270 3344 2898 327 328 + 9271 2881 335 2899 2883 + 9272 2886 2885 2929 2928 + 9273 334 3344 327 328 + 9274 1726 1731 1768 4515 + 9275 2671 3187 645 2675 + 9276 862 3192 861 863 + 9277 3191 3194 3192 861 + 9278 3194 3191 3263 861 + 9279 218 2043 2042 4634 + 9280 2935 807 806 811 + 9281 641 3197 862 863 + 9282 3197 641 3193 863 + 9283 4037 3024 4075 4566 + 9284 643 862 2907 628 + 9285 2935 2694 2689 811 + 9286 1503 438 1502 440 + 9287 3254 3263 871 860 + 9288 3233 2767 3255 3235 + 9289 2701 788 789 811 + 9290 3246 3189 2912 643 + 9291 648 3246 623 643 + 9292 622 648 623 643 + 9293 2886 2887 4162 4512 + 9294 3196 3247 2913 2907 + 9295 643 641 2913 2907 + 9296 3270 607 702 4572 + 9297 2912 3189 2908 643 + 9298 648 622 3189 643 + 9299 3246 648 3189 643 + 9300 2903 2912 2908 643 + 9301 641 2903 2908 643 + 9302 623 3246 3247 2913 + 9303 862 3197 643 2907 + 9304 2907 643 628 626 + 9305 858 621 628 626 + 9306 3348 3274 2826 3761 + 9307 2903 2904 2913 2905 + 9308 2903 3995 2904 2905 + 9309 641 2913 2907 2905 + 9310 641 3197 3193 2905 + 9311 3995 641 3193 2905 + 9312 3995 2903 641 2905 + 9313 641 2903 2913 2905 + 9314 2896 2897 2899 2928 + 9315 702 607 4713 4720 + 9316 3189 3177 3274 2911 + 9317 2645 2646 3997 2676 + 9318 3773 3955 642 3774 + 9319 3309 3270 3302 3761 + 9320 3882 3885 3997 2676 + 9321 3102 3769 726 3768 + 9322 3885 3882 640 2676 + 9323 3204 3188 2675 2676 + 9324 1265 4275 4444 4728 + 9325 1280 1585 4571 4737 + 9326 3343 648 3189 3176 + 9327 2899 2883 326 2882 + 9328 326 2883 328 2882 + 9329 338 2899 326 2882 + 9330 2898 338 319 2882 + 9331 2898 319 328 2882 + 9332 319 326 328 2882 + 9333 319 338 326 2882 + 9334 2886 2929 2703 2928 + 9335 2888 2896 2900 2928 + 9336 2886 2888 2900 2928 + 9337 2888 2886 2896 2928 + 9338 2896 2901 2929 2928 + 9339 2929 2901 2703 2928 + 9340 2896 2886 2901 2928 + 9341 2901 2886 2703 2928 + 9342 334 320 321 2827 + 9343 3344 334 321 2827 + 9344 644 607 608 649 + 9345 3344 334 2827 580 + 9346 3348 3344 2827 580 + 9347 320 334 333 580 + 9348 334 320 2827 580 + 9349 607 320 333 580 + 9350 3312 3805 3806 4073 + 9351 3276 3348 2827 580 + 9352 320 607 321 580 + 9353 3348 607 3762 580 + 9354 321 3276 2827 580 + 9355 320 321 2827 580 + 9356 3315 3321 2704 2943 + 9357 607 3348 3270 580 + 9358 1920 1919 4520 4578 + 9359 1030 3173 1029 4694 + 9360 704 3100 702 703 + 9361 814 1320 1561 339 + 9362 632 644 3774 3768 + 9363 1411 1413 1415 4287 + 9364 704 3100 3269 702 + 9365 1020 1019 4085 4729 + 9366 2280 1197 4020 4182 + 9367 2592 2588 4480 4585 + 9368 3852 607 3762 702 + 9369 3213 323 2825 4594 + 9370 2588 2592 4412 4585 + 9371 4527 4320 4649 4774 + 9372 3270 607 580 702 + 9373 3274 2826 2829 3860 + 9374 2699 2702 2698 2941 + 9375 702 644 703 726 + 9376 3102 3852 703 726 + 9377 3852 3102 3264 726 + 9378 3116 3160 3126 1273 + 9379 3894 586 4491 4730 + 9380 4251 4714 4728 4729 + 9381 3852 702 703 726 + 9382 2849 3221 2846 4448 + 9383 2785 3546 3504 4723 + 9384 3207 2643 3188 2675 + 9385 3161 2863 3157 3283 + 9386 3191 3187 2674 2675 + 9387 3187 3191 3207 2675 + 9388 3207 3191 2674 2675 + 9389 2643 3204 3188 2675 + 9390 645 2671 2675 2676 + 9391 2671 645 2683 2676 + 9392 645 3985 2683 2676 + 9393 2646 2677 2678 2920 + 9394 1844 2169 2132 4375 + 9395 2643 2646 2678 2920 + 9396 939 966 958 936 + 9397 639 631 2919 2895 + 9398 2677 2643 2678 2920 + 9399 4517 4499 4571 4623 + 9400 4417 4499 4517 4623 + 9401 1726 1723 1731 4515 + 9402 725 2890 2894 1137 + 9403 632 3955 3956 3774 + 9404 3204 2645 2643 3881 + 9405 3042 3054 4148 4318 + 9406 3204 633 2645 3881 + 9407 3054 3042 4237 4318 + 9408 3205 2643 2677 4549 + 9409 2645 633 640 3881 + 9410 2645 640 3884 3881 + 9411 2645 2646 2643 3881 + 9412 2643 2646 2920 3881 + 9413 1240 1675 1668 4667 + 9414 2643 3204 2920 2918 + 9415 2816 3991 2818 4550 + 9416 2677 2643 2920 2918 + 9417 3054 4127 4148 4318 + 9418 4148 4127 4235 4318 + 9419 725 1657 1137 1658 + 9420 2919 639 2895 2880 + 9421 2890 2863 2862 2894 + 9422 3099 3115 3096 4577 + 9423 3047 3048 2753 4318 + 9424 639 725 2895 2880 + 9425 1650 725 752 84 + 9426 3546 2785 4722 4723 + 9427 3157 632 726 3271 + 9428 704 3128 3115 4577 + 9429 3277 3309 3270 3302 + 9430 1196 1616 1185 4182 + 9431 1313 985 1274 4115 + 9432 2280 1196 1185 4182 + 9433 1610 1607 1918 4561 + 9434 2890 725 2880 3271 + 9435 3024 3022 4006 4075 + 9436 2890 2863 2894 3271 + 9437 2826 3274 2829 3302 + 9438 632 3955 640 3956 + 9439 725 639 2895 3272 + 9440 639 725 632 3272 + 9441 1616 1071 1619 4182 + 9442 644 632 726 3768 + 9443 3274 3277 3275 3302 + 9444 3769 644 726 3768 + 9445 633 640 3881 3956 + 9446 1616 1619 1185 4182 + 9447 632 644 642 3774 + 9448 644 3773 642 3774 + 9449 3769 3767 3764 3763 + 9450 3408 3562 4427 4435 + 9451 3309 3274 2826 3302 + 9452 3309 3277 3274 3302 + 9453 3276 3338 3275 3303 + 9454 3767 3869 3764 3763 + 9455 3270 3275 3302 3303 + 9456 3277 3309 3275 3303 + 9457 3309 3277 3270 3303 + 9458 3270 3277 3275 3303 + 9459 3348 3270 580 3303 + 9460 2826 3270 3302 3303 + 9461 1413 1756 1143 4287 + 9462 3528 3518 3519 4211 + 9463 580 3270 702 3303 + 9464 1003 992 4329 4379 + 9465 2885 2929 2928 2941 + 9466 1234 1275 1312 1273 + 9467 993 991 4482 4503 + 9468 2845 3276 2942 2943 + 9469 991 4179 4482 4503 + 9470 4122 4013 4482 4503 + 9471 2702 2699 2704 2943 + 9472 3186 3202 3224 4074 + 9473 3321 2852 2704 2943 + 9474 3869 644 3768 3767 + 9475 1019 4265 4729 4762 + 9476 4265 4254 4444 4762 + 9477 4078 4029 4432 4762 + 9478 644 3773 3774 3767 + 9479 644 3774 3768 3767 + 9480 3869 3763 3868 3786 + 9481 3769 644 3868 3786 + 9482 1725 1699 4515 4670 + 9483 3215 644 3348 3860 + 9484 644 3215 3274 3860 + 9485 3274 3215 3348 3860 + 9486 1117 4459 4468 4700 + 9487 1117 1109 4511 4541 + 9488 3763 3769 3868 3786 + 9489 3264 3098 3097 3786 + 9490 1742 4269 4453 4709 + 9491 4120 2282 4296 4299 + 9492 3768 3869 3763 3868 + 9493 3769 3768 3763 3868 + 9494 3769 644 3768 3868 + 9495 3869 644 3860 3868 + 9496 644 3869 3768 3868 + 9497 644 3769 726 3786 + 9498 3769 3102 726 3786 + 9499 3102 3264 726 3786 + 9500 295 3887 4368 4462 + 9501 3937 3486 3092 4427 + 9502 3312 3805 576 3870 + 9503 2824 2969 2836 2833 + 9504 3224 3202 3184 4074 + 9505 3185 3186 3184 4074 + 9506 3807 2702 3804 3806 + 9507 3055 4186 4205 4450 + 9508 4186 3049 4205 4450 + 9509 3186 3224 3184 4074 + 9510 1414 1411 1143 4287 + 9511 3049 4230 4443 4450 + 9512 2489 2501 2503 4007 + 9513 1741 4173 4223 4269 + 9514 3055 2997 4186 4450 + 9515 4013 4130 4327 4503 + 9516 3807 3819 3861 3266 + 9517 3819 3831 3861 3266 + 9518 4152 1760 4163 4177 + 9519 1764 4152 4163 4177 + 9520 1762 1759 4163 4177 + 9521 3807 3806 3266 3870 + 9522 933 1091 1088 4206 + 9523 3805 3312 3806 3870 + 9524 3833 3807 3861 3266 + 9525 3831 3833 3861 3266 + 9526 3833 3831 3807 3266 + 9527 1759 1762 1711 4177 + 9528 933 932 973 4206 + 9529 1116 1705 1711 4177 + 9530 1705 1710 4226 4526 + 9531 3820 3094 3312 4451 + 9532 727 3128 3103 3129 + 9533 3103 3128 3127 3129 + 9534 727 3103 3127 3129 + 9535 710 3640 3105 3111 + 9536 3217 3314 3306 3307 + 9537 3128 3130 3152 3121 + 9538 3152 3130 1256 3121 + 9539 3154 3152 1256 3121 + 9540 3536 3638 3105 3111 + 9541 1722 1706 1672 4166 + 9542 3138 3172 4283 4306 + 9543 3112 3536 3105 3111 + 9544 3104 3638 3536 3111 + 9545 3106 3104 3655 3111 + 9546 3536 3106 3655 3111 + 9547 3104 3536 3655 3111 + 9548 3172 3138 4136 4306 + 9549 2282 4015 4120 4296 + 9550 786 347 4239 4537 + 9551 4127 3138 4235 4306 + 9552 1686 1237 345 4319 + 9553 3346 3314 3217 3310 + 9554 3311 3314 3346 3310 + 9555 2798 3156 3224 3070 + 9556 2872 2873 4582 4693 + 9557 4341 739 4366 4496 + 9558 2801 2794 2734 3005 + 9559 3882 3885 640 4009 + 9560 3223 2798 3224 3070 + 9561 2801 3153 2794 3005 + 9562 2480 2494 53 4007 + 9563 2794 3153 3186 3181 + 9564 3156 2798 3155 3070 + 9565 3186 3153 3005 3181 + 9566 2494 2276 2493 4007 + 9567 2276 2494 2495 4007 + 9568 3153 3223 3039 3070 + 9569 3225 3257 3260 3326 + 9570 2781 2783 3450 4385 + 9571 3260 3324 3298 3325 + 9572 3298 3324 3217 3325 + 9573 3316 3290 3202 3337 + 9574 3352 3334 3217 3337 + 9575 3333 3182 3202 3337 + 9576 3182 3316 3202 3337 + 9577 3182 3352 3217 3337 + 9578 3316 3182 3217 3337 + 9579 3334 3352 3333 3337 + 9580 3333 3352 3182 3337 + 9581 3037 3186 1262 3184 + 9582 2739 3037 1262 3184 + 9583 4325 3455 4425 4574 + 9584 2494 2506 53 4007 + 9585 3037 3022 3179 3183 + 9586 3022 3037 3023 3183 + 9587 2760 2752 3251 3250 + 9588 3182 3186 3202 3183 + 9589 3202 3186 3179 3183 + 9590 3352 3182 3179 3183 + 9591 3178 3352 3179 3183 + 9592 3352 3178 3182 3183 + 9593 2346 2348 778 4034 + 9594 3093 3700 4101 4128 + 9595 2752 2760 2812 3242 + 9596 3179 3182 3217 3183 + 9597 3202 3179 3217 3183 + 9598 3182 3202 3217 3183 + 9599 3037 3023 3183 3181 + 9600 3186 3037 3183 3181 + 9601 3037 3186 3023 3181 + 9602 3333 3290 3202 3289 + 9603 1606 1574 1197 4419 + 9604 1619 2579 4182 4419 + 9605 4568 1392 4686 4739 + 9606 3186 2794 3181 2795 + 9607 2794 2734 3181 2795 + 9608 3298 3225 3326 3259 + 9609 3244 3298 3326 3259 + 9610 2810 2747 3245 3259 + 9611 2747 2810 3225 3259 + 9612 2762 2810 3245 3259 + 9613 2810 2762 3225 3259 + 9614 2748 2812 2753 3250 + 9615 836 2767 857 2751 + 9616 3298 3186 3224 3143 + 9617 4484 4143 4672 4673 + 9618 3202 3298 3224 3226 + 9619 862 2907 628 4248 + 9620 2907 3247 628 4248 + 9621 3225 3298 3326 3222 + 9622 3260 3225 3326 3222 + 9623 2284 1200 4143 4484 + 9624 3628 3372 3613 4200 + 9625 1623 2345 774 4034 + 9626 1049 2344 774 4034 + 9627 2345 1049 774 4034 + 9628 3690 3688 3684 4180 + 9629 2501 2483 2503 4007 + 9630 1374 2044 2033 1993 + 9631 2044 1374 2036 1993 + 9632 795 1140 4255 4479 + 9633 2831 2848 2837 2832 + 9634 2483 2501 2502 4007 + 9635 2971 3887 2988 4368 + 9636 631 3204 4514 4549 + 9637 2748 2752 2728 3251 + 9638 3699 3646 4101 4128 + 9639 3314 3200 3304 3143 + 9640 3179 3037 3183 3143 + 9641 3200 3046 3179 3143 + 9642 3186 3200 3179 3143 + 9643 3200 3244 3304 3143 + 9644 2356 745 2344 4034 + 9645 1036 753 1631 4359 + 9646 3046 3200 3232 3143 + 9647 3232 3200 3198 3143 + 9648 3022 3037 3179 3143 + 9649 3037 3022 3046 3143 + 9650 3046 3022 3179 3143 + 9651 2752 2760 3242 3250 + 9652 3314 3200 3217 3216 + 9653 3227 3305 3231 3048 + 9654 3305 3244 3231 3048 + 9655 996 974 1125 1261 + 9656 958 966 952 1261 + 9657 3231 3244 3259 3048 + 9658 982 3231 3259 3048 + 9659 3305 3314 3304 3216 + 9660 3314 3305 3200 3216 + 9661 1225 786 740 4231 + 9662 4068 434 4281 4549 + 9663 3199 2754 3198 3347 + 9664 3346 3199 3198 3347 + 9665 2754 3346 3198 3347 + 9666 3351 3265 3355 3353 + 9667 3199 3201 3198 3353 + 9668 3199 3354 3217 3353 + 9669 3201 3199 3217 3353 + 9670 3265 3200 3355 3353 + 9671 3200 3265 3201 3353 + 9672 3200 3201 3217 3353 + 9673 3354 3355 3217 3353 + 9674 3355 3200 3217 3353 + 9675 9 16 1847 4010 + 9676 3204 633 3881 4317 + 9677 3342 2855 4753 4754 + 9678 3408 3410 3562 4435 + 9679 3179 3350 3293 3349 + 9680 2735 3232 2749 2738 + 9681 3351 3265 3179 3349 + 9682 3265 3351 3355 3349 + 9683 3355 3351 3210 3349 + 9684 3265 3200 3179 3349 + 9685 3179 3200 3217 3349 + 9686 3352 3179 3217 3349 + 9687 3179 3352 3350 3349 + 9688 3350 3352 3217 3349 + 9689 3355 3210 3217 3349 + 9690 3265 3355 3217 3349 + 9691 3200 3265 3217 3349 + 9692 3199 3345 3217 3209 + 9693 3345 3199 3347 3209 + 9694 753 1036 4132 4359 + 9695 3354 3199 3217 3209 + 9696 1243 1246 4004 4536 + 9697 3217 3298 3325 3216 + 9698 3200 3298 3217 3216 + 9699 3298 3244 3326 3216 + 9700 3244 3298 3200 3216 + 9701 3260 3326 3325 3216 + 9702 3298 3260 3325 3216 + 9703 3260 3298 3326 3216 + 9704 3305 3304 3218 3306 + 9705 3304 3305 3216 3306 + 9706 3216 3305 3218 3306 + 9707 377 3735 3853 370 + 9708 375 3735 377 370 + 9709 3739 374 3738 375 + 9710 2670 2712 2686 2711 + 9711 1211 750 752 4100 + 9712 3292 3268 3301 4187 + 9713 3059 3055 3068 3049 + 9714 1385 551 154 552 + 9715 1237 1314 4027 4052 + 9716 2587 1064 2588 4053 + 9717 2587 2588 1062 4053 + 9718 2346 778 2347 4034 + 9719 2014 1518 1502 4238 + 9720 1392 1159 1129 4349 + 9721 2733 2801 2800 2736 + 9722 3062 3059 3049 3063 + 9723 1669 1670 4343 4562 + 9724 3212 3211 4316 4595 + 9725 4316 4070 4492 4595 + 9726 1244 1714 1691 4351 + 9727 2823 2825 2824 3220 + 9728 3977 241 240 243 + 9729 3977 3776 241 243 + 9730 302 3994 3979 243 + 9731 3213 2823 2820 2850 + 9732 2823 3213 3220 2850 + 9733 2848 2837 2843 2844 + 9734 323 2837 3221 316 + 9735 324 315 323 3221 + 9736 3213 324 323 3221 + 9737 1714 1244 4204 4351 + 9738 1714 1240 1667 4351 + 9739 3317 2994 4420 4687 + 9740 4070 2819 4492 4595 + 9741 3993 3986 2822 2813 + 9742 2976 2843 2842 2832 + 9743 2851 351 317 325 + 9744 1445 1426 504 502 + 9745 2084 1354 557 6 + 9746 2849 3221 2847 2846 + 9747 3221 2849 2847 2844 + 9748 1691 1714 1713 4351 + 9749 505 1444 2070 2048 + 9750 3096 704 4072 4577 + 9751 3213 324 3220 3212 + 9752 3204 2643 2920 4317 + 9753 2643 3204 3881 4317 + 9754 1407 4602 4660 4715 + 9755 3053 3051 4649 4656 + 9756 324 3220 3212 4595 + 9757 2849 3206 2848 2844 + 9758 348 1216 4021 4657 + 9759 2819 3206 4350 4595 + 9760 1435 1426 498 1442 + 9761 356 2960 2955 352 + 9762 1435 1433 1421 498 + 9763 2066 2065 2070 2071 + 9764 2960 2956 2955 352 + 9765 2843 2976 2842 2973 + 9766 2975 349 2841 2973 + 9767 2841 349 353 2973 + 9768 2841 2959 2842 2973 + 9769 2959 2841 353 2973 + 9770 2959 2843 2842 2973 + 9771 2843 2959 353 2973 + 9772 2951 349 356 2955 + 9773 2841 2951 2972 2955 + 9774 2972 2951 356 2955 + 9775 349 2841 353 2955 + 9776 2841 349 2951 2955 + 9777 2975 2965 2972 2974 + 9778 2851 317 2832 325 + 9779 3778 3987 3971 3974 + 9780 2841 2975 2972 2974 + 9781 2951 2841 2972 2974 + 9782 349 2975 2841 2974 + 9783 349 2841 2951 2974 + 9784 2964 2990 2961 2967 + 9785 2976 2831 2830 2833 + 9786 357 2956 350 358 + 9787 2951 349 2974 2954 + 9788 3855 3854 3858 3753 + 9789 349 2975 2974 2954 + 9790 2974 2975 2834 2954 + 9791 241 302 240 243 + 9792 349 2975 2954 2835 + 9793 2969 2851 2836 325 + 9794 353 2959 351 317 + 9795 1397 1419 2063 1440 + 9796 2959 2837 2843 317 + 9797 636 637 587 593 + 9798 2959 2843 353 351 + 9799 2843 2976 317 2832 + 9800 2976 2851 2835 351 + 9801 2851 349 2954 351 + 9802 2954 349 2835 351 + 9803 2851 2954 2835 351 + 9804 349 353 2973 351 + 9805 353 2843 2973 351 + 9806 2843 2976 2973 351 + 9807 2975 2976 2835 351 + 9808 349 2975 2835 351 + 9809 2975 349 2973 351 + 9810 2976 2975 2973 351 + 9811 2837 2848 2843 2832 + 9812 2851 2976 2835 2836 + 9813 1397 1396 1419 1440 + 9814 2976 2851 2832 2836 + 9815 2969 2851 2835 2836 + 9816 2824 2969 2835 2836 + 9817 351 353 317 316 + 9818 2851 349 351 325 + 9819 323 2825 2830 2833 + 9820 2837 2831 2832 316 + 9821 317 2837 2832 316 + 9822 2152 2163 2160 2153 + 9823 323 2831 2844 316 + 9824 2837 323 2844 316 + 9825 2831 2837 2844 316 + 9826 2160 105 2150 2153 + 9827 323 2969 2825 325 + 9828 2825 2969 2833 325 + 9829 323 2825 2833 325 + 9830 323 2831 316 325 + 9831 2831 323 2833 325 + 9832 2836 2832 2833 325 + 9833 2831 2832 316 325 + 9834 2832 2831 2833 325 + 9835 2989 355 2956 357 + 9836 2989 2960 2956 2985 + 9837 355 2989 2956 2985 + 9838 2978 791 2924 2986 + 9839 2050 2049 2057 1434 + 9840 2965 2972 2963 2961 + 9841 2992 2914 2982 2926 + 9842 2966 2965 2974 2952 + 9843 356 2963 352 358 + 9844 554 563 1355 1358 + 9845 2977 2956 352 358 + 9846 3790 245 249 246 + 9847 301 3973 3981 3986 + 9848 2968 2991 2916 2990 + 9849 798 791 790 802 + 9850 52 10 544 2 + 9851 799 2948 2936 2932 + 9852 340 2964 2916 2990 + 9853 354 355 2956 2985 + 9854 357 2964 2990 2961 + 9855 2977 2989 2956 2961 + 9856 2956 2989 357 2961 + 9857 2923 2906 2986 790 + 9858 241 242 3974 240 + 9859 344 340 2916 350 + 9860 2980 2923 2987 2986 + 9861 2978 2927 2924 2926 + 9862 2978 2980 2987 2986 + 9863 2934 2948 785 789 + 9864 344 354 2983 2962 + 9865 2947 797 798 803 + 9866 354 355 2985 2962 + 9867 355 354 340 2962 + 9868 3778 3977 3974 240 + 9869 2923 2944 2924 2922 + 9870 354 344 340 2962 + 9871 2964 344 2962 2981 + 9872 344 2964 340 2981 + 9873 2940 2925 2915 2982 + 9874 2978 2983 2927 2926 + 9875 3797 374 372 3775 + 9876 2944 798 2906 2923 + 9877 2991 344 2983 2992 + 9878 2991 2968 2916 2992 + 9879 344 2991 2916 2992 + 9880 2968 2964 2916 2992 + 9881 2964 344 2916 2992 + 9882 2983 344 2962 2992 + 9883 2964 2983 2962 2992 + 9884 344 2964 2962 2992 + 9885 2991 2968 2992 2914 + 9886 799 788 785 800 + 9887 1422 1435 1425 1442 + 9888 2937 2710 2708 2714 + 9889 2963 2960 2958 352 + 9890 356 2963 2958 352 + 9891 2960 356 2958 352 + 9892 2977 2960 2963 352 + 9893 2960 2977 2956 352 + 9894 2977 2965 2961 2967 + 9895 2834 2954 2835 2952 + 9896 2953 2966 2990 2952 + 9897 2953 2990 2967 350 + 9898 2990 357 2961 350 + 9899 2990 2961 2967 350 + 9900 2980 2978 2924 2986 + 9901 1423 1424 1353 1432 + 9902 10 63 2082 2073 + 9903 357 2956 2961 350 + 9904 805 2647 2648 2714 + 9905 2956 2977 2961 350 + 9906 2961 2977 2967 350 + 9907 1350 1356 1355 1358 + 9908 542 2003 534 1377 + 9909 2953 2965 2952 358 + 9910 356 2953 2952 358 + 9911 2965 356 2952 358 + 9912 2965 2953 2967 358 + 9913 2967 2953 350 358 + 9914 2963 2977 352 358 + 9915 2977 2967 350 358 + 9916 2977 2965 2967 358 + 9917 2965 2977 2963 358 + 9918 2944 2927 2923 2924 + 9919 374 376 372 3775 + 9920 36 37 557 6 + 9921 2978 791 801 2924 + 9922 354 2983 2962 2987 + 9923 1428 1349 1351 1431 + 9924 354 791 341 2987 + 9925 354 341 2983 2987 + 9926 2978 2980 2962 2987 + 9927 2983 2978 2962 2987 + 9928 341 791 2978 2987 + 9929 2983 341 2978 2987 + 9930 791 2924 2986 790 + 9931 799 798 803 802 + 9932 2927 2923 2924 790 + 9933 791 801 2924 790 + 9934 799 797 784 803 + 9935 2924 2927 2982 2926 + 9936 341 2978 801 2926 + 9937 231 3727 245 249 + 9938 3756 3790 245 249 + 9939 791 801 790 802 + 9940 2950 2925 2939 803 + 9941 784 2950 2939 803 + 9942 3726 234 238 3760 + 9943 801 804 2982 2926 + 9944 2944 2947 798 803 + 9945 2925 2940 2939 803 + 9946 2947 2944 2925 803 + 9947 2947 2950 784 803 + 9948 2950 2947 2925 803 + 9949 2927 2940 802 804 + 9950 2701 2935 807 2691 + 9951 2940 803 802 804 + 9952 2081 2003 2000 2 + 9953 2925 2940 803 802 + 9954 2940 2925 2927 802 + 9955 2944 2925 803 802 + 9956 2925 2944 2927 802 + 9957 2927 2944 2923 802 + 9958 2944 798 2923 802 + 9959 2003 544 1 2 + 9960 2083 2085 557 6 + 9961 2927 2925 2982 2926 + 9962 2921 2940 2914 2982 + 9963 2940 2921 804 2982 + 9964 2947 2944 2906 2922 + 9965 2924 2927 804 2982 + 9966 1445 504 1438 502 + 9967 1353 1424 496 1432 + 9968 2921 801 804 2982 + 9969 2921 801 2982 2926 + 9970 2914 2921 2982 2926 + 9971 801 2924 804 2926 + 9972 801 2978 2924 2926 + 9973 2991 341 801 2926 + 9974 341 2991 2983 2926 + 9975 2991 2921 2914 2926 + 9976 2921 2991 801 2926 + 9977 2710 805 2648 2714 + 9978 2934 799 2938 785 + 9979 2938 799 784 785 + 9980 797 2906 2931 2932 + 9981 805 2710 2687 2711 + 9982 809 2694 782 781 + 9983 2934 2935 2689 2933 + 9984 2949 2934 2700 2933 + 9985 2701 2949 2700 2933 + 9986 3821 3809 3834 3832 + 9987 234 231 245 249 + 9988 2931 2948 2946 783 + 9989 264 3859 3813 262 + 9990 3873 3854 3825 3823 + 9991 3825 3824 3816 3823 + 9992 231 234 3765 249 + 9993 799 2934 2948 785 + 9994 809 782 2721 783 + 9995 2923 2906 2922 2932 + 9996 3765 3743 3731 3760 + 9997 3996 3776 241 3974 + 9998 3971 3996 241 3974 + 9999 3854 3873 3813 3823 +10000 3759 3756 3727 249 +10001 3727 3756 245 249 +10002 3855 230 3766 238 +10003 230 3855 3726 238 +10004 3784 3759 3727 3731 +10005 3759 3765 249 3731 +10006 3727 3759 249 3731 +10007 3737 3743 3742 3731 +10008 3743 3737 3765 3731 +10009 3743 3799 3742 3731 +10010 3799 3743 3727 3731 +10011 231 3727 249 3731 +10012 3765 231 249 3731 +10013 3799 3737 3742 3731 +10014 3737 231 3765 3731 +10015 231 3799 3727 3731 +10016 231 3737 3799 3731 +10017 3821 255 3809 235 +10018 3792 248 247 246 +10019 3789 3791 248 246 +10020 3791 3789 3792 246 +10021 3792 3789 248 246 +10022 3777 3791 3792 246 +10023 3777 3790 3791 246 +10024 3777 3792 247 246 +10025 3790 3777 247 246 +10026 3778 3971 242 3974 +10027 242 3971 241 3974 +10028 3726 3855 3732 3845 +10029 3977 3778 3974 3980 +10030 3776 3977 3974 3980 +10031 3791 3789 248 3770 +10032 3789 3791 3977 3770 +10033 242 248 3989 3770 +10034 3778 3789 3977 3770 +10035 3778 242 3989 3770 +10036 3778 3987 3789 3770 +10037 3789 3987 248 3770 +10038 3987 3778 3989 3770 +10039 248 3987 3989 3770 +10040 303 3979 301 3986 +10041 302 3973 301 3986 +10042 3979 302 301 3986 +10043 302 3979 3973 3986 +10044 3994 3776 3979 243 +10045 3996 3994 3990 243 +10046 241 3996 3990 243 +10047 244 241 3990 243 +10048 244 302 241 243 +10049 302 244 3994 243 +10050 3994 244 3990 243 +10051 3776 3996 241 243 +10052 3996 3776 3994 243 +10053 4485 1183 4505 4551 +10054 1609 1183 4485 4551 +10055 4553 4592 4613 4625 +10056 301 304 2816 300 +10057 644 3868 4713 4720 +10058 1584 4226 4422 4526 +10059 1607 1197 1918 4551 +10060 4427 3472 4597 4644 +10061 3482 3472 3484 4644 +10062 4320 4047 4372 4716 +10063 1121 1176 1120 4708 +10064 1197 1183 1918 4551 +10065 3821 3824 267 235 +10066 3824 3821 3825 235 +10067 265 263 267 235 +10068 3824 3825 265 235 +10069 3824 265 267 235 +10070 3873 3859 3813 3823 +10071 264 3816 3815 3823 +10072 264 3859 262 3823 +10073 3859 3825 235 3823 +10074 3859 235 262 3823 +10075 3859 264 3813 3823 +10076 3813 264 3815 3823 +10077 263 264 262 3823 +10078 264 263 3816 3823 +10079 235 263 262 3823 +10080 265 3825 3816 3823 +10081 263 265 3816 3823 +10082 265 263 235 3823 +10083 3825 265 235 3823 +10084 237 3755 239 3753 +10085 232 237 239 3753 +10086 3968 3920 3954 3972 +10087 3726 3855 3731 3732 +10088 238 3726 3760 3845 +10089 359 3780 3794 3775 +10090 3743 234 3726 3760 +10091 232 239 3766 3862 +10092 3846 232 3766 3862 +10093 3846 3858 3753 3862 +10094 3752 3855 3766 3862 +10095 3752 239 3753 3862 +10096 232 3846 3753 3862 +10097 239 232 3753 3862 +10098 3858 3855 3753 3862 +10099 3855 3752 3753 3862 +10100 366 376 359 3775 +10101 3800 359 3794 3775 +10102 3797 3800 3794 3775 +10103 366 3797 372 3775 +10104 376 366 372 3775 +10105 3800 366 359 3775 +10106 366 3800 3797 3775 +10107 3725 3797 3775 3738 +10108 373 3739 374 3738 +10109 3797 373 374 3738 +10110 373 3797 3739 3738 +10111 3857 368 3842 370 +10112 3837 250 257 3827 +10113 250 3837 3811 3827 +10114 3826 3817 3828 3827 +10115 3822 3828 3832 3827 +10116 3811 3822 3832 3827 +10117 773 776 755 758 +10118 258 3826 257 3827 +10119 250 258 257 3827 +10120 254 250 256 3827 +10121 250 3811 256 3827 +10122 3817 254 3832 3827 +10123 258 3817 3826 3827 +10124 258 254 3817 3827 +10125 250 254 258 3827 +10126 3828 3817 3832 3827 +10127 256 3811 3832 3827 +10128 254 256 3832 3827 +10129 2712 2668 780 755 +10130 251 3843 370 259 +10131 260 369 3843 259 +10132 258 254 260 259 +10133 258 251 370 259 +10134 367 3734 3842 3853 +10135 3734 367 377 3853 +10136 3735 3734 377 3853 +10137 368 3857 3842 3843 +10138 3812 368 3842 3843 +10139 369 3812 3844 3843 +10140 3810 251 3818 3843 +10141 367 377 370 259 +10142 3812 369 368 3843 +10143 2686 2712 780 755 +10144 260 369 3844 3843 +10145 3818 260 3844 3843 +10146 763 761 2649 777 +10147 2775 2811 886 2663 +10148 882 2792 919 916 +10149 369 368 3843 370 +10150 2743 2732 2809 2742 +10151 3909 3910 3954 3972 +10152 915 918 2740 2809 +10153 3914 3961 3912 3972 +10154 360 3961 363 365 +10155 364 360 363 365 +10156 3961 360 378 365 +10157 378 360 364 365 +10158 3961 378 364 365 +10159 3914 3793 3796 3771 +10160 3961 3779 3775 3771 +10161 360 3961 3796 3771 +10162 3793 360 3796 3771 +10163 3959 3961 363 3771 +10164 359 3793 3794 3771 +10165 3961 360 363 3771 +10166 3780 3794 3775 3771 +10167 3779 3780 3775 3771 +10168 3959 3779 3961 3771 +10169 359 3959 363 3771 +10170 360 359 363 3771 +10171 360 3793 359 3771 +10172 3780 3779 3959 3771 +10173 3780 359 3794 3771 +10174 3780 3959 359 3771 +10175 3960 387 364 3972 +10176 3909 388 387 3972 +10177 387 388 364 3972 +10178 394 393 392 390 +10179 3913 394 392 390 +10180 2758 2768 2755 2809 +10181 2758 2768 2809 2742 +10182 2883 335 326 383 +10183 3263 3192 861 860 +10184 2877 2879 2878 2875 +10185 2866 330 2864 383 +10186 381 379 2878 2875 +10187 2866 2864 2874 383 +10188 392 3913 390 3948 +10189 2879 2876 2878 2875 +10190 380 392 390 3948 +10191 2867 3913 392 3948 +10192 2879 2867 392 3948 +10193 2867 2879 3913 3948 +10194 2876 2866 2869 2875 +10195 2867 2868 2869 2875 +10196 3192 862 861 860 +10197 1913 1924 171 178 +10198 329 2866 381 384 +10199 2879 2867 3913 2875 +10200 381 2866 2876 384 +10201 2874 381 2876 384 +10202 2866 2874 2876 384 +10203 331 330 2866 384 +10204 329 331 2866 384 +10205 331 329 330 384 +10206 2874 381 384 2878 +10207 379 2874 384 2878 +10208 381 2876 2869 2878 +10209 623 621 858 626 +10210 857 2767 868 3229 +10211 2868 381 2869 2875 +10212 2869 381 2878 2875 +10213 380 2879 2877 2875 +10214 379 380 2877 2875 +10215 2867 2879 392 2875 +10216 2879 380 392 2875 +10217 2868 3943 381 2875 +10218 3943 2868 2867 2875 +10219 380 379 386 2875 +10220 3943 2867 386 2875 +10221 392 380 386 2875 +10222 2867 392 386 2875 +10223 381 3943 385 2875 +10224 385 3943 386 2875 +10225 381 385 386 2875 +10226 379 381 386 2875 +10227 3239 3240 828 3235 +10228 3252 3240 3255 3235 +10229 2767 3252 3255 3235 +10230 3252 2767 3240 3235 +10231 2757 3239 828 3235 +10232 2767 836 3240 3235 +10233 3240 836 828 3235 +10234 3236 3229 2751 2756 +10235 836 828 3235 2751 +10236 836 828 2751 2756 +10237 852 836 2751 2756 +10238 836 852 828 2756 +10239 2757 3236 2756 2755 +10240 837 2758 2759 851 +10241 920 2792 2773 890 +10242 905 837 915 851 +10243 2779 893 920 890 +10244 2732 905 2809 2742 +10245 2757 2768 2755 2759 +10246 2768 2757 837 2759 +10247 2768 837 905 2759 +10248 905 837 2758 2759 +10249 2768 905 2758 2759 +10250 2744 2808 2740 2807 +10251 905 915 906 851 +10252 906 915 2732 851 +10253 905 906 2732 851 +10254 1366 539 1367 541 +10255 920 893 919 890 +10256 922 2776 2771 2770 +10257 2776 2775 886 2663 +10258 917 2808 882 916 +10259 2775 2776 922 2770 +10260 2774 2775 922 2770 +10261 2775 2774 2776 2770 +10262 2779 922 2771 2770 +10263 2791 2792 2797 2793 +10264 2774 922 882 2773 +10265 882 922 920 2773 +10266 761 760 887 777 +10267 2796 906 919 904 +10268 2797 2796 919 904 +10269 763 776 761 777 +10270 2796 2743 2732 904 +10271 906 2796 2732 904 +10272 2743 915 2732 904 +10273 915 906 2732 904 +10274 2808 2774 2741 2740 +10275 888 2669 2769 2649 +10276 2744 2808 2741 2740 +10277 894 2780 760 777 +10278 548 428 426 425 +10279 2797 2779 2793 890 +10280 2796 2797 919 890 +10281 2797 2792 2743 2741 +10282 1386 1385 1387 1372 +10283 2768 905 2766 2742 +10284 905 2758 851 2809 +10285 3239 2768 2766 2742 +10286 2768 2740 2809 2742 +10287 757 773 755 758 +10288 2743 915 2740 2809 +10289 915 2743 2732 2809 +10290 1334 569 1344 1345 +10291 2779 893 890 2778 +10292 2788 2644 2651 2811 +10293 1371 1511 1370 1514 +10294 2673 763 761 2649 +10295 773 2673 761 2649 +10296 1366 550 1375 1367 +10297 2657 756 2672 2686 +10298 2664 2788 2651 2811 +10299 756 2657 776 2686 +10300 2668 2715 780 2644 +10301 569 542 567 1345 +10302 756 2654 2672 2686 +10303 2647 2708 2714 2709 +10304 2935 2937 2710 2688 +10305 2808 917 2744 916 +10306 773 780 2644 887 +10307 806 2937 2693 2688 +10308 782 2931 2721 783 +10309 888 2788 2811 886 +10310 2693 782 781 2655 +10311 1334 1336 1338 1339 +10312 2776 886 2666 2663 +10313 1429 1352 1357 562 +10314 2710 2648 2708 2714 +10315 2715 2668 2709 2644 +10316 2776 889 881 886 +10317 780 888 2788 887 +10318 2644 2665 2788 2651 +10319 2715 2665 2670 2644 +10320 2644 780 2788 887 +10321 2712 2715 2670 780 +10322 818 757 759 758 +10323 1340 542 1376 534 +10324 2670 776 2686 780 +10325 2712 2670 2686 780 +10326 2647 757 805 755 +10327 2668 2647 805 755 +10328 2647 2668 2712 755 +10329 776 756 2686 758 +10330 2654 756 759 758 +10331 757 2654 759 758 +10332 2654 757 2686 758 +10333 756 2654 2686 758 +10334 2665 888 2649 887 +10335 2774 2776 2770 2666 +10336 2665 2715 2664 2651 +10337 761 773 2649 887 +10338 2647 757 755 2711 +10339 2712 2647 755 2711 +10340 757 2647 805 2711 +10341 805 2647 2714 2711 +10342 757 2654 2686 2711 +10343 2686 2654 2685 2711 +10344 2654 757 759 2711 +10345 2654 759 2685 2711 +10346 759 805 2685 2711 +10347 759 757 805 2711 +10348 888 881 760 887 +10349 2670 773 780 2644 +10350 2670 773 2644 887 +10351 2649 773 2670 887 +10352 2665 2649 2670 887 +10353 2665 2644 2788 887 +10354 2665 2670 2644 887 +10355 2775 917 889 886 +10356 2811 2664 2666 2663 +10357 2651 2664 2811 2663 +10358 918 915 2744 916 +10359 888 2788 2664 2811 +10360 915 918 851 916 +10361 2769 888 2664 2811 +10362 881 888 760 2811 +10363 2769 881 760 2811 +10364 888 2769 760 2811 +10365 2664 2769 2811 2666 +10366 2772 2769 2770 2666 +10367 2776 2772 2770 2666 +10368 917 882 889 916 +10369 881 2769 2772 2666 +10370 2776 881 2772 2666 +10371 2769 881 2811 2666 +10372 2811 881 886 2666 +10373 881 2776 886 2666 +10374 2772 894 2771 2778 +10375 894 760 761 777 +10376 888 2669 2649 777 +10377 922 2779 920 890 +10378 2743 915 904 921 +10379 915 2744 916 921 +10380 2792 919 916 921 +10381 2743 2797 2741 921 +10382 2743 2741 2740 921 +10383 915 2743 2740 921 +10384 2744 2808 916 921 +10385 2808 2744 2741 921 +10386 2741 2744 2740 921 +10387 2792 2797 919 921 +10388 919 2797 904 921 +10389 2797 2792 2741 921 +10390 2808 2792 916 921 +10391 2792 2808 2741 921 +10392 2796 2743 904 921 +10393 2797 2796 904 921 +10394 2796 2797 2743 921 +10395 918 915 2740 921 +10396 915 918 2744 921 +10397 2744 918 2740 921 +10398 2418 841 2414 2412 +10399 1385 154 153 552 +10400 1112 1111 4298 4671 +10401 534 2003 540 1377 +10402 1501 437 1519 436 +10403 4750 4297 4758 4774 +10404 551 427 552 1372 +10405 1385 551 552 1372 +10406 548 551 154 1384 +10407 1362 549 426 425 +10408 427 551 548 1384 +10409 1365 1388 2010 1996 +10410 1363 1365 1995 1996 +10411 1388 2011 2010 1996 +10412 162 548 154 1384 +10413 428 1364 1368 1373 +10414 549 548 162 1384 +10415 1365 2011 1388 1996 +10416 2011 2002 2008 2007 +10417 551 1385 154 1383 +10418 1981 1987 1978 64 +10419 427 1386 1372 1383 +10420 551 427 1372 1383 +10421 1386 427 1372 1373 +10422 1374 1369 1370 1514 +10423 909 2403 2406 2411 +10424 168 186 1992 2005 +10425 1998 1999 1989 1988 +10426 207 158 205 160 +10427 1374 431 1362 1368 +10428 431 1374 1369 1368 +10429 1369 1371 1368 1370 +10430 549 1361 1362 426 +10431 1361 428 1362 426 +10432 549 548 1384 426 +10433 1361 549 1384 426 +10434 428 1369 1368 425 +10435 428 1361 1364 1373 +10436 548 1384 426 1373 +10437 428 548 426 1373 +10438 1384 1361 426 1373 +10439 1361 428 426 1373 +10440 1501 1510 436 1509 +10441 3852 702 726 4713 +10442 1374 431 1369 1514 +10443 1374 1512 2036 1514 +10444 426 431 425 432 +10445 2617 730 2619 732 +10446 2698 2699 2941 4162 +10447 3256 3234 3229 3241 +10448 1593 1602 1592 4088 +10449 836 828 868 829 +10450 830 867 907 829 +10451 867 830 2765 829 +10452 2035 2044 2036 2041 +10453 627 867 868 829 +10454 3254 867 868 3262 +10455 868 867 871 3262 +10456 3254 868 871 3262 +10457 3254 867 3262 2765 +10458 830 3237 3262 2765 +10459 867 830 3262 2765 +10460 836 3252 2767 2765 +10461 2767 3252 3240 2765 +10462 836 2767 3240 2765 +10463 836 3240 828 2765 +10464 3249 3256 3229 3241 +10465 3252 836 868 2765 +10466 867 3252 868 2765 +10467 3249 621 858 3241 +10468 3237 3240 2765 2746 +10469 1369 431 425 420 +10470 2766 2764 2731 2726 +10471 907 906 2726 827 +10472 2766 2764 2726 827 +10473 905 2766 2726 827 +10474 906 905 2730 827 +10475 2730 905 2726 827 +10476 906 2730 2726 827 +10477 1369 1371 1514 420 +10478 431 1369 1514 420 +10479 828 836 2765 829 +10480 431 1362 425 432 +10481 974 1086 4172 4214 +10482 766 768 891 895 +10483 2324 2327 896 895 +10484 431 1374 1362 432 +10485 2403 909 2406 900 +10486 2403 2413 2412 2411 +10487 979 990 977 4265 +10488 3004 3006 4006 4475 +10489 2623 2556 2627 2558 +10490 1587 1744 4116 4215 +10491 1515 1516 429 430 +10492 161 205 432 160 +10493 1502 229 1499 440 +10494 627 3254 871 860 +10495 2694 809 788 811 +10496 3263 3254 3192 860 +10497 3234 3195 859 628 +10498 858 3234 859 628 +10499 871 3263 861 860 +10500 861 862 863 860 +10501 3195 3234 3253 628 +10502 2812 4103 4127 4249 +10503 3050 3052 4127 4249 +10504 3195 3253 3196 628 +10505 3249 3196 858 628 +10506 621 3249 858 628 +10507 3052 2812 4127 4249 +10508 3253 3234 3196 628 +10509 3196 3234 858 628 +10510 3249 621 3247 628 +10511 3196 3249 3247 628 +10512 807 2701 789 811 +10513 788 2701 2689 811 +10514 2701 2935 2689 811 +10515 2693 806 2688 781 +10516 2694 806 2693 781 +10517 807 2701 2691 2707 +10518 806 2694 811 781 +10519 2694 809 811 781 +10520 2693 2688 2687 2655 +10521 2656 2693 2687 2655 +10522 2647 2648 2708 2707 +10523 2080 2072 4 2077 +10524 2685 2656 2687 2720 +10525 759 2685 2687 2720 +10526 2687 2656 2655 2720 +10527 818 2687 2655 2720 +10528 818 759 2687 2720 +10529 806 2688 781 810 +10530 2688 754 2687 810 +10531 754 818 2687 810 +10532 1356 1337 565 1360 +10533 1337 1359 565 1360 +10534 1349 561 1428 1351 +10535 781 2688 2655 810 +10536 818 781 2655 810 +10537 2687 818 2655 810 +10538 2688 2687 2655 810 +10539 2648 2710 2708 2707 +10540 806 2937 2688 2707 +10541 2937 2710 2688 2707 +10542 806 2688 810 2707 +10543 2710 754 2688 2707 +10544 754 2710 2648 2707 +10545 2688 754 810 2707 +10546 754 806 810 2707 +10547 806 754 2648 2707 +10548 1337 1359 2089 572 +10549 573 1337 2089 572 +10550 573 2089 556 572 +10551 2089 42 556 572 +10552 2089 1359 42 572 +10553 1340 1344 1345 1339 +10554 2694 2721 2719 2946 +10555 2689 2694 2948 2946 +10556 2931 797 2932 800 +10557 1366 1387 1372 1367 +10558 542 1340 1376 1345 +10559 567 1335 1343 1336 +10560 567 1335 1344 1345 +10561 1334 567 1342 1336 +10562 1343 1334 1342 1336 +10563 2179 2178 2189 192 +10564 799 2931 2932 800 +10565 1334 567 1336 1344 +10566 569 567 1334 1344 +10567 1335 1346 1344 1347 +10568 1359 1360 1346 1347 +10569 1359 565 1360 1347 +10570 1343 1359 1346 1347 +10571 1335 1343 1346 1347 +10572 572 1335 565 1347 +10573 1359 572 565 1347 +10574 1359 1343 572 1347 +10575 566 1335 572 1347 +10576 1335 566 1343 1347 +10577 1343 566 572 1347 +10578 496 561 559 564 +10579 782 809 781 783 +10580 2931 797 800 783 +10581 2689 2933 789 2946 +10582 2689 2934 2933 2946 +10583 2934 2689 2948 2946 +10584 2934 2948 789 2946 +10585 2933 2934 789 2946 +10586 1424 1349 1428 1431 +10587 2931 799 2948 800 +10588 2948 799 785 800 +10589 2083 2084 2091 6 +10590 554 1356 565 1358 +10591 2689 788 2721 783 +10592 785 2948 800 783 +10593 2948 785 789 783 +10594 788 785 800 783 +10595 785 788 789 783 +10596 788 2689 789 783 +10597 789 2689 2946 783 +10598 496 1424 561 1432 +10599 2059 1425 498 1442 +10600 1424 561 1432 1428 +10601 1423 1424 1432 1428 +10602 2095 2054 2087 2051 +10603 1429 1349 1354 1431 +10604 561 1352 564 562 +10605 1349 561 11 1428 +10606 1349 11 2054 1428 +10607 2050 2055 2057 2051 +10608 2050 44 26 2058 +10609 1349 2052 2092 7 +10610 559 1349 2092 7 +10611 561 11 1428 1434 +10612 1433 496 497 498 +10613 1426 1435 1445 1442 +10614 1353 1422 1435 1421 +10615 1433 1353 1435 1421 +10616 2054 2050 2057 2051 +10617 2057 2055 2087 2051 +10618 2052 559 3 7 +10619 2054 2057 2087 2051 +10620 2046 2068 2048 2045 +10621 496 1353 497 1434 +10622 2059 2070 2048 2071 +10623 1444 2059 1425 1427 +10624 2047 20 2060 2067 +10625 2068 2065 2071 2067 +10626 1444 2059 2070 2048 +10627 1441 503 2063 1440 +10628 1438 1441 1427 1443 +10629 1396 1441 1419 1440 +10630 637 2290 2300 2297 +10631 1441 2065 20 1438 +10632 2054 11 2057 1434 +10633 1441 1437 1438 502 +10634 1441 503 1437 502 +10635 20 1441 1438 502 +10636 503 505 504 502 +10637 505 20 1438 502 +10638 504 505 1438 502 +10639 503 1441 50 502 +10640 50 1441 20 502 +10641 50 20 505 502 +10642 503 50 505 502 +10643 503 1437 1397 1440 +10644 1420 1397 1439 1436 +10645 1420 1439 1419 1504 +10646 1397 1439 1436 1504 +10647 1397 1437 1439 1504 +10648 1189 1222 1204 4129 +10649 495 1439 1419 2027 +10650 2479 51 2282 4076 +10651 2024 2061 2025 2064 +10652 2024 2023 2061 2064 +10653 1439 495 1419 1508 +10654 1441 2063 2065 2070 +10655 1419 2063 1440 1508 +10656 542 52 544 2 +10657 561 1352 1432 1351 +10658 1350 1348 1357 1358 +10659 1348 1350 1355 1358 +10660 563 1348 1355 1358 +10661 1349 1429 564 1431 +10662 2003 2001 2002 2000 +10663 1340 542 534 1377 +10664 1348 1429 1354 1431 +10665 1430 1428 1351 1431 +10666 1429 1428 1430 1431 +10667 1429 559 564 562 +10668 37 2084 557 6 +10669 1348 1429 1357 562 +10670 1429 1348 1354 562 +10671 1354 1348 1357 562 +10672 2314 2305 2315 2229 +10673 2066 2062 2069 2025 +10674 2065 2066 2069 2025 +10675 2317 2292 2308 2319 +10676 1429 1349 559 562 +10677 561 559 564 1351 +10678 564 1352 1351 1431 +10679 559 564 1351 1431 +10680 1349 559 1351 1431 +10681 559 1349 564 1431 +10682 168 2004 2008 2007 +10683 2003 542 1338 1377 +10684 1334 2003 1342 1338 +10685 542 1334 1342 1338 +10686 542 1340 1345 1338 +10687 1334 542 1345 1338 +10688 1338 1380 1377 1378 +10689 2011 2008 2012 2010 +10690 550 1366 539 1367 +10691 1376 1340 1377 1378 +10692 831 834 833 835 +10693 2003 2081 1342 1338 +10694 539 1389 2002 1381 +10695 1389 1375 2002 1381 +10696 550 1375 1367 1381 +10697 539 550 1367 1381 +10698 2011 2008 1992 2012 +10699 1974 1983 93 64 +10700 534 536 540 535 +10701 1389 2002 540 535 +10702 1389 539 2002 535 +10703 1379 541 536 540 +10704 1389 1379 540 1380 +10705 2003 2002 1380 1377 +10706 831 870 2379 2383 +10707 2002 176 540 535 +10708 1334 2003 1338 1377 +10709 540 176 544 535 +10710 1376 541 1379 540 +10711 2032 1994 1990 1997 +10712 539 1389 541 536 +10713 2416 2418 2419 2395 +10714 541 1389 1379 536 +10715 1382 1389 1381 1380 +10716 2605 2610 2604 651 +10717 151 2113 150 174 +10718 1999 1989 2012 2010 +10719 540 2003 1380 1377 +10720 1376 534 540 1377 +10721 1376 540 1380 1377 +10722 2003 534 540 544 +10723 176 2003 540 544 +10724 163 1998 162 1991 +10725 2324 891 896 2332 +10726 1811 1805 113 119 +10727 2003 542 534 544 +10728 652 2605 2604 651 +10729 534 540 544 535 +10730 700 2562 2557 2564 +10731 731 724 2624 2552 +10732 2004 2006 168 1992 +10733 2628 2638 2637 716 +10734 1365 1375 1383 1388 +10735 542 569 534 544 +10736 569 542 52 544 +10737 686 687 2455 2430 +10738 2186 2185 2112 2117 +10739 483 148 484 150 +10740 483 1489 148 150 +10741 167 176 2006 168 +10742 2002 2011 2008 1388 +10743 2457 692 684 2461 +10744 2642 2639 721 720 +10745 884 2404 2341 883 +10746 2418 2416 845 2412 +10747 664 2600 674 668 +10748 1458 1452 2181 2184 +10749 2333 2334 892 2332 +10750 909 2403 898 900 +10751 2242 2240 2243 2391 +10752 664 2594 2615 663 +10753 2414 841 2415 2412 +10754 2413 2418 2412 2395 +10755 2392 2416 2397 2417 +10756 2416 2419 2392 2395 +10757 1988 1997 1995 1991 +10758 1989 163 153 2010 +10759 2010 1999 1996 1991 +10760 163 154 153 2010 +10761 1383 1365 1996 1991 +10762 1385 1365 1388 2010 +10763 154 1385 153 2010 +10764 1385 154 1383 2010 +10765 1365 1385 1383 2010 +10766 2095 1349 2054 2092 +10767 2053 2095 2054 2092 +10768 2095 2053 2086 2092 +10769 2316 2306 2270 2318 +10770 2231 2226 646 635 +10771 44 2056 43 2058 +10772 1349 2095 559 2092 +10773 2314 2315 2319 2229 +10774 745 2344 764 765 +10775 1349 559 562 3 +10776 1349 2052 559 3 +10777 1429 1349 562 3 +10778 1349 1429 2052 3 +10779 2083 2052 1354 3 +10780 1135 1000 1086 1083 +10781 1342 10 52 2 +10782 2052 1429 1354 3 +10783 1354 1429 562 3 +10784 687 2464 695 676 +10785 2090 2052 2083 2085 +10786 2083 2052 3 2085 +10787 2081 2000 2009 2 +10788 2085 3 557 6 +10789 1354 2083 3 2085 +10790 2084 37 2091 6 +10791 2066 2063 2062 2025 +10792 1419 1397 2063 2025 +10793 2063 1397 2062 2025 +10794 2063 2025 2026 2027 +10795 1439 495 50 12 +10796 1397 2061 2062 2025 +10797 1397 1419 2061 2025 +10798 2063 1419 2025 2027 +10799 2061 1439 50 12 +10800 50 2061 2022 49 +10801 2287 2273 2274 2265 +10802 2066 2063 2025 2026 +10803 1419 1439 2061 2027 +10804 2264 581 583 2265 +10805 1439 2061 2027 12 +10806 495 1439 2027 12 +10807 2025 2026 2027 2022 +10808 2061 2025 2027 2022 +10809 2025 2061 2026 2022 +10810 497 1433 498 26 +10811 20 50 505 49 +10812 2310 2271 2268 2267 +10813 2224 613 618 619 +10814 2063 2062 50 49 +10815 2063 2066 2062 49 +10816 2065 50 20 49 +10817 2063 2066 2065 2070 +10818 1183 1197 4505 4551 +10819 2063 50 2065 49 +10820 2066 2063 2065 49 +10821 2070 2071 2067 47 +10822 2066 2062 49 2060 +10823 2062 2066 2069 2060 +10824 20 2065 49 2060 +10825 2070 505 2048 47 +10826 505 2070 2067 47 +10827 20 505 2067 47 +10828 2071 2047 2067 47 +10829 2065 2066 49 2060 +10830 2066 2065 2069 2060 +10831 2047 2068 2071 2067 +10832 2069 2062 2060 2067 +10833 1441 2065 1438 2070 +10834 1441 1438 1427 2070 +10835 1441 1427 1440 2070 +10836 2063 1441 1440 2070 +10837 498 1426 504 26 +10838 505 1444 504 2070 +10839 504 1444 1427 2070 +10840 505 504 1438 2070 +10841 1438 504 1427 2070 +10842 2049 2059 2048 2071 +10843 2068 2065 2048 2071 +10844 2047 2068 2048 2071 +10845 2047 2068 2067 2045 +10846 1778 1818 1780 1823 +10847 2065 20 1438 2067 +10848 20 505 1438 2067 +10849 2047 2056 43 2045 +10850 2211 616 2217 619 +10851 44 43 2045 26 +10852 43 1442 2045 26 +10853 2065 1438 2070 2067 +10854 2065 2070 2071 2067 +10855 1438 505 2070 2067 +10856 2048 2047 43 2045 +10857 2049 2056 2046 2045 +10858 2224 2213 613 619 +10859 2049 2046 2048 2045 +10860 2056 44 43 2045 +10861 44 2056 2049 2045 +10862 2224 616 2213 619 +10863 1442 2048 43 2045 +10864 1424 1353 496 1434 +10865 2059 2049 2048 2045 +10866 1442 2059 2048 2045 +10867 44 2049 2059 2045 +10868 2054 1349 1428 1434 +10869 2304 2314 2290 2315 +10870 617 616 618 619 +10871 2161 2195 2202 117 +10872 1442 2059 2045 26 +10873 2059 44 2045 26 +10874 104 2160 417 2199 +10875 2251 2246 615 2248 +10876 2050 44 2059 26 +10877 1421 2059 498 26 +10878 1433 1421 498 26 +10879 105 2197 2150 2153 +10880 2306 2317 2308 2319 +10881 2316 2306 2290 2315 +10882 2215 2221 2216 2291 +10883 2303 2296 2300 2297 +10884 2306 2317 2319 2318 +10885 1206 1208 1210 1209 +10886 2050 2059 1421 26 +10887 2050 1353 2055 2058 +10888 497 1433 26 2058 +10889 44 497 26 2058 +10890 1353 1433 497 2058 +10891 2055 1353 497 2058 +10892 1421 2050 26 2058 +10893 1433 1421 26 2058 +10894 1353 2050 1421 2058 +10895 1433 1353 1421 2058 +10896 2224 2226 613 2225 +10897 2224 614 629 2260 +10898 2212 2237 2248 2260 +10899 2222 2261 2213 2225 +10900 2219 2294 2293 2229 +10901 2240 2320 2387 864 +10902 2238 2249 2239 2320 +10903 2387 2240 2391 2241 +10904 2259 866 625 2249 +10905 2251 2254 2256 2250 +10906 613 2224 618 614 +10907 2259 625 2236 2256 +10908 2249 2238 2247 2248 +10909 2244 2376 2385 2383 +10910 27 2072 42 4 +10911 2072 27 2080 4 +10912 1342 1343 1336 1341 +10913 10 1342 52 4 +10914 27 10 52 4 +10915 2076 1359 2089 1341 +10916 2074 2080 2076 2077 +10917 1342 567 52 4 +10918 567 1342 1336 4 +10919 10 2082 4 2073 +10920 2075 2072 2074 2073 +10921 2370 2390 2369 2389 +10922 63 2009 2078 2073 +10923 52 27 4 46 +10924 2246 2259 2254 2247 +10925 556 573 572 46 +10926 573 566 572 46 +10927 567 52 4 46 +10928 567 566 52 46 +10929 42 556 572 46 +10930 2081 63 2082 2 +10931 559 3 7 557 +10932 556 42 36 46 +10933 42 27 36 46 +10934 2224 2261 618 614 +10935 37 556 36 557 +10936 2251 624 2252 2206 +10937 10 63 4 2 +10938 572 42 46 1341 +10939 4 2080 2077 1341 +10940 1336 4 2077 1341 +10941 567 566 46 1341 +10942 566 567 1343 1341 +10943 1336 567 4 1341 +10944 4 567 46 1341 +10945 27 2080 4 1341 +10946 27 4 46 1341 +10947 566 572 46 1341 +10948 566 1343 572 1341 +10949 63 2081 2009 2 +10950 176 167 1 2 +10951 833 2382 2376 2389 +10952 1354 563 1355 557 +10953 563 554 1355 557 +10954 554 37 1355 557 +10955 3 1354 2085 557 +10956 1357 1354 562 557 +10957 563 1357 562 557 +10958 2084 1354 1355 557 +10959 37 2084 1355 557 +10960 62 2529 40 5 +10961 1354 2083 2085 557 +10962 1348 1354 1357 557 +10963 563 1348 1357 557 +10964 1348 563 1354 557 +10965 2525 2522 2528 2518 +10966 2370 2388 2390 2389 +10967 885 891 892 2332 +10968 2324 2333 892 2332 +10969 866 877 874 864 +10970 866 2233 2239 864 +10971 849 832 847 2372 +10972 2888 332 4210 4328 +10973 876 872 2387 874 +10974 877 855 2243 878 +10975 2388 2377 2389 2372 +10976 2320 2240 2243 864 +10977 877 2243 874 864 +10978 2249 2259 2243 2247 +10979 872 876 875 874 +10980 2386 846 847 2372 +10981 896 772 879 880 +10982 849 2380 840 2384 +10983 866 2259 625 878 +10984 872 873 2387 874 +10985 873 872 877 874 +10986 2422 2448 2424 2423 +10987 2242 2240 2387 874 +10988 873 2242 2387 874 +10989 873 877 855 874 +10990 877 873 855 878 +10991 855 877 2243 874 +10992 768 2360 767 765 +10993 866 877 2239 878 +10994 2259 866 2239 878 +10995 2239 877 2243 878 +10996 2259 2239 2243 878 +10997 873 835 2235 869 +10998 873 855 835 869 +10999 855 873 874 869 +11000 2242 873 2235 869 +11001 2257 2242 2235 869 +11002 2243 855 874 869 +11003 873 2242 874 869 +11004 2240 2243 874 869 +11005 2242 2240 874 869 +11006 2240 2242 2257 869 +11007 438 2016 2013 202 +11008 229 438 440 202 +11009 2014 229 1502 202 +11010 1502 229 440 202 +11011 438 2014 1502 202 +11012 438 1502 440 202 +11013 767 2360 2367 765 +11014 745 2356 2344 765 +11015 2356 767 2367 765 +11016 2344 2356 2367 765 +11017 172 815 1644 4314 +11018 2321 2336 2338 880 +11019 2338 2334 2332 880 +11020 549 426 425 432 +11021 2281 2578 596 609 +11022 2619 730 721 732 +11023 2336 2321 897 880 +11024 779 762 771 772 +11025 2360 2325 2328 2366 +11026 2344 764 765 2366 +11027 2321 2338 879 880 +11028 1202 2346 1626 749 +11029 2346 2348 744 778 +11030 1049 1051 1201 2352 +11031 2661 2917 4281 4457 +11032 4092 3603 4307 4325 +11033 1192 812 815 56 +11034 1924 185 171 178 +11035 2571 2572 600 2568 +11036 3051 3082 4573 4584 +11037 2327 2323 2324 896 +11038 2342 2338 2322 2337 +11039 896 885 897 880 +11040 701 707 733 696 +11041 2392 2397 902 2417 +11042 2546 2547 2582 708 +11043 4050 1254 4121 4635 +11044 1512 2044 2041 2038 +11045 2324 891 892 895 +11046 154 155 153 538 +11047 1198 1636 1607 4089 +11048 1198 1607 1918 4089 +11049 1183 1576 4485 4680 +11050 1593 1592 1563 4088 +11051 885 891 884 892 +11052 4001 3595 4198 4217 +11053 189 1964 88 187 +11054 1610 1646 1918 4089 +11055 31 39 45 32 +11056 1228 741 740 4088 +11057 2039 207 2036 4213 +11058 100 1987 1943 64 +11059 2106 2103 2104 2109 +11060 2120 2114 2111 2102 +11061 2360 768 771 2325 +11062 771 768 2367 2325 +11063 2360 771 2367 2325 +11064 197 201 1031 1032 +11065 3620 3380 3371 4440 +11066 1512 1517 2044 2038 +11067 218 2043 220 2040 +11068 740 1218 1562 4239 +11069 2784 2782 3624 4440 +11070 2037 2034 2021 2042 +11071 1055 2016 2028 2019 +11072 223 207 220 2040 +11073 207 2039 158 2040 +11074 982 3082 3053 4584 +11075 1592 1602 740 4088 +11076 2014 222 2015 2042 +11077 2034 2043 2041 2042 +11078 3319 3317 4687 4730 +11079 1512 1374 2044 2033 +11080 2652 2658 2653 1032 +11081 1045 1038 4011 4167 +11082 222 219 2015 2042 +11083 218 2034 2037 2042 +11084 2043 220 2040 2041 +11085 2035 2043 2040 2041 +11086 220 223 2040 2041 +11087 1592 740 1563 4088 +11088 204 2044 223 2041 +11089 1602 1593 1557 4088 +11090 3220 2830 3212 4594 +11091 1082 1100 1125 4214 +11092 2039 2035 2040 2041 +11093 2035 2039 2044 2041 +11094 1512 2044 204 2041 +11095 1515 1512 204 2041 +11096 1218 1560 1562 4239 +11097 1513 1512 1515 2041 +11098 1361 1365 1384 1991 +11099 2032 1994 158 1993 +11100 2032 158 205 1993 +11101 1994 2036 158 1993 +11102 158 2036 205 1993 +11103 1053 1597 1047 1050 +11104 1663 1136 1212 1138 +11105 2862 2890 2894 1658 +11106 1365 1361 1995 1991 +11107 205 2036 432 1993 +11108 2036 1374 432 1993 +11109 1374 1362 432 1993 +11110 154 1383 1996 1991 +11111 1374 1361 1362 1993 +11112 1361 1374 1363 1993 +11113 1989 163 2010 1996 +11114 549 1361 1384 1991 +11115 1361 549 1995 1991 +11116 163 162 154 1991 +11117 1384 1365 1383 1991 +11118 154 1384 1383 1991 +11119 2032 160 159 1990 +11120 2032 205 160 1990 +11121 205 2032 1993 1990 +11122 160 161 159 1990 +11123 1361 1362 1993 1990 +11124 432 1362 160 1990 +11125 1362 432 1993 1990 +11126 205 432 160 1990 +11127 432 205 1993 1990 +11128 1992 2011 2012 2005 +11129 1361 549 1362 1990 +11130 1362 549 160 1990 +11131 549 161 160 1990 +11132 155 2011 1992 2005 +11133 161 159 1990 1995 +11134 1990 159 1997 1995 +11135 549 161 1995 1991 +11136 161 162 1988 1991 +11137 161 1988 159 1995 +11138 159 1988 1997 1995 +11139 153 155 186 2005 +11140 833 831 2379 2383 +11141 161 1988 1995 1991 +11142 186 155 1992 2005 +11143 1361 1363 1365 1995 +11144 2418 841 2412 839 +11145 1981 66 1987 64 +11146 74 93 1965 1976 +11147 1987 67 1943 64 +11148 1987 100 1978 64 +11149 1983 100 93 64 +11150 186 177 535 538 +11151 186 155 177 538 +11152 539 186 535 538 +11153 550 539 536 538 +11154 536 539 535 538 +11155 154 153 552 538 +11156 550 154 552 538 +11157 539 550 1381 538 +11158 153 155 2010 538 +11159 155 186 2008 538 +11160 550 1375 1381 538 +11161 1381 1375 1388 538 +11162 1385 153 2010 538 +11163 1388 1385 2010 538 +11164 2011 1388 2010 538 +11165 155 2011 2010 538 +11166 153 1385 552 538 +11167 1385 550 552 538 +11168 539 2002 186 538 +11169 2002 539 1381 538 +11170 2008 1381 1388 538 +11171 2011 2008 1388 538 +11172 2011 155 2008 538 +11173 550 1385 1375 538 +11174 1375 1385 1388 538 +11175 186 2002 2008 538 +11176 2002 1381 2008 538 +11177 721 732 722 720 +11178 2409 2410 838 842 +11179 846 850 842 847 +11180 845 910 842 839 +11181 898 910 899 839 +11182 664 2594 670 674 +11183 2323 2324 896 2332 +11184 2244 2376 2375 2385 +11185 1974 1981 1983 64 +11186 2336 2334 2338 880 +11187 901 2398 843 902 +11188 914 2404 884 908 +11189 914 2393 2405 911 +11190 902 912 2417 2399 +11191 2398 844 843 902 +11192 901 843 912 902 +11193 2334 2336 897 880 +11194 901 2398 910 843 +11195 2639 719 732 720 +11196 2409 2418 842 839 +11197 2409 841 2418 839 +11198 2616 2639 2642 2641 +11199 2556 2561 2559 2558 +11200 2556 2559 2627 2558 +11201 911 2403 900 912 +11202 901 911 900 912 +11203 2403 901 900 912 +11204 2398 901 2392 902 +11205 2398 2392 2397 902 +11206 884 914 908 897 +11207 223 207 2040 4213 +11208 853 832 846 903 +11209 914 2405 2404 908 +11210 2406 911 900 908 +11211 2393 2402 2335 2417 +11212 2409 2410 842 2378 +11213 2418 2409 842 2378 +11214 2409 2418 2410 2378 +11215 845 2418 842 2378 +11216 2418 845 2419 2378 +11217 2396 845 842 2378 +11218 845 2396 2419 2378 +11219 853 2396 2381 2386 +11220 838 846 842 903 +11221 2410 842 2378 903 +11222 2377 2410 2378 903 +11223 2410 838 842 903 +11224 2410 2377 838 903 +11225 831 834 2382 2383 +11226 2610 654 655 2602 +11227 832 853 846 2386 +11228 2376 2375 2372 2373 +11229 2386 2381 2375 2385 +11230 1909 1887 1908 1910 +11231 2611 673 668 2613 +11232 870 2244 2245 2234 +11233 2244 870 2383 2234 +11234 2598 2596 2600 2599 +11235 2598 2600 2597 2599 +11236 2607 672 2608 2612 +11237 2379 2386 2375 2385 +11238 2245 2257 2235 2234 +11239 832 833 2379 2376 +11240 891 2324 892 2332 +11241 2333 2324 2326 2332 +11242 2405 2406 911 2394 +11243 832 846 833 847 +11244 833 2379 2376 2383 +11245 2606 2471 2458 2463 +11246 849 832 833 847 +11247 850 846 2377 847 +11248 2374 2381 2375 2373 +11249 849 850 2380 847 +11250 2380 850 2377 847 +11251 2642 2639 720 663 +11252 2378 2377 903 2373 +11253 846 832 2386 847 +11254 2419 2378 2374 2373 +11255 2605 669 2603 2602 +11256 853 2381 2374 2373 +11257 846 2377 2372 2373 +11258 2607 654 2610 2602 +11259 832 2376 2375 2372 +11260 846 2377 847 2372 +11261 2240 2242 2387 2391 +11262 2380 849 2388 2384 +11263 2391 2240 2390 2241 +11264 876 2391 2390 2241 +11265 876 2387 2391 2241 +11266 872 876 2387 2391 +11267 876 872 875 2391 +11268 2242 2382 2245 2391 +11269 873 872 2387 2391 +11270 2242 873 2387 2391 +11271 2382 831 2245 2391 +11272 873 2242 835 2391 +11273 872 873 835 2391 +11274 2242 2245 835 2391 +11275 872 835 875 2391 +11276 2245 831 835 2391 +11277 835 831 875 2391 +11278 2386 846 2372 2373 +11279 2391 2382 2390 2389 +11280 849 2388 2389 2372 +11281 2388 2380 2377 2372 +11282 875 834 848 2369 +11283 875 848 2384 2369 +11284 2384 2370 2369 2389 +11285 848 849 2384 2389 +11286 849 2388 2384 2389 +11287 2381 2386 2375 2373 +11288 2381 853 2386 2373 +11289 833 849 848 2389 +11290 834 833 848 2389 +11291 834 2382 833 2389 +11292 848 2384 2369 2389 +11293 832 849 833 2389 +11294 832 833 2376 2389 +11295 834 848 2369 2389 +11296 2382 834 2369 2389 +11297 909 2415 2413 2411 +11298 2342 2321 2338 879 +11299 2415 2414 2412 2411 +11300 2403 2407 2406 2411 +11301 2407 2405 2406 883 +11302 2405 2404 908 883 +11303 2404 2405 2407 883 +11304 2406 2405 911 883 +11305 911 2405 908 883 +11306 2406 911 908 883 +11307 2416 910 2412 2395 +11308 2398 2416 2392 2395 +11309 2416 2398 910 2395 +11310 910 2398 2392 2395 +11311 2415 2413 2412 2395 +11312 910 2415 2412 2395 +11313 2415 910 2413 2395 +11314 718 717 734 2627 +11315 901 2392 902 2417 +11316 912 901 902 2417 +11317 2403 901 912 2417 +11318 2403 911 2394 2417 +11319 911 2403 912 2417 +11320 2413 2403 2394 2417 +11321 2392 2413 2394 2417 +11322 2392 901 2413 2417 +11323 901 2403 2413 2417 +11324 2393 914 2335 2343 +11325 2402 2393 2335 2343 +11326 914 911 908 897 +11327 722 2630 667 716 +11328 2393 911 2343 2399 +11329 914 912 2343 2399 +11330 911 914 2343 2399 +11331 914 911 912 2399 +11332 911 2393 2394 2399 +11333 2394 2393 2417 2399 +11334 911 2394 2417 2399 +11335 2334 885 2332 880 +11336 913 914 897 880 +11337 884 2404 913 897 +11338 2335 2401 913 897 +11339 2335 2339 2401 897 +11340 884 885 2341 897 +11341 2404 884 2341 897 +11342 2339 2335 2404 897 +11343 2404 2335 913 897 +11344 2339 2336 2401 897 +11345 2401 2336 2321 897 +11346 2334 2404 2341 897 +11347 2334 2339 2404 897 +11348 762 770 2362 2359 +11349 2339 2334 2336 897 +11350 885 2334 892 897 +11351 892 2334 2341 897 +11352 885 892 2341 897 +11353 2561 2556 2559 2557 +11354 2342 2362 2359 2322 +11355 2362 762 2359 2330 +11356 2557 724 2564 2563 +11357 2546 2585 2581 2548 +11358 2327 2323 772 2330 +11359 2361 770 2362 2355 +11360 707 2547 2548 708 +11361 2362 2359 2322 2330 +11362 2571 2574 2570 602 +11363 762 772 2359 2330 +11364 2322 2359 2337 2330 +11365 772 896 2359 2337 +11366 1886 1931 1894 209 +11367 724 700 731 729 +11368 1271 1258 1259 4405 +11369 1012 974 930 931 +11370 2616 2617 2618 2558 +11371 1893 1886 1931 1894 +11372 2562 2565 701 2555 +11373 701 700 699 2555 +11374 2562 701 699 2555 +11375 700 2562 699 2555 +11376 183 209 214 208 +11377 707 2560 701 2548 +11378 2560 2550 701 2548 +11379 2547 2560 707 2548 +11380 2591 2573 2586 1063 +11381 2565 2550 2538 2535 +11382 698 707 701 2548 +11383 2550 698 701 2548 +11384 2536 2538 2539 2535 +11385 697 604 698 2548 +11386 2556 2559 724 734 +11387 2547 2546 2548 708 +11388 2546 604 2548 708 +11389 604 707 698 708 +11390 698 707 2548 708 +11391 604 698 2548 708 +11392 2617 2623 2618 2558 +11393 2556 2623 2617 2558 +11394 724 700 701 733 +11395 700 724 2557 733 +11396 700 2557 2555 733 +11397 701 700 2555 696 +11398 2555 700 733 696 +11399 2615 2630 663 2641 +11400 2543 2561 2559 2542 +11401 2616 2639 2618 719 +11402 665 668 667 716 +11403 730 715 721 716 +11404 2617 2623 2621 2618 +11405 2623 2556 717 2627 +11406 733 2555 696 2542 +11407 2559 724 733 2542 +11408 2557 2555 733 2542 +11409 2557 2561 2555 2542 +11410 724 2559 2557 2542 +11411 2559 2561 2557 2542 +11412 724 2557 733 2542 +11413 2617 2616 2618 719 +11414 718 2617 2618 719 +11415 655 654 671 656 +11416 660 661 662 656 +11417 2616 2617 2619 719 +11418 2616 2619 2639 719 +11419 2639 2619 721 719 +11420 718 2617 719 732 +11421 2617 2619 719 732 +11422 2619 721 719 732 +11423 2630 2615 2597 2641 +11424 721 2639 719 732 +11425 661 660 662 658 +11426 652 2462 659 651 +11427 666 654 2607 2602 +11428 2462 2606 2458 2463 +11429 2471 2457 2458 2463 +11430 2603 669 2599 2602 +11431 692 690 684 691 +11432 657 654 670 2602 +11433 4108 1664 4277 4667 +11434 714 735 2626 2625 +11435 1907 1909 1908 1910 +11436 2462 652 2606 2604 +11437 2596 2605 669 2603 +11438 670 2596 669 2599 +11439 669 2596 2603 2599 +11440 673 666 668 2613 +11441 2594 664 2600 674 +11442 2451 2460 653 2452 +11443 718 730 732 723 +11444 654 666 670 2602 +11445 670 666 2599 2602 +11446 2605 2610 655 2602 +11447 657 2605 655 2602 +11448 2596 2594 2600 674 +11449 2600 2611 673 668 +11450 2460 2451 2472 2452 +11451 2534 2607 2608 2612 +11452 2614 672 2612 2613 +11453 661 2470 2463 658 +11454 658 652 659 651 +11455 670 657 669 651 +11456 2450 2425 2426 2424 +11457 2422 2450 2426 2424 +11458 2422 2448 675 2424 +11459 961 1122 1104 1105 +11460 2450 2422 675 2424 +11461 2448 2425 680 2424 +11462 675 2448 680 2424 +11463 2421 2426 694 2428 +11464 2450 675 2432 2424 +11465 19 680 2447 682 +11466 144 1847 8 1848 +11467 2287 2274 1199 2286 +11468 2422 2426 693 689 +11469 2422 2433 2426 689 +11470 2421 2433 690 689 +11471 2433 2421 2426 689 +11472 2437 2439 2509 2443 +11473 2457 2471 2458 2461 +11474 2460 653 2452 2614 +11475 2474 2472 661 2463 +11476 2470 2474 661 2463 +11477 2470 661 662 658 +11478 683 2470 662 658 +11479 684 2473 688 2461 +11480 2470 2457 688 2461 +11481 2473 2470 688 2461 +11482 683 684 688 2461 +11483 2457 683 688 2461 +11484 683 2457 684 2461 +11485 2257 2256 2255 2258 +11486 2254 2259 2249 2247 +11487 2462 660 2458 659 +11488 652 2462 2458 659 +11489 2449 690 695 676 +11490 2242 2259 2243 2255 +11491 661 653 671 656 +11492 684 683 688 691 +11493 684 688 687 691 +11494 2467 692 684 691 +11495 2465 690 692 691 +11496 1818 1779 1781 1780 +11497 99 83 1776 72 +11498 2440 2436 2445 2434 +11499 1856 1859 1792 1867 +11500 2498 1850 1851 1845 +11501 2531 59 62 40 +11502 62 2529 2533 40 +11503 3503 3514 3418 4194 +11504 640 3882 4009 4374 +11505 2483 2485 2479 2490 +11506 2309 2307 581 587 +11507 2271 2309 581 588 +11508 2501 2483 2502 2503 +11509 2489 2485 2483 2503 +11510 2489 2483 2506 2503 +11511 2486 2484 2532 2503 +11512 2483 2485 2486 2503 +11513 2485 2484 2486 2503 +11514 57 2485 2479 61 +11515 58 2489 2484 61 +11516 57 58 2484 61 +11517 2485 2489 2483 61 +11518 2484 2489 2485 61 +11519 57 2484 2485 61 +11520 203 1829 145 1832 +11521 2426 693 689 694 +11522 2486 2531 59 62 +11523 2526 2486 59 62 +11524 2486 2526 2531 62 +11525 2317 2272 2285 2318 +11526 2317 2290 2299 593 +11527 2514 2508 2533 2510 +11528 2526 2532 2531 2533 +11529 2526 2531 62 2533 +11530 2529 2526 62 2533 +11531 203 1829 1832 1881 +11532 2446 2425 2423 2437 +11533 2516 2508 2514 2510 +11534 2507 2519 2443 2518 +11535 2510 2521 5 2518 +11536 2516 2514 2530 2510 +11537 60 2516 2530 2510 +11538 2527 681 678 41 +11539 2432 2424 677 2442 +11540 702 580 3303 4273 +11541 2446 2515 2447 2437 +11542 2439 2446 2447 2437 +11543 2514 2508 2517 2438 +11544 2515 19 2447 2437 +11545 2196 2159 2160 2199 +11546 2447 682 679 678 +11547 690 695 676 694 +11548 690 689 695 694 +11549 2519 2512 2443 2441 +11550 2446 2515 2437 2438 +11551 2515 2446 2517 2438 +11552 2245 2242 2257 2255 +11553 615 2251 624 2262 +11554 854 615 624 2262 +11555 2257 2235 2234 2258 +11556 870 2235 869 2232 +11557 854 615 2262 856 +11558 617 2251 2248 2206 +11559 1627 736 738 4341 +11560 2254 854 2262 856 +11561 2251 2262 2250 2252 +11562 2240 2242 2243 2255 +11563 854 2236 2256 2250 +11564 625 854 2232 2236 +11565 2259 625 878 2236 +11566 855 625 2232 2236 +11567 625 855 878 2236 +11568 2256 2236 2255 2258 +11569 855 2259 878 2236 +11570 2254 625 2249 2256 +11571 2259 2254 2249 2256 +11572 625 2259 2249 2256 +11573 2224 2261 2260 2225 +11574 2242 2240 2257 2255 +11575 2259 855 2243 2255 +11576 855 2259 2236 2255 +11577 2232 855 2236 2255 +11578 2240 2243 869 2255 +11579 2257 2240 869 2255 +11580 2243 855 869 2255 +11581 869 855 2232 2255 +11582 2257 869 2232 2255 +11583 2237 2233 2238 2247 +11584 2440 2435 2436 2434 +11585 2253 2207 2210 2206 +11586 617 630 618 614 +11587 865 856 2247 630 +11588 865 866 2233 2247 +11589 2233 866 2249 2247 +11590 866 865 856 2247 +11591 2254 625 856 2247 +11592 625 2254 2249 2247 +11593 625 866 856 2247 +11594 866 625 2249 2247 +11595 2208 2246 2253 2206 +11596 1004 986 1020 1019 +11597 624 2251 617 2206 +11598 2254 2246 2247 630 +11599 856 2254 2247 630 +11600 2246 2237 2247 630 +11601 2246 2254 2262 630 +11602 2262 2254 856 630 +11603 2208 2212 2248 2260 +11604 2455 685 2464 676 +11605 2237 2233 629 2260 +11606 687 2455 2464 676 +11607 2237 865 2233 630 +11608 2233 865 2247 630 +11609 2237 2233 2247 630 +11610 2294 2314 2319 2229 +11611 2214 2211 2210 2216 +11612 2221 2211 2217 2216 +11613 2253 2228 2209 2230 +11614 2221 2215 2293 2291 +11615 1464 474 410 116 +11616 2231 2227 2228 2230 +11617 2211 2214 2209 2216 +11618 2215 2209 2230 2293 +11619 2214 2215 2209 2216 +11620 2209 2215 2230 2216 +11621 646 620 647 635 +11622 2231 2305 2223 2301 +11623 2253 2224 2261 618 +11624 630 865 614 629 +11625 616 2253 2210 618 +11626 616 2210 617 618 +11627 2208 2253 2261 618 +11628 617 2210 2206 618 +11629 2210 2253 2206 618 +11630 2305 2302 2223 2301 +11631 2208 617 2206 618 +11632 2253 2208 2206 618 +11633 866 865 2233 629 +11634 617 630 614 629 +11635 617 615 630 629 +11636 615 617 2248 629 +11637 2248 617 614 629 +11638 2213 2224 613 2225 +11639 613 2227 2228 2225 +11640 2227 2222 2228 2225 +11641 2213 613 2228 2225 +11642 2222 2213 2228 2225 +11643 2292 2294 2295 2319 +11644 3594 4701 4710 4726 +11645 2306 2308 636 2319 +11646 2226 613 620 635 +11647 2306 636 2315 2319 +11648 2308 638 636 2319 +11649 2294 2292 2317 2319 +11650 746 740 736 4341 +11651 638 2314 2220 2218 +11652 4262 3101 4356 4427 +11653 2435 2439 2441 2442 +11654 2299 2306 2308 636 +11655 2525 2521 2477 2518 +11656 2525 41 5 2518 +11657 2450 2425 2424 2442 +11658 2218 2302 647 635 +11659 2271 2309 588 2313 +11660 2316 637 587 2311 +11661 2227 2231 2223 2301 +11662 2227 2226 2231 2301 +11663 2295 2299 2308 636 +11664 2299 2295 638 636 +11665 638 2295 2308 636 +11666 2521 2525 5 2518 +11667 2302 2305 647 635 +11668 2231 2305 2301 635 +11669 2305 646 647 635 +11670 2522 2525 2477 2518 +11671 2172 2173 2176 2177 +11672 2302 2218 2301 635 +11673 2295 2294 638 2319 +11674 2294 2314 638 2319 +11675 638 2314 2315 2319 +11676 1208 2266 1210 1209 +11677 2271 2310 2311 2313 +11678 2304 2296 2315 2303 +11679 636 2306 2315 2303 +11680 2290 2304 2315 2303 +11681 2306 2290 2315 2303 +11682 2315 2296 634 2303 +11683 636 2315 634 2303 +11684 2296 636 634 2303 +11685 2228 2213 2209 619 +11686 2213 616 2209 619 +11687 2207 2211 2209 619 +11688 2211 2207 616 619 +11689 616 2207 2209 619 +11690 634 2218 647 635 +11691 2296 2302 2218 2301 +11692 2226 2227 2225 2301 +11693 2209 2211 2216 619 +11694 2221 2217 646 2293 +11695 2221 2215 2217 2293 +11696 2440 2435 2442 2436 +11697 2226 2217 646 620 +11698 2512 2511 2519 2443 +11699 2447 2439 2437 679 +11700 2507 2508 2519 2518 +11701 1797 1875 1864 1862 +11702 2217 2226 2230 620 +11703 2228 613 619 620 +11704 2217 2230 2216 620 +11705 2217 2216 619 620 +11706 2226 2227 2230 620 +11707 2227 2226 613 620 +11708 2227 2228 2230 620 +11709 2227 613 2228 620 +11710 2209 2228 619 620 +11711 2228 2209 2230 620 +11712 2230 2209 2216 620 +11713 2216 2209 619 620 +11714 2314 2220 2218 2229 +11715 2302 2314 2218 2229 +11716 646 2226 620 635 +11717 2444 2511 2443 678 +11718 19 2437 2509 678 +11719 638 634 2218 647 +11720 2264 1200 1208 583 +11721 646 638 2220 647 +11722 2220 638 2218 647 +11723 2220 2218 2229 647 +11724 2218 2302 2229 647 +11725 2219 2220 2229 647 +11726 2219 646 2220 647 +11727 2302 2305 2229 647 +11728 2305 646 2293 647 +11729 2305 2293 2229 647 +11730 646 2219 2293 647 +11731 2293 2219 2229 647 +11732 2287 2269 2274 2288 +11733 2271 2269 2270 2267 +11734 218 219 2037 2017 +11735 2521 2487 2531 2520 +11736 1795 1798 1862 1792 +11737 2266 1208 1206 1209 +11738 4082 4433 4547 4651 +11739 1841 1874 1838 1833 +11740 1843 2132 2176 2168 +11741 2269 581 2274 583 +11742 634 2296 2218 2300 +11743 60 2529 62 5 +11744 2169 1844 2176 2177 +11745 583 581 588 2267 +11746 2269 2287 2265 2285 +11747 2271 2307 2270 2285 +11748 2269 2271 2270 2285 +11749 2307 2273 2270 2285 +11750 2273 2307 2265 2285 +11751 2287 2273 2265 2285 +11752 581 2269 2265 2285 +11753 2307 581 2265 2285 +11754 2271 2269 581 2285 +11755 2307 2271 581 2285 +11756 2482 40 2523 2524 +11757 215 1880 226 1871 +11758 2307 2273 2265 2298 +11759 2307 2309 2270 587 +11760 2273 2307 2270 587 +11761 2531 62 2533 40 +11762 3131 3714 4513 4641 +11763 2170 2155 2173 2177 +11764 637 2309 587 2297 +11765 4178 3386 4244 4312 +11766 2150 1541 1537 1539 +11767 587 2309 2311 2297 +11768 2317 2290 593 2318 +11769 2316 2270 587 2318 +11770 2316 587 593 2318 +11771 2317 593 2298 2318 +11772 2272 2317 2298 2318 +11773 2270 2273 587 2318 +11774 587 2273 593 2318 +11775 593 2273 2298 2318 +11776 2273 2272 2298 2318 +11777 636 2306 2303 2300 +11778 2290 2299 593 2300 +11779 637 2290 593 2300 +11780 2290 2306 2299 2300 +11781 2299 2306 636 2300 +11782 636 637 593 2300 +11783 2299 636 593 2300 +11784 2316 2270 2312 2311 +11785 2309 2271 2270 2311 +11786 2270 2271 2312 2311 +11787 637 2316 2290 2297 +11788 2290 2316 2312 2297 +11789 2290 2312 2303 2297 +11790 2290 2303 2300 2297 +11791 3131 3644 3646 4125 +11792 2271 588 2267 2313 +11793 2310 2271 2267 2313 +11794 583 588 2313 2266 +11795 588 583 2267 2266 +11796 974 1005 1008 930 +11797 2268 2310 2266 1206 +11798 1200 1208 583 578 +11799 1270 1702 4032 4335 +11800 1874 1841 1831 1833 +11801 1853 1830 1831 1833 +11802 1395 1393 1120 4066 +11803 1393 1118 1120 4066 +11804 583 2267 2266 2288 +11805 2274 2269 583 2288 +11806 2264 2275 2274 1199 +11807 583 2269 2267 2288 +11808 1017 1018 1022 4340 +11809 2264 2263 2275 1199 +11810 2264 2263 1199 2286 +11811 1401 1391 1393 4066 +11812 2264 1200 2284 2286 +11813 1148 1189 1191 4129 +11814 976 975 952 1106 +11815 974 924 966 930 +11816 4433 4082 4540 4651 +11817 4404 4082 4547 4651 +11818 4082 4294 4540 4651 +11819 1266 1146 1171 1147 +11820 1153 794 795 1154 +11821 1322 1318 1319 1315 +11822 1706 1707 1672 4166 +11823 1122 978 1093 977 +11824 943 926 941 942 +11825 321 607 702 4273 +11826 3121 3160 1273 3168 +11827 1398 1400 4040 4197 +11828 1594 1592 1563 1579 +11829 3541 3540 3578 4490 +11830 1648 753 1649 4240 +11831 1592 1593 1563 1579 +11832 1556 1558 1555 1615 +11833 740 1218 1217 1562 +11834 2863 2894 3271 1137 +11835 2890 725 3271 1137 +11836 1005 974 1008 972 +11837 2280 1620 1616 4182 +11838 979 976 961 977 +11839 1173 1171 1157 1141 +11840 3999 301 3981 4516 +11841 974 1012 1008 972 +11842 3214 3211 4070 4316 +11843 1773 1142 1181 4568 +11844 1084 1095 1094 1085 +11845 1005 974 966 930 +11846 1005 948 1008 930 +11847 1008 948 1011 930 +11848 924 1005 966 930 +11849 1005 924 948 930 +11850 948 924 1011 930 +11851 966 924 943 971 +11852 974 1100 1087 1086 +11853 3151 3023 3183 4475 +11854 4013 1084 4122 4179 +11855 949 1007 971 1006 +11856 1005 948 966 1006 +11857 1005 966 971 1006 +11858 924 949 971 1006 +11859 924 923 949 1006 +11860 966 924 971 1006 +11861 923 924 948 1006 +11862 948 924 966 1006 +11863 926 943 941 947 +11864 3259 982 3048 983 +11865 1090 955 983 936 +11866 975 976 952 947 +11867 1080 1077 4083 4208 +11868 924 966 943 947 +11869 1234 1275 1273 3168 +11870 1101 1025 969 959 +11871 924 939 966 947 +11872 953 924 946 947 +11873 952 953 946 947 +11874 939 956 936 950 +11875 924 953 939 947 +11876 939 953 952 947 +11877 954 939 952 947 +11878 939 954 966 947 +11879 966 954 952 947 +11880 966 1007 977 1261 +11881 961 976 947 977 +11882 978 961 947 977 +11883 966 943 947 977 +11884 966 1007 943 977 +11885 1754 1752 1711 4014 +11886 1262 3186 3143 983 +11887 3224 3186 1262 983 +11888 1071 1619 4182 4419 +11889 1606 2579 4361 4419 +11890 3103 3097 703 4123 +11891 3276 3338 4002 4097 +11892 3940 2872 4228 4247 +11893 3686 3690 3405 4180 +11894 966 974 995 994 +11895 970 1007 966 1261 +11896 3690 3686 3688 4180 +11897 990 979 977 1261 +11898 966 974 994 1261 +11899 977 979 983 1261 +11900 995 996 994 1261 +11901 974 996 995 1261 +11902 960 3222 3226 983 +11903 1262 960 3226 983 +11904 3244 3259 3048 983 +11905 3298 3244 3143 983 +11906 3244 3298 3259 983 +11907 979 947 977 983 +11908 1084 970 4122 4179 +11909 1009 931 1010 928 +11910 3305 3244 3048 983 +11911 2660 434 2677 4549 +11912 1077 1080 1646 4208 +11913 324 3214 4070 4316 +11914 3244 3305 3304 983 +11915 3244 3304 3143 983 +11916 3304 3305 3048 983 +11917 976 952 947 983 +11918 979 976 947 983 +11919 976 979 952 983 +11920 954 958 952 983 +11921 979 954 952 983 +11922 954 979 958 983 +11923 3304 3314 3143 983 +11924 3048 3314 3242 983 +11925 3304 3048 3242 983 +11926 3314 3304 3242 983 +11927 3048 3143 2753 983 +11928 3314 3048 2753 983 +11929 3143 3314 2753 983 +11930 808 1608 1221 165 +11931 1266 1146 1735 4098 +11932 1313 1124 1130 1083 +11933 996 1089 1125 1331 +11934 3868 3264 3786 4713 +11935 3598 3599 4001 4137 +11936 934 1262 996 1088 +11937 956 936 1024 951 +11938 1024 936 938 951 +11939 974 924 932 936 +11940 924 974 966 936 +11941 936 932 938 951 +11942 982 979 983 959 +11943 710 706 3107 4378 +11944 1092 3156 1090 985 +11945 955 982 983 969 +11946 3454 2783 4373 4416 +11947 3118 3119 711 4086 +11948 4096 4122 4179 4379 +11949 955 936 969 959 +11950 983 955 969 959 +11951 955 983 936 959 +11952 932 1013 929 1026 +11953 1578 1243 1197 4285 +11954 1262 934 1090 1088 +11955 996 1331 1330 1088 +11956 1096 1097 4196 4207 +11957 2782 3595 3529 4236 +11958 924 1013 932 938 +11959 1512 1515 430 4216 +11960 932 933 973 1016 +11961 934 996 1330 1091 +11962 1092 934 935 1091 +11963 935 934 1330 1091 +11964 939 924 936 940 +11965 933 932 929 1016 +11966 929 932 1026 1016 +11967 996 1262 1261 1088 +11968 1018 1132 4013 4207 +11969 1131 1132 1019 4207 +11970 933 1089 1125 1088 +11971 932 933 1125 1088 +11972 1113 1740 1400 4223 +11973 1089 996 1125 1088 +11974 1125 996 1261 1088 +11975 1262 1090 983 1088 +11976 983 1090 1261 1088 +11977 1262 983 1261 1088 +11978 1090 932 936 1088 +11979 1261 1090 936 1088 +11980 932 974 936 1088 +11981 974 932 1125 1088 +11982 974 1125 1261 1088 +11983 966 1261 936 1088 +11984 974 966 936 1088 +11985 966 974 1261 1088 +11986 2101 39 2097 4212 +11987 4030 4127 4136 4149 +11988 1516 1371 420 4216 +11989 935 1092 1091 1313 +11990 1092 935 1330 1313 +11991 1330 935 1091 1313 +11992 1108 1089 1088 1124 +11993 1088 1089 1313 1124 +11994 1091 1108 1088 1124 +11995 1091 1088 1313 1124 +11996 3268 4176 4187 4378 +11997 1024 936 1014 938 +11998 936 1013 1014 938 +11999 1320 1214 752 4100 +12000 1009 933 1125 928 +12001 974 1009 1125 928 +12002 933 974 1125 928 +12003 974 924 930 928 +12004 930 924 931 928 +12005 974 930 931 928 +12006 578 1200 4065 4673 +12007 956 939 936 940 +12008 956 936 951 940 +12009 1184 1603 4616 4678 +12010 4247 4228 4266 4691 +12011 1268 4027 4032 4655 +12012 955 956 969 950 +12013 956 955 936 950 +12014 936 955 969 950 +12015 1082 1130 1083 4342 +12016 2579 1604 1603 4678 +12017 737 739 4110 4290 +12018 1258 1271 1259 1702 +12019 3323 3335 2690 4344 +12020 1606 2579 4616 4678 +12021 3057 3042 3062 4148 +12022 1666 1760 4152 4525 +12023 1678 1681 1680 1687 +12024 1512 430 204 4216 +12025 3175 3554 3566 1259 +12026 1719 3069 1704 1258 +12027 3069 1719 1260 1258 +12028 705 3095 711 4618 +12029 3160 3126 1273 3168 +12030 3171 2798 3155 1258 +12031 3155 2798 1260 1258 +12032 3171 3175 3566 1258 +12033 3069 3171 3566 1258 +12034 3069 3566 1704 1258 +12035 3171 3069 2798 1258 +12036 2798 3069 1260 1258 +12037 1124 1082 1130 1083 +12038 1650 1649 752 4697 +12039 750 753 751 4233 +12040 1514 431 420 4216 +12041 3276 2853 2828 4097 +12042 4341 1557 4496 4563 +12043 345 1689 4554 4662 +12044 1089 1125 1331 998 +12045 1645 815 4055 4314 +12046 2579 1606 1197 4419 +12047 1714 1667 1713 4351 +12048 3500 3386 3504 4178 +12049 1189 1917 1205 1191 +12050 4024 1241 4035 4357 +12051 3056 3134 4044 4528 +12052 1682 1247 1684 1687 +12053 1247 1678 1680 1687 +12054 1550 1071 1564 4454 +12055 1598 1583 737 4058 +12056 710 3106 3109 4378 +12057 1256 1272 1323 1257 +12058 1272 1256 1275 1257 +12059 793 815 816 56 +12060 2815 3988 4219 4550 +12061 1225 742 741 1224 +12062 3128 3121 1273 3168 +12063 727 3128 1273 3168 +12064 1271 1275 1257 3168 +12065 986 1004 1020 4085 +12066 727 704 3128 3168 +12067 3128 3130 3121 3168 +12068 3121 3130 3126 3168 +12069 3130 1256 3126 3168 +12070 3863 3312 2855 4682 +12071 704 710 3128 3168 +12072 1256 1271 1257 3168 +12073 1256 3130 1271 3168 +12074 764 774 1201 2352 +12075 3549 3548 3540 4094 +12076 819 786 1225 817 +12077 3300 3291 3312 4187 +12078 3402 3491 3368 4723 +12079 1227 2351 1195 1224 +12080 1220 1228 1615 1193 +12081 1275 1312 1273 1232 +12082 1312 1311 1321 1232 +12083 786 347 611 787 +12084 2577 1220 1554 1195 +12085 1555 1554 1193 1195 +12086 1275 1273 3168 1232 +12087 3005 3186 3181 4495 +12088 2652 1503 438 4346 +12089 1072 2580 2588 1060 +12090 1548 1575 1574 1549 +12091 1575 1548 1573 1549 +12092 2351 1228 1195 1224 +12093 3936 2984 1044 4651 +12094 1228 1558 1195 1224 +12095 1558 1215 1195 1224 +12096 2576 611 2578 1194 +12097 1554 1220 1193 1195 +12098 1556 1558 1228 1224 +12099 1625 2349 1626 736 +12100 3160 3161 3129 342 +12101 1417 1416 1733 1765 +12102 1575 1548 1574 1552 +12103 3113 706 4218 4305 +12104 814 1218 1320 1217 +12105 1594 814 1320 1217 +12106 814 1594 740 1217 +12107 1218 814 740 1217 +12108 1556 1612 1228 1615 +12109 1228 1612 1613 1615 +12110 1218 1562 1563 1579 +12111 1594 740 1217 1562 +12112 814 1594 1320 1601 +12113 987 1255 4254 4599 +12114 822 1198 1918 4089 +12115 1198 184 1636 4089 +12116 1594 1592 740 1563 +12117 1594 740 1562 1563 +12118 1317 1547 4199 4209 +12119 779 770 769 2355 +12120 1656 3287 1638 4022 +12121 3603 2787 4307 4325 +12122 1089 1082 1125 4342 +12123 1558 1555 1615 1195 +12124 1228 1556 1615 1195 +12125 1220 2577 1193 1195 +12126 1618 2577 611 1620 +12127 611 2577 2280 1620 +12128 1618 611 2280 1620 +12129 786 347 1225 4021 +12130 1228 1615 1193 1195 +12131 1061 2587 2588 1060 +12132 1066 1072 1616 1070 +12133 1073 1066 1616 1070 +12134 1616 2577 1070 1069 +12135 2577 2580 1070 1069 +12136 1676 1668 4108 4277 +12137 1601 1320 752 4100 +12138 3284 3161 726 1137 +12139 347 786 1225 1555 +12140 1888 1907 216 228 +12141 1003 1002 984 4122 +12142 1264 1030 1029 4407 +12143 1004 1023 1020 4085 +12144 3907 3549 3904 4094 +12145 2356 779 778 2355 +12146 741 1228 2351 1224 +12147 3287 3933 1638 4022 +12148 1216 347 346 348 +12149 2358 2356 2329 2355 +12150 742 741 1224 743 +12151 1556 1558 1615 1195 +12152 1314 348 4052 4718 +12153 1112 1111 1127 1403 +12154 3541 3543 3540 4094 +12155 2970 3322 3335 4431 +12156 1734 1735 4098 4364 +12157 1914 1920 149 4355 +12158 1560 814 1320 1561 +12159 1211 750 4169 4240 +12160 1224 1195 2578 1194 +12161 3089 710 3112 4575 +12162 1591 1598 1600 737 +12163 1555 1558 1215 1195 +12164 2577 1193 1195 1194 +12165 4024 1656 4035 4059 +12166 1591 1602 739 1601 +12167 3514 3418 4194 4369 +12168 2838 3335 4431 4456 +12169 1592 1594 740 1601 +12170 1602 1592 740 1601 +12171 1602 740 739 1601 +12172 1648 1652 1649 84 +12173 2501 2502 2479 4170 +12174 2782 2784 4031 4440 +12175 1053 1056 1213 1597 +12176 1053 1056 1597 1050 +12177 3514 3502 4156 4369 +12178 814 1136 3284 1137 +12179 3157 3161 3283 1137 +12180 3161 3157 726 1137 +12181 726 3157 3283 1137 +12182 1136 814 1212 1137 +12183 1212 814 1138 1137 +12184 1136 1212 1138 1137 +12185 1073 1196 1185 4424 +12186 1138 1663 1137 1651 +12187 725 1138 1137 1651 +12188 2661 1652 1662 1633 +12189 4279 2667 4386 4564 +12190 1663 1657 1137 1651 +12191 1652 725 1658 1633 +12192 1657 725 1137 1633 +12193 1137 725 1651 1633 +12194 1657 1137 1651 1633 +12195 201 197 1031 1035 +12196 3699 3131 3646 4128 +12197 1055 1203 2028 85 +12198 347 1215 4063 4133 +12199 739 1591 737 4441 +12200 2658 197 1031 1032 +12201 3418 4156 4194 4369 +12202 3887 2971 295 4139 +12203 1054 2030 1036 1035 +12204 1624 775 1599 1596 +12205 2129 3559 2133 4225 +12206 1053 1054 2030 1052 +12207 1056 1053 1213 1052 +12208 775 1048 1049 1047 +12209 2658 197 2662 1031 +12210 201 2016 2029 85 +12211 219 2037 2021 2042 +12212 740 1562 1563 4088 +12213 4049 1734 4098 4364 +12214 3284 3160 3116 342 +12215 3116 727 1273 342 +12216 3284 3116 1273 342 +12217 1321 1312 1232 343 +12218 727 3160 3129 342 +12219 3161 3160 3284 342 +12220 3157 3161 726 342 +12221 1312 1234 1273 343 +12222 710 704 728 345 +12223 1312 1273 1232 343 +12224 3161 727 3129 342 +12225 814 1136 1212 342 +12226 3284 1273 1233 342 +12227 1233 1273 343 342 +12228 3160 727 3116 342 +12229 1318 1231 1314 346 +12230 1273 1234 1233 343 +12231 1315 347 339 346 +12232 704 727 728 343 +12233 1401 1393 4146 4232 +12234 1273 727 3168 343 +12235 1273 3168 1232 343 +12236 1234 3284 1233 342 +12237 1234 1233 343 342 +12238 1234 1136 3284 342 +12239 794 1153 795 796 +12240 1570 1571 1642 1635 +12241 1216 347 1315 346 +12242 3312 4073 4449 4502 +12243 1215 1555 1195 1224 +12244 727 704 3168 343 +12245 3386 3500 3504 4723 +12246 346 1231 348 345 +12247 1228 1559 4088 4161 +12248 1612 1228 1614 4161 +12249 728 704 346 345 +12250 3958 4498 4685 4759 +12251 728 704 343 346 +12252 2884 3342 2704 4038 +12253 1191 1192 165 815 +12254 1321 1315 339 346 +12255 1321 1322 1315 346 +12256 1614 1228 740 4161 +12257 3168 1232 343 346 +12258 704 3168 343 346 +12259 1749 1751 1750 4422 +12260 1554 2577 1195 1224 +12261 1555 1225 1554 1224 +12262 1555 1554 1195 1224 +12263 1225 786 1215 1224 +12264 786 1225 1555 1224 +12265 1559 741 740 1224 +12266 3138 3134 4235 4472 +12267 742 1225 740 1224 +12268 347 786 740 743 +12269 786 1225 817 787 +12270 1225 1224 2578 1194 +12271 1225 2281 817 787 +12272 2576 611 2281 2578 +12273 2281 2576 2578 609 +12274 2570 596 595 602 +12275 2583 603 2582 602 +12276 741 742 748 743 +12277 2349 1202 1626 736 +12278 1225 2577 1224 1194 +12279 2577 1225 611 1194 +12280 611 1225 787 1194 +12281 611 2281 2578 1194 +12282 2281 611 787 1194 +12283 2281 1225 2578 1194 +12284 1225 2281 787 1194 +12285 750 1213 1597 737 +12286 1652 725 1649 84 +12287 1583 747 1597 737 +12288 778 779 769 2355 +12289 2737 2735 2802 4118 +12290 2576 2573 2592 1061 +12291 408 3899 4039 4054 +12292 744 746 736 749 +12293 999 1130 4115 4342 +12294 3061 4230 4443 4474 +12295 1228 1068 1614 1067 +12296 2816 4077 4219 4645 +12297 1650 725 1651 752 +12298 775 1047 1599 1596 +12299 1583 1037 1047 1595 +12300 1048 775 1049 1201 +12301 822 184 1198 4089 +12302 3312 3094 576 4451 +12303 2351 1227 2350 1067 +12304 1037 1049 1047 1599 +12305 3128 3130 4032 4291 +12306 1086 4172 4214 4342 +12307 1625 1624 1628 1627 +12308 1740 1400 4223 4497 +12309 1625 1628 1557 1627 +12310 771 2356 2367 2358 +12311 2344 1049 764 1051 +12312 200 2016 2015 2019 +12313 1049 775 1047 1599 +12314 746 2354 736 749 +12315 746 748 2354 749 +12316 582 1200 578 4672 +12317 2363 2346 1202 2364 +12318 744 2348 1626 736 +12319 2354 1202 749 2353 +12320 603 599 595 602 +12321 770 779 771 2355 +12322 2357 2347 2329 2355 +12323 2356 2357 2329 2355 +12324 2346 744 749 2353 +12325 3545 3400 3581 4369 +12326 2346 2349 1626 2347 +12327 2281 817 787 609 +12328 2697 2886 2703 4162 +12329 2348 2346 1626 2347 +12330 771 779 2356 2355 +12331 2268 1207 4484 4567 +12332 2697 2699 2698 4162 +12333 778 769 2347 2355 +12334 2356 2357 778 2347 +12335 1211 750 1213 4169 +12336 2591 2566 2537 2586 +12337 2361 2362 2329 2355 +12338 3663 3636 4125 4358 +12339 3663 3637 3636 4358 +12340 2502 2479 4007 4326 +12341 1031 4184 4281 4560 +12342 750 1600 1213 737 +12343 2349 1625 1627 736 +12344 2349 2350 1202 736 +12345 2350 746 2354 736 +12346 1202 2350 2354 736 +12347 1408 1770 1417 4406 +12348 2350 2349 1627 736 +12349 2362 771 2330 2329 +12350 2361 770 2355 2365 +12351 2364 2361 2355 2365 +12352 2346 2364 2347 2365 +12353 2575 594 2570 596 +12354 2346 1202 2364 2353 +12355 770 771 2362 2329 +12356 2330 771 2358 2329 +12357 2331 2330 2358 2329 +12358 778 2346 2347 2365 +12359 770 769 2355 2365 +12360 2356 2357 2358 2329 +12361 770 2361 2362 2365 +12362 1191 1148 4026 4271 +12363 740 742 1224 743 +12364 600 2589 605 2568 +12365 599 2571 595 602 +12366 599 2574 2571 602 +12367 2576 2575 2578 609 +12368 2578 2575 596 609 +12369 611 2281 787 609 +12370 611 2576 2281 609 +12371 4397 963 4712 4725 +12372 594 611 787 609 +12373 1623 745 774 738 +12374 1624 775 1596 738 +12375 2345 1623 774 738 +12376 710 3128 4032 4291 +12377 1082 1083 4214 4342 +12378 1623 2345 1622 738 +12379 775 2345 1049 738 +12380 1049 2345 774 738 +12381 3089 3715 4202 4291 +12382 713 710 4032 4291 +12383 1625 1627 736 738 +12384 1625 1624 1627 738 +12385 775 1624 2345 738 +12386 2345 1624 1622 738 +12387 1624 1625 1622 738 +12388 1623 2348 736 738 +12389 2348 1623 1622 738 +12390 1622 1625 1626 738 +12391 1626 1625 736 738 +12392 2348 1622 1626 738 +12393 2348 1626 736 738 +12394 1212 1650 1651 752 +12395 814 1212 1651 752 +12396 740 739 1601 752 +12397 740 814 739 752 +12398 739 1591 1601 752 +12399 1601 1591 1600 752 +12400 3700 3701 3703 4101 +12401 1214 1650 1212 752 +12402 814 1214 1212 752 +12403 1594 740 1601 752 +12404 814 1594 1601 752 +12405 1594 814 740 752 +12406 1320 814 1601 752 +12407 814 1320 1214 752 +12408 1492 1476 458 4141 +12409 3093 3700 3703 4101 +12410 3701 3646 3705 4101 +12411 2661 1648 1036 84 +12412 4205 3061 4443 4474 +12413 3335 3336 2690 4344 +12414 3679 3105 4310 4382 +12415 1213 750 1597 751 +12416 1056 1213 1597 751 +12417 1597 747 1047 751 +12418 1056 1597 1047 751 +12419 3559 1844 4018 4225 +12420 1056 1631 1052 751 +12421 2479 2502 2490 4170 +12422 1691 1244 4351 4396 +12423 1048 1056 1047 751 +12424 747 1048 1047 751 +12425 1048 747 1056 751 +12426 1048 1049 1051 1201 +12427 774 1049 738 1201 +12428 775 774 738 1201 +12429 1049 775 738 1201 +12430 2344 764 2368 2352 +12431 764 2344 1051 2352 +12432 1051 2344 2368 2352 +12433 2030 1054 2016 2031 +12434 3888 299 3893 4538 +12435 202 2016 200 2029 +12436 201 202 200 2029 +12437 3536 3112 3105 4382 +12438 2016 1055 2028 85 +12439 229 222 2014 2042 +12440 1513 1512 2041 2038 +12441 1517 2018 2044 2038 +12442 219 2037 2017 2015 +12443 2585 2581 2548 2549 +12444 202 219 2017 2015 +12445 2971 2840 590 4139 +12446 2016 202 200 2015 +12447 200 202 2017 2015 +12448 229 219 202 2015 +12449 2014 229 202 2015 +12450 2013 2014 202 2015 +12451 2016 2013 202 2015 +12452 222 229 2014 2015 +12453 2017 200 2015 2019 +12454 222 219 229 2015 +12455 2020 2015 2021 2019 +12456 2020 2017 2015 2019 +12457 3335 576 590 4139 +12458 1608 1221 1191 4661 +12459 1593 1563 1557 4088 +12460 2016 200 2029 2019 +12461 348 826 345 4319 +12462 2020 200 2017 2019 +12463 1228 1559 741 4088 +12464 741 1559 740 4088 +12465 2020 2028 2029 2019 +12466 3624 2782 3593 4440 +12467 3380 3620 3596 4440 +12468 3371 3624 3593 4440 +12469 3380 2782 4031 4440 +12470 2782 3380 3596 4440 +12471 1055 1054 85 2031 +12472 1054 1055 2016 2031 +12473 2016 1055 85 2031 +12474 2782 3596 3593 4440 +12475 3371 3081 3624 4440 +12476 2574 2583 600 2549 +12477 2572 2574 600 2549 +12478 2583 603 600 2549 +12479 2583 2581 2582 2549 +12480 603 2583 2582 2549 +12481 603 601 600 2549 +12482 600 601 606 2549 +12483 2581 601 2582 2549 +12484 601 603 2582 2549 +12485 604 601 2581 2549 +12486 2585 604 2581 2549 +12487 1060 1072 1059 1062 +12488 1295 1167 1164 1163 +12489 601 709 606 2549 +12490 4727 4270 4760 4774 +12491 604 709 601 2549 +12492 2589 2540 2566 2537 +12493 2573 2572 2591 2586 +12494 2984 3328 1044 4651 +12495 29 30 23 54 +12496 2536 2537 2584 2586 +12497 2589 600 606 2537 +12498 606 600 2549 2537 +12499 709 606 2549 2537 +12500 604 709 2549 2537 +12501 2585 604 2549 2537 +12502 606 709 697 2590 +12503 2537 2585 2536 2584 +12504 2572 2585 2549 2584 +12505 2549 2585 2537 2584 +12506 600 2549 2537 2584 +12507 600 2572 2549 2584 +12508 2589 600 2537 2584 +12509 2589 2572 600 2584 +12510 2579 1230 1603 1229 +12511 2695 2692 2690 4344 +12512 633 3273 4317 4622 +12513 2280 1546 1198 824 +12514 1901 1970 1900 1897 +12515 2713 1046 1039 1038 +12516 2713 3610 3626 1038 +12517 1045 2713 3626 1038 +12518 1039 3557 2723 1038 +12519 2723 3557 3610 1038 +12520 2713 1039 2723 1038 +12521 2713 2723 3610 1038 +12522 1895 1923 1932 1915 +12523 2135 466 1040 2137 +12524 2136 2135 1040 2137 +12525 1039 3557 1038 2137 +12526 3557 1039 2136 2137 +12527 2136 1039 1038 2137 +12528 466 3557 1040 2137 +12529 3557 2136 1040 2137 +12530 2134 2125 2126 2127 +12531 2125 2134 21 2127 +12532 3590 2140 3591 54 +12533 2125 2128 2126 2127 +12534 2128 2125 24 2127 +12535 2125 21 24 2127 +12536 1886 1926 209 214 +12537 1926 183 209 214 +12538 466 21 24 488 +12539 3627 3618 2144 488 +12540 3618 3627 3617 488 +12541 487 480 466 488 +12542 2145 3627 2144 488 +12543 3611 487 3617 488 +12544 3627 3611 3617 488 +12545 3611 3627 2125 488 +12546 480 487 3611 488 +12547 480 3611 466 488 +12548 21 2125 24 488 +12549 2145 2144 2128 488 +12550 2125 3627 2145 488 +12551 2264 1200 4484 4673 +12552 466 2125 21 488 +12553 2125 2128 24 488 +12554 2125 2145 2128 488 +12555 2571 600 605 2568 +12556 2589 606 605 2568 +12557 610 2571 605 2568 +12558 606 610 605 2568 +12559 212 180 1914 1929 +12560 225 1905 164 1922 +12561 2571 599 595 598 +12562 2570 2571 595 598 +12563 594 2570 595 598 +12564 610 2571 2591 598 +12565 610 2591 597 598 +12566 2576 594 597 598 +12567 2576 2575 594 598 +12568 2575 2571 2570 598 +12569 594 2575 2570 598 +12570 2575 2576 2571 598 +12571 2571 2576 2591 598 +12572 2567 2576 597 598 +12573 1079 1920 1919 149 +12574 2576 2573 2591 598 +12575 750 1211 1213 4100 +12576 1923 179 1932 1915 +12577 2591 597 598 2569 +12578 3170 3120 1235 1277 +12579 182 1924 1934 1925 +12580 1932 182 1929 1934 +12581 1909 1900 1962 188 +12582 212 1926 1928 1914 +12583 213 1888 216 208 +12584 1907 216 190 217 +12585 1924 181 178 182 +12586 1921 1890 1895 1892 +12587 1932 1924 1934 1912 +12588 1930 1079 1920 1919 +12589 1913 178 179 182 +12590 1931 1932 1929 1934 +12591 2135 1629 1226 1223 +12592 1893 1889 1890 1894 +12593 2573 1063 2569 1065 +12594 1929 1930 1920 1919 +12595 1243 1577 1578 1197 +12596 466 33 34 816 +12597 210 1923 179 1932 +12598 210 179 182 1932 +12599 3598 3595 3602 4401 +12600 1079 1076 1934 1912 +12601 1891 1889 1893 1894 +12602 183 210 182 1932 +12603 180 183 182 1932 +12604 201 1035 4359 4702 +12605 2591 2587 2588 1065 +12606 3869 3767 3764 4028 +12607 1886 1893 1890 1894 +12608 184 822 1198 820 +12609 821 1198 824 820 +12610 2591 2566 2568 2569 +12611 1909 89 199 188 +12612 1907 221 216 217 +12613 13 30 28 2148 +12614 212 1926 213 214 +12615 1040 2135 1223 1044 +12616 2984 3328 1040 1044 +12617 3168 1232 346 345 +12618 33 466 793 816 +12619 181 185 178 172 +12620 1546 1618 611 824 +12621 1186 1198 1187 1197 +12622 610 2567 597 2569 +12623 1914 1928 1929 1920 +12624 825 821 184 172 +12625 1855 2500 1847 4521 +12626 2591 610 597 2569 +12627 2591 2573 1063 2569 +12628 2567 610 2568 2569 +12629 2573 2591 598 2569 +12630 3090 3471 4262 4356 +12631 3471 3090 4293 4356 +12632 3273 633 632 4666 +12633 201 1054 1035 4439 +12634 4282 4527 4611 4627 +12635 821 184 1198 820 +12636 2970 3339 3340 4221 +12637 3339 3341 3340 4221 +12638 3406 4435 4545 4555 +12639 2280 1618 824 823 +12640 4322 3287 4553 4625 +12641 2790 3594 4701 4710 +12642 2692 2725 2724 4413 +12643 2577 611 2280 823 +12644 4292 4295 4315 4626 +12645 1084 4013 4122 4429 +12646 405 434 2677 4529 +12647 984 1085 4122 4429 +12648 180 212 1914 149 +12649 181 180 1914 149 +12650 180 181 185 149 +12651 1930 1079 1919 149 +12652 1079 1930 1912 149 +12653 1912 1930 1919 149 +12654 1924 1912 1919 149 +12655 185 1924 1919 149 +12656 1924 181 1912 149 +12657 181 1924 185 149 +12658 181 1079 1934 149 +12659 1934 1079 1912 149 +12660 181 1934 1912 149 +12661 1608 808 1221 172 +12662 795 1645 1647 1644 +12663 171 808 178 172 +12664 185 171 178 172 +12665 184 821 1636 172 +12666 3648 3637 3680 4090 +12667 1042 1221 1911 172 +12668 632 2895 3272 4666 +12669 1253 1155 1664 4023 +12670 1198 1636 1637 1634 +12671 1924 808 171 172 +12672 185 1924 171 172 +12673 825 184 822 172 +12674 1548 1553 1547 4209 +12675 3315 4106 4210 4328 +12676 1221 808 1205 172 +12677 808 1924 1205 172 +12678 1221 1924 1911 172 +12679 1924 1221 1205 172 +12680 1232 1231 346 345 +12681 728 346 348 345 +12682 1250 1235 796 1248 +12683 4230 3058 4450 4566 +12684 1235 1251 796 1248 +12685 4065 2268 4484 4567 +12686 3599 3597 3603 4150 +12687 3901 295 591 4139 +12688 3257 2810 3225 4276 +12689 794 821 1637 826 +12690 1192 793 815 1638 +12691 3886 793 3931 1639 +12692 3151 3007 4367 4495 +12693 611 821 826 348 +12694 1771 1734 4642 4670 +12695 166 1221 1205 1191 +12696 1189 2135 1188 1191 +12697 1316 1551 4209 4285 +12698 1240 1707 1409 1672 +12699 3063 3057 3061 4474 +12700 704 3168 346 345 +12701 704 710 3168 345 +12702 710 3128 3168 345 +12703 3168 3128 1232 345 +12704 1675 1244 1665 4667 +12705 3342 3321 2704 4038 +12706 1417 1416 1121 1733 +12707 1675 1238 1236 1239 +12708 794 1253 1238 345 +12709 1249 1140 4022 4024 +12710 794 1238 1675 345 +12711 1179 1159 4686 4717 +12712 4257 3222 4632 4653 +12713 1237 1686 345 4027 +12714 2130 34 813 1190 +12715 2135 466 813 816 +12716 466 2135 467 816 +12717 793 2135 813 816 +12718 2135 793 467 816 +12719 182 1932 1915 1925 +12720 1913 182 1915 1925 +12721 1932 182 1934 1925 +12722 33 2134 2137 2124 +12723 2130 33 2137 2124 +12724 33 2130 2134 2124 +12725 2130 1188 1190 2124 +12726 2134 2130 1190 2124 +12727 169 2101 2118 2102 +12728 1672 1666 4166 4510 +12729 2135 2136 1040 1223 +12730 2136 2135 2137 1223 +12731 1222 2135 1226 1223 +12732 1917 1913 1915 1925 +12733 1913 1917 1924 1925 +12734 1913 1924 178 1925 +12735 178 1924 182 1925 +12736 1913 178 182 1925 +12737 1063 2591 2569 1065 +12738 3679 3648 3680 4090 +12739 1019 986 4477 4609 +12740 4215 4517 4734 4736 +12741 2577 2580 2576 4746 +12742 1578 1197 1572 4209 +12743 1231 348 345 4027 +12744 1236 794 1239 4023 +12745 1736 1725 1699 4515 +12746 1724 1730 1729 4745 +12747 1176 1113 1112 1110 +12748 3291 3856 4176 4187 +12749 3082 3173 3050 4282 +12750 2264 1208 4065 4567 +12751 1252 1238 1239 4023 +12752 1238 1236 1239 4023 +12753 3056 4148 4472 4559 +12754 821 1198 1636 1647 +12755 2872 1661 1655 1654 +12756 1518 438 1502 4346 +12757 3940 3931 2872 1654 +12758 795 794 1647 1154 +12759 1221 1042 1041 4083 +12760 2871 3940 4012 4247 +12761 4515 1768 4621 4670 +12762 1549 1151 1551 4209 +12763 2871 2872 1661 1655 +12764 2871 3940 2872 1655 +12765 1153 795 1645 1647 +12766 1637 1569 1570 1635 +12767 815 795 1139 1638 +12768 3932 3286 1659 4357 +12769 2886 332 2888 4328 +12770 822 825 1041 4083 +12771 795 1139 1638 4255 +12772 825 172 1041 4083 +12773 1605 822 1041 4083 +12774 348 1237 345 4027 +12775 3931 793 1654 1639 +12776 821 1636 172 1647 +12777 794 821 795 1647 +12778 1198 821 1637 1647 +12779 3904 3907 4094 4490 +12780 644 3958 4498 4685 +12781 1570 1569 1571 1635 +12782 795 821 172 1647 +12783 793 3933 712 1638 +12784 795 793 712 1638 +12785 1067 1557 4341 4563 +12786 1236 1675 1239 4523 +12787 3156 960 1260 4043 +12788 1042 1221 172 4083 +12789 1059 1066 1062 4053 +12790 3296 3312 576 4451 +12791 3044 2782 3078 3043 +12792 3449 3603 4195 4267 +12793 3603 3449 3459 4267 +12794 3603 3459 4150 4267 +12795 3454 3449 4025 4267 +12796 1186 1198 1197 1918 +12797 3108 3870 4176 4426 +12798 1080 1186 1605 1187 +12799 822 1080 1605 1187 +12800 1135 1085 1086 4612 +12801 1183 1186 1187 1197 +12802 2500 1842 4280 4521 +12803 3075 3067 3065 4286 +12804 706 3107 4218 4305 +12805 1577 1243 1149 1197 +12806 1081 1077 4628 4681 +12807 1153 795 796 1248 +12808 1546 1198 1547 1197 +12809 1553 1546 1547 1197 +12810 4417 4259 4736 4744 +12811 1173 1168 1161 1163 +12812 3029 4041 4061 4445 +12813 3134 3138 4127 4306 +12814 4207 1019 4265 4477 +12815 1149 1243 826 1197 +12816 1198 1149 826 1197 +12817 16 4104 4280 4353 +12818 1080 1611 1605 4681 +12819 4428 4721 4732 4746 +12820 1565 1198 1567 1197 +12821 1174 1305 1158 4608 +12822 348 1314 4099 4718 +12823 1077 1930 1919 1078 +12824 1221 1043 1630 1041 +12825 3341 3339 2970 4221 +12826 1087 974 4172 4465 +12827 295 3966 592 4455 +12828 3966 3965 592 4455 +12829 3405 3690 3684 4180 +12830 1721 1761 1716 4323 +12831 987 1255 4252 4254 +12832 3414 3503 4112 4194 +12833 2576 2580 1061 2588 +12834 3895 2692 4344 4413 +12835 2725 2692 3895 4413 +12836 2692 2690 4344 4413 +12837 2690 3323 4344 4413 +12838 1675 1717 4204 4667 +12839 1732 1735 1734 4364 +12840 1685 1247 1686 1680 +12841 1714 1244 1691 1245 +12842 1761 1281 1718 4323 +12843 1196 1183 1197 1185 +12844 1183 2579 1197 1185 +12845 1073 1196 1616 1185 +12846 1073 1616 1619 1185 +12847 2579 1073 1619 1185 +12848 2280 1196 1197 1185 +12849 1225 347 1554 4021 +12850 786 1225 611 4021 +12851 347 611 348 4021 +12852 990 4207 4265 4477 +12853 3315 3321 4045 4183 +12854 1740 1400 4766 4770 +12855 1118 1391 1399 4531 +12856 4749 4177 4751 4771 +12857 4694 4320 4758 4774 +12858 1761 1762 1666 1672 +12859 1617 2577 1554 4084 +12860 822 825 172 1041 +12861 607 3852 4699 4720 +12862 611 1546 1545 4084 +12863 822 1646 1605 1041 +12864 4229 1117 4468 4688 +12865 1554 2577 1224 4084 +12866 611 1545 4021 4084 +12867 3322 2970 4221 4431 +12868 3322 3312 3335 4431 +12869 1175 1120 4157 4190 +12870 1225 611 4021 4084 +12871 1225 1554 1224 4084 +12872 1640 1641 1634 1569 +12873 1640 1636 1646 1569 +12874 1637 1198 1634 1569 +12875 1636 1640 1634 1569 +12876 1198 1636 1634 1569 +12877 1636 1198 1607 1569 +12878 4765 1739 4766 4770 +12879 1567 1565 1197 1566 +12880 3937 3486 4427 4506 +12881 1638 3933 4553 4684 +12882 3500 3388 4615 4639 +12883 1221 172 4598 4638 +12884 3887 3330 2988 4486 +12885 1732 1724 1735 4745 +12886 2763 3069 4257 4653 +12887 3069 2763 4042 4653 +12888 1578 1574 1150 1572 +12889 1159 1179 1129 1127 +12890 1115 1110 1109 4688 +12891 1569 1637 1566 1571 +12892 1637 1567 1566 1571 +12893 1643 1570 1571 1642 +12894 3107 3113 4218 4305 +12895 3067 3074 2998 4286 +12896 1617 2577 4084 4093 +12897 1153 795 1248 1140 +12898 1049 2345 2344 4034 +12899 4127 4136 4149 4306 +12900 3101 3406 4427 4545 +12901 2900 3276 321 4587 +12902 712 795 1638 4024 +12903 1119 1113 4173 4269 +12904 3212 3220 4594 4648 +12905 408 3916 4117 4201 +12906 3939 3332 2994 4420 +12907 1575 1574 1549 1551 +12908 3715 3554 4291 4641 +12909 1548 1552 1573 1549 +12910 3114 3089 4291 4305 +12911 1680 1685 1684 1671 +12912 1550 1575 1574 1573 +12913 1574 1575 1552 1573 +12914 1550 1574 1552 1573 +12915 3106 3638 3105 4107 +12916 3138 3134 4136 4306 +12917 3011 3067 2998 4286 +12918 1114 1116 1582 4771 +12919 786 1225 1215 4231 +12920 1247 1682 1686 1680 +12921 1679 1681 1687 1671 +12922 740 1559 4231 4537 +12923 4322 3940 4592 4613 +12924 3067 3011 3065 4286 +12925 1694 1670 1713 1690 +12926 1681 1680 1687 1671 +12927 794 345 1665 1664 +12928 1253 1238 345 1664 +12929 1611 1080 4628 4681 +12930 1605 1611 1918 4681 +12931 1919 1077 4603 4681 +12932 1080 1077 1081 4681 +12933 1238 1675 345 1664 +12934 345 1675 1665 1664 +12935 3169 3077 2784 3043 +12936 713 1250 796 1248 +12937 792 713 796 1248 +12938 713 792 3122 1248 +12939 713 3122 3124 1248 +12940 1250 713 3124 1248 +12941 1675 794 796 1236 +12942 794 1251 796 1236 +12943 1239 1236 1152 1154 +12944 1042 1077 4083 4603 +12945 3120 713 1235 1277 +12946 2576 612 597 4663 +12947 3125 792 712 1140 +12948 1281 1279 1761 1269 +12949 1279 1760 1761 1269 +12950 1239 794 1154 4023 +12951 611 2576 609 4663 +12952 1160 3002 3016 3085 +12953 1641 1634 1570 1635 +12954 1641 1640 1634 1635 +12955 1244 1675 1665 4662 +12956 1638 1656 4022 4024 +12957 1117 1114 4229 4468 +12958 2667 4307 4371 4386 +12959 4320 1144 4403 4527 +12960 794 1155 1154 4023 +12961 3124 1250 1248 1140 +12962 1249 1250 3124 1140 +12963 792 3125 3122 1140 +12964 792 3122 1248 1140 +12965 795 792 796 1140 +12966 796 792 1248 1140 +12967 795 796 1248 1140 +12968 3122 3124 1248 1140 +12969 3122 3125 3124 1140 +12970 3288 1249 3124 1140 +12971 3125 3288 3124 1140 +12972 2499 16 4104 4280 +12973 982 3082 3052 4136 +12974 3048 982 4127 4136 +12975 3077 2782 2784 3043 +12976 1152 1239 1154 4023 +12977 1253 794 1236 4023 +12978 1688 1679 1671 4446 +12979 1025 982 1029 980 +12980 1322 1231 1318 4160 +12981 1587 1744 4215 4394 +12982 1117 1114 4171 4229 +12983 982 3052 4127 4136 +12984 1160 1175 3085 3043 +12985 2782 3044 2784 3043 +12986 1319 1321 339 4164 +12987 3077 2679 3078 3043 +12988 1513 2018 2038 4238 +12989 1983 1974 1965 1966 +12990 2587 2591 2586 1065 +12991 2591 2592 2569 1065 +12992 1164 1166 1297 1299 +12993 960 3156 3226 4042 +12994 85 753 4132 4359 +12995 1899 1962 1898 1902 +12996 986 1004 4336 4609 +12997 4047 4185 4407 4716 +12998 982 979 4029 4252 +12999 1063 1060 1062 1065 +13000 1060 2587 1062 1065 +13001 1061 2573 2588 1065 +13002 2587 1061 2588 1065 +13003 2592 2591 2588 1065 +13004 1063 2573 1060 1065 +13005 1705 1584 1711 4389 +13006 1146 1175 1254 1147 +13007 2592 2573 2569 1065 +13008 4098 4297 4320 4774 +13009 1747 1739 4765 4770 +13010 824 823 4624 4746 +13011 1410 1770 1732 1769 +13012 1815 1822 1806 1819 +13013 3148 3036 3013 3145 +13014 3030 3148 3013 3145 +13015 3012 1305 3145 1156 +13016 4218 706 4614 4665 +13017 1309 1292 1324 1286 +13018 4177 4268 4749 4751 +13019 1305 3012 1158 1156 +13020 1294 1291 1302 1289 +13021 3322 3296 3312 4221 +13022 1618 1546 611 4084 +13023 1225 2577 611 4084 +13024 1545 1554 4021 4084 +13025 48 39 173 4212 +13026 1320 814 1214 4056 +13027 3206 2849 3221 4648 +13028 1546 1618 1545 4084 +13029 4400 822 4520 4603 +13030 1405 1404 4442 4602 +13031 1394 1405 1120 4602 +13032 1675 1238 4108 4667 +13033 633 639 632 4666 +13034 4312 3386 4543 4607 +13035 632 639 2895 4666 +13036 4178 3386 4312 4607 +13037 3622 3428 3594 3605 +13038 345 1244 1665 4662 +13039 1394 1120 4589 4602 +13040 3220 3212 4595 4648 +13041 1240 1409 1703 1277 +13042 51 2282 4076 4296 +13043 1409 1240 1717 1277 +13044 3279 3278 3782 4618 +13045 1276 3120 3169 1277 +13046 3085 1178 3043 1267 +13047 3169 3170 1235 1277 +13048 1240 1675 796 1277 +13049 3200 4348 4391 4475 +13050 2996 3023 3151 4475 +13051 1674 1676 1677 1673 +13052 2803 3452 3597 4150 +13053 1402 1129 1127 1414 +13054 4389 1705 4757 4771 +13055 3543 3542 3540 4094 +13056 1667 1676 1674 1673 +13057 1133 1112 1402 1403 +13058 3714 1409 1717 1277 +13059 4203 1162 4519 4608 +13060 1409 3554 1703 1277 +13061 1707 1240 1703 1277 +13062 3714 3716 1409 1277 +13063 3716 3714 3120 1277 +13064 3555 3120 3584 1277 +13065 3554 3555 1703 1277 +13066 3555 3554 3120 1277 +13067 3716 3554 1409 1277 +13068 3554 3716 3120 1277 +13069 3555 1707 1703 1277 +13070 1276 3584 1277 1701 +13071 3519 3518 4415 4466 +13072 2500 1855 1847 4109 +13073 705 3312 576 4378 +13074 4176 3312 4187 4378 +13075 4394 4313 4517 4525 +13076 1134 1748 1746 4146 +13077 1174 3086 1305 4061 +13078 2658 2653 1032 4560 +13079 1087 974 4465 4532 +13080 992 1001 1331 4096 +13081 1197 4020 4182 4419 +13082 1676 1667 1674 1668 +13083 753 750 751 4224 +13084 1720 1279 1760 1666 +13085 1703 3554 4291 4405 +13086 1551 1150 4209 4285 +13087 1111 1402 1127 1403 +13088 1666 1672 1674 1673 +13089 1667 1240 1672 1674 +13090 1675 1674 1677 1673 +13091 51 2279 2282 4015 +13092 3069 3580 4019 4257 +13093 3852 3102 703 4123 +13094 1672 1667 1674 1673 +13095 1574 1550 1552 4454 +13096 1240 1714 1667 1672 +13097 1714 1240 1409 1672 +13098 1238 1675 1677 1673 +13099 2125 2145 4011 4167 +13100 1112 1159 1402 1127 +13101 1676 1674 1677 1668 +13102 1740 1400 4497 4766 +13103 3064 3081 2784 4764 +13104 3637 3681 3640 4125 +13105 2799 3226 4042 4653 +13106 3174 3041 1144 4352 +13107 584 3319 3908 4464 +13108 3300 705 4003 4378 +13109 710 3109 4003 4378 +13110 1101 1025 959 1098 +13111 1667 1666 1672 1669 +13112 1696 982 1025 1027 +13113 1116 1705 1706 1711 +13114 1720 1761 1269 1716 +13115 1754 1710 1584 1711 +13116 3999 3957 301 4516 +13117 2782 3077 2784 4588 +13118 1320 1561 339 4164 +13119 1757 1752 1722 1711 +13120 2852 2702 2943 4502 +13121 339 1311 4056 4164 +13122 1580 1753 1584 1755 +13123 1171 1159 1143 1147 +13124 1025 982 1028 1029 +13125 3049 3055 4186 4205 +13126 3054 3062 3049 4205 +13127 3059 3055 3049 4205 +13128 3459 3602 2786 4600 +13129 639 631 2895 4666 +13130 1698 1723 4155 4461 +13131 2838 3312 4321 4431 +13132 3056 3134 3138 4472 +13133 1678 1679 1683 1688 +13134 1679 1686 1684 1687 +13135 1686 1685 1680 1671 +13136 1693 1694 1713 1690 +13137 1683 1693 1713 1690 +13138 1715 1683 1713 1690 +13139 3123 3125 712 4696 +13140 1685 1684 1671 1245 +13141 1714 1691 1713 1245 +13142 1713 1691 1688 1245 +13143 1714 1713 1688 1245 +13144 1686 1685 1671 1245 +13145 1685 1686 345 1245 +13146 712 3125 4360 4696 +13147 1685 1689 1684 1245 +13148 3519 3518 4383 4415 +13149 3068 3049 4186 4387 +13150 1125 998 4465 4542 +13151 1400 1112 4531 4671 +13152 3319 3939 3317 4119 +13153 1855 1852 1847 4109 +13154 1241 1249 4022 4024 +13155 304 303 2816 4643 +13156 2763 2799 4042 4653 +13157 4217 3595 4236 4524 +13158 1744 1743 1586 1587 +13159 1133 1402 1127 1414 +13160 1133 1129 1402 1414 +13161 1695 1708 4461 4757 +13162 324 315 3221 4350 +13163 1021 4329 4379 4503 +13164 1745 1748 1128 1398 +13165 966 984 1085 4096 +13166 1159 1133 1127 1414 +13167 3077 2783 2679 4588 +13168 3062 3063 4472 4559 +13169 3819 3856 3301 4176 +13170 3500 3169 1121 1735 +13171 3340 3342 4753 4754 +13172 1416 1394 1418 1405 +13173 1708 1736 981 1699 +13174 3452 2803 3530 4150 +13175 1770 1416 1765 1766 +13176 3831 3268 3266 4176 +13177 1027 1025 1029 980 +13178 1221 1630 4390 4661 +13179 2132 2169 2176 2168 +13180 3110 3119 3282 4086 +13181 1712 1723 1731 1727 +13182 1249 3288 1241 4022 +13183 1030 3173 1027 980 +13184 1726 1406 1768 1727 +13185 1731 1726 1768 1727 +13186 1723 1726 1731 1727 +13187 1255 1029 980 4407 +13188 1546 2280 1197 4020 +13189 1393 4469 4589 4679 +13190 432 1514 4213 4216 +13191 3931 3886 1639 4438 +13192 1654 3931 1639 4438 +13193 343 1321 339 4160 +13194 1314 1318 346 4099 +13195 3127 3103 703 4123 +13196 3886 793 1639 4438 +13197 1244 1245 4204 4554 +13198 1199 2286 4334 4567 +13199 2725 2692 2724 4703 +13200 3169 3500 1725 1735 +13201 4013 1132 4046 4429 +13202 4297 4017 4758 4774 +13203 2692 2695 3895 4473 +13204 1707 3555 1703 1701 +13205 1703 3554 1697 1701 +13206 1697 3554 3584 1701 +13207 1707 1706 1672 1701 +13208 1240 1707 1672 1701 +13209 3554 3555 3584 1701 +13210 3555 3554 1703 1701 +13211 1025 1099 964 1103 +13212 712 4354 4447 4696 +13213 2785 3546 3538 981 +13214 3083 3582 3538 981 +13215 3582 3083 1695 981 +13216 2911 4498 4759 4761 +13217 2856 3340 4753 4754 +13218 407 3560 2133 4018 +13219 3055 3068 3049 4186 +13220 3169 1177 1121 1735 +13221 1410 1408 1412 1724 +13222 1412 1408 1730 1724 +13223 2692 2724 4308 4413 +13224 3169 1177 1176 1121 +13225 1589 1117 1582 4171 +13226 1394 1395 1393 1120 +13227 3807 3819 3266 4176 +13228 1728 1726 1769 1724 +13229 1410 1728 1769 1724 +13230 1732 1410 1769 1724 +13231 3530 3452 4150 4158 +13232 1726 1732 1769 1724 +13233 1920 149 4355 4400 +13234 3043 3169 1176 1121 +13235 1726 1732 1724 1735 +13236 822 1080 1187 4603 +13237 1159 1118 1112 1120 +13238 1735 1266 4364 4640 +13239 1004 987 4275 4444 +13240 1920 1919 149 4400 +13241 1159 1133 1414 1120 +13242 1133 1395 1414 1120 +13243 1416 1405 1417 1121 +13244 3053 1027 4244 4649 +13245 2264 2274 583 4334 +13246 1842 2500 4192 4521 +13247 3200 4006 4348 4475 +13248 4259 4269 4417 4453 +13249 1119 1113 1117 1738 +13250 2815 300 4077 4219 +13251 3269 3270 3303 4273 +13252 1620 1618 1616 4182 +13253 1729 1407 1134 1746 +13254 2819 3206 4595 4648 +13255 1208 2268 4065 4567 +13256 229 1519 2042 4238 +13257 3939 3330 3317 4119 +13258 3002 1160 3016 4041 +13259 1395 1416 1418 1766 +13260 1720 1590 4145 4191 +13261 2588 4424 4480 4585 +13262 1679 1681 1671 4446 +13263 1708 4155 1698 1712 +13264 4076 2283 4296 4299 +13265 1400 4040 4497 4766 +13266 2480 4076 4296 4299 +13267 3906 3578 408 4117 +13268 4270 1027 4284 4760 +13269 1232 343 346 4160 +13270 3131 3699 3714 4128 +13271 1177 1276 1117 1114 +13272 1276 1177 1736 1114 +13273 313 3315 2943 4502 +13274 2824 2825 2830 4594 +13275 1712 1723 4048 4155 +13276 3640 3699 3645 4128 +13277 3082 3051 3053 4584 +13278 4621 1771 4642 4670 +13279 3204 2920 2918 4576 +13280 1408 1770 4406 4745 +13281 710 3107 3106 4378 +13282 3268 3108 4176 4378 +13283 3169 1276 1736 1114 +13284 1030 1264 1029 4758 +13285 1700 1276 3169 1114 +13286 3083 3225 982 4584 +13287 1589 1116 1117 1588 +13288 970 4096 4122 4179 +13289 713 3131 3699 4641 +13290 1692 1721 1716 4323 +13291 2785 3169 1736 1114 +13292 2785 3923 3169 1114 +13293 3995 3985 4498 4761 +13294 3169 3923 3583 1114 +13295 1700 3169 3583 1114 +13296 1648 753 4240 4488 +13297 1099 1025 1098 1028 +13298 1771 1732 1735 1734 +13299 1107 1122 1105 1028 +13300 1712 1698 1731 4155 +13301 1107 1102 1106 1028 +13302 965 979 1122 1028 +13303 979 965 1099 1028 +13304 982 979 1099 1028 +13305 1025 982 959 1028 +13306 1025 959 1098 1028 +13307 982 1099 959 1028 +13308 959 1099 1098 1028 +13309 1624 1627 4341 4620 +13310 3485 4385 4564 4735 +13311 229 222 2042 4634 +13312 1313 1274 1130 4115 +13313 976 1107 1106 1028 +13314 1107 976 1122 1028 +13315 1122 976 1106 1028 +13316 2762 2810 3257 4330 +13317 1025 1030 1028 1027 +13318 1150 1578 4209 4285 +13319 4022 1140 4381 4479 +13320 1042 1075 1077 4603 +13321 1175 1160 1174 1158 +13322 1025 1099 1098 964 +13323 1101 1025 1098 964 +13324 1099 1025 1028 1103 +13325 965 1099 1028 1103 +13326 1025 1102 1028 1103 +13327 1102 965 1028 1103 +13328 1627 1557 1067 4620 +13329 1159 1178 1120 1143 +13330 3451 3603 4325 4425 +13331 1017 1023 1018 4340 +13332 3561 1505 3559 4018 +13333 2785 1736 1725 1699 +13334 2785 3546 981 1699 +13335 1736 2785 981 1699 +13336 2785 1725 3504 1699 +13337 3546 2785 3504 1699 +13338 1266 1175 1146 1147 +13339 1748 1398 4146 4159 +13340 1392 1413 1171 1143 +13341 1159 1392 1171 1143 +13342 1159 1414 1411 1143 +13343 3173 982 1027 980 +13344 1165 1182 1172 4288 +13345 1159 1411 1415 1143 +13346 1413 1392 1415 1143 +13347 1392 1159 1415 1143 +13348 1112 1110 1400 4531 +13349 3597 2803 4001 4137 +13350 1715 1669 4080 4323 +13351 1254 1146 3174 1145 +13352 1146 3064 3132 1145 +13353 3410 3409 3562 4036 +13354 3132 3133 3041 3174 +13355 3133 3044 3174 1145 +13356 3628 4174 4200 4476 +13357 1272 1001 998 4115 +13358 1175 3043 1121 1145 +13359 1266 1175 1121 1145 +13360 1175 3044 3043 1145 +13361 3108 3312 4176 4378 +13362 1274 999 1130 4115 +13363 1091 1330 1313 4115 +13364 706 3108 4218 4378 +13365 1331 1330 1088 4115 +13366 1279 1281 4323 4343 +13367 1089 1088 1130 4115 +13368 3044 3064 2784 1145 +13369 2784 3064 1121 1145 +13370 1175 1174 3044 1145 +13371 2345 1623 2356 4034 +13372 3064 3044 3132 1145 +13373 1266 3064 1146 1145 +13374 3044 3133 3132 1145 +13375 3023 2996 3003 4006 +13376 3200 2735 3006 4006 +13377 2805 3530 4217 4471 +13378 3559 407 2133 4018 +13379 1173 1163 1157 1298 +13380 1158 1171 1157 1147 +13381 622 644 608 4498 +13382 4207 4013 4327 4482 +13383 1298 1173 1166 1172 +13384 1302 1173 1157 1298 +13385 3042 3057 4237 4370 +13386 3195 3193 3192 4248 +13387 3965 3123 712 4447 +13388 2607 2601 2608 2611 +13389 2636 2629 721 2622 +13390 1294 1298 1166 1299 +13391 1666 1722 4166 4510 +13392 1119 1113 1738 4223 +13393 1159 1178 1175 1147 +13394 3012 3145 3031 1296 +13395 1020 1019 4340 4609 +13396 988 1023 4340 4609 +13397 1303 1291 3145 1156 +13398 1302 1291 1303 1156 +13399 1302 1303 1158 1156 +13400 1294 1302 1298 1289 +13401 2197 2156 2152 2154 +13402 1294 1302 1157 1298 +13403 1163 1294 1157 1298 +13404 1295 1294 1164 1166 +13405 1164 1168 1165 1166 +13406 1165 1168 1173 1166 +13407 1173 1163 1298 1166 +13408 1164 1294 1163 1166 +13409 1163 1294 1298 1166 +13410 1173 1168 1163 1166 +13411 1168 1164 1163 1166 +13412 156 484 157 2184 +13413 3040 4237 4272 4370 +13414 1295 1164 1297 1299 +13415 226 1866 1872 1871 +13416 3440 3503 3418 4194 +13417 1322 1318 4099 4160 +13418 3560 2132 2133 4018 +13419 4265 986 4444 4599 +13420 3417 3581 3503 4194 +13421 172 1608 1644 4302 +13422 3414 3440 3418 4194 +13423 4146 1746 4469 4679 +13424 1620 2577 1069 4569 +13425 1606 1197 1572 4570 +13426 1636 172 1644 4302 +13427 1164 1295 1166 1299 +13428 1291 1309 1324 1286 +13429 1458 1449 1446 2181 +13430 227 1882 1866 226 +13431 2180 1470 2178 2181 +13432 2120 1489 2121 2122 +13433 1291 3145 1296 1287 +13434 1324 1292 3145 1287 +13435 1289 1291 1296 1286 +13436 1296 1291 1287 1286 +13437 1291 1324 3145 1286 +13438 3145 1324 1287 1286 +13439 1291 3145 1287 1286 +13440 2199 105 117 116 +13441 730 718 714 723 +13442 1310 1332 1329 1325 +13443 1282 1328 1329 1326 +13444 1292 1333 1324 3167 +13445 1288 1297 1293 1301 +13446 1332 1309 1310 1327 +13447 1309 1288 1310 1327 +13448 1489 2120 2121 2114 +13449 3165 3146 3145 3167 +13450 3032 3146 1290 3167 +13451 1292 3032 1290 3167 +13452 1532 2157 1540 1539 +13453 1541 413 1537 1539 +13454 1283 1285 1326 1301 +13455 1538 1532 1540 1539 +13456 1537 1532 1538 1539 +13457 3147 3146 3032 3167 +13458 3146 3147 3145 3167 +13459 3145 3147 3032 3167 +13460 3032 1292 1287 3167 +13461 3145 3032 1287 3167 +13462 1292 3145 1287 3167 +13463 1327 1288 1329 1326 +13464 1291 1286 1310 1325 +13465 1288 1310 1329 1325 +13466 1295 1294 1299 1325 +13467 1328 1327 1329 1326 +13468 1288 1299 1310 1325 +13469 1291 1309 1286 1325 +13470 1309 1291 1332 1325 +13471 1309 1332 1310 1325 +13472 1286 1309 1310 1325 +13473 1299 1294 1289 1325 +13474 1294 1291 1289 1325 +13475 1289 1291 1310 1325 +13476 1299 1289 1310 1325 +13477 1282 1329 1293 1326 +13478 2619 2630 2642 2622 +13479 2629 2619 721 2622 +13480 1329 1288 1325 1326 +13481 1325 1288 1293 1326 +13482 1329 1325 1293 1326 +13483 1282 1307 1284 1301 +13484 1300 1307 1295 1301 +13485 1300 1295 1297 1301 +13486 1850 1841 1836 146 +13487 1790 196 1789 1792 +13488 1854 1839 1840 1848 +13489 1285 1283 1284 1301 +13490 1283 1282 1284 1301 +13491 1307 1282 1293 1301 +13492 1297 1295 1293 1301 +13493 1295 1307 1293 1301 +13494 1795 1878 1859 1792 +13495 1795 1783 1789 1792 +13496 1790 1856 1859 1792 +13497 196 1790 1859 1792 +13498 2642 721 2622 2633 +13499 2642 715 721 2633 +13500 2630 2642 2622 2633 +13501 2628 2630 2622 2633 +13502 2630 2628 715 2633 +13503 2642 2630 715 2633 +13504 2637 2595 2634 2609 +13505 2628 2637 2634 2609 +13506 2631 2628 2601 2609 +13507 2628 2631 2637 2609 +13508 2631 2601 665 2609 +13509 2637 2631 665 2609 +13510 665 2601 2593 2609 +13511 2601 2595 2593 2609 +13512 2637 665 2593 2609 +13513 2595 2637 2593 2609 +13514 2593 2601 2611 2613 +13515 2595 2601 2593 2613 +13516 672 2608 2612 2613 +13517 2601 2595 2608 2613 +13518 2451 2460 2472 2453 +13519 2472 661 2463 2453 +13520 660 2462 2458 2453 +13521 2458 2462 2463 2453 +13522 2459 2474 2427 2456 +13523 2460 2472 661 2454 +13524 2472 2460 661 2453 +13525 2460 660 661 2453 +13526 2463 661 658 2453 +13527 661 660 658 2453 +13528 2458 2463 658 2453 +13529 660 2458 658 2453 +13530 483 1489 150 2184 +13531 2607 2534 2610 2614 +13532 659 655 651 656 +13533 653 2451 671 656 +13534 654 2607 2610 2614 +13535 654 672 2607 2614 +13536 654 2610 655 2614 +13537 2601 2608 2611 2613 +13538 2621 2617 2618 723 +13539 670 2596 2611 673 +13540 2596 670 674 673 +13541 672 2607 2614 2612 +13542 2534 2452 2614 2612 +13543 148 484 150 174 +13544 2185 2186 2116 2117 +13545 2596 2600 2611 673 +13546 2600 2596 674 673 +13547 2607 2534 2614 2612 +13548 90 189 188 88 +13549 228 190 217 188 +13550 1940 1939 1941 125 +13551 2149 2138 2140 2147 +13552 2123 3589 1481 2141 +13553 1896 1890 1921 164 +13554 1921 1890 224 164 +13555 187 1962 1903 1904 +13556 1921 1896 164 1922 +13557 2108 2098 2107 2104 +13558 1970 1909 1900 1962 +13559 189 1972 1971 97 +13560 95 189 1971 97 +13561 79 68 1977 1979 +13562 1973 1971 92 1969 +13563 1887 221 199 198 +13564 1962 1897 88 187 +13565 1970 1909 189 90 +13566 1968 1906 1887 198 +13567 1964 97 88 187 +13568 1972 1971 97 96 +13569 1982 1972 1961 96 +13570 1961 1972 97 96 +13571 1974 66 1981 64 +13572 66 67 1987 64 +13573 1971 1972 1973 96 +13574 94 1971 1973 96 +13575 1972 1982 1973 96 +13576 93 100 87 64 +13577 2036 205 432 4213 +13578 1971 1968 1906 1969 +13579 1971 1906 92 1969 +13580 92 1906 1967 1969 +13581 1982 1985 1981 1978 +13582 1981 1987 1942 1978 +13583 1981 1985 1983 1978 +13584 1985 1984 1983 1978 +13585 1942 1938 1959 1943 +13586 1983 1984 100 1978 +13587 79 1976 1944 1975 +13588 1967 1965 1966 1976 +13589 1945 1980 70 1944 +13590 70 79 1944 1979 +13591 724 2556 734 731 +13592 2626 2625 2635 2632 +13593 1939 1938 1935 1959 +13594 152 2113 2112 150 +13595 1942 1981 1978 1959 +13596 2113 152 151 150 +13597 1489 2120 2114 2122 +13598 1984 1978 1935 1959 +13599 2617 718 2618 723 +13600 81 139 136 70 +13601 1957 1940 1937 125 +13602 2098 2108 2107 14 +13603 1945 1975 1977 1979 +13604 2467 2473 2449 2430 +13605 1939 1980 1935 1941 +13606 1938 1939 1935 1941 +13607 65 1935 1941 1986 +13608 1935 65 1959 1986 +13609 1938 1935 1959 1986 +13610 1956 1937 1936 133 +13611 1954 140 1958 133 +13612 140 1954 138 133 +13613 1957 1939 1940 125 +13614 1937 81 1958 125 +13615 1957 1937 1958 125 +13616 81 1957 1958 125 +13617 1957 1939 125 126 +13618 81 1957 125 126 +13619 1939 1980 1941 126 +13620 1939 1957 1980 126 +13621 1980 1957 1953 126 +13622 1957 81 1953 126 +13623 2638 2628 715 716 +13624 94 189 96 87 +13625 2630 2642 715 716 +13626 715 2642 720 716 +13627 1945 1980 1953 126 +13628 77 1945 1953 126 +13629 81 77 1953 126 +13630 77 68 74 79 +13631 1807 1808 1813 1812 +13632 1937 81 125 133 +13633 1801 1885 1954 1884 +13634 1807 1805 1885 1884 +13635 1805 1802 1804 1884 +13636 1808 1885 1955 1948 +13637 110 112 113 119 +13638 1937 125 1936 133 +13639 134 110 113 114 +13640 1952 1946 1953 1947 +13641 103 138 101 102 +13642 2472 2460 2452 2456 +13643 1827 1804 1825 108 +13644 1948 1937 1936 1809 +13645 1937 1956 1936 1809 +13646 1946 130 1953 1947 +13647 66 87 64 69 +13648 1983 1974 1966 1977 +13649 68 65 1977 1979 +13650 1954 1951 1949 1950 +13651 2473 2468 2427 2430 +13652 1952 1953 1958 1947 +13653 140 1952 1958 1947 +13654 130 139 1953 1947 +13655 1953 139 1958 1947 +13656 139 140 1958 1947 +13657 139 130 136 1947 +13658 140 139 136 1947 +13659 130 140 136 1947 +13660 1954 1952 140 1949 +13661 1954 1951 1952 1949 +13662 132 1814 109 120 +13663 1816 1815 1822 120 +13664 470 485 194 468 +13665 1784 1824 1823 1786 +13666 484 483 150 2184 +13667 157 150 2117 2184 +13668 150 2112 2117 2184 +13669 136 68 77 70 +13670 81 136 77 70 +13671 81 77 126 70 +13672 65 81 126 70 +13673 65 68 100 70 +13674 2556 717 734 729 +13675 140 137 139 138 +13676 1489 2112 2122 150 +13677 2186 156 2116 2117 +13678 1936 1948 138 101 +13679 1811 1803 1884 122 +13680 1949 115 1950 122 +13681 1885 1955 1948 138 +13682 1948 1955 1936 138 +13683 717 724 734 729 +13684 140 1954 137 138 +13685 1955 1885 1954 138 +13686 1803 1811 1950 122 +13687 1973 1982 1985 87 +13688 1982 1960 1985 87 +13689 1985 1960 1981 87 +13690 1960 66 1981 87 +13691 94 1973 92 87 +13692 92 1973 1967 87 +13693 94 92 1967 87 +13694 1961 1982 96 87 +13695 1982 1961 1960 87 +13696 1960 1961 96 87 +13697 1946 1945 1944 1975 +13698 1976 1966 1975 1977 +13699 1983 1966 1976 1977 +13700 77 1945 70 1944 +13701 68 70 79 1944 +13702 77 68 79 1944 +13703 68 77 70 1944 +13704 1945 1944 1975 1979 +13705 1980 1984 1935 1979 +13706 1945 1980 1944 1979 +13707 1984 1983 1977 1979 +13708 74 1976 79 1977 +13709 79 1976 1975 1977 +13710 100 1984 1976 1977 +13711 74 100 1976 1977 +13712 68 74 79 1977 +13713 1984 100 1959 1977 +13714 673 674 668 667 +13715 74 68 65 1977 +13716 74 67 100 1977 +13717 100 67 1959 1977 +13718 67 65 1959 1977 +13719 67 74 65 1977 +13720 65 68 70 1979 +13721 674 664 668 667 +13722 2636 730 2629 2625 +13723 664 665 668 667 +13724 714 730 715 2625 +13725 2629 2633 2635 2632 +13726 2631 2637 665 667 +13727 2615 664 722 667 +13728 2597 2631 665 667 +13729 664 2597 665 667 +13730 664 2615 2597 667 +13731 2597 2615 2631 667 +13732 2615 2630 2631 667 +13733 722 2630 663 667 +13734 2615 722 663 667 +13735 2630 2615 663 667 +13736 2623 2621 2618 723 +13737 2618 717 2627 723 +13738 2623 2618 2627 723 +13739 717 2623 2627 723 +13740 699 700 731 2551 +13741 730 2629 2625 2632 +13742 714 730 2625 2632 +13743 2626 2621 723 2632 +13744 714 2626 723 2632 +13745 2617 730 714 2632 +13746 2617 714 723 2632 +13747 2621 2617 723 2632 +13748 2619 2617 2621 2632 +13749 2629 2619 2621 2632 +13750 730 2617 2619 2632 +13751 2629 730 2619 2632 +13752 724 2552 2564 2563 +13753 735 714 2626 729 +13754 2626 714 723 729 +13755 735 2626 723 729 +13756 2550 2565 701 2535 +13757 2562 2565 2557 2564 +13758 735 2640 2624 2620 +13759 2541 2545 2553 2544 +13760 2562 2554 2624 2564 +13761 2626 735 2640 2552 +13762 2621 2629 2632 2620 +13763 735 731 729 2624 +13764 2556 2623 729 2624 +13765 731 2556 729 2624 +13766 724 2556 731 2624 +13767 2624 2620 2564 2563 +13768 700 731 2551 2552 +13769 724 700 2551 2552 +13770 700 724 731 2552 +13771 735 2626 2640 2620 +13772 729 735 2624 2620 +13773 2556 2623 2624 2563 +13774 724 2556 2624 2563 +13775 2621 2626 723 2620 +13776 2626 735 723 2620 +13777 723 735 729 2620 +13778 2623 729 2624 2620 +13779 2623 723 729 2620 +13780 2623 2621 723 2620 +13781 2551 724 2552 2564 +13782 2554 2551 2552 2564 +13783 700 724 2551 2564 +13784 2562 700 2551 2564 +13785 2559 724 2557 2563 +13786 2556 2559 2557 2563 +13787 2559 2556 724 2563 +13788 1886 1926 1931 209 +13789 90 1909 199 198 +13790 1970 1909 90 198 +13791 1970 1968 1909 198 +13792 189 1970 90 198 +13793 1970 189 1906 198 +13794 1968 1970 1906 198 +13795 2553 2562 2539 2535 +13796 2538 2550 2539 2535 +13797 604 2540 2536 2544 +13798 604 2536 2538 2544 +13799 2540 604 2537 2590 +13800 2540 2536 2544 2539 +13801 2553 2540 2544 2539 +13802 2536 2538 2544 2539 +13803 2550 2548 2538 2539 +13804 2548 604 2538 2539 +13805 2538 604 2544 2539 +13806 2545 2553 2544 2539 +13807 697 2545 2544 2539 +13808 604 697 2544 2539 +13809 697 604 2548 2539 +13810 698 697 2548 2539 +13811 2550 698 2548 2539 +13812 190 1907 217 188 +13813 2545 697 698 2539 +13814 2545 698 701 2535 +13815 1904 1905 1898 1902 +13816 210 183 182 209 +13817 1886 208 211 228 +13818 212 1926 183 209 +13819 1926 212 1931 209 +13820 1931 212 183 209 +13821 1894 1931 1892 209 +13822 183 210 1932 209 +13823 1890 1894 1892 209 +13824 1890 1886 1894 209 +13825 1921 1890 1892 209 +13826 1931 183 1932 209 +13827 1931 1932 1892 209 +13828 1932 1895 1892 209 +13829 1895 1921 1892 209 +13830 1923 1895 1932 209 +13831 210 1923 1932 209 +13832 1923 210 179 224 +13833 1923 1921 1895 209 +13834 209 1890 211 224 +13835 2149 2140 2143 2141 +13836 1900 1909 1910 188 +13837 190 89 1905 188 +13838 1909 1887 1910 188 +13839 221 1887 199 188 +13840 447 3587 3591 54 +13841 1893 1886 1890 228 +13842 1890 1886 211 228 +13843 1927 1886 1893 228 +13844 1889 1893 1890 228 +13845 1927 1888 213 228 +13846 213 1888 208 228 +13847 30 13 442 2148 +13848 1896 1889 1890 228 +13849 2128 2144 55 22 +13850 2144 2128 488 22 +13851 1886 1927 213 228 +13852 1886 213 208 228 +13853 1887 221 1908 188 +13854 1887 1908 1910 188 +13855 1908 1907 1910 188 +13856 1907 221 217 188 +13857 221 1907 1908 188 +13858 13 2105 15 35 +13859 460 454 462 32 +13860 31 460 462 32 +13861 1494 454 460 32 +13862 45 1494 460 32 +13863 15 453 32 14 +13864 2128 24 488 22 +13865 1910 188 1899 1898 +13866 45 2096 1494 32 +13867 1484 454 1482 32 +13868 1482 454 1494 32 +13869 2096 1482 1494 32 +13870 2096 2098 1482 32 +13871 15 453 14 35 +13872 1484 15 32 2109 +13873 1480 453 1484 2109 +13874 453 1480 2142 2109 +13875 1480 1484 1482 2109 +13876 2098 1480 1482 2109 +13877 1482 1484 32 2109 +13878 2098 1482 32 2109 +13879 2096 2108 2098 14 +13880 2105 13 2143 35 +13881 13 453 2143 35 +13882 2105 2106 15 35 +13883 13 30 442 35 +13884 453 13 442 35 +13885 2143 453 2142 35 +13886 2105 2143 2142 35 +13887 15 2106 2109 35 +13888 453 15 2109 35 +13889 2106 453 2109 35 +13890 2106 2105 2142 35 +13891 453 2106 2142 35 +13892 190 211 228 225 +13893 1909 189 90 88 +13894 1909 89 1962 88 +13895 1909 90 199 88 +13896 89 1909 199 88 +13897 1972 1963 1964 1903 +13898 1970 1909 1962 88 +13899 1909 1970 189 88 +13900 1970 1962 1897 88 +13901 189 1970 1897 88 +13902 1904 1905 1902 1922 +13903 1897 1964 187 1903 +13904 1962 1897 187 1903 +13905 188 190 1899 1898 +13906 1910 1900 188 1898 +13907 1905 190 188 1898 +13908 89 1962 187 1904 +13909 1905 89 187 1904 +13910 89 1905 188 1904 +13911 1962 89 188 1904 +13912 1962 1900 1897 1898 +13913 1889 1891 1899 1898 +13914 224 211 225 164 +13915 2149 2140 28 2147 +13916 1480 2143 2142 2141 +13917 1962 1904 1898 1902 +13918 2146 2140 3591 2141 +13919 2140 3589 3591 2141 +13920 1889 1891 228 1899 +13921 190 1905 225 1899 +13922 1907 1891 1910 1899 +13923 1891 1907 228 1899 +13924 228 1907 225 1899 +13925 1907 1910 188 1899 +13926 190 1907 188 1899 +13927 1907 190 225 1899 +13928 1890 1896 228 164 +13929 211 1890 228 164 +13930 1923 1921 224 164 +13931 225 228 1899 1922 +13932 1889 1896 1899 1922 +13933 1896 1889 228 1922 +13934 228 1889 1899 1922 +13935 2138 2146 2140 2147 +13936 2144 2138 2128 55 +13937 55 29 22 23 +13938 451 3587 447 54 +13939 488 22 23 54 +13940 29 2140 442 54 +13941 29 55 28 2147 +13942 2149 28 2148 2147 +13943 2146 2138 23 2147 +13944 29 2140 54 2147 +13945 2144 55 22 23 +13946 13 2105 2143 2148 +13947 13 30 29 28 +13948 55 29 23 2147 +13949 2140 29 28 2147 +13950 2138 55 23 2147 +13951 2146 2138 2144 23 +13952 2144 2138 55 23 +13953 2140 2146 3591 54 +13954 2144 3618 3591 54 +13955 3618 445 447 54 +13956 445 3618 488 54 +13957 22 2144 23 54 +13958 2144 2146 23 54 +13959 2146 2144 3591 54 +13960 3618 2144 488 54 +13961 488 2144 22 54 +13962 2140 2146 54 2147 +13963 2149 2143 2148 2141 +13964 3589 2140 442 2141 +13965 1480 2142 1481 2141 +13966 3589 1480 1481 2141 +13967 1480 3589 3600 2141 +13968 1480 3600 2143 2141 +13969 2143 442 2148 2141 +13970 2140 30 442 2141 +13971 3600 3589 442 2141 +13972 3600 442 2143 2141 +13973 442 30 2148 2141 +13974 2140 2149 28 2141 +13975 30 2140 28 2141 +13976 28 2149 2148 2141 +13977 30 28 2148 2141 +13978 2119 2114 2118 2102 +13979 48 175 148 174 +13980 156 478 484 2184 +13981 1489 2112 150 2184 +13982 478 1458 1452 2181 +13983 156 478 1452 2181 +13984 156 2179 193 2181 +13985 2186 156 1452 2181 +13986 156 2186 2179 2181 +13987 2187 2189 2190 2182 +13988 485 1458 482 2181 +13989 1458 478 482 2181 +13990 1801 115 1884 122 +13991 170 175 2120 2114 +13992 2120 175 2121 2114 +13993 170 1489 1486 2114 +13994 1486 1489 2121 2114 +13995 170 1486 175 2114 +13996 175 1486 2121 2114 +13997 170 151 2114 2118 +13998 170 169 151 2118 +13999 2185 152 2112 2117 +14000 48 2120 169 2118 +14001 170 48 169 2118 +14002 48 170 2120 2118 +14003 191 2180 2190 2182 +14004 152 2185 2113 2116 +14005 169 173 2101 2111 +14006 1123 960 4043 4142 +14007 1260 1123 4043 4142 +14008 2120 48 169 2111 +14009 169 48 173 2111 +14010 2121 2120 2097 2111 +14011 960 1090 4043 4142 +14012 1090 1092 1260 4142 +14013 1478 2121 2097 2111 +14014 1478 1492 2121 2111 +14015 1092 1123 1260 4142 +14016 1090 1260 4043 4142 +14017 1477 1478 2097 2111 +14018 1477 1492 1478 2111 +14019 2178 2180 2187 2190 +14020 91 191 2190 2182 +14021 476 470 473 468 +14022 2179 193 2181 192 +14023 471 98 135 106 +14024 156 152 157 150 +14025 475 1459 473 472 +14026 151 170 2122 174 +14027 170 151 150 174 +14028 443 148 477 484 +14029 483 443 477 484 +14030 443 483 148 484 +14031 73 83 99 72 +14032 132 1815 1822 1825 +14033 1821 1824 1828 1786 +14034 2162 2203 2193 2161 +14035 123 1810 124 102 +14036 2204 2180 1470 2183 +14037 2192 471 135 106 +14038 485 2181 194 192 +14039 91 127 2204 2183 +14040 485 194 468 192 +14041 1813 121 1806 123 +14042 1470 468 191 192 +14043 2181 193 194 192 +14044 1449 1470 2178 192 +14045 2180 91 1470 2183 +14046 1470 91 1450 2183 +14047 115 1884 112 102 +14048 2519 2522 2477 2518 +14049 2201 127 2182 2183 +14050 1449 2178 2181 192 +14051 485 1449 2181 192 +14052 2180 2187 2182 2183 +14053 474 1464 471 116 +14054 135 471 2202 116 +14055 1470 2204 2183 2191 +14056 475 1459 472 2191 +14057 476 1459 1450 2191 +14058 91 1450 2183 2191 +14059 2204 91 2183 2191 +14060 476 91 472 2191 +14061 91 476 1450 2191 +14062 1459 476 473 2191 +14063 473 476 472 2191 +14064 1459 473 472 2191 +14065 98 2204 472 2191 +14066 91 98 472 2191 +14067 98 91 2204 2191 +14068 2159 2205 1462 2161 +14069 2193 2194 2192 2161 +14070 2205 1472 471 2192 +14071 1472 2205 2191 2192 +14072 2198 2162 2160 2161 +14073 1782 1779 1791 1781 +14074 1784 1778 1785 1780 +14075 2205 2194 2192 135 +14076 2194 2205 471 135 +14077 471 2205 2192 135 +14078 2203 2194 135 107 +14079 2194 2203 2193 107 +14080 2194 2192 135 107 +14081 2194 2193 2192 107 +14082 471 475 472 106 +14083 98 471 472 106 +14084 1472 471 2192 106 +14085 471 1472 475 106 +14086 472 475 2191 106 +14087 2200 98 2204 106 +14088 2204 98 472 106 +14089 475 1472 2191 106 +14090 2191 1472 2192 106 +14091 2204 2191 2192 106 +14092 2200 2204 2192 106 +14093 2204 472 2191 106 +14094 2205 2159 2198 2161 +14095 2155 2175 2173 2171 +14096 118 110 113 119 +14097 1948 1936 124 101 +14098 2205 1464 1462 2202 +14099 2205 1462 2161 2202 +14100 2194 2205 2161 2202 +14101 2205 2194 2203 2202 +14102 2203 2194 2161 2202 +14103 1813 1810 1812 1819 +14104 1815 1822 1825 1806 +14105 82 141 2167 2171 +14106 112 1811 113 119 +14107 2468 2473 2467 2430 +14108 1813 1815 1806 1819 +14109 80 129 72 1785 +14110 1815 132 1822 120 +14111 1814 1815 1825 1806 +14112 1822 132 1825 108 +14113 1820 1816 1822 120 +14114 122 115 112 113 +14115 128 1824 111 1821 +14116 132 1822 118 108 +14117 2460 653 661 656 +14118 115 137 103 112 +14119 123 124 101 102 +14120 2462 660 659 656 +14121 121 1813 1806 1819 +14122 1822 121 1806 1819 +14123 1810 1813 121 1819 +14124 1810 121 1822 1819 +14125 672 654 666 671 +14126 982 955 983 4289 +14127 134 132 1824 109 +14128 134 132 109 108 +14129 1820 128 1814 109 +14130 1814 128 1824 109 +14131 132 1814 1824 109 +14132 2172 2173 2167 2171 +14133 2173 82 2167 2171 +14134 80 1796 1817 78 +14135 2170 1533 2173 1540 +14136 2175 2170 2173 1540 +14137 1544 1533 1538 1540 +14138 1533 1544 2173 1540 +14139 2157 2156 2174 2154 +14140 105 104 2151 142 +14141 1798 99 1799 1776 +14142 83 1782 1789 73 +14143 1544 75 2173 1540 +14144 82 2175 2173 1540 +14145 75 82 2173 1540 +14146 1538 1532 412 1540 +14147 1532 75 412 1540 +14148 75 1532 2157 1540 +14149 1544 1538 412 1540 +14150 75 1544 412 1540 +14151 2175 82 2157 1540 +14152 82 75 2157 1540 +14153 1543 2176 2166 403 +14154 2166 1533 406 403 +14155 2528 2519 41 2518 +14156 76 75 2167 403 +14157 2167 75 400 403 +14158 1533 400 406 403 +14159 86 1878 1876 195 +14160 2170 1533 2166 403 +14161 76 2170 2166 403 +14162 2508 2510 41 2518 +14163 2170 2167 400 403 +14164 2170 76 2167 403 +14165 1533 2170 400 403 +14166 2170 1844 2177 2165 +14167 2476 2475 2523 2520 +14168 2478 2487 2488 2520 +14169 2475 2522 2525 2477 +14170 2533 2531 40 2520 +14171 40 2476 2523 2520 +14172 2525 2521 2524 2477 +14173 2522 2511 2525 2528 +14174 59 2487 2476 2520 +14175 2478 2475 2520 2477 +14176 2450 2439 2425 2442 +14177 2156 2152 2160 2153 +14178 1522 1532 1537 1539 +14179 1532 1522 2157 1539 +14180 413 1522 1537 1539 +14181 2156 2197 2164 2154 +14182 2164 141 2151 142 +14183 141 2164 2158 142 +14184 2160 2163 105 2153 +14185 1885 115 1950 103 +14186 2443 2511 681 678 +14187 2197 105 104 2151 +14188 414 2197 104 2151 +14189 2197 2156 2164 2151 +14190 2156 2197 414 2151 +14191 1874 1835 1832 1873 +14192 1522 2157 1539 2158 +14193 1539 2157 2174 2158 +14194 143 1858 1832 1873 +14195 2159 2198 2160 2153 +14196 2156 2164 2151 142 +14197 2164 2156 2174 142 +14198 2174 1539 2158 142 +14199 2156 1539 2174 142 +14200 82 141 75 411 +14201 1542 413 412 411 +14202 75 1542 412 411 +14203 104 414 2151 411 +14204 104 2151 142 411 +14205 141 1542 75 411 +14206 1541 413 1539 411 +14207 1541 414 413 411 +14208 413 1522 1539 411 +14209 1522 141 2158 411 +14210 2158 141 142 411 +14211 1522 413 1542 411 +14212 141 1522 1542 411 +14213 2156 1541 1539 411 +14214 1541 2156 414 411 +14215 1539 1522 2158 411 +14216 1539 2158 142 411 +14217 414 2156 2151 411 +14218 2156 1539 142 411 +14219 2151 2156 142 411 +14220 137 139 138 103 +14221 2196 2199 117 116 +14222 2196 2159 2199 116 +14223 2159 410 1468 116 +14224 1468 410 1527 116 +14225 2159 1468 1527 116 +14226 1464 1462 2202 116 +14227 2161 2159 117 116 +14228 2202 2161 117 116 +14229 2159 2196 2198 116 +14230 2198 2196 117 116 +14231 2159 2198 117 116 +14232 1462 2159 2161 116 +14233 1462 2161 2202 116 +14234 137 1954 1950 103 +14235 1954 1885 1950 103 +14236 103 115 112 102 +14237 1948 1885 138 103 +14238 1936 138 133 101 +14239 1884 115 101 102 +14240 115 103 101 102 +14241 124 1807 101 102 +14242 1805 1802 1811 113 +14243 1810 123 119 102 +14244 1804 118 110 113 +14245 115 1884 122 112 +14246 103 112 119 102 +14247 653 2534 2614 671 +14248 112 1884 119 102 +14249 1824 134 111 1821 +14250 1822 1825 1806 118 +14251 121 1822 1806 118 +14252 109 132 118 108 +14253 138 1948 103 101 +14254 1807 1884 101 102 +14255 1825 1804 1806 118 +14256 2459 686 662 2454 +14257 1807 1805 1884 102 +14258 1804 118 113 123 +14259 1804 1806 118 123 +14260 1802 1813 1806 123 +14261 1805 1802 113 123 +14262 1802 1805 1813 123 +14263 1802 1804 113 123 +14264 1804 1802 1806 123 +14265 1884 1811 122 119 +14266 122 1811 112 119 +14267 1884 122 112 119 +14268 1807 1808 124 101 +14269 1885 115 103 101 +14270 1808 1807 1885 101 +14271 109 134 108 111 +14272 128 109 108 111 +14273 1885 1807 1884 101 +14274 1801 1885 1884 101 +14275 1885 1801 115 101 +14276 115 1801 1884 101 +14277 1884 1805 119 102 +14278 1810 1807 124 102 +14279 1805 1807 1810 102 +14280 1805 1810 119 102 +14281 1827 134 1821 114 +14282 1804 1802 113 114 +14283 1802 1804 1826 114 +14284 1826 1804 1827 114 +14285 110 1804 113 114 +14286 2450 2426 2449 2428 +14287 2455 2459 2466 2464 +14288 685 2432 2436 2434 +14289 110 1827 108 114 +14290 1804 110 108 114 +14291 1827 1804 108 114 +14292 687 686 2455 676 +14293 1796 1793 1814 1787 +14294 2610 655 2614 671 +14295 2534 2610 2614 671 +14296 2451 2534 653 671 +14297 2431 685 2429 2464 +14298 655 2610 651 671 +14299 2534 2451 2610 671 +14300 685 2431 2429 2428 +14301 2610 2604 651 671 +14302 2604 2462 651 671 +14303 2451 2462 2610 671 +14304 2610 2462 2604 671 +14305 2460 2451 653 656 +14306 686 2467 687 2430 +14307 2468 686 2427 2430 +14308 686 2468 2467 2430 +14309 661 2474 662 2454 +14310 2459 2474 2456 2454 +14311 2474 2459 662 2454 +14312 2474 2472 2456 2454 +14313 2472 2474 661 2454 +14314 2429 2449 2430 2464 +14315 2429 2430 2466 2464 +14316 687 2455 2430 2464 +14317 2431 2440 2445 2434 +14318 2465 690 691 695 +14319 690 2465 2449 695 +14320 682 677 679 678 +14321 687 2467 691 695 +14322 1790 1783 1856 1792 +14323 2465 2467 2449 695 +14324 2467 687 2430 695 +14325 2449 2467 2430 695 +14326 2430 687 2464 695 +14327 2449 2430 2464 695 +14328 2467 2465 692 695 +14329 692 2465 691 695 +14330 2467 692 691 695 +14331 2421 2449 2429 676 +14332 2421 690 2449 676 +14333 2444 2443 2441 679 +14334 2435 2444 679 2436 +14335 2431 685 2434 2428 +14336 2432 685 676 694 +14337 2447 19 682 678 +14338 2439 2446 2425 682 +14339 2425 2446 680 682 +14340 1788 1790 1783 1856 +14341 19 2509 681 678 +14342 677 2435 682 679 +14343 1793 1860 1861 1794 +14344 1857 1788 1867 1883 +14345 2529 2533 40 2524 +14346 2437 2439 2443 2441 +14347 2511 2522 2519 2528 +14348 2437 2439 2441 679 +14349 19 2517 2513 681 +14350 2513 2517 2507 681 +14351 2507 2511 2527 681 +14352 2507 2517 2509 681 +14353 2517 19 2509 681 +14354 2494 2481 2278 2480 +14355 2511 2507 2443 681 +14356 2276 2478 2493 2488 +14357 2487 2482 2476 2478 +14358 58 2481 2482 2478 +14359 2525 2523 2524 2528 +14360 2487 2484 2482 2478 +14361 3627 3611 2125 4011 +14362 2484 58 2482 2478 +14363 2525 2528 41 2518 +14364 2444 2511 2527 2528 +14365 2521 2531 2533 2520 +14366 3958 644 2909 4016 +14367 2276 2495 2278 2480 +14368 1830 1834 1845 1831 +14369 1831 1853 1833 1840 +14370 2476 2487 2478 2520 +14371 2495 2494 2277 2480 +14372 2277 2494 2278 2480 +14373 2495 2277 2278 2480 +14374 1850 1834 1841 1831 +14375 9 16 18 17 +14376 2279 51 2282 53 +14377 196 195 1789 1792 +14378 2479 2480 2282 53 +14379 51 2479 2282 53 +14380 1846 1851 2496 1845 +14381 2481 58 2480 53 +14382 58 2481 2494 53 +14383 2494 2481 2480 53 +14384 2489 2483 61 53 +14385 2483 2489 2506 53 +14386 58 2489 61 53 +14387 2734 3183 2750 4008 +14388 2485 2479 61 53 +14389 2483 2485 61 53 +14390 2485 2483 2479 53 +14391 2494 2506 2488 53 +14392 58 2494 2488 53 +14393 2506 2489 2488 53 +14394 2489 58 2488 53 +14395 2475 2523 2520 2477 +14396 2521 2533 2524 2477 +14397 2533 2521 2520 2477 +14398 2475 2525 2523 2477 +14399 2523 2525 2524 2477 +14400 40 2523 2524 2477 +14401 2523 40 2520 2477 +14402 2533 40 2524 2477 +14403 40 2533 2520 2477 +14404 60 681 41 5 +14405 59 62 40 5 +14406 2508 2513 2527 41 +14407 2508 60 2513 41 +14408 2521 2520 2477 2518 +14409 60 2508 2510 41 +14410 2508 2507 2510 2518 +14411 2519 2527 2528 41 +14412 2508 2527 2519 41 +14413 2439 2443 2441 2442 +14414 2529 2521 2524 5 +14415 2510 60 41 5 +14416 2521 2525 2524 5 +14417 2521 2529 2530 5 +14418 2530 60 2510 5 +14419 2521 2530 2510 5 +14420 2524 2525 2528 5 +14421 2528 2525 41 5 +14422 2524 2528 41 5 +14423 2529 60 2516 5 +14424 2516 60 2530 5 +14425 2529 2516 2530 5 +14426 2432 2450 2424 2445 +14427 2424 2450 2442 2445 +14428 2432 2424 2442 2445 +14429 2442 2440 2436 2445 +14430 677 2442 2436 2445 +14431 2432 677 2436 2445 +14432 677 2432 2442 2445 +14433 690 2421 689 694 +14434 2421 2426 689 694 +14435 2450 2442 2445 2428 +14436 1829 1869 1839 1840 +14437 2429 2421 676 694 +14438 1841 1850 1831 146 +14439 1246 1243 4285 4319 +14440 2436 2432 2445 2434 +14441 2432 2450 2445 2434 +14442 693 2450 2432 2434 +14443 2432 685 694 2434 +14444 693 2432 694 2434 +14445 2426 693 694 2428 +14446 685 694 2434 2428 +14447 694 693 2434 2428 +14448 693 2450 2434 2428 +14449 1839 1881 144 1848 +14450 1795 1797 71 1861 +14451 1829 1874 1832 1873 +14452 1869 1854 1839 1840 +14453 3627 2145 3592 4011 +14454 3625 1045 3626 4011 +14455 1834 2497 1841 1831 +14456 16 9 4126 4353 +14457 3640 3679 3105 4310 +14458 1783 1791 71 73 +14459 1788 86 196 1859 +14460 1849 1853 8 1848 +14461 147 1877 145 1838 +14462 1788 1790 196 1883 +14463 1836 1841 1838 1833 +14464 147 1836 1838 1833 +14465 1865 195 1798 227 +14466 1874 1877 145 1835 +14467 1835 1882 1832 1873 +14468 147 146 8 17 +14469 1868 1869 1858 1832 +14470 143 203 1881 144 +14471 1836 1833 144 1840 +14472 215 203 145 143 +14473 215 145 1882 143 +14474 1839 1829 203 1881 +14475 1869 1829 1839 1881 +14476 144 1836 1840 146 +14477 203 145 143 1832 +14478 227 86 226 1883 +14479 1850 1836 1851 146 +14480 1839 144 1840 1848 +14481 1878 1795 1862 1792 +14482 1878 195 1859 1792 +14483 1868 1832 1870 1867 +14484 145 1835 1882 1832 +14485 145 1882 143 1832 +14486 1829 1874 145 1832 +14487 145 1874 1835 1832 +14488 147 1839 1881 144 +14489 1818 1781 1794 1785 +14490 1791 1795 1859 1792 +14491 1853 1840 146 8 +14492 99 1776 1781 1861 +14493 203 147 144 8 +14494 147 1836 1833 144 +14495 1839 147 1833 144 +14496 1854 1853 1849 1848 +14497 1853 1840 8 1848 +14498 1853 1854 1840 1848 +14499 147 1836 144 146 +14500 1779 1782 1818 1781 +14501 1852 1849 1847 1848 +14502 1841 1831 1840 146 +14503 1836 1841 1840 146 +14504 1850 1845 1831 146 +14505 16 9 1847 17 +14506 16 1847 1851 17 +14507 8 1849 1848 17 +14508 1847 8 1848 17 +14509 1849 1847 1848 17 +14510 1696 4257 982 3083 +14511 1700 1697 4147 4289 +14512 4257 1696 982 4289 +14513 1850 1845 146 17 +14514 1851 1850 146 17 +14515 1850 1851 1845 17 +14516 1853 1849 8 17 +14517 1849 1853 1830 17 +14518 146 1853 8 17 +14519 1845 1830 1831 17 +14520 1845 1831 146 17 +14521 1830 1853 1831 17 +14522 1831 1853 146 17 +14523 1860 1777 1799 1776 +14524 215 1858 1870 206 +14525 226 1870 1867 206 +14526 226 1867 1883 206 +14527 1867 1857 1883 206 +14528 1782 83 1818 1781 +14529 1783 1779 1856 1792 +14530 99 1860 1776 1861 +14531 86 226 1883 206 +14532 1858 215 1870 1873 +14533 1858 1868 1832 1870 +14534 1820 128 109 78 +14535 1866 1880 1879 1871 +14536 1827 1821 1828 1786 +14537 1824 1804 1825 1828 +14538 1783 1795 1789 71 +14539 1789 1795 1798 71 +14540 1876 1863 227 1872 +14541 1795 1797 1798 71 +14542 1798 195 71 99 +14543 1795 1783 1791 71 +14544 86 1788 1863 1859 +14545 1788 1790 1856 1859 +14546 1790 1788 196 1859 +14547 1877 1837 1838 1880 +14548 86 1863 1878 1859 +14549 1782 1783 1789 73 +14550 86 1878 195 1859 +14551 86 195 196 1859 +14552 1880 1835 1871 1873 +14553 196 1789 71 73 +14554 195 1878 1798 1792 +14555 1791 71 73 99 +14556 1796 1793 1800 1860 +14557 1797 1864 1777 1799 +14558 1882 215 1880 1866 +14559 1788 1863 1859 1867 +14560 1865 1876 227 1866 +14561 145 215 1882 226 +14562 1880 1835 1879 1871 +14563 1875 1878 1876 1872 +14564 1878 1875 1862 1872 +14565 215 1880 1866 226 +14566 1788 1868 1857 1867 +14567 1880 1876 1866 1879 +14568 1863 1870 1871 1873 +14569 1876 227 1866 1872 +14570 1876 1866 1879 1872 +14571 1870 215 1871 1873 +14572 215 1880 1871 1873 +14573 1882 143 1832 1873 +14574 1857 1868 1870 1867 +14575 1866 227 226 1872 +14576 1790 1788 1857 1883 +14577 128 80 109 78 +14578 1820 109 120 78 +14579 1868 1858 1857 1870 +14580 196 86 1883 206 +14581 1863 227 1872 1867 +14582 227 226 1872 1867 +14583 1870 1863 1871 1867 +14584 1863 1872 1871 1867 +14585 226 1870 1871 1867 +14586 1872 226 1871 1867 +14587 1788 86 1863 1883 +14588 1788 1863 1867 1883 +14589 227 226 1867 1883 +14590 1863 227 1867 1883 +14591 1863 86 227 1883 +14592 109 132 120 78 +14593 1820 120 1817 78 +14594 134 111 1821 131 +14595 1797 1798 99 1799 +14596 80 1777 1776 1817 +14597 1777 1799 1776 1817 +14598 1824 1814 1825 1787 +14599 1797 1795 1862 1861 +14600 1864 1797 1862 1861 +14601 1860 99 1799 1861 +14602 1796 1820 1817 78 +14603 80 1793 1784 1785 +14604 1821 1778 131 1823 +14605 1864 1862 1860 1861 +14606 1795 1878 1862 1861 +14607 1796 1820 1800 1817 +14608 1793 1800 1777 1817 +14609 80 1793 1777 1817 +14610 1793 1796 1800 1817 +14611 1793 80 1796 1817 +14612 80 1784 1796 78 +14613 71 73 99 72 +14614 129 128 80 72 +14615 129 1778 1784 131 +14616 80 1784 78 131 +14617 80 129 1784 131 +14618 128 129 80 131 +14619 128 80 78 131 +14620 1777 80 1776 72 +14621 83 129 1818 72 +14622 1793 80 72 1785 +14623 1198 1547 1197 4199 +14624 73 1791 99 1781 +14625 83 73 99 1781 +14626 1781 1791 1794 1785 +14627 3647 3679 3659 4331 +14628 83 99 1776 1781 +14629 83 1818 1781 1794 +14630 1818 83 72 1794 +14631 1777 1793 72 1794 +14632 1793 1777 1860 1794 +14633 1860 1776 1781 1794 +14634 1776 83 1781 1794 +14635 83 1776 72 1794 +14636 1781 1784 1785 1780 +14637 1860 1777 1776 1794 +14638 1776 1777 72 1794 +14639 1784 1796 78 1787 +14640 1796 1820 78 1787 +14641 1820 128 78 1787 +14642 3348 3276 3303 4002 +14643 1804 1802 1826 1828 +14644 128 1824 1821 1823 +14645 111 1821 131 1823 +14646 128 111 131 1823 +14647 111 128 1821 1823 +14648 1820 1796 1814 1787 +14649 1784 131 1823 1787 +14650 1824 1784 1823 1787 +14651 131 128 1823 1787 +14652 128 1824 1823 1787 +14653 78 128 131 1787 +14654 1784 78 131 1787 +14655 332 2889 2888 4210 +14656 134 1827 1821 1828 +14657 1824 134 1821 1828 +14658 1827 134 1825 1828 +14659 132 134 1824 1828 +14660 1814 1824 1825 1828 +14661 1815 1814 1825 1828 +14662 132 1815 1825 1828 +14663 134 132 1825 1828 +14664 132 1814 1815 1828 +14665 1814 132 1824 1828 +14666 1418 1414 1143 4287 +14667 3071 3000 4234 4274 +14668 1411 1415 1143 4287 +14669 1003 1002 4122 4329 +14670 4330 3258 4478 4483 +14671 4096 984 4122 4379 +14672 984 1003 4122 4379 +14673 1003 984 4096 4379 +14674 1774 1773 1181 4568 +14675 3086 1174 4041 4061 +14676 4104 16 4126 4353 +14677 1247 1682 1680 4004 +14678 786 740 4231 4537 +14679 2274 1199 2286 4334 +14680 3086 3030 3013 4061 +14681 3086 3013 1305 4061 +14682 4543 4615 4683 4704 +14683 3300 3638 4105 4107 +14684 639 2917 4068 4281 +14685 1323 1272 1274 4115 +14686 1330 1091 1088 4115 +14687 434 405 2677 4549 +14688 1844 3560 3561 4018 +14689 1330 3156 985 4115 +14690 1705 1698 4155 4461 +14691 1712 1723 1749 4048 +14692 1844 3561 3559 4018 +14693 1660 4012 4064 4522 +14694 3379 3066 3594 4467 +14695 3557 3611 3610 4011 +14696 3626 1045 1038 4011 +14697 3610 3626 1038 4011 +14698 3557 3610 1038 4011 +14699 3611 3557 466 4011 +14700 3557 1038 2137 4011 +14701 466 3557 2137 4011 +14702 1612 1556 1228 4168 +14703 1612 1228 4161 4168 +14704 1614 1612 4161 4168 +14705 3611 466 488 4011 +14706 2125 3611 488 4011 +14707 1513 2038 2042 4238 +14708 1151 1316 1551 4209 +14709 1574 1549 1551 4209 +14710 1548 1316 1549 4209 +14711 3067 3075 3074 4286 +14712 1721 1692 1715 4323 +14713 3119 3700 3280 4086 +14714 2909 3274 2829 4016 +14715 2829 3274 3860 4016 +14716 2826 2829 3860 4016 +14717 2826 2909 2829 4016 +14718 1088 1091 1313 4115 +14719 2505 2504 4120 4299 +14720 343 339 346 4160 +14721 1318 346 4099 4160 +14722 985 1323 1274 4115 +14723 1313 1330 985 4115 +14724 3703 3118 711 4086 +14725 3257 2810 4276 4330 +14726 4012 4057 4245 4247 +14727 4230 3040 4237 4272 +14728 3899 3917 4039 4054 +14729 1189 1148 1191 4055 +14730 346 1322 4099 4160 +14731 1323 3156 1330 4115 +14732 984 1085 4096 4122 +14733 3701 3699 3646 4101 +14734 704 706 3269 4072 +14735 1670 1691 1694 4396 +14736 2038 229 2042 4634 +14737 339 1321 346 4160 +14738 4022 3288 4357 4430 +14739 2588 1072 1060 4053 +14740 1060 1072 1062 4053 +14741 3808 3805 3870 4426 +14742 706 704 3113 4072 +14743 3064 3500 4607 4615 +14744 3455 3453 4425 4574 +14745 4053 2588 4253 4347 +14746 3805 3312 313 4073 +14747 3113 704 3099 4072 +14748 3606 2786 4304 4605 +14749 3100 704 3269 4072 +14750 3681 3716 3091 4151 +14751 3388 3500 4583 4639 +14752 3634 3644 3091 4151 +14753 2577 1618 4084 4093 +14754 2282 2480 4296 4299 +14755 967 1087 966 4532 +14756 793 1192 1632 4263 +14757 401 3543 3578 4094 +14758 4047 4050 4131 4711 +14759 3543 3541 3578 4094 +14760 1399 1401 4146 4159 +14761 1192 793 1639 4263 +14762 1401 1748 4146 4159 +14763 3680 3681 3644 4151 +14764 1066 1073 1064 4253 +14765 1028 1029 980 4029 +14766 982 1028 980 4029 +14767 1066 1064 4053 4253 +14768 982 979 1028 4029 +14769 1555 786 1224 4063 +14770 3065 3380 3066 4031 +14771 3064 3065 3066 4031 +14772 3930 3101 3473 4545 +14773 3064 3044 2784 4031 +14774 3045 3064 3066 4031 +14775 3380 3045 3066 4031 +14776 1576 1183 1184 4544 +14777 4592 4322 4613 4625 +14778 1577 1183 1576 4544 +14779 3044 2782 2784 4031 +14780 3044 3380 2782 4031 +14781 3045 3380 3044 4031 +14782 3064 3045 3044 4031 +14783 713 710 345 4032 +14784 611 597 823 4663 +14785 1707 1672 4166 4510 +14786 713 345 796 4032 +14787 3686 3685 4180 4633 +14788 1627 2350 736 4341 +14789 3008 2729 3054 4548 +14790 295 4139 4241 4462 +14791 3006 3004 4006 4037 +14792 2579 1183 1606 4616 +14793 1669 1670 1715 4080 +14794 1713 1715 1690 4080 +14795 1672 1667 1669 4080 +14796 1609 1610 1918 4561 +14797 3156 1323 985 4115 +14798 3102 3852 3264 4123 +14799 1698 1723 1731 4155 +14800 3514 3502 3418 4156 +14801 1331 1323 1330 4115 +14802 434 2660 4281 4549 +14803 3550 3542 3549 4094 +14804 3550 3549 3540 4094 +14805 3449 2781 3450 4092 +14806 3082 982 4573 4584 +14807 3907 3904 4117 4411 +14808 3408 3410 4435 4555 +14809 3319 3965 592 4447 +14810 2287 2269 2288 4334 +14811 1119 1109 1738 4541 +14812 1405 1417 1121 4740 +14813 1245 1671 4258 4446 +14814 3134 3172 3138 4136 +14815 1611 1080 1081 4628 +14816 3200 3046 3232 4075 +14817 3200 3232 4006 4075 +14818 3428 3594 3605 3427 +14819 3109 710 4003 4107 +14820 705 710 3645 4107 +14821 4399 1417 4406 4442 +14822 1602 740 4088 4110 +14823 737 1598 4058 4110 +14824 3104 3109 4105 4107 +14825 3104 3638 3106 4107 +14826 3923 2785 2679 4646 +14827 3638 3300 3105 4107 +14828 710 3640 3645 4107 +14829 3638 3104 4105 4107 +14830 3106 710 3109 4107 +14831 710 705 4003 4107 +14832 3094 705 3645 4107 +14833 1089 1331 1088 4115 +14834 704 3100 3096 4072 +14835 3100 3099 3096 4072 +14836 3437 3438 3417 4112 +14837 1001 1331 998 4115 +14838 3439 3437 3503 4112 +14839 2286 2284 4334 4567 +14840 3437 3417 3503 4112 +14841 3438 3414 3417 4112 +14842 1705 1698 1708 4155 +14843 1322 1318 1315 4099 +14844 346 1216 348 4099 +14845 1513 2018 4238 4365 +14846 2018 1518 4238 4365 +14847 3540 3541 4094 4490 +14848 3093 3646 3645 4128 +14849 3646 3131 4125 4128 +14850 3546 3400 3402 4723 +14851 3226 2799 4042 4227 +14852 1148 1191 1139 4129 +14853 750 737 752 4100 +14854 1705 1708 1709 4155 +14855 1723 1749 4048 4155 +14856 1621 1189 1221 4055 +14857 1221 1189 1191 4055 +14858 1621 1148 1189 4055 +14859 1216 348 4718 4733 +14860 1659 3932 4292 4315 +14861 314 2900 321 4587 +14862 2996 3003 4006 4475 +14863 1077 1075 1919 4603 +14864 1136 1312 1212 4056 +14865 343 814 339 4056 +14866 1311 343 339 4056 +14867 1136 1212 342 4056 +14868 343 1136 342 4056 +14869 862 3197 2907 4248 +14870 3253 3195 3196 4248 +14871 3195 862 628 4248 +14872 1671 1243 4004 4060 +14873 1243 1671 1245 4060 +14874 345 1244 1245 4060 +14875 826 1149 1665 4060 +14876 1149 1244 1665 4060 +14877 1680 1671 4004 4060 +14878 345 826 1665 4060 +14879 1244 345 1665 4060 +14880 1680 1686 1671 4060 +14881 1671 1686 1245 4060 +14882 1682 1680 4004 4060 +14883 1686 345 1245 4060 +14884 1686 1682 345 4060 +14885 1682 1686 1680 4060 +14886 3054 3062 4205 4474 +14887 3172 3050 4030 4149 +14888 3082 982 3048 4136 +14889 1305 3013 3145 4061 +14890 3030 1305 3145 4061 +14891 3013 3030 3145 4061 +14892 3029 3030 3019 4061 +14893 4273 706 4534 4591 +14894 3086 3015 3030 4061 +14895 2287 2274 2286 4334 +14896 1208 2264 583 4334 +14897 2274 2287 2288 4334 +14898 3700 3699 4101 4128 +14899 438 2016 202 4134 +14900 1661 2871 1655 4064 +14901 1655 2871 4012 4064 +14902 1276 1240 1277 4144 +14903 1183 1576 1184 4485 +14904 3158 3157 726 4243 +14905 1200 2264 1208 4065 +14906 3108 706 4218 4591 +14907 3386 4543 4607 4615 +14908 1118 1395 1120 4066 +14909 1148 1222 1189 4129 +14910 1395 1118 1133 4066 +14911 1118 1112 1133 4066 +14912 1133 1112 1403 4066 +14913 1584 1705 4226 4389 +14914 2970 3335 2839 4456 +14915 1175 1121 1120 4190 +14916 2480 2479 2282 4076 +14917 4139 295 4368 4462 +14918 3605 4234 4467 4755 +14919 3330 3887 3317 4119 +14920 3887 3319 3317 4119 +14921 1714 1667 1672 4080 +14922 3923 2679 4588 4646 +14923 1670 1667 1713 4080 +14924 4016 644 4028 4069 +14925 644 3773 4028 4069 +14926 1303 1158 1156 4608 +14927 3321 2704 4038 4183 +14928 576 3312 3870 4426 +14929 3050 4030 4127 4136 +14930 576 3108 4176 4426 +14931 990 994 1261 4179 +14932 1546 2280 4020 4182 +14933 3633 3405 3662 4180 +14934 576 3312 3108 4378 +14935 3292 3268 4187 4378 +14936 3300 3312 705 4378 +14937 640 642 4069 4434 +14938 1140 4022 4024 4479 +14939 3955 640 3884 4434 +14940 3662 3405 3684 4180 +14941 2885 2941 2942 4183 +14942 2885 2886 2941 4183 +14943 1001 1272 1331 4115 +14944 1119 1742 4518 4541 +14945 705 576 3108 4378 +14946 970 966 4096 4179 +14947 3965 3319 4119 4447 +14948 966 970 994 4179 +14949 1088 1313 1130 4115 +14950 4517 1116 4525 4719 +14951 1322 1315 346 4099 +14952 737 1600 752 4100 +14953 611 4021 4199 4535 +14954 3232 4006 4075 4387 +14955 2280 1618 1620 4182 +14956 1314 346 348 4099 +14957 1315 1216 346 4099 +14958 3699 3701 3700 4101 +14959 1589 1116 1588 4719 +14960 295 2971 590 4139 +14961 592 3908 4455 4464 +14962 1670 1713 1690 4080 +14963 1715 1670 1690 4080 +14964 1666 1672 1669 4080 +14965 1722 1721 1715 4080 +14966 1331 1001 998 4542 +14967 348 1216 4657 4733 +14968 1721 1666 1669 4080 +14969 4307 3446 4371 4386 +14970 201 1054 85 4359 +14971 1054 1203 85 4359 +14972 3762 702 4572 4581 +14973 3715 3175 3171 4405 +14974 1631 753 4233 4359 +14975 3130 3171 1258 4405 +14976 3100 702 703 4581 +14977 3264 3100 3097 4581 +14978 4292 3932 4357 4626 +14979 2733 2735 2736 4348 +14980 577 575 576 4426 +14981 3282 3119 3280 4086 +14982 3110 3282 3280 4086 +14983 3110 3095 711 4086 +14984 3119 3110 711 4086 +14985 3093 3703 711 4086 +14986 3095 3093 711 4086 +14987 3093 3095 3703 4086 +14988 430 1515 204 4087 +14989 1510 1515 429 4087 +14990 1515 1510 1519 4087 +14991 429 441 436 4087 +14992 1510 429 436 4087 +14993 441 1519 436 4087 +14994 1519 1510 436 4087 +14995 607 644 702 4713 +14996 4047 4050 4121 4635 +14997 1254 1144 4403 4635 +14998 1646 822 1605 4089 +14999 1646 1605 1918 4089 +15000 1221 172 1041 4089 +15001 1607 1610 1918 4089 +15002 1646 1610 1607 4089 +15003 1605 822 1187 4089 +15004 1187 822 1918 4089 +15005 1605 1187 1918 4089 +15006 1607 1636 1569 4089 +15007 1646 1607 1569 4089 +15008 1636 1646 1569 4089 +15009 3189 2908 643 4498 +15010 650 622 608 4498 +15011 3076 3379 3427 4467 +15012 3453 4325 4425 4574 +15013 2830 3206 2819 4594 +15014 982 3225 3259 4584 +15015 3567 3549 3576 4094 +15016 3904 3567 3576 4094 +15017 3577 3904 3576 4094 +15018 3906 3577 3576 4094 +15019 401 3906 3576 4094 +15020 3549 401 3576 4094 +15021 3575 3549 3567 4094 +15022 3549 3575 3904 4094 +15023 3904 3575 3567 4094 +15024 1191 1221 4055 4390 +15025 713 3699 4513 4641 +15026 1280 1585 1586 4313 +15027 2658 197 4134 4153 +15028 4025 3449 4195 4267 +15029 2276 2504 4007 4326 +15030 222 218 2042 4634 +15031 3179 2733 4008 4391 +15032 3519 3607 4211 4476 +15033 4466 3609 4470 4476 +15034 1230 1576 1184 4544 +15035 1631 1052 751 4233 +15036 2652 2658 4134 4153 +15037 197 438 4134 4153 +15038 753 1648 1631 4233 +15039 1702 1270 4032 4617 +15040 4194 3581 4264 4369 +15041 3101 3091 4338 4530 +15042 434 404 440 4153 +15043 1321 1322 346 4160 +15044 3901 295 4139 4241 +15045 1551 1316 1246 4285 +15046 3916 3906 408 4117 +15047 3583 3083 4019 4257 +15048 1689 1270 345 4650 +15049 3133 4044 4081 4669 +15050 992 1001 4096 4542 +15051 1303 1304 4203 4608 +15052 4404 2135 4547 4580 +15053 3300 3292 4003 4105 +15054 3292 3104 3655 4105 +15055 3292 3655 4003 4105 +15056 3655 3104 3109 4105 +15057 3655 3109 4003 4105 +15058 1158 1303 1157 4608 +15059 1675 1677 1668 4108 +15060 3319 584 592 4464 +15061 3213 2823 3220 4648 +15062 4116 1587 4417 4744 +15063 3051 3060 3050 4282 +15064 3005 3181 4367 4495 +15065 3186 3023 3181 4495 +15066 1002 4013 4122 4329 +15067 4234 3065 4286 4467 +15068 3380 3369 3375 4380 +15069 3455 3597 4267 4574 +15070 2699 2887 4038 4162 +15071 2886 2697 2929 4162 +15072 1614 740 1557 4110 +15073 2852 3342 2854 4111 +15074 4230 3058 4443 4450 +15075 1207 2268 4334 4567 +15076 1208 2264 4334 4567 +15077 1637 794 826 4113 +15078 1198 826 1155 4113 +15079 1643 1252 1152 4113 +15080 1643 1571 1252 4113 +15081 1642 1152 1154 4113 +15082 1642 1154 1635 4113 +15083 1571 1642 1635 4113 +15084 821 1637 826 4113 +15085 1077 1080 4603 4681 +15086 1198 1565 1567 4113 +15087 1567 1565 1155 4113 +15088 1198 821 826 4113 +15089 1571 1643 1642 4113 +15090 1642 1643 1152 4113 +15091 1198 1567 1637 4113 +15092 821 1198 1637 4113 +15093 1498 405 2677 4529 +15094 990 989 994 4179 +15095 3041 1144 4352 4527 +15096 592 3965 4119 4455 +15097 1589 4742 4747 4749 +15098 4307 2667 4385 4386 +15099 751 1052 4169 4233 +15100 1578 1197 4209 4285 +15101 592 712 4354 4447 +15102 1576 1577 1149 4680 +15103 753 1036 84 4132 +15104 998 999 4115 4342 +15105 3322 3312 4682 4753 +15106 966 1087 4172 4532 +15107 1721 1715 4080 4323 +15108 3072 4726 4752 4755 +15109 2499 2497 4280 4521 +15110 3286 3287 4357 4430 +15111 966 970 4096 4122 +15112 970 966 1085 4122 +15113 1085 966 4096 4122 +15114 775 1047 1595 4290 +15115 1146 1254 1144 4403 +15116 1222 1148 1226 4129 +15117 3644 3680 3636 4125 +15118 1113 1119 4173 4223 +15119 1054 1055 1203 4359 +15120 3637 3663 3680 4125 +15121 3680 3663 3636 4125 +15122 3560 3544 3537 4339 +15123 982 3052 3048 4127 +15124 3312 576 3108 4176 +15125 3052 2812 3048 4127 +15126 2681 3205 2677 4549 +15127 1582 1589 4747 4749 +15128 1197 1317 4209 4319 +15129 2812 2753 3250 4127 +15130 2729 2812 4103 4127 +15131 2812 2729 2753 4127 +15132 347 786 1561 4537 +15133 1197 4209 4285 4319 +15134 3048 2812 3242 4127 +15135 3242 2812 3250 4127 +15136 3048 3242 3250 4127 +15137 955 4289 4397 4712 +15138 2661 1036 4138 4702 +15139 3258 3083 4257 4276 +15140 2135 1632 1629 4580 +15141 1265 1263 4121 4131 +15142 2754 2753 4051 4548 +15143 2129 2131 3559 4225 +15144 3319 3939 3285 4420 +15145 3901 591 576 4139 +15146 576 591 590 4139 +15147 3319 3939 4119 4447 +15148 591 295 590 4139 +15149 4172 966 4465 4612 +15150 584 3908 592 4464 +15151 2828 3276 4097 4165 +15152 201 2016 2013 4134 +15153 202 2016 2029 4134 +15154 201 202 2029 4134 +15155 2016 201 2029 4134 +15156 1734 4049 4098 4154 +15157 1191 1645 4055 4271 +15158 2013 438 1034 4134 +15159 3081 2784 3624 4440 +15160 1034 438 4102 4134 +15161 3221 3213 4350 4648 +15162 3633 3686 4180 4633 +15163 2658 197 1032 4134 +15164 2652 2658 1032 4134 +15165 2784 3081 4031 4440 +15166 197 201 1032 4134 +15167 1034 2652 1032 4134 +15168 3958 2904 2908 4761 +15169 1148 1191 4055 4271 +15170 3958 2908 2911 4761 +15171 1693 1681 1688 4446 +15172 822 1187 4400 4520 +15173 4327 988 4460 4609 +15174 1023 1020 4340 4609 +15175 1266 1733 4579 4640 +15176 988 4340 4460 4609 +15177 1688 1244 1245 4204 +15178 1732 1735 4364 4745 +15179 1019 4207 4340 4609 +15180 4029 4251 4444 4728 +15181 1264 1265 4131 4185 +15182 4041 3086 4061 4445 +15183 3955 3773 642 4434 +15184 4527 4611 4627 4631 +15185 3335 2840 2839 4139 +15186 2838 3901 576 4139 +15187 3335 2838 576 4139 +15188 2840 2696 590 4139 +15189 2696 2840 3335 4139 +15190 2838 3335 2839 4139 +15191 2840 2838 2839 4139 +15192 2696 3335 3313 4139 +15193 3313 3335 590 4139 +15194 2696 3313 590 4139 +15195 2838 2840 3788 4139 +15196 3901 2838 3788 4139 +15197 2840 3901 3788 4139 +15198 207 2039 2040 4213 +15199 2121 1492 1493 4141 +15200 175 2121 1493 4141 +15201 2039 2044 2041 4213 +15202 1512 2044 2036 4213 +15203 207 223 205 4213 +15204 1512 2036 1514 4213 +15205 2036 207 205 4213 +15206 2044 204 223 4213 +15207 2044 223 2041 4213 +15208 404 2684 440 4153 +15209 1503 2652 438 4153 +15210 1503 2684 434 4153 +15211 434 2684 404 4153 +15212 2684 438 440 4153 +15213 1845 1830 17 4192 +15214 1720 1280 1666 4145 +15215 1280 1720 1590 4145 +15216 1175 1266 1121 4190 +15217 31 1477 2097 4212 +15218 1371 1512 1514 4216 +15219 1515 1512 1371 4216 +15220 1514 1512 4213 4216 +15221 1608 172 1644 4314 +15222 1691 1693 1713 4258 +15223 3592 3625 4011 4167 +15224 3625 1045 4011 4167 +15225 2145 3592 4011 4167 +15226 1230 1603 1576 4544 +15227 3933 793 3886 4438 +15228 1666 1280 4313 4571 +15229 3172 4030 4136 4149 +15230 3134 3172 4136 4149 +15231 1150 1574 1551 4209 +15232 3181 3151 4367 4495 +15233 3597 3599 4137 4150 +15234 1401 4066 4124 4402 +15235 4312 4178 4607 4656 +15236 3863 3819 3312 4176 +15237 3077 3456 4481 4546 +15238 3312 3806 3870 4176 +15239 1228 1559 4161 4188 +15240 3806 3266 3870 4176 +15241 2501 2283 4076 4299 +15242 1031 1032 4184 4560 +15243 3644 3681 3091 4151 +15244 3665 3680 3636 4151 +15245 3680 3665 3685 4151 +15246 3685 3665 3636 4151 +15247 3634 3685 3636 4151 +15248 3644 3634 3636 4151 +15249 3101 3634 3091 4151 +15250 3686 3556 3685 4151 +15251 4385 4279 4508 4674 +15252 3685 3688 4180 4246 +15253 1319 1219 1321 4164 +15254 1319 339 4133 4164 +15255 1320 1560 1561 4164 +15256 1834 1830 1845 4192 +15257 1851 1845 17 4192 +15258 821 826 348 4535 +15259 3319 592 586 4447 +15260 795 1153 1645 4271 +15261 3449 3454 4025 4195 +15262 1179 1392 1390 4739 +15263 3276 318 321 4587 +15264 4607 3064 4615 4704 +15265 2781 3449 3603 4195 +15266 2276 2495 2480 4326 +15267 4525 4152 4742 4749 +15268 4204 1244 4509 4667 +15269 4251 987 4254 4444 +15270 2038 2014 2042 4238 +15271 3172 4282 4493 4500 +15272 1159 1143 1147 4157 +15273 1175 1159 1147 4157 +15274 2268 1208 1206 4567 +15275 4615 4683 4704 4764 +15276 1159 1178 1143 4157 +15277 1178 1159 1175 4157 +15278 1178 1120 1143 4157 +15279 1178 1175 1120 4157 +15280 740 4088 4110 4161 +15281 1614 740 4110 4161 +15282 1213 1056 1052 4169 +15283 1557 1614 4110 4161 +15284 4088 1557 4110 4161 +15285 3312 3322 2855 4753 +15286 1213 750 751 4169 +15287 1056 1213 751 4169 +15288 1052 1056 751 4169 +15289 295 3901 3966 4241 +15290 1560 1320 1219 4164 +15291 339 1561 4133 4164 +15292 814 339 4056 4164 +15293 1320 814 4056 4164 +15294 814 1320 339 4164 +15295 1219 1319 1561 4164 +15296 1561 1319 4133 4164 +15297 3951 3895 4337 4398 +15298 4327 4207 4477 4609 +15299 4311 1117 4487 4511 +15300 1038 2125 2137 4167 +15301 1038 2137 4011 4167 +15302 2125 466 2137 4167 +15303 2137 466 4011 4167 +15304 466 2125 488 4167 +15305 488 2125 4011 4167 +15306 466 488 4011 4167 +15307 1371 1514 420 4216 +15308 4177 4389 4757 4771 +15309 989 4179 4207 4482 +15310 420 431 430 4216 +15311 1317 348 4052 4319 +15312 962 955 4397 4677 +15313 986 1004 4275 4444 +15314 1738 1113 1400 4497 +15315 1121 1416 1120 4190 +15316 1754 1753 1584 4437 +15317 2496 2497 1845 4192 +15318 1121 1266 1733 4190 +15319 1733 1266 4157 4190 +15320 2660 1498 2677 4529 +15321 3134 4148 4235 4472 +15322 1584 1751 4226 4437 +15323 1211 1650 752 4697 +15324 1685 1689 1245 4705 +15325 1851 2496 1845 4192 +15326 2497 1834 1845 4192 +15327 1416 1395 1120 4190 +15328 1147 1175 4157 4190 +15329 1120 1143 4157 4190 +15330 1266 1147 4157 4190 +15331 3940 1655 4012 4522 +15332 1574 1553 1197 4419 +15333 2014 229 2042 4238 +15334 3602 3459 3529 4600 +15335 1183 1565 1197 4680 +15336 2579 1185 4182 4419 +15337 3916 408 4039 4201 +15338 2282 51 4015 4296 +15339 4035 1241 4292 4357 +15340 3597 2803 4150 4175 +15341 2803 3597 4001 4175 +15342 2280 1196 4424 4721 +15343 4145 1666 4152 4525 +15344 4001 3597 4137 4175 +15345 4137 3597 4150 4175 +15346 3599 4137 4150 4175 +15347 1556 1228 4168 4188 +15348 3267 3831 3301 4176 +15349 3831 3267 3268 4176 +15350 3268 3267 3301 4176 +15351 1758 1759 4014 4177 +15352 1764 1580 4152 4177 +15353 1580 1764 1758 4177 +15354 1580 1758 1755 4177 +15355 1621 1221 1630 4390 +15356 1755 1758 4014 4177 +15357 1580 1584 4095 4177 +15358 1584 1580 1755 4177 +15359 1584 1755 4014 4177 +15360 1653 4012 4057 4245 +15361 1653 1659 4012 4245 +15362 1725 3500 3504 4178 +15363 3064 3500 3132 4178 +15364 4410 2789 4435 4545 +15365 2789 3092 4435 4545 +15366 1004 4085 4275 4395 +15367 1264 4407 4716 4750 +15368 2840 2696 2690 4308 +15369 3500 1725 1735 4178 +15370 1735 1725 4098 4178 +15371 3500 1146 3132 4178 +15372 3292 3300 4003 4378 +15373 3955 642 640 4434 +15374 1118 1115 1110 4629 +15375 1146 1735 4098 4178 +15376 1146 3500 1735 4178 +15377 3132 1146 1144 4178 +15378 1144 1146 4098 4178 +15379 3041 3132 1144 4178 +15380 2699 2941 4162 4183 +15381 4038 2699 4162 4183 +15382 2704 2699 4038 4183 +15383 2699 2704 2942 4183 +15384 2942 2704 2943 4183 +15385 3315 2942 2943 4183 +15386 2704 3315 2943 4183 +15387 1265 1264 1255 4185 +15388 3595 4001 4198 4408 +15389 1077 1919 1081 4628 +15390 2696 2840 2971 4308 +15391 3597 3603 4267 4325 +15392 1263 1255 4131 4185 +15393 1265 1263 4131 4185 +15394 1263 1265 1255 4185 +15395 3595 3598 4001 4408 +15396 1559 1558 1224 4231 +15397 2579 1197 1185 4419 +15398 1705 1711 4177 4389 +15399 992 1021 4329 4379 +15400 3312 3291 4176 4187 +15401 3301 3268 4176 4187 +15402 3856 3301 4176 4187 +15403 1738 1109 4091 4189 +15404 1109 1738 4040 4189 +15405 2908 641 643 4498 +15406 4075 3024 4418 4566 +15407 4244 3053 4649 4656 +15408 751 750 4233 4488 +15409 1395 1414 1120 4190 +15410 1120 1414 1143 4190 +15411 1418 1395 1766 4190 +15412 1395 1418 1414 4190 +15413 1414 1418 1143 4190 +15414 1767 1418 1766 4190 +15415 1143 1767 1766 4190 +15416 1775 1767 1143 4190 +15417 1418 1775 1143 4190 +15418 1775 1418 1767 4190 +15419 1119 4173 4259 4269 +15420 1666 4145 4152 4191 +15421 1720 1666 4152 4191 +15422 1666 1720 4145 4191 +15423 1590 1720 1763 4191 +15424 1763 1720 4152 4191 +15425 1590 1763 4152 4191 +15426 1849 1855 1847 4192 +15427 1855 1849 2500 4192 +15428 1849 1847 17 4192 +15429 1830 1849 17 4192 +15430 1849 1854 2500 4192 +15431 1854 1849 1830 4192 +15432 1830 1834 1831 4192 +15433 2497 1830 1831 4192 +15434 1834 2497 1831 4192 +15435 1853 1830 2500 4192 +15436 1854 1853 2500 4192 +15437 1853 1854 1830 4192 +15438 960 4043 4257 4289 +15439 955 1696 962 4289 +15440 982 983 4257 4289 +15441 1697 1719 4147 4289 +15442 1034 2652 4134 4346 +15443 1029 3173 980 4407 +15444 960 3222 983 955 +15445 172 1636 4089 4302 +15446 1696 1700 4147 4289 +15447 1700 1696 4257 4289 +15448 982 1696 955 4289 +15449 1719 1697 4257 4289 +15450 987 4254 4444 4599 +15451 1697 1700 4257 4289 +15452 1150 1578 1572 4209 +15453 795 712 1140 4479 +15454 1548 1574 1552 4209 +15455 1399 1398 4159 4197 +15456 3341 3322 3820 4388 +15457 1519 1513 2042 4238 +15458 3315 3321 2943 4502 +15459 4266 3286 4625 4691 +15460 3060 3051 3050 4630 +15461 1757 1710 1750 4437 +15462 2785 3504 2679 4646 +15463 3940 1655 4522 4592 +15464 1197 1607 4505 4551 +15465 1198 1546 1547 4199 +15466 1246 1247 4004 4536 +15467 821 611 824 4199 +15468 1198 821 824 4199 +15469 1546 1198 824 4199 +15470 611 1546 824 4199 +15471 1546 611 1545 4199 +15472 4427 4293 4506 4597 +15473 3605 3076 3427 4467 +15474 3076 3605 4234 4467 +15475 4286 3065 4436 4467 +15476 1317 348 4535 4657 +15477 3528 3629 3613 4200 +15478 1477 31 4141 4212 +15479 31 175 4141 4212 +15480 39 31 2097 4212 +15481 3060 4081 4300 4627 +15482 2917 2662 4281 4457 +15483 2276 2480 4299 4326 +15484 1771 1734 4515 4642 +15485 1304 1294 1303 4203 +15486 1303 1294 1157 4203 +15487 4207 4327 4460 4609 +15488 1304 1163 1157 4203 +15489 1163 1304 1170 4203 +15490 1173 1161 1157 4203 +15491 1161 1173 1163 4203 +15492 1163 1173 1157 4203 +15493 1180 1163 1170 4203 +15494 1161 1180 1170 4203 +15495 1180 1161 1163 4203 +15496 4013 1084 4179 4207 +15497 3049 4205 4443 4474 +15498 1093 977 4196 4207 +15499 977 1096 4196 4207 +15500 3143 3047 2753 4548 +15501 2268 1207 4065 4484 +15502 1240 1675 1236 4523 +15503 1007 1094 1096 4207 +15504 1007 1084 1094 4207 +15505 977 990 1261 4207 +15506 2729 2728 2753 4548 +15507 4269 4259 4744 4763 +15508 4292 1241 4295 4626 +15509 1261 990 4179 4207 +15510 1007 1096 977 4207 +15511 1007 977 1261 4207 +15512 1084 970 4179 4207 +15513 970 1084 1007 4207 +15514 970 1007 1261 4207 +15515 994 1261 4179 4207 +15516 970 994 4179 4207 +15517 994 970 1261 4207 +15518 2097 1477 2111 4212 +15519 2101 2097 2111 4212 +15520 48 173 2111 4212 +15521 175 48 2120 4212 +15522 2120 48 2111 4212 +15523 1492 1477 4141 4212 +15524 1477 1492 2111 4212 +15525 173 2101 2111 4212 +15526 2110 39 2101 4212 +15527 39 2110 173 4212 +15528 173 2110 2101 4212 +15529 2121 2120 2111 4212 +15530 175 2120 2121 4212 +15531 175 2121 4141 4212 +15532 2121 1492 4141 4212 +15533 1492 2121 2111 4212 +15534 2040 2039 2041 4213 +15535 223 2040 2041 4213 +15536 580 321 4002 4273 +15537 2805 3530 4001 4217 +15538 2805 4001 4198 4217 +15539 1561 786 4231 4537 +15540 1183 1609 1918 4551 +15541 3042 3057 3054 4237 +15542 4252 4029 4254 4265 +15543 3065 3075 4286 4436 +15544 1515 1516 430 4216 +15545 1515 1371 1516 4216 +15546 1912 1924 1919 4220 +15547 3598 3602 3623 4401 +15548 3604 3602 3529 4401 +15549 3269 706 4591 4665 +15550 1244 4204 4351 4667 +15551 1924 185 1919 4220 +15552 1077 1075 1078 4220 +15553 1075 1077 1919 4220 +15554 1919 1077 1078 4220 +15555 1930 1919 1078 4220 +15556 1076 1930 1078 4220 +15557 1930 1912 1919 4220 +15558 1930 1076 1912 4220 +15559 1244 1689 1245 4222 +15560 3942 3422 4435 4555 +15561 3410 3942 4435 4555 +15562 1684 1688 1671 4222 +15563 1671 1688 1245 4222 +15564 1684 1671 1245 4222 +15565 3483 3472 4427 4706 +15566 1666 1672 4080 4510 +15567 4494 3486 4564 4735 +15568 4525 1666 4571 4737 +15569 4259 1742 4269 4453 +15570 1604 2579 1619 4361 +15571 3112 3640 3105 4382 +15572 712 3285 4354 4360 +15573 1606 2579 1604 4361 +15574 3933 3285 712 4360 +15575 750 1211 752 4697 +15576 1211 750 4240 4697 +15577 3638 3536 3105 4382 +15578 2133 3559 4018 4225 +15579 2132 2133 4018 4225 +15580 1748 1746 4146 4629 +15581 3197 862 863 4248 +15582 3193 3197 863 4248 +15583 2852 3321 3342 4502 +15584 3024 3058 4418 4566 +15585 1112 4298 4531 4671 +15586 2943 2853 2828 4502 +15587 1114 1582 4171 4229 +15588 607 644 4713 4720 +15589 4173 1113 4223 4269 +15590 1113 1741 4223 4269 +15591 1743 1586 4313 4394 +15592 3389 4583 4615 4639 +15593 1586 1744 1587 4394 +15594 4282 4300 4493 4500 +15595 4193 4288 4301 4590 +15596 3341 3094 3820 4451 +15597 607 3762 702 4572 +15598 631 405 4068 4549 +15599 1743 1744 1586 4394 +15600 3472 3482 4293 4597 +15601 987 1265 1263 4275 +15602 1670 1694 4351 4396 +15603 1750 1710 4226 4437 +15604 607 321 580 4273 +15605 3453 2787 4325 4371 +15606 2579 1619 4361 4419 +15607 4230 3049 4418 4566 +15608 2789 3937 3092 4427 +15609 706 321 702 4273 +15610 607 580 702 4273 +15611 2643 3204 2918 4549 +15612 3038 3047 4075 4237 +15613 3319 592 4119 4455 +15614 321 3808 4002 4273 +15615 3808 3303 4002 4273 +15616 3270 702 3303 4273 +15617 3269 706 702 4273 +15618 2787 2667 4307 4371 +15619 1560 814 1561 4239 +15620 3895 299 3888 4538 +15621 814 1218 740 4239 +15622 814 1560 1218 4239 +15623 814 347 1561 4239 +15624 347 814 740 4239 +15625 2725 2717 2716 4538 +15626 786 347 740 4239 +15627 1208 1206 1210 4242 +15628 1208 2268 1206 4242 +15629 3127 727 3129 4243 +15630 3102 726 4123 4243 +15631 3127 3102 4123 4243 +15632 3161 3157 3129 4243 +15633 727 3161 3129 4243 +15634 726 3157 342 4243 +15635 727 726 342 4243 +15636 727 3127 703 4243 +15637 727 703 726 4243 +15638 703 3127 4123 4243 +15639 726 703 4123 4243 +15640 3157 3161 342 4243 +15641 3161 727 342 4243 +15642 1585 1743 1586 4313 +15643 3004 3068 4033 4387 +15644 3328 2984 4404 4651 +15645 3504 3546 1699 4244 +15646 4085 1265 4275 4395 +15647 2653 2658 1033 4560 +15648 3206 3221 4350 4648 +15649 795 1645 815 4271 +15650 3546 1027 1699 4244 +15651 795 815 4026 4271 +15652 1268 4032 4335 4650 +15653 1725 3504 1699 4244 +15654 4032 1270 4335 4650 +15655 1192 1191 4026 4271 +15656 1182 1141 4288 4590 +15657 3449 3454 3459 4267 +15658 3452 3597 4150 4267 +15659 3597 3603 4150 4267 +15660 2501 2283 4299 4326 +15661 4399 1407 4442 4707 +15662 1659 1653 4012 4247 +15663 4012 1653 4057 4247 +15664 2671 3193 863 4248 +15665 3193 2671 3192 4248 +15666 3192 2671 863 4248 +15667 3051 4282 4630 4631 +15668 3060 3051 4630 4631 +15669 4062 3050 4127 4249 +15670 4103 4062 4127 4249 +15671 4186 3049 4450 4566 +15672 4186 4033 4387 4566 +15673 4033 4037 4387 4566 +15674 4033 2997 4037 4566 +15675 345 1270 4032 4650 +15676 2997 4033 4186 4566 +15677 2997 4186 4450 4566 +15678 318 3276 313 4587 +15679 3050 3082 3052 4249 +15680 1264 1265 1029 4251 +15681 3471 3938 3090 4262 +15682 2576 2592 2567 4412 +15683 3907 4117 4201 4411 +15684 1741 1587 4116 4744 +15685 1200 2284 2286 4484 +15686 2810 3083 3225 4276 +15687 1029 1265 1255 4251 +15688 2579 1073 1185 4253 +15689 3312 576 4176 4426 +15690 2500 1855 4192 4521 +15691 4228 3940 4247 4266 +15692 1692 1716 1718 4323 +15693 2567 2592 2569 4363 +15694 1265 1255 4251 4252 +15695 987 1265 4251 4252 +15696 1265 987 1255 4252 +15697 2787 3453 4325 4425 +15698 3805 3808 321 4426 +15699 576 575 3108 4426 +15700 3455 3451 4325 4425 +15701 3603 2787 4325 4425 +15702 1664 1668 4277 4667 +15703 1734 4049 4332 4364 +15704 9 16 4010 4353 +15705 3258 3083 3222 4257 +15706 1304 1162 4203 4608 +15707 1595 1047 737 4290 +15708 1200 578 4672 4673 +15709 3069 1697 3580 4257 +15710 1696 3083 3583 4257 +15711 998 1125 4214 4342 +15712 1047 747 737 4290 +15713 3222 982 983 4257 +15714 1000 1086 1083 4342 +15715 747 775 737 4290 +15716 1158 1171 1147 4601 +15717 3459 3599 3529 4600 +15718 1125 998 4214 4465 +15719 1400 1113 4223 4497 +15720 3069 1719 1697 4257 +15721 1089 998 4115 4342 +15722 3686 3633 3101 4633 +15723 3580 1697 3583 4257 +15724 1719 3069 4042 4257 +15725 3226 960 4042 4257 +15726 1700 1696 3583 4257 +15727 775 747 1047 4290 +15728 1697 1700 3583 4257 +15729 4042 960 4043 4257 +15730 1719 4042 4043 4257 +15731 1237 1317 4052 4319 +15732 408 3548 4054 4411 +15733 3285 3319 4420 4491 +15734 594 2576 597 4663 +15735 2853 313 2828 4502 +15736 3379 3075 3065 4436 +15737 1114 1117 4459 4468 +15738 3317 2994 4687 4730 +15739 1191 1192 1139 4263 +15740 4054 4039 4201 4411 +15741 2789 4427 4676 4690 +15742 4117 3578 4201 4411 +15743 1682 1316 1686 4319 +15744 3514 3400 4722 4723 +15745 3958 2911 4498 4759 +15746 3581 3562 3503 4264 +15747 3578 408 4201 4411 +15748 408 4054 4201 4411 +15749 3105 4310 4382 4552 +15750 1197 1565 4485 4505 +15751 3437 3562 4036 4264 +15752 1317 1316 4209 4319 +15753 3387 3386 3402 4723 +15754 961 979 977 4265 +15755 3400 3403 3402 4723 +15756 3546 3386 3504 4723 +15757 3640 3105 4382 4552 +15758 1113 1119 1117 4269 +15759 2660 2677 2917 4549 +15760 405 1507 2677 4549 +15761 1185 1197 4182 4419 +15762 3040 4230 4237 4418 +15763 1742 1119 4259 4269 +15764 1119 1742 1117 4269 +15765 1117 1742 1588 4269 +15766 1116 1117 1588 4269 +15767 2264 1199 4334 4567 +15768 4185 4114 4372 4421 +15769 4185 4372 4407 4421 +15770 3023 2996 4006 4475 +15771 2735 3200 3006 4348 +15772 3483 3472 3101 4427 +15773 3049 4230 4450 4566 +15774 1710 1584 4226 4437 +15775 1756 1775 1143 4287 +15776 1418 1775 1756 4287 +15777 1174 3086 4041 4445 +15778 986 4085 4265 4275 +15779 3225 3083 3258 4276 +15780 3257 3225 3258 4276 +15781 3679 3638 3659 4331 +15782 1502 229 4238 4365 +15783 3638 3647 3659 4331 +15784 3638 3105 4090 4331 +15785 3940 1655 4592 4613 +15786 1161 4193 4288 4301 +15787 3603 3459 2786 4600 +15788 1197 1183 1572 4570 +15789 1180 1161 4288 4301 +15790 4184 4138 4281 4560 +15791 3599 3602 3529 4600 +15792 1750 1751 4226 4422 +15793 1112 1111 1403 4298 +15794 1751 1584 4226 4422 +15795 1391 1112 1403 4298 +15796 2838 3312 3741 4321 +15797 2787 2781 3603 4307 +15798 1741 4269 4417 4763 +15799 3312 3296 3741 4321 +15800 197 2662 1031 4281 +15801 1740 1128 1400 4770 +15802 2662 2660 2917 4281 +15803 2660 197 434 4281 +15804 2917 2660 4068 4281 +15805 4068 434 4079 4281 +15806 3071 3011 2998 4286 +15807 3011 3015 3065 4286 +15808 3000 2998 4274 4286 +15809 3071 3000 4274 4286 +15810 3000 3071 2998 4286 +15811 3015 3011 3071 4286 +15812 1751 1750 4226 4437 +15813 3015 3071 2995 4286 +15814 3065 3015 3066 4286 +15815 3065 3066 4234 4286 +15816 2995 3071 4234 4286 +15817 4234 3071 4274 4286 +15818 3101 4293 4427 4597 +15819 3001 2995 4234 4286 +15820 3001 3015 2995 4286 +15821 1173 1172 1141 4288 +15822 1173 1168 1172 4288 +15823 1157 1173 1141 4288 +15824 1168 1173 1161 4288 +15825 1171 1157 1141 4288 +15826 1161 1173 1157 4288 +15827 1169 1161 4193 4288 +15828 1168 1169 4193 4288 +15829 1169 1168 1161 4288 +15830 1404 1407 4442 4602 +15831 1686 1685 345 4695 +15832 1180 1161 1157 4288 +15833 4254 4265 4444 4599 +15834 1170 1180 1157 4288 +15835 4257 960 3222 3226 +15836 955 3222 983 4289 +15837 983 3222 4257 4289 +15838 345 1685 1245 4705 +15839 960 4257 3222 4289 +15840 1130 1089 4115 4342 +15841 3049 3054 4205 4474 +15842 1749 1723 1584 4422 +15843 3596 3080 3073 4392 +15844 3714 1717 1703 4291 +15845 3282 3110 3280 4618 +15846 796 713 4032 4291 +15847 3278 3781 3782 4618 +15848 3114 3089 4202 4291 +15849 3322 3296 4221 4451 +15850 3110 3278 3279 4618 +15851 713 796 1277 4291 +15852 3120 713 1277 4291 +15853 3295 3279 3782 4618 +15854 710 3089 3112 4291 +15855 4235 3138 4283 4306 +15856 3348 3762 3761 4699 +15857 303 2813 4619 4643 +15858 2282 2480 4076 4296 +15859 702 3100 4572 4581 +15860 3175 3554 1259 4405 +15861 4082 793 4294 4404 +15862 3372 3613 4200 4583 +15863 4017 1266 4098 4297 +15864 1266 1146 4098 4297 +15865 2727 2754 4051 4548 +15866 793 3328 3894 4404 +15867 955 4397 4677 4712 +15868 4388 3322 4682 4753 +15869 3296 3312 4221 4321 +15870 1400 1398 4040 4766 +15871 3312 3322 4221 4321 +15872 706 705 3108 4378 +15873 4138 2661 4281 4457 +15874 1032 2653 4184 4560 +15875 1170 1180 4288 4301 +15876 1617 2577 4093 4569 +15877 1577 1183 4544 4570 +15878 1702 4032 4291 4617 +15879 1405 1121 1120 4602 +15880 2577 1620 4093 4569 +15881 1141 1171 4288 4301 +15882 3555 3563 3091 4338 +15883 1162 4135 4519 4608 +15884 1391 1118 1110 4531 +15885 3556 3101 3091 4338 +15886 1018 1022 4340 4345 +15887 4130 4013 4340 4345 +15888 1022 1021 4130 4345 +15889 1022 1018 4013 4345 +15890 1264 1255 4185 4407 +15891 1002 1022 4013 4345 +15892 3134 4127 4149 4306 +15893 3603 2781 4092 4307 +15894 1726 1771 4515 4642 +15895 2495 2276 4007 4326 +15896 4076 2501 4299 4326 +15897 2479 2501 4076 4326 +15898 2480 2495 4007 4326 +15899 3112 710 4552 4575 +15900 2479 2480 4007 4326 +15901 2690 2696 590 4308 +15902 2696 2971 590 4308 +15903 577 318 313 4587 +15904 318 314 321 4587 +15905 3887 2971 2724 4308 +15906 2971 3887 295 4308 +15907 295 3887 2724 4308 +15908 3134 4127 4235 4309 +15909 1067 1557 4563 4620 +15910 3714 3681 4128 4310 +15911 1624 1628 1627 4620 +15912 2919 631 2918 4576 +15913 3699 3714 4128 4310 +15914 3681 3640 4125 4310 +15915 4125 3640 4128 4310 +15916 3681 4125 4128 4310 +15917 2818 2815 4077 4550 +15918 3957 3991 2816 4550 +15919 4217 4236 4471 4524 +15920 3152 3153 3005 4495 +15921 3930 3422 3406 4545 +15922 3092 2789 4427 4545 +15923 3092 4427 4435 4545 +15924 3101 3930 3406 4545 +15925 2920 2643 3881 4317 +15926 727 704 703 4577 +15927 3103 727 703 4577 +15928 3128 727 3103 4577 +15929 993 988 4460 4327 +15930 3099 3096 4072 4577 +15931 704 3099 4072 4577 +15932 704 3103 703 4577 +15933 3042 3047 4237 4318 +15934 2753 3048 3250 4318 +15935 3250 3048 4127 4318 +15936 2753 3250 4127 4318 +15937 1507 3204 3205 4549 +15938 2681 1507 3205 4549 +15939 4127 3048 4136 4318 +15940 4127 4136 4306 4318 +15941 3042 4235 4283 4318 +15942 4283 4235 4306 4318 +15943 1316 1246 4285 4319 +15944 705 710 4003 4378 +15945 4126 1842 4280 4353 +15946 3108 3107 4218 4378 +15947 1842 4010 4280 4353 +15948 4282 4300 4527 4627 +15949 1034 2013 4134 4439 +15950 1279 1281 1761 4323 +15951 1721 1279 1761 4323 +15952 3007 3005 4367 4495 +15953 4289 963 4397 4725 +15954 3540 3548 4411 4490 +15955 3882 4374 4498 4759 +15956 3312 2838 3335 4431 +15957 4282 4611 4630 4631 +15958 3288 1241 4022 4357 +15959 822 825 4083 4664 +15960 1270 1689 345 4662 +15961 1221 172 4089 4598 +15962 1608 1221 1630 4598 +15963 2501 2502 4007 4326 +15964 2502 2501 2479 4326 +15965 3080 3622 3594 4392 +15966 740 746 743 4341 +15967 4006 3006 4348 4475 +15968 3380 3620 3369 4380 +15969 3006 3200 4006 4348 +15970 2289 2284 4143 4484 +15971 3450 2781 4385 4674 +15972 438 4102 4134 4346 +15973 332 2886 2900 4328 +15974 314 332 2900 4328 +15975 2783 3454 4373 4481 +15976 3319 4464 4486 4652 +15977 3454 3077 3456 4481 +15978 3077 3454 2783 4481 +15979 3315 313 2942 4328 +15980 2900 2886 2885 4328 +15981 3319 3887 3908 4652 +15982 2942 2885 4183 4328 +15983 4106 3315 4183 4328 +15984 3315 2942 4183 4328 +15985 2886 4106 4183 4328 +15986 2885 2886 4183 4328 +15987 3560 3537 3561 4339 +15988 712 795 4024 4479 +15989 2852 3342 4111 4449 +15990 3819 3312 3806 4449 +15991 3030 3019 4061 4333 +15992 3015 3030 4061 4333 +15993 3086 3015 4061 4333 +15994 3015 3086 3001 4333 +15995 3071 3015 3001 4333 +15996 586 592 4354 4447 +15997 3030 3015 3149 4333 +15998 3149 3015 3019 4333 +15999 3030 3149 3019 4333 +16000 3018 3001 3019 4333 +16001 3015 3018 3019 4333 +16002 3001 3018 2995 4333 +16003 3071 3001 2995 4333 +16004 3018 3071 2995 4333 +16005 3018 3015 3011 4333 +16006 3011 3015 3071 4333 +16007 3018 3011 3071 4333 +16008 2268 2267 2288 4334 +16009 2787 4307 4325 4371 +16010 1117 4459 4700 4708 +16011 3662 3633 4180 4633 +16012 1073 1064 4253 4347 +16013 4180 3634 4246 4633 +16014 3685 3634 4180 4633 +16015 2735 3006 4118 4348 +16016 1022 4130 4340 4345 +16017 1002 4013 4329 4345 +16018 1229 1073 4253 4347 +16019 4102 1034 4134 4346 +16020 3991 3982 3981 4516 +16021 4155 1723 4389 4461 +16022 1023 1020 1018 4340 +16023 1018 1019 4207 4340 +16024 1018 1020 1019 4340 +16025 3388 3501 3614 4639 +16026 741 740 743 4341 +16027 748 741 743 4341 +16028 740 1614 1557 4341 +16029 1557 1614 1067 4341 +16030 4683 4738 4743 4764 +16031 1741 1740 4223 4768 +16032 2733 2801 4008 4367 +16033 2801 2734 4008 4367 +16034 2350 1627 1067 4341 +16035 1227 748 1067 4341 +16036 1227 741 748 4341 +16037 741 2351 740 4341 +16038 1614 1228 1067 4341 +16039 1228 1614 740 4341 +16040 3081 3064 4743 4764 +16041 1738 4040 4189 4497 +16042 4091 1738 4189 4497 +16043 3622 3588 3594 4392 +16044 2734 2801 3005 4367 +16045 775 738 4290 4341 +16046 1595 775 4290 4341 +16047 1624 1627 738 4341 +16048 1730 1407 1729 4707 +16049 741 1227 2351 4341 +16050 2351 1227 1067 4341 +16051 1228 2351 1067 4341 +16052 2351 1228 740 4341 +16053 775 1595 1596 4341 +16054 775 1596 738 4341 +16055 1596 1624 738 4341 +16056 3080 3588 3073 4392 +16057 2735 2745 2749 4348 +16058 3200 2735 2749 4348 +16059 2745 3200 2749 4348 +16060 2737 2735 4118 4348 +16061 2735 2737 2736 4348 +16062 1127 1159 1414 4349 +16063 1129 1127 1414 4349 +16064 1133 1414 1411 4349 +16065 1133 1129 1414 4349 +16066 1393 4146 4232 4469 +16067 3056 3172 3134 4493 +16068 1503 2652 4153 4529 +16069 3213 324 3221 4350 +16070 4081 3060 4300 4493 +16071 3319 3317 4420 4687 +16072 1031 1036 1035 4702 +16073 3066 3080 3594 4668 +16074 1036 84 4132 4702 +16075 1694 1691 1713 4351 +16076 1670 1694 1713 4351 +16077 2661 4138 4281 4702 +16078 3106 3268 3109 4378 +16079 706 710 705 4378 +16080 16 1851 4010 4353 +16081 4010 1851 4280 4353 +16082 3939 3319 3285 4354 +16083 3285 3319 586 4354 +16084 2994 3329 3317 4420 +16085 3332 3329 2994 4420 +16086 4644 4604 4706 4748 +16087 3329 3332 3317 4420 +16088 1851 4010 4280 4521 +16089 1851 2496 4192 4521 +16090 4520 1187 4578 4603 +16091 3198 3228 2802 4548 +16092 3870 3312 4176 4426 +16093 1847 1851 4192 4521 +16094 1851 1847 4010 4521 +16095 2782 3595 3593 4401 +16096 3602 3604 3623 4401 +16097 3623 3604 2786 4401 +16098 3529 3623 2786 4401 +16099 3932 1659 4292 4357 +16100 1656 4024 4035 4357 +16101 1656 4022 4024 4357 +16102 1660 1656 4035 4357 +16103 1660 3286 1656 4357 +16104 1659 3286 1660 4357 +16105 1659 1660 4035 4357 +16106 1203 1055 1057 4359 +16107 1054 201 1035 4359 +16108 751 1631 4233 4359 +16109 753 751 4233 4359 +16110 751 753 4224 4359 +16111 1056 1631 751 4359 +16112 1056 1054 1631 4359 +16113 1054 1056 1057 4359 +16114 1203 1057 751 4359 +16115 1203 751 4224 4359 +16116 1057 1056 751 4359 +16117 1741 4173 4763 4772 +16118 2692 2690 4308 4703 +16119 3805 576 3870 4426 +16120 577 3805 321 4426 +16121 3323 2690 3313 4413 +16122 3323 3313 4337 4413 +16123 633 3273 4622 4666 +16124 1770 1732 1765 4364 +16125 229 1502 1519 4365 +16126 2840 2692 2718 4703 +16127 738 4290 4341 4366 +16128 1736 1114 4459 4468 +16129 3065 3066 4436 4467 +16130 3066 3379 4436 4467 +16131 2653 1033 4184 4560 +16132 3049 4230 4418 4548 +16133 3588 3623 3596 4392 +16134 3183 3151 4008 4367 +16135 2734 3183 4008 4367 +16136 3151 3183 3181 4367 +16137 3183 2734 3181 4367 +16138 2734 3005 3181 4367 +16139 3887 3908 2988 4368 +16140 3514 4194 4264 4369 +16141 3500 3387 3491 4723 +16142 3514 3400 3403 4369 +16143 3502 3514 3403 4369 +16144 3400 3502 3403 4369 +16145 3545 3581 3572 4369 +16146 4156 3417 4194 4369 +16147 3417 3581 4194 4369 +16148 4047 1144 4320 4372 +16149 1144 4047 4114 4372 +16150 295 299 574 4507 +16151 1675 345 1665 4662 +16152 4327 997 4477 4482 +16153 3540 3548 408 4411 +16154 3023 3151 3181 4495 +16155 989 991 4179 4482 +16156 3595 3529 4236 4524 +16157 4207 4327 4477 4482 +16158 997 989 4477 4482 +16159 3388 3614 3365 4639 +16160 2264 1200 2286 4484 +16161 3006 2800 4348 4391 +16162 3686 3101 4151 4633 +16163 3578 3540 408 4411 +16164 3023 2996 3151 4495 +16165 1732 1770 1408 4745 +16166 984 1003 4096 4542 +16167 3089 3715 4291 4641 +16168 4204 1245 4222 4554 +16169 3917 3907 4039 4411 +16170 3548 3917 4054 4411 +16171 4129 1191 4263 4504 +16172 4515 4621 4642 4670 +16173 1734 4515 4642 4670 +16174 3064 4071 4178 4377 +16175 3132 3064 4178 4377 +16176 4005 4044 4081 4528 +16177 4044 3134 4081 4528 +16178 2856 3342 3340 4754 +16179 3056 3138 4493 4528 +16180 4081 3060 4493 4528 +16181 3060 3134 4493 4528 +16182 3319 3908 4464 4652 +16183 3328 2984 2993 4404 +16184 3328 2993 3894 4404 +16185 3008 3049 2802 4548 +16186 1693 1688 4258 4446 +16187 3428 3380 3375 4380 +16188 3621 3620 3596 4380 +16189 3380 3621 3596 4380 +16190 3620 3380 3596 4380 +16191 3622 3380 3428 4380 +16192 3380 3622 3621 4380 +16193 3621 3622 3428 4380 +16194 1851 2497 2496 4521 +16195 4010 2500 4280 4521 +16196 1847 2500 4010 4521 +16197 3536 3638 3679 4382 +16198 3679 3638 3105 4382 +16199 955 960 4289 4725 +16200 631 3204 633 4514 +16201 3052 2812 4249 4565 +16202 229 1513 4087 4634 +16203 1565 1183 1197 4485 +16204 4293 4356 4427 4506 +16205 1711 1116 4177 4525 +16206 3153 3186 3005 4495 +16207 3154 2996 3023 4495 +16208 3007 3152 3005 4495 +16209 2286 2264 4484 4567 +16210 3049 4387 4418 4566 +16211 4033 3068 4186 4387 +16212 2781 4092 4307 4385 +16213 3685 3686 4151 4633 +16214 3486 3485 2783 4385 +16215 3092 3486 2783 4385 +16216 3548 3540 4094 4490 +16217 3173 1030 4611 4694 +16218 4267 3597 4325 4574 +16219 2735 3232 4118 4387 +16220 4020 1071 4182 4419 +16221 295 3908 3966 4455 +16222 3200 2735 4006 4387 +16223 3232 3200 4006 4387 +16224 3200 3232 2735 4387 +16225 1158 1157 4203 4608 +16226 1584 4095 4177 4389 +16227 1584 1754 1711 4389 +16228 1754 1584 4014 4389 +16229 4014 1584 4177 4389 +16230 1711 1754 4014 4389 +16231 1711 4014 4177 4389 +16232 3607 3519 4470 4476 +16233 4174 3628 4466 4476 +16234 2745 3200 4348 4391 +16235 3519 4466 4470 4476 +16236 4179 989 4207 4477 +16237 3519 4174 4466 4476 +16238 3628 3609 4466 4476 +16239 3609 3628 4200 4476 +16240 4130 1022 4340 4460 +16241 1177 1736 1114 4393 +16242 4130 4013 4327 4460 +16243 4013 1018 4327 4460 +16244 3930 3938 4545 4610 +16245 1552 1550 1564 4454 +16246 988 1023 1004 4395 +16247 1723 1749 4155 4422 +16248 4138 4184 4281 4702 +16249 1268 4032 4650 4655 +16250 1696 962 4289 4397 +16251 1101 957 969 4397 +16252 1696 1101 969 4397 +16253 957 962 969 4397 +16254 962 1696 969 4397 +16255 1170 1158 4203 4519 +16256 1919 4400 4520 4603 +16257 1160 1170 4203 4519 +16258 4300 4278 4463 4500 +16259 1244 1689 345 4705 +16260 2718 2692 2725 4703 +16261 2887 4038 4162 4512 +16262 4032 345 4650 4695 +16263 1264 1029 1255 4407 +16264 1584 1723 4389 4422 +16265 4047 4185 4372 4407 +16266 1919 4520 4578 4603 +16267 1392 1179 4686 4717 +16268 2580 2577 1070 4746 +16269 3057 3063 3061 4559 +16270 3063 3062 4148 4559 +16271 2497 2499 4192 4521 +16272 3056 3063 4148 4559 +16273 3063 3057 4472 4559 +16274 3342 3322 3340 4753 +16275 2790 3594 4710 4726 +16276 4374 641 4498 4636 +16277 1146 1144 4098 4403 +16278 1146 4098 4297 4403 +16279 4428 4624 4663 4746 +16280 4297 4098 4320 4403 +16281 1699 1725 4270 4670 +16282 3134 3056 4493 4528 +16283 4005 4081 4300 4528 +16284 4281 4138 4457 4560 +16285 4300 4081 4493 4528 +16286 4085 1265 4714 4728 +16287 3130 1271 4032 4405 +16288 3130 4032 4291 4405 +16289 1259 1703 1702 4405 +16290 1702 1703 4291 4405 +16291 1271 1702 4032 4405 +16292 4032 1702 4291 4405 +16293 1226 1189 4129 4504 +16294 3379 3065 3066 4436 +16295 3578 3906 4094 4490 +16296 3904 4117 4411 4490 +16297 1408 1730 1724 4745 +16298 993 997 4327 4482 +16299 4122 4013 4179 4482 +16300 2852 3342 4449 4502 +16301 3541 3578 4094 4490 +16302 1135 1126 1085 4612 +16303 1122 1028 4029 4432 +16304 1084 1132 4013 4429 +16305 970 1084 4122 4429 +16306 3940 4322 4438 4613 +16307 3312 3322 4321 4431 +16308 2592 2576 1061 4412 +16309 1061 2576 2588 4412 +16310 2592 1061 2588 4412 +16311 1708 1736 1699 4757 +16312 3322 4221 4321 4431 +16313 1582 4749 4751 4771 +16314 1700 1114 4757 4771 +16315 2690 2840 4308 4703 +16316 3057 3062 3063 4472 +16317 1553 1552 1564 4454 +16318 1588 1116 4269 4417 +16319 1742 1588 4269 4417 +16320 4020 1071 4419 4454 +16321 4001 3530 4175 4524 +16322 3530 4217 4471 4524 +16323 984 4096 4465 4542 +16324 1574 1553 4419 4454 +16325 2782 4383 4415 4524 +16326 4001 3595 4217 4524 +16327 1125 1331 998 4542 +16328 1003 992 4096 4542 +16329 1553 4020 4419 4454 +16330 4282 4278 4300 4500 +16331 3001 3002 4333 4445 +16332 4061 3086 4333 4445 +16333 4276 3222 4483 4632 +16334 1116 4525 4741 4742 +16335 1745 1746 4197 4501 +16336 1576 1565 4485 4680 +16337 1198 1149 1197 4680 +16338 2753 2728 4051 4548 +16339 2988 3887 4486 4652 +16340 3923 2783 4373 4481 +16341 1196 1073 1616 4424 +16342 1616 1073 1070 4424 +16343 1196 1616 1070 4424 +16344 1066 1073 4253 4424 +16345 1073 1066 1070 4424 +16346 2280 1196 1070 4424 +16347 1066 1072 1070 4424 +16348 4046 984 4122 4429 +16349 1072 1066 4053 4424 +16350 4053 1066 4253 4424 +16351 2580 1072 2588 4424 +16352 2588 1072 4053 4424 +16353 2588 4053 4253 4424 +16354 3101 3408 3091 4427 +16355 3408 3101 3406 4427 +16356 3563 3092 3091 4427 +16357 3092 3563 3562 4427 +16358 3058 4230 4418 4566 +16359 4317 633 4622 4659 +16360 1392 1142 1773 4568 +16361 1774 1392 1773 4568 +16362 3408 3563 3091 4427 +16363 3563 3408 3562 4427 +16364 4025 3455 4267 4574 +16365 2900 314 4328 4587 +16366 2264 4065 4484 4567 +16367 1028 1029 4029 4432 +16368 4029 1029 4078 4432 +16369 1122 1030 1028 4432 +16370 1028 1030 1029 4432 +16371 1109 1117 1738 4541 +16372 2728 2729 2727 4548 +16373 2728 2727 4051 4548 +16374 1588 1117 4311 4541 +16375 1742 1119 1117 4541 +16376 1109 1119 4518 4541 +16377 3884 640 4069 4434 +16378 3581 3562 4264 4435 +16379 3562 3410 4036 4435 +16380 4260 3923 4264 4435 +16381 3562 4036 4264 4435 +16382 3923 3581 4264 4435 +16383 3581 3923 3092 4435 +16384 3410 3942 4036 4435 +16385 3820 3322 3863 4682 +16386 1565 1198 1197 4680 +16387 3503 4260 4264 4435 +16388 4036 3503 4264 4435 +16389 3942 3411 4036 4435 +16390 3921 3942 3422 4435 +16391 3921 3411 3942 4435 +16392 3411 3503 4036 4435 +16393 3922 3921 3422 4435 +16394 3411 3921 3503 4435 +16395 3503 3921 4260 4435 +16396 1054 201 85 4439 +16397 1054 85 2031 4439 +16398 201 2016 85 4439 +16399 85 2016 2031 4439 +16400 1591 1598 737 4441 +16401 737 1598 4110 4441 +16402 739 737 4110 4441 +16403 3341 3322 4221 4451 +16404 3296 3341 4221 4451 +16405 2996 3004 3003 4475 +16406 4348 3006 4391 4475 +16407 1181 1774 4301 1169 +16408 3296 3322 3312 4451 +16409 4085 4265 4275 4444 +16410 1729 1407 4647 4715 +16411 987 1265 4275 4444 +16412 1265 987 4251 4444 +16413 1159 1178 1147 4601 +16414 1117 4229 4487 4688 +16415 3002 1160 4041 4445 +16416 1160 3002 1174 4445 +16417 710 713 4513 4641 +16418 4179 991 4379 4503 +16419 1021 4130 4345 4503 +16420 1736 1114 4393 4459 +16421 2849 3206 4594 4648 +16422 3213 3220 4595 4648 +16423 3342 2855 4111 4449 +16424 3342 3312 2855 4449 +16425 4329 1021 4345 4503 +16426 3321 3312 3342 4449 +16427 3094 3095 705 4451 +16428 705 3095 576 4451 +16429 3094 705 576 4451 +16430 3282 3280 3281 4618 +16431 311 3951 4303 4452 +16432 4303 3951 4398 4452 +16433 311 299 3895 4452 +16434 3313 4398 4413 4452 +16435 3933 4438 4553 4684 +16436 311 3895 3951 4452 +16437 3951 3895 4398 4452 +16438 4398 3895 4413 4452 +16439 2579 1603 1184 4678 +16440 4247 4266 4625 4691 +16441 4262 4427 4637 4690 +16442 3485 3450 4385 4508 +16443 3000 3071 4234 4755 +16444 1739 1740 4766 4770 +16445 1692 1715 4323 4562 +16446 1715 1669 4323 4562 +16447 1609 1918 4551 4561 +16448 1918 1607 4551 4561 +16449 3830 3807 3804 4458 +16450 3804 3807 3806 4458 +16451 2702 3806 4073 4458 +16452 4073 3806 4449 4458 +16453 2702 4073 4449 4458 +16454 3807 3819 3806 4458 +16455 3806 3819 4449 4458 +16456 3609 3608 2782 4470 +16457 4324 4564 4604 4689 +16458 1746 1134 4146 4469 +16459 4269 4417 4453 4709 +16460 4269 1742 4417 4709 +16461 4417 1588 4499 4709 +16462 1117 1177 1114 4459 +16463 1114 1177 4393 4459 +16464 4207 1018 4340 4460 +16465 1018 4207 4327 4460 +16466 1698 1705 1708 4461 +16467 3991 3999 3992 4516 +16468 3981 3982 3992 4516 +16469 3957 303 301 4516 +16470 3981 301 3986 4516 +16471 1708 1699 4461 4757 +16472 2818 3991 3981 4516 +16473 1627 1067 4341 4620 +16474 4636 4374 4685 4759 +16475 1705 1695 1708 4461 +16476 4029 4254 4265 4762 +16477 3348 3270 3762 4572 +16478 607 3348 3762 4572 +16479 1226 1189 4504 4533 +16480 1629 2135 1226 4533 +16481 1100 1125 4214 4465 +16482 1100 974 1125 4465 +16483 974 1100 4214 4465 +16484 4172 974 4214 4465 +16485 3518 3519 4211 4466 +16486 3519 4174 4211 4466 +16487 3062 3057 4148 4472 +16488 4415 3519 4470 4471 +16489 2747 3083 3053 4584 +16490 3047 4237 4318 4548 +16491 3083 982 3053 4584 +16492 2747 3245 3259 4584 +16493 4215 4517 4736 4744 +16494 3054 2729 4127 4548 +16495 4230 3049 4474 4548 +16496 4525 4737 4741 4742 +16497 1169 1774 4301 4773 +16498 4152 4145 4525 4742 +16499 3609 2782 2784 4470 +16500 2784 2782 4466 4470 +16501 3609 2784 4466 4470 +16502 3057 3042 4148 4472 +16503 2662 4281 4457 4560 +16504 4235 4148 4318 4472 +16505 3042 4235 4318 4472 +16506 4148 3042 4318 4472 +16507 3348 3761 3860 4699 +16508 4230 3049 4443 4474 +16509 4237 4230 4272 4474 +16510 3057 3054 4237 4474 +16511 3057 3062 3054 4474 +16512 3062 3057 3063 4474 +16513 2677 2643 2918 4549 +16514 3204 1507 4514 4549 +16515 4272 3057 4370 4474 +16516 4237 4272 4370 4474 +16517 3057 4237 4370 4474 +16518 3183 3023 4006 4475 +16519 3179 3183 4006 4475 +16520 3183 3179 2750 4475 +16521 3183 2750 4008 4475 +16522 1030 1029 4407 4694 +16523 4376 3173 4611 4694 +16524 3179 2745 4391 4475 +16525 2750 3179 4008 4475 +16526 4008 3179 4391 4475 +16527 3222 3258 4276 4483 +16528 1019 986 4265 4477 +16529 986 990 4265 4477 +16530 3258 3257 4330 4478 +16531 1774 1181 4301 4568 +16532 1207 4484 4672 4673 +16533 706 3108 4534 4591 +16534 3257 2762 4330 4478 +16535 4195 4025 4267 4586 +16536 4427 3937 4637 4690 +16537 3699 3131 4513 4641 +16538 2497 1851 4280 4521 +16539 4195 4267 4325 4586 +16540 4267 4025 4574 4586 +16541 713 710 4291 4641 +16542 3863 3322 3312 4682 +16543 3472 3483 4644 4706 +16544 1085 1126 4429 4612 +16545 311 299 4452 4507 +16546 4237 3054 4318 4548 +16547 2729 2753 4127 4548 +16548 2754 3228 3198 4548 +16549 4075 3038 4237 4548 +16550 4237 4230 4474 4548 +16551 3155 3154 3023 4495 +16552 4042 3226 4257 4653 +16553 3887 3319 4119 4486 +16554 4119 3319 4464 4486 +16555 3321 3315 313 4502 +16556 2887 2704 4038 4512 +16557 4073 2702 4449 4502 +16558 4517 4417 4734 4736 +16559 4385 4508 4564 4689 +16560 1110 1176 1109 4487 +16561 1729 1730 4740 4745 +16562 2887 2884 2704 4512 +16563 313 3805 4073 4502 +16564 2702 2852 4449 4502 +16565 4385 3485 4508 4689 +16566 4508 4279 4564 4689 +16567 405 2684 434 4529 +16568 4038 2886 4162 4512 +16569 3577 3906 3904 4490 +16570 3904 3906 4117 4490 +16571 3906 3578 4117 4490 +16572 4117 3578 4411 4490 +16573 3578 3540 4411 4490 +16574 3319 3285 586 4491 +16575 793 3894 586 4491 +16576 3886 793 586 4491 +16577 793 3886 3894 4491 +16578 3213 324 4350 4492 +16579 3080 3045 3078 4596 +16580 1503 2684 1498 4529 +16581 3134 3172 3060 4493 +16582 434 1503 4153 4529 +16583 2684 1503 434 4529 +16584 3172 3056 3138 4493 +16585 3060 3172 3050 4493 +16586 3060 3050 4282 4493 +16587 3082 3172 4282 4493 +16588 3172 3082 3050 4493 +16589 3050 3082 4282 4493 +16590 574 591 590 4507 +16591 1184 1603 4544 4616 +16592 1630 1221 1041 4598 +16593 1183 2579 1184 4616 +16594 3923 2783 4546 4588 +16595 299 591 574 4507 +16596 2783 3923 2679 4588 +16597 3156 3039 3070 4495 +16598 3155 3156 3070 4495 +16599 3156 3155 3039 4495 +16600 4279 4385 4508 4564 +16601 3389 3372 4583 4639 +16602 739 4290 4366 4496 +16603 739 4110 4290 4496 +16604 4363 4412 4585 4721 +16605 3215 650 608 4498 +16606 644 3215 608 4498 +16607 622 650 3189 4498 +16608 3189 650 2911 4498 +16609 2908 3189 2911 4498 +16610 1392 1159 4658 4686 +16611 2280 2577 823 4746 +16612 3958 2911 2909 4498 +16613 644 3958 2909 4498 +16614 3215 644 3177 4498 +16615 650 3343 2911 4498 +16616 3343 650 3215 4498 +16617 3995 641 2908 4498 +16618 645 3882 4374 4498 +16619 641 645 4374 4498 +16620 2784 4583 4615 4764 +16621 3274 644 2909 4498 +16622 2911 3274 2909 4498 +16623 3177 644 3274 4498 +16624 3177 3274 2911 4498 +16625 3343 3177 2911 4498 +16626 3343 3215 3177 4498 +16627 3995 3985 641 4498 +16628 3985 3882 645 4498 +16629 641 3985 645 4498 +16630 4329 4122 4379 4503 +16631 4179 4122 4482 4503 +16632 4122 4179 4379 4503 +16633 4130 993 4327 4503 +16634 4327 993 4482 4503 +16635 705 706 3108 4534 +16636 1191 1192 4263 4504 +16637 3105 3640 4310 4552 +16638 3372 3613 4583 4639 +16639 1221 1608 172 4638 +16640 3173 3050 4282 4611 +16641 3372 3388 3365 4639 +16642 3613 3372 3614 4639 +16643 3933 793 4438 4684 +16644 172 1608 4302 4638 +16645 172 4089 4598 4638 +16646 2971 574 590 4507 +16647 590 2690 4308 4507 +16648 2724 295 4308 4507 +16649 2745 3200 4391 4539 +16650 299 295 3893 4507 +16651 295 2971 4308 4507 +16652 2971 295 574 4507 +16653 2724 4308 4413 4507 +16654 2725 2724 4413 4507 +16655 3200 3179 4475 4539 +16656 2692 2690 4413 4507 +16657 2690 2692 4308 4507 +16658 4308 2692 4413 4507 +16659 3406 3408 4435 4555 +16660 3279 705 711 4618 +16661 1717 1240 1675 4509 +16662 3110 3279 711 4618 +16663 3281 3280 3782 4618 +16664 3781 3110 3282 4618 +16665 1721 1666 4080 4510 +16666 1722 1721 4080 4510 +16667 1721 1722 1666 4510 +16668 303 3957 2816 4643 +16669 4550 3957 4619 4643 +16670 3582 2785 3581 4722 +16671 644 642 4069 4685 +16672 3500 2784 2679 4646 +16673 3699 3131 3714 4513 +16674 3287 1656 1638 4553 +16675 4322 3933 4438 4553 +16676 3640 3699 4128 4513 +16677 4128 3699 4310 4513 +16678 3640 4128 4310 4513 +16679 4077 2815 4219 4550 +16680 1726 1771 1735 4515 +16681 1668 4108 4277 4667 +16682 1698 1723 4461 4515 +16683 1723 1698 1731 4515 +16684 1731 1698 1768 4515 +16685 1768 1698 1699 4515 +16686 1699 1698 4461 4515 +16687 1264 1030 4407 4758 +16688 4763 1737 4768 4772 +16689 1735 1771 1734 4515 +16690 1725 1735 1734 4515 +16691 3986 303 2822 4516 +16692 3986 2822 2813 4516 +16693 3993 3986 2813 4516 +16694 3993 3981 3986 4516 +16695 3973 3993 2813 4516 +16696 3993 3973 3981 4516 +16697 3981 3973 2813 4516 +16698 4215 1587 4394 4517 +16699 3529 2782 4236 4524 +16700 3530 4001 4217 4524 +16701 2782 3529 4383 4524 +16702 1666 1280 4571 4737 +16703 1587 4116 4417 4517 +16704 4116 1587 4215 4517 +16705 3938 3930 3473 4610 +16706 986 4336 4477 4609 +16707 4207 1019 4477 4609 +16708 1174 1160 4445 4519 +16709 3762 3852 702 4581 +16710 4041 1174 4445 4519 +16711 1174 4041 4135 4519 +16712 1160 4041 4445 4519 +16713 4041 1160 4135 4519 +16714 3097 3100 703 4581 +16715 1689 1244 345 4554 +16716 3940 4012 4247 4522 +16717 4236 2782 4415 4524 +16718 4415 4383 4471 4524 +16719 4236 4415 4471 4524 +16720 3519 3530 4471 4524 +16721 4383 3519 4471 4524 +16722 3518 3530 3519 4524 +16723 3518 3519 4383 4524 +16724 3595 3599 3529 4524 +16725 3599 4001 4137 4524 +16726 4137 4001 4175 4524 +16727 3599 4137 4175 4524 +16728 3599 3459 3529 4524 +16729 3529 3459 4175 4524 +16730 3598 3595 4001 4524 +16731 3595 3598 3599 4524 +16732 3599 3598 4001 4524 +16733 3459 3599 4150 4524 +16734 4150 3599 4175 4524 +16735 3459 4150 4175 4524 +16736 1586 1666 4394 4525 +16737 1666 1586 1587 4525 +16738 1762 1666 1672 4525 +16739 1762 1760 1666 4525 +16740 1762 1711 4177 4525 +16741 1760 1762 4177 4525 +16742 4152 1760 4177 4525 +16743 1706 1116 1711 4525 +16744 1116 1706 1672 4525 +16745 1587 4394 4517 4525 +16746 1587 1586 4394 4525 +16747 1762 1706 1711 4525 +16748 1706 1762 1672 4525 +16749 984 1000 4172 4612 +16750 4278 4282 4300 4527 +16751 984 4172 4465 4612 +16752 1086 1087 4172 4612 +16753 984 1085 4429 4612 +16754 3134 3060 4081 4528 +16755 3686 3556 4151 4530 +16756 3556 3686 3101 4530 +16757 3101 3686 4151 4530 +16758 4172 1087 4465 4532 +16759 966 4172 4465 4532 +16760 1632 2135 1629 4533 +16761 1189 2135 1191 4533 +16762 1189 1191 4504 4533 +16763 3852 3264 3097 4581 +16764 1192 2135 1632 4533 +16765 2135 1192 1191 4533 +16766 1191 1192 4504 4533 +16767 1192 1632 4263 4533 +16768 4263 1632 4504 4533 +16769 1192 4263 4504 4533 +16770 575 3108 4426 4534 +16771 575 705 3108 4534 +16772 706 705 575 4534 +16773 706 321 4273 4534 +16774 4273 321 4426 4534 +16775 706 575 321 4534 +16776 321 575 4426 4534 +16777 821 1317 826 4535 +16778 586 3319 4491 4687 +16779 821 611 4199 4535 +16780 611 821 348 4535 +16781 345 1682 4319 4536 +16782 1243 826 4319 4536 +16783 4004 1682 4060 4536 +16784 1682 345 4060 4536 +16785 826 345 4319 4536 +16786 1243 4004 4060 4536 +16787 826 1243 4060 4536 +16788 345 826 4060 4536 +16789 1559 740 1562 4537 +16790 2692 3895 2717 4538 +16791 2725 2692 2717 4538 +16792 2692 2725 3895 4538 +16793 3895 3896 2717 4538 +16794 2717 3896 2716 4538 +16795 3929 3895 3888 4538 +16796 3893 299 4507 4538 +16797 2725 2724 4507 4538 +16798 3895 2725 4413 4538 +16799 4413 2725 4507 4538 +16800 299 3895 4452 4538 +16801 299 4452 4507 4538 +16802 3896 3895 3929 4538 +16803 3896 3929 2716 4538 +16804 295 3893 4507 4538 +16805 2724 295 4507 4538 +16806 295 2724 3893 4538 +16807 3895 4413 4452 4538 +16808 4452 4413 4507 4538 +16809 3931 1654 4082 4540 +16810 4082 1654 4433 4540 +16811 3228 2754 2727 4548 +16812 3054 4237 4474 4548 +16813 3500 3501 3388 4583 +16814 1265 4085 4275 4728 +16815 3061 4205 4443 3055 +16816 1001 1331 4096 4542 +16817 995 1125 4096 4542 +16818 1125 995 1331 4542 +16819 1331 995 4096 4542 +16820 974 1125 4465 4542 +16821 1125 974 4096 4542 +16822 966 974 4465 4542 +16823 974 966 4096 4542 +16824 4096 966 4465 4542 +16825 1174 1158 4135 4608 +16826 3112 3107 3111 4305 +16827 3605 3076 4234 3000 +16828 1162 1306 4135 4608 +16829 1162 1304 1303 4608 +16830 1158 1305 1156 4608 +16831 4135 1158 4519 4608 +16832 1085 1087 1086 4612 +16833 3922 3422 3930 4545 +16834 3922 3930 3938 4545 +16835 1746 1748 4197 4629 +16836 3922 4410 4435 4545 +16837 3422 3922 4435 4545 +16838 4197 1115 4501 4629 +16839 1733 4557 4579 4640 +16840 2783 3077 4481 4546 +16841 3923 2783 4481 4546 +16842 1582 1116 4749 4771 +16843 1080 1186 1187 4603 +16844 4585 1196 4593 4721 +16845 4027 4032 4655 4695 +16846 3403 3491 3402 4723 +16847 962 4654 4675 4677 +16848 4654 963 4675 4677 +16849 4127 2753 4318 4548 +16850 3054 4127 4318 4548 +16851 2802 3049 4387 4548 +16852 4387 3049 4418 4548 +16853 2802 4387 4418 4548 +16854 3232 2802 4075 4548 +16855 3232 3198 2802 4548 +16856 3047 3143 4075 4548 +16857 4237 3038 4418 4548 +16858 4075 2802 4418 4548 +16859 3038 4075 4418 4548 +16860 3198 3143 2753 4548 +16861 2754 3198 2753 4548 +16862 3198 3232 3143 4548 +16863 3046 3232 4075 4548 +16864 3232 3046 3143 4548 +16865 3143 3046 4075 4548 +16866 3933 3287 1638 4553 +16867 3545 3582 3581 4722 +16868 4282 3060 4627 4631 +16869 3119 3699 3118 4692 +16870 4049 1734 4332 4556 +16871 1734 4049 4154 4556 +16872 1735 1121 4640 4745 +16873 1668 1675 4108 4667 +16874 2135 1044 4547 4580 +16875 4394 4215 4517 4734 +16876 955 4289 4712 4725 +16877 1675 4204 4509 4667 +16878 706 3269 4614 4665 +16879 4427 4545 4676 4690 +16880 1653 3932 4057 4691 +16881 1664 1665 1668 4667 +16882 631 633 4622 4666 +16883 2895 631 4622 4666 +16884 1238 1675 1664 4667 +16885 3957 2816 4643 4645 +16886 4219 4077 4550 4645 +16887 1670 1669 1715 4562 +16888 1692 1670 1715 4562 +16889 1694 1670 1690 4562 +16890 1692 1694 1690 4562 +16891 1670 1692 1690 4562 +16892 1200 582 4143 4672 +16893 4290 4341 4366 4563 +16894 4366 4341 4496 4563 +16895 4290 4366 4496 4563 +16896 4058 4290 4496 4563 +16897 4058 1595 4290 4563 +16898 2873 4540 4582 4693 +16899 3286 3287 4625 4691 +16900 1653 4057 4247 4691 +16901 3287 3286 1656 4691 +16902 3486 3485 4385 4564 +16903 3092 3486 4385 4564 +16904 4540 3931 4582 4693 +16905 4494 3486 4506 4564 +16906 4116 4417 4517 4744 +16907 4545 4262 4676 4690 +16908 1142 1392 1171 4568 +16909 1142 1171 1141 4568 +16910 3206 4350 4595 4648 +16911 1746 4197 4501 4629 +16912 1717 1240 4509 4617 +16913 345 796 4032 4617 +16914 3081 3389 2784 4764 +16915 1280 1585 4313 4571 +16916 4040 4091 4497 4765 +16917 1719 963 4289 4725 +16918 644 3860 3868 4720 +16919 1070 2280 4424 4721 +16920 1390 1392 4568 4739 +16921 825 172 4083 4664 +16922 1240 1717 1675 4667 +16923 172 1042 4083 4664 +16924 4522 3940 4592 4625 +16925 4322 4553 4613 4625 +16926 1244 1675 4509 4667 +16927 185 1919 4220 4664 +16928 3082 982 3052 4573 +16929 3050 3082 4249 4573 +16930 2823 2825 3220 4594 +16931 3051 3082 3050 4573 +16932 3051 3050 4249 4573 +16933 3051 4249 4565 4573 +16934 3988 4550 4643 4645 +16935 3451 3455 4325 4574 +16936 3455 3451 3597 4574 +16937 3597 3451 4325 4574 +16938 710 3699 3645 4575 +16939 3640 4310 4552 4575 +16940 4310 3640 4513 4575 +16941 3699 3640 3645 4575 +16942 710 3640 4552 4575 +16943 3640 710 3645 4575 +16944 3204 631 633 4576 +16945 4372 4047 4407 4716 +16946 2918 631 4549 4576 +16947 3204 2918 4549 4576 +16948 631 3204 4549 4576 +16949 2690 590 4413 4606 +16950 590 2690 4507 4606 +16951 304 2816 4219 4645 +16952 4304 2786 4600 4605 +16953 2690 4413 4507 4606 +16954 2135 793 1632 4580 +16955 1632 793 4082 4580 +16956 793 2135 4404 4580 +16957 4082 793 4404 4580 +16958 703 3852 4123 4581 +16959 3097 703 4123 4581 +16960 3852 3097 4123 4581 +16961 3528 3501 3518 4583 +16962 3628 3372 4200 4583 +16963 3389 3372 3628 4583 +16964 2784 3389 3628 4583 +16965 2784 3628 4466 4583 +16966 1699 1736 4461 4757 +16967 1141 1142 4568 4590 +16968 1565 1183 4485 4680 +16969 3528 3518 4211 4583 +16970 4211 3518 4466 4583 +16971 3528 4174 4200 4583 +16972 3613 3528 4200 4583 +16973 4174 3528 4211 4583 +16974 4174 4211 4466 4583 +16975 3628 4174 4466 4583 +16976 4174 3628 4200 4583 +16977 3053 3051 1027 4649 +16978 825 172 4664 4724 +16979 3581 2785 4264 4722 +16980 4264 3514 4369 4722 +16981 3400 3545 3581 4722 +16982 3276 2942 2943 4587 +16983 313 3276 2943 4587 +16984 3276 2885 2942 4587 +16985 313 2942 4328 4587 +16986 313 4328 4423 4587 +16987 2885 2900 4328 4587 +16988 2900 2885 3276 4587 +16989 2942 2885 4328 4587 +16990 3077 3456 4546 4588 +16991 3456 3077 3529 4588 +16992 4012 1656 4553 4592 +16993 3932 4292 4315 4626 +16994 1241 4292 4357 4626 +16995 706 3269 4072 4614 +16996 1660 1656 4012 4592 +16997 1660 4012 4522 4592 +16998 3699 3131 3118 4692 +16999 4012 4247 4522 4592 +17000 2825 323 2830 4594 +17001 323 3213 3221 4594 +17002 3220 2824 2833 4594 +17003 2830 3220 2833 4594 +17004 2824 2830 2833 4594 +17005 323 3221 2844 4594 +17006 2830 323 2844 4594 +17007 2849 3206 2844 4594 +17008 3221 2849 2844 4594 +17009 2848 2830 2844 4594 +17010 3206 2848 2844 4594 +17011 2831 3206 2830 4594 +17012 3206 2831 2848 4594 +17013 2848 2831 2830 4594 +17014 324 3213 3220 4595 +17015 3211 2819 4070 4595 +17016 3211 4070 4316 4595 +17017 3045 3044 3078 4596 +17018 3086 3045 3066 4596 +17019 3133 4005 4044 4669 +17020 4098 1144 4178 4669 +17021 3133 3041 4300 4669 +17022 3086 3002 3044 4596 +17023 3045 3086 3044 4596 +17024 3078 3044 3043 4596 +17025 3044 3002 3043 4596 +17026 4278 3041 4527 4669 +17027 3132 3133 4081 4669 +17028 3002 3086 4445 4596 +17029 3001 3002 4445 4596 +17030 3086 3001 4445 4596 +17031 4044 4005 4300 4669 +17032 4065 2264 4484 4673 +17033 3133 3132 3041 4669 +17034 4081 4044 4300 4669 +17035 4178 3132 4377 4669 +17036 3002 3078 3085 4596 +17037 3085 3078 3043 4596 +17038 3002 3085 3043 4596 +17039 3041 1144 4527 4669 +17040 1144 3041 4178 4669 +17041 2781 4092 4385 4674 +17042 4081 4300 4627 4669 +17043 582 2289 4143 4672 +17044 4300 4278 4527 4669 +17045 1144 4403 4527 4669 +17046 4627 4527 4631 4669 +17047 3204 633 4317 4659 +17048 594 611 609 4663 +17049 2576 594 609 4663 +17050 1175 1178 3043 4601 +17051 1160 1175 3043 4601 +17052 2919 631 4576 4659 +17053 633 3204 4576 4659 +17054 4185 4047 4711 4716 +17055 1244 4204 4509 4662 +17056 611 2577 1194 4663 +17057 4032 345 4617 4662 +17058 631 633 4576 4659 +17059 631 2919 4622 4659 +17060 633 631 4622 4659 +17061 1178 1160 3085 4601 +17062 3085 1160 3043 4601 +17063 1178 3085 3043 4601 +17064 1244 345 4554 4662 +17065 1608 1191 4384 4661 +17066 2576 611 1194 4663 +17067 1270 4032 4617 4662 +17068 612 2576 4428 4663 +17069 793 3933 1638 4684 +17070 1639 793 1638 4684 +17071 1746 4146 4629 4679 +17072 1197 1183 4570 4616 +17073 3500 3386 4178 4607 +17074 3064 3500 4178 4607 +17075 1183 1184 4544 4616 +17076 1606 1197 4570 4616 +17077 1305 1174 4061 4608 +17078 1306 4041 4135 4608 +17079 4041 1306 4061 4608 +17080 1305 1303 3145 4608 +17081 3145 1303 1156 4608 +17082 1305 3145 1156 4608 +17083 4041 1174 4135 4608 +17084 1174 4041 4061 4608 +17085 4049 4332 4364 4640 +17086 4583 3500 4615 4639 +17087 3501 3613 3614 4639 +17088 3101 3473 4545 4610 +17089 3473 3101 4262 4610 +17090 4262 3101 4545 4610 +17091 3120 713 4291 4641 +17092 3131 713 3120 4641 +17093 966 984 4096 4612 +17094 4096 984 4465 4612 +17095 966 4096 4465 4612 +17096 967 1087 1085 4612 +17097 1087 967 966 4612 +17098 966 967 1085 4612 +17099 3931 3940 4438 4613 +17100 3940 3931 1654 4613 +17101 1654 3931 4438 4613 +17102 4049 1266 4579 4640 +17103 1266 4049 4364 4640 +17104 4279 4564 4604 4767 +17105 3500 2784 4583 4615 +17106 824 2280 823 4746 +17107 4557 4332 4579 4640 +17108 1121 1266 1735 4640 +17109 1703 1702 4291 4617 +17110 1717 1703 4291 4617 +17111 1703 1717 1702 4617 +17112 963 1719 4043 4725 +17113 1675 796 4509 4617 +17114 1240 1675 4509 4617 +17115 1675 1240 796 4617 +17116 796 1240 1277 4617 +17117 1240 1717 1277 4617 +17118 796 1277 4291 4617 +17119 611 594 597 4663 +17120 1768 1726 4515 4642 +17121 1771 1726 1772 4642 +17122 1772 1726 1768 4642 +17123 3095 705 576 4618 +17124 705 3279 575 4618 +17125 3781 3281 3782 4618 +17126 3781 3282 3281 4618 +17127 3095 3296 3295 4618 +17128 3295 3296 576 4618 +17129 705 575 576 4618 +17130 575 3279 576 4618 +17131 3279 3295 576 4618 +17132 576 3296 4451 4618 +17133 3095 576 4451 4618 +17134 3296 3095 4451 4618 +17135 1595 4341 4563 4620 +17136 1595 1596 4341 4620 +17137 2813 3988 4550 4643 +17138 2785 3923 4588 4646 +17139 4324 4494 4564 4735 +17140 4564 4385 4689 4735 +17141 644 607 4699 4720 +17142 3064 4615 4704 4764 +17143 1417 1121 4740 4745 +17144 4247 3940 4522 4625 +17145 3940 4247 4266 4625 +17146 4247 4522 4592 4625 +17147 4300 4282 4493 4627 +17148 3060 4300 4493 4627 +17149 4282 3060 4493 4627 +17150 204 1515 2041 4634 +17151 2041 2038 2042 4634 +17152 220 223 2041 4634 +17153 218 222 220 4634 +17154 3051 3060 4282 4631 +17155 4257 3222 4276 4632 +17156 2763 4276 4483 4632 +17157 1044 2135 4547 4651 +17158 4611 4527 4630 4631 +17159 4159 1399 4197 4629 +17160 1398 4159 4197 4629 +17161 1398 4146 4159 4629 +17162 1748 1398 4197 4629 +17163 1398 1748 4146 4629 +17164 1149 1577 1197 4680 +17165 1183 1577 1576 4680 +17166 4417 4259 4453 4736 +17167 3051 4282 4611 4630 +17168 3226 3222 4257 4653 +17169 957 1101 968 4654 +17170 957 962 4397 4654 +17171 1101 957 4397 4654 +17172 3328 2135 1044 4651 +17173 1513 1515 4087 4634 +17174 1515 1513 2041 4634 +17175 2043 218 220 4634 +17176 2041 1513 2038 4634 +17177 2043 2041 2042 4634 +17178 2043 220 2041 4634 +17179 1144 4047 4320 4635 +17180 4320 4047 4403 4635 +17181 1144 4320 4403 4635 +17182 644 3868 3786 4713 +17183 4412 2588 4585 4721 +17184 962 4397 4654 4677 +17185 3264 3852 726 4713 +17186 4417 4499 4623 4734 +17187 3714 3131 3120 4641 +17188 710 4513 4575 4641 +17189 4291 710 4575 4641 +17190 3714 1717 4291 4641 +17191 4291 1717 4617 4641 +17192 1277 4291 4617 4641 +17193 1277 3120 4291 4641 +17194 3714 3120 1277 4641 +17195 1717 3714 1277 4641 +17196 1717 1277 4617 4641 +17197 1116 1589 1582 4749 +17198 4407 1030 4694 4758 +17199 4177 4152 4525 4749 +17200 2784 3077 2679 4646 +17201 2679 3077 4588 4646 +17202 1565 1155 4113 4680 +17203 4384 1191 4390 4661 +17204 1191 1221 4390 4661 +17205 1155 1198 4113 4680 +17206 1221 1608 1630 4661 +17207 1736 1708 981 4757 +17208 1708 1695 981 4757 +17209 1742 1588 4417 4709 +17210 4320 4372 4407 4716 +17211 1176 1117 4487 4688 +17212 348 4021 4535 4657 +17213 1588 1116 4417 4719 +17214 348 1317 4052 4657 +17215 4027 345 4032 4695 +17216 1030 3051 4611 4649 +17217 3051 1030 1027 4649 +17218 4527 4320 4611 4649 +17219 4611 3051 4630 4649 +17220 4527 4611 4630 4649 +17221 4630 4377 4631 4649 +17222 4377 4527 4631 4649 +17223 4377 4178 4527 4649 +17224 4244 1027 4270 4649 +17225 4230 3061 4443 3058 +17226 4071 4377 4630 4649 +17227 4071 4178 4377 4649 +17228 3894 3328 586 4730 +17229 3504 4178 4244 4649 +17230 3319 3328 3317 4730 +17231 4251 4029 4261 4728 +17232 1109 1110 4487 4688 +17233 1392 1159 1171 4658 +17234 4228 3286 4266 4691 +17235 4545 4262 4610 4676 +17236 4288 1170 4301 4658 +17237 1171 1159 1147 4658 +17238 1147 1159 4601 4658 +17239 1171 1147 4601 4658 +17240 1398 1747 4040 4766 +17241 1171 4288 4301 4658 +17242 1157 1170 4288 4658 +17243 1171 1157 4288 4658 +17244 1170 1158 4601 4658 +17245 1158 1170 1157 4658 +17246 4497 4040 4765 4766 +17247 1158 1171 4601 4658 +17248 1171 1158 1157 4658 +17249 2576 2577 4428 4663 +17250 2577 2576 1194 4663 +17251 4083 1042 4603 4664 +17252 1080 4083 4603 4664 +17253 822 1080 4603 4664 +17254 1080 822 4083 4664 +17255 1919 149 4558 4664 +17256 1919 185 149 4664 +17257 1924 185 4220 4664 +17258 1911 1924 4220 4664 +17259 1924 1911 172 4664 +17260 185 1924 172 4664 +17261 1042 1911 4220 4664 +17262 1911 1042 172 4664 +17263 1919 1075 4220 4664 +17264 1075 1042 4220 4664 +17265 1919 4400 4603 4664 +17266 4400 1919 4558 4664 +17267 1042 1075 4603 4664 +17268 1075 1919 4603 4664 +17269 1714 1240 4351 4667 +17270 1240 1714 1717 4667 +17271 1717 1714 4204 4667 +17272 4204 1714 4351 4667 +17273 4234 3001 4286 4668 +17274 197 1031 1035 4702 +17275 725 2661 4281 4702 +17276 3045 3066 4596 4668 +17277 3080 3045 4596 4668 +17278 3045 3080 3066 4668 +17279 3001 3015 4286 4668 +17280 3066 4234 4286 4668 +17281 3086 3001 4596 4668 +17282 3086 3015 3001 4668 +17283 3015 3066 4286 4668 +17284 197 725 4281 4702 +17285 1031 197 4281 4702 +17286 2661 725 84 4702 +17287 3015 3086 3066 4668 +17288 3066 3086 4596 4668 +17289 4279 4385 4386 4674 +17290 2579 1606 1604 4678 +17291 2579 1184 4616 4678 +17292 957 962 4654 4675 +17293 968 957 4654 4675 +17294 1120 1118 4589 4679 +17295 1393 4146 4469 4679 +17296 1407 1729 4647 4679 +17297 1729 1407 1746 4679 +17298 1407 4647 4660 4679 +17299 1399 1118 1110 4679 +17300 1110 1118 4629 4679 +17301 1118 1399 1393 4679 +17302 4146 1399 4159 4679 +17303 4159 1399 4629 4679 +17304 4146 4159 4629 4679 +17305 1134 1407 4660 4679 +17306 1407 1134 1746 4679 +17307 1746 1134 4660 4679 +17308 1399 1110 4197 4679 +17309 4197 1110 4629 4679 +17310 1399 4197 4629 4679 +17311 1401 1399 4146 4679 +17312 1399 1401 1393 4679 +17313 1393 1401 4146 4679 +17314 1198 1565 4113 4680 +17315 1149 1198 826 4680 +17316 826 1198 1155 4680 +17317 1149 826 1155 4680 +17318 1186 1605 1918 4681 +17319 1080 1186 4603 4681 +17320 1186 1080 1605 4681 +17321 4438 1654 4613 4684 +17322 4322 4438 4613 4684 +17323 4438 4322 4553 4684 +17324 4553 4322 4613 4684 +17325 4270 4284 4727 4760 +17326 642 4498 4636 4685 +17327 640 4069 4374 4685 +17328 4009 640 4374 4685 +17329 640 4009 4069 4685 +17330 1708 1709 4155 1712 +17331 4069 642 4374 4685 +17332 4374 642 4636 4685 +17333 1170 4301 4658 4686 +17334 1170 1180 4301 4686 +17335 1180 4301 4686 4739 +17336 4499 4417 4517 4719 +17337 1171 1392 4658 4686 +17338 1392 1171 4568 4686 +17339 1141 4301 4568 4686 +17340 1171 1141 4568 4686 +17341 1141 1171 4301 4686 +17342 4301 1171 4658 4686 +17343 1110 1176 4487 4688 +17344 4050 4121 4131 4711 +17345 1659 1653 4247 4691 +17346 1659 3932 1653 4691 +17347 3287 1656 4553 4691 +17348 3287 4553 4625 4691 +17349 3286 3932 1659 4691 +17350 1656 1659 4012 4691 +17351 4012 1659 4247 4691 +17352 3286 1659 1660 4691 +17353 1660 1659 1656 4691 +17354 3286 1660 1656 4691 +17355 1656 4012 4553 4691 +17356 4592 4247 4625 4691 +17357 4553 4592 4625 4691 +17358 4553 4012 4592 4691 +17359 4012 4247 4592 4691 +17360 4352 4376 4611 4694 +17361 4320 4352 4611 4694 +17362 4611 1030 4649 4694 +17363 4320 4611 4649 4694 +17364 3387 3388 3365 3372 +17365 4032 4650 4655 4695 +17366 1237 1685 1686 4695 +17367 1237 1686 4027 4695 +17368 345 1689 4650 4695 +17369 4650 1689 4655 4695 +17370 4597 4427 4644 4706 +17371 750 752 84 4697 +17372 753 1649 4240 4697 +17373 1649 753 84 4697 +17374 750 753 4240 4697 +17375 753 750 84 4697 +17376 303 2822 2813 4698 +17377 303 2814 2822 4698 +17378 3762 3348 580 4699 +17379 644 3348 3860 4699 +17380 3348 644 580 4699 +17381 607 3762 580 4699 +17382 607 3852 3762 4699 +17383 644 607 580 4699 +17384 1115 1176 1110 4700 +17385 1110 1176 4688 4700 +17386 1115 1110 4688 4700 +17387 1176 1117 4688 4700 +17388 3080 2790 3594 4701 +17389 3080 3594 4668 4701 +17390 4050 4047 4121 4711 +17391 1265 1264 4131 4711 +17392 1159 1179 1127 4717 +17393 2995 3001 4234 4701 +17394 4234 3001 4668 4701 +17395 4098 1734 4154 4727 +17396 1734 1725 4670 4727 +17397 2904 3995 2908 4761 +17398 3594 3066 4668 4701 +17399 3066 3594 4467 4701 +17400 3066 4234 4668 4701 +17401 3066 3065 4234 4701 +17402 3065 3066 4467 4701 +17403 1035 4132 4359 4702 +17404 201 4132 4414 4702 +17405 4132 201 4359 4702 +17406 345 1689 4695 4705 +17407 1244 345 1245 4705 +17408 4494 4427 4597 4706 +17409 4324 4597 4644 4706 +17410 1589 1116 4742 4749 +17411 1116 4525 4742 4749 +17412 4564 4324 4604 4706 +17413 4604 4324 4644 4706 +17414 3092 3486 4564 4706 +17415 3486 3092 4427 4706 +17416 4427 4494 4506 4706 +17417 4506 4494 4564 4706 +17418 4324 4494 4597 4706 +17419 4494 4324 4564 4706 +17420 3486 4427 4506 4706 +17421 3486 4506 4564 4706 +17422 1115 1176 4700 4708 +17423 1115 1118 1176 4708 +17424 1176 1177 1117 4708 +17425 1117 1177 4459 4708 +17426 4320 4407 4694 4716 +17427 4047 4403 4711 4716 +17428 1177 4393 4459 4708 +17429 1177 1121 4393 4708 +17430 1177 1176 1121 4708 +17431 1725 4270 4670 4727 +17432 3080 2790 4701 4710 +17433 2790 3080 3073 4710 +17434 4154 1734 4556 4727 +17435 3572 3545 4369 3502 +17436 1725 1735 4098 4727 +17437 4275 4085 4444 4728 +17438 1729 1730 4707 4740 +17439 4131 1264 4185 4711 +17440 4047 4131 4185 4711 +17441 4714 4085 4728 4729 +17442 3328 2994 3317 4730 +17443 3328 2993 2994 4730 +17444 1589 1116 4719 4741 +17445 3064 4704 4743 4764 +17446 4517 4417 4736 4744 +17447 1587 1741 4417 4744 +17448 1116 4525 4719 4741 +17449 4525 4571 4719 4741 +17450 4269 4259 4417 4744 +17451 1120 4589 4602 4715 +17452 4602 4589 4660 4715 +17453 4660 4589 4679 4715 +17454 4647 4660 4679 4715 +17455 1120 4647 4679 4715 +17456 4589 1120 4679 4715 +17457 1739 4766 4768 4772 +17458 1705 1695 4461 4757 +17459 1736 2785 1114 4757 +17460 2580 1070 4721 4746 +17461 1070 2280 4721 4746 +17462 1121 1120 4602 4740 +17463 4624 823 4663 4746 +17464 4399 1417 4442 4740 +17465 1417 1405 4442 4740 +17466 611 2577 4663 4746 +17467 823 611 4663 4746 +17468 4320 4750 4758 4774 +17469 4742 4191 4747 4749 +17470 1589 1116 4741 4742 +17471 4424 1196 4585 4721 +17472 2576 4412 4428 4721 +17473 2576 2588 4412 4721 +17474 1280 1666 4145 4737 +17475 4052 348 4657 4733 +17476 2588 2580 4424 4721 +17477 2588 4424 4585 4721 +17478 2580 2576 4428 4721 +17479 2576 2580 2588 4721 +17480 1072 1070 4424 4721 +17481 2580 1072 4424 4721 +17482 1072 2580 1070 4721 +17483 4234 2995 4701 4726 +17484 4701 2995 4710 4726 +17485 3594 3072 4467 4726 +17486 3594 4467 4701 4726 +17487 3340 3322 4388 4753 +17488 3072 3605 4467 4755 +17489 3065 4234 4701 4726 +17490 4234 3065 4467 4726 +17491 4467 3065 4701 4726 +17492 4251 1265 4444 4728 +17493 986 1019 4265 4729 +17494 1019 986 4085 4729 +17495 4085 986 4265 4729 +17496 1741 4173 4269 4763 +17497 4412 4428 4721 4732 +17498 4444 4029 4728 4729 +17499 4085 4444 4728 4729 +17500 4491 586 4687 4730 +17501 3894 4491 4687 4730 +17502 4177 1705 4389 4771 +17503 1400 1128 1398 4766 +17504 4223 1740 4497 4766 +17505 979 1122 4029 4762 +17506 4374 4009 4685 4759 +17507 3882 4009 4374 4759 +17508 4407 4694 4716 4750 +17509 1019 4207 4265 4762 +17510 4313 4517 4623 4734 +17511 4313 4394 4517 4734 +17512 3483 4644 4706 4748 +17513 4525 4145 4737 4742 +17514 1161 1169 4301 4773 +17515 4145 1666 4525 4737 +17516 1417 1770 1765 4745 +17517 1121 1417 1765 4745 +17518 1121 1765 4640 4745 +17519 1765 1770 4364 4745 +17520 1770 1732 4364 4745 +17521 1732 1408 1724 4745 +17522 1770 1417 4406 4745 +17523 1747 4765 4766 4770 +17524 1128 1747 4766 4770 +17525 1116 4177 4525 4771 +17526 1705 1700 4757 4771 +17527 4301 4568 4686 4739 +17528 1700 1276 1114 4771 +17529 1114 4756 4757 4771 +17530 1116 4525 4749 4771 +17531 1276 1116 1114 4771 +17532 1400 1128 4766 4770 +17533 4399 1407 4707 4740 +17534 1729 1407 4715 4740 +17535 1407 1729 4707 4740 +17536 1405 1121 4602 4740 +17537 4442 1405 4602 4740 +17538 2577 4428 4663 4746 +17539 2577 2280 1070 4746 +17540 1407 4442 4602 4740 +17541 1407 4602 4715 4740 +17542 4215 4116 4517 4744 +17543 4738 3081 4743 4764 +17544 4399 1730 4406 4745 +17545 1730 1408 4406 4745 +17546 1730 4399 4707 4745 +17547 4707 4399 4740 4745 +17548 1730 4707 4740 4745 +17549 4399 1417 4740 4745 +17550 1417 4399 4406 4745 +17551 4364 1735 4640 4745 +17552 1765 4364 4640 4745 +17553 2576 2580 4428 4746 +17554 2577 2576 4428 4746 +17555 1131 4078 4432 4762 +17556 4254 4029 4444 4762 +17557 4145 4191 4742 4749 +17558 4152 4145 4742 4749 +17559 4145 4152 4191 4749 +17560 1019 1131 4207 4762 +17561 1122 1131 4432 4762 +17562 4694 4320 4716 4750 +17563 4389 1705 4461 4757 +17564 2995 3071 4752 4755 +17565 3071 2995 4234 4755 +17566 4467 4234 4726 4755 +17567 3072 4467 4726 4755 +17568 4234 2995 4726 4755 +17569 4726 2995 4752 4755 +17570 1264 4407 4750 4758 +17571 4498 4374 4636 4759 +17572 4177 4095 4756 4757 +17573 4095 4177 4389 4757 +17574 1741 4763 4768 4772 +17575 1695 1700 3583 4757 +17576 2785 1736 981 4757 +17577 3583 1700 1114 4757 +17578 1737 1739 4768 4772 +17579 3582 1695 3583 4757 +17580 2785 3582 3583 4757 +17581 1695 3582 981 4757 +17582 3923 3583 1114 4757 +17583 2785 3923 1114 4757 +17584 3923 2785 3583 4757 +17585 3582 2785 3538 4757 +17586 3538 2785 981 4757 +17587 3582 3538 981 4757 +17588 4556 4154 4727 4760 +17589 4284 4556 4727 4760 +17590 3985 3882 4498 4761 +17591 4498 3882 4759 4761 +17592 4207 977 4265 4762 +17593 4029 4728 4729 4762 +17594 4729 4728 4731 4762 +17595 4085 4444 4729 4762 +17596 4265 4085 4729 4762 +17597 4085 4265 4444 4762 +17598 1131 1122 1093 4762 +17599 1093 1122 977 4762 +17600 1093 977 4207 4762 +17601 1131 1093 4207 4762 +17602 961 979 4265 4762 +17603 977 961 4265 4762 +17604 976 979 961 4762 +17605 979 976 1122 4762 +17606 1122 976 961 4762 +17607 978 961 977 4762 +17608 1122 978 977 4762 +17609 978 1122 961 4762 +17610 1116 1705 4177 4771 +17611 4040 1747 4189 4765 +17612 4091 4040 4189 4765 +17613 4279 2667 4564 4767 +17614 2667 4279 4386 4767 +17615 3446 2667 4386 4767 +17616 3483 2667 3446 4767 +17617 4040 1747 4765 4766 +17618 4564 3483 4706 4767 +17619 4706 3483 4748 4767 +17620 4564 4706 4748 4767 +17621 4098 4017 4297 4774 +17622 1027 4270 4649 4774 +17623 1030 4694 4758 4774 +17624 4270 1027 4760 4774 +17625 1027 1030 4760 4774 +17626 4716 4297 4750 4774 +17627 4320 4716 4750 4774 +17628 1030 1027 4649 4774 +17629 4173 1741 4223 4772 +17630 4301 1774 4568 4773 +17631 1774 1390 4568 4773 +17632 1161 1180 4769 4773 +17633 1180 1161 4301 4773 +17634 4568 1390 4739 4773 +17635 4739 1390 4769 4773 +17636 4301 4568 4739 4773 +17637 1180 4301 4739 4773 +17638 1180 4739 4769 4773 +17639 4649 4320 4694 4774 +17640 1030 4649 4694 4774 +17641 4154 4727 4760 4774 +17642 4154 4098 4727 4774 +17643 4270 4244 4649 4774 +17644 4320 4297 4403 4774 +17645 4403 4297 4716 4774 +17646 4320 4403 4716 4774 +17647 3504 4178 4649 4774 +17648 4098 1725 4727 4774 +17649 1725 4098 4178 4774 +17650 1725 4270 4727 4774 +17651 1725 4244 4270 4774 +17652 4244 3504 4649 4774 +17653 3504 1725 4178 4774 +17654 1725 3504 4244 4774 +# Generated by tetgen octopus.ele diff --git a/examples/testbed/data/octopus.face b/examples/testbed/data/octopus.face new file mode 100644 index 0000000..1da0daa --- /dev/null +++ b/examples/testbed/data/octopus.face @@ -0,0 +1,9546 @@ +9544 1 + 1 3402 3368 3387 -1 + 2 1521 1528 1529 -1 + 3 1521 1541 1528 -1 + 4 1098 959 958 -1 + 5 958 1099 1098 -1 + 6 751 1058 747 -1 + 7 3413 3407 3382 -1 + 8 3382 3407 507 -1 + 9 878 877 865 -1 + 10 865 877 866 -1 + 11 1598 1213 1600 -1 + 12 4404 4294 3894 -1 + 13 921 922 920 -1 + 14 882 922 921 -1 + 15 323 351 325 -1 + 16 323 316 351 -1 + 17 2313 2297 2311 -1 + 18 2313 2309 2297 -1 + 19 2936 2925 2934 -1 + 20 2950 2934 2925 -1 + 21 1658 2661 2862 -1 + 22 4605 4600 3603 -1 + 23 3710 3691 3692 -1 + 24 3692 3691 3719 -1 + 25 239 234 238 -1 + 26 239 230 234 -1 + 27 1265 4085 1263 -1 + 28 4754 2854 2856 -1 + 29 1539 1540 2175 -1 + 30 786 742 819 -1 + 31 1664 1155 1149 -1 + 32 2547 2546 2543 -1 + 33 2543 2546 2560 -1 + 34 3429 3363 3364 -1 + 35 3429 3430 3363 -1 + 36 2499 1916 4280 -1 + 37 2499 4104 1916 -1 + 38 3962 3802 3803 -1 + 39 3962 3800 3802 -1 + 40 2728 2812 2729 -1 + 41 2756 2758 2759 -1 + 42 2756 2755 2758 -1 + 43 2650 2780 2673 -1 + 44 763 2673 2780 -1 + 45 3547 3692 3693 -1 + 46 3547 3565 3692 -1 + 47 4381 3933 4022 -1 + 48 804 803 784 -1 + 49 784 2939 804 -1 + 50 3094 3640 4107 -1 + 51 3094 3645 3640 -1 + 52 1124 1092 1313 -1 + 53 3664 3673 3639 -1 + 54 3639 3673 3672 -1 + 55 3901 3880 2840 -1 + 56 4380 3375 3620 -1 + 57 3620 3375 3369 -1 + 58 1267 1235 1277 -1 + 59 4753 2856 4388 -1 + 60 3491 3514 4723 -1 + 61 323 324 3214 -1 + 62 3214 315 323 -1 + 63 3537 1543 1536 -1 + 64 2358 2327 2367 -1 + 65 1544 1543 1535 -1 + 66 1544 1534 1543 -1 + 67 2323 2334 2332 -1 + 68 2323 2340 2334 -1 + 69 571 500 558 -1 + 70 501 558 500 -1 + 71 3390 3396 3361 -1 + 72 3361 3398 3390 -1 + 73 154 549 551 -1 + 74 2077 2081 2078 -1 + 75 2082 2081 2077 -1 + 76 4441 1602 4110 -1 + 77 4588 3529 2782 -1 + 78 3654 3666 3653 -1 + 79 3653 3667 3654 -1 + 80 3017 3071 3018 -1 + 81 3017 3000 3071 -1 + 82 3543 3707 3542 -1 + 83 3579 3707 3543 -1 + 84 4449 2855 3819 -1 + 85 2982 2915 2950 -1 + 86 2950 2925 2982 -1 + 87 3713 3694 435 -1 + 88 3713 3695 3694 -1 + 89 3109 3661 3106 -1 + 90 3707 3695 3708 -1 + 91 3708 3565 3707 -1 + 92 262 235 255 -1 + 93 255 267 262 -1 + 94 3507 3526 3525 -1 + 95 3507 3527 3526 -1 + 96 3915 3356 3919 -1 + 97 3946 3356 3915 -1 + 98 565 554 572 -1 + 99 573 572 554 -1 + 100 1259 1258 1719 -1 + 101 1719 1704 1259 -1 + 102 4259 4173 4763 -1 + 103 529 511 526 -1 + 104 526 511 527 -1 + 105 3092 3131 2682 -1 + 106 3092 3091 3131 -1 + 107 421 424 422 -1 + 108 421 418 424 -1 + 109 2359 762 879 -1 + 110 772 879 762 -1 + 111 519 282 518 -1 + 112 519 285 282 -1 + 113 3771 3794 3914 -1 + 114 3914 3794 3793 -1 + 115 689 690 675 -1 + 116 675 690 2433 -1 + 117 4666 3273 3272 -1 + 118 647 634 636 -1 + 119 636 638 647 -1 + 120 3578 3579 3905 -1 + 121 3543 3905 3579 -1 + 122 3851 3751 3747 -1 + 123 3747 3737 3851 -1 + 124 3624 3372 3628 -1 + 125 3612 3611 3557 -1 + 126 491 3425 3381 -1 + 127 3381 508 491 -1 + 128 3413 3657 3407 -1 + 129 3656 3657 3413 -1 + 130 384 381 380 -1 + 131 380 379 384 -1 + 132 2833 3220 2836 -1 + 133 2824 2836 3220 -1 + 134 3902 362 366 -1 + 135 3902 3803 362 -1 + 136 3947 3919 3943 -1 + 137 3943 3919 3356 -1 + 138 1287 3031 3032 -1 + 139 3772 3989 3745 -1 + 140 3745 3746 3772 -1 + 141 4740 4715 1120 -1 + 142 3309 3344 2827 -1 + 143 3309 3348 3344 -1 + 144 3027 3135 3036 -1 + 145 396 398 397 -1 + 146 3945 397 398 -1 + 147 2645 3885 3884 -1 + 148 3933 4322 3287 -1 + 149 1333 3146 3162 -1 + 150 1195 2351 1193 -1 + 151 1228 1193 2351 -1 + 152 2674 3194 3192 -1 + 153 3191 3194 2674 -1 + 154 3718 3639 3722 -1 + 155 3671 3722 3639 -1 + 156 2865 2902 2930 -1 + 157 2930 2897 2865 -1 + 158 3134 4148 3062 -1 + 159 2124 1188 2137 -1 + 160 4703 2724 4308 -1 + 161 3597 2803 3451 -1 + 162 3452 3451 2803 -1 + 163 3406 3473 3930 -1 + 164 3393 3394 3512 -1 + 165 3393 3510 3394 -1 + 166 3778 3777 3783 -1 + 167 3778 3792 3777 -1 + 168 3768 3769 3098 -1 + 169 3098 3102 3768 -1 + 170 358 349 356 -1 + 171 2951 349 358 -1 + 172 3994 3778 3980 -1 + 173 3994 3974 3778 -1 + 174 903 853 844 -1 + 175 2396 844 853 -1 + 176 2297 2303 2311 -1 + 177 2312 2311 2303 -1 + 178 2101 2108 2099 -1 + 179 2110 2108 2101 -1 + 180 2723 2713 2705 -1 + 181 838 840 849 -1 + 182 849 850 838 -1 + 183 3014 3009 3027 -1 + 184 977 943 1096 -1 + 185 3066 3065 3011 -1 + 186 3011 3015 3066 -1 + 187 4615 3386 3388 -1 + 188 255 254 256 -1 + 189 255 266 254 -1 + 190 778 767 771 -1 + 191 771 779 778 -1 + 192 306 307 305 -1 + 193 306 3889 307 -1 + 194 3738 3739 3780 -1 + 195 3780 3739 3725 -1 + 196 3834 3873 3825 -1 + 197 3821 3873 3834 -1 + 198 4409 4246 3636 -1 + 199 3636 3634 4409 -1 + 200 1719 1260 4043 -1 + 201 3043 3084 3169 -1 + 202 3655 3650 3661 -1 + 203 3655 3104 3650 -1 + 204 3179 3180 3265 -1 + 205 3265 3180 3351 -1 + 206 2735 3228 2802 -1 + 207 2735 2738 3228 -1 + 208 4616 4570 4544 -1 + 209 2685 2686 2657 -1 + 210 2657 2672 2685 -1 + 211 1717 1409 1714 -1 + 212 1916 1842 4280 -1 + 213 1916 4126 1842 -1 + 214 3469 3891 3480 -1 + 215 3470 3891 3469 -1 + 216 3570 3677 3678 -1 + 217 3570 3720 3677 -1 + 218 332 314 4587 -1 + 219 2840 2690 2692 -1 + 220 3163 1333 3162 -1 + 221 3740 3787 3281 -1 + 222 3878 3281 3787 -1 + 223 459 462 4141 -1 + 224 459 458 462 -1 + 225 3578 3905 3540 -1 + 226 3540 3905 3541 -1 + 227 450 461 1473 -1 + 228 1473 1479 450 -1 + 229 4197 4040 1110 -1 + 230 4464 4119 4455 -1 + 231 1916 18 4126 -1 + 232 1916 4104 18 -1 + 233 997 4336 4327 -1 + 234 1391 1401 4402 -1 + 235 3830 3833 3804 -1 + 236 3514 3491 3401 -1 + 237 4618 3280 3095 -1 + 238 3003 2997 4037 -1 + 239 3245 3238 3231 -1 + 240 3546 2747 3538 -1 + 241 3546 3053 2747 -1 + 242 546 545 543 -1 + 243 543 553 546 -1 + 244 3669 3670 3424 -1 + 245 3424 3421 3669 -1 + 246 2901 2930 2902 -1 + 247 4671 1111 1400 -1 + 248 1112 1400 1111 -1 + 249 1590 1581 4152 -1 + 250 1590 1763 1581 -1 + 251 1268 1231 4032 -1 + 252 2958 2843 2842 -1 + 253 2959 2843 2958 -1 + 254 4243 3129 3158 -1 + 255 3158 3129 3157 -1 + 256 3829 3822 3825 -1 + 257 3825 3822 3834 -1 + 258 584 592 585 -1 + 259 4653 2763 4632 -1 + 260 4004 1246 1680 -1 + 261 1247 1680 1246 -1 + 262 283 517 290 -1 + 263 290 517 3457 -1 + 264 306 296 3897 -1 + 265 306 298 296 -1 + 266 2787 3451 4425 -1 + 267 2787 3603 3451 -1 + 268 3163 3141 3136 -1 + 269 3163 3139 3141 -1 + 270 3927 3941 3926 -1 + 271 2661 2890 2862 -1 + 272 2661 2880 2890 -1 + 273 2505 2283 4299 -1 + 274 2505 4015 2283 -1 + 275 4652 3908 2988 -1 + 276 296 584 3883 -1 + 277 297 584 296 -1 + 278 386 392 381 -1 + 279 380 381 392 -1 + 280 1207 2289 4672 -1 + 281 1207 4484 2289 -1 + 282 4015 4296 2283 -1 + 283 2899 2881 2866 -1 + 284 2866 2897 2899 -1 + 285 430 429 420 -1 + 286 1516 420 429 -1 + 287 3388 3386 3387 -1 + 288 481 3551 480 -1 + 289 3552 3551 481 -1 + 290 4309 4149 3062 -1 + 291 3062 4149 3134 -1 + 292 270 3466 268 -1 + 293 270 3467 3466 -1 + 294 1254 4050 4047 -1 + 295 3879 3912 3911 -1 + 296 3879 3960 3912 -1 + 297 3012 3013 3036 -1 + 298 3788 3787 3740 -1 + 299 3465 3524 3516 -1 + 300 3516 3517 3465 -1 + 301 586 3320 3319 -1 + 302 364 387 389 -1 + 303 389 388 364 -1 + 304 1621 1148 1222 -1 + 305 3300 3094 4107 -1 + 306 2984 3558 2722 -1 + 307 3617 446 3587 -1 + 308 3587 3619 3617 -1 + 309 450 455 461 -1 + 310 456 461 455 -1 + 311 4227 3223 2799 -1 + 312 2799 3223 2798 -1 + 313 2729 4062 4127 -1 + 314 2729 4103 4062 -1 + 315 3862 3845 3760 -1 + 316 3760 3732 3862 -1 + 317 4126 18 9 -1 + 318 4188 4168 4161 -1 + 319 3125 3122 3123 -1 + 320 2493 2476 2494 -1 + 321 2494 2476 2481 -1 + 322 2938 2949 2915 -1 + 323 2915 2949 2950 -1 + 324 2506 2488 2276 -1 + 325 4232 4124 1401 -1 + 326 4232 1393 4124 -1 + 327 741 1225 819 -1 + 328 819 742 741 -1 + 329 800 799 798 -1 + 330 798 797 800 -1 + 331 1383 1384 1361 -1 + 332 3860 2826 3761 -1 + 333 1309 1333 1327 -1 + 334 2356 2344 2345 -1 + 335 1652 1650 1649 -1 + 336 926 924 942 -1 + 337 4351 1667 1668 -1 + 338 2641 2597 2598 -1 + 339 2598 2615 2641 -1 + 340 2835 2969 2954 -1 + 341 2851 2954 2969 -1 + 342 3710 3708 3711 -1 + 343 3712 3711 3708 -1 + 344 4346 1502 1503 -1 + 345 436 1516 429 -1 + 346 1510 1516 436 -1 + 347 3728 3743 3727 -1 + 348 3728 3799 3743 -1 + 349 2705 3625 1046 -1 + 350 2705 2713 3625 -1 + 351 3079 3120 3084 -1 + 352 3292 3267 3301 -1 + 353 3292 3268 3267 -1 + 354 321 338 319 -1 + 355 321 322 338 -1 + 356 1142 1767 1143 -1 + 357 1142 1756 1767 -1 + 358 2827 3344 2898 -1 + 359 2898 2900 2827 -1 + 360 4074 3184 3289 -1 + 361 3202 3289 3184 -1 + 362 2012 1992 2004 -1 + 363 2004 2008 2012 -1 + 364 4374 645 640 -1 + 365 640 645 3188 -1 + 366 1705 4155 4526 -1 + 367 1378 1382 1380 -1 + 368 1379 1382 1378 -1 + 369 2845 3276 2900 -1 + 370 2900 2885 2845 -1 + 371 3058 3040 4418 -1 + 372 3058 4230 3040 -1 + 373 4349 1415 1129 -1 + 374 1129 1415 1392 -1 + 375 2868 2865 2869 -1 + 376 2868 2860 2865 -1 + 377 3986 3993 3776 -1 + 378 3776 3979 3986 -1 + 379 3471 3090 3472 -1 + 380 235 3809 266 -1 + 381 266 255 235 -1 + 382 3980 3783 3789 -1 + 383 3980 3778 3783 -1 + 384 246 245 248 -1 + 385 248 245 247 -1 + 386 3159 3170 3084 -1 + 387 3169 3084 3170 -1 + 388 2415 2414 2409 -1 + 389 2409 841 2415 -1 + 390 3842 3812 3730 -1 + 391 3876 3730 3812 -1 + 392 262 267 264 -1 + 393 264 267 263 -1 + 394 3970 3983 3798 -1 + 395 3970 3984 3983 -1 + 396 3936 4547 4433 -1 + 397 3864 3847 3867 -1 + 398 3864 3849 3847 -1 + 399 1462 1472 1461 -1 + 400 1467 1472 1462 -1 + 401 380 393 390 -1 + 402 380 392 393 -1 + 403 3729 3800 3795 -1 + 404 3729 3802 3800 -1 + 405 3176 3177 3189 -1 + 406 3176 3343 3177 -1 + 407 529 3505 3493 -1 + 408 3392 3493 3505 -1 + 409 2745 2735 2733 -1 + 410 159 160 162 -1 + 411 162 160 161 -1 + 412 2832 2973 2841 -1 + 413 2841 2842 2832 -1 + 414 358 340 350 -1 + 415 357 340 358 -1 + 416 2604 2602 2462 -1 + 417 2462 2602 2610 -1 + 418 851 852 2759 -1 + 419 837 852 851 -1 + 420 1104 947 941 -1 + 421 941 978 1104 -1 + 422 863 645 641 -1 + 423 863 3187 645 -1 + 424 3592 3627 3591 -1 + 425 2078 2079 2009 -1 + 426 2081 2079 2078 -1 + 427 888 889 886 -1 + 428 888 881 889 -1 + 429 1217 1218 1579 -1 + 430 4623 4313 4571 -1 + 431 3523 3461 3460 -1 + 432 3460 3464 3523 -1 + 433 2939 784 2700 -1 + 434 2700 2938 2939 -1 + 435 2986 2985 2987 -1 + 436 354 2985 2986 -1 + 437 2001 2004 2079 -1 + 438 2006 2079 2004 -1 + 439 3192 2671 2674 -1 + 440 1663 1212 1651 -1 + 441 3054 3049 3008 -1 + 442 3818 251 3817 -1 + 443 258 3817 251 -1 + 444 810 754 811 -1 + 445 806 811 754 -1 + 446 2832 2848 3206 -1 + 447 3206 2831 2832 -1 + 448 2352 1058 1051 -1 + 449 1048 1051 1058 -1 + 450 2604 2606 2603 -1 + 451 2603 2606 2605 -1 + 452 3248 3247 3234 -1 + 453 3234 3247 3196 -1 + 454 1474 1483 1479 -1 + 455 1479 1473 1474 -1 + 456 2613 2637 2593 -1 + 457 2595 2637 2613 -1 + 458 2255 2256 2254 -1 + 459 2254 2259 2255 -1 + 460 2248 2213 2212 -1 + 461 2248 2208 2213 -1 + 462 2687 2685 2693 -1 + 463 2693 2685 2656 -1 + 464 2590 2544 2589 -1 + 465 2589 2544 2540 -1 + 466 832 831 2379 -1 + 467 832 833 831 -1 + 468 1113 1741 1740 -1 + 469 2613 2612 2595 -1 + 470 2595 2612 2608 -1 + 471 243 3770 240 -1 + 472 3977 3770 243 -1 + 473 2343 2321 913 -1 + 474 2343 2401 2321 -1 + 475 119 108 118 -1 + 476 119 110 108 -1 + 477 869 831 835 -1 + 478 870 831 869 -1 + 479 2342 2401 2400 -1 + 480 2321 2401 2342 -1 + 481 383 2874 2883 -1 + 482 2864 2883 2874 -1 + 483 3524 3527 3516 -1 + 484 3524 3526 3527 -1 + 485 2509 2437 2425 -1 + 486 2425 2439 2509 -1 + 487 2810 2747 3230 -1 + 488 3230 3238 2810 -1 + 489 3722 3721 3719 -1 + 490 3719 3691 3722 -1 + 491 2234 2257 2244 -1 + 492 2244 2257 2245 -1 + 493 3087 3139 3144 -1 + 494 2394 2413 2405 -1 + 495 2405 2413 2403 -1 + 496 965 954 952 -1 + 497 2453 2610 2451 -1 + 498 2453 2462 2610 -1 + 499 1065 2586 1063 -1 + 500 2058 2056 2087 -1 + 501 2055 2087 2056 -1 + 502 2274 2263 2275 -1 + 503 2653 1518 4346 -1 + 504 2653 4102 1518 -1 + 505 1167 1169 1308 -1 + 506 1168 1308 1169 -1 + 507 2303 2304 2312 -1 + 508 2290 2312 2304 -1 + 509 1504 1436 1420 -1 + 510 2092 1354 2083 -1 + 511 2083 2052 2092 -1 + 512 2227 2302 2301 -1 + 513 2227 2223 2302 -1 + 514 1330 995 1331 -1 + 515 996 995 1330 -1 + 516 2609 2633 2634 -1 + 517 2628 2633 2609 -1 + 518 790 2906 797 -1 + 519 797 798 790 -1 + 520 3182 3333 3331 -1 + 521 3126 3121 3154 -1 + 522 1362 1384 1364 -1 + 523 1361 1384 1362 -1 + 524 1233 1136 3284 -1 + 525 1234 1136 1233 -1 + 526 934 1091 4206 -1 + 527 3477 3927 3474 -1 + 528 3924 3927 3477 -1 + 529 991 995 4179 -1 + 530 991 4096 995 -1 + 531 1679 1684 1688 -1 + 532 1557 1628 4620 -1 + 533 4362 4255 4026 -1 + 534 3086 3013 3012 -1 + 535 3402 3387 3546 -1 + 536 3386 3546 3387 -1 + 537 644 4498 642 -1 + 538 1895 1921 1896 -1 + 539 1896 1890 1895 -1 + 540 1058 1056 1048 -1 + 541 2343 913 912 -1 + 542 914 912 913 -1 + 543 1034 1032 4439 -1 + 544 2399 2401 2343 -1 + 545 2402 2401 2399 -1 + 546 739 4341 740 -1 + 547 1234 1257 1275 -1 + 548 394 382 396 -1 + 549 396 382 398 -1 + 550 481 480 445 -1 + 551 445 480 487 -1 + 552 2676 2646 2678 -1 + 553 4303 3944 3951 -1 + 554 4303 311 3944 -1 + 555 1506 2681 2680 -1 + 556 2684 2680 2681 -1 + 557 4089 1041 1646 -1 + 558 1057 2028 1056 -1 + 559 1056 2028 1055 -1 + 560 2746 2731 3237 -1 + 561 3237 2731 2764 -1 + 562 1296 1289 1302 -1 + 563 3456 3454 3459 -1 + 564 4582 3936 4433 -1 + 565 2873 3936 4582 -1 + 566 1568 1569 1570 -1 + 567 942 945 943 -1 + 568 942 924 945 -1 + 569 2354 741 748 -1 + 570 1227 741 2354 -1 + 571 4456 2839 2838 -1 + 572 1679 1678 1687 -1 + 573 2330 2338 2337 -1 + 574 2322 2338 2330 -1 + 575 1205 1204 1189 -1 + 576 1205 1221 1204 -1 + 577 3308 3243 3242 -1 + 578 3308 3304 3243 -1 + 579 3891 3889 3892 -1 + 580 3892 3468 3891 -1 + 581 3617 481 446 -1 + 582 445 446 481 -1 + 583 1438 1397 1437 -1 + 584 1438 1396 1397 -1 + 585 4560 4457 1033 -1 + 586 965 1106 1102 -1 + 587 667 722 674 -1 + 588 664 674 722 -1 + 589 88 190 188 -1 + 590 89 190 88 -1 + 591 4208 4083 1042 -1 + 592 166 1191 1192 -1 + 593 769 762 770 -1 + 594 769 779 762 -1 + 595 1005 970 971 -1 + 596 1005 966 970 -1 + 597 4090 3663 3648 -1 + 598 3637 3663 4090 -1 + 599 1277 1251 1236 -1 + 600 1184 1230 1918 -1 + 601 1701 1700 1705 -1 + 602 1619 1604 1229 -1 + 603 1081 1930 1919 -1 + 604 1077 1930 1081 -1 + 605 1137 2894 3283 -1 + 606 2863 3283 2894 -1 + 607 3605 3594 3072 -1 + 608 4486 3330 4119 -1 + 609 3863 3291 3819 -1 + 610 3856 3819 3291 -1 + 611 729 723 730 -1 + 612 730 714 729 -1 + 613 954 965 1099 -1 + 614 4048 1749 4422 -1 + 615 4263 1139 1639 -1 + 616 1154 1251 1153 -1 + 617 2781 2667 4385 -1 + 618 1230 1611 1918 -1 + 619 1230 1609 1611 -1 + 620 4327 4336 4609 -1 + 621 2809 2808 2807 -1 + 622 2740 2808 2809 -1 + 623 3078 2679 3079 -1 + 624 3079 3084 3078 -1 + 625 4479 1638 4255 -1 + 626 3167 1333 3165 -1 + 627 1269 1720 1280 -1 + 628 1699 1025 1027 -1 + 629 981 1025 1699 -1 + 630 4749 4152 4268 -1 + 631 1099 958 954 -1 + 632 1316 1682 4536 -1 + 633 368 3874 257 -1 + 634 257 369 368 -1 + 635 1979 1953 1945 -1 + 636 1979 1980 1953 -1 + 637 2134 2128 2127 -1 + 638 2134 2126 2128 -1 + 639 2572 2573 1063 -1 + 640 3159 3120 3117 -1 + 641 1015 1016 933 -1 + 642 2346 2349 2350 -1 + 643 2350 2363 2346 -1 + 644 1300 1168 1164 -1 + 645 1300 1308 1168 -1 + 646 4552 3112 4382 -1 + 647 1154 1236 1251 -1 + 648 291 513 516 -1 + 649 291 284 513 -1 + 650 3673 3436 3419 -1 + 651 3673 3656 3436 -1 + 652 587 588 583 -1 + 653 583 581 587 -1 + 654 3145 3165 3148 -1 + 655 3166 3150 3020 -1 + 656 1759 1752 4014 -1 + 657 2126 2125 2144 -1 + 658 2145 2144 2125 -1 + 659 1484 3601 1483 -1 + 660 1483 3601 1479 -1 + 661 2422 2465 2420 -1 + 662 2422 2433 2465 -1 + 663 3635 3666 3654 -1 + 664 3702 3666 3635 -1 + 665 2123 2142 2141 -1 + 666 2123 1481 2142 -1 + 667 2555 2561 2560 -1 + 668 2543 2560 2561 -1 + 669 77 140 136 -1 + 670 130 140 77 -1 + 671 1941 1940 1938 -1 + 672 1939 1938 1940 -1 + 673 3981 3996 3973 -1 + 674 3982 3996 3981 -1 + 675 1465 1463 1464 -1 + 676 1465 1460 1463 -1 + 677 48 170 174 -1 + 678 2378 2377 2418 -1 + 679 2418 2377 2410 -1 + 680 666 665 668 -1 + 681 666 2593 665 -1 + 682 106 127 107 -1 + 683 2200 107 127 -1 + 684 745 767 778 -1 + 685 745 765 767 -1 + 686 1093 1122 1105 -1 + 687 914 901 912 -1 + 688 914 911 901 -1 + 689 1922 1902 1896 -1 + 690 1896 1902 1899 -1 + 691 1888 1908 1887 -1 + 692 1887 221 1888 -1 + 693 586 3553 3320 -1 + 694 3021 3142 3026 -1 + 695 2022 50 12 -1 + 696 4730 2993 3894 -1 + 697 479 463 483 -1 + 698 479 1485 463 -1 + 699 240 3770 246 -1 + 700 246 248 240 -1 + 701 3754 3755 3744 -1 + 702 3752 3744 3755 -1 + 703 3363 3361 3364 -1 + 704 3357 3364 3361 -1 + 705 2513 2516 2529 -1 + 706 2529 60 2513 -1 + 707 3435 3419 3436 -1 + 708 3423 3419 3435 -1 + 709 1845 2498 1846 -1 + 710 2176 1843 2172 -1 + 711 2172 1843 2132 -1 + 712 2424 2421 2426 -1 + 713 2420 2421 2424 -1 + 714 3664 3656 3673 -1 + 715 3657 3656 3664 -1 + 716 7 11 3 -1 + 717 2086 3 11 -1 + 718 2503 2484 2501 -1 + 719 2501 2484 2489 -1 + 720 2523 2481 2476 -1 + 721 2482 2481 2523 -1 + 722 3578 3694 3579 -1 + 723 3578 416 3694 -1 + 724 435 416 399 -1 + 725 3694 416 435 -1 + 726 2423 2446 2422 -1 + 727 2422 2446 2448 -1 + 728 1540 1538 1530 -1 + 729 1530 1538 1533 -1 + 730 400 1534 1524 -1 + 731 1544 1524 1534 -1 + 732 2952 2966 2834 -1 + 733 2974 2834 2966 -1 + 734 47 49 20 -1 + 735 47 505 49 -1 + 736 2133 403 2129 -1 + 737 1855 1854 2500 -1 + 738 1855 1849 1854 -1 + 739 2514 2532 2530 -1 + 740 2514 2533 2532 -1 + 741 568 553 543 -1 + 742 568 3660 553 -1 + 743 358 350 2951 -1 + 744 2951 350 2953 -1 + 745 256 250 253 -1 + 746 252 253 250 -1 + 747 3228 2738 3232 -1 + 748 1465 1464 417 -1 + 749 410 417 1464 -1 + 750 3671 3639 3672 -1 + 751 3672 3642 3671 -1 + 752 238 3766 232 -1 + 753 232 239 238 -1 + 754 3148 3166 3149 -1 + 755 423 3682 3704 -1 + 756 3631 3704 3682 -1 + 757 3459 4150 3456 -1 + 758 2901 2697 2930 -1 + 759 3534 3493 3531 -1 + 760 3534 3492 3493 -1 + 761 3660 555 3658 -1 + 762 3660 568 555 -1 + 763 473 474 475 -1 + 764 471 474 473 -1 + 765 3425 3382 3381 -1 + 766 3415 3382 3425 -1 + 767 3704 3631 3630 -1 + 768 3649 3630 3631 -1 + 769 3036 3147 3031 -1 + 770 3397 3392 3391 -1 + 771 3505 3391 3392 -1 + 772 3668 3652 3649 -1 + 773 3649 3652 3630 -1 + 774 568 543 558 -1 + 775 570 558 543 -1 + 776 3696 3631 3698 -1 + 777 3682 3698 3631 -1 + 778 3851 3848 3751 -1 + 779 3749 3751 3848 -1 + 780 3710 3711 3709 -1 + 781 3709 3691 3710 -1 + 782 3707 3579 3695 -1 + 783 3694 3695 3579 -1 + 784 3720 3721 3677 -1 + 785 3720 3719 3721 -1 + 786 4368 4139 2971 -1 + 787 3678 3676 3697 -1 + 788 3678 3677 3676 -1 + 789 1044 1223 1629 -1 + 790 422 3683 3682 -1 + 791 3682 3683 3698 -1 + 792 3798 3971 3970 -1 + 793 3798 3987 3971 -1 + 794 419 3702 3635 -1 + 795 419 547 3702 -1 + 796 3866 3849 3864 -1 + 797 3866 3871 3849 -1 + 798 3975 3978 3999 -1 + 799 3713 419 3712 -1 + 800 418 419 3713 -1 + 801 3853 3818 3810 -1 + 802 3853 251 3818 -1 + 803 1531 1521 1522 -1 + 804 1531 1532 1521 -1 + 805 3028 3135 3027 -1 + 806 3720 3693 3719 -1 + 807 3719 3693 3692 -1 + 808 3668 3632 3643 -1 + 809 3668 3649 3632 -1 + 810 3751 3748 3750 -1 + 811 3750 3747 3751 -1 + 812 547 546 553 -1 + 813 553 3702 547 -1 + 814 423 3704 545 -1 + 815 545 546 423 -1 + 816 3035 3033 3009 -1 + 817 2174 1539 2175 -1 + 818 2175 2157 2174 -1 + 819 435 424 418 -1 + 820 418 3713 435 -1 + 821 3718 3722 3691 -1 + 822 3691 3709 3718 -1 + 823 1358 1356 1357 -1 + 824 1350 1357 1356 -1 + 825 3641 3676 3677 -1 + 826 3677 3721 3641 -1 + 827 3721 3671 3641 -1 + 828 3721 3722 3671 -1 + 829 3641 3643 3676 -1 + 830 3632 3676 3643 -1 + 831 4019 3583 3582 -1 + 832 3582 3083 4019 -1 + 833 3540 3542 3547 -1 + 834 3547 3550 3540 -1 + 835 3034 3087 3144 -1 + 836 3695 3713 3712 -1 + 837 3712 3708 3695 -1 + 838 3798 3799 3728 -1 + 839 3798 3983 3799 -1 + 840 3542 3707 3565 -1 + 841 3565 3547 3542 -1 + 842 3683 3569 3687 -1 + 843 3683 415 3569 -1 + 844 2981 2963 2961 -1 + 845 2977 2963 2981 -1 + 846 3635 3654 3711 -1 + 847 3711 3654 3709 -1 + 848 1544 1522 1524 -1 + 849 1544 1531 1522 -1 + 850 723 732 721 -1 + 851 721 730 723 -1 + 852 3989 3736 3745 -1 + 853 247 3736 3989 -1 + 854 1424 1349 1434 -1 + 855 1329 1327 1333 -1 + 856 1333 1332 1329 -1 + 857 399 401 402 -1 + 858 399 416 401 -1 + 859 4252 980 1255 -1 + 860 3832 3818 3817 -1 + 861 3832 3828 3818 -1 + 862 3687 3569 3571 -1 + 863 3571 3568 3687 -1 + 864 317 2955 353 -1 + 865 317 2959 2955 -1 + 866 301 303 302 -1 + 867 3569 3906 3571 -1 + 868 402 3906 3569 -1 + 869 415 402 3569 -1 + 870 399 402 415 -1 + 871 2816 4077 300 -1 + 872 3541 3542 3540 -1 + 873 3541 3543 3542 -1 + 874 1513 1502 4365 -1 + 875 1513 1519 1502 -1 + 876 3903 3577 3573 -1 + 877 3990 3957 244 -1 + 878 3998 3957 3990 -1 + 879 253 263 267 -1 + 880 265 263 253 -1 + 881 523 490 494 -1 + 882 523 509 490 -1 + 883 116 104 409 -1 + 884 3047 3143 3046 -1 + 885 236 3851 3737 -1 + 886 3737 230 236 -1 + 887 1348 2089 1354 -1 + 888 264 239 232 -1 + 889 264 237 239 -1 + 890 3398 3508 3390 -1 + 891 3398 3395 3508 -1 + 892 3847 3848 3865 -1 + 893 3850 3848 3847 -1 + 894 2712 2665 2670 -1 + 895 2712 2715 2665 -1 + 896 3816 3814 3824 -1 + 897 3871 3814 3816 -1 + 898 2933 2949 2691 -1 + 899 2701 2691 2949 -1 + 900 3651 570 545 -1 + 901 543 545 570 -1 + 902 3651 3704 3630 -1 + 903 3651 545 3704 -1 + 904 3046 3022 4075 -1 + 905 230 231 234 -1 + 906 3737 231 230 -1 + 907 3564 3720 3570 -1 + 908 3564 3693 3720 -1 + 909 502 1439 503 -1 + 910 502 1437 1439 -1 + 911 3747 3736 3737 -1 + 912 231 3737 3736 -1 + 913 3564 3550 3693 -1 + 914 3693 3550 3547 -1 + 915 3742 3983 3748 -1 + 916 3799 3983 3742 -1 + 917 3728 3987 3798 -1 + 918 3728 3792 3987 -1 + 919 2981 2961 2983 -1 + 920 2983 2961 2964 -1 + 921 3969 3971 3982 -1 + 922 3996 3982 3971 -1 + 923 3990 3989 3772 -1 + 924 242 3989 3990 -1 + 925 2922 2924 2947 -1 + 926 2947 2924 2944 -1 + 927 3435 3436 3416 -1 + 928 3416 3436 3412 -1 + 929 562 554 1358 -1 + 930 563 554 562 -1 + 931 2297 2309 2300 -1 + 932 637 2300 2309 -1 + 933 262 233 235 -1 + 934 3859 235 233 -1 + 935 2820 2825 2823 -1 + 936 3451 3603 3597 -1 + 937 236 261 3865 -1 + 938 236 237 261 -1 + 939 3576 3567 3568 -1 + 940 3568 3571 3576 -1 + 941 401 3906 402 -1 + 942 1523 1524 1522 -1 + 943 1523 1542 1524 -1 + 944 1425 1424 1434 -1 + 945 1425 1421 1424 -1 + 946 236 230 237 -1 + 947 237 230 239 -1 + 948 3745 3750 3746 -1 + 949 3984 3746 3750 -1 + 950 4252 979 980 -1 + 951 982 980 979 -1 + 952 355 791 341 -1 + 953 354 791 355 -1 + 954 1541 1532 1537 -1 + 955 1541 1521 1532 -1 + 956 2835 2824 2825 -1 + 957 2825 2969 2835 -1 + 958 498 1433 1426 -1 + 959 1435 1426 1433 -1 + 960 3742 3754 3744 -1 + 961 3742 3749 3754 -1 + 962 3413 3382 3415 -1 + 963 3415 3412 3413 -1 + 964 4291 3130 3115 -1 + 965 3115 3130 3128 -1 + 966 3668 3643 3669 -1 + 967 3642 3669 3643 -1 + 968 2936 2927 2925 -1 + 969 2936 2944 2927 -1 + 970 519 520 285 -1 + 971 285 520 512 -1 + 972 4633 4409 3634 -1 + 973 3359 3357 3396 -1 + 974 3396 3357 3361 -1 + 975 3652 3444 3420 -1 + 976 3421 3444 3652 -1 + 977 3754 3872 3755 -1 + 978 3754 3850 3872 -1 + 979 3373 560 3443 -1 + 980 3443 3442 3373 -1 + 981 3841 3871 3866 -1 + 982 3814 3871 3841 -1 + 983 3374 3373 3442 -1 + 984 3442 3432 3374 -1 + 985 265 3840 3867 -1 + 986 3864 3867 3840 -1 + 987 3835 3866 3836 -1 + 988 3835 3841 3866 -1 + 989 3766 3765 3845 -1 + 990 3766 238 3765 -1 + 991 3360 3358 3374 -1 + 992 3374 3358 3367 -1 + 993 475 1464 1463 -1 + 994 474 1464 475 -1 + 995 3394 3385 3512 -1 + 996 3512 3385 3513 -1 + 997 1395 1394 1418 -1 + 998 2183 2187 2178 -1 + 999 2178 2180 2183 -1 + 1000 494 492 506 -1 + 1001 494 490 492 -1 + 1002 2672 2673 2654 -1 + 1003 2654 2673 756 -1 + 1004 804 2939 2921 -1 + 1005 2921 2939 2940 -1 + 1006 3367 506 3373 -1 + 1007 3373 3374 3367 -1 + 1008 3836 3840 3839 -1 + 1009 252 3839 3840 -1 + 1010 3399 3377 3385 -1 + 1011 3384 3385 3377 -1 + 1012 2202 106 107 -1 + 1013 2202 135 106 -1 + 1014 2640 2635 2632 -1 + 1015 2626 2635 2640 -1 + 1016 1450 1446 1449 -1 + 1017 1449 1447 1450 -1 + 1018 3792 3727 3777 -1 + 1019 3728 3727 3792 -1 + 1020 3034 3144 3033 -1 + 1021 3697 3632 3696 -1 + 1022 3697 3676 3632 -1 + 1023 1450 1447 1469 -1 + 1024 1448 1469 1447 -1 + 1025 688 662 660 -1 + 1026 660 683 688 -1 + 1027 3664 3667 3657 -1 + 1028 3653 3657 3667 -1 + 1029 3412 3426 3416 -1 + 1030 3415 3426 3412 -1 + 1031 412 406 400 -1 + 1032 412 75 406 -1 + 1033 3407 3653 3658 -1 + 1034 3657 3653 3407 -1 + 1035 1867 1859 1870 -1 + 1036 1863 1870 1859 -1 + 1037 102 119 118 -1 + 1038 118 123 102 -1 + 1039 2468 2421 2469 -1 + 1040 2468 2467 2421 -1 + 1041 1531 1544 1535 -1 + 1042 1535 1533 1531 -1 + 1043 412 400 1542 -1 + 1044 1542 400 1524 -1 + 1045 2165 2166 2131 -1 + 1046 2131 1844 2165 -1 + 1047 2153 2174 2156 -1 + 1048 2153 2150 2174 -1 + 1049 124 123 121 -1 + 1050 121 1810 124 -1 + 1051 3670 3423 3424 -1 + 1052 3419 3423 3670 -1 + 1053 3511 3515 3516 -1 + 1054 3516 3527 3511 -1 + 1055 2115 152 2116 -1 + 1056 156 152 2115 -1 + 1057 1799 1798 1865 -1 + 1058 1865 1797 1799 -1 + 1059 1341 2080 2088 -1 + 1060 2076 2088 2080 -1 + 1061 508 525 491 -1 + 1062 508 509 525 -1 + 1063 3641 3642 3643 -1 + 1064 3671 3642 3641 -1 + 1065 4641 3089 4575 -1 + 1066 1477 2100 1482 -1 + 1067 1477 1478 2100 -1 + 1068 1537 1520 1541 -1 + 1069 1537 2150 1520 -1 + 1070 3745 3747 3750 -1 + 1071 3745 3736 3747 -1 + 1072 1867 1868 1856 -1 + 1073 1788 1856 1868 -1 + 1074 478 485 1454 -1 + 1075 478 482 485 -1 + 1076 1492 1486 1493 -1 + 1077 1492 1495 1486 -1 + 1078 1450 1459 1446 -1 + 1079 1470 1446 1459 -1 + 1080 1453 1487 1486 -1 + 1081 1486 1491 1453 -1 + 1082 491 3366 3425 -1 + 1083 3378 3425 3366 -1 + 1084 1465 1466 1527 -1 + 1085 417 1466 1465 -1 + 1086 1786 1826 1828 -1 + 1087 1827 1826 1786 -1 + 1088 2204 2194 2192 -1 + 1089 2204 2191 2194 -1 + 1090 483 463 449 -1 + 1091 449 443 483 -1 + 1092 3384 3366 3383 -1 + 1093 3377 3366 3384 -1 + 1094 2161 2194 2198 -1 + 1095 2205 2198 2194 -1 + 1096 2178 2186 2179 -1 + 1097 2188 2186 2178 -1 + 1098 3702 3660 3666 -1 + 1099 3702 553 3660 -1 + 1100 2657 2669 2650 -1 + 1101 2657 2649 2669 -1 + 1102 3033 3028 3027 -1 + 1103 14 15 45 -1 + 1104 45 15 2107 -1 + 1105 308 280 277 -1 + 1106 308 275 280 -1 + 1107 1394 1393 4589 -1 + 1108 2116 2185 2188 -1 + 1109 2188 2185 2186 -1 + 1110 457 1473 460 -1 + 1111 457 1474 1473 -1 + 1112 173 169 170 -1 + 1113 170 48 173 -1 + 1114 3585 3600 3590 -1 + 1115 3589 3590 3600 -1 + 1116 3158 3102 4243 -1 + 1117 288 281 282 -1 + 1118 282 286 288 -1 + 1119 4287 1411 1418 -1 + 1120 1418 1411 1414 -1 + 1121 3869 3958 4028 -1 + 1122 3869 3764 3958 -1 + 1123 2870 2861 2859 -1 + 1124 2870 2893 2861 -1 + 1125 1476 1495 1475 -1 + 1126 1492 1475 1495 -1 + 1127 283 289 272 -1 + 1128 290 289 283 -1 + 1129 2858 2857 2892 -1 + 1130 2892 2902 2858 -1 + 1131 1450 1469 1472 -1 + 1132 1472 1459 1450 -1 + 1133 123 118 132 -1 + 1134 132 121 123 -1 + 1135 308 309 305 -1 + 1136 308 312 309 -1 + 1137 512 513 284 -1 + 1138 284 285 512 -1 + 1139 3404 493 3367 -1 + 1140 3367 3358 3404 -1 + 1141 372 371 373 -1 + 1142 372 361 371 -1 + 1143 2147 55 2128 -1 + 1144 2128 2138 2147 -1 + 1145 3586 3601 3585 -1 + 1146 3600 3585 3601 -1 + 1147 3496 3494 3495 -1 + 1148 3509 3495 3494 -1 + 1149 365 3959 3960 -1 + 1150 3912 3960 3959 -1 + 1151 420 1516 1369 -1 + 1152 1371 1369 1516 -1 + 1153 464 469 467 -1 + 1154 3553 469 464 -1 + 1155 921 920 904 -1 + 1156 919 904 920 -1 + 1157 1481 2123 1480 -1 + 1158 1480 2123 3589 -1 + 1159 3920 3915 3919 -1 + 1160 3919 3918 3920 -1 + 1161 4492 4350 315 -1 + 1162 366 361 372 -1 + 1163 366 362 361 -1 + 1164 1496 1485 1491 -1 + 1165 463 1485 1496 -1 + 1166 1326 1309 1327 -1 + 1167 1288 1309 1326 -1 + 1168 2706 2722 3558 -1 + 1169 293 279 3934 -1 + 1170 293 292 279 -1 + 1171 3229 3256 3236 -1 + 1172 3236 3256 2767 -1 + 1173 3946 3945 398 -1 + 1174 3946 3915 3945 -1 + 1175 305 275 308 -1 + 1176 305 307 275 -1 + 1177 1454 486 1456 -1 + 1178 485 486 1454 -1 + 1179 2102 2099 2111 -1 + 1180 2101 2099 2102 -1 + 1181 3481 3460 3480 -1 + 1182 3481 3475 3460 -1 + 1183 3479 3448 3447 -1 + 1184 3489 3447 3448 -1 + 1185 3935 398 382 -1 + 1186 3946 398 3935 -1 + 1187 372 373 359 -1 + 1188 374 359 373 -1 + 1189 1635 1641 1642 -1 + 1190 3017 3018 3020 -1 + 1191 3968 3918 3954 -1 + 1192 3968 3920 3918 -1 + 1193 383 384 379 -1 + 1194 379 2874 383 -1 + 1195 3947 3953 3918 -1 + 1196 3918 3919 3947 -1 + 1197 2913 3196 2912 -1 + 1198 3247 2912 3196 -1 + 1199 378 396 397 -1 + 1200 378 388 396 -1 + 1201 491 3383 3366 -1 + 1202 491 525 3383 -1 + 1203 288 3466 281 -1 + 1204 3517 281 3466 -1 + 1205 3962 3964 3796 -1 + 1206 3963 3964 3962 -1 + 1207 3857 3724 3723 -1 + 1208 3857 3734 3724 -1 + 1209 2742 2766 2743 -1 + 1210 2743 2766 2732 -1 + 1211 378 3902 366 -1 + 1212 366 360 378 -1 + 1213 887 755 773 -1 + 1214 887 780 755 -1 + 1215 3912 3959 3780 -1 + 1216 3780 3779 3912 -1 + 1217 3404 530 521 -1 + 1218 3404 3506 530 -1 + 1219 2260 2237 2261 -1 + 1220 2212 2261 2237 -1 + 1221 174 150 157 -1 + 1222 3378 3377 3376 -1 + 1223 3378 3366 3377 -1 + 1224 493 494 506 -1 + 1225 506 3367 493 -1 + 1226 3445 3457 3458 -1 + 1227 290 3457 3445 -1 + 1228 4395 4275 1263 -1 + 1229 274 291 272 -1 + 1230 272 291 283 -1 + 1231 3492 529 3493 -1 + 1232 3492 511 529 -1 + 1233 3525 3495 3509 -1 + 1234 3509 3507 3525 -1 + 1235 3020 3150 3021 -1 + 1236 3753 3752 3815 -1 + 1237 3755 3815 3752 -1 + 1238 388 360 364 -1 + 1239 388 378 360 -1 + 1240 3429 3416 3430 -1 + 1241 3426 3430 3416 -1 + 1242 2999 3087 3010 -1 + 1243 3034 3010 3087 -1 + 1244 2009 2079 2006 -1 + 1245 2006 167 2009 -1 + 1246 2806 2775 2744 -1 + 1247 917 2744 2775 -1 + 1248 4741 4571 4737 -1 + 1249 365 363 376 -1 + 1250 376 3959 365 -1 + 1251 4674 3450 4508 -1 + 1252 519 515 520 -1 + 1253 519 3498 515 -1 + 1254 3364 3431 3429 -1 + 1255 3364 3434 3431 -1 + 1256 3429 3435 3416 -1 + 1257 3429 3431 3435 -1 + 1258 3362 3430 3376 -1 + 1259 3362 3363 3430 -1 + 1260 571 570 537 -1 + 1261 558 570 571 -1 + 1262 1488 1458 2184 -1 + 1263 1488 1452 1458 -1 + 1264 3869 3264 3786 -1 + 1265 3869 3868 3264 -1 + 1266 492 560 506 -1 + 1267 3373 506 560 -1 + 1268 3513 3384 514 -1 + 1269 3513 3385 3384 -1 + 1270 510 509 523 -1 + 1271 510 525 509 -1 + 1272 1490 1457 1452 -1 + 1273 1490 1455 1457 -1 + 1274 3634 3633 4633 -1 + 1275 3634 3101 3633 -1 + 1276 1453 1491 1490 -1 + 1277 1485 1490 1491 -1 + 1278 1520 1526 1541 -1 + 1279 1541 1526 1528 -1 + 1280 3489 3458 3447 -1 + 1281 3489 3445 3458 -1 + 1282 3397 3396 3390 -1 + 1283 3391 3396 3397 -1 + 1284 1496 448 463 -1 + 1285 463 448 449 -1 + 1286 1496 1491 1495 -1 + 1287 1486 1495 1491 -1 + 1288 3509 3508 3510 -1 + 1289 3509 3494 3508 -1 + 1290 3407 3658 507 -1 + 1291 555 507 3658 -1 + 1292 3531 3392 3496 -1 + 1293 3493 3392 3531 -1 + 1294 3419 3672 3673 -1 + 1295 3419 3670 3672 -1 + 1296 3014 3027 3036 -1 + 1297 3359 3360 3357 -1 + 1298 3359 3358 3360 -1 + 1299 3397 3390 3494 -1 + 1300 3494 3390 3508 -1 + 1301 3506 3505 530 -1 + 1302 3506 3391 3505 -1 + 1303 514 515 3498 -1 + 1304 3498 3513 514 -1 + 1305 530 529 526 -1 + 1306 530 3505 529 -1 + 1307 3497 3512 3513 -1 + 1308 3513 3498 3497 -1 + 1309 859 836 868 -1 + 1310 857 836 859 -1 + 1311 3963 3803 3902 -1 + 1312 3963 3962 3803 -1 + 1313 3521 3458 3535 -1 + 1314 3535 3458 3457 -1 + 1315 2710 2712 2711 -1 + 1316 2710 2714 2712 -1 + 1317 1452 1453 1490 -1 + 1318 1452 1488 1453 -1 + 1319 1476 1474 457 -1 + 1320 1476 1475 1474 -1 + 1321 517 3492 3534 -1 + 1322 517 516 3492 -1 + 1323 3462 3464 3463 -1 + 1324 3462 3523 3464 -1 + 1325 3497 3515 3511 -1 + 1326 518 3515 3497 -1 + 1327 2893 3935 385 -1 + 1328 2893 2870 3935 -1 + 1329 338 330 335 -1 + 1330 336 330 338 -1 + 1331 3911 3952 3879 -1 + 1332 3911 3909 3952 -1 + 1333 4012 4245 2871 -1 + 1334 1618 1546 4182 -1 + 1335 2818 4077 2816 -1 + 1336 3434 3433 3431 -1 + 1337 3432 3433 3434 -1 + 1338 251 3735 370 -1 + 1339 251 3853 3735 -1 + 1340 3962 3796 3800 -1 + 1341 3800 3796 3793 -1 + 1342 755 780 805 -1 + 1343 805 780 2668 -1 + 1344 3843 3827 3844 -1 + 1345 3843 3826 3827 -1 + 1346 526 521 530 -1 + 1347 526 522 521 -1 + 1348 921 904 905 -1 + 1349 905 915 921 -1 + 1350 3758 3802 3757 -1 + 1351 3757 3802 3729 -1 + 1352 3814 3837 3811 -1 + 1353 3814 3841 3837 -1 + 1354 3836 3866 3864 -1 + 1355 3864 3840 3836 -1 + 1356 3836 3839 3877 -1 + 1357 3877 3835 3836 -1 + 1358 758 759 776 -1 + 1359 776 759 756 -1 + 1360 4133 1561 1560 -1 + 1361 3796 3964 3920 -1 + 1362 3920 3968 3796 -1 + 1363 3531 3496 3533 -1 + 1364 3533 3496 3495 -1 + 1365 4537 4239 1561 -1 + 1366 3576 3571 3903 -1 + 1367 3903 3571 3906 -1 + 1368 3696 3698 3675 -1 + 1369 3675 3697 3696 -1 + 1370 4720 3852 4713 -1 + 1371 4733 4718 4052 -1 + 1372 3668 3669 3421 -1 + 1373 3421 3652 3668 -1 + 1374 4219 3988 4645 -1 + 1375 1215 1558 4231 -1 + 1376 4421 1255 4407 -1 + 1377 4185 1255 4421 -1 + 1378 3045 3133 3132 -1 + 1379 3045 3044 3133 -1 + 1380 3558 2984 2993 -1 + 1381 1775 1416 1767 -1 + 1382 1775 1418 1416 -1 + 1383 3010 3074 2998 -1 + 1384 3010 3035 3074 -1 + 1385 3162 3135 3028 -1 + 1386 175 459 4141 -1 + 1387 3635 3711 3712 -1 + 1388 3712 419 3635 -1 + 1389 2998 2999 3010 -1 + 1390 2998 3025 2999 -1 + 1391 3137 3141 3140 -1 + 1392 3140 3141 3139 -1 + 1393 3146 3135 3162 -1 + 1394 3861 3856 3301 -1 + 1395 3819 3856 3861 -1 + 1396 4047 4185 4114 -1 + 1397 2109 2103 2143 -1 + 1398 2143 2142 2109 -1 + 1399 3087 3140 3139 -1 + 1400 4569 1069 1617 -1 + 1401 3538 3572 3545 -1 + 1402 3872 3850 3849 -1 + 1403 3847 3849 3850 -1 + 1404 4250 2682 3131 -1 + 1405 3131 3120 4250 -1 + 1406 3026 3142 3137 -1 + 1407 3333 3334 3331 -1 + 1408 3352 3331 3334 -1 + 1409 3650 3104 3647 -1 + 1410 1106 965 952 -1 + 1411 1115 4197 1110 -1 + 1412 4325 4195 4092 -1 + 1413 571 537 560 -1 + 1414 560 537 3443 -1 + 1415 368 361 3785 -1 + 1416 371 361 368 -1 + 1417 3875 3757 3876 -1 + 1418 3801 3757 3875 -1 + 1419 4439 1031 1035 -1 + 1420 820 824 2280 -1 + 1421 2280 1198 820 -1 + 1422 1188 1204 2135 -1 + 1423 1189 1204 1188 -1 + 1424 4311 1589 4171 -1 + 1425 4087 229 441 -1 + 1426 4381 4360 3933 -1 + 1427 3365 3368 3499 -1 + 1428 3023 3022 3039 -1 + 1429 3039 3022 3037 -1 + 1430 4642 1768 4621 -1 + 1431 3014 3011 3009 -1 + 1432 3015 3011 3014 -1 + 1433 2348 2357 4034 -1 + 1434 2348 2347 2357 -1 + 1435 4489 2567 4412 -1 + 1436 612 2567 4489 -1 + 1437 434 4068 405 -1 + 1438 4746 4732 2280 -1 + 1439 2280 4732 4721 -1 + 1440 4763 4116 4744 -1 + 1441 3088 3090 3473 -1 + 1442 1305 1306 4608 -1 + 1443 1305 4061 1306 -1 + 1444 2747 3083 3538 -1 + 1445 3368 3402 3499 -1 + 1446 2736 4348 2800 -1 + 1447 3040 3038 4418 -1 + 1448 3040 4237 3038 -1 + 1449 3689 3650 3648 -1 + 1450 4436 4274 4286 -1 + 1451 4234 4274 4436 -1 + 1452 1165 1168 4288 -1 + 1453 4263 4129 1139 -1 + 1454 4028 3860 3869 -1 + 1455 3288 1242 3124 -1 + 1456 3288 1241 1242 -1 + 1457 4192 1830 2499 -1 + 1458 2499 1830 2497 -1 + 1459 2148 2103 2106 -1 + 1460 2106 2105 2148 -1 + 1461 4732 4489 4412 -1 + 1462 4732 4428 4489 -1 + 1463 3375 3370 3369 -1 + 1464 3488 3474 3950 -1 + 1465 3950 3934 3488 -1 + 1466 2682 2783 3092 -1 + 1467 4256 3904 3577 -1 + 1468 4256 3906 3904 -1 + 1469 4002 3303 3338 -1 + 1470 2679 2682 3079 -1 + 1471 3519 3518 2805 -1 + 1472 2805 3518 3530 -1 + 1473 2781 3077 2786 -1 + 1474 3057 4235 3042 -1 + 1475 4261 4251 1264 -1 + 1476 1264 1029 4261 -1 + 1477 4739 1180 4717 -1 + 1478 4686 4717 1180 -1 + 1479 4026 1139 1148 -1 + 1480 3077 3080 2786 -1 + 1481 3259 3245 3244 -1 + 1482 3244 3245 3231 -1 + 1483 3159 3084 3120 -1 + 1484 4307 4092 4386 -1 + 1485 1029 4029 4261 -1 + 1486 4729 1020 4714 -1 + 1487 4085 4714 1020 -1 + 1488 3401 3403 3502 -1 + 1489 3502 3514 3401 -1 + 1490 4181 3774 3102 -1 + 1491 4294 3931 3894 -1 + 1492 3329 3332 3887 -1 + 1493 3223 2794 3153 -1 + 1494 3225 3257 2762 -1 + 1495 1993 2033 1517 -1 + 1496 1517 2044 1993 -1 + 1497 4479 4024 1638 -1 + 1498 3583 3580 3584 -1 + 1499 4175 4158 3530 -1 + 1500 4408 3623 4401 -1 + 1501 3598 3623 4408 -1 + 1502 3659 3106 3661 -1 + 1503 3659 3536 3106 -1 + 1504 3583 3581 3582 -1 + 1505 3538 3083 3572 -1 + 1506 3582 3572 3083 -1 + 1507 3029 3019 4333 -1 + 1508 3662 3472 3090 -1 + 1509 3633 3472 3662 -1 + 1510 3024 3003 4037 -1 + 1511 3024 4006 3003 -1 + 1512 4558 4400 149 -1 + 1513 4469 4660 4589 -1 + 1514 4113 1571 1567 -1 + 1515 1155 1252 4113 -1 + 1516 4496 4110 1557 -1 + 1517 4724 184 4664 -1 + 1518 825 184 4724 -1 + 1519 4682 4388 3820 -1 + 1520 3593 3623 3596 -1 + 1521 3620 3608 3593 -1 + 1522 3175 3715 3554 -1 + 1523 2783 3077 2781 -1 + 1524 3624 3628 3609 -1 + 1525 3614 3615 3629 -1 + 1526 3629 3615 3501 -1 + 1527 3408 3405 3406 -1 + 1528 4038 2886 4183 -1 + 1529 3402 3401 3499 -1 + 1530 3499 3401 3491 -1 + 1531 185 825 4724 -1 + 1532 2282 4120 2504 -1 + 1533 4157 1266 1141 -1 + 1534 1171 1141 1266 -1 + 1535 4470 4236 3608 -1 + 1536 4480 4424 1185 -1 + 1537 1185 1196 4480 -1 + 1538 4622 2895 2919 -1 + 1539 992 991 4379 -1 + 1540 992 4096 991 -1 + 1541 3522 3526 3462 -1 + 1542 3524 3462 3526 -1 + 1543 4393 1725 1735 -1 + 1544 3375 3428 3427 -1 + 1545 3427 3379 3375 -1 + 1546 2357 2356 4034 -1 + 1547 2652 2653 4346 -1 + 1548 3663 3688 3665 -1 + 1549 3633 3101 3472 -1 + 1550 1156 1157 1158 -1 + 1551 1156 1302 1157 -1 + 1552 3091 3717 3131 -1 + 1553 18 4104 16 -1 + 1554 3094 3093 3645 -1 + 1555 3484 3482 3483 -1 + 1556 3472 3483 3482 -1 + 1557 4093 1618 1620 -1 + 1558 3370 3372 3624 -1 + 1559 3370 3371 3372 -1 + 1560 4505 1566 1607 -1 + 1561 1842 1830 4192 -1 + 1562 1842 2500 1830 -1 + 1563 3073 2804 3606 -1 + 1564 3604 3606 2804 -1 + 1565 3162 3139 3163 -1 + 1566 3162 3144 3139 -1 + 1567 4090 3105 3637 -1 + 1568 3665 3648 3663 -1 + 1569 3585 3590 3619 -1 + 1570 3619 3587 3585 -1 + 1571 3878 3278 3781 -1 + 1572 2929 2930 2698 -1 + 1573 2697 2698 2930 -1 + 1574 4157 1142 1143 -1 + 1575 4138 4184 1033 -1 + 1576 3400 3546 3545 -1 + 1577 3625 1045 1046 -1 + 1578 4190 1766 1733 -1 + 1579 3437 3409 4036 -1 + 1580 4393 1735 1121 -1 + 1581 3623 2804 3588 -1 + 1582 4597 4494 4506 -1 + 1583 3372 3371 3389 -1 + 1584 4381 4022 3125 -1 + 1585 1031 4138 1036 -1 + 1586 4250 3079 2682 -1 + 1587 3120 3079 4250 -1 + 1588 3122 3159 3117 -1 + 1589 3690 3473 3405 -1 + 1590 4181 3956 3774 -1 + 1591 2622 2631 2630 -1 + 1592 2622 2628 2631 -1 + 1593 3700 3699 3706 -1 + 1594 3706 3701 3700 -1 + 1595 4175 3529 3456 -1 + 1596 4181 3158 3881 -1 + 1597 3273 3881 3158 -1 + 1598 4405 4291 3715 -1 + 1599 3088 3662 3090 -1 + 1600 4664 184 822 -1 + 1601 4590 4301 4568 -1 + 1602 3714 3681 3716 -1 + 1603 3405 3556 3686 -1 + 1604 1764 1763 1760 -1 + 1605 3066 3015 3045 -1 + 1606 3086 3045 3015 -1 + 1607 3056 4148 3134 -1 + 1608 4265 979 4252 -1 + 1609 1684 1686 1685 -1 + 1610 1934 1892 1933 -1 + 1611 3473 3406 3405 -1 + 1612 3088 3684 3662 -1 + 1613 3088 3473 3684 -1 + 1614 3690 3684 3473 -1 + 1615 3689 3685 3680 -1 + 1616 3648 3665 3689 -1 + 1617 3689 3680 3679 -1 + 1618 4334 2288 2284 -1 + 1619 2284 2288 2286 -1 + 1620 1637 1634 1635 -1 + 1621 3554 3716 3555 -1 + 1622 1198 1187 822 -1 + 1623 3106 3111 3107 -1 + 1624 4720 3860 4699 -1 + 1625 928 1015 1010 -1 + 1626 2999 3026 3140 -1 + 1627 3140 3087 2999 -1 + 1628 3006 4067 4387 -1 + 1629 3006 4118 4067 -1 + 1630 4256 3903 3906 -1 + 1631 3577 3903 4256 -1 + 1632 4719 4499 4571 -1 + 1633 1144 3174 1175 -1 + 1634 1175 1254 1144 -1 + 1635 4175 3456 4158 -1 + 1636 4150 4158 3456 -1 + 1637 3461 3469 3480 -1 + 1638 3480 3460 3461 -1 + 1639 4637 4506 3937 -1 + 1640 3124 3170 3159 -1 + 1641 4140 4101 3705 -1 + 1642 3705 3646 4140 -1 + 1643 2860 2902 2865 -1 + 1644 2858 2902 2860 -1 + 1645 2853 3808 4002 -1 + 1646 2853 3805 3808 -1 + 1647 2790 3588 3073 -1 + 1648 3389 3371 3081 -1 + 1649 3689 3690 3685 -1 + 1650 3688 3690 3689 -1 + 1651 1246 1316 4536 -1 + 1652 3313 3323 3322 -1 + 1653 3322 3312 3313 -1 + 1654 188 217 90 -1 + 1655 90 217 199 -1 + 1656 3028 3144 3162 -1 + 1657 4587 4423 332 -1 + 1658 332 4423 4328 -1 + 1659 3149 3166 3020 -1 + 1660 4045 4106 3315 -1 + 1661 3521 3535 3533 -1 + 1662 3533 3532 3521 -1 + 1663 3730 3876 3729 -1 + 1664 3729 3876 3757 -1 + 1665 3439 3440 4112 -1 + 1666 3439 3503 3440 -1 + 1667 3365 3499 3615 -1 + 1668 3615 3499 3491 -1 + 1669 4471 4236 4470 -1 + 1670 600 601 604 -1 + 1671 604 709 600 -1 + 1672 1079 1930 1078 -1 + 1673 1174 3012 1158 -1 + 1674 600 599 601 -1 + 1675 601 599 603 -1 + 1676 4195 3449 4092 -1 + 1677 3008 3049 4387 -1 + 1678 1267 1277 1278 -1 + 1679 487 465 466 -1 + 1680 487 480 465 -1 + 1681 3351 3180 3293 -1 + 1682 4067 3008 4387 -1 + 1683 4067 4118 3008 -1 + 1684 1673 1676 1666 -1 + 1685 1667 1666 1676 -1 + 1686 3279 3278 3900 -1 + 1687 4448 3221 3206 -1 + 1688 3206 2849 4448 -1 + 1689 2892 2888 2886 -1 + 1690 963 1123 4725 -1 + 1691 963 4043 1123 -1 + 1692 3699 3131 3717 -1 + 1693 3646 3705 3644 -1 + 1694 3300 3291 3094 -1 + 1695 3094 3291 3820 -1 + 1696 3406 3422 4555 -1 + 1697 4651 3936 4540 -1 + 1698 1109 1110 4040 -1 + 1699 3501 3491 3500 -1 + 1700 3375 3379 3370 -1 + 1701 3370 3379 3380 -1 + 1702 4445 3029 4333 -1 + 1703 3244 3305 3216 -1 + 1704 2860 3947 3943 -1 + 1705 2860 2868 3947 -1 + 1706 3861 3301 3830 -1 + 1707 3830 3301 3267 -1 + 1708 3267 3833 3830 -1 + 1709 4334 1207 2288 -1 + 1710 2268 2288 1207 -1 + 1711 3093 4101 4140 -1 + 1712 3861 3830 3819 -1 + 1713 2943 2942 2941 -1 + 1714 3804 3831 3807 -1 + 1715 3833 3831 3804 -1 + 1716 4588 2785 4546 -1 + 1717 3923 4546 2785 -1 + 1718 3916 3899 4039 -1 + 1719 2752 2812 2728 -1 + 1720 3327 3330 2988 -1 + 1721 586 3319 3883 -1 + 1722 3315 3312 3322 -1 + 1723 3322 3321 3315 -1 + 1724 2696 3336 2690 -1 + 1725 3000 3605 4755 -1 + 1726 3939 3330 3332 -1 + 1727 4674 4386 4092 -1 + 1728 4753 4682 2855 -1 + 1729 3895 3896 2695 -1 + 1730 2692 2695 3896 -1 + 1731 4762 1019 4731 -1 + 1732 4729 4731 1019 -1 + 1733 3890 3888 3928 -1 + 1734 3327 3887 3332 -1 + 1735 3332 3330 3327 -1 + 1736 3340 3323 3335 -1 + 1737 3340 3322 3323 -1 + 1738 1676 1677 4108 -1 + 1739 3285 3886 3933 -1 + 1740 3941 3924 3476 -1 + 1741 3941 3927 3924 -1 + 1742 3927 3926 3896 -1 + 1743 3896 3949 3927 -1 + 1744 3336 3339 3335 -1 + 1745 3336 2970 3339 -1 + 1746 3603 3606 4605 -1 + 1747 3603 2786 3606 -1 + 1748 386 329 385 -1 + 1749 381 329 386 -1 + 1750 3110 3279 3967 -1 + 1751 3275 2845 3338 -1 + 1752 3276 2845 3275 -1 + 1753 3487 3934 279 -1 + 1754 3487 3488 3934 -1 + 1755 336 337 331 -1 + 1756 331 337 2891 -1 + 1757 4180 4246 3662 -1 + 1758 332 2891 337 -1 + 1759 332 2889 2891 -1 + 1760 3194 3254 3192 -1 + 1761 3941 2717 3926 -1 + 1762 2725 2718 2716 -1 + 1763 4303 3313 4452 -1 + 1764 4303 4398 3313 -1 + 1765 4702 4414 84 -1 + 1766 84 4414 4132 -1 + 1767 3291 3863 3820 -1 + 1768 1566 1569 1607 -1 + 1769 3133 4300 4005 -1 + 1770 1061 2572 2571 -1 + 1771 2573 2572 1061 -1 + 1772 712 713 792 -1 + 1773 586 3883 585 -1 + 1774 585 3883 584 -1 + 1775 3320 3552 3612 -1 + 1776 3320 3551 3552 -1 + 1777 705 710 711 -1 + 1778 3890 3468 3892 -1 + 1779 3928 3468 3890 -1 + 1780 322 336 338 -1 + 1781 337 336 322 -1 + 1782 3929 3928 3888 -1 + 1783 3893 3888 3327 -1 + 1784 3887 3327 3888 -1 + 1785 464 3551 3553 -1 + 1786 3553 3551 3320 -1 + 1787 1158 1147 1175 -1 + 1788 3616 3552 3617 -1 + 1789 481 3617 3552 -1 + 1790 3944 3950 3951 -1 + 1791 3949 3951 3950 -1 + 1792 4761 3958 4759 -1 + 1793 3965 3123 3967 -1 + 1794 585 592 469 -1 + 1795 3557 3318 3612 -1 + 1796 3951 3949 3895 -1 + 1797 3896 3895 3949 -1 + 1798 1284 1307 1308 -1 + 1799 1308 1300 1284 -1 + 1800 598 600 605 -1 + 1801 599 600 598 -1 + 1802 3967 3901 3966 -1 + 1803 1758 4163 1759 -1 + 1804 649 650 608 -1 + 1805 3215 650 649 -1 + 1806 2723 2705 3936 -1 + 1807 1039 3936 2705 -1 + 1808 4645 4643 304 -1 + 1809 3094 3095 3093 -1 + 1810 4471 3519 2805 -1 + 1811 4257 4276 2763 -1 + 1812 2716 3941 3925 -1 + 1813 589 313 577 -1 + 1814 589 576 313 -1 + 1815 2900 3276 2827 -1 + 1816 2017 200 219 -1 + 1817 219 200 202 -1 + 1818 2745 3179 4539 -1 + 1819 3616 3612 3552 -1 + 1820 3616 3611 3612 -1 + 1821 2697 2901 2703 -1 + 1822 706 705 321 -1 + 1823 2861 2889 2857 -1 + 1824 2891 2889 2861 -1 + 1825 2692 3896 2717 -1 + 1826 3476 3929 3925 -1 + 1827 3928 3929 3476 -1 + 1828 3925 3941 3476 -1 + 1829 3318 3320 3612 -1 + 1830 2716 3925 3929 -1 + 1831 3342 3339 2884 -1 + 1832 2856 2884 3339 -1 + 1833 2718 3941 2716 -1 + 1834 2718 2717 3941 -1 + 1835 330 336 329 -1 + 1836 329 336 331 -1 + 1837 3891 3470 3889 -1 + 1838 307 3889 3470 -1 + 1839 3477 3474 3448 -1 + 1840 3488 3448 3474 -1 + 1841 298 297 296 -1 + 1842 295 297 298 -1 + 1843 3254 3261 2765 -1 + 1844 294 574 295 -1 + 1845 294 299 574 -1 + 1846 1074 1933 1078 -1 + 1847 1076 1933 1074 -1 + 1848 4735 4324 4689 -1 + 1849 2706 3610 2723 -1 + 1850 2706 3557 3610 -1 + 1851 3474 3949 3950 -1 + 1852 3927 3949 3474 -1 + 1853 3246 3343 3176 -1 + 1854 3246 648 3343 -1 + 1855 464 467 465 -1 + 1856 3463 3479 3520 -1 + 1857 3520 3479 3447 -1 + 1858 277 271 278 -1 + 1859 277 280 271 -1 + 1860 277 312 308 -1 + 1861 277 292 312 -1 + 1862 3897 296 3883 -1 + 1863 3883 3898 3897 -1 + 1864 4540 3931 4294 -1 + 1865 4107 3640 3105 -1 + 1866 883 909 900 -1 + 1867 2406 909 883 -1 + 1868 626 859 628 -1 + 1869 858 859 626 -1 + 1870 3897 3889 306 -1 + 1871 3897 3892 3889 -1 + 1872 56 34 816 -1 + 1873 813 34 56 -1 + 1874 3627 3618 3591 -1 + 1875 1610 1605 1611 -1 + 1876 4248 3253 2907 -1 + 1877 3196 2907 3253 -1 + 1878 3272 3271 2880 -1 + 1879 1342 2003 2082 -1 + 1880 2933 2950 2949 -1 + 1881 2934 2950 2933 -1 + 1882 711 712 295 -1 + 1883 295 705 711 -1 + 1884 1170 1160 4601 -1 + 1885 1660 1659 4012 -1 + 1886 1638 1639 1139 -1 + 1887 3282 3278 3110 -1 + 1888 3282 3781 3278 -1 + 1889 2107 2108 2110 -1 + 1890 2110 45 2107 -1 + 1891 293 3934 3944 -1 + 1892 3950 3944 3934 -1 + 1893 293 3944 311 -1 + 1894 311 310 293 -1 + 1895 444 3586 3587 -1 + 1896 3587 3586 3585 -1 + 1897 1946 1951 1947 -1 + 1898 1946 1952 1951 -1 + 1899 3480 3891 3468 -1 + 1900 3468 3481 3480 -1 + 1901 1484 3600 3601 -1 + 1902 1480 3600 1484 -1 + 1903 860 867 871 -1 + 1904 627 867 860 -1 + 1905 2893 385 331 -1 + 1906 331 385 329 -1 + 1907 3478 3464 3475 -1 + 1908 3460 3475 3464 -1 + 1909 3340 3335 3339 -1 + 1910 2857 2888 2892 -1 + 1911 2857 2889 2888 -1 + 1912 3448 3488 3489 -1 + 1913 3489 3488 3487 -1 + 1914 4669 4631 4377 -1 + 1915 2807 2806 2744 -1 + 1916 2807 2808 2806 -1 + 1917 3882 3997 3985 -1 + 1918 623 648 621 -1 + 1919 3246 621 648 -1 + 1920 4734 4499 4709 -1 + 1921 3235 2757 2767 -1 + 1922 2767 2757 3236 -1 + 1923 1559 1556 4188 -1 + 1924 1559 1558 1556 -1 + 1925 2878 2876 2864 -1 + 1926 2864 2874 2878 -1 + 1927 2943 2941 2702 -1 + 1928 2707 2708 2691 -1 + 1929 2691 2708 2937 -1 + 1930 4022 3287 4430 -1 + 1931 3019 3029 3149 -1 + 1932 3149 3029 3030 -1 + 1933 3190 3189 3274 -1 + 1934 3190 2910 3189 -1 + 1935 2897 2930 2896 -1 + 1936 579 334 580 -1 + 1937 579 333 334 -1 + 1938 811 789 785 -1 + 1939 785 788 811 -1 + 1940 439 1497 1501 -1 + 1941 1501 1497 1500 -1 + 1942 623 650 648 -1 + 1943 623 622 650 -1 + 1944 3262 3263 871 -1 + 1945 3261 3263 3262 -1 + 1946 2868 3953 3947 -1 + 1947 2868 2867 3953 -1 + 1948 4331 3648 3647 -1 + 1949 2020 2037 2021 -1 + 1950 2020 2017 2037 -1 + 1951 1506 1497 404 -1 + 1952 2680 1497 1506 -1 + 1953 1499 1519 1500 -1 + 1954 4530 4338 3555 -1 + 1955 817 819 2281 -1 + 1956 1225 2281 819 -1 + 1957 2917 2918 2919 -1 + 1958 434 433 440 -1 + 1959 2908 3958 2903 -1 + 1960 2903 3958 2904 -1 + 1961 322 321 318 -1 + 1962 318 314 322 -1 + 1963 2659 2646 2920 -1 + 1964 2659 2677 2646 -1 + 1965 2941 2929 2698 -1 + 1966 4016 4028 3958 -1 + 1967 2869 2865 2897 -1 + 1968 2897 2866 2869 -1 + 1969 2685 2672 2656 -1 + 1970 2654 2656 2672 -1 + 1971 2917 2919 2661 -1 + 1972 2881 2898 2882 -1 + 1973 2881 2899 2898 -1 + 1974 1001 1003 4542 -1 + 1975 1001 992 1003 -1 + 1976 3417 3414 4194 -1 + 1977 328 327 326 -1 + 1978 319 326 327 -1 + 1979 3269 3100 4572 -1 + 1980 3761 3270 3762 -1 + 1981 2882 2883 2864 -1 + 1982 2864 2881 2882 -1 + 1983 1116 4517 4417 -1 + 1984 1502 1499 1503 -1 + 1985 326 319 335 -1 + 1986 335 319 338 -1 + 1987 2912 2903 2913 -1 + 1988 249 3790 3765 -1 + 1989 3759 3765 3790 -1 + 1990 2877 379 390 -1 + 1991 380 390 379 -1 + 1992 3253 3195 3252 -1 + 1993 2911 2908 2910 -1 + 1994 1714 1409 4080 -1 + 1995 2882 3344 328 -1 + 1996 2898 3344 2882 -1 + 1997 4358 3640 4125 -1 + 1998 3637 3640 4358 -1 + 1999 2660 2659 2917 -1 + 2000 1499 2680 2684 -1 + 2001 440 437 441 -1 + 2002 1509 1519 1515 -1 + 2003 3196 3253 3233 -1 + 2004 3233 3234 3196 -1 + 2005 2671 3997 2676 -1 + 2006 2671 2683 3997 -1 + 2007 3215 648 650 -1 + 2008 3343 648 3215 -1 + 2009 319 327 320 -1 + 2010 320 321 319 -1 + 2011 577 575 589 -1 + 2012 575 591 589 -1 + 2013 2910 2908 2912 -1 + 2014 2912 3189 2910 -1 + 2015 3302 3190 3275 -1 + 2016 3274 3275 3190 -1 + 2017 3188 633 640 -1 + 2018 1506 3205 2643 -1 + 2019 2643 2681 1506 -1 + 2020 404 439 433 -1 + 2021 404 1497 439 -1 + 2022 3768 3102 3774 -1 + 2023 315 3214 4070 -1 + 2024 2645 3997 3885 -1 + 2025 3177 3277 3274 -1 + 2026 3177 3309 3277 -1 + 2027 1646 1605 1610 -1 + 2028 3328 3318 3557 -1 + 2029 2826 3270 3761 -1 + 2030 322 314 337 -1 + 2031 332 337 314 -1 + 2032 3298 3299 3297 -1 + 2033 3297 3299 3219 -1 + 2034 3189 3247 3176 -1 + 2035 3189 2912 3247 -1 + 2036 3224 2739 3223 -1 + 2037 4539 2749 2745 -1 + 2038 4539 3200 2749 -1 + 2039 1197 1198 2280 -1 + 2040 2910 3190 2909 -1 + 2041 702 321 320 -1 + 2042 320 607 702 -1 + 2043 726 642 632 -1 + 2044 726 644 642 -1 + 2045 4638 1608 4598 -1 + 2046 4664 822 4558 -1 + 2047 4400 4558 822 -1 + 2048 3343 3215 3348 -1 + 2049 3348 3309 3343 -1 + 2050 3343 3309 3177 -1 + 2051 3378 3415 3425 -1 + 2052 3378 3426 3415 -1 + 2053 1652 1648 2661 -1 + 2054 34 21 33 -1 + 2055 34 2130 21 -1 + 2056 3453 4325 4307 -1 + 2057 592 467 469 -1 + 2058 3958 3764 3773 -1 + 2059 2549 2581 2574 -1 + 2060 2574 2581 2583 -1 + 2061 394 396 388 -1 + 2062 388 389 394 -1 + 2063 1506 404 1507 -1 + 2064 1507 404 405 -1 + 2065 752 737 739 -1 + 2066 827 828 905 -1 + 2067 905 828 837 -1 + 2068 3248 3256 3249 -1 + 2069 3248 3234 3256 -1 + 2070 4515 1736 4461 -1 + 2071 1658 2894 1137 -1 + 2072 1053 1037 1047 -1 + 2073 1053 1597 1037 -1 + 2074 4700 4688 4468 -1 + 2075 4587 314 577 -1 + 2076 318 577 314 -1 + 2077 2684 2681 2677 -1 + 2078 3116 3284 3161 -1 + 2079 3161 3160 3116 -1 + 2080 3313 590 4606 -1 + 2081 4349 1411 1415 -1 + 2082 3768 3767 3769 -1 + 2083 2644 2709 2647 -1 + 2084 2647 2668 2644 -1 + 2085 85 4132 201 -1 + 2086 3275 3277 3276 -1 + 2087 589 590 576 -1 + 2088 589 591 590 -1 + 2089 3054 4127 4309 -1 + 2090 579 3215 649 -1 + 2091 579 580 3215 -1 + 2092 1762 4163 1760 -1 + 2093 3302 2829 3190 -1 + 2094 1263 1023 4395 -1 + 2095 1263 4085 1023 -1 + 2096 4573 4565 3052 -1 + 2097 1031 4184 4138 -1 + 2098 4437 1751 1753 -1 + 2099 1753 1751 1584 -1 + 2100 4305 3112 3114 -1 + 2101 3089 3114 3112 -1 + 2102 3109 3655 3661 -1 + 2103 4309 4030 4149 -1 + 2104 4127 4030 4309 -1 + 2105 295 309 294 -1 + 2106 295 298 309 -1 + 2107 2942 2845 2885 -1 + 2108 2829 2909 3190 -1 + 2109 3786 3098 3769 -1 + 2110 3158 3157 3272 -1 + 2111 2852 2884 2854 -1 + 2112 2884 2856 2854 -1 + 2113 3304 3227 3243 -1 + 2114 3203 3310 3311 -1 + 2115 3311 3346 3203 -1 + 2116 4362 1140 4255 -1 + 2117 4075 3038 3046 -1 + 2118 3046 3038 3047 -1 + 2119 1071 4020 1553 -1 + 2120 2886 4045 4183 -1 + 2121 993 1021 4503 -1 + 2122 993 4130 1021 -1 + 2123 4614 3113 4072 -1 + 2124 1739 1740 4768 -1 + 2125 4582 1654 1661 -1 + 2126 703 728 704 -1 + 2127 703 727 728 -1 + 2128 3154 3152 4495 -1 + 2129 3096 3097 3099 -1 + 2130 3100 3099 3097 -1 + 2131 4582 1661 2873 -1 + 2132 2747 2810 3083 -1 + 2133 4712 4677 963 -1 + 2134 4531 1400 1399 -1 + 2135 4554 4222 1689 -1 + 2136 3715 3716 3554 -1 + 2137 3714 3716 3715 -1 + 2138 4014 1754 1755 -1 + 2139 4285 1243 1150 -1 + 2140 1578 1150 1243 -1 + 2141 4331 4090 3648 -1 + 2142 1670 1667 4351 -1 + 2143 1722 1761 1721 -1 + 2144 3104 3638 3647 -1 + 2145 4676 2789 4545 -1 + 2146 2531 2489 2484 -1 + 2147 2487 2489 2531 -1 + 2148 346 728 339 -1 + 2149 343 339 728 -1 + 2150 639 631 4068 -1 + 2151 3371 3370 3380 -1 + 2152 3978 3998 3999 -1 + 2153 3957 3998 3978 -1 + 2154 1355 1350 1360 -1 + 2155 1356 1360 1350 -1 + 2156 3201 3232 3265 -1 + 2157 3265 3232 3200 -1 + 2158 3069 3175 3566 -1 + 2159 3069 3171 3175 -1 + 2160 2748 2728 2753 -1 + 2161 4663 612 4624 -1 + 2162 4428 4624 612 -1 + 2163 3308 3307 3306 -1 + 2164 3306 3304 3308 -1 + 2165 3238 3230 3231 -1 + 2166 1254 4047 1144 -1 + 2167 3293 3294 3350 -1 + 2168 3178 3294 3293 -1 + 2169 4311 1588 1589 -1 + 2170 4469 4232 1134 -1 + 2171 1134 4232 4146 -1 + 2172 2795 3185 3186 -1 + 2173 3324 3299 3260 -1 + 2174 3143 1262 3037 -1 + 2175 4315 1653 1659 -1 + 2176 4315 3932 1653 -1 + 2177 4580 4082 4547 -1 + 2178 3219 3299 3324 -1 + 2179 1920 4355 4520 -1 + 2180 2661 1033 4457 -1 + 2181 2661 4138 1033 -1 + 2182 3333 3182 3289 -1 + 2183 3219 3316 3297 -1 + 2184 4258 1694 1691 -1 + 2185 4258 1693 1694 -1 + 2186 3197 3195 4248 -1 + 2187 3197 3193 3195 -1 + 2188 4268 1580 4177 -1 + 2189 4585 4480 1196 -1 + 2190 3182 3331 3178 -1 + 2191 3178 3331 3294 -1 + 2192 3294 3331 3352 -1 + 2193 4580 1044 1629 -1 + 2194 4463 4300 3041 -1 + 2195 3041 4278 4463 -1 + 2196 3114 3099 3113 -1 + 2197 3114 3115 3099 -1 + 2198 3202 3297 3290 -1 + 2199 3316 3290 3297 -1 + 2200 2739 3202 3184 -1 + 2201 2739 3224 3202 -1 + 2202 3184 3185 3223 -1 + 2203 3223 2739 3184 -1 + 2204 3171 2798 3070 -1 + 2205 3622 3621 4392 -1 + 2206 3258 3257 3260 -1 + 2207 3566 3580 3069 -1 + 2208 4363 2592 4585 -1 + 2209 3121 3126 3160 -1 + 2210 4707 4442 1404 -1 + 2211 1404 1407 4707 -1 + 2212 3202 3298 3297 -1 + 2213 936 960 932 -1 + 2214 1090 932 960 -1 + 2215 2936 2948 2944 -1 + 2216 2944 2948 2947 -1 + 2217 2761 2760 3243 -1 + 2218 2761 3052 2760 -1 + 2219 3128 3121 3129 -1 + 2220 2588 1064 4347 -1 + 2221 1584 1751 1749 -1 + 2222 1163 1161 1167 -1 + 2223 1167 1161 1169 -1 + 2224 3566 3554 3580 -1 + 2225 3181 3182 3183 -1 + 2226 3186 3182 3181 -1 + 2227 2832 2842 2848 -1 + 2228 2848 2842 2843 -1 + 2229 1708 1699 1698 -1 + 2230 4693 4540 3936 -1 + 2231 3936 2873 4693 -1 + 2232 4009 3884 3885 -1 + 2233 4350 4492 2819 -1 + 2234 1442 1435 1353 -1 + 2235 1353 1422 1442 -1 + 2236 863 861 3187 -1 + 2237 3187 861 3191 -1 + 2238 1444 1441 1443 -1 + 2239 1444 1427 1441 -1 + 2240 564 559 557 -1 + 2241 3096 3103 3098 -1 + 2242 3098 3097 3096 -1 + 2243 4007 2283 2506 -1 + 2244 2501 2506 2283 -1 + 2245 3294 3352 3350 -1 + 2246 2841 2977 2957 -1 + 2247 2841 2963 2977 -1 + 2248 4473 4344 3895 -1 + 2249 4184 1032 1033 -1 + 2250 1033 1032 2653 -1 + 2251 3227 2761 3243 -1 + 2252 3251 3311 3314 -1 + 2253 2748 3311 3251 -1 + 2254 3198 3232 3201 -1 + 2255 562 1358 1352 -1 + 2256 1352 1358 1357 -1 + 2257 4385 2667 4564 -1 + 2258 3200 3232 2749 -1 + 2259 4598 1041 4089 -1 + 2260 4694 4407 3173 -1 + 2261 4488 4233 1648 -1 + 2262 2750 2745 2733 -1 + 2263 2750 3179 2745 -1 + 2264 2844 2846 2847 -1 + 2265 3333 3290 3337 -1 + 2266 2649 2772 2669 -1 + 2267 2649 2769 2772 -1 + 2268 3243 2760 3242 -1 + 2269 352 355 357 -1 + 2270 2956 355 352 -1 + 2271 3250 3251 3314 -1 + 2272 3250 3314 3242 -1 + 2273 4317 3881 3273 -1 + 2274 3308 3314 3307 -1 + 2275 1337 1348 1355 -1 + 2276 1337 2089 1348 -1 + 2277 3650 3659 3661 -1 + 2278 1754 1757 4437 -1 + 2279 3203 3217 3310 -1 + 2280 3203 3345 3217 -1 + 2281 3346 3345 3203 -1 + 2282 3307 3310 3217 -1 + 2283 3307 3314 3310 -1 + 2284 1142 1182 4590 -1 + 2285 3306 3217 3218 -1 + 2286 3216 3218 3217 -1 + 2287 3219 3324 3217 -1 + 2288 2795 3223 3185 -1 + 2289 2794 3223 2795 -1 + 2290 3316 3219 3217 -1 + 2291 4362 4271 1153 -1 + 2292 2332 2326 2327 -1 + 2293 2327 2323 2332 -1 + 2294 4470 3608 3607 -1 + 2295 3609 3607 3608 -1 + 2296 3148 3149 3030 -1 + 2297 4131 987 1255 -1 + 2298 1263 987 4131 -1 + 2299 3180 3178 3293 -1 + 2300 3180 3179 3178 -1 + 2301 1764 4163 1758 -1 + 2302 4644 4597 3482 -1 + 2303 1341 1337 1343 -1 + 2304 1359 1343 1337 -1 + 2305 3126 3155 1256 -1 + 2306 3154 3155 3126 -1 + 2307 3129 3160 3157 -1 + 2308 3157 3160 3161 -1 + 2309 3621 3623 4392 -1 + 2310 3621 3596 3623 -1 + 2311 3546 3386 4244 -1 + 2312 4496 4058 4110 -1 + 2313 1764 1760 4163 -1 + 2314 3152 3171 3070 -1 + 2315 2827 3276 3277 -1 + 2316 3277 3309 2827 -1 + 2317 2833 2836 2975 -1 + 2318 2975 2976 2833 -1 + 2319 2832 2831 2973 -1 + 2320 2976 2973 2831 -1 + 2321 2814 3986 3979 -1 + 2322 3979 303 2814 -1 + 2323 1443 1396 1438 -1 + 2324 1441 1396 1443 -1 + 2325 2824 2834 2836 -1 + 2326 2824 2835 2834 -1 + 2327 235 3859 3809 -1 + 2328 3821 3809 3859 -1 + 2329 1769 1772 1771 -1 + 2330 3992 2817 3999 -1 + 2331 3991 2817 3992 -1 + 2332 2524 2527 41 -1 + 2333 2524 2528 2527 -1 + 2334 593 587 581 -1 + 2335 581 2307 593 -1 + 2336 975 953 946 -1 + 2337 975 952 953 -1 + 2338 3978 3991 3957 -1 + 2339 2817 3991 3978 -1 + 2340 2967 2974 2966 -1 + 2341 2967 2965 2974 -1 + 2342 2981 2962 2977 -1 + 2343 2977 2962 2979 -1 + 2344 2973 2976 2972 -1 + 2345 2975 2972 2976 -1 + 2346 3992 3969 3991 -1 + 2347 3991 3969 3982 -1 + 2348 2960 2959 2958 -1 + 2349 2960 2955 2959 -1 + 2350 3813 3821 3859 -1 + 2351 3873 3821 3813 -1 + 2352 497 559 496 -1 + 2353 3832 3834 3828 -1 + 2354 3822 3828 3834 -1 + 2355 2973 2963 2841 -1 + 2356 2973 2972 2963 -1 + 2357 340 357 341 -1 + 2358 341 357 355 -1 + 2359 2085 2091 2086 -1 + 2360 2086 2091 3 -1 + 2361 3903 3573 3576 -1 + 2362 3573 3577 3576 -1 + 2363 2851 3213 325 -1 + 2364 2851 2969 3213 -1 + 2365 864 2387 2239 -1 + 2366 2239 2387 2320 -1 + 2367 2307 2264 2265 -1 + 2368 2307 581 2264 -1 + 2369 2228 2231 2213 -1 + 2370 2213 2231 2222 -1 + 2371 390 393 391 -1 + 2372 391 395 390 -1 + 2373 2954 2851 2951 -1 + 2374 349 2951 2851 -1 + 2375 351 356 349 -1 + 2376 351 353 356 -1 + 2377 356 357 358 -1 + 2378 356 352 357 -1 + 2379 3823 3829 3825 -1 + 2380 3823 3824 3829 -1 + 2381 3783 3756 3789 -1 + 2382 3791 3789 3756 -1 + 2383 3791 3759 3790 -1 + 2384 3791 3756 3759 -1 + 2385 3827 3811 3844 -1 + 2386 3837 3844 3811 -1 + 2387 3846 3859 233 -1 + 2388 3813 3859 3846 -1 + 2389 368 3785 3874 -1 + 2390 3801 3874 3785 -1 + 2391 409 104 413 -1 + 2392 413 414 409 -1 + 2393 3770 3790 246 -1 + 2394 3791 3790 3770 -1 + 2395 2924 2962 2978 -1 + 2396 2924 2980 2962 -1 + 2397 3775 3779 3725 -1 + 2398 3780 3725 3779 -1 + 2399 260 256 254 -1 + 2400 260 250 256 -1 + 2401 2982 2914 2915 -1 + 2402 2915 2914 2940 -1 + 2403 2964 2965 2967 -1 + 2404 2964 2961 2965 -1 + 2405 2957 2958 2842 -1 + 2406 2842 2841 2957 -1 + 2407 2388 2370 2371 -1 + 2408 2371 2370 2380 -1 + 2409 2328 2326 2325 -1 + 2410 2327 2326 2328 -1 + 2411 153 163 155 -1 + 2412 154 155 163 -1 + 2413 2933 2691 2937 -1 + 2414 2937 2935 2933 -1 + 2415 2938 2700 2701 -1 + 2416 2701 2949 2938 -1 + 2417 301 244 3957 -1 + 2418 3724 3734 3797 -1 + 2419 3797 3734 3733 -1 + 2420 2916 2921 2991 -1 + 2421 2916 344 2921 -1 + 2422 3598 2803 4137 -1 + 2423 3598 4001 2803 -1 + 2424 3862 3732 3858 -1 + 2425 3858 3732 3855 -1 + 2426 1370 1364 1368 -1 + 2427 1362 1364 1370 -1 + 2428 3742 3744 3799 -1 + 2429 3743 3799 3744 -1 + 2430 3989 242 247 -1 + 2431 248 247 242 -1 + 2432 3340 3342 3321 -1 + 2433 3321 3322 3340 -1 + 2434 263 261 237 -1 + 2435 237 264 263 -1 + 2436 3975 3999 2817 -1 + 2437 3783 3777 3784 -1 + 2438 3784 3756 3783 -1 + 2439 243 3979 3977 -1 + 2440 3977 3979 3776 -1 + 2441 3974 3792 3778 -1 + 2442 3974 3987 3792 -1 + 2443 3823 3815 3824 -1 + 2444 3824 3815 3816 -1 + 2445 3789 3776 3980 -1 + 2446 3789 3977 3776 -1 + 2447 2822 3986 2821 -1 + 2448 2822 2821 2814 -1 + 2449 2814 2821 3986 -1 + 2450 2816 301 3957 -1 + 2451 243 303 3979 -1 + 2452 243 302 303 -1 + 2453 2850 2969 2825 -1 + 2454 3213 2969 2850 -1 + 2455 2850 2825 2820 -1 + 2456 3743 3752 3726 -1 + 2457 3743 3744 3752 -1 + 2458 3760 3759 3756 -1 + 2459 3756 3784 3760 -1 + 2460 789 807 2700 -1 + 2461 2701 2700 807 -1 + 2462 3938 3473 3090 -1 + 2463 785 803 799 -1 + 2464 785 784 803 -1 + 2465 3817 258 266 -1 + 2466 254 266 258 -1 + 2467 369 371 368 -1 + 2468 369 367 371 -1 + 2469 2967 2992 2964 -1 + 2470 2967 2968 2992 -1 + 2471 3749 3850 3754 -1 + 2472 3749 3848 3850 -1 + 2473 2720 756 759 -1 + 2474 2654 756 2720 -1 + 2475 3832 3817 3809 -1 + 2476 3809 3817 266 -1 + 2477 4485 1609 1576 -1 + 2478 1230 1576 1609 -1 + 2479 3867 3865 261 -1 + 2480 3847 3865 3867 -1 + 2481 3845 3759 3760 -1 + 2482 3765 3759 3845 -1 + 2483 3829 3824 3811 -1 + 2484 3814 3811 3824 -1 + 2485 801 340 341 -1 + 2486 801 344 340 -1 + 2487 3760 3784 3732 -1 + 2488 3731 3732 3784 -1 + 2489 3725 3723 3724 -1 + 2490 3725 3739 3723 -1 + 2491 509 489 490 -1 + 2492 509 508 489 -1 + 2493 256 253 267 -1 + 2494 267 255 256 -1 + 2495 2759 2751 2756 -1 + 2496 852 2751 2759 -1 + 2497 3858 3873 3813 -1 + 2498 3854 3873 3858 -1 + 2499 3862 3846 3845 -1 + 2500 3766 3845 3846 -1 + 2501 2939 2938 2940 -1 + 2502 2940 2938 2915 -1 + 2503 3766 3846 233 -1 + 2504 233 232 3766 -1 + 2505 3862 3858 3813 -1 + 2506 3813 3846 3862 -1 + 2507 3731 3777 3727 -1 + 2508 3731 3784 3777 -1 + 2509 3731 3727 3726 -1 + 2510 3743 3726 3727 -1 + 2511 3775 3724 3797 -1 + 2512 3775 3725 3724 -1 + 2513 377 375 374 -1 + 2514 374 373 377 -1 + 2515 3261 3194 3263 -1 + 2516 3254 3194 3261 -1 + 2517 3142 3164 3136 -1 + 2518 3150 3164 3142 -1 + 2519 391 3960 395 -1 + 2520 395 3960 3879 -1 + 2521 2555 2550 2557 -1 + 2522 2565 2557 2550 -1 + 2523 3874 3839 257 -1 + 2524 3877 3839 3874 -1 + 2525 3826 3822 3827 -1 + 2526 3826 3828 3822 -1 + 2527 3810 3723 3853 -1 + 2528 3810 3857 3723 -1 + 2529 375 3738 376 -1 + 2530 376 374 375 -1 + 2531 3434 3364 3357 -1 + 2532 3357 3360 3434 -1 + 2533 3818 3828 3810 -1 + 2534 3826 3810 3828 -1 + 2535 365 391 387 -1 + 2536 365 3960 391 -1 + 2537 3843 3857 3826 -1 + 2538 3826 3857 3810 -1 + 2539 3174 3041 3044 -1 + 2540 3044 3041 3133 -1 + 2541 372 359 360 -1 + 2542 360 366 372 -1 + 2543 3794 3797 3733 -1 + 2544 3733 3795 3794 -1 + 2545 3827 3829 3811 -1 + 2546 3827 3822 3829 -1 + 2547 492 490 500 -1 + 2548 489 500 490 -1 + 2549 3376 3399 3362 -1 + 2550 3376 3377 3399 -1 + 2551 3948 3952 2879 -1 + 2552 2879 3952 3913 -1 + 2553 673 674 657 -1 + 2554 657 674 670 -1 + 2555 2905 3197 2907 -1 + 2556 3910 2867 3913 -1 + 2557 3953 2867 3910 -1 + 2558 2325 895 2360 -1 + 2559 2360 895 768 -1 + 2560 3909 3913 3952 -1 + 2561 3909 3910 3913 -1 + 2562 2809 2755 2757 -1 + 2563 2757 2768 2809 -1 + 2564 393 387 391 -1 + 2565 389 387 393 -1 + 2566 2878 2877 3948 -1 + 2567 3948 2879 2878 -1 + 2568 2878 2874 2877 -1 + 2569 379 2877 2874 -1 + 2570 3935 2870 3946 -1 + 2571 3356 3946 2870 -1 + 2572 626 643 622 -1 + 2573 622 623 626 -1 + 2574 3235 2768 2757 -1 + 2575 3239 2768 3235 -1 + 2576 2765 3252 3254 -1 + 2577 3956 3881 3884 -1 + 2578 4769 4739 1179 -1 + 2579 1179 1390 4769 -1 + 2580 2663 2806 2776 -1 + 2581 2663 2775 2806 -1 + 2582 809 818 810 -1 + 2583 809 781 818 -1 + 2584 704 706 702 -1 + 2585 552 548 427 -1 + 2586 552 551 548 -1 + 2587 2765 3237 2764 -1 + 2588 2764 3240 2765 -1 + 2589 2806 2808 2776 -1 + 2590 2774 2776 2808 -1 + 2591 2741 2773 2770 -1 + 2592 2770 2774 2741 -1 + 2593 2932 2922 2945 -1 + 2594 2945 2922 2947 -1 + 2595 3241 2751 857 -1 + 2596 3241 3229 2751 -1 + 2597 1995 1993 1990 -1 + 2598 2776 2664 2663 -1 + 2599 2776 2666 2664 -1 + 2600 4719 1588 4499 -1 + 2601 3961 3909 3911 -1 + 2602 3961 3972 3909 -1 + 2603 851 2759 918 -1 + 2604 918 2759 2758 -1 + 2605 760 894 922 -1 + 2606 922 881 760 -1 + 2607 2651 2709 2644 -1 + 2608 2715 2709 2651 -1 + 2609 363 364 359 -1 + 2610 359 364 360 -1 + 2611 2399 902 2398 -1 + 2612 2398 2397 2399 -1 + 2613 2732 2792 2743 -1 + 2614 2791 2792 2732 -1 + 2615 2978 2944 2924 -1 + 2616 2978 2927 2944 -1 + 2617 1519 1499 1502 -1 + 2618 2367 2327 2328 -1 + 2619 2877 395 3948 -1 + 2620 390 395 2877 -1 + 2621 63 2072 2073 -1 + 2622 63 4 2072 -1 + 2623 375 3735 3738 -1 + 2624 3739 3738 3735 -1 + 2625 540 536 539 -1 + 2626 539 541 540 -1 + 2627 2720 782 2655 -1 + 2628 781 782 2720 -1 + 2629 848 834 847 -1 + 2630 833 847 834 -1 + 2631 970 966 967 -1 + 2632 915 882 921 -1 + 2633 915 916 882 -1 + 2634 836 837 828 -1 + 2635 836 852 837 -1 + 2636 2711 2687 2688 -1 + 2637 2688 2710 2711 -1 + 2638 2650 2669 2777 -1 + 2639 2777 2780 2650 -1 + 2640 2793 2777 2771 -1 + 2641 2779 2777 2793 -1 + 2642 2880 2661 2919 -1 + 2643 1351 1430 1423 -1 + 2644 1423 1430 1428 -1 + 2645 2730 2797 2791 -1 + 2646 2730 2796 2797 -1 + 2647 3771 3775 3797 -1 + 2648 3797 3794 3771 -1 + 2649 883 2341 2406 -1 + 2650 2406 2341 2407 -1 + 2651 2711 2686 2687 -1 + 2652 2685 2687 2686 -1 + 2653 3843 3844 3842 -1 + 2654 3812 3842 3844 -1 + 2655 920 894 893 -1 + 2656 920 922 894 -1 + 2657 881 882 889 -1 + 2658 922 882 881 -1 + 2659 2673 763 756 -1 + 2660 756 763 776 -1 + 2661 758 761 773 -1 + 2662 758 776 761 -1 + 2663 2726 2796 2730 -1 + 2664 906 2796 2726 -1 + 2665 2650 2672 2657 -1 + 2666 2650 2673 2672 -1 + 2667 363 359 374 -1 + 2668 374 376 363 -1 + 2669 2778 777 763 -1 + 2670 763 2780 2778 -1 + 2671 2771 2669 2772 -1 + 2672 2777 2669 2771 -1 + 2673 550 551 552 -1 + 2674 550 154 551 -1 + 2675 1366 427 1372 -1 + 2676 1366 552 427 -1 + 2677 2773 2772 2770 -1 + 2678 2771 2772 2773 -1 + 2679 1514 1511 1515 -1 + 2680 1515 1512 1514 -1 + 2681 2542 2627 734 -1 + 2682 2542 2559 2627 -1 + 2683 908 900 898 -1 + 2684 898 911 908 -1 + 2685 1371 1510 1511 -1 + 2686 1371 1516 1510 -1 + 2687 1503 1499 2684 -1 + 2688 2666 2665 2664 -1 + 2689 2769 2665 2666 -1 + 2690 2670 2769 2649 -1 + 2691 2665 2769 2670 -1 + 2692 2778 2780 2779 -1 + 2693 2779 2780 2777 -1 + 2694 918 917 916 -1 + 2695 918 2744 917 -1 + 2696 2771 2792 2793 -1 + 2697 2771 2773 2792 -1 + 2698 2530 2532 2486 -1 + 2699 2486 2526 2530 -1 + 2700 2369 876 2384 -1 + 2701 875 2384 876 -1 + 2702 628 859 868 -1 + 2703 868 627 628 -1 + 2704 222 219 202 -1 + 2705 153 162 163 -1 + 2706 153 1998 162 -1 + 2707 1514 1362 1370 -1 + 2708 1374 1362 1514 -1 + 2709 696 708 2547 -1 + 2710 696 707 708 -1 + 2711 4562 1281 4343 -1 + 2712 4323 1281 4562 -1 + 2713 2332 2404 2333 -1 + 2714 2332 2334 2404 -1 + 2715 1573 1550 1564 -1 + 2716 1373 427 428 -1 + 2717 548 428 427 -1 + 2718 440 441 438 -1 + 2719 438 441 229 -1 + 2720 2732 2764 2731 -1 + 2721 2732 2766 2764 -1 + 2722 1509 1500 1519 -1 + 2723 1501 1500 1509 -1 + 2724 3195 3192 3254 -1 + 2725 829 827 907 -1 + 2726 829 828 827 -1 + 2727 4447 3939 4354 -1 + 2728 2392 2393 2417 -1 + 2729 2392 2394 2393 -1 + 2730 3240 2766 3239 -1 + 2731 3240 2764 2766 -1 + 2732 2997 3058 4566 -1 + 2733 2997 4450 3058 -1 + 2734 3261 3237 2765 -1 + 2735 3261 3262 3237 -1 + 2736 159 158 160 -1 + 2737 2032 158 159 -1 + 2738 2726 2746 830 -1 + 2739 830 907 2726 -1 + 2740 426 551 549 -1 + 2741 548 551 426 -1 + 2742 2730 2791 2731 -1 + 2743 2732 2731 2791 -1 + 2744 3995 3985 2683 -1 + 2745 2726 2730 2746 -1 + 2746 2731 2746 2730 -1 + 2747 827 904 906 -1 + 2748 827 905 904 -1 + 2749 3262 871 830 -1 + 2750 830 871 867 -1 + 2751 2746 3262 830 -1 + 2752 2746 3237 3262 -1 + 2753 1383 1385 1384 -1 + 2754 1386 1384 1385 -1 + 2755 858 857 859 -1 + 2756 858 3241 857 -1 + 2757 903 843 845 -1 + 2758 903 844 843 -1 + 2759 2033 1363 1361 -1 + 2760 1361 1374 2033 -1 + 2761 2751 852 857 -1 + 2762 836 857 852 -1 + 2763 2709 2715 2714 -1 + 2764 2712 2714 2715 -1 + 2765 436 439 1501 -1 + 2766 436 437 439 -1 + 2767 3252 3195 3254 -1 + 2768 2322 2400 2338 -1 + 2769 2342 2400 2322 -1 + 2770 736 745 778 -1 + 2771 2394 2405 2339 -1 + 2772 2339 2393 2394 -1 + 2773 2687 2694 2688 -1 + 2774 2693 2694 2687 -1 + 2775 789 784 785 -1 + 2776 2700 784 789 -1 + 2777 802 801 341 -1 + 2778 341 791 802 -1 + 2779 755 754 757 -1 + 2780 755 805 754 -1 + 2781 2720 2655 2654 -1 + 2782 2654 2655 2656 -1 + 2783 1347 1360 1356 -1 + 2784 1356 565 1347 -1 + 2785 2688 2689 2935 -1 + 2786 2688 2694 2689 -1 + 2787 2686 2670 2649 -1 + 2788 2649 2657 2686 -1 + 2789 1378 1339 1340 -1 + 2790 1378 1338 1339 -1 + 2791 2648 805 2668 -1 + 2792 2668 2647 2648 -1 + 2793 572 567 1335 -1 + 2794 566 567 572 -1 + 2795 773 760 887 -1 + 2796 773 761 760 -1 + 2797 765 774 764 -1 + 2798 765 745 774 -1 + 2799 806 805 2648 -1 + 2800 806 754 805 -1 + 2801 2067 2069 2068 -1 + 2802 2068 2069 2065 -1 + 2803 1995 2033 1993 -1 + 2804 1363 2033 1995 -1 + 2805 810 811 788 -1 + 2806 788 809 810 -1 + 2807 37 42 36 -1 + 2808 37 38 42 -1 + 2809 810 757 754 -1 + 2810 818 757 810 -1 + 2811 2252 2262 2210 -1 + 2812 2210 2262 624 -1 + 2813 972 1009 974 -1 + 2814 972 1012 1009 -1 + 2815 2655 2719 2656 -1 + 2816 2693 2656 2719 -1 + 2817 2298 2308 2295 -1 + 2818 2295 2299 2298 -1 + 2819 2089 2088 2083 -1 + 2820 2083 2088 2084 -1 + 2821 3 37 6 -1 + 2822 3 2091 37 -1 + 2823 1826 1811 1803 -1 + 2824 1803 1802 1826 -1 + 2825 2936 2689 2948 -1 + 2826 2934 2689 2936 -1 + 2827 1346 1336 1359 -1 + 2828 1359 1336 1343 -1 + 2829 552 539 550 -1 + 2830 552 1366 539 -1 + 2831 783 809 800 -1 + 2832 788 800 809 -1 + 2833 2655 2721 2719 -1 + 2834 782 2721 2655 -1 + 2835 1382 1387 1381 -1 + 2836 1382 1367 1387 -1 + 2837 2089 2083 1354 -1 + 2838 2946 2693 2719 -1 + 2839 2694 2693 2946 -1 + 2840 2720 759 781 -1 + 2841 818 781 759 -1 + 2842 1781 1792 1779 -1 + 2843 1781 1791 1792 -1 + 2844 352 353 2955 -1 + 2845 356 353 352 -1 + 2846 2952 2954 2953 -1 + 2847 2953 2954 2951 -1 + 2848 1445 1437 502 -1 + 2849 1445 1438 1437 -1 + 2850 4179 995 994 -1 + 2851 2095 2055 2051 -1 + 2852 2095 2087 2055 -1 + 2853 2051 2054 2053 -1 + 2854 2053 2095 2051 -1 + 2855 2916 2953 350 -1 + 2856 2916 2990 2953 -1 + 2857 2979 2980 2987 -1 + 2858 2979 2962 2980 -1 + 2859 2048 2050 2046 -1 + 2860 2048 2049 2050 -1 + 2861 2957 2989 2958 -1 + 2862 2960 2958 2989 -1 + 2863 1342 2080 1341 -1 + 2864 2165 2175 1540 -1 + 2865 2170 2175 2165 -1 + 2866 316 3221 317 -1 + 2867 2837 317 3221 -1 + 2868 4538 3929 3893 -1 + 2869 3888 3893 3929 -1 + 2870 2957 2977 2979 -1 + 2871 2979 2989 2957 -1 + 2872 2987 2985 2989 -1 + 2873 2989 2979 2987 -1 + 2874 790 354 2986 -1 + 2875 791 354 790 -1 + 2876 2932 2721 2931 -1 + 2877 2932 2945 2721 -1 + 2878 2844 2837 2846 -1 + 2879 3221 2846 2837 -1 + 2880 1430 1357 1350 -1 + 2881 1350 1429 1430 -1 + 2882 1420 1396 1419 -1 + 2883 1420 1397 1396 -1 + 2884 2369 2384 2380 -1 + 2885 2380 2370 2369 -1 + 2886 20 2061 2060 -1 + 2887 20 49 2061 -1 + 2888 1210 2313 2266 -1 + 2889 1208 2313 1210 -1 + 2890 2946 2947 2948 -1 + 2891 2945 2947 2946 -1 + 2892 1991 1995 1990 -1 + 2893 1990 1997 1991 -1 + 2894 655 660 656 -1 + 2895 655 659 660 -1 + 2896 2000 2082 2003 -1 + 2897 2000 2081 2082 -1 + 2898 2778 893 777 -1 + 2899 894 777 893 -1 + 2900 2000 2001 2081 -1 + 2901 2079 2081 2001 -1 + 2902 1377 1380 1389 -1 + 2903 1389 2002 1377 -1 + 2904 1991 1997 1999 -1 + 2905 1999 1997 1988 -1 + 2906 1381 1387 1385 -1 + 2907 1385 1375 1381 -1 + 2908 2241 2320 2387 -1 + 2909 2240 2320 2241 -1 + 2910 2088 2076 2084 -1 + 2911 2084 2076 2093 -1 + 2912 1379 541 1367 -1 + 2913 1367 1382 1379 -1 + 2914 1339 1346 1344 -1 + 2915 1336 1346 1339 -1 + 2916 63 167 2 -1 + 2917 63 2009 167 -1 + 2918 540 542 534 -1 + 2919 1376 542 540 -1 + 2920 2064 2025 2069 -1 + 2921 2069 2062 2064 -1 + 2922 2058 25 44 -1 + 2923 44 25 497 -1 + 2924 26 47 44 -1 + 2925 44 47 43 -1 + 2926 2376 2245 2382 -1 + 2927 2376 2244 2245 -1 + 2928 2085 2053 2090 -1 + 2929 2052 2090 2053 -1 + 2930 2087 2086 11 -1 + 2931 2095 2086 2087 -1 + 2932 976 961 1107 -1 + 2933 2261 2227 2225 -1 + 2934 2261 2222 2227 -1 + 2935 936 955 960 -1 + 2936 2084 2052 2083 -1 + 2937 2084 2090 2052 -1 + 2938 557 573 563 -1 + 2939 554 563 573 -1 + 2940 1378 1340 1379 -1 + 2941 1376 1379 1340 -1 + 2942 1351 1357 1430 -1 + 2943 1352 1357 1351 -1 + 2944 801 803 804 -1 + 2945 801 802 803 -1 + 2946 4591 3269 4273 -1 + 2947 2285 2272 2287 -1 + 2948 2273 2287 2272 -1 + 2949 2291 2214 2216 -1 + 2950 2215 2214 2291 -1 + 2951 2266 2313 2311 -1 + 2952 2311 2310 2266 -1 + 2953 939 958 950 -1 + 2954 2024 2023 2062 -1 + 2955 503 495 50 -1 + 2956 503 1439 495 -1 + 2957 2312 2290 2271 -1 + 2958 2271 2290 2316 -1 + 2959 2168 2169 1843 -1 + 2960 62 61 59 -1 + 2961 62 57 61 -1 + 2962 1355 1429 1350 -1 + 2963 1355 1348 1429 -1 + 2964 978 943 977 -1 + 2965 978 941 943 -1 + 2966 3992 3976 4000 -1 + 2967 4000 3969 3992 -1 + 2968 2152 2197 2196 -1 + 2969 2163 2196 2197 -1 + 2970 2 1 10 -1 + 2971 10 1 544 -1 + 2972 1443 1442 1422 -1 + 2973 1422 1444 1443 -1 + 2974 301 241 244 -1 + 2975 301 302 241 -1 + 2976 2248 2247 2208 -1 + 2977 2208 2247 2246 -1 + 2978 1432 1433 561 -1 + 2979 496 561 1433 -1 + 2980 1421 1422 1423 -1 + 2981 1423 1422 1353 -1 + 2982 1421 1428 1424 -1 + 2983 1421 1423 1428 -1 + 2984 1421 1425 1444 -1 + 2985 1444 1422 1421 -1 + 2986 2510 2531 2533 -1 + 2987 2521 2531 2510 -1 + 2988 1443 1438 1442 -1 + 2989 1442 1438 1445 -1 + 2990 2282 2504 2480 -1 + 2991 2276 2480 2504 -1 + 2992 1601 1600 4100 -1 + 2993 2091 38 37 -1 + 2994 2091 2094 38 -1 + 2995 1351 1353 1432 -1 + 2996 1423 1353 1351 -1 + 2997 1432 1435 1433 -1 + 2998 1432 1353 1435 -1 + 2999 2922 2980 2924 -1 + 3000 2923 2980 2922 -1 + 3001 4063 1315 1216 -1 + 3002 2051 2055 2046 -1 + 3003 2046 2050 2051 -1 + 3004 4367 2801 2800 -1 + 3005 2991 2940 2914 -1 + 3006 2991 2921 2940 -1 + 3007 2926 2964 2992 -1 + 3008 2983 2964 2926 -1 + 3009 1800 1864 1860 -1 + 3010 1800 1777 1864 -1 + 3011 2338 2400 2335 -1 + 3012 2335 2336 2338 -1 + 3013 25 559 497 -1 + 3014 2076 2074 2093 -1 + 3015 2075 2093 2074 -1 + 3016 1953 1952 1945 -1 + 3017 1945 1952 1946 -1 + 3018 2058 11 25 -1 + 3019 2087 11 2058 -1 + 3020 2094 2090 2093 -1 + 3021 2084 2093 2090 -1 + 3022 2291 2292 2294 -1 + 3023 2291 2219 2292 -1 + 3024 38 2094 2075 -1 + 3025 2075 2094 2093 -1 + 3026 38 2072 42 -1 + 3027 2075 2072 38 -1 + 3028 1105 1104 978 -1 + 3029 978 1093 1105 -1 + 3030 2085 2094 2091 -1 + 3031 2090 2094 2085 -1 + 3032 192 191 193 -1 + 3033 193 191 194 -1 + 3034 52 569 566 -1 + 3035 567 566 569 -1 + 3036 1848 1839 1849 -1 + 3037 1854 1849 1839 -1 + 3038 2335 2393 2336 -1 + 3039 2336 2393 2339 -1 + 3040 2946 2719 2945 -1 + 3041 2945 2719 2721 -1 + 3042 682 677 693 -1 + 3043 693 680 682 -1 + 3044 2192 2183 2204 -1 + 3045 2192 2201 2183 -1 + 3046 4204 1714 1688 -1 + 3047 1162 1294 1303 -1 + 3048 1162 1304 1294 -1 + 3049 616 615 617 -1 + 3050 616 624 615 -1 + 3051 2369 2370 2241 -1 + 3052 2390 2241 2370 -1 + 3053 840 2409 2380 -1 + 3054 2371 2380 2409 -1 + 3055 1341 2089 1337 -1 + 3056 2092 2057 1349 -1 + 3057 2092 2054 2057 -1 + 3058 2248 2212 2238 -1 + 3059 2237 2238 2212 -1 + 3060 687 662 688 -1 + 3061 686 662 687 -1 + 3062 2448 2515 2447 -1 + 3063 2448 2446 2515 -1 + 3064 2515 2516 2513 -1 + 3065 2515 2517 2516 -1 + 3066 2863 3271 3157 -1 + 3067 883 892 2341 -1 + 3068 884 892 883 -1 + 3069 862 627 860 -1 + 3070 862 628 627 -1 + 3071 1201 747 1058 -1 + 3072 775 747 1201 -1 + 3073 433 405 404 -1 + 3074 433 434 405 -1 + 3075 766 767 768 -1 + 3076 766 771 767 -1 + 3077 1622 1624 1628 -1 + 3078 838 841 840 -1 + 3079 2409 840 841 -1 + 3080 1994 2036 2035 -1 + 3081 2035 2039 1994 -1 + 3082 897 885 908 -1 + 3083 884 908 885 -1 + 3084 1373 1364 1386 -1 + 3085 1386 1364 1384 -1 + 3086 2548 2560 2581 -1 + 3087 2546 2581 2560 -1 + 3088 1314 1318 1268 -1 + 3089 1268 1318 1231 -1 + 3090 2352 2368 2366 -1 + 3091 2366 764 2352 -1 + 3092 2344 2367 2368 -1 + 3093 429 437 436 -1 + 3094 429 441 437 -1 + 3095 2041 2034 2035 -1 + 3096 2035 2034 2043 -1 + 3097 1217 1594 1219 -1 + 3098 1320 1219 1594 -1 + 3099 433 437 440 -1 + 3100 433 439 437 -1 + 3101 839 850 842 -1 + 3102 838 850 839 -1 + 3103 159 1998 1988 -1 + 3104 162 1998 159 -1 + 3105 220 219 222 -1 + 3106 218 219 220 -1 + 3107 2368 2328 2366 -1 + 3108 2367 2328 2368 -1 + 3109 210 178 182 -1 + 3110 210 179 178 -1 + 3111 4165 2845 2828 -1 + 3112 2017 2029 200 -1 + 3113 2017 2020 2029 -1 + 3114 4329 1003 1021 -1 + 3115 992 1021 1003 -1 + 3116 899 900 909 -1 + 3117 899 898 900 -1 + 3118 2325 2326 2324 -1 + 3119 2324 895 2325 -1 + 3120 1517 1512 2018 -1 + 3121 2018 1512 1513 -1 + 3122 4069 3884 4009 -1 + 3123 4435 4410 3923 -1 + 3124 3923 4410 2789 -1 + 3125 2741 2743 2773 -1 + 3126 2792 2773 2743 -1 + 3127 2005 1998 153 -1 + 3128 1989 1998 2005 -1 + 3129 2234 2383 2235 -1 + 3130 2235 2383 870 -1 + 3131 2341 2333 2407 -1 + 3132 2404 2407 2333 -1 + 3133 161 163 162 -1 + 3134 161 549 163 -1 + 3135 2007 2000 2002 -1 + 3136 2007 2001 2000 -1 + 3137 2395 2378 2418 -1 + 3138 2395 2419 2378 -1 + 3139 163 549 154 -1 + 3140 2032 1988 1997 -1 + 3141 2032 159 1988 -1 + 3142 723 717 732 -1 + 3143 718 732 717 -1 + 3144 3021 3150 3142 -1 + 3145 2007 1388 2011 -1 + 3146 2007 2002 1388 -1 + 3147 1383 1363 1365 -1 + 3148 1361 1363 1383 -1 + 3149 970 1007 971 -1 + 3150 2372 2382 2389 -1 + 3151 2376 2382 2372 -1 + 3152 2092 1349 1354 -1 + 3153 3292 4187 3300 -1 + 3154 3414 4112 3440 -1 + 3155 956 962 955 -1 + 3156 2396 2398 844 -1 + 3157 2396 2408 2398 -1 + 3158 2400 2401 2402 -1 + 3159 2402 2335 2400 -1 + 3160 2369 2241 2387 -1 + 3161 2387 876 2369 -1 + 3162 2615 2639 2641 -1 + 3163 2615 663 2639 -1 + 3164 2359 879 2321 -1 + 3165 2321 2342 2359 -1 + 3166 2453 2473 2470 -1 + 3167 2474 2473 2453 -1 + 3168 2397 2408 2416 -1 + 3169 2397 2398 2408 -1 + 3170 2007 2008 2001 -1 + 3171 2001 2008 2004 -1 + 3172 2552 2551 735 -1 + 3173 735 2551 731 -1 + 3174 64 96 87 -1 + 3175 66 96 64 -1 + 3176 1987 1960 1961 -1 + 3177 1961 66 1987 -1 + 3178 2373 2372 2378 -1 + 3179 2378 2372 2377 -1 + 3180 900 884 883 -1 + 3181 900 908 884 -1 + 3182 2593 2638 665 -1 + 3183 2637 2638 2593 -1 + 3184 2456 2427 2472 -1 + 3185 2472 2427 2474 -1 + 3186 2410 2371 2414 -1 + 3187 2409 2414 2371 -1 + 3188 2383 2385 2386 -1 + 3189 2386 2379 2383 -1 + 3190 901 845 843 -1 + 3191 901 910 845 -1 + 3192 2452 2472 2534 -1 + 3193 2534 2472 2451 -1 + 3194 2377 2388 2410 -1 + 3195 2371 2410 2388 -1 + 3196 848 875 872 -1 + 3197 872 834 848 -1 + 3198 848 847 850 -1 + 3199 850 849 848 -1 + 3200 2563 2623 2557 -1 + 3201 2556 2557 2623 -1 + 3202 1702 1703 1409 -1 + 3203 1409 1717 1702 -1 + 3204 2340 2336 2339 -1 + 3205 2339 2334 2340 -1 + 3206 719 2627 2618 -1 + 3207 719 718 2627 -1 + 3208 4197 1745 4040 -1 + 3209 2404 2334 2405 -1 + 3210 2339 2405 2334 -1 + 3211 898 910 911 -1 + 3212 901 911 910 -1 + 3213 2407 2404 2403 -1 + 3214 2403 2404 2405 -1 + 3215 2374 2408 2381 -1 + 3216 2381 2408 2396 -1 + 3217 2411 2415 2406 -1 + 3218 909 2406 2415 -1 + 3219 2542 734 696 -1 + 3220 696 734 733 -1 + 3221 839 899 838 -1 + 3222 838 899 841 -1 + 3223 2341 892 2324 -1 + 3224 2324 2333 2341 -1 + 3225 1715 1716 1683 -1 + 3226 1692 1683 1716 -1 + 3227 734 717 733 -1 + 3228 724 733 717 -1 + 3229 2395 2394 2392 -1 + 3230 2395 2413 2394 -1 + 3231 2615 2596 2594 -1 + 3232 2598 2596 2615 -1 + 3233 899 909 841 -1 + 3234 841 909 2415 -1 + 3235 880 913 879 -1 + 3236 2321 879 913 -1 + 3237 708 603 2582 -1 + 3238 601 603 708 -1 + 3239 2535 2564 2565 -1 + 3240 2535 2562 2564 -1 + 3241 2337 2338 2336 -1 + 3242 2336 2340 2337 -1 + 3243 2329 2330 2337 -1 + 3244 2337 2331 2329 -1 + 3245 2563 2620 2621 -1 + 3246 2621 2623 2563 -1 + 3247 2618 2559 2558 -1 + 3248 2618 2627 2559 -1 + 3249 54 447 488 -1 + 3250 896 772 771 -1 + 3251 771 766 896 -1 + 3252 2337 2340 2331 -1 + 3253 2331 2340 2323 -1 + 3254 766 885 896 -1 + 3255 766 891 885 -1 + 3256 1190 813 1192 -1 + 3257 998 4172 999 -1 + 3258 3956 3884 3955 -1 + 3259 645 3187 3188 -1 + 3260 3207 3188 3187 -1 + 3261 3270 3269 4572 -1 + 3262 198 92 95 -1 + 3263 198 1906 92 -1 + 3264 708 707 601 -1 + 3265 601 707 604 -1 + 3266 736 778 744 -1 + 3267 2258 2250 2256 -1 + 3268 2258 2236 2250 -1 + 3269 701 698 604 -1 + 3270 604 707 701 -1 + 3271 1942 1981 1963 -1 + 3272 1963 1960 1942 -1 + 3273 2641 2642 2597 -1 + 3274 2630 2597 2642 -1 + 3275 2635 2634 2633 -1 + 3276 2635 2636 2634 -1 + 3277 2397 2402 2399 -1 + 3278 2397 2417 2402 -1 + 3279 2234 2385 2383 -1 + 3280 2244 2385 2234 -1 + 3281 182 181 183 -1 + 3282 180 183 181 -1 + 3283 1973 1971 1970 -1 + 3284 1970 1901 1973 -1 + 3285 1969 1971 1974 -1 + 3286 1973 1974 1971 -1 + 3287 2399 2343 902 -1 + 3288 902 2343 912 -1 + 3289 1910 1900 1970 -1 + 3290 1970 1909 1910 -1 + 3291 1063 2586 2584 -1 + 3292 2584 2572 1063 -1 + 3293 1286 1287 1292 -1 + 3294 2642 2617 2619 -1 + 3295 2616 2617 2642 -1 + 3296 1105 1107 1104 -1 + 3297 961 1104 1107 -1 + 3298 1981 1972 1963 -1 + 3299 1982 1972 1981 -1 + 3300 845 846 903 -1 + 3301 845 842 846 -1 + 3302 4763 1737 4116 -1 + 3303 3556 3409 3438 -1 + 3304 4739 4717 1179 -1 + 3305 950 958 959 -1 + 3306 2599 2603 2598 -1 + 3307 2598 2603 2596 -1 + 3308 2469 2420 2465 -1 + 3309 2469 2421 2420 -1 + 3310 1002 1018 4013 -1 + 3311 2085 2086 2095 -1 + 3312 2095 2053 2085 -1 + 3313 673 655 654 -1 + 3314 673 657 655 -1 + 3315 1494 1482 1484 -1 + 3316 1484 1483 1494 -1 + 3317 2600 2630 2631 -1 + 3318 2600 2597 2630 -1 + 3319 2467 2468 2470 -1 + 3320 2470 2473 2467 -1 + 3321 2433 2448 675 -1 + 3322 2422 2448 2433 -1 + 3323 2027 1508 2063 -1 + 3324 2223 2222 2231 -1 + 3325 2227 2222 2223 -1 + 3326 2513 2447 2515 -1 + 3327 2513 19 2447 -1 + 3328 1094 970 1095 -1 + 3329 2252 2210 2214 -1 + 3330 2214 2207 2252 -1 + 3331 2027 495 1508 -1 + 3332 2447 680 675 -1 + 3333 675 2448 2447 -1 + 3334 2463 2604 2462 -1 + 3335 2463 2606 2604 -1 + 3336 2232 869 625 -1 + 3337 625 869 855 -1 + 3338 2463 2468 2471 -1 + 3339 2463 2470 2468 -1 + 3340 2461 2471 2469 -1 + 3341 2468 2469 2471 -1 + 3342 2604 2599 2602 -1 + 3343 2603 2599 2604 -1 + 3344 2206 2254 2251 -1 + 3345 2246 2254 2206 -1 + 3346 653 672 671 -1 + 3347 653 2614 672 -1 + 3348 2230 2305 2228 -1 + 3349 2231 2228 2305 -1 + 3350 4651 4294 4404 -1 + 3351 2239 2238 2237 -1 + 3352 2237 2233 2239 -1 + 3353 692 2433 690 -1 + 3354 2465 2433 692 -1 + 3355 1782 1790 1789 -1 + 3356 1782 1783 1790 -1 + 3357 583 588 1208 -1 + 3358 2288 2267 2270 -1 + 3359 2270 2269 2288 -1 + 3360 4488 4240 4169 -1 + 3361 2220 2295 2221 -1 + 3362 2219 2221 2295 -1 + 3363 2486 2485 2526 -1 + 3364 57 2526 2485 -1 + 3365 2265 2263 2274 -1 + 3366 2264 2263 2265 -1 + 3367 4086 3700 3703 -1 + 3368 2493 2495 2276 -1 + 3369 2493 2494 2495 -1 + 3370 3163 3136 1333 -1 + 3371 3164 1333 3136 -1 + 3372 1006 1008 1005 -1 + 3373 1006 948 1008 -1 + 3374 2491 2490 2483 -1 + 3375 1752 1759 1762 -1 + 3376 126 125 1986 -1 + 3377 1986 125 1941 -1 + 3378 2509 2510 2508 -1 + 3379 2509 2507 2510 -1 + 3380 1522 1529 1523 -1 + 3381 1522 1521 1529 -1 + 3382 2059 1425 1434 -1 + 3383 4708 1120 1118 -1 + 3384 2022 12 2027 -1 + 3385 4167 2145 1038 -1 + 3386 2125 1038 2145 -1 + 3387 2428 2430 2429 -1 + 3388 2428 2449 2430 -1 + 3389 99 80 72 -1 + 3390 99 1776 80 -1 + 3391 111 108 110 -1 + 3392 110 134 111 -1 + 3393 678 682 681 -1 + 3394 19 681 682 -1 + 3395 2530 2517 2514 -1 + 3396 2530 2516 2517 -1 + 3397 438 222 202 -1 + 3398 438 229 222 -1 + 3399 206 143 203 -1 + 3400 203 215 206 -1 + 3401 2437 2508 2438 -1 + 3402 2437 2509 2508 -1 + 3403 2454 653 661 -1 + 3404 2460 653 2454 -1 + 3405 1282 1332 1293 -1 + 3406 1282 1329 1332 -1 + 3407 409 414 417 -1 + 3408 1466 417 414 -1 + 3409 2226 2218 635 -1 + 3410 2226 2301 2218 -1 + 3411 689 680 693 -1 + 3412 675 680 689 -1 + 3413 694 677 2432 -1 + 3414 694 693 677 -1 + 3415 663 664 720 -1 + 3416 722 720 664 -1 + 3417 651 659 657 -1 + 3418 657 659 655 -1 + 3419 1269 1280 1281 -1 + 3420 864 874 876 -1 + 3421 876 2387 864 -1 + 3422 2548 2555 2560 -1 + 3423 2548 2550 2555 -1 + 3424 2458 2605 2606 -1 + 3425 652 2605 2458 -1 + 3426 1085 1095 967 -1 + 3427 970 967 1095 -1 + 3428 2251 2207 2206 -1 + 3429 2251 2252 2207 -1 + 3430 2213 2222 2212 -1 + 3431 2212 2222 2261 -1 + 3432 2291 2216 2219 -1 + 3433 2219 2216 2221 -1 + 3434 624 2211 2210 -1 + 3435 616 2211 624 -1 + 3436 26 504 505 -1 + 3437 2209 2207 2215 -1 + 3438 2214 2215 2207 -1 + 3439 856 855 878 -1 + 3440 625 855 856 -1 + 3441 856 615 625 -1 + 3442 625 615 854 -1 + 3443 875 849 2384 -1 + 3444 875 848 849 -1 + 3445 4711 4635 4403 -1 + 3446 630 617 856 -1 + 3447 615 856 617 -1 + 3448 2228 2213 2208 -1 + 3449 2208 2253 2228 -1 + 3450 2436 2432 679 -1 + 3451 677 679 2432 -1 + 3452 630 856 878 -1 + 3453 878 865 630 -1 + 3454 2258 2235 2236 -1 + 3455 2232 2236 2235 -1 + 3456 2480 2482 58 -1 + 3457 2480 2481 2482 -1 + 3458 2060 2024 2062 -1 + 3459 2061 2024 2060 -1 + 3460 2229 2293 2319 -1 + 3461 2319 2293 2294 -1 + 3462 2521 2507 2518 -1 + 3463 2521 2510 2507 -1 + 3464 2223 2304 2302 -1 + 3465 2314 2304 2223 -1 + 3466 62 2529 57 -1 + 3467 57 2529 2526 -1 + 3468 1434 1349 2057 -1 + 3469 7 25 11 -1 + 3470 7 559 25 -1 + 3471 950 959 969 -1 + 3472 2308 2219 2295 -1 + 3473 2292 2219 2308 -1 + 3474 26 505 47 -1 + 3475 2272 2292 2308 -1 + 3476 2272 2317 2292 -1 + 3477 2300 637 634 -1 + 3478 636 634 637 -1 + 3479 3061 4205 3063 -1 + 3480 2158 2197 2154 -1 + 3481 2164 2197 2158 -1 + 3482 2209 2206 2207 -1 + 3483 2209 2253 2206 -1 + 3484 2229 2230 2293 -1 + 3485 2305 2230 2229 -1 + 3486 2223 2231 2314 -1 + 3487 2314 2231 2305 -1 + 3488 2229 2319 2306 -1 + 3489 2306 2315 2229 -1 + 3490 2428 2429 2445 -1 + 3491 2431 2445 2429 -1 + 3492 4511 4487 1109 -1 + 3493 2220 2217 646 -1 + 3494 2221 2217 2220 -1 + 3495 4086 3095 3280 -1 + 3496 2294 2215 2291 -1 + 3497 2294 2293 2215 -1 + 3498 2318 2285 2269 -1 + 3499 2269 2270 2318 -1 + 3500 620 619 618 -1 + 3501 618 613 620 -1 + 3502 620 646 619 -1 + 3503 2217 619 646 -1 + 3504 2311 2312 2310 -1 + 3505 2271 2310 2312 -1 + 3506 2298 2273 2308 -1 + 3507 2272 2308 2273 -1 + 3508 4255 1139 4026 -1 + 3509 2267 2316 2270 -1 + 3510 2271 2316 2267 -1 + 3511 47 20 43 -1 + 3512 2047 43 20 -1 + 3513 2445 2441 2442 -1 + 3514 2440 2441 2445 -1 + 3515 4008 2750 2733 -1 + 3516 71 196 86 -1 + 3517 86 195 71 -1 + 3518 2299 2307 2298 -1 + 3519 2299 593 2307 -1 + 3520 1208 2309 2313 -1 + 3521 1208 588 2309 -1 + 3522 2220 638 2299 -1 + 3523 2299 2295 2220 -1 + 3524 2070 1427 2059 -1 + 3525 4144 1278 1277 -1 + 3526 4144 1276 1278 -1 + 3527 2045 2055 2056 -1 + 3528 2045 2046 2055 -1 + 3529 578 582 583 -1 + 3530 583 582 1200 -1 + 3531 2064 2062 2023 -1 + 3532 587 637 588 -1 + 3533 2309 588 637 -1 + 3534 2483 2502 2491 -1 + 3535 1289 1299 1298 -1 + 3536 2491 2502 2490 -1 + 3537 4065 1207 4673 -1 + 3538 1044 4547 3936 -1 + 3539 1551 1550 1151 -1 + 3540 1575 1550 1551 -1 + 3541 937 954 939 -1 + 3542 1094 1007 970 -1 + 3543 3453 3446 4371 -1 + 3544 3453 4307 3446 -1 + 3545 4752 2790 4726 -1 + 3546 3072 2790 4752 -1 + 3547 2530 2526 2516 -1 + 3548 2529 2516 2526 -1 + 3549 1024 955 936 -1 + 3550 956 955 1024 -1 + 3551 937 952 954 -1 + 3552 937 953 952 -1 + 3553 2374 2419 2416 -1 + 3554 2416 2408 2374 -1 + 3555 2303 2302 2304 -1 + 3556 2303 2296 2302 -1 + 3557 1017 1002 1022 -1 + 3558 1017 1018 1002 -1 + 3559 2542 696 2547 -1 + 3560 2547 2543 2542 -1 + 3561 4689 4604 4279 -1 + 3562 339 740 347 -1 + 3563 339 814 740 -1 + 3564 1330 3156 996 -1 + 3565 1262 996 3156 -1 + 3566 3138 3057 4472 -1 + 3567 3138 4235 3057 -1 + 3568 927 1015 928 -1 + 3569 927 1016 1015 -1 + 3570 4208 1074 1646 -1 + 3571 1646 1074 1077 -1 + 3572 947 946 926 -1 + 3573 926 946 924 -1 + 3574 4362 1153 1140 -1 + 3575 947 975 946 -1 + 3576 924 1013 929 -1 + 3577 1010 1012 931 -1 + 3578 1009 1012 1010 -1 + 3579 1745 1746 1748 -1 + 3580 930 1012 1008 -1 + 3581 1104 961 947 -1 + 3582 4100 1600 1213 -1 + 3583 989 990 4477 -1 + 3584 1991 1996 1995 -1 + 3585 944 949 1096 -1 + 3586 1007 1096 949 -1 + 3587 944 1096 943 -1 + 3588 3222 3298 3226 -1 + 3589 4208 1075 1074 -1 + 3590 1014 936 932 -1 + 3591 226 227 195 -1 + 3592 195 86 226 -1 + 3593 432 204 4216 -1 + 3594 432 4213 204 -1 + 3595 563 564 557 -1 + 3596 563 562 564 -1 + 3597 3232 3198 3228 -1 + 3598 2753 3346 2748 -1 + 3599 2748 3346 3311 -1 + 3600 3047 3048 3143 -1 + 3601 4097 2853 4002 -1 + 3602 2662 2658 4529 -1 + 3603 4034 1623 2348 -1 + 3604 1124 1108 1092 -1 + 3605 1092 1108 1091 -1 + 3606 1010 1015 1009 -1 + 3607 933 1009 1015 -1 + 3608 1274 1275 1272 -1 + 3609 3156 3037 1262 -1 + 3610 3156 3039 3037 -1 + 3611 1323 1256 3156 -1 + 3612 3156 1256 3155 -1 + 3613 1703 1707 1409 -1 + 3614 2872 3931 4693 -1 + 3615 985 1260 1258 -1 + 3616 1355 1360 1359 -1 + 3617 1359 1337 1355 -1 + 3618 1137 1138 1657 -1 + 3619 1657 1138 1663 -1 + 3620 3292 4003 3268 -1 + 3621 3130 3171 3152 -1 + 3622 4130 993 4460 -1 + 3623 3129 3121 3160 -1 + 3624 3227 3304 3305 -1 + 3625 1762 1722 1752 -1 + 3626 3222 3258 3260 -1 + 3627 3486 3454 4416 -1 + 3628 3222 3299 3298 -1 + 3629 3260 3299 3222 -1 + 3630 1124 1082 1108 -1 + 3631 1089 1108 1082 -1 + 3632 1059 1071 1066 -1 + 3633 1059 1072 1071 -1 + 3634 935 1090 1092 -1 + 3635 4586 4325 4574 -1 + 3636 4633 3662 4409 -1 + 3637 4246 4409 3662 -1 + 3638 1232 1274 1271 -1 + 3639 1275 1274 1232 -1 + 3640 1103 1101 964 -1 + 3641 1025 1101 1103 -1 + 3642 1084 1132 4207 -1 + 3643 4049 4579 4332 -1 + 3644 4165 4097 3338 -1 + 3645 935 932 1090 -1 + 3646 935 934 932 -1 + 3647 4314 1644 1645 -1 + 3648 56 812 813 -1 + 3649 1192 813 812 -1 + 3650 1274 985 1258 -1 + 3651 1617 1069 1220 -1 + 3652 1220 1069 2577 -1 + 3653 1016 1026 973 -1 + 3654 973 1026 932 -1 + 3655 940 951 924 -1 + 3656 1018 1019 1132 -1 + 3657 1715 1721 1716 -1 + 3658 4258 1691 1245 -1 + 3659 951 938 924 -1 + 3660 950 969 957 -1 + 3661 957 956 950 -1 + 3662 964 968 1098 -1 + 3663 1101 968 964 -1 + 3664 3168 1257 1273 -1 + 3665 1568 1570 1643 -1 + 3666 1643 1641 1568 -1 + 3667 939 954 958 -1 + 3668 4208 1646 4083 -1 + 3669 1014 1026 1013 -1 + 3670 1014 932 1026 -1 + 3671 1657 1662 1658 -1 + 3672 1259 1704 1703 -1 + 3673 1697 1703 1704 -1 + 3674 1321 1322 1219 -1 + 3675 1319 1219 1322 -1 + 3676 4685 3958 4069 -1 + 3677 4772 4223 4766 -1 + 3678 342 726 725 -1 + 3679 725 814 342 -1 + 3680 4637 4356 4506 -1 + 3681 1615 1554 1220 -1 + 3682 1151 1573 1549 -1 + 3683 4606 4507 311 -1 + 3684 311 4452 4606 -1 + 3685 3886 3285 4491 -1 + 3686 4656 3053 4312 -1 + 3687 4244 4312 3053 -1 + 3688 4656 4178 4649 -1 + 3689 1594 1217 1592 -1 + 3690 2124 2126 2134 -1 + 3691 2124 2125 2126 -1 + 3692 1154 1647 1637 -1 + 3693 2675 2674 2671 -1 + 3694 1185 1184 1183 -1 + 3695 1185 2579 1184 -1 + 3696 1069 2576 2577 -1 + 3697 1069 2580 2576 -1 + 3698 1930 1931 1929 -1 + 3699 1930 1079 1931 -1 + 3700 1219 1319 1218 -1 + 3701 1560 1218 1319 -1 + 3702 4537 1562 4239 -1 + 3703 1073 1071 1619 -1 + 3704 741 1227 1224 -1 + 3705 4401 3623 3593 -1 + 3706 1971 1909 1970 -1 + 3707 1971 1968 1909 -1 + 3708 208 214 216 -1 + 3709 216 214 213 -1 + 3710 229 204 4634 -1 + 3711 229 4087 204 -1 + 3712 4606 4452 3313 -1 + 3713 4724 4664 185 -1 + 3714 747 750 751 -1 + 3715 1599 2345 1049 -1 + 3716 4488 4169 4233 -1 + 3717 1212 1136 1234 -1 + 3718 1234 1312 1212 -1 + 3719 1579 1593 1217 -1 + 3720 1592 1217 1593 -1 + 3721 84 752 814 -1 + 3722 814 725 84 -1 + 3723 1599 1624 2345 -1 + 3724 1614 1612 1613 -1 + 3725 4133 1215 1561 -1 + 3726 999 1000 4342 -1 + 3727 999 4172 1000 -1 + 3728 1631 2030 1036 -1 + 3729 1900 1891 1898 -1 + 3730 1900 1910 1891 -1 + 3731 1498 2659 2660 -1 + 3732 2695 2690 3336 -1 + 3733 2331 2323 2358 -1 + 3734 1222 2135 1204 -1 + 3735 4046 4122 4013 -1 + 3736 4442 4406 1405 -1 + 3737 1405 4406 1417 -1 + 3738 2905 2907 2904 -1 + 3739 2904 2907 2913 -1 + 3740 4059 4035 1660 -1 + 3741 1660 1656 4059 -1 + 3742 2037 2043 2034 -1 + 3743 218 2043 2037 -1 + 3744 3620 3593 3596 -1 + 3745 3272 3157 3271 -1 + 3746 2132 1844 4225 -1 + 3747 4124 1393 4066 -1 + 3748 342 343 728 -1 + 3749 728 727 342 -1 + 3750 640 632 642 -1 + 3751 2355 2347 2346 -1 + 3752 2346 2364 2355 -1 + 3753 2871 4057 4247 -1 + 3754 4291 3115 3114 -1 + 3755 751 1057 1058 -1 + 3756 1058 1057 1056 -1 + 3757 3288 3125 4022 -1 + 3758 2352 1201 1058 -1 + 3759 1929 1931 1886 -1 + 3760 1886 1928 1929 -1 + 3761 1627 1068 2349 -1 + 3762 2350 2349 1068 -1 + 3763 2323 2327 2358 -1 + 3764 1061 2571 2576 -1 + 3765 3204 633 3188 -1 + 3766 1022 988 4340 -1 + 3767 1022 4460 988 -1 + 3768 1628 1625 1626 -1 + 3769 4586 3454 4195 -1 + 3770 4025 3454 4586 -1 + 3771 1614 1613 1068 -1 + 3772 4619 2818 4516 -1 + 3773 4406 1730 1412 -1 + 3774 1412 1408 4406 -1 + 3775 3502 3400 3545 -1 + 3776 1067 1068 1627 -1 + 3777 815 165 812 -1 + 3778 1116 1240 1672 -1 + 3779 1116 1276 1240 -1 + 3780 1774 1413 1773 -1 + 3781 1410 1770 1408 -1 + 3782 2360 764 2366 -1 + 3783 2360 765 764 -1 + 3784 4133 1315 4063 -1 + 3785 161 432 549 -1 + 3786 2348 1628 1626 -1 + 3787 2348 1622 1628 -1 + 3788 2135 2137 1188 -1 + 3789 1149 1243 4060 -1 + 3790 1211 1650 1214 -1 + 3791 2351 2350 1228 -1 + 3792 1050 1056 1055 -1 + 3793 1055 1054 1050 -1 + 3794 1050 1047 1048 -1 + 3795 1048 1056 1050 -1 + 3796 85 200 2029 -1 + 3797 201 200 85 -1 + 3798 1892 1889 1894 -1 + 3799 1890 1889 1892 -1 + 3800 2350 2351 2363 -1 + 3801 2363 2351 1227 -1 + 3802 2350 1613 1228 -1 + 3803 2350 1068 1613 -1 + 3804 2575 2574 2570 -1 + 3805 2575 2571 2574 -1 + 3806 2353 770 2365 -1 + 3807 769 770 2353 -1 + 3808 2139 3591 3589 -1 + 3809 3589 2123 2139 -1 + 3810 2033 1374 1517 -1 + 3811 1512 1517 1374 -1 + 3812 744 779 769 -1 + 3813 744 778 779 -1 + 3814 1583 1597 1598 -1 + 3815 596 2578 2570 -1 + 3816 2575 2570 2578 -1 + 3817 1596 1037 1595 -1 + 3818 1554 1617 1220 -1 + 3819 3411 3437 4036 -1 + 3820 85 2029 1203 -1 + 3821 1203 2029 2028 -1 + 3822 1925 1915 1895 -1 + 3823 1895 1932 1925 -1 + 3824 1057 1203 2028 -1 + 3825 1249 1241 4035 -1 + 3826 214 183 180 -1 + 3827 180 212 214 -1 + 3828 4111 2854 2855 -1 + 3829 1135 1000 4612 -1 + 3830 2354 1202 1227 -1 + 3831 1227 1202 2363 -1 + 3832 1615 1220 1613 -1 + 3833 1613 1220 1228 -1 + 3834 3583 4019 3580 -1 + 3835 3997 2683 3985 -1 + 3836 2331 2357 2329 -1 + 3837 2331 2358 2357 -1 + 3838 750 747 737 -1 + 3839 602 2570 2583 -1 + 3840 2583 2570 2574 -1 + 3841 105 104 116 -1 + 3842 1032 1034 2653 -1 + 3843 1086 4214 1083 -1 + 3844 4059 1249 4035 -1 + 3845 4024 1249 4059 -1 + 3846 1633 1651 1652 -1 + 3847 738 745 736 -1 + 3848 1129 1392 1179 -1 + 3849 1598 1597 1213 -1 + 3850 4513 4310 3714 -1 + 3851 1054 1053 1050 -1 + 3852 1563 1560 1562 -1 + 3853 1218 1560 1563 -1 + 3854 1194 2575 2578 -1 + 3855 2576 2575 1194 -1 + 3856 4088 1563 1559 -1 + 3857 1559 1563 1562 -1 + 3858 2031 1035 2030 -1 + 3859 4165 3338 2845 -1 + 3860 4162 2703 2887 -1 + 3861 2887 2703 2886 -1 + 3862 4100 1213 1214 -1 + 3863 1214 1213 1211 -1 + 3864 4080 1713 1714 -1 + 3865 2917 2659 2918 -1 + 3866 3188 3207 3204 -1 + 3867 4497 4223 1738 -1 + 3868 4577 3103 3096 -1 + 3869 3273 3158 3272 -1 + 3870 3272 2880 2895 -1 + 3871 1052 2030 1631 -1 + 3872 4614 4218 3113 -1 + 3873 2129 2132 4225 -1 + 3874 3072 3594 2790 -1 + 3875 2549 2571 2572 -1 + 3876 2549 2574 2571 -1 + 3877 1037 1597 1583 -1 + 3878 1633 1662 1663 -1 + 3879 1663 1662 1657 -1 + 3880 84 753 752 -1 + 3881 750 752 753 -1 + 3882 1633 1663 1651 -1 + 3883 3016 1160 4135 -1 + 3884 2677 2659 2684 -1 + 3885 2684 2659 1498 -1 + 3886 1509 1511 1510 -1 + 3887 1509 1515 1511 -1 + 3888 3830 2702 4458 -1 + 3889 3830 3804 2702 -1 + 3890 4530 3716 4151 -1 + 3891 90 94 189 -1 + 3892 95 94 90 -1 + 3893 179 171 178 -1 + 3894 1913 171 179 -1 + 3895 2330 2361 2322 -1 + 3896 2362 2322 2361 -1 + 3897 1650 1652 1651 -1 + 3898 3155 3023 3039 -1 + 3899 1071 1073 1066 -1 + 3900 2584 2538 2585 -1 + 3901 2584 2536 2538 -1 + 3902 79 1965 1976 -1 + 3903 74 1965 79 -1 + 3904 1214 1312 4056 -1 + 3905 1214 1212 1312 -1 + 3906 794 345 826 -1 + 3907 3477 3479 3478 -1 + 3908 3477 3448 3479 -1 + 3909 2658 2652 4529 -1 + 3910 1934 1933 1076 -1 + 3911 2461 2465 692 -1 + 3912 2469 2465 2461 -1 + 3913 4246 3663 3636 -1 + 3914 3688 3663 4246 -1 + 3915 826 172 794 -1 + 3916 794 172 795 -1 + 3917 22 21 2127 -1 + 3918 24 21 22 -1 + 3919 1602 1557 4110 -1 + 3920 4767 4748 3484 -1 + 3921 2705 1046 1039 -1 + 3922 4247 2872 2871 -1 + 3923 1915 1921 1895 -1 + 3924 1923 1921 1915 -1 + 3925 2124 2137 2125 -1 + 3926 1084 1085 1126 -1 + 3927 4027 1314 1268 -1 + 3928 2127 21 2130 -1 + 3929 2130 2134 2127 -1 + 3930 4268 4152 1581 -1 + 3931 1581 1580 4268 -1 + 3932 4541 4311 4511 -1 + 3933 1644 1636 1647 -1 + 3934 1551 1150 1575 -1 + 3935 1575 1150 1574 -1 + 3936 3164 3165 1333 -1 + 3937 3616 3617 3619 -1 + 3938 3619 3618 3616 -1 + 3939 88 96 97 -1 + 3940 88 189 96 -1 + 3941 1325 1332 1324 -1 + 3942 1324 1291 1325 -1 + 3943 2139 3592 3591 -1 + 3944 2139 2146 3592 -1 + 3945 4133 1560 1315 -1 + 3946 1319 1315 1560 -1 + 3947 1911 1042 1043 -1 + 3948 1038 1046 1045 -1 + 3949 3595 3608 4236 -1 + 3950 24 33 21 -1 + 3951 466 33 24 -1 + 3952 823 824 821 -1 + 3953 795 808 815 -1 + 3954 795 172 808 -1 + 3955 825 181 172 -1 + 3956 825 185 181 -1 + 3957 1128 1747 1745 -1 + 3958 3626 3625 2713 -1 + 3959 171 1913 166 -1 + 3960 1917 166 1913 -1 + 3961 1039 2136 1040 -1 + 3962 816 795 815 -1 + 3963 793 795 816 -1 + 3964 1404 1394 4602 -1 + 3965 1404 1405 1394 -1 + 3966 598 597 611 -1 + 3967 611 594 598 -1 + 3968 2552 2541 2551 -1 + 3969 2554 2541 2552 -1 + 3970 1034 4102 2653 -1 + 3971 1176 1117 1113 -1 + 3972 598 605 597 -1 + 3973 597 605 610 -1 + 3974 1911 1043 1924 -1 + 3975 1924 1043 1221 -1 + 3976 2141 2139 2123 -1 + 3977 2141 2146 2139 -1 + 3978 1648 1631 1036 -1 + 3979 1190 1191 1189 -1 + 3980 1189 1188 1190 -1 + 3981 1928 1927 1926 -1 + 3982 1928 1886 1927 -1 + 3983 4087 441 430 -1 + 3984 429 430 441 -1 + 3985 4611 4527 4352 -1 + 3986 1204 1221 1222 -1 + 3987 1615 1556 1554 -1 + 3988 1555 1554 1556 -1 + 3989 165 166 812 -1 + 3990 1192 812 166 -1 + 3991 597 610 612 -1 + 3992 2567 612 610 -1 + 3993 1295 1167 1307 -1 + 3994 1307 1167 1308 -1 + 3995 605 709 606 -1 + 3996 600 709 605 -1 + 3997 2550 2585 2538 -1 + 3998 2550 2548 2585 -1 + 3999 1568 1646 1569 -1 + 4000 1640 1646 1568 -1 + 4001 2286 2288 2269 -1 + 4002 1151 1550 1573 -1 + 4003 742 786 743 -1 + 4004 1229 1604 1603 -1 + 4005 1552 1547 1548 -1 + 4006 1552 1553 1547 -1 + 4007 4337 3895 4413 -1 + 4008 1217 1219 1218 -1 + 4009 1596 1599 1037 -1 + 4010 3313 4398 4337 -1 + 4011 165 171 166 -1 + 4012 165 808 171 -1 + 4013 4108 1677 1253 -1 + 4014 1253 1677 1238 -1 + 4015 1069 1620 1616 -1 + 4016 826 823 821 -1 + 4017 826 611 823 -1 + 4018 728 706 704 -1 + 4019 728 710 706 -1 + 4020 3332 3329 3328 -1 + 4021 594 599 598 -1 + 4022 594 595 599 -1 + 4023 1190 1192 1191 -1 + 4024 796 792 728 -1 + 4025 728 792 713 -1 + 4026 2539 2536 2537 -1 + 4027 2537 2540 2539 -1 + 4028 2678 2677 2681 -1 + 4029 2681 2643 2678 -1 + 4030 3268 4003 3109 -1 + 4031 2676 2678 2675 -1 + 4032 2643 2675 2678 -1 + 4033 1925 1205 1917 -1 + 4034 1925 1924 1205 -1 + 4035 1080 1605 1646 -1 + 4036 22 2127 55 -1 + 4037 2128 55 2127 -1 + 4038 488 447 487 -1 + 4039 487 447 445 -1 + 4040 4505 1565 1566 -1 + 4041 1043 1222 1221 -1 + 4042 1621 1222 1043 -1 + 4043 1647 1636 1637 -1 + 4044 3052 2761 2747 -1 + 4045 3230 2747 2761 -1 + 4046 3117 3118 3110 -1 + 4047 2126 2138 2128 -1 + 4048 2144 2138 2126 -1 + 4049 1332 1333 1324 -1 + 4050 4172 998 4465 -1 + 4051 1241 1249 1242 -1 + 4052 1242 1249 1250 -1 + 4053 3455 3451 3452 -1 + 4054 1912 1892 1934 -1 + 4055 1932 1892 1912 -1 + 4056 966 974 4532 -1 + 4057 1310 1289 1286 -1 + 4058 1062 1063 1059 -1 + 4059 1060 1059 1063 -1 + 4060 1075 1076 1074 -1 + 4061 945 925 944 -1 + 4062 944 925 949 -1 + 4063 3773 3764 3767 -1 + 4064 826 825 172 -1 + 4065 821 825 826 -1 + 4066 3086 3012 1174 -1 + 4067 4718 1216 4099 -1 + 4068 1097 1084 4207 -1 + 4069 1097 1094 1084 -1 + 4070 1616 1071 1072 -1 + 4071 4721 4363 4593 -1 + 4072 4585 4593 4363 -1 + 4073 1060 1061 1072 -1 + 4074 1072 1061 2580 -1 + 4075 1153 1251 1248 -1 + 4076 609 595 594 -1 + 4077 609 596 595 -1 + 4078 1112 1113 1400 -1 + 4079 4492 315 4070 -1 + 4080 4362 4026 4271 -1 + 4081 4021 1554 1216 -1 + 4082 1216 1554 1555 -1 + 4083 2146 2145 3592 -1 + 4084 2579 1230 1184 -1 + 4085 4696 4354 4360 -1 + 4086 1061 1060 2573 -1 + 4087 4764 4615 3389 -1 + 4088 1564 1071 1552 -1 + 4089 1553 1552 1071 -1 + 4090 4687 4491 4420 -1 + 4091 1178 1176 1159 -1 + 4092 2569 2568 2589 -1 + 4093 2589 2566 2569 -1 + 4094 1684 1689 4222 -1 + 4095 1163 1295 1304 -1 + 4096 1294 1304 1295 -1 + 4097 1184 1918 1186 -1 + 4098 1186 1183 1184 -1 + 4099 4498 644 643 -1 + 4100 622 643 644 -1 + 4101 1232 1231 4160 -1 + 4102 4771 1114 4751 -1 + 4103 1582 4751 1114 -1 + 4104 2994 3558 2993 -1 + 4105 1333 1309 1292 -1 + 4106 945 944 943 -1 + 4107 4510 4080 1409 -1 + 4108 2966 2968 2967 -1 + 4109 2966 2990 2968 -1 + 4110 1250 1235 3170 -1 + 4111 4667 4351 1668 -1 + 4112 1267 1278 1178 -1 + 4113 1178 1278 1177 -1 + 4114 1611 1609 1610 -1 + 4115 1149 1578 1243 -1 + 4116 1149 1577 1578 -1 + 4117 4281 639 4079 -1 + 4118 4068 4079 639 -1 + 4119 1606 1574 1572 -1 + 4120 1321 1232 4160 -1 + 4121 1276 1116 1117 -1 + 4122 4439 1035 2031 -1 + 4123 3060 3134 4149 -1 + 4124 4205 3059 3063 -1 + 4125 1131 1097 4207 -1 + 4126 1131 1093 1097 -1 + 4127 1892 1932 1890 -1 + 4128 1890 1932 1895 -1 + 4129 4489 4428 612 -1 + 4130 4102 1034 2013 -1 + 4131 1635 1152 1154 -1 + 4132 1642 1152 1635 -1 + 4133 2997 3004 4033 -1 + 4134 2997 3003 3004 -1 + 4135 1154 1153 1647 -1 + 4136 4665 4591 4218 -1 + 4137 1737 1744 4116 -1 + 4138 1737 1587 1744 -1 + 4139 985 1313 1092 -1 + 4140 985 1092 1260 -1 + 4141 3122 3117 3123 -1 + 4142 1766 1765 1733 -1 + 4143 3124 3159 3122 -1 + 4144 4734 4709 4453 -1 + 4145 4415 4466 2782 -1 + 4146 1641 1643 1642 -1 + 4147 1312 1311 4056 -1 + 4148 1892 1894 1933 -1 + 4149 1933 1894 1931 -1 + 4150 1294 1291 1303 -1 + 4151 2097 2100 1478 -1 + 4152 2098 2100 2097 -1 + 4153 172 182 178 -1 + 4154 172 181 182 -1 + 4155 4065 578 1208 -1 + 4156 1619 1229 1073 -1 + 4157 166 1917 1191 -1 + 4158 1933 1079 1078 -1 + 4159 4007 2506 2504 -1 + 4160 2276 2504 2506 -1 + 4161 4253 1229 2579 -1 + 4162 1074 1078 1077 -1 + 4163 1253 1238 4023 -1 + 4164 4570 1572 1577 -1 + 4165 1041 1043 1042 -1 + 4166 1932 1924 1925 -1 + 4167 2180 2179 2181 -1 + 4168 2180 2178 2179 -1 + 4169 1956 1939 1940 -1 + 4170 1957 1939 1956 -1 + 4171 3036 3135 3147 -1 + 4172 3372 3365 3614 -1 + 4173 4102 2013 2014 -1 + 4174 1760 1761 1762 -1 + 4175 4157 1141 1182 -1 + 4176 1182 1142 4157 -1 + 4177 343 342 814 -1 + 4178 3603 2781 2786 -1 + 4179 4603 4578 1186 -1 + 4180 1186 4578 1187 -1 + 4181 3514 3503 4264 -1 + 4182 1756 1773 1413 -1 + 4183 1756 1142 1773 -1 + 4184 1586 1281 1280 -1 + 4185 1279 1281 1586 -1 + 4186 4439 1032 1031 -1 + 4187 4147 1695 1700 -1 + 4188 4147 1696 1695 -1 + 4189 1320 1311 4164 -1 + 4190 1320 4056 1311 -1 + 4191 4535 1317 4199 -1 + 4192 4674 4092 3450 -1 + 4193 4355 1187 4520 -1 + 4194 4355 4400 1187 -1 + 4195 3485 3486 4735 -1 + 4196 1323 1331 1272 -1 + 4197 1246 1551 1151 -1 + 4198 1151 1316 1246 -1 + 4199 1271 1274 1258 -1 + 4200 4681 4628 1919 -1 + 4201 1502 1518 4365 -1 + 4202 4576 2920 4317 -1 + 4203 4683 4743 4704 -1 + 4204 4293 3471 3482 -1 + 4205 3482 3471 3472 -1 + 4206 4262 4356 4637 -1 + 4207 2660 2662 4529 -1 + 4208 1699 1027 4270 -1 + 4209 1704 1719 1697 -1 + 4210 1276 1177 1278 -1 + 4211 4153 197 434 -1 + 4212 4149 4030 3060 -1 + 4213 3060 4030 3050 -1 + 4214 4481 2789 4373 -1 + 4215 3923 2789 4481 -1 + 4216 4231 1561 1215 -1 + 4217 3563 3581 3584 -1 + 4218 4433 1654 4582 -1 + 4219 1093 4196 1097 -1 + 4220 3029 1306 4061 -1 + 4221 3029 4041 1306 -1 + 4222 728 794 796 -1 + 4223 728 345 794 -1 + 4224 4512 4038 2884 -1 + 4225 1718 1716 1720 -1 + 4226 1720 1269 1718 -1 + 4227 1578 1577 1572 -1 + 4228 1316 1151 1549 -1 + 4229 1664 1149 1665 -1 + 4230 4748 4644 3484 -1 + 4231 4227 3224 3223 -1 + 4232 1675 1674 1240 -1 + 4233 1769 1406 1772 -1 + 4234 4670 4621 1699 -1 + 4235 1699 4621 1768 -1 + 4236 1711 1722 1706 -1 + 4237 4388 2856 3341 -1 + 4238 3341 2856 3339 -1 + 4239 1641 1640 1568 -1 + 4240 968 969 959 -1 + 4241 4696 3123 4447 -1 + 4242 4343 1281 1669 -1 + 4243 1279 1669 1281 -1 + 4244 1762 1761 1722 -1 + 4245 1112 1159 1176 -1 + 4246 4177 4756 4771 -1 + 4247 1724 1728 1412 -1 + 4248 1410 1412 1728 -1 + 4249 1042 1075 4208 -1 + 4250 3032 3031 3147 -1 + 4251 4609 1004 988 -1 + 4252 1670 1669 1667 -1 + 4253 2191 1459 1467 -1 + 4254 1467 1459 1472 -1 + 4255 4264 4260 2785 -1 + 4256 2785 4260 3923 -1 + 4257 4123 3098 3103 -1 + 4258 4241 3908 3966 -1 + 4259 1768 1772 1406 -1 + 4260 3607 3609 4476 -1 + 4261 4495 3151 3154 -1 + 4262 3154 3151 2996 -1 + 4263 1718 1269 1281 -1 + 4264 3013 3014 3036 -1 + 4265 1754 1753 1755 -1 + 4266 1586 1666 1279 -1 + 4267 1385 1383 1375 -1 + 4268 1741 1737 4768 -1 + 4269 3500 3504 4646 -1 + 4270 1752 1757 1754 -1 + 4271 1711 1705 1710 -1 + 4272 986 997 4477 -1 + 4273 986 4336 997 -1 + 4274 4679 1118 4647 -1 + 4275 1120 4647 1118 -1 + 4276 2798 4042 2799 -1 + 4277 4263 1639 1632 -1 + 4278 1683 1692 1690 -1 + 4279 1098 968 959 -1 + 4280 4322 3940 4625 -1 + 4281 1688 1713 1683 -1 + 4282 204 223 4634 -1 + 4283 1701 1705 1706 -1 + 4284 1741 1587 1737 -1 + 4285 1711 1710 1722 -1 + 4286 1757 1722 1710 -1 + 4287 4216 431 432 -1 + 4288 1682 1686 1687 -1 + 4289 1182 1141 1172 -1 + 4290 1720 1760 1763 -1 + 4291 3417 3562 3556 -1 + 4292 3556 3438 3417 -1 + 4293 1101 1025 1696 -1 + 4294 4613 1655 1654 -1 + 4295 3556 3563 4338 -1 + 4296 4434 4069 3773 -1 + 4297 1678 1683 1693 -1 + 4298 3226 3224 4227 -1 + 4299 1720 1761 1760 -1 + 4300 4187 3856 3300 -1 + 4301 3291 3300 3856 -1 + 4302 3408 3409 3556 -1 + 4303 3043 3169 1267 -1 + 4304 3409 3437 3438 -1 + 4305 1145 1175 3174 -1 + 4306 4736 4734 4453 -1 + 4307 956 957 962 -1 + 4308 1767 1756 1775 -1 + 4309 1400 1398 1399 -1 + 4310 928 924 927 -1 + 4311 929 927 924 -1 + 4312 929 1016 927 -1 + 4313 945 924 925 -1 + 4314 1549 1573 1564 -1 + 4315 1564 1552 1549 -1 + 4316 950 940 939 -1 + 4317 950 956 940 -1 + 4318 4429 1132 1126 -1 + 4319 1126 1132 1084 -1 + 4320 1277 1236 1240 -1 + 4321 4487 4171 4229 -1 + 4322 1401 1391 1399 -1 + 4323 4483 2763 4478 -1 + 4324 4330 4478 2763 -1 + 4325 3053 3546 4244 -1 + 4326 3981 3988 4516 -1 + 4327 3981 2813 3988 -1 + 4328 1697 1700 1701 -1 + 4329 4462 3908 4241 -1 + 4330 1416 1417 1770 -1 + 4331 4607 4543 4704 -1 + 4332 4082 1632 1654 -1 + 4333 4531 1391 4298 -1 + 4334 1025 981 1696 -1 + 4335 937 939 924 -1 + 4336 924 953 937 -1 + 4337 3629 3501 3528 -1 + 4338 2761 3227 3230 -1 + 4339 3118 3120 3131 -1 + 4340 4490 3907 4411 -1 + 4341 1693 1681 1678 -1 + 4342 3172 3082 4136 -1 + 4343 1326 1328 1285 -1 + 4344 1283 1285 1328 -1 + 4345 3599 3603 4600 -1 + 4346 4031 3075 3065 -1 + 4347 3380 3075 4031 -1 + 4348 3483 4564 2667 -1 + 4349 223 222 4634 -1 + 4350 223 220 222 -1 + 4351 3132 3066 3045 -1 + 4352 3132 3064 3066 -1 + 4353 3022 3023 4006 -1 + 4354 1555 1215 4063 -1 + 4355 4757 1736 4756 -1 + 4356 1114 4756 1736 -1 + 4357 1409 1707 4510 -1 + 4358 1769 1728 1406 -1 + 4359 1726 1406 1728 -1 + 4360 1333 1290 3146 -1 + 4361 3012 3036 3031 -1 + 4362 76 82 2167 -1 + 4363 76 75 82 -1 + 4364 748 743 746 -1 + 4365 2464 2431 2466 -1 + 4366 2429 2466 2431 -1 + 4367 729 714 731 -1 + 4368 735 731 714 -1 + 4369 1293 1325 1295 -1 + 4370 2199 2151 105 -1 + 4371 2199 2163 2151 -1 + 4372 1296 1302 1156 -1 + 4373 1806 1825 1805 -1 + 4374 1805 1825 1804 -1 + 4375 1986 1943 1987 -1 + 4376 1987 67 1986 -1 + 4377 1809 1808 1956 -1 + 4378 1955 1956 1808 -1 + 4379 93 94 69 -1 + 4380 93 87 94 -1 + 4381 2451 2474 2453 -1 + 4382 2451 2472 2474 -1 + 4383 2611 2631 2601 -1 + 4384 2600 2631 2611 -1 + 4385 2610 2607 2451 -1 + 4386 2451 2607 2534 -1 + 4387 1899 1890 1896 -1 + 4388 1899 1889 1890 -1 + 4389 126 1986 67 -1 + 4390 67 65 126 -1 + 4391 2412 2410 2414 -1 + 4392 2412 2418 2410 -1 + 4393 1985 1973 1901 -1 + 4394 1901 1982 1985 -1 + 4395 133 81 138 -1 + 4396 139 138 81 -1 + 4397 2555 2557 2556 -1 + 4398 2556 2561 2555 -1 + 4399 1907 1886 1893 -1 + 4400 1927 1886 1907 -1 + 4401 669 670 2594 -1 + 4402 2594 2596 669 -1 + 4403 2590 2545 2544 -1 + 4404 697 2545 2590 -1 + 4405 2613 2593 672 -1 + 4406 672 2593 666 -1 + 4407 1855 1852 2492 -1 + 4408 1902 1904 1903 -1 + 4409 1903 1962 1902 -1 + 4410 69 95 92 -1 + 4411 94 95 69 -1 + 4412 1904 1964 1903 -1 + 4413 187 1964 1904 -1 + 4414 2563 2565 2564 -1 + 4415 2557 2565 2563 -1 + 4416 2628 2629 2633 -1 + 4417 2628 2622 2629 -1 + 4418 1966 1969 1974 -1 + 4419 1966 1967 1969 -1 + 4420 1943 1942 1960 -1 + 4421 1960 1987 1943 -1 + 4422 1983 1974 1973 -1 + 4423 1973 1985 1983 -1 + 4424 4145 1585 1590 -1 + 4425 1280 1590 1585 -1 + 4426 1293 1332 1325 -1 + 4427 2634 2636 2637 -1 + 4428 2638 2637 2636 -1 + 4429 2625 715 2638 -1 + 4430 2638 2636 2625 -1 + 4431 2621 2619 2623 -1 + 4432 2623 2619 2617 -1 + 4433 89 97 187 -1 + 4434 89 88 97 -1 + 4435 228 211 209 -1 + 4436 209 208 228 -1 + 4437 64 100 65 -1 + 4438 65 67 64 -1 + 4439 1986 1941 1943 -1 + 4440 1943 1941 1938 -1 + 4441 1955 1807 1885 -1 + 4442 1808 1807 1955 -1 + 4443 2611 2610 2602 -1 + 4444 2607 2610 2611 -1 + 4445 663 720 719 -1 + 4446 719 2639 663 -1 + 4447 1949 130 1947 -1 + 4448 140 130 1949 -1 + 4449 1809 1956 1940 -1 + 4450 1940 1937 1809 -1 + 4451 194 482 484 -1 + 4452 2200 2203 107 -1 + 4453 2200 2193 2203 -1 + 4454 114 113 122 -1 + 4455 122 1811 114 -1 + 4456 1958 1954 1953 -1 + 4457 1952 1953 1954 -1 + 4458 2184 2181 2179 -1 + 4459 269 287 273 -1 + 4460 268 287 269 -1 + 4461 2533 2531 2484 -1 + 4462 2484 2532 2533 -1 + 4463 3710 3565 3708 -1 + 4464 3710 3692 3565 -1 + 4465 2114 2112 2119 -1 + 4466 2114 2122 2112 -1 + 4467 1944 1947 130 -1 + 4468 1944 1946 1947 -1 + 4469 1952 1954 1801 -1 + 4470 1801 1951 1952 -1 + 4471 1910 1907 1893 -1 + 4472 1893 1891 1910 -1 + 4473 2611 2602 2600 -1 + 4474 2600 2602 2599 -1 + 4475 1455 1490 1485 -1 + 4476 1485 479 1455 -1 + 4477 2104 2099 2108 -1 + 4478 2104 2096 2099 -1 + 4479 1296 1156 3012 -1 + 4480 1931 1079 1933 -1 + 4481 1957 1955 1958 -1 + 4482 1957 1956 1955 -1 + 4483 1949 115 140 -1 + 4484 140 115 137 -1 + 4485 2109 2142 2098 -1 + 4486 716 721 667 -1 + 4487 722 667 721 -1 + 4488 668 673 654 -1 + 4489 654 666 668 -1 + 4490 136 137 139 -1 + 4491 140 137 136 -1 + 4492 217 216 199 -1 + 4493 221 199 216 -1 + 4494 1958 1885 1954 -1 + 4495 1955 1885 1958 -1 + 4496 2626 2636 2635 -1 + 4497 2626 2625 2636 -1 + 4498 2441 2440 2512 -1 + 4499 2512 2440 2435 -1 + 4500 1809 1812 1808 -1 + 4501 1809 1948 1812 -1 + 4502 2119 2112 2185 -1 + 4503 2185 2113 2119 -1 + 4504 535 544 1 -1 + 4505 1958 1953 1980 -1 + 4506 1980 1957 1958 -1 + 4507 1935 1957 1980 -1 + 4508 1939 1957 1935 -1 + 4509 92 1965 69 -1 + 4510 92 1967 1965 -1 + 4511 2551 2545 699 -1 + 4512 2541 2545 2551 -1 + 4513 93 68 100 -1 + 4514 74 68 93 -1 + 4515 2640 2554 2552 -1 + 4516 2640 2624 2554 -1 + 4517 69 1965 74 -1 + 4518 74 93 69 -1 + 4519 187 97 1961 -1 + 4520 1961 1964 187 -1 + 4521 720 732 718 -1 + 4522 718 719 720 -1 + 4523 468 98 472 -1 + 4524 716 665 715 -1 + 4525 2638 715 665 -1 + 4526 2184 2117 1489 -1 + 4527 2609 2608 2601 -1 + 4528 2609 2595 2608 -1 + 4529 698 697 709 -1 + 4530 709 604 698 -1 + 4531 96 66 97 -1 + 4532 1961 97 66 -1 + 4533 1908 1968 1887 -1 + 4534 1909 1968 1908 -1 + 4535 198 95 199 -1 + 4536 90 199 95 -1 + 4537 2553 2541 2554 -1 + 4538 2554 2562 2553 -1 + 4539 216 213 221 -1 + 4540 221 213 1888 -1 + 4541 213 1926 1888 -1 + 4542 1927 1888 1926 -1 + 4543 1944 130 79 -1 + 4544 77 79 130 -1 + 4545 1888 1927 1908 -1 + 4546 1907 1908 1927 -1 + 4547 2569 2566 2592 -1 + 4548 2592 2566 2591 -1 + 4549 1297 1299 1288 -1 + 4550 2353 749 769 -1 + 4551 769 749 744 -1 + 4552 2590 2568 606 -1 + 4553 2590 2589 2568 -1 + 4554 2353 2365 1202 -1 + 4555 1202 2365 2364 -1 + 4556 225 89 1905 -1 + 4557 190 89 225 -1 + 4558 2583 2546 2582 -1 + 4559 2583 2581 2546 -1 + 4560 1992 1989 2005 -1 + 4561 1992 2012 1989 -1 + 4562 698 700 699 -1 + 4563 698 701 700 -1 + 4564 699 697 698 -1 + 4565 2545 697 699 -1 + 4566 1990 1993 2036 -1 + 4567 2036 1994 1990 -1 + 4568 729 724 723 -1 + 4569 717 723 724 -1 + 4570 1972 1962 1903 -1 + 4571 1972 1897 1962 -1 + 4572 125 1937 1941 -1 + 4573 1940 1941 1937 -1 + 4574 54 30 442 -1 + 4575 2108 2106 2104 -1 + 4576 2108 2107 2106 -1 + 4577 1897 1982 1901 -1 + 4578 1972 1982 1897 -1 + 4579 1482 1481 1480 -1 + 4580 1482 2100 1481 -1 + 4581 275 307 276 -1 + 4582 3470 276 307 -1 + 4583 1018 1017 1020 -1 + 4584 1023 1020 1017 -1 + 4585 1388 2002 1389 -1 + 4586 1389 1375 1388 -1 + 4587 23 24 22 -1 + 4588 488 24 23 -1 + 4589 2162 2196 2203 -1 + 4590 2203 2196 2195 -1 + 4591 484 482 477 -1 + 4592 478 477 482 -1 + 4593 114 1811 1827 -1 + 4594 1826 1827 1811 -1 + 4595 461 456 454 -1 + 4596 453 454 456 -1 + 4597 2111 2096 2097 -1 + 4598 2099 2096 2111 -1 + 4599 1964 1960 1963 -1 + 4600 1961 1960 1964 -1 + 4601 450 444 455 -1 + 4602 3586 444 450 -1 + 4603 2140 2144 2146 -1 + 4604 2140 2138 2144 -1 + 4605 15 2105 2107 -1 + 4606 2106 2107 2105 -1 + 4607 2109 2096 2104 -1 + 4608 2098 2096 2109 -1 + 4609 3512 3511 3393 -1 + 4610 3512 3497 3511 -1 + 4611 217 228 208 -1 + 4612 208 216 217 -1 + 4613 2148 2149 2103 -1 + 4614 2143 2103 2149 -1 + 4615 1478 2120 2097 -1 + 4616 1478 2121 2120 -1 + 4617 1481 2098 2142 -1 + 4618 2100 2098 1481 -1 + 4619 3445 3487 3490 -1 + 4620 3489 3487 3445 -1 + 4621 3477 3478 3924 -1 + 4622 3924 3478 3475 -1 + 4623 4214 1086 1100 -1 + 4624 454 453 32 -1 + 4625 1301 1164 1297 -1 + 4626 1300 1164 1301 -1 + 4627 3025 3026 2999 -1 + 4628 3025 3021 3026 -1 + 4629 2096 2098 2097 -1 + 4630 271 289 278 -1 + 4631 278 289 3490 -1 + 4632 1494 1475 1492 -1 + 4633 1492 1477 1494 -1 + 4634 269 276 268 -1 + 4635 270 268 276 -1 + 4636 455 451 452 -1 + 4637 455 444 451 -1 + 4638 446 451 444 -1 + 4639 444 3587 446 -1 + 4640 1455 1454 1456 -1 + 4641 1456 1457 1455 -1 + 4642 2184 2179 2117 -1 + 4643 2186 2117 2179 -1 + 4644 192 156 2115 -1 + 4645 193 156 192 -1 + 4646 2187 2189 2188 -1 + 4647 2188 2178 2187 -1 + 4648 1452 1449 1458 -1 + 4649 1457 1449 1452 -1 + 4650 1285 1300 1301 -1 + 4651 1285 1284 1300 -1 + 4652 533 510 531 -1 + 4653 533 532 510 -1 + 4654 472 471 473 -1 + 4655 449 459 443 -1 + 4656 169 2110 2101 -1 + 4657 169 173 2110 -1 + 4658 2181 1446 1470 -1 + 4659 1455 479 1454 -1 + 4660 478 1454 479 -1 + 4661 148 443 175 -1 + 4662 459 175 443 -1 + 4663 515 533 520 -1 + 4664 520 533 528 -1 + 4665 1476 1496 1495 -1 + 4666 1476 448 1496 -1 + 4667 186 176 168 -1 + 4668 186 177 176 -1 + 4669 174 157 148 -1 + 4670 148 157 484 -1 + 4671 2157 2170 2155 -1 + 4672 2175 2170 2157 -1 + 4673 2386 853 2379 -1 + 4674 832 2379 853 -1 + 4675 194 191 468 -1 + 4676 1471 486 476 -1 + 4677 1448 486 1471 -1 + 4678 1292 1290 1333 -1 + 4679 1819 1822 1816 -1 + 4680 1816 1815 1819 -1 + 4681 236 3865 3851 -1 + 4682 3848 3851 3865 -1 + 4683 2194 2191 2205 -1 + 4684 468 472 473 -1 + 4685 510 532 525 -1 + 4686 525 532 3383 -1 + 4687 1456 486 1448 -1 + 4688 1448 1447 1456 -1 + 4689 192 2190 91 -1 + 4690 91 191 192 -1 + 4691 2182 2201 2200 -1 + 4692 2200 127 2182 -1 + 4693 194 470 482 -1 + 4694 485 482 470 -1 + 4695 2201 2187 2183 -1 + 4696 2201 2182 2187 -1 + 4697 116 409 410 -1 + 4698 410 409 417 -1 + 4699 1810 1948 124 -1 + 4700 1810 1812 1948 -1 + 4701 1950 1951 1803 -1 + 4702 1801 1803 1951 -1 + 4703 2459 2460 2454 -1 + 4704 2459 2456 2460 -1 + 4705 694 695 693 -1 + 4706 693 695 689 -1 + 4707 2160 2152 2162 -1 + 4708 2162 2152 2196 -1 + 4709 1802 1801 1884 -1 + 4710 1802 1803 1801 -1 + 4711 2182 127 2190 -1 + 4712 91 2190 127 -1 + 4713 2470 2462 2453 -1 + 4714 2470 2463 2462 -1 + 4715 1786 1780 1823 -1 + 4716 1784 1780 1786 -1 + 4717 2153 2159 2150 -1 + 4718 2449 2426 2467 -1 + 4719 2421 2467 2426 -1 + 4720 2438 2423 2437 -1 + 4721 2446 2423 2438 -1 + 4722 3561 1535 3537 -1 + 4723 1543 3537 1535 -1 + 4724 3147 3135 3146 -1 + 4725 2133 407 403 -1 + 4726 406 3560 400 -1 + 4727 1534 400 3560 -1 + 4728 2132 2167 2172 -1 + 4729 2132 76 2167 -1 + 4730 4390 4055 1621 -1 + 4731 1470 1459 2191 -1 + 4732 4648 4594 2819 -1 + 4733 411 413 104 -1 + 4734 1329 1283 1328 -1 + 4735 1329 1282 1283 -1 + 4736 1301 1288 1326 -1 + 4737 1540 1530 2166 -1 + 4738 3537 1536 3544 -1 + 4739 2496 1846 2498 -1 + 4740 2174 2150 1539 -1 + 4741 1950 1949 1947 -1 + 4742 1947 1951 1950 -1 + 4743 403 406 75 -1 + 4744 133 126 81 -1 + 4745 133 125 126 -1 + 4746 2158 2171 2164 -1 + 4747 2164 2171 141 -1 + 4748 116 135 117 -1 + 4749 2202 117 135 -1 + 4750 194 484 193 -1 + 4751 1527 1466 1529 -1 + 4752 1529 1528 1527 -1 + 4753 1523 414 1542 -1 + 4754 413 1542 414 -1 + 4755 1462 1461 1525 -1 + 4756 1460 1525 1461 -1 + 4757 415 422 424 -1 + 4758 3683 422 415 -1 + 4759 1883 1789 1790 -1 + 4760 1883 196 1789 -1 + 4761 1523 1466 414 -1 + 4762 1529 1466 1523 -1 + 4763 1882 145 1836 -1 + 4764 1836 1877 1882 -1 + 4765 2158 2173 2171 -1 + 4766 2155 2173 2158 -1 + 4767 2620 2624 2632 -1 + 4768 2632 2624 2640 -1 + 4769 651 669 652 -1 + 4770 652 669 2605 -1 + 4771 2249 2240 2243 -1 + 4772 2249 2320 2240 -1 + 4773 3434 3360 3432 -1 + 4774 3432 3360 3374 -1 + 4775 2805 2803 4001 -1 + 4776 2805 3530 2803 -1 + 4777 2250 2236 2262 -1 + 4778 2262 2236 854 -1 + 4779 652 2457 658 -1 + 4780 652 2458 2457 -1 + 4781 557 556 573 -1 + 4782 2622 2621 2629 -1 + 4783 2622 2619 2621 -1 + 4784 2151 2163 2164 -1 + 4785 2197 2164 2163 -1 + 4786 661 686 2454 -1 + 4787 661 662 686 -1 + 4788 1786 1823 1827 -1 + 4789 1827 1823 1821 -1 + 4790 2466 2456 2459 -1 + 4791 2466 2427 2456 -1 + 4792 2430 2474 2427 -1 + 4793 2473 2474 2430 -1 + 4794 658 684 683 -1 + 4795 658 2457 684 -1 + 4796 1855 2492 1849 -1 + 4797 925 923 949 -1 + 4798 924 923 925 -1 + 4799 2078 2074 2077 -1 + 4800 2078 2073 2074 -1 + 4801 659 658 683 -1 + 4802 683 660 659 -1 + 4803 804 344 801 -1 + 4804 2921 344 804 -1 + 4805 2512 2522 2519 -1 + 4806 2512 2511 2522 -1 + 4807 2789 3937 4373 -1 + 4808 5 40 41 -1 + 4809 2524 41 40 -1 + 4810 678 2527 679 -1 + 4811 2444 679 2527 -1 + 4812 19 60 681 -1 + 4813 19 2513 60 -1 + 4814 2435 2511 2512 -1 + 4815 2435 2444 2511 -1 + 4816 2493 2478 2475 -1 + 4817 2475 2476 2493 -1 + 4818 106 98 91 -1 + 4819 91 127 106 -1 + 4820 1845 2496 1834 -1 + 4821 1834 2496 2497 -1 + 4822 1851 2499 2497 -1 + 4823 1851 16 2499 -1 + 4824 1873 1829 1870 -1 + 4825 1832 1870 1829 -1 + 4826 694 676 687 -1 + 4827 687 695 694 -1 + 4828 2488 2506 2487 -1 + 4829 2487 2506 2489 -1 + 4830 1871 1872 1835 -1 + 4831 1835 1872 1879 -1 + 4832 2286 2285 2287 -1 + 4833 2286 2269 2285 -1 + 4834 53 59 61 -1 + 4835 58 59 53 -1 + 4836 1872 1875 1879 -1 + 4837 1876 1879 1875 -1 + 4838 593 638 636 -1 + 4839 2299 638 593 -1 + 4840 2523 2476 2525 -1 + 4841 2475 2525 2476 -1 + 4842 2437 2423 2424 -1 + 4843 2424 2425 2437 -1 + 4844 2429 2427 2466 -1 + 4845 2429 2430 2427 -1 + 4846 695 691 689 -1 + 4847 689 691 690 -1 + 4848 411 142 82 -1 + 4849 82 142 141 -1 + 4850 2454 2455 2459 -1 + 4851 2454 686 2455 -1 + 4852 2434 2464 685 -1 + 4853 2434 2431 2464 -1 + 4854 2423 2420 2424 -1 + 4855 2422 2420 2423 -1 + 4856 1829 1853 1840 -1 + 4857 1829 1833 1853 -1 + 4858 1840 2500 1854 -1 + 4859 1840 1853 2500 -1 + 4860 40 59 58 -1 + 4861 58 2482 40 -1 + 4862 2492 1852 1849 -1 + 4863 1873 1833 1829 -1 + 4864 1873 1874 1833 -1 + 4865 1797 1876 1875 -1 + 4866 1797 1865 1876 -1 + 4867 3559 2131 2166 -1 + 4868 695 688 691 -1 + 4869 687 688 695 -1 + 4870 4011 3610 3627 -1 + 4871 3627 3610 3611 -1 + 4872 1835 1841 1874 -1 + 4873 1835 1837 1841 -1 + 4874 206 1883 143 -1 + 4875 143 1883 1857 -1 + 4876 2166 1505 3559 -1 + 4877 1873 1871 1874 -1 + 4878 1874 1871 1835 -1 + 4879 1871 1878 1872 -1 + 4880 1863 1878 1871 -1 + 4881 1434 2049 2059 -1 + 4882 2192 2193 2201 -1 + 4883 2201 2193 2200 -1 + 4884 1834 1837 1838 -1 + 4885 1834 1841 1837 -1 + 4886 1840 1832 1829 -1 + 4887 1840 1869 1832 -1 + 4888 145 215 147 -1 + 4889 147 215 203 -1 + 4890 146 145 147 -1 + 4891 146 1836 145 -1 + 4892 1838 1845 1834 -1 + 4893 1838 1850 1845 -1 + 4894 1877 1850 1838 -1 + 4895 1877 1836 1850 -1 + 4896 1881 143 1857 -1 + 4897 1857 1858 1881 -1 + 4898 2158 2154 2155 -1 + 4899 2155 2154 2157 -1 + 4900 1881 1858 1839 -1 + 4901 1839 1858 1869 -1 + 4902 1866 1877 1880 -1 + 4903 1882 1877 1866 -1 + 4904 3442 3444 3441 -1 + 4905 3442 3443 3444 -1 + 4906 78 1776 1817 -1 + 4907 80 1776 78 -1 + 4908 120 1822 121 -1 + 4909 121 132 120 -1 + 4910 1857 1788 1858 -1 + 4911 1868 1858 1788 -1 + 4912 1817 1777 1820 -1 + 4913 1820 1777 1800 -1 + 4914 1879 1880 1837 -1 + 4915 1837 1835 1879 -1 + 4916 1794 1861 1795 -1 + 4917 1795 1791 1794 -1 + 4918 1786 1828 1784 -1 + 4919 1784 1828 1824 -1 + 4920 1816 1796 1815 -1 + 4921 1814 1815 1796 -1 + 4922 72 129 73 -1 + 4923 83 73 129 -1 + 4924 1859 1795 1863 -1 + 4925 1863 1795 1878 -1 + 4926 2507 2439 2443 -1 + 4927 2507 2509 2439 -1 + 4928 1787 1785 1824 -1 + 4929 1824 1785 1784 -1 + 4930 131 129 111 -1 + 4931 128 111 129 -1 + 4932 1818 1783 1782 -1 + 4933 1818 1779 1783 -1 + 4934 1818 1782 1778 -1 + 4935 1778 1782 83 -1 + 4936 215 86 206 -1 + 4937 215 226 86 -1 + 4938 1806 1807 1813 -1 + 4939 1805 1807 1806 -1 + 4940 1467 2205 2191 -1 + 4941 131 83 129 -1 + 4942 131 1778 83 -1 + 4943 1787 1794 1785 -1 + 4944 1793 1794 1787 -1 + 4945 3441 3424 3433 -1 + 4946 3433 3424 3423 -1 + 4947 3441 3421 3424 -1 + 4948 3444 3421 3441 -1 + 4949 3441 3432 3442 -1 + 4950 3441 3433 3432 -1 + 4951 1816 1800 1796 -1 + 4952 1816 1820 1800 -1 + 4953 421 423 546 -1 + 4954 546 547 421 -1 + 4955 253 252 265 -1 + 4956 3840 265 252 -1 + 4957 568 558 501 -1 + 4958 501 555 568 -1 + 4959 537 3420 3443 -1 + 4960 3444 3443 3420 -1 + 4961 3630 3420 3651 -1 + 4962 3630 3652 3420 -1 + 4963 3669 3642 3670 -1 + 4964 3672 3670 3642 -1 + 4965 537 3651 3420 -1 + 4966 537 570 3651 -1 + 4967 3666 3658 3653 -1 + 4968 3666 3660 3658 -1 + 4969 1465 1527 1460 -1 + 4970 1460 1527 1525 -1 + 4971 2150 2159 1520 -1 + 4972 421 422 423 -1 + 4973 3682 423 422 -1 + 4974 421 547 418 -1 + 4975 418 547 419 -1 + 4976 399 424 435 -1 + 4977 399 415 424 -1 + 4978 3718 3664 3639 -1 + 4979 3718 3667 3664 -1 + 4980 3709 3667 3718 -1 + 4981 3709 3654 3667 -1 + 4982 3696 3632 3649 -1 + 4983 3649 3631 3696 -1 + 4984 4662 4617 4509 -1 + 4985 3678 3674 3574 -1 + 4986 3574 3570 3678 -1 + 4987 3035 3010 3034 -1 + 4988 3034 3033 3035 -1 + 4989 3687 3698 3683 -1 + 4990 3675 3698 3687 -1 + 4991 3675 3568 3674 -1 + 4992 3675 3687 3568 -1 + 4993 3564 3549 3550 -1 + 4994 3564 3575 3549 -1 + 4995 3564 3570 3575 -1 + 4996 3575 3570 3574 -1 + 4997 3678 3697 3674 -1 + 4998 3675 3674 3697 -1 + 4999 3167 3145 1324 -1 + 5000 2737 3008 4118 -1 + 5001 2737 2802 3008 -1 + 5002 408 3578 3548 -1 + 5003 3548 3578 3540 -1 + 5004 1745 1747 4040 -1 + 5005 3561 1505 1530 -1 + 5006 1530 1505 2166 -1 + 5007 3561 1530 1533 -1 + 5008 1533 1535 3561 -1 + 5009 499 3381 507 -1 + 5010 3382 507 3381 -1 + 5011 3916 3906 401 -1 + 5012 1766 1767 1770 -1 + 5013 1770 1767 1416 -1 + 5014 3567 3904 3574 -1 + 5015 3574 3904 3575 -1 + 5016 408 401 3578 -1 + 5017 3578 401 416 -1 + 5018 3899 401 408 -1 + 5019 3899 3916 401 -1 + 5020 3567 3674 3568 -1 + 5021 3567 3574 3674 -1 + 5022 3151 3007 4391 -1 + 5023 3541 3905 3539 -1 + 5024 3539 3543 3541 -1 + 5025 3539 3905 3543 -1 + 5026 3007 2800 4391 -1 + 5027 3007 4367 2800 -1 + 5028 4705 1237 4695 -1 + 5029 1685 1237 4705 -1 + 5030 6 557 7 -1 + 5031 7 3 6 -1 + 5032 3576 3577 3567 -1 + 5033 3904 3567 3577 -1 + 5034 4390 4384 4055 -1 + 5035 3815 3755 3816 -1 + 5036 3872 3816 3755 -1 + 5037 535 1 177 -1 + 5038 176 177 1 -1 + 5039 3838 3835 3875 -1 + 5040 3877 3875 3835 -1 + 5041 3984 3748 3983 -1 + 5042 3984 3750 3748 -1 + 5043 3748 3749 3742 -1 + 5044 3748 3751 3749 -1 + 5045 265 3867 261 -1 + 5046 261 263 265 -1 + 5047 3975 2817 3978 -1 + 5048 3349 3351 3293 -1 + 5049 4000 3746 3984 -1 + 5050 3984 3970 4000 -1 + 5051 3974 3973 3996 -1 + 5052 3974 3994 3973 -1 + 5053 240 248 241 -1 + 5054 242 241 248 -1 + 5055 2813 3994 3993 -1 + 5056 3973 3994 2813 -1 + 5057 4000 3970 3969 -1 + 5058 3971 3969 3970 -1 + 5059 3976 3772 3746 -1 + 5060 3746 4000 3976 -1 + 5061 4220 1911 1912 -1 + 5062 1924 1912 1911 -1 + 5063 3980 3993 3994 -1 + 5064 3980 3776 3993 -1 + 5065 2836 2834 2974 -1 + 5066 2974 2975 2836 -1 + 5067 303 301 304 -1 + 5068 3213 323 325 -1 + 5069 2498 2497 2496 -1 + 5070 2498 1851 2497 -1 + 5071 2981 2978 2962 -1 + 5072 2983 2978 2981 -1 + 5073 3996 3987 3974 -1 + 5074 3996 3971 3987 -1 + 5075 3992 3999 3976 -1 + 5076 3998 3976 3999 -1 + 5077 4541 1742 1588 -1 + 5078 2818 2816 3957 -1 + 5079 3957 3991 2818 -1 + 5080 244 242 3990 -1 + 5081 244 241 242 -1 + 5082 3772 3998 3990 -1 + 5083 3772 3976 3998 -1 + 5084 1076 1075 4220 -1 + 5085 245 231 247 -1 + 5086 3736 247 231 -1 + 5087 238 249 3765 -1 + 5088 238 234 249 -1 + 5089 3816 3872 3871 -1 + 5090 3871 3872 3849 -1 + 5091 2707 807 2648 -1 + 5092 806 2648 807 -1 + 5093 262 232 233 -1 + 5094 264 232 262 -1 + 5095 3730 3795 3733 -1 + 5096 3730 3729 3795 -1 + 5097 2077 2080 2082 -1 + 5098 2082 2080 1342 -1 + 5099 1341 2088 2089 -1 + 5100 3035 3067 3074 -1 + 5101 257 3839 252 -1 + 5102 252 250 257 -1 + 5103 3853 3739 3735 -1 + 5104 3723 3739 3853 -1 + 5105 3753 3726 3752 -1 + 5106 3753 3855 3726 -1 + 5107 3753 3823 3854 -1 + 5108 3753 3815 3823 -1 + 5109 3838 3875 3876 -1 + 5110 3876 3812 3838 -1 + 5111 369 250 260 -1 + 5112 369 257 250 -1 + 5113 3801 3877 3874 -1 + 5114 3801 3875 3877 -1 + 5115 835 834 873 -1 + 5116 873 834 872 -1 + 5117 2092 2052 2054 -1 + 5118 2053 2054 2052 -1 + 5119 3794 3795 3793 -1 + 5120 3793 3795 3800 -1 + 5121 2788 2644 2668 -1 + 5122 2668 780 2788 -1 + 5123 259 367 260 -1 + 5124 260 367 369 -1 + 5125 492 571 560 -1 + 5126 492 500 571 -1 + 5127 3376 3430 3426 -1 + 5128 3426 3378 3376 -1 + 5129 1462 1526 1468 -1 + 5130 1525 1526 1462 -1 + 5131 1468 1520 2159 -1 + 5132 1468 1526 1520 -1 + 5133 1527 1528 1525 -1 + 5134 1526 1525 1528 -1 + 5135 2165 1540 2166 -1 + 5136 403 407 406 -1 + 5137 3560 406 407 -1 + 5138 2195 2163 2199 -1 + 5139 2195 2196 2163 -1 + 5140 3544 1543 1534 -1 + 5141 1534 3560 3544 -1 + 5142 3164 3150 3166 -1 + 5143 1532 1533 1538 -1 + 5144 1532 1531 1533 -1 + 5145 1536 1543 3544 -1 + 5146 1451 1460 1461 -1 + 5147 1463 1460 1451 -1 + 5148 76 2129 403 -1 + 5149 76 2132 2129 -1 + 5150 499 508 3381 -1 + 5151 489 508 499 -1 + 5152 3412 3656 3413 -1 + 5153 3412 3436 3656 -1 + 5154 3435 3431 3423 -1 + 5155 3423 3431 3433 -1 + 5156 499 501 489 -1 + 5157 489 501 500 -1 + 5158 499 507 555 -1 + 5159 555 501 499 -1 + 5160 1462 1468 1467 -1 + 5161 1467 1468 2205 -1 + 5162 2205 1468 2159 -1 + 5163 117 2202 2199 -1 + 5164 2195 2199 2202 -1 + 5165 2202 107 2195 -1 + 5166 2195 107 2203 -1 + 5167 2115 2116 2189 -1 + 5168 2189 2116 2188 -1 + 5169 1451 1461 1469 -1 + 5170 1472 1469 1461 -1 + 5171 1493 1486 1487 -1 + 5172 1456 1447 1457 -1 + 5173 1449 1457 1447 -1 + 5174 470 476 485 -1 + 5175 485 476 486 -1 + 5176 476 475 1471 -1 + 5177 476 473 475 -1 + 5178 477 478 483 -1 + 5179 479 483 478 -1 + 5180 191 98 468 -1 + 5181 91 98 191 -1 + 5182 192 2115 2190 -1 + 5183 2189 2190 2115 -1 + 5184 1451 1471 1463 -1 + 5185 1463 1471 475 -1 + 5186 1451 1448 1471 -1 + 5187 1469 1448 1451 -1 + 5188 1489 1488 2184 -1 + 5189 524 522 531 -1 + 5190 524 521 522 -1 + 5191 1477 1492 1493 -1 + 5192 1493 1487 2121 -1 + 5193 1489 2121 1487 -1 + 5194 1493 2121 1478 -1 + 5195 1478 1477 1493 -1 + 5196 1453 1488 1487 -1 + 5197 1489 1487 1488 -1 + 5198 2117 2122 1489 -1 + 5199 2117 2112 2122 -1 + 5200 157 193 484 -1 + 5201 156 193 157 -1 + 5202 460 461 454 -1 + 5203 460 1473 461 -1 + 5204 2118 2119 2113 -1 + 5205 2113 151 2118 -1 + 5206 2146 2144 2145 -1 + 5207 270 276 3470 -1 + 5208 3470 3469 270 -1 + 5209 445 451 446 -1 + 5210 445 447 451 -1 + 5211 392 394 389 -1 + 5212 389 393 392 -1 + 5213 3522 3525 3526 -1 + 5214 3522 3532 3525 -1 + 5215 3465 3517 3467 -1 + 5216 3466 3467 3517 -1 + 5217 3490 290 3445 -1 + 5218 3490 289 290 -1 + 5219 224 210 211 -1 + 5220 209 211 210 -1 + 5221 2148 2105 28 -1 + 5222 13 28 2105 -1 + 5223 442 452 54 -1 + 5224 158 2032 2039 -1 + 5225 2039 2032 1994 -1 + 5226 2355 2330 2329 -1 + 5227 2361 2330 2355 -1 + 5228 54 452 447 -1 + 5229 451 447 452 -1 + 5230 3618 3611 3616 -1 + 5231 3618 3627 3611 -1 + 5232 1475 1483 1474 -1 + 5233 1475 1494 1483 -1 + 5234 3467 270 3469 -1 + 5235 3469 3461 3467 -1 + 5236 3463 3478 3479 -1 + 5237 3464 3478 3463 -1 + 5238 3463 3520 3522 -1 + 5239 3522 3462 3463 -1 + 5240 271 273 272 -1 + 5241 272 289 271 -1 + 5242 3035 3009 3067 -1 + 5243 3067 3009 3011 -1 + 5244 382 385 3935 -1 + 5245 382 386 385 -1 + 5246 455 452 456 -1 + 5247 456 452 442 -1 + 5248 456 442 453 -1 + 5249 450 1479 3586 -1 + 5250 3601 3586 1479 -1 + 5251 14 39 32 -1 + 5252 45 39 14 -1 + 5253 173 45 2110 -1 + 5254 173 39 45 -1 + 5255 1494 1477 1482 -1 + 5256 3393 3507 3510 -1 + 5257 3510 3507 3509 -1 + 5258 458 449 448 -1 + 5259 459 449 458 -1 + 5260 448 457 458 -1 + 5261 448 1476 457 -1 + 5262 458 460 462 -1 + 5263 457 460 458 -1 + 5264 4567 2284 4484 -1 + 5265 4584 4573 3052 -1 + 5266 3954 3953 3910 -1 + 5267 3918 3953 3954 -1 + 5268 275 269 280 -1 + 5269 275 276 269 -1 + 5270 273 280 269 -1 + 5271 273 271 280 -1 + 5272 292 278 279 -1 + 5273 292 277 278 -1 + 5274 279 3490 3487 -1 + 5275 279 278 3490 -1 + 5276 386 394 392 -1 + 5277 382 394 386 -1 + 5278 384 330 381 -1 + 5279 329 381 330 -1 + 5280 3963 3945 3964 -1 + 5281 3963 397 3945 -1 + 5282 2809 2807 2755 -1 + 5283 2755 2807 2758 -1 + 5284 517 3534 3457 -1 + 5285 3457 3534 3535 -1 + 5286 3948 3879 3952 -1 + 5287 395 3879 3948 -1 + 5288 3256 3234 2767 -1 + 5289 3233 2767 3234 -1 + 5290 2893 2891 2861 -1 + 5291 331 2891 2893 -1 + 5292 3964 3915 3920 -1 + 5293 3945 3915 3964 -1 + 5294 3796 3968 3793 -1 + 5295 3914 3793 3968 -1 + 5296 3972 3914 3954 -1 + 5297 3968 3954 3914 -1 + 5298 2742 2741 2740 -1 + 5299 2743 2741 2742 -1 + 5300 3525 3532 3495 -1 + 5301 3495 3532 3533 -1 + 5302 365 387 363 -1 + 5303 364 363 387 -1 + 5304 378 397 3902 -1 + 5305 3963 3902 397 -1 + 5306 3523 3465 3461 -1 + 5307 3461 3465 3467 -1 + 5308 288 287 268 -1 + 5309 268 3466 288 -1 + 5310 3142 3136 3137 -1 + 5311 3141 3137 3136 -1 + 5312 3738 3959 376 -1 + 5313 3780 3959 3738 -1 + 5314 3911 3912 3779 -1 + 5315 3779 3961 3911 -1 + 5316 291 516 283 -1 + 5317 517 283 516 -1 + 5318 282 285 286 -1 + 5319 284 286 285 -1 + 5320 3498 518 3497 -1 + 5321 3498 519 518 -1 + 5322 511 516 513 -1 + 5323 511 3492 516 -1 + 5324 520 528 512 -1 + 5325 527 512 528 -1 + 5326 512 527 513 -1 + 5327 511 513 527 -1 + 5328 522 527 528 -1 + 5329 526 527 522 -1 + 5330 3020 3021 3017 -1 + 5331 3017 3021 3025 -1 + 5332 3395 3362 3399 -1 + 5333 3398 3362 3395 -1 + 5334 514 3383 532 -1 + 5335 3384 3383 514 -1 + 5336 524 494 493 -1 + 5337 523 494 524 -1 + 5338 531 523 524 -1 + 5339 531 510 523 -1 + 5340 3363 3362 3398 -1 + 5341 3398 3361 3363 -1 + 5342 521 493 3404 -1 + 5343 521 524 493 -1 + 5344 3359 3391 3506 -1 + 5345 3359 3396 3391 -1 + 5346 3506 3358 3359 -1 + 5347 3506 3404 3358 -1 + 5348 514 532 515 -1 + 5349 533 515 532 -1 + 5350 531 528 533 -1 + 5351 531 522 528 -1 + 5352 3397 3496 3392 -1 + 5353 3397 3494 3496 -1 + 5354 4384 4314 4055 -1 + 5355 3393 3511 3527 -1 + 5356 3527 3507 3393 -1 + 5357 3395 3399 3394 -1 + 5358 3394 3399 3385 -1 + 5359 3395 3394 3510 -1 + 5360 3510 3508 3395 -1 + 5361 3531 3535 3534 -1 + 5362 3533 3535 3531 -1 + 5363 274 284 291 -1 + 5364 286 284 274 -1 + 5365 286 287 288 -1 + 5366 286 274 287 -1 + 5367 274 273 287 -1 + 5368 274 272 273 -1 + 5369 281 518 282 -1 + 5370 3515 518 281 -1 + 5371 281 3517 3515 -1 + 5372 3516 3515 3517 -1 + 5373 2039 2043 2040 -1 + 5374 2039 2035 2043 -1 + 5375 3523 3524 3465 -1 + 5376 3462 3524 3523 -1 + 5377 3520 3532 3522 -1 + 5378 3521 3532 3520 -1 + 5379 2858 3943 2859 -1 + 5380 2858 2860 3943 -1 + 5381 3447 3521 3520 -1 + 5382 3458 3521 3447 -1 + 5383 310 292 293 -1 + 5384 310 312 292 -1 + 5385 3843 3842 3734 -1 + 5386 3734 3857 3843 -1 + 5387 3758 3785 362 -1 + 5388 362 3785 361 -1 + 5389 3812 3837 3838 -1 + 5390 3812 3844 3837 -1 + 5391 377 373 367 -1 + 5392 371 367 373 -1 + 5393 3838 3841 3835 -1 + 5394 3838 3837 3841 -1 + 5395 3842 3730 3733 -1 + 5396 3733 3734 3842 -1 + 5397 3758 3803 3802 -1 + 5398 362 3803 3758 -1 + 5399 3758 3801 3785 -1 + 5400 3757 3801 3758 -1 + 5401 3972 3771 3914 -1 + 5402 3972 3961 3771 -1 + 5403 4476 4174 4211 -1 + 5404 3144 3028 3033 -1 + 5405 3166 3148 3165 -1 + 5406 3165 3164 3166 -1 + 5407 3033 3027 3009 -1 + 5408 3013 3015 3014 -1 + 5409 3086 3015 3013 -1 + 5410 4610 3938 4262 -1 + 5411 4456 4431 2970 -1 + 5412 3245 2762 3238 -1 + 5413 3238 2762 2810 -1 + 5414 1178 1177 1176 -1 + 5415 3082 3173 982 -1 + 5416 3449 3454 3450 -1 + 5417 3340 3339 3342 -1 + 5418 1231 1271 4032 -1 + 5419 1231 1232 1271 -1 + 5420 3044 3086 1174 -1 + 5421 3380 3379 3075 -1 + 5422 1172 1166 1165 -1 + 5423 2998 3000 3025 -1 + 5424 3017 3025 3000 -1 + 5425 2787 2667 2781 -1 + 5426 4662 4204 4554 -1 + 5427 1239 1152 1252 -1 + 5428 1774 1390 1392 -1 + 5429 2798 3069 4042 -1 + 5430 2658 2662 4560 -1 + 5431 2754 3346 2753 -1 + 5432 3347 3345 3346 -1 + 5433 3064 3081 4031 -1 + 5434 3227 3231 3230 -1 + 5435 3052 2812 2752 -1 + 5436 2752 2760 3052 -1 + 5437 4063 1216 1555 -1 + 5438 1235 1251 1277 -1 + 5439 4326 2283 4007 -1 + 5440 4326 2504 4299 -1 + 5441 4102 2014 1518 -1 + 5442 3608 3595 3593 -1 + 5443 3124 1242 3170 -1 + 5444 1250 3170 1242 -1 + 5445 4737 1585 4145 -1 + 5446 4416 4373 3937 -1 + 5447 3052 2747 4584 -1 + 5448 4028 4016 3860 -1 + 5449 640 642 4374 -1 + 5450 4660 4469 1134 -1 + 5451 3819 3830 4458 -1 + 5452 3598 3599 3602 -1 + 5453 4393 1736 1725 -1 + 5454 3038 3024 4418 -1 + 5455 3038 4075 3024 -1 + 5456 407 1505 4018 -1 + 5457 407 3559 1505 -1 + 5458 1287 1296 3031 -1 + 5459 1653 2871 4245 -1 + 5460 1653 4057 2871 -1 + 5461 1770 1769 1771 -1 + 5462 1771 1732 1770 -1 + 5463 3583 3584 3581 -1 + 5464 3937 3486 4416 -1 + 5465 4638 4089 4302 -1 + 5466 3572 3582 3581 -1 + 5467 4384 1608 4314 -1 + 5468 3665 3688 3689 -1 + 5469 1133 4124 4066 -1 + 5470 4467 4234 4436 -1 + 5471 1705 1695 1709 -1 + 5472 3538 3545 3546 -1 + 5473 1709 981 1708 -1 + 5474 1709 1696 981 -1 + 5475 1695 1696 1709 -1 + 5476 4320 4352 1144 -1 + 5477 3562 3563 3556 -1 + 5478 4522 4064 1655 -1 + 5479 4659 4576 4317 -1 + 5480 3410 3409 3408 -1 + 5481 1546 1553 4020 -1 + 5482 3581 3563 3562 -1 + 5483 3584 3580 3554 -1 + 5484 968 957 969 -1 + 5485 4767 4604 4748 -1 + 5486 3402 3403 3401 -1 + 5487 4716 1264 4711 -1 + 5488 3562 3417 3581 -1 + 5489 1637 1635 1154 -1 + 5490 1666 1669 1279 -1 + 5491 3503 3439 3921 -1 + 5492 3921 3439 3411 -1 + 5493 3411 3439 3437 -1 + 5494 3907 4201 4039 -1 + 5495 4767 3484 3446 -1 + 5496 3483 3446 3484 -1 + 5497 4412 2567 4363 -1 + 5498 4199 1545 4021 -1 + 5499 4640 1733 1765 -1 + 5500 3614 3629 3613 -1 + 5501 3365 3387 3368 -1 + 5502 3501 3615 3491 -1 + 5503 3365 3615 3614 -1 + 5504 1774 1415 1413 -1 + 5505 1774 1392 1415 -1 + 5506 4276 4330 2763 -1 + 5507 3105 3640 3637 -1 + 5508 4381 3125 4360 -1 + 5509 3937 2789 4690 -1 + 5510 3588 2804 3073 -1 + 5511 4108 1253 1664 -1 + 5512 3609 3620 3624 -1 + 5513 3609 3608 3620 -1 + 5514 3624 3369 3370 -1 + 5515 3624 3620 3369 -1 + 5516 4426 4273 3808 -1 + 5517 4181 3881 3956 -1 + 5518 1682 1247 4536 -1 + 5519 4199 1547 1545 -1 + 5520 1546 1545 1547 -1 + 5521 1591 1602 4441 -1 + 5522 1133 1403 4402 -1 + 5523 4735 4689 3485 -1 + 5524 3588 2790 3594 -1 + 5525 1123 960 4725 -1 + 5526 3679 3680 3681 -1 + 5527 4646 4588 2784 -1 + 5528 632 640 633 -1 + 5529 4106 4045 2888 -1 + 5530 2888 4045 2886 -1 + 5531 3690 3686 3685 -1 + 5532 3659 3679 3536 -1 + 5533 3650 3647 3648 -1 + 5534 3689 3659 3650 -1 + 5535 3679 3659 3689 -1 + 5536 1580 1581 1764 -1 + 5537 1311 1321 4164 -1 + 5538 3102 3158 4181 -1 + 5539 3705 3101 3644 -1 + 5540 3705 3706 3101 -1 + 5541 3706 3705 3701 -1 + 5542 3717 3706 3699 -1 + 5543 3563 3584 3555 -1 + 5544 3091 3101 3717 -1 + 5545 3706 3717 3101 -1 + 5546 3408 3556 3405 -1 + 5547 1581 1763 1764 -1 + 5548 2667 4371 3446 -1 + 5549 4526 4048 4422 -1 + 5550 4155 4048 4526 -1 + 5551 3644 3101 3634 -1 + 5552 3043 3085 3084 -1 + 5553 3084 3085 3078 -1 + 5554 3026 3137 3140 -1 + 5555 2786 3073 3606 -1 + 5556 3805 2853 4502 -1 + 5557 3450 3454 3485 -1 + 5558 3485 3454 3486 -1 + 5559 4635 1146 4403 -1 + 5560 1254 1146 4635 -1 + 5561 2679 2783 2682 -1 + 5562 4301 4193 1169 -1 + 5563 2786 3080 3073 -1 + 5564 1401 1133 4402 -1 + 5565 1401 4124 1133 -1 + 5566 3604 2804 3623 -1 + 5567 1097 1096 1094 -1 + 5568 3080 3077 3078 -1 + 5569 1267 3170 1235 -1 + 5570 3169 3170 1267 -1 + 5571 3019 3020 3018 -1 + 5572 3019 3149 3020 -1 + 5573 3679 3681 4310 -1 + 5574 3476 3481 3928 -1 + 5575 3928 3481 3468 -1 + 5576 3558 3557 2706 -1 + 5577 3558 3328 3557 -1 + 5578 4600 4304 3602 -1 + 5579 1695 1705 1700 -1 + 5580 4251 4714 1265 -1 + 5581 4711 4121 4635 -1 + 5582 3781 3281 3878 -1 + 5583 3118 3117 3120 -1 + 5584 3699 3700 3119 -1 + 5585 4281 725 639 -1 + 5586 4387 3068 3006 -1 + 5587 3004 3006 3068 -1 + 5588 3118 3282 3110 -1 + 5589 3118 3119 3282 -1 + 5590 574 575 705 -1 + 5591 705 295 574 -1 + 5592 3787 3880 3878 -1 + 5593 3741 3295 3296 -1 + 5594 3336 2696 2839 -1 + 5595 197 201 4702 -1 + 5596 4439 2031 2013 -1 + 5597 2013 2031 2016 -1 + 5598 3741 3782 3295 -1 + 5599 3606 3604 4304 -1 + 5600 3900 3278 3880 -1 + 5601 3880 3278 3878 -1 + 5602 3831 3833 3267 -1 + 5603 2828 2845 2943 -1 + 5604 2896 2930 2929 -1 + 5605 4450 3055 4443 -1 + 5606 574 591 575 -1 + 5607 1515 1519 1513 -1 + 5608 1265 4121 4711 -1 + 5609 3740 3281 3782 -1 + 5610 1624 1596 4620 -1 + 5611 4619 3988 4550 -1 + 5612 4347 1064 1229 -1 + 5613 1073 1229 1064 -1 + 5614 2696 2840 2839 -1 + 5615 2840 3787 3788 -1 + 5616 2840 3880 3787 -1 + 5617 3880 3901 3900 -1 + 5618 2970 3336 2839 -1 + 5619 3788 3740 3782 -1 + 5620 3788 3782 3741 -1 + 5621 3863 3819 2855 -1 + 5622 2892 2886 2703 -1 + 5623 2718 2692 2717 -1 + 5624 4219 304 300 -1 + 5625 705 318 321 -1 + 5626 705 575 318 -1 + 5627 3926 2717 3896 -1 + 5628 4754 2855 2854 -1 + 5629 3308 3242 3314 -1 + 5630 2489 2506 2501 -1 + 5631 1233 1273 1257 -1 + 5632 1257 1234 1233 -1 + 5633 4473 3336 4344 -1 + 5634 2695 3336 4473 -1 + 5635 4509 1717 4204 -1 + 5636 2884 2852 2704 -1 + 5637 1842 4010 2500 -1 + 5638 4074 3289 3182 -1 + 5639 2702 2704 2852 -1 + 5640 4222 4204 1688 -1 + 5641 2807 2744 2758 -1 + 5642 2758 2744 918 -1 + 5643 2703 2901 2902 -1 + 5644 2902 2892 2703 -1 + 5645 4462 4368 3908 -1 + 5646 3893 3327 2724 -1 + 5647 4305 3114 3113 -1 + 5648 4548 2802 2727 -1 + 5649 2727 2802 3228 -1 + 5650 4424 4253 1185 -1 + 5651 590 3313 576 -1 + 5652 963 4654 4397 -1 + 5653 4074 3182 3185 -1 + 5654 3185 3182 3186 -1 + 5655 313 576 3312 -1 + 5656 1252 1155 4023 -1 + 5657 3332 3558 2994 -1 + 5658 3332 3328 3558 -1 + 5659 3455 3452 4267 -1 + 5660 586 585 3553 -1 + 5661 3553 585 469 -1 + 5662 2873 1661 2871 -1 + 5663 2871 2872 2873 -1 + 5664 1639 1654 1632 -1 + 5665 2888 4210 4106 -1 + 5666 1595 1583 4058 -1 + 5667 4035 4292 1659 -1 + 5668 3327 2988 2971 -1 + 5669 3327 2971 2724 -1 + 5670 3476 3475 3481 -1 + 5671 3476 3924 3475 -1 + 5672 3317 3318 3329 -1 + 5673 3328 3329 3318 -1 + 5674 1630 1621 1043 -1 + 5675 712 467 592 -1 + 5676 793 467 712 -1 + 5677 3894 3931 3886 -1 + 5678 3317 3319 3320 -1 + 5679 3320 3318 3317 -1 + 5680 3208 3317 3329 -1 + 5681 3329 3887 3208 -1 + 5682 3208 3887 3888 -1 + 5683 4083 1646 1041 -1 + 5684 4487 4311 4171 -1 + 5685 2282 2279 4120 -1 + 5686 3208 3898 3317 -1 + 5687 3319 3317 3898 -1 + 5688 3883 3319 3898 -1 + 5689 3892 3898 3890 -1 + 5690 3892 3897 3898 -1 + 5691 3890 3208 3888 -1 + 5692 3890 3898 3208 -1 + 5693 467 793 465 -1 + 5694 465 793 466 -1 + 5695 712 592 297 -1 + 5696 297 295 712 -1 + 5697 815 808 165 -1 + 5698 3102 3098 4123 -1 + 5699 3967 3123 3110 -1 + 5700 584 297 592 -1 + 5701 711 728 713 -1 + 5702 711 710 728 -1 + 5703 711 713 712 -1 + 5704 2279 2505 4120 -1 + 5705 2279 4015 2505 -1 + 5706 712 792 793 -1 + 5707 795 793 792 -1 + 5708 3967 3966 3965 -1 + 5709 4049 4098 1266 -1 + 5710 4402 1403 1391 -1 + 5711 3110 3123 3117 -1 + 5712 3604 3602 4304 -1 + 5713 313 3312 3315 -1 + 5714 3279 3900 3901 -1 + 5715 2838 3788 3741 -1 + 5716 4141 462 31 -1 + 5717 4619 4516 3988 -1 + 5718 3646 3093 4140 -1 + 5719 3967 3279 3901 -1 + 5720 4619 4550 2818 -1 + 5721 576 3313 3312 -1 + 5722 3062 3054 4309 -1 + 5723 2859 3356 2870 -1 + 5724 2859 3943 3356 -1 + 5725 2887 2704 2699 -1 + 5726 318 575 577 -1 + 5727 3152 3070 3153 -1 + 5728 1654 1639 4684 -1 + 5729 702 608 644 -1 + 5730 702 607 608 -1 + 5731 3215 580 3348 -1 + 5732 607 579 649 -1 + 5733 333 327 334 -1 + 5734 333 320 327 -1 + 5735 305 298 306 -1 + 5736 305 309 298 -1 + 5737 294 310 299 -1 + 5738 311 299 310 -1 + 5739 294 312 310 -1 + 5740 294 309 312 -1 + 5741 2859 2857 2858 -1 + 5742 2861 2857 2859 -1 + 5743 383 335 384 -1 + 5744 330 384 335 -1 + 5745 464 480 3551 -1 + 5746 464 465 480 -1 + 5747 3591 3590 3589 -1 + 5748 3591 3618 3590 -1 + 5749 3619 3590 3618 -1 + 5750 1044 1039 1040 -1 + 5751 3936 1039 1044 -1 + 5752 1913 1923 1915 -1 + 5753 1913 179 1923 -1 + 5754 2722 2723 2984 -1 + 5755 2984 2723 3936 -1 + 5756 2706 2723 2722 -1 + 5757 4206 973 934 -1 + 5758 934 973 932 -1 + 5759 3235 3255 3239 -1 + 5760 3239 3255 3240 -1 + 5761 2881 2876 2866 -1 + 5762 2881 2864 2876 -1 + 5763 2900 2898 2899 -1 + 5764 811 806 789 -1 + 5765 807 789 806 -1 + 5766 1844 2131 4225 -1 + 5767 1662 2661 1658 -1 + 5768 3958 3773 4069 -1 + 5769 4375 1843 2169 -1 + 5770 4375 2132 1843 -1 + 5771 2042 2015 2021 -1 + 5772 3193 3995 2683 -1 + 5773 1500 2680 1499 -1 + 5774 1500 1497 2680 -1 + 5775 4475 3151 4391 -1 + 5776 3249 3246 3248 -1 + 5777 3249 621 3246 -1 + 5778 4641 3714 3715 -1 + 5779 4530 3556 4338 -1 + 5780 3555 3716 4530 -1 + 5781 1306 3016 4135 -1 + 5782 1306 4041 3016 -1 + 5783 3205 3207 2643 -1 + 5784 3205 3204 3207 -1 + 5785 2671 3193 2683 -1 + 5786 1506 1507 3205 -1 + 5787 726 632 639 -1 + 5788 639 725 726 -1 + 5789 632 633 631 -1 + 5790 631 639 632 -1 + 5791 2918 2659 2920 -1 + 5792 4408 4401 3595 -1 + 5793 2909 2911 2910 -1 + 5794 643 862 641 -1 + 5795 3176 3248 3246 -1 + 5796 3176 3247 3248 -1 + 5797 607 333 579 -1 + 5798 3255 3253 3252 -1 + 5799 3255 3233 3253 -1 + 5800 2928 2900 2899 -1 + 5801 3212 4316 324 -1 + 5802 2928 2885 2900 -1 + 5803 328 334 327 -1 + 5804 3344 334 328 -1 + 5805 861 3263 3191 -1 + 5806 3191 3263 3194 -1 + 5807 863 641 862 -1 + 5808 3024 4037 4566 -1 + 5809 862 643 628 -1 + 5810 3235 3233 3255 -1 + 5811 2767 3233 3235 -1 + 5812 2887 2886 4512 -1 + 5813 2907 3196 2913 -1 + 5814 2912 2908 2903 -1 + 5815 626 628 643 -1 + 5816 2904 2913 2903 -1 + 5817 2905 2904 3995 -1 + 5818 2905 3193 3197 -1 + 5819 2905 3995 3193 -1 + 5820 2928 2899 2897 -1 + 5821 2897 2896 2928 -1 + 5822 3177 3274 3189 -1 + 5823 2676 3997 2646 -1 + 5824 2646 3997 2645 -1 + 5825 3955 3773 3774 -1 + 5826 3885 3997 3882 -1 + 5827 4737 4571 1585 -1 + 5828 2882 328 2883 -1 + 5829 2883 328 326 -1 + 5830 2928 2896 2929 -1 + 5831 649 608 607 -1 + 5832 334 3344 580 -1 + 5833 3344 3348 580 -1 + 5834 320 333 607 -1 + 5835 4073 3806 3805 -1 + 5836 4578 1920 4520 -1 + 5837 1919 1920 4578 -1 + 5838 703 704 702 -1 + 5839 1413 1411 4287 -1 + 5840 1413 1415 1411 -1 + 5841 1019 1020 4729 -1 + 5842 4585 2592 4480 -1 + 5843 2588 4480 2592 -1 + 5844 2941 2698 2702 -1 + 5845 2702 2698 2699 -1 + 5846 726 703 644 -1 + 5847 644 703 702 -1 + 5848 1273 3116 3126 -1 + 5849 3160 3126 3116 -1 + 5850 4729 4251 4728 -1 + 5851 4714 4251 4729 -1 + 5852 4448 2846 3221 -1 + 5853 4448 2849 2846 -1 + 5854 4723 2785 3504 -1 + 5855 2643 3207 2675 -1 + 5856 2863 3161 3283 -1 + 5857 2863 3157 3161 -1 + 5858 3191 3207 3187 -1 + 5859 2675 3207 2674 -1 + 5860 3191 2674 3207 -1 + 5861 2676 2675 2671 -1 + 5862 2677 2678 2646 -1 + 5863 4375 1844 2132 -1 + 5864 2169 1844 4375 -1 + 5865 4623 4571 4499 -1 + 5866 1723 1726 4515 -1 + 5867 3774 3956 3955 -1 + 5868 3881 2645 3884 -1 + 5869 2646 2645 3881 -1 + 5870 3881 2920 2646 -1 + 5871 1658 1137 1657 -1 + 5872 2880 2919 2895 -1 + 5873 2894 2862 2863 -1 + 5874 2863 2862 2890 -1 + 5875 4577 3096 3115 -1 + 5876 3115 3096 3099 -1 + 5877 3048 3047 4318 -1 + 5878 4723 4722 2785 -1 + 5879 4577 3115 3128 -1 + 5880 985 1274 1313 -1 + 5881 1607 1610 4561 -1 + 5882 3271 2890 2880 -1 + 5883 3022 3024 4075 -1 + 5884 3022 4006 3024 -1 + 5885 2863 2890 3271 -1 + 5886 3302 2826 2829 -1 + 5887 1071 1616 4182 -1 + 5888 3277 3275 3274 -1 + 5889 3763 3769 3764 -1 + 5890 3767 3764 3769 -1 + 5891 3303 3275 3338 -1 + 5892 3763 3764 3869 -1 + 5893 3303 3302 3275 -1 + 5894 3303 2826 3302 -1 + 5895 3270 2826 3303 -1 + 5896 1756 1413 4287 -1 + 5897 4211 3528 3519 -1 + 5898 3518 3519 3528 -1 + 5899 2941 2928 2929 -1 + 5900 2941 2885 2928 -1 + 5901 1275 1312 1234 -1 + 5902 991 993 4503 -1 + 5903 991 4482 993 -1 + 5904 2943 2845 2942 -1 + 5905 2699 2704 2702 -1 + 5906 4029 4078 4762 -1 + 5907 3767 3774 3773 -1 + 5908 3767 3768 3774 -1 + 5909 3763 3869 3786 -1 + 5910 4700 4468 4459 -1 + 5911 4541 4511 1109 -1 + 5912 3769 3763 3786 -1 + 5913 3786 3097 3098 -1 + 5914 3786 3264 3097 -1 + 5915 4709 1742 4453 -1 + 5916 3868 3869 3860 -1 + 5917 4074 3185 3184 -1 + 5918 2702 3807 3806 -1 + 5919 2702 3804 3807 -1 + 5920 2997 3055 4450 -1 + 5921 2997 4186 3055 -1 + 5922 1759 4163 1762 -1 + 5923 3870 3807 3266 -1 + 5924 3806 3807 3870 -1 + 5925 1091 933 4206 -1 + 5926 1091 1088 933 -1 + 5927 3870 3805 3806 -1 + 5928 3266 3807 3831 -1 + 5929 4206 933 973 -1 + 5930 4526 4226 1710 -1 + 5931 1710 1705 4526 -1 + 5932 3129 3127 3128 -1 + 5933 3128 3127 3103 -1 + 5934 3307 3217 3306 -1 + 5935 3121 3128 3152 -1 + 5936 3130 3152 3128 -1 + 5937 3152 3154 3121 -1 + 5938 1706 1722 4166 -1 + 5939 4306 4283 3172 -1 + 5940 3172 4283 3138 -1 + 5941 3536 3112 3111 -1 + 5942 3106 3536 3111 -1 + 5943 4306 3172 4136 -1 + 5944 1237 1686 4319 -1 + 5945 3314 3311 3310 -1 + 5946 2873 2872 4693 -1 + 5947 739 4366 4341 -1 + 5948 2794 2734 2801 -1 + 5949 3885 3882 4009 -1 + 5950 2798 3223 3070 -1 + 5951 3153 2801 3005 -1 + 5952 3153 2794 2801 -1 + 5953 3223 3153 3070 -1 + 5954 3326 3260 3257 -1 + 5955 3257 3225 3326 -1 + 5956 2783 2781 4385 -1 + 5957 3324 3260 3325 -1 + 5958 3325 3217 3324 -1 + 5959 3290 3316 3337 -1 + 5960 3337 3217 3334 -1 + 5961 3334 3217 3352 -1 + 5962 3337 3316 3217 -1 + 5963 3337 3334 3333 -1 + 5964 3250 2760 3251 -1 + 5965 2752 3251 2760 -1 + 5966 3183 3178 3179 -1 + 5967 3183 3182 3178 -1 + 5968 3289 3202 3290 -1 + 5969 3290 3333 3289 -1 + 5970 1574 1606 4419 -1 + 5971 2795 3186 3181 -1 + 5972 2795 3181 2734 -1 + 5973 2734 2794 2795 -1 + 5974 3259 3326 3225 -1 + 5975 3259 3244 3326 -1 + 5976 3259 2762 3245 -1 + 5977 3259 3225 2762 -1 + 5978 3226 3202 3224 -1 + 5979 3298 3202 3226 -1 + 5980 1200 4143 2284 -1 + 5981 4200 3628 3613 -1 + 5982 3372 3613 3628 -1 + 5983 4180 3684 3688 -1 + 5984 3688 3684 3690 -1 + 5985 1993 2044 2036 -1 + 5986 4479 4255 1140 -1 + 5987 4368 2971 2988 -1 + 5988 4549 631 4514 -1 + 5989 2752 2748 3251 -1 + 5990 2752 2728 2748 -1 + 5991 3143 3037 3046 -1 + 5992 3022 3046 3037 -1 + 5993 3250 3242 2760 -1 + 5994 3305 3231 3227 -1 + 5995 3244 3231 3305 -1 + 5996 2754 3199 3347 -1 + 5997 2754 3198 3199 -1 + 5998 3346 2754 3347 -1 + 5999 3353 3351 3355 -1 + 6000 3265 3351 3353 -1 + 6001 3353 3198 3201 -1 + 6002 3353 3199 3198 -1 + 6003 3354 3199 3353 -1 + 6004 3353 3201 3265 -1 + 6005 3355 3354 3353 -1 + 6006 3355 3217 3354 -1 + 6007 4010 9 1847 -1 + 6008 4754 4753 2855 -1 + 6009 3349 3293 3350 -1 + 6010 2738 2749 3232 -1 + 6011 2738 2735 2749 -1 + 6012 3349 3210 3351 -1 + 6013 3351 3210 3355 -1 + 6014 3200 3179 3265 -1 + 6015 3349 3350 3217 -1 + 6016 3352 3217 3350 -1 + 6017 3349 3217 3210 -1 + 6018 3210 3217 3355 -1 + 6019 3209 3217 3345 -1 + 6020 3209 3347 3199 -1 + 6021 3209 3345 3347 -1 + 6022 3209 3354 3217 -1 + 6023 3199 3354 3209 -1 + 6024 1246 4004 1243 -1 + 6025 3216 3217 3325 -1 + 6026 3216 3326 3244 -1 + 6027 3216 3325 3326 -1 + 6028 3326 3325 3260 -1 + 6029 3306 3218 3304 -1 + 6030 3304 3218 3305 -1 + 6031 3305 3218 3216 -1 + 6032 370 375 377 -1 + 6033 3735 375 370 -1 + 6034 2711 2670 2686 -1 + 6035 2712 2670 2711 -1 + 6036 4187 3292 3301 -1 + 6037 3049 3059 3068 -1 + 6038 3055 3068 3059 -1 + 6039 1314 1237 4052 -1 + 6040 1314 4027 1237 -1 + 6041 1064 2587 4053 -1 + 6042 1064 2588 2587 -1 + 6043 4053 2587 1062 -1 + 6044 1518 2014 4238 -1 + 6045 2736 2800 2801 -1 + 6046 2801 2733 2736 -1 + 6047 3059 3062 3063 -1 + 6048 3059 3049 3062 -1 + 6049 4562 4343 1670 -1 + 6050 1670 4343 1669 -1 + 6051 3211 3212 4595 -1 + 6052 3211 4316 3212 -1 + 6053 3220 2823 2824 -1 + 6054 2825 2824 2823 -1 + 6055 2850 2820 2823 -1 + 6056 2850 3220 3213 -1 + 6057 2850 2823 3220 -1 + 6058 2844 2843 2837 -1 + 6059 2844 2848 2843 -1 + 6060 316 323 3221 -1 + 6061 3221 323 315 -1 + 6062 324 323 3213 -1 + 6063 4687 4420 2994 -1 + 6064 2819 4492 4070 -1 + 6065 2813 3993 2822 -1 + 6066 3986 2822 3993 -1 + 6067 502 504 1426 -1 + 6068 1426 1445 502 -1 + 6069 2846 2849 2847 -1 + 6070 2844 2847 2849 -1 + 6071 3212 3213 3220 -1 + 6072 324 3213 3212 -1 + 6073 4602 4660 1407 -1 + 6074 4656 4649 3051 -1 + 6075 3051 3053 4656 -1 + 6076 2844 2849 2848 -1 + 6077 3206 2848 2849 -1 + 6078 4657 4021 1216 -1 + 6079 3206 4350 2819 -1 + 6080 2071 2066 2070 -1 + 6081 2065 2066 2071 -1 + 6082 352 2955 2956 -1 + 6083 2956 2955 2960 -1 + 6084 2965 2975 2974 -1 + 6085 2965 2972 2975 -1 + 6086 2833 2976 2830 -1 + 6087 2831 2830 2976 -1 + 6088 3854 3855 3753 -1 + 6089 3854 3858 3855 -1 + 6090 243 240 302 -1 + 6091 302 240 241 -1 + 6092 2837 2959 317 -1 + 6093 2837 2843 2959 -1 + 6094 593 636 587 -1 + 6095 637 587 636 -1 + 6096 316 317 353 -1 + 6097 353 351 316 -1 + 6098 325 351 349 -1 + 6099 349 2851 325 -1 + 6100 2985 2956 2960 -1 + 6101 2960 2989 2985 -1 + 6102 1434 2057 2049 -1 + 6103 2049 2057 2050 -1 + 6104 2961 2963 2972 -1 + 6105 2972 2965 2961 -1 + 6106 2926 2992 2982 -1 + 6107 2914 2982 2992 -1 + 6108 246 249 245 -1 + 6109 246 3790 249 -1 + 6110 2990 2916 2991 -1 + 6111 2991 2968 2990 -1 + 6112 791 798 802 -1 + 6113 791 790 798 -1 + 6114 10 544 52 -1 + 6115 2985 354 2956 -1 + 6116 355 2956 354 -1 + 6117 790 2986 2906 -1 + 6118 2906 2986 2923 -1 + 6119 350 344 2916 -1 + 6120 340 344 350 -1 + 6121 2986 2987 2923 -1 + 6122 2923 2987 2980 -1 + 6123 2926 2927 2983 -1 + 6124 2983 2927 2978 -1 + 6125 2914 2992 2968 -1 + 6126 2968 2991 2914 -1 + 6127 788 799 800 -1 + 6128 788 785 799 -1 + 6129 2714 2937 2708 -1 + 6130 2710 2937 2714 -1 + 6131 2952 2835 2954 -1 + 6132 2952 2834 2835 -1 + 6133 2952 2990 2966 -1 + 6134 2952 2953 2990 -1 + 6135 6 36 557 -1 + 6136 37 36 6 -1 + 6137 802 799 803 -1 + 6138 798 799 802 -1 + 6139 2926 2982 2925 -1 + 6140 2925 2927 2926 -1 + 6141 2932 2931 2906 -1 + 6142 2906 2931 797 -1 + 6143 2935 2934 2933 -1 + 6144 2935 2689 2934 -1 + 6145 3832 3821 3834 -1 + 6146 3809 3821 3832 -1 + 6147 249 234 245 -1 + 6148 231 245 234 -1 + 6149 3823 3825 3854 -1 + 6150 3854 3825 3873 -1 + 6151 2932 2923 2922 -1 + 6152 2906 2923 2932 -1 + 6153 3770 3977 3791 -1 + 6154 3791 3977 3789 -1 + 6155 4551 4485 4505 -1 + 6156 4551 1609 4485 -1 + 6157 4592 4613 4553 -1 + 6158 300 301 2816 -1 + 6159 304 301 300 -1 + 6160 4720 4713 3868 -1 + 6161 4526 4422 4226 -1 + 6162 4644 3482 3484 -1 + 6163 4708 1121 1120 -1 + 6164 3732 3731 3855 -1 + 6165 3855 3731 3726 -1 + 6166 259 260 254 -1 + 6167 254 258 259 -1 + 6168 259 258 370 -1 + 6169 251 370 258 -1 + 6170 259 370 377 -1 + 6171 377 367 259 -1 + 6172 2811 2775 2663 -1 + 6173 2811 886 2775 -1 + 6174 3972 3954 3910 -1 + 6175 3910 3909 3972 -1 + 6176 3771 3961 3775 -1 + 6177 3779 3775 3961 -1 + 6178 383 326 335 -1 + 6179 383 2883 326 -1 + 6180 2876 2879 2875 -1 + 6181 2876 2878 2879 -1 + 6182 2875 2869 2866 -1 + 6183 2866 2876 2875 -1 + 6184 2875 2867 2869 -1 + 6185 2868 2869 2867 -1 + 6186 2875 3913 2867 -1 + 6187 2875 2879 3913 -1 + 6188 626 623 858 -1 + 6189 621 858 623 -1 + 6190 3240 3255 3252 -1 + 6191 2756 2751 3229 -1 + 6192 3229 3236 2756 -1 + 6193 2755 2756 3236 -1 + 6194 3236 2757 2755 -1 + 6195 851 915 837 -1 + 6196 837 915 905 -1 + 6197 541 1366 1367 -1 + 6198 539 1366 541 -1 + 6199 890 919 893 -1 + 6200 893 919 920 -1 + 6201 2793 2791 2797 -1 + 6202 2792 2791 2793 -1 + 6203 904 919 906 -1 + 6204 906 919 2796 -1 + 6205 777 761 776 -1 + 6206 776 763 777 -1 + 6207 2740 2741 2774 -1 + 6208 2774 2808 2740 -1 + 6209 425 548 426 -1 + 6210 428 548 425 -1 + 6211 2779 2797 890 -1 + 6212 2779 2793 2797 -1 + 6213 890 2796 919 -1 + 6214 2797 2796 890 -1 + 6215 1372 1386 1387 -1 + 6216 1385 1387 1386 -1 + 6217 2742 3239 2766 -1 + 6218 2768 3239 2742 -1 + 6219 2740 2768 2742 -1 + 6220 2740 2809 2768 -1 + 6221 773 757 758 -1 + 6222 773 755 757 -1 + 6223 2778 890 893 -1 + 6224 2778 2779 890 -1 + 6225 2811 2651 2644 -1 + 6226 2644 2788 2811 -1 + 6227 1514 1370 1511 -1 + 6228 1511 1370 1371 -1 + 6229 1345 567 542 -1 + 6230 542 567 569 -1 + 6231 2709 2714 2708 -1 + 6232 2708 2647 2709 -1 + 6233 2688 2935 2710 -1 + 6234 2937 2710 2935 -1 + 6235 2931 782 783 -1 + 6236 2931 2721 782 -1 + 6237 886 2811 2788 -1 + 6238 2788 888 886 -1 + 6239 1339 1338 1336 -1 + 6240 1336 1338 1334 -1 + 6241 888 780 887 -1 + 6242 888 2788 780 -1 + 6243 758 818 759 -1 + 6244 757 818 758 -1 + 6245 2666 2774 2770 -1 + 6246 2776 2774 2666 -1 + 6247 2651 2664 2715 -1 + 6248 2715 2664 2665 -1 + 6249 887 760 881 -1 + 6250 881 888 887 -1 + 6251 886 889 917 -1 + 6252 917 2775 886 -1 + 6253 2663 2651 2811 -1 + 6254 2664 2651 2663 -1 + 6255 916 851 918 -1 + 6256 916 915 851 -1 + 6257 2666 2770 2769 -1 + 6258 2769 2770 2772 -1 + 6259 916 889 882 -1 + 6260 916 917 889 -1 + 6261 777 894 761 -1 + 6262 760 761 894 -1 + 6263 4671 4298 1111 -1 + 6264 4297 4758 4750 -1 + 6265 1996 1363 1995 -1 + 6266 1365 1363 1996 -1 + 6267 1996 2010 2011 -1 + 6268 1373 1368 1364 -1 + 6269 1373 428 1368 -1 + 6270 2011 1365 1996 -1 + 6271 2011 1388 1365 -1 + 6272 2007 2011 2008 -1 + 6273 1373 1372 427 -1 + 6274 1373 1386 1372 -1 + 6275 2005 168 1992 -1 + 6276 186 168 2005 -1 + 6277 1988 1989 1999 -1 + 6278 1988 1998 1989 -1 + 6279 160 207 205 -1 + 6280 158 207 160 -1 + 6281 1370 1368 1371 -1 + 6282 1371 1368 1369 -1 + 6283 1369 428 425 -1 + 6284 1369 1368 428 -1 + 6285 1510 1501 1509 -1 + 6286 1510 436 1501 -1 + 6287 1512 1374 1514 -1 + 6288 431 426 432 -1 + 6289 431 425 426 -1 + 6290 1602 1592 1593 -1 + 6291 829 868 828 -1 + 6292 828 868 836 -1 + 6293 829 907 867 -1 + 6294 867 907 830 -1 + 6295 2041 2036 2044 -1 + 6296 2041 2035 2036 -1 + 6297 829 627 868 -1 + 6298 867 627 829 -1 + 6299 2765 3240 3252 -1 + 6300 3241 3249 3229 -1 + 6301 3256 3229 3249 -1 + 6302 3241 858 621 -1 + 6303 621 3249 3241 -1 + 6304 420 425 431 -1 + 6305 420 1369 425 -1 + 6306 906 907 827 -1 + 6307 906 2726 907 -1 + 6308 895 891 768 -1 + 6309 768 891 766 -1 + 6310 2411 2403 2412 -1 + 6311 2413 2412 2403 -1 + 6312 990 979 4265 -1 + 6313 3006 3004 4475 -1 + 6314 4215 4116 1744 -1 + 6315 205 161 160 -1 + 6316 205 432 161 -1 + 6317 860 871 861 -1 + 6318 3263 861 871 -1 + 6319 860 863 862 -1 + 6320 860 861 863 -1 + 6321 4103 2812 4249 -1 + 6322 2707 2691 2701 -1 + 6323 2701 807 2707 -1 + 6324 2707 2647 2708 -1 + 6325 2648 2647 2707 -1 + 6326 1344 1340 1339 -1 + 6327 1344 1345 1340 -1 + 6328 2946 2948 2694 -1 + 6329 2694 2948 2689 -1 + 6330 1367 1372 1387 -1 + 6331 1367 1366 1372 -1 + 6332 1345 1376 1340 -1 + 6333 1345 542 1376 -1 + 6334 1345 1344 1335 -1 + 6335 1335 567 1345 -1 + 6336 1334 1343 1336 -1 + 6337 1334 1342 1343 -1 + 6338 1347 1344 1346 -1 + 6339 1347 1335 1344 -1 + 6340 1347 1346 1360 -1 + 6341 1360 1346 1359 -1 + 6342 1347 565 1335 -1 + 6343 1335 565 572 -1 + 6344 564 496 559 -1 + 6345 561 496 564 -1 + 6346 783 781 809 -1 + 6347 783 782 781 -1 + 6348 783 800 797 -1 + 6349 797 2931 783 -1 + 6350 1431 1424 1428 -1 + 6351 1349 1424 1431 -1 + 6352 1358 565 1356 -1 + 6353 1358 554 565 -1 + 6354 1431 1354 1349 -1 + 6355 562 561 564 -1 + 6356 1352 561 562 -1 + 6357 498 497 496 -1 + 6358 496 1433 498 -1 + 6359 1442 1445 1435 -1 + 6360 1435 1445 1426 -1 + 6361 2050 2054 2051 -1 + 6362 2050 2057 2054 -1 + 6363 2068 2046 2045 -1 + 6364 2068 2048 2046 -1 + 6365 2070 2059 2071 -1 + 6366 1427 1425 2059 -1 + 6367 1427 1444 1425 -1 + 6368 2067 2047 2060 -1 + 6369 20 2060 2047 -1 + 6370 1440 1419 1441 -1 + 6371 1441 1419 1396 -1 + 6372 502 503 504 -1 + 6373 505 504 503 -1 + 6374 50 505 503 -1 + 6375 1397 1420 1436 -1 + 6376 1504 1419 1439 -1 + 6377 1504 1420 1419 -1 + 6378 1504 1397 1436 -1 + 6379 1504 1439 1437 -1 + 6380 1437 1397 1504 -1 + 6381 51 2479 4076 -1 + 6382 2064 2024 2025 -1 + 6383 2061 2025 2024 -1 + 6384 2023 2024 2064 -1 + 6385 1508 1439 1419 -1 + 6386 495 1439 1508 -1 + 6387 1508 1440 2063 -1 + 6388 1508 1419 1440 -1 + 6389 1351 1432 1352 -1 + 6390 1352 1432 561 -1 + 6391 2000 2003 2002 -1 + 6392 1431 1348 1354 -1 + 6393 1429 1348 1431 -1 + 6394 1428 1429 1431 -1 + 6395 1428 1430 1429 -1 + 6396 2229 2315 2305 -1 + 6397 2305 2315 2314 -1 + 6398 2025 2065 2069 -1 + 6399 2066 2065 2025 -1 + 6400 2003 1342 1334 -1 + 6401 1380 1338 1378 -1 + 6402 1380 1377 1338 -1 + 6403 2010 2012 2008 -1 + 6404 2008 2011 2010 -1 + 6405 835 833 834 -1 + 6406 835 831 833 -1 + 6407 1375 1389 1381 -1 + 6408 536 534 535 -1 + 6409 536 540 534 -1 + 6410 2002 2003 1377 -1 + 6411 2383 2379 870 -1 + 6412 870 2379 831 -1 + 6413 1377 1334 1338 -1 + 6414 2003 1334 1377 -1 + 6415 541 1376 540 -1 + 6416 541 1379 1376 -1 + 6417 1997 1990 1994 -1 + 6418 1994 2032 1997 -1 + 6419 1380 1381 1389 -1 + 6420 1380 1382 1381 -1 + 6421 2010 1999 2012 -1 + 6422 1989 2012 1999 -1 + 6423 535 534 544 -1 + 6424 1992 168 2006 -1 + 6425 2006 2004 1992 -1 + 6426 1375 1365 1388 -1 + 6427 1375 1383 1365 -1 + 6428 544 534 569 -1 + 6429 569 534 542 -1 + 6430 544 569 52 -1 + 6431 2117 2186 2112 -1 + 6432 2185 2112 2186 -1 + 6433 168 167 2006 -1 + 6434 176 167 168 -1 + 6435 692 2457 2461 -1 + 6436 692 684 2457 -1 + 6437 2184 1458 2181 -1 + 6438 2391 2243 2240 -1 + 6439 2391 2242 2243 -1 + 6440 663 2615 2594 -1 + 6441 2594 664 663 -1 + 6442 2418 2413 2395 -1 + 6443 2418 2412 2413 -1 + 6444 2417 2397 2416 -1 + 6445 2416 2392 2417 -1 + 6446 2395 2392 2419 -1 + 6447 2419 2392 2416 -1 + 6448 1991 2010 1996 -1 + 6449 1999 2010 1991 -1 + 6450 2318 2270 2306 -1 + 6451 2306 2270 2316 -1 + 6452 2058 43 2056 -1 + 6453 2058 44 43 -1 + 6454 1083 1135 1086 -1 + 6455 1000 1135 1083 -1 + 6456 2027 2063 2026 -1 + 6457 12 50 495 -1 + 6458 49 2022 2061 -1 + 6459 49 50 2022 -1 + 6460 2265 2274 2273 -1 + 6461 2273 2274 2287 -1 + 6462 2026 2066 2025 -1 + 6463 2063 2066 2026 -1 + 6464 581 583 2264 -1 + 6465 12 495 2027 -1 + 6466 2022 2027 2026 -1 + 6467 2022 2026 2061 -1 + 6468 2061 2026 2025 -1 + 6469 26 497 498 -1 + 6470 49 505 50 -1 + 6471 2267 2268 2271 -1 + 6472 2271 2268 2310 -1 + 6473 2066 2063 2070 -1 + 6474 2067 2060 2062 -1 + 6475 2062 2069 2067 -1 + 6476 2070 1440 1427 -1 + 6477 1427 1440 1441 -1 + 6478 2070 2063 1440 -1 + 6479 26 498 504 -1 + 6480 1426 504 498 -1 + 6481 2071 2049 2048 -1 + 6482 2059 2049 2071 -1 + 6483 2071 2048 2065 -1 + 6484 2065 2048 2068 -1 + 6485 2045 2067 2068 -1 + 6486 2045 2047 2067 -1 + 6487 1823 1780 1818 -1 + 6488 1818 1778 1823 -1 + 6489 2056 2047 2045 -1 + 6490 2056 43 2047 -1 + 6491 619 2217 616 -1 + 6492 616 2217 2211 -1 + 6493 2315 2290 2314 -1 + 6494 2314 2290 2304 -1 + 6495 619 617 618 -1 + 6496 616 617 619 -1 + 6497 2315 2316 2290 -1 + 6498 2306 2316 2315 -1 + 6499 2297 2300 2296 -1 + 6500 2296 2303 2297 -1 + 6501 2318 2319 2317 -1 + 6502 2318 2306 2319 -1 + 6503 1209 1206 1210 -1 + 6504 497 26 44 -1 + 6505 2226 2224 2225 -1 + 6506 2226 613 2224 -1 + 6507 2260 2224 629 -1 + 6508 614 629 2224 -1 + 6509 2320 2238 2239 -1 + 6510 2249 2238 2320 -1 + 6511 2250 2251 2256 -1 + 6512 2254 2256 2251 -1 + 6513 614 613 618 -1 + 6514 2224 613 614 -1 + 6515 2248 2249 2247 -1 + 6516 2238 2249 2248 -1 + 6517 4 42 2072 -1 + 6518 4 27 42 -1 + 6519 1343 1342 1341 -1 + 6520 10 27 4 -1 + 6521 10 52 27 -1 + 6522 2077 2076 2080 -1 + 6523 2077 2074 2076 -1 + 6524 2073 2075 2074 -1 + 6525 2072 2075 2073 -1 + 6526 2073 2078 2009 -1 + 6527 2009 63 2073 -1 + 6528 27 52 46 -1 + 6529 2259 2246 2247 -1 + 6530 2259 2254 2246 -1 + 6531 573 556 46 -1 + 6532 566 573 46 -1 + 6533 566 572 573 -1 + 6534 46 52 566 -1 + 6535 557 559 7 -1 + 6536 46 556 36 -1 + 6537 46 36 27 -1 + 6538 27 36 42 -1 + 6539 557 36 556 -1 + 6540 2 4 63 -1 + 6541 2 10 4 -1 + 6542 2 176 1 -1 + 6543 167 176 2 -1 + 6544 2389 2390 2388 -1 + 6545 2388 2390 2370 -1 + 6546 864 866 874 -1 + 6547 877 874 866 -1 + 6548 864 2239 2233 -1 + 6549 2233 866 864 -1 + 6550 4328 4210 332 -1 + 6551 2372 2389 2377 -1 + 6552 2377 2389 2388 -1 + 6553 2247 2243 2259 -1 + 6554 2247 2249 2243 -1 + 6555 874 875 876 -1 + 6556 874 872 875 -1 + 6557 880 879 772 -1 + 6558 772 896 880 -1 + 6559 2384 840 2380 -1 + 6560 2384 849 840 -1 + 6561 874 877 872 -1 + 6562 872 877 873 -1 + 6563 878 855 873 -1 + 6564 873 877 878 -1 + 6565 765 768 767 -1 + 6566 2360 768 765 -1 + 6567 869 835 855 -1 + 6568 855 835 873 -1 + 6569 2356 2367 2344 -1 + 6570 426 549 432 -1 + 6571 609 2281 596 -1 + 6572 2578 596 2281 -1 + 6573 772 779 771 -1 + 6574 762 779 772 -1 + 6575 2366 2328 2325 -1 + 6576 2325 2360 2366 -1 + 6577 2917 2661 4457 -1 + 6578 4325 4092 4307 -1 + 6579 56 815 812 -1 + 6580 4584 3051 4573 -1 + 6581 880 896 897 -1 + 6582 885 897 896 -1 + 6583 696 733 707 -1 + 6584 707 733 701 -1 + 6585 708 2582 2547 -1 + 6586 2547 2582 2546 -1 + 6587 4635 4121 1254 -1 + 6588 1254 4121 4050 -1 + 6589 2038 2041 2044 -1 + 6590 895 892 891 -1 + 6591 895 2324 892 -1 + 6592 155 154 538 -1 + 6593 892 884 891 -1 + 6594 891 884 885 -1 + 6595 4217 4198 3595 -1 + 6596 39 31 32 -1 + 6597 2109 2104 2103 -1 + 6598 2103 2104 2106 -1 + 6599 2102 2111 2114 -1 + 6600 2114 2111 2120 -1 + 6601 4440 3371 3380 -1 + 6602 2040 218 220 -1 + 6603 2043 218 2040 -1 + 6604 2042 2021 2034 -1 + 6605 2034 2021 2037 -1 + 6606 2019 1055 2028 -1 + 6607 2016 1055 2019 -1 + 6608 2040 220 207 -1 + 6609 207 220 223 -1 + 6610 2040 158 2039 -1 + 6611 2040 207 158 -1 + 6612 2042 2014 2015 -1 + 6613 2042 2034 2041 -1 + 6614 2658 2653 2652 -1 + 6615 1038 1045 4167 -1 + 6616 1593 1557 1602 -1 + 6617 4594 3212 2830 -1 + 6618 2830 3212 3220 -1 + 6619 1100 1082 4214 -1 + 6620 1100 1125 1082 -1 + 6621 4239 1562 1560 -1 + 6622 1512 1515 1513 -1 + 6623 1050 1053 1047 -1 + 6624 1136 1663 1138 -1 + 6625 1136 1212 1663 -1 + 6626 1658 2862 2894 -1 + 6627 1361 1362 1374 -1 + 6628 2005 153 186 -1 + 6629 155 186 153 -1 + 6630 538 535 177 -1 + 6631 538 177 155 -1 + 6632 155 177 186 -1 + 6633 538 550 536 -1 + 6634 539 536 550 -1 + 6635 538 536 535 -1 + 6636 154 550 538 -1 + 6637 720 722 732 -1 + 6638 732 722 721 -1 + 6639 847 842 850 -1 + 6640 847 846 842 -1 + 6641 839 842 910 -1 + 6642 910 842 845 -1 + 6643 839 898 899 -1 + 6644 910 898 839 -1 + 6645 674 664 670 -1 + 6646 2594 670 664 -1 + 6647 2385 2244 2375 -1 + 6648 2376 2375 2244 -1 + 6649 902 843 844 -1 + 6650 844 2398 902 -1 + 6651 902 912 843 -1 + 6652 843 912 901 -1 + 6653 2641 2616 2642 -1 + 6654 2639 2616 2641 -1 + 6655 2558 2559 2561 -1 + 6656 2561 2556 2558 -1 + 6657 903 846 832 -1 + 6658 832 853 903 -1 + 6659 2417 2335 2402 -1 + 6660 2417 2393 2335 -1 + 6661 2386 2381 2396 -1 + 6662 2396 853 2386 -1 + 6663 2373 2376 2372 -1 + 6664 2375 2376 2373 -1 + 6665 2385 2375 2381 -1 + 6666 2381 2386 2385 -1 + 6667 2599 2597 2600 -1 + 6668 2599 2598 2597 -1 + 6669 2332 2333 2326 -1 + 6670 2324 2326 2333 -1 + 6671 847 833 846 -1 + 6672 846 833 832 -1 + 6673 2471 2606 2463 -1 + 6674 2471 2458 2606 -1 + 6675 2373 2374 2375 -1 + 6676 2381 2375 2374 -1 + 6677 2373 2419 2374 -1 + 6678 2378 2419 2373 -1 + 6679 2241 2390 2240 -1 + 6680 2240 2390 2391 -1 + 6681 2382 2242 2391 -1 + 6682 2382 2245 2242 -1 + 6683 2389 2391 2390 -1 + 6684 2382 2391 2389 -1 + 6685 2411 2412 2414 -1 + 6686 2414 2415 2411 -1 + 6687 2411 2406 2407 -1 + 6688 2407 2403 2411 -1 + 6689 2627 718 734 -1 + 6690 717 734 718 -1 + 6691 897 908 911 -1 + 6692 911 914 897 -1 + 6693 880 897 914 -1 + 6694 914 913 880 -1 + 6695 2359 2362 770 -1 + 6696 770 762 2359 -1 + 6697 2362 2342 2322 -1 + 6698 2362 2359 2342 -1 + 6699 729 731 700 -1 + 6700 700 724 729 -1 + 6701 931 1012 930 -1 + 6702 2558 2616 2618 -1 + 6703 2617 2616 2558 -1 + 6704 1894 1893 1931 -1 + 6705 1886 1931 1893 -1 + 6706 208 183 214 -1 + 6707 209 183 208 -1 + 6708 2535 2565 2538 -1 + 6709 2550 2538 2565 -1 + 6710 2535 2536 2539 -1 + 6711 2538 2536 2535 -1 + 6712 2558 2556 2617 -1 + 6713 2623 2617 2556 -1 + 6714 733 724 701 -1 + 6715 700 701 724 -1 + 6716 2542 2543 2559 -1 + 6717 2561 2559 2543 -1 + 6718 719 2618 2639 -1 + 6719 2639 2618 2616 -1 + 6720 716 667 668 -1 + 6721 668 665 716 -1 + 6722 716 730 721 -1 + 6723 715 730 716 -1 + 6724 656 671 654 -1 + 6725 654 655 656 -1 + 6726 656 662 661 -1 + 6727 656 660 662 -1 + 6728 691 684 690 -1 + 6729 690 684 692 -1 + 6730 1664 4277 4108 -1 + 6731 2625 2626 735 -1 + 6732 735 714 2625 -1 + 6733 1909 1907 1910 -1 + 6734 1909 1908 1907 -1 + 6735 2605 2596 2603 -1 + 6736 2605 669 2596 -1 + 6737 2612 2534 2608 -1 + 6738 2607 2608 2534 -1 + 6739 2613 2614 2612 -1 + 6740 672 2614 2613 -1 + 6741 651 658 659 -1 + 6742 652 658 651 -1 + 6743 651 670 669 -1 + 6744 657 670 651 -1 + 6745 2424 2426 2425 -1 + 6746 2425 2426 2450 -1 + 6747 680 19 682 -1 + 6748 680 2447 19 -1 + 6749 1847 144 1848 -1 + 6750 1847 8 144 -1 + 6751 2286 2287 1199 -1 + 6752 2274 1199 2287 -1 + 6753 2461 2458 2471 -1 + 6754 2461 2457 2458 -1 + 6755 2614 2460 2452 -1 + 6756 653 2460 2614 -1 + 6757 2256 2257 2258 -1 + 6758 2256 2255 2257 -1 + 6759 2259 2242 2255 -1 + 6760 2259 2243 2242 -1 + 6761 656 661 671 -1 + 6762 653 671 661 -1 + 6763 691 688 683 -1 + 6764 683 684 691 -1 + 6765 1780 1781 1779 -1 + 6766 1779 1818 1780 -1 + 6767 1867 1792 1859 -1 + 6768 1867 1856 1792 -1 + 6769 1850 2498 1845 -1 + 6770 1850 1851 2498 -1 + 6771 3514 3418 3503 -1 + 6772 2490 2479 2485 -1 + 6773 2485 2483 2490 -1 + 6774 2503 2502 2483 -1 + 6775 2503 2501 2502 -1 + 6776 2503 2532 2484 -1 + 6777 2503 2486 2532 -1 + 6778 2503 2483 2486 -1 + 6779 2485 2486 2483 -1 + 6780 61 57 2479 -1 + 6781 2485 2479 57 -1 + 6782 2318 2317 2285 -1 + 6783 2272 2285 2317 -1 + 6784 2510 2533 2508 -1 + 6785 2508 2533 2514 -1 + 6786 2518 2443 2519 -1 + 6787 2518 2507 2443 -1 + 6788 41 678 681 -1 + 6789 41 2527 678 -1 + 6790 2438 2514 2517 -1 + 6791 2508 2514 2438 -1 + 6792 2441 2519 2443 -1 + 6793 2512 2519 2441 -1 + 6794 2438 2517 2446 -1 + 6795 2446 2517 2515 -1 + 6796 2255 2245 2257 -1 + 6797 2242 2245 2255 -1 + 6798 2262 854 624 -1 + 6799 615 624 854 -1 + 6800 2258 2234 2235 -1 + 6801 2258 2257 2234 -1 + 6802 2232 870 869 -1 + 6803 2235 870 2232 -1 + 6804 4341 738 736 -1 + 6805 2252 2250 2262 -1 + 6806 2252 2251 2250 -1 + 6807 2236 2232 854 -1 + 6808 854 2232 625 -1 + 6809 2225 2260 2261 -1 + 6810 2225 2224 2260 -1 + 6811 2434 2436 2435 -1 + 6812 2435 2440 2434 -1 + 6813 614 618 630 -1 + 6814 630 618 617 -1 + 6815 2206 2253 2246 -1 + 6816 2246 2253 2208 -1 + 6817 685 2455 676 -1 + 6818 685 2464 2455 -1 + 6819 2260 629 2233 -1 + 6820 2233 2237 2260 -1 + 6821 2216 2210 2211 -1 + 6822 2216 2214 2210 -1 + 6823 2211 2221 2216 -1 + 6824 2211 2217 2221 -1 + 6825 2230 2253 2209 -1 + 6826 2228 2253 2230 -1 + 6827 116 410 474 -1 + 6828 474 410 1464 -1 + 6829 2293 2230 2209 -1 + 6830 2209 2215 2293 -1 + 6831 635 647 620 -1 + 6832 620 647 646 -1 + 6833 629 614 865 -1 + 6834 865 614 630 -1 + 6835 629 866 2233 -1 + 6836 865 866 629 -1 + 6837 635 620 613 -1 + 6838 613 2226 635 -1 + 6839 2319 2294 2317 -1 + 6840 2292 2317 2294 -1 + 6841 4341 736 740 -1 + 6842 740 736 746 -1 + 6843 2177 2176 2173 -1 + 6844 2173 2176 2172 -1 + 6845 1209 1210 2266 -1 + 6846 635 634 647 -1 + 6847 2218 634 635 -1 + 6848 2301 2296 2218 -1 + 6849 2302 2296 2301 -1 + 6850 2301 2225 2227 -1 + 6851 2301 2226 2225 -1 + 6852 1862 1864 1875 -1 + 6853 1875 1864 1797 -1 + 6854 1200 2264 583 -1 + 6855 638 646 647 -1 + 6856 638 2220 646 -1 + 6857 2017 218 2037 -1 + 6858 219 218 2017 -1 + 6859 2487 2521 2520 -1 + 6860 2487 2531 2521 -1 + 6861 1209 2266 1206 -1 + 6862 4433 4547 4082 -1 + 6863 2168 1843 2176 -1 + 6864 2300 2218 2296 -1 + 6865 2300 634 2218 -1 + 6866 5 60 62 -1 + 6867 2529 62 60 -1 + 6868 2177 2169 2176 -1 + 6869 1844 2169 2177 -1 + 6870 2524 2482 2523 -1 + 6871 40 2482 2524 -1 + 6872 2298 2265 2273 -1 + 6873 2298 2307 2265 -1 + 6874 4641 4513 3714 -1 + 6875 2177 2173 2155 -1 + 6876 2155 2170 2177 -1 + 6877 4312 4244 3386 -1 + 6878 1539 2150 1537 -1 + 6879 4125 3646 3644 -1 + 6880 1206 2266 2310 -1 + 6881 2310 2268 1206 -1 + 6882 578 583 1208 -1 + 6883 4335 4032 1702 -1 + 6884 1702 1270 4335 -1 + 6885 1833 1874 1831 -1 + 6886 1841 1831 1874 -1 + 6887 1833 1831 1830 -1 + 6888 1830 1853 1833 -1 + 6889 1393 1395 4066 -1 + 6890 1199 2274 2275 -1 + 6891 4340 1017 1022 -1 + 6892 1199 2275 2263 -1 + 6893 2286 1199 2263 -1 + 6894 2263 2264 2286 -1 + 6895 2286 2264 2284 -1 + 6896 1200 2284 2264 -1 + 6897 1106 952 975 -1 + 6898 975 976 1106 -1 + 6899 4651 4540 4294 -1 + 6900 1147 1171 1146 -1 + 6901 1146 1171 1266 -1 + 6902 1315 1319 1318 -1 + 6903 1318 1319 1322 -1 + 6904 1707 1706 4166 -1 + 6905 977 1093 978 -1 + 6906 942 941 926 -1 + 6907 942 943 941 -1 + 6908 4240 1648 1649 -1 + 6909 1579 1563 1593 -1 + 6910 1558 1555 1556 -1 + 6911 972 1005 1008 -1 + 6912 974 1005 972 -1 + 6913 1171 1173 1141 -1 + 6914 1171 1157 1173 -1 + 6915 972 1008 1012 -1 + 6916 3211 3214 4316 -1 + 6917 3211 4070 3214 -1 + 6918 4568 1181 1142 -1 + 6919 1142 1181 1773 -1 + 6920 1085 1094 1095 -1 + 6921 1085 1084 1094 -1 + 6922 974 966 1005 -1 + 6923 930 1008 1011 -1 + 6924 948 1011 1008 -1 + 6925 930 1011 924 -1 + 6926 924 1011 948 -1 + 6927 1086 1087 1100 -1 + 6928 1100 1087 974 -1 + 6929 1006 971 1007 -1 + 6930 1007 949 1006 -1 + 6931 1006 1005 971 -1 + 6932 1006 949 923 -1 + 6933 1006 923 948 -1 + 6934 924 948 923 -1 + 6935 947 926 941 -1 + 6936 983 3048 982 -1 + 6937 976 975 947 -1 + 6938 924 946 953 -1 + 6939 976 947 961 -1 + 6940 1752 1754 4014 -1 + 6941 983 1262 3143 -1 + 6942 1619 1071 4419 -1 + 6943 4419 1606 4361 -1 + 6944 4097 4002 3338 -1 + 6945 4247 4228 2872 -1 + 6946 2872 4228 3940 -1 + 6947 3690 3405 3686 -1 + 6948 979 990 1261 -1 + 6949 1261 983 979 -1 + 6950 1261 994 996 -1 + 6951 996 994 995 -1 + 6952 928 1010 931 -1 + 6953 1080 1646 1077 -1 + 6954 3214 324 4316 -1 + 6955 3143 3048 983 -1 + 6956 1083 1313 1130 -1 + 6957 1124 1313 1083 -1 + 6958 3264 3868 4713 -1 + 6959 3599 3598 4137 -1 + 6960 951 956 1024 -1 + 6961 951 1024 938 -1 + 6962 979 983 982 -1 + 6963 4416 3454 4373 -1 + 6964 1026 929 1013 -1 + 6965 1097 4196 1096 -1 + 6966 1013 924 938 -1 + 6967 1016 973 933 -1 + 6968 934 1092 1091 -1 + 6969 934 935 1092 -1 + 6970 924 939 940 -1 + 6971 1016 929 1026 -1 + 6972 1262 1261 996 -1 + 6973 1132 4013 1018 -1 + 6974 1132 1131 4207 -1 + 6975 1132 1019 1131 -1 + 6976 1089 933 1088 -1 + 6977 1089 1125 933 -1 + 6978 1740 1400 1113 -1 + 6979 983 1261 1262 -1 + 6980 1089 1088 1108 -1 + 6981 1108 1088 1091 -1 + 6982 938 1024 1014 -1 + 6983 936 1014 1024 -1 + 6984 938 1014 1013 -1 + 6985 1214 1320 4100 -1 + 6986 933 1125 1009 -1 + 6987 1009 1125 974 -1 + 6988 928 931 924 -1 + 6989 924 931 930 -1 + 6990 4673 578 4065 -1 + 6991 940 956 951 -1 + 6992 4678 4616 1603 -1 + 6993 4228 4247 4691 -1 + 6994 4027 1268 4655 -1 + 6995 4342 1083 1130 -1 + 6996 4678 1603 1604 -1 + 6997 739 737 4290 -1 + 6998 1702 1258 1259 -1 + 6999 1271 1258 1702 -1 + 7000 3335 3323 4344 -1 + 7001 4678 1606 4616 -1 + 7002 1681 1680 1678 -1 + 7003 4216 204 430 -1 + 7004 3554 3566 3175 -1 + 7005 1258 1260 1719 -1 + 7006 3168 1273 3126 -1 + 7007 3069 2798 3171 -1 + 7008 1082 1124 1083 -1 + 7009 1649 1650 4697 -1 + 7010 4097 2828 2853 -1 + 7011 4563 4496 1557 -1 + 7012 4662 4554 1689 -1 + 7013 4314 1645 4055 -1 + 7014 1917 1189 1191 -1 + 7015 1917 1205 1189 -1 + 7016 4528 3056 4044 -1 + 7017 3134 4044 3056 -1 + 7018 1247 1682 1687 -1 + 7019 1678 1247 1687 -1 + 7020 1678 1680 1247 -1 + 7021 1071 1550 4454 -1 + 7022 1071 1564 1550 -1 + 7023 1583 1598 4058 -1 + 7024 1257 1323 1272 -1 + 7025 1257 1256 1323 -1 + 7026 1257 1272 1275 -1 + 7027 56 816 815 -1 + 7028 3988 2815 4550 -1 + 7029 3988 4219 2815 -1 + 7030 1224 1225 741 -1 + 7031 3168 3126 1256 -1 + 7032 4682 3863 2855 -1 + 7033 3168 1256 1257 -1 + 7034 2352 764 1201 -1 + 7035 774 1201 764 -1 + 7036 3548 3549 4094 -1 + 7037 3548 3540 3549 -1 + 7038 786 819 817 -1 + 7039 1224 1227 1195 -1 + 7040 2351 1195 1227 -1 + 7041 1228 1220 1193 -1 + 7042 1312 1275 1232 -1 + 7043 1232 1321 1311 -1 + 7044 1311 1312 1232 -1 + 7045 787 611 347 -1 + 7046 347 786 787 -1 + 7047 1503 2652 4346 -1 + 7048 2984 3936 4651 -1 + 7049 2349 1626 1625 -1 + 7050 1612 1556 1615 -1 + 7051 1615 1613 1612 -1 + 7052 1579 1218 1563 -1 + 7053 1601 1320 1594 -1 + 7054 4599 4254 1255 -1 + 7055 1255 987 4599 -1 + 7056 1547 1317 4209 -1 + 7057 1547 4199 1317 -1 + 7058 1082 1125 1089 -1 + 7059 2577 1193 1220 -1 + 7060 1070 1616 1072 -1 + 7061 1069 1616 1070 -1 + 7062 1069 1070 2580 -1 + 7063 4277 1676 4108 -1 + 7064 1668 1676 4277 -1 + 7065 1320 1601 4100 -1 + 7066 3161 3284 1137 -1 + 7067 4122 984 1002 -1 + 7068 1002 984 1003 -1 + 7069 4085 1020 1023 -1 + 7070 3549 3907 4094 -1 + 7071 3549 3904 3907 -1 + 7072 3933 3287 4022 -1 + 7073 348 346 347 -1 + 7074 4718 1314 4052 -1 + 7075 1111 1127 1112 -1 + 7076 4355 1914 149 -1 + 7077 1920 1914 4355 -1 + 7078 4240 1211 4169 -1 + 7079 1194 2578 1195 -1 + 7080 1195 2578 1224 -1 + 7081 4575 3089 3112 -1 + 7082 1598 1600 1591 -1 + 7083 1558 1215 1555 -1 + 7084 1194 1195 1193 -1 + 7085 1193 2577 1194 -1 + 7086 1656 4024 4059 -1 + 7087 1602 1591 1601 -1 + 7088 4456 2838 4431 -1 + 7089 1594 1592 1601 -1 + 7090 1592 1602 1601 -1 + 7091 1652 1649 1648 -1 + 7092 4170 2501 2479 -1 + 7093 2502 2501 4170 -1 + 7094 1597 1053 1213 -1 + 7095 4369 4156 3502 -1 + 7096 1137 3284 1136 -1 + 7097 1137 3283 3161 -1 + 7098 1137 1136 1138 -1 + 7099 1633 2661 1662 -1 + 7100 1652 2661 1633 -1 + 7101 4133 4063 1215 -1 + 7102 4156 4194 3418 -1 + 7103 1035 1036 2030 -1 + 7104 1596 1624 1599 -1 + 7105 3559 2133 2129 -1 + 7106 1052 1053 2030 -1 + 7107 1054 2030 1053 -1 + 7108 1052 1213 1053 -1 + 7109 1047 1049 1048 -1 + 7110 3116 1273 3284 -1 + 7111 1273 1233 3284 -1 + 7112 346 339 347 -1 + 7113 4232 1401 4146 -1 + 7114 796 794 795 -1 + 7115 4723 3504 3500 -1 + 7116 4161 4088 1559 -1 + 7117 4759 3958 4685 -1 + 7118 3342 2884 4038 -1 + 7119 4422 1749 1750 -1 + 7120 1751 1750 1749 -1 + 7121 743 347 740 -1 + 7122 786 347 743 -1 + 7123 787 786 817 -1 + 7124 1224 2578 1225 -1 + 7125 602 595 596 -1 + 7126 596 2570 602 -1 + 7127 602 2582 603 -1 + 7128 602 2583 2582 -1 + 7129 743 748 742 -1 + 7130 742 748 741 -1 + 7131 1225 2578 2281 -1 + 7132 2735 2802 2737 -1 + 7133 3899 408 4054 -1 + 7134 746 744 749 -1 + 7135 746 736 744 -1 + 7136 1130 999 4342 -1 + 7137 4230 3061 4474 -1 + 7138 1067 1614 1068 -1 + 7139 1037 1583 1595 -1 + 7140 1049 1037 1599 -1 + 7141 1049 1047 1037 -1 + 7142 1627 1625 1557 -1 + 7143 1628 1557 1625 -1 + 7144 2358 2367 2356 -1 + 7145 1049 2344 1051 -1 + 7146 2019 2015 2016 -1 + 7147 749 2354 748 -1 + 7148 748 746 749 -1 + 7149 4672 582 578 -1 + 7150 2364 2363 1202 -1 + 7151 2346 2363 2364 -1 + 7152 2353 2354 749 -1 + 7153 1202 2354 2353 -1 + 7154 602 603 595 -1 + 7155 599 595 603 -1 + 7156 2355 2329 2347 -1 + 7157 2347 2329 2357 -1 + 7158 2347 1626 2349 -1 + 7159 2349 2346 2347 -1 + 7160 609 787 817 -1 + 7161 817 2281 609 -1 + 7162 4162 2697 2703 -1 + 7163 2347 2348 1626 -1 + 7164 4567 4484 1207 -1 + 7165 2699 2697 4162 -1 + 7166 2699 2698 2697 -1 + 7167 4169 1211 1213 -1 + 7168 2586 2591 2537 -1 + 7169 2566 2537 2591 -1 + 7170 4358 4125 3636 -1 + 7171 4358 3636 3637 -1 + 7172 3637 3636 3663 -1 + 7173 1625 1627 2349 -1 + 7174 4406 1408 1417 -1 + 7175 1770 1417 1408 -1 + 7176 2361 2364 2365 -1 + 7177 2361 2355 2364 -1 + 7178 2357 2358 2356 -1 + 7179 2365 2362 2361 -1 + 7180 2365 770 2362 -1 + 7181 4271 4026 1148 -1 + 7182 4725 4712 963 -1 + 7183 609 594 787 -1 + 7184 611 787 594 -1 + 7185 738 774 745 -1 + 7186 1083 4214 1082 -1 + 7187 2345 1622 1623 -1 + 7188 4291 4202 3715 -1 + 7189 3715 4202 3089 -1 + 7190 1624 1622 2345 -1 + 7191 1623 1622 2348 -1 + 7192 1650 1651 1212 -1 + 7193 752 739 814 -1 + 7194 814 739 740 -1 + 7195 1591 1600 1601 -1 + 7196 4101 3703 3701 -1 + 7197 3701 3703 3700 -1 + 7198 1650 1212 1214 -1 + 7199 4101 3093 3703 -1 + 7200 4101 3701 3705 -1 + 7201 1648 1036 2661 -1 + 7202 3336 3335 4344 -1 + 7203 4382 3679 4310 -1 + 7204 4170 2490 2502 -1 + 7205 4170 2479 2490 -1 + 7206 4396 4351 1244 -1 + 7207 1244 1691 4396 -1 + 7208 1049 1051 1048 -1 + 7209 774 775 1201 -1 + 7210 774 738 775 -1 + 7211 2352 1051 2368 -1 + 7212 2344 2368 1051 -1 + 7213 2031 2030 2016 -1 + 7214 1054 2016 2030 -1 + 7215 202 200 201 -1 + 7216 3112 3536 4382 -1 + 7217 2038 2044 2018 -1 + 7218 2018 2044 1517 -1 + 7219 2549 2548 2581 -1 + 7220 2549 2585 2548 -1 + 7221 2840 2971 4139 -1 + 7222 2014 2013 2015 -1 + 7223 2013 2016 2015 -1 + 7224 2019 2021 2015 -1 + 7225 2019 2020 2021 -1 + 7226 4088 1557 1563 -1 + 7227 1563 1557 1593 -1 + 7228 826 345 348 -1 + 7229 2028 2020 2019 -1 + 7230 2028 2029 2020 -1 + 7231 4440 3380 4031 -1 + 7232 1055 2016 1054 -1 + 7233 3081 3371 4440 -1 + 7234 1072 1059 1060 -1 + 7235 1167 1295 1163 -1 + 7236 2537 2566 2540 -1 + 7237 2540 2566 2589 -1 + 7238 54 23 30 -1 + 7239 30 23 29 -1 + 7240 2586 2536 2584 -1 + 7241 2537 2536 2586 -1 + 7242 2590 606 697 -1 + 7243 709 697 606 -1 + 7244 2585 2572 2584 -1 + 7245 2585 2549 2572 -1 + 7246 1229 1603 1230 -1 + 7247 1230 2579 1229 -1 + 7248 2692 2690 2695 -1 + 7249 4622 4317 3273 -1 + 7250 1897 1901 1900 -1 + 7251 1970 1900 1901 -1 + 7252 1038 1039 1046 -1 + 7253 3610 3626 2713 -1 + 7254 2723 3610 2713 -1 + 7255 2137 2136 1038 -1 + 7256 1039 1038 2136 -1 + 7257 488 466 24 -1 + 7258 488 487 466 -1 + 7259 610 606 2568 -1 + 7260 610 605 606 -1 + 7261 1922 164 1905 -1 + 7262 1905 164 225 -1 + 7263 2576 2571 2575 -1 + 7264 1914 1928 1926 -1 + 7265 1926 212 1914 -1 + 7266 1924 1932 1912 -1 + 7267 1223 1226 1629 -1 + 7268 1919 1929 1920 -1 + 7269 1930 1929 1919 -1 + 7270 816 34 33 -1 + 7271 1912 1934 1076 -1 + 7272 1894 1891 1893 -1 + 7273 1889 1891 1894 -1 + 7274 2587 2588 2591 -1 + 7275 820 1198 822 -1 + 7276 822 184 820 -1 + 7277 820 821 824 -1 + 7278 214 212 213 -1 + 7279 1926 213 212 -1 + 7280 1044 1040 1223 -1 + 7281 816 33 793 -1 + 7282 466 793 33 -1 + 7283 1197 1187 1198 -1 + 7284 1920 1929 1928 -1 + 7285 1928 1914 1920 -1 + 7286 821 184 825 -1 + 7287 2569 2567 2568 -1 + 7288 610 2568 2567 -1 + 7289 4356 4262 3471 -1 + 7290 4356 3471 4293 -1 + 7291 4527 4611 4282 -1 + 7292 184 821 820 -1 + 7293 3287 4322 4625 -1 + 7294 4626 4315 4295 -1 + 7295 4295 4315 4292 -1 + 7296 149 1914 212 -1 + 7297 212 180 149 -1 + 7298 149 180 185 -1 + 7299 181 185 180 -1 + 7300 1644 1647 1645 -1 + 7301 172 178 808 -1 + 7302 808 178 171 -1 + 7303 4666 3272 2895 -1 + 7304 1155 1253 4023 -1 + 7305 1155 1664 1253 -1 + 7306 1634 1637 1636 -1 + 7307 4209 1548 1547 -1 + 7308 4328 3315 4210 -1 + 7309 4106 4210 3315 -1 + 7310 1221 1205 1924 -1 + 7311 345 728 348 -1 + 7312 346 348 728 -1 + 7313 1235 1250 1248 -1 + 7314 1251 1235 1248 -1 + 7315 3597 3603 3599 -1 + 7316 3007 3151 4495 -1 + 7317 348 611 826 -1 + 7318 1734 1771 4670 -1 + 7319 4474 3061 3057 -1 + 7320 4667 1665 1244 -1 + 7321 3321 3342 4038 -1 + 7322 1238 1675 1239 -1 + 7323 1140 1249 4024 -1 + 7324 4717 4686 1159 -1 + 7325 4653 4632 3222 -1 + 7326 1190 2130 813 -1 + 7327 34 813 2130 -1 + 7328 2124 1190 1188 -1 + 7329 2124 2134 1190 -1 + 7330 2130 1190 2134 -1 + 7331 2102 2118 2101 -1 + 7332 2101 2118 169 -1 + 7333 1223 1040 2136 -1 + 7334 1223 2137 2135 -1 + 7335 1223 2136 2137 -1 + 7336 1223 1222 1226 -1 + 7337 2135 1222 1223 -1 + 7338 1925 1917 1915 -1 + 7339 1913 1915 1917 -1 + 7340 4736 4215 4734 -1 + 7341 1725 1736 4515 -1 + 7342 4745 1724 1729 -1 + 7343 1730 1729 1724 -1 + 7344 1113 1112 1176 -1 + 7345 3173 3082 4282 -1 + 7346 1238 1252 4023 -1 + 7347 1238 1239 1252 -1 + 7348 4559 3056 4472 -1 + 7349 1654 1655 1661 -1 + 7350 4346 1518 1502 -1 + 7351 3931 2872 3940 -1 + 7352 4083 1041 1042 -1 + 7353 1647 1153 1645 -1 + 7354 3286 3932 4357 -1 + 7355 4255 1638 1139 -1 + 7356 4490 4094 3907 -1 + 7357 1569 1571 1570 -1 + 7358 4523 1239 1675 -1 + 7359 4523 1236 1239 -1 + 7360 4053 1062 1066 -1 + 7361 1066 1062 1059 -1 + 7362 4267 4150 3459 -1 + 7363 4267 3454 4025 -1 + 7364 3870 3108 4426 -1 + 7365 3870 4176 3108 -1 + 7366 1085 1086 1135 -1 + 7367 1197 1183 1187 -1 + 7368 1186 1187 1183 -1 + 7369 4521 4280 1842 -1 + 7370 3067 3065 3075 -1 + 7371 1546 1547 1553 -1 + 7372 4744 4736 4259 -1 + 7373 4041 3029 4445 -1 + 7374 1611 1605 1080 -1 + 7375 4746 4428 4732 -1 + 7376 4718 4099 1314 -1 + 7377 1930 1077 1078 -1 + 7378 1041 1630 1043 -1 + 7379 4221 3341 2970 -1 + 7380 3339 2970 3341 -1 + 7381 3965 3966 4455 -1 + 7382 1761 1716 1721 -1 + 7383 4254 4252 1255 -1 + 7384 2580 1061 2576 -1 + 7385 4413 3895 4344 -1 + 7386 4413 4344 3323 -1 + 7387 4364 1732 1734 -1 + 7388 1245 1691 1244 -1 + 7389 4323 1718 1281 -1 + 7390 1183 1196 1185 -1 + 7391 1183 1197 1196 -1 + 7392 1196 1197 2280 -1 + 7393 611 348 347 -1 + 7394 4183 4045 3321 -1 + 7395 3321 4045 3315 -1 + 7396 4531 1399 1391 -1 + 7397 4771 4751 4177 -1 + 7398 4084 1617 1554 -1 + 7399 4720 4699 3852 -1 + 7400 4688 4229 4468 -1 + 7401 4431 4221 2970 -1 + 7402 1636 1646 1640 -1 + 7403 1640 1634 1636 -1 + 7404 1739 4766 4765 -1 + 7405 1565 1567 1566 -1 + 7406 3486 3937 4506 -1 + 7407 4684 1638 4553 -1 + 7408 4639 4615 3388 -1 + 7409 4486 2988 3330 -1 + 7410 4745 1735 1724 -1 + 7411 3069 4257 2763 -1 + 7412 2763 4042 3069 -1 + 7413 1574 1578 1572 -1 + 7414 1574 1150 1578 -1 + 7415 1127 1129 1179 -1 + 7416 4688 1115 1109 -1 + 7417 1110 1109 1115 -1 + 7418 1571 1569 1566 -1 + 7419 1571 1566 1567 -1 + 7420 1570 1571 1643 -1 + 7421 3113 3107 4305 -1 + 7422 3113 4218 3107 -1 + 7423 4286 2998 3074 -1 + 7424 4093 1617 4084 -1 + 7425 1140 1153 1248 -1 + 7426 2345 2344 1049 -1 + 7427 4648 3212 4594 -1 + 7428 4201 4117 3916 -1 + 7429 4420 3939 2994 -1 + 7430 3332 2994 3939 -1 + 7431 1552 1548 1549 -1 + 7432 1575 1574 1550 -1 + 7433 4537 4231 1559 -1 + 7434 3011 3065 3067 -1 + 7435 1680 1681 1671 -1 + 7436 4681 1611 4628 -1 + 7437 4681 1918 1611 -1 + 7438 4681 1919 4603 -1 + 7439 1077 1081 1080 -1 + 7440 1154 1152 1236 -1 + 7441 1236 1152 1239 -1 + 7442 4663 597 612 -1 + 7443 3085 3016 3002 -1 + 7444 3085 1160 3016 -1 + 7445 1635 1634 1640 -1 + 7446 1640 1641 1635 -1 + 7447 1656 1638 4024 -1 + 7448 4468 4229 1114 -1 + 7449 1140 1248 1250 -1 + 7450 1250 1249 1140 -1 + 7451 792 796 795 -1 + 7452 3125 3124 3122 -1 + 7453 3288 3124 3125 -1 + 7454 16 4104 2499 -1 + 7455 1231 1322 4160 -1 + 7456 1231 1318 1322 -1 + 7457 4394 4215 1744 -1 + 7458 2679 3078 3077 -1 + 7459 4238 2038 2018 -1 + 7460 1065 2587 2586 -1 + 7461 2591 2586 2587 -1 + 7462 1299 1297 1166 -1 + 7463 1166 1297 1164 -1 + 7464 753 85 4359 -1 + 7465 753 4132 85 -1 + 7466 1962 1899 1902 -1 + 7467 1962 1898 1899 -1 + 7468 4609 4336 1004 -1 + 7469 1004 4336 986 -1 + 7470 1065 1063 1062 -1 + 7471 1065 1062 2587 -1 + 7472 2591 2588 2592 -1 + 7473 2573 1060 1063 -1 + 7474 1147 1254 1175 -1 + 7475 1147 1146 1254 -1 + 7476 1739 1747 4770 -1 + 7477 1739 4765 1747 -1 + 7478 4746 824 4624 -1 + 7479 823 4624 824 -1 + 7480 1770 1410 1769 -1 + 7481 3148 3030 3145 -1 + 7482 4665 4218 4614 -1 + 7483 1292 1309 1286 -1 + 7484 4751 4749 4268 -1 + 7485 4268 4177 4751 -1 + 7486 1156 1158 3012 -1 + 7487 1554 1545 4084 -1 + 7488 1554 4021 1545 -1 + 7489 39 48 4212 -1 + 7490 39 173 48 -1 + 7491 4056 1320 1214 -1 + 7492 4084 1545 1618 -1 + 7493 1618 1545 1546 -1 + 7494 1404 4442 1405 -1 + 7495 4607 4312 4543 -1 + 7496 3386 4543 4312 -1 + 7497 3605 3622 3594 -1 + 7498 3428 3622 3605 -1 + 7499 4602 1394 4589 -1 + 7500 4648 4595 3212 -1 + 7501 4296 51 4076 -1 + 7502 1267 3085 3043 -1 + 7503 1178 3085 1267 -1 + 7504 4475 2996 3151 -1 + 7505 1673 1677 1676 -1 + 7506 1129 1127 1402 -1 + 7507 1403 1133 1402 -1 + 7508 1162 4519 4203 -1 + 7509 4466 4415 3518 -1 + 7510 4109 2500 1847 -1 + 7511 1855 2500 4109 -1 + 7512 1748 1134 4146 -1 + 7513 1748 1746 1134 -1 + 7514 974 1087 4532 -1 + 7515 4096 992 1331 -1 + 7516 1001 1331 992 -1 + 7517 1667 1676 1668 -1 + 7518 4224 751 750 -1 + 7519 750 753 4224 -1 + 7520 1150 1551 4285 -1 + 7521 1402 1111 1403 -1 + 7522 1402 1127 1111 -1 + 7523 1673 1666 1674 -1 + 7524 1672 1674 1666 -1 + 7525 1674 1672 1240 -1 + 7526 1674 1675 1673 -1 + 7527 2279 51 4015 -1 + 7528 4257 3069 4019 -1 + 7529 3580 4019 3069 -1 + 7530 1550 1574 4454 -1 + 7531 1673 1238 1677 -1 + 7532 1675 1238 1673 -1 + 7533 1159 1112 1127 -1 + 7534 3226 2799 4653 -1 + 7535 4352 3174 1144 -1 + 7536 3041 3174 4352 -1 + 7537 1666 1667 1669 -1 + 7538 1711 1706 1705 -1 + 7539 1761 1720 1716 -1 + 7540 4588 2782 2784 -1 + 7541 1752 1722 1757 -1 + 7542 4502 2943 2702 -1 + 7543 1753 1580 1755 -1 + 7544 1753 1584 1580 -1 + 7545 3062 3049 3054 -1 + 7546 3055 3059 4205 -1 + 7547 4431 2838 4321 -1 + 7548 4472 3056 3138 -1 + 7549 1688 1683 1679 -1 + 7550 1679 1683 1678 -1 + 7551 1686 1679 1687 -1 + 7552 1686 1684 1679 -1 + 7553 1694 1693 1690 -1 + 7554 1693 1683 1690 -1 + 7555 1683 1713 1715 -1 + 7556 3125 3123 4696 -1 + 7557 1713 1688 1714 -1 + 7558 4696 4360 3125 -1 + 7559 1689 1684 1685 -1 + 7560 4415 4383 3518 -1 + 7561 3049 3068 4387 -1 + 7562 4542 4465 998 -1 + 7563 4671 1400 4531 -1 + 7564 4109 1847 1852 -1 + 7565 1852 1855 4109 -1 + 7566 303 304 4643 -1 + 7567 2799 2763 4653 -1 + 7568 2799 4042 2763 -1 + 7569 3595 4236 4217 -1 + 7570 1587 1586 1743 -1 + 7571 1743 1744 1587 -1 + 7572 1129 1402 1133 -1 + 7573 4350 3221 315 -1 + 7574 4503 1021 4379 -1 + 7575 1398 1128 1748 -1 + 7576 1748 1128 1745 -1 + 7577 2783 2679 3077 -1 + 7578 1405 1418 1394 -1 + 7579 1405 1416 1418 -1 + 7580 1699 1708 981 -1 + 7581 2803 3530 3452 -1 + 7582 1766 1770 1765 -1 + 7583 4176 3266 3268 -1 + 7584 3268 3266 3831 -1 + 7585 4661 4390 1630 -1 + 7586 2168 2176 2169 -1 + 7587 1727 1712 1731 -1 + 7588 1723 1712 1727 -1 + 7589 1727 1768 1406 -1 + 7590 1406 1726 1727 -1 + 7591 1727 1731 1768 -1 + 7592 1726 1723 1727 -1 + 7593 4407 1255 980 -1 + 7594 4469 4589 1393 -1 + 7595 3886 3931 4438 -1 + 7596 1318 1314 4099 -1 + 7597 3103 3127 4123 -1 + 7598 4703 2725 2724 -1 + 7599 4429 4046 1132 -1 + 7600 1132 4046 4013 -1 + 7601 4774 4758 4017 -1 + 7602 4017 4758 4297 -1 + 7603 4473 3895 2695 -1 + 7604 1701 1707 1703 -1 + 7605 1701 1703 1697 -1 + 7606 1706 1707 1701 -1 + 7607 3555 3584 3554 -1 + 7608 1103 964 1099 -1 + 7609 4696 4447 4354 -1 + 7610 4754 2856 4753 -1 + 7611 3560 407 4018 -1 + 7612 3068 3055 4186 -1 + 7613 1408 1412 1410 -1 + 7614 1724 1412 1730 -1 + 7615 4171 1589 1582 -1 + 7616 1395 1393 1394 -1 + 7617 1726 1728 1724 -1 + 7618 1728 1769 1410 -1 + 7619 4158 4150 3452 -1 + 7620 3452 3530 4158 -1 + 7621 4400 4355 149 -1 + 7622 1735 1726 1724 -1 + 7623 987 1004 4444 -1 + 7624 987 4275 1004 -1 + 7625 1395 1414 1133 -1 + 7626 1405 1417 1416 -1 + 7627 4521 1842 4192 -1 + 7628 300 2815 4219 -1 + 7629 300 4077 2815 -1 + 7630 4273 3269 3303 -1 + 7631 3270 3303 3269 -1 + 7632 4182 1616 1618 -1 + 7633 1618 1616 1620 -1 + 7634 1746 1729 1134 -1 + 7635 1407 1134 1729 -1 + 7636 4648 2819 4595 -1 + 7637 2268 4065 1208 -1 + 7638 3330 3939 4119 -1 + 7639 4041 3002 3016 -1 + 7640 4191 4145 1590 -1 + 7641 4424 4480 2588 -1 + 7642 4446 1671 1681 -1 + 7643 1712 1708 1698 -1 + 7644 2283 4296 4076 -1 + 7645 4760 4284 1027 -1 + 7646 1027 4284 4270 -1 + 7647 1276 1117 1177 -1 + 7648 4155 1712 4048 -1 + 7649 4128 3640 3645 -1 + 7650 4584 3053 3051 -1 + 7651 1771 4621 4670 -1 + 7652 1771 4642 4621 -1 + 7653 4576 2918 2920 -1 + 7654 4378 3106 3107 -1 + 7655 3108 3268 4378 -1 + 7656 3108 4176 3268 -1 + 7657 4758 1029 1264 -1 + 7658 4758 1030 1029 -1 + 7659 3985 3995 4761 -1 + 7660 4488 1648 4240 -1 + 7661 1732 1771 1734 -1 + 7662 1028 1105 1122 -1 + 7663 1028 1107 1105 -1 + 7664 1698 1731 1712 -1 + 7665 1102 1107 1028 -1 + 7666 1102 1106 1107 -1 + 7667 222 229 4634 -1 + 7668 1274 1130 1313 -1 + 7669 1107 1106 976 -1 + 7670 2810 2762 4330 -1 + 7671 1027 1028 1030 -1 + 7672 1027 1025 1028 -1 + 7673 1158 1175 1174 -1 + 7674 964 1098 1099 -1 + 7675 1099 965 1103 -1 + 7676 1102 1025 1103 -1 + 7677 1102 1028 1025 -1 + 7678 965 1102 1103 -1 + 7679 1557 1067 1627 -1 + 7680 1023 1017 4340 -1 + 7681 1505 3561 4018 -1 + 7682 1398 1748 4159 -1 + 7683 982 3173 980 -1 + 7684 1182 1165 4288 -1 + 7685 1182 1172 1165 -1 + 7686 2803 3597 4137 -1 + 7687 3409 3410 4036 -1 + 7688 1145 3174 3044 -1 + 7689 4476 4200 4174 -1 + 7690 4115 1272 998 -1 + 7691 1001 998 1272 -1 + 7692 999 1274 4115 -1 + 7693 999 1130 1274 -1 + 7694 1145 3044 1174 -1 + 7695 1174 1175 1145 -1 + 7696 4034 2356 1623 -1 + 7697 1623 2356 2345 -1 + 7698 4006 3023 3003 -1 + 7699 2996 3003 3023 -1 + 7700 4471 2805 4217 -1 + 7701 407 2133 3559 -1 + 7702 1147 1157 1171 -1 + 7703 1147 1158 1157 -1 + 7704 644 608 622 -1 + 7705 1172 1298 1166 -1 + 7706 1173 1298 1172 -1 + 7707 1173 1302 1298 -1 + 7708 1173 1157 1302 -1 + 7709 4370 3042 4237 -1 + 7710 3057 3042 4370 -1 + 7711 3193 3192 3195 -1 + 7712 3123 3965 4447 -1 + 7713 2601 2607 2611 -1 + 7714 2601 2608 2607 -1 + 7715 1299 1166 1298 -1 + 7716 4510 4166 1722 -1 + 7717 4223 1119 1738 -1 + 7718 1296 3012 3031 -1 + 7719 1023 4340 988 -1 + 7720 1291 3145 1303 -1 + 7721 1289 1298 1302 -1 + 7722 2154 2152 2156 -1 + 7723 2154 2197 2152 -1 + 7724 1166 1164 1165 -1 + 7725 1168 1165 1164 -1 + 7726 4370 3040 4272 -1 + 7727 4237 3040 4370 -1 + 7728 3503 3418 3440 -1 + 7729 4599 4444 986 -1 + 7730 986 4265 4599 -1 + 7731 4302 1644 1608 -1 + 7732 4194 3414 3418 -1 + 7733 3440 3418 3414 -1 + 7734 4569 1620 1069 -1 + 7735 4570 1606 1572 -1 + 7736 4302 1636 1644 -1 + 7737 2181 1458 1446 -1 + 7738 1449 1446 1458 -1 + 7739 1882 227 226 -1 + 7740 1882 1866 227 -1 + 7741 1470 2180 2181 -1 + 7742 2122 2121 1489 -1 + 7743 2122 2120 2121 -1 + 7744 1286 1289 1296 -1 + 7745 1286 1296 1287 -1 + 7746 1324 3145 1291 -1 + 7747 116 117 105 -1 + 7748 105 117 2199 -1 + 7749 3167 1324 1333 -1 + 7750 1297 1288 1301 -1 + 7751 1288 1310 1309 -1 + 7752 3167 3165 3145 -1 + 7753 3146 1290 3032 -1 + 7754 3032 1290 1292 -1 + 7755 1301 1326 1285 -1 + 7756 1539 1538 1540 -1 + 7757 1539 1537 1538 -1 + 7758 1532 1538 1537 -1 + 7759 3146 3032 3147 -1 + 7760 1292 1287 3032 -1 + 7761 1294 1295 1325 -1 + 7762 1327 1328 1326 -1 + 7763 1327 1329 1328 -1 + 7764 1299 1310 1288 -1 + 7765 1309 1310 1286 -1 + 7766 1291 1294 1325 -1 + 7767 1289 1310 1299 -1 + 7768 2630 2619 2622 -1 + 7769 2630 2642 2619 -1 + 7770 1307 1284 1282 -1 + 7771 1283 1284 1285 -1 + 7772 1282 1284 1283 -1 + 7773 1282 1293 1307 -1 + 7774 1307 1293 1295 -1 + 7775 2609 2634 2595 -1 + 7776 2595 2634 2637 -1 + 7777 2609 2601 2628 -1 + 7778 2628 2601 2631 -1 + 7779 2612 2614 2452 -1 + 7780 2452 2534 2612 -1 + 7781 88 188 189 -1 + 7782 189 188 90 -1 + 7783 188 228 217 -1 + 7784 190 228 188 -1 + 7785 2138 2149 2147 -1 + 7786 2138 2140 2149 -1 + 7787 1922 1921 164 -1 + 7788 1896 1921 1922 -1 + 7789 198 199 221 -1 + 7790 221 1887 198 -1 + 7791 198 1887 1906 -1 + 7792 1906 1887 1968 -1 + 7793 67 66 64 -1 + 7794 67 1987 66 -1 + 7795 64 87 100 -1 + 7796 100 87 93 -1 + 7797 4213 432 205 -1 + 7798 1969 1906 1968 -1 + 7799 1968 1971 1969 -1 + 7800 1969 1967 1906 -1 + 7801 1906 1967 92 -1 + 7802 1978 1982 1981 -1 + 7803 1985 1982 1978 -1 + 7804 1984 1985 1978 -1 + 7805 1984 1983 1985 -1 + 7806 1938 1942 1943 -1 + 7807 1938 1959 1942 -1 + 7808 1975 1944 1976 -1 + 7809 1976 1944 79 -1 + 7810 1976 1967 1966 -1 + 7811 1965 1967 1976 -1 + 7812 1959 1939 1935 -1 + 7813 1938 1939 1959 -1 + 7814 1959 1978 1981 -1 + 7815 1981 1942 1959 -1 + 7816 150 151 152 -1 + 7817 152 151 2113 -1 + 7818 2122 2114 2120 -1 + 7819 1959 1935 1978 -1 + 7820 1978 1935 1984 -1 + 7821 70 136 139 -1 + 7822 139 81 70 -1 + 7823 1979 1945 1977 -1 + 7824 1975 1977 1945 -1 + 7825 2430 2449 2473 -1 + 7826 2473 2449 2467 -1 + 7827 87 96 189 -1 + 7828 189 94 87 -1 + 7829 79 77 74 -1 + 7830 68 74 77 -1 + 7831 1812 1813 1808 -1 + 7832 1808 1813 1807 -1 + 7833 1884 1954 1885 -1 + 7834 1884 1801 1954 -1 + 7835 1884 1885 1805 -1 + 7836 1805 1885 1807 -1 + 7837 1884 1804 1802 -1 + 7838 1884 1805 1804 -1 + 7839 112 110 119 -1 + 7840 112 113 110 -1 + 7841 133 1936 125 -1 + 7842 125 1936 1937 -1 + 7843 114 134 113 -1 + 7844 110 113 134 -1 + 7845 102 101 138 -1 + 7846 138 103 102 -1 + 7847 2456 2452 2460 -1 + 7848 2456 2472 2452 -1 + 7849 1937 1948 1809 -1 + 7850 1937 1936 1948 -1 + 7851 1977 1966 1974 -1 + 7852 1974 1983 1977 -1 + 7853 468 470 194 -1 + 7854 68 136 70 -1 + 7855 68 77 136 -1 + 7856 81 65 70 -1 + 7857 81 126 65 -1 + 7858 70 100 68 -1 + 7859 70 65 100 -1 + 7860 122 1949 1950 -1 + 7861 115 1949 122 -1 + 7862 122 1950 1811 -1 + 7863 1811 1950 1803 -1 + 7864 1975 1946 1944 -1 + 7865 1945 1946 1975 -1 + 7866 1977 1975 1966 -1 + 7867 1966 1975 1976 -1 + 7868 1984 1980 1979 -1 + 7869 1984 1935 1980 -1 + 7870 1979 1977 1983 -1 + 7871 1983 1984 1979 -1 + 7872 667 673 668 -1 + 7873 674 673 667 -1 + 7874 2625 714 715 -1 + 7875 730 715 714 -1 + 7876 2632 2635 2633 -1 + 7877 2633 2629 2632 -1 + 7878 2551 699 731 -1 + 7879 700 731 699 -1 + 7880 2544 2541 2553 -1 + 7881 2545 2541 2544 -1 + 7882 2564 2562 2624 -1 + 7883 2554 2624 2562 -1 + 7884 2552 2626 2640 -1 + 7885 735 2626 2552 -1 + 7886 2620 2632 2629 -1 + 7887 2629 2621 2620 -1 + 7888 2563 2564 2620 -1 + 7889 2620 2564 2624 -1 + 7890 2535 2539 2562 -1 + 7891 2562 2539 2553 -1 + 7892 2540 2553 2539 -1 + 7893 2540 2544 2553 -1 + 7894 209 182 183 -1 + 7895 209 210 182 -1 + 7896 224 179 210 -1 + 7897 224 1923 179 -1 + 7898 2141 2143 2140 -1 + 7899 2140 2143 2149 -1 + 7900 35 13 15 -1 + 7901 2105 15 13 -1 + 7902 32 462 454 -1 + 7903 454 462 460 -1 + 7904 32 31 462 -1 + 7905 14 32 453 -1 + 7906 35 14 453 -1 + 7907 35 15 14 -1 + 7908 1484 1482 1480 -1 + 7909 35 442 30 -1 + 7910 30 13 35 -1 + 7911 35 453 442 -1 + 7912 211 190 225 -1 + 7913 211 228 190 -1 + 7914 1903 1964 1963 -1 + 7915 1963 1972 1903 -1 + 7916 1922 1904 1902 -1 + 7917 1905 1904 1922 -1 + 7918 1904 1905 187 -1 + 7919 89 187 1905 -1 + 7920 1898 1897 1900 -1 + 7921 1898 1962 1897 -1 + 7922 1898 1889 1899 -1 + 7923 1891 1889 1898 -1 + 7924 164 224 225 -1 + 7925 211 225 224 -1 + 7926 2141 2142 2143 -1 + 7927 2140 2146 2141 -1 + 7928 164 1923 224 -1 + 7929 1921 1923 164 -1 + 7930 23 22 29 -1 + 7931 29 22 55 -1 + 7932 54 488 23 -1 + 7933 2147 28 55 -1 + 7934 55 28 29 -1 + 7935 2147 2148 28 -1 + 7936 2147 2149 2148 -1 + 7937 28 13 29 -1 + 7938 30 29 13 -1 + 7939 3589 3600 1480 -1 + 7940 2102 2119 2118 -1 + 7941 2114 2119 2102 -1 + 7942 174 148 175 -1 + 7943 175 48 174 -1 + 7944 2182 2190 2189 -1 + 7945 2189 2187 2182 -1 + 7946 2118 151 169 -1 + 7947 169 151 170 -1 + 7948 2116 2113 2185 -1 + 7949 2116 152 2113 -1 + 7950 960 1123 4142 -1 + 7951 1123 4043 1260 -1 + 7952 2111 2097 2120 -1 + 7953 1090 960 4142 -1 + 7954 1092 1090 4142 -1 + 7955 1123 1092 4142 -1 + 7956 1123 1260 1092 -1 + 7957 468 473 470 -1 + 7958 470 473 476 -1 + 7959 106 135 98 -1 + 7960 98 135 471 -1 + 7961 150 156 157 -1 + 7962 152 156 150 -1 + 7963 174 170 150 -1 + 7964 151 150 170 -1 + 7965 484 477 148 -1 + 7966 148 477 443 -1 + 7967 443 477 483 -1 + 7968 2161 2162 2193 -1 + 7969 2203 2193 2162 -1 + 7970 2180 2204 2183 -1 + 7971 2180 1470 2204 -1 + 7972 2518 2519 2477 -1 + 7973 2522 2477 2519 -1 + 7974 116 474 471 -1 + 7975 471 135 116 -1 + 7976 2204 1470 2191 -1 + 7977 2161 2192 2194 -1 + 7978 2161 2193 2192 -1 + 7979 2161 2160 2162 -1 + 7980 2161 2198 2160 -1 + 7981 471 472 98 -1 + 7982 2159 2198 2205 -1 + 7983 101 124 1936 -1 + 7984 1936 124 1948 -1 + 7985 1819 1812 1810 -1 + 7986 1819 1813 1812 -1 + 7987 2171 2167 141 -1 + 7988 141 2167 82 -1 + 7989 1815 1813 1819 -1 + 7990 1815 1806 1813 -1 + 7991 1806 1814 1825 -1 + 7992 1815 1814 1806 -1 + 7993 120 1820 1822 -1 + 7994 1816 1822 1820 -1 + 7995 113 112 115 -1 + 7996 115 122 113 -1 + 7997 112 103 137 -1 + 7998 137 115 112 -1 + 7999 102 123 101 -1 + 8000 124 101 123 -1 + 8001 1819 1810 1822 -1 + 8002 121 1822 1810 -1 + 8003 671 666 654 -1 + 8004 671 672 666 -1 + 8005 2171 2172 2167 -1 + 8006 2173 2172 2171 -1 + 8007 2156 2157 2154 -1 + 8008 2156 2174 2157 -1 + 8009 142 105 2151 -1 + 8010 104 105 142 -1 + 8011 1776 1798 1799 -1 + 8012 99 1798 1776 -1 + 8013 73 83 1789 -1 + 8014 1782 1789 83 -1 + 8015 75 76 403 -1 + 8016 1844 2170 2165 -1 + 8017 1844 2177 2170 -1 + 8018 2520 2488 2487 -1 + 8019 2520 2478 2488 -1 + 8020 2522 2475 2477 -1 + 8021 2522 2525 2475 -1 + 8022 2528 2525 2511 -1 + 8023 2511 2525 2522 -1 + 8024 2477 2478 2520 -1 + 8025 2475 2478 2477 -1 + 8026 2439 2450 2442 -1 + 8027 2439 2425 2450 -1 + 8028 2153 2156 2160 -1 + 8029 2152 2160 2156 -1 + 8030 142 2151 141 -1 + 8031 141 2151 2164 -1 + 8032 2153 2160 2198 -1 + 8033 2198 2159 2153 -1 + 8034 411 82 75 -1 + 8035 411 412 413 -1 + 8036 413 412 1542 -1 + 8037 411 75 412 -1 + 8038 411 104 142 -1 + 8039 103 138 139 -1 + 8040 139 137 103 -1 + 8041 101 133 138 -1 + 8042 101 1936 133 -1 + 8043 102 103 119 -1 + 8044 112 119 103 -1 + 8045 108 109 118 -1 + 8046 132 118 109 -1 + 8047 111 128 108 -1 + 8048 109 108 128 -1 + 8049 114 1821 134 -1 + 8050 114 1827 1821 -1 + 8051 2428 2450 2449 -1 + 8052 2426 2449 2450 -1 + 8053 2464 2466 2459 -1 + 8054 2459 2455 2464 -1 + 8055 2434 685 2436 -1 + 8056 2432 2436 685 -1 + 8057 676 2455 686 -1 + 8058 686 687 676 -1 + 8059 1787 1814 1793 -1 + 8060 1793 1814 1796 -1 + 8061 2440 2431 2434 -1 + 8062 2440 2445 2431 -1 + 8063 678 679 677 -1 + 8064 677 682 678 -1 + 8065 2436 679 2444 -1 + 8066 2444 2435 2436 -1 + 8067 694 2432 676 -1 + 8068 685 676 2432 -1 + 8069 1856 1788 1783 -1 + 8070 1790 1783 1788 -1 + 8071 1794 1793 1861 -1 + 8072 1860 1861 1793 -1 + 8073 2480 2278 2481 -1 + 8074 2481 2278 2494 -1 + 8075 2478 2276 2488 -1 + 8076 2478 2493 2276 -1 + 8077 2528 2524 2523 -1 + 8078 2523 2525 2528 -1 + 8079 2528 2444 2527 -1 + 8080 2511 2444 2528 -1 + 8081 4016 3958 2909 -1 + 8082 2480 2276 2278 -1 + 8083 2495 2278 2276 -1 + 8084 2494 2277 2495 -1 + 8085 2494 2278 2277 -1 + 8086 2277 2278 2495 -1 + 8087 17 18 16 -1 + 8088 17 9 18 -1 + 8089 53 2279 2282 -1 + 8090 51 2279 53 -1 + 8091 53 2282 2480 -1 + 8092 2479 51 53 -1 + 8093 1845 1846 2496 -1 + 8094 53 2480 58 -1 + 8095 4008 2734 2750 -1 + 8096 3183 2750 2734 -1 + 8097 53 61 2479 -1 + 8098 5 41 681 -1 + 8099 681 60 5 -1 + 8100 5 59 40 -1 + 8101 62 59 5 -1 + 8102 2518 2477 2520 -1 + 8103 2520 2521 2518 -1 + 8104 2442 2441 2443 -1 + 8105 2443 2439 2442 -1 + 8106 2428 2445 2442 -1 + 8107 2442 2450 2428 -1 + 8108 1243 4285 1246 -1 + 8109 1848 144 1881 -1 + 8110 1881 1839 1848 -1 + 8111 1854 1869 1840 -1 + 8112 1854 1839 1869 -1 + 8113 4011 3627 3592 -1 + 8114 4011 3625 3626 -1 + 8115 1831 1841 2497 -1 + 8116 2497 1841 1834 -1 + 8117 4353 4126 9 -1 + 8118 227 1798 195 -1 + 8119 227 1865 1798 -1 + 8120 17 147 8 -1 + 8121 146 147 17 -1 + 8122 1869 1868 1832 -1 + 8123 1869 1858 1868 -1 + 8124 144 143 1881 -1 + 8125 203 143 144 -1 + 8126 146 1851 1836 -1 + 8127 1836 1851 1850 -1 + 8128 1867 1870 1832 -1 + 8129 1832 1868 1867 -1 + 8130 1792 1791 1859 -1 + 8131 1795 1859 1791 -1 + 8132 8 203 144 -1 + 8133 147 203 8 -1 + 8134 1848 1852 1847 -1 + 8135 1849 1852 1848 -1 + 8136 17 1847 9 -1 + 8137 17 16 1851 -1 + 8138 8 1847 17 -1 + 8139 1697 4147 1700 -1 + 8140 17 1851 146 -1 + 8141 1792 1856 1779 -1 + 8142 1779 1856 1783 -1 + 8143 1804 1824 1828 -1 + 8144 1804 1825 1824 -1 + 8145 99 71 195 -1 + 8146 195 1798 99 -1 + 8147 1880 1838 1837 -1 + 8148 1880 1877 1838 -1 + 8149 73 196 71 -1 + 8150 1789 196 73 -1 + 8151 1860 1796 1800 -1 + 8152 1793 1796 1860 -1 + 8153 1799 1797 1777 -1 + 8154 1864 1777 1797 -1 + 8155 1866 1865 227 -1 + 8156 1876 1865 1866 -1 + 8157 226 145 1882 -1 + 8158 215 145 226 -1 + 8159 1872 1862 1875 -1 + 8160 1872 1878 1862 -1 + 8161 1876 1880 1879 -1 + 8162 1876 1866 1880 -1 + 8163 1873 1863 1871 -1 + 8164 1870 1863 1873 -1 + 8165 1883 1790 1857 -1 + 8166 1788 1857 1790 -1 + 8167 78 109 80 -1 + 8168 80 109 128 -1 + 8169 206 196 1883 -1 + 8170 86 196 206 -1 + 8171 78 120 132 -1 + 8172 132 109 78 -1 + 8173 78 1817 120 -1 + 8174 120 1817 1820 -1 + 8175 131 134 1821 -1 + 8176 111 134 131 -1 + 8177 1817 1776 1799 -1 + 8178 1799 1777 1817 -1 + 8179 1787 1825 1814 -1 + 8180 1787 1824 1825 -1 + 8181 1778 1821 1823 -1 + 8182 1778 131 1821 -1 + 8183 1861 1860 1862 -1 + 8184 1862 1860 1864 -1 + 8185 1861 1862 1878 -1 + 8186 1878 1795 1861 -1 + 8187 72 71 99 -1 + 8188 73 71 72 -1 + 8189 72 80 128 -1 + 8190 128 129 72 -1 + 8191 1785 1794 1791 -1 + 8192 1791 1781 1785 -1 + 8193 1784 1781 1780 -1 + 8194 1784 1785 1781 -1 + 8195 1828 1826 1802 -1 + 8196 1802 1804 1828 -1 + 8197 4210 2888 2889 -1 + 8198 2889 332 4210 -1 + 8199 4274 4234 3000 -1 + 8200 1002 1003 4329 -1 + 8201 4483 4478 3258 -1 + 8202 1773 1181 1774 -1 + 8203 4704 4543 4683 -1 + 8204 4615 4683 4543 -1 + 8205 3638 4105 3300 -1 + 8206 4115 1274 1272 -1 + 8207 4018 3561 3560 -1 + 8208 4048 1712 1749 -1 + 8209 1723 1749 1712 -1 + 8210 4522 1660 4064 -1 + 8211 4012 4064 1660 -1 + 8212 3611 3610 3557 -1 + 8213 3626 3610 4011 -1 + 8214 1556 1612 4168 -1 + 8215 4168 1614 4161 -1 + 8216 1612 1614 4168 -1 + 8217 1316 1548 4209 -1 + 8218 1316 1549 1548 -1 + 8219 4286 3074 3075 -1 + 8220 3075 3074 3067 -1 + 8221 4086 3280 3700 -1 + 8222 3700 3280 3119 -1 + 8223 4016 2826 3860 -1 + 8224 2909 2826 4016 -1 + 8225 2909 2829 2826 -1 + 8226 2504 2505 4299 -1 + 8227 2504 4120 2505 -1 + 8228 4330 4276 2810 -1 + 8229 3040 4230 4272 -1 + 8230 3917 3899 4054 -1 + 8231 3917 4039 3899 -1 + 8232 3156 1330 1323 -1 + 8233 1691 1670 4396 -1 + 8234 1691 1694 1670 -1 + 8235 4430 4357 3288 -1 + 8236 3288 4022 4430 -1 + 8237 4426 3808 3870 -1 + 8238 3805 3870 3808 -1 + 8239 3453 3455 4574 -1 + 8240 3453 4425 3455 -1 + 8241 4347 4253 2588 -1 + 8242 4072 3113 3099 -1 + 8243 4605 3606 4304 -1 + 8244 4072 3100 3269 -1 + 8245 3716 3681 4151 -1 + 8246 4093 4084 1618 -1 + 8247 4532 967 966 -1 + 8248 1087 967 4532 -1 + 8249 4050 4131 4047 -1 + 8250 1401 1399 4159 -1 + 8251 1748 1401 4159 -1 + 8252 1748 4146 1401 -1 + 8253 3681 3680 4151 -1 + 8254 1073 1064 1066 -1 + 8255 1064 4053 1066 -1 + 8256 3065 3064 4031 -1 + 8257 3065 3066 3064 -1 + 8258 4544 1577 1576 -1 + 8259 4663 823 597 -1 + 8260 597 823 611 -1 + 8261 4510 1707 4166 -1 + 8262 2729 3008 4548 -1 + 8263 2729 3054 3008 -1 + 8264 4462 4241 4139 -1 + 8265 1715 1713 4080 -1 + 8266 1610 1609 4561 -1 + 8267 4156 3418 3502 -1 + 8268 3502 3418 3514 -1 + 8269 1323 1330 1331 -1 + 8270 3549 3540 3550 -1 + 8271 4092 3449 3450 -1 + 8272 3904 4117 3907 -1 + 8273 3410 3408 4555 -1 + 8274 1109 1738 1119 -1 + 8275 4446 4258 1671 -1 + 8276 1671 4258 1245 -1 + 8277 4628 1611 1081 -1 + 8278 1080 1081 1611 -1 + 8279 3427 3428 3605 -1 + 8280 4442 4399 4406 -1 + 8281 4110 4058 1598 -1 + 8282 4107 3105 3300 -1 + 8283 3300 3105 3638 -1 + 8284 3104 4105 3638 -1 + 8285 3099 3100 4072 -1 + 8286 3438 3437 4112 -1 + 8287 3437 3439 4112 -1 + 8288 4567 4334 2284 -1 + 8289 3414 3438 4112 -1 + 8290 3414 3417 3438 -1 + 8291 4099 1315 1318 -1 + 8292 2018 1513 4365 -1 + 8293 1518 2018 4365 -1 + 8294 1518 4238 2018 -1 + 8295 4128 3645 3646 -1 + 8296 3646 3645 3093 -1 + 8297 4128 3646 4125 -1 + 8298 3400 3402 3546 -1 + 8299 2799 3226 4227 -1 + 8300 4129 1148 1139 -1 + 8301 737 752 750 -1 + 8302 4155 1705 1709 -1 + 8303 1148 1621 4055 -1 + 8304 4733 1216 4718 -1 + 8305 4315 1659 4292 -1 + 8306 814 339 343 -1 + 8307 4248 2907 3197 -1 + 8308 3195 3253 4248 -1 + 8309 1243 4004 1671 -1 + 8310 4060 1243 1245 -1 + 8311 1671 1245 1243 -1 + 8312 4060 1245 1244 -1 + 8313 1244 1149 4060 -1 + 8314 1244 1665 1149 -1 + 8315 1671 4004 1680 -1 + 8316 4136 3082 3048 -1 + 8317 982 3048 3082 -1 + 8318 1305 3030 4061 -1 + 8319 1305 3145 3030 -1 + 8320 3030 3029 4061 -1 + 8321 4591 4273 4534 -1 + 8322 4134 438 202 -1 + 8323 4064 1661 1655 -1 + 8324 2871 1661 4064 -1 + 8325 4064 4012 2871 -1 + 8326 4144 1277 1240 -1 + 8327 1240 1276 4144 -1 + 8328 4591 3108 4218 -1 + 8329 4543 3386 4615 -1 + 8330 4066 1395 1133 -1 + 8331 4456 2970 2839 -1 + 8332 4462 4139 4368 -1 + 8333 4183 3321 4038 -1 + 8334 4030 4127 3050 -1 + 8335 994 1261 990 -1 + 8336 4182 1546 4020 -1 + 8337 4479 1140 4024 -1 + 8338 4434 3955 3884 -1 + 8339 4180 3662 3684 -1 + 8340 2941 2942 2885 -1 + 8341 1272 1331 1001 -1 + 8342 4541 4518 1742 -1 + 8343 1742 4518 1119 -1 + 8344 4447 3965 4119 -1 + 8345 1116 4525 4517 -1 + 8346 4535 4199 4021 -1 + 8347 1216 1315 4099 -1 + 8348 4719 1589 1588 -1 + 8349 4464 4455 3908 -1 + 8350 4080 1722 1715 -1 + 8351 1721 1715 1722 -1 + 8352 4542 998 1001 -1 + 8353 4733 4657 1216 -1 + 8354 3446 4307 4386 -1 + 8355 4359 85 1203 -1 + 8356 4581 3762 4572 -1 + 8357 4405 3715 3171 -1 + 8358 3175 3171 3715 -1 + 8359 3171 3130 4405 -1 + 8360 3100 3264 4581 -1 + 8361 3100 3097 3264 -1 + 8362 4626 4357 3932 -1 + 8363 2735 2736 2733 -1 + 8364 3119 3280 3282 -1 + 8365 4086 3703 3095 -1 + 8366 3095 3703 3093 -1 + 8367 4087 430 204 -1 + 8368 1610 1607 1646 -1 + 8369 1607 1569 1646 -1 + 8370 1646 1636 4089 -1 + 8371 622 608 650 -1 + 8372 3379 3076 4467 -1 + 8373 3379 3427 3076 -1 + 8374 4325 3453 4574 -1 + 8375 4594 2830 2819 -1 + 8376 3206 2819 2830 -1 + 8377 3575 3904 3549 -1 + 8378 1585 1586 1280 -1 + 8379 4326 4007 2504 -1 + 8380 4476 4211 3607 -1 + 8381 3607 4211 3519 -1 + 8382 1052 1631 4233 -1 + 8383 438 197 4153 -1 + 8384 438 4134 197 -1 + 8385 4233 1631 1648 -1 + 8386 1270 1702 4617 -1 + 8387 4153 434 440 -1 + 8388 1322 1321 4160 -1 + 8389 4241 3901 4139 -1 + 8390 4285 1551 1246 -1 + 8391 3906 3916 4117 -1 + 8392 4257 4019 3083 -1 + 8393 1270 1689 4650 -1 + 8394 4044 4081 3133 -1 + 8395 3292 3300 4105 -1 + 8396 3104 3292 4105 -1 + 8397 3104 3655 3292 -1 + 8398 3655 4003 3292 -1 + 8399 3109 4003 3655 -1 + 8400 4013 4122 1002 -1 + 8401 2887 2699 4162 -1 + 8402 4111 2852 2854 -1 + 8403 4450 4443 3058 -1 + 8404 4567 1207 4334 -1 + 8405 1252 1152 1643 -1 + 8406 4113 1252 1571 -1 + 8407 1571 1252 1643 -1 + 8408 4113 1567 1155 -1 + 8409 1565 1155 1567 -1 + 8410 1643 1152 1642 -1 + 8411 4179 994 989 -1 + 8412 989 994 990 -1 + 8413 4527 3041 4352 -1 + 8414 4455 4119 3965 -1 + 8415 4742 4747 1589 -1 + 8416 4233 4169 1052 -1 + 8417 4680 1576 1149 -1 + 8418 1577 1149 1576 -1 + 8419 4132 753 84 -1 + 8420 999 4115 998 -1 + 8421 4755 3072 4752 -1 + 8422 4521 2499 4280 -1 + 8423 4430 3286 4357 -1 + 8424 3287 3286 4430 -1 + 8425 4129 1226 1148 -1 + 8426 1148 1226 1222 -1 + 8427 4125 3644 3636 -1 + 8428 4223 4173 1119 -1 + 8429 4339 3537 3544 -1 + 8430 3544 3560 4339 -1 + 8431 4749 1582 4747 -1 + 8432 1589 4747 1582 -1 + 8433 2812 4103 2729 -1 + 8434 1036 4138 2661 -1 + 8435 4276 4257 3083 -1 + 8436 4580 1629 1632 -1 + 8437 4131 4121 1263 -1 + 8438 1263 4121 1265 -1 + 8439 2753 4051 2754 -1 + 8440 2131 2129 4225 -1 + 8441 2131 3559 2129 -1 + 8442 4420 3285 3939 -1 + 8443 4447 4119 3939 -1 + 8444 4165 2828 4097 -1 + 8445 202 201 4134 -1 + 8446 4154 4098 4049 -1 + 8447 4271 4055 1645 -1 + 8448 4440 4031 3081 -1 + 8449 201 197 4134 -1 + 8450 2904 3958 4761 -1 + 8451 4271 1148 4055 -1 + 8452 2908 2911 3958 -1 + 8453 1681 1693 4446 -1 + 8454 1187 4400 822 -1 + 8455 988 4327 4609 -1 + 8456 1733 4579 1266 -1 + 8457 3773 3955 4434 -1 + 8458 2838 2839 2840 -1 + 8459 2840 3788 2838 -1 + 8460 3901 2840 4139 -1 + 8461 4213 205 223 -1 + 8462 223 205 207 -1 + 8463 4213 223 204 -1 + 8464 4153 440 438 -1 + 8465 1720 1590 1280 -1 + 8466 4314 1608 1644 -1 + 8467 3625 3592 4167 -1 + 8468 3625 4011 3592 -1 + 8469 1045 3625 4167 -1 + 8470 3592 2145 4167 -1 + 8471 4544 1576 1603 -1 + 8472 1603 1576 1230 -1 + 8473 4438 3933 3886 -1 + 8474 3599 4137 3597 -1 + 8475 4656 4607 4178 -1 + 8476 4656 4312 4607 -1 + 8477 4546 4481 3456 -1 + 8478 4188 4161 1559 -1 + 8479 4176 3870 3266 -1 + 8480 2283 4076 2501 -1 + 8481 1032 4184 1031 -1 + 8482 4151 3680 3685 -1 + 8483 3634 3636 3644 -1 + 8484 4151 3685 3556 -1 + 8485 3556 3685 3686 -1 + 8486 4674 4508 4279 -1 + 8487 4246 4180 3688 -1 + 8488 4164 1321 1219 -1 + 8489 4271 1645 1153 -1 + 8490 3454 3449 4195 -1 + 8491 1392 1390 1179 -1 + 8492 3064 4607 4704 -1 + 8493 2014 2038 4238 -1 + 8494 2014 2042 2038 -1 + 8495 4500 3172 4493 -1 + 8496 4282 3172 4500 -1 + 8497 4683 4615 4764 -1 + 8498 4169 1213 1052 -1 + 8499 1614 1557 4161 -1 + 8500 1557 4088 4161 -1 + 8501 4241 3966 3901 -1 + 8502 4164 1219 1320 -1 + 8503 4398 3951 4337 -1 + 8504 3895 4337 3951 -1 + 8505 4511 4311 4487 -1 + 8506 2125 2137 1038 -1 + 8507 4216 430 431 -1 + 8508 431 430 420 -1 + 8509 955 962 4677 -1 + 8510 1004 986 4444 -1 + 8511 1753 1754 4437 -1 + 8512 4190 1733 4157 -1 + 8513 1266 4157 1733 -1 + 8514 1498 2660 4529 -1 + 8515 1650 1211 4697 -1 + 8516 1689 1685 4705 -1 + 8517 4190 4157 1143 -1 + 8518 4201 3916 4039 -1 + 8519 4296 4015 51 -1 + 8520 1241 4292 4035 -1 + 8521 1196 2280 4721 -1 + 8522 4188 1556 4168 -1 + 8523 3267 3268 3831 -1 + 8524 1759 4014 1758 -1 + 8525 1764 1758 1580 -1 + 8526 1758 1755 1580 -1 + 8527 4390 1621 1630 -1 + 8528 1758 4014 1755 -1 + 8529 4177 1580 4095 -1 + 8530 1584 4095 1580 -1 + 8531 4245 4012 1659 -1 + 8532 1659 1653 4245 -1 + 8533 2789 4410 4545 -1 + 8534 4395 1004 4275 -1 + 8535 4750 1264 4716 -1 + 8536 2696 2690 2840 -1 + 8537 1115 1118 4629 -1 + 8538 4408 4198 4001 -1 + 8539 4408 3595 4198 -1 + 8540 4628 1081 1919 -1 + 8541 4308 2971 2840 -1 + 8542 4185 4131 1255 -1 + 8543 4408 4001 3598 -1 + 8544 1558 1559 4231 -1 + 8545 1021 992 4379 -1 + 8546 3301 3856 4187 -1 + 8547 4189 4091 1109 -1 + 8548 1109 4091 1738 -1 + 8549 4189 1109 4040 -1 + 8550 4498 643 641 -1 + 8551 1418 1414 1395 -1 + 8552 4190 1143 1766 -1 + 8553 1767 1766 1143 -1 + 8554 4173 4259 1119 -1 + 8555 1720 1763 1590 -1 + 8556 4191 1590 4152 -1 + 8557 1830 1831 2497 -1 + 8558 1830 2500 1853 -1 + 8559 4289 4147 1719 -1 + 8560 1719 4147 1697 -1 + 8561 4407 980 3173 -1 + 8562 4302 4089 1636 -1 + 8563 4289 1696 4147 -1 + 8564 4599 987 4444 -1 + 8565 1398 4159 1399 -1 + 8566 4388 3341 3820 -1 + 8567 3286 4625 4266 -1 + 8568 4630 3050 3051 -1 + 8569 4630 3060 3050 -1 + 8570 4437 1757 1750 -1 + 8571 1710 1750 1757 -1 + 8572 3504 2785 4646 -1 + 8573 4592 4522 1655 -1 + 8574 4551 4505 1607 -1 + 8575 1247 1246 4536 -1 + 8576 4597 4506 4293 -1 + 8577 3076 3427 3605 -1 + 8578 4467 3076 4234 -1 + 8579 4657 1317 4535 -1 + 8580 4200 3613 3629 -1 + 8581 3629 3528 4200 -1 + 8582 175 31 4212 -1 + 8583 175 4141 31 -1 + 8584 31 39 4212 -1 + 8585 4081 3060 4627 -1 + 8586 2662 2917 4457 -1 + 8587 4203 1170 1304 -1 + 8588 1304 1170 1163 -1 + 8589 1163 1170 1180 -1 + 8590 1161 1163 1180 -1 + 8591 977 4196 1093 -1 + 8592 1096 4196 977 -1 + 8593 1207 4065 2268 -1 + 8594 4523 1240 1236 -1 + 8595 1675 1240 4523 -1 + 8596 1094 1096 1007 -1 + 8597 4763 4744 4259 -1 + 8598 4626 4295 1241 -1 + 8599 1241 4295 4292 -1 + 8600 48 175 4212 -1 + 8601 4217 2805 4198 -1 + 8602 4001 4198 2805 -1 + 8603 4537 1561 4231 -1 + 8604 4265 4252 4254 -1 + 8605 4436 4286 3075 -1 + 8606 3602 3623 3598 -1 + 8607 4665 3269 4591 -1 + 8608 4667 1244 4351 -1 + 8609 4220 1912 1076 -1 + 8610 3422 3942 4555 -1 + 8611 3942 3410 4555 -1 + 8612 1688 1684 4222 -1 + 8613 4706 3483 4427 -1 + 8614 3486 4494 4735 -1 + 8615 1742 4259 4453 -1 + 8616 4361 1604 1619 -1 + 8617 4360 4354 3285 -1 + 8618 4361 1606 1604 -1 + 8619 3285 3933 4360 -1 + 8620 4697 1211 4240 -1 + 8621 3058 3024 4566 -1 + 8622 3058 4418 3024 -1 + 8623 4671 4531 4298 -1 + 8624 2853 2943 4502 -1 + 8625 2853 2828 2943 -1 + 8626 4229 4171 1582 -1 + 8627 1582 1114 4229 -1 + 8628 1741 1113 4269 -1 + 8629 4394 1743 4313 -1 + 8630 4639 3389 4615 -1 + 8631 4500 4493 4300 -1 + 8632 4590 4193 4301 -1 + 8633 4288 4193 4590 -1 + 8634 3094 3341 4451 -1 + 8635 3094 3820 3341 -1 + 8636 405 631 4549 -1 + 8637 405 4068 631 -1 + 8638 1744 1743 4394 -1 + 8639 4597 4293 3482 -1 + 8640 4275 987 1263 -1 + 8641 4396 1670 4351 -1 + 8642 1710 4226 1750 -1 + 8643 2787 3453 4371 -1 + 8644 4419 4361 1619 -1 + 8645 321 702 706 -1 + 8646 3047 3038 4237 -1 + 8647 3303 3808 4273 -1 + 8648 3303 4002 3808 -1 + 8649 2667 2787 4371 -1 + 8650 4239 1560 1561 -1 + 8651 4538 2725 2716 -1 + 8652 4242 1210 1206 -1 + 8653 4242 1208 1210 -1 + 8654 4242 1206 2268 -1 + 8655 2268 1208 4242 -1 + 8656 4243 3127 3129 -1 + 8657 3102 3127 4243 -1 + 8658 3102 4123 3127 -1 + 8659 726 342 727 -1 + 8660 703 726 727 -1 + 8661 1743 1585 4313 -1 + 8662 1743 1586 1585 -1 + 8663 3068 4033 3004 -1 + 8664 4651 4404 2984 -1 + 8665 4560 1033 2658 -1 + 8666 2658 1033 2653 -1 + 8667 3221 4350 3206 -1 + 8668 4650 1268 4335 -1 + 8669 4032 4335 1268 -1 + 8670 4650 4335 1270 -1 + 8671 4590 1182 4288 -1 + 8672 4267 3459 3454 -1 + 8673 4267 3452 4150 -1 + 8674 4326 4299 2283 -1 + 8675 4707 4399 4442 -1 + 8676 2671 3192 3193 -1 + 8677 4631 3060 4630 -1 + 8678 3050 4062 4249 -1 + 8679 3050 4127 4062 -1 + 8680 4062 4103 4249 -1 + 8681 4566 4037 2997 -1 + 8682 4033 4186 2997 -1 + 8683 1265 1264 4251 -1 + 8684 3938 3471 4262 -1 + 8685 3938 3090 3471 -1 + 8686 4117 4201 3907 -1 + 8687 3083 2810 4276 -1 + 8688 4253 2579 1185 -1 + 8689 3940 4228 4266 -1 + 8690 4323 1692 1718 -1 + 8691 1716 1718 1692 -1 + 8692 4363 2569 2592 -1 + 8693 4363 2567 2569 -1 + 8694 3453 2787 4425 -1 + 8695 3451 3455 4425 -1 + 8696 1668 4277 1664 -1 + 8697 4364 1734 4332 -1 + 8698 4353 9 4010 -1 + 8699 1162 4203 1304 -1 + 8700 4673 4672 578 -1 + 8701 4342 1000 1083 -1 + 8702 4290 737 775 -1 + 8703 775 737 747 -1 + 8704 1317 1237 4319 -1 + 8705 1317 4052 1237 -1 + 8706 3548 4054 408 -1 + 8707 4491 3285 4420 -1 + 8708 3075 3379 4436 -1 + 8709 4730 4687 2994 -1 + 8710 4690 2789 4676 -1 + 8711 4319 1686 1316 -1 + 8712 1316 1686 1682 -1 + 8713 4723 3514 4722 -1 + 8714 4552 4382 4310 -1 + 8715 4505 4485 1565 -1 + 8716 1316 1317 4319 -1 + 8717 1316 4209 1317 -1 + 8718 3403 3402 3400 -1 + 8719 4269 1113 1117 -1 + 8720 1507 405 4549 -1 + 8721 1119 4259 1742 -1 + 8722 1117 1116 4269 -1 + 8723 4421 4372 4114 -1 + 8724 4114 4185 4421 -1 + 8725 4421 4407 4372 -1 + 8726 4427 3483 3101 -1 + 8727 3472 3101 3483 -1 + 8728 4287 1418 1756 -1 + 8729 1775 1756 1418 -1 + 8730 3647 3638 4331 -1 + 8731 4331 3638 4090 -1 + 8732 3105 4090 3638 -1 + 8733 4613 4592 1655 -1 + 8734 3602 3599 4600 -1 + 8735 4422 1750 4226 -1 + 8736 4298 1403 1111 -1 + 8737 4298 1391 1403 -1 + 8738 4321 2838 3741 -1 + 8739 2781 3603 2787 -1 + 8740 4269 4417 1741 -1 + 8741 4321 3741 3296 -1 + 8742 1128 1740 4770 -1 + 8743 1128 1400 1740 -1 + 8744 2660 2917 2662 -1 + 8745 4281 434 197 -1 + 8746 4281 4079 434 -1 + 8747 434 4079 4068 -1 + 8748 4286 4274 2998 -1 + 8749 2998 4274 3000 -1 + 8750 1750 1751 4437 -1 + 8751 1172 1141 1173 -1 + 8752 4288 1168 4193 -1 + 8753 1169 4193 1168 -1 + 8754 1407 1404 4602 -1 + 8755 4265 4254 4599 -1 + 8756 1723 1584 1749 -1 + 8757 4291 3114 4202 -1 + 8758 3089 4202 3114 -1 + 8759 3278 3279 3110 -1 + 8760 4618 3295 3782 -1 + 8761 3138 4283 4235 -1 + 8762 4699 3761 3762 -1 + 8763 2813 303 4643 -1 + 8764 4581 4572 3100 -1 + 8765 1266 4017 4297 -1 + 8766 1266 4098 4017 -1 + 8767 1146 1266 4297 -1 + 8768 2754 4051 2727 -1 + 8769 4712 955 4677 -1 + 8770 4753 4388 4682 -1 + 8771 4321 3296 4221 -1 + 8772 4569 1617 4093 -1 + 8773 4570 1577 4544 -1 + 8774 4569 4093 1620 -1 + 8775 3563 3555 4338 -1 + 8776 4135 4519 1162 -1 + 8777 1021 1022 4345 -1 + 8778 1021 4130 1022 -1 + 8779 1022 1002 4345 -1 + 8780 2501 4076 2479 -1 + 8781 4575 3112 4552 -1 + 8782 4587 577 313 -1 + 8783 4308 2724 2971 -1 + 8784 4620 4563 1557 -1 + 8785 3681 3714 4310 -1 + 8786 1628 1624 4620 -1 + 8787 4576 2919 2918 -1 + 8788 3640 4128 4125 -1 + 8789 2815 2818 4550 -1 + 8790 2815 4077 2818 -1 + 8791 4236 4471 4217 -1 + 8792 3153 3005 3152 -1 + 8793 3422 3406 3930 -1 + 8794 4317 2920 3881 -1 + 8795 4577 3128 3103 -1 + 8796 988 993 4327 -1 + 8797 988 4460 993 -1 + 8798 3047 3042 4318 -1 + 8799 3047 4237 3042 -1 + 8800 3204 3205 1507 -1 + 8801 4318 4136 3048 -1 + 8802 4318 4306 4136 -1 + 8803 4318 3042 4283 -1 + 8804 4235 4283 3042 -1 + 8805 4318 4283 4306 -1 + 8806 1842 4126 4353 -1 + 8807 3107 3108 4378 -1 + 8808 3107 4218 3108 -1 + 8809 4010 1842 4353 -1 + 8810 2013 1034 4439 -1 + 8811 3005 4367 3007 -1 + 8812 963 4397 4289 -1 + 8813 4490 4411 3548 -1 + 8814 1241 3288 4357 -1 + 8815 1689 1270 4662 -1 + 8816 4598 1608 1630 -1 + 8817 746 743 740 -1 + 8818 2284 2289 4484 -1 + 8819 2284 4143 2289 -1 + 8820 4481 4373 3454 -1 + 8821 4652 4486 4464 -1 + 8822 4481 3454 3456 -1 + 8823 313 3315 4328 -1 + 8824 4339 3561 3537 -1 + 8825 4339 3560 3561 -1 + 8826 4449 2852 4111 -1 + 8827 4333 3019 3001 -1 + 8828 3001 3019 3018 -1 + 8829 3018 2995 3001 -1 + 8830 3071 2995 3018 -1 + 8831 2267 2288 2268 -1 + 8832 4708 4700 4459 -1 + 8833 3633 3662 4633 -1 + 8834 4348 4118 3006 -1 + 8835 4345 1002 4329 -1 + 8836 4347 1229 4253 -1 + 8837 3982 3981 3991 -1 + 8838 4461 4389 1723 -1 + 8839 1020 1019 1018 -1 + 8840 1614 1067 1557 -1 + 8841 4738 4683 4764 -1 + 8842 4738 4743 4683 -1 + 8843 1740 1741 4768 -1 + 8844 2801 4008 2733 -1 + 8845 2734 4008 2801 -1 + 8846 3064 4743 3081 -1 + 8847 1738 4091 4497 -1 + 8848 3588 3622 4392 -1 + 8849 3588 3594 3622 -1 + 8850 4367 3005 2801 -1 + 8851 738 4290 775 -1 + 8852 1407 1730 4707 -1 + 8853 1407 1729 1730 -1 + 8854 2745 2749 2735 -1 + 8855 4348 2737 4118 -1 + 8856 4348 2736 2737 -1 + 8857 2737 2736 2735 -1 + 8858 4349 1133 1411 -1 + 8859 1414 1411 1133 -1 + 8860 1129 1133 4349 -1 + 8861 4469 1393 4232 -1 + 8862 2652 1503 4529 -1 + 8863 1036 1035 1031 -1 + 8864 3268 3106 4378 -1 + 8865 3268 3109 3106 -1 + 8866 710 705 706 -1 + 8867 4354 3939 3285 -1 + 8868 4604 4644 4748 -1 + 8869 1187 4578 4520 -1 + 8870 4401 3593 3595 -1 + 8871 3604 3623 3602 -1 + 8872 1660 4035 1659 -1 + 8873 4359 4224 753 -1 + 8874 1057 751 1203 -1 + 8875 4359 1203 4224 -1 + 8876 751 4224 1203 -1 + 8877 4772 4763 4173 -1 + 8878 4413 3323 4337 -1 + 8879 3313 4337 3323 -1 + 8880 4666 4622 3273 -1 + 8881 4364 1765 1732 -1 + 8882 1732 1765 1770 -1 + 8883 4703 2840 2718 -1 + 8884 2692 2718 2840 -1 + 8885 4366 738 4341 -1 + 8886 4290 738 4366 -1 + 8887 4468 1736 4459 -1 + 8888 1114 1736 4468 -1 + 8889 4467 4436 3379 -1 + 8890 3623 3588 4392 -1 + 8891 2734 3181 3183 -1 + 8892 4368 2988 3908 -1 + 8893 4723 3500 3491 -1 + 8894 3502 3403 3400 -1 + 8895 4369 3572 3581 -1 + 8896 3417 4156 4369 -1 + 8897 3417 4194 4156 -1 + 8898 3581 3417 4369 -1 + 8899 4372 4320 1144 -1 + 8900 4372 1144 4114 -1 + 8901 4047 4114 1144 -1 + 8902 991 989 4482 -1 + 8903 991 4179 989 -1 + 8904 989 997 4482 -1 + 8905 989 4477 997 -1 + 8906 2800 3006 4391 -1 + 8907 2800 4348 3006 -1 + 8908 1003 984 4542 -1 + 8909 3715 3089 4641 -1 + 8910 4554 4204 4222 -1 + 8911 3907 3917 4411 -1 + 8912 3907 4039 3917 -1 + 8913 3917 3548 4411 -1 + 8914 3917 4054 3548 -1 + 8915 4504 4129 4263 -1 + 8916 4071 3064 4377 -1 + 8917 4071 4178 3064 -1 + 8918 3064 3132 4377 -1 + 8919 4044 4005 4528 -1 + 8920 3134 4081 4044 -1 + 8921 4528 4493 3138 -1 + 8922 3138 3056 4528 -1 + 8923 4652 4464 3908 -1 + 8924 4404 2993 2984 -1 + 8925 4404 3894 2993 -1 + 8926 4548 3008 2802 -1 + 8927 4446 1693 4258 -1 + 8928 4380 3428 3375 -1 + 8929 3620 3621 4380 -1 + 8930 3620 3596 3621 -1 + 8931 4380 3621 3428 -1 + 8932 3622 3428 3621 -1 + 8933 2500 4010 1847 -1 + 8934 4382 3536 3679 -1 + 8935 960 955 4725 -1 + 8936 4514 633 3204 -1 + 8937 4514 631 633 -1 + 8938 4565 4249 2812 -1 + 8939 2812 3052 4565 -1 + 8940 4356 4293 4506 -1 + 8941 2996 3023 3154 -1 + 8942 3152 3007 4495 -1 + 8943 3152 3005 3007 -1 + 8944 3068 4186 4033 -1 + 8945 4385 3092 2783 -1 + 8946 4490 3548 4094 -1 + 8947 1071 4182 4020 -1 + 8948 4455 3966 3908 -1 + 8949 4095 1584 4389 -1 + 8950 3519 4470 3607 -1 + 8951 4476 3609 4200 -1 + 8952 3628 4200 3609 -1 + 8953 1022 4130 4460 -1 + 8954 4610 4545 3938 -1 + 8955 4395 988 1004 -1 + 8956 1023 988 4395 -1 + 8957 4655 1268 4650 -1 + 8958 4397 1696 4289 -1 + 8959 1101 1696 4397 -1 + 8960 4519 1160 4203 -1 + 8961 1170 4203 1160 -1 + 8962 4500 4463 4278 -1 + 8963 4500 4300 4463 -1 + 8964 4703 2718 2725 -1 + 8965 1723 4389 1584 -1 + 8966 4603 1919 4578 -1 + 8967 4559 3061 3063 -1 + 8968 4559 3057 3061 -1 + 8969 3062 4148 3063 -1 + 8970 4521 4192 2499 -1 + 8971 3063 3056 4559 -1 + 8972 3063 4148 3056 -1 + 8973 4559 4472 3057 -1 + 8974 4726 2790 4710 -1 + 8975 4636 4498 641 -1 + 8976 641 4374 4636 -1 + 8977 4403 1146 4297 -1 + 8978 4624 4428 4746 -1 + 8979 4670 1699 4270 -1 + 8980 4528 4005 4300 -1 + 8981 4528 4300 4493 -1 + 8982 1265 4714 4085 -1 + 8983 4405 3130 4291 -1 + 8984 1703 1702 1259 -1 + 8985 1702 4032 1271 -1 + 8986 4504 1226 4129 -1 + 8987 997 993 4482 -1 + 8988 997 4327 993 -1 + 8989 1126 1135 4612 -1 + 8990 1126 1085 1135 -1 + 8991 4322 4438 3940 -1 + 8992 4431 4321 4221 -1 + 8993 4749 4751 1582 -1 + 8994 4703 4308 2840 -1 + 8995 4417 4269 1116 -1 + 8996 4454 4419 1071 -1 + 8997 4524 4175 3530 -1 + 8998 4542 984 4465 -1 + 8999 4454 1574 4419 -1 + 9000 4383 4415 2782 -1 + 9001 4278 4282 4500 -1 + 9002 4445 4333 3002 -1 + 9003 3002 4333 3001 -1 + 9004 4632 4483 3222 -1 + 9005 4501 1745 4197 -1 + 9006 1746 1745 4501 -1 + 9007 1565 1576 4680 -1 + 9008 1565 4485 1576 -1 + 9009 2728 4051 2753 -1 + 9010 4652 2988 4486 -1 + 9011 984 4046 4429 -1 + 9012 984 4122 4046 -1 + 9013 4424 2588 4253 -1 + 9014 4427 3101 3091 -1 + 9015 4427 3091 3092 -1 + 9016 4659 4317 4622 -1 + 9017 3455 4025 4574 -1 + 9018 3455 4267 4025 -1 + 9019 4432 4078 1029 -1 + 9020 1029 4078 4029 -1 + 9021 1030 1122 4432 -1 + 9022 1030 1028 1122 -1 + 9023 4432 1029 1030 -1 + 9024 4548 2727 2729 -1 + 9025 2729 2727 2728 -1 + 9026 2727 4051 2728 -1 + 9027 4541 1588 4311 -1 + 9028 4541 1109 4518 -1 + 9029 1119 4518 1109 -1 + 9030 4434 3884 4069 -1 + 9031 3923 4260 4435 -1 + 9032 3942 4036 3410 -1 + 9033 4682 3820 3863 -1 + 9034 4260 4264 3503 -1 + 9035 3411 4036 3942 -1 + 9036 3942 3422 3921 -1 + 9037 3411 3942 3921 -1 + 9038 3921 3922 4435 -1 + 9039 3921 3422 3922 -1 + 9040 4435 4260 3921 -1 + 9041 3921 4260 3503 -1 + 9042 1598 1591 4441 -1 + 9043 4441 4110 1598 -1 + 9044 3341 3296 4451 -1 + 9045 3341 4221 3296 -1 + 9046 3004 2996 4475 -1 + 9047 3004 3003 2996 -1 + 9048 4475 4391 3006 -1 + 9049 1169 1181 4301 -1 + 9050 1774 1181 1169 -1 + 9051 4715 1729 4647 -1 + 9052 1178 1159 4601 -1 + 9053 4688 4487 4229 -1 + 9054 4445 3002 4041 -1 + 9055 4503 4379 991 -1 + 9056 4459 1736 4393 -1 + 9057 4449 4111 2855 -1 + 9058 1021 4345 4329 -1 + 9059 3095 3094 4451 -1 + 9060 3280 3281 3282 -1 + 9061 4452 311 4303 -1 + 9062 3951 4398 4303 -1 + 9063 4690 4262 4637 -1 + 9064 3450 3485 4508 -1 + 9065 3071 3000 4755 -1 + 9066 1740 1739 4770 -1 + 9067 4562 1692 4323 -1 + 9068 4561 1609 4551 -1 + 9069 4561 4551 1607 -1 + 9070 3806 4073 2702 -1 + 9071 4458 2702 4449 -1 + 9072 4458 4449 3819 -1 + 9073 4689 4324 4604 -1 + 9074 4709 4499 1588 -1 + 9075 4516 2818 3981 -1 + 9076 3991 3981 2818 -1 + 9077 4572 3762 3270 -1 + 9078 4533 1226 4504 -1 + 9079 4533 1629 1226 -1 + 9080 974 1125 1100 -1 + 9081 4471 4470 3519 -1 + 9082 4584 2747 3053 -1 + 9083 4744 4215 4736 -1 + 9084 2729 4127 3054 -1 + 9085 4742 4741 4737 -1 + 9086 1774 1169 4773 -1 + 9087 2782 4466 2784 -1 + 9088 4560 2662 4457 -1 + 9089 4699 3860 3761 -1 + 9090 4474 4272 4230 -1 + 9091 4549 4514 1507 -1 + 9092 1507 4514 3204 -1 + 9093 3057 4272 4474 -1 + 9094 3057 4370 4272 -1 + 9095 3179 2750 3183 -1 + 9096 3173 4376 4694 -1 + 9097 3173 4611 4376 -1 + 9098 3258 3222 4483 -1 + 9099 990 986 4477 -1 + 9100 990 4265 986 -1 + 9101 3257 3258 4478 -1 + 9102 4568 4301 1181 -1 + 9103 4673 1207 4672 -1 + 9104 4591 4534 3108 -1 + 9105 4478 4330 2762 -1 + 9106 2762 3257 4478 -1 + 9107 4690 4637 3937 -1 + 9108 4586 4195 4325 -1 + 9109 4586 4574 4025 -1 + 9110 4612 4429 1126 -1 + 9111 299 311 4507 -1 + 9112 3228 3198 2754 -1 + 9113 3154 3023 3155 -1 + 9114 4486 4119 4464 -1 + 9115 2702 4073 4502 -1 + 9116 4745 1729 4740 -1 + 9117 2884 2887 4512 -1 + 9118 2884 2704 2887 -1 + 9119 4502 4073 3805 -1 + 9120 2852 4449 2702 -1 + 9121 4689 4508 3485 -1 + 9122 4279 4508 4689 -1 + 9123 2886 4038 4512 -1 + 9124 3906 4117 3904 -1 + 9125 4491 3894 3886 -1 + 9126 4596 3080 3078 -1 + 9127 4529 1503 1498 -1 + 9128 2684 1498 1503 -1 + 9129 4493 3172 3138 -1 + 9130 3172 4282 3082 -1 + 9131 4507 590 591 -1 + 9132 4616 4544 1603 -1 + 9133 4598 1630 1041 -1 + 9134 591 299 4507 -1 + 9135 591 574 299 -1 + 9136 3155 3039 3156 -1 + 9137 3372 3389 4639 -1 + 9138 4290 4366 739 -1 + 9139 4412 4363 4721 -1 + 9140 4686 4658 1159 -1 + 9141 2911 2909 3958 -1 + 9142 645 4374 641 -1 + 9143 4611 3173 4282 -1 + 9144 3388 3372 4639 -1 + 9145 3372 3614 3613 -1 + 9146 4638 4302 1608 -1 + 9147 4638 4598 4089 -1 + 9148 3179 3200 4539 -1 + 9149 3408 3406 4555 -1 + 9150 4618 3782 3280 -1 + 9151 3280 3782 3281 -1 + 9152 4510 1722 4080 -1 + 9153 2784 3500 4646 -1 + 9154 4553 1638 1656 -1 + 9155 3933 4438 4322 -1 + 9156 4515 1726 1735 -1 + 9157 4515 4461 1723 -1 + 9158 1698 1768 1731 -1 + 9159 1698 1699 1768 -1 + 9160 1737 4763 4772 -1 + 9161 1735 1725 4515 -1 + 9162 3973 2813 3981 -1 + 9163 4524 4383 3529 -1 + 9164 3529 4383 2782 -1 + 9165 4517 1587 4417 -1 + 9166 3930 3473 3938 -1 + 9167 3852 3762 4581 -1 + 9168 4519 4135 1160 -1 + 9169 3530 3518 4524 -1 + 9170 4524 3518 4383 -1 + 9171 4524 3529 4175 -1 + 9172 4525 1666 1587 -1 + 9173 1586 1587 1666 -1 + 9174 4525 1672 1666 -1 + 9175 4525 1116 1672 -1 + 9176 4525 1587 4517 -1 + 9177 1000 984 4612 -1 + 9178 1000 4172 984 -1 + 9179 4282 4278 4527 -1 + 9180 4172 4465 984 -1 + 9181 4612 984 4429 -1 + 9182 3060 4081 3134 -1 + 9183 4530 4151 3556 -1 + 9184 4533 1632 1629 -1 + 9185 3264 3852 4581 -1 + 9186 4533 4504 1632 -1 + 9187 1632 4504 4263 -1 + 9188 4534 4426 3108 -1 + 9189 4534 4273 4426 -1 + 9190 4537 1559 1562 -1 + 9191 2724 2725 4538 -1 + 9192 4538 2716 3929 -1 + 9193 4538 3893 2724 -1 + 9194 1654 4433 4082 -1 + 9195 2754 2727 3228 -1 + 9196 3501 3500 4583 -1 + 9197 3055 3061 4443 -1 + 9198 4205 3061 3055 -1 + 9199 995 4096 1331 -1 + 9200 3107 3112 4305 -1 + 9201 3107 3111 3112 -1 + 9202 3000 4234 3076 -1 + 9203 3076 3605 3000 -1 + 9204 1306 1162 4608 -1 + 9205 1306 4135 1162 -1 + 9206 4608 1162 1303 -1 + 9207 1087 1086 1085 -1 + 9208 3422 3930 3922 -1 + 9209 4545 3922 3938 -1 + 9210 3930 3938 3922 -1 + 9211 4410 3922 4545 -1 + 9212 4410 4435 3922 -1 + 9213 4629 4501 1115 -1 + 9214 1115 4501 4197 -1 + 9215 4557 1733 4640 -1 + 9216 4557 4579 1733 -1 + 9217 4546 3923 4481 -1 + 9218 4721 4593 1196 -1 + 9219 1196 4593 4585 -1 + 9220 4695 4027 4655 -1 + 9221 4677 962 4675 -1 + 9222 4677 4675 963 -1 + 9223 963 4675 4654 -1 + 9224 4631 4627 3060 -1 + 9225 4692 3119 3118 -1 + 9226 3699 3119 4692 -1 + 9227 4556 4332 1734 -1 + 9228 4556 4049 4332 -1 + 9229 4556 4154 4049 -1 + 9230 1121 1735 4745 -1 + 9231 4580 4547 1044 -1 + 9232 4215 4394 4734 -1 + 9233 4725 955 4712 -1 + 9234 4665 4614 3269 -1 + 9235 4691 4057 3932 -1 + 9236 3932 4057 1653 -1 + 9237 4667 1668 1665 -1 + 9238 1665 1668 1664 -1 + 9239 4666 2895 4622 -1 + 9240 1670 1694 4562 -1 + 9241 1694 1692 4562 -1 + 9242 1694 1690 1692 -1 + 9243 582 4143 1200 -1 + 9244 4563 4058 4496 -1 + 9245 1595 4058 4563 -1 + 9246 3287 4625 3286 -1 + 9247 4691 4247 4057 -1 + 9248 4564 3092 4385 -1 + 9249 3931 4540 4693 -1 + 9250 3486 4506 4494 -1 + 9251 4690 4676 4262 -1 + 9252 4629 1746 4501 -1 + 9253 4617 1717 4509 -1 + 9254 3389 3081 4764 -1 + 9255 4571 4313 1585 -1 + 9256 4765 4497 4091 -1 + 9257 963 4289 1719 -1 + 9258 4720 3868 3860 -1 + 9259 3050 4249 3051 -1 + 9260 4573 3051 4565 -1 + 9261 4249 4565 3051 -1 + 9262 4645 3988 4643 -1 + 9263 4575 4552 4310 -1 + 9264 4575 4310 4513 -1 + 9265 4606 590 4507 -1 + 9266 4645 304 4219 -1 + 9267 4605 4304 4600 -1 + 9268 4580 1632 4082 -1 + 9269 4583 3518 3501 -1 + 9270 3501 3518 3528 -1 + 9271 4583 2784 4466 -1 + 9272 4757 4461 1736 -1 + 9273 4590 4568 1142 -1 + 9274 4583 4466 3518 -1 + 9275 4174 4200 3528 -1 + 9276 3528 4211 4174 -1 + 9277 4722 4264 2785 -1 + 9278 3514 4264 4722 -1 + 9279 4587 313 4423 -1 + 9280 4328 4423 313 -1 + 9281 4588 4546 3456 -1 + 9282 4588 3456 3529 -1 + 9283 4592 4553 1656 -1 + 9284 4626 3932 4315 -1 + 9285 4626 1241 4357 -1 + 9286 4614 4072 3269 -1 + 9287 1656 1660 4592 -1 + 9288 4592 1660 4522 -1 + 9289 4692 3118 3131 -1 + 9290 3131 3699 4692 -1 + 9291 3220 2833 2830 -1 + 9292 3206 2830 2831 -1 + 9293 2819 3211 4595 -1 + 9294 2819 4070 3211 -1 + 9295 4005 4044 3133 -1 + 9296 3041 4300 3133 -1 + 9297 3086 3044 3045 -1 + 9298 3041 4527 4278 -1 + 9299 4669 3132 4081 -1 + 9300 3133 4081 3132 -1 + 9301 3002 3001 4596 -1 + 9302 4669 4377 3132 -1 + 9303 3078 3002 4596 -1 + 9304 3078 3085 3002 -1 + 9305 4669 4081 4627 -1 + 9306 2289 582 4672 -1 + 9307 2289 4143 582 -1 + 9308 4669 4627 4631 -1 + 9309 4659 2919 4576 -1 + 9310 4662 4509 4204 -1 + 9311 4659 4622 2919 -1 + 9312 1160 1178 4601 -1 + 9313 1160 3085 1178 -1 + 9314 4661 1608 4384 -1 + 9315 4662 1270 4617 -1 + 9316 4684 1639 1638 -1 + 9317 4679 1746 4629 -1 + 9318 4607 3064 4178 -1 + 9319 4616 1606 4570 -1 + 9320 1303 1305 4608 -1 + 9321 1303 3145 1305 -1 + 9322 4640 4364 4332 -1 + 9323 1087 1085 967 -1 + 9324 3940 4438 3931 -1 + 9325 1266 4579 4049 -1 + 9326 4767 4279 4604 -1 + 9327 2784 4583 3500 -1 + 9328 2280 824 4746 -1 + 9329 4332 4557 4640 -1 + 9330 4332 4579 4557 -1 + 9331 4617 1702 1717 -1 + 9332 1719 4043 963 -1 + 9333 4642 1771 1772 -1 + 9334 4642 1772 1768 -1 + 9335 3282 3281 3781 -1 + 9336 4618 3095 3295 -1 + 9337 3296 3295 3095 -1 + 9338 3095 4451 3296 -1 + 9339 4620 1595 4563 -1 + 9340 1596 1595 4620 -1 + 9341 3988 2813 4643 -1 + 9342 4646 2785 4588 -1 + 9343 4494 4324 4735 -1 + 9344 4745 4740 1121 -1 + 9345 4625 3940 4266 -1 + 9346 2038 2042 2041 -1 + 9347 4632 2763 4483 -1 + 9348 4736 4453 4259 -1 + 9349 3222 3226 4653 -1 + 9350 4654 968 1101 -1 + 9351 4654 1101 4397 -1 + 9352 3852 3264 4713 -1 + 9353 4734 4623 4499 -1 + 9354 4641 4575 4513 -1 + 9355 1155 1565 4680 -1 + 9356 4661 4384 4390 -1 + 9357 4661 1630 1608 -1 + 9358 1588 1742 4709 -1 + 9359 4372 4407 4320 -1 + 9360 4657 4535 4021 -1 + 9361 4657 4052 1317 -1 + 9362 4649 4630 3051 -1 + 9363 4377 4631 4630 -1 + 9364 3058 4443 3061 -1 + 9365 3061 4230 3058 -1 + 9366 4649 4071 4630 -1 + 9367 4377 4630 4071 -1 + 9368 4178 4071 4649 -1 + 9369 4728 4261 4029 -1 + 9370 4728 4251 4261 -1 + 9371 4688 1109 4487 -1 + 9372 3286 4228 4691 -1 + 9373 3286 4266 4228 -1 + 9374 4676 4610 4262 -1 + 9375 4676 4545 4610 -1 + 9376 4658 4601 1159 -1 + 9377 4658 1170 4601 -1 + 9378 4766 4497 4765 -1 + 9379 2576 1194 2577 -1 + 9380 4664 4558 149 -1 + 9381 4664 149 185 -1 + 9382 1911 4220 1042 -1 + 9383 1042 4220 1075 -1 + 9384 1714 4204 1717 -1 + 9385 4668 3080 4596 -1 + 9386 4668 4596 3001 -1 + 9387 725 197 4702 -1 + 9388 725 4281 197 -1 + 9389 4702 84 725 -1 + 9390 4674 4279 4386 -1 + 9391 4678 1604 1606 -1 + 9392 962 957 4675 -1 + 9393 4675 968 4654 -1 + 9394 957 968 4675 -1 + 9395 4679 4647 1729 -1 + 9396 4679 1729 1746 -1 + 9397 4679 4629 1118 -1 + 9398 1407 4660 1134 -1 + 9399 4680 1149 1155 -1 + 9400 4681 1186 1918 -1 + 9401 4681 4603 1186 -1 + 9402 4684 4613 1654 -1 + 9403 4684 4553 4613 -1 + 9404 4284 4727 4270 -1 + 9405 4498 4636 642 -1 + 9406 4685 4069 4009 -1 + 9407 1712 4155 1709 -1 + 9408 1709 1708 1712 -1 + 9409 642 4636 4374 -1 + 9410 4686 1170 4658 -1 + 9411 1180 1170 4686 -1 + 9412 4121 4131 4050 -1 + 9413 3932 3286 4691 -1 + 9414 4376 4352 4694 -1 + 9415 4376 4611 4352 -1 + 9416 4352 4320 4694 -1 + 9417 3372 3387 3365 -1 + 9418 3388 3387 3372 -1 + 9419 1685 1686 1237 -1 + 9420 4695 1237 4027 -1 + 9421 4695 4655 1689 -1 + 9422 1689 4655 4650 -1 + 9423 4697 4240 1649 -1 + 9424 4698 2813 2822 -1 + 9425 4698 303 2813 -1 + 9426 4698 2822 2814 -1 + 9427 2814 303 4698 -1 + 9428 4699 3762 3852 -1 + 9429 4700 1115 4688 -1 + 9430 4701 3080 4668 -1 + 9431 1264 1265 4711 -1 + 9432 4717 1127 1179 -1 + 9433 4717 1159 1127 -1 + 9434 3001 2995 4701 -1 + 9435 4701 4668 3001 -1 + 9436 4727 1734 4670 -1 + 9437 3995 2904 4761 -1 + 9438 4702 201 4414 -1 + 9439 4132 4414 201 -1 + 9440 4705 4695 1689 -1 + 9441 4597 4644 4324 -1 + 9442 4324 4644 4604 -1 + 9443 4706 3092 4564 -1 + 9444 4706 4427 3092 -1 + 9445 4494 4597 4324 -1 + 9446 4708 1115 4700 -1 + 9447 1118 1115 4708 -1 + 9448 1177 1117 1176 -1 + 9449 4407 4694 4320 -1 + 9450 4716 4711 4403 -1 + 9451 4708 4459 4393 -1 + 9452 4708 4393 1121 -1 + 9453 4727 4670 4270 -1 + 9454 4710 3080 4701 -1 + 9455 4710 3073 3080 -1 + 9456 4710 2790 3073 -1 + 9457 4727 4556 1734 -1 + 9458 3502 3572 4369 -1 + 9459 3545 3572 3502 -1 + 9460 4131 4185 4047 -1 + 9461 4730 2994 2993 -1 + 9462 4741 1589 4719 -1 + 9463 4704 4743 3064 -1 + 9464 1741 4417 1587 -1 + 9465 4741 4719 4571 -1 + 9466 4589 4660 4602 -1 + 9467 4647 1120 4715 -1 + 9468 4766 1739 4772 -1 + 9469 1120 1121 4740 -1 + 9470 823 4663 4624 -1 + 9471 4749 4747 4191 -1 + 9472 4191 4747 4742 -1 + 9473 4742 1589 4741 -1 + 9474 4733 4052 4657 -1 + 9475 2580 1070 1072 -1 + 9476 4726 4710 2995 -1 + 9477 2995 4710 4701 -1 + 9478 3605 3072 4755 -1 + 9479 4732 4412 4721 -1 + 9480 4730 3894 4687 -1 + 9481 4491 4687 3894 -1 + 9482 1128 1398 1400 -1 + 9483 4766 4223 4497 -1 + 9484 4759 4685 4009 -1 + 9485 4009 3882 4759 -1 + 9486 4734 4313 4623 -1 + 9487 4394 4313 4734 -1 + 9488 4742 4737 4145 -1 + 9489 1169 1161 4773 -1 + 9490 1747 1128 4770 -1 + 9491 4756 1114 4771 -1 + 9492 4740 1729 4715 -1 + 9493 4116 4215 4744 -1 + 9494 3081 4738 4764 -1 + 9495 3081 4743 4738 -1 + 9496 1730 4406 4399 -1 + 9497 4399 4707 1730 -1 + 9498 4364 4640 1765 -1 + 9499 4078 1131 4762 -1 + 9500 4078 4432 1131 -1 + 9501 4191 4742 4145 -1 + 9502 4749 4191 4152 -1 + 9503 1131 1019 4762 -1 + 9504 1131 4432 1122 -1 + 9505 4757 4389 4461 -1 + 9506 4755 4752 3071 -1 + 9507 3071 4752 2995 -1 + 9508 2995 4752 4726 -1 + 9509 4758 1264 4750 -1 + 9510 4757 4756 4095 -1 + 9511 4095 4756 4177 -1 + 9512 4757 4095 4389 -1 + 9513 1739 1737 4772 -1 + 9514 1739 4768 1737 -1 + 9515 4154 4556 4760 -1 + 9516 4556 4284 4760 -1 + 9517 4556 4727 4284 -1 + 9518 3882 3985 4761 -1 + 9519 4761 4759 3882 -1 + 9520 4728 4029 4762 -1 + 9521 4762 4731 4728 -1 + 9522 4728 4731 4729 -1 + 9523 1122 1093 1131 -1 + 9524 4765 4189 1747 -1 + 9525 1747 4189 4040 -1 + 9526 4765 4091 4189 -1 + 9527 4767 4386 4279 -1 + 9528 4767 3446 4386 -1 + 9529 2667 3446 3483 -1 + 9530 3483 4706 4564 -1 + 9531 4017 4098 4774 -1 + 9532 4774 1030 4758 -1 + 9533 4774 4760 1030 -1 + 9534 1030 4760 1027 -1 + 9535 4297 4750 4716 -1 + 9536 4772 4173 4223 -1 + 9537 1390 1774 4773 -1 + 9538 4773 1161 4769 -1 + 9539 1180 4769 1161 -1 + 9540 4773 4769 1390 -1 + 9541 4739 4769 1180 -1 + 9542 4774 4154 4760 -1 + 9543 4098 4154 4774 -1 + 9544 4297 4716 4403 -1 +# Generated by tetgen octopus.ele diff --git a/examples/testbed/data/octopus.node b/examples/testbed/data/octopus.node new file mode 100644 index 0000000..8d17cd2 --- /dev/null +++ b/examples/testbed/data/octopus.node @@ -0,0 +1,4776 @@ +4774 3 0 0 + 1 0.12928913533687592 -2.3292934894561768 4.8082275390625 + 2 0.22848351299762726 -2.5150847434997559 5.1533470153808594 + 3 0.15359748899936676 -3.5371599197387695 6.6278963088989258 + 4 0.21191030740737915 -2.7872929573059082 5.5216031074523926 + 5 7.4534802436828613 -4.1323933601379395 -0.012706581503152847 + 6 0.090939536690711975 -3.3597564697265625 6.2897367477416992 + 7 0.0794353187084198 -3.601933479309082 6.6720685958862305 + 8 5.733891487121582 -4.5503382682800293 -5.6988787651062012 + 9 5.9305686950683594 -4.7203531265258789 -5.9104099273681641 + 10 0.12088794261217117 -2.6031010150909424 5.181243896484375 + 11 0.13141778111457825 -3.7876529693603516 6.9895343780517578 + 12 0 -4.7214765548706055 8.3730478286743164 + 13 0.30043706297874451 -0.77314436435699463 -3.0958561897277832 + 14 0.15556269884109497 -1.1804682016372681 -3.4687802791595459 + 15 0.29069599509239197 -1.0763047933578491 -3.4575517177581787 + 16 5.9110593795776367 -4.7203531265258789 -5.9299201965332031 + 17 5.7539796829223633 -4.5885286331176758 -5.7562785148620605 + 18 5.9297690391540527 -4.7310037612915039 -5.9288845062255859 + 19 7.053131103515625 -3.8475000858306885 0.04787827655673027 + 20 0.058089233934879303 -4.5062036514282227 8.0269784927368164 + 21 0.33243417739868164 0.16106957197189331 -2.0188655853271484 + 22 0.32063156366348267 -0.15193744003772736 -2.3741934299468994 + 23 0.17163762450218201 -0.26006120443344116 -2.3804967403411865 + 24 0.17775309085845947 0.055172242224216461 -2.0221281051635742 + 25 0.067227303981781006 -3.847501277923584 7.033785343170166 + 26 0 -4.0943899154663086 7.3812389373779297 + 27 0.11166942864656448 -2.8716044425964355 5.5515551567077637 + 28 0.40423643589019775 -0.40400749444961548 -2.9007570743560791 + 29 0.31020811200141907 -0.46494585275650024 -2.7345607280731201 + 30 0.16614249348640442 -0.57239788770675659 -2.7422559261322021 + 31 0 -1.4946216344833374 -3.816291332244873 + 32 0 -1.198735237121582 -3.4524121284484863 + 33 0.18492360413074493 0.36625471711158752 -1.6730034351348877 + 34 0.34635475277900696 0.46977710723876953 -1.6723108291625977 + 35 0.16088935732841492 -0.8792494535446167 -3.1051433086395264 + 36 0.10166426002979279 -3.1283869743347168 5.9190397262573242 + 37 0.17444801330566406 -3.2905640602111816 6.2500824928283691 + 38 0.25050902366638184 -3.0152220726013184 6.0076909065246582 + 39 0.1499076634645462 -1.4759331941604614 -3.8327596187591553 + 40 7.7417998313903809 -4.3237524032592773 -0.049269165843725204 + 41 7.3995852470397949 -4.088407039642334 -0.068113408982753754 + 42 0.19385586678981781 -3.0510354042053223 5.8870100975036621 + 43 0.083579860627651215 -4.273777961730957 7.6846208572387695 + 44 0.054388120770454407 -4.0884079933166504 7.3858790397644043 + 45 0.28047811985015869 -1.3743467330932617 -3.8192181587219238 + 46 0 -2.884960412979126 5.5411190986633301 + 47 0.040964014828205109 -4.323753833770752 7.7335186004638672 + 48 0.14372867345809937 -1.7657732963562012 -4.1964893341064453 + 49 0.026993326842784882 -4.5503401756286621 8.0788030624389648 + 50 0 -4.5531744956970215 8.0765905380249023 + 51 8.3732624053955078 -4.7203545570373535 0.014443694613873959 + 52 0 -2.6178488731384277 5.1696228981018066 + 53 8.1373386383056641 -4.5885295867919922 -0.0023008158896118402 + 54 0 -0.27503973245620728 -2.3662841320037842 + 55 0.41766586899757385 -0.089639224112033844 -2.5453078746795654 + 56 0.37174102663993835 0.70892167091369629 -1.3684937953948975 + 57 8.0311183929443359 -4.5062026977539063 0.053932595998048782 + 58 8.081944465637207 -4.5503392219543457 -0.030153879895806313 + 59 7.79583740234375 -4.3656406402587891 -0.0073484005406498909 + 60 7.3995852470397949 -4.088407039642334 0.040662754327058792 + 61 8.081944465637207 -4.5503392219543457 0.02383284829556942 + 62 7.7417998313903809 -4.3237524032592773 0.032658863812685013 + 63 0.29635533690452576 -2.4722685813903809 5.2852649688720703 + 64 2.54923415184021 -1.4759334325790405 -2.8389244079589844 + 65 2.815988302230835 -1.7657734155654907 -3.0896031856536865 + 66 2.4454989433288574 -1.3743468523025513 -2.9224364757537842 + 67 2.7140092849731445 -1.667242169380188 -3.1678256988525391 + 68 3.019254207611084 -1.7657734155654907 -2.8863375186920166 + 69 2.8421578407287598 -1.3743470907211304 -2.5257844924926758 + 70 2.9530956745147705 -1.8273136615753174 -3.0218956470489502 + 71 4.775327205657959 -3.649594783782959 -4.7993483543395996 + 72 4.5090866088867188 -3.4062826633453369 -4.5389184951782227 + 73 4.7918243408203125 -3.6019315719604492 -4.7046365737915039 + 74 3.0949854850769043 -1.6672422885894775 -2.7868499755859375 + 75 0 -4.3281698226928711 -7.7465124130249023 + 76 0.026993401348590851 -4.5503382682800293 -8.0850858688354492 + 77 3.3461925983428955 -1.955012321472168 -3.0481150150299072 + 78 4.3191771507263184 -3.2905621528625488 -4.5985026359558105 + 79 3.2559134960174561 -1.6156532764434814 -2.8342342376708984 + 80 4.4050941467285156 -3.3597545623779297 -4.5646452903747559 + 81 3.0824289321899414 -2.0501956939697266 -3.3392043113708496 + 82 0.040964014828205109 -4.3237514495849609 -7.7501530647277832 + 83 4.814117431640625 -3.5371580123901367 -4.623593807220459 + 84 0.247362419962883 0.4328511655330658 1.253116250038147 + 85 0.39043432474136353 0.20794177055358887 1.6945970058441162 + 86 5.0252494812011719 -3.8935565948486328 -5.0435171127319336 + 87 2.4277169704437256 -1.2441211938858032 -2.511314868927002 + 88 2.0161886215209961 -0.87924957275390625 -2.3366529941558838 + 89 1.9090272188186646 -0.77314454317092896 -2.4295585155487061 + 90 2.2437248229980469 -0.87924963235855103 -2.1091165542602539 + 91 0.19385586678981781 -3.0510332584381104 -5.9638409614562988 + 92 2.7605230808258057 -1.0195832252502441 -2.3142096996307373 + 93 2.7612297534942627 -1.4759334325790405 -2.6269283294677734 + 94 2.5025346279144287 -1.1804683208465576 -2.3677546977996826 + 95 2.5882666110992432 -1.0763049125671387 -2.2650406360626221 + 96 2.282536506652832 -1.1804683208465576 -2.5877599716186523 + 97 2.1771595478057861 -1.0763047933578491 -2.6761476993560791 + 98 0.10166426002979279 -3.1283848285675049 -5.9921159744262695 + 99 4.6794795989990234 -3.6019315719604492 -4.8169732093811035 + 100 2.6904885768890381 -1.5387157201766968 -2.7665908336639404 + 101 3.5188844203948975 -2.5150833129882813 -3.8935935497283936 + 102 3.6131467819213867 -2.6030995845794678 -3.8335168361663818 + 103 3.4760258197784424 -2.3872346878051758 -3.5307846069335938 + 104 0 -3.8550090789794922 -7.0663375854492188 + 105 0.067227303981781006 -3.8474991321563721 -7.0724835395812988 + 106 0.17444801330566406 -3.2905619144439697 -6.3153071403503418 + 107 0.22471728920936584 -3.279789924621582 -6.4201488494873047 + 108 3.9928522109985352 -2.9257194995880127 -4.0343270301818848 + 109 4.1393389701843262 -3.128385066986084 -4.3196554183959961 + 110 4.0347690582275391 -2.87160325050354 -3.9196414947509766 + 111 4.2831206321716309 -3.128385066986084 -4.1758737564086914 + 112 3.784111499786377 -2.6030995845794678 -3.6625595092773438 + 113 4.0859298706054688 -2.7872915267944336 -3.8311111927032471 + 114 4.2210955619812012 -2.7475948333740234 -3.8788740634918213 + 115 3.8420038223266602 -2.5150833129882813 -3.5704669952392578 + 116 0 -3.6109471321105957 -6.7149996757507324 + 117 0.0794353187084198 -3.6019313335418701 -6.7223806381225586 + 118 3.8768460750579834 -2.87160325050354 -4.0775713920593262 + 119 3.7353560924530029 -2.6591429710388184 -3.783376932144165 + 120 4.098721981048584 -3.0152201652526855 -4.4922833442687988 + 121 3.8330309391021729 -2.7475945949554443 -4.2669382095336914 + 122 3.9840364456176758 -2.4722671508789063 -3.6175732612609863 + 123 3.786245584487915 -2.7872912883758545 -4.1308021545410156 + 124 3.56492018699646 -2.4722671508789063 -4.0366826057434082 + 125 3.0282199382781982 -1.9062427282333374 -3.567896842956543 + 126 2.9825344085693359 -1.955012321472168 -3.4117732048034668 + 127 0.25050902366638184 -3.0152199268341064 -6.0862717628479004 + 128 4.2487063407897949 -3.1773614883422852 -4.2840490341186523 + 129 4.5336995124816895 -3.3597545623779297 -4.436039924621582 + 130 3.5010883808135986 -1.9062427282333374 -3.0950286388397217 + 131 4.5658864974975586 -3.2905621528625488 -4.3517928123474121 + 132 4.0528359413146973 -3.0510334968566895 -4.3654055595397949 + 133 3.2508950233459473 -2.237642765045166 -3.6538147926330566 + 134 4.3269863128662109 -3.0510334968566895 -4.0912480354309082 + 135 0.090939536690711975 -3.3597543239593506 -6.3516273498535156 + 136 3.276015043258667 -2.0501956939697266 -3.1456184387207031 + 137 3.5310983657836914 -2.3292922973632813 -3.4044563770294189 + 138 3.3482556343078613 -2.3292922973632813 -3.587299108505249 + 139 3.2151389122009277 -2.1100010871887207 -3.2768290042877197 + 140 3.5953226089477539 -2.237642765045166 -3.3093874454498291 + 141 0.083579860627651215 -4.273775577545166 -7.7035384178161621 + 142 0.054388120770454407 -4.0884056091308594 -7.413362979888916 + 143 5.4996380805969238 -4.2737760543823242 -5.3908853530883789 + 144 5.7199358940124512 -4.5062017440795898 -5.6419429779052734 + 145 5.4453363418579102 -4.3237519264221191 -5.5115742683410645 + 146 5.6957154273986816 -4.5503382682800293 -5.7370471954345703 + 147 5.5125160217285156 -4.3656396865844727 -5.5198616981506348 + 148 0 -2.0676131248474121 -4.5443716049194336 + 149 1.5242108106613159 0.70710855722427368 -0.97397637367248535 + 150 0.24354764819145203 -2.2376425266265869 -4.8994998931884766 + 151 0.33436727523803711 -1.9062424898147583 -4.6837277412414551 + 152 0.31632855534553528 -2.1917994022369385 -5.0380206108093262 + 153 0.2693907618522644 -1.6672433614730835 4.0347332954406738 + 154 0 -1.4946228265762329 3.6618444919586182 + 155 0.14372867345809937 -1.7657746076583862 4.0557842254638672 + 156 0.22848351299762726 -2.5150830745697021 -5.2565193176269531 + 157 0.12928913533687592 -2.3292920589447021 -4.9206366539001465 + 158 0.39148008823394775 -0.72436171770095825 3.0698797702789307 + 159 0.37875664234161377 -1.020455002784729 3.4364428520202637 + 160 0.29069244861602783 -1.0771338939666748 3.2823250293731689 + 161 0.15556196868419647 -1.1807905435562134 3.2985436916351318 + 162 0.28047811985015869 -1.3743479251861572 3.6586611270904541 + 163 0.1499076634645462 -1.4759345054626465 3.6773707866668701 + 164 1.424059271812439 -0.08904680609703064 -2.1279976367950439 + 165 0.5951048731803894 0.73868811130523682 -1.2571135759353638 + 166 0.63396912813186646 0.81479769945144653 -1.4549254179000854 + 167 0.31632855534553528 -2.1918010711669922 4.9187440872192383 + 168 0.33436727523803711 -1.9062440395355225 4.55010986328125 + 169 0.35060638189315796 -1.6156529188156128 -4.3281073570251465 + 170 0.25714558362960815 -1.9550119638442993 -4.5406670570373535 + 171 0.83568435907363892 0.49771460890769958 -1.4510635137557983 + 172 0.80409699678421021 0.72967231273651123 -0.91349273920059204 + 173 0.2693907618522644 -1.6672420501708984 -4.1804208755493164 + 174 0.13688650727272034 -2.0501954555511475 -4.5592913627624512 + 175 0 -1.7842260599136353 -4.1804637908935547 + 176 0.24354764819145203 -2.2376441955566406 4.7825145721435547 + 177 0.13688650727272034 -2.0501968860626221 4.4329123497009277 + 178 0.95583063364028931 0.40196093916893005 -1.3331090211868286 + 179 1.1048730611801147 0.16865263879299164 -1.69384765625 + 180 1.5756072998046875 0.20482635498046875 -1.2096432447433472 + 181 1.2177953720092773 0.44115632772445679 -1.0573670864105225 + 182 1.1230117082595825 0.34768986701965332 -1.2320008277893066 + 183 1.4716581106185913 0.071751043200492859 -1.3303248882293701 + 184 1.122133731842041 0.89904493093490601 -0.6831439733505249 + 185 1.3255065679550171 0.60455054044723511 -0.92761260271072388 + 186 0.25714558362960815 -1.9550135135650635 4.4095058441162109 + 187 1.9578109979629517 -0.71420282125473022 -2.608689546585083 + 188 1.9031096696853638 -0.6370004415512085 -2.0022547245025635 + 189 2.1651527881622314 -0.94350934028625488 -2.2564387321472168 + 190 1.75030517578125 -0.57239800691604614 -2.086073637008667 + 191 0.11166942864656448 -2.8716027736663818 -5.637153148651123 + 192 0.21191030740737915 -2.7872912883758545 -5.6113400459289551 + 193 0.12088794261217117 -2.6030993461608887 -5.2800617218017578 + 194 0 -2.617847204208374 -5.2677125930786133 + 195 4.9397811889648438 -3.8474993705749512 -5.0542030334472656 + 196 5.0348577499389648 -3.8474993705749512 -4.9591269493103027 + 197 0 0.17983153462409973 1.5404220819473267 + 198 2.5115218162536621 -0.714202880859375 -2.0549788475036621 + 199 2.3339042663574219 -0.77314460277557373 -2.0046746730804443 + 200 0.36198073625564575 -0.030953029170632362 1.9737652540206909 + 201 0.21200595796108246 0.16691389679908752 1.6249692440032959 + 202 0.19385729730129242 -0.096714109182357788 1.9296927452087402 + 203 5.5032715797424316 -4.3237519264221191 -5.453639030456543 + 204 0 -0.62140166759490967 2.5483057498931885 + 205 0.16087506711483002 -0.88580352067947388 2.9236352443695068 + 206 5.2707738876342773 -4.0884060859680176 -5.207585334777832 + 207 0.30039352178573608 -0.78412425518035889 2.9118642807006836 + 208 1.7276835441589355 -0.25898915529251099 -1.5932319164276123 + 209 1.381678581237793 -0.0020965307485312223 -1.4945437908172607 + 210 1.2201151847839355 0.062228258699178696 -1.5862150192260742 + 211 1.4849282503128052 -0.25984856486320496 -1.8364083766937256 + 212 1.7696287631988525 0.2801114022731781 -1.2594407796859741 + 213 2.0149037837982178 -0.081183254718780518 -1.5333693027496338 + 214 1.8266143798828125 -0.14602982997894287 -1.4817516803741455 + 215 5.2703962326049805 -4.132392406463623 -5.2831029891967773 + 216 2.0797491073608398 -0.46460551023483276 -1.7444518804550171 + 217 1.9852653741836548 -0.57238733768463135 -1.8511065244674683 + 218 0.42801940441131592 -0.21408291161060333 2.4220418930053711 + 219 0.33040830492973328 -0.26805838942527771 2.2500283718109131 + 220 0.31137880682945251 -0.51454639434814453 2.5632894039154053 + 221 2.2626490592956543 -0.40348798036575317 -1.7958550453186035 + 222 0.17514044046401978 -0.3499157726764679 2.2321822643280029 + 223 0.16632382571697235 -0.60790443420410156 2.5651237964630127 + 224 1.3730553388595581 -0.15176331996917725 -1.9381375312805176 + 225 1.6410378217697144 -0.46494591236114502 -2.1833128929138184 + 226 5.1938576698303223 -4.0884060859680176 -5.2845025062561035 + 227 5.1234736442565918 -4.0342254638671875 -5.2912545204162598 + 228 1.6418373584747314 -0.32455170154571533 -1.7489131689071655 + 229 0 -0.35467422008514404 2.2044494152069092 + 230 -6.6972246170043945 -3.6019322872161865 -0.10458944737911224 + 231 -7.053131103515625 -3.8475000858306885 -0.086576469242572784 + 232 -6.3206820487976074 -3.3597555160522461 0.059993423521518707 + 233 -6.282691478729248 -3.2905631065368652 0.14183646440505981 + 234 -6.7533326148986816 -3.6495954990386963 -0.024022066965699196 + 235 -5.9254293441772461 -3.0510342121124268 0.15544012188911438 + 236 -6.6545944213867188 -3.5371589660644531 -0.1802942305803299 + 237 -6.3206820487976074 -3.3597555160522461 -0.12188500910997391 + 238 -6.6972246170043945 -3.6019322872161865 0.054280743002891541 + 239 -6.3768110275268555 -3.4062836170196533 -0.029828323051333427 + 240 -7.7417998313903809 -4.3237524032592773 0.032658863812685013 + 241 -7.79583740234375 -4.3656406402587891 -0.0073484005406498909 + 242 -7.7417998313903809 -4.3237524032592773 -0.049269165843725204 + 243 -8.0311183929443359 -4.5062026977539063 0.053932595998048782 + 244 -8.081944465637207 -4.5503392219543457 -0.030153879895806313 + 245 -7.1067757606506348 -3.8935573101043701 -0.018268350511789322 + 246 -7.3995852470397949 -4.088407039642334 0.040662754327058792 + 247 -7.3995852470397949 -4.088407039642334 -0.068113408982753754 + 248 -7.4534802436828613 -4.1323933601379395 -0.012706581503152847 + 249 -7.053131103515625 -3.8475000858306885 0.04787827655673027 + 250 -5.230649471282959 -2.6031002998352051 -0.17029605805873871 + 251 -4.9783782958984375 -2.1918003559112549 0.25668910145759583 + 252 -5.5664749145507813 -2.7872922420501709 -0.25677838921546936 + 253 -5.5943574905395508 -2.8716037273406982 -0.1544681191444397 + 254 -5.230649471282959 -2.6031002998352051 0.071479856967926025 + 255 -5.5943574905395508 -2.8716037273406982 0.06887044757604599 + 256 -5.282588005065918 -2.6591436862945557 -0.04802381619811058 + 257 -5.2049369812011719 -2.5150837898254395 -0.28007093071937561 + 258 -5.2049369812011719 -2.5150837898254395 0.17689606547355652 + 259 -4.8644356727600098 -2.3292927742004395 0.073083184659481049 + 260 -4.9158391952514648 -2.387235164642334 -0.054762993007898331 + 261 -6.282691478729248 -3.2905631065368652 -0.2070595771074295 + 262 -5.9555745124816895 -3.1283857822418213 0.065128885209560394 + 263 -5.9555745124816895 -3.1283857822418213 -0.13819977641105652 + 264 -6.0085768699645996 -3.1773624420166016 -0.035347979515790939 + 265 -5.9254293441772461 -3.0510342121124268 -0.23227162659168243 + 266 -5.5664749145507813 -2.7872922420501709 0.16704292595386505 + 267 -5.6467466354370117 -2.9257199764251709 -0.041473820805549622 + 268 -1.9090272188186646 -0.77314454317092896 -2.4295585155487061 + 269 -1.75030517578125 -0.57239800691604614 -2.086073637008667 + 270 -1.6909564733505249 -0.40400755405426025 -2.3677759170532227 + 271 -1.9852653741836548 -0.57238733768463135 -1.8511065244674683 + 272 -2.2437248229980469 -0.87924963235855103 -2.1091165542602539 + 273 -1.9031096696853638 -0.6370004415512085 -2.0022547245025635 + 274 -2.1651527881622314 -0.94350934028625488 -2.2564387321472168 + 275 -1.4849282503128052 -0.25984856486320496 -1.8364083766937256 + 276 -1.6410378217697144 -0.46494591236114502 -2.1833128929138184 + 277 -1.7276835441589355 -0.25898915529251099 -1.5932319164276123 + 278 -2.0797491073608398 -0.46460551023483276 -1.7444518804550171 + 279 -2.0149037837982178 -0.081183254718780518 -1.5333693027496338 + 280 -1.6418373584747314 -0.32455170154571533 -1.7489131689071655 + 281 -2.2248725891113281 -1.0195831060409546 -2.8498604297637939 + 282 -2.4454989433288574 -1.3743468523025513 -2.9224364757537842 + 283 -2.5882666110992432 -1.0763049125671387 -2.2650406360626221 + 284 -2.4277169704437256 -1.2441211938858032 -2.511314868927002 + 285 -2.54923415184021 -1.4759334325790405 -2.8389244079589844 + 286 -2.282536506652832 -1.1804683208465576 -2.5877599716186523 + 287 -2.0161886215209961 -0.87924957275390625 -2.3366529941558838 + 288 -2.1771595478057861 -1.0763047933578491 -2.6761476993560791 + 289 -2.3339042663574219 -0.77314460277557373 -2.0046746730804443 + 290 -2.5115218162536621 -0.714202880859375 -2.0549788475036621 + 291 -2.5025346279144287 -1.1804683208465576 -2.3677546977996826 + 292 -1.8266143798828125 -0.14602982997894287 -1.4817516803741455 + 293 -1.7696287631988525 0.2801114022731781 -1.2594407796859741 + 294 -1.2177953720092773 0.44115632772445679 -1.0573670864105225 + 295 -0.80409699678421021 0.72967231273651123 -0.91349273920059204 + 296 -0.83568435907363892 0.49771460890769958 -1.4510635137557983 + 297 -0.66567790508270264 0.703865647315979 -1.1194782257080078 + 298 -0.95583063364028931 0.40196093916893005 -1.3331090211868286 + 299 -1.3255065679550171 0.60455054044723511 -0.92761260271072388 + 300 -8.3859682083129883 -4.7310047149658203 0.00088632421102374792 + 301 -8.1373386383056641 -4.5885295867919922 -0.0023008158896118402 + 302 -8.081944465637207 -4.5503392219543457 0.02383284829556942 + 303 -8.3593425750732422 -4.7075052261352539 0.032635245472192764 + 304 -8.3732624053955078 -4.7203545570373535 0.014443694613873959 + 305 -1.2201151847839355 0.062228258699178696 -1.5862150192260742 + 306 -1.1048730611801147 0.16865263879299164 -1.69384765625 + 307 -1.3730553388595581 -0.15176331996917725 -1.9381375312805176 + 308 -1.381678581237793 -0.0020965307485312223 -1.4945437908172607 + 309 -1.1230117082595825 0.34768986701965332 -1.2320008277893066 + 310 -1.5756072998046875 0.20482635498046875 -1.2096432447433472 + 311 -1.5242108106613159 0.70710855722427368 -0.97397637367248535 + 312 -1.4716581106185913 0.071751043200492859 -1.3303248882293701 + 313 -1.3696004152297974 1.0821444988250732 -0.54531300067901611 + 314 -1.5281940698623657 0.64085561037063599 -0.43099957704544067 + 315 -5.9110593795776367 -4.7203555107116699 5.931218147277832 + 316 -5.6957154273986816 -4.5503401756286621 5.7307291030883789 + 317 -5.6377878189086914 -4.5062036514282227 5.7157812118530273 + 318 -1.1768994331359863 0.80231964588165283 -0.31374287605285645 + 319 -1.5371884107589722 0.41687062382698059 0.10917248576879501 + 320 -1.1779131889343262 0.67153710126876831 0.18536059558391571 + 321 -1.1382594108581543 0.72663116455078125 -0.068224139511585236 + 322 -1.5371884107589722 0.47721084952354431 -0.27195480465888977 + 323 -5.7539796829223633 -4.5885310173034668 5.7516803741455078 + 324 -5.9305686950683594 -4.7203555107116699 5.9117083549499512 + 325 -5.733891487121582 -4.5503401756286621 5.6925530433654785 + 326 -1.8954142332077026 0.19112969934940338 0.24536441266536713 + 327 -1.5281940698623657 0.4941408634185791 0.28827044367790222 + 328 -1.705376148223877 0.56153297424316406 0.39845657348632813 + 329 -2.2721571922302246 -0.24005977809429169 -0.27075850963592529 + 330 -1.9545772075653076 0.026811672374606133 -0.098357744514942169 + 331 -2.2630844116210938 -0.12527371942996979 -0.42016935348510742 + 332 -1.705376148223877 0.74873167276382446 -0.53135746717453003 + 333 -1.2151968479156494 0.69306784868240356 0.34135156869888306 + 334 -1.3687508106231689 0.77012532949447632 0.44824475049972534 + 335 -1.9038233757019043 0.090541921555995941 0.08255266398191452 + 336 -1.904023289680481 0.10116890072822571 -0.27525636553764343 + 337 -1.8962638378143311 0.23680123686790466 -0.4280286431312561 + 338 -1.5894557237625122 0.37999656796455383 -0.086212441325187683 + 339 0.38436958193778992 0.75412106513977051 0.35205847024917603 + 340 -5.0348577499389648 -3.8475010395050049 4.9204287528991699 + 341 -4.775327205657959 -3.6495964527130127 4.7513055801391602 + 342 0 0.7148669958114624 0.49468424916267395 + 343 0.17547522485256195 0.75897514820098877 0.38534194231033325 + 344 -5.0499482154846191 -3.7876527309417725 4.8433413505554199 + 345 0.41361194849014282 0.88980770111083984 -0.27305689454078674 + 346 0.4165472686290741 0.81285578012466431 0.11093716323375702 + 347 0.78142541646957397 0.73252737522125244 0.28535020351409912 + 348 0.541298508644104 0.85442268848419189 -0.077582761645317078 + 349 -5.5032715797424316 -4.3237533569335938 5.4370336532592773 + 350 -5.2762703895568848 -4.0342273712158203 5.1084895133972168 + 351 -5.5125160217285156 -4.3656415939331055 5.5051627159118652 + 352 -5.1938576698303223 -4.0884075164794922 5.2570538520812988 + 353 -5.4453363418579102 -4.3237533569335938 5.494962215423584 + 354 -4.8640928268432617 -3.7876527309417725 5.0291962623596191 + 355 -4.9397811889648438 -3.8475010395050049 5.0155048370361328 + 356 -5.2703962326049805 -4.1323943138122559 5.2576889991760254 + 357 -5.0252494812011719 -3.8935580253601074 5.0069818496704102 + 358 -5.2707738876342773 -4.0884075164794922 5.1801304817199707 + 359 -3.8049197196960449 -1.5387160778045654 -0.076101928949356079 + 360 -3.7550652027130127 -1.4759337902069092 -0.22760356962680817 + 361 -4.4750866889953613 -1.9550127983093262 -0.32272630929946899 + 362 -4.2539534568786621 -1.6156535148620605 -0.4247574508190155 + 363 -3.7550652027130127 -1.4759337902069092 0.072211019694805145 + 364 -3.4333093166351318 -1.2441216707229614 -0.083597905933856964 + 365 -3.7389395236968994 -1.374347448348999 0.20019882917404175 + 366 -4.1075735092163086 -1.6672427654266357 -0.34223473072052002 + 367 -4.5468916893005371 -2.1100015640258789 -0.061687689274549484 + 368 -4.8410077095031738 -2.2376432418823242 -0.30204072594642639 + 369 -4.8644356727600098 -2.3292927742004395 -0.18549506366252899 + 370 -4.8410077095031738 -2.2376432418823242 0.18505387008190155 + 371 -4.4961018562316895 -2.0501961708068848 -0.2000744640827179 + 372 -4.1261329650878906 -1.7657740116119385 -0.21408066153526306 + 373 -4.1763086318969727 -1.8273141384124756 -0.06879812479019165 + 374 -4.1261329650878906 -1.7657740116119385 0.073376663029193878 + 375 -4.4750866889953613 -1.9550127983093262 0.19156557321548462 + 376 -4.1075735092163086 -1.6672427654266357 0.1965467780828476 + 377 -4.4961018562316895 -2.0501961708068848 0.073698565363883972 + 378 -3.7389395236968994 -1.374347448348999 -0.36075741052627563 + 379 -2.6312897205352783 -0.45541656017303467 0.21137914061546326 + 380 -2.6416831016540527 -0.56526070833206177 0.068870827555656433 + 381 -2.322376012802124 -0.30733129382133484 -0.098596945405006409 + 382 -3.0002732276916504 -0.77151727676391602 -0.39529216289520264 + 383 -2.2629630565643311 -0.13010793924331665 0.22238872945308685 + 384 -2.2721502780914307 -0.24047672748565674 0.072759442031383514 + 385 -2.6312825679779053 -0.45553568005561829 -0.40916767716407776 + 386 -2.6416831016540527 -0.56528842449188232 -0.26345744729042053 + 387 -3.3835549354553223 -1.1804686784744263 0.070340529084205627 + 388 -3.3835549354553223 -1.1804686784744263 -0.24078458547592163 + 389 -3.0620131492614746 -0.94303154945373535 -0.091046050190925598 + 390 -3.0002732276916504 -0.7714959979057312 0.20560410618782043 + 391 -3.3696637153625488 -1.0763052701950073 0.20281295478343964 + 392 -2.6915876865386963 -0.63152211904525757 -0.096446193754673004 + 393 -3.012251615524292 -0.87829345464706421 0.068433403968811035 + 394 -3.012251615524292 -0.87829339504241943 -0.25335094332695007 + 395 -3.1603586673736572 -0.7127642035484314 0.29508531093597412 + 396 -3.3696637153625488 -1.0763052701950073 -0.37857908010482788 + 397 -3.5252091884613037 -1.0195764303207397 -0.46809235215187073 + 398 -3.1603586673736572 -0.71279335021972656 -0.48800769448280334 + 399 -5.5125160217285156 -4.3656396865844727 -5.5198616981506348 + 400 -0.058089233934879303 -4.5062017440795898 -8.0352602005004883 + 401 -5.7539796829223633 -4.5885286331176758 -5.7562785148620605 + 402 -5.733891487121582 -4.5503382682800293 -5.6988787651062012 + 403 0 -4.5531725883483887 -8.0827302932739258 + 404 -0.50544238090515137 0.20618070662021637 1.9310276508331299 + 405 -0.39043432474136353 0.20794177055358887 1.6945970058441162 + 406 -0.026993401348590851 -4.5503382682800293 -8.0850858688354492 + 407 -0.013794656842947006 -4.7203526496887207 -8.37261962890625 + 408 -5.9110593795776367 -4.7203531265258789 -5.9299201965332031 + 409 -0.067227303981781006 -3.8474991321563721 -7.0724835395812988 + 410 -0.0794353187084198 -3.6019313335418701 -6.7223806381225586 + 411 0 -4.0943875312805176 -7.4084372520446777 + 412 -0.040964014828205109 -4.3237514495849609 -7.7501530647277832 + 413 -0.054388120770454407 -4.0884056091308594 -7.413362979888916 + 414 -0.10804598033428192 -4.0342249870300293 -7.3687477111816406 + 415 -5.5032715797424316 -4.3237519264221191 -5.453639030456543 + 416 -5.6957154273986816 -4.5503382682800293 -5.7370471954345703 + 417 -0.13141778111457825 -3.7876508235931396 -7.0310454368591309 + 418 -5.1938576698303223 -4.0884060859680176 -5.2845025062561035 + 419 -5.1234736442565918 -4.0342254638671875 -5.2912545204162598 + 420 -0.30039352178573608 -0.78412425518035889 2.9118642807006836 + 421 -5.0252494812011719 -3.8935565948486328 -5.0435171127319336 + 422 -5.2707738876342773 -4.0884060859680176 -5.207585334777832 + 423 -5.0348577499389648 -3.8474993705749512 -4.9591269493103027 + 424 -5.2703962326049805 -4.132392406463623 -5.2831029891967773 + 425 -0.29069244861602783 -1.0771338939666748 3.2823250293731689 + 426 -0.15556196868419647 -1.1807905435562134 3.2985436916351318 + 427 -0.36528435349464417 -1.3200820684432983 3.8082458972930908 + 428 -0.37875664234161377 -1.020455002784729 3.4364428520202637 + 429 -0.31137880682945251 -0.51454639434814453 2.5632894039154053 + 430 -0.16632382571697235 -0.60790443420410156 2.5651237964630127 + 431 -0.16087506711483002 -0.88580352067947388 2.9236352443695068 + 432 0 -0.90266436338424683 2.9081947803497314 + 433 -0.36198073625564575 -0.030953029170632362 1.9737652540206909 + 434 -0.21200595796108246 0.16691389679908752 1.6249692440032959 + 435 -5.4453363418579102 -4.3237519264221191 -5.5115742683410645 + 436 -0.42801940441131592 -0.21408291161060333 2.4220418930053711 + 437 -0.33040830492973328 -0.26805838942527771 2.2500283718109131 + 438 0 -0.093039244413375854 1.8724002838134766 + 439 -0.46483922004699707 0.0076111718080937862 2.1626615524291992 + 440 -0.19385729730129242 -0.096714109182357788 1.9296927452087402 + 441 -0.17514044046401978 -0.3499157726764679 2.2321822643280029 + 442 0 -0.58875834941864014 -2.7270441055297852 + 443 -0.13688650727272034 -2.0501954555511475 -4.5592913627624512 + 444 -0.40423643589019775 -0.40400749444961548 -2.9007570743560791 + 445 -0.32063156366348267 -0.15193744003772736 -2.3741934299468994 + 446 -0.41766586899757385 -0.089639224112033844 -2.5453078746795654 + 447 -0.17163762450218201 -0.26006120443344116 -2.3804967403411865 + 448 -0.35060638189315796 -1.6156529188156128 -4.3281073570251465 + 449 -0.25714558362960815 -1.9550119638442993 -4.5406670570373535 + 450 -0.39153221249580383 -0.71420270204544067 -3.2574622631072998 + 451 -0.31020811200141907 -0.46494585275650024 -2.7345607280731201 + 452 -0.16614249348640442 -0.57239788770675659 -2.7422559261322021 + 453 0 -0.89671701192855835 -3.0892107486724854 + 454 -0.15556269884109497 -1.1804682016372681 -3.4687802791595459 + 455 -0.30043706297874451 -0.77314436435699463 -3.0958561897277832 + 456 -0.16088935732841492 -0.8792494535446167 -3.1051433086395264 + 457 -0.36528503894805908 -1.3200807571411133 -3.9715657234191895 + 458 -0.2693907618522644 -1.6672420501708984 -4.1804208755493164 + 459 -0.14372867345809937 -1.7657732963562012 -4.1964893341064453 + 460 -0.28047811985015869 -1.3743467330932617 -3.8192181587219238 + 461 -0.29069599509239197 -1.0763047933578491 -3.4575517177581787 + 462 -0.1499076634645462 -1.4759331941604614 -3.8327596187591553 + 463 -0.33436727523803711 -1.9062424898147583 -4.6837277412414551 + 464 -0.34635475277900696 0.46977710723876953 -1.6723108291625977 + 465 -0.18492360413074493 0.36625471711158752 -1.6730034351348877 + 466 0 0.3540273904800415 -1.6621459722518921 + 467 -0.23304706811904907 0.65510749816894531 -1.3271197080612183 + 468 0 -2.8849589824676514 -5.6260671615600586 + 469 -0.37174102663993835 0.70892167091369629 -1.3684937953948975 + 470 -0.11166942864656448 -2.8716027736663818 -5.637153148651123 + 471 0 -3.3702406883239746 -6.3430113792419434 + 472 0 -3.1403203010559082 -5.9822654724121094 + 473 -0.10166426002979279 -3.1283848285675049 -5.9921159744262695 + 474 -0.090939536690711975 -3.3597543239593506 -6.3516273498535156 + 475 -0.17444801330566406 -3.2905619144439697 -6.3153071403503418 + 476 -0.19385586678981781 -3.0510332584381104 -5.9638409614562988 + 477 -0.12928913533687592 -2.3292920589447021 -4.9206366539001465 + 478 -0.22848351299762726 -2.5150830745697021 -5.2565193176269531 + 479 -0.31632855534553528 -2.1917994022369385 -5.0380206108093262 + 480 -0.33243417739868164 0.16106957197189331 -2.0188655853271484 + 481 -0.43274575471878052 0.22307582199573517 -2.1960403919219971 + 482 -0.12088794261217117 -2.6030993461608887 -5.2800617218017578 + 483 -0.24354764819145203 -2.2376425266265869 -4.8994998931884766 + 484 0 -2.3453962802886963 -4.9070096015930176 + 485 -0.21191030740737915 -2.7872912883758545 -5.6113400459289551 + 486 -0.27440261840820313 -2.7475943565368652 -5.7409663200378418 + 487 -0.17775309085845947 0.055172242224216461 -2.0221281051635742 + 488 0 0.041730150580406189 -2.0092573165893555 + 489 -3.8768460750579834 -2.87160325050354 -4.0775713920593262 + 490 -3.7353560924530029 -2.6591429710388184 -3.783376932144165 + 491 -3.56492018699646 -2.4722671508789063 -4.0366826057434082 + 492 -4.0347690582275391 -2.87160325050354 -3.9196414947509766 + 493 -3.8420038223266602 -2.5150833129882813 -3.5704669952392578 + 494 -3.784111499786377 -2.6030995845794678 -3.6625595092773438 + 495 -0.013794656842947006 -4.7203559875488281 8.3739748001098633 + 496 -0.067227303981781006 -3.847501277923584 7.033785343170166 + 497 0 -3.855010986328125 7.0279889106750488 + 498 -0.054388120770454407 -4.0884079933166504 7.3858790397644043 + 499 -4.0528359413146973 -3.0510334968566895 -4.3654055595397949 + 500 -3.9928522109985352 -2.9257194995880127 -4.0343270301818848 + 501 -4.1393389701843262 -3.128385066986084 -4.3196554183959961 + 502 -0.058089233934879303 -4.5062036514282227 8.0269784927368164 + 503 -0.026993326842784882 -4.5503401756286621 8.0788030624389648 + 504 -0.040964014828205109 -4.323753833770752 7.7335186004638672 + 505 0 -4.3281722068786621 7.7300925254821777 + 506 -4.0859298706054688 -2.7872915267944336 -3.8311111927032471 + 507 -4.098721981048584 -3.0152201652526855 -4.4922833442687988 + 508 -3.786245584487915 -2.7872912883758545 -4.1308021545410156 + 509 -3.6131467819213867 -2.6030995845794678 -3.8335168361663818 + 510 -3.3482556343078613 -2.3292922973632813 -3.587299108505249 + 511 -3.0949854850769043 -1.6672422885894775 -2.7868499755859375 + 512 -2.6904885768890381 -1.5387157201766968 -2.7665908336639404 + 513 -2.7612297534942627 -1.4759334325790405 -2.6269283294677734 + 514 -3.0282199382781982 -1.9062427282333374 -3.567896842956543 + 515 -2.9825344085693359 -1.955012321472168 -3.4117732048034668 + 516 -2.8421578407287598 -1.3743470907211304 -2.5257844924926758 + 517 -2.7605230808258057 -1.0195832252502441 -2.3142096996307373 + 518 -2.492283821105957 -1.3200808763504028 -3.0905382633209229 + 519 -2.7140092849731445 -1.667242169380188 -3.1678256988525391 + 520 -2.815988302230835 -1.7657734155654907 -3.0896031856536865 + 521 -3.5953226089477539 -2.237642765045166 -3.3093874454498291 + 522 -3.276015043258667 -2.0501956939697266 -3.1456184387207031 + 523 -3.4760258197784424 -2.3872346878051758 -3.5307846069335938 + 524 -3.5310983657836914 -2.3292922973632813 -3.4044563770294189 + 525 -3.5188844203948975 -2.5150833129882813 -3.8935935497283936 + 526 -3.3461925983428955 -1.955012321472168 -3.0481150150299072 + 527 -3.019254207611084 -1.7657734155654907 -2.8863375186920166 + 528 -2.9530956745147705 -1.8273136615753174 -3.0218956470489502 + 529 -3.2559134960174561 -1.6156532764434814 -2.8342342376708984 + 530 -3.5010883808135986 -1.9062427282333374 -3.0950286388397217 + 531 -3.2151389122009277 -2.1100010871887207 -3.2768290042877197 + 532 -3.2508950233459473 -2.237642765045166 -3.6538147926330566 + 533 -3.0824289321899414 -2.0501956939697266 -3.3392043113708496 + 534 -0.12928913533687592 -2.3292934894561768 4.8082275390625 + 535 0 -2.0676145553588867 4.4188714027404785 + 536 -0.13688650727272034 -2.0501968860626221 4.4329123497009277 + 537 -4.5658864974975586 -3.2905621528625488 -4.3517928123474121 + 538 0 -1.7842273712158203 4.0406937599182129 + 539 -0.25714558362960815 -1.9550135135650635 4.4095058441162109 + 540 -0.24354764819145203 -2.2376441955566406 4.7825145721435547 + 541 -0.33436727523803711 -1.9062440395355225 4.55010986328125 + 542 -0.22848351299762726 -2.5150847434997559 5.1533470153808594 + 543 -4.5090866088867188 -3.4062826633453369 -4.5389184951782227 + 544 0 -2.34539794921875 4.7953996658325195 + 545 -4.7918243408203125 -3.6019315719604492 -4.7046365737915039 + 546 -4.775327205657959 -3.649594783782959 -4.7993483543395996 + 547 -4.9397811889648438 -3.8474993705749512 -5.0542030334472656 + 548 -0.28047811985015869 -1.3743479251861572 3.6586611270904541 + 549 0 -1.199007511138916 3.2830746173858643 + 550 -0.14372867345809937 -1.7657746076583862 4.0557842254638672 + 551 -0.1499076634645462 -1.4759345054626465 3.6773707866668701 + 552 -0.2693907618522644 -1.6672433614730835 4.0347332954406738 + 553 -4.6794795989990234 -3.6019315719604492 -4.8169732093811035 + 554 -0.17444801330566406 -3.2905640602111816 6.2500824928283691 + 555 -4.3191771507263184 -3.2905621528625488 -4.5985026359558105 + 556 0 -3.1403224468231201 5.9097743034362793 + 557 0 -3.3702425956726074 6.2816271781921387 + 558 -4.2487063407897949 -3.1773614883422852 -4.2840490341186523 + 559 0 -3.6109492778778076 6.6651163101196289 + 560 -4.3269863128662109 -3.0510334968566895 -4.0912480354309082 + 561 -0.13141778111457825 -3.7876529693603516 6.9895343780517578 + 562 -0.15359748899936676 -3.5371599197387695 6.6278963088989258 + 563 -0.090939536690711975 -3.3597564697265625 6.2897367477416992 + 564 -0.0794353187084198 -3.601933479309082 6.6720685958862305 + 565 -0.25050902366638184 -3.0152220726013184 6.0076909065246582 + 566 -0.11166942864656448 -2.8716044425964355 5.5515551567077637 + 567 -0.21191030740737915 -2.7872929573059082 5.5216031074523926 + 568 -4.4050941467285156 -3.3597545623779297 -4.5646452903747559 + 569 -0.12088794261217117 -2.6031010150909424 5.181243896484375 + 570 -4.5336995124816895 -3.3597545623779297 -4.436039924621582 + 571 -4.2831206321716309 -3.128385066986084 -4.1758737564086914 + 572 -0.19385586678981781 -3.0510354042053223 5.8870100975036621 + 573 -0.10166426002979279 -3.1283869743347168 5.9190397262573242 + 574 -0.99770438671112061 0.7869221568107605 -0.76371496915817261 + 575 -0.98819607496261597 0.92363661527633667 -0.49983930587768555 + 576 -1.2995012998580933 1.1635723114013672 -0.61121672391891479 + 577 -1.2146544456481934 0.91269344091415405 -0.45159593224525452 + 578 5.9110593795776367 -4.7203555107116699 5.931218147277832 + 579 -1.1584895849227905 0.70768439769744873 0.46653896570205688 + 580 -1.3002294301986694 0.82550495862960815 0.53613686561584473 + 581 5.733891487121582 -4.5503401756286621 5.6925530433654785 + 582 5.9297690391540527 -4.7310056686401367 5.9306540489196777 + 583 5.7539796829223633 -4.5885310173034668 5.7516803741455078 + 584 -0.5951048731803894 0.73868811130523682 -1.2571135759353638 + 585 -0.47978061437606812 0.77655017375946045 -1.3074105978012085 + 586 -0.53754013776779175 0.86835706233978271 -1.4656829833984375 + 587 5.5125160217285156 -4.3656415939331055 5.5051627159118652 + 588 5.6957154273986816 -4.5503401756286621 5.7307291030883789 + 589 -1.1584324836730957 0.98618316650390625 -0.56030577421188354 + 590 -1.2976953983306885 1.0382149219512939 -0.71548080444335938 + 591 -1.122133731842041 0.89904493093490601 -0.6831439733505249 + 592 -0.40624162554740906 0.78772169351577759 -1.1312136650085449 + 593 5.5032715797424316 -4.3237533569335938 5.4370336532592773 + 594 1.5371884107589722 0.41687062382698059 0.10917248576879501 + 595 1.8954142332077026 0.19112969934940338 0.24536441266536713 + 596 1.705376148223877 0.56153297424316406 0.39845657348632813 + 597 1.5371884107589722 0.47721084952354431 -0.27195480465888977 + 598 1.5894557237625122 0.37999656796455383 -0.086212441325187683 + 599 1.9038233757019043 0.090541921555995941 0.08255266398191452 + 600 1.9545772075653076 0.026811672374606133 -0.098357744514942169 + 601 2.2721502780914307 -0.24047672748565674 0.072759442031383514 + 602 2.0691769123077393 0.25670364499092102 0.34784814715385437 + 603 2.2629630565643311 -0.13010793924331665 0.22238872945308685 + 604 2.322376012802124 -0.30733129382133484 -0.098596945405006409 + 605 1.904023289680481 0.10116890072822571 -0.27525636553764343 + 606 2.2630844116210938 -0.12527371942996979 -0.42016935348510742 + 607 -0.99127274751663208 0.70076584815979004 0.38590583205223083 + 608 -0.9987109899520874 0.54044967889785767 0.66285330057144165 + 609 1.5281940698623657 0.4941408634185791 0.28827044367790222 + 610 1.8962638378143311 0.23680123686790466 -0.4280286431312561 + 611 1.1382594108581543 0.72663116455078125 -0.068224139511585236 + 612 1.5281940698623657 0.64085561037063599 -0.43099957704544067 + 613 4.6794795989990234 -3.6019332408905029 4.7666678428649902 + 614 4.4050941467285156 -3.3597562313079834 4.5027542114257813 + 615 4.5658864974975586 -3.2905638217926025 4.2865676879882813 + 616 4.814117431640625 -3.5371596813201904 4.5702047348022461 + 617 4.5336995124816895 -3.3597562313079834 4.3741488456726074 + 618 4.5090866088867188 -3.4062843322753906 4.4792618751525879 + 619 4.7918243408203125 -3.6019332408905029 4.6543312072753906 + 620 4.775327205657959 -3.6495964527130127 4.7513055801391602 + 621 -1.76787269115448 0.17861592769622803 1.0992766618728638 + 622 -1.2158607244491577 0.26517826318740845 0.9361501932144165 + 623 -1.5740010738372803 0.10272199660539627 1.0519205331802368 + 624 4.6753826141357422 -3.2797918319702148 4.3247156143188477 + 625 4.3269863128662109 -3.0510349273681641 4.0144166946411133 + 626 -1.4703588485717773 -0.013164597563445568 1.1649783849716187 + 627 -1.4861416816711426 -0.33122736215591431 1.6721826791763306 + 628 -1.381956934928894 -0.089138031005859375 1.3333301544189453 + 629 4.3191771507263184 -3.2905638217926025 4.5332779884338379 + 630 4.2487063407897949 -3.1773631572723389 4.2133564949035645 + 631 -0.41788357496261597 0.43241354823112488 1.3999316692352295 + 632 -0.42721346020698547 0.54356980323791504 1.0701591968536377 + 633 -0.53311866521835327 0.47865322232246399 1.3701645135879517 + 634 5.1938576698303223 -4.0884075164794922 5.2570538520812988 + 635 4.9397811889648438 -3.8475010395050049 5.0155048370361328 + 636 5.2703962326049805 -4.1323943138122559 5.2576889991760254 + 637 5.4453363418579102 -4.3237533569335938 5.494962215423584 + 638 5.2707738876342773 -4.0884075164794922 5.1801304817199707 + 639 -0.247362419962883 0.4328511655330658 1.253116250038147 + 640 -0.64032942056655884 0.46405026316642761 1.287380576133728 + 641 -0.97927308082580566 0.22088234126567841 1.2718687057495117 + 642 -0.67941570281982422 0.46199515461921692 1.0577526092529297 + 643 -1.1256673336029053 0.18436092138290405 1.1174865961074829 + 644 -0.8078874945640564 0.50014626979827881 0.81971585750579834 + 645 -0.87726545333862305 0.26381117105484009 1.4568244218826294 + 646 5.0348577499389648 -3.8475010395050049 4.9204287528991699 + 647 5.0252494812011719 -3.8935580253601074 5.0069818496704102 + 648 -1.5242108106613159 0.47044274210929871 0.86769294738769531 + 649 -1.1226762533187866 0.61920899152755737 0.58923798799514771 + 650 -1.3255065679550171 0.39007443189620972 0.81682479381561279 + 651 5.2049369812011719 -2.5150837898254395 0.17689606547355652 + 652 5.3379178047180176 -2.4722676277160645 0.24370571970939636 + 653 5.3379178047180176 -2.4722676277160645 -0.34900495409965515 + 654 4.8644356727600098 -2.3292927742004395 -0.18549506366252899 + 655 4.9158391952514648 -2.387235164642334 -0.054762993007898331 + 656 5.230649471282959 -2.6031002998352051 -0.17029605805873871 + 657 4.8644356727600098 -2.3292927742004395 0.073083184659481049 + 658 5.5664749145507813 -2.7872922420501709 0.16704292595386505 + 659 5.230649471282959 -2.6031002998352051 0.071479856967926025 + 660 5.282588005065918 -2.6591436862945557 -0.04802381619811058 + 661 5.5664749145507813 -2.7872922420501709 -0.25677838921546936 + 662 5.5943574905395508 -2.8716037273406982 -0.1544681191444397 + 663 4.2539534568786621 -1.6156536340713501 0.2764560878276825 + 664 4.4750866889953613 -1.9550127983093262 0.19156557321548462 + 665 4.4750866889953613 -1.9550127983093262 -0.32272630929946899 + 666 4.8410077095031738 -2.2376432418823242 -0.30204072594642639 + 667 4.1763086318969727 -1.8273141384124756 -0.06879812479019165 + 668 4.4961018562316895 -2.0501961708068848 -0.2000744640827179 + 669 4.9783782958984375 -2.1918003559112549 0.25668910145759583 + 670 4.8410077095031738 -2.2376432418823242 0.18505387008190155 + 671 5.2049369812011719 -2.5150837898254395 -0.28007093071937561 + 672 4.9783782958984375 -2.1918003559112549 -0.37596732378005981 + 673 4.5468916893005371 -2.1100015640258789 -0.061687689274549484 + 674 4.4961018562316895 -2.0501961708068848 0.073698565363883972 + 675 6.6545944213867188 -3.5371589660644531 0.12690004706382751 + 676 6.282691478729248 -3.2905631065368652 -0.2070595771074295 + 677 6.6972246170043945 -3.6019322872161865 -0.10458944737911224 + 678 7.053131103515625 -3.8475000858306885 -0.086576469242572784 + 679 7.0102863311767578 -3.7876517772674561 -0.1521754264831543 + 680 6.6972246170043945 -3.6019322872161865 0.054280743002891541 + 681 7.1067757606506348 -3.8935573101043701 -0.018268350511789322 + 682 6.7533326148986816 -3.6495954990386963 -0.024022066965699196 + 683 5.5943574905395508 -2.8716037273406982 0.06887044757604599 + 684 5.9254293441772461 -3.0510342121124268 0.15544012188911438 + 685 6.3872761726379395 -3.2797911167144775 -0.25758868455886841 + 686 5.9254293441772461 -3.0510342121124268 -0.23227162659168243 + 687 5.9555745124816895 -3.1283857822418213 -0.13819977641105652 + 688 5.6467466354370117 -2.9257199764251709 -0.041473820805549622 + 689 6.3206820487976074 -3.3597555160522461 0.059993423521518707 + 690 6.282691478729248 -3.2905631065368652 0.14183646440505981 + 691 5.9555745124816895 -3.1283857822418213 0.065128885209560394 + 692 6.0469813346862793 -3.0152208805084229 0.21122021973133087 + 693 6.3768110275268555 -3.4062836170196533 -0.029828323051333427 + 694 6.3206820487976074 -3.3597555160522461 -0.12188500910997391 + 695 6.0085768699645996 -3.1773624420166016 -0.035347979515790939 + 696 3.0002732276916504 -0.7714959979057312 0.20560410618782043 + 697 2.6312825679779053 -0.45553568005561829 -0.40916767716407776 + 698 2.6416831016540527 -0.56528842449188232 -0.26345744729042053 + 699 3.0002732276916504 -0.77151727676391602 -0.39529216289520264 + 700 3.012251615524292 -0.87829339504241943 -0.25335094332695007 + 701 2.6915876865386963 -0.63152211904525757 -0.096446193754673004 + 702 -0.78142541646957397 0.73252737522125244 0.28535020351409912 + 703 -0.38436958193778992 0.75412106513977051 0.35205847024917603 + 704 -0.4165472686290741 0.81285578012466431 0.11093716323375702 + 705 -0.7772209644317627 0.88504642248153687 -0.41930615901947021 + 706 -0.541298508644104 0.85442268848419189 -0.077582761645317078 + 707 2.6416831016540527 -0.56526070833206177 0.068870827555656433 + 708 2.6312897205352783 -0.45541656017303467 0.21137914061546326 + 709 2.2721571922302246 -0.24005977809429169 -0.27075850963592529 + 710 -0.41361194849014282 0.88980770111083984 -0.27305689454078674 + 711 -0.38114157319068909 0.89443343877792358 -0.49682408571243286 + 712 -0.31772983074188232 0.82719695568084717 -0.91432791948318481 + 713 -0.1683882474899292 0.89437633752822876 -0.54026907682418823 + 714 3.7389395236968994 -1.374347448348999 -0.36075741052627563 + 715 4.1075735092163086 -1.6672427654266357 -0.34223473072052002 + 716 4.1261329650878906 -1.7657740116119385 -0.21408066153526306 + 717 3.3835549354553223 -1.1804686784744263 0.070340529084205627 + 718 3.7389395236968994 -1.374347448348999 0.20019882917404175 + 719 3.8899023532867432 -1.320081353187561 0.2836230993270874 + 720 4.1075735092163086 -1.6672427654266357 0.1965467780828476 + 721 3.8049197196960449 -1.5387160778045654 -0.076101928949356079 + 722 4.1261329650878906 -1.7657740116119385 0.073376663029193878 + 723 3.4333093166351318 -1.2441216707229614 -0.083597905933856964 + 724 3.0620131492614746 -0.94303154945373535 -0.091046050190925598 + 725 0 0.4783305823802948 1.1347618103027344 + 726 -0.32788348197937012 0.61850368976593018 0.79747271537780762 + 727 -0.17547522485256195 0.75897514820098877 0.38534194231033325 + 728 0 0.68922173976898193 -0.091379150748252869 + 729 3.3835549354553223 -1.1804686784744263 -0.24078458547592163 + 730 3.7550652027130127 -1.4759337902069092 -0.22760356962680817 + 731 3.3696637153625488 -1.0763052701950073 -0.37857908010482788 + 732 3.7550652027130127 -1.4759337902069092 0.072211019694805145 + 733 3.012251615524292 -0.87829345464706421 0.068433403968811035 + 734 3.3696637153625488 -1.0763052701950073 0.20281295478343964 + 735 3.5252091884613037 -1.0195764303207397 -0.46809235215187073 + 736 1.1256673336029053 0.18436092138290405 1.1174865961074829 + 737 0.64032942056655884 0.46405026316642761 1.287380576133728 + 738 0.97927308082580566 0.22088234126567841 1.2718687057495117 + 739 0.67941570281982422 0.46199515461921692 1.0577526092529297 + 740 0.8078874945640564 0.50014626979827881 0.81971585750579834 + 741 1.302942156791687 0.73270577192306519 0.63484013080596924 + 742 1.1226762533187866 0.61920899152755737 0.58923798799514771 + 743 0.9987109899520874 0.54044967889785767 0.66285330057144165 + 744 1.4703588485717773 -0.013164597563445568 1.1649783849716187 + 745 1.2325361967086792 -0.063323773443698883 1.4729715585708618 + 746 1.2158607244491577 0.26517826318740845 0.9361501932144165 + 747 0.70291668176651001 0.43213513493537903 1.572188138961792 + 748 1.3255065679550171 0.39007443189620972 0.81682479381561279 + 749 1.5740010738372803 0.10272199660539627 1.0519205331802368 + 750 0.53311866521835327 0.47865322232246399 1.3701645135879517 + 751 0.60888558626174927 0.46691834926605225 1.6176453828811646 + 752 0.42721346020698547 0.54356980323791504 1.0701591968536377 + 753 0.41788357496261597 0.43241354823112488 1.3999316692352295 + 754 -3.784111499786377 -2.6031007766723633 3.5637416839599609 + 755 -3.5310983657836914 -2.3292932510375977 3.2920475006103516 + 756 -3.2965664863586426 -2.1918010711669922 3.6842880249023438 + 757 -3.4760258197784424 -2.3872356414794922 3.4212594032287598 + 758 -3.3482556343078613 -2.3292932510375977 3.4748902320861816 + 759 -3.5188844203948975 -2.5150845050811768 3.7904214859008789 + 760 -2.9530956745147705 -1.8273147344589233 2.8842952251434326 + 761 -3.0824289321899414 -2.050196647644043 3.2128255367279053 + 762 2.0795845985412598 -0.47115123271942139 1.5426634550094604 + 763 -3.0282199382781982 -1.9062438011169434 3.4342799186706543 + 764 1.1853941679000854 0.03176889568567276 1.8438465595245361 + 765 1.3797941207885742 -0.25594520568847656 1.8008806705474854 + 766 1.7499909400939941 -0.59199213981628418 1.8974987268447876 + 767 1.4861416816711426 -0.33122736215591431 1.6721826791763306 + 768 1.6411590576171875 -0.50435620546340942 2.0030896663665771 + 769 1.8259291648864746 -0.17990267276763916 1.2881585359573364 + 770 2.0142898559570313 -0.1090841218829155 1.3339012861251831 + 771 1.6412662267684937 -0.36064893007278442 1.5605523586273193 + 772 1.9850869178771973 -0.57960629463195801 1.654621958732605 + 773 -3.2151389122009277 -2.1100020408630371 3.1534485816955566 + 774 1.1307854652404785 0.0046354564838111401 1.6276676654815674 + 775 0.87726545333862305 0.26381117105484009 1.4568244218826294 + 776 -3.2508950233459473 -2.2376437187194824 3.536829948425293 + 777 -2.9825344085693359 -1.9550133943557739 3.2806119918823242 + 778 1.381956934928894 -0.089138031005859375 1.3333301544189453 + 779 1.7270411252975464 -0.29127588868141174 1.4003883600234985 + 780 -3.5953226089477539 -2.2376437187194824 3.1924026012420654 + 781 -3.786245584487915 -2.7872927188873291 4.0410642623901367 + 782 -3.8330309391021729 -2.747596263885498 4.1752519607543945 + 783 -4.0528359413146973 -3.0510351657867432 4.28857421875 + 784 -4.5658864974975586 -3.2905638217926025 4.2865676879882813 + 785 -4.2831206321716309 -3.1283867359161377 4.1028041839599609 + 786 0.99127274751663208 0.70076584815979004 0.38590583205223083 + 787 1.1779131889343262 0.67153710126876831 0.18536059558391571 + 788 -3.9928522109985352 -2.9257204532623291 3.9513776302337646 + 789 -4.3269863128662109 -3.0510349273681641 4.0144166946411133 + 790 -4.596895694732666 -3.5371596813201904 4.7874188423156738 + 791 -4.6794795989990234 -3.6019332408905029 4.7666678428649902 + 792 0 0.84633499383926392 -0.65023744106292725 + 793 0 0.66388493776321411 -1.2576489448547363 + 794 0.38114157319068909 0.89443343877792358 -0.49682408571243286 + 795 0.31772983074188232 0.82719695568084717 -0.91432791948318481 + 796 0.1683882474899292 0.89437633752822876 -0.54026907682418823 + 797 -4.3191771507263184 -3.2905638217926025 4.5332779884338379 + 798 -4.4050941467285156 -3.3597562313079834 4.5027542114257813 + 799 -4.2487063407897949 -3.1773631572723389 4.2133564949035645 + 800 -4.1393389701843262 -3.1283867359161377 4.2465786933898926 + 801 -4.7918243408203125 -3.6019332408905029 4.6543312072753906 + 802 -4.5090866088867188 -3.4062843322753906 4.4792618751525879 + 803 -4.5336995124816895 -3.3597562313079834 4.3741488456726074 + 804 -4.814117431640625 -3.5371596813201904 4.5702047348022461 + 805 -3.8420038223266602 -2.5150845050811768 3.467294454574585 + 806 -4.0859298706054688 -2.7872927188873291 3.7413802146911621 + 807 -4.2210955619812012 -2.7475960254669189 3.7871875762939453 + 808 0.66567790508270264 0.703865647315979 -1.1194782257080078 + 809 -3.8768460750579834 -2.8716042041778564 3.9919736385345459 + 810 -3.7353560924530029 -2.6591441631317139 3.6873283386230469 + 811 -4.0347690582275391 -2.8716042041778564 3.8340437412261963 + 812 0.47978061437606812 0.77655017375946045 -1.3074105978012085 + 813 0.46647316217422485 0.77274543046951294 -1.5350323915481567 + 814 0.32788348197937012 0.61850368976593018 0.79747271537780762 + 815 0.40624162554740906 0.78772169351577759 -1.1312136650085449 + 816 0.23304706811904907 0.65510749816894531 -1.3271197080612183 + 817 1.2151968479156494 0.69306784868240356 0.34135156869888306 + 818 -3.6131467819213867 -2.6031007766723633 3.7346992492675781 + 819 1.1584895849227905 0.70768439769744873 0.46653896570205688 + 820 1.1584324836730957 0.98618316650390625 -0.56030577421188354 + 821 0.98819607496261597 0.92363661527633667 -0.49983930587768555 + 822 1.2976953983306885 1.0382149219512939 -0.71548080444335938 + 823 1.1768994331359863 0.80231964588165283 -0.31374287605285645 + 824 1.2146544456481934 0.91269344091415405 -0.45159593224525452 + 825 0.99770438671112061 0.7869221568107605 -0.76371496915817261 + 826 0.7772209644317627 0.88504642248153687 -0.41930615901947021 + 827 -2.0161387920379639 -0.88099920749664307 2.1519179344177246 + 828 -1.9029310941696167 -0.64433860778808594 1.8089826107025146 + 829 -1.7499909400939941 -0.59199213981628418 1.8974987268447876 + 830 -1.6911920309066772 -0.44686439633369446 2.1864321231842041 + 831 3.8420038223266602 -2.5150845050811768 3.467294454574585 + 832 3.5953226089477539 -2.2376437187194824 3.1924026012420654 + 833 3.5310983657836914 -2.3292932510375977 3.2920475006103516 + 834 3.4760258197784424 -2.3872356414794922 3.4212594032287598 + 835 3.784111499786377 -2.6031007766723633 3.5637416839599609 + 836 -1.9850869178771973 -0.57960629463195801 1.654621958732605 + 837 -2.2437036037445068 -0.87995707988739014 1.9237393140792847 + 838 2.9825344085693359 -1.9550133943557739 3.2806119918823242 + 839 2.815988302230835 -1.7657744884490967 2.9488978385925293 + 840 3.0282199382781982 -1.9062438011169434 3.4342799186706543 + 841 2.7600805759429932 -1.6156542301177979 3.1817667484283447 + 842 2.9530956745147705 -1.8273147344589233 2.8842952251434326 + 843 3.0949854850769043 -1.6672431230545044 2.6411619186401367 + 844 3.2559134960174561 -1.6156539916992188 2.6859338283538818 + 845 3.019254207611084 -1.7657743692398071 2.7456324100494385 + 846 3.276015043258667 -2.050196647644043 3.0192394256591797 + 847 3.2151389122009277 -2.1100020408630371 3.1534485816955566 + 848 3.3482556343078613 -2.3292932510375977 3.4748902320861816 + 849 3.2508950233459473 -2.2376437187194824 3.536829948425293 + 850 3.0824289321899414 -2.050196647644043 3.2128255367279053 + 851 -2.5882666110992432 -1.076305627822876 2.0892784595489502 + 852 -2.3338971138000488 -0.77353781461715698 1.8136439323425293 + 853 3.5010883808135986 -1.9062438011169434 2.961418628692627 + 854 4.4530000686645508 -3.0152213573455811 4.0594387054443359 + 855 4.0347690582275391 -2.8716042041778564 3.8340437412261963 + 856 4.2831206321716309 -3.1283867359161377 4.1028041839599609 + 857 -2.0795845985412598 -0.47115123271942139 1.5426634550094604 + 858 -1.8259291648864746 -0.17990267276763916 1.2881585359573364 + 859 -1.7270411252975464 -0.29127588868141174 1.4003883600234985 + 860 -1.3797941207885742 -0.25594520568847656 1.8008806705474854 + 861 -1.1853941679000854 0.03176889568567276 1.8438465595245361 + 862 -1.2325361967086792 -0.063323773443698883 1.4729715585708618 + 863 -1.1307854652404785 0.0046354564838111401 1.6276676654815674 + 864 3.8330309391021729 -2.747596263885498 4.1752519607543945 + 865 4.1393389701843262 -3.1283867359161377 4.2465786933898926 + 866 4.0528359413146973 -3.0510351657867432 4.28857421875 + 867 -1.6411590576171875 -0.50435620546340942 2.0030896663665771 + 868 -1.6412662267684937 -0.36064893007278442 1.5605523586273193 + 869 4.0859298706054688 -2.7872927188873291 3.7413802146911621 + 870 3.9840364456176758 -2.4722683429718018 3.5122737884521484 + 871 -1.4314688444137573 -0.20814655721187592 1.9959657192230225 + 872 3.6131467819213867 -2.6031007766723633 3.7346992492675781 + 873 3.7353560924530029 -2.6591441631317139 3.6873283386230469 + 874 3.786245584487915 -2.7872927188873291 4.0410642623901367 + 875 3.5188844203948975 -2.5150845050811768 3.7904214859008789 + 876 3.56492018699646 -2.4722683429718018 3.9313828945159912 + 877 3.8768460750579834 -2.8716042041778564 3.9919736385345459 + 878 3.9928522109985352 -2.9257204532623291 3.9513776302337646 + 879 2.3338971138000488 -0.77353781461715698 1.8136439323425293 + 880 2.2437036037445068 -0.87995707988739014 1.9237393140792847 + 881 -3.019254207611084 -1.7657743692398071 2.7456324100494385 + 882 -2.7612297534942627 -1.4759342670440674 2.4715323448181152 + 883 2.2248725891113281 -1.019584059715271 2.6711857318878174 + 884 2.1771595478057861 -1.0763057470321655 2.5003857612609863 + 885 2.0161387920379639 -0.88099920749664307 2.1519179344177246 + 886 -3.2559134960174561 -1.6156539916992188 2.6859338283538818 + 887 -3.276015043258667 -2.050196647644043 3.0192394256591797 + 888 -3.3461925983428955 -1.9550133943557739 2.9169535636901855 + 889 -3.0949854850769043 -1.6672431230545044 2.6411619186401367 + 890 -2.492283821105957 -1.3200819492340088 2.9272115230560303 + 891 1.9088773727416992 -0.77898448705673218 2.2419190406799316 + 892 1.9576611518859863 -0.72017133235931396 2.4180660247802734 + 893 -2.7140092849731445 -1.6672432422637939 3.0221378803253174 + 894 -2.815988302230835 -1.7657744884490967 2.9488978385925293 + 895 1.6911920309066772 -0.44686439633369446 2.1864321231842041 + 896 1.9029310941696167 -0.64433860778808594 1.8089826107025146 + 897 2.1651382446289063 -0.9439883828163147 2.0741951465606689 + 898 2.54923415184021 -1.4759342670440674 2.6835355758666992 + 899 2.7140092849731445 -1.6672432422637939 3.0221378803253174 + 900 2.4454989433288574 -1.3743478059768677 2.7618792057037354 + 901 2.7612297534942627 -1.4759342670440674 2.4715323448181152 + 902 3.0088748931884766 -1.3200818300247192 2.4106202125549316 + 903 3.3461925983428955 -1.9550133943557739 2.9169535636901855 + 904 -2.282536506652832 -1.1804690361022949 2.417309045791626 + 905 -2.1651382446289063 -0.9439883828163147 2.0741951465606689 + 906 -2.1771595478057861 -1.0763057470321655 2.5003857612609863 + 907 -1.9088773727416992 -0.77898448705673218 2.2419190406799316 + 908 2.282536506652832 -1.1804690361022949 2.417309045791626 + 909 2.492283821105957 -1.3200819492340088 2.9272115230560303 + 910 2.6904885768890381 -1.5387164354324341 2.6143856048583984 + 911 2.4277169704437256 -1.2441220283508301 2.3441188335418701 + 912 2.8421578407287598 -1.3743478059768677 2.3652200698852539 + 913 2.5882666110992432 -1.076305627822876 2.0892784595489502 + 914 2.5025346279144287 -1.1804690361022949 2.1973111629486084 + 915 -2.5025346279144287 -1.1804690361022949 2.1973111629486084 + 916 -2.8421578407287598 -1.3743478059768677 2.3652200698852539 + 917 -3.0088748931884766 -1.3200818300247192 2.4106202125549316 + 918 -2.7605230808258057 -1.0195839405059814 2.135542631149292 + 919 -2.4454989433288574 -1.3743478059768677 2.7618792057037354 + 920 -2.54923415184021 -1.4759342670440674 2.6835355758666992 + 921 -2.4277169704437256 -1.2441220283508301 2.3441188335418701 + 922 -2.6904885768890381 -1.5387164354324341 2.6143856048583984 + 923 0.79974973201751709 3.653735876083374 0.24021320044994354 + 924 0.81027168035507202 3.5944371223449707 0.18752041459083557 + 925 0.80059206485748291 3.6683194637298584 0.21626244485378265 + 926 0.80405420064926147 3.6611599922180176 0.1353587806224823 + 927 0.80139154195785522 3.5127954483032227 0.24255955219268799 + 928 0.80036360025405884 3.537344217300415 0.25963246822357178 + 929 0.80256932973861694 3.5021307468414307 0.21657651662826538 + 930 0.79914295673370361 3.5897045135498047 0.27315542101860046 + 931 0.79959267377853394 3.5661978721618652 0.27237877249717712 + 932 0.74926698207855225 3.3852181434631348 0.241178959608078 + 933 0.74211430549621582 3.4599854946136475 0.34871956706047058 + 934 0.72984343767166138 3.3881664276123047 0.31451451778411865 + 935 0.72965782880783081 3.3630034923553467 0.28683826327323914 + 936 0.75852549076080322 3.384390115737915 0.1731707900762558 + 937 0.80658829212188721 3.5963218212127686 0.093215122818946838 + 938 0.80490362644195557 3.501317024230957 0.16136391460895538 + 939 0.80578875541687012 3.5600874423980713 0.094838395714759827 + 940 0.80648124217987061 3.5323545932769775 0.11537910997867584 + 941 0.78610825538635254 3.724198579788208 0.1348855197429657 + 942 0.80285489559173584 3.6728167533874512 0.16159093379974365 + 943 0.7811470627784729 3.7367196083068848 0.18253642320632935 + 944 0.78300303220748901 3.7219791412353516 0.22469432651996613 + 945 0.8016628623008728 3.6748940944671631 0.18917155265808105 + 946 0.80514633655548096 3.6432065963745117 0.1146838441491127 + 947 0.78775006532669067 3.7007277011871338 0.08957885205745697 + 948 0.79921430349349976 3.6325631141662598 0.25867879390716553 + 949 0.77841299772262573 3.7083728313446045 0.26476570963859558 + 950 0.79036271572113037 3.4998533725738525 0.058744180947542191 + 951 0.80583876371383667 3.5132594108581543 0.13600835204124451 + 952 0.79189032316207886 3.6373319625854492 0.045735914260149002 + 953 0.80601722002029419 3.6215057373046875 0.10137718170881271 + 954 0.79269701242446899 3.5911822319030762 0.036198873072862625 + 955 0.76656335592269897 3.3885159492492676 0.10980185866355896 + 956 0.78776443004608154 3.4621486663818359 0.091704629361629486 + 957 0.78562277555465698 3.4461586475372314 0.038981817662715912 + 958 0.79004150629043579 3.5425126552581787 0.034447956830263138 + 959 0.78433793783187866 3.5054214000701904 0.017464932054281235 + 960 0.74901008605957031 3.3479270935058594 0.18372419476509094 + 961 0.78210365772247314 3.7304878234863281 0.069892667233943939 + 962 0.77713525295257568 3.4121725559234619 0.068002738058567047 + 963 0.79982829093933105 3.2810261249542236 0.037044435739517212 + 964 0.81546849012374878 3.5503575801849365 -0.062470488250255585 + 965 0.78947049379348755 3.6429355144500732 0.0059549757279455662 + 966 0.73039311170578003 3.6464619636535645 0.38751319050788879 + 967 0.73369097709655762 3.6624019145965576 0.39961990714073181 + 968 0.8093222975730896 3.4924941062927246 -0.036150746047496796 + 969 0.78308159112930298 3.4821932315826416 0.023853788152337074 + 970 0.73885214328765869 3.704425573348999 0.35712003707885742 + 971 0.7466471791267395 3.6985006332397461 0.34386977553367615 + 972 0.7473682165145874 3.5835657119750977 0.37959674000740051 + 973 0.75278621912002563 3.4160702228546143 0.29393458366394043 + 974 0.72603869438171387 3.5824377536773682 0.40260520577430725 + 975 0.790691077709198 3.6725668907165527 0.063821963965892792 + 976 0.78380972146987915 3.7109930515289307 0.05467589944601059 + 977 0.76385074853897095 3.7816629409790039 0.17926277220249176 + 978 0.77249538898468018 3.772240161895752 0.14273917675018311 + 979 0.090249255299568176 4.0761785507202148 0.21815063059329987 + 980 0.085681401193141937 4.1636309623718262 -0.036938928067684174 + 981 0.80234807729721069 3.4949352741241455 -0.35814681649208069 + 982 0 4.0440988540649414 0.20505738258361816 + 983 0 3.9404995441436768 0.38070890307426453 + 984 0.61358475685119629 3.8213596343994141 0.59428668022155762 + 985 0.70195305347442627 3.1302134990692139 0.44549679756164551 + 986 0.24783496558666229 4.1544580459594727 0.23867067694664001 + 987 0.28465405106544495 4.2166123390197754 -0.068520091474056244 + 988 0.39874985814094543 4.1932697296142578 0.30929005146026611 + 989 0.23841799795627594 4.0316281318664551 0.47366580367088318 + 990 0.16881939768791199 4.0835165977478027 0.30628904700279236 + 991 0.30960917472839355 3.9247803688049316 0.61396944522857666 + 992 0.39064636826515198 3.8664312362670898 0.66397875547409058 + 993 0.35752779245376587 4.0949454307556152 0.48416069149971008 + 994 0.16457633674144745 3.9426407814025879 0.5115734338760376 + 995 0.22449029982089996 3.8026783466339111 0.64762908220291138 + 996 0.097283430397510529 3.7328577041625977 0.63710916042327881 + 997 0.29690927267074585 4.1099863052368164 0.42820137739181519 + 998 0.54680579900741577 3.5365090370178223 0.70457911491394043 + 999 0.63398128747940063 3.4533398151397705 0.66746586561203003 +1000 0.68165719509124756 3.6382601261138916 0.62630313634872437 +1001 0.44522148370742798 3.5799531936645508 0.74700474739074707 +1002 0.54029273986816406 3.9428977966308594 0.59916585683822632 +1003 0.47826367616653442 3.8670597076416016 0.6566091775894165 +1004 0.32853519916534424 4.1893010139465332 0.22926655411720276 +1005 0.74429160356521606 3.6440134048461914 0.37125480175018311 +1006 0.77443695068359375 3.6737372875213623 0.30658668279647827 +1007 0.75093734264373779 3.7394034862518311 0.30648314952850342 +1008 0.77829176187515259 3.6213061809539795 0.32319554686546326 +1009 0.74763232469558716 3.5257444381713867 0.37203571200370789 +1010 0.78059029579162598 3.5387721061706543 0.32126173377037048 +1011 0.80117738246917725 3.6100702285766602 0.26196885108947754 +1012 0.77660703659057617 3.5743069648742676 0.33412373065948486 +1013 0.80377578735351563 3.4977047443389893 0.1889587938785553 +1014 0.78457349538803101 3.4318604469299316 0.18506765365600586 +1015 0.77868437767028809 3.495741605758667 0.30762884020805359 +1016 0.78047603368759155 3.4608635902404785 0.27460739016532898 +1017 0.5087931752204895 4.1451139450073242 0.4134049117565155 +1018 0.5696636438369751 4.078277587890625 0.41864237189292908 +1019 0.58243358135223389 4.0845804214477539 0.12337062507867813 +1020 0.51534903049468994 4.1593127250671387 0.14647406339645386 +1021 0.41908711194992065 4.0688261985778809 0.53898918628692627 +1022 0.46797296404838562 4.1129846572875977 0.48315772414207458 +1023 0.45060169696807861 4.1989378929138184 0.20891855657100677 +1024 0.78649371862411499 3.4399981498718262 0.1374146044254303 +1025 0.78934907913208008 3.6141819953918457 -0.2269652932882309 +1026 0.78250336647033691 3.4389915466308594 0.23183839023113251 +1027 0.73438346385955811 3.6994144916534424 -0.41104725003242493 +1028 0.73835957050323486 3.7979886531829834 -0.13775964081287384 +1029 0.60193777084350586 3.9714441299438477 -0.29249924421310425 +1030 0.6708032488822937 3.8526685237884521 -0.38765400648117065 +1031 0.13369564712047577 0.8496895432472229 2.1405398845672607 +1032 0.079277552664279938 0.83601957559585571 2.1715846061706543 +1033 0 0.95697242021560669 2.1045193672180176 +1034 0.11891704052686691 0.68753165006637573 2.2759976387023926 +1035 0.21767240762710571 0.8355984091758728 2.1221156120300293 +1036 0.25183320045471191 0.93969041109085083 2.031707763671875 +1037 1.0827584266662598 0.8163604736328125 1.8066200017929077 +1038 0.19092127680778503 0.91007393598556519 -2.3022811412811279 +1039 0.11090348660945892 1.0762628316879272 -2.2310042381286621 +1040 0.18157140910625458 1.0987130403518677 -2.1700139045715332 +1041 1.1220123767852783 1.3960412740707397 -1.5658701658248901 +1042 1.218609094619751 1.2856459617614746 -1.6049314737319946 +1043 1.0129877328872681 1.2691420316696167 -1.6862235069274902 +1044 0.21092520654201508 1.2103004455566406 -2.0802843570709229 +1045 0.098265677690505981 0.9050413966178894 -2.3678758144378662 +1046 0.065510444343090057 1.0369160175323486 -2.2906451225280762 +1047 0.95256131887435913 0.71380245685577393 1.8040144443511963 +1048 0.87119787931442261 0.60122507810592651 1.8082761764526367 +1049 1.1826101541519165 0.51313519477844238 1.8764193058013916 +1050 0.70684355497360229 0.68794929981231689 1.8933299779891968 +1051 1.0895826816558838 0.38648080825805664 1.90385901927948 +1052 0.56171292066574097 0.94920593500137329 1.9347184896469116 +1053 0.72965067625045776 0.80100572109222412 1.918792724609375 +1054 0.50528603792190552 0.67574697732925415 2.0136191844940186 +1055 0.56168437004089355 0.57331317663192749 1.95428466796875 +1056 0.67538177967071533 0.58850163221359253 1.8261935710906982 +1057 0.63389921188354492 0.49082621932029724 1.760627269744873 +1058 0.77098202705383301 0.47905218601226807 1.7643963098526001 +1059 2.0249977111816406 1.3799152374267578 -0.069984421133995056 +1060 2.0415444374084473 1.3235077857971191 -0.0034289250615984201 +1061 2.0829541683197021 1.1595317125320435 0.12676161527633667 +1062 2.0415444374084473 1.3515616655349731 -0.13686417043209076 +1063 2.185875415802002 1.1610594987869263 -0.07600083202123642 +1064 1.9545415639877319 1.3941135406494141 -0.34226924180984497 +1065 2.1598274707794189 1.1844162940979004 -0.17434217035770416 +1066 1.9647352695465088 1.4347168207168579 -0.17963454127311707 +1067 1.4912599325180054 0.99071580171585083 1.3064186573028564 +1068 1.5816105604171753 0.93248790502548218 1.2322793006896973 +1069 1.8084045648574829 1.3540600538253784 0.32803645730018616 +1070 1.9060220718383789 1.3912439346313477 0.12771104276180267 +1071 1.7983822822570801 1.6739956140518188 -0.063968777656555176 +1072 1.9650707244873047 1.3816356658935547 0.047932811081409454 +1073 1.9046870470046997 1.4913599491119385 -0.24881552159786224 +1074 1.4327394962310791 1.2597050666809082 -1.5468606948852539 +1075 1.3115439414978027 1.2694060802459717 -1.5895125865936279 +1076 1.3980897665023804 1.2054818868637085 -1.6077655553817749 +1077 1.4686099290847778 1.3173333406448364 -1.4179198741912842 +1078 1.4907388687133789 1.2294454574584961 -1.5077351331710815 +1079 1.5974364280700684 1.0571818351745605 -1.5645925998687744 +1080 1.4762979745864868 1.3815003633499146 -1.3186819553375244 +1081 1.5757784843444824 1.2852675914764404 -1.2810198068618774 +1082 0.73112833499908447 3.4762184619903564 0.42756238579750061 +1083 0.71718710660934448 3.4607565402984619 0.55350857973098755 +1084 0.69867002964019775 3.8855552673339844 0.35130587220191956 +1085 0.72808027267456055 3.7326223850250244 0.43240654468536377 +1086 0.72315478324890137 3.5996413230895996 0.48368090391159058 +1087 0.72945088148117065 3.6160593032836914 0.42858749628067017 +1088 0.72366875410079956 3.4410831928253174 0.36436408758163452 +1089 0.72362589836120605 3.465660572052002 0.38173750042915344 +1090 0.73155665397644043 3.3550941944122314 0.25438284873962402 +1091 0.72623854875564575 3.4053699970245361 0.3481113612651825 +1092 0.73476183414459229 3.2652075290679932 0.35687944293022156 +1093 0.75794732570648193 3.8527398109436035 0.12812618911266327 +1094 0.74611181020736694 3.7899794578552246 0.32535496354103088 +1095 0.73954451084136963 3.7202229499816895 0.37635946273803711 +1096 0.760231614112854 3.7747533321380615 0.237174391746521 +1097 0.74435579776763916 3.8679089546203613 0.22330808639526367 +1098 0.78977024555206299 3.5359597206115723 -0.0011700560571625829 +1099 0.79030561447143555 3.5844078063964844 -0.0025994612369686365 +1100 0.72885119915008545 3.5506360530853271 0.42903792858123779 +1101 0.81920903921127319 3.480158805847168 -0.13342097401618958 +1102 0.80236947536468506 3.7128632068634033 -0.059987723827362061 +1103 0.8165249228477478 3.6131467819213867 -0.077832870185375214 +1104 0.78005486726760864 3.7507181167602539 0.096842169761657715 +1105 0.77817034721374512 3.8142285346984863 0.044856559485197067 +1106 0.78709328174591064 3.6928398609161377 0.029252946376800537 +1107 0.79530251026153564 3.7595767974853516 0.0089781396090984344 +1108 0.73382663726806641 3.4081754684448242 0.38152545690536499 +1109 0.80757337808609009 3.0231735706329346 -1.268455982208252 +1110 0.80947935581207275 3.0539186000823975 -1.3119360208511353 +1111 1.0343029499053955 2.9711916446685791 -1.5412425994873047 +1112 0.89444053173065186 2.9132993221282959 -1.5167577266693115 +1113 0.79116225242614746 2.8833179473876953 -1.3678724765777588 +1114 0.74002999067306519 3.1033306121826172 -1.0750055313110352 +1115 0.80214822292327881 3.0972983837127686 -1.2769720554351807 +1116 0.7194785475730896 2.6844067573547363 -1.1131532192230225 +1117 0.66194015741348267 2.7776768207550049 -1.2935618162155151 +1118 0.78667932748794556 3.1717236042022705 -1.2953104972839355 +1119 0.81810253858566284 2.9544591903686523 -1.2378466129302979 +1120 0.76791244745254517 3.2190937995910645 -1.2791136503219604 +1121 0.74076521396636963 3.2669994831085205 -1.2379751205444336 +1122 0.70754599571228027 3.9053571224212646 -0.049069095402956009 +1123 0.7877928614616394 3.2434995174407959 0.20897409319877625 +1124 0.73009330034255981 3.3455286026000977 0.45382592082023621 +1125 0.72376149892807007 3.5052928924560547 0.39473935961723328 +1126 0.70292961597442627 3.8517334461212158 0.46282109618186951 +1127 1.0896968841552734 2.9848690032958984 -1.722550630569458 +1128 1.0559179782867432 3.0186333656311035 -1.2770363092422485 +1129 1.2346490621566772 3.1530351638793945 -1.8277060985565186 +1130 0.66052818298339844 3.3713481426239014 0.60447239875793457 +1131 0.66165828704833984 4.0117263793945313 0.060723025351762772 +1132 0.6454961895942688 3.9954078197479248 0.42718487977981567 +1133 1.1529072523117065 3.1858429908752441 -1.6287378072738647 +1134 1.0283424854278564 3.2259109020233154 -1.2641658782958984 +1135 0.70586699247360229 3.7397677898406982 0.53718245029449463 +1136 0.1209464892745018 1.7391475439071655 1.5621159076690674 +1137 0 1.4248226881027222 1.7604490518569946 +1138 0.073244892060756683 1.4472157955169678 1.7532035112380981 +1139 0.41205224394798279 1.5752291679382324 -1.7714916467666626 +1140 0.28354477882385254 1.9056583642959595 -1.6449849605560303 +1141 1.2624530792236328 4.1310234069824219 -2.298490047454834 +1142 1.3895807266235352 3.5919821262359619 -2.0384814739227295 +1143 1.1669700145721436 3.7164328098297119 -1.7045187950134277 +1144 0.13265272974967957 4.4605956077575684 -0.93263721466064453 +1145 0 4.6171479225158691 -2.0580189228057861 +1146 0.51717931032180786 4.3047075271606445 -1.1015387773513794 +1147 0.69699257612228394 4.4663567543029785 -2.1452789306640625 +1148 0.59365934133529663 1.5179650783538818 -1.7218084335327148 +1149 1.3444375991821289 2.1380910873413086 -0.65158849954605103 +1150 1.575343132019043 2.002718448638916 -0.27155193686485291 +1151 1.5958874225616455 1.997714638710022 -0.097415164113044739 +1152 0.96472513675689697 2.1264557838439941 -1.2385962009429932 +1153 0.55987757444381714 1.9001616239547729 -1.5355249643325806 +1154 0.80932939052581787 2.0351059436798096 -1.35505211353302 +1155 1.2045893669128418 2.1399185657501221 -0.91843235492706299 +1156 0.50637251138687134 4.6327524185180664 -3.6938743591308594 +1157 0.89889490604400635 4.5396251678466797 -3.08156418800354 +1158 0.4638819694519043 4.66204833984375 -3.0742619037628174 +1159 0.87153339385986328 2.7884058952331543 -1.8090965747833252 +1160 0.47663825750350952 2.3270232677459717 -2.4140825271606445 +1161 1.51224684715271 2.8945326805114746 -2.7523846626281738 +1162 0.44953307509422302 2.1520829200744629 -2.9845614433288574 +1163 1.2506963014602661 2.4581413269042969 -3.2123827934265137 +1164 1.6411304473876953 3.6761362552642822 -3.6990070343017578 +1165 1.6688418388366699 3.8266065120697021 -3.0686798095703125 +1166 1.4825583696365356 3.9981496334075928 -3.7246551513671875 +1167 1.4958071708679199 2.6856207847595215 -3.472698450088501 +1168 1.7195028066635132 3.4870333671569824 -2.9952113628387451 +1169 1.6680494546890259 3.1540062427520752 -2.8839526176452637 +1170 0.92153781652450562 2.4672787189483643 -2.5448577404022217 +1171 0.98893857002258301 4.3132805824279785 -2.1923706531524658 +1172 1.5110691785812378 4.1025199890136719 -3.0330448150634766 +1173 1.2558002471923828 4.3504867553710938 -3.0674517154693604 +1174 0 4.7310051918029785 -3.0512905120849609 +1175 0.35497364401817322 4.5632529258728027 -2.0989005565643311 +1176 0.67068684101104736 2.8387386798858643 -1.4411338567733765 +1177 0.54369634389877319 2.7197632789611816 -1.380486011505127 +1178 0.47708585858345032 2.615314245223999 -1.7342147827148438 +1179 1.3123433589935303 3.1053149700164795 -2.0353763103485107 +1180 1.2659366130828857 2.7029953002929688 -2.542644739151001 +1181 1.4660543203353882 3.4623630046844482 -2.1743249893188477 +1182 1.4106533527374268 3.8796234130859375 -2.2506699562072754 +1183 1.47222900390625 1.4228744506835938 -0.65726011991500854 +1184 1.5473748445510864 1.5309852361679077 -0.68695640563964844 +1185 1.6175953149795532 1.4022657871246338 -0.54537862539291382 +1186 1.4887901544570923 1.3580292463302612 -0.86447328329086304 +1187 1.4316258430480957 1.2151614427566528 -0.76769816875457764 +1188 0.49085581302642822 0.98290687799453735 -1.8227381706237793 +1189 0.6207767128944397 1.0382436513900757 -1.6481261253356934 +1190 0.51950430870056152 0.88158446550369263 -1.6896142959594727 +1191 0.59147858619689941 0.95071983337402344 -1.5542418956756592 +1192 0.53754013776779175 0.86835706233978271 -1.4656829833984375 +1193 1.5360391139984131 1.1292934417724609 0.66564875841140747 +1194 1.6146113872528076 1.0805168151855469 0.47917759418487549 +1195 1.4612644910812378 1.0229456424713135 0.63248670101165771 +1196 1.5269591808319092 1.239753246307373 -0.57918035984039307 +1197 1.3936996459960938 1.2958823442459106 -0.6234591007232666 +1198 1.2995012998580933 1.1635723114013672 -0.61121672391891479 +1199 5.9562239646911621 -4.6919302940368652 5.8938412666320801 +1200 5.9305686950683594 -4.7203555107116699 5.9117083549499512 +1201 0.94066870212554932 0.2583516538143158 1.7173399925231934 +1202 1.664408802986145 0.52781176567077637 0.95959258079528809 +1203 0.53999006748199463 0.40797597169876099 1.6739884614944458 +1204 0.65227842330932617 1.1276092529296875 -1.7319236993789673 +1205 0.80181270837783813 1.0479804277420044 -1.649203896522522 +1206 5.8938274383544922 -4.6919302940368652 5.956244945526123 +1207 5.9257001876831055 -4.6919236183166504 5.9485855102539063 +1208 5.8881525993347168 -4.7075061798095703 5.9341516494750977 +1209 5.8976106643676758 -4.7047724723815918 5.9621415138244629 +1210 5.8969326019287109 -4.7099757194519043 5.9570231437683105 +1211 0.51797527074813843 1.2071303129196167 1.7855260372161865 +1212 0.30705147981643677 1.625540018081665 1.5982861518859863 +1213 0.70824193954467773 1.1187355518341064 1.77153480052948 +1214 0.59553390741348267 1.5299925804138184 1.5554773807525635 +1215 1.3201740980148315 1.5398937463760376 0.93585765361785889 +1216 1.3313529491424561 1.8736493587493896 0.55672132968902588 +1217 1.1056369543075562 1.4760264158248901 1.330896258354187 +1218 1.1595174074172974 1.5174789428710938 1.2368336915969849 +1219 0.98349910974502563 1.7521466016769409 1.1993355751037598 +1220 1.5797404050827026 1.2525662183761597 0.66695445775985718 +1221 0.88359725475311279 1.172702431678772 -1.646755576133728 +1222 0.65636509656906128 1.2334215641021729 -1.7809927463531494 +1223 0.35991129279136658 1.1433635950088501 -2.0411515235900879 +1224 1.3795156478881836 0.91403526067733765 0.59909039735794067 +1225 1.3002294301986694 0.82550495862960815 0.53613686561584473 +1226 0.4709210991859436 1.3223302364349365 -1.8842566013336182 +1227 1.4423121213912964 0.84123075008392334 0.72631716728210449 +1228 1.4895111322402954 1.0352237224578857 0.91802585124969482 +1229 1.8004666566848755 1.5668772459030151 -0.43508541584014893 +1230 1.5791407823562622 1.633456826210022 -0.7156948447227478 +1231 0.98534786701202393 2.3470888137817383 0.63934117555618286 +1232 0.73765993118286133 2.3038086891174316 0.92366540431976318 +1233 0.10559680312871933 2.0028326511383057 1.3973833322525024 +1234 0.28473547101020813 2.0023117065429688 1.371785044670105 +1235 0.19459113478660583 2.3797545433044434 -1.4026222229003906 +1236 0.70049399137496948 2.3015105724334717 -1.2349340915679932 +1237 1.2912851572036743 2.2047493457794189 0.24709221720695496 +1238 0.95663726329803467 2.3743293285369873 -0.98148560523986816 +1239 0.87491697072982788 2.3052153587341309 -1.1328624486923218 +1240 0.7564125657081604 2.5419814586639404 -1.0568313598632813 +1241 0 1.9757001399993896 -1.7000435590744019 +1242 0 2.2228240966796875 -1.5214407444000244 +1243 1.4192620515823364 2.1769094467163086 -0.37207546830177307 +1244 1.1935106515884399 2.3599951267242432 -0.60292959213256836 +1245 1.2326503992080688 2.3761637210845947 -0.48922497034072876 +1246 1.4518846273422241 2.1833984851837158 -0.17306710779666901 +1247 1.3186393976211548 2.3368740081787109 -0.16541185975074768 +1248 0.30536255240440369 2.1560659408569336 -1.4806591272354126 +1249 0.11285297572612762 1.9639359712600708 -1.6895427703857422 +1250 0.11164873093366623 2.2201900482177734 -1.4997684955596924 +1251 0.49253687262535095 2.2245516777038574 -1.3695499897003174 +1252 1.0982843637466431 2.1579644680023193 -1.0908458232879639 +1253 1.0471948385238647 2.3343114852905273 -0.89986521005630493 +1254 0.33029836416244507 4.4195427894592285 -0.99995237588882446 +1255 0.18329678475856781 4.2041988372802734 -0.082759693264961243 +1256 0 2.9707200527191162 0.93111801147460938 +1257 0.18002809584140778 2.4064085483551025 1.1748796701431274 +1258 0.75100159645080566 3.0099101066589355 0.36207398772239685 +1259 0.85265946388244629 2.9515180587768555 0.12605947256088257 +1260 0.74717551469802856 3.1535344123840332 0.24685037136077881 +1261 0.083968184888362885 3.9464812278747559 0.44331973791122437 +1262 0 3.7409670352935791 0.5826561450958252 +1263 0.37855678796768188 4.2256922721862793 -0.14092829823493958 +1264 0.5324169397354126 4.0891709327697754 -0.32530012726783752 +1265 0.46082028746604919 4.1658940315246582 -0.16952827572822571 +1266 0.7380097508430481 4.1020054817199707 -1.303091287612915 +1267 0.093789182603359222 2.5671589374542236 -1.5083990097045898 +1268 1.1054728031158447 2.3935885429382324 0.40820431709289551 +1269 1.0341817140579224 2.8420863151550293 -0.75778979063034058 +1270 1.0519633293151855 2.62091064453125 0.1493220180273056 +1271 0.85103905200958252 2.6975197792053223 0.50975865125656128 +1272 0.41557645797729492 3.0600571632385254 0.84811276197433472 +1273 0 2.0335919857025146 1.3635116815567017 +1274 0.61401879787445068 3.0304899215698242 0.65044361352920532 +1275 0.47000738978385925 2.3683967590332031 1.0955507755279541 +1276 0.59522193670272827 2.6674959659576416 -1.2205932140350342 +1277 0.40979582071304321 2.4573347568511963 -1.2981232404708862 +1278 0.45857098698616028 2.6006448268890381 -1.3434805870056152 +1279 0.99687641859054565 2.7359457015991211 -0.81431156396865845 +1280 1.0267006158828735 2.858983039855957 -0.86340230703353882 +1281 1.0389502048492432 2.776669979095459 -0.753406822681427 +1282 0.97102123498916626 2.8072946071624756 -4.4211196899414063 +1283 1.1212629079818726 2.994734525680542 -4.4646849632263184 +1284 1.4467592239379883 2.9416534900665283 -4.1336064338684082 +1285 1.4437682628631592 3.2162458896636963 -4.2692217826843262 +1286 0.529712975025177 3.9480240345001221 -4.5687770843505859 +1287 0.27357670664787292 4.0781211853027344 -4.5488324165344238 +1288 1.0083835124969482 3.6530513763427734 -4.524383544921875 +1289 0.79863613843917847 4.233823299407959 -4.2337007522583008 +1290 0 3.6644439697265625 -4.7775821685791016 +1291 0.35452467203140259 2.4380898475646973 -4.132843017578125 +1292 0.20594330132007599 3.6960172653198242 -4.7500209808349609 +1293 0.84527838230133057 2.6268575191497803 -4.2996530532836914 +1294 0.70014488697052002 2.2884047031402588 -3.6900343894958496 +1295 1.0583950281143188 2.4409806728363037 -3.8216164112091064 +1296 0.4171854555606842 4.401054859161377 -4.2014141082763672 +1297 1.3385555744171143 3.8085753917694092 -4.1993441581726074 +1298 1.2259045839309692 4.2721710205078125 -3.7412376403808594 +1299 1.0929734706878662 4.0315647125244141 -4.2370133399963379 +1300 1.6799062490463257 3.3079597949981689 -3.7682278156280518 +1301 1.4420979022979736 3.5212054252624512 -4.2287187576293945 +1302 0.87765097618103027 4.4836459159851074 -3.7479262351989746 +1303 0.33639243245124817 2.2145652770996094 -3.5827012062072754 +1304 0.87051254510879517 2.2640414237976074 -3.1298630237579346 +1305 0 2.193178653717041 -3.5370156764984131 +1306 0 2.1002798080444336 -2.9076950550079346 +1307 1.3016000986099243 2.675776481628418 -4.0224404335021973 +1308 1.6511741876602173 2.9900872707366943 -3.5902535915374756 +1309 0.52334690093994141 3.5388870239257813 -4.7286128997802734 +1310 0.78408092260360718 3.8437533378601074 -4.5438427925109863 +1311 0.7251250147819519 1.9069355726242065 1.2222356796264648 +1312 0.52495801448822021 1.9427986145019531 1.3105875253677368 +1313 0.68695884943008423 3.2892997264862061 0.51947486400604248 +1314 1.2078872919082642 2.1768524646759033 0.46095630526542664 +1315 1.2095291614532471 1.8343952894210815 0.8223501443862915 +1316 1.4511921405792236 2.1300961971282959 0.01991831511259079 +1317 1.4188553094863892 2.0075371265411377 0.25080773234367371 +1318 1.1115260124206543 2.1267766952514648 0.6948506236076355 +1319 1.1023390293121338 1.8166135549545288 1.0103610754013062 +1320 0.83865392208099365 1.6281812191009521 1.3652533292770386 +1321 0.88620990514755249 2.0237767696380615 1.0178636312484741 +1322 1.018741250038147 2.0509312152862549 0.88641726970672607 +1323 0.20280955731868744 3.1288139820098877 0.86553770303726196 +1324 0.20766082406044006 2.7593531608581543 -4.5493893623352051 +1325 0.59079825878143311 2.5364785194396973 -4.2465720176696777 +1326 1.0609791278839111 3.3971114158630371 -4.5549783706665039 +1327 0.62134349346160889 3.2946183681488037 -4.725214958190918 +1328 1.0572956800460815 3.1853868961334229 -4.556391716003418 +1329 0.64930951595306396 3.0687811374664307 -4.6731410026550293 +1330 0.17167903482913971 3.5326685905456543 0.74957460165023804 +1331 0.32168447971343994 3.5662617683410645 0.75455003976821899 +1332 0.48456475138664246 2.8644590377807617 -4.6037192344665527 +1333 0 3.2310867309570313 -4.7974557876586914 +1334 -0.12185163795948029 -2.2697310447692871 5.4456071853637695 +1335 -0.27440261840820313 -2.747596263885498 5.6492800712585449 +1336 -0.21319736540317535 -2.5442521572113037 5.7126617431640625 +1337 -0.10241593420505524 -2.8508033752441406 6.1404299736022949 +1338 -0.22989620268344879 -2.2534339427947998 5.3609600067138672 +1339 -0.29720619320869446 -2.3508231639862061 5.3810906410217285 +1340 -0.33914715051651001 -2.1690795421600342 5.0355424880981445 +1341 0 -2.5492992401123047 5.8047823905944824 +1342 0 -2.2549686431884766 5.4572358131408691 +1343 -0.11254102736711502 -2.5626621246337891 5.7943534851074219 +1344 -0.31709021329879761 -2.4537017345428467 5.3971447944641113 +1345 -0.29635533690452576 -2.4722685813903809 5.2852649688720703 +1346 -0.27516502141952515 -2.6364591121673584 5.736182689666748 +1347 -0.29288178682327271 -2.7332985401153564 5.7562556266784668 +1348 -0.091512039303779602 -3.1340832710266113 6.483393669128418 +1349 0 -3.4034290313720703 6.8297414779663086 +1350 -0.22525908052921295 -3.1927967071533203 6.4376649856567383 +1351 -0.20795348286628723 -3.5429203510284424 6.8135800361633301 +1352 -0.19715452194213867 -3.5371456146240234 6.7242646217346191 +1353 -0.17592708766460419 -3.803429126739502 7.1577363014221191 +1354 0 -3.1235899925231934 6.4914889335632324 +1355 -0.17531390488147736 -3.1112618446350098 6.4077200889587402 +1356 -0.26655539870262146 -3.0084762573242188 6.1096634864807129 +1357 -0.2382088303565979 -3.2778506278991699 6.4625997543334961 +1358 -0.22471728920936584 -3.2797920703887939 6.3544034957885742 +1359 -0.19498516619205475 -2.8302164077758789 6.0617294311523438 +1360 -0.25115716457366943 -2.9171335697174072 6.0885119438171387 +1361 -0.15684831142425537 -0.70774859189987183 3.7050180435180664 +1362 -0.29244419932365417 -0.72493994235992432 3.5852069854736328 +1363 0 -0.6687665581703186 3.7410027980804443 +1364 -0.37985882163047791 -0.85126090049743652 3.5803530216217041 +1365 0 -0.99685573577880859 4.0819392204284668 +1366 -0.35060638189315796 -1.6156543493270874 4.1798062324523926 +1367 -0.37703415751457214 -1.5849591493606567 4.3065910339355469 +1368 -0.42188751697540283 -0.6799323558807373 3.2104487419128418 +1369 -0.39148008823394775 -0.72436171770095825 3.0698797702789307 +1370 -0.39257797598838806 -0.54810196161270142 3.2254464626312256 +1371 -0.43615216016769409 -0.40118378400802612 2.8676846027374268 +1372 -0.39322400093078613 -1.2855750322341919 3.9401278495788574 +1373 -0.40801692008972168 -0.98185068368911743 3.5732500553131104 +1374 -0.16216140985488892 -0.39318385720252991 3.364124059677124 +1375 -0.14493076503276825 -1.3471364974975586 4.4013609886169434 +1376 -0.31632855534553528 -2.1918010711669922 4.9187440872192383 +1377 -0.13033203780651093 -1.9720528125762939 5.0946559906005859 +1378 -0.31725728511810303 -2.0602834224700928 5.0236639976501465 +1379 -0.35908681154251099 -1.8794823884963989 4.6718769073486328 +1380 -0.24506743252277374 -1.9578189849853516 5.0070886611938477 +1381 -0.25876030325889587 -1.6559864282608032 4.6527175903320313 +1382 -0.33536165952682495 -1.7648968696594238 4.6643462181091309 +1383 -0.15116402506828308 -1.0285429954528809 4.0532569885253906 +1384 -0.28221416473388672 -1.0383154153823853 3.940934419631958 +1385 -0.27108111977577209 -1.3489710092544556 4.2975683212280273 +1386 -0.36636722087860107 -1.1598037481307983 3.9418907165527344 +1387 -0.35165289044380188 -1.4647202491760254 4.3036079406738281 +1388 0 -1.3234941959381104 4.4221477508544922 +1389 -0.13800367712974548 -1.6663656234741211 4.7448883056640625 +1390 1.3966834545135498 3.1949374675750732 -2.1542947292327881 +1391 1.056496262550354 3.0709221363067627 -1.4990473985671997 +1392 1.3159768581390381 3.1913681030273438 -2.0246469974517822 +1393 1.0783112049102783 3.2685270309448242 -1.4448598623275757 +1394 1.0899111032485962 3.3461642265319824 -1.4183264970779419 +1395 1.1173580884933472 3.301177978515625 -1.5174643993377686 +1396 -0.071528822183609009 -4.4940185546875 8.1224184036254883 +1397 -0.070549651980400085 -4.5469145774841309 8.1673192977905273 +1398 1.0203831195831299 3.0459020137786865 -1.3492910861968994 +1399 1.0459957122802734 3.082150936126709 -1.4332528114318848 +1400 0.99231493473052979 2.9879024028778076 -1.3848117589950562 +1401 1.0822157859802246 3.1463034152984619 -1.454004168510437 +1402 1.1272662878036499 3.0665676593780518 -1.6817474365234375 +1403 1.0732429027557373 3.0426111221313477 -1.573643684387207 +1404 1.0557752847671509 3.3823843002319336 -1.3039480447769165 +1405 1.0669467449188232 3.4146997928619385 -1.3225650787353516 +1406 0.94340264797210693 3.4184403419494629 -0.87751483917236328 +1407 1.0153148174285889 3.3290748596191406 -1.2482116222381592 +1408 0.98768222332000732 3.4937360286712646 -1.1134672164916992 +1409 0.96323317289352417 2.8430001735687256 -0.098031066358089447 +1410 0.96122729778289795 3.4844563007354736 -1.013108491897583 +1411 1.263973593711853 3.3007783889770508 -1.8160991668701172 +1412 0.96880120038986206 3.4184904098510742 -1.0527123212814331 +1413 1.3282691240310669 3.359555721282959 -1.932997465133667 +1414 1.1814180612564087 3.2945537567138672 -1.6519660949707031 +1415 1.2956751585006714 3.2564988136291504 -1.9088268280029297 +1416 1.1076499223709106 3.4904882907867432 -1.4175270795822144 +1417 1.0467238426208496 3.4673953056335449 -1.2737455368041992 +1418 1.1892489194869995 3.3981387615203857 -1.6153746843338013 +1419 -0.033853251487016678 -4.6827225685119629 8.3792581558227539 +1420 -0.044122114777565002 -4.6919307708740234 8.3792581558227539 +1421 -0.10842645913362503 -3.9243896007537842 7.4231419563293457 +1422 -0.13738405704498291 -3.9891488552093506 7.4621171951293945 +1423 -0.16824546456336975 -3.7288541793823242 7.1248354911804199 +1424 -0.067562311887741089 -3.6858313083648682 7.1582355499267578 +1425 -0.054624829441308975 -3.9541923999786377 7.4893145561218262 +1426 -0.083579860627651215 -4.273777961730957 7.6846208572387695 +1427 -0.041112352162599564 -4.2174630165100098 7.8156819343566895 +1428 -0.13193675875663757 -3.6584055423736572 7.0888147354125977 +1429 -0.1542656421661377 -3.3873462677001953 6.7502198219299316 +1430 -0.19757997989654541 -3.4633846282958984 6.7832345962524414 +1431 -0.079875744879245758 -3.412445068359375 6.8228030204772949 +1432 -0.16788069903850555 -3.7999167442321777 7.0706338882446289 +1433 -0.10804598033428192 -4.0342273712158203 7.3387656211853027 +1434 0 -3.678321361541748 7.1640176773071289 +1435 -0.13711421191692352 -4.050382137298584 7.4154324531555176 +1436 -0.045414518564939499 -4.70477294921875 8.3861827850341797 +1437 -0.071441024541854858 -4.528803825378418 8.0956497192382813 +1438 -0.10711798816919327 -4.3055005073547363 7.834813117980957 +1439 -0.032272528856992722 -4.7075066566467285 8.3597698211669922 +1440 -0.027065567672252655 -4.4756016731262207 8.1369094848632813 +1441 -0.058223653584718704 -4.4409022331237793 8.0774459838867188 +1442 -0.14227741956710815 -4.0571274757385254 7.4985942840576172 +1443 -0.10511210560798645 -4.2442178726196289 7.794766902923584 +1444 -0.083829700946807861 -4.1852331161499023 7.7527923583984375 +1445 -0.10493579506874084 -4.2939009666442871 7.7567896842956543 +1446 -0.11254102736711502 -2.5626599788665771 -5.895169734954834 +1447 -0.27516502141952515 -2.6364569664001465 -5.8333511352539063 +1448 -0.29288178682327271 -2.7332963943481445 -5.8486495018005371 +1449 -0.21319736540317535 -2.5442502498626709 -5.81439208984375 +1450 -0.19498516619205475 -2.830214262008667 -6.1493539810180664 +1451 -0.26655539870262146 -3.0084741115570068 -6.1885719299316406 +1452 -0.22989620268344879 -2.2534322738647461 -5.4771604537963867 +1453 -0.24506743252277374 -1.9578173160552979 -5.1381077766418457 +1454 -0.29635533690452576 -2.4722669124603271 -5.3905644416809082 +1455 -0.33914715051651001 -2.1690778732299805 -5.1559534072875977 +1456 -0.31709021329879761 -2.453700065612793 -5.5033650398254395 +1457 -0.29720619320869446 -2.3508214950561523 -5.492429256439209 +1458 -0.12185163795948029 -2.2697296142578125 -5.5609931945800781 +1459 -0.10241593420505524 -2.8508012294769287 -6.2270474433898926 +1460 -0.2382088303565979 -3.277848482131958 -6.528437614440918 +1461 -0.22525908052921295 -3.1927945613861084 -6.5076155662536621 +1462 -0.1542656421661377 -3.3873443603515625 -6.8107829093933105 +1463 -0.22471728920936584 -3.279789924621582 -6.4201488494873047 +1464 -0.15359748899936676 -3.5371577739715576 -6.6812925338745117 +1465 -0.19715452194213867 -3.5371434688568115 -6.7776603698730469 +1466 -0.16788069903850555 -3.7999145984649658 -7.1115660667419434 +1467 -0.091512039303779602 -3.1340811252593994 -6.5561847686767578 +1468 -0.079875744879245758 -3.4124429225921631 -6.882166862487793 +1469 -0.25115716457366943 -2.9171314239501953 -6.171882152557373 +1470 0 -2.5492970943450928 -5.9062628746032715 +1471 -0.25050902366638184 -3.0152199268341064 -6.0862717628479004 +1472 -0.17531390488147736 -3.111259937286377 -6.4816246032714844 +1473 -0.3787616491317749 -1.019582986831665 -3.6145391464233398 +1474 -0.4080197811126709 -0.98137116432189941 -3.7535591125488281 +1475 -0.36636722087860107 -1.1598024368286133 -4.1133913993835449 +1476 -0.39322400093078613 -1.2855737209320068 -4.1052107810974121 +1477 -0.15116402506828308 -1.0285416841506958 -4.2314672470092773 +1478 0 -0.99685442447662354 -4.2617697715759277 +1479 -0.42192822694778442 -0.67242169380187988 -3.4019362926483154 +1480 -0.16218568384647369 -0.38029155135154724 -3.5670185089111328 +1481 0 -0.33434811234474182 -3.6129255294799805 +1482 -0.15685117244720459 -0.70655590295791626 -3.8989534378051758 +1483 -0.37986379861831665 -0.85021752119064331 -3.7670149803161621 +1484 -0.29245135188102722 -0.72312557697296143 -3.7778868675231934 +1485 -0.35908681154251099 -1.8794807195663452 -4.8068432807922363 +1486 -0.25876030325889587 -1.6559847593307495 -4.7989702224731445 +1487 -0.13800367712974548 -1.6663640737533569 -4.8906197547912598 +1488 -0.13033203780651093 -1.9720511436462402 -5.2249608039855957 +1489 0 -1.6485607624053955 -4.9060673713684082 +1490 -0.31725728511810303 -2.0602817535400391 -5.149528980255127 +1491 -0.33536165952682495 -1.7648952007293701 -4.8050947189331055 +1492 -0.27108111977577209 -1.3489696979522705 -4.4594249725341797 +1493 -0.14493076503276825 -1.3471349477767944 -4.5633096694946289 +1494 -0.28221416473388672 -1.0383141040802002 -4.118645191192627 +1495 -0.35165289044380188 -1.4647188186645508 -4.4595675468444824 +1496 -0.37703415751457214 -1.5849577188491821 -4.4564552307128906 +1497 -0.54028916358947754 0.24448814988136292 2.1006646156311035 +1498 -0.32650861144065857 0.68689924478530884 2.2018015384674072 +1499 -0.37000781297683716 0.27889013290405273 2.4298655986785889 +1500 -0.46833205223083496 0.15868677198886871 2.3810749053955078 +1501 -0.49582269787788391 0.055357705801725388 2.316272497177124 +1502 -0.1998392641544342 0.36611628532409668 2.5314521789550781 +1503 -0.23027308285236359 0.64066880941390991 2.2723000049591064 +1504 -0.042190965265035629 -4.7099766731262207 8.3821134567260742 +1505 -0.016172453761100769 -4.6919207572937012 -8.3963909149169922 +1506 -0.58778446912765503 0.44587075710296631 1.8785964250564575 +1507 -0.53999006748199463 0.40797597169876099 1.6739884614944458 +1508 -0.016172453761100769 -4.6919240951538086 8.3963899612426758 +1509 -0.42999172210693359 -0.041582480072975159 2.6129655838012695 +1510 -0.45825973153114319 -0.16298845410346985 2.568129301071167 +1511 -0.40658283233642578 -0.27343299984931946 2.8954458236694336 +1512 -0.16775363683700562 -0.10457079857587814 3.0454521179199219 +1513 -0.17806646227836609 0.14495529234409332 2.7656486034393311 +1514 -0.30212172865867615 -0.42044395208358765 3.2386384010314941 +1515 -0.31351959705352783 -0.14858539402484894 2.9205801486968994 +1516 -0.40542212128639221 -0.45327401161193848 2.7257022857666016 +1517 0 -0.047646123915910721 3.0934436321258545 +1518 0 0.49443241953849792 2.5255486965179443 +1519 -0.3347327709197998 0.077794477343559265 2.6526334285736084 +1520 -0.067562311887741089 -3.6858291625976563 -7.2045650482177734 +1521 -0.13738405704498291 -3.9891462326049805 -7.4941697120666504 +1522 -0.14227813482284546 -4.0571250915527344 -7.5275058746337891 +1523 -0.13711421191692352 -4.0503792762756348 -7.4446291923522949 +1524 -0.10493579506874084 -4.2938985824584961 -7.7747793197631836 +1525 -0.19757997989654541 -3.4633824825286865 -6.8401503562927246 +1526 -0.13193675875663757 -3.6584033966064453 -7.1364436149597168 +1527 -0.20795348286628723 -3.5429182052612305 -6.8666982650756836 +1528 -0.16824546456336975 -3.7288520336151123 -7.1690874099731445 +1529 -0.17592708766460419 -3.80342698097229 -7.1984977722167969 +1530 -0.027065567672252655 -4.4755992889404297 -8.1466188430786133 +1531 -0.10511210560798645 -4.2442226409912109 -7.8149685859680176 +1532 -0.083829700946807861 -4.1852307319641113 -7.7757792472839355 +1533 -0.058223653584718704 -4.4408993721008301 -8.0887260437011719 +1534 -0.071441024541854858 -4.5288019180297852 -8.1029319763183594 +1535 -0.071528822183609009 -4.4940166473388672 -8.1312704086303711 +1536 -0.045414593070745468 -4.7047700881958008 -8.3856124877929688 +1537 -0.054624829441308975 -3.9541897773742676 -7.5230088233947754 +1538 -0.041112422943115234 -4.2174601554870605 -7.8371696472167969 +1539 0 -3.9482145309448242 -7.5279335975646973 +1540 0 -4.2130417823791504 -7.8408102989196777 +1541 -0.10842645913362503 -3.9243869781494141 -7.4581923484802246 +1542 -0.083579860627651215 -4.273775577545166 -7.7035384178161621 +1543 -0.070549651980400085 -4.5469112396240234 -8.1738157272338867 +1544 -0.10711798816919327 -4.3054981231689453 -7.8523030281066895 +1545 1.5519006252288818 1.7030847072601318 0.38720092177391052 +1546 1.6362621784210205 1.6973453760147095 0.23749816417694092 +1547 1.5731301307678223 1.8429757356643677 0.2032267302274704 +1548 1.5878709554672241 1.926152229309082 0.078563310205936432 +1549 1.595623254776001 1.9714524745941162 -0.012935157865285873 +1550 1.6787141561508179 1.8727927207946777 -0.13188646733760834 +1551 1.5890130996704102 2.0073370933532715 -0.1814347505569458 +1552 1.6602399349212646 1.8460522890090942 0.031739518046379089 +1553 1.6694628000259399 1.7751467227935791 0.095225006341934204 +1554 1.5274301767349243 1.5348397493362427 0.60083013772964478 +1555 1.4155787229537964 1.5367456674575806 0.77956974506378174 +1556 1.418476939201355 1.319117546081543 1.0257728099822998 +1557 1.3060330152511597 1.1118613481521606 1.4421265125274658 +1558 1.3805792331695557 1.3935567140579224 1.0046145915985107 +1559 1.317540168762207 1.3596993684768677 1.1501666307449341 +1560 1.210057258605957 1.5419566631317139 1.1390806436538696 +1561 1.2576347589492798 1.5455329418182373 1.0472238063812256 +1562 1.2752808332443237 1.3994172811508179 1.1830458641052246 +1563 1.251816987991333 1.3599348068237305 1.2708338499069214 +1564 1.6825474500656128 1.8516631126403809 -0.033048242330551147 +1565 1.3795654773712158 1.8981626033782959 -0.88932907581329346 +1566 1.3454867601394653 1.7900946140289307 -1.0575594902038574 +1567 1.2872161865234375 1.9317773580551147 -1.0289630889892578 +1568 1.2186235189437866 1.7911797761917114 -1.2800847291946411 +1569 1.2844251394271851 1.7819783687591553 -1.1792764663696289 +1570 1.2436007261276245 1.8236021995544434 -1.2150610685348511 +1571 1.226739764213562 1.9484241008758545 -1.1255885362625122 +1572 1.6170313358306885 1.8443034887313843 -0.38494104146957397 +1573 1.6655867099761963 1.8937580585479736 -0.085579730570316315 +1574 1.6580413579940796 1.8545613288879395 -0.25330910086631775 +1575 1.6518881320953369 1.8970990180969238 -0.20167066156864166 +1576 1.4878478050231934 1.8560676574707031 -0.72819411754608154 +1577 1.5230613946914673 1.923525333404541 -0.54645711183547974 +1578 1.5537352561950684 1.9770561456680298 -0.38686051964759827 +1579 1.2098716497421265 1.3807146549224854 1.3094525337219238 +1580 0.9539104700088501 3.0871403217315674 -0.75980275869369507 +1581 0.97484731674194336 3.0313537120819092 -0.78697872161865234 +1582 0.74973815679550171 3.0658111572265625 -1.0831789970397949 +1583 1.0373226404190063 1.0097037553787231 1.678014874458313 +1584 0.93216687440872192 3.151392936706543 -0.74626839160919189 +1585 1.0250658988952637 2.9186959266662598 -0.9092593789100647 +1586 1.0517064332962036 2.8052525520324707 -0.96669489145278931 +1587 1.0699735879898071 2.8016407489776611 -1.0863128900527954 +1588 0.7813112735748291 2.9817278385162354 -1.1442195177078247 +1589 0.76682031154632568 3.0156638622283936 -1.1120753288269043 +1590 1.0035008192062378 2.9644460678100586 -0.84863293170928955 +1591 1.0024657249450684 1.1954734325408936 1.5706819295883179 +1592 1.1355540752410889 1.3167475461959839 1.4260226488113403 +1593 1.1908693313598633 1.3217943906784058 1.3819286823272705 +1594 1.0480444431304932 1.4215247631072998 1.4116815328598022 +1595 1.1768139600753784 0.93863403797149658 1.6598831415176392 +1596 1.2662007808685303 0.83445632457733154 1.6990087032318115 +1597 0.90070080757141113 0.96096289157867432 1.7896734476089478 +1598 1.0146510601043701 1.1077710390090942 1.6187304258346558 +1599 1.2440218925476074 0.69808948040008545 1.821168065071106 +1600 0.84841924905776978 1.2396243810653687 1.6286742687225342 +1601 0.96840852499008179 1.3455437421798706 1.5030030012130737 +1602 1.1092917919158936 1.2449780702590942 1.4843074083328247 +1603 1.6445997953414917 1.7171046733856201 -0.57089406251907349 +1604 1.69805908203125 1.7066895961761475 -0.40123158693313599 +1605 1.4260008335113525 1.5200779438018799 -1.2252902984619141 +1606 1.651980996131897 1.7732264995574951 -0.3940696120262146 +1607 1.3848907947540283 1.7147130966186523 -1.0643553733825684 +1608 0.9401547908782959 1.5420213937759399 -1.5584890842437744 +1609 1.5046588182449341 1.6710904836654663 -0.91949599981307983 +1610 1.428884744644165 1.6387178897857666 -1.0776684284210205 +1611 1.5308494567871094 1.4821515083312988 -1.1055225133895874 +1612 1.4553325176239014 1.2408878803253174 1.0429476499557495 +1613 1.5413286685943604 1.1003756523132324 1.0539979934692383 +1614 1.4462953805923462 1.1048586368560791 1.2193444967269897 +1615 1.5277156829833984 1.3077389001846313 0.83706939220428467 +1616 1.8072338104248047 1.4916526079177856 0.16658890247344971 +1617 1.6663861274719238 1.4483938217163086 0.45825713872909546 +1618 1.6703122854232788 1.6113991737365723 0.26532214879989624 +1619 1.8022725582122803 1.6155107021331787 -0.28138658404350281 +1620 1.7125859260559082 1.527551531791687 0.28486847877502441 +1621 0.81000041961669922 1.3941994905471802 -1.6956247091293335 +1622 1.4313259124755859 0.78988420963287354 1.6603829860687256 +1623 1.4917097091674805 0.63123774528503418 1.763225793838501 +1624 1.3507479429244995 0.83112978935241699 1.6621532440185547 +1625 1.5114401578903198 0.85367286205291748 1.5126252174377441 +1626 1.6118701696395874 0.72691643238067627 1.5415929555892944 +1627 1.4871196746826172 0.93397986888885498 1.4162499904632568 +1628 1.4604650735855103 0.85505771636962891 1.5799118280410767 +1629 0.3139471709728241 1.3151133060455322 -1.9682328701019287 +1630 0.965781569480896 1.4541192054748535 -1.5950876474380493 +1631 0.37447503209114075 1.010467529296875 1.9440624713897705 +1632 0.29557722806930542 1.4043645858764648 -1.9298210144042969 +1633 0.15806610882282257 1.2829114198684692 1.8486009836196899 +1634 1.077475905418396 1.749769926071167 -1.3947486877441406 +1635 1.0512353181838989 1.892216682434082 -1.3344293832778931 +1636 1.0448107719421387 1.6799923181533813 -1.4412908554077148 +1637 0.97754561901092529 1.8376792669296265 -1.3951199054718018 +1638 0.25866317749023438 1.6259403228759766 -1.8138080835342407 +1639 0.27470102906227112 1.4837077856063843 -1.8930511474609375 +1640 1.1451693773269653 1.7588642835617065 -1.3590495586395264 +1641 1.1713529825210571 1.8105677366256714 -1.3105370998382568 +1642 1.1183576583862305 1.9288723468780518 -1.268805980682373 +1643 1.1773778200149536 1.9476175308227539 -1.1989568471908569 +1644 0.9238506555557251 1.6220356225967407 -1.5230541229248047 +1645 0.74711835384368896 1.6460777521133423 -1.5875780582427979 +1646 1.2709193229675293 1.567298412322998 -1.39150071144104 +1647 0.88144856691360474 1.7580362558364868 -1.4723430871963501 +1648 0.35700878500938416 1.1111546754837036 1.8807380199432373 +1649 0.34531685709953308 1.1824171543121338 1.844603419303894 +1650 0.35262230038642883 1.2890076637268066 1.7940208911895752 +1651 0.2370988130569458 1.3578646183013916 1.7915225028991699 +1652 0.2077728807926178 1.2234629392623901 1.8638699054718018 +1653 0 1.6258118152618408 -1.9169149398803711 +1654 0.15323841571807861 1.4898681640625 -1.9475172758102417 +1655 0.10780756175518036 1.5425924062728882 -1.9396649599075317 +1656 0.16171099245548248 1.6635524034500122 -1.8413978815078735 +1657 0.048829950392246246 1.3424813747406006 1.8230527639389038 +1658 0 1.3036128282546997 1.8428188562393188 +1659 0.046488847583532333 1.6815698146820068 -1.8756122589111328 +1660 0.098539076745510101 1.6835827827453613 -1.8600717782974243 +1661 0.057944469153881073 1.5231188535690308 -1.9749857187271118 +1662 0.08834616094827652 1.2946469783782959 1.8544831275939941 +1663 0.15083064138889313 1.4113739728927612 1.7779452800750732 +1664 1.1113904714584351 2.3251099586486816 -0.8068091869354248 +1665 1.1566264629364014 2.3382587432861328 -0.70812374353408813 +1666 0.91770446300506592 2.6923162937164307 -0.9053688645362854 +1667 1.0668326616287231 2.5395898818969727 -0.76467835903167725 +1668 1.0815377235412598 2.4631590843200684 -0.77823424339294434 +1669 1.0620428323745728 2.6155710220336914 -0.75083702802658081 +1670 1.1231759786605835 2.5623612403869629 -0.66341465711593628 +1671 1.2697913646697998 2.3780481815338135 -0.3731997013092041 +1672 0.81121397018432617 2.6227953433990479 -0.99649769067764282 +1673 0.9447019100189209 2.5126783847808838 -0.94724279642105103 +1674 0.86532300710678101 2.5345077514648438 -0.99534845352172852 +1675 0.83230078220367432 2.4524731636047363 -1.0450102090835571 +1676 1.0355879068374634 2.4862878322601318 -0.85031771659851074 +1677 1.0085905790328979 2.4577412605285645 -0.90143567323684692 +1678 1.2520883083343506 2.4837677478790283 -0.24812938272953033 +1679 1.2349703311920166 2.4828040599822998 -0.14658504724502563 +1680 1.300686240196228 2.359616756439209 -0.26443636417388916 +1681 1.2540868520736694 2.4707901477813721 -0.29963359236717224 +1682 1.3169475793838501 2.322390079498291 -0.07347223162651062 +1683 1.1601957082748413 2.6746625900268555 -0.37038144469261169 +1684 1.2086082696914673 2.4929192066192627 -0.078184254467487335 +1685 1.2324646711349487 2.3985352516174316 0.059151533991098404 +1686 1.2900786399841309 2.3362243175506592 0.0039292946457862854 +1687 1.2649301290512085 2.4424078464508057 -0.16697587072849274 +1688 1.1565407514572144 2.5943198204040527 -0.14065660536289215 +1689 1.1508586406707764 2.4974091053009033 0.1037605032324791 +1690 1.1653925180435181 2.6175127029418945 -0.49441316723823547 +1691 1.1758930683135986 2.5037193298339844 -0.56756317615509033 +1692 1.1162660121917725 2.7225968837738037 -0.56378328800201416 +1693 1.2133196592330933 2.5436804294586182 -0.38565334677696228 +1694 1.1691471338272095 2.5678865909576416 -0.5401110053062439 +1695 0.84188765287399292 3.2423715591430664 -0.30968931317329407 +1696 0.80005669593811035 3.3557150363922119 -0.23120696842670441 +1697 0.81578254699707031 3.0961775779724121 -0.10572550445795059 +1698 0.86720752716064453 3.4454090595245361 -0.62345302104949951 +1699 0.80808019638061523 3.579268217086792 -0.60054868459701538 +1700 0.83872538805007935 3.18373703956604 -0.29317748546600342 +1701 0.85518646240234375 3.060513973236084 -0.21384429931640625 +1702 0.94837105274200439 2.7714662551879883 0.16652555763721466 +1703 0.88006377220153809 2.9341645240783691 -0.032312307506799698 +1704 0.8422088623046875 2.9896726608276367 0.016043024137616158 +1705 0.8757164478302002 3.1794540882110596 -0.42289349436759949 +1706 0.90339910984039307 3.0225090980529785 -0.30868640542030334 +1707 0.912300705909729 2.927325963973999 -0.14121261239051819 +1708 0.84623491764068604 3.4419541358947754 -0.48140186071395874 +1709 0.85529351234436035 3.3775656223297119 -0.40924912691116333 +1710 0.94148963689804077 3.1469244956970215 -0.55580109357833862 +1711 0.90882432460784912 3.1254520416259766 -0.45653033256530762 +1712 0.93696385622024536 3.3256981372833252 -0.73775225877761841 +1713 1.0971779823303223 2.7159082889556885 -0.23237060010433197 +1714 1.0632134675979614 2.723182201385498 -0.14525581896305084 +1715 1.068809986114502 2.8158814907073975 -0.40156331658363342 +1716 1.0723220109939575 2.8273172378540039 -0.59852516651153564 +1717 1.0250301361083984 2.7313625812530518 -0.035286124795675278 +1718 1.076419472694397 2.800612211227417 -0.64176523685455322 +1719 0.78958463668823242 3.1329045295715332 0.047867141664028168 +1720 1.0099682807922363 2.9102938175201416 -0.77511465549468994 +1721 1.04600989818573 2.8699333667755127 -0.54674750566482544 +1722 0.9749329686164856 2.965423583984375 -0.43144243955612183 +1723 0.89613944292068481 3.2867653369903564 -0.78705000877380371 +1724 0.91090160608291626 3.3570930957794189 -1.0709152221679688 +1725 0.72042793035507202 3.2293586730957031 -1.1060289144515991 +1726 0.88902962207794189 3.3628895282745361 -0.90927356481552124 +1727 0.9374421238899231 3.3831336498260498 -0.80010610818862915 +1728 0.95440298318862915 3.4300904273986816 -0.9653313159942627 +1729 0.94945609569549561 3.2745518684387207 -1.1853508949279785 +1730 0.98497664928436279 3.3840262889862061 -1.1288076639175415 +1731 0.89087128639221191 3.4424893856048584 -0.72117692232131958 +1732 0.88129866123199463 3.6642224788665771 -0.98116415739059448 +1733 0.93602162599563599 3.7731969356536865 -1.2912344932556152 +1734 0.83451366424560547 3.6997501850128174 -0.917411208152771 +1735 0.72419697046279907 3.2678275108337402 -1.1612017154693604 +1736 0.73139959573745728 3.1442046165466309 -1.0747342109680176 +1737 1.042690634727478 2.8932759761810303 -1.1710383892059326 +1738 0.81985855102539063 2.9863748550415039 -1.2840033769607544 +1739 1.0539121627807617 2.9585354328155518 -1.2358263731002808 +1740 1.0626709461212158 2.9287967681884766 -1.2680063247680664 +1741 1.0459885597229004 2.8523516654968262 -1.2011411190032959 +1742 0.81243467330932617 2.9530887603759766 -1.196358323097229 +1743 1.0483013391494751 2.8761796951293945 -0.99472731351852417 +1744 1.0600368976593018 2.8652150630950928 -1.0706226825714111 +1745 1.0126094818115234 3.0859270095825195 -1.2567846775054932 +1746 0.98042243719100952 3.1815743446350098 -1.2631878852844238 +1747 1.0366086959838867 3.0127227306365967 -1.2497390508651733 +1748 1.0599654912948608 3.1147804260253906 -1.3290109634399414 +1749 0.94239616394042969 3.2499814033508301 -0.69809705018997192 +1750 0.95198303461074829 3.1574463844299316 -0.60960817337036133 +1751 0.95051974058151245 3.1773123741149902 -0.68048232793807983 +1752 0.96790885925292969 3.0380208492279053 -0.5842055082321167 +1753 0.95808637142181396 3.1250882148742676 -0.67967361211776733 +1754 0.9624408483505249 3.0749979019165039 -0.59896975755691528 +1755 0.96556025743484497 3.0837855339050293 -0.68901485204696655 +1756 1.312036395072937 3.4826431274414063 -1.8565524816513062 +1757 0.95909291505813599 3.0934791564941406 -0.57928067445755005 +1758 0.9734196662902832 3.0447525978088379 -0.70295697450637817 +1759 0.97266304492950439 3.0212814807891846 -0.61490201950073242 +1760 0.98620450496673584 2.9670867919921875 -0.65202665328979492 +1761 1.0023372173309326 2.9332935810089111 -0.6094861626625061 +1762 0.97787398099899292 2.9919924736022949 -0.60591191053390503 +1763 0.99372124671936035 2.9624898433685303 -0.75625503063201904 +1764 0.98251396417617798 3.005291223526001 -0.72448199987411499 +1765 0.95760095119476318 3.6927618980407715 -1.2168095111846924 +1766 1.1029385328292847 3.6753439903259277 -1.5059002637863159 +1767 1.188021183013916 3.5963435173034668 -1.5963937044143677 +1768 0.88349735736846924 3.4905450344085693 -0.75122231245040894 +1769 0.94537287950515747 3.5090267658233643 -0.96054142713546753 +1770 0.99286466836929321 3.5780975818634033 -1.150572657585144 +1771 0.88960063457489014 3.5866994857788086 -0.8753090500831604 +1772 0.90449845790863037 3.5282144546508789 -0.84809744358062744 +1773 1.3764889240264893 3.4346089363098145 -2.0009548664093018 +1774 1.4519345760345459 3.330702543258667 -2.1918997764587402 +1775 1.2116278409957886 3.5222823619842529 -1.6334775686264038 +1776 4.596895694732666 -3.5371580123901367 -4.840815544128418 +1777 4.4245891571044922 -3.2778487205505371 -4.7943873405456543 +1778 4.6753826141357422 -3.2797901630401611 -4.3904542922973633 +1779 4.9563064575195313 -3.4633827209472656 -4.7053432464599609 +1780 4.7361302375793457 -3.1927947998046875 -4.4525370597839355 +1781 4.903618335723877 -3.3873445987701416 -4.7157363891601563 +1782 4.9130620956420898 -3.5371437072753906 -4.6609349250793457 +1783 4.9837541580200195 -3.5429184436798096 -4.7162284851074219 +1784 4.6810359954833984 -3.1112601757049561 -4.4700546264648438 +1785 4.6748967170715332 -3.1340813636779785 -4.5818772315979004 +1786 4.5122981071472168 -2.9171316623687744 -4.1987957954406738 +1787 4.4172224998474121 -2.8896703720092773 -4.4595823287963867 +1788 5.2000751495361328 -3.8034272193908691 -4.9716620445251465 +1789 5.0499482154846191 -3.7876510620117188 -4.8848514556884766 +1790 5.1328749656677246 -3.7999148368835449 -4.9159259796142578 +1791 4.9019198417663574 -3.4124431610107422 -4.818636417388916 +1792 5.1226811408996582 -3.6584036350250244 -4.959904670715332 +1793 4.545478343963623 -3.1340813636779785 -4.7112960815429688 +1794 4.653839111328125 -3.1732141971588135 -4.6892886161804199 +1795 4.8880348205566406 -3.4518539905548096 -4.9167675971984863 +1796 4.43310546875 -3.1112601757049561 -4.717984676361084 +1797 4.689666748046875 -3.5429184436798096 -5.0103168487548828 +1798 4.8640928268432617 -3.7876510620117188 -5.0707073211669922 +1799 4.6342368125915527 -3.5371437072753906 -4.939753532409668 +1800 4.417564868927002 -3.1927947998046875 -4.7711014747619629 +1801 3.9944155216217041 -2.2534325122833252 -3.7273974418640137 +1802 4.2261848449707031 -2.5442507266998291 -3.9755420684814453 +1803 4.054527759552002 -2.3508217334747314 -3.689877986907959 +1804 4.2124505043029785 -2.5626604557037354 -4.1036982536315918 +1805 3.9379580020904541 -2.308091402053833 -3.9946944713592529 +1806 4.0532932281494141 -2.5626604557037354 -4.262855052947998 +1807 3.8052554130554199 -2.2697298526763916 -4.035275936126709 +1808 3.669297456741333 -2.2534325122833252 -4.0525150299072266 +1809 3.3724331855773926 -2.0602819919586182 -3.8840348720550537 +1810 3.6296939849853516 -2.453700065612793 -4.1312375068664551 +1811 4.0781278610229492 -2.4537003040313721 -3.6828038692474365 +1812 3.6342122554779053 -2.3508214950561523 -4.1101932525634766 +1813 3.9246737957000732 -2.54425048828125 -4.2770466804504395 +1814 4.3001456260681152 -2.8508014678955078 -4.4882931709289551 +1815 4.1793928146362305 -2.8302145004272461 -4.498957633972168 +1816 4.1571140289306641 -2.9171316623687744 -4.5539870262145996 +1817 4.3575887680053711 -3.2797901630401611 -4.7082552909851074 +1818 4.7614641189575195 -3.2778487205505371 -4.4575052261352539 +1819 3.8958773612976074 -2.6364572048187256 -4.3336038589477539 +1820 4.1595978736877441 -3.0084743499755859 -4.576016902923584 +1821 4.4530000686645508 -3.0152201652526855 -4.1380119323730469 +1822 3.8958556652069092 -2.7332966327667236 -4.3562469482421875 +1823 4.5365695953369141 -3.0084743499755859 -4.1990528106689453 +1824 4.4449834823608398 -2.8508014678955078 -4.3434548377990723 +1825 4.1784930229187012 -2.6012721061706543 -4.2279415130615234 +1826 4.2850193977355957 -2.6364574432373047 -3.9444615840911865 +1827 4.310053825378418 -2.7332968711853027 -3.9420490264892578 +1828 4.455141544342041 -2.8302145004272461 -4.2232089042663574 +1829 5.5631766319274902 -4.2174606323242188 -5.5157790184020996 +1830 5.7949824333190918 -4.516237735748291 -5.7989087104797363 +1831 5.7379679679870605 -4.4755992889404297 -5.7810912132263184 +1832 5.5494499206542969 -4.1852312088012695 -5.4423818588256836 +1833 5.5732421875 -4.2577857971191406 -5.5830583572387695 +1834 5.6959724426269531 -4.4940166473388672 -5.8015570640563965 +1835 5.4308953285217285 -4.1852312088012695 -5.5609359741210938 +1836 5.6377878189086914 -4.5062017440795898 -5.7240910530090332 +1837 5.4445514678955078 -4.2442154884338379 -5.6033306121826172 +1838 5.4704995155334473 -4.3054986000061035 -5.6307072639465332 +1839 5.6219830513000488 -4.3054986000061035 -5.4792227745056152 +1840 5.7567987442016602 -4.4408998489379883 -5.6800904273986816 +1841 5.6744575500488281 -4.4408993721008301 -5.7624316215515137 +1842 5.9485650062561035 -4.6919212341308594 -5.9256792068481445 +1843 0.0421910360455513 -4.7099733352661133 -8.3813285827636719 +1844 0.033853322267532349 -4.6827192306518555 -8.3796157836914063 +1845 5.7275815010070801 -4.5469117164611816 -5.8305892944335938 +1846 5.8976106643676758 -4.704770565032959 -5.9615359306335449 +1847 5.9337882995605469 -4.7075042724609375 -5.8877887725830078 +1848 5.7775859832763672 -4.5288019180297852 -5.6802043914794922 +1849 5.827354907989502 -4.5469117164611816 -5.7308225631713867 +1850 5.6765561103820801 -4.5288019180297852 -5.7812337875366211 +1851 5.8881525993347168 -4.7075042724609375 -5.9334244728088379 +1852 5.9566025733947754 -4.7099738121032715 -5.8965187072753906 +1853 5.7762441635131836 -4.4755992889404297 -5.7428150177001953 +1854 5.7971305847167969 -4.4940166473388672 -5.7003989219665527 +1855 5.9562239646911621 -4.6919283866882324 -5.8938136100769043 +1856 5.172642707824707 -3.7288522720336914 -4.9568567276000977 +1857 5.3507742881774902 -4.0503802299499512 -5.1714801788330078 +1858 5.4131350517272949 -4.0571255683898926 -5.2263741493225098 +1859 5.1257939338684082 -3.6858294010162354 -5.0534110069274902 +1860 4.6854548454284668 -3.3873445987701416 -4.9339070320129395 +1861 4.7889542579650879 -3.4124431610107422 -4.9316010475158691 +1862 4.9360976219177246 -3.6584036350250244 -5.1464958190917969 +1863 5.1195192337036133 -3.7255403995513916 -5.1417412757873535 +1864 4.6768813133239746 -3.4633827209472656 -4.9847607612609863 +1865 4.8954520225524902 -3.7999148368835449 -5.1533412933349609 +1866 5.1568665504455566 -4.0503802299499512 -5.365386962890625 +1867 5.338017463684082 -3.9243874549865723 -5.2022247314453125 +1868 5.3849959373474121 -3.9891469478607178 -5.2067360877990723 +1869 5.59320068359375 -4.2442231178283691 -5.4546809196472168 +1870 5.3462915420532227 -3.9541902542114258 -5.285886287689209 +1871 5.2690396308898926 -3.9541902542114258 -5.3631386756896973 +1872 5.1846780776977539 -3.9243874549865723 -5.3555645942687988 +1873 5.3480048179626465 -3.9942007064819336 -5.3639240264892578 +1874 5.5050349235534668 -4.2174606323242188 -5.573920726776123 +1875 4.9347057342529297 -3.7288522720336914 -5.1947870254516602 +1876 4.9512815475463867 -3.8034272193908691 -5.2204632759094238 +1877 5.4170327186584473 -4.2938990592956543 -5.5744280815124512 +1878 5.0302467346191406 -3.6858294010162354 -5.1489582061767578 +1879 5.1907033920288086 -3.9891469478607178 -5.401029109954834 +1880 5.2119255065917969 -4.0571255683898926 -5.427584171295166 +1881 5.5654330253601074 -4.2938990592956543 -5.426020622253418 +1882 5.3814406394958496 -4.2737760543823242 -5.5090832710266113 +1883 5.2762703895568848 -4.0342254638671875 -5.1384577751159668 +1884 3.977583646774292 -2.2697298526763916 -3.862947940826416 +1885 3.6959452629089355 -2.0101776123046875 -3.760141134262085 +1886 2.093576192855835 0.25874128937721252 -1.7548023462295532 +1887 2.3900907039642334 -0.35848474502563477 -1.8802025318145752 +1888 2.1468358039855957 -0.034892123192548752 -1.6222138404846191 +1889 1.8462164402008057 0.28600218892097473 -2.2130799293518066 +1890 1.6376327276229858 0.24933724105358124 -2.2152500152587891 +1891 2.037989616394043 0.27153405547142029 -2.1601130962371826 +1892 1.601476788520813 0.63592392206192017 -1.9799542427062988 +1893 2.0907349586486816 0.28804230690002441 -1.9675834178924561 +1894 1.801073431968689 0.62679463624954224 -1.9259095191955566 +1895 1.2925057411193848 0.43433293700218201 -2.0328423976898193 +1896 1.551486611366272 0.10599476844072342 -2.2619493007659912 +1897 2.3327977657318115 -0.38029167056083679 -2.6679167747497559 +1898 2.0890860557556152 -0.049813438206911087 -2.4401378631591797 +1899 1.8899534940719604 -0.079399928450584412 -2.4445779323577881 +1900 2.2733423709869385 -0.067873001098632813 -2.3871498107910156 +1901 2.5097086429595947 -0.40232503414154053 -2.6148927211761475 +1902 1.8103176355361938 -0.21725799143314362 -2.4934902191162109 +1903 2.0693554878234863 -0.53601568937301636 -2.7263731956481934 +1904 1.7740330696105957 -0.35877370834350586 -2.4963881969451904 +1905 1.6909564733505249 -0.40400755405426025 -2.3677759170532227 +1906 2.6344163417816162 -0.67242193222045898 -2.1359570026397705 +1907 2.3310775756835938 -0.078977413475513458 -2.0032682418823242 +1908 2.3835446834564209 -0.21671625971794128 -1.9200133085250854 +1909 2.570063591003418 -0.40346512198448181 -2.2478723526000977 +1910 2.3258593082427979 -0.049787208437919617 -2.203357458114624 +1911 1.1892489194869995 1.1414862871170044 -1.7103941440582275 +1912 1.3379987478256226 1.0469526052474976 -1.7319378852844238 +1913 0.88755905628204346 0.55451339483261108 -1.6572846174240112 +1914 1.664408802986145 0.76465046405792236 -1.0667111873626709 +1915 0.98038673400878906 0.60902070999145508 -1.7987960577011108 +1916 5.953953742980957 -4.7151350975036621 -5.9534192085266113 +1917 0.71712994575500488 0.91322898864746094 -1.6041250228881836 +1918 1.4918596744537354 1.458594799041748 -0.95058369636535645 +1919 1.629794716835022 1.0960502624511719 -1.2090864181518555 +1920 1.6667002439498901 0.94685077667236328 -1.1138460636138916 +1921 1.2465915679931641 0.28246220946311951 -2.0283455848693848 +1922 1.5104551315307617 -0.04048701748251915 -2.2612283229827881 +1923 1.156740665435791 0.23105150461196899 -1.8904174566268921 +1924 1.1237469911575317 0.93270248174667358 -1.7630684375762939 +1925 1.0299129486083984 0.76755577325820923 -1.805027961730957 +1926 1.9060789346694946 0.32485345005989075 -1.3515473604202271 +1927 2.1438374519348145 0.11483985930681229 -1.6653939485549927 +1928 1.9059504270553589 0.49240937829017639 -1.396105170249939 +1929 1.8553893566131592 0.64496397972106934 -1.4893112182617188 +1930 1.6083439588546753 1.1278232336044312 -1.4370294809341431 +1931 1.8540830612182617 0.65573787689208984 -1.7183963060379028 +1932 1.3837058544158936 0.58838659524917603 -1.9829950332641602 +1933 1.5463755130767822 1.0345745086669922 -1.6587764024734497 +1934 1.4584661722183228 1.0212399959564209 -1.7146415710449219 +1935 3.0670101642608643 -1.3471353054046631 -3.3529455661773682 +1936 3.2965664863586426 -2.1917996406555176 -3.8035640716552734 +1937 3.0973198413848877 -1.8794810771942139 -3.6726315021514893 +1938 2.8495960235595703 -1.4647190570831299 -3.4248864650726318 +1939 3.1587023735046387 -1.6559851169586182 -3.5977780818939209 +1940 3.1108181476593018 -1.7648955583572388 -3.655463695526123 +1941 2.8316001892089844 -1.5849579572677612 -3.4397342205047607 +1942 2.5889089107513428 -1.1598025560379028 -3.1927814483642578 +1943 2.5664091110229492 -1.2855738401412964 -3.205052375793457 +1944 3.364809513092041 -1.5849579572677612 -2.9065320491790771 +1945 3.5246450901031494 -1.6559851169586182 -3.2318356037139893 +1946 3.5850932598114014 -1.7648955583572388 -3.1811959743499756 +1947 3.605144739151001 -1.8794810771942139 -3.1648061275482178 +1948 3.36342453956604 -2.1690781116485596 -3.9032585620880127 +1949 3.743922233581543 -2.1917998790740967 -3.3562078475952148 +1950 3.8430531024932861 -2.1690781116485596 -3.4236299991607666 +1951 3.8211026191711426 -2.0602819919586182 -3.4353656768798828 +1952 3.7601549625396729 -1.957817554473877 -3.479088306427002 +1953 3.5042507648468018 -1.666364312171936 -3.3819491863250732 +1954 3.7406959533691406 -1.9720513820648193 -3.6215279102325439 +1955 3.55637526512146 -1.9720512628555298 -3.8058481216430664 +1956 3.4135787487030029 -1.9578174352645874 -3.8256642818450928 +1957 3.3090801239013672 -1.666364312171936 -3.5771126747131348 +1958 3.4540178775787354 -1.7057609558105469 -3.5258874893188477 +1959 2.9043831825256348 -1.3489698171615601 -3.3686716556549072 +1960 2.3017957210540771 -0.98137128353118896 -2.9691357612609863 +1961 2.2248725891113281 -1.0195831060409546 -2.8498604297637939 +1962 2.1427097320556641 -0.40346503257751465 -2.6752195358276367 +1963 2.3288431167602539 -0.85021764039993286 -2.9597203731536865 +1964 2.0377182960510254 -0.67242181301116943 -2.7326550483703613 +1965 3.0088748931884766 -1.3200809955596924 -2.5739474296569824 +1966 3.1070349216461182 -1.1598026752471924 -2.6746628284454346 +1967 2.8788206577301025 -0.98137140274047852 -2.3921036720275879 +1968 2.6246294975280762 -0.53601574897766113 -2.1710989475250244 +1969 2.8660500049591064 -0.85021770000457764 -2.422513484954834 +1970 2.5621685981750488 -0.38029170036315918 -2.4385530948638916 +1971 2.8096210956573486 -0.72312581539154053 -2.492962121963501 +1972 2.3960297107696533 -0.72312569618225098 -2.9065530300140381 +1973 2.7990419864654541 -0.70655614137649536 -2.6745841503143311 +1974 3.0490498542785645 -1.0383143424987793 -2.7387940883636475 +1975 3.3469061851501465 -1.4647190570831299 -2.9275760650634766 +1976 3.1225109100341797 -1.2855740785598755 -2.6489503383636475 +1977 3.287750244140625 -1.3489698171615601 -2.9853041172027588 +1978 2.8222060203552246 -1.0285418033599854 -3.1250884532928467 +1979 3.2719745635986328 -1.3471353054046631 -3.1479809284210205 +1980 3.2198503017425537 -1.3835339546203613 -3.2999002933502197 +1981 2.6499350070953369 -1.0383142232894897 -3.137901782989502 +1982 2.577223539352417 -0.70655614137649536 -2.8964023590087891 +1983 3.0359866619110107 -1.0285419225692749 -2.9113075733184814 +1984 2.9840974807739258 -1.0588656663894653 -3.0724213123321533 +1985 2.7467389106750488 -0.7326769232749939 -2.843428373336792 +1986 2.7600805759429932 -1.6156531572341919 -3.3300671577453613 +1987 2.492283821105957 -1.3200808763504028 -3.0905382633209229 +1988 0.40801692008972168 -0.98185068368911743 3.5732500553131104 +1989 0.39322400093078613 -1.2855750322341919 3.9401278495788574 +1990 0.29244419932365417 -0.72493994235992432 3.5852069854736328 +1991 0.28221416473388672 -1.0383154153823853 3.940934419631958 +1992 0.37703415751457214 -1.5849591493606567 4.3065910339355469 +1993 0.16216140985488892 -0.39318385720252991 3.364124059677124 +1994 0.39257797598838806 -0.54810196161270142 3.2254464626312256 +1995 0.15684831142425537 -0.70774859189987183 3.7050180435180664 +1996 0.15116402506828308 -1.0285429954528809 4.0532569885253906 +1997 0.37985882163047791 -0.85126090049743652 3.5803530216217041 +1998 0.36528435349464417 -1.3200820684432983 3.8082458972930908 +1999 0.36636722087860107 -1.1598037481307983 3.9418907165527344 +2000 0.13033203780651093 -1.9720528125762939 5.0946559906005859 +2001 0.24506743252277374 -1.9578189849853516 5.0070886611938477 +2002 0 -1.6485623121261597 4.7594361305236816 +2003 0 -1.9559344053268433 5.1074976921081543 +2004 0.33536165952682495 -1.7648968696594238 4.6643462181091309 +2005 0.35060638189315796 -1.6156543493270874 4.1798062324523926 +2006 0.35908681154251099 -1.8794823884963989 4.6718769073486328 +2007 0.13800367712974548 -1.6663656234741211 4.7448883056640625 +2008 0.25876030325889587 -1.6559864282608032 4.6527175903320313 +2009 0.33914715051651001 -2.1690795421600342 5.0355424880981445 +2010 0.27108111977577209 -1.3489710092544556 4.2975683212280273 +2011 0.14493076503276825 -1.3471364974975586 4.4013609886169434 +2012 0.35165289044380188 -1.4647202491760254 4.3036079406738281 +2013 0.23027308285236359 0.64066880941390991 2.2723000049591064 +2014 0.1998392641544342 0.36611628532409668 2.5314521789550781 +2015 0.37000781297683716 0.27889013290405273 2.4298655986785889 +2016 0.41981598734855652 0.48832991719245911 2.2114810943603516 +2017 0.46483922004699707 0.0076111718080937862 2.1626615524291992 +2018 0 0.22899201512336731 2.8017690181732178 +2019 0.51209968328475952 0.35851821303367615 2.1632823944091797 +2020 0.54028916358947754 0.24448814988136292 2.1006646156311035 +2021 0.46833205223083496 0.15868677198886871 2.3810749053955078 +2022 0.013794656842947006 -4.7203559875488281 8.3739748001098633 +2023 0.045414518564939499 -4.70477294921875 8.3861827850341797 +2024 0.042190965265035629 -4.7099766731262207 8.3821134567260742 +2025 0.033853251487016678 -4.6827225685119629 8.3792581558227539 +2026 0.016172453761100769 -4.6919240951538086 8.3963899612426758 +2027 0 -4.6912031173706055 8.3969602584838867 +2028 0.58778446912765503 0.44587075710296631 1.8785964250564575 +2029 0.50544238090515137 0.20618070662021637 1.9310276508331299 +2030 0.42964765429496765 0.79886424541473389 2.0830826759338379 +2031 0.32650861144065857 0.68689924478530884 2.2018015384674072 +2032 0.42188751697540283 -0.6799323558807373 3.2104487419128418 +2033 0 -0.34650737047195435 3.4071829319000244 +2034 0.42999172210693359 -0.041582480072975159 2.6129655838012695 +2035 0.40658283233642578 -0.27343299984931946 2.8954458236694336 +2036 0.30212172865867615 -0.42044395208358765 3.2386384010314941 +2037 0.49582269787788391 0.055357705801725388 2.316272497177124 +2038 0.17806646227836609 0.14495529234409332 2.7656486034393311 +2039 0.43615216016769409 -0.40118378400802612 2.8676846027374268 +2040 0.40542212128639221 -0.45327401161193848 2.7257022857666016 +2041 0.31351959705352783 -0.14858539402484894 2.9205801486968994 +2042 0.3347327709197998 0.077794477343559265 2.6526334285736084 +2043 0.45825973153114319 -0.16298845410346985 2.568129301071167 +2044 0.16775363683700562 -0.10457079857587814 3.0454521179199219 +2045 0.14227741956710815 -4.0571274757385254 7.4985942840576172 +2046 0.13738405704498291 -3.9891488552093506 7.4621171951293945 +2047 0.10493579506874084 -4.2939009666442871 7.7567896842956543 +2048 0.083829700946807861 -4.1852331161499023 7.7527923583984375 +2049 0.054624829441308975 -3.9541923999786377 7.4893145561218262 +2050 0.10842645913362503 -3.9243896007537842 7.4231419563293457 +2051 0.16824546456336975 -3.7288541793823242 7.1248354911804199 +2052 0.1542656421661377 -3.3873462677001953 6.7502198219299316 +2053 0.19757997989654541 -3.4633846282958984 6.7832345962524414 +2054 0.13193675875663757 -3.6584055423736572 7.0888147354125977 +2055 0.17592708766460419 -3.803429126739502 7.1577363014221191 +2056 0.13711421191692352 -4.050382137298584 7.4154324531555176 +2057 0.067562311887741089 -3.6858313083648682 7.1582355499267578 +2058 0.10804598033428192 -4.0342273712158203 7.3387656211853027 +2059 0 -3.9482171535491943 7.4939546585083008 +2060 0.071441024541854858 -4.528803825378418 8.0956497192382813 +2061 0.032272528856992722 -4.7075066566467285 8.3597698211669922 +2062 0.070549651980400085 -4.5469145774841309 8.1673192977905273 +2063 0 -4.4727673530578613 8.1391220092773438 +2064 0.044122114777565002 -4.6919307708740234 8.3792581558227539 +2065 0.058223653584718704 -4.4409022331237793 8.0774459838867188 +2066 0.027065567672252655 -4.4756016731262207 8.1369094848632813 +2067 0.10711798816919327 -4.3055005073547363 7.834813117980957 +2068 0.10511210560798645 -4.2442178726196289 7.794766902923584 +2069 0.071528822183609009 -4.4940185546875 8.1224184036254883 +2070 0 -4.2130441665649414 7.8191084861755371 +2071 0.041112352162599564 -4.2174630165100098 7.8156819343566895 +2072 0.27440261840820313 -2.747596263885498 5.6492800712585449 +2073 0.31709021329879761 -2.4537017345428467 5.3971447944641113 +2074 0.27516502141952515 -2.6364591121673584 5.736182689666748 +2075 0.29288178682327271 -2.7332985401153564 5.7562556266784668 +2076 0.19498516619205475 -2.8302164077758789 6.0617294311523438 +2077 0.21319736540317535 -2.5442521572113037 5.7126617431640625 +2078 0.29720619320869446 -2.3508231639862061 5.3810906410217285 +2079 0.31725728511810303 -2.0602834224700928 5.0236639976501465 +2080 0.11254102736711502 -2.5626621246337891 5.7943534851074219 +2081 0.22989620268344879 -2.2534339427947998 5.3609600067138672 +2082 0.12185163795948029 -2.2697310447692871 5.4456071853637695 +2083 0.091512039303779602 -3.1340832710266113 6.483393669128418 +2084 0.17531390488147736 -3.1112618446350098 6.4077200889587402 +2085 0.2382088303565979 -3.2778506278991699 6.4625997543334961 +2086 0.19715452194213867 -3.5371456146240234 6.7242646217346191 +2087 0.16788069903850555 -3.7999167442321777 7.0706338882446289 +2088 0.10241593420505524 -2.8508033752441406 6.1404299736022949 +2089 0 -2.8388609886169434 6.1496882438659668 +2090 0.22525908052921295 -3.1927967071533203 6.4376649856567383 +2091 0.22471728920936584 -3.2797920703887939 6.3544034957885742 +2092 0.079875744879245758 -3.412445068359375 6.8228030204772949 +2093 0.25115716457366943 -2.9171335697174072 6.0885119438171387 +2094 0.26655539870262146 -3.0084762573242188 6.1096634864807129 +2095 0.20795348286628723 -3.5429203510284424 6.8135800361633301 +2096 0.28221416473388672 -1.0383141040802002 -4.118645191192627 +2097 0.15116402506828308 -1.0285416841506958 -4.2314672470092773 +2098 0.15685117244720459 -0.70655590295791626 -3.8989534378051758 +2099 0.36636722087860107 -1.1598024368286133 -4.1133913993835449 +2100 0 -0.66764956712722778 -3.9369869232177734 +2101 0.39322400093078613 -1.2855737209320068 -4.1052107810974121 +2102 0.35165289044380188 -1.4647188186645508 -4.4595675468444824 +2103 0.3926372230052948 -0.53601557016372681 -3.4208886623382568 +2104 0.37986379861831665 -0.85021752119064331 -3.7670149803161621 +2105 0.39153221249580383 -0.71420270204544067 -3.2574622631072998 +2106 0.42192822694778442 -0.67242169380187988 -3.4019362926483154 +2107 0.3787616491317749 -1.019582986831665 -3.6145391464233398 +2108 0.4080197811126709 -0.98137116432189941 -3.7535591125488281 +2109 0.29245135188102722 -0.72312557697296143 -3.7778868675231934 +2110 0.36528503894805908 -1.3200807571411133 -3.9715657234191895 +2111 0.27108111977577209 -1.3489696979522705 -4.4594249725341797 +2112 0.24506743252277374 -1.9578173160552979 -5.1381077766418457 +2113 0.35908681154251099 -1.8794807195663452 -4.8068432807922363 +2114 0.25876030325889587 -1.6559847593307495 -4.7989702224731445 +2115 0.29635533690452576 -2.4722669124603271 -5.3905644416809082 +2116 0.33914715051651001 -2.1690778732299805 -5.1559534072875977 +2117 0.13033203780651093 -1.9720511436462402 -5.2249608039855957 +2118 0.37703415751457214 -1.5849577188491821 -4.4564552307128906 +2119 0.33536165952682495 -1.7648952007293701 -4.8050947189331055 +2120 0.14493076503276825 -1.3471349477767944 -4.5633096694946289 +2121 0 -1.3234926462173462 -4.5852956771850586 +2122 0.13800367712974548 -1.6663640737533569 -4.8906197547912598 +2123 0 0.0029790515545755625 -3.2900488376617432 +2124 0.4502754807472229 0.70977699756622314 -2.0869870185852051 +2125 0.33353206515312195 0.54753285646438599 -2.4481472969055176 +2126 0.43370082974433899 0.41721367835998535 -2.4012265205383301 +2127 0.43274575471878052 0.22307582199573517 -2.1960403919219971 +2128 0.46575075387954712 0.2750726044178009 -2.3572609424591064 +2129 0.013794656842947006 -4.7203526496887207 -8.37261962890625 +2130 0.45018056035041809 0.52612191438674927 -1.8587658405303955 +2131 0.016172453761100769 -4.6919207572937012 -8.3963909149169922 +2132 0.032272599637508392 -4.7075037956237793 -8.3589859008789063 +2133 0 -4.7214736938476563 -8.3716926574707031 +2134 0.48367384076118469 0.57403624057769775 -2.030144214630127 +2135 0.43411487340927124 1.0673612356185913 -1.905001163482666 +2136 0.272356778383255 0.99773335456848145 -2.186931848526001 +2137 0.35783115029335022 0.82802528142929077 -2.1545162200927734 +2138 0.41871306300163269 0.10461572557687759 -2.7322123050689697 +2139 0 0.3387129008769989 -2.9731259346008301 +2140 0.31191346049308777 -0.079399816691875458 -3.0982263088226318 +2141 0.16742168366909027 -0.049813311547040939 -3.2361183166503906 +2142 0.16218568384647369 -0.38029155135154724 -3.5670185089111328 +2143 0.3021831214427948 -0.4034649133682251 -3.4375922679901123 +2144 0.32224699854850769 0.24394215643405914 -2.7641279697418213 +2145 0.17861896753311157 0.58585178852081299 -2.6050491333007813 +2146 0.17286400496959686 0.27956485748291016 -2.9107577800750732 +2147 0.44989711046218872 -0.040920600295066833 -2.7008533477783203 +2148 0.43561249971389771 -0.3587736189365387 -3.0507771968841553 +2149 0.40532433986663818 -0.21725788712501526 -3.0754547119140625 +2150 0 -3.6783192157745361 -7.2107043266296387 +2151 0.10804598033428192 -4.0342249870300293 -7.3687477111816406 +2152 0.16824546456336975 -3.7288520336151123 -7.1690874099731445 +2153 0.067562311887741089 -3.6858291625976563 -7.2045650482177734 +2154 0.13738405704498291 -3.9891462326049805 -7.4941697120666504 +2155 0.10511210560798645 -4.2442226409912109 -7.8149685859680176 +2156 0.10842645913362503 -3.9243869781494141 -7.4581923484802246 +2157 0.083829700946807861 -4.1852307319641113 -7.7757792472839355 +2158 0.14227813482284546 -4.0571250915527344 -7.5275058746337891 +2159 0 -3.4034271240234375 -6.8895339965820313 +2160 0.13193675875663757 -3.6584033966064453 -7.1364436149597168 +2161 0.1542656421661377 -3.3873443603515625 -6.8107829093933105 +2162 0.19757997989654541 -3.4633824825286865 -6.8401503562927246 +2163 0.16788069903850555 -3.7999145984649658 -7.1115660667419434 +2164 0.13711421191692352 -4.0503792762756348 -7.4446291923522949 +2165 0.027065567672252655 -4.4755992889404297 -8.1466188430786133 +2166 0 -4.4727654457092285 -8.1489744186401367 +2167 0.058089233934879303 -4.5062017440795898 -8.0352602005004883 +2168 0.045414593070745468 -4.7047700881958008 -8.3856124877929688 +2169 0.044122114777565002 -4.6919279098510742 -8.3792591094970703 +2170 0.058223653584718704 -4.4408993721008301 -8.0887260437011719 +2171 0.10493579506874084 -4.2938985824584961 -7.7747793197631836 +2172 0.071441024541854858 -4.5288019180297852 -8.1029319763183594 +2173 0.10711798816919327 -4.3054981231689453 -7.8523030281066895 +2174 0.054624829441308975 -3.9541897773742676 -7.5230088233947754 +2175 0.041112422943115234 -4.2174601554870605 -7.8371696472167969 +2176 0.070549651980400085 -4.5469112396240234 -8.1738157272338867 +2177 0.071528822183609009 -4.4940166473388672 -8.1312704086303711 +2178 0.21319736540317535 -2.5442502498626709 -5.81439208984375 +2179 0.12185163795948029 -2.2697296142578125 -5.5609931945800781 +2180 0.11254102736711502 -2.5626599788665771 -5.895169734954834 +2181 0 -2.2549669742584229 -5.5733499526977539 +2182 0.29288178682327271 -2.7332963943481445 -5.8486495018005371 +2183 0.19498516619205475 -2.830214262008667 -6.1493539810180664 +2184 0 -1.9559327363967896 -5.2386093139648438 +2185 0.31725728511810303 -2.0602817535400391 -5.149528980255127 +2186 0.22989620268344879 -2.2534322738647461 -5.4771604537963867 +2187 0.27516502141952515 -2.6364569664001465 -5.8333511352539063 +2188 0.29720619320869446 -2.3508214950561523 -5.492429256439209 +2189 0.31709021329879761 -2.453700065612793 -5.5033650398254395 +2190 0.27440261840820313 -2.7475943565368652 -5.7409663200378418 +2191 0 -2.8388588428497314 -6.2368917465209961 +2192 0.17531390488147736 -3.111259937286377 -6.4816246032714844 +2193 0.22525908052921295 -3.1927945613861084 -6.5076155662536621 +2194 0.091512039303779602 -3.1340811252593994 -6.5561847686767578 +2195 0.19715452194213867 -3.5371434688568115 -6.7776603698730469 +2196 0.20795348286628723 -3.5429182052612305 -6.8666982650756836 +2197 0.17592708766460419 -3.80342698097229 -7.1984977722167969 +2198 0.079875744879245758 -3.4124429225921631 -6.882166862487793 +2199 0.13141778111457825 -3.7876508235931396 -7.0310454368591309 +2200 0.26655539870262146 -3.0084741115570068 -6.1885719299316406 +2201 0.25115716457366943 -2.9171314239501953 -6.171882152557373 +2202 0.15359748899936676 -3.5371577739715576 -6.6812925338745117 +2203 0.2382088303565979 -3.277848482131958 -6.528437614440918 +2204 0.10241593420505524 -2.8508012294769287 -6.2270474433898926 +2205 0 -3.1235878467559814 -6.5647940635681152 +2206 4.6748967170715332 -3.1340830326080322 4.5090789794921875 +2207 4.903618335723877 -3.3873460292816162 4.6551728248596191 +2208 4.545478343963623 -3.1340830326080322 4.6384978294372559 +2209 4.9019198417663574 -3.4124448299407959 4.7592792510986328 +2210 4.7614641189575195 -3.2778503894805908 4.3916664123535156 +2211 4.9130620956420898 -3.5371453762054443 4.6075458526611328 +2212 4.417564868927002 -3.1927964687347412 4.7011585235595703 +2213 4.6854548454284668 -3.3873460292816162 4.8733367919921875 +2214 4.9563064575195313 -3.4633843898773193 4.6484198570251465 +2215 5.1226811408996582 -3.6584053039550781 4.9122838973999023 +2216 4.9837541580200195 -3.5429201126098633 4.6631040573120117 +2217 5.0499482154846191 -3.7876527309417725 4.8433413505554199 +2218 5.1234736442565918 -4.0342273712158203 5.261293888092041 +2219 5.2000751495361328 -3.8034288883209229 4.9308934211730957 +2220 5.2762703895568848 -4.0342273712158203 5.1084895133972168 +2221 5.1328749656677246 -3.7999165058135986 4.8749856948852539 +2222 4.6768813133239746 -3.4633843898773193 4.9278459548950195 +2223 4.9347057342529297 -3.728853702545166 5.1504993438720703 +2224 4.596895694732666 -3.5371596813201904 4.7874188423156738 +2225 4.6342368125915527 -3.5371453762054443 4.8863639831542969 +2226 4.8640928268432617 -3.7876527309417725 5.0291962623596191 +2227 4.689666748046875 -3.5429201126098633 4.9571986198425293 +2228 4.7889542579650879 -3.4124448299407959 4.8722372055053711 +2229 5.1195192337036133 -3.7255420684814453 5.0972895622253418 +2230 4.8880348205566406 -3.4518556594848633 4.8592953681945801 +2231 4.9360909461975098 -3.6584053039550781 5.0988674163818359 +2232 4.2210955619812012 -2.7475960254669189 3.7871875762939453 +2233 4.098721981048584 -3.0152215957641602 4.4137096405029297 +2234 4.054527759552002 -2.350822925567627 3.5785396099090576 +2235 4.0781278610229492 -2.4537014961242676 3.5765833854675293 +2236 4.310053825378418 -2.7332980632781982 3.8496556282043457 +2237 4.1595978736877441 -3.0084760189056396 4.4971146583557129 +2238 4.1571140289306641 -2.9171333312988281 4.4706168174743652 +2239 3.8958556652069092 -2.7332983016967773 4.2638535499572754 +2240 3.9246737957000732 -2.5442521572113037 4.1753158569335938 +2241 3.6342122554779053 -2.350822925567627 3.9988555908203125 +2242 3.9379580020904541 -2.3080923557281494 3.8812220096588135 +2243 4.0532932281494141 -2.56266188621521 4.1620388031005859 +2244 3.9944155216217041 -2.2534337043762207 3.6111977100372314 +2245 3.977583646774292 -2.269730806350708 3.7475695610046387 +2246 4.4172224998474121 -2.889671802520752 4.3748698234558105 +2247 4.3001456260681152 -2.8508031368255615 4.401674747467041 +2248 4.43310546875 -3.1112616062164307 4.6440868377685547 +2249 4.1793928146362305 -2.8302161693572998 4.4113330841064453 +2250 4.5122981071472168 -2.9171333312988281 4.1154251098632813 +2251 4.6810359954833984 -3.1112616062164307 4.3961567878723145 +2252 4.7361302375793457 -3.1927964687347412 4.3825936317443848 +2253 4.653839111328125 -3.1732158660888672 4.6183891296386719 +2254 4.4449834823608398 -2.8508031368255615 4.2568364143371582 +2255 4.2124505043029785 -2.5626616477966309 4.0028810501098633 +2256 4.455141544342041 -2.8302161693572998 4.1355772018432617 +2257 4.2261848449707031 -2.5442519187927246 3.8738117218017578 +2258 4.2850193977355957 -2.6364583969116211 3.8472929000854492 +2259 4.1784930229187012 -2.6012735366821289 4.1290378570556641 +2260 4.3575887680053711 -3.2797918319702148 4.642509937286377 +2261 4.4245891571044922 -3.2778503894805908 4.728548526763916 +2262 4.5365695953369141 -3.0084760189056396 4.1201505661010742 +2263 5.9566025733947754 -4.7099757194519043 5.8973531723022461 +2264 5.9337882995605469 -4.7075061798095703 5.8885083198547363 +2265 5.7775931358337402 -4.528803825378418 5.6729083061218262 +2266 5.7275815010070801 -4.5469136238098145 5.824120044708252 +2267 5.7379679679870605 -4.4756016731262207 5.7713961601257324 +2268 5.9012155532836914 -4.6827220916748047 5.9488997459411621 +2269 5.7762441635131836 -4.4756016731262207 5.7331199645996094 +2270 5.5732421875 -4.2577877044677734 5.5634269714355469 +2271 5.6744575500488281 -4.4409017562866211 5.7511591911315918 +2272 5.59320068359375 -4.2442169189453125 5.4344215393066406 +2273 5.6219830513000488 -4.3055000305175781 5.4617757797241211 +2274 5.827354907989502 -4.5469136238098145 5.7243471145629883 +2275 5.9618420600891113 -4.7047724723815918 5.8979167938232422 +2276 8.3794012069702148 -4.6827211380004883 -0.034043539315462112 +2277 8.3858966827392578 -4.7047715187072754 -0.045112758874893188 +2278 8.3816852569580078 -4.7099747657775879 -0.041773136705160141 +2279 8.3859682083129883 -4.7310047149658203 0.00088632421102374792 +2280 1.3696004152297974 1.0821444988250732 -0.54531300067901611 +2281 1.3687508106231689 0.77012532949447632 0.44824475049972534 +2282 8.3732624053955078 -4.7203545570373535 -0.013145617209374905 +2283 8.3963899612426758 -4.6919221878051758 0.016187610104680061 +2284 5.9485650062561035 -4.6919236183166504 5.9257140159606934 +2285 5.7567987442016602 -4.4409012794494629 5.6688175201416016 +2286 5.9490928649902344 -4.6827220916748047 5.9010219573974609 +2287 5.7971305847167969 -4.4940185546875 5.6915392875671387 +2288 5.7949824333190918 -4.516240119934082 5.7910552024841309 +2289 5.953953742980957 -4.7151370048522949 5.9544892311096191 +2290 5.4308953285217285 -4.1852326393127441 5.5379638671875 +2291 5.172642707824707 -3.728853702545166 4.912562370300293 +2292 5.3849959373474121 -3.9891483783721924 5.1746697425842285 +2293 5.1257939338684082 -3.68583083152771 5.0070886611938477 +2294 5.338017463684082 -3.924389123916626 5.1671319007873535 +2295 5.3507742881774902 -4.0503816604614258 5.1422615051269531 +2296 5.1568665504455566 -4.0503816604614258 5.3361682891845703 +2297 5.4170327186584473 -4.2939004898071289 5.5564517974853516 +2298 5.5654330253601074 -4.2939004898071289 5.4080452919006348 +2299 5.4996380805969238 -4.2737774848937988 5.3719892501831055 +2300 5.3814334869384766 -4.2737774848937988 5.4901866912841797 +2301 4.8954520225524902 -3.7999165058135986 5.1124081611633301 +2302 4.9512815475463867 -3.8034288883209229 5.1796879768371582 +2303 5.2119183540344238 -4.0571269989013672 5.398679256439209 +2304 5.1907033920288086 -3.9891483783721924 5.368962287902832 +2305 5.0302467346191406 -3.68583083152771 5.1026358604431152 +2306 5.3480048179626465 -3.9942021369934082 5.3320927619934082 +2307 5.7199358940124512 -4.5062036514282227 5.6336321830749512 +2308 5.4131350517272949 -4.0571269989013672 5.1974701881408691 +2309 5.6377878189086914 -4.5062036514282227 5.7157812118530273 +2310 5.6959724426269531 -4.4940185546875 5.7926974296569824 +2311 5.4704995155334473 -4.3055000305175781 5.6132597923278809 +2312 5.4445514678955078 -4.2442245483398438 5.5830707550048828 +2313 5.6765561103820801 -4.528803825378418 5.7739443778991699 +2314 5.1846780776977539 -3.924389123916626 5.3204712867736816 +2315 5.2690396308898926 -3.9541919231414795 5.3294439315795898 +2316 5.5050349235534668 -4.2174625396728516 5.5524334907531738 +2317 5.5494499206542969 -4.1852326393127441 5.4194092750549316 +2318 5.5631766319274902 -4.2174625396728516 5.494290828704834 +2319 5.3462915420532227 -3.9541919231414795 5.252192497253418 +2320 3.8958773612976074 -2.6364588737487793 4.2364349365234375 +2321 2.5115218162536621 -0.714324951171875 1.8607287406921387 +2322 2.3834307193756104 -0.22118119895458221 1.7044767141342163 +2323 2.0883579254150391 -0.091529540717601776 2.2385070323944092 +2324 1.7738475799560547 -0.39586770534515381 2.3080205917358398 +2325 1.5159374475479126 -0.15953697264194489 2.1214656829833984 +2326 1.8104103803634644 -0.26958960294723511 2.3082418441772461 +2327 1.8897892236709595 -0.14650657773017883 2.2615780830383301 +2328 1.5591104030609131 -0.042107440531253815 2.1375484466552734 +2329 2.0897784233093262 0.24044002592563629 1.7610912322998047 +2330 2.3308348655700684 -0.088644132018089294 1.7846051454544067 +2331 2.0370969772338867 0.21608379483222961 1.9566047191619873 +2332 2.1424384117126465 -0.41419559717178345 2.471468448638916 +2333 2.0691769123077393 -0.54284584522247314 2.5270977020263672 +2334 2.3326835632324219 -0.38450700044631958 2.4590189456939697 +2335 2.8096210956573486 -0.72312659025192261 2.2990975379943848 +2336 2.5621256828308105 -0.3815295398235321 2.2278919219970703 +2337 2.3255810737609863 -0.060906935483217239 1.9832593202590942 +2338 2.5700423717498779 -0.40425601601600647 2.0380964279174805 +2339 2.5096871852874756 -0.40310108661651611 2.405052661895752 +2340 2.2730352878570557 -0.080489993095397949 2.1682865619659424 +2341 2.0376324653625488 -0.67565774917602539 2.5381834506988525 +2342 2.390026330947876 -0.36062470078468323 1.6696913242340088 +2343 2.7605230808258057 -1.0195839405059814 2.135542631149292 +2344 1.4153217077255249 0.28307333588600159 1.9857863187789917 +2345 1.3813716173171997 0.62882423400878906 1.8133586645126343 +2346 1.8553893566131592 0.49521857500076294 1.3470360040664673 +2347 1.8566815853118896 0.51048183441162109 1.5757930278778076 +2348 1.5701606273651123 0.6742822527885437 1.6721683740615845 +2349 1.6159747838973999 0.80701643228530884 1.3890954256057739 +2350 1.629794716835022 0.7816893458366394 1.1350400447845459 +2351 1.4858062267303467 0.95184731483459473 0.8240845799446106 +2352 1.0347741842269897 0.28835004568099976 1.8671176433563232 +2353 1.76787269115448 0.17861592769622803 1.0992766618728638 +2354 1.5242108106613159 0.47044274210929871 0.86769294738769531 +2355 2.0925838947296143 0.21030177175998688 1.5544061660766602 +2356 1.6218568086624146 0.36259001493453979 1.9409430027008057 +2357 1.8056846857070923 0.4742908775806427 1.7874747514724731 +2358 1.8472944498062134 0.15244074165821075 2.0615389347076416 +2359 2.2625489234924316 -0.40729475021362305 1.5893200635910034 +2360 1.4314688444137573 -0.20814655721187592 1.9959657192230225 +2361 2.1431307792663574 0.082626648247241974 1.4603579044342041 +2362 2.1463501453399658 -0.056141968816518784 1.4148792028427124 +2363 1.6667002439498901 0.66668707132339478 1.0228461027145386 +2364 1.9059504270553589 0.37316709756851196 1.2406668663024902 +2365 1.9044516086578369 0.23588082194328308 1.1837382316589355 +2366 1.2729036808013916 0.068676002323627472 1.9796901941299438 +2367 1.6451922655105591 0.073212429881095886 2.1026990413665771 +2368 1.3224585056304932 0.16898253560066223 2.0138616561889648 +2369 3.36342453956604 -2.1690795421600342 3.7828404903411865 +2370 3.3724331855773926 -2.0602831840515137 3.7581627368927002 +2371 3.1108181476593018 -1.7648966312408447 3.5147154331207275 +2372 3.4540178775787354 -1.7057620286941528 3.3821485042572021 +2373 3.5042507648468018 -1.666365385055542 3.2362182140350342 +2374 3.5246450901031494 -1.6559861898422241 3.0855765342712402 +2375 3.7601549625396729 -1.9578186273574829 3.348069429397583 +2376 3.7406959533691406 -1.9720524549484253 3.4912230968475342 +2377 3.3090801239013672 -1.666365385055542 3.4313817024230957 +2378 3.2198503017425537 -1.3835350275039673 3.1398069858551025 +2379 3.743922233581543 -2.1918008327484131 3.2369320392608643 +2380 3.0973198413848877 -1.8794821500778198 3.5376651287078857 +2381 3.5850932598114014 -1.7648966312408447 3.0404477119445801 +2382 3.6959452629089355 -2.010178804397583 3.6317567825317383 +2383 3.8430531024932861 -2.1690793037414551 3.3032193183898926 +2384 3.2965664863586426 -2.1918010711669922 3.6842880249023438 +2385 3.8211026191711426 -2.0602831840515137 3.3094937801361084 +2386 3.605144739151001 -1.8794821500778198 3.0298402309417725 +2387 3.6296939849853516 -2.4537014961242676 4.025017261505127 +2388 3.4135787487030029 -1.957818865776062 3.6946456432342529 +2389 3.55637526512146 -1.9720525741577148 3.6755435466766357 +2390 3.669297456741333 -2.2534337043762207 3.9363157749176025 +2391 3.8052554130554199 -2.269730806350708 3.9198904037475586 +2392 3.0359866619110107 -1.0285427570343018 2.7330970764160156 +2393 2.7990419864654541 -0.70655697584152222 2.479863166809082 +2394 2.7467389106750488 -0.73267781734466553 2.6500494480133057 +2395 2.9840974807739258 -1.0588666200637817 2.8957667350769043 +2396 3.364809513092041 -1.5849587917327881 2.7566752433776855 +2397 3.1070349216461182 -1.1598035097122192 2.5031623840332031 +2398 3.1225109100341797 -1.2855747938156128 2.4838674068450928 +2399 2.8788206577301025 -0.98137211799621582 2.2114808559417725 +2400 2.6246225833892822 -0.53617274761199951 1.9677189588546753 +2401 2.6344163417816162 -0.67247819900512695 1.9395222663879395 +2402 2.8660500049591064 -0.85021847486495972 2.2351658344268799 +2403 2.6499350070953369 -1.0383152961730957 2.9601907730102539 +2404 2.3960297107696533 -0.72312664985656738 2.712681770324707 +2405 2.577223539352417 -0.70655703544616699 2.7016885280609131 +2406 2.3017957210540771 -0.98137223720550537 2.7885055541992188 +2407 2.3288431167602539 -0.85021859407424927 2.7723798751831055 +2408 3.3469061851501465 -1.4647198915481567 2.7716159820556641 +2409 2.8316001892089844 -1.5849589109420776 3.2898776531219482 +2410 3.1587023735046387 -1.6559861898422241 3.4515190124511719 +2411 2.5889089107513428 -1.1598036289215088 3.0212812423706055 +2412 2.9043831825256348 -1.348970890045166 3.2068221569061279 +2413 2.8222060203552246 -1.0285427570343018 2.9468777179718018 +2414 2.8495960235595703 -1.4647201299667358 3.2689261436462402 +2415 2.5664091110229492 -1.2855749130249023 3.0399696826934814 +2416 3.287750244140625 -1.3489707708358765 2.8234548568725586 +2417 3.0490498542785645 -1.0383151769638062 2.5610833168029785 +2418 3.0670101642608643 -1.347136378288269 3.1909964084625244 +2419 3.2719745635986328 -1.347136378288269 2.9860391616821289 +2420 6.4726371765136719 -3.1927957534790039 0.19028472900390625 +2421 6.4446754455566406 -3.1112608909606934 0.13836222887039185 +2422 6.4955224990844727 -3.2778494358062744 0.20529033243656158 +2423 6.8116888999938965 -3.463383674621582 0.16912008821964264 +2424 6.7805013656616211 -3.3873453140258789 0.12398260086774826 +2425 6.852485179901123 -3.4124438762664795 0.050195399671792984 +2426 6.5197930335998535 -3.1340823173522949 0.055114436894655228 +2427 6.1055383682250977 -2.8302152156829834 -0.23879894614219666 +2428 6.5197930335998535 -3.1340823173522949 -0.12790906429290771 +2429 6.4446754455566406 -3.1112608909606934 -0.21226486563682556 +2430 6.183739185333252 -2.8508024215698242 -0.14572429656982422 +2431 6.4726371765136719 -3.1927957534790039 -0.26023346185684204 +2432 6.6545944213867188 -3.5371589660644531 -0.1802942305803299 +2433 6.3872761726379395 -3.2797911167144775 0.19184517860412598 +2434 6.4955224990844727 -3.2778494358062744 -0.27112734317779541 +2435 6.8401422500610352 -3.542919397354126 -0.23451317846775055 +2436 6.7509622573852539 -3.5371444225311279 -0.22385199368000031 +2437 7.1126294136047363 -3.6584043502807617 0.10812319070100784 +2438 7.1469578742980957 -3.7288529872894287 0.14610084891319275 +2439 6.9127259254455566 -3.451854944229126 -0.028735874220728874 +2440 6.8116888999938965 -3.463383674621582 -0.22603987157344818 +2441 7.1126294136047363 -3.6584043502807617 -0.1557496041059494 +2442 6.852485179901123 -3.4124438762664795 -0.10955630987882614 +2443 7.1814360618591309 -3.6858301162719727 -0.090725265443325043 +2444 7.091099739074707 -3.7999157905578613 -0.18834921717643738 +2445 6.7805013656616211 -3.3873453140258789 -0.18454796075820923 +2446 6.8401422500610352 -3.542919397354126 0.18139380216598511 +2447 7.0102863311767578 -3.7876517772674561 0.11066088080406189 +2448 6.7509622573852539 -3.5371444225311279 0.17045708000659943 +2449 6.2468991279602051 -2.8896710872650146 -0.042356118559837341 +2450 6.5815258026123047 -3.1732151508331299 -0.035448413342237473 +2451 5.5033001899719238 -2.2697303295135498 -0.17954304814338684 +2452 5.4367561340332031 -2.3508224487304688 -0.35287535190582275 +2453 5.5691089630126953 -2.3080918788909912 -0.056734595447778702 +2454 5.6951231956481934 -2.7475955486297607 -0.32024654746055603 +2455 6.0469813346862793 -3.0152208805084229 -0.28979706764221191 +2456 5.7847671508789063 -2.6364579200744629 -0.32374861836433411 +2457 5.6951231956481934 -2.7475955486297607 0.22855871915817261 +2458 5.4502549171447754 -2.4537010192871094 0.2639794647693634 +2459 5.8024487495422363 -2.73329758644104 -0.33907759189605713 +2460 5.4502549171447754 -2.4537010192871094 -0.37020096182823181 +2461 5.8024487495422363 -2.73329758644104 0.24668595194816589 +2462 5.5033001899719238 -2.2697303295135498 0.064159713685512543 +2463 5.7635235786437988 -2.5442512035369873 0.16233231127262115 +2464 6.1491174697875977 -3.0084750652313232 -0.30600836873054504 +2465 6.1491174697875977 -3.0084750652313232 0.22710244357585907 +2466 6.1302008628845215 -2.9171323776245117 -0.29284083843231201 +2467 6.183739185333252 -2.8508024215698242 0.05910763144493103 +2468 6.1055383682250977 -2.8302152156829834 0.15117138624191284 +2469 6.1302008628845215 -2.9171323776245117 0.20947349071502686 +2470 5.8447651863098145 -2.5626611709594727 0.062132507562637329 +2471 5.7847671508789063 -2.6364579200744629 0.22658067941665649 +2472 5.7635235786437988 -2.5442512035369873 -0.26406165957450867 +2473 5.9092750549316406 -2.6012728214263916 -0.049453489482402802 +2474 5.8447651863098145 -2.5626611709594727 -0.16294991970062256 +2475 7.8049030303955078 -4.2442164421081543 -0.11524074524641037 +2476 7.8435940742492676 -4.3054990768432617 -0.1158403754234314 +2477 7.7642855644226074 -4.1852321624755859 -0.095315337181091309 +2478 8.0830860137939453 -4.4409008026123047 -0.063859641551971436 +2479 8.3593425750732422 -4.7075052261352539 0.032635245472192764 +2480 8.3593425750732422 -4.7075052261352539 -0.031909875571727753 +2481 8.0992908477783203 -4.5288028717041016 -0.07508733868598938 +2482 8.0311183929443359 -4.5062026977539063 -0.062245938926935196 +2483 8.1706037521362305 -4.546912670135498 0.067311942577362061 +2484 8.0830860137939453 -4.4409008026123047 0.052587665617465973 +2485 8.0992908477783203 -4.5288028717041016 0.067794635891914368 +2486 7.8435940742492676 -4.3054990768432617 0.098394885659217834 +2487 7.8817844390869141 -4.257786750793457 -0.0098167667165398598 +2488 8.1417646408081055 -4.4756007194519043 -0.031914766877889633 +2489 8.1417646408081055 -4.4756007194519043 0.022216442972421646 +2490 8.3816852569580078 -4.7099747657775879 0.04260886088013649 +2491 8.3858966827392578 -4.7047715187072754 0.04571634903550148 +2492 5.9618420600891113 -4.704770565032959 -5.8973112106323242 +2493 8.1268453598022461 -4.4940176010131836 -0.075961068272590637 +2494 8.1706037521362305 -4.546912670135498 -0.073787443339824677 +2495 8.3792591094970703 -4.6919293403625488 -0.04410688579082489 +2496 5.8938274383544922 -4.6919283866882324 -5.9562101364135742 +2497 5.9012155532836914 -4.6827201843261719 -5.9492788314819336 +2498 5.8969326019287109 -4.7099738121032715 -5.9561820030212402 +2499 5.9257001876831055 -4.6919212341308594 -5.9485511779785156 +2500 5.9490928649902344 -4.6827201843261719 -5.9014019966125488 +2501 8.3794012069702148 -4.6827211380004883 0.033663034439086914 +2502 8.3792591094970703 -4.6919293403625488 0.044137340039014816 +2503 8.1268453598022461 -4.4940176010131836 0.06709665060043335 +2504 8.3963899612426758 -4.6919221878051758 -0.016157299280166626 +2505 8.420161247253418 -4.7151360511779785 0.00053280516294762492 +2506 8.19537353515625 -4.5162386894226074 -0.0039299065247178078 +2507 7.2401137351989746 -3.7255411148071289 -0.022223491221666336 +2508 7.4407029151916504 -3.9243881702423096 0.090880364179611206 +2509 7.1814360618591309 -3.6858301162719727 0.044399268925189972 +2510 7.5061616897583008 -3.9541909694671631 0.037775367498397827 +2511 7.1780810356140137 -3.8034281730651855 -0.1963128000497818 +2512 7.1469578742980957 -3.7288529872894287 -0.19039079546928406 +2513 7.3537569046020508 -4.0342264175415039 0.093061842024326324 +2514 7.4781794548034668 -3.9891476631164551 0.12135057896375656 +2515 7.091099739074707 -3.7999157905578613 0.14741216599941254 +2516 7.429995059967041 -4.0503807067871094 0.12250556796789169 +2517 7.1780810356140137 -3.8034281730651855 0.15554137527942657 +2518 7.5061616897583008 -3.9541909694671631 -0.071474500000476837 +2519 7.4407029151916504 -3.9243881702423096 -0.12597325444221497 +2520 7.826390266418457 -4.2174615859985352 -0.051856104284524918 +2521 7.5631976127624512 -3.9942014217376709 -0.015915766358375549 +2522 7.4781794548034668 -3.9891476631164551 -0.15341754257678986 +2523 7.7657852172851563 -4.2939000129699707 -0.11392443627119064 +2524 7.694115161895752 -4.2737765312194824 -0.093029648065567017 +2525 7.5130147933959961 -4.0571260452270508 -0.15672905743122101 +2526 7.7657852172851563 -4.2939000129699707 0.095947861671447754 +2527 7.3537569046020508 -4.0342264175415039 -0.12302941083908081 +2528 7.429995059967041 -4.0503807067871094 -0.15172217786312103 +2529 7.694115161895752 -4.2737765312194824 0.074130065739154816 +2530 7.5130147933959961 -4.0571260452270508 0.12782652676105499 +2531 7.826390266418457 -4.2174615859985352 0.030368741601705551 +2532 7.8049030303955078 -4.2442235946655273 0.094983458518981934 +2533 7.7642855644226074 -4.1852321624755859 0.072343349456787109 +2534 5.419060230255127 -2.2534329891204834 -0.28799447417259216 +2535 3.1223611831665039 -0.034215193241834641 -0.27398359775543213 +2536 2.7849223613739014 0.32754871249198914 -0.27735716104507446 +2537 2.6394417285919189 0.29734334349632263 -0.42491632699966431 +2538 2.8831250667572021 0.30668461322784424 -0.10574380308389664 +2539 2.9852397441864014 -0.062951549887657166 -0.41731894016265869 +2540 2.6138005256652832 0.14210547506809235 -0.52154016494750977 +2541 2.9447579383850098 -0.35055205225944519 -0.53789502382278442 +2542 3.1603586673736572 -0.7127642035484314 0.29508531093597412 +2543 2.9447650909423828 -0.3503594696521759 0.33351057767868042 +2544 2.5867319107055664 -0.014047908596694469 -0.55239588022232056 +2545 2.7959225177764893 -0.39472728967666626 -0.50483494997024536 +2546 2.5866317749023438 -0.017567068338394165 0.34850484132766724 +2547 2.7959294319152832 -0.39453750848770142 0.30382272601127625 +2548 2.9852466583251953 -0.062640421092510223 0.20677420496940613 +2549 2.6391918659210205 0.28734380006790161 0.22095182538032532 +2550 3.1223611831665039 -0.03407490998506546 0.06098569929599762 +2551 3.1603586673736572 -0.71279335021972656 -0.48800769448280334 +2552 3.3037333488464355 -0.67160981893539429 -0.51976925134658813 +2553 2.9658875465393066 -0.20550991594791412 -0.50951272249221802 +2554 3.319216251373291 -0.53419429063796997 -0.49349069595336914 +2555 3.3325796127319336 -0.39990130066871643 0.19880199432373047 +2556 3.6809544563293457 -0.72296911478042603 0.19559253752231598 +2557 3.4613919258117676 -0.37722757458686829 0.057954113930463791 +2558 3.6733453273773193 -0.8501896858215332 0.28620654344558716 +2559 3.3037333488464355 -0.67158925533294678 0.32410645484924316 +2560 2.9659018516540527 -0.20522524416446686 0.30138570070266724 +2561 3.3192238807678223 -0.53414660692214966 0.29182448983192444 +2562 3.3325724601745605 -0.39996972680091858 -0.40562346577644348 +2563 3.5493438243865967 -0.40038236975669861 -0.10422486811876297 +2564 3.4613845348358154 -0.37725251913070679 -0.2664419412612915 +2565 3.2154459953308105 -0.054700121283531189 -0.1072729080915451 +2566 2.2616493701934814 0.54459798336029053 -0.52710020542144775 +2567 1.705376148223877 0.74873167276382446 -0.53135746717453003 +2568 2.070340633392334 0.31512796878814697 -0.52731722593307495 +2569 1.8701516389846802 0.81658214330673218 -0.56391644477844238 +2570 1.8701516389846802 0.63150560855865479 0.42891609668731689 +2571 2.2903814315795898 0.65141534805297852 0.24875661730766296 +2572 2.4434499740600586 0.71445310115814209 0.085148274898529053 +2573 2.1598274707794189 1.1423354148864746 0.025810826569795609 +2574 2.2616493701934814 0.47459161281585693 0.34875401854515076 +2575 1.9067929983139038 0.80954355001449585 0.40089720487594604 +2576 1.9470535516738892 1.0099608898162842 0.3074849545955658 +2577 1.6756947040557861 1.2351272106170654 0.41760110855102539 +2578 1.5051227807998657 0.91117280721664429 0.50942790508270264 +2579 1.6780431270599365 1.5227119922637939 -0.48784741759300232 +2580 1.9565404653549194 1.2439361810684204 0.22252100706100464 +2581 2.6136007308959961 0.13429021835327148 0.31755495071411133 +2582 2.4321784973144531 -0.066643223166465759 0.31870564818382263 +2583 2.2293910980224609 0.31370297074317932 0.3774796724319458 +2584 2.5470995903015137 0.70372688770294189 -0.09843473881483078 +2585 2.784865140914917 0.32556349039077759 0.068766400218009949 +2586 2.4434499740600586 0.73953747749328613 -0.27346596121788025 +2587 2.0824615955352783 1.2496683597564697 -0.2646278440952301 +2588 1.9470535516738892 1.1939960718154907 -0.42873519659042358 +2589 2.2304403781890869 0.36336281895637512 -0.56186562776565552 +2590 2.4323499202728271 -0.059897445142269135 -0.51822012662887573 +2591 2.2903814315795898 0.72092050313949585 -0.42435944080352783 +2592 1.9067929983139038 1.0156575441360474 -0.52561318874359131 +2593 4.6169185638427734 -1.9062433242797852 -0.40117576718330383 +2594 4.6169185638427734 -1.9062433242797852 0.2675594687461853 +2595 4.7347240447998047 -1.764896035194397 -0.40573576092720032 +2596 4.7393636703491211 -1.8794815540313721 0.29160439968109131 +2597 4.7258439064025879 -1.6559855937957764 0.18563146889209747 +2598 4.7347240447998047 -1.764896035194397 0.2649875283241272 +2599 5.0725984573364258 -1.9578181505203247 0.17955736815929413 +2600 4.8177509307861328 -1.6663647890090942 0.065137267112731934 +2601 4.8177509307861328 -1.6663647890090942 -0.21086978912353516 +2602 5.1598081588745117 -1.9720519781112671 0.065180122852325439 +2603 5.0865964889526367 -2.0602824687957764 0.25432199239730835 +2604 5.419060230255127 -2.2534329891204834 0.17179717123508453 +2605 5.0957479476928711 -2.1690788269042969 0.27893945574760437 +2606 5.4367561340332031 -2.3508224487304688 0.24153709411621094 +2607 5.1598081588745117 -1.9720519781112671 -0.19548375904560089 +2608 5.0725984573364258 -1.9578181505203247 -0.31057676672935486 +2609 4.7258439064025879 -1.6559855937957764 -0.33188906311988831 +2610 5.2268590927124023 -2.0101783275604248 -0.064193174242973328 +2611 4.8847227096557617 -1.7057615518569946 -0.071869604289531708 +2612 5.0865964889526367 -2.0602824687957764 -0.38019177317619324 +2613 4.7393636703491211 -1.8794814348220825 -0.42656919360160828 +2614 5.0957479476928711 -2.1690788269042969 -0.39935481548309326 +2615 4.3815231323242188 -1.5849584341049194 0.30210503935813904 +2616 4.0276446342468262 -1.159803032875061 0.28061783313751221 +2617 4.0297932624816895 -1.0383148193359375 0.1933588832616806 +2618 3.6632513999938965 -0.98136472702026367 0.31771108508110046 +2619 4.1423659324645996 -1.0285422801971436 0.062058940529823303 +2620 3.801607608795166 -0.70638734102249146 -0.25412759184837341 +2621 3.8844842910766602 -0.73259174823760986 -0.096644662320613861 +2622 4.2201457023620605 -1.0588661432266235 -0.088329210877418518 +2623 3.801607608795166 -0.70638597011566162 0.059575546532869339 +2624 3.6809544563293457 -0.72297626733779907 -0.38931304216384888 +2625 3.8899023532867432 -1.3200812339782715 -0.44694632291793823 +2626 3.6632513999938965 -0.98136460781097412 -0.49832844734191895 +2627 3.5252091884613037 -1.0195765495300293 0.2894308865070343 +2628 4.4823384284973145 -1.3471357822418213 -0.22590318322181702 +2629 4.1423659324645996 -1.0285422801971436 -0.24026913940906525 +2630 4.4823384284973145 -1.3471357822418213 0.063958130776882172 +2631 4.5535588264465332 -1.3835345506668091 -0.080045871436595917 +2632 4.0297932624816895 -1.0383147001266479 -0.37106946110725403 +2633 4.3784966468811035 -1.3489704132080078 -0.3520064651966095 +2634 4.3815879821777344 -1.4647194147109985 -0.42963361740112305 +2635 4.0276446342468262 -1.1598029136657715 -0.4521166980266571 +2636 4.0226688385009766 -1.2855743169784546 -0.47576472163200378 +2637 4.3815231323242188 -1.5849583148956299 -0.45196250081062317 +2638 4.2539534568786621 -1.6156535148620605 -0.4247574508190155 +2639 4.0226688385009766 -1.2855744361877441 0.31068262457847595 +2640 3.6733453273773193 -0.85018956661224365 -0.47352322936058044 +2641 4.3815879821777344 -1.4647195339202881 0.27367144823074341 +2642 4.3784966468811035 -1.3489704132080078 0.19015511870384216 +2643 -0.67538177967071533 0.58850163221359253 1.8261935710906982 +2644 -3.605144739151001 -1.8794821500778198 3.0298402309417725 +2645 -0.90070080757141113 0.96096289157867432 1.7896734476089478 +2646 -0.72965067625045776 0.80100572109222412 1.918792724609375 +2647 -3.8430531024932861 -2.1690793037414551 3.3032193183898926 +2648 -3.9840364456176758 -2.4722683429718018 3.5122737884521484 +2649 -3.3090801239013672 -1.666365385055542 3.4313817024230957 +2650 -3.1108181476593018 -1.7648966312408447 3.5147154331207275 +2651 -3.5850932598114014 -1.7648966312408447 3.0404477119445801 +2652 -0.11891704052686691 0.68753165006637573 2.2759976387023926 +2653 0 0.80159825086593628 2.2182767391204834 +2654 -3.36342453956604 -2.1690795421600342 3.7828404903411865 +2655 -3.6296939849853516 -2.4537014961242676 4.025017261505127 +2656 -3.6342122554779053 -2.350822925567627 3.9988555908203125 +2657 -3.4135787487030029 -1.957818865776062 3.6946456432342529 +2658 -0.079277552664279938 0.83601957559585571 2.1715846061706543 +2659 -0.42964765429496765 0.79886424541473389 2.0830826759338379 +2660 -0.21767240762710571 0.8355984091758728 2.1221156120300293 +2661 0 1.1316490173339844 1.9682117700576782 +2662 -0.13369564712047577 0.8496895432472229 2.1405398845672607 +2663 -3.3469061851501465 -1.4647198915481567 2.7716159820556641 +2664 -3.5246450901031494 -1.6559861898422241 3.0855765342712402 +2665 -3.5042507648468018 -1.666365385055542 3.2362182140350342 +2666 -3.2719745635986328 -1.347136378288269 2.9860391616821289 +2667 -1.0459885597229004 2.8523516654968262 -1.2011411190032959 +2668 -3.743922233581543 -2.1918008327484131 3.2369320392608643 +2669 -3.1587023735046387 -1.6559861898422241 3.4515190124511719 +2670 -3.4540178775787354 -1.7057620286941528 3.3821485042572021 +2671 -1.1826101541519165 0.51313519477844238 1.8764193058013916 +2672 -3.3724331855773926 -2.0602831840515137 3.7581627368927002 +2673 -3.0973198413848877 -1.8794821500778198 3.5376651287078857 +2674 -1.0895826816558838 0.38648080825805664 1.90385901927948 +2675 -0.87119787931442261 0.60122507810592651 1.8082761764526367 +2676 -0.95256131887435913 0.71380245685577393 1.8040144443511963 +2677 -0.50528603792190552 0.67574697732925415 2.0136191844940186 +2678 -0.70684355497360229 0.68794929981231689 1.8933299779891968 +2679 -0.54369634389877319 2.7197632789611816 -1.380486011505127 +2680 -0.51209968328475952 0.35851821303367615 2.1632823944091797 +2681 -0.56168437004089355 0.57331317663192749 1.95428466796875 +2682 -0.59522193670272827 2.6674959659576416 -1.2205932140350342 +2683 -1.2440218925476074 0.69808948040008545 1.821168065071106 +2684 -0.41981598734855652 0.48832991719245911 2.2114810943603516 +2685 -3.669297456741333 -2.2534337043762207 3.9363157749176025 +2686 -3.55637526512146 -1.9720525741577148 3.6755435466766357 +2687 -3.8052554130554199 -2.269730806350708 3.9198904037475586 +2688 -3.9379580020904541 -2.3080923557281494 3.8812220096588135 +2689 -4.1784930229187012 -2.6012735366821289 4.1290378570556641 +2690 -1.4762979745864868 1.3815003633499146 -1.3186819553375244 +2691 -4.2850193977355957 -2.6364583969116211 3.8472929000854492 +2692 -1.4686099290847778 1.3173333406448364 -1.4179198741912842 +2693 -3.9246737957000732 -2.5442521572113037 4.1753158569335938 +2694 -4.0532932281494141 -2.56266188621521 4.1620388031005859 +2695 -1.5757784843444824 1.2852675914764404 -1.2810198068618774 +2696 -1.4260008335113525 1.5200779438018799 -1.2252902984619141 +2697 -2.0415444374084473 1.3515616655349731 -0.13686417043209076 +2698 -2.0249977111816406 1.3799152374267578 -0.069984421133995056 +2699 -1.9647352695465088 1.4347168207168579 -0.17963454127311707 +2700 -4.4530000686645508 -3.0152213573455811 4.0594387054443359 +2701 -4.310053825378418 -2.7332980632781982 3.8496556282043457 +2702 -1.7983822822570801 1.6739956140518188 -0.063968777656555176 +2703 -2.0824615955352783 1.2496683597564697 -0.2646278440952301 +2704 -1.9046870470046997 1.4913599491119385 -0.24881552159786224 +2705 0 1.1209919452667236 -2.272392749786377 +2706 -0.272356778383255 0.99773335456848145 -2.186931848526001 +2707 -4.0781278610229492 -2.4537014961242676 3.5765833854675293 +2708 -4.054527759552002 -2.350822925567627 3.5785396099090576 +2709 -3.8211026191711426 -2.0602831840515137 3.3094937801361084 +2710 -3.977583646774292 -2.269730806350708 3.7475695610046387 +2711 -3.6959452629089355 -2.010178804397583 3.6317567825317383 +2712 -3.7406959533691406 -1.9720524549484253 3.4912230968475342 +2713 -0.065510444343090057 1.0369160175323486 -2.2906451225280762 +2714 -3.9944155216217041 -2.2534337043762207 3.6111977100372314 +2715 -3.7601549625396729 -1.9578186273574829 3.348069429397583 +2716 -1.3980897665023804 1.2054818868637085 -1.6077655553817749 +2717 -1.4907388687133789 1.2294454574584961 -1.5077351331710815 +2718 -1.4327394962310791 1.2597050666809082 -1.5468606948852539 +2719 -3.8958773612976074 -2.6364588737487793 4.2364349365234375 +2720 -3.56492018699646 -2.4722683429718018 3.9313828945159912 +2721 -3.8958556652069092 -2.7332983016967773 4.2638535499572754 +2722 -0.18157140910625458 1.0987130403518677 -2.1700139045715332 +2723 -0.11090348660945892 1.0762628316879272 -2.2310042381286621 +2724 -1.218609094619751 1.2856459617614746 -1.6049314737319946 +2725 -1.3115439414978027 1.2694060802459717 -1.5895125865936279 +2726 -1.9576611518859863 -0.72017133235931396 2.4180660247802734 +2727 -0.74435579776763916 3.8679089546203613 0.22330808639526367 +2728 -0.75794732570648193 3.8527398109436035 0.12812618911266327 +2729 -0.66165828704833984 4.0117263793945313 0.060723025351762772 +2730 -2.0376324653625488 -0.67565774917602539 2.5381834506988525 +2731 -2.0691769123077393 -0.54284584522247314 2.5270977020263672 +2732 -2.3960297107696533 -0.72312664985656738 2.712681770324707 +2733 -0.72315478324890137 3.5996413230895996 0.48368090391159058 +2734 -0.73112833499908447 3.4762184619903564 0.42756238579750061 +2735 -0.72808027267456055 3.7326223850250244 0.43240654468536377 +2736 -0.70586699247360229 3.7397677898406982 0.53718245029449463 +2737 -0.70292961597442627 3.8517334461212158 0.46282109618186951 +2738 -0.73954451084136963 3.7202229499816895 0.37635946273803711 +2739 -0.72965782880783081 3.3630034923553467 0.28683826327323914 +2740 -2.7990419864654541 -0.70655697584152222 2.479863166809082 +2741 -2.7467389106750488 -0.73267781734466553 2.6500494480133057 +2742 -2.5096871852874756 -0.40310108661651611 2.405052661895752 +2743 -2.577223539352417 -0.70655703544616699 2.7016885280609131 +2744 -2.8788206577301025 -0.98137211799621582 2.2114808559417725 +2745 -0.72945088148117065 3.6160593032836914 0.42858749628067017 +2746 -1.7738475799560547 -0.39586770534515381 2.3080205917358398 +2747 -0.78934907913208008 3.6141819953918457 -0.2269652932882309 +2748 -0.77249538898468018 3.772240161895752 0.14273917675018311 +2749 -0.73369097709655762 3.6624019145965576 0.39961990714073181 +2750 -0.72885119915008545 3.5506360530853271 0.42903792858123779 +2751 -2.2625489234924316 -0.40729475021362305 1.5893200635910034 +2752 -0.77817034721374512 3.8142285346984863 0.044856559485197067 +2753 -0.76385074853897095 3.7816629409790039 0.17926277220249176 +2754 -0.760231614112854 3.7747533321380615 0.237174391746521 +2755 -2.6246225833892822 -0.53617274761199951 1.9677189588546753 +2756 -2.390026330947876 -0.36062470078468323 1.6696913242340088 +2757 -2.5700423717498779 -0.40425601601600647 2.0380964279174805 +2758 -2.6344163417816162 -0.67247819900512695 1.9395222663879395 +2759 -2.5115218162536621 -0.714324951171875 1.8607287406921387 +2760 -0.79530251026153564 3.7595767974853516 0.0089781396090984344 +2761 -0.80236947536468506 3.7128632068634033 -0.059987723827362061 +2762 -0.8093222975730896 3.4924941062927246 -0.036150746047496796 +2763 -0.79982829093933105 3.2810261249542236 0.037044435739517212 +2764 -2.1424384117126465 -0.41419559717178345 2.471468448638916 +2765 -1.8897892236709595 -0.14650657773017883 2.2615780830383301 +2766 -2.3326835632324219 -0.38450700044631958 2.4590189456939697 +2767 -2.3308348655700684 -0.088644132018089294 1.7846051454544067 +2768 -2.5621256828308105 -0.3815295398235321 2.2278919219970703 +2769 -3.2198503017425537 -1.3835350275039673 3.1398069858551025 +2770 -2.9840974807739258 -1.0588666200637817 2.8957667350769043 +2771 -2.9043831825256348 -1.348970890045166 3.2068221569061279 +2772 -3.0670101642608643 -1.347136378288269 3.1909964084625244 +2773 -2.8222060203552246 -1.0285427570343018 2.9468777179718018 +2774 -3.0359866619110107 -1.0285427570343018 2.7330970764160156 +2775 -3.1225109100341797 -1.2855747938156128 2.4838674068450928 +2776 -3.287750244140625 -1.3489707708358765 2.8234548568725586 +2777 -2.8495960235595703 -1.4647201299667358 3.2689261436462402 +2778 -2.7600805759429932 -1.6156542301177979 3.1817667484283447 +2779 -2.5664091110229492 -1.2855749130249023 3.0399696826934814 +2780 -2.8316001892089844 -1.5849589109420776 3.2898776531219482 +2781 -0.79116225242614746 2.8833179473876953 -1.3678724765777588 +2782 -0.76791244745254517 3.2190937995910645 -1.2791136503219604 +2783 -0.66194015741348267 2.7776768207550049 -1.2935618162155151 +2784 -0.74076521396636963 3.2669994831085205 -1.2379751205444336 +2785 -0.73139959573745728 3.1442046165466309 -1.0747342109680176 +2786 -0.89444053173065186 2.9132993221282959 -1.5167577266693115 +2787 -1.0626709461212158 2.9287967681884766 -1.2680063247680664 +2788 -3.5010883808135986 -1.9062438011169434 2.961418628692627 +2789 -0.74973815679550171 3.0658111572265625 -1.0831789970397949 +2790 -1.3123433589935303 3.1053149700164795 -2.0353763103485107 +2791 -2.3288431167602539 -0.85021859407424927 2.7723798751831055 +2792 -2.6499350070953369 -1.0383152961730957 2.9601907730102539 +2793 -2.5889089107513428 -1.1598036289215088 3.0212812423706055 +2794 -0.73009330034255981 3.3455286026000977 0.45382592082023621 +2795 -0.73382663726806641 3.4081754684448242 0.38152545690536499 +2796 -2.2248725891113281 -1.019584059715271 2.6711857318878174 +2797 -2.3017957210540771 -0.98137223720550537 2.7885055541992188 +2798 -0.74717551469802856 3.1535344123840332 0.24685037136077881 +2799 -0.7877928614616394 3.2434995174407959 0.20897409319877625 +2800 -0.68165719509124756 3.6382601261138916 0.62630313634872437 +2801 -0.71718710660934448 3.4607565402984619 0.55350857973098755 +2802 -0.69867002964019775 3.8855552673339844 0.35130587220191956 +2803 -1.0599654912948608 3.1147804260253906 -1.3290109634399414 +2804 -1.1272662878036499 3.0665676593780518 -1.6817474365234375 +2805 -1.0283424854278564 3.2259109020233154 -1.2641658782958984 +2806 -3.1070349216461182 -1.1598035097122192 2.5031623840332031 +2807 -2.8660500049591064 -0.85021847486495972 2.2351658344268799 +2808 -3.0490498542785645 -1.0383151769638062 2.5610833168029785 +2809 -2.8096210956573486 -0.72312659025192261 2.2990975379943848 +2810 -0.81920903921127319 3.480158805847168 -0.13342097401618958 +2811 -3.364809513092041 -1.5849587917327881 2.7566752433776855 +2812 -0.70754599571228027 3.9053571224212646 -0.049069095402956009 +2813 -8.3794012069702148 -4.6827211380004883 0.033663034439086914 +2814 -8.3816852569580078 -4.7099747657775879 0.04260886088013649 +2815 -8.420161247253418 -4.7151360511779785 0.00053280516294762492 +2816 -8.3732624053955078 -4.7203545570373535 -0.013145617209374905 +2817 -8.3792591094970703 -4.6919293403625488 -0.04410688579082489 +2818 -8.3963899612426758 -4.6919221878051758 -0.016157299280166626 +2819 -5.9257001876831055 -4.6919236183166504 5.9485855102539063 +2820 -5.9618420600891113 -4.7047724723815918 5.8979167938232422 +2821 -8.3858966827392578 -4.7047715187072754 0.04571634903550148 +2822 -8.3792591094970703 -4.6919293403625488 0.044137340039014816 +2823 -5.9562239646911621 -4.6919302940368652 5.8938412666320801 +2824 -5.7971305847167969 -4.4940185546875 5.6915392875671387 +2825 -5.827354907989502 -4.5469136238098145 5.7243471145629883 +2826 -1.418476939201355 1.319117546081543 1.0257728099822998 +2827 -1.6146113872528076 1.0805168151855469 0.47917759418487549 +2828 -1.7125859260559082 1.527551531791687 0.28486847877502441 +2829 -1.4553325176239014 1.2408878803253174 1.0429476499557495 +2830 -5.7949824333190918 -4.516240119934082 5.7910552024841309 +2831 -5.7379679679870605 -4.4756016731262207 5.7713961601257324 +2832 -5.6744575500488281 -4.4409017562866211 5.7511591911315918 +2833 -5.7762441635131836 -4.4756016731262207 5.7331199645996094 +2834 -5.59320068359375 -4.2442169189453125 5.4344215393066406 +2835 -5.6219830513000488 -4.3055000305175781 5.4617757797241211 +2836 -5.7567987442016602 -4.4409012794494629 5.6688175201416016 +2837 -5.6765561103820801 -4.528803825378418 5.7739443778991699 +2838 -1.3848907947540283 1.7147130966186523 -1.0643553733825684 +2839 -1.428884744644165 1.6387178897857666 -1.0776684284210205 +2840 -1.2709193229675293 1.567298412322998 -1.39150071144104 +2841 -5.4308953285217285 -4.1852326393127441 5.5379638671875 +2842 -5.4445514678955078 -4.2442245483398438 5.5830707550048828 +2843 -5.4704995155334473 -4.3055000305175781 5.6132597923278809 +2844 -5.7275815010070801 -4.5469136238098145 5.824120044708252 +2845 -1.8084045648574829 1.3540600538253784 0.32803645730018616 +2846 -5.8969326019287109 -4.7099757194519043 5.9570231437683105 +2847 -5.8976106643676758 -4.7047724723815918 5.9621415138244629 +2848 -5.6959724426269531 -4.4940185546875 5.7926974296569824 +2849 -5.8938274383544922 -4.6919302940368652 5.956244945526123 +2850 -5.9566025733947754 -4.7099757194519043 5.8973531723022461 +2851 -5.7199358940124512 -4.5062036514282227 5.6336321830749512 +2852 -1.8022725582122803 1.6155107021331787 -0.28138658404350281 +2853 -1.6703122854232788 1.6113991737365723 0.26532214879989624 +2854 -1.69805908203125 1.7066895961761475 -0.40123158693313599 +2855 -1.651980996131897 1.7732264995574951 -0.3940696120262146 +2856 -1.6445997953414917 1.7171046733856201 -0.57089406251907349 +2857 -2.2616493701934814 0.54459798336029053 -0.52710020542144775 +2858 -2.6394417285919189 0.29734334349632263 -0.42491632699966431 +2859 -2.6138005256652832 0.14210547506809235 -0.52154016494750977 +2860 -2.7849223613739014 0.32754871249198914 -0.27735716104507446 +2861 -2.2304403781890869 0.36336281895637512 -0.56186562776565552 +2862 -0.08834616094827652 1.2946469783782959 1.8544831275939941 +2863 -0.15083064138889313 1.4113739728927612 1.7779452800750732 +2864 -2.2293910980224609 0.31370297074317932 0.3774796724319458 +2865 -2.5470995903015137 0.70372688770294189 -0.09843473881483078 +2866 -2.6391918659210205 0.28734380006790161 0.22095182538032532 +2867 -3.1223611831665039 -0.03407490998506546 0.06098569929599762 +2868 -2.8831250667572021 0.30668461322784424 -0.10574380308389664 +2869 -2.784865140914917 0.32556349039077759 0.068766400218009949 +2870 -2.5867319107055664 -0.014047908596694469 -0.55239588022232056 +2871 0 1.5546064376831055 -1.9624649286270142 +2872 -0.057944469153881073 1.5231188535690308 -1.9749857187271118 +2873 0 1.4846001863479614 -2.0128834247589111 +2874 -2.4321784973144531 -0.066643223166465759 0.31870564818382263 +2875 -2.9852466583251953 -0.062640421092510223 0.20677420496940613 +2876 -2.6136007308959961 0.13429021835327148 0.31755495071411133 +2877 -2.7959294319152832 -0.39453750848770142 0.30382272601127625 +2878 -2.5866317749023438 -0.017567068338394165 0.34850484132766724 +2879 -2.9659018516540527 -0.20522524416446686 0.30138570070266724 +2880 -0.2077728807926178 1.2234629392623901 1.8638699054718018 +2881 -2.2616493701934814 0.47459161281585693 0.34875401854515076 +2882 -1.8701516389846802 0.63150560855865479 0.42891609668731689 +2883 -2.0691769123077393 0.25670364499092102 0.34784814715385437 +2884 -1.8004666566848755 1.5668772459030151 -0.43508541584014893 +2885 -1.9565404653549194 1.2439361810684204 0.22252100706100464 +2886 -1.9470535516738892 1.1939960718154907 -0.42873519659042358 +2887 -1.9545415639877319 1.3941135406494141 -0.34226924180984497 +2888 -1.9067929983139038 1.0156575441360474 -0.52561318874359131 +2889 -1.8701516389846802 0.81658214330673218 -0.56391644477844238 +2890 -0.15806610882282257 1.2829114198684692 1.8486009836196899 +2891 -2.070340633392334 0.31512796878814697 -0.52731722593307495 +2892 -2.2903814315795898 0.72092050313949585 -0.42435944080352783 +2893 -2.4323499202728271 -0.059897445142269135 -0.51822012662887573 +2894 -0.048829950392246246 1.3424813747406006 1.8230527639389038 +2895 -0.34531685709953308 1.1824171543121338 1.844603419303894 +2896 -2.1598274707794189 1.1423354148864746 0.025810826569795609 +2897 -2.4434499740600586 0.71445310115814209 0.085148274898529053 +2898 -1.9067929983139038 0.80954355001449585 0.40089720487594604 +2899 -2.2903814315795898 0.65141534805297852 0.24875661730766296 +2900 -1.9470535516738892 1.0099608898162842 0.3074849545955658 +2901 -2.1598274707794189 1.1844162940979004 -0.17434217035770416 +2902 -2.4434499740600586 0.73953747749328613 -0.27346596121788025 +2903 -1.5114401578903198 0.85367286205291748 1.5126252174377441 +2904 -1.4604650735855103 0.85505771636962891 1.5799118280410767 +2905 -1.4313259124755859 0.78988420963287354 1.6603829860687256 +2906 -4.3575887680053711 -3.2797918319702148 4.642509937286377 +2907 -1.5701606273651123 0.6742822527885437 1.6721683740615845 +2908 -1.4871196746826172 0.93397986888885498 1.4162499904632568 +2909 -1.4462953805923462 1.1048586368560791 1.2193444967269897 +2910 -1.5816105604171753 0.93248790502548218 1.2322793006896973 +2911 -1.4912599325180054 0.99071580171585083 1.3064186573028564 +2912 -1.6159747838973999 0.80701643228530884 1.3890954256057739 +2913 -1.6118701696395874 0.72691643238067627 1.5415929555892944 +2914 -4.9563064575195313 -3.4633843898773193 4.6484198570251465 +2915 -4.7361302375793457 -3.1927964687347412 4.3825936317443848 +2916 -5.1328749656677246 -3.7999165058135986 4.8749856948852539 +2917 -0.25183320045471191 0.93969041109085083 2.031707763671875 +2918 -0.37447503209114075 1.010467529296875 1.9440624713897705 +2919 -0.35700878500938416 1.1111546754837036 1.8807380199432373 +2920 -0.56171292066574097 0.94920593500137329 1.9347184896469116 +2921 -4.9130620956420898 -3.5371453762054443 4.6075458526611328 +2922 -4.417564868927002 -3.1927964687347412 4.7011585235595703 +2923 -4.4245891571044922 -3.2778503894805908 4.728548526763916 +2924 -4.6854548454284668 -3.3873460292816162 4.8733367919921875 +2925 -4.6748967170715332 -3.1340830326080322 4.5090789794921875 +2926 -4.9019198417663574 -3.4124448299407959 4.7592792510986328 +2927 -4.653839111328125 -3.1732158660888672 4.6183891296386719 +2928 -2.0829541683197021 1.1595317125320435 0.12676161527633667 +2929 -2.0415444374084473 1.3235077857971191 -0.0034289250615984201 +2930 -2.185875415802002 1.1610594987869263 -0.07600083202123642 +2931 -4.098721981048584 -3.0152215957641602 4.4137096405029297 +2932 -4.1595978736877441 -3.0084760189056396 4.4971146583557129 +2933 -4.455141544342041 -2.8302161693572998 4.1355772018432617 +2934 -4.4449834823608398 -2.8508031368255615 4.2568364143371582 +2935 -4.2124505043029785 -2.5626616477966309 4.0028810501098633 +2936 -4.4172224998474121 -2.889671802520752 4.3748698234558105 +2937 -4.2261848449707031 -2.5442519187927246 3.8738117218017578 +2938 -4.5365695953369141 -3.0084760189056396 4.1201505661010742 +2939 -4.6753826141357422 -3.2797918319702148 4.3247156143188477 +2940 -4.7614641189575195 -3.2778503894805908 4.3916664123535156 +2941 -1.9650707244873047 1.3816356658935547 0.047932811081409454 +2942 -1.9060220718383789 1.3912439346313477 0.12771104276180267 +2943 -1.8072338104248047 1.4916526079177856 0.16658890247344971 +2944 -4.545478343963623 -3.1340830326080322 4.6384978294372559 +2945 -4.1571140289306641 -2.9171333312988281 4.4706168174743652 +2946 -4.1793928146362305 -2.8302161693572998 4.4113330841064453 +2947 -4.43310546875 -3.1112616062164307 4.6440868377685547 +2948 -4.3001456260681152 -2.8508031368255615 4.401674747467041 +2949 -4.5122981071472168 -2.9171333312988281 4.1154251098632813 +2950 -4.6810359954833984 -3.1112616062164307 4.3961567878723145 +2951 -5.4996380805969238 -4.2737774848937988 5.3719892501831055 +2952 -5.4131350517272949 -4.0571269989013672 5.1974701881408691 +2953 -5.3507742881774902 -4.0503816604614258 5.1422615051269531 +2954 -5.5654330253601074 -4.2939004898071289 5.4080452919006348 +2955 -5.3814334869384766 -4.2737774848937988 5.4901866912841797 +2956 -5.1234736442565918 -4.0342273712158203 5.261293888092041 +2957 -5.1907033920288086 -3.9891483783721924 5.368962287902832 +2958 -5.2119183540344238 -4.0571269989013672 5.398679256439209 +2959 -5.4170327186584473 -4.2939004898071289 5.5564517974853516 +2960 -5.1568665504455566 -4.0503816604614258 5.3361682891845703 +2961 -5.1195192337036133 -3.7255420684814453 5.0972895622253418 +2962 -4.9360909461975098 -3.6584053039550781 5.0988674163818359 +2963 -5.2690396308898926 -3.9541919231414795 5.3294439315795898 +2964 -5.1257939338684082 -3.68583083152771 5.0070886611938477 +2965 -5.3462915420532227 -3.9541919231414795 5.252192497253418 +2966 -5.3849959373474121 -3.9891483783721924 5.1746697425842285 +2967 -5.338017463684082 -3.924389123916626 5.1671319007873535 +2968 -5.172642707824707 -3.728853702545166 4.912562370300293 +2969 -5.7775931358337402 -4.528803825378418 5.6729083061218262 +2970 -1.5046588182449341 1.6710904836654663 -0.91949599981307983 +2971 -1.1220123767852783 1.3960412740707397 -1.5658701658248901 +2972 -5.3480048179626465 -3.9942021369934082 5.3320927619934082 +2973 -5.5050349235534668 -4.2174625396728516 5.5524334907531738 +2974 -5.5494499206542969 -4.1852326393127441 5.4194092750549316 +2975 -5.5631766319274902 -4.2174625396728516 5.494290828704834 +2976 -5.5732421875 -4.2577877044677734 5.5634269714355469 +2977 -5.1846780776977539 -3.924389123916626 5.3204712867736816 +2978 -4.7889542579650879 -3.4124448299407959 4.8722372055053711 +2979 -4.9347057342529297 -3.728853702545166 5.1504993438720703 +2980 -4.6768813133239746 -3.4633843898773193 4.9278459548950195 +2981 -5.0302467346191406 -3.68583083152771 5.1026358604431152 +2982 -4.903618335723877 -3.3873460292816162 4.6551728248596191 +2983 -4.8880348205566406 -3.4518556594848633 4.8592953681945801 +2984 -0.21092520654201508 1.2103004455566406 -2.0802843570709229 +2985 -4.8954520225524902 -3.7999165058135986 5.1124081611633301 +2986 -4.6342368125915527 -3.5371453762054443 4.8863639831542969 +2987 -4.689666748046875 -3.5429201126098633 4.9571986198425293 +2988 -0.965781569480896 1.4541192054748535 -1.5950876474380493 +2989 -4.9512815475463867 -3.8034288883209229 5.1796879768371582 +2990 -5.2000751495361328 -3.8034288883209229 4.9308934211730957 +2991 -4.9837541580200195 -3.5429201126098633 4.6631040573120117 +2992 -5.1226811408996582 -3.6584053039550781 4.9122838973999023 +2993 -0.3139471709728241 1.3151133060455322 -1.9682328701019287 +2994 -0.4709210991859436 1.3223302364349365 -1.8842566013336182 +2995 -1.2659366130828857 2.7029953002929688 -2.542644739151001 +2996 -0.44522148370742798 3.5799531936645508 0.74700474739074707 +2997 -0.41908711194992065 4.0688261985778809 0.53898918628692627 +2998 -1.7195028066635132 3.4870333671569824 -2.9952113628387451 +2999 -1.6799062490463257 3.3079597949981689 -3.7682278156280518 +3000 -1.6680494546890259 3.1540062427520752 -2.8839526176452637 +3001 -0.92153781652450562 2.4672787189483643 -2.5448577404022217 +3002 -0.47663825750350952 2.3270232677459717 -2.4140825271606445 +3003 -0.39064636826515198 3.8664312362670898 0.66397875547409058 +3004 -0.47826367616653442 3.8670597076416016 0.6566091775894165 +3005 -0.66052818298339844 3.3713481426239014 0.60447239875793457 +3006 -0.61358475685119629 3.8213596343994141 0.59428668022155762 +3007 -0.63398128747940063 3.4533398151397705 0.66746586561203003 +3008 -0.6454961895942688 3.9954078197479248 0.42718487977981567 +3009 -1.2259045839309692 4.2721710205078125 -3.7412376403808594 +3010 -1.6411304473876953 3.6761362552642822 -3.6990070343017578 +3011 -1.2558002471923828 4.3504867553710938 -3.0674517154693604 +3012 0 4.6980900764465332 -3.7400240898132324 +3013 -0.50637251138687134 4.6327524185180664 -3.6938743591308594 +3014 -0.87765097618103027 4.4836459159851074 -3.7479262351989746 +3015 -0.89889490604400635 4.5396251678466797 -3.08156418800354 +3016 0 2.2212109565734863 -2.3460536003112793 +3017 -1.4958071708679199 2.6856207847595215 -3.472698450088501 +3018 -1.2506963014602661 2.4581413269042969 -3.2123827934265137 +3019 -0.87051254510879517 2.2640414237976074 -3.1298630237579346 +3020 -1.0583950281143188 2.4409806728363037 -3.8216164112091064 +3021 -1.3016000986099243 2.675776481628418 -4.0224404335021973 +3022 -0.22449029982089996 3.8026783466339111 0.64762908220291138 +3023 -0.32168447971343994 3.5662617683410645 0.75455003976821899 +3024 -0.30960917472839355 3.9247803688049316 0.61396944522857666 +3025 -1.6511741876602173 2.9900872707366943 -3.5902535915374756 +3026 -1.4467592239379883 2.9416534900665283 -4.1336064338684082 +3027 -0.79863613843917847 4.233823299407959 -4.2337007522583008 +3028 -0.78408092260360718 3.8437533378601074 -4.5438427925109863 +3029 -0.44953307509422302 2.1520829200744629 -2.9845614433288574 +3030 -0.33639243245124817 2.2145652770996094 -3.5827012062072754 +3031 0 4.4528226852416992 -4.2359638214111328 +3032 0 4.0804767608642578 -4.5811123847961426 +3033 -1.0929734706878662 4.0315647125244141 -4.2370133399963379 +3034 -1.3385555744171143 3.8085753917694092 -4.1993441581726074 +3035 -1.4825583696365356 3.9981496334075928 -3.7246551513671875 +3036 -0.4171854555606842 4.401054859161377 -4.2014141082763672 +3037 -0.097283430397510529 3.7328577041625977 0.63710916042327881 +3038 -0.23841799795627594 4.0316281318664551 0.47366580367088318 +3039 -0.17167903482913971 3.5326685905456543 0.74957460165023804 +3040 -0.29690927267074585 4.1099863052368164 0.42820137739181519 +3041 -0.13265272974967957 4.4605956077575684 -0.93263721466064453 +3042 -0.24783496558666229 4.1544580459594727 0.23867067694664001 +3043 0 2.5935420989990234 -1.6978517770767212 +3044 -0.35497364401817322 4.5632529258728027 -2.0989005565643311 +3045 -0.69699257612228394 4.4663567543029785 -2.1452789306640625 +3046 -0.16457633674144745 3.9426407814025879 0.5115734338760376 +3047 -0.16881939768791199 4.0835165977478027 0.30628904700279236 +3048 -0.090249255299568176 4.0761785507202148 0.21815063059329987 +3049 -0.5696636438369751 4.078277587890625 0.41864237189292908 +3050 -0.60193777084350586 3.9714441299438477 -0.29249924421310425 +3051 -0.6708032488822937 3.8526685237884521 -0.38765400648117065 +3052 -0.73835957050323486 3.7979886531829834 -0.13775964081287384 +3053 -0.73438346385955811 3.6994144916534424 -0.41104725003242493 +3054 -0.58243358135223389 4.0845804214477539 0.12337062507867813 +3055 -0.46797296404838562 4.1129846572875977 0.48315772414207458 +3056 -0.37855678796768188 4.2256922721862793 -0.14092829823493958 +3057 -0.32853519916534424 4.1893010139465332 0.22926655411720276 +3058 -0.35752779245376587 4.0949454307556152 0.48416069149971008 +3059 -0.5087931752204895 4.1451139450073242 0.4134049117565155 +3060 -0.5324169397354126 4.0891709327697754 -0.32530012726783752 +3061 -0.39874985814094543 4.1932697296142578 0.30929005146026611 +3062 -0.51534903049468994 4.1593127250671387 0.14647406339645386 +3063 -0.45060169696807861 4.1989378929138184 0.20891855657100677 +3064 -0.7380097508430481 4.1020054817199707 -1.303091287612915 +3065 -1.2624530792236328 4.1310234069824219 -2.298490047454834 +3066 -0.98893857002258301 4.3132805824279785 -2.1923706531524658 +3067 -1.5110691785812378 4.1025199890136719 -3.0330448150634766 +3068 -0.54029273986816406 3.9428977966308594 0.59916585683822632 +3069 -0.78958463668823242 3.1329045295715332 0.047867141664028168 +3070 -0.70195305347442627 3.1302134990692139 0.44549679756164551 +3071 -1.51224684715271 2.8945326805114746 -2.7523846626281738 +3072 -1.3966834545135498 3.1949374675750732 -2.1542947292327881 +3073 -1.0896968841552734 2.9848690032958984 -1.722550630569458 +3074 -1.6688418388366699 3.8266065120697021 -3.0686798095703125 +3075 -1.4106533527374268 3.8796234130859375 -2.2506699562072754 +3076 -1.4660543203353882 3.4623630046844482 -2.1743249893188477 +3077 -0.67068684101104736 2.8387386798858643 -1.4411338567733765 +3078 -0.47708585858345032 2.615314245223999 -1.7342147827148438 +3079 -0.45857098698616028 2.6006448268890381 -1.3434805870056152 +3080 -0.87153339385986328 2.7884058952331543 -1.8090965747833252 +3081 -0.93602162599563599 3.7731969356536865 -1.2912344932556152 +3082 -0.085681401193141937 4.1636309623718262 -0.036938928067684174 +3083 -0.80005669593811035 3.3557150363922119 -0.23120696842670441 +3084 -0.093789182603359222 2.5671589374542236 -1.5083990097045898 +3085 0 2.4648511409759521 -1.8469729423522949 +3086 -0.4638819694519043 4.66204833984375 -3.0742619037628174 +3087 -1.4420979022979736 3.5212054252624512 -4.2287187576293945 +3088 -1.0341817140579224 2.8420863151550293 -0.75778979063034058 +3089 -1.0519633293151855 2.62091064453125 0.1493220180273056 +3090 -1.0267006158828735 2.858983039855957 -0.86340230703353882 +3091 -0.81121397018432617 2.6227953433990479 -0.99649769067764282 +3092 -0.7194785475730896 2.6844067573547363 -1.1131532192230225 +3093 -1.1113904714584351 2.3251099586486816 -0.8068091869354248 +3094 -1.3444375991821289 2.1380910873413086 -0.65158849954605103 +3095 -1.2045893669128418 2.1399185657501221 -0.91843235492706299 +3096 -1.018741250038147 2.0509312152862549 0.88641726970672607 +3097 -1.1023390293121338 1.8166135549545288 1.0103610754013062 +3098 -0.98349910974502563 1.7521466016769409 1.1993355751037598 +3099 -1.1115260124206543 2.1267766952514648 0.6948506236076355 +3100 -1.2095291614532471 1.8343952894210815 0.8223501443862915 +3101 -0.91770446300506592 2.6923162937164307 -0.9053688645362854 +3102 -0.83865392208099365 1.6281812191009521 1.3652533292770386 +3103 -0.88620990514755249 2.0237767696380615 1.0178636312484741 +3104 -1.300686240196228 2.359616756439209 -0.26443636417388916 +3105 -1.2326503992080688 2.3761637210845947 -0.48922497034072876 +3106 -1.2900786399841309 2.3362243175506592 0.0039292946457862854 +3107 -1.2912851572036743 2.2047493457794189 0.24709221720695496 +3108 -1.4188553094863892 2.0075371265411377 0.25080773234367371 +3109 -1.3169475793838501 2.322390079498291 -0.07347223162651062 +3110 -0.80932939052581787 2.0351059436798096 -1.35505211353302 +3111 -1.2324646711349487 2.3985352516174316 0.059151533991098404 +3112 -1.1508586406707764 2.4974091053009033 0.1037605032324791 +3113 -1.2078872919082642 2.1768524646759033 0.46095630526542664 +3114 -1.1054728031158447 2.3935885429382324 0.40820431709289551 +3115 -0.98534786701202393 2.3470888137817383 0.63934117555618286 +3116 -0.10559680312871933 2.0028326511383057 1.3973833322525024 +3117 -0.49253687262535095 2.2245516777038574 -1.3695499897003174 +3118 -0.70049399137496948 2.3015105724334717 -1.2349340915679932 +3119 -0.87491697072982788 2.3052153587341309 -1.1328624486923218 +3120 -0.40979582071304321 2.4573347568511963 -1.2981232404708862 +3121 -0.47000738978385925 2.3683967590332031 1.0955507755279541 +3122 -0.30536255240440369 2.1560659408569336 -1.4806591272354126 +3123 -0.55987757444381714 1.9001616239547729 -1.5355249643325806 +3124 -0.11164873093366623 2.2201900482177734 -1.4997684955596924 +3125 -0.28354477882385254 1.9056583642959595 -1.6449849605560303 +3126 -0.18002809584140778 2.4064085483551025 1.1748796701431274 +3127 -0.7251250147819519 1.9069355726242065 1.2222356796264648 +3128 -0.73765993118286133 2.3038086891174316 0.92366540431976318 +3129 -0.52495801448822021 1.9427986145019531 1.3105875253677368 +3130 -0.85103905200958252 2.6975197792053223 0.50975865125656128 +3131 -0.7564125657081604 2.5419814586639404 -1.0568313598632813 +3132 -0.51717931032180786 4.3047075271606445 -1.1015387773513794 +3133 -0.33029836416244507 4.4195427894592285 -0.99995237588882446 +3134 -0.46082028746604919 4.1658940315246582 -0.16952827572822571 +3135 -0.529712975025177 3.9480240345001221 -4.5687770843505859 +3136 -0.64930951595306396 3.0687811374664307 -4.6731410026550293 +3137 -1.1212629079818726 2.994734525680542 -4.4646849632263184 +3138 -0.28465405106544495 4.2166123390197754 -0.068520091474056244 +3139 -1.0609791278839111 3.3971114158630371 -4.5549783706665039 +3140 -1.4437682628631592 3.2162458896636963 -4.2692217826843262 +3141 -1.0572956800460815 3.1853868961334229 -4.556391716003418 +3142 -0.97102123498916626 2.8072946071624756 -4.4211196899414063 +3143 -0.083968184888362885 3.9464812278747559 0.44331973791122437 +3144 -1.0083835124969482 3.6530513763427734 -4.524383544921875 +3145 0 2.4462990760803223 -4.1473197937011719 +3146 -0.20594330132007599 3.6960172653198242 -4.7500209808349609 +3147 -0.27357670664787292 4.0781211853027344 -4.5488324165344238 +3148 -0.35452467203140259 2.4380898475646973 -4.132843017578125 +3149 -0.70014488697052002 2.2884047031402588 -3.6900343894958496 +3150 -0.84527838230133057 2.6268575191497803 -4.2996530532836914 +3151 -0.54680579900741577 3.5365090370178223 0.70457911491394043 +3152 -0.61401879787445068 3.0304899215698242 0.65044361352920532 +3153 -0.68695884943008423 3.2892997264862061 0.51947486400604248 +3154 -0.41557645797729492 3.0600571632385254 0.84811276197433472 +3155 -0.20280955731868744 3.1288139820098877 0.86553770303726196 +3156 0 3.453610897064209 0.75377190113067627 +3157 -0.30705147981643677 1.625540018081665 1.5982861518859863 +3158 -0.59553390741348267 1.5299925804138184 1.5554773807525635 +3159 -0.19459113478660583 2.3797545433044434 -1.4026222229003906 +3160 -0.28473547101020813 2.0023117065429688 1.371785044670105 +3161 -0.1209464892745018 1.7391475439071655 1.5621159076690674 +3162 -0.52334690093994141 3.5388870239257813 -4.7286128997802734 +3163 -0.62134349346160889 3.2946183681488037 -4.725214958190918 +3164 -0.48456475138664246 2.8644590377807617 -4.6037192344665527 +3165 -0.20766082406044006 2.7593531608581543 -4.5493893623352051 +3166 -0.59079825878143311 2.5364785194396973 -4.2465720176696777 +3167 0 2.8081443309783936 -4.5945677757263184 +3168 0 2.3561685085296631 1.1888852119445801 +3169 0 2.5983247756958008 -1.4273210763931274 +3170 0 2.4342918395996094 -1.4014444351196289 +3171 -0.75100159645080566 3.0099101066589355 0.36207398772239685 +3172 -0.18329678475856781 4.2041988372802734 -0.082759693264961243 +3173 0 4.1770586967468262 -0.1015736386179924 +3174 0 4.4901556968688965 -0.9824061393737793 +3175 -0.85265946388244629 2.9515180587768555 0.12605947256088257 +3176 -1.6667002439498901 0.66668707132339478 1.0228461027145386 +3177 -1.4858062267303467 0.95184731483459473 0.8240845799446106 +3178 -0.74763232469558716 3.5257444381713867 0.37203571200370789 +3179 -0.72603869438171387 3.5824377536773682 0.40260520577430725 +3180 -0.7473682165145874 3.5835657119750977 0.37959674000740051 +3181 -0.72362589836120605 3.465660572052002 0.38173750042915344 +3182 -0.74211430549621582 3.4599854946136475 0.34871956706047058 +3183 -0.72376149892807007 3.5052928924560547 0.39473935961723328 +3184 -0.72984343767166138 3.3881664276123047 0.31451451778411865 +3185 -0.72623854875564575 3.4053699970245361 0.3481113612651825 +3186 -0.72366875410079956 3.4410831928253174 0.36436408758163452 +3187 -0.94066870212554932 0.2583516538143158 1.7173399925231934 +3188 -0.70291668176651001 0.43213513493537903 1.572188138961792 +3189 -1.629794716835022 0.7816893458366394 1.1350400447845459 +3190 -1.5413286685943604 1.1003756523132324 1.0539979934692383 +3191 -1.0347741842269897 0.28835004568099976 1.8671176433563232 +3192 -1.4153217077255249 0.28307333588600159 1.9857863187789917 +3193 -1.3813716173171997 0.62882423400878906 1.8133586645126343 +3194 -1.3224585056304932 0.16898253560066223 2.0138616561889648 +3195 -1.6218568086624146 0.36259001493453979 1.9409430027008057 +3196 -1.8566815853118896 0.51048183441162109 1.5757930278778076 +3197 -1.4917097091674805 0.63123774528503418 1.763225793838501 +3198 -0.75093734264373779 3.7394034862518311 0.30648314952850342 +3199 -0.77841299772262573 3.7083728313446045 0.26476570963859558 +3200 -0.73039311170578003 3.6464619636535645 0.38751319050788879 +3201 -0.7466471791267395 3.6985006332397461 0.34386977553367615 +3202 -0.74926698207855225 3.3852181434631348 0.241178959608078 +3203 -0.80285489559173584 3.6728167533874512 0.16159093379974365 +3204 -0.60888558626174927 0.46691834926605225 1.6176453828811646 +3205 -0.63389921188354492 0.49082621932029724 1.760627269744873 +3206 -5.9012155532836914 -4.6827220916748047 5.9488997459411621 +3207 -0.77098202705383301 0.47905218601226807 1.7643963098526001 +3208 -0.80181270837783813 1.0479804277420044 -1.649203896522522 +3209 -0.80059206485748291 3.6683194637298584 0.21626244485378265 +3210 -0.80117738246917725 3.6100702285766602 0.26196885108947754 +3211 -5.953953742980957 -4.7151370048522949 5.9544892311096191 +3212 -5.9485650062561035 -4.6919236183166504 5.9257140159606934 +3213 -5.9337882995605469 -4.7075061798095703 5.8885083198547363 +3214 -5.9297690391540527 -4.7310056686401367 5.9306540489196777 +3215 -1.302942156791687 0.73270577192306519 0.63484013080596924 +3216 -0.80578875541687012 3.5600874423980713 0.094838395714759827 +3217 -0.81027168035507202 3.5944371223449707 0.18752041459083557 +3218 -0.80658829212188721 3.5963218212127686 0.093215122818946838 +3219 -0.80490362644195557 3.501317024230957 0.16136391460895538 +3220 -5.9490928649902344 -4.6827220916748047 5.9010219573974609 +3221 -5.8881525993347168 -4.7075061798095703 5.9341516494750977 +3222 -0.76656335592269897 3.3885159492492676 0.10980185866355896 +3223 -0.73476183414459229 3.2652075290679932 0.35687944293022156 +3224 -0.73155665397644043 3.3550941944122314 0.25438284873962402 +3225 -0.78308159112930298 3.4821932315826416 0.023853788152337074 +3226 -0.74901008605957031 3.3479270935058594 0.18372419476509094 +3227 -0.78947049379348755 3.6429355144500732 0.0059549757279455662 +3228 -0.74611181020736694 3.7899794578552246 0.32535496354103088 +3229 -2.1463501453399658 -0.056141968816518784 1.4148792028427124 +3230 -0.8165249228477478 3.6131467819213867 -0.077832870185375214 +3231 -0.79030561447143555 3.5844078063964844 -0.0025994612369686365 +3232 -0.73885214328765869 3.704425573348999 0.35712003707885742 +3233 -2.0897784233093262 0.24044002592563629 1.7610912322998047 +3234 -2.0925838947296143 0.21030177175998688 1.5544061660766602 +3235 -2.3255810737609863 -0.060906935483217239 1.9832593202590942 +3236 -2.3834307193756104 -0.22118119895458221 1.7044767141342163 +3237 -1.8104103803634644 -0.26958960294723511 2.3082418441772461 +3238 -0.81546849012374878 3.5503575801849365 -0.062470488250255585 +3239 -2.2730352878570557 -0.080489993095397949 2.1682865619659424 +3240 -2.0883579254150391 -0.091529540717601776 2.2385070323944092 +3241 -2.0142898559570313 -0.1090841218829155 1.3339012861251831 +3242 -0.78380972146987915 3.7109930515289307 0.05467589944601059 +3243 -0.78709328174591064 3.6928398609161377 0.029252946376800537 +3244 -0.79004150629043579 3.5425126552581787 0.034447956830263138 +3245 -0.78977024555206299 3.5359597206115723 -0.0011700560571625829 +3246 -1.664408802986145 0.52781176567077637 0.95959258079528809 +3247 -1.8553893566131592 0.49521857500076294 1.3470360040664673 +3248 -1.9059504270553589 0.37316709756851196 1.2406668663024902 +3249 -1.9044516086578369 0.23588082194328308 1.1837382316589355 +3250 -0.78210365772247314 3.7304878234863281 0.069892667233943939 +3251 -0.78005486726760864 3.7507181167602539 0.096842169761657715 +3252 -1.8472944498062134 0.15244074165821075 2.0615389347076416 +3253 -1.8056846857070923 0.4742908775806427 1.7874747514724731 +3254 -1.6451922655105591 0.073212429881095886 2.1026990413665771 +3255 -2.0370969772338867 0.21608379483222961 1.9566047191619873 +3256 -2.1431307792663574 0.082626648247241974 1.4603579044342041 +3257 -0.78562277555465698 3.4461586475372314 0.038981817662715912 +3258 -0.77713525295257568 3.4121725559234619 0.068002738058567047 +3259 -0.78433793783187866 3.5054214000701904 0.017464932054281235 +3260 -0.78776443004608154 3.4621486663818359 0.091704629361629486 +3261 -1.5591104030609131 -0.042107440531253815 2.1375484466552734 +3262 -1.5159374475479126 -0.15953697264194489 2.1214656829833984 +3263 -1.2729036808013916 0.068676002323627472 1.9796901941299438 +3264 -1.210057258605957 1.5419566631317139 1.1390806436538696 +3265 -0.74429160356521606 3.6440134048461914 0.37125480175018311 +3266 -1.5878709554672241 1.926152229309082 0.078563310205936432 +3267 -1.5958874225616455 1.997714638710022 -0.097415164113044739 +3268 -1.4511921405792236 2.1300961971282959 0.01991831511259079 +3269 -1.3313529491424561 1.8736493587493896 0.55672132968902588 +3270 -1.4155787229537964 1.5367456674575806 0.77956974506378174 +3271 -0.2370988130569458 1.3578646183013916 1.7915225028991699 +3272 -0.35262230038642883 1.2890076637268066 1.7940208911895752 +3273 -0.51797527074813843 1.2071303129196167 1.7855260372161865 +3274 -1.4895111322402954 1.0352237224578857 0.91802585124969482 +3275 -1.5797404050827026 1.2525662183761597 0.66695445775985718 +3276 -1.6756947040557861 1.2351272106170654 0.41760110855102539 +3277 -1.5360391139984131 1.1292934417724609 0.66564875841140747 +3278 -1.0512353181838989 1.892216682434082 -1.3344293832778931 +3279 -0.97754561901092529 1.8376792669296265 -1.3951199054718018 +3280 -1.0982843637466431 2.1579644680023193 -1.0908458232879639 +3281 -1.1773778200149536 1.9476175308227539 -1.1989568471908569 +3282 -0.96472513675689697 2.1264557838439941 -1.2385962009429932 +3283 -0.073244892060756683 1.4472157955169678 1.7532035112380981 +3284 0 1.7700996398925781 1.5370315313339233 +3285 -0.41205224394798279 1.5752291679382324 -1.7714916467666626 +3286 -0.098539076745510101 1.6835827827453613 -1.8600717782974243 +3287 -0.16171099245548248 1.6635524034500122 -1.8413978815078735 +3288 -0.11285297572612762 1.9639359712600708 -1.6895427703857422 +3289 -0.75278621912002563 3.4160702228546143 0.29393458366394043 +3290 -0.78250336647033691 3.4389915466308594 0.23183839023113251 +3291 -1.5537352561950684 1.9770561456680298 -0.38686051964759827 +3292 -1.4518846273422241 2.1833984851837158 -0.17306710779666901 +3293 -0.77660703659057617 3.5743069648742676 0.33412373065948486 +3294 -0.78059029579162598 3.5387721061706543 0.32126173377037048 +3295 -1.2872161865234375 1.9317773580551147 -1.0289630889892578 +3296 -1.3795654773712158 1.8981626033782959 -0.88932907581329346 +3297 -0.78457349538803101 3.4318604469299316 0.18506765365600586 +3298 -0.75852549076080322 3.384390115737915 0.1731707900762558 +3299 -0.78649371862411499 3.4399981498718262 0.1374146044254303 +3300 -1.4192620515823364 2.1769094467163086 -0.37207546830177307 +3301 -1.5890130996704102 2.0073370933532715 -0.1814347505569458 +3302 -1.5277156829833984 1.3077389001846313 0.83706939220428467 +3303 -1.5274301767349243 1.5348397493362427 0.60083013772964478 +3304 -0.79189032316207886 3.6373319625854492 0.045735914260149002 +3305 -0.79269701242446899 3.5911822319030762 0.036198873072862625 +3306 -0.80601722002029419 3.6215057373046875 0.10137718170881271 +3307 -0.80514633655548096 3.6432065963745117 0.1146838441491127 +3308 -0.790691077709198 3.6725668907165527 0.063821963965892792 +3309 -1.4612644910812378 1.0229456424713135 0.63248670101165771 +3310 -0.80405420064926147 3.6611599922180176 0.1353587806224823 +3311 -0.78610825538635254 3.724198579788208 0.1348855197429657 +3312 -1.3936996459960938 1.2958823442459106 -0.6234591007232666 +3313 -1.4316258430480957 1.2151614427566528 -0.76769816875457764 +3314 -0.78775006532669067 3.7007277011871338 0.08957885205745697 +3315 -1.5269591808319092 1.239753246307373 -0.57918035984039307 +3316 -0.80377578735351563 3.4977047443389893 0.1889587938785553 +3317 -0.6207767128944397 1.0382436513900757 -1.6481261253356934 +3318 -0.49085581302642822 0.98290687799453735 -1.8227381706237793 +3319 -0.59147858619689941 0.95071983337402344 -1.5542418956756592 +3320 -0.51950430870056152 0.88158446550369263 -1.6896142959594727 +3321 -1.6175953149795532 1.4022657871246338 -0.54537862539291382 +3322 -1.47222900390625 1.4228744506835938 -0.65726011991500854 +3323 -1.4887901544570923 1.3580292463302612 -0.86447328329086304 +3324 -0.80583876371383667 3.5132594108581543 0.13600835204124451 +3325 -0.80648124217987061 3.5323545932769775 0.11537910997867584 +3326 -0.79036271572113037 3.4998533725738525 0.058744180947542191 +3327 -1.0129877328872681 1.2691420316696167 -1.6862235069274902 +3328 -0.43411487340927124 1.0673612356185913 -1.905001163482666 +3329 -0.65227842330932617 1.1276092529296875 -1.7319236993789673 +3330 -0.81000041961669922 1.3941994905471802 -1.6956247091293335 +3331 -0.77868437767028809 3.495741605758667 0.30762884020805359 +3332 -0.65636509656906128 1.2334215641021729 -1.7809927463531494 +3333 -0.78047603368759155 3.4608635902404785 0.27460739016532898 +3334 -0.80139154195785522 3.5127954483032227 0.24255955219268799 +3335 -1.4918596744537354 1.458594799041748 -0.95058369636535645 +3336 -1.5308494567871094 1.4821515083312988 -1.1055225133895874 +3337 -0.80256932973861694 3.5021307468414307 0.21657651662826538 +3338 -1.6663861274719238 1.4483938217163086 0.45825713872909546 +3339 -1.5791407823562622 1.633456826210022 -0.7156948447227478 +3340 -1.5473748445510864 1.5309852361679077 -0.68695640563964844 +3341 -1.4878478050231934 1.8560676574707031 -0.72819411754608154 +3342 -1.6780431270599365 1.5227119922637939 -0.48784741759300232 +3343 -1.4423121213912964 0.84123075008392334 0.72631716728210449 +3344 -1.5051227807998657 0.91117280721664429 0.50942790508270264 +3345 -0.8016628623008728 3.6748940944671631 0.18917155265808105 +3346 -0.7811470627784729 3.7367196083068848 0.18253642320632935 +3347 -0.78300303220748901 3.7219791412353516 0.22469432651996613 +3348 -1.3795156478881836 0.91403526067733765 0.59909039735794067 +3349 -0.79914295673370361 3.5897045135498047 0.27315542101860046 +3350 -0.79959267377853394 3.5661978721618652 0.27237877249717712 +3351 -0.77829176187515259 3.6213061809539795 0.32319554686546326 +3352 -0.80036360025405884 3.537344217300415 0.25963246822357178 +3353 -0.77443695068359375 3.6737372875213623 0.30658668279647827 +3354 -0.79974973201751709 3.653735876083374 0.24021320044994354 +3355 -0.79921430349349976 3.6325631141662598 0.25867879390716553 +3356 -2.9658875465393066 -0.20550991594791412 -0.50951272249221802 +3357 -3.9944155216217041 -2.2534325122833252 -3.7273974418640137 +3358 -3.8430531024932861 -2.1690781116485596 -3.4236299991607666 +3359 -3.8211026191711426 -2.0602819919586182 -3.4353656768798828 +3360 -4.054527759552002 -2.3508217334747314 -3.689877986907959 +3361 -3.7406959533691406 -1.9720513820648193 -3.6215279102325439 +3362 -3.55637526512146 -1.9720512628555298 -3.8058481216430664 +3363 -3.6959452629089355 -2.0101776123046875 -3.760141134262085 +3364 -3.977583646774292 -2.2697298526763916 -3.862947940826416 +3365 -0.94537287950515747 3.5090267658233643 -0.96054142713546753 +3366 -3.36342453956604 -2.1690781116485596 -3.9032585620880127 +3367 -3.9840364456176758 -2.4722671508789063 -3.6175732612609863 +3368 -0.90449845790863037 3.5282144546508789 -0.84809744358062744 +3369 -1.2116278409957886 3.5222823619842529 -1.6334775686264038 +3370 -1.188021183013916 3.5963435173034668 -1.5963937044143677 +3371 -1.1029385328292847 3.6753439903259277 -1.5059002637863159 +3372 -0.99286466836929321 3.5780975818634033 -1.150572657585144 +3373 -4.2210955619812012 -2.7475948333740234 -3.8788740634918213 +3374 -4.0781278610229492 -2.4537003040313721 -3.6828038692474365 +3375 -1.312036395072937 3.4826431274414063 -1.8565524816513062 +3376 -3.669297456741333 -2.2534325122833252 -4.0525150299072266 +3377 -3.3724331855773926 -2.0602819919586182 -3.8840348720550537 +3378 -3.6342122554779053 -2.3508214950561523 -4.1101932525634766 +3379 -1.3895807266235352 3.5919821262359619 -2.0384814739227295 +3380 -1.1669700145721436 3.7164328098297119 -1.7045187950134277 +3381 -3.8330309391021729 -2.7475945949554443 -4.2669382095336914 +3382 -3.8958556652069092 -2.7332966327667236 -4.3562469482421875 +3383 -3.2965664863586426 -2.1917996406555176 -3.8035640716552734 +3384 -3.0973198413848877 -1.8794810771942139 -3.6726315021514893 +3385 -3.1108181476593018 -1.7648955583572388 -3.655463695526123 +3386 -0.83451366424560547 3.6997501850128174 -0.917411208152771 +3387 -0.88960063457489014 3.5866994857788086 -0.8753090500831604 +3388 -0.88129866123199463 3.6642224788665771 -0.98116415739059448 +3389 -0.95760095119476318 3.6927618980407715 -1.2168095111846924 +3390 -3.5042507648468018 -1.666364312171936 -3.3819491863250732 +3391 -3.5850932598114014 -1.7648955583572388 -3.1811959743499756 +3392 -3.3469061851501465 -1.4647190570831299 -2.9275760650634766 +3393 -2.9043831825256348 -1.3489698171615601 -3.3686716556549072 +3394 -3.1587023735046387 -1.6559851169586182 -3.5977780818939209 +3395 -3.3090801239013672 -1.666364312171936 -3.5771126747131348 +3396 -3.7601549625396729 -1.957817554473877 -3.479088306427002 +3397 -3.5246450901031494 -1.6559851169586182 -3.2318356037139893 +3398 -3.4540178775787354 -1.7057609558105469 -3.5258874893188477 +3399 -3.4135787487030029 -1.9578174352645874 -3.8256642818450928 +3400 -0.86720752716064453 3.4454090595245361 -0.62345302104949951 +3401 -0.9374421238899231 3.3831336498260498 -0.80010610818862915 +3402 -0.88349735736846924 3.4905450344085693 -0.75122231245040894 +3403 -0.89087128639221191 3.4424893856048584 -0.72117692232131958 +3404 -3.743922233581543 -2.1917998790740967 -3.3562078475952148 +3405 -1.0023372173309326 2.9332935810089111 -0.6094861626625061 +3406 -0.98620450496673584 2.9670867919921875 -0.65202665328979492 +3407 -4.1595978736877441 -3.0084743499755859 -4.576016902923584 +3408 -0.97787398099899292 2.9919924736022949 -0.60591191053390503 +3409 -0.96790885925292969 3.0380208492279053 -0.5842055082321167 +3410 -0.97266304492950439 3.0212814807891846 -0.61490201950073242 +3411 -0.96556025743484497 3.0837855339050293 -0.68901485204696655 +3412 -4.1793928146362305 -2.8302145004272461 -4.498957633972168 +3413 -4.1571140289306641 -2.9171316623687744 -4.5539870262145996 +3414 -0.95198303461074829 3.1574463844299316 -0.60960817337036133 +3415 -3.8958773612976074 -2.6364572048187256 -4.3336038589477539 +3416 -4.0532932281494141 -2.5626604557037354 -4.262855052947998 +3417 -0.94148963689804077 3.1469244956970215 -0.55580109357833862 +3418 -0.94239616394042969 3.2499814033508301 -0.69809705018997192 +3419 -4.4172224998474121 -2.8896703720092773 -4.4595823287963867 +3420 -4.6753826141357422 -3.2797901630401611 -4.3904542922973633 +3421 -4.7361302375793457 -3.1927947998046875 -4.4525370597839355 +3422 -0.98251396417617798 3.005291223526001 -0.72448199987411499 +3423 -4.4449834823608398 -2.8508014678955078 -4.3434548377990723 +3424 -4.6810359954833984 -3.1112601757049561 -4.4700546264648438 +3425 -3.6296939849853516 -2.453700065612793 -4.1312375068664551 +3426 -3.9246737957000732 -2.54425048828125 -4.2770466804504395 +3427 -1.3764889240264893 3.4346089363098145 -2.0009548664093018 +3428 -1.3282691240310669 3.359555721282959 -1.932997465133667 +3429 -3.9379580020904541 -2.308091402053833 -3.9946944713592529 +3430 -3.8052554130554199 -2.2697298526763916 -4.035275936126709 +3431 -4.2124505043029785 -2.5626604557037354 -4.1036982536315918 +3432 -4.2850193977355957 -2.6364574432373047 -3.9444615840911865 +3433 -4.455141544342041 -2.8302145004272461 -4.2232089042663574 +3434 -4.2261848449707031 -2.5442507266998291 -3.9755420684814453 +3435 -4.1784930229187012 -2.6012721061706543 -4.2279415130615234 +3436 -4.3001456260681152 -2.8508014678955078 -4.4882931709289551 +3437 -0.9624408483505249 3.0749979019165039 -0.59896975755691528 +3438 -0.95909291505813599 3.0934791564941406 -0.57928067445755005 +3439 -0.95808637142181396 3.1250882148742676 -0.67967361211776733 +3440 -0.95051974058151245 3.1773123741149902 -0.68048232793807983 +3441 -4.5122981071472168 -2.9171316623687744 -4.1987957954406738 +3442 -4.310053825378418 -2.7332968711853027 -3.9420490264892578 +3443 -4.4530000686645508 -3.0152201652526855 -4.1380119323730469 +3444 -4.5365695953369141 -3.0084743499755859 -4.1990528106689453 +3445 -2.3900907039642334 -0.35848474502563477 -1.8802025318145752 +3446 -1.042690634727478 2.8932759761810303 -1.1710383892059326 +3447 -2.570063591003418 -0.40346512198448181 -2.2478723526000977 +3448 -2.3310775756835938 -0.078977413475513458 -2.0032682418823242 +3449 -0.81985855102539063 2.9863748550415039 -1.2840033769607544 +3450 -0.81810253858566284 2.9544591903686523 -1.2378466129302979 +3451 -1.0559179782867432 3.0186333656311035 -1.2770363092422485 +3452 -1.0126094818115234 3.0859270095825195 -1.2567846775054932 +3453 -1.0539121627807617 2.9585354328155518 -1.2358263731002808 +3454 -0.80757337808609009 3.0231735706329346 -1.268455982208252 +3455 -1.0366086959838867 3.0127227306365967 -1.2497390508651733 +3456 -0.80214822292327881 3.0972983837127686 -1.2769720554351807 +3457 -2.6344163417816162 -0.67242193222045898 -2.1359570026397705 +3458 -2.6246294975280762 -0.53601574897766113 -2.1710989475250244 +3459 -0.80947935581207275 3.0539186000823975 -1.3119360208511353 +3460 -1.8899534940719604 -0.079399928450584412 -2.4445779323577881 +3461 -1.8103176355361938 -0.21725799143314362 -2.4934902191162109 +3462 -2.3327977657318115 -0.38029167056083679 -2.6679167747497559 +3463 -2.2733423709869385 -0.067873001098632813 -2.3871498107910156 +3464 -2.0890860557556152 -0.049813438206911087 -2.4401378631591797 +3465 -2.0693554878234863 -0.53601568937301636 -2.7263731956481934 +3466 -1.9578109979629517 -0.71420282125473022 -2.608689546585083 +3467 -1.7740330696105957 -0.35877370834350586 -2.4963881969451904 +3468 -1.2925057411193848 0.43433293700218201 -2.0328423976898193 +3469 -1.5104551315307617 -0.04048701748251915 -2.2612283229827881 +3470 -1.424059271812439 -0.08904680609703064 -2.1279976367950439 +3471 -1.0250658988952637 2.9186959266662598 -0.9092593789100647 +3472 -1.0517064332962036 2.8052525520324707 -0.96669489145278931 +3473 -1.0099682807922363 2.9102938175201416 -0.77511465549468994 +3474 -2.093576192855835 0.25874128937721252 -1.7548023462295532 +3475 -1.8462164402008057 0.28600218892097473 -2.2130799293518066 +3476 -1.601476788520813 0.63592392206192017 -1.9799542427062988 +3477 -2.0907349586486816 0.28804230690002441 -1.9675834178924561 +3478 -2.037989616394043 0.27153405547142029 -2.1601130962371826 +3479 -2.3258593082427979 -0.049787208437919617 -2.203357458114624 +3480 -1.551486611366272 0.10599476844072342 -2.2619493007659912 +3481 -1.6376327276229858 0.24933724105358124 -2.2152500152587891 +3482 -1.0483013391494751 2.8761796951293945 -0.99472731351852417 +3483 -1.0699735879898071 2.8016407489776611 -1.0863128900527954 +3484 -1.0600368976593018 2.8652150630950928 -1.0706226825714111 +3485 -0.81243467330932617 2.9530887603759766 -1.196358323097229 +3486 -0.7813112735748291 2.9817278385162354 -1.1442195177078247 +3487 -2.1468358039855957 -0.034892123192548752 -1.6222138404846191 +3488 -2.1438374519348145 0.11483985930681229 -1.6653939485549927 +3489 -2.3835446834564209 -0.21671625971794128 -1.9200133085250854 +3490 -2.2626490592956543 -0.40348798036575317 -1.7958550453186035 +3491 -0.88902962207794189 3.3628895282745361 -0.90927356481552124 +3492 -3.0088748931884766 -1.3200809955596924 -2.5739474296569824 +3493 -3.1225109100341797 -1.2855740785598755 -2.6489503383636475 +3494 -3.2719745635986328 -1.3471353054046631 -3.1479809284210205 +3495 -3.0359866619110107 -1.0285419225692749 -2.9113075733184814 +3496 -3.287750244140625 -1.3489698171615601 -2.9853041172027588 +3497 -2.5664091110229492 -1.2855738401412964 -3.205052375793457 +3498 -2.7600805759429932 -1.6156531572341919 -3.3300671577453613 +3499 -0.94340264797210693 3.4184403419494629 -0.87751483917236328 +3500 -0.72419697046279907 3.2678275108337402 -1.1612017154693604 +3501 -0.91090160608291626 3.3570930957794189 -1.0709152221679688 +3502 -0.93696385622024536 3.3256981372833252 -0.73775225877761841 +3503 -0.93216687440872192 3.151392936706543 -0.74626839160919189 +3504 -0.72042793035507202 3.2293586730957031 -1.1060289144515991 +3505 -3.364809513092041 -1.5849579572677612 -2.9065320491790771 +3506 -3.605144739151001 -1.8794810771942139 -3.1648061275482178 +3507 -2.8222060203552246 -1.0285418033599854 -3.1250884532928467 +3508 -3.2198503017425537 -1.3835339546203613 -3.2999002933502197 +3509 -2.9840974807739258 -1.0588656663894653 -3.0724213123321533 +3510 -3.0670101642608643 -1.3471353054046631 -3.3529455661773682 +3511 -2.5889089107513428 -1.1598025560379028 -3.1927814483642578 +3512 -2.8495960235595703 -1.4647190570831299 -3.4248864650726318 +3513 -2.8316001892089844 -1.5849579572677612 -3.4397342205047607 +3514 -0.89613944292068481 3.2867653369903564 -0.78705000877380371 +3515 -2.3017957210540771 -0.98137128353118896 -2.9691357612609863 +3516 -2.3288431167602539 -0.85021764039993286 -2.9597203731536865 +3517 -2.0377182960510254 -0.67242181301116943 -2.7326550483703613 +3518 -0.94945609569549561 3.2745518684387207 -1.1853508949279785 +3519 -1.0153148174285889 3.3290748596191406 -1.2482116222381592 +3520 -2.5621685981750488 -0.38029170036315918 -2.4385530948638916 +3521 -2.8096210956573486 -0.72312581539154053 -2.492962121963501 +3522 -2.5097086429595947 -0.40232503414154053 -2.6148927211761475 +3523 -2.1427097320556641 -0.40346503257751465 -2.6752195358276367 +3524 -2.3960297107696533 -0.72312569618225098 -2.9065530300140381 +3525 -2.7467389106750488 -0.7326769232749939 -2.843428373336792 +3526 -2.577223539352417 -0.70655614137649536 -2.8964023590087891 +3527 -2.6499350070953369 -1.0383142232894897 -3.137901782989502 +3528 -0.98497664928436279 3.3840262889862061 -1.1288076639175415 +3529 -0.78667932748794556 3.1717236042022705 -1.2953104972839355 +3530 -0.98042243719100952 3.1815743446350098 -1.2631878852844238 +3531 -3.1070349216461182 -1.1598026752471924 -2.6746628284454346 +3532 -2.7990419864654541 -0.70655614137649536 -2.6745841503143311 +3533 -3.0490498542785645 -1.0383143424987793 -2.7387940883636475 +3534 -2.8788206577301025 -0.98137140274047852 -2.3921036720275879 +3535 -2.8660500049591064 -0.85021770000457764 -2.422513484954834 +3536 -1.2086082696914673 2.4929192066192627 -0.078184254467487335 +3537 -0.044122114777565002 -4.6919279098510742 -8.3792591094970703 +3538 -0.80234807729721069 3.4949352741241455 -0.35814681649208069 +3539 -5.8976106643676758 -4.704770565032959 -5.9615359306335449 +3540 -5.9012155532836914 -4.6827201843261719 -5.9492788314819336 +3541 -5.8938274383544922 -4.6919283866882324 -5.9562101364135742 +3542 -5.6959724426269531 -4.4940166473388672 -5.8015570640563965 +3543 -5.7275815010070801 -4.5469117164611816 -5.8305892944335938 +3544 -0.0421910360455513 -4.7099733352661133 -8.3813285827636719 +3545 -0.84623491764068604 3.4419541358947754 -0.48140186071395874 +3546 -0.80808019638061523 3.579268217086792 -0.60054868459701538 +3547 -5.6744575500488281 -4.4408993721008301 -5.7624316215515137 +3548 -5.9257001876831055 -4.6919212341308594 -5.9485511779785156 +3549 -5.7949824333190918 -4.516237735748291 -5.7989087104797363 +3550 -5.7379679679870605 -4.4755992889404297 -5.7810912132263184 +3551 -0.45018056035041809 0.52612191438674927 -1.8587658405303955 +3552 -0.48367384076118469 0.57403624057769775 -2.030144214630127 +3553 -0.46647316217422485 0.77274543046951294 -1.5350323915481567 +3554 -0.88006377220153809 2.9341645240783691 -0.032312307506799698 +3555 -0.912300705909729 2.927325963973999 -0.14121261239051819 +3556 -0.9749329686164856 2.965423583984375 -0.43144243955612183 +3557 -0.35783115029335022 0.82802528142929077 -2.1545162200927734 +3558 -0.35991129279136658 1.1433635950088501 -2.0411515235900879 +3559 0 -4.691199779510498 -8.3969612121582031 +3560 -0.032272599637508392 -4.7075037956237793 -8.3589859008789063 +3561 -0.033853322267532349 -4.6827192306518555 -8.3796157836914063 +3562 -0.90882432460784912 3.1254520416259766 -0.45653033256530762 +3563 -0.90339910984039307 3.0225090980529785 -0.30868640542030334 +3564 -5.5732421875 -4.2577857971191406 -5.5830583572387695 +3565 -5.4445514678955078 -4.2442154884338379 -5.6033306121826172 +3566 -0.8422088623046875 2.9896726608276367 0.016043024137616158 +3567 -5.7971305847167969 -4.4940166473388672 -5.7003989219665527 +3568 -5.6219830513000488 -4.3054986000061035 -5.4792227745056152 +3569 -5.7199358940124512 -4.5062017440795898 -5.6419429779052734 +3570 -5.5631766319274902 -4.2174606323242188 -5.5157790184020996 +3571 -5.7775859832763672 -4.5288019180297852 -5.6802043914794922 +3572 -0.85529351234436035 3.3775656223297119 -0.40924912691116333 +3573 -5.9618420600891113 -4.704770565032959 -5.8973112106323242 +3574 -5.7567987442016602 -4.4408998489379883 -5.6800904273986816 +3575 -5.7762441635131836 -4.4755992889404297 -5.7428150177001953 +3576 -5.827354907989502 -4.5469117164611816 -5.7308225631713867 +3577 -5.9562239646911621 -4.6919283866882324 -5.8938136100769043 +3578 -5.8881525993347168 -4.7075042724609375 -5.9334244728088379 +3579 -5.6765561103820801 -4.5288019180297852 -5.7812337875366211 +3580 -0.81578254699707031 3.0961775779724121 -0.10572550445795059 +3581 -0.8757164478302002 3.1794540882110596 -0.42289349436759949 +3582 -0.84188765287399292 3.2423715591430664 -0.30968931317329407 +3583 -0.83872538805007935 3.18373703956604 -0.29317748546600342 +3584 -0.85518646240234375 3.060513973236084 -0.21384429931640625 +3585 -0.40532433986663818 -0.21725788712501526 -3.0754547119140625 +3586 -0.43561249971389771 -0.3587736189365387 -3.0507771968841553 +3587 -0.44989711046218872 -0.040920600295066833 -2.7008533477783203 +3588 -1.2346490621566772 3.1530351638793945 -1.8277060985565186 +3589 -0.16742168366909027 -0.049813311547040939 -3.2361183166503906 +3590 -0.31191346049308777 -0.079399816691875458 -3.0982263088226318 +3591 -0.17286400496959686 0.27956485748291016 -2.9107577800750732 +3592 0 0.64812856912612915 -2.6768755912780762 +3593 -1.1173580884933472 3.301177978515625 -1.5174643993377686 +3594 -1.3159768581390381 3.1913681030273438 -2.0246469974517822 +3595 -1.0783112049102783 3.2685270309448242 -1.4448598623275757 +3596 -1.1814180612564087 3.2945537567138672 -1.6519660949707031 +3597 -1.0203831195831299 3.0459020137786865 -1.3492910861968994 +3598 -1.0822157859802246 3.1463034152984619 -1.454004168510437 +3599 -1.0459957122802734 3.082150936126709 -1.4332528114318848 +3600 -0.3021831214427948 -0.4034649133682251 -3.4375922679901123 +3601 -0.3926372230052948 -0.53601557016372681 -3.4208886623382568 +3602 -1.056496262550354 3.0709221363067627 -1.4990473985671997 +3603 -0.99231493473052979 2.9879024028778076 -1.3848117589950562 +3604 -1.0732429027557373 3.0426111221313477 -1.573643684387207 +3605 -1.4519345760345459 3.330702543258667 -2.1918997764587402 +3606 -1.0343029499053955 2.9711916446685791 -1.5412425994873047 +3607 -1.0557752847671509 3.3823843002319336 -1.3039480447769165 +3608 -1.0899111032485962 3.3461642265319824 -1.4183264970779419 +3609 -1.0669467449188232 3.4146997928619385 -1.3225650787353516 +3610 -0.19092127680778503 0.91007393598556519 -2.3022811412811279 +3611 -0.33353206515312195 0.54753285646438599 -2.4481472969055176 +3612 -0.4502754807472229 0.70977699756622314 -2.0869870185852051 +3613 -0.98768222332000732 3.4937360286712646 -1.1134672164916992 +3614 -0.96122729778289795 3.4844563007354736 -1.013108491897583 +3615 -0.95440298318862915 3.4300904273986816 -0.9653313159942627 +3616 -0.43370082974433899 0.41721367835998535 -2.4012265205383301 +3617 -0.46575075387954712 0.2750726044178009 -2.3572609424591064 +3618 -0.32224699854850769 0.24394215643405914 -2.7641279697418213 +3619 -0.41871306300163269 0.10461572557687759 -2.7322123050689697 +3620 -1.1892489194869995 3.3981387615203857 -1.6153746843338013 +3621 -1.263973593711853 3.3007783889770508 -1.8160991668701172 +3622 -1.2956751585006714 3.2564988136291504 -1.9088268280029297 +3623 -1.1529072523117065 3.1858429908752441 -1.6287378072738647 +3624 -1.1076499223709106 3.4904882907867432 -1.4175270795822144 +3625 0 0.97716766595840454 -2.3643636703491211 +3626 -0.098265677690505981 0.9050413966178894 -2.3678758144378662 +3627 -0.17861896753311157 0.58585178852081299 -2.6050491333007813 +3628 -1.0467238426208496 3.4673953056335449 -1.2737455368041992 +3629 -0.96880120038986206 3.4184904098510742 -1.0527123212814331 +3630 -4.9130620956420898 -3.5371437072753906 -4.6609349250793457 +3631 -5.1328749656677246 -3.7999148368835449 -4.9159259796142578 +3632 -5.172642707824707 -3.7288522720336914 -4.9568567276000977 +3633 -0.99687641859054565 2.7359457015991211 -0.81431156396865845 +3634 -1.0620428323745728 2.6155710220336914 -0.75083702802658081 +3635 -4.8954520225524902 -3.7999148368835449 -5.1533412933349609 +3636 -1.1231759786605835 2.5623612403869629 -0.66341465711593628 +3637 -1.1758930683135986 2.5037193298339844 -0.56756317615509033 +3638 -1.2697913646697998 2.3780481815338135 -0.3731997013092041 +3639 -4.7889542579650879 -3.4124431610107422 -4.9316010475158691 +3640 -1.1935106515884399 2.3599951267242432 -0.60292959213256836 +3641 -5.1257939338684082 -3.6858294010162354 -5.0534110069274902 +3642 -4.9019198417663574 -3.4124431610107422 -4.818636417388916 +3643 -5.1226811408996582 -3.6584036350250244 -4.959904670715332 +3644 -1.0668326616287231 2.5395898818969727 -0.76467835903167725 +3645 -1.1566264629364014 2.3382587432861328 -0.70812374353408813 +3646 -1.0815377235412598 2.4631590843200684 -0.77823424339294434 +3647 -1.2540868520736694 2.4707901477813721 -0.29963359236717224 +3648 -1.2133196592330933 2.5436804294586182 -0.38565334677696228 +3649 -4.9837541580200195 -3.5429184436798096 -4.7162284851074219 +3650 -1.2520883083343506 2.4837677478790283 -0.24812938272953033 +3651 -4.814117431640625 -3.5371580123901367 -4.623593807220459 +3652 -4.7614641189575195 -3.2778487205505371 -4.4575052261352539 +3653 -4.4245891571044922 -3.2778487205505371 -4.7943873405456543 +3654 -4.689666748046875 -3.5429184436798096 -5.0103168487548828 +3655 -1.3186393976211548 2.3368740081787109 -0.16541185975074768 +3656 -4.43310546875 -3.1112601757049561 -4.717984676361084 +3657 -4.417564868927002 -3.1927947998046875 -4.7711014747619629 +3658 -4.3575887680053711 -3.2797901630401611 -4.7082552909851074 +3659 -1.2349703311920166 2.4828040599822998 -0.14658504724502563 +3660 -4.596895694732666 -3.5371580123901367 -4.840815544128418 +3661 -1.2649301290512085 2.4424078464508057 -0.16697587072849274 +3662 -1.0389502048492432 2.776669979095459 -0.753406822681427 +3663 -1.1691471338272095 2.5678865909576416 -0.5401110053062439 +3664 -4.6854548454284668 -3.3873445987701416 -4.9339070320129395 +3665 -1.1653925180435181 2.6175127029418945 -0.49441316723823547 +3666 -4.6342368125915527 -3.5371437072753906 -4.939753532409668 +3667 -4.6768813133239746 -3.4633827209472656 -4.9847607612609863 +3668 -4.9563064575195313 -3.4633827209472656 -4.7053432464599609 +3669 -4.903618335723877 -3.3873445987701416 -4.7157363891601563 +3670 -4.6748967170715332 -3.1340813636779785 -4.5818772315979004 +3671 -4.8880348205566406 -3.4518539905548096 -4.9167675971984863 +3672 -4.653839111328125 -3.1732141971588135 -4.6892886161804199 +3673 -4.545478343963623 -3.1340813636779785 -4.7112960815429688 +3674 -5.59320068359375 -4.2442231178283691 -5.4546809196472168 +3675 -5.4131350517272949 -4.0571255683898926 -5.2263741493225098 +3676 -5.338017463684082 -3.9243874549865723 -5.2022247314453125 +3677 -5.3462915420532227 -3.9541902542114258 -5.285886287689209 +3678 -5.5494499206542969 -4.1852312088012695 -5.4423818588256836 +3679 -1.1565407514572144 2.5943198204040527 -0.14065660536289215 +3680 -1.0971779823303223 2.7159082889556885 -0.23237060010433197 +3681 -1.0632134675979614 2.723182201385498 -0.14525581896305084 +3682 -5.2762703895568848 -4.0342254638671875 -5.1384577751159668 +3683 -5.4996380805969238 -4.2737760543823242 -5.3908853530883789 +3684 -1.076419472694397 2.800612211227417 -0.64176523685455322 +3685 -1.068809986114502 2.8158814907073975 -0.40156331658363342 +3686 -1.04600989818573 2.8699333667755127 -0.54674750566482544 +3687 -5.5654330253601074 -4.2938990592956543 -5.426020622253418 +3688 -1.1162660121917725 2.7225968837738037 -0.56378328800201416 +3689 -1.1601957082748413 2.6746625900268555 -0.37038144469261169 +3690 -1.0723220109939575 2.8273172378540039 -0.59852516651153564 +3691 -5.1846780776977539 -3.9243874549865723 -5.3555645942687988 +3692 -5.4308953285217285 -4.1852312088012695 -5.5609359741210938 +3693 -5.5050349235534668 -4.2174606323242188 -5.573920726776123 +3694 -5.6377878189086914 -4.5062017440795898 -5.7240910530090332 +3695 -5.4170327186584473 -4.2938990592956543 -5.5744280815124512 +3696 -5.2000751495361328 -3.8034272193908691 -4.9716620445251465 +3697 -5.3849959373474121 -3.9891469478607178 -5.2067360877990723 +3698 -5.3507742881774902 -4.0503802299499512 -5.1714801788330078 +3699 -0.83230078220367432 2.4524731636047363 -1.0450102090835571 +3700 -0.95663726329803467 2.3743293285369873 -0.98148560523986816 +3701 -1.0085905790328979 2.4577412605285645 -0.90143567323684692 +3702 -4.8640928268432617 -3.7876510620117188 -5.0707073211669922 +3703 -1.0471948385238647 2.3343114852905273 -0.89986521005630493 +3704 -5.0499482154846191 -3.7876510620117188 -4.8848514556884766 +3705 -1.0355879068374634 2.4862878322601318 -0.85031771659851074 +3706 -0.9447019100189209 2.5126783847808838 -0.94724279642105103 +3707 -5.4704995155334473 -4.3054986000061035 -5.6307072639465332 +3708 -5.2119255065917969 -4.0571255683898926 -5.427584171295166 +3709 -4.9347057342529297 -3.7288522720336914 -5.1947870254516602 +3710 -5.1907033920288086 -3.9891469478607178 -5.401029109954834 +3711 -4.9512815475463867 -3.8034272193908691 -5.2204632759094238 +3712 -5.1568665504455566 -4.0503802299499512 -5.365386962890625 +3713 -5.3814406394958496 -4.2737760543823242 -5.5090832710266113 +3714 -1.0250301361083984 2.7313625812530518 -0.035286124795675278 +3715 -0.94837105274200439 2.7714662551879883 0.16652555763721466 +3716 -0.96323317289352417 2.8430001735687256 -0.098031066358089447 +3717 -0.86532300710678101 2.5345077514648438 -0.99534845352172852 +3718 -4.9360976219177246 -3.6584036350250244 -5.1464958190917969 +3719 -5.2690396308898926 -3.9541902542114258 -5.3631386756896973 +3720 -5.3480048179626465 -3.9942007064819336 -5.3639240264892578 +3721 -5.1195192337036133 -3.7255403995513916 -5.1417412757873535 +3722 -5.0302467346191406 -3.6858294010162354 -5.1489582061767578 +3723 -4.7347240447998047 -1.764896035194397 0.2649875283241272 +3724 -4.7258439064025879 -1.6559855937957764 0.18563146889209747 +3725 -4.3815879821777344 -1.4647195339202881 0.27367144823074341 +3726 -6.852485179901123 -3.4124438762664795 0.050195399671792984 +3727 -7.1814360618591309 -3.6858301162719727 0.044399268925189972 +3728 -7.2401137351989746 -3.7255411148071289 -0.022223491221666336 +3729 -4.7258439064025879 -1.6559855937957764 -0.33188906311988831 +3730 -4.8177509307861328 -1.6663647890090942 -0.21086978912353516 +3731 -7.1126294136047363 -3.6584043502807617 0.10812319070100784 +3732 -6.8116888999938965 -3.463383674621582 0.16912008821964264 +3733 -4.5535588264465332 -1.3835345506668091 -0.080045871436595917 +3734 -4.8177509307861328 -1.6663647890090942 0.065137267112731934 +3735 -4.6169185638427734 -1.9062433242797852 0.2675594687461853 +3736 -7.3537569046020508 -4.0342264175415039 -0.12302941083908081 +3737 -7.0102863311767578 -3.7876517772674561 -0.1521754264831543 +3738 -4.2539534568786621 -1.6156536340713501 0.2764560878276825 +3739 -4.3815231323242188 -1.5849584341049194 0.30210503935813904 +3740 -1.2436007261276245 1.8236021995544434 -1.2150610685348511 +3741 -1.3454867601394653 1.7900946140289307 -1.0575594902038574 +3742 -7.1126294136047363 -3.6584043502807617 -0.1557496041059494 +3743 -6.9127259254455566 -3.451854944229126 -0.028735874220728874 +3744 -6.852485179901123 -3.4124438762664795 -0.10955630987882614 +3745 -7.429995059967041 -4.0503807067871094 -0.15172217786312103 +3746 -7.5130147933959961 -4.0571260452270508 -0.15672905743122101 +3747 -7.091099739074707 -3.7999157905578613 -0.18834921717643738 +3748 -7.1469578742980957 -3.7288529872894287 -0.19039079546928406 +3749 -6.8116888999938965 -3.463383674621582 -0.22603987157344818 +3750 -7.1780810356140137 -3.8034281730651855 -0.1963128000497818 +3751 -6.8401422500610352 -3.542919397354126 -0.23451317846775055 +3752 -6.5815258026123047 -3.1732151508331299 -0.035448413342237473 +3753 -6.5197930335998535 -3.1340823173522949 0.055114436894655228 +3754 -6.7805013656616211 -3.3873453140258789 -0.18454796075820923 +3755 -6.5197930335998535 -3.1340823173522949 -0.12790906429290771 +3756 -7.1780810356140137 -3.8034281730651855 0.15554137527942657 +3757 -4.7347240447998047 -1.764896035194397 -0.40573576092720032 +3758 -4.3815231323242188 -1.5849583148956299 -0.45196250081062317 +3759 -7.091099739074707 -3.7999157905578613 0.14741216599941254 +3760 -6.8401422500610352 -3.542919397354126 0.18139380216598511 +3761 -1.3805792331695557 1.3935567140579224 1.0046145915985107 +3762 -1.3201740980148315 1.5398937463760376 0.93585765361785889 +3763 -1.2098716497421265 1.3807146549224854 1.3094525337219238 +3764 -1.1908693313598633 1.3217943906784058 1.3819286823272705 +3765 -7.0102863311767578 -3.7876517772674561 0.11066088080406189 +3766 -6.6545944213867188 -3.5371589660644531 0.12690004706382751 +3767 -1.1355540752410889 1.3167475461959839 1.4260226488113403 +3768 -1.0480444431304932 1.4215247631072998 1.4116815328598022 +3769 -1.1056369543075562 1.4760264158248901 1.330896258354187 +3770 -7.694115161895752 -4.2737765312194824 0.074130065739154816 +3771 -4.1423659324645996 -1.0285422801971436 0.062058940529823303 +3772 -7.7657852172851563 -4.2939000129699707 -0.11392443627119064 +3773 -1.1092917919158936 1.2449780702590942 1.4843074083328247 +3774 -0.96840852499008179 1.3455437421798706 1.5030030012130737 +3775 -4.3784966468811035 -1.3489704132080078 0.19015511870384216 +3776 -7.8435940742492676 -4.3054990768432617 0.098394885659217834 +3777 -7.4407029151916504 -3.9243881702423096 0.090880364179611206 +3778 -7.7642855644226074 -4.1852321624755859 0.072343349456787109 +3779 -4.0276446342468262 -1.159803032875061 0.28061783313751221 +3780 -4.0226688385009766 -1.2855744361877441 0.31068262457847595 +3781 -1.1183576583862305 1.9288723468780518 -1.268805980682373 +3782 -1.226739764213562 1.9484241008758545 -1.1255885362625122 +3783 -7.4781794548034668 -3.9891476631164551 0.12135057896375656 +3784 -7.1469578742980957 -3.7288529872894287 0.14610084891319275 +3785 -4.6169185638427734 -1.9062433242797852 -0.40117576718330383 +3786 -1.1595174074172974 1.5174789428710938 1.2368336915969849 +3787 -1.2186235189437866 1.7911797761917114 -1.2800847291946411 +3788 -1.2844251394271851 1.7819783687591553 -1.1792764663696289 +3789 -7.5130147933959961 -4.0571260452270508 0.12782652676105499 +3790 -7.3537569046020508 -4.0342264175415039 0.093061842024326324 +3791 -7.429995059967041 -4.0503807067871094 0.12250556796789169 +3792 -7.5061616897583008 -3.9541909694671631 0.037775367498397827 +3793 -4.1423659324645996 -1.0285422801971436 -0.24026913940906525 +3794 -4.2201457023620605 -1.0588661432266235 -0.088329210877418518 +3795 -4.4823384284973145 -1.3471357822418213 -0.22590318322181702 +3796 -4.0297932624816895 -1.0383147001266479 -0.37106946110725403 +3797 -4.4823384284973145 -1.3471357822418213 0.063958130776882172 +3798 -7.5061616897583008 -3.9541909694671631 -0.071474500000476837 +3799 -7.1814360618591309 -3.6858301162719727 -0.090725265443325043 +3800 -4.3784966468811035 -1.3489704132080078 -0.3520064651966095 +3801 -4.7393636703491211 -1.8794814348220825 -0.42656919360160828 +3802 -4.3815879821777344 -1.4647194147109985 -0.42963361740112305 +3803 -4.0226688385009766 -1.2855743169784546 -0.47576472163200378 +3804 -1.6825474500656128 1.8516631126403809 -0.033048242330551147 +3805 -1.6362621784210205 1.6973453760147095 0.23749816417694092 +3806 -1.6694628000259399 1.7751467227935791 0.095225006341934204 +3807 -1.6602399349212646 1.8460522890090942 0.031739518046379089 +3808 -1.5519006252288818 1.7030847072601318 0.38720092177391052 +3809 -5.6951231956481934 -2.7475955486297607 0.22855871915817261 +3810 -5.0865964889526367 -2.0602824687957764 0.25432199239730835 +3811 -5.5691089630126953 -2.3080918788909912 -0.056734595447778702 +3812 -5.1598081588745117 -1.9720519781112671 -0.19548375904560089 +3813 -6.1491174697875977 -3.0084750652313232 0.22710244357585907 +3814 -5.8447651863098145 -2.5626611709594727 -0.16294991970062256 +3815 -6.2468991279602051 -2.8896710872650146 -0.042356118559837341 +3816 -6.183739185333252 -2.8508024215698242 -0.14572429656982422 +3817 -5.3379178047180176 -2.4722676277160645 0.24370571970939636 +3818 -5.0957479476928711 -2.1690788269042969 0.27893945574760437 +3819 -1.6580413579940796 1.8545613288879395 -0.25330910086631775 +3820 -1.5230613946914673 1.923525333404541 -0.54645711183547974 +3821 -5.8024487495422363 -2.73329758644104 0.24668595194816589 +3822 -5.7635235786437988 -2.5442512035369873 0.16233231127262115 +3823 -6.183739185333252 -2.8508024215698242 0.05910763144493103 +3824 -5.9092750549316406 -2.6012728214263916 -0.049453489482402802 +3825 -6.1055383682250977 -2.8302152156829834 0.15117138624191284 +3826 -5.419060230255127 -2.2534329891204834 0.17179717123508453 +3827 -5.5033001899719238 -2.2697303295135498 0.064159713685512543 +3828 -5.4367561340332031 -2.3508224487304688 0.24153709411621094 +3829 -5.8447651863098145 -2.5626611709594727 0.062132507562637329 +3830 -1.6787141561508179 1.8727927207946777 -0.13188646733760834 +3831 -1.595623254776001 1.9714524745941162 -0.012935157865285873 +3832 -5.4502549171447754 -2.4537010192871094 0.2639794647693634 +3833 -1.6655867099761963 1.8937580585479736 -0.085579730570316315 +3834 -5.7847671508789063 -2.6364579200744629 0.22658067941665649 +3835 -5.4367561340332031 -2.3508224487304688 -0.35287535190582275 +3836 -5.4502549171447754 -2.4537010192871094 -0.37020096182823181 +3837 -5.5033001899719238 -2.2697303295135498 -0.17954304814338684 +3838 -5.419060230255127 -2.2534329891204834 -0.28799447417259216 +3839 -5.3379178047180176 -2.4722676277160645 -0.34900495409965515 +3840 -5.6951231956481934 -2.7475955486297607 -0.32024654746055603 +3841 -5.7635235786437988 -2.5442512035369873 -0.26406165957450867 +3842 -4.8847227096557617 -1.7057615518569946 -0.071869604289531708 +3843 -5.1598081588745117 -1.9720519781112671 0.065180122852325439 +3844 -5.2268590927124023 -2.0101783275604248 -0.064193174242973328 +3845 -6.7509622573852539 -3.5371444225311279 0.17045708000659943 +3846 -6.3872761726379395 -3.2797911167144775 0.19184517860412598 +3847 -6.1491174697875977 -3.0084750652313232 -0.30600836873054504 +3848 -6.4955224990844727 -3.2778494358062744 -0.27112734317779541 +3849 -6.1302008628845215 -2.9171323776245117 -0.29284083843231201 +3850 -6.4726371765136719 -3.1927957534790039 -0.26023346185684204 +3851 -6.7509622573852539 -3.5371444225311279 -0.22385199368000031 +3852 -1.2576347589492798 1.5455329418182373 1.0472238063812256 +3853 -4.7393636703491211 -1.8794815540313721 0.29160439968109131 +3854 -6.4446754455566406 -3.1112608909606934 0.13836222887039185 +3855 -6.7805013656616211 -3.3873453140258789 0.12398260086774826 +3856 -1.575343132019043 2.002718448638916 -0.27155193686485291 +3857 -5.0725984573364258 -1.9578181505203247 0.17955736815929413 +3858 -6.4726371765136719 -3.1927957534790039 0.19028472900390625 +3859 -6.0469813346862793 -3.0152208805084229 0.21122021973133087 +3860 -1.317540168762207 1.3596993684768677 1.1501666307449341 +3861 -1.6518881320953369 1.8970990180969238 -0.20167066156864166 +3862 -6.4955224990844727 -3.2778494358062744 0.20529033243656158 +3863 -1.6170313358306885 1.8443034887313843 -0.38494104146957397 +3864 -5.8024487495422363 -2.73329758644104 -0.33907759189605713 +3865 -6.3872761726379395 -3.2797911167144775 -0.25758868455886841 +3866 -5.7847671508789063 -2.6364579200744629 -0.32374861836433411 +3867 -6.0469813346862793 -3.0152208805084229 -0.28979706764221191 +3868 -1.2752808332443237 1.3994172811508179 1.1830458641052246 +3869 -1.251816987991333 1.3599348068237305 1.2708338499069214 +3870 -1.5731301307678223 1.8429757356643677 0.2032267302274704 +3871 -6.1055383682250977 -2.8302152156829834 -0.23879894614219666 +3872 -6.4446754455566406 -3.1112608909606934 -0.21226486563682556 +3873 -6.1302008628845215 -2.9171323776245117 0.20947349071502686 +3874 -4.9783782958984375 -2.1918003559112549 -0.37596732378005981 +3875 -5.0865964889526367 -2.0602824687957764 -0.38019177317619324 +3876 -5.0725984573364258 -1.9578181505203247 -0.31057676672935486 +3877 -5.0957479476928711 -2.1690788269042969 -0.39935481548309326 +3878 -1.1713529825210571 1.8105677366256714 -1.3105370998382568 +3879 -3.3037333488464355 -0.67158925533294678 0.32410645484924316 +3880 -1.1451693773269653 1.7588642835617065 -1.3590495586395264 +3881 -0.70824193954467773 1.1187355518341064 1.77153480052948 +3882 -1.1768139600753784 0.93863403797149658 1.6598831415176392 +3883 -0.63396912813186646 0.81479769945144653 -1.4549254179000854 +3884 -1.0146510601043701 1.1077710390090942 1.6187304258346558 +3885 -1.0373226404190063 1.0097037553787231 1.678014874458313 +3886 -0.27470102906227112 1.4837077856063843 -1.8930511474609375 +3887 -0.88359725475311279 1.172702431678772 -1.646755576133728 +3888 -1.1237469911575317 0.93270248174667358 -1.7630684375762939 +3889 -1.156740665435791 0.23105150461196899 -1.8904174566268921 +3890 -1.0299129486083984 0.76755577325820923 -1.805027961730957 +3891 -1.2465915679931641 0.28246220946311951 -2.0283455848693848 +3892 -0.98038673400878906 0.60902070999145508 -1.7987960577011108 +3893 -1.1892489194869995 1.1414862871170044 -1.7103941440582275 +3894 -0.29557722806930542 1.4043645858764648 -1.9298210144042969 +3895 -1.629794716835022 1.0960502624511719 -1.2090864181518555 +3896 -1.6083439588546753 1.1278232336044312 -1.4370294809341431 +3897 -0.88755905628204346 0.55451339483261108 -1.6572846174240112 +3898 -0.71712994575500488 0.91322898864746094 -1.6041250228881836 +3899 -5.9297690391540527 -4.7310037612915039 -5.9288845062255859 +3900 -1.077475905418396 1.749769926071167 -1.3947486877441406 +3901 -1.0448107719421387 1.6799923181533813 -1.4412908554077148 +3902 -3.8899023532867432 -1.3200812339782715 -0.44694632291793823 +3903 -5.9566025733947754 -4.7099738121032715 -5.8965187072753906 +3904 -5.9490928649902344 -4.6827201843261719 -5.9014019966125488 +3905 -5.8969326019287109 -4.7099738121032715 -5.9561820030212402 +3906 -5.9337882995605469 -4.7075042724609375 -5.8877887725830078 +3907 -5.9485650062561035 -4.6919212341308594 -5.9256792068481445 +3908 -0.9401547908782959 1.5420213937759399 -1.5584890842437744 +3909 -3.6809544563293457 -0.72296911478042603 0.19559253752231598 +3910 -3.4613919258117676 -0.37722757458686829 0.057954113930463791 +3911 -3.6733453273773193 -0.8501896858215332 0.28620654344558716 +3912 -3.6632513999938965 -0.98136472702026367 0.31771108508110046 +3913 -3.3325796127319336 -0.39990130066871643 0.19880199432373047 +3914 -3.8844842910766602 -0.73259174823760986 -0.096644662320613861 +3915 -3.319216251373291 -0.53419429063796997 -0.49349069595336914 +3916 -5.9305686950683594 -4.7203531265258789 -5.9104099273681641 +3917 -5.953953742980957 -4.7151350975036621 -5.9534192085266113 +3918 -3.4613845348358154 -0.37725251913070679 -0.2664419412612915 +3919 -3.3325724601745605 -0.39996972680091858 -0.40562346577644348 +3920 -3.6809544563293457 -0.72297626733779907 -0.38931304216384888 +3921 -0.9539104700088501 3.0871403217315674 -0.75980275869369507 +3922 -0.97484731674194336 3.0313537120819092 -0.78697872161865234 +3923 -0.74002999067306519 3.1033306121826172 -1.0750055313110352 +3924 -1.801073431968689 0.62679463624954224 -1.9259095191955566 +3925 -1.4584661722183228 1.0212399959564209 -1.7146415710449219 +3926 -1.5974364280700684 1.0571818351745605 -1.5645925998687744 +3927 -1.8540830612182617 0.65573787689208984 -1.7183963060379028 +3928 -1.3837058544158936 0.58838659524917603 -1.9829950332641602 +3929 -1.3379987478256226 1.0469526052474976 -1.7319378852844238 +3930 -0.99372124671936035 2.9624898433685303 -0.75625503063201904 +3931 -0.15323841571807861 1.4898681640625 -1.9475172758102417 +3932 -0.046488847583532333 1.6815698146820068 -1.8756122589111328 +3933 -0.25866317749023438 1.6259403228759766 -1.8138080835342407 +3934 -1.9060789346694946 0.32485345005989075 -1.3515473604202271 +3935 -2.7959225177764893 -0.39472728967666626 -0.50483494997024536 +3936 0 1.3017361164093018 -2.139411449432373 +3937 -0.76682031154632568 3.0156638622283936 -1.1120753288269043 +3938 -1.0035008192062378 2.9644460678100586 -0.84863293170928955 +3939 -0.59365934133529663 1.5179650783538818 -1.7218084335327148 +3940 -0.10780756175518036 1.5425924062728882 -1.9396649599075317 +3941 -1.5463755130767822 1.0345745086669922 -1.6587764024734497 +3942 -0.9734196662902832 3.0447525978088379 -0.70295697450637817 +3943 -2.9852397441864014 -0.062951549887657166 -0.41731894016265869 +3944 -1.664408802986145 0.76465046405792236 -1.0667111873626709 +3945 -3.3037333488464355 -0.67160981893539429 -0.51976925134658813 +3946 -2.9447579383850098 -0.35055205225944519 -0.53789502382278442 +3947 -3.1223611831665039 -0.034215193241834641 -0.27398359775543213 +3948 -2.9447650909423828 -0.3503594696521759 0.33351057767868042 +3949 -1.8553893566131592 0.64496397972106934 -1.4893112182617188 +3950 -1.9059504270553589 0.49240937829017639 -1.396105170249939 +3951 -1.6667002439498901 0.94685077667236328 -1.1138460636138916 +3952 -3.3192238807678223 -0.53414660692214966 0.29182448983192444 +3953 -3.2154459953308105 -0.054700121283531189 -0.1072729080915451 +3954 -3.5493438243865967 -0.40038236975669861 -0.10422486811876297 +3955 -1.0024657249450684 1.1954734325408936 1.5706819295883179 +3956 -0.84841924905776978 1.2396243810653687 1.6286742687225342 +3957 -8.3593425750732422 -4.7075052261352539 -0.031909875571727753 +3958 -1.3060330152511597 1.1118613481521606 1.4421265125274658 +3959 -3.8899023532867432 -1.320081353187561 0.2836230993270874 +3960 -3.5252091884613037 -1.0195765495300293 0.2894308865070343 +3961 -4.0297932624816895 -1.0383148193359375 0.1933588832616806 +3962 -4.0276446342468262 -1.1598029136657715 -0.4521166980266571 +3963 -3.6632513999938965 -0.98136460781097412 -0.49832844734191895 +3964 -3.6733453273773193 -0.85018956661224365 -0.47352322936058044 +3965 -0.74711835384368896 1.6460777521133423 -1.5875780582427979 +3966 -0.9238506555557251 1.6220356225967407 -1.5230541229248047 +3967 -0.88144856691360474 1.7580362558364868 -1.4723430871963501 +3968 -3.801607608795166 -0.70638734102249146 -0.25412759184837341 +3969 -8.0830860137939453 -4.4409008026123047 -0.063859641551971436 +3970 -7.7642855644226074 -4.1852321624755859 -0.095315337181091309 +3971 -7.826390266418457 -4.2174615859985352 -0.051856104284524918 +3972 -3.801607608795166 -0.70638597011566162 0.059575546532869339 +3973 -8.1417646408081055 -4.4756007194519043 0.022216442972421646 +3974 -7.826390266418457 -4.2174615859985352 0.030368741601705551 +3975 -8.3858966827392578 -4.7047715187072754 -0.045112758874893188 +3976 -7.8435940742492676 -4.3054990768432617 -0.1158403754234314 +3977 -7.7657852172851563 -4.2939000129699707 0.095947861671447754 +3978 -8.3816852569580078 -4.7099747657775879 -0.041773136705160141 +3979 -8.0992908477783203 -4.5288028717041016 0.067794635891914368 +3980 -7.8049030303955078 -4.2442235946655273 0.094983458518981934 +3981 -8.19537353515625 -4.5162386894226074 -0.0039299065247178078 +3982 -8.1417646408081055 -4.4756007194519043 -0.031914766877889633 +3983 -7.4407029151916504 -3.9243881702423096 -0.12597325444221497 +3984 -7.4781794548034668 -3.9891476631164551 -0.15341754257678986 +3985 -1.2662007808685303 0.83445632457733154 1.6990087032318115 +3986 -8.1706037521362305 -4.546912670135498 0.067311942577362061 +3987 -7.5631976127624512 -3.9942014217376709 -0.015915766358375549 +3988 -8.3963899612426758 -4.6919221878051758 0.016187610104680061 +3989 -7.694115161895752 -4.2737765312194824 -0.093029648065567017 +3990 -8.0311183929443359 -4.5062026977539063 -0.062245938926935196 +3991 -8.3794012069702148 -4.6827211380004883 -0.034043539315462112 +3992 -8.1268453598022461 -4.4940176010131836 -0.075961068272590637 +3993 -8.1268453598022461 -4.4940176010131836 0.06709665060043335 +3994 -8.0830860137939453 -4.4409008026123047 0.052587665617465973 +3995 -1.3507479429244995 0.83112978935241699 1.6621532440185547 +3996 -7.8817844390869141 -4.257786750793457 -0.0098167667165398598 +3997 -1.0827584266662598 0.8163604736328125 1.8066200017929077 +3998 -8.0992908477783203 -4.5288028717041016 -0.07508733868598938 +3999 -8.1706037521362305 -4.546912670135498 -0.073787443339824677 +4000 -7.8049030303955078 -4.2442164421081543 -0.11524074524641037 +4001 -1.0555425672743937 3.1857178911568602 -1.3600133013885816 +4002 -1.6180436206411069 1.5833924559842116 0.38805658186568537 +4003 -1.3463749869425663 2.2936074237226731 -0.12663301968931651 +4004 1.3205687409955313 2.3237693974744675 -0.31739199120126943 +4005 -0.30747620761394501 4.318077564239502 -0.53423623368144035 +4006 -0.31347146030012341 3.8101082160532509 0.65893413991977068 +4007 8.307767391204834 -4.615687370300293 -0.0016985506808850914 +4008 -0.72711443029231937 3.5383498205881465 0.45581257982046569 +4009 -1.1043609647741313 1.0351902065213792 1.6191650420742938 +4010 5.942576837017036 -4.6982361642910044 -5.9103243420872689 +4011 -0.089309483766555786 0.78150972723960876 -2.4847064018249512 +4012 0.037050787343905232 1.6031016670999318 -1.9239650059198836 +4013 0.58127753684052164 4.0103417497990232 0.46508732885360848 +4014 0.96732506768560289 3.0493965693649447 -0.61025794957190016 +4015 8.3873869066771611 -4.7187829021781065 0.010254158836554632 +4016 -1.358621508237555 1.2087922513819689 1.2474035519888926 +4017 0.66997376084327698 4.0367248058319092 -0.79779526591300964 +4018 -0.022682618540064557 -4.6982218273625369 -8.3812660330173667 +4019 -0.80876019149417222 3.1730377987034162 -0.083221730373474773 +4020 1.7002457248492069 1.6881299823448999 0.11851894066945817 +4021 1.4814058565710293 1.7352137030921477 0.45245746495312877 +4022 -0.185758076608181 1.7949381470680237 -1.7516754269599915 +4023 1.0327932504809274 2.3023324275788033 -0.96211943551330903 +4024 0.185758076608181 1.7949381470680237 -1.7516754269599915 +4025 -0.9220910370349884 3.0179481506347656 -1.2590975165367126 +4026 0.5018667874419791 1.6327205950110599 -1.6990690215993463 +4027 1.1848999414622605 2.306063793997974 0.31697630747485561 +4028 -1.3127540180343926 1.2566165235127853 1.2716010062700853 +4029 0.59232875189049239 4.0271823481262121 -0.087614982921436685 +4030 -0.5486018960330481 4.0876854826729874 -0.18011411949090983 +4031 -1.0487270859436448 3.7984688347152797 -1.5190608458778971 +4032 0.96572364711955927 2.5723130899117161 0.38840994100263121 +4033 -0.47508854109539778 3.9429383177439488 0.60309181293503067 +4034 1.5960087180137634 0.51843613386154175 1.8065556883811951 +4035 0.064824006152444796 1.7835305156302039 -1.8053182441038682 +4036 -0.96732506768560289 3.0493965693649447 -0.61025794957190016 +4037 -0.38097567231126395 3.9331578765861588 0.61147149086172847 +4038 -1.8612577164803277 1.298833856361405 -0.44758793204980463 +4039 -5.9375179976737922 -4.7188024920001412 -5.9231909364441027 +4040 0.92304402589797974 3.0333206653594971 -1.2808375358581543 +4041 -0.26843363941250648 2.1799319240596184 -2.7273314051951862 +4042 -0.7651775071922855 3.1971238490027272 0.17511768890933077 +4043 0.7651775071922855 3.1971238490027272 0.17511768890933077 +4044 -0.35640913697610244 4.3146577472798224 -0.53516756187158465 +4045 -1.7567954002593478 1.2161788870727253 -0.53586488919884778 +4046 0.62980375467533889 3.9098196907745471 0.50935712661625099 +4047 0.25679757446050644 4.311870813369751 -0.54135603457689285 +4048 0.93840851119273716 3.268632572543249 -0.67967241040019832 +4049 0.85108340958655992 3.7986483203760919 -1.0018559224189976 +4050 0.30749632893522028 4.3181670217975805 -0.53464683572513882 +4051 -0.75936182205238112 3.8044483653484367 0.19565193749110446 +4052 1.3052368466457043 2.0894596337743012 0.35490572552708832 +4053 2.008984020681829 1.3651642951603284 -0.21158022099392435 +4054 -5.923871283065103 -4.7187945794599431 -5.9369389992341439 +4055 0.77754266882189338 1.5307316394521864 -1.6351357068639936 +4056 0.65886198629379533 1.7141956174031343 1.392630120908422 +4057 -0.023396476037409717 1.6185033885000935 -1.9187545225529241 +4058 1.1043609647741313 1.0351902065213792 1.6191650420742936 +4059 0.10371828725423776 1.7850232206076697 -1.7983691191536761 +4060 1.2461477491846766 2.317306138716587 -0.54910272307703289 +4061 -0.25172862895479259 2.185801721320185 -3.412814542207121 +4062 -0.64312687866544038 3.9992267082163369 -0.048882626829644948 +4063 1.3227697563965215 1.6173896842682769 0.84782460211964628 +4064 0.074413608567372738 1.5882187024707994 -1.9283653960347831 +4065 5.9101357785138111 -4.6983829775897714 5.9426023159946766 +4066 1.1002052872508048 3.2442591073135767 -1.4988283902523374 +4067 -0.62980375467533889 3.9098196907745471 0.50935712661625099 +4068 -0.29540722403739089 0.35732461241255142 1.4013693710698716 +4069 -1.1603420376777649 1.1098161935806274 1.5304284691810608 +4070 -5.9241275500474142 -4.7187656439143328 5.9383079014180398 +4071 -0.66997376084327698 4.0367248058319092 -0.79779526591300964 +4072 -1.2085597010930602 2.0366029845973443 0.60896108280463546 +4073 -1.7002457248492069 1.6881299823448999 0.11851894066945817 +4074 -0.73427651412763761 3.4141123851082162 0.32687172028931394 +4075 -0.23371299132597464 3.9542851102806345 0.532433474463746 +4076 8.3811959450021742 -4.6983131633082982 0.022933177463977734 +4077 -8.387489324250156 -4.7187715060181246 -0.0089962225241026934 +4078 0.63714920762643512 3.9951946929018436 -0.084238090333470239 +4079 -0.23400867768317526 0.33240961826903037 1.3935609881766924 +4080 1.0322064880965811 2.8485221360131705 -0.33817446462284245 +4081 -0.48330760109330861 4.2212807727045343 -0.54140151610605669 +4082 0.21845403480548323 1.3775863548984328 -1.9845081895040266 +4083 1.232700214830545 1.3615163730344109 -1.5474383481404108 +4084 1.6180436206411069 1.5833924559842116 0.38805658186568537 +4085 0.4557109922170639 4.1824159622192383 0.019695140421390533 +4086 -1.0327932504809274 2.3023324275788033 -0.96211943551330903 +4087 -0.083161912858486176 -0.4812893271446228 2.3847866058349609 +4088 1.3147238370059651 1.2990419620753204 1.2216226937776697 +4089 1.0784630648769893 1.5525909091498962 -1.4886633581469819 +4090 -1.2388666133952086 2.4363794494529634 -0.4244862034545594 +4091 0.90990071477841616 2.9963233590440348 -1.2549018802771703 +4092 -0.89087731912714996 2.9567033302840309 -1.2480003286757348 +4093 1.6681936923945881 1.5234398998845817 0.36943174645717958 +4094 -5.8744680881500244 -4.6156864166259766 -5.8761639595031738 +4095 0.84265503287315369 3.1156724691390991 -0.91726848483085632 +4096 0.31347146030012341 3.8101082160532509 0.65893413991977057 +4097 -1.6681936923945881 1.5234398998845817 0.36943174645717958 +4098 0.70169068289560366 3.9672612880374603 -0.80837979623871892 +4099 1.2085597010930602 2.0366029845973443 0.60896108280463546 +4100 0.74896808742989163 1.2778295183629367 1.6446578145390127 +4101 -1.0653399094317986 2.3845238923579726 -0.84919836433600171 +4102 0.055814993353606374 0.6799683844071136 2.3057464621843771 +4103 -0.66380810344785068 3.9743347322054325 -0.044682367661416894 +4104 5.923871283065103 -4.7187945794599431 -5.9369389992341439 +4105 -1.3205687409955313 2.3237693974744675 -0.31739199120126943 +4106 -1.6925102489597148 1.142080840245024 -0.55583303797619166 +4107 -1.2461477491846766 2.317306138716587 -0.54910272307703289 +4108 1.0653399094317986 2.3845238923579726 -0.84919836433600171 +4109 5.9522095734105234 -4.6940310910352734 -5.8993753148592827 +4110 1.1603420376777649 1.1098161935806274 1.5304284691810608 +4111 -1.6934816301971329 1.7296757805110548 -0.36295397888659708 +4112 -0.95489476970589493 3.1345309318753638 -0.62883749292156954 +4113 1.2431097203958041 1.9845811364592418 -1.0434097220874616 +4114 0.1572264392693416 4.3361857484289121 -0.52025626818227644 +4115 0.57145554897346118 3.3509311743411696 0.68472541702939771 +4116 0.99986657064378359 2.8874105393508245 -1.1122121393688718 +4117 -5.942576837017036 -4.6982361642910044 -5.9103243420872689 +4118 -0.65603902547599091 3.8912804740679552 0.48523832910207076 +4119 -0.77754266882189338 1.5307316394521864 -1.6351357068639936 +4120 8.387489324250156 -4.7187715060181246 -0.0089962225241026934 +4121 0.35638611622223931 4.31475021994454 -0.5355773428029712 +4122 0.59629903501145287 3.922473016436141 0.52515959194581829 +4123 -0.85798123951924243 1.7889559584579013 1.2240700220330596 +4124 1.1040656938596221 3.2015535266338526 -1.485789992858815 +4125 -1.1301716566085815 2.4497925043106079 -0.6838039755821228 +4126 5.9375179976737922 -4.7188024920001412 -5.9231909364441027 +4127 -0.59069747638580961 4.0366447490685102 -0.052832797729125802 +4128 -1.1277126982690733 2.4206167196799182 -0.70594277095591673 +4129 0.4745242666162417 1.472562142192658 -1.8049452379961404 +4130 0.41395537845173486 4.100227244306585 0.48102881682081555 +4131 0.29607519000033261 4.2673896804086784 -0.30158346359959753 +4132 0.29540722403739089 0.35732461241255142 1.4013693710698716 +4133 1.2813641815956449 1.643193369712511 0.89604365866134073 +4134 0.056628759900207874 0.099048210588456712 1.6541341538316503 +4135 0.26843363941250653 2.1799319240596184 -2.7273314051951862 +4136 -0.16070638617212479 4.1731176326813193 -0.0097036519680335637 +4137 -1.0552298073315221 3.1024846664786669 -1.4083036485715825 +4138 0.076532615061327441 0.97024439154440201 2.0668591197363226 +4139 -1.0784630648769893 1.5525909091498962 -1.4886633581469819 +4140 -1.0757118624599693 2.4009728580181324 -0.82728770010591357 +4141 -0.071864336729049683 -1.6301974654197693 -4.0063903331756592 +4142 0.74443460380957682 3.3213637016546258 0.23932882396454547 +4143 5.937729584379686 -4.7187575177133594 5.9248085683840195 +4144 0.58159756344226576 2.5764133096271249 -1.2250771283265769 +4145 0.88539227045946434 2.9900048239094512 -0.98009622587562506 +4146 1.0555425672743937 3.1857178911568602 -1.3600133013885816 +4147 0.80876019149417222 3.1730377987034162 -0.083221730373474828 +4148 -0.4557109922170639 4.1824159622192383 0.019695140421390533 +4149 -0.52719238030338356 4.1106416329802187 -0.18088804592502539 +4150 -0.91988857225246923 3.0713164301434164 -1.2819590955946418 +4151 -1.0322064880965811 2.8485221360131705 -0.3381744646228424 +4152 0.85994331055909379 3.0489420611783586 -0.938170251481099 +4153 -0.056628759900207881 0.099048210588456698 1.6541341538316503 +4154 0.79348376604838755 3.8159078149774737 -0.80561795990400131 +4155 0.92281525993296276 3.2919146995162287 -0.66501759681804318 +4156 -0.93840851119273716 3.268632572543249 -0.67967241040019832 +4157 1.1411522579102111 3.8191932931201071 -1.7058919881255175 +4158 -0.9160108636073464 3.0911463028332933 -1.2660503866601116 +4159 1.0552298073315221 3.1024846664786669 -1.4083036485715825 +4160 0.91378957881959022 2.11372052583422 0.91256062108771108 +4161 1.3740343587469228 1.2372011567599102 1.1903333506750999 +4162 -2.008984020681829 1.3651642951603284 -0.21158022099392435 +4163 0.9795582576776295 2.9968198131628463 -0.64895190198075337 +4164 0.85798123951924243 1.7889559584579013 1.2240700220330596 +4165 -1.6937819063718407 1.4953331415816673 0.35544029672015048 +4166 0.94841927901454326 2.9492959777336289 -0.30858142163757141 +4167 0.089309483766555786 0.78150972723960876 -2.4847064018249512 +4168 1.4254558617601605 1.2653655650125064 1.0743349056217071 +4169 0.5325943001172988 1.1149444432256357 1.8261460678891197 +4170 8.3803510507707006 -4.6940548170019092 0.03738324101541917 +4171 0.76215733845070754 3.0566554226857492 -1.1229642435382012 +4172 0.62385679242288705 3.6360186691425649 0.63114094770167695 +4173 0.88288255235002122 2.936811552202117 -1.2185765014382626 +4174 -1.025961697101593 3.3993630409240723 -1.2256863713264465 +4175 -0.90545791607739901 3.1461362018958847 -1.2689841453803914 +4176 -1.5312734777345893 1.9415332198056885 0.14030395515410576 +4177 0.84457318009381388 3.0954169184848217 -0.92093675763886751 +4178 -0.70169068289560366 3.9672612880374603 -0.80837979623871892 +4179 0.23371299132597464 3.9542851102806345 0.532433474463746 +4180 -1.0781990224218239 2.7492201583303206 -0.65714578918891009 +4181 -0.74896808742989163 1.2778295183629367 1.6446578145390127 +4182 1.7242562754604462 1.6377652378906131 0.12662247756348516 +4183 -1.8362956629332101 1.2640125758425687 -0.46794860354380219 +4184 0.066847823560237885 0.90333098173141479 2.1225296258926392 +4185 0.21998231991658257 4.2579398117702292 -0.31165318231127104 +4186 -0.47024749396135446 4.0586284914661217 0.52149525752016979 +4187 -1.5041375756263733 2.09212327003479 -0.27675510942935944 +4188 1.4013747771249809 1.2875949082741738 1.0890981391942167 +4189 0.91376538854510103 3.0183280484646384 -1.2597778945351896 +4190 1.0709377302331851 3.7400363028221495 -1.5326681238575808 +4191 0.87767089020988664 3.0147086315884239 -0.9649341813357688 +4192 5.8744680881500244 -4.6156864166259766 -5.8761639595031738 +4193 1.5545417666435242 3.5395077466964722 -2.5168464183807373 +4194 -0.94210284837561709 3.2166363472599966 -0.65205582148696439 +4195 -0.90252683569276448 2.9765419058112412 -1.2669871461934143 +4196 0.75936182205238112 3.8044483653484367 0.19565193749110446 +4197 0.92352572032626912 3.0718895569722706 -1.2809715827812169 +4198 -1.0628919350622519 3.2147976128260858 -1.3652840340428078 +4199 1.4850058546570584 1.8561624358834989 0.31862285073415814 +4200 -1.030615716065969 3.4161029845795388 -1.2226723290019437 +4201 -5.9399691348820705 -4.7055016222114103 -5.9183858920674215 +4202 -0.9963668315422376 2.6560217420140759 0.24036025554444518 +4203 0.7163724699119709 2.2886889483193511 -2.8497471008813129 +4204 1.0911190457581266 2.6512977089672232 -0.065970854388777006 +4205 -0.46353432705216296 4.1349470858100448 0.41308522399761588 +4206 0.73427651412763761 3.4141123851082162 0.32687172028931394 +4207 0.68656292085713544 3.9268277736258939 0.25625176529043592 +4208 1.3003728352096096 1.3513216039964373 -1.5350625564473603 +4209 1.5312734777345893 1.9415332198056885 0.14030395515410576 +4210 -1.648876524016792 1.0894239037916684 -0.57375793307446354 +4211 -1.0248152640066448 3.3831023367530353 -1.2273597067329236 +4212 0.071864336729049683 -1.6301974654197693 -4.0063903331756592 +4213 0.080437533557415009 -0.75360259413719177 2.7359704971313477 +4214 0.72711443029231937 3.5383498205881465 0.45581257982046569 +4215 0.99505846560177003 2.8882758214683264 -1.1036195782841451 +4216 -0.080437533557415009 -0.75360259413719177 2.7359704971313477 +4217 -1.0562649797483432 3.2497247725826872 -1.3651375820964236 +4218 -1.3052368466457043 2.0894596337743012 0.35490572552708832 +4219 -8.3873869066771611 -4.7187829021781065 0.010254158836554632 +4220 1.3187176710633595 1.2090835418466179 -1.628133945731191 +4221 -1.4452802606845121 1.7976246576126684 -0.86717988573570093 +4222 1.1545032974837814 2.5595702125600059 -0.053015123561579114 +4223 0.89866852109281214 2.9534481788896367 -1.2440505696463167 +4224 0.53852116592821586 0.42308466702392095 1.6090399324996434 +4225 0.022682618540064557 -4.6982218273625369 -8.3812660330173667 +4226 0.94174660323248016 3.1761372544567288 -0.59613664747387418 +4227 -0.74443460380957682 3.3213637016546258 0.23932882396454547 +4228 -0.078305920713743177 1.6036043839108178 -1.9173471610361525 +4229 0.76074385833288172 3.0724232270549461 -1.1238740145869159 +4230 -0.3507601452682354 4.1169705490562993 0.42466157825019446 +4231 1.3187985931483597 1.4457916578464292 1.0477752537861977 +4232 1.0628919350622519 3.2147976128260858 -1.3652840340428078 +4233 0.42414330089535934 1.0580421662724757 1.8984413882178157 +4234 -1.5288150906562805 3.3729941844940186 -2.4612170457839966 +4235 -0.27528710068275219 4.2007999913854839 0.0096307003883887021 +4236 -1.0534785766743064 3.2923944585874274 -1.367342835802555 +4237 -0.24444427951882186 4.1102317414020098 0.32328333926728126 +4238 0.068910931014779575 0.35918632156363595 2.618466295434744 +4239 1.2645543578459435 1.4882362452593023 1.1004840468775228 +4240 0.43749202787876129 1.1591424942016602 1.8331320285797119 +4241 -0.97243466889765495 1.5845768651770933 -1.5223407008216638 +4242 5.8994431823219644 -4.6940001881252078 5.9522613704907039 +4243 -0.65886198629379533 1.7141956174031343 1.392630120908422 +4244 -0.77320412232883984 3.6995446405715957 -0.60736546575111416 +4245 0.020874670897224199 1.6116162120400652 -1.9234658785084529 +4246 -1.0819388786530806 2.7077334762242158 -0.6829803971977938 +4247 -0.041526779632923991 1.6089602274298143 -1.9193139480202115 +4248 -1.5960087180137634 0.51843613386154175 1.8065556883811951 +4249 -0.66829160326795134 3.8963534480903155 -0.2645081447462706 +4250 -0.58159756344226576 2.5764133096271249 -1.2250771283265769 +4251 0.52641552838053141 4.1138341614528038 -0.15941507685772088 +4252 0.16070638617212479 4.1731176326813193 -0.0097036519680334943 +4253 1.8612577164803277 1.298833856361405 -0.44758793204980468 +4254 0.18036488307197926 4.1797587598063686 -0.0039711209770682127 +4255 0.31756752874406619 1.8181762098300152 -1.6784779980540603 +4256 -5.9522095734105234 -4.6940310910352734 -5.8993753148592827 +4257 -0.79364226836035079 3.2192374033438882 -0.060266313366376653 +4258 1.2388666133952086 2.4363794494529634 -0.4244862034545594 +4259 0.87308797936116544 2.9341763016811959 -1.199840925957177 +4260 -0.84265503287315369 3.1156724691390991 -0.91726848483085632 +4261 0.55100847491968441 4.0874646076348133 -0.15852607349773043 +4262 -0.88539227045946434 2.9900048239094512 -0.98009622587562506 +4263 0.32467847710300013 1.4470550522920005 -1.8902624676875526 +4264 -0.83302829544736123 3.1478433550690896 -0.90846431708135267 +4265 0.17195762287003888 4.1096765699367008 0.22195597309764747 +4266 -0.10427898450085446 1.5962684315327396 -1.9093632773848939 +4267 -0.92733414050913909 3.032542540431669 -1.2796627333007007 +4268 0.85653853169650818 3.067618070188745 -0.93209621565409306 +4269 0.78229977241338267 2.801079692656713 -1.2645974474859638 +4270 0.77320412232883984 3.6995446405715957 -0.60736546575111416 +4271 0.58188171780877751 1.6512134972919312 -1.6568628498247631 +4272 -0.3098873532827866 4.1425340531591122 0.34656607183196164 +4273 -1.4814058565710293 1.7352137030921477 0.45245746495312877 +4274 -1.5504264850952294 3.5408168245602569 -2.5049126525106837 +4275 0.35825371687120744 4.2109215637577382 0.0093286725413095906 +4276 -0.79987719629131837 3.2970182771529308 -0.020392701853590656 +4277 1.0757118624599693 2.4009728580181324 -0.82728770010591357 +4278 -0.11208092613845277 4.3305354508045921 -0.54035260588012279 +4279 -0.9390697181224823 2.9098371267318726 -1.1542346477508545 +4280 5.9362818904446106 -4.6930249138844573 -5.9362432366949038 +4281 -0.11415650432652323 0.31064548259914382 1.3987183875815887 +4282 -0.098881163665797356 4.2470832065882096 -0.28864576697390348 +4283 -0.19936221891959646 4.1918169046346927 -0.0027463060927338384 +4284 0.77892196945331726 3.715711802440318 -0.6054659257461793 +4285 1.5041375756263733 2.09212327003479 -0.27675510942935944 +4286 -1.5398889018990192 3.7153472923544903 -2.5622172694254171 +4287 1.2782352013353073 3.3734419104087623 -1.8186837672573617 +4288 1.5442606659712341 3.709790180188405 -2.5727562531613795 +4289 0.79364226836035079 3.2192374033438886 -0.060266313366376695 +4290 0.74892086971910732 0.39237257072266574 1.1979476454806415 +4291 -0.95680739683901128 2.6746435787787521 0.27439951395144174 +4292 0.030729949168974521 1.7812748011088888 -1.8160975699197217 +4293 -0.91164249597428504 2.9480258067669598 -1.0185905493778822 +4294 -0.21845403480548317 1.3775863548984328 -1.9845081895040269 +4295 0 1.7814792194145679 -1.8204275680585018 +4296 8.3851639596041476 -4.7057231197193339 0.01534112211923906 +4297 0.63225245144455533 4.0954033674307713 -0.80011381511911406 +4298 1.0483689923970847 3.0257959244715549 -1.5156045602170174 +4299 8.3951275202018607 -4.6929856310371019 3.5810441765898654e-05 +4300 -0.25673271476755188 4.3117757998149671 -0.54095135296723673 +4301 1.5288150906562805 3.3729941844940186 -2.4612170457839966 +4302 0.97243466889765495 1.5845768651770933 -1.5223407008216638 +4303 -1.5480173230171204 0.9899059534072876 -0.91720467805862427 +4304 -1.0483689923970847 3.0257959244715549 -1.5156045602170174 +4305 -1.1848999414622605 2.306063793997974 0.31697630747485561 +4306 -0.18036488307197926 4.1797587598063686 -0.003971120977068282 +4307 -0.971424449433682 2.9230508714352768 -1.2071668659283854 +4308 -1.232700214830545 1.3615163730344109 -1.5474383481404108 +4309 -0.56218264391833461 4.0864390459888424 -0.058288979414520686 +4310 -1.0911190457581266 2.6512977089672232 -0.065970854388777006 +4311 0.77517024740541185 3.0172025337646122 -1.1441163146030826 +4312 -0.77892196945331726 3.715711802440318 -0.6054659257461793 +4313 0.96734050031159557 2.9081856284137935 -1.0400586415768918 +4314 0.89896331018992137 1.5642256757281046 -1.5646962953233625 +4315 0 1.6845889169779411 -1.8804831120435745 +4316 -5.937729584379686 -4.7187575177133594 5.9248085683840195 +4317 -0.5325943001172988 1.1149444432256357 1.8261460678891197 +4318 -0.17195762287003888 4.1096765699367008 0.2219559730976475 +4319 1.3618350064393292 2.1530746941105576 0.14149380624410648 +4320 0.068207354575404361 4.3228476500613828 -0.52888974521308962 +4321 -1.3836299332210549 1.7581479587477538 -1.0229148525647662 +4322 -0.1514498402080762 1.5667048197693085 -1.9032547751283215 +4323 1.0781990224218239 2.7492201583303206 -0.65714578918891009 +4324 -0.90864515211104746 2.9217174007766458 -1.1141126360954519 +4325 -0.99628646114528607 2.9653897010614676 -1.2476879018728275 +4326 8.3514474557033473 -4.6543365006686974 -0.00083137011955959639 +4327 0.3507601452682354 4.1169705490562993 0.42466157825019446 +4328 -1.5928105641353574 1.0585235666287451 -0.5615295518390393 +4329 0.47508854109539778 3.9429383177439488 0.60309181293503067 +4330 -0.80422447320030555 3.3261958752155807 -0.0016226476961983399 +4331 -1.2428808153445086 2.4569771733744266 -0.37913425628763331 +4332 0.8686621376081517 3.748549083148065 -0.99719807792322179 +4333 -0.7163724699119709 2.2886889483193511 -2.8497471008813129 +4334 5.8744680881500244 -4.6156885623931885 5.872772216796875 +4335 1.0470928772406964 2.5340101027140856 0.31839508181037235 +4336 0.31950342507203516 4.1666502140888557 0.28607862516958898 +4337 -1.6072481722089607 1.0842539556945205 -1.0305133618640006 +4338 -0.94841927901454326 2.9492959777336289 -0.30858142163757141 +4339 -0.037289522467829524 -4.6939513935957704 -8.3803216750990366 +4340 0.46353432705216296 4.1349470858100448 0.41308522399761594 +4341 0.91287775336274157 0.32907008556773226 1.0967032377699055 +4342 0.6671741193503028 3.4553028513809543 0.61133905641946718 +4343 1.089525199481806 2.6479841526621946 -0.69936928661031761 +4344 -1.6012318663369505 1.1711240451445109 -1.1555570577495706 +4345 0.47024749396135446 4.0586284914661217 0.52149525752016979 +4346 -0.055814993353606367 0.6799683844071136 2.3057464621843771 +4347 1.8970189112209497 1.3212719478652393 -0.43090272280060188 +4348 -0.67025733063830517 3.7508519979701411 0.56353051418860733 +4349 1.2580399652898255 3.2378731602046691 -1.8349922758773081 +4350 -5.9101357785138111 -4.6983829775897714 5.9426023159946766 +4351 1.1301716566085815 2.4497925043106079 -0.6838039755821228 +4352 0 4.3299941263951123 -0.53182527596596985 +4353 5.9399691348820705 -4.7055016222114103 -5.9183858920674215 +4354 -0.5018667874419791 1.6327205950110599 -1.6990690215993463 +4355 1.5480173230171204 0.9899059534072876 -0.91720467805862427 +4356 -0.90420061596490942 2.9640793041218196 -1.0041822289813234 +4357 -0.064824006152444782 1.7835305156302039 -1.8053182441038682 +4358 -1.1504025493092513 2.4840252638169744 -0.64000087232947567 +4359 0.51293435677584609 0.41339072063943727 1.6132644071429243 +4360 -0.31756752874406619 1.8181762098300152 -1.6784779980540603 +4361 1.6934816301971329 1.7296757805110548 -0.36295397888659708 +4362 0.34506585941239504 1.8287470780444497 -1.6602253382855214 +4363 1.7880370707619904 0.91783294757629696 -0.56756859320106867 +4364 0.85809807247221148 3.7168148380722261 -1.0042654884326501 +4365 -0.068910931014779561 0.359186321563636 2.618466295434744 +4366 0.79604522172326742 0.36821433251679081 1.1410330590785847 +4367 -0.6671741193503028 3.4553028513809543 0.61133905641946718 +4368 -0.99949420797379152 1.4943886532024016 -1.5608975022175053 +4369 -0.92281525993296276 3.2919146995162287 -0.66501759681804318 +4370 -0.27118661064958544 4.1332964979643787 0.32885745508575287 +4371 -1.0480579376236334 2.8800832845470685 -1.2101997376374534 +4372 0.11120373341204419 4.3249896160381915 -0.52362538001274594 +4373 -0.76215733845070754 3.0566554226857492 -1.1229642435382012 +4374 -0.74892086971910732 0.39237257072266574 1.1979476454806415 +4375 0.037289522467829524 -4.6939513935957704 -8.3803216750990366 +4376 0 4.2439503926207633 -0.28975934379390766 +4377 -0.63225245144455533 4.0954033674307713 -0.80011381511911406 +4378 -1.3618350064393292 2.1530746941105576 0.14149380624410648 +4379 0.38097567231126395 3.9331578765861583 0.61147149086172858 +4380 -1.2782352013353073 3.3734419104087623 -1.8186837672573617 +4381 -0.27597679230928213 1.8205793399612888 -1.6963341925011819 +4382 -1.1545032974837814 2.5595702125600059 -0.053015123561579114 +4383 -0.85157052543853773 3.212716242912085 -1.2514749401487137 +4384 0.89753939446011022 1.5390627130214769 -1.5785756938136488 +4385 -0.78229977241338267 2.801079692656713 -1.2645974474859638 +4386 -0.96361310890357743 2.914818601471624 -1.1945615817863464 +4387 -0.59629903501145287 3.922473016436141 0.52515959194581829 +4388 -1.5565905254970216 1.8213718928682567 -0.58825516107263798 +4389 0.83302829544736123 3.1478433550690896 -0.90846431708135267 +4390 0.87507760524749756 1.4681104421615601 -1.627056896686554 +4391 -0.62385679242288705 3.6360186691425649 0.63114094770167695 +4392 -1.2580399652898255 3.2378731602046691 -1.8349922758773081 +4393 0.73486289483925804 3.1896127616017727 -1.1350988427194864 +4394 0.97588492967153173 2.8997925203737931 -1.0566326009871578 +4395 0.38536505174800822 4.2147607387262784 0.010866612453114904 +4396 1.1504025493092513 2.4840252638169744 -0.64000087232947567 +4397 0.79987719629131837 3.2970182771529313 -0.020392701853590933 +4398 -1.5856669781636596 1.0393410241628664 -0.9945242903460445 +4399 1.025961697101593 3.3993630409240723 -1.2256863713264465 +4400 1.4806318816344834 0.94624457693954778 -0.87688303893748099 +4401 -1.1002052872508048 3.2442591073135767 -1.4988283902523374 +4402 1.0761981223810171 3.094406539629794 -1.5255500022646091 +4403 0.52324059737920514 4.2189704919227067 -0.7927633664595688 +4404 -0.2685439739486547 1.3423910484898811 -1.9778708270935088 +4405 -0.9095828650394141 2.7419975319646168 0.30330883097861217 +4406 1.030615716065969 3.4161029845795388 -1.2226723290019437 +4407 0.098640301248953149 4.2455604133353253 -0.28405275117678142 +4408 -1.0940229425973131 3.2047839109176812 -1.4563972095579008 +4409 -1.0755798057158616 2.6834675874059011 -0.71426943092329098 +4410 -0.85252637939979548 3.0688478869568852 -0.93701751956879298 +4411 -5.9362818904446106 -4.6930249138844573 -5.9362432366949038 +4412 1.6679755428417813 0.85166192863939139 -0.54138232727035729 +4413 -1.5998991792681365 1.1515945825938063 -1.1360221516803508 +4414 0.23400867768317526 0.33240961826903037 1.3935609881766924 +4415 -0.8351269422407267 3.2396265261039465 -1.2443990442492632 +4416 -0.77517024740541185 3.0172025337646122 -1.1441163146030826 +4417 0.80729274344288471 2.7295751947996423 -1.1368173838059195 +4418 -0.30099980538672538 4.0503330150988788 0.4880357253750725 +4419 1.7251816391944885 1.723611056804657 -0.22901919484138489 +4420 -0.4745242666162417 1.472562142192658 -1.8049452379961404 +4421 0.17162012593355472 4.2633145145729614 -0.27871023195392475 +4422 0.94210284837561709 3.2166363472599966 -0.65205582148696428 +4423 -1.4706101429352829 0.98184559254718273 -0.54111482761063123 +4424 1.8362956629332101 1.2640125758425687 -0.46794860354380219 +4425 -1.0519569752625832 2.9632980223478 -1.2604968027877699 +4426 -1.4850058546570584 1.8561624358834989 0.31862285073415814 +4427 -0.82020316770158219 2.6884258334865732 -1.0075716652225941 +4428 1.4713642819706312 0.79898522777523406 -0.47196217188143863 +4429 0.65603902547599091 3.8912804740679552 0.48523832910207076 +4430 -0.10371828725423776 1.7850232206076697 -1.7983691191536761 +4431 -1.4012056333846075 1.7371125121025368 -1.0110863766577514 +4432 0.66450156910167513 3.9622733193929429 -0.078682954780919329 +4433 0.093965673519041393 1.4170985370230225 -2.0217420961942336 +4434 -1.05043940338557 1.159655806180468 1.5678984320074005 +4435 -0.84457318009381388 3.0954169184848217 -0.92093675763886751 +4436 -1.443377936958387 3.574869144692352 -2.1944861858881053 +4437 0.95489476970589493 3.1345309318753638 -0.62883749292156954 +4438 -0.18530262270504599 1.5312535633939042 -1.906850556292643 +4439 0.20274169293478186 0.70025448054313366 2.2347390651536019 +4440 -1.0597393383007565 3.7427887226705692 -1.5126284967394601 +4441 1.05043940338557 1.159655806180468 1.5678984320074005 +4442 1.0569472780587283 3.4109579564871688 -1.2989287655973465 +4443 -0.40835166401790457 4.1345163384790542 0.41773765817389769 +4444 0.27668341802896484 4.203157114502833 -0.0020191217884653334 +4445 -0.46386430367113124 2.2445785200651578 -2.6829341152975781 +4446 1.2428808153445086 2.4569771733744266 -0.37913425628763331 +4447 -0.58188171780877751 1.6512134972919312 -1.6568628498247631 +4448 -5.8994431823219644 -4.6940001881252078 5.9522613704907039 +4449 -1.7251816391944885 1.723611056804657 -0.22901919484138489 +4450 -0.41395537845173486 4.100227244306585 0.48102881682081555 +4451 -1.3824877177308577 1.9616481184583381 -0.79895464786077519 +4452 -1.4806318816344834 0.94624457693954778 -0.87688303893748099 +4453 0.85814921998835303 2.9368647290824303 -1.1731438797572113 +4454 1.7098725248895874 1.7878742114243342 -0.18338131648074862 +4455 -0.89896331018992137 1.5642256757281046 -1.5646962953233625 +4456 -1.4221615076864649 1.7011381386511408 -1.0192764619125885 +4457 -0.076532615061327441 0.97024439154440201 2.0668591197363226 +4458 -1.7098725248895874 1.7878742114243342 -0.18338131648074862 +4459 0.74964848633263137 3.1532891576454265 -1.1475506373399389 +4460 0.40835166401790457 4.1345163384790542 0.41773765817389763 +4461 0.82037516782521935 3.2212012987434773 -0.91935670571652761 +4462 -0.99759551881421493 1.5464110129900128 -1.5294898030027133 +4463 -0.15622145614974911 4.3412736985585472 -0.53712127953600519 +4464 -0.89753939446011022 1.5390627130214769 -1.5785756938136488 +4465 0.57316595613430976 3.6489502422584592 0.66104255785654875 +4466 -0.82024994876286939 3.2698759832223638 -1.2179319706840372 +4467 -1.4361496908217868 3.5187384145296265 -2.1798715527879216 +4468 0.75579081652564573 3.1280332740499359 -1.1444575132049613 +4469 1.0562649797483432 3.2497247725826872 -1.3651375820964236 +4470 -1.0440654010011692 3.3356613708137552 -1.3137765745981387 +4471 -1.0363619799969068 3.3088457632501243 -1.3139120162351736 +4472 -0.35660956137238359 4.2097254224760121 0.021496576774343912 +4473 -1.6066026109434741 1.1865498070981249 -1.1848117328902712 +4474 -0.34207459159864839 4.1452374403163228 0.34830080954369258 +4475 -0.57316595613430976 3.6489502422584592 0.66104255785654875 +4476 -1.0569472780587283 3.4109579564871688 -1.2989287655973465 +4477 0.24444427951882186 4.1102317414020098 0.32328333926728126 +4478 -0.80235554721552538 3.337317825989226 0.017560250643136929 +4479 0.27597679230928213 1.8205793399612888 -1.6963341925011819 +4480 1.779231799000768 1.186185228076559 -0.53433145638275648 +4481 -0.76074385833288172 3.0724232270549461 -1.1238740145869159 +4482 0.30099980538672538 4.0503330150988788 0.4880357253750725 +4483 -0.78992694321588164 3.3382474814104928 0.050552054964738927 +4484 5.9362812652598134 -4.6930260920404265 5.9363206431052546 +4485 1.4452802606845121 1.7976246576126684 -0.86717988573570093 +4486 -0.87507760524749756 1.4681104421615601 -1.627056896686554 +4487 0.78486535826839887 3.0399144966593417 -1.1957101128732266 +4488 0.45735173491186376 1.1133204377018284 1.8495400164708087 +4489 1.6273737037642546 0.82618520168643672 -0.53459940999565481 +4490 -5.905374989297318 -4.6543556652552169 -5.9062035980990384 +4491 -0.32467847710300013 1.4470550522920005 -1.8902624676875526 +4492 -5.918505959232812 -4.7058945362940214 5.9400515020264253 +4493 -0.21331436755092956 4.2481718676353175 -0.27004956728439655 +4494 -0.89216708542104772 2.9379036419161637 -1.0821494941493928 +4495 -0.57145554897346118 3.3509311743411696 0.68472541702939771 +4496 1.1829209996845718 1.0650569508168344 1.5502008349429932 +4497 0.90787850271206683 2.9759053537125757 -1.2658855725807912 +4498 -0.91287775336274157 0.32907008556773232 1.0967032377699055 +4499 0.83463746212108858 2.960646603966028 -1.1143612926475153 +4500 -0.17134417441710323 4.2647115803808644 -0.28334108133567393 +4501 0.91980012192894733 3.0909415664638602 -1.2656869221906151 +4502 -1.7242562754604462 1.6377652378906131 0.12662247756348516 +4503 0.36443629351282247 4.0472776033821116 0.52167055582719191 +4504 0.34651016284970576 1.4237753663858292 -1.8942781530801054 +4505 1.3836299332210549 1.7581479587477538 -1.0229148525647662 +4506 -0.87643844847471986 2.9571291352303186 -1.0525244608918367 +4507 -1.3071161716948803 0.89131517233780544 -0.78733846529725926 +4508 -0.8583354618204484 2.9396181726004418 -1.2100377990895965 +4509 1.0514966186291168 2.6821533372627182 -0.0060393721375950754 +4510 0.9707609858570353 2.9217691160174835 -0.31255262868045386 +4511 0.78791834834775409 3.0195516703797729 -1.1930342617418495 +4512 -1.8970189112209497 1.3212719478652393 -0.43090272280060188 +4513 -1.0514966186291168 2.6821533372627182 -0.0060393721375950754 +4514 -0.53852116592821586 0.42308466702392095 1.6090399324996434 +4515 0.81703965461505346 3.2609226651295469 -0.9306442647486588 +4516 -8.307767391204834 -4.615687370300293 -0.0016985506808850914 +4517 0.81552701526929738 2.716533161322976 -1.1057979867889285 +4518 0.81096686965999454 2.9742499397600581 -1.2181272555096452 +4519 0.46386430367113124 2.2445785200651578 -2.6829341152975781 +4520 1.5708304116378424 1.0562752757516176 -0.97267739315030377 +4521 5.905374989297318 -4.6543556652552169 -5.9062035980990384 +4522 0.10538318755450043 1.57947151120831 -1.9188456290412415 +4523 0.80214950525957152 2.450601253866211 -1.0861756640953979 +4524 -0.8985236342510956 3.1774102540990952 -1.2767667086763648 +4525 0.82020316770158219 2.6884258334865732 -1.0075716652225941 +4526 0.94060613217087274 3.1818239680045224 -0.59132086928377614 +4527 -0.070842956421927783 4.328481084123597 -0.54540167602468803 +4528 -0.29491115470740509 4.2622144857284727 -0.27782979137920971 +4529 -0.20274169293478186 0.70025448054313366 2.2347390651536019 +4530 -0.9707609858570353 2.9217691160174835 -0.31255262868045386 +4531 1.0371367044477944 3.0458801901898358 -1.4645895340012243 +4532 0.72986483573913574 3.6224198341369629 0.40111255645751953 +4533 0.3529489947118164 1.3775232966836488 -1.9149125328175349 +4534 -1.4512715254252144 1.8664081466814182 0.35531075559348363 +4535 1.4512715254252144 1.8664081466814182 0.35531075559348363 +4536 1.3463749869425663 2.2936074237226731 -0.12663301968931651 +4537 1.2789487639422075 1.4794144105800648 1.0838502793379416 +4538 -1.3187176710633595 1.2090835418466179 -1.628133945731191 +4539 -0.72986483573913574 3.6224198341369629 0.40111255645751953 +4540 -0.093965673519041393 1.4170985370230225 -2.0217420961942336 +4541 0.79223290267659618 2.9989638872813176 -1.19588577261922 +4542 0.52199537376718919 3.6561595870986343 0.68721527360818802 +4543 -0.85747179772447657 3.744454369047002 -0.94835020950089732 +4544 1.5565905254970216 1.8213718928682567 -0.58825516107263798 +4545 -0.85604662298740619 3.0495385269031616 -0.94329754142451416 +4546 -0.7503643349714626 3.1316310231141067 -1.1289456937363465 +4547 0.14888380057353778 1.3534306149372255 -2.0338396476576963 +4548 -0.68656292085713544 3.9268277736258934 0.25625176529043597 +4549 -0.51293435677584609 0.41339072063943727 1.6132644071429243 +4550 -8.3951275202018607 -4.6929856310371019 3.5810441765898654e-05 +4551 1.4012056333846075 1.7371125121025368 -1.0110863766577514 +4552 -1.1082963834828574 2.6070490574484313 -0.017166826564342666 +4553 0.18323536962270737 1.5842663645744324 -1.8767365217208862 +4554 1.1082963834828574 2.6070490574484313 -0.017166826564342666 +4555 -0.9795582576776295 2.9968198131628463 -0.64895190198075337 +4556 0.82298325848798504 3.7318344924923381 -0.79780146440334931 +4557 0.91954054778191918 3.7671662606193319 -1.2192915899648811 +4558 1.399976190654892 0.88870712034336208 -0.83220192006643134 +4559 -0.38591638924166505 4.21387549625417 0.023159060029904505 +4560 -0.066847823560237885 0.90333098173141479 2.1225296258926392 +4561 1.4221615076864649 1.7011381386511408 -1.0192764619125885 +4562 1.0983052591073263 2.6814883997111725 -0.65616808661670201 +4563 1.2342731936801343 1.0156622167401324 1.563054318577324 +4564 -0.80729274344288471 2.7295751947996423 -1.1368173838059195 +4565 -0.68473688481886685 3.8726491679889201 -0.25925536207904298 +4566 -0.36443629351282247 4.0472776033821116 0.52167055582719202 +4567 5.9053746767049189 -4.6543573272168075 5.9045464299510648 +4568 1.4441651952066343 3.5061316215194718 -2.2042077779247577 +4569 1.6937819063718407 1.4953331415816673 0.35544029672015048 +4570 1.6234898815779912 1.8064424050102692 -0.42774711075518212 +4571 0.90318858623504639 2.9502118825912476 -1.0267394483089447 +4572 -1.3227697563965215 1.6173896842682769 0.84782460211964628 +4573 -0.70417298017647523 3.825659170029625 -0.2642176257813651 +4574 -0.99708443737298791 2.9734467605275436 -1.2433536752620133 +4575 -1.0800668660858892 2.6290325541415029 0.025532143272291849 +4576 -0.42414330089535934 1.0580421662724757 1.8984413882178157 +4577 -0.91378957881959022 2.11372052583422 0.91256062108771108 +4578 1.596362956008877 1.1094114189491662 -1.0152557863326139 +4579 0.91523949980253239 3.7794242136853189 -1.2204312408333766 +4580 0.2685439739486547 1.3423910484898809 -1.977870827093509 +4581 -1.2813641815956449 1.643193369712511 0.89604365866134073 +4582 0.044724195054136009 1.4726094519136115 -2.0125001400466282 +4583 -0.79551350648079944 3.295990761216788 -1.1842168148112295 +4584 -0.73652733968952644 3.7525646662592118 -0.26369338039530454 +4585 1.7245623169279616 1.1231706475586452 -0.55131280167853036 +4586 -0.9179233657530439 2.9942182531834152 -1.2538392138130312 +4587 -1.416394216964064 0.95194014227806578 -0.5115842888117742 +4588 -0.74558853094708666 3.151268057712425 -1.13135066940967 +4589 1.0534785766743064 3.2923944585874274 -1.367342835802555 +4590 1.4526375754913774 3.5719236383672235 -2.2213378987046495 +4591 -1.3735900417524642 1.9382765302375633 0.40905787362966872 +4592 0.12167345995082463 1.5737076615509538 -1.9143871383037685 +4593 1.6724807388035741 1.0603188888955071 -0.5727081060137269 +4594 -5.8744680881500244 -4.6156885623931885 5.872772216796875 +4595 -5.9362812652598134 -4.6930260920404265 5.9363206431052546 +4596 -0.7780580100753518 2.5150681555300425 -2.2831626514500427 +4597 -0.93319188480086823 2.9216854676629831 -1.059179022673185 +4598 0.99949420797379163 1.4943886532024016 -1.5608975022175053 +4599 0.19936221891959649 4.1918169046346927 -0.0027463060927336996 +4600 -1.0371367044477944 3.0458801901898358 -1.4645895340012243 +4601 0.7780580100753518 2.5150681555300425 -2.2831626514500427 +4602 1.0440654010011692 3.3356613708137552 -1.3137765745981387 +4603 1.5848106327909925 1.1796283001390571 -1.0991459597735402 +4604 -0.92940344348682769 2.9115767007453375 -1.1369600552569636 +4605 -1.0335503016588927 3.015778230822701 -1.4810276151612363 +4606 -1.3754423615232214 0.92456917005132777 -0.80420432793126784 +4607 -0.83697273960547203 3.8028765241877887 -0.95378185534642801 +4608 0.25172862895479259 2.185801721320185 -3.412814542207121 +4609 0.33509277876935856 4.1679595547089168 0.28691881653657958 +4610 -0.87767089020988664 3.0147086315884239 -0.9649341813357688 +4611 -0.029832888488405658 4.2408246295285519 -0.28847538604257539 +4612 0.67025733063830517 3.7508519979701411 0.56353051418860733 +4613 0.13779279555171314 1.5591592854945862 -1.9146486696980747 +4614 -1.281692857046133 1.9956030623257721 0.51820292676616386 +4615 -0.88526764512062073 3.736473560333252 -1.1043228507041931 +4616 1.6282155420765905 1.7852213783445092 -0.44244873955701003 +4617 1.0328646921377294 2.6992333836435751 0.018414259979082907 +4618 -1.2431097203958041 1.9845811364592418 -1.0434097220874616 +4619 -8.3514474557033473 -4.6543365006686974 -0.00083137011955959639 +4620 1.3272258222997069 0.97880757690278664 1.5464090040497176 +4621 0.85458377971594834 3.5835074070440744 -0.75728657556837276 +4622 -0.43749202787876129 1.1591424942016602 1.8331320285797119 +4623 0.92334017808925628 2.9255801045303298 -1.0646951580701167 +4624 1.3976778340109501 0.75401276074190704 -0.4395731624724048 +4625 -0.12798880810330071 1.5878793846634083 -1.9028741269062022 +4626 -0.030729949168974525 1.7812748011088888 -1.8160975699197217 +4627 -0.52790810132643351 4.1529485486279487 -0.55499035958874166 +4628 1.5948973466757035 1.2322257405885615 -1.1725600810854491 +4629 0.90889598067992727 3.1477614857277669 -1.2687183140729013 +4630 -0.63784596525592185 4.0058981182105038 -0.55918556395478214 +4631 -0.58147158180309411 4.0922332684684797 -0.55860203665492758 +4632 -0.78502667810524684 3.3288549501728357 0.069418682206222715 +4633 -1.0542044796809096 2.6702529954317544 -0.75170929588925173 +4634 0.083161912858486176 -0.4812893271446228 2.3847866058349609 +4635 0.49804330956737891 4.2575751422714747 -0.78508627767192407 +4636 -0.79604522172326742 0.36821433251679081 1.1410330590785847 +4637 -0.86668677797599836 2.9781652734587594 -1.0336441251742177 +4638 0.99759551881421493 1.5464110129900128 -1.5294898030027133 +4639 -0.91982292049817671 3.6786317447498083 -1.1001391279827568 +4640 0.91695580155049461 3.759401733487838 -1.2210207976557752 +4641 -1.0328646921377294 2.6992333836435751 0.018414259979082907 +4642 0.8873974433334415 3.5519891755765554 -0.8305156048100536 +4643 -8.3811959450021742 -4.6983131633082982 0.022933177463977737 +4644 -0.94534028414381455 2.9097522157273406 -1.0827437417583223 +4645 -8.3851639596041476 -4.7057231197193339 0.01534112211923906 +4646 -0.73409239102332591 3.1795105012970848 -1.1216691106216561 +4647 0.85407368789192084 3.2142975241539817 -1.2497839940079511 +4648 -5.9053746767049189 -4.6543573272168075 5.9045464299510648 +4649 -0.68454017783287602 3.9036327010024325 -0.57476829635991578 +4650 1.0783671259682548 2.5087404787850778 0.2770652959199526 +4651 -0.14888380057353778 1.3534306149372255 -2.0338396476576963 +4652 -0.9145546142273181 1.5129461748735029 -1.5854624097508863 +4653 -0.78190382818645787 3.3046232575521231 0.088780932207172217 +4654 0.80422447320030555 3.3261958752155811 -0.0016226476961985967 +4655 1.1301483033817379 2.4500339993228288 0.24268344506382031 +4656 -0.72536438682612947 3.8363195048025482 -0.57353999265923272 +4657 1.3735900417524642 1.9382765302375633 0.40905787362966872 +4658 0.90274757452814225 2.5879491923276454 -2.2683795887350273 +4659 -0.45735173491186376 1.1133204377018284 1.8495400164708087 +4660 1.0363619799969068 3.3088457632501243 -1.3139120162351736 +4661 0.9145546142273181 1.5129461748735029 -1.5854624097508863 +4662 1.0800668660858892 2.6290325541415029 0.025532143272291849 +4663 1.3778102159325258 0.7099758911625228 -0.3808037791286672 +4664 1.3100889906258641 0.84495949221204258 -0.8100138663778822 +4665 -1.3192736694824105 1.9734664224119884 0.46337711331002085 +4666 -0.41943157660384678 1.1930254344390838 1.8192440782213868 +4667 1.1277126982690733 2.4206167196799182 -0.70594277095591673 +4668 -0.90274757452814225 2.5879491923276454 -2.2683795887350273 +4669 -0.52324059737920514 4.2189704919227067 -0.7927633664595688 +4670 0.82079426028796032 3.6372180648978318 -0.75295435436788249 +4671 1.0335503016588927 3.015778230822701 -1.4810276151612363 +4672 5.9241275500474142 -4.7187656439143328 5.9383079014180398 +4673 5.918505959232812 -4.7058945362940214 5.9400515020264253 +4674 -0.8655021644533698 2.9415463890595133 -1.2237466456398616 +4675 0.80235554721552538 3.3373178259892264 0.017560250643136766 +4676 -0.847929853775367 3.0265886409991269 -0.99242302862441023 +4677 0.78992694321588153 3.3382474814104932 0.050552054964739038 +4678 1.6497856962752955 1.7565348743808051 -0.44666033565416546 +4679 0.90227972274728474 3.1776012299375398 -1.2761439491181856 +4680 1.3824877177308577 1.9616481184583381 -0.79895464786077519 +4681 1.5868159390001146 1.2090144579477304 -1.1285403084934438 +4682 -1.6234898815779912 1.8064424050102692 -0.42774711075518212 +4683 -0.89214834210668348 3.7571430395193879 -1.0997196312459894 +4684 0.20705182043649573 1.5593253393033961 -1.8792662619348517 +4685 -1.1829209996845718 1.0650569508168344 1.5502008349429932 +4686 1.1080623231033122 2.7371840096536038 -2.2490153011268648 +4687 -0.34651016284970576 1.4237753663858292 -1.8942781530801054 +4688 0.78086738140846701 3.0845131551087772 -1.1982833670384085 +4689 -0.84221785470842347 2.9425187492494742 -1.181234034937285 +4690 -0.85439671209345103 2.9967122800999419 -1.0145965076127026 +4691 -0.0798640185407586 1.6591392743360591 -1.8794772578830659 +4692 -0.80214950525957152 2.450601253866211 -1.0861756640953979 +4693 -0.04472419505413603 1.4726094519136115 -2.0125001400466282 +4694 0.029832888488405658 4.2408246295285519 -0.28847538604257539 +4695 1.1683491543057307 2.399095359982284 0.21331136775825488 +4696 -0.34506585941239504 1.8287470780444497 -1.6602253382855214 +4697 0.41943157660384678 1.1930254344390838 1.8192440782213868 +4698 -8.3803510507707006 -4.6940548170019092 0.03738324101541917 +4699 -1.3187985931483597 1.4457916578464292 1.0477752537861977 +4700 0.77553255755985095 3.1149445295989082 -1.2008900873989485 +4701 -1.1080623231033122 2.7371840096536038 -2.2490153011268648 +4702 0.11415650432652323 0.31064548259914382 1.3987183875815887 +4703 -1.3003728352096096 1.3513216039964373 -1.5350625564473603 +4704 -0.88069878179438632 3.7897741967645433 -1.1027534270595534 +4705 1.1887800143496803 2.4183780193236877 0.14246640878266431 +4706 -0.81552701526929738 2.716533161322976 -1.1057979867889285 +4707 1.0248152640066448 3.3831023367530353 -1.2273597067329236 +4708 0.76441875451313723 3.1606419941165664 -1.2064867211446031 +4709 0.85211084522183056 2.9401515473811508 -1.1624410818862212 +4710 -1.1991920140560428 2.8097450518820066 -2.2320629380178771 +4711 0.48333248634176829 4.2213420656112142 -0.54181304405179942 +4712 0.78502667810524673 3.3288549501728362 0.069418682206222979 +4713 -1.2645543578459435 1.4882362452593023 1.1004840468775228 +4714 0.49312407557121468 4.1619951496240208 0.017677077574317746 +4715 0.83805060548857591 3.2405196486078505 -1.2428890452786816 +4716 0.52790021897753814 4.1530600446218857 -0.55539190393005322 +4717 1.1991920140560428 2.8097450518820066 -2.2320629380178771 +4718 1.281692857046133 1.9956030623257721 0.51820292676616386 +4719 0.82707134316524111 2.9698948541415597 -1.1001104375694508 +4720 -1.2789487639422075 1.4794144105800648 1.0838502793379416 +4721 1.6055599413354837 1.0234359886589155 -0.55811220815076357 +4722 -0.82037516782521935 3.2212012987434773 -0.91935670571652761 +4723 -0.81703965461505346 3.2609226651295469 -0.9306442647486588 +4724 1.1997185772360397 0.78669806007407328 -0.77640648163386994 +4725 0.78190382818645776 3.3046232575521235 0.088780932207172675 +4726 -1.2898961701162699 2.9107108141312632 -2.2807447370579732 +4727 0.80027916297492596 3.6996354115009922 -0.74428544289051701 +4728 0.55818515144673775 4.0868059345654713 -0.094148203399836622 +4729 0.52002411151949091 4.1401001456240429 0.017250118292321981 +4730 -0.3529489947118164 1.3775232966836488 -1.9149125328175349 +4731 0.57080819275651218 4.0856473958835906 0.019085897456964318 +4732 1.4972001560323356 0.95544269757725275 -0.54000969170532986 +4733 1.3192736694824105 1.9734664224119884 0.46337711331002085 +4734 0.93652830495084571 2.9126255385866329 -1.0902766767524896 +4735 -0.83828383317497634 2.9446601224605877 -1.1742611260138369 +4736 0.95541429666503452 2.9023454838441172 -1.1237514045933001 +4737 0.89519203249635826 2.9674619127086772 -1.0112572169117489 +4738 -0.91954054778191918 3.7671662606193319 -1.2192915899648811 +4739 1.2898961701162699 2.9107108141312632 -2.2807447370579732 +4740 0.82355405145031368 3.2699955565188077 -1.2170987965470395 +4741 0.814080622658654 2.9979182161183031 -1.0749589351258151 +4742 0.80826453285428279 3.0066953095251474 -1.0659449350906802 +4743 -0.91523949980253239 3.7794242136853189 -1.2204312408333766 +4744 0.96315588782721784 2.9009522820868519 -1.1375863966271227 +4745 0.7977893394554334 3.2971959002935582 -1.1819821346188593 +4746 1.4287123157179078 0.91766487370222327 -0.50270547178425373 +4747 0.79550090463740442 3.047531300832909 -1.0408817089239517 +4748 -0.9655673838892741 2.8987421668078852 -1.1185955344115774 +4749 0.78886557570948845 3.0598219256413115 -1.0316948536953006 +4750 0.58155733910212382 4.0922386220421112 -0.559009894928155 +4751 0.78335465644975 3.0900506077097347 -1.0218635188643601 +4752 -1.3934742914606519 3.0197688704832957 -2.3263745152464788 +4753 -1.6282155420765905 1.7852213783445092 -0.44244873955701003 +4754 -1.6497856962752955 1.7565348743808051 -0.44666033565416546 +4755 -1.4340392711151784 3.0978317503101023 -2.3476270410744826 +4756 0.77723672775265529 3.1324493995337224 -1.0098584847775334 +4757 0.77327052128560869 3.1457037732828477 -1.006231176217991 +4758 0.63736048331081274 4.0054322972603229 -0.55557994146774914 +4759 -1.2342731936801343 1.0156622167401324 1.563054318577324 +4760 0.73792385321530818 3.8325561127218903 -0.61632921743724078 +4761 -1.3272258222997069 0.97880757690278664 1.5464090040497176 +4762 0.58717760820991172 4.0570621487344587 0.022218109610037076 +4763 0.98899428425337466 2.9079041571424833 -1.1870114432425571 +4764 -0.91695580155049461 3.759401733487838 -1.2210207976557752 +4765 1.0232007158411931 2.9665939719624084 -1.2398943582128175 +4766 1.0227694649966406 2.9622396897435479 -1.2422367072972993 +4767 -0.97255769330566755 2.897484168140851 -1.1310879113743624 +4768 1.0480579376236334 2.8800832845470685 -1.2101997376374534 +4769 1.3934742914606519 3.0197688704832957 -2.3263745152464788 +4770 1.0519569752625832 2.9632980223478 -1.2604968027877699 +4771 0.77920831255530454 3.1003648978143494 -1.0172671359131609 +4772 0.99429845221269009 2.9134941506148402 -1.1955708632302775 +4773 1.4340392711151784 3.0978317503101023 -2.3476270410744826 +4774 0.68770229108687331 3.9153641804930182 -0.61784026522328939 +# Generated by tetgen octopus.ele diff --git a/examples/testbed/data/teapot.cdf b/examples/testbed/data/teapot.cdf new file mode 100644 index 0000000..7af0440 Binary files /dev/null and b/examples/testbed/data/teapot.cdf differ diff --git a/examples/testbed/framework/body_dragger.cpp b/examples/testbed/framework/body_dragger.cpp index 5e0be78..bb51d5d 100644 --- a/examples/testbed/framework/body_dragger.cpp +++ b/examples/testbed/framework/body_dragger.cpp @@ -35,12 +35,23 @@ bool b3BodyDragger::StartDragging() { B3_ASSERT(IsDragging() == false); + class RayCastFilter : public b3RayCastFilter + { + public: + bool ShouldRayCast(b3Shape* shape) + { + return true; + } + }; + + RayCastFilter filter; + b3RayCastSingleOutput out; - if (m_world->RayCastSingle(&out, m_ray->A(), m_ray->B()) == false) + if (m_world->RayCastSingle(&out, &filter, m_ray->A(), m_ray->B()) == false) { return false; } - + m_x = out.fraction; m_shape = out.shape; @@ -54,7 +65,7 @@ bool b3BodyDragger::StartDragging() jd.bodyA = groundBody; jd.bodyB = body; jd.target = out.point; - jd.maxForce = 2000.0f * body->GetMass(); + jd.maxForce = 1000.0f * body->GetMass(); m_mouseJoint = (b3MouseJoint*)m_world->CreateJoint(jd); @@ -79,10 +90,10 @@ void b3BodyDragger::StopDragging() m_shape = nullptr; } -b3Body* b3BodyDragger::GetBody() const +b3Shape* b3BodyDragger::GetShape() const { B3_ASSERT(IsDragging() == true); - return m_shape->GetBody(); + return m_shape; } b3Vec3 b3BodyDragger::GetPointA() const diff --git a/examples/testbed/framework/body_dragger.h b/examples/testbed/framework/body_dragger.h index d7f4858..33457a2 100644 --- a/examples/testbed/framework/body_dragger.h +++ b/examples/testbed/framework/body_dragger.h @@ -24,6 +24,7 @@ #include #include #include +#include #include // A body shape dragger. @@ -43,14 +44,14 @@ public: b3Ray3* GetRay() const; - b3Body* GetBody() const; + b3Shape* GetShape() const; b3Vec3 GetPointA() const; b3Vec3 GetPointB() const; private: b3Ray3 * m_ray; - float32 m_x; + scalar m_x; b3World* m_world; diff --git a/examples/testbed/framework/cloth_dragger.cpp b/examples/testbed/framework/cloth_dragger.cpp index 40467c9..e383aff 100644 --- a/examples/testbed/framework/cloth_dragger.cpp +++ b/examples/testbed/framework/cloth_dragger.cpp @@ -23,9 +23,9 @@ b3ClothDragger::b3ClothDragger(b3Ray3* ray, b3Cloth* cloth) m_staticDrag = true; m_ray = ray; m_cloth = cloth; - m_triangle = nullptr; - m_km = 10000.0f; - m_kd = 0.0f; + m_isDragging = false; + m_km = 100000.0f; + m_kd = 1000.0f; } b3ClothDragger::~b3ClothDragger() @@ -43,22 +43,20 @@ bool b3ClothDragger::StartDragging() return false; } - m_mesh = m_cloth->GetMesh(); - m_triangleIndex = rayOut.triangle; - m_triangle = m_mesh->triangles + m_triangleIndex; + m_isDragging = true; m_x = rayOut.fraction; - b3Particle* p1 = m_cloth->GetParticle(m_triangle->v1); - b3Particle* p2 = m_cloth->GetParticle(m_triangle->v2); - b3Particle* p3 = m_cloth->GetParticle(m_triangle->v3); + m_p1 = rayOut.triangle->GetParticle1(); + m_p2 = rayOut.triangle->GetParticle2(); + m_p3 = rayOut.triangle->GetParticle3(); - b3Vec3 v1 = p1->GetPosition(); - b3Vec3 v2 = p2->GetPosition(); - b3Vec3 v3 = p3->GetPosition(); + b3Vec3 v1 = m_p1->GetPosition(); + b3Vec3 v2 = m_p2->GetPosition(); + b3Vec3 v3 = m_p3->GetPosition(); b3Vec3 B = GetPointB(); - float32 wABC[4]; + scalar wABC[4]; b3BarycentricCoordinates(wABC, v1, v2, v3, B); if (wABC[3] > B3_EPSILON) @@ -73,33 +71,34 @@ bool b3ClothDragger::StartDragging() if (m_staticDrag) { - m_t1 = p1->GetType(); - p1->SetType(e_staticParticle); + m_t1 = m_p1->GetType(); + m_p1->SetType(e_staticClothParticle); - m_t2 = p2->GetType(); - p2->SetType(e_staticParticle); + m_t2 = m_p2->GetType(); + m_p2->SetType(e_staticClothParticle); - m_t3 = p3->GetType(); - p3->SetType(e_staticParticle); + m_t3 = m_p3->GetType(); + m_p3->SetType(e_staticClothParticle); } else { - b3ParticleDef pd; - pd.type = e_staticParticle; + b3ClothParticleDef pd; + pd.type = e_staticClothParticle; pd.position = GetPointA(); m_particle = m_cloth->CreateParticle(pd); - b3ClothTriangle* triangle = m_cloth->GetTriangle(m_triangleIndex); - b3MouseForceDef def; - def.particle = m_particle; - def.triangle = triangle; + def.p1 = m_particle; + def.p2 = m_p1; + def.p3 = m_p2; + def.p4 = m_p3; def.w2 = m_u; def.w3 = m_v; - def.w4 = (1.0f - m_u - m_v); + def.w4 = 1.0f - m_u - m_v; def.mouse = m_km; def.damping = m_kd; + def.restLength = 0.0f; m_mf = (b3MouseForce*)m_cloth->CreateForce(def); } @@ -111,24 +110,23 @@ void b3ClothDragger::Drag() { B3_ASSERT(IsDragging() == true); - b3Vec3 A = GetPointA(); b3Vec3 B = GetPointB(); - b3Vec3 dx = B - A; - if (m_staticDrag) { - b3Particle* p1 = m_cloth->GetParticle(m_triangle->v1); - p1->ApplyTranslation(dx); + b3Vec3 A = GetPointA(); - b3Particle* p2 = m_cloth->GetParticle(m_triangle->v2); - p2->ApplyTranslation(dx); + b3Vec3 dx = B - A; - b3Particle* p3 = m_cloth->GetParticle(m_triangle->v3); - p3->ApplyTranslation(dx); + m_p1->ApplyTranslation(dx); + m_p2->ApplyTranslation(dx); + m_p3->ApplyTranslation(dx); } else { + //b3Vec3 A = m_particle->GetPosition(); + //b3Vec3 dx = B - A; + //m_particle->ApplyTranslation(dx); m_particle->SetPosition(B); } } @@ -154,9 +152,9 @@ void b3ClothDragger::StopDragging() if (m_staticDrag) { - m_cloth->GetParticle(m_triangle->v1)->SetType(m_t1); - m_cloth->GetParticle(m_triangle->v2)->SetType(m_t2); - m_cloth->GetParticle(m_triangle->v3)->SetType(m_t3); + m_p1->SetType(m_t1); + m_p2->SetType(m_t2); + m_p3->SetType(m_t3); } else { @@ -164,18 +162,18 @@ void b3ClothDragger::StopDragging() m_cloth->DestroyParticle(m_particle); } - m_triangle = nullptr; + m_isDragging = false; } b3Vec3 b3ClothDragger::GetPointA() const { B3_ASSERT(IsDragging() == true); - b3Vec3 A = m_cloth->GetParticle(m_triangle->v1)->GetPosition(); - b3Vec3 B = m_cloth->GetParticle(m_triangle->v2)->GetPosition(); - b3Vec3 C = m_cloth->GetParticle(m_triangle->v3)->GetPosition(); + b3Vec3 v1 = m_p1->GetPosition() + m_p1->GetTranslation(); + b3Vec3 v2 = m_p2->GetPosition() + m_p2->GetTranslation(); + b3Vec3 v3 = m_p3->GetPosition() + m_p3->GetTranslation(); - return m_u * A + m_v * B + (1.0f - m_u - m_v) * C; + return m_u * v1 + m_v * v2 + (1.0f - m_u - m_v) * v3; } b3Vec3 b3ClothDragger::GetPointB() const diff --git a/examples/testbed/framework/cloth_dragger.h b/examples/testbed/framework/cloth_dragger.h index 9661570..610f699 100644 --- a/examples/testbed/framework/cloth_dragger.h +++ b/examples/testbed/framework/cloth_dragger.h @@ -21,9 +21,8 @@ #include #include -#include -#include -#include +#include +#include #include // A cloth triangle dragger. @@ -37,13 +36,13 @@ public: bool GetStaticDrag() const; - void SetMouseStiffness(float32 k); + void SetMouseStiffness(scalar k); - float32 GetMouseStiffness(); + scalar GetMouseStiffness(); - void SetMouseDamping(float32 k); + void SetMouseDamping(scalar k); - float32 GetMouseDamping(); + scalar GetMouseDamping(); bool IsDragging() const; @@ -58,21 +57,23 @@ public: b3Vec3 GetPointB() const; private: b3Ray3* m_ray; - float32 m_x; + scalar m_x; b3Cloth* m_cloth; - const b3ClothMesh* m_mesh; - u32 m_triangleIndex; - b3ClothMeshTriangle* m_triangle; - float32 m_u, m_v; + + bool m_isDragging; + b3ClothParticle* m_p1; + b3ClothParticle* m_p2; + b3ClothParticle* m_p3; + scalar m_u, m_v; - float32 m_km; - float32 m_kd; - b3Particle* m_particle; + scalar m_km; + scalar m_kd; + b3ClothParticle* m_particle; b3MouseForce* m_mf; bool m_staticDrag; - b3ParticleType m_t1, m_t2, m_t3; + b3ClothParticleType m_t1, m_t2, m_t3; }; inline bool b3ClothDragger::GetStaticDrag() const @@ -80,29 +81,29 @@ inline bool b3ClothDragger::GetStaticDrag() const return m_staticDrag; } -inline void b3ClothDragger::SetMouseStiffness(float32 k) +inline void b3ClothDragger::SetMouseStiffness(scalar k) { m_km = k; } -inline float32 b3ClothDragger::GetMouseStiffness() +inline scalar b3ClothDragger::GetMouseStiffness() { return m_km; } -inline void b3ClothDragger::SetMouseDamping(float32 k) +inline void b3ClothDragger::SetMouseDamping(scalar k) { m_kd = k; } -inline float32 b3ClothDragger::GetMouseDamping() +inline scalar b3ClothDragger::GetMouseDamping() { return m_kd; } inline bool b3ClothDragger::IsDragging() const { - return m_triangle != nullptr; + return m_isDragging; } #endif \ No newline at end of file diff --git a/examples/testbed/framework/draw.cpp b/examples/testbed/framework/draw.cpp index ba206e6..35c1475 100644 --- a/examples/testbed/framework/draw.cpp +++ b/examples/testbed/framework/draw.cpp @@ -50,17 +50,17 @@ Camera::Camera() b3Mat44 Camera::BuildProjectionMatrix() const { - float32 w = m_width, h = m_height; + scalar w = m_width, h = m_height; - float32 t = tan(0.5f * m_fovy); - float32 ratio = w / h; - float32 sx = 1.0f / (ratio * t); - float32 sy = 1.0f / t; + scalar t = tan(0.5f * m_fovy); + scalar ratio = w / h; + scalar sx = 1.0f / (ratio * t); + scalar sy = 1.0f / t; - float32 inv_range = 1.0f / (m_zNear - m_zFar); - float32 sz = inv_range * (m_zNear + m_zFar); + scalar inv_range = 1.0f / (m_zNear - m_zFar); + scalar sz = inv_range * (m_zNear + m_zFar); - float32 tz = inv_range * m_zNear * m_zFar; + scalar tz = inv_range * m_zNear * m_zFar; b3Mat44 m; m.x = b3Vec4(sx, 0.0f, 0.0f, 0.0f); @@ -73,8 +73,8 @@ b3Mat44 Camera::BuildProjectionMatrix() const b3Transform Camera::BuildWorldTransform() const { b3Transform xf; - xf.rotation = b3QuatMat33(m_q); - xf.position = (m_zoom * xf.rotation.z) - m_center; + xf.rotation = m_q; + xf.translation = (m_zoom * m_q.GetZAxis()) - m_center; return xf; } @@ -87,8 +87,8 @@ b3Mat44 Camera::BuildWorldMatrix() const b3Transform Camera::BuildViewTransform() const { b3Transform xf; - xf.rotation = b3QuatMat33(m_q); - xf.position = (m_zoom * xf.rotation.z) - m_center; + xf.rotation = m_q; + xf.translation = (m_zoom * m_q.GetZAxis()) - m_center; return b3Inverse(xf); } @@ -100,7 +100,7 @@ b3Mat44 Camera::BuildViewMatrix() const b3Vec2 Camera::ConvertWorldToScreen(const b3Vec3& pw3) const { - float32 w = m_width, h = m_height; + scalar w = m_width, h = m_height; b3Mat44 P = BuildProjectionMatrix(); b3Mat44 V = BuildViewMatrix(); @@ -109,11 +109,11 @@ b3Vec2 Camera::ConvertWorldToScreen(const b3Vec3& pw3) const b3Vec4 pp = P * V * pw; b3Vec3 pn(pp.x, pp.y, pp.z); - float32 inv_w = pp.w != 0.0f ? 1.0f / pp.w : 1.0f; + scalar inv_w = pp.w != 0.0f ? 1.0f / pp.w : 1.0f; pn *= inv_w; - float32 u = 0.5f * (pn.x + 1.0f); - float32 v = 0.5f * (pn.y + 1.0f); + scalar u = 0.5f * (pn.x + 1.0f); + scalar v = 0.5f * (pn.y + 1.0f); b3Vec2 ps; ps.x = u * w; @@ -123,10 +123,10 @@ b3Vec2 Camera::ConvertWorldToScreen(const b3Vec3& pw3) const b3Ray3 Camera::ConvertScreenToWorld(const b3Vec2& ps) const { - float32 w = m_width, h = m_height; + scalar w = m_width, h = m_height; - float32 t = tan(0.5f * m_fovy); - float32 ratio = w / h; + scalar t = tan(0.5f * m_fovy); + scalar ratio = w / h; b3Vec3 vv; vv.x = 2.0f * ratio * ps.x / w - ratio; @@ -135,12 +135,12 @@ b3Ray3 Camera::ConvertScreenToWorld(const b3Vec2& ps) const b3Transform xf = BuildWorldTransform(); - b3Vec3 vw = xf.rotation * vv; + b3Vec3 vw = b3Mul(xf.rotation, vv); vw.Normalize(); b3Ray3 rw; rw.direction = vw; - rw.origin = xf.position; + rw.origin = xf.translation; rw.fraction = m_zFar; return rw; } @@ -194,7 +194,7 @@ void Draw::EnableDrawTriangles(bool flag) g_glDrawTriangles = flag; } -void Draw::DrawPoint(const b3Vec3& p, float32 size, const b3Color& color) +void Draw::DrawPoint(const b3Vec3& p, scalar size, const b3Color& color) { m_points->Vertex(p, size, color); } @@ -249,14 +249,15 @@ void Draw::DrawSolidPolygon(const b3Vec3& normal, const b3Vec3* vertices, u32 co } } -void Draw::DrawCircle(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) +void Draw::DrawCircle(const b3Vec3& normal, const b3Vec3& center, scalar radius, const b3Color& color) { b3Vec3 n1, n3; b3ComputeBasis(normal, n1, n3); u32 kEdgeCount = 20; - float32 kAngleInc = 2.0f * B3_PI / float32(kEdgeCount); - b3Quat q(normal, kAngleInc); + scalar kAngleInc = 2.0f * B3_PI / scalar(kEdgeCount); + b3Quat q; + q.SetAxisAngle(normal, kAngleInc); b3Vec3 p1 = center + radius * n1; for (u32 i = 0; i < kEdgeCount; ++i) @@ -272,7 +273,7 @@ void Draw::DrawCircle(const b3Vec3& normal, const b3Vec3& center, float32 radius } } -void Draw::DrawSolidCircle(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) +void Draw::DrawSolidCircle(const b3Vec3& normal, const b3Vec3& center, scalar radius, const b3Color& color) { b3Color fillColor(color.r, color.g, color.b, color.a); b3Color frameColor(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 1.0f); @@ -281,8 +282,10 @@ void Draw::DrawSolidCircle(const b3Vec3& normal, const b3Vec3& center, float32 r b3ComputeBasis(normal, n1, n3); const u32 kEdgeCount = 20; - const float32 kAngleInc = 2.0f * B3_PI / float32(kEdgeCount); - b3Quat q(normal, kAngleInc); + const scalar kAngleInc = 2.0f * B3_PI / scalar(kEdgeCount); + + b3Quat q; + q.SetAxisAngle(normal, kAngleInc); b3Vec3 p1 = center + radius * n1; for (u32 i = 0; i < kEdgeCount; ++i) @@ -299,32 +302,32 @@ void Draw::DrawSolidCircle(const b3Vec3& normal, const b3Vec3& center, float32 r } } -void Draw::DrawSphere(const b3Vec3& center, float32 radius, const b3Color& color) +void Draw::DrawSphere(const b3Vec3& center, scalar radius, const b3Color& color) { b3Transform xf; xf.rotation.SetIdentity(); - xf.position = center; + xf.translation = center; m_wire->DrawSphere(radius, color, xf); } -void Draw::DrawSolidSphere(const b3Vec3& center, float32 radius, const b3Mat33& rotation, const b3Color& color) +void Draw::DrawSolidSphere(const b3Vec3& center, scalar radius, const b3Quat& rotation, const b3Color& color) { b3Transform xf; xf.rotation = rotation; - xf.position = center; + xf.translation = center; m_solid->DrawSphere(radius, color, xf); } -void Draw::DrawCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, const b3Color& color) +void Draw::DrawCapsule(const b3Vec3& c1, const b3Vec3& c2, scalar radius, const b3Color& color) { - float32 height = b3Length(c1 - c2); + scalar height = b3Length(c1 - c2); { b3Transform xf; xf.rotation.SetIdentity(); - xf.position = c1; + xf.translation = c1; m_wire->DrawSphere(radius, color, xf); } @@ -335,20 +338,20 @@ void Draw::DrawCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, const { b3Transform xf; xf.rotation.SetIdentity(); - xf.position = c2; + xf.translation = c2; m_wire->DrawSphere(radius, color, xf); } } } -void Draw::DrawSolidCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, const b3Mat33& rotation, const b3Color& c) +void Draw::DrawSolidCapsule(const b3Vec3& c1, const b3Vec3& c2, scalar radius, const b3Quat& rotation, const b3Color& c) { - float32 height = b3Length(c1 - c2); + scalar height = b3Length(c1 - c2); { b3Transform xf; xf.rotation = rotation; - xf.position = c1; + xf.translation = c1; m_solid->DrawSphere(radius, c, xf); } @@ -359,9 +362,11 @@ void Draw::DrawSolidCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, R.y = (1.0f / height) * (c1 - c2); b3ComputeBasis(R.y, R.z, R.x); + b3Quat Q = b3Mat33Quat(R); + b3Transform xf; - xf.position = 0.5f * (c1 + c2); - xf.rotation = R; + xf.translation = 0.5f * (c1 + c2); + xf.rotation = Q; m_solid->DrawCylinder(radius, height, c, xf); } @@ -369,7 +374,7 @@ void Draw::DrawSolidCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, { b3Transform xf; xf.rotation = rotation; - xf.position = c2; + xf.translation = c2; m_solid->DrawSphere(radius, c, xf); } } @@ -377,24 +382,24 @@ void Draw::DrawSolidCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, void Draw::DrawTransform(const b3Transform& xf) { - float32 lenght = 1.0f; + scalar lenght = 1.0f; - b3Vec3 position = xf.position; - b3Mat33 rotation = xf.rotation; + b3Vec3 translation = xf.translation; + b3Mat33 rotation = b3QuatMat33(xf.rotation); - b3Vec3 A = position + lenght * rotation.x; - b3Vec3 B = position + lenght * rotation.y; - b3Vec3 C = position + lenght * rotation.z; + b3Vec3 A = translation + lenght * rotation.x; + b3Vec3 B = translation + lenght * rotation.y; + b3Vec3 C = translation + lenght * rotation.z; - DrawSegment(position, A, b3Color_red); - DrawSegment(position, B, b3Color_green); - DrawSegment(position, C, b3Color_blue); + DrawSegment(translation, A, b3Color_red); + DrawSegment(translation, B, b3Color_green); + DrawSegment(translation, C, b3Color_blue); } -void Draw::DrawAABB(const b3AABB3& aabb, const b3Color& color) +void Draw::DrawAABB(const b3AABB& aabb, const b3Color& color) { - b3Vec3 lower = aabb.m_lower; - b3Vec3 upper = aabb.m_upper; + b3Vec3 lower = aabb.lowerBound; + b3Vec3 upper = aabb.upperBound; b3Vec3 vs[8]; @@ -425,12 +430,12 @@ void Draw::DrawAABB(const b3AABB3& aabb, const b3Color& color) DrawSegment(vs[1], vs[7], color); } -void Draw::DrawPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) +void Draw::DrawPlane(const b3Vec3& normal, const b3Vec3& center, scalar radius, const b3Color& color) { b3Vec3 n1, n2; b3ComputeBasis(normal, n1, n2); - float32 scale = 2.0f * radius; + scalar scale = 2.0f * radius; // v1__v4 // | | @@ -448,14 +453,14 @@ void Draw::DrawPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, DrawSegment(center, center + normal, color); } -void Draw::DrawSolidPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) +void Draw::DrawSolidPlane(const b3Vec3& normal, const b3Vec3& center, scalar radius, const b3Color& color) { b3Color frameColor(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 1.0f); b3Vec3 n1, n2; b3ComputeBasis(normal, n1, n2); - float32 scale = 2.0f * radius; + scalar scale = 2.0f * radius; b3Vec3 v1 = center - scale * n1 - scale * n2; b3Vec3 v2 = center + scale * n1 - scale * n2; diff --git a/examples/testbed/framework/draw.h b/examples/testbed/framework/draw.h index 32acb8f..cb3816c 100644 --- a/examples/testbed/framework/draw.h +++ b/examples/testbed/framework/draw.h @@ -42,13 +42,13 @@ public: b3Vec2 ConvertWorldToScreen(const b3Vec3& pw) const; b3Ray3 ConvertScreenToWorld(const b3Vec2& ps) const; - float32 m_zoom; + scalar m_zoom; b3Vec3 m_center; b3Quat m_q; - float32 m_width, m_height; - float32 m_fovy; - float32 m_zNear; - float32 m_zFar; + scalar m_width, m_height; + scalar m_fovy; + scalar m_zNear; + scalar m_zFar; }; class Draw : public b3Draw @@ -67,7 +67,7 @@ public: void EnableDrawTriangles(bool flag); - void DrawPoint(const b3Vec3& p, float32 size, const b3Color& color); + void DrawPoint(const b3Vec3& p, scalar size, const b3Color& color); void DrawSegment(const b3Vec3& p1, const b3Vec3& p2, const b3Color& color); @@ -79,23 +79,23 @@ public: void DrawSolidPolygon(const b3Vec3& normal, const b3Vec3* vertices, u32 count, const b3Color& color); - void DrawCircle(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color); + void DrawCircle(const b3Vec3& normal, const b3Vec3& center, scalar radius, const b3Color& color); - void DrawSolidCircle(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color); + void DrawSolidCircle(const b3Vec3& normal, const b3Vec3& center, scalar radius, const b3Color& color); - void DrawSphere(const b3Vec3& center, float32 radius, const b3Color& color); + void DrawSphere(const b3Vec3& center, scalar radius, const b3Color& color); - void DrawSolidSphere(const b3Vec3& center, float32 radius, const b3Mat33& rotation, const b3Color& color); + void DrawSolidSphere(const b3Vec3& center, scalar radius, const b3Quat& rotation, const b3Color& color); - void DrawCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color); + void DrawCapsule(const b3Vec3& p1, const b3Vec3& p2, scalar radius, const b3Color& color); - void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Mat33& rotation, const b3Color& color); + void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, scalar radius, const b3Quat& rotation, const b3Color& color); - void DrawPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color); + void DrawPlane(const b3Vec3& normal, const b3Vec3& center, scalar radius, const b3Color& color); - void DrawSolidPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color); + void DrawSolidPlane(const b3Vec3& normal, const b3Vec3& center, scalar radius, const b3Color& color); - void DrawAABB(const b3AABB3& aabb, const b3Color& color); + void DrawAABB(const b3AABB& aabb, const b3Color& color); void DrawTransform(const b3Transform& xf); diff --git a/examples/testbed/framework/draw_gl2.h b/examples/testbed/framework/draw_gl2.h index 16dd8fe..51c2151 100644 --- a/examples/testbed/framework/draw_gl2.h +++ b/examples/testbed/framework/draw_gl2.h @@ -166,7 +166,7 @@ struct DrawPoints glBufferData(GL_ARRAY_BUFFER, e_vertexCapacity * sizeof(b3Color), m_colors, GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[2]); - glBufferData(GL_ARRAY_BUFFER, e_vertexCapacity * sizeof(float32), m_sizes, GL_DYNAMIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, e_vertexCapacity * sizeof(scalar), m_sizes, GL_DYNAMIC_DRAW); AssertGL(); @@ -181,7 +181,7 @@ struct DrawPoints glDeleteBuffers(3, m_vboIds); } - void Vertex(const b3Vec3& v, float32 size, const b3Color& color) + void Vertex(const b3Vec3& v, scalar size, const b3Color& color) { if (m_count == e_vertexCapacity) { @@ -226,7 +226,7 @@ struct DrawPoints glVertexAttribPointer(m_colorAttribute, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[2]); - glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(float32), m_sizes); + glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(scalar), m_sizes); glEnableVertexAttribArray(m_sizeAttribute); glVertexAttribPointer(m_sizeAttribute, 1, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); @@ -255,7 +255,7 @@ struct DrawPoints b3Vec3 m_vertices[e_vertexCapacity]; b3Color m_colors[e_vertexCapacity]; - float32 m_sizes[e_vertexCapacity]; + scalar m_sizes[e_vertexCapacity]; u32 m_count; GLuint m_programId; @@ -365,7 +365,7 @@ struct DrawLines glDisableVertexAttribArray(m_colorAttribute); - glEnableVertexAttribArray(m_vertexAttribute); + glDisableVertexAttribArray(m_vertexAttribute); glBindBuffer(GL_ARRAY_BUFFER, 0); glUseProgram(0); @@ -608,7 +608,7 @@ struct DrawWire glDeleteProgram(m_programId); } - void DrawSphere(float32 radius, const b3Color& c, const b3Transform& xf) + void DrawSphere(scalar radius, const b3Color& c, const b3Transform& xf) { if (!g_glDrawLines) { @@ -777,7 +777,7 @@ struct DrawSolid { } - void DrawCylinder(float32 radius, float32 height, const b3Color& c, const b3Transform& xf) + void DrawCylinder(scalar radius, scalar height, const b3Color& c, const b3Transform& xf) { if (!g_glDrawTriangles) { @@ -820,7 +820,7 @@ struct DrawSolid glUseProgram(0); } - void DrawSphere(float32 radius, const b3Color& c, const b3Transform& xf) + void DrawSphere(scalar radius, const b3Color& c, const b3Transform& xf) { if (!g_glDrawTriangles) { @@ -874,4 +874,4 @@ struct DrawSolid DrawSolidCylinder m_cylinder; }; -#endif \ No newline at end of file +#endif diff --git a/examples/testbed/framework/draw_gl4.h b/examples/testbed/framework/draw_gl4.h index d276500..e0c990d 100644 --- a/examples/testbed/framework/draw_gl4.h +++ b/examples/testbed/framework/draw_gl4.h @@ -178,7 +178,7 @@ struct DrawPoints glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[2]); glVertexAttribPointer(m_sizeAttribute, 1, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); - glBufferData(GL_ARRAY_BUFFER, e_vertexCapacity * sizeof(float32), m_sizes, GL_DYNAMIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, e_vertexCapacity * sizeof(scalar), m_sizes, GL_DYNAMIC_DRAW); AssertGL(); @@ -195,7 +195,7 @@ struct DrawPoints glDeleteBuffers(3, m_vboIds); } - void Vertex(const b3Vec3& v, float32 size, const b3Color& color) + void Vertex(const b3Vec3& v, scalar size, const b3Color& color) { if (m_count == e_vertexCapacity) { @@ -238,7 +238,7 @@ struct DrawPoints glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b3Color), m_colors); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[2]); - glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(float32), m_sizes); + glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(scalar), m_sizes); glEnable(GL_PROGRAM_POINT_SIZE); glDrawArrays(GL_POINTS, 0, m_count); @@ -260,7 +260,7 @@ struct DrawPoints b3Vec3 m_vertices[e_vertexCapacity]; b3Color m_colors[e_vertexCapacity]; - float32 m_sizes[e_vertexCapacity]; + scalar m_sizes[e_vertexCapacity]; u32 m_count; GLuint m_programId; @@ -625,7 +625,7 @@ struct DrawWire glDeleteProgram(m_programId); } - void DrawSphere(float32 radius, const b3Color& c, const b3Transform& xf) + void DrawSphere(scalar radius, const b3Color& c, const b3Transform& xf) { if (!g_glDrawLines) { @@ -792,7 +792,7 @@ struct DrawSolid { } - void DrawCylinder(float32 radius, float32 height, const b3Color& c, const b3Transform& xf) + void DrawCylinder(scalar radius, scalar height, const b3Color& c, const b3Transform& xf) { if (!g_glDrawTriangles) { @@ -831,7 +831,7 @@ struct DrawSolid glUseProgram(0); } - void DrawSphere(float32 radius, const b3Color& c, const b3Transform& xf) + void DrawSphere(scalar radius, const b3Color& c, const b3Transform& xf) { if (!g_glDrawTriangles) { diff --git a/examples/testbed/framework/json_profiler.cpp b/examples/testbed/framework/json_profiler.cpp index bce7c98..06a5e71 100644 --- a/examples/testbed/framework/json_profiler.cpp +++ b/examples/testbed/framework/json_profiler.cpp @@ -77,7 +77,7 @@ void JsonProfiler::EndEvents() m_file = nullptr; } -void JsonProfiler::BeginEvent(const char* name, float64 t) +void JsonProfiler::BeginEvent(const char* name, scalar64 t) { if (!m_writer) { @@ -86,7 +86,7 @@ void JsonProfiler::BeginEvent(const char* name, float64 t) const char* phase = "B"; - float64 scale = 1000.0; + scalar64 scale = 1000.0; m_writer->StartObject(); m_writer->STRING("pid"); m_writer->Int(0); @@ -99,7 +99,7 @@ void JsonProfiler::BeginEvent(const char* name, float64 t) m_writer->EndObject(); } -void JsonProfiler::EndEvent(const char* name, float64 t) +void JsonProfiler::EndEvent(const char* name, scalar64 t) { if (!m_writer) { @@ -108,7 +108,7 @@ void JsonProfiler::EndEvent(const char* name, float64 t) const char* phase = "E"; - float64 scale = 1000.0; + scalar64 scale = 1000.0; m_writer->StartObject(); m_writer->STRING("pid"); m_writer->Int(0); diff --git a/examples/testbed/framework/json_profiler.h b/examples/testbed/framework/json_profiler.h index dc547a5..7db828c 100644 --- a/examples/testbed/framework/json_profiler.h +++ b/examples/testbed/framework/json_profiler.h @@ -43,9 +43,9 @@ public: void EndEvents(); - void BeginEvent(const char* name, float64 time); + void BeginEvent(const char* name, scalar64 time); - void EndEvent(const char* name, float64 time); + void EndEvent(const char* name, scalar64 time); private: FILE* m_file; FileWriteStream* m_stream; diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index 823170d..1ee852b 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -24,6 +24,8 @@ // error #endif +#include + #include #include @@ -103,14 +105,12 @@ static void Run() while (glfwWindowShouldClose(g_window) == 0) { + g_frameAllocator->Reset(); + g_profiler->Begin(); - - g_profilerSt->Begin(); g_profiler->BeginScope("Frame"); - g_profilerSt->BeginScope("Frame"); - g_view->BeginInterface(); if (g_model->IsPaused()) @@ -126,22 +126,13 @@ static void Run() g_model->Update(); - g_profilerSt->EndScope(); - g_profiler->EndScope(); - if (g_settings->drawProfileTree) + if (g_settings->drawProfiler) { - g_view->InterfaceProfileTree(); + g_view->InterfaceProfiler(); } - if (g_settings->drawProfileTreeStats) - { - g_view->InterfaceProfileTreeStats(); - } - - g_profilerSt->End(); - #if PROFILE_JSON == 1 g_model->UpdateJson(); #endif @@ -160,7 +151,7 @@ int main(int argc, char** args) #if defined(_WIN32) // Report memory leaks _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)); - //_CrtSetBreakAlloc(0); + //_CrtSetBreakAlloc(); #endif if (glfwInit() == 0) @@ -186,7 +177,7 @@ int main(int argc, char** args) glfwSwapInterval(1); glfwMakeContextCurrent(g_window); - + if (gladLoadGL() == 0) { fprintf(stderr, "Failed to load OpenGL extensions\n"); diff --git a/examples/testbed/framework/model.cpp b/examples/testbed/framework/model.cpp index ce522d8..d297101 100644 --- a/examples/testbed/framework/model.cpp +++ b/examples/testbed/framework/model.cpp @@ -20,14 +20,17 @@ #include #include +b3FrameAllocator* g_frameAllocator = nullptr; +b3Profiler* g_profiler = nullptr; + Model::Model() { m_viewModel = nullptr; g_draw = &m_draw; g_camera = &m_camera; g_profiler = &m_profiler; - g_profilerSt = &m_profilerSt; - + g_frameAllocator = &m_frame; + #if (PROFILE_JSON == 1) g_jsonProfiler = &m_jsonProfiler; #endif @@ -41,7 +44,7 @@ Model::Model() glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glClearColor(0.3f, 0.3f, 0.3f, 1.0f); + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); glClearDepth(1.0f); Action_ResetCamera(); @@ -56,7 +59,7 @@ Model::~Model() g_draw = nullptr; g_camera = nullptr; g_profiler = nullptr; - g_profilerSt = nullptr; + g_frameAllocator = nullptr; #if (PROFILE_JSON == 1) g_jsonProfiler = nullptr; @@ -114,11 +117,11 @@ void Model::Update() if (m_setTest) { + Action_ResetCamera(); delete m_test; m_test = g_tests[g_settings->testID].create(); m_setTest = false; m_pause = true; - Action_ResetCamera(); } if (g_settings->drawGrid) @@ -129,9 +132,9 @@ void Model::Update() b3Vec3 vs[h * w]; b3Vec3 t; - t.x = -0.5f * float32(w) + 0.5f; + t.x = -0.5f * scalar(w) + 0.5f; t.y = 0.0f; - t.z = -0.5f * float32(h) + 0.5f; + t.z = -0.5f * scalar(h) + 0.5f; for (u32 i = 0; i < h; ++i) { @@ -140,9 +143,9 @@ void Model::Update() u32 iv = i * w + j; b3Vec3 v; - v.x = float32(j); + v.x = scalar(j); v.y = 0.0f; - v.z = float32(i); + v.z = scalar(i); v += t; @@ -150,57 +153,55 @@ void Model::Update() } } - b3Color color(0.2f, 0.2f, 0.2f, 1.0f); + b3Color borderColor(0.0f, 0.0f, 0.0f, 1.0f); + b3Color centerColor(0.8f, 0.8f, 0.8f, 1.0f); + b3Color color(0.4f, 0.4f, 0.4f, 1.0f); - // Left-Right Lines - u32 hv1 = (h - 1) / 2 * w + 0; - u32 hv2 = (h - 1) / 2 * w + (w - 1); - { - b3Vec3 v1 = vs[hv1]; - b3Vec3 v2 = vs[hv2]; - - b3Draw_draw->DrawSegment(v1, v2, b3Color_black); - } - + // Left to right lines for (u32 i = 0; i < h; ++i) { - if (i == hv1) - { - continue; - } - u32 iv1 = i * w + 0; u32 iv2 = i * w + (w - 1); b3Vec3 v1 = vs[iv1]; b3Vec3 v2 = vs[iv2]; - b3Draw_draw->DrawSegment(v1, v2, color); - } - - // Up-Bottom Lines - u32 wv1 = 0 * w + (w - 1) / 2; - u32 wv2 = (h - 1) * w + (w - 1) / 2; - { - b3Vec3 v1 = vs[wv1]; - b3Vec3 v2 = vs[wv2]; - - b3Draw_draw->DrawSegment(v1, v2, b3Color_black); - } - - for (u32 j = 0; j < w; ++j) - { - if (j == wv1) + if (i == 0 || i == (h - 1)) { + b3Draw_draw->DrawSegment(v1, v2, borderColor); continue; } + if (i == (h - 1) / 2) + { + b3Draw_draw->DrawSegment(v1, v2, centerColor); + continue; + } + + b3Draw_draw->DrawSegment(v1, v2, color); + } + + // Up to bottom lines + for (u32 j = 0; j < w; ++j) + { u32 iv1 = 0 * w + j; u32 iv2 = (h - 1) * w + j; b3Vec3 v1 = vs[iv1]; b3Vec3 v2 = vs[iv2]; + if (j == 0 || j == (w - 1)) + { + b3Draw_draw->DrawSegment(v1, v2, borderColor); + continue; + } + + if (j == (w - 1) / 2) + { + b3Draw_draw->DrawSegment(v1, v2, centerColor); + continue; + } + b3Draw_draw->DrawSegment(v1, v2, color); } } @@ -233,15 +234,17 @@ void Model::Update() #if (PROFILE_JSON == 1) -static inline void RecurseEvents(ProfilerNode* node) +static inline void RecurseEvents(b3ProfilerNode* node) { g_jsonProfiler->BeginEvent(node->name, node->t0); g_jsonProfiler->EndEvent(node->name, node->t1); - for (u32 i = 0; i < node->children.Count(); ++i) + b3ProfilerNode* child = node->head; + while (child) { - RecurseEvents(node->children[i]); + RecurseEvents(child); + child = child->next; } } @@ -249,7 +252,7 @@ void Model::UpdateJson() { m_jsonProfiler.BeginEvents(); - ProfilerNode* root = m_profiler.GetRoot(); + b3ProfilerNode* root = m_profiler.GetRoot(); if (root) { diff --git a/examples/testbed/framework/model.h b/examples/testbed/framework/model.h index f586afa..e3835c8 100644 --- a/examples/testbed/framework/model.h +++ b/examples/testbed/framework/model.h @@ -20,8 +20,11 @@ #define MODEL_H #include -#include -#include +#include + +extern b3FrameAllocator* g_frameAllocator; + +extern b3Profiler* g_profiler; // Set to 1 to write profile events into a .json file. Set to 0 otherwise. #define PROFILE_JSON 0 @@ -53,12 +56,12 @@ public: void Command_Release_Mouse_Left(const b3Vec2& ps); void Command_Move_Cursor(const b3Vec2& ps); - void Command_ResizeCamera(float32 w, float32 h); - void Command_RotateCameraX(float32 angle); - void Command_RotateCameraY(float32 angle); - void Command_TranslateCameraX(float32 d); - void Command_TranslateCameraY(float32 d); - void Command_ZoomCamera(float32 d); + void Command_ResizeCamera(scalar w, scalar h); + void Command_RotateCameraX(scalar angle); + void Command_RotateCameraY(scalar angle); + void Command_TranslateCameraX(scalar d); + void Command_TranslateCameraY(scalar d); + void Command_ZoomCamera(scalar d); void Update(); @@ -74,8 +77,8 @@ private: Draw m_draw; Camera m_camera; - Profiler m_profiler; - ProfilerSt m_profilerSt; + b3FrameAllocator m_frame; + b3Profiler m_profiler; #if (PROFILE_JSON == 1) JsonProfiler m_jsonProfiler; @@ -115,13 +118,13 @@ inline void Model::Action_ResetCamera() m_camera.m_zoom = 50.0f; } -inline void Model::Command_ResizeCamera(float32 w, float32 h) +inline void Model::Command_ResizeCamera(scalar w, scalar h) { m_camera.m_width = w; m_camera.m_height = h; } -inline void Model::Command_RotateCameraX(float32 angle) +inline void Model::Command_RotateCameraX(scalar angle) { b3Quat d = b3QuatRotationX(angle); @@ -129,7 +132,7 @@ inline void Model::Command_RotateCameraX(float32 angle) m_camera.m_q.Normalize(); } -inline void Model::Command_RotateCameraY(float32 angle) +inline void Model::Command_RotateCameraY(scalar angle) { b3Quat d = b3QuatRotationY(angle); @@ -137,21 +140,21 @@ inline void Model::Command_RotateCameraY(float32 angle) m_camera.m_q.Normalize(); } -inline void Model::Command_TranslateCameraX(float32 d) +inline void Model::Command_TranslateCameraX(scalar d) { b3Transform transform = m_camera.BuildWorldTransform(); - m_camera.m_center += d * transform.rotation.x; + m_camera.m_center += d * transform.rotation.GetXAxis(); } -inline void Model::Command_TranslateCameraY(float32 d) +inline void Model::Command_TranslateCameraY(scalar d) { b3Transform transform = m_camera.BuildWorldTransform(); - m_camera.m_center += d * transform.rotation.y; + m_camera.m_center += d * transform.rotation.GetYAxis(); } -inline void Model::Command_ZoomCamera(float32 d) +inline void Model::Command_ZoomCamera(scalar d) { m_camera.m_zoom += d; } diff --git a/examples/testbed/framework/profiler.cpp b/examples/testbed/framework/profiler.cpp deleted file mode 100644 index fea9077..0000000 --- a/examples/testbed/framework/profiler.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#include - -Profiler* g_profiler = nullptr; - -Profiler::Profiler() : m_pool(sizeof(ProfilerNode)) -{ - m_root = nullptr; - m_top = nullptr; -} - -Profiler::~Profiler() -{ - assert(m_root == nullptr); - assert(m_top == nullptr); -} - -ProfilerNode* Profiler::CreateNode() -{ - void* block = m_pool.Allocate(); - ProfilerNode* n = new (block) ProfilerNode(); - return n; -} - -void Profiler::DestroyNode(ProfilerNode* node) -{ - node->~ProfilerNode(); - m_pool.Free(node); -} - -void Profiler::BeginScope(const char* name) -{ - m_time.Update(); - - ProfilerNode* n = CreateNode(); - n->name = name; - n->t0 = m_time.GetCurrentMilis(); - n->t1 = 0.0; - n->parent = m_top; - - if (m_root == nullptr) - { - m_root = n; - m_top = n; - return; - } - - if (m_top) - { - m_top->children.PushBack(n); - } - - m_top = n; -} - -void Profiler::EndScope() -{ - m_time.Update(); - - assert(m_top != nullptr); - m_top->t1 = m_time.GetCurrentMilis(); - assert(m_top->t1 > m_top->t0); - - m_top = m_top->parent; -} - -void Profiler::Begin() -{ - // If this assert is hit then it means Profiler::End hasn't been called. - assert(m_root == nullptr); - assert(m_top == nullptr); -} - -void Profiler::RecurseDestroyNode(ProfilerNode* node) -{ - for (u32 i = 0; i < node->children.Count(); ++i) - { - RecurseDestroyNode(node->children[i]); - } - - DestroyNode(node); -} - -void Profiler::End() -{ - assert(m_top == nullptr); - - if (m_root) - { - RecurseDestroyNode(m_root); - m_root = nullptr; - } -} \ No newline at end of file diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h deleted file mode 100644 index 070ae7e..0000000 --- a/examples/testbed/framework/profiler.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef PROFILER_H -#define PROFILER_H - -#include -#include -#include -#include - -// Profiler node -struct ProfilerNode -{ - const char* name; - float64 t0; - float64 t1; - ProfilerNode* parent; - b3StackArray children; -}; - -// A single-threaded profiler. -class Profiler -{ -public: - Profiler(); - - ~Profiler(); - - // Must be called before profiling. - void Begin(); - - // Must be called after profiling. - void End(); - - // Begin a new scope. - void BeginScope(const char* name); - - // End the top scope. - void EndScope(); - - // Get the root profiler node. - ProfilerNode* GetRoot() { return m_root; } -private: - ProfilerNode* CreateNode(); - void DestroyNode(ProfilerNode* node); - - void RecurseDestroyNode(ProfilerNode* node); - - b3BlockPool m_pool; // pool of nodes - b3Time m_time; // timer - ProfilerNode* m_root; // tree root node - ProfilerNode* m_top; // top node -}; - -extern Profiler* g_profiler; - -#endif \ No newline at end of file diff --git a/examples/testbed/framework/profiler_st.cpp b/examples/testbed/framework/profiler_st.cpp deleted file mode 100644 index 3866e84..0000000 --- a/examples/testbed/framework/profiler_st.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#include - -ProfilerSt* g_profilerSt = nullptr; - -ProfilerSt::ProfilerSt() : m_pool(sizeof(ProfilerStNode)) -{ - m_root = nullptr; - m_top = nullptr; -} - -ProfilerSt::~ProfilerSt() -{ - assert(m_root == nullptr); - assert(m_top == nullptr); -} - -ProfilerStNodeStat* ProfilerSt::FindStat(const char* name) -{ - for (u32 i = 0; i < m_stats.Count(); ++i) - { - if (m_stats[i].name == name) - { - return &m_stats[i]; - } - } - - return nullptr; -} - -ProfilerStNodeStat* ProfilerSt::CreateStat() -{ - m_stats.PushBack(ProfilerStNodeStat()); - return &m_stats.Back(); -} - -ProfilerStNode* ProfilerSt::CreateNode() -{ - void* block = m_pool.Allocate(); - return new (block) ProfilerStNode(); -} - -void ProfilerSt::DestroyNode(ProfilerStNode* node) -{ - node->~ProfilerStNode(); - m_pool.Free(node); -} - -void ProfilerSt::RecurseDestroyNode(ProfilerStNode* node) -{ - for (u32 i = 0; i < node->children.Count(); ++i) - { - return RecurseDestroyNode(node->children[i]); - } - - DestroyNode(node); -} - -static ProfilerStNode* RecurseFindNode(ProfilerStNode* node, const char* name) -{ - if (node->name == name) - { - return node; - } - - ProfilerStNode* result = nullptr; - for (u32 i = 0; result == nullptr && i < node->children.Count(); ++i) - { - result = RecurseFindNode(node->children[i], name); - } - - return result; -} - -ProfilerStNode* ProfilerSt::FindNode(const char* name) -{ - if (m_top) - { - return RecurseFindNode(m_top, name); - } - - if (m_root) - { - return RecurseFindNode(m_root, name); - } - - return nullptr; -} - -void ProfilerSt::BeginScope(const char* name) -{ - ProfilerStNode* fn = FindNode(name); - - if (fn) - { - m_time.Update(); - fn->t0 = m_time.GetCurrentMilis(); - ++fn->callCount; - m_top = fn; - return; - } - - m_time.Update(); - - ProfilerStNode* n = CreateNode(); - n->name = name; - n->t0 = m_time.GetCurrentMilis(); - n->elapsed = 0.0f; - n->callCount = 1; - n->parent = m_top; - n->stat = nullptr; - - if (m_root == nullptr) - { - m_root = n; - m_top = n; - - return; - } - - if (m_top) - { - m_top->children.PushBack(n); - } - - m_top = n; -} - -void ProfilerSt::EndScope() -{ - assert(m_top != nullptr); - - m_time.Update(); - - m_top->t1 = m_time.GetCurrentMilis(); - - float64 elapsedTime = m_top->t1 - m_top->t0; - - m_top->elapsed += elapsedTime; - - ProfilerStNodeStat* stat = FindStat(m_top->name); - - if (stat == nullptr) - { - stat = CreateStat(); - stat->name = m_top->name; - stat->minElapsed = elapsedTime; - stat->maxElapsed = elapsedTime; - } - else - { - stat->minElapsed = b3Min(stat->minElapsed, elapsedTime); - stat->maxElapsed = b3Max(stat->maxElapsed, elapsedTime); - } - - if (m_top->stat == nullptr) - { - m_top->stat = stat; - } - - assert(m_top->stat == stat); - - m_top = m_top->parent; -} - -void ProfilerSt::Begin() -{ - assert(m_top == nullptr); -} - -void ProfilerSt::End() -{ - assert(m_top == nullptr); - - if (m_root) - { - RecurseDestroyNode(m_root); - m_root = nullptr; - } -} \ No newline at end of file diff --git a/examples/testbed/framework/profiler_st.h b/examples/testbed/framework/profiler_st.h deleted file mode 100644 index 9cb1290..0000000 --- a/examples/testbed/framework/profiler_st.h +++ /dev/null @@ -1,94 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef PROFILER_ST_H -#define PROFILER_ST_H - -#include -#include -#include -#include - -// Profiler tree node statistics -struct ProfilerStNodeStat -{ - const char* name; - float64 minElapsed; - float64 maxElapsed; -}; - -// A profiler tree node -struct ProfilerStNode -{ - const char* name; - float64 t0; - float64 t1; - - float64 elapsed; - u32 callCount; - - ProfilerStNode* parent; - b3StackArray children; - - ProfilerStNodeStat* stat; -}; - -// A profiler tree -class ProfilerSt -{ -public: - ProfilerSt(); - - ~ProfilerSt(); - - // Must be called before profiling. - void Begin(); - - // Must be called after profiling. - void End(); - - // Begin a new scope. - void BeginScope(const char* name); - - // End the top scope. - void EndScope(); - - ProfilerStNode* GetRoot() { return m_root; } -private: - ProfilerStNode* CreateNode(); - void DestroyNode(ProfilerStNode* node); - - void RecurseDestroyNode(ProfilerStNode* node); - - ProfilerStNode* FindNode(const char* name); - - ProfilerStNodeStat* CreateStat(); - - ProfilerStNodeStat* FindStat(const char* name); - - b3BlockPool m_pool; // pool of nodes - b3Time m_time; // timer - ProfilerStNode* m_root; // tree root node - ProfilerStNode* m_top; // top node - - b3StackArray m_stats; // node statistics -}; - -extern ProfilerSt* g_profilerSt; - -#endif \ No newline at end of file diff --git a/examples/testbed/framework/softbody_dragger.cpp b/examples/testbed/framework/softbody_dragger.cpp index 1667c2e..c513a7a 100644 --- a/examples/testbed/framework/softbody_dragger.cpp +++ b/examples/testbed/framework/softbody_dragger.cpp @@ -22,7 +22,7 @@ b3SoftBodyDragger::b3SoftBodyDragger(b3Ray3* ray, b3SoftBody* body) { m_ray = ray; m_body = body; - m_tetrahedron = nullptr; + m_isDragging = false; } b3SoftBodyDragger::~b3SoftBodyDragger() @@ -40,41 +40,45 @@ bool b3SoftBodyDragger::StartDragging() return false; } - m_mesh = m_body->GetMesh(); - m_tetrahedron = m_mesh->tetrahedrons + rayOut.tetrahedron; - m_v1 = m_tetrahedron->v1; - m_v2 = m_tetrahedron->v2; - m_v3 = m_tetrahedron->v3; - m_v4 = m_tetrahedron->v4; + m_isDragging = true; m_x = rayOut.fraction; - b3SoftBodyNode* n1 = m_body->GetVertexNode(m_v1); - b3SoftBodyNode* n2 = m_body->GetVertexNode(m_v2); - b3SoftBodyNode* n3 = m_body->GetVertexNode(m_v3); - b3SoftBodyNode* n4 = m_body->GetVertexNode(m_v4); + const b3SoftBodyMesh* mesh = m_body->GetMesh(); + const b3SoftBodyMeshTriangle* triangle = mesh->triangles + rayOut.triangle; - b3Vec3 v1 = n1->GetPosition(); - b3Vec3 v2 = n2->GetPosition(); - b3Vec3 v3 = n3->GetPosition(); - b3Vec3 v4 = n4->GetPosition(); + m_n1 = m_body->GetNode(triangle->v1); + m_n2 = m_body->GetNode(triangle->v2); + m_n3 = m_body->GetNode(triangle->v3); + + b3Vec3 v1 = m_n1->GetPosition(); + b3Vec3 v2 = m_n2->GetPosition(); + b3Vec3 v3 = m_n3->GetPosition(); b3Vec3 B = GetPointB(); - float32 wABCD[5]; - b3BarycentricCoordinates(wABCD, v1, v2, v3, v4, B); + scalar wABC[4]; + b3BarycentricCoordinates(wABC, v1, v2, v3, B); - if (wABCD[4] > B3_EPSILON) + if (wABC[3] > B3_EPSILON) { - m_tu = wABCD[0] / wABCD[4]; - m_tv = wABCD[1] / wABCD[4]; - m_tw = wABCD[2] / wABCD[4]; - m_tx = wABCD[3] / wABCD[4]; + m_tu = wABC[0] / wABC[3]; + m_tv = wABC[1] / wABC[3]; + m_tw = wABC[2] / wABC[3]; } else { - m_tu = m_tv = m_tw = m_tx = 0.0f; + m_tu = m_tv = m_tw = 0.0f; } + m_t1 = m_n1->GetType(); + m_n1->SetType(e_staticSoftBodyNode); + + m_t2 = m_n2->GetType(); + m_n2->SetType(e_staticSoftBodyNode); + + m_t3 = m_n3->GetType(); + m_n3->SetType(e_staticSoftBodyNode); + return true; } @@ -87,38 +91,31 @@ void b3SoftBodyDragger::Drag() b3Vec3 dx = B - A; - const float32 k = 100.0f; - - b3Vec3 f = k * dx; - - b3Vec3 f1 = m_tu * f; - b3Vec3 f2 = m_tv * f; - b3Vec3 f3 = m_tw * f; - b3Vec3 f4 = m_tx * f; - - m_body->GetVertexNode(m_v1)->ApplyForce(f1); - m_body->GetVertexNode(m_v2)->ApplyForce(f2); - m_body->GetVertexNode(m_v3)->ApplyForce(f3); - m_body->GetVertexNode(m_v4)->ApplyForce(f4); + m_n1->ApplyTranslation(dx); + m_n2->ApplyTranslation(dx); + m_n3->ApplyTranslation(dx); } void b3SoftBodyDragger::StopDragging() { B3_ASSERT(IsDragging() == true); - m_tetrahedron = nullptr; + m_n1->SetType(m_t1); + m_n2->SetType(m_t2); + m_n3->SetType(m_t3); + + m_isDragging = false; } b3Vec3 b3SoftBodyDragger::GetPointA() const { B3_ASSERT(IsDragging() == true); - b3Vec3 A = m_body->GetVertexNode(m_v1)->GetPosition(); - b3Vec3 B = m_body->GetVertexNode(m_v2)->GetPosition(); - b3Vec3 C = m_body->GetVertexNode(m_v3)->GetPosition(); - b3Vec3 D = m_body->GetVertexNode(m_v4)->GetPosition(); + b3Vec3 A = m_n1->GetPosition() + m_n1->GetTranslation(); + b3Vec3 B = m_n2->GetPosition() + m_n2->GetTranslation(); + b3Vec3 C = m_n3->GetPosition() + m_n3->GetTranslation(); - return m_tu * A + m_tv * B + m_tw * C + m_tx * D; + return m_tu * A + m_tv * B + m_tw * C; } b3Vec3 b3SoftBodyDragger::GetPointB() const diff --git a/examples/testbed/framework/softbody_dragger.h b/examples/testbed/framework/softbody_dragger.h index e8a6a92..5c40181 100644 --- a/examples/testbed/framework/softbody_dragger.h +++ b/examples/testbed/framework/softbody_dragger.h @@ -44,18 +44,18 @@ public: b3Vec3 GetPointB() const; private: b3Ray3* m_ray; - float32 m_x; - b3SoftBody* m_body; - const b3SoftBodyMesh* m_mesh; - const b3SoftBodyMeshTetrahedron* m_tetrahedron; - u32 m_v1, m_v2, m_v3, m_v4; - float32 m_tu, m_tv, m_tw, m_tx; + + bool m_isDragging; + scalar m_x; + scalar m_tu, m_tv, m_tw; + b3SoftBodyNode * m_n1, * m_n2, * m_n3; + b3SoftBodyNodeType m_t1, m_t2, m_t3; }; inline bool b3SoftBodyDragger::IsDragging() const { - return m_tetrahedron != nullptr; + return m_isDragging; } #endif \ No newline at end of file diff --git a/examples/testbed/framework/test.cpp b/examples/testbed/framework/test.cpp index fe3ff77..9cd5f81 100644 --- a/examples/testbed/framework/test.cpp +++ b/examples/testbed/framework/test.cpp @@ -17,8 +17,10 @@ */ #include -#include -#include +#include + +extern b3FrameAllocator* g_frameAllocator; +extern b3Profiler* g_profiler; extern u32 b3_allocCalls, b3_maxAllocCalls; extern u32 b3_convexCalls, b3_convexCacheHits; @@ -28,19 +30,18 @@ extern bool b3_convexCache; void b3BeginProfileScope(const char* name) { g_profiler->BeginScope(name); - g_profilerSt->BeginScope(name); } void b3EndProfileScope() { g_profiler->EndScope(); - g_profilerSt->EndScope(); } Test::Test() : m_bodyDragger(&m_ray, &m_world) { b3Draw_draw = g_draw; + b3FrameAllocator_sparseAllocator = g_frameAllocator; b3_convexCache = g_testSettings->convexCache; m_world.SetContactListener(this); @@ -49,13 +50,16 @@ Test::Test() : m_ray.direction.Set(0.0f, 0.0f, -1.0f); m_ray.fraction = g_camera->m_zFar; - m_groundHull.Set(50.0f, 1.0f, 50.0f); + m_groundHull.SetExtents(50.0f, 1.0f, 50.0f); + m_groundMesh.BuildTree(); + m_groundMesh.BuildAdjacency(); } Test::~Test() { b3Draw_draw = nullptr; + b3FrameAllocator_sparseAllocator = nullptr; } void Test::Step() @@ -63,7 +67,7 @@ void Test::Step() b3_convexCache = g_testSettings->convexCache; // Step - float32 dt = g_testSettings->inv_hertz; + scalar dt = g_testSettings->inv_hertz; m_world.SetSleeping(g_testSettings->sleep); m_world.SetWarmStart(g_testSettings->warmStart); @@ -97,19 +101,19 @@ void Test::Step() g_draw->DrawString(b3Color_white, "Joints %d", m_world.GetJointList().m_count); g_draw->DrawString(b3Color_white, "Contacts %d", m_world.GetContactList().m_count); - float32 avgGjkIters = 0.0f; + scalar avgGjkIters = 0.0f; if (b3_gjkCalls > 0) { - avgGjkIters = float32(b3_gjkIters) / float32(b3_gjkCalls); + avgGjkIters = scalar(b3_gjkIters) / scalar(b3_gjkCalls); } g_draw->DrawString(b3Color_white, "GJK Calls %d", b3_gjkCalls); g_draw->DrawString(b3Color_white, "GJK Iterations %d (%d) (%f)", b3_gjkIters, b3_gjkMaxIters, avgGjkIters); - float32 convexCacheHitRatio = 0.0f; + scalar convexCacheHitRatio = 0.0f; if (b3_convexCalls > 0) { - convexCacheHitRatio = float32(b3_convexCacheHits) / float32(b3_convexCalls); + convexCacheHitRatio = scalar(b3_convexCacheHits) / scalar(b3_convexCalls); } g_draw->DrawString(b3Color_white, "Convex Calls %d", b3_convexCalls); diff --git a/examples/testbed/framework/test.h b/examples/testbed/framework/test.h index 8e21ca7..ff34a90 100644 --- a/examples/testbed/framework/test.h +++ b/examples/testbed/framework/test.h @@ -24,15 +24,16 @@ #include #include +#include #include #include -inline float32 RandomFloat(float32 a, float32 b) +inline float RandomFloat(scalar a, scalar b) { - float32 x = float32(rand()) / float32(RAND_MAX); - float32 diff = b - a; - float32 r = x * diff; + float x = float(rand()) / float(RAND_MAX); + float diff = b - a; + float r = x * diff; return a + r; } diff --git a/examples/testbed/framework/test_entries.cpp b/examples/testbed/framework/test_entries.cpp index 9224561..f0c7605 100644 --- a/examples/testbed/framework/test_entries.cpp +++ b/examples/testbed/framework/test_entries.cpp @@ -20,12 +20,13 @@ #include #include #include -#include +#include +#include +#include #include #include #include #include -#include #include #include #include @@ -35,14 +36,18 @@ #include #include #include -#include +#include +#include #include #include -#include +#include +#include +#include #include #include #include #include +#include #include #include #include @@ -53,46 +58,61 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include +#include #include #include #include #include +#include +#include +#include #include #include +#include +#include #include +#include #include +#include TestEntry g_tests[] = { { "Convex Hull", &ConvexHull::Create }, { "Cluster", &Cluster::Create }, { "Distance", &Distance::Create }, - { "Shape Cast", &ShapeCast::Create }, + { "Linear Time of Impact", &LinearTimeOfImpact::Create }, + { "Time of Impact", &TimeOfImpact::Create }, + { "AABB Time of Impact", &AABBTimeOfImpact::Create }, { "Capsule Collision", &CapsuleCollision::Create }, { "Hull Collision", &HullCollision::Create }, { "Deep Capsule", &DeepCapsule::Create }, - { "Degenerate Capsule", &DegenerateCapsule::Create }, { "Box Face Contact", &BoxFaceContact::Create }, { "Box Edge Contact", &BoxEdgeContact::Create }, { "Capsule Spin", &CapsuleSpin::Create }, { "Hull Contact Test", &HullContactTest::Create }, + { "Triangle Contact Test", &TriangleContactTest::Create }, { "Mesh Contact Test", &MeshContactTest::Create }, { "Linear Motion", &LinearMotion::Create }, { "Angular Motion", &AngularMotion::Create }, { "Gyroscopic Motion", &GyroMotion::Create }, { "Compound Body", &CompoundBody::Create }, { "Quadric Shapes", &QuadricShapes::Create }, - { "Springs", &Spring::Create }, + { "Spring Test", &SpringTest::Create }, + { "Prismatic Test", &PrismaticTest::Create }, + { "Wheel Test", &WheelTest::Create }, { "Weld Test", &WeldTest::Create }, { "Cone Test", &ConeTest::Create }, - { "Hinge Motor", &HingeMotor::Create }, + { "Motor Test", &MotorTest::Create }, + { "Revolute Test", &RevoluteTest::Create }, { "Hinge Chain", &HingeChain::Create }, { "Ragdoll", &Ragdoll::Create }, { "Newton's Cradle", &NewtonCradle::Create }, @@ -105,6 +125,7 @@ TestEntry g_tests[] = { "Box Pyramid", &Pyramid::Create }, { "Box Pyramid Rows", &Pyramids::Create }, { "Ray Cast", &RayCast::Create }, + { "Convex Cast", &ConvexCast::Create }, { "Sensor Test", &SensorTest::Create }, { "Body Types", &BodyTypes::Create }, { "Varying Friction", &VaryingFriction::Create }, @@ -112,14 +133,23 @@ TestEntry g_tests[] = { "Tumbler", &Tumbler::Create }, { "Initial Overlap", &InitialOverlap::Create }, { "Multiple Pendulum", &MultiplePendulum::Create }, + { "Conveyor Belt", &ConveyorBelt::Create }, { "Table Cloth", &TableCloth::Create }, + { "Cloth SDF", &ClothSDF::Create }, { "Pinned Cloth", &PinnedCloth::Create }, { "Particle Types", &ParticleTypes::Create }, { "Tension Mapping", &TensionMapping::Create }, { "Cloth Self-Collision", &ClothSelfCollision::Create }, + { "Cloth Tearing", &ClothTearing::Create }, + { "Cloth Element Test", &ClothElementTest::Create }, + { "Cape", &Cape::Create }, { "Beam", &Beam::Create }, + { "Sheet", &Sheet::Create }, + { "Node Types", &NodeTypes::Create }, { "Pinned Soft Body", &PinnedSoftBody::Create }, + { "Soft Body Anchor", &SoftBodyAnchor::Create }, { "Smash Soft Body", &SmashSoftBody::Create }, + { "TetGen Soft Body", &TetGenSoftBody::Create }, { "Rope", &Rope::Create }, { NULL, NULL } }; diff --git a/examples/testbed/framework/view.cpp b/examples/testbed/framework/view.cpp index 0f4cffb..e6b79cf 100644 --- a/examples/testbed/framework/view.cpp +++ b/examples/testbed/framework/view.cpp @@ -19,14 +19,18 @@ #include #include #include -#include -#include +#include + +extern b3Profiler* g_profiler; #include + +#include + #if defined (U_OPENGL_2) -#include +#include #elif defined (U_OPENGL_4) -#include +#include #else #endif @@ -39,16 +43,15 @@ static inline bool GetTestName(void* userData, int idx, const char** name) return true; } -static inline bool ImGui_GLFW_GL_Init(GLFWwindow* w, bool install_callbacks) +static inline bool ImGui_OpenGL_Init() { - #if defined(U_OPENGL_2) - return ImGui_ImplGlfwGL2_Init(w, install_callbacks); + return ImGui_ImplOpenGL2_Init(); #elif defined(U_OPENGL_4) - return ImGui_ImplGlfwGL3_Init(w, install_callbacks); + return ImGui_ImplOpenGL3_Init(); #else @@ -56,16 +59,16 @@ static inline bool ImGui_GLFW_GL_Init(GLFWwindow* w, bool install_callbacks) return false; } -static inline void ImGui_GLFW_GL_Shutdown() +static inline void ImGui_OpenGL_Shutdown() { #if defined(U_OPENGL_2) - ImGui_ImplGlfwGL2_Shutdown(); + ImGui_ImplOpenGL2_Shutdown(); #elif defined(U_OPENGL_4) - ImGui_ImplGlfwGL3_Shutdown(); + ImGui_ImplOpenGL3_Shutdown(); #else @@ -75,16 +78,16 @@ static inline void ImGui_GLFW_GL_Shutdown() } -static inline void ImGui_GLFW_GL_NewFrame() +static inline void ImGui_OpenGL_NewFrame() { #if defined(U_OPENGL_2) - ImGui_ImplGlfwGL2_NewFrame(); + ImGui_ImplOpenGL2_NewFrame(); #elif defined(U_OPENGL_4) - ImGui_ImplGlfwGL3_NewFrame(); + ImGui_ImplOpenGL3_NewFrame(); #else @@ -94,16 +97,16 @@ static inline void ImGui_GLFW_GL_NewFrame() } -static inline void ImGui_GLFW_GL_RenderDrawData(ImDrawData* draw_data) +static inline void ImGui_OpenGL_RenderDrawData(ImDrawData* draw_data) { #if defined(U_OPENGL_2) - ImGui_ImplGlfwGL2_RenderDrawData(draw_data); + ImGui_ImplOpenGL2_RenderDrawData(draw_data); #elif defined(U_OPENGL_4) - ImGui_ImplGlfwGL3_RenderDrawData(draw_data); + ImGui_ImplOpenGL3_RenderDrawData(draw_data); #else @@ -124,9 +127,9 @@ View::View(GLFWwindow* window) ImGuiIO& io = ImGui::GetIO(); io.IniFilename = NULL; - io.Fonts[0].AddFontDefault(); - ImGui_GLFW_GL_Init(m_window, false); + ImGui_ImplGlfw_InitForOpenGL(m_window, false); + ImGui_OpenGL_Init(); ImGui::StyleColorsDark(); @@ -136,7 +139,8 @@ View::View(GLFWwindow* window) View::~View() { // Destroy UI - ImGui_GLFW_GL_Shutdown(); + ImGui_OpenGL_Shutdown(); + ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); } @@ -145,7 +149,7 @@ b3Vec2 View::GetCursorPosition() const { double x, y; glfwGetCursorPos(m_window, &x, &y); - return b3Vec2(float32(x), float32(y)); + return b3Vec2(scalar(x), scalar(y)); } void View::Event_SetWindowSize(int w, int h) @@ -186,7 +190,9 @@ void View::Event_Scroll(float dx, float dy) void View::BeginInterface() { - ImGui_GLFW_GL_NewFrame(); + ImGui_OpenGL_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); } @@ -219,9 +225,8 @@ void View::Interface() if (ImGui::BeginMenu("View")) { - ImGui::MenuItem("Profile Tree", "", &settings.drawProfileTree); - ImGui::MenuItem("Profile Tree Statistics", "", &settings.drawProfileTreeStats); ImGui::MenuItem("Statistics", "", &settings.drawStats); + ImGui::MenuItem("Profiler", "", &settings.drawProfiler); ImGui::Separator(); @@ -403,54 +408,7 @@ void View::Interface() ImGui::End(); } -static void TreeNode(ProfilerNode* node, u32& index) -{ - ImGui::PushID(index); - ++index; - - if (ImGui::TreeNode(node->name)) - { - float64 elapsedTime = node->t1 - node->t0; - ImGui::Text("%.4f [ms]", elapsedTime); - - for (u32 i = 0; i < node->children.Count(); ++i) - { - TreeNode(node->children[i], index); - } - ImGui::TreePop(); - } - - ImGui::PopID(); -} - -void View::InterfaceProfileTree() -{ - ImGui::Begin("Overlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar); - ImVec2 ws = ImGui::GetWindowSize(); - ImVec2 wp = ImGui::GetWindowPos(); - ImGui::End(); - - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - - ImGui::SetNextWindowBgAlpha(0.0f); - ImGui::SetNextWindowPos(ImVec2(0.0f, wp.y + ws.y)); - ImGui::SetNextWindowSize(ImVec2(g_camera->m_width - 250.0f, 0.0f)); - - ImGui::Begin("Profile Tree", NULL, ImGuiWindowFlags_AlwaysAutoResize); - - ProfilerNode* root = g_profiler->GetRoot(); - if (root) - { - u32 index = 0; - TreeNode(root, index); - } - - ImGui::End(); - - ImGui::PopStyleVar(); -} - -static void TreeNode(ProfilerStNode* node, u32& index) +static void TreeNode(b3ProfilerNode* node, u32& index) { ImGui::PushID(index); ++index; @@ -459,9 +417,11 @@ static void TreeNode(ProfilerStNode* node, u32& index) { ImGui::Text("%.4f (min = %.4f) (max = %.4f) (calls = %d) [ms]", node->elapsed, node->stat->minElapsed, node->stat->maxElapsed, node->callCount); - for (u32 i = 0; i < node->children.Count(); ++i) + b3ProfilerNode* n = node->head; + while(n) { - TreeNode(node->children[i], index); + TreeNode(n, index); + n = n->next; } ImGui::TreePop(); } @@ -469,7 +429,7 @@ static void TreeNode(ProfilerStNode* node, u32& index) ImGui::PopID(); } -void View::InterfaceProfileTreeStats() +void View::InterfaceProfiler() { ImGui::Begin("Overlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar); ImVec2 wp = ImGui::GetWindowPos(); @@ -478,25 +438,15 @@ void View::InterfaceProfileTreeStats() wp.y = wp.y + ws.y; - if (g_settings->drawProfileTree) - { - ImGui::Begin("Profile Tree", NULL, ImGuiWindowFlags_AlwaysAutoResize); - ImVec2 ptwp = ImGui::GetWindowPos(); - ImVec2 ptws = ImGui::GetWindowSize(); - ImGui::End(); - - wp.y = ptwp.y + ptws.y; - } - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::SetNextWindowBgAlpha(0.0f); ImGui::SetNextWindowPos(ImVec2(0.0f, wp.y)); ImGui::SetNextWindowSize(ImVec2(g_camera->m_width - 250.0f, 0.0f)); - ImGui::Begin("Profile Tree Statistics", NULL, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Begin("Profiler", NULL, ImGuiWindowFlags_AlwaysAutoResize); - ProfilerStNode* root = g_profilerSt->GetRoot(); + b3ProfilerNode* root = g_profiler->GetRoot(); if (root) { u32 index = 0; @@ -514,5 +464,5 @@ void View::EndInterface() ImGui::Render(); - ImGui_GLFW_GL_RenderDrawData(ImGui::GetDrawData()); + ImGui_OpenGL_RenderDrawData(ImGui::GetDrawData()); } \ No newline at end of file diff --git a/examples/testbed/framework/view.h b/examples/testbed/framework/view.h index 3851e0c..db19ff2 100644 --- a/examples/testbed/framework/view.h +++ b/examples/testbed/framework/view.h @@ -41,8 +41,7 @@ public: void BeginInterface(); void Interface(); - void InterfaceProfileTree(); - void InterfaceProfileTreeStats(); + void InterfaceProfiler(); void EndInterface(); private: friend class ViewModel; diff --git a/examples/testbed/framework/view_model.cpp b/examples/testbed/framework/view_model.cpp index ffb3372..ca53bb5 100644 --- a/examples/testbed/framework/view_model.cpp +++ b/examples/testbed/framework/view_model.cpp @@ -84,7 +84,7 @@ void ViewModel::Action_ResetCamera() void ViewModel::Event_SetWindowSize(int w, int h) { - m_model->Command_ResizeCamera(float32(w), float32(h)); + m_model->Command_ResizeCamera(scalar(w), scalar(h)); } void ViewModel::Event_Press_Key(int button) @@ -148,8 +148,7 @@ void ViewModel::Event_Move_Cursor(float x, float y) b3Vec2 dp = ps - m_view->m_ps0; - float32 ndx = b3Clamp(dp.x, -1.0f, 1.0f); - float32 ndy = b3Clamp(dp.y, -1.0f, 1.0f); + b3Vec2 n = b3Normalize(dp); bool shiftDown = glfwGetKey(m_view->m_window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS; bool leftDown = glfwGetMouseButton(m_view->m_window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS; @@ -159,8 +158,8 @@ void ViewModel::Event_Move_Cursor(float x, float y) { if (leftDown) { - float32 ax = -0.005f * B3_PI * ndx; - float32 ay = -0.005f * B3_PI * ndy; + scalar ax = -0.005f * B3_PI * n.x; + scalar ay = -0.005f * B3_PI * n.y; m_model->Command_RotateCameraY(ax); m_model->Command_RotateCameraX(ay); @@ -168,8 +167,8 @@ void ViewModel::Event_Move_Cursor(float x, float y) if (rightDown) { - float32 tx = 0.2f * ndx; - float32 ty = -0.2f * ndy; + scalar tx = 0.2f * n.x; + scalar ty = -0.2f * n.y; m_model->Command_TranslateCameraX(tx); m_model->Command_TranslateCameraY(ty); @@ -177,16 +176,18 @@ void ViewModel::Event_Move_Cursor(float x, float y) } else { - m_model->Command_Move_Cursor(m_view->GetCursorPosition()); + m_model->Command_Move_Cursor(ps); } } void ViewModel::Event_Scroll(float dx, float dy) { + b3Vec2 n(dx, dy); + n.Normalize(); + bool shiftDown = glfwGetKey(m_view->m_window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS; if (shiftDown) { - float32 ny = b3Clamp(dy, -1.0f, 1.0f); - m_model->Command_ZoomCamera(1.0f * ny); + m_model->Command_ZoomCamera(1.0f * n.y); } } diff --git a/examples/testbed/framework/view_model.h b/examples/testbed/framework/view_model.h index 09f46ab..7137e92 100644 --- a/examples/testbed/framework/view_model.h +++ b/examples/testbed/framework/view_model.h @@ -32,9 +32,8 @@ struct Settings drawLines = true; drawTriangles = true; drawGrid = true; - drawProfileTree = false; - drawProfileTreeStats = false; drawStats = false; + drawProfiler = false; } int testID; @@ -43,9 +42,8 @@ struct Settings bool drawLines; bool drawTriangles; bool drawGrid; - bool drawProfileTree; - bool drawProfileTreeStats; bool drawStats; + bool drawProfiler; }; // diff --git a/examples/testbed/tests/aabb_time_of_impact.h b/examples/testbed/tests/aabb_time_of_impact.h new file mode 100644 index 0000000..e05fdeb --- /dev/null +++ b/examples/testbed/tests/aabb_time_of_impact.h @@ -0,0 +1,178 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef AABB_TIME_OF_IMPACT_H +#define AABB_TIME_OF_IMPACT_H + +class AABBTimeOfImpact : public Test +{ +public: + AABBTimeOfImpact() + { + b3Vec3 cA = b3Vec3_zero; + b3Vec3 eA(1.0f, 1.0f, 1.0f); + m_aabbA.Set(cA, eA); + + b3Vec3 cB(-2.0f, -2.0f, 0.0f); + b3Vec3 eB(1.0f, 1.0f, 1.0f); + m_aabbB.Set(cB, eB); + + m_dB.Set(1.0f, 1.0f, 0.0f); + + m_time = 0.0f; + } + + void Step() + { + g_draw->DrawString(b3Color_white, "Arrows - Translate AABB"); + + g_draw->DrawAABB(m_aabbA, b3Color_white); + g_draw->DrawAABB(m_aabbB, b3Color_white); + + b3Vec3 cA = m_aabbA.GetCenter(); + b3Vec3 cB = m_aabbB.GetCenter(); + + b3Vec3 eA = m_aabbA.GetExtents(); + b3Vec3 eB = m_aabbB.GetExtents(); + + b3Vec3 dA = b3Vec3_zero; + b3Vec3 dB = m_dB; + + g_draw->DrawSegment(cA, cA + dA, b3Color_white); + g_draw->DrawSegment(cB, cB + dB, b3Color_white); + + { + b3Vec3 cBt = cB + m_time * dB; + + b3AABB B; + B.Set(cBt, eB); + + g_draw->DrawAABB(B, b3Color_red); + } + + b3TOIOutput out = b3TimeOfImpact(m_aabbA, dA, m_aabbB, dB); + + b3TOIOutput::State state = out.state; + scalar t = out.t; + + if (state == b3TOIOutput::e_touching) + { + b3Vec3 cAt = cA + t * dA; + b3Vec3 cBt = cB + t * dB; + + b3AABB A; + A.Set(cAt, eA); + + b3AABB B; + B.Set(cBt, eB); + + g_draw->DrawAABB(A, b3Color_black); + g_draw->DrawAABB(B, b3Color_black); + } + + if (state == b3TOIOutput::e_failed) + { + g_draw->DrawString(b3Color_white, "State = Failed"); + } + else if (state == b3TOIOutput::e_overlapped) + { + g_draw->DrawString(b3Color_white, "State = Overlapped"); + } + else if (state == b3TOIOutput::e_separated) + { + g_draw->DrawString(b3Color_white, "State = Separated!"); + } + else if (state == b3TOIOutput::e_touching) + { + g_draw->DrawString(b3Color_white, "State = Touching!"); + } + } + + void KeyDown(int key) + { + const scalar dt = 0.01f; + const scalar d = 0.1f; + + if (key == GLFW_KEY_F) + { + m_time += dt; + if (m_time > 1.0f) + { + m_time = 0.0f; + } + } + + if (key == GLFW_KEY_B) + { + m_time -= dt; + if (m_time < 0.0f) + { + m_time = 1.0f; + } + } + + if (key == GLFW_KEY_LEFT) + { + m_aabbB.lowerBound.x -= d; + m_aabbB.upperBound.x -= d; + } + + if (key == GLFW_KEY_RIGHT) + { + m_aabbB.lowerBound.x += d; + m_aabbB.upperBound.x += d; + } + + if (key == GLFW_KEY_UP) + { + m_aabbB.lowerBound.y += d; + m_aabbB.upperBound.y += d; + } + + if (key == GLFW_KEY_DOWN) + { + m_aabbB.lowerBound.y -= d; + m_aabbB.upperBound.y -= d; + } + + if (key == GLFW_KEY_W) + { + m_aabbB.lowerBound.z += d; + m_aabbB.upperBound.z += d; + } + + if (key == GLFW_KEY_S) + { + m_aabbB.lowerBound.z -= d; + m_aabbB.upperBound.z -= d; + } + } + + static Test* Create() + { + return new AABBTimeOfImpact(); + } + + scalar m_time; + + b3AABB m_aabbA; + b3AABB m_aabbB; + b3Vec3 m_dB; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/angular_motion.h b/examples/testbed/tests/angular_motion.h index ae3578e..f10a51f 100644 --- a/examples/testbed/tests/angular_motion.h +++ b/examples/testbed/tests/angular_motion.h @@ -32,8 +32,8 @@ public: b3Body* body = m_world.CreateBody(bd); b3CapsuleShape shape; - shape.m_centers[0].Set(0.0f, 0.0f, -1.0f); - shape.m_centers[1].Set(0.0f, 0.0f, 1.0f); + shape.m_vertex1.Set(0.0f, 0.0f, -1.0f); + shape.m_vertex2.Set(0.0f, 0.0f, 1.0f); shape.m_radius = 1.0f; b3ShapeDef sdef; diff --git a/examples/testbed/tests/beam.h b/examples/testbed/tests/beam.h index 6a0a913..af40c43 100644 --- a/examples/testbed/tests/beam.h +++ b/examples/testbed/tests/beam.h @@ -19,33 +19,34 @@ #ifndef BEAM_H #define BEAM_H -#include - class Beam : public Test { public: + enum + { + e_w = 5, + e_h = 2, + e_d = 2 + }; + Beam() { + m_E0 = 1000.0f; + m_E = m_E0; + // Create soft body b3SoftBodyDef def; def.mesh = &m_mesh; def.density = 0.2f; - def.E = 1000.0f; + def.E = m_E0; def.nu = 0.33f; + def.radius = 0.2f; + def.friction = 0.6f; m_body = new b3SoftBody(def); b3Vec3 gravity(0.0f, -9.8f, 0.0f); m_body->SetGravity(gravity); - m_body->SetWorld(&m_world); - - for (u32 i = 0; i < m_mesh.vertexCount; ++i) - { - b3SoftBodyNode* n = m_body->GetVertexNode(i); - - n->SetRadius(0.05f); - n->SetFriction(0.2f); - } // Create body { @@ -55,7 +56,7 @@ public: b3Body* b = m_world.CreateBody(bd); - m_wallHull.Set(1.0f, 5.0f, 5.0f); + m_wallHull.SetExtents(1.0f, 5.0f, 5.0f); b3HullShape wallShape; wallShape.m_hull = &m_wallHull; @@ -64,18 +65,20 @@ public: sd.shape = &wallShape; b3Shape* wall = b->CreateShape(sd); + + b3SoftBodyWorldShapeDef ssd; + ssd.shape = wall; + + m_body->CreateWorldShape(ssd); } - b3AABB3 aabb; - aabb.m_lower.Set(-3.0f, -5.0f, -5.0f); - aabb.m_upper.Set(-2.0f, 5.0f, 5.0f); - - for (u32 i = 0; i < m_mesh.vertexCount; ++i) + for (u32 i = 0; i < e_h + 1; ++i) { - b3SoftBodyNode* n = m_body->GetVertexNode(i); - b3Vec3 p = n->GetPosition(); - if (aabb.Contains(p)) + for (u32 k = 0; k < e_d + 1; ++k) { + u32 v = m_mesh.GetVertex(i, 0, k); + + b3SoftBodyNode* n = m_body->GetNode(v); n->SetType(e_staticSoftBodyNode); } } @@ -107,9 +110,9 @@ public: b3Vec3 pA = m_bodyDragger->GetPointA(); b3Vec3 pB = m_bodyDragger->GetPointB(); - g_draw->DrawPoint(pA, 2.0f, b3Color_green); + g_draw->DrawPoint(pA, 4.0f, b3Color_green); - g_draw->DrawPoint(pB, 2.0f, b3Color_green); + g_draw->DrawPoint(pB, 4.0f, b3Color_green); g_draw->DrawSegment(pA, pB, b3Color_white); } @@ -117,8 +120,10 @@ public: extern u32 b3_softBodySolverIterations; g_draw->DrawString(b3Color_white, "Iterations = %d", b3_softBodySolverIterations); - float32 E = m_body->GetEnergy(); + scalar E = m_body->GetEnergy(); g_draw->DrawString(b3Color_white, "E = %f", E); + + g_draw->DrawString(b3Color_white, "Up/Down - Young Modulus (%f)", m_E); } void MouseMove(const b3Ray3& pw) @@ -146,13 +151,37 @@ public: } } + void KeyDown(int button) + { + if (button == GLFW_KEY_UP) + { + m_E = b3Clamp(m_E + scalar(10), scalar(0), m_E0); + for (u32 i = 0; i < m_mesh.tetrahedronCount; ++i) + { + m_body->GetElement(i)->SetE(m_E); + } + } + + if (button == GLFW_KEY_DOWN) + { + m_E = b3Clamp(m_E - scalar(10), scalar(10), m_E0); + for (u32 i = 0; i < m_mesh.tetrahedronCount; ++i) + { + m_body->GetElement(i)->SetE(m_E); + } + } + } + static Test* Create() { return new Beam(); } - b3BlockSoftBodyMesh<5, 2, 2> m_mesh; + b3BlockSoftBodyMesh m_mesh; + scalar m_E0; + scalar m_E; + b3SoftBody* m_body; b3SoftBodyDragger* m_bodyDragger; diff --git a/examples/testbed/tests/body_types.h b/examples/testbed/tests/body_types.h index ac71468..1f2a898 100644 --- a/examples/testbed/tests/body_types.h +++ b/examples/testbed/tests/body_types.h @@ -47,8 +47,8 @@ public: m_body = m_world.CreateBody(bd); b3CapsuleShape cap; - cap.m_centers[0].Set(0.0f, 2.0f, 0.0f); - cap.m_centers[1].Set(0.0f, -2.0f, 0.0f); + cap.m_vertex1.Set(0.0f, 2.0f, 0.0f); + cap.m_vertex2.Set(0.0f, -2.0f, 0.0f); cap.m_radius = 0.5f; b3ShapeDef sd; @@ -94,7 +94,7 @@ public: bd.type = b3BodyType::e_dynamicBody; bd.position.Set(RandomFloat(-20.0f, 20.0f), RandomFloat(10.0f, 20.0f), RandomFloat(-20.0f, 20.0f)); - b3Vec3 n = m_body->GetTransform().position - bd.position; + b3Vec3 n = m_body->GetTransform().translation - bd.position; n.Normalize(); bd.linearVelocity = 100.0f * n; @@ -122,7 +122,7 @@ public: p.x -= 1.0f; - m_body->SetTransform(p, b3Vec3(q.x, q.y, q.z), q.w); + m_body->SetTransform(p, q); } if (button == GLFW_KEY_RIGHT) @@ -132,7 +132,7 @@ public: p.x += 1.0f; - m_body->SetTransform(p, b3Vec3(q.x, q.y, q.z), q.w); + m_body->SetTransform(p, q); } if (button == GLFW_KEY_UP) @@ -142,7 +142,7 @@ public: p.z += 1.0f; - m_body->SetTransform(p, b3Vec3(q.x, q.y, q.z), q.w); + m_body->SetTransform(p, q); } if (button == GLFW_KEY_DOWN) @@ -152,7 +152,7 @@ public: p.z -= 1.0f; - m_body->SetTransform(p, b3Vec3(q.x, q.y, q.z), q.w); + m_body->SetTransform(p, q); } } diff --git a/examples/testbed/tests/box_edge_contact.h b/examples/testbed/tests/box_edge_contact.h index 5e90f7f..6733473 100644 --- a/examples/testbed/tests/box_edge_contact.h +++ b/examples/testbed/tests/box_edge_contact.h @@ -24,27 +24,19 @@ class BoxEdgeContact : public Collide public: BoxEdgeContact() { - b3Transform xf; - xf.position.SetZero(); - xf.rotation = b3Diagonal(1.0f, 2.0f, 1.0f); - m_box.SetTransform(xf); + m_box.SetExtents(1.0f, 2.0f, 1.0f); m_sA.m_hull = &m_box; - m_sB.m_hull = &m_box; - - m_xfA.position.Set(1.500000, 1.000000, 0.000000); - m_xfA.rotation.x.Set(0.707107, 0.000000, -0.707107); - m_xfA.rotation.y.Set(0.000000, 1.000000, 0.000000); - m_xfA.rotation.z.Set(0.707107, 0.000000, 0.707107); + m_xfA.translation.Set(1.500000, 1.000000, 0.000000); + m_xfA.rotation.SetIdentity(); + m_shapeA = &m_sA; - m_xfB.position.Set(-1.300000, 0.000000, 0.000000); - m_xfB.rotation.x.Set(0.809017, 0.266849, -0.523721); - m_xfB.rotation.y.Set(0.000000, 0.891007, 0.453991); - m_xfB.rotation.z.Set(0.587785, -0.367286, 0.720840); + m_sB.m_hull = &m_box; + m_xfB.translation.Set(-1.29999995, 1.34999979, 0.000000000); + m_xfB.rotation.Set(0.810514629, 0.342624813, 0.334119707, 0.337692767); + m_shapeB = &m_sB; m_cache.count = 0; - m_shapeA = &m_sA; - m_shapeB = &m_sB; } static Test* Create() diff --git a/examples/testbed/tests/box_face_contact.h b/examples/testbed/tests/box_face_contact.h index 71243e3..62a1894 100644 --- a/examples/testbed/tests/box_face_contact.h +++ b/examples/testbed/tests/box_face_contact.h @@ -24,28 +24,24 @@ class BoxFaceContact : public Collide public: BoxFaceContact() { - b3Transform m; - m.rotation = b3Diagonal(1.0f, 2.0f, 1.0f); - m.position.Set(0.0f, 2.0f, 0.0f); - m_box1.SetTransform(m); + m_boxA.SetExtents(1.0f, 2.0f, 1.0f); + b3Vec3 translation(0.0f, 2.0f, 0.0f); + m_boxA.Translate(translation); - m.rotation = b3Diagonal(1.0f, 1.0f, 1.0f); - m.position.Set(0.0f, 0.0f, 0.0f); - m_box2.SetTransform(m); - - m_xfA.SetIdentity(); - m_xfA.position.SetZero(); - m_xfA.rotation.SetIdentity(); - m_sA.m_hull = &m_box1; + m_sA.m_hull = &m_boxA; - m_xfB.SetIdentity(); - m_xfB.position.Set(0.0f, 0.0f, 0.0f); - m_xfB.rotation.SetIdentity(); - m_sB.m_hull = &m_box2; + m_xfA.SetIdentity(); - m_cache.count = 0; m_shapeA = &m_sA; + + m_boxB.SetExtents(1.0f, 1.0f, 1.0f); + + m_xfB.SetIdentity(); + m_sB.m_hull = &m_boxB; + m_shapeB = &m_sB; + + m_cache.count = 0; } static Test* Create() @@ -53,8 +49,8 @@ public: return new BoxFaceContact(); } - b3BoxHull m_box1; - b3BoxHull m_box2; + b3BoxHull m_boxA; + b3BoxHull m_boxB; b3HullShape m_sA; b3HullShape m_sB; }; diff --git a/examples/testbed/tests/box_stack.h b/examples/testbed/tests/box_stack.h index 1d1e9ef..234a81f 100644 --- a/examples/testbed/tests/box_stack.h +++ b/examples/testbed/tests/box_stack.h @@ -24,17 +24,15 @@ class BoxStack : public Test public: enum { - e_rowCount = 1, - e_columnCount = 5, - e_depthCount = 1 + e_h = 5, + e_w = 1, + e_d = 1 }; BoxStack() { { b3BodyDef bdef; - bdef.type = b3BodyType::e_staticBody; - b3Body* body = m_world.CreateBody(bdef); b3HullShape hs; @@ -47,30 +45,53 @@ public: body->CreateShape(sdef); } - b3Vec3 boxScale(1.0f, 1.0f, 1.0f); + b3Vec3 e(1.0f, 1.0f, 1.0f); - b3Vec3 stackOrigin(0.0f, 4.05f, 0.0f); + m_boxHull.SetExtents(e.x, e.y, e.z); - for (u32 i = 0; i < e_rowCount; ++i) + b3Vec3 separation; + separation.x = 1.0f; + separation.y = 1.0f; + separation.z = 1.0f; + + b3Vec3 scale; + scale.x = 2.0f * e.x + separation.x; + scale.y = 2.0f * e.y + separation.y; + scale.z = 2.0f * e.z + separation.z; + + b3Vec3 size; + size.x = 2.0f * e.x + scale.x * scalar(e_w - 1); + size.y = 2.0f * e.y + scale.y * scalar(e_h - 1); + size.z = 2.0f * e.z + scale.z * scalar(e_d - 1); + + b3Vec3 translation; + translation.x = e.x - 0.5f * size.x; + translation.y = e.y - 0.5f * size.y; + translation.z = e.z - 0.5f * size.z; + + translation.y += 9.0f; + + for (u32 i = 0; i < e_h; ++i) { - for (u32 j = 0; j < e_columnCount; ++j) + for (u32 j = 0; j < e_w; ++j) { - for (u32 k = 0; k < e_depthCount; ++k) + for (u32 k = 0; k < e_d; ++k) { b3BodyDef bdef; - bdef.type = b3BodyType::e_dynamicBody; - bdef.orientation.Set(b3Vec3(0.0f, 1.0f, 0.0f), 0.5f * B3_PI); + bdef.type = e_dynamicBody; - bdef.position.x = float32(i) * boxScale.x; - bdef.position.y = 2.5f * float32(j) * boxScale.y; - bdef.position.z = float32(k) * boxScale.z; + bdef.position.Set(scalar(j), scalar(i), scalar(k)); - bdef.position += stackOrigin; + bdef.position.x *= scale.x; + bdef.position.y *= scale.y; + bdef.position.z *= scale.z; + + bdef.position += translation; b3Body* body = m_world.CreateBody(bdef); b3HullShape hs; - hs.m_hull = &b3BoxHull_identity; + hs.m_hull = &m_boxHull; b3ShapeDef sdef; sdef.density = 0.1f; @@ -78,15 +99,30 @@ public: sdef.shape = &hs; body->CreateShape(sdef); + + u32 bodyIndex = GetBodyIndex(i, j, k); + + m_bodies[bodyIndex] = body; } } } } + u32 GetBodyIndex(u32 i, u32 j, u32 k) + { + B3_ASSERT(i < e_h); + B3_ASSERT(j < e_w); + B3_ASSERT(k < e_d); + return k + e_d * (j + e_w * i); + } + static Test* Create() { return new BoxStack(); } + + b3BoxHull m_boxHull; + b3Body* m_bodies[e_h * e_w * e_d]; }; #endif \ No newline at end of file diff --git a/examples/testbed/tests/cape.h b/examples/testbed/tests/cape.h new file mode 100644 index 0000000..8be2ac6 --- /dev/null +++ b/examples/testbed/tests/cape.h @@ -0,0 +1,215 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef CAPE_H +#define CAPE_H + +class Cape : public Test +{ +public: + enum + { + e_w = 5, + e_h = 10 + }; + + Cape() + { + // Translate the cloth mesh + for (u32 i = 0; i < m_clothMesh.vertexCount; ++i) + { + m_clothMesh.vertices[i].y += 5.0f; + m_clothMesh.vertices[i].z -= 6.0f; + } + + // Create cloth + b3ClothDef def; + def.mesh = &m_clothMesh; + def.density = 0.2f; + def.streching = 100000.0f; + + m_cloth = new b3Cloth(def); + + m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); + + // Freeze some particles + for (u32 j = 0; j < e_w + 1; ++j) + { + u32 vj = m_clothMesh.GetVertex(e_h, j); + + b3ClothParticle* p = m_cloth->GetParticle(vj); + p->SetType(e_kinematicClothParticle); + } + + m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); + + { + // Create body + b3BodyDef bdef; + bdef.type = b3BodyType::e_kinematicBody; + + m_body = m_world.CreateBody(bdef); + + static b3BoxHull box(1.0f, 5.0f, 1.0f); + + b3HullShape hs; + hs.m_hull = &box; + hs.m_radius = 0.25f; + + b3ShapeDef sdef; + sdef.density = 0.1f; + sdef.friction = 0.3f; + sdef.shape = &hs; + + m_body->CreateShape(sdef); + } + + // Store cloth vertices in body space + for (u32 j = 0; j < e_w + 1; ++j) + { + u32 vj = m_clothMesh.GetVertex(e_h, j); + + b3ClothParticle* p = m_cloth->GetParticle(vj); + b3Vec3 position = p->GetPosition(); + + m_localPoints[j] = m_body->GetLocalPoint(position); + } + } + + ~Cape() + { + delete m_clothDragger; + delete m_cloth; + } + + void KeyDown(int button) + { + b3Vec3 v = m_body->GetLinearVelocity(); + + if (button == GLFW_KEY_LEFT) + { + v.x -= 5.0f; + } + + if (button == GLFW_KEY_RIGHT) + { + v.x += 5.0f; + } + + if (button == GLFW_KEY_UP) + { + v.z -= 5.0f; + } + + if (button == GLFW_KEY_DOWN) + { + v.z += 5.0f; + } + + m_body->SetLinearVelocity(v); + } + + void Step() + { + Test::Step(); + + scalar inv_h = g_testSettings->hertz; + + for (u32 j = 0; j < e_w + 1; ++j) + { + u32 vj = m_clothMesh.GetVertex(e_h, j); + + b3ClothParticle* p = m_cloth->GetParticle(vj); + b3Vec3 x0 = p->GetPosition(); + + b3Vec3 x = m_body->GetWorldPoint(m_localPoints[j]); + + // Apply finite difference method + b3Vec3 v = inv_h * (x - x0); + + p->SetVelocity(v); + } + + m_cloth->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); + + m_cloth->Draw(); + + if (m_clothDragger->IsDragging()) + { + b3Vec3 pA = m_clothDragger->GetPointA(); + b3Vec3 pB = m_clothDragger->GetPointB(); + + g_draw->DrawPoint(pA, 4.0f, b3Color_green); + + g_draw->DrawPoint(pB, 4.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_clothSolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); + + scalar E = m_cloth->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + + g_draw->DrawString(b3Color_white, "Arrows - Apply Velocity"); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->Drag(); + } + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_clothDragger->IsDragging() == false) + { + m_clothDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->StopDragging(); + } + } + + static Test* Create() + { + return new Cape(); + } + + b3GridClothMesh m_clothMesh; + b3Cloth* m_cloth; + b3ClothDragger* m_clothDragger; + b3Body* m_body; + b3Vec3 m_localPoints[e_w + 1]; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/capsule_collision.h b/examples/testbed/tests/capsule_collision.h index 00a51ac..8655bb1 100644 --- a/examples/testbed/tests/capsule_collision.h +++ b/examples/testbed/tests/capsule_collision.h @@ -24,25 +24,21 @@ class CapsuleCollision : public Collide public: CapsuleCollision() { - m_xfA.SetIdentity(); - m_xfA.position.Set(0.0f, 0.0f, 0.0f); - //m_xfA.rotation = b3ConvertQuatToRot(b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), 0.25f * B3_PI)); - m_xfA.rotation.SetIdentity(); - m_sA.m_centers[0].Set(0.0f, -5.0f, 0.0f); - m_sA.m_centers[1].Set(0.0f, 5.0f, 0.0f); + m_sA.m_vertex1.Set(0.0f, -5.0f, 0.0f); + m_sA.m_vertex2.Set(0.0f, 5.0f, 0.0f); m_sA.m_radius = 1.0f; - m_xfB.SetIdentity(); - m_xfB.position.Set(0.f, 0.0f, 0.0f); - //m_xfB.rotation = b3ConvertQuatToRot(b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), 0.251f * B3_PI)); - m_xfB.rotation.SetIdentity(); - m_sB.m_centers[0].Set(0.0f, -1.0f, 0.0f); - m_sB.m_centers[1].Set(0.0f, 1.0f, 0.0f); + m_xfA.SetIdentity(); + + m_sB.m_vertex1.Set(0.0f, -1.0f, 0.0f); + m_sB.m_vertex2.Set(0.0f, 1.0f, 0.0f); m_sB.m_radius = 1.0f; - m_cache.count = 0; + m_xfB.SetIdentity(); + m_shapeA = &m_sA; m_shapeB = &m_sB; + m_cache.count = 0; } static Test* Create() diff --git a/examples/testbed/tests/capsule_spin.h b/examples/testbed/tests/capsule_spin.h index 94be411..6ac6e8a 100644 --- a/examples/testbed/tests/capsule_spin.h +++ b/examples/testbed/tests/capsule_spin.h @@ -40,16 +40,15 @@ public: { b3BodyDef bdef; bdef.type = e_dynamicBody; + bdef.position.Set(0.0f, 10.0f, 0.0f); - bdef.orientation.Set(b3Vec3(0.0f, 0.0f, -1.0f), 1.5f * B3_PI); - bdef.linearVelocity.Set(0.005f, -10.0f, 0.005f); - bdef.angularVelocity.Set(2000.0f * B3_PI, 2000.0f * B3_PI, 10000.0f * B3_PI); + bdef.angularVelocity.Set(0.5f * B3_PI, 10.0f * B3_PI, 0.0f); b3Body* body = m_world.CreateBody(bdef); b3CapsuleShape capsule; - capsule.m_centers[0].Set(0.0f, 4.0f, 0.0f); - capsule.m_centers[1].Set(0.0f, -4.0f, 0.0f); + capsule.m_vertex1.Set(0.0f, 4.0f, 0.0f); + capsule.m_vertex2.Set(0.0f, -4.0f, 0.0f); capsule.m_radius = 0.5f; b3ShapeDef sd; diff --git a/examples/testbed/tests/capsule_stack.h b/examples/testbed/tests/capsule_stack.h index d4b370c..bccecc7 100644 --- a/examples/testbed/tests/capsule_stack.h +++ b/examples/testbed/tests/capsule_stack.h @@ -24,104 +24,110 @@ class CapsuleStack : public Test public: enum { - e_rowCount = 1, - e_columnCount = 5, - e_depthCount = 1 + e_h = 5, + e_w = 1, + e_d = 1 }; CapsuleStack() { { - b3BodyDef bd; - bd.type = b3BodyType::e_staticBody; - - b3Body* body = m_world.CreateBody(bd); + b3BodyDef bdef; + b3Body* body = m_world.CreateBody(bdef); b3HullShape hs; hs.m_hull = &m_groundHull; - b3ShapeDef sd; - sd.shape = &hs; - - body->CreateShape(sd); + b3ShapeDef sdef; + sdef.shape = &hs; + sdef.friction = 1.0f; + + body->CreateShape(sdef); } - - float32 height = 3.0f; - float32 radius = 1.0f; - float32 separation = 0.0f; + + scalar rx = 1.0f; + scalar r = 1.0f; b3CapsuleShape capsule; - capsule.m_centers[0].Set(0.0f, -0.5f * height, 0.0f); - capsule.m_centers[1].Set(0.0f, 0.5f * height, 0.0f); - capsule.m_radius = radius; + capsule.m_vertex1.Set(-rx, 0.0f, 0.0f); + capsule.m_vertex2.Set(rx, 0.0f, 0.0f); + capsule.m_radius = r; - b3ShapeDef sdef; - sdef.shape = &capsule; - sdef.density = 0.1f; - sdef.friction = 0.4f; + b3Vec3 e; + e.x = rx + r; + e.y = r; + e.z = r; - const u32 c = e_rowCount * e_columnCount * e_depthCount; - b3Body* bs[c]; - u32 n = 0; + b3Vec3 separation; + separation.x = 1.0f; + separation.y = 1.0f; + separation.z = 1.0f; - b3AABB3 aabb; - aabb.m_lower.Set(0.0f, 0.0f, 0.0f); - aabb.m_upper.Set(0.0f, 0.0f, 0.0f); + b3Vec3 scale; + scale.x = 2.0f * e.x + separation.x; + scale.y = 2.0f * e.y + separation.y; + scale.z = 2.0f * e.z + separation.z; - for (u32 i = 0; i < e_rowCount; ++i) + b3Vec3 size; + size.x = 2.0f * e.x + scale.x * scalar(e_w - 1); + size.y = 2.0f * e.y + scale.y * scalar(e_h - 1); + size.z = 2.0f * e.z + scale.z * scalar(e_d - 1); + + b3Vec3 translation; + translation.x = e.x - 0.5f * size.x; + translation.y = e.y - 0.5f * size.y; + translation.z = e.z - 0.5f * size.z; + + translation.y += 9.0f; + + for (u32 i = 0; i < e_h; ++i) { - for (u32 j = 0; j < e_columnCount; ++j) + for (u32 j = 0; j < e_w; ++j) { - for (u32 k = 0; k < e_depthCount; ++k) + for (u32 k = 0; k < e_d; ++k) { b3BodyDef bdef; - bdef.type = b3BodyType::e_dynamicBody; - - bdef.position.x = (2.0f + separation) * float32(i) * (0.5f * height + radius); - bdef.position.y = 2.0f + (2.0f + separation) * float32(j) * radius; - bdef.position.z = (2.0f + separation) * float32(k) * radius; - - bdef.orientation = b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), 0.5f * B3_PI); - + bdef.type = e_dynamicBody; + + bdef.position.Set(scalar(j), scalar(i), scalar(k)); + + bdef.position.x *= scale.x; + bdef.position.y *= scale.y; + bdef.position.z *= scale.z; + + bdef.position += translation; + b3Body* body = m_world.CreateBody(bdef); - bs[n++] = body; - b3Shape* shape = body->CreateShape(sdef); + b3ShapeDef sdef; + sdef.density = 0.1f; + sdef.friction = 0.3f; + sdef.shape = &capsule; - b3AABB3 aabb2; - shape->ComputeAABB(&aabb2, body->GetTransform()); + body->CreateShape(sdef); - aabb = b3Combine(aabb, aabb2); + u32 bodyIndex = GetBodyIndex(i, j, k); + + m_bodies[bodyIndex] = body; } } } + } - b3Vec3 center = aabb.Centroid(); - - for (u32 i = 0; i < n; ++i) - { - b3Body* b = bs[i]; - const b3Vec3& p = b->GetSweep().worldCenter; - const b3Quat& q = b->GetSweep().orientation; - - // centralize - b3Vec3 position = p - center; - - // move up - position.y += 5.0f + 0.5f * aabb.Height() + radius; - - // maintain orientation - b3Vec3 axis; - float32 angle; - q.GetAxisAngle(&axis, &angle); - b->SetTransform(position, axis, angle); - } + u32 GetBodyIndex(u32 i, u32 j, u32 k) + { + B3_ASSERT(i < e_h); + B3_ASSERT(j < e_w); + B3_ASSERT(k < e_d); + return k + e_d * (j + e_w * i); } static Test* Create() { return new CapsuleStack(); } + + b3Body* m_bodies[e_h * e_w * e_d]; }; -#endif +#endif \ No newline at end of file diff --git a/examples/testbed/tests/cloth_element_test.h b/examples/testbed/tests/cloth_element_test.h new file mode 100644 index 0000000..bdac1a2 --- /dev/null +++ b/examples/testbed/tests/cloth_element_test.h @@ -0,0 +1,181 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef CLOTH_ELEMENT_TEST_H +#define CLOTH_ELEMENT_TEST_H + +class ClothElementTest : public Test +{ +public: + enum + { + e_w = 10, + e_h = 10 + }; + + ClothElementTest() + { + g_camera->m_zoom = 20.0f; + + b3GridClothMesh m; + + b3ClothParticle** particles = (b3ClothParticle * *)b3Alloc(m.vertexCount * sizeof(b3ClothParticle*)); + for (u32 i = 0; i < m.vertexCount; ++i) + { + b3ClothParticleDef pd; + pd.type = e_dynamicClothParticle; + pd.position = m.vertices[i]; + + b3ClothParticle* p = m_cloth.CreateParticle(pd); + particles[i] = p; + + b3ClothSphereShapeDef sd; + sd.p = p; + sd.radius = 0.2f; + sd.friction = 0.4f; + + m_cloth.CreateSphereShape(sd); + } + + for (u32 i = 0; i < m.triangleCount; ++i) + { + u32 v1 = m.triangles[i].v1; + u32 v2 = m.triangles[i].v2; + u32 v3 = m.triangles[i].v3; + + b3Vec3 x1 = m.vertices[v1]; + b3Vec3 x2 = m.vertices[v2]; + b3Vec3 x3 = m.vertices[v3]; + + b3ClothParticle* p1 = particles[v1]; + b3ClothParticle* p2 = particles[v2]; + b3ClothParticle* p3 = particles[v3]; + + b3ClothTriangleShapeDef tsd; + tsd.p1 = p1; + tsd.p2 = p2; + tsd.p3 = p3; + tsd.v1 = x1; + tsd.v2 = x2; + tsd.v3 = x3; + tsd.density = 0.1f; + + m_cloth.CreateTriangleShape(tsd); + + b3ElementForceDef fd; + fd.p1 = p1; + fd.p2 = p2; + fd.p3 = p3; + fd.E_x = 500.0f; + fd.E_y = 500.0f; + fd.E_s = 500.0f; + fd.nu_xy = 0.3f; + fd.nu_yx = 0.3f; + fd.v1 = x1; + fd.v2 = x2; + fd.v3 = x3; + + m_cloth.CreateForce(fd); + } + + for (u32 i = 0; i < e_w + 1; ++i) + { + u32 vertex = m.GetVertex(0, i); + particles[vertex]->SetType(e_staticClothParticle); + } + + b3Free(particles); + + m_cloth.SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); + + m_clothDragger = new b3ClothDragger(&m_ray, &m_cloth); + m_clothDragger->SetStaticDrag(false); + } + + ~ClothElementTest() + { + delete m_clothDragger; + } + + void Step() + { + Test::Step(); + + m_cloth.Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); + + m_cloth.Draw(); + + if (m_clothDragger->IsDragging()) + { + b3Vec3 pA = m_clothDragger->GetPointA(); + b3Vec3 pB = m_clothDragger->GetPointB(); + + g_draw->DrawPoint(pA, 4.0f, b3Color_green); + + g_draw->DrawPoint(pB, 4.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_clothSolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); + + scalar E = m_cloth.GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->Drag(); + } + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_clothDragger->IsDragging() == false) + { + m_clothDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->StopDragging(); + } + } + + static Test* Create() + { + return new ClothElementTest(); + } + + b3Cloth m_cloth; + b3ClothDragger* m_clothDragger; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/cloth_sdf.h b/examples/testbed/tests/cloth_sdf.h new file mode 100644 index 0000000..8ccb9f7 --- /dev/null +++ b/examples/testbed/tests/cloth_sdf.h @@ -0,0 +1,268 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef CLOTH_SDF_H +#define CLOTH_SDF_H + +#define TINYOBJLOADER_IMPLEMENTATION +#include + +struct SDFMesh +{ + u32 vertexCount; + b3Vec3* vertices; + u32 indexCount; + u32* indices; + + SDFMesh() + { + vertexCount = 0; + vertices = nullptr; + indexCount = 0; + indices = nullptr; + } + + ~SDFMesh() + { + free(vertices); + free(indices); + } + + bool Load(const char* filename) + { + tinyobj::attrib_t attributes; + std::vector shapes; + std::vector materials; + + std::string warning; + std::string error; + bool ok = tinyobj::LoadObj(&attributes, &shapes, &materials, &warning, &error, filename); + if (!ok) + { + return false; + } + + assert(vertexCount == 0); + vertexCount = attributes.vertices.size() / 3; + vertices = (b3Vec3*)malloc(vertexCount * sizeof(b3Vec3)); + for (size_t i = 0; i < attributes.vertices.size() / 3; ++i) + { + tinyobj::real_t x = attributes.vertices[3 * i + 0]; + tinyobj::real_t y = attributes.vertices[3 * i + 1]; + tinyobj::real_t z = attributes.vertices[3 * i + 2]; + + b3Vec3 v(x, y, z); + + vertices[i] = v; + } + + assert(indexCount == 0); + for (size_t s = 0; s < shapes.size(); s++) + { + tinyobj::shape_t& shape = shapes[s]; + for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) + { + indexCount += 3; + } + } + indices = (u32*)malloc(indexCount * sizeof(u32)); + + indexCount = 0; + for (size_t s = 0; s < shapes.size(); s++) + { + tinyobj::shape_t& shape = shapes[s]; + + size_t index_offset = 0; + for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) + { + unsigned char fv = shapes[s].mesh.num_face_vertices[f]; + + for (size_t v = 0; v < 3; v++) + { + tinyobj::index_t idx = shapes[s].mesh.indices[index_offset + v]; + + size_t vi = idx.vertex_index; + + indices[indexCount++] = vi; + } + + index_offset += fv; + } + } + + return true; + } + + void Draw(const b3Transform& xf, const b3Vec3& scale, const b3Color& color) const + { + for (u32 i = 0; i < indexCount / 3; ++i) + { + u32 i1 = indices[3 * i + 0]; + u32 i2 = indices[3 * i + 1]; + u32 i3 = indices[3 * i + 2]; + + b3Vec3 v1 = xf * b3MulCW(scale, vertices[i1]); + b3Vec3 v2 = xf * b3MulCW(scale, vertices[i2]); + b3Vec3 v3 = xf * b3MulCW(scale, vertices[i3]); + + b3Vec3 n = b3Cross(v2 - v1, v3 - v1); + n.Normalize(); + + g_draw->DrawSolidTriangle(n, v1, v2, v3, color); + } + } +}; + +class ClothSDF : public Test +{ +public: + ClothSDF() + { + // Translate the cloth mesh + for (u32 i = 0; i < m_clothMesh.vertexCount; ++i) + { + m_clothMesh.vertices[i].y += 5.0f; + } + + // Create cloth + b3ClothDef def; + def.mesh = &m_clothMesh; + def.density = 0.2f; + def.streching = 10000.0f; + def.strechDamping = 100.0f; + def.thickness = 0.2f; + def.friction = 0.2f; + + m_cloth = new b3Cloth(def); + + m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); + + { + bool ok = m_sdfMesh.Load("data/teapot.obj"); + assert(ok); + } + + { + b3BodyDef bd; + bd.type = e_staticBody; + + b3Body* b = m_world.CreateBody(bd); + + bool ok = m_sdf.Load("data/teapot.cdf"); + assert(ok); + + b3SDFShape sdfShape; + sdfShape.m_sdf = &m_sdf; + sdfShape.m_radius = 0.2f; + + b3ShapeDef sd; + sd.shape = &sdfShape; + sd.friction = 1.0f; + + m_sdfShape = (b3SDFShape*)b->CreateShape(sd); + + b3ClothWorldShapeDef csd; + csd.shape = m_sdfShape; + + m_cloth->CreateWorldShape(csd); + } + + m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); + } + + ~ClothSDF() + { + delete m_clothDragger; + delete m_cloth; + } + + void Step() + { + Test::Step(); + + m_cloth->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); + + m_cloth->Draw(); + + if (m_clothDragger->IsDragging()) + { + b3Vec3 pA = m_clothDragger->GetPointA(); + b3Vec3 pB = m_clothDragger->GetPointB(); + + g_draw->DrawPoint(pA, 4.0f, b3Color_green); + + g_draw->DrawPoint(pB, 4.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + b3Body* sdfBody = m_sdfShape->GetBody(); + m_sdfMesh.Draw(sdfBody->GetTransform(), m_sdfShape->m_scale, b3Color_white); + + extern u32 b3_clothSolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); + + scalar E = m_cloth->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->Drag(); + } + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_clothDragger->IsDragging() == false) + { + m_clothDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->StopDragging(); + } + } + + static Test* Create() + { + return new ClothSDF(); + } + + b3GridClothMesh<10, 10> m_clothMesh; + b3Cloth* m_cloth; + b3ClothDragger* m_clothDragger; + + SDFMesh m_sdfMesh; + b3SDF m_sdf; + b3SDFShape* m_sdfShape; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/cloth_self_collision.h b/examples/testbed/tests/cloth_self_collision.h index b363025..e877526 100644 --- a/examples/testbed/tests/cloth_self_collision.h +++ b/examples/testbed/tests/cloth_self_collision.h @@ -22,15 +22,82 @@ class ClothSelfCollision : public Test { public: + enum + { + e_w1 = 5, + e_h1 = 5, + e_w2 = 5, + e_h2 = 5 + }; + ClothSelfCollision() { - // Translate the mesh - for (u32 i = 0; i < m_clothMesh.vertexCount; ++i) + b3GridClothMesh mesh1; + b3Quat qX = b3QuatRotationX(0.5f * B3_PI); + for (u32 i = 0; i < mesh1.vertexCount; ++i) { - m_clothMesh.vertices[i].y += 5.0f; + mesh1.vertices[i] = b3Mul(qX, mesh1.vertices[i]); + mesh1.vertices[i].y += 5.0f; } - // Create cloth + b3GridClothMesh mesh2; + b3Quat qY = b3QuatRotationY(0.5f * B3_PI); + for (u32 i = 0; i < mesh2.vertexCount; ++i) + { + mesh2.vertices[i] = b3Mul(qY * qX, mesh2.vertices[i]); + mesh2.vertices[i].y += 12.0f; + } + + // Merge the meshes + m_clothMesh.vertexCount = mesh1.vertexCount + mesh2.vertexCount; + m_clothMesh.vertices = (b3Vec3*)b3Alloc(m_clothMesh.vertexCount * sizeof(b3Vec3)); + + u32* newVertices1 = (u32*)b3Alloc(mesh1.vertexCount * sizeof(u32)); + u32 vertexIndex = 0; + for (u32 i = 0; i < mesh1.vertexCount; ++i) + { + newVertices1[i] = vertexIndex; + m_clothMesh.vertices[vertexIndex++] = mesh1.vertices[i]; + } + + u32* newVertices2 = (u32*)b3Alloc(mesh2.vertexCount * sizeof(u32)); + for (u32 i = 0; i < mesh2.vertexCount; ++i) + { + newVertices2[i] = vertexIndex; + m_clothMesh.vertices[vertexIndex++] = mesh2.vertices[i]; + } + + m_clothMesh.triangleCount = mesh1.triangleCount + mesh2.triangleCount; + m_clothMesh.triangles = (b3ClothMeshTriangle*)b3Alloc(m_clothMesh.triangleCount * sizeof(b3ClothMeshTriangle)); + u32 triangleIndex = 0; + for (u32 i = 0; i < mesh1.triangleCount; ++i) + { + m_clothMesh.triangles[triangleIndex].v1 = newVertices1[mesh1.triangles[i].v1]; + m_clothMesh.triangles[triangleIndex].v2 = newVertices1[mesh1.triangles[i].v2]; + m_clothMesh.triangles[triangleIndex].v3 = newVertices1[mesh1.triangles[i].v3]; + ++triangleIndex; + } + + for (u32 i = 0; i < mesh2.triangleCount; ++i) + { + m_clothMesh.triangles[triangleIndex].v1 = newVertices2[mesh2.triangles[i].v1]; + m_clothMesh.triangles[triangleIndex].v2 = newVertices2[mesh2.triangles[i].v2]; + m_clothMesh.triangles[triangleIndex].v3 = newVertices2[mesh2.triangles[i].v3]; + ++triangleIndex; + } + + m_clothMesh.meshCount = 1; + m_clothMesh.meshes = (b3ClothMeshMesh*)b3Alloc(sizeof(b3ClothMeshMesh)); + m_clothMesh.meshes->startTriangle = 0; + m_clothMesh.meshes->triangleCount = m_clothMesh.triangleCount; + m_clothMesh.meshes->startVertex = 0; + m_clothMesh.meshes->vertexCount = m_clothMesh.vertexCount; + + m_clothMesh.shearingLineCount = 0; + m_clothMesh.bendingLineCount = 0; + m_clothMesh.sewingLineCount = 0; + + // Create the cloth b3ClothDef def; def.mesh = &m_clothMesh; def.density = 1.0f; @@ -41,24 +108,37 @@ public: m_cloth = new b3Cloth(def); m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); - m_cloth->SetWorld(&m_world); + m_cloth->EnableSelfCollision(true); + + for (u32 i = 0; i < mesh1.vertexCount; ++i) + { + u32 newVertex = newVertices1[i]; + + m_cloth->GetParticle(newVertex)->SetType(e_staticClothParticle); + } + + b3Free(newVertices1); + b3Free(newVertices2); { b3BodyDef bd; - bd.type = e_staticBody; b3Body* b = m_world.CreateBody(bd); - b3CapsuleShape capsuleShape; - capsuleShape.m_centers[0].Set(0.0f, 0.0f, -5.0f); - capsuleShape.m_centers[1].Set(0.0f, 0.0f, 5.0f); - capsuleShape.m_radius = 1.0f;; + b3HullShape hullShape; + hullShape.m_hull = &m_groundHull; + hullShape.m_radius = 0.0f;; b3ShapeDef sd; - sd.shape = &capsuleShape; + sd.shape = &hullShape; sd.friction = 1.0f; - b->CreateShape(sd); + b3Shape* s = b->CreateShape(sd); + + b3ClothWorldShapeDef csd; + csd.shape = s; + + m_cloth->CreateWorldShape(csd); } m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); @@ -66,6 +146,10 @@ public: ~ClothSelfCollision() { + b3Free(m_clothMesh.vertices); + b3Free(m_clothMesh.triangles); + b3Free(m_clothMesh.meshes); + delete m_cloth; delete m_clothDragger; } @@ -83,17 +167,27 @@ public: b3Vec3 pA = m_clothDragger->GetPointA(); b3Vec3 pB = m_clothDragger->GetPointB(); - g_draw->DrawPoint(pA, 2.0f, b3Color_green); + g_draw->DrawPoint(pA, 4.0f, b3Color_green); - g_draw->DrawPoint(pB, 2.0f, b3Color_green); + g_draw->DrawPoint(pB, 4.0f, b3Color_green); g_draw->DrawSegment(pA, pB, b3Color_white); } + g_draw->DrawString(b3Color_white, "S - Turn on/off self collision"); + if (m_cloth->IsSelfCollisionEnabled()) + { + g_draw->DrawString(b3Color_white, "Self collision enabled"); + } + else + { + g_draw->DrawString(b3Color_white, "Self collision disabled"); + } + extern u32 b3_clothSolverIterations; g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); - float32 E = m_cloth->GetEnergy(); + scalar E = m_cloth->GetEnergy(); g_draw->DrawString(b3Color_white, "E = %f", E); } @@ -127,12 +221,20 @@ public: } } + void KeyDown(int key) + { + if (key == GLFW_KEY_S) + { + m_cloth->EnableSelfCollision(!m_cloth->IsSelfCollisionEnabled()); + } + } + static Test* Create() { return new ClothSelfCollision(); } - b3GridClothMesh<10, 10> m_clothMesh; + b3ClothMesh m_clothMesh; b3Cloth* m_cloth; b3ClothDragger* m_clothDragger; }; diff --git a/examples/testbed/tests/cloth_tearing.h b/examples/testbed/tests/cloth_tearing.h new file mode 100644 index 0000000..d2dd632 --- /dev/null +++ b/examples/testbed/tests/cloth_tearing.h @@ -0,0 +1,571 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef CLOTH_TEARING_H +#define CLOTH_TEARING_H + +class ClothTearing : public Test +{ +public: + enum + { + e_w = 10, + e_h = 10 + }; + + ClothTearing() + { + g_camera->m_zoom = 20.0f; + + b3GridClothMesh m; + + b3ClothParticle** particles = (b3ClothParticle * *)b3Alloc(m.vertexCount * sizeof(b3ClothParticle*)); + for (u32 i = 0; i < m.vertexCount; ++i) + { + b3ClothParticleDef pd; + pd.type = e_dynamicClothParticle; + pd.position = m.vertices[i]; + + b3ClothParticle* p = m_cloth.CreateParticle(pd); + particles[i] = p; + + b3ClothSphereShapeDef sd; + sd.p = p; + sd.radius = 0.2f; + sd.friction = 0.4f; + + m_cloth.CreateSphereShape(sd); + } + + for (u32 i = 0; i < m.triangleCount; ++i) + { + u32 v1 = m.triangles[i].v1; + u32 v2 = m.triangles[i].v2; + u32 v3 = m.triangles[i].v3; + + b3ClothParticle* p1 = particles[v1]; + b3ClothParticle* p2 = particles[v2]; + b3ClothParticle* p3 = particles[v3]; + + b3ClothTriangleShapeDef tsd; + tsd.p1 = p1; + tsd.p2 = p2; + tsd.p3 = p3; + tsd.v1 = m.vertices[v1]; + tsd.v2 = m.vertices[v2]; + tsd.v3 = m.vertices[v3]; + tsd.density = 0.1f; + + m_cloth.CreateTriangleShape(tsd); + + { + b3SpringForceDef sfd; + sfd.Initialize(p1, p2, 1000.0f, 10.0f); + + CreateSpringForce(sfd); + } + + { + b3SpringForceDef sfd; + sfd.Initialize(p2, p3, 1000.0f, 10.0f); + + CreateSpringForce(sfd); + } + + { + b3SpringForceDef sfd; + sfd.Initialize(p3, p1, 1000.0f, 10.0f); + + CreateSpringForce(sfd); + } + } + + for (u32 i = 0; i < e_w + 1; ++i) + { + u32 vertex = m.GetVertex(0, i); + particles[vertex]->SetType(e_staticClothParticle); + } + + b3Free(particles); + + m_cloth.SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); + + m_clothDragger = new b3ClothDragger(&m_ray, &m_cloth); + m_clothDragger->SetStaticDrag(false); + } + + ~ClothTearing() + { + delete m_clothDragger; + } + + b3SpringForce* FindSpringForce(b3ClothParticle* p1, b3ClothParticle* p2) + { + for (b3Force* f = m_cloth.GetForceList().m_head; f; f = f->GetNext()) + { + if (f->GetType() != e_springForce) + { + continue; + } + + b3SpringForce* sf = (b3SpringForce*)f; + + b3ClothParticle* sp1 = sf->GetParticle1(); + b3ClothParticle* sp2 = sf->GetParticle2(); + + if (sp1 == p1 && sp2 == p2) + { + return sf; + } + + if (sp1 == p2 && sp2 == p1) + { + return sf; + } + } + + return nullptr; + } + + b3SpringForce* CreateSpringForce(const b3SpringForceDef& def) + { + b3SpringForce* sf = FindSpringForce(def.p1, def.p2); + if (sf != nullptr) + { + return sf; + } + + return (b3SpringForce*)m_cloth.CreateForce(def); + } + + void DrawSpringForces() + { + for (b3Force* f = m_cloth.GetForceList().m_head; f; f = f->GetNext()) + { + if (f->GetType() != e_springForce) + { + continue; + } + + b3SpringForce* s = (b3SpringForce*)f; + + b3ClothParticle* p1 = s->GetParticle1(); + b3ClothParticle* p2 = s->GetParticle2(); + + g_draw->DrawSegment(p1->GetPosition(), p2->GetPosition(), b3Color_black); + } + } + + void Partition(b3ClothParticle* p, const b3Plane& plane, + b3Array& above, + b3Array& below) + { + for (b3ClothTriangleShape* t = m_cloth.GetTriangleShapeList().m_head; t; t = t->GetNext()) + { + b3ClothParticle* p1 = t->GetParticle1(); + b3ClothParticle* p2 = t->GetParticle2(); + b3ClothParticle* p3 = t->GetParticle3(); + + if (p1 != p && p2 != p && p3 != p) + { + continue; + } + + b3Vec3 x1 = p1->GetPosition(); + b3Vec3 x2 = p2->GetPosition(); + b3Vec3 x3 = p3->GetPosition(); + + b3Vec3 center = (x1 + x2 + x3) / 3.0f; + + scalar distance = b3Distance(center, plane); + if (distance > 0.0f) + { + above.PushBack(t); + } + else + { + below.PushBack(t); + } + } + } + + bool HasSpring(const b3Array& triangles, + b3ClothParticle* pOld, b3ClothParticle* pOther) + { + for (u32 i = 0; i < triangles.Count(); ++i) + { + b3ClothTriangleShape* triangle = triangles[i]; + + b3ClothParticle* tp1 = triangle->GetParticle1(); + b3ClothParticle* tp2 = triangle->GetParticle2(); + b3ClothParticle* tp3 = triangle->GetParticle3(); + + // 1, 2 + if (tp1 == pOld && tp2 == pOther) + { + return true; + } + + // 2, 1 + if (tp2 == pOld && tp1 == pOther) + { + return true; + } + + // 2, 3 + if (tp2 == pOld && tp3 == pOther) + { + return true; + } + + // 3, 2 + if (tp3 == pOld && tp2 == pOther) + { + return true; + } + + // 3, 1 + if (tp3 == pOld && tp1 == pOther) + { + return true; + } + + // 1, 3 + if (tp1 == pOld && tp3 == pOther) + { + return true; + } + } + + return false; + } + + bool SplitParticle(b3ClothParticle* pOld, const b3Plane& plane) + { + // Collect triangles. + b3StackArray trianglesAbove, trianglesBelow; + Partition(pOld, plane, trianglesAbove, trianglesBelow); + + // There must be at least one triangle on each side of the plane. + if (trianglesAbove.Count() == 0 || trianglesBelow.Count() == 0) + { + return false; + } + + b3ClothParticleDef pdNew; + pdNew.type = pOld->GetType(); + pdNew.position = pOld->GetPosition() - 0.2f * plane.normal; + + b3ClothParticle* pNew = m_cloth.CreateParticle(pdNew); + + b3ClothSphereShapeDef ssdNew; + ssdNew.p = pNew; + ssdNew.radius = 0.2f; + ssdNew.friction = 0.4f; + + m_cloth.CreateSphereShape(ssdNew); + + for (u32 i = 0; i < trianglesBelow.Count(); ++i) + { + b3ClothTriangleShape* triangle = trianglesBelow[i]; + + b3ClothParticle* p1 = triangle->GetParticle1(); + b3ClothParticle* p2 = triangle->GetParticle2(); + b3ClothParticle* p3 = triangle->GetParticle3(); + + m_cloth.DestroyTriangleShape(triangle); + + if (p1 == pOld) + { + b3ClothTriangleShapeDef tdNew; + tdNew.p1 = pNew; + tdNew.p2 = p2; + tdNew.p3 = p3; + tdNew.v1 = pNew->GetPosition(); + tdNew.v2 = p2->GetPosition(); + tdNew.v3 = p3->GetPosition(); + + m_cloth.CreateTriangleShape(tdNew); + + b3SpringForce* sf1 = FindSpringForce(p1, p2); + if (sf1) + { + b3SpringForceDef sNew; + sNew.p1 = pNew; + sNew.p2 = p2; + sNew.restLength = sf1->GetRestLenght(); + sNew.structural = sf1->GetStructuralStiffness(); + sNew.damping = sf1->GetDampingStiffness(); + + m_cloth.CreateForce(sNew); + + if (HasSpring(trianglesAbove, p1, p2) == false) + { + m_cloth.DestroyForce(sf1); + } + } + + b3SpringForce* sf2 = FindSpringForce(p3, p1); + if (sf2) + { + b3SpringForceDef sNew; + sNew.p1 = p3; + sNew.p2 = pNew; + sNew.restLength = sf2->GetRestLenght(); + sNew.structural = sf2->GetStructuralStiffness(); + sNew.damping = sf2->GetDampingStiffness(); + + m_cloth.CreateForce(sNew); + + if (HasSpring(trianglesAbove, p3, p1) == false) + { + m_cloth.DestroyForce(sf2); + } + } + } + + if (p2 == pOld) + { + b3ClothTriangleShapeDef tdNew; + tdNew.p1 = p1; + tdNew.p2 = pNew; + tdNew.p3 = p3; + tdNew.v1 = p1->GetPosition(); + tdNew.v2 = pNew->GetPosition(); + tdNew.v3 = p3->GetPosition(); + + m_cloth.CreateTriangleShape(tdNew); + + b3SpringForce* sf1 = FindSpringForce(p1, p2); + if (sf1) + { + b3SpringForceDef sNew; + sNew.p1 = p1; + sNew.p2 = pNew; + sNew.restLength = sf1->GetRestLenght(); + sNew.structural = sf1->GetStructuralStiffness(); + sNew.damping = sf1->GetDampingStiffness(); + + m_cloth.CreateForce(sNew); + + if (HasSpring(trianglesAbove, p1, p2) == false) + { + m_cloth.DestroyForce(sf1); + } + } + + b3SpringForce* sf2 = FindSpringForce(p2, p3); + if (sf2) + { + b3SpringForceDef sNew; + sNew.p1 = pNew; + sNew.p2 = p3; + sNew.restLength = sf2->GetRestLenght(); + sNew.structural = sf2->GetStructuralStiffness(); + sNew.damping = sf2->GetDampingStiffness(); + + m_cloth.CreateForce(sNew); + + if (HasSpring(trianglesAbove, p2, p3) == false) + { + m_cloth.DestroyForce(sf2); + } + } + } + + if (p3 == pOld) + { + b3ClothTriangleShapeDef tdNew; + tdNew.p1 = p1; + tdNew.p2 = p2; + tdNew.p3 = pNew; + tdNew.v1 = p1->GetPosition(); + tdNew.v2 = p2->GetPosition(); + tdNew.v3 = pNew->GetPosition(); + + m_cloth.CreateTriangleShape(tdNew); + + b3SpringForce* sf1 = FindSpringForce(p2, p3); + if (sf1) + { + b3SpringForceDef sNew; + sNew.p1 = p2; + sNew.p2 = pNew; + sNew.restLength = sf1->GetRestLenght(); + sNew.structural = sf1->GetStructuralStiffness(); + sNew.damping = sf1->GetDampingStiffness(); + + m_cloth.CreateForce(sNew); + + if (HasSpring(trianglesAbove, p2, p3) == false) + { + m_cloth.DestroyForce(sf1); + } + } + + b3SpringForce* sf2 = FindSpringForce(p3, p1); + if (sf2) + { + b3SpringForceDef sNew; + sNew.p1 = pNew; + sNew.p2 = p1; + sNew.restLength = sf2->GetRestLenght(); + sNew.structural = sf2->GetStructuralStiffness(); + sNew.damping = sf2->GetDampingStiffness(); + + m_cloth.CreateForce(sNew); + + if (HasSpring(trianglesAbove, p3, p1) == false) + { + m_cloth.DestroyForce(sf2); + } + } + } + } + + return true; + } + + bool Tear() + { + b3Force* f = m_cloth.GetForceList().m_head; + while (f) + { + if (f->GetType() != e_springForce) + { + f = f->GetNext(); + continue; + } + + b3SpringForce* s = (b3SpringForce*)f; + f = f->GetNext(); + + b3Vec3 tension = s->GetActionForce(); + + const scalar kMaxTension = 1000.0f; + + if (b3LengthSquared(tension) <= kMaxTension * kMaxTension) + { + continue; + } + + b3ClothParticle* p1 = s->GetParticle1(); + b3ClothParticle* p2 = s->GetParticle2(); + + b3Vec3 x1 = p1->GetPosition(); + b3Vec3 x2 = p2->GetPosition(); + + if (p1->GetType() == e_dynamicClothParticle) + { + b3Vec3 n = b3Normalize(x2 - x1); + b3Plane plane(n, x1); + + bool wasSplit = SplitParticle(p1, plane); + if (wasSplit) + { + return true; + } + } + + if (p2->GetType() == e_dynamicClothParticle) + { + b3Vec3 n = b3Normalize(x1 - x2); + b3Plane plane(n, x2); + + bool wasSplit = SplitParticle(p2, plane); + if (wasSplit) + { + return true; + } + } + } + + return false; + } + + void Step() + { + Test::Step(); + + m_cloth.Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); + + while (Tear()); + + m_cloth.Draw(); + + DrawSpringForces(); + + if (m_clothDragger->IsDragging()) + { + b3Vec3 pA = m_clothDragger->GetPointA(); + b3Vec3 pB = m_clothDragger->GetPointB(); + + g_draw->DrawPoint(pA, 4.0f, b3Color_green); + + g_draw->DrawPoint(pB, 4.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_clothSolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); + + scalar E = m_cloth.GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->Drag(); + } + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_clothDragger->IsDragging() == false) + { + m_clothDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->StopDragging(); + } + } + + static Test* Create() + { + return new ClothTearing(); + } + + b3Cloth m_cloth; + b3ClothDragger* m_clothDragger; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/cluster.h b/examples/testbed/tests/cluster.h index 74bf446..45412eb 100644 --- a/examples/testbed/tests/cluster.h +++ b/examples/testbed/tests/cluster.h @@ -38,9 +38,9 @@ public: { for (u32 i = 0; i < e_count; ++i) { - float32 x = 3.0f * RandomFloat(-1.0f, 1.0f); - float32 y = 3.0f * RandomFloat(-1.0f, 1.0f); - float32 z = 3.0f * RandomFloat(-1.0f, 1.0f); + scalar x = 3.0f * RandomFloat(-1.0f, 1.0f); + scalar y = 3.0f * RandomFloat(-1.0f, 1.0f); + scalar z = 3.0f * RandomFloat(-1.0f, 1.0f); b3Vec3 p(x, y, z); m_points[i] = p; @@ -48,9 +48,9 @@ public: for (u32 i = 0; i < B3_MAX_MANIFOLDS; ++i) { - float32 r = RandomFloat(0.0f, 1.0f); - float32 g = RandomFloat(0.0f, 1.0f); - float32 b = RandomFloat(0.0f, 1.0f); + scalar r = RandomFloat(0.0f, 1.0f); + scalar g = RandomFloat(0.0f, 1.0f); + scalar b = RandomFloat(0.0f, 1.0f); b3Color c(r, g, b); m_colors[i] = c; diff --git a/examples/testbed/tests/collide_test.h b/examples/testbed/tests/collide_test.h index f76d704..427dfb1 100644 --- a/examples/testbed/tests/collide_test.h +++ b/examples/testbed/tests/collide_test.h @@ -68,46 +68,43 @@ public: { if (key == GLFW_KEY_LEFT) { - m_xfB.position.x -= 0.05f; + m_xfB.translation.x -= 0.05f; } if (key == GLFW_KEY_RIGHT) { - m_xfB.position.x += 0.05f; + m_xfB.translation.x += 0.05f; } if (key == GLFW_KEY_UP) { - m_xfB.position.y += 0.05f; + m_xfB.translation.y += 0.05f; } if (key == GLFW_KEY_DOWN) { - m_xfB.position.y -= 0.05f; + m_xfB.translation.y -= 0.05f; } if (key == GLFW_KEY_X) { - b3Quat qx(b3Vec3(1.0f, 0.0f, 0.0f), 0.05f * B3_PI); - b3Mat33 xfx = b3QuatMat33(qx); + b3Quat qx = b3QuatRotationX(0.05f * B3_PI); - m_xfB.rotation = m_xfB.rotation * xfx; + m_xfB.rotation = m_xfB.rotation * qx; } if (key == GLFW_KEY_Y) { - b3Quat qy(b3Vec3(0.0f, 1.0f, 0.0f), 0.05f * B3_PI); - b3Mat33 xfy = b3QuatMat33(qy); + b3Quat qy = b3QuatRotationY(0.05f * B3_PI); - m_xfB.rotation = m_xfB.rotation * xfy; + m_xfB.rotation = m_xfB.rotation * qy; } if (key == GLFW_KEY_Z) { - b3Quat qy(b3Vec3(0.0f, 0.0f, 1.0f), 0.05f * B3_PI); - b3Mat33 xfz = b3QuatMat33(qy); + b3Quat qz = b3QuatRotationZ(0.05f * B3_PI); - m_xfB.rotation = m_xfB.rotation * xfz; + m_xfB.rotation = m_xfB.rotation * qz; } } diff --git a/examples/testbed/tests/compound_body.h b/examples/testbed/tests/compound_body.h index a1d1922..426594a 100644 --- a/examples/testbed/tests/compound_body.h +++ b/examples/testbed/tests/compound_body.h @@ -38,38 +38,33 @@ public: } { - b3Transform xf; - xf.SetIdentity(); - xf.position.Set(-5.0f, 10.0f, 0.0f); - m_box1.SetTransform(xf); + m_box1.SetExtents(1.0f, 1.0f, 1.0f); + b3Vec3 translation(-5.0f, 10.0f, 0.0f); + m_box1.Translate(translation); } { - b3Transform xf; - xf.SetIdentity(); - xf.position.Set(5.0f, 10.0f, 0.0f); - m_box2.SetTransform(xf); + b3Vec3 translation(5.0f, 10.0f, 0.0f); + m_box2.SetExtents(1.0f, 1.0f, 1.0f); + m_box2.Translate(translation); } { - b3Transform xf; - xf.SetIdentity(); - xf.position.Set(0.0f, 2.0f, 0.0f); - m_box3.SetTransform(xf); + b3Vec3 translation(0.0f, 2.0f, 0.0f); + m_box3.SetExtents(1.0f, 1.0f, 1.0f); + m_box3.Translate(translation); } { - b3Transform xf; - xf.SetIdentity(); - xf.position.Set(0.0f, 6.0f, 0.0f); - m_box4.SetTransform(xf); + b3Vec3 translation(0.0f, 6.0f, 0.0f); + m_box4.SetExtents(1.0f, 1.0f, 1.0f); + m_box4.Translate(translation); } { - b3Transform xf; - xf.SetIdentity(); - xf.position.Set(0.0f, 10.0f, 0.0f); - m_box5.SetTransform(xf); + b3Vec3 translation(0.0f, 10.0f, 0.0f); + m_box5.SetExtents(1.0f, 1.0f, 1.0f); + m_box5.Translate(translation); } { diff --git a/examples/testbed/tests/cone_test.h b/examples/testbed/tests/cone_test.h index a75a598..7c44fb7 100644 --- a/examples/testbed/tests/cone_test.h +++ b/examples/testbed/tests/cone_test.h @@ -42,8 +42,8 @@ public: head = m_world.CreateBody(bd); b3CapsuleShape cs; - cs.m_centers[0].Set(0.0f, 0.15f, 0.0f); - cs.m_centers[1].Set(0.0f, -0.15f, 0.0f); + cs.m_vertex1.Set(0.0f, 0.15f, 0.0f); + cs.m_vertex2.Set(0.0f, -0.15f, 0.0f); cs.m_radius = 0.5f; b3ShapeDef sd; @@ -55,19 +55,22 @@ public: { b3Vec3 anchor(0.0f, 0.0f, 0.0f); b3Vec3 axis(0.0f, 1.0f, 0.0f); - float32 coneAngle = 0.5f * B3_PI; + scalar coneAngle = 0.5f * B3_PI; b3ConeJointDef cd; cd.Initialize(ref, head, axis, anchor, coneAngle); - cd.enableLimit = true; + cd.enableConeLimit = true; + cd.enableTwistLimit = true; + cd.lowerAngle = 0.0f; + cd.upperAngle = B3_PI; + b3ConeJoint* cj = (b3ConeJoint*)m_world.CreateJoint(cd); } // Invalidate the orientation - b3Vec3 axis(1.0f, 0.0f, 0.0f); - float32 angle = B3_PI; - head->SetTransform(head->GetPosition(), axis, angle); + b3Quat q = b3QuatRotationX(B3_PI); + head->SetTransform(head->GetPosition(), q); } static Test* Create() diff --git a/examples/testbed/tests/convex_cast.h b/examples/testbed/tests/convex_cast.h new file mode 100644 index 0000000..c67075a --- /dev/null +++ b/examples/testbed/tests/convex_cast.h @@ -0,0 +1,282 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef CONVEX_CAST_H +#define CONVEX_CAST_H + +class ConvexCast : public Test +{ +public: + ConvexCast() + { + { + b3BodyDef bdef; + b3Body* body = m_world.CreateBody(bdef); + + b3HullShape hs; + hs.m_hull = &m_groundHull; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(0.0f, 2.0f, 0.0f); + + b3Body* body = m_world.CreateBody(bdef); + + b3HullShape hs; + hs.m_hull = &b3BoxHull_identity; + + b3ShapeDef sdef; + sdef.shape = &hs; + + m_shape = body->CreateShape(sdef); + } + + { + m_grid.BuildTree(); + m_grid.BuildAdjacency(); + + b3BodyDef bdef; + bdef.position.Set(-10.0f, 5.0f, -2.0f); + bdef.orientation = b3QuatRotationZ(0.25f * B3_PI); + + b3Body* body = m_world.CreateBody(bdef); + + b3MeshShape hs; + hs.m_mesh = &m_grid; + hs.m_scale.Set(-1.0f, -1.0f, -2.0f); + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(0.0f, 2.0f, 10.0f); + bdef.orientation = b3QuatRotationY(0.25f * B3_PI); + + b3Body* body = m_world.CreateBody(bdef); + + b3HullShape hs; + hs.m_hull = &b3BoxHull_identity; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(-10.0f, 6.0f, -10.0f); + bdef.orientation = b3QuatRotationY(0.25f * B3_PI); + + b3Body* body = m_world.CreateBody(bdef); + + static b3BoxHull boxHull(2.0f, 4.0f, 0.5f); + + b3HullShape hs; + hs.m_hull = &boxHull; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(10.0f, 2.0f, 0.0f); + bdef.orientation = b3QuatRotationY(0.20f * B3_PI); + + b3Body* body = m_world.CreateBody(bdef); + + b3HullShape hs; + hs.m_hull = &b3BoxHull_identity; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(-10.0f, 2.0f, 14.0f); + bdef.orientation = b3QuatRotationY(0.05f * B3_PI); + + b3Body* body = m_world.CreateBody(bdef); + + b3HullShape hs; + hs.m_hull = &b3BoxHull_identity; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(-14.0f, 2.0f, 5.0f); + bdef.orientation = b3QuatRotationY(-0.05f * B3_PI); + + b3Body* body = m_world.CreateBody(bdef); + + b3HullShape hs; + hs.m_hull = &b3BoxHull_identity; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(20.0f, 2.0f, 5.0f); + bdef.orientation = b3QuatRotationY(-0.05f * B3_PI); + + b3Body* body = m_world.CreateBody(bdef); + + b3HullShape hs; + hs.m_hull = &b3BoxHull_identity; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(12.0f, 2.0f, 5.0f); + bdef.orientation = b3QuatRotationY(-0.35f * B3_PI); + + b3Body* body = m_world.CreateBody(bdef); + + b3SphereShape hs; + hs.m_center.SetZero(); + hs.m_radius = 2.5f; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(0.0f, 1.0f, -12.0f); + + b3Body* body = m_world.CreateBody(bdef); + + b3CapsuleShape hs; + hs.m_vertex1.Set(0.0f, 1.0f, 0.0f); + hs.m_vertex2.Set(0.0f, -1.0f, 0.0f); + hs.m_radius = 3.0f; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + m_d.Set(-50.0f, 2.0f, 0.0f); + } + + void CastConvex() const + { + class ConvexCastFilter : public b3ConvexCastFilter + { + public: + bool ShouldConvexCast(b3Shape* shape) + { + return true; + } + }; + + ConvexCastFilter filter; + + b3Body* body = m_shape->GetBody(); + b3Transform xf = body->GetTransform(); + + m_world.DrawSolidShape(xf, m_shape, b3Color_red); + + b3Vec3 p1 = xf.translation; + + b3Vec3 p2 = p1 + m_d; + + b3ConvexCastSingleOutput out; + if (m_world.ConvexCastSingle(&out, &filter, m_shape, m_d)) + { + g_draw->DrawPoint(out.point, 4.0f, b3Color_red); + g_draw->DrawSegment(out.point, out.point + out.normal, b3Color_white); + + b3Transform xft; + xft.rotation = xf.rotation; + xft.translation = xf.translation + out.fraction * m_d; + + m_world.DrawShape(xft, m_shape, b3Color_red); + + g_draw->DrawSegment(p1, xft.translation, b3Color_green); + } + else + { + g_draw->DrawSegment(p1, p2, b3Color_green); + + b3Transform xf1; + xf1.rotation = xf.rotation; + xf1.translation = xf.translation + m_d; + + m_world.DrawShape(xf1, m_shape, b3Color_red); + } + } + + void Step() + { + scalar dt = g_testSettings->inv_hertz; + + b3Quat q = b3QuatRotationY(0.05f * B3_PI * dt); + + m_d = b3Mul(q, m_d); + + CastConvex(); + + Test::Step(); + } + + static Test* Create() + { + return new ConvexCast(); + } + + b3GridMesh<5, 5> m_grid; + + b3Shape* m_shape; + b3Vec3 m_d; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/convex_hull.h b/examples/testbed/tests/convex_hull.h index 1ddb27d..f71ff6a 100644 --- a/examples/testbed/tests/convex_hull.h +++ b/examples/testbed/tests/convex_hull.h @@ -66,9 +66,9 @@ public: m_count = 0; for (u32 i = 0; i < e_count; ++i) { - float32 x = 3.0f * RandomFloat(-1.0f, 1.0f); - float32 y = 3.0f * RandomFloat(-1.0f, 1.0f); - float32 z = 3.0f * RandomFloat(-1.0f, 1.0f); + float x = 3.0f * RandomFloat(-1.0f, 1.0f); + float y = 3.0f * RandomFloat(-1.0f, 1.0f); + float z = 3.0f * RandomFloat(-1.0f, 1.0f); // Clamp to force coplanarities. // This will stress the convex hull creation code. @@ -135,7 +135,7 @@ public: edge = m_hull.GetEdge(edge->next); } while (edge != begin); - c /= float32(vn); + c /= scalar(vn); g_draw->DrawSegment(c, c + n, b3Color_white); } diff --git a/examples/testbed/tests/conveyor_belt.h b/examples/testbed/tests/conveyor_belt.h new file mode 100644 index 0000000..774a3d9 --- /dev/null +++ b/examples/testbed/tests/conveyor_belt.h @@ -0,0 +1,119 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef CONVEYOR_BELT_H +#define CONVEYOR_BELT_H + +class ConveyorBelt : public Test +{ +public: + + ConveyorBelt() + { + { + // Ground + b3BodyDef bd; + b3Body* body = m_world.CreateBody(bd); + + b3HullShape hs; + hs.m_hull = &m_groundHull; + + b3ShapeDef sd; + sd.shape = &hs; + + body->CreateShape(sd); + } + + { + // Platform + b3BodyDef bd; + bd.position.Set(0.0f, 5.0f, 0.0f); + b3Body* body = m_world.CreateBody(bd); + + m_platformHull.SetExtents(10.0f, 0.5f, 2.0f); + + b3HullShape hs; + hs.m_hull = &m_platformHull; + + b3ShapeDef sd; + sd.shape = &hs; + sd.friction = 0.8f; + m_platform = body->CreateShape(sd); + } + + // Boxes + m_boxHull.SetExtents(0.5f, 0.5f, 0.5f); + for (u32 i = 0; i < 5; ++i) + { + b3BodyDef bd; + bd.type = e_dynamicBody; + bd.position.Set(2.0f * i, 7.0f, 0.0f); + b3Body* body = m_world.CreateBody(bd); + + b3HullShape hs; + hs.m_hull = &m_boxHull; + + b3ShapeDef sd; + sd.shape = &hs; + sd.density = 0.2f; + + body->CreateShape(sd); + } + } + + void PreSolve(b3Contact* contact) + { + Test::PreSolve(contact); + + b3Shape* shapeA = contact->GetShapeA(); + b3Shape* shapeB = contact->GetShapeB(); + + if (shapeA == m_platform) + { + for (u32 i = 0; i < contact->GetManifoldCount(); ++i) + { + b3Manifold* manifold = contact->GetManifold(i); + + manifold->motorSpeed = 0.25f * B3_PI; + manifold->tangentSpeed2 = -2.0f; + } + } + + if (shapeB == m_platform) + { + for (u32 i = 0; i < contact->GetManifoldCount(); ++i) + { + b3Manifold* manifold = contact->GetManifold(i); + + manifold->motorSpeed = -0.25f * B3_PI; + manifold->tangentSpeed2 = 2.0f; + } + } + } + + static Test* Create() + { + return new ConveyorBelt(); + } + + b3BoxHull m_platformHull; + b3Shape* m_platform; + b3BoxHull m_boxHull; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/deep_capsule.h b/examples/testbed/tests/deep_capsule.h index 98eb010..9a60410 100644 --- a/examples/testbed/tests/deep_capsule.h +++ b/examples/testbed/tests/deep_capsule.h @@ -24,23 +24,17 @@ class DeepCapsule : public Collide public: DeepCapsule() { - m_xfA.position.Set(0.0f, 0.0f, 0.0f); - m_xfA.rotation = b3QuatMat33(b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), 0.55f * B3_PI)); - - m_sA.m_centers[0].Set(1.0f, -1.0f, 0.0f); - m_sA.m_centers[1].Set(0.0f, 1.0f, 0.0f); - m_sA.m_radius = 2.0f; + m_box.SetExtents(4.0f, 1.0f, 4.0f); + m_sA.m_hull = &m_box; - m_xfB.position.Set(0.f, 0.0f, 0.0f); - m_xfB.rotation = b3QuatMat33(b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), 0.0f * B3_PI)); + m_xfA.SetIdentity(); - b3Transform xf; - xf.SetIdentity(); - xf.rotation = b3Diagonal(4.0f, 1.0f, 4.0f); + m_sB.m_vertex1.Set(1.0f, -1.0f, 0.0f); + m_sB.m_vertex2.Set(0.0f, 1.0f, 0.0f); + m_sB.m_radius = 2.0f; - m_box.SetTransform(xf); - - m_sB.m_hull = &m_box; + m_xfB.translation.Set(0.0f, 0.0f, 0.0f); + m_xfB.rotation = b3QuatRotationZ(0.55f * B3_PI); m_shapeA = &m_sA; m_shapeB = &m_sB; @@ -52,8 +46,8 @@ public: return new DeepCapsule(); } - b3CapsuleShape m_sA; - b3HullShape m_sB; + b3HullShape m_sA; + b3CapsuleShape m_sB; b3BoxHull m_box; }; diff --git a/examples/testbed/tests/distance_test.h b/examples/testbed/tests/distance_test.h index 4f659c7..dd86512 100644 --- a/examples/testbed/tests/distance_test.h +++ b/examples/testbed/tests/distance_test.h @@ -24,15 +24,11 @@ class Distance : public Test public: Distance() { - m_xfA.SetIdentity(); - m_xfA.position.Set(-5.0f, 0.0f, 0.0f); + m_xfA.translation.Set(-5.0f, 0.0f, 0.0f); m_xfA.rotation.SetIdentity(); - m_shapeA.m_centers[0].Set(0.0f, -2.0f, 0.0f); - m_shapeA.m_centers[1].Set(0.0f, 2.0f, 0.0f); - m_shapeA.m_radius = 1.0f; + m_shapeA.m_hull = &b3BoxHull_identity; - m_xfB.SetIdentity(); - m_xfB.position.Set(5.0f, 0.0f, 0.0f); + m_xfB.translation.Set(5.0f, 0.0f, 0.0f); m_xfB.rotation.SetIdentity(); m_shapeB.m_hull = &b3BoxHull_identity; @@ -79,46 +75,43 @@ public: { if (key == GLFW_KEY_LEFT) { - m_xfB.position.x -= 0.05f; + m_xfB.translation.x -= 0.05f; } if (key == GLFW_KEY_RIGHT) { - m_xfB.position.x += 0.05f; + m_xfB.translation.x += 0.05f; } if (key == GLFW_KEY_UP) { - m_xfB.position.y += 0.05f; + m_xfB.translation.y += 0.05f; } if (key == GLFW_KEY_DOWN) { - m_xfB.position.y -= 0.05f; + m_xfB.translation.y -= 0.05f; } if (key == GLFW_KEY_X) { - b3Quat qx(b3Vec3(1.0f, 0.0f, 0.0f), 0.05f * B3_PI); - b3Mat33 xfx = b3QuatMat33(qx); + b3Quat qx = b3QuatRotationX(0.05f * B3_PI); - m_xfB.rotation = m_xfB.rotation * xfx; + m_xfB.rotation = m_xfB.rotation * qx; } if (key == GLFW_KEY_Y) { - b3Quat qy(b3Vec3(0.0f, 1.0f, 0.0f), 0.05f * B3_PI); - b3Mat33 xfy = b3QuatMat33(qy); + b3Quat qy = b3QuatRotationY(0.05f * B3_PI); - m_xfB.rotation = m_xfB.rotation * xfy; + m_xfB.rotation = m_xfB.rotation * qy; } if (key == GLFW_KEY_Z) { - b3Quat qy(b3Vec3(0.0f, 0.0f, 1.0f), 0.05f * B3_PI); - b3Mat33 xfz = b3QuatMat33(qy); + b3Quat qz = b3QuatRotationZ(0.05f * B3_PI); - m_xfB.rotation = m_xfB.rotation * xfz; + m_xfB.rotation = m_xfB.rotation * qz; } } @@ -127,7 +120,7 @@ public: return new Distance(); } - b3CapsuleShape m_shapeA; + b3HullShape m_shapeA; b3Transform m_xfA; b3ShapeGJKProxy m_proxyA; diff --git a/examples/testbed/tests/gyro_motion.h b/examples/testbed/tests/gyro_motion.h index 2d35f1b..0cc4b11 100644 --- a/examples/testbed/tests/gyro_motion.h +++ b/examples/testbed/tests/gyro_motion.h @@ -40,14 +40,14 @@ public: { b3BodyDef bdef; bdef.type = e_dynamicBody; - bdef.orientation.Set(b3Vec3(1.0f, 0.0f, 0.0f), 0.5f * B3_PI); + bdef.orientation.SetAxisAngle(b3Vec3(1.0f, 0.0f, 0.0f), 0.5f * B3_PI); bdef.position.Set(0.0f, 10.0f, 0.0f); bdef.angularVelocity.Set(0.0f, 0.0f, 4.0f * B3_PI); b3Body* body = m_world.CreateBody(bdef); { - m_rotorBox.Set(1.0f, 0.5f, 7.0f); + m_rotorBox.SetExtents(1.0f, 0.5f, 7.0f); b3HullShape hull; hull.m_hull = &m_rotorBox; @@ -60,7 +60,7 @@ public: } { - m_cylinderHull.SetAsCylinder(0.95f, 4.0f); + m_cylinderHull.SetExtents(0.95f, 4.0f); b3HullShape hull; hull.m_hull = &m_cylinderHull; @@ -86,7 +86,7 @@ public: } b3BoxHull m_rotorBox; - b3QHull m_cylinderHull; + b3CylinderHull m_cylinderHull; }; #endif \ No newline at end of file diff --git a/examples/testbed/tests/hinge_chain.h b/examples/testbed/tests/hinge_chain.h index 7c0e297..b9075d0 100644 --- a/examples/testbed/tests/hinge_chain.h +++ b/examples/testbed/tests/hinge_chain.h @@ -24,16 +24,10 @@ class HingeChain : public Test public: HingeChain() { - static b3BoxHull doorHull; - { - b3Transform xf; - xf.position.SetZero(); - xf.rotation = b3Diagonal(2.0f, 4.0f, 0.5f); - doorHull.SetTransform(xf); - } + static b3BoxHull box(2.0f, 4.0f, 0.5f); - float32 x = -50.0f; - float32 y = 0.0f; + scalar x = -50.0f; + scalar y = 0.0f; b3Body* lastHinge; { @@ -42,7 +36,7 @@ public: lastHinge = m_world.CreateBody(bd); b3HullShape hull; - hull.m_hull = &doorHull; + hull.m_hull = &box; b3ShapeDef sdef; sdef.shape = &hull; @@ -60,7 +54,7 @@ public: b3Body* hinge = m_world.CreateBody(bd); b3HullShape hull; - hull.m_hull = &doorHull; + hull.m_hull = &box; b3ShapeDef sdef; sdef.shape = &hull; diff --git a/examples/testbed/tests/hull_collision.h b/examples/testbed/tests/hull_collision.h index 1a24a06..c5b35ea 100644 --- a/examples/testbed/tests/hull_collision.h +++ b/examples/testbed/tests/hull_collision.h @@ -29,7 +29,7 @@ public: HullCollision() { - m_xfA.position.Set(0.0f, 1.5f, 0.0f); + m_xfA.translation.Set(0.0f, 1.5f, 0.0f); m_xfA.rotation.SetIdentity(); m_xfB.SetIdentity(); @@ -53,9 +53,9 @@ public: { for (u32 i = 0; i < e_count; ++i) { - float32 x = 3.0f * RandomFloat(-1.0f, 1.0f); - float32 y = 3.0f * RandomFloat(-1.0f, 1.0f); - float32 z = 3.0f * RandomFloat(-1.0f, 1.0f); + float x = 3.0f * RandomFloat(-1.0f, 1.0f); + float y = 3.0f * RandomFloat(-1.0f, 1.0f); + float z = 3.0f * RandomFloat(-1.0f, 1.0f); x = b3Clamp(x, -2.5f, 2.5f); y = b3Clamp(y, -2.5f, 2.5f); @@ -68,9 +68,9 @@ public: for (u32 i = 0; i < e_count; ++i) { - float32 x = 3.0f * RandomFloat(-1.0f, 1.0f); - float32 y = 3.0f * RandomFloat(-1.0f, 1.0f); - float32 z = 3.0f * RandomFloat(-1.0f, 1.0f); + float x = 3.0f * RandomFloat(-1.0f, 1.0f); + float y = 3.0f * RandomFloat(-1.0f, 1.0f); + float z = 3.0f * RandomFloat(-1.0f, 1.0f); x = b3Clamp(x, -2.5f, 2.5f); y = b3Clamp(y, -2.5f, 2.5f); @@ -98,7 +98,7 @@ public: sB.m_hull = &hull2; m_shapeB = &sB; - g_draw->DrawString(b3Color_white, "G - Generate a random convex hull pair"); + g_draw->DrawString(b3Color_white, "G - Generate random convex hulls"); Collide::Step(); } diff --git a/examples/testbed/tests/hull_contact_test.h b/examples/testbed/tests/hull_contact_test.h index 186f387..1a56c31 100644 --- a/examples/testbed/tests/hull_contact_test.h +++ b/examples/testbed/tests/hull_contact_test.h @@ -53,9 +53,9 @@ public: { // Clamp to force coplanarities. // This will stress the generation code. - float32 x = 3.0f * RandomFloat(-1.0f, 1.0f); - float32 y = 3.0f * RandomFloat(-1.0f, 1.0f); - float32 z = 3.0f * RandomFloat(-1.0f, 1.0f); + float x = 3.0f * RandomFloat(-1.0f, 1.0f); + float y = 3.0f * RandomFloat(-1.0f, 1.0f); + float z = 3.0f * RandomFloat(-1.0f, 1.0f); x = b3Clamp(x, -1.5f, 1.5f); y = b3Clamp(y, -1.5f, 1.5f); diff --git a/examples/testbed/tests/initial_overlap.h b/examples/testbed/tests/initial_overlap.h index fbfeec9..9b349de 100644 --- a/examples/testbed/tests/initial_overlap.h +++ b/examples/testbed/tests/initial_overlap.h @@ -38,14 +38,7 @@ public: } b3Vec3 boxScale(1.0f, 0.5f, 2.0f); - - static b3BoxHull boxHull; - - b3Transform m; - m.rotation = b3Diagonal(boxScale.x, boxScale.y, boxScale.z); - m.position.SetZero(); - - boxHull.SetTransform(m); + static b3BoxHull boxHull(boxScale.x, boxScale.y, boxScale.z); { b3BodyDef bd; @@ -70,8 +63,8 @@ public: bd.type = e_dynamicBody; bd.position.Set(0.0f, 1.5f, 0.0f); - b3Quat q_y(b3Vec3(0.0f, 1.0f, 0.0f), 0.4f * B3_PI); - b3Quat q_z(b3Vec3(0.0f, 0.0f, 1.0f), 0.04f * B3_PI); + b3Quat q_y = b3QuatRotationY(0.4f * B3_PI); + b3Quat q_z = b3QuatRotationZ(0.04f * B3_PI); b3Quat q = q_z * q_y; bd.orientation = q; diff --git a/examples/testbed/tests/jenga.h b/examples/testbed/tests/jenga.h index 7079d82..394e8ba 100644 --- a/examples/testbed/tests/jenga.h +++ b/examples/testbed/tests/jenga.h @@ -45,15 +45,9 @@ public: b3Vec3 boxScale(1.0f, 0.5f, 3.0f); - static b3BoxHull boxHull; + static b3BoxHull boxHull(boxScale.x, boxScale.y, boxScale.z); - b3Transform m; - m.rotation = b3Diagonal(boxScale.x, boxScale.y, boxScale.z); - m.position.SetZero(); - - boxHull.SetTransform(m); - - float32 y = 2.0f; + scalar y = 2.0f; for (u32 i = 0; i < e_layerCount / 2; ++i) { @@ -62,7 +56,7 @@ public: b3BodyDef bd; bd.type = b3BodyType::e_dynamicBody; - bd.position.x = 2.0f * float32(j) * boxScale.x; + bd.position.x = 2.0f * scalar(j) * boxScale.x; bd.position.y = y; bd.position.z = 0.0f; @@ -86,11 +80,11 @@ public: b3BodyDef bd; bd.type = b3BodyType::e_dynamicBody; - bd.orientation.Set(b3Vec3(0.0f, 1.0f, 0.0f), 0.5f * B3_PI); + bd.orientation = b3QuatRotationY(0.5f * B3_PI); bd.position.x = 2.0f * boxScale.x; bd.position.y = y; - bd.position.z = -2.0f * boxScale.x + 2.0f * float32(j) * boxScale.x; + bd.position.z = -2.0f * boxScale.x + 2.0f * scalar(j) * boxScale.x; b3Body* body = m_world.CreateBody(bd); diff --git a/examples/testbed/tests/linear_motion.h b/examples/testbed/tests/linear_motion.h index b94b5ac..0f35b48 100644 --- a/examples/testbed/tests/linear_motion.h +++ b/examples/testbed/tests/linear_motion.h @@ -34,8 +34,8 @@ public: m_body = m_world.CreateBody(bdef); b3CapsuleShape shape; - shape.m_centers[0].Set(0.0f, 1.0f, 0.0f); - shape.m_centers[1].Set(0.0f, -1.0f, 0.0f); + shape.m_vertex1.Set(0.0f, 1.0f, 0.0f); + shape.m_vertex2.Set(0.0f, -1.0f, 0.0f); shape.m_radius = 1.0f; b3ShapeDef sdef; @@ -55,16 +55,11 @@ public: { Test::Step(); - b3Vec3 q(0.0f, 0.0f, 0.0f); - b3Vec3 p = m_body->GetTransform().position; - if (b3Distance(p, q) > 50.0f) + b3Vec3 p(0.0f, 0.0f, 0.0f); + if (b3Distance(m_body->GetPosition(), p) > 50.0f) { - b3Quat quat = m_body->GetSweep().orientation; - - b3Vec3 axis; - float32 angle; - quat.GetAxisAngle(&axis, &angle); - m_body->SetTransform(q, axis, angle); + b3Quat q = m_body->GetOrientation(); + m_body->SetTransform(p, q); } } diff --git a/examples/testbed/tests/linear_time_of_impact.h b/examples/testbed/tests/linear_time_of_impact.h new file mode 100644 index 0000000..a1d8228 --- /dev/null +++ b/examples/testbed/tests/linear_time_of_impact.h @@ -0,0 +1,171 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef LINEAR_TIME_OF_IMPACT_H +#define LINEAR_TIME_OF_IMPACT_H + +class LinearTimeOfImpact : public Test +{ +public: + LinearTimeOfImpact() + { + m_shapeA.m_hull = &b3BoxHull_identity; + m_shapeA.m_radius = 0.0f; + + m_shapeB.m_hull = &b3BoxHull_identity; + m_shapeB.m_radius = 0.0f; + + m_xfA.translation.Set(0.0f, 0.0f, 0.0f); + m_xfA.rotation.SetIdentity(); + + m_xfB.translation.Set(5.0f, 1.0f, 0.0f); + m_xfB.rotation.SetIdentity(); + + m_proxyA.Set(&m_shapeA, 0); + m_proxyB.Set(&m_shapeB, 0); + } + + void Step() + { + b3Vec3 dA = b3Vec3_zero; + b3Vec3 dB(-10.0f, 0.0f, 0.0f); + + b3TOIOutput out = b3TimeOfImpact(m_xfA, m_proxyA, dA, m_xfB, m_proxyB, dB); + + b3TOIOutput::State state = out.state; + scalar t = out.t; + u32 iterations = out.iterations; + + if (state == b3TOIOutput::e_touching) + { + b3Transform xfA; + xfA.rotation = m_xfA.rotation; + xfA.translation = m_xfA.translation + t * dA; + + b3Transform xfB; + xfB.rotation = m_xfB.rotation; + xfB.translation = m_xfB.translation + t * dB; + + b3GJKOutput query = b3GJK(xfA, m_proxyA, xfB, m_proxyB, false); + + b3Vec3 p1 = query.point1; + b3Vec3 p2 = query.point2; + b3Vec3 n1 = b3Normalize(p2 - p1); + b3Vec3 p = 0.5f * (p1 + p2); + + g_draw->DrawPoint(p, 4.0f, b3Color_green); + g_draw->DrawSegment(p1, p1 + n1, b3Color_green); + + m_world.DrawShape(xfA, &m_shapeA, b3Color_black); + m_world.DrawShape(xfB, &m_shapeB, b3Color_black); + } + + if (state == b3TOIOutput::e_failed) + { + g_draw->DrawString(b3Color_white, "State = Failed"); + } + else if (state == b3TOIOutput::e_overlapped) + { + g_draw->DrawString(b3Color_white, "State = Overlapped"); + } + else if (state == b3TOIOutput::e_separated) + { + g_draw->DrawString(b3Color_white, "State = Separated!"); + } + else if (state == b3TOIOutput::e_touching) + { + g_draw->DrawString(b3Color_white, "State = Touching!"); + } + + g_draw->DrawString(b3Color_white, "Iterations = %d", out.iterations); + + g_draw->DrawString(b3Color_white, "Left/Right/Up/Down Arrow - Translate shape"); + g_draw->DrawString(b3Color_white, "X/Y/Z - Rotate shape"); + + g_draw->DrawTransform(m_xfA); + g_draw->DrawTransform(m_xfB); + + m_world.DrawShape(m_xfA, &m_shapeA, b3Color_black); + m_world.DrawShape(m_xfB, &m_shapeB, b3Color_black); + + m_world.DrawSolidShape(m_xfA, &m_shapeA, b3Color_white); + m_world.DrawSolidShape(m_xfB, &m_shapeB, b3Color_white); + + g_draw->DrawSegment(m_xfA.translation, m_xfA.translation + dA, b3Color_white); + g_draw->DrawSegment(m_xfB.translation, m_xfB.translation + dB, b3Color_white); + } + + void KeyDown(int key) + { + if (key == GLFW_KEY_LEFT) + { + m_xfB.translation.x -= 0.105f; + } + + if (key == GLFW_KEY_RIGHT) + { + m_xfB.translation.x += 0.105f; + } + + if (key == GLFW_KEY_UP) + { + m_xfB.translation.y += 0.105f; + } + + if (key == GLFW_KEY_DOWN) + { + m_xfB.translation.y -= 0.105f; + } + + if (key == GLFW_KEY_X) + { + b3Quat qx = b3QuatRotationX(0.05f * B3_PI); + + m_xfB.rotation = m_xfB.rotation * qx; + } + + if (key == GLFW_KEY_Y) + { + b3Quat qy = b3QuatRotationY(0.05f * B3_PI); + + m_xfB.rotation = m_xfB.rotation * qy; + } + + if (key == GLFW_KEY_Z) + { + b3Quat qz = b3QuatRotationZ(0.05f * B3_PI); + + m_xfB.rotation = m_xfB.rotation * qz; + } + } + + static Test* Create() + { + return new LinearTimeOfImpact(); + } + + b3HullShape m_shapeA; + b3Transform m_xfA; + b3ShapeGJKProxy m_proxyA; + + b3HullShape m_shapeB; + b3Transform m_xfB; + b3ShapeGJKProxy m_proxyB; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/mesh_contact_test.h b/examples/testbed/tests/mesh_contact_test.h index e68dd41..19ad7b8 100644 --- a/examples/testbed/tests/mesh_contact_test.h +++ b/examples/testbed/tests/mesh_contact_test.h @@ -25,6 +25,7 @@ public: MeshContactTest() { m_gridMesh.BuildTree(); + m_gridMesh.BuildAdjacency(); // Transform grid into a terrain for (u32 i = 0; i < m_terrainMesh.vertexCount; ++i) @@ -33,18 +34,22 @@ public: } m_terrainMesh.BuildTree(); + m_terrainMesh.BuildAdjacency(); { b3BodyDef bd; - m_ground = m_world.CreateBody(bd); + b3Body* groundBody = m_world.CreateBody(bd); b3MeshShape ms; ms.m_mesh = &m_gridMesh; + ms.m_scale.Set(2.0f, 1.0f, 2.0f); b3ShapeDef sd; sd.shape = &ms; - m_ground->CreateShape(sd); + m_groundShape = (b3MeshShape*)groundBody->CreateShape(sd); + + m_selection = m_groundShape->m_mesh->triangleCount / 2; } { @@ -52,8 +57,8 @@ public: bd.type = b3BodyType::e_dynamicBody; bd.position.Set(0.0f, 5.0f, 0.0f); - m_body = m_world.CreateBody(bd); - + b3Body* body = m_world.CreateBody(bd); + { b3SphereShape sphere; sphere.m_center.SetZero(); @@ -64,25 +69,43 @@ public: sd.density = 1.0f; sd.friction = 0.5f; - m_body->CreateShape(sd); + m_bodyShape = body->CreateShape(sd); } } + + m_drawEdgeTypes = true; } void KeyDown(int key) { + u32 minSelection = 0; + if (key == GLFW_KEY_LEFT) + { + m_selection = m_selection == minSelection ? minSelection : m_selection - 1; + } + + u32 maxSelection = m_groundShape->m_mesh->triangleCount - 1; + if (key == GLFW_KEY_RIGHT) + { + m_selection = m_selection == maxSelection ? maxSelection : m_selection + 1; + } + + if (key == GLFW_KEY_E) + { + m_drawEdgeTypes = !m_drawEdgeTypes; + } + if (key == GLFW_KEY_S || key == GLFW_KEY_C || key == GLFW_KEY_H) { - if (m_body) - { - m_world.DestroyBody(m_body); - } + b3Body* body = m_bodyShape->GetBody(); + + m_world.DestroyBody(body); b3BodyDef bd; bd.type = b3BodyType::e_dynamicBody; bd.position.Set(0.0f, 5.0f, 0.0f); - m_body = m_world.CreateBody(bd); + body = m_world.CreateBody(bd); if (key == GLFW_KEY_S) { @@ -95,14 +118,14 @@ public: sd.density = 1.0f; sd.friction = 0.5f; - m_body->CreateShape(sd); + m_bodyShape = body->CreateShape(sd); } if (key == GLFW_KEY_C) { b3CapsuleShape capsule; - capsule.m_centers[0].Set(0.0f, -1.0f, 0.0f); - capsule.m_centers[1].Set(0.0f, 1.0f, 0.0f); + capsule.m_vertex1.Set(0.0f, -1.0f, 0.0f); + capsule.m_vertex2.Set(0.0f, 1.0f, 0.0f); capsule.m_radius = 1.0f; b3ShapeDef sd; @@ -110,7 +133,7 @@ public: sd.density = 1.0f; sd.friction = 0.5f; - m_body->CreateShape(sd); + m_bodyShape = body->CreateShape(sd); } if (key == GLFW_KEY_H) @@ -123,41 +146,43 @@ public: sd.density = 1.0f; sd.friction = 0.5f; - m_body->CreateShape(sd); + m_bodyShape = body->CreateShape(sd); } } if (key == GLFW_KEY_G || key == GLFW_KEY_T) { - if (m_ground) - { - m_world.DestroyBody(m_ground); - } + b3Body* groundBody = m_groundShape->GetBody(); + m_world.DestroyBody(groundBody); b3BodyDef bd; - m_ground = m_world.CreateBody(bd); + groundBody = m_world.CreateBody(bd); if (key == GLFW_KEY_G) { b3MeshShape ms; ms.m_mesh = &m_gridMesh; + ms.m_scale.Set(2.0f, 1.0f, 2.0f); b3ShapeDef sd; sd.shape = &ms; - m_ground->CreateShape(sd); + m_groundShape = (b3MeshShape*)groundBody->CreateShape(sd); } if (key == GLFW_KEY_T) { b3MeshShape ms; ms.m_mesh = &m_terrainMesh; + ms.m_scale.Set(2.0f, 1.5f, 2.0f); b3ShapeDef sd; sd.shape = &ms; - m_ground->CreateShape(sd); + m_groundShape = (b3MeshShape*)groundBody->CreateShape(sd); } + + m_selection = m_groundShape->m_mesh->triangleCount / 2; } } @@ -165,6 +190,108 @@ public: { Test::Step(); + const b3Mesh* mesh = m_groundShape->m_mesh; + b3Vec3 scale = m_groundShape->m_scale; + b3Body* body = m_groundShape->GetBody(); + b3Transform xf = body->GetTransform(); + + { + const b3MeshTriangle* triangle = mesh->triangles + m_selection; + const b3MeshTriangleWings* triangleWings = mesh->triangleWings + m_selection; + + for (u32 i = 0; i < 3; ++i) + { + u32 j = i + 1 < 3 ? i + 1 : 0; + + u32 v1 = triangle->GetVertex(i); + u32 v2 = triangle->GetVertex(j); + + b3Vec3 p1 = xf * b3MulCW(scale, mesh->vertices[v1]); + b3Vec3 p2 = xf * b3MulCW(scale, mesh->vertices[v2]); + + b3Vec3 center = scalar(0.5) * (p1 + p2); + g_draw->DrawString(b3Color_white, center, "e%d", i); + + u32 wingVertex = triangleWings->GetVertex(i); + + if (wingVertex != B3_NULL_VERTEX) + { + b3Vec3 vertex = xf * b3MulCW(scale, mesh->vertices[wingVertex]); + g_draw->DrawString(b3Color_white, vertex, "u%d", i); + } + } + } + + if (m_drawEdgeTypes) + { + b3Vec3 eyePoint(0.0f, 10.0f, 0.0f); + + for (u32 i = 0; i < mesh->triangleCount; ++i) + { + b3MeshTriangle* triangle = mesh->triangles + i; + b3MeshTriangleWings* triangleWings = mesh->triangleWings + i; + + b3Vec3 A = xf * b3MulCW(scale, mesh->vertices[triangle->v1]); + b3Vec3 B = xf * b3MulCW(scale, mesh->vertices[triangle->v2]); + b3Vec3 C = xf * b3MulCW(scale, mesh->vertices[triangle->v3]); + + b3Vec3 N = b3Cross(B - A, C - A); + N.Normalize(); + + b3Plane plane(N, A); + if (b3Distance(eyePoint, plane) < 0.0f) + { + plane = b3Plane(-N, A); + } + + for (u32 j = 0; j < 3; ++j) + { + u32 k = j + 1 < 3 ? j + 1 : 0; + + u32 v1 = triangle->GetVertex(j); + u32 v2 = triangle->GetVertex(k); + + u32 u = triangleWings->GetVertex(j); + + b3Vec3 p1 = xf * b3MulCW(scale, mesh->vertices[v1]); + b3Vec3 p2 = xf * b3MulCW(scale, mesh->vertices[v2]); + + b3Vec3 center = scalar(0.5) * (p1 + p2); + + if (u == B3_NULL_VERTEX) + { + g_draw->DrawPoint(center, scalar(4), b3Color_white); + continue; + } + + b3Vec3 wingVertex = xf * b3MulCW(scale, mesh->vertices[u]); + + scalar d = b3Distance(wingVertex, plane); + + const scalar kCoplanarTol = 0.005f; + + if (d < -kCoplanarTol) + { + // Below <=> Convex + g_draw->DrawPoint(center, scalar(4), b3Color_green); + } + else if (d > kCoplanarTol) + { + // Above <=> Concave + g_draw->DrawPoint(center, scalar(4), b3Color_yellow); + } + else + { + // d > -e && d < e + // On <=> Coplanar + g_draw->DrawPoint(center, scalar(4), b3Color_red); + } + } + } + } + + g_draw->DrawString(b3Color_white, "E - View Edge Types"); + g_draw->DrawString(b3Color_white, "Arrows - Select Face Wings"); g_draw->DrawString(b3Color_white, "S - Sphere"); g_draw->DrawString(b3Color_white, "C - Capsule"); g_draw->DrawString(b3Color_white, "H - Hull"); @@ -177,11 +304,14 @@ public: return new MeshContactTest(); } + bool m_drawEdgeTypes; + u32 m_selection; + b3GridMesh<25, 25> m_terrainMesh; b3GridMesh<25, 25> m_gridMesh; - b3Body* m_ground; - b3Body* m_body; + b3MeshShape* m_groundShape; + b3Shape* m_bodyShape; }; #endif \ No newline at end of file diff --git a/examples/testbed/tests/motor_test.h b/examples/testbed/tests/motor_test.h new file mode 100644 index 0000000..f3f1631 --- /dev/null +++ b/examples/testbed/tests/motor_test.h @@ -0,0 +1,123 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef MOTOR_TEST_H +#define MOTOR_TEST_H + +class MotorTest : public Test +{ +public: + + MotorTest() + { + b3Body* ground = nullptr; + { + // Ground + b3BodyDef bd; + ground = m_world.CreateBody(bd); + + b3HullShape hs; + hs.m_hull = &m_groundHull; + + b3ShapeDef sd; + sd.shape = &hs; + + ground->CreateShape(sd); + } + + { + // Motorized body + b3BodyDef bd; + bd.type = e_dynamicBody; + bd.position.Set(0.0f, 8.0f, 0.0f); + b3Body* body = m_world.CreateBody(bd); + + m_boxHull.SetExtents(2.0f, 0.5f, 0.5f); + + b3HullShape shape; + shape.m_hull = &m_boxHull; + + b3ShapeDef sd; + sd.shape = &shape; + sd.friction = 0.3f; + sd.density = 2.0f; + body->CreateShape(sd); + + b3MotorJointDef mjd; + mjd.Initialize(ground, body); + mjd.maxForce = 1000.0f; + mjd.maxTorque = 1000.0f; + m_joint = (b3MotorJoint*)m_world.CreateJoint(mjd); + } + + m_play = false; + m_x = 0.0f; + } + + void KeyDown(int key) + { + if (key == GLFW_KEY_S) + { + m_play = !m_play; + } + } + + void Step() + { + if (m_play) + { + m_x += g_testSettings->inv_hertz; + + if (m_x >= 2.0f * B3_PI) + { + m_x = 0.0f; + } + } + + b3Vec3 linearOffset; + linearOffset.x = 8.0f * sinf(2.0f * m_x); + linearOffset.y = 8.0f + sinf(m_x); + linearOffset.z = 0.0f; + + m_joint->SetLinearOffset(linearOffset); + + b3Quat angularOffset; + angularOffset.SetAxisAngle(b3Vec3_z, m_x); + angularOffset.Normalize(); + + m_joint->SetAngularOffset(angularOffset); + + Test::Step(); + + g_draw->DrawPoint(linearOffset, 4.0f, b3Color(0.9f, 0.9f, 0.9f)); + + g_draw->DrawString(b3Color_white, "S - Play/Pause"); + } + + static Test* Create() + { + return new MotorTest(); + } + + b3BoxHull m_boxHull; + b3MotorJoint* m_joint; + bool m_play; + scalar m_x; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/multiple_pendulum.h b/examples/testbed/tests/multiple_pendulum.h index 6bc43b7..e95e504 100644 --- a/examples/testbed/tests/multiple_pendulum.h +++ b/examples/testbed/tests/multiple_pendulum.h @@ -39,8 +39,8 @@ public: bs[1] = m_world.CreateBody(bd); b3CapsuleShape s; - s.m_centers[0].Set(0.5f, 0.0f, 0.0f); - s.m_centers[1].Set(-0.5f, 0.0f, 0.0f); + s.m_vertex1.Set(0.5f, 0.0f, 0.0f); + s.m_vertex2.Set(-0.5f, 0.0f, 0.0f); s.m_radius = 0.05f; b3ShapeDef sd; @@ -60,8 +60,8 @@ public: bs[2] = m_world.CreateBody(bd); b3CapsuleShape s; - s.m_centers[0].Set(0.5f, 0.0f, 0.0f); - s.m_centers[1].Set(-0.5f, 0.0f, 0.0f); + s.m_vertex1.Set(0.5f, 0.0f, 0.0f); + s.m_vertex2.Set(-0.5f, 0.0f, 0.0f); s.m_radius = 0.05f; b3ShapeDef sd; @@ -81,8 +81,8 @@ public: bs[3] = m_world.CreateBody(bd); b3CapsuleShape s; - s.m_centers[0].Set(0.5f, 0.0f, 0.0f); - s.m_centers[1].Set(-0.5f, 0.0f, 0.0f); + s.m_vertex1.Set(0.5f, 0.0f, 0.0f); + s.m_vertex2.Set(-0.5f, 0.0f, 0.0f); s.m_radius = 0.05f; b3ShapeDef sd; @@ -102,8 +102,8 @@ public: bs[4] = m_world.CreateBody(bd); b3CapsuleShape s; - s.m_centers[0].Set(0.5f, 0.0f, 0.0f); - s.m_centers[1].Set(-0.5f, 0.0f, 0.0f); + s.m_vertex1.Set(0.5f, 0.0f, 0.0f); + s.m_vertex2.Set(-0.5f, 0.0f, 0.0f); s.m_radius = 0.05f; b3ShapeDef sd; @@ -123,8 +123,8 @@ public: bs[5] = m_world.CreateBody(bd); b3CapsuleShape s; - s.m_centers[0].Set(0.5f, 0.0f, 0.0f); - s.m_centers[1].Set(-0.5f, 0.0f, 0.0f); + s.m_vertex1.Set(0.5f, 0.0f, 0.0f); + s.m_vertex2.Set(-0.5f, 0.0f, 0.0f); s.m_radius = 0.05f; b3ShapeDef sd; diff --git a/examples/testbed/tests/newton_cradle.h b/examples/testbed/tests/newton_cradle.h index 865cb3a..f8ec031 100644 --- a/examples/testbed/tests/newton_cradle.h +++ b/examples/testbed/tests/newton_cradle.h @@ -38,15 +38,15 @@ public: } b3CapsuleShape edge; - edge.m_centers[0].Set(0.0f, -10.0f, 0.0f); - edge.m_centers[1].Set(0.0f, 10.0f, 0.0f); + edge.m_vertex1.Set(0.0f, -10.0f, 0.0f); + edge.m_vertex2.Set(0.0f, 10.0f, 0.0f); edge.m_radius = 0.5f; b3Body* frame1, *frame2; { b3BodyDef bd; - bd.orientation = b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), 0.5f * B3_PI); + bd.orientation = b3QuatRotationZ(0.5f * B3_PI); bd.position.Set(0.0f, 10.0f, -5.0f); frame1 = m_world.CreateBody(bd); @@ -59,7 +59,7 @@ public: { b3BodyDef bd; - bd.orientation = b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), 0.5f * B3_PI); + bd.orientation = b3QuatRotationZ(0.5f * B3_PI); bd.position.Set(0.0f, 10.0f, 5.0f); frame2 = m_world.CreateBody(bd); diff --git a/examples/testbed/tests/node_types.h b/examples/testbed/tests/node_types.h new file mode 100644 index 0000000..1cd4802 --- /dev/null +++ b/examples/testbed/tests/node_types.h @@ -0,0 +1,222 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef NODE_TYPES_H +#define NODE_TYPES_H + +class NodeTypes : public Test +{ +public: + enum + { + e_w = 5, + e_h = 2, + e_d = 2 + }; + + NodeTypes() + { + // Create soft body + b3SoftBodyDef def; + def.mesh = &m_mesh; + def.density = 0.2f; + def.E = 1000.0f; + def.nu = 0.33f; + def.radius = 0.2f; + def.friction = 0.6f; + + m_body = new b3SoftBody(def); + + b3Vec3 gravity(0.0f, -9.8f, 0.0f); + m_body->SetGravity(gravity); + + for (u32 i = 0; i < e_h + 1; ++i) + { + for (u32 k = 0; k < e_d + 1; ++k) + { + u32 v = m_mesh.GetVertex(i, 0, k); + + b3SoftBodyNode* n = m_body->GetNode(v); + n->SetType(e_staticSoftBodyNode); + } + } + + m_bodyDragger = new b3SoftBodyDragger(&m_ray, m_body); + } + + ~NodeTypes() + { + delete m_bodyDragger; + delete m_body; + } + + void Step() + { + Test::Step(); + + if (m_bodyDragger->IsDragging()) + { + m_bodyDragger->Drag(); + } + + m_body->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); + + m_body->Draw(); + + if (m_bodyDragger->IsDragging()) + { + b3Vec3 pA = m_bodyDragger->GetPointA(); + b3Vec3 pB = m_bodyDragger->GetPointB(); + + g_draw->DrawPoint(pA, 4.0f, b3Color_green); + + g_draw->DrawPoint(pB, 4.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_softBodySolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_softBodySolverIterations); + + scalar E = m_body->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + + g_draw->DrawString(b3Color_white, "S - Static"); + g_draw->DrawString(b3Color_white, "D - Dynamic"); + g_draw->DrawString(b3Color_white, "K - Kinematic"); + g_draw->DrawString(b3Color_white, "Arrows - Apply Force/Velocity/Position"); + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_bodyDragger->IsDragging() == false) + { + m_bodyDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_bodyDragger->IsDragging() == true) + { + m_bodyDragger->StopDragging(); + } + } + + void SetBodyType(b3SoftBodyNodeType type) + { + for (u32 i = 0; i < e_h + 1; ++i) + { + for (u32 k = 0; k < e_d + 1; ++k) + { + u32 v = m_mesh.GetVertex(i, 0, k); + + b3SoftBodyNode* n = m_body->GetNode(v); + n->SetType(type); + } + } + } + + void KeyDown(int button) + { + if (button == GLFW_KEY_S) + { + SetBodyType(e_staticSoftBodyNode); + } + + if (button == GLFW_KEY_K) + { + SetBodyType(e_kinematicSoftBodyNode); + } + + if (button == GLFW_KEY_D) + { + SetBodyType(e_dynamicSoftBodyNode); + } + + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + b3SoftBodyNode* n = m_body->GetNode(i); + + b3Vec3 d; + d.SetZero(); + + if (button == GLFW_KEY_LEFT) + { + d.x = -1.0f; + } + + if (button == GLFW_KEY_RIGHT) + { + d.x = 1.0f; + } + + if (button == GLFW_KEY_UP) + { + d.y = 1.0f; + } + + if (button == GLFW_KEY_DOWN) + { + d.y = -1.0f; + } + + if (button == GLFW_KEY_LEFT || + button == GLFW_KEY_RIGHT || + button == GLFW_KEY_UP || + button == GLFW_KEY_DOWN) + { + if (n->GetType() == e_staticSoftBodyNode) + { + n->ApplyTranslation(d); + } + + if (n->GetType() == e_kinematicSoftBodyNode) + { + b3Vec3 v = n->GetVelocity(); + + v += 5.0f * d; + + n->SetVelocity(v); + } + + if (n->GetType() == e_dynamicSoftBodyNode) + { + b3Vec3 f = 100.0f * d; + + n->ApplyForce(f); + } + } + } + } + + static Test* Create() + { + return new NodeTypes(); + } + + b3BlockSoftBodyMesh m_mesh; + b3SoftBody* m_body; + b3SoftBodyDragger* m_bodyDragger; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/particle_types.h b/examples/testbed/tests/particle_types.h index 55f51f6..1f95429 100644 --- a/examples/testbed/tests/particle_types.h +++ b/examples/testbed/tests/particle_types.h @@ -36,10 +36,21 @@ public: g_draw->DrawString(b3Color_white, "Arrows - Apply Force/Velocity/Position"); } - void SetClothType(b3ParticleType type) + void SetClothType(b3ClothParticleType type) { - for (b3Particle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) + for (u32 j = 0; j < e_w + 1; ++j) { + u32 v = m_clothMesh.GetVertex(0, j); + + b3ClothParticle* p = m_cloth->GetParticle(v); + p->SetType(type); + } + + for (u32 j = 0; j < e_w + 1; ++j) + { + u32 v = m_clothMesh.GetVertex(e_h, j); + + b3ClothParticle* p = m_cloth->GetParticle(v); p->SetType(type); } } @@ -48,20 +59,20 @@ public: { if (button == GLFW_KEY_S) { - SetClothType(e_staticParticle); + SetClothType(e_staticClothParticle); } if (button == GLFW_KEY_K) { - SetClothType(e_kinematicParticle); + SetClothType(e_kinematicClothParticle); } if (button == GLFW_KEY_D) { - SetClothType(e_dynamicParticle); + SetClothType(e_dynamicClothParticle); } - for (b3Particle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) + for (b3ClothParticle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) { b3Vec3 d; d.SetZero(); @@ -91,12 +102,12 @@ public: button == GLFW_KEY_UP || button == GLFW_KEY_DOWN) { - if (p->GetType() == e_staticParticle) + if (p->GetType() == e_staticClothParticle) { p->ApplyTranslation(d); } - if (p->GetType() == e_kinematicParticle) + if (p->GetType() == e_kinematicClothParticle) { b3Vec3 v = p->GetVelocity(); @@ -105,7 +116,7 @@ public: p->SetVelocity(v); } - if (p->GetType() == e_dynamicParticle) + if (p->GetType() == e_dynamicClothParticle) { b3Vec3 f = 100.0f * d; diff --git a/examples/testbed/tests/pinned_cloth.h b/examples/testbed/tests/pinned_cloth.h index e856ac5..8fb6b29 100644 --- a/examples/testbed/tests/pinned_cloth.h +++ b/examples/testbed/tests/pinned_cloth.h @@ -22,6 +22,12 @@ class PinnedCloth : public Test { public: + enum + { + e_w = 10, + e_h = 10 + }; + PinnedCloth() { // Create cloth @@ -33,30 +39,24 @@ public: m_cloth = new b3Cloth(def); m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); - m_cloth->SetWorld(&m_world); // Freeze some particles - b3AABB3 aabb1; - aabb1.m_lower.Set(-5.0f, -1.0f, -6.0f); - aabb1.m_upper.Set(5.0f, 1.0f, -4.0f); - - b3AABB3 aabb2; - aabb2.m_lower.Set(-5.0f, -1.0f, 4.0f); - aabb2.m_upper.Set(5.0f, 1.0f, 6.0f); - - for (b3Particle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) + for (u32 j = 0; j < e_w + 1; ++j) { - if (aabb1.Contains(p->GetPosition())) - { - p->SetType(e_staticParticle); - } + u32 v = m_clothMesh.GetVertex(0, j); - if (aabb2.Contains(p->GetPosition())) - { - p->SetType(e_staticParticle); - } + b3ClothParticle* p = m_cloth->GetParticle(v); + p->SetType(e_staticClothParticle); } - + + for (u32 j = 0; j < e_w + 1; ++j) + { + u32 v = m_clothMesh.GetVertex(e_h, j); + + b3ClothParticle* p = m_cloth->GetParticle(v); + p->SetType(e_staticClothParticle); + } + m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); } @@ -79,9 +79,9 @@ public: b3Vec3 pA = m_clothDragger->GetPointA(); b3Vec3 pB = m_clothDragger->GetPointB(); - g_draw->DrawPoint(pA, 2.0f, b3Color_green); + g_draw->DrawPoint(pA, 4.0f, b3Color_green); - g_draw->DrawPoint(pB, 2.0f, b3Color_green); + g_draw->DrawPoint(pB, 4.0f, b3Color_green); g_draw->DrawSegment(pA, pB, b3Color_white); } @@ -89,7 +89,7 @@ public: extern u32 b3_clothSolverIterations; g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); - float32 E = m_cloth->GetEnergy(); + scalar E = m_cloth->GetEnergy(); g_draw->DrawString(b3Color_white, "E = %f", E); } @@ -128,7 +128,7 @@ public: return new PinnedCloth(); } - b3GridClothMesh<10, 10> m_clothMesh; + b3GridClothMesh m_clothMesh; b3Cloth* m_cloth; b3ClothDragger* m_clothDragger; }; diff --git a/examples/testbed/tests/pinned_softbody.h b/examples/testbed/tests/pinned_softbody.h index e058e24..32d0021 100644 --- a/examples/testbed/tests/pinned_softbody.h +++ b/examples/testbed/tests/pinned_softbody.h @@ -19,14 +19,12 @@ #ifndef PINNED_SOFTBODY_H #define PINNED_SOFTBODY_H -#include - class PinnedSoftBody : public Test { public: PinnedSoftBody() { - m_mesh.SetAsSphere(5.0f, 0); + m_mesh.SetAsSphere(4.0f, 0); // Create soft body b3SoftBodyDef def; @@ -37,20 +35,15 @@ public: def.c_yield = 0.1f; def.c_creep = 0.5f; def.c_max = 1.0f; + def.massDamping = 0.2f; m_body = new b3SoftBody(def); - for (u32 i = 0; i < m_mesh.vertexCount; ++i) - { - b3SoftBodyNode* n = m_body->GetVertexNode(i); - n->SetMassDamping(0.2f); - } - u32 pinIndex = ~0; - float32 pinDot = -B3_MAX_FLOAT; + scalar pinDot = -B3_MAX_SCALAR; for (u32 i = 0; i < m_mesh.vertexCount; ++i) { - float32 dot = b3Dot(m_mesh.vertices[i], b3Vec3_y); + scalar dot = b3Dot(m_mesh.vertices[i], b3Vec3_y); if (dot > pinDot) { pinDot = dot; @@ -58,7 +51,7 @@ public: } } - b3SoftBodyNode* pinNode = m_body->GetVertexNode(pinIndex); + b3SoftBodyNode* pinNode = m_body->GetNode(pinIndex); pinNode->SetType(e_staticSoftBodyNode); b3Vec3 gravity(0.0f, -9.8f, 0.0f); @@ -91,9 +84,9 @@ public: b3Vec3 pA = m_bodyDragger->GetPointA(); b3Vec3 pB = m_bodyDragger->GetPointB(); - g_draw->DrawPoint(pA, 2.0f, b3Color_green); + g_draw->DrawPoint(pA, 4.0f, b3Color_green); - g_draw->DrawPoint(pB, 2.0f, b3Color_green); + g_draw->DrawPoint(pB, 4.0f, b3Color_green); g_draw->DrawSegment(pA, pB, b3Color_white); } @@ -101,7 +94,7 @@ public: extern u32 b3_softBodySolverIterations; g_draw->DrawString(b3Color_white, "Iterations = %d", b3_softBodySolverIterations); - float32 E = m_body->GetEnergy(); + scalar E = m_body->GetEnergy(); g_draw->DrawString(b3Color_white, "E = %f", E); } diff --git a/examples/testbed/tests/prismatic_test.h b/examples/testbed/tests/prismatic_test.h new file mode 100644 index 0000000..c2663b8 --- /dev/null +++ b/examples/testbed/tests/prismatic_test.h @@ -0,0 +1,135 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef PRISMATIC_TEST_H +#define PRISMATIC_TEST_H + +class PrismaticTest : public Test +{ +public: + PrismaticTest() + { + { + b3BodyDef bd; + b3Body* ground = m_world.CreateBody(bd); + + b3HullShape shape; + shape.m_hull = &m_groundHull; + + b3ShapeDef sd; + sd.shape = &shape; + + ground->CreateShape(sd); + } + + b3Body* bA, * bB; + + { + b3BodyDef bd; + bd.type = e_dynamicBody; + bd.position.Set(-5.0f, 5.0f, 0.0f); + + bA = m_world.CreateBody(bd); + + m_hullA.SetExtents(2.0f, 2.0f, 0.5f); + + b3HullShape hull; + hull.m_hull = &m_hullA; + + b3ShapeDef sdef; + sdef.shape = &hull; + sdef.density = 1.0f; + + bA->CreateShape(sdef); + } + + { + b3BodyDef bd; + bd.type = e_dynamicBody; + bd.position.Set(5.0f, 5.0f, 0.0f); + + bB = m_world.CreateBody(bd); + + m_hullB.SetExtents(2.0f, 2.0f, 0.5f); + + b3HullShape hull; + hull.m_hull = &m_hullB; + + b3ShapeDef sdef; + sdef.shape = &hull; + sdef.density = 1.0f; + + bB->CreateShape(sdef); + } + + // Create prismatic joint + { + b3Vec3 anchor(0.0f, 5.0f, 0.0f); + b3Vec3 axis(1.0f, 0.0f, 0.0f); + + b3PrismaticJointDef jd; + jd.Initialize(bA, bB, anchor, axis); + jd.motorSpeed = 10.0f; + jd.maxMotorForce = 10000.0f; + jd.enableMotor = true; + jd.lowerTranslation = 0.0f; + jd.upperTranslation = 10.0f; + jd.enableLimit = true; + + m_joint = (b3PrismaticJoint*)m_world.CreateJoint(jd); + } + } + + void Step() + { + Test::Step(); + + g_draw->DrawString(b3Color_white, "L - Enable Limit"); + g_draw->DrawString(b3Color_white, "M - Enable Motor"); + g_draw->DrawString(b3Color_white, "S - Flip Motor Speed"); + } + + void KeyDown(int button) + { + if (button == GLFW_KEY_L) + { + m_joint->EnableLimit(!m_joint->IsLimitEnabled()); + } + + if (button == GLFW_KEY_M) + { + m_joint->EnableMotor(!m_joint->IsMotorEnabled()); + } + + if (button == GLFW_KEY_S) + { + m_joint->SetMotorSpeed(-m_joint->GetMotorSpeed()); + } + } + + static Test* Create() + { + return new PrismaticTest(); + } + + b3BoxHull m_hullA; + b3BoxHull m_hullB; + b3PrismaticJoint* m_joint; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/pyramid.h b/examples/testbed/tests/pyramid.h index d7528e3..8555a80 100644 --- a/examples/testbed/tests/pyramid.h +++ b/examples/testbed/tests/pyramid.h @@ -47,9 +47,9 @@ public: // shift to ground center b3Vec3 translation; - translation.x = -0.5f * float32(e_count) * boxSize.x; + translation.x = -0.5f * scalar(e_count) * boxSize.x; translation.y = 1.5f * boxSize.y; - translation.z = -0.5f * float32(e_count) * boxSize.z; + translation.z = -0.5f * scalar(e_count) * boxSize.z; u32 count = e_count; for (u32 i = 0; i < e_count; ++i) @@ -61,9 +61,9 @@ public: { b3BodyDef bd; bd.type = b3BodyType::e_dynamicBody; - bd.position.x = 1.05f * float32(j) * boxSize.x; + bd.position.x = 1.05f * scalar(j) * boxSize.x; bd.position.y = 0.0f; - bd.position.z = 1.05f * float32(k) * boxSize.z; + bd.position.z = 1.05f * scalar(k) * boxSize.z; bd.position += translation; b3Body* body = m_world.CreateBody(bd); diff --git a/examples/testbed/tests/pyramids.h b/examples/testbed/tests/pyramids.h index e440e9d..66bf173 100644 --- a/examples/testbed/tests/pyramids.h +++ b/examples/testbed/tests/pyramids.h @@ -48,15 +48,15 @@ public: // shift to ground center b3Vec3 translation; - translation.x = -0.5f * float32(e_count - 1) * 4.0f * boxSize.x; + translation.x = -0.5f * scalar(e_count - 1) * 4.0f * boxSize.x; translation.y = 1.5f * boxSize.y; - translation.z = -0.5f * float32(e_depthCount) * boxSize.z; + translation.z = -0.5f * scalar(e_depthCount) * boxSize.z; for (u32 i = 0; i < e_count; ++i) { // reset translation.y = 1.5f * boxSize.y; - translation.z = -0.5f * float32(e_depthCount) * boxSize.z; + translation.z = -0.5f * scalar(e_depthCount) * boxSize.z; for (u32 j = 0; j < e_depthCount; ++j) { @@ -66,7 +66,7 @@ public: bd.type = e_dynamicBody; bd.position.x = 0.0f; bd.position.y = 0.0f; - bd.position.z = 1.05f * float32(k) * boxSize.z; + bd.position.z = 1.05f * scalar(k) * boxSize.z; bd.position += translation; b3Body* body = m_world.CreateBody(bd); diff --git a/examples/testbed/tests/quadric_shapes.h b/examples/testbed/tests/quadric_shapes.h index f00133a..793a3aa 100644 --- a/examples/testbed/tests/quadric_shapes.h +++ b/examples/testbed/tests/quadric_shapes.h @@ -64,8 +64,8 @@ public: b3Body* body = m_world.CreateBody(bdef); b3CapsuleShape capsule; - capsule.m_centers[0].Set(0.0f, 0.0f, -1.0f); - capsule.m_centers[1].Set(0.0f, 0.0f, 1.0f); + capsule.m_vertex1.Set(0.0f, 0.0f, -1.0f); + capsule.m_vertex2.Set(0.0f, 0.0f, 1.0f); capsule.m_radius = 1.0f; b3ShapeDef sdef; @@ -83,7 +83,7 @@ public: b3Body* body = m_world.CreateBody(bdef); - m_coneHull.SetAsCone(); + m_coneHull.SetExtents(1.0f, 1.0f); b3HullShape hull; hull.m_hull = &m_coneHull; @@ -103,7 +103,7 @@ public: b3Body* body = m_world.CreateBody(bdef); - m_cylinderHull.SetAsCylinder(); + m_cylinderHull.SetExtents(1.0f, 1.0f); b3HullShape hull; hull.m_hull = &m_cylinderHull; @@ -126,8 +126,8 @@ public: return new QuadricShapes(); } - b3QHull m_coneHull; - b3QHull m_cylinderHull; + b3ConeHull m_coneHull; + b3CylinderHull m_cylinderHull; }; #endif \ No newline at end of file diff --git a/examples/testbed/tests/ragdoll.h b/examples/testbed/tests/ragdoll.h index 5dac8cb..9f7a400 100644 --- a/examples/testbed/tests/ragdoll.h +++ b/examples/testbed/tests/ragdoll.h @@ -52,8 +52,8 @@ public: hip->ApplyForceToCenter(b3Vec3(0.0f, 0.0f, -5000.0f), true); b3CapsuleShape cs; - cs.m_centers[0].Set(0.0f, 0.5f, 0.0f); - cs.m_centers[1].Set(0.0f, -0.5f, 0.0f); + cs.m_vertex1.Set(0.0f, 0.5f, 0.0f); + cs.m_vertex2.Set(0.0f, -0.5f, 0.0f); cs.m_radius = 1.0f; b3ShapeDef sd; @@ -70,8 +70,8 @@ public: head = m_world.CreateBody(bd); b3CapsuleShape cs; - cs.m_centers[0].Set(0.0f, 0.15f, 0.0f); - cs.m_centers[1].Set(0.0f, -0.15f, 0.0f); + cs.m_vertex1.Set(0.0f, 0.15f, 0.0f); + cs.m_vertex2.Set(0.0f, -0.15f, 0.0f); cs.m_radius = 0.5f; b3ShapeDef sd; @@ -86,7 +86,7 @@ public: cd.bodyA = hip; cd.bodyB = head; cd.collideLinked = false; - cd.enableLimit = true; + cd.enableConeLimit = true; cd.Initialize(hip, head, b3Vec3(0.0f, 1.0f, 0.0f), b3Vec3(0.0f, 11.55f, 0.0f), 0.25f * B3_PI); b3ConeJoint* cj = (b3ConeJoint*)m_world.CreateJoint(cd); } @@ -95,12 +95,12 @@ public: b3BodyDef bd; bd.type = e_dynamicBody; bd.position.Set(-2.5f, 11.0f, 0.0f); - bd.orientation.Set(b3Vec3(0.0f, 0.0f, 1.0f), 0.5f * B3_PI); + bd.orientation = b3QuatRotationZ(0.5f * B3_PI); lArm = m_world.CreateBody(bd); b3CapsuleShape cs; - cs.m_centers[0].Set(0.0f, 1.0f, 0.0f); - cs.m_centers[1].Set(0.0f, -1.0f, 0.0f); + cs.m_vertex1.Set(0.0f, 1.0f, 0.0f); + cs.m_vertex2.Set(0.0f, -1.0f, 0.0f); cs.m_radius = 0.5f; b3ShapeDef sd; @@ -116,7 +116,7 @@ public: cd.bodyA = hip; cd.bodyB = lArm; cd.collideLinked = false; - cd.enableLimit = true; + cd.enableConeLimit = true; cd.Initialize(hip, lArm, b3Vec3(-1.0f, 0.0f, 0.0f), b3Vec3(-1.0f, 11.0f, 0.0f), B3_PI); b3ConeJoint* cj = (b3ConeJoint*)m_world.CreateJoint(cd); } @@ -125,12 +125,12 @@ public: b3BodyDef bd; bd.type = e_dynamicBody; bd.position.Set(2.5f, 11.0f, 0.0f); - bd.orientation.Set(b3Vec3(0.0f, 0.0f, 1.0f), 0.5f * B3_PI); + bd.orientation.SetAxisAngle(b3Vec3(0.0f, 0.0f, 1.0f), 0.5f * B3_PI); rArm = m_world.CreateBody(bd); b3CapsuleShape cs; - cs.m_centers[0].Set(0.0f, 1.0f, 0.0f); - cs.m_centers[1].Set(0.0f, -1.0f, 0.0f); + cs.m_vertex1.Set(0.0f, 1.0f, 0.0f); + cs.m_vertex2.Set(0.0f, -1.0f, 0.0f); cs.m_radius = 0.5f; b3ShapeDef sd; @@ -146,7 +146,7 @@ public: cd.bodyA = hip; cd.bodyB = rArm; cd.collideLinked = false; - cd.enableLimit = true; + cd.enableConeLimit = true; cd.Initialize(hip, rArm, b3Vec3(1.0f, 0.0f, 0.0f), b3Vec3(1.0f, 11.0f, 0.0f), B3_PI); b3ConeJoint* cj = (b3ConeJoint*)m_world.CreateJoint(cd); } @@ -158,8 +158,8 @@ public: lLeg = m_world.CreateBody(bd); b3CapsuleShape cs; - cs.m_centers[0].Set(0.0f, 2.0f, 0.0f); - cs.m_centers[1].Set(0.0f, -2.0f, 0.0f); + cs.m_vertex1.Set(0.0f, 2.0f, 0.0f); + cs.m_vertex2.Set(0.0f, -2.0f, 0.0f); cs.m_radius = 0.45f; b3ShapeDef sd; @@ -175,7 +175,7 @@ public: cd.bodyA = hip; cd.bodyB = lLeg; cd.collideLinked = false; - cd.enableLimit = true; + cd.enableConeLimit = true; cd.Initialize(hip, lLeg, b3Vec3(0.0f, -1.0f, 0.0f), b3Vec3(-0.5f, 8.5f, 0.0f), 0.25f * B3_PI); b3ConeJoint* cj = (b3ConeJoint*)m_world.CreateJoint(cd); } @@ -187,8 +187,8 @@ public: rLeg = m_world.CreateBody(bd); b3CapsuleShape cs; - cs.m_centers[0].Set(0.0f, 2.0f, 0.0f); - cs.m_centers[1].Set(0.0f, -2.0f, 0.0f); + cs.m_vertex1.Set(0.0f, 2.0f, 0.0f); + cs.m_vertex2.Set(0.0f, -2.0f, 0.0f); cs.m_radius = 0.45f; b3ShapeDef sd; @@ -204,7 +204,7 @@ public: cd.bodyA = hip; cd.bodyB = rLeg; cd.collideLinked = false; - cd.enableLimit = true; + cd.enableConeLimit = true; cd.Initialize(hip, rLeg, b3Vec3(0.0f, -1.0f, 0.0f), b3Vec3(0.5f, 8.5f, 0.0f), 0.25f * B3_PI); b3ConeJoint* cj = (b3ConeJoint*)m_world.CreateJoint(cd); } diff --git a/examples/testbed/tests/ray_cast.h b/examples/testbed/tests/ray_cast.h index 579bdd8..06861e0 100644 --- a/examples/testbed/tests/ray_cast.h +++ b/examples/testbed/tests/ray_cast.h @@ -159,8 +159,8 @@ public: b3Body* body = m_world.CreateBody(bdef); b3CapsuleShape hs; - hs.m_centers[0].Set(0.0f, 1.0f, 0.0f); - hs.m_centers[1].Set(0.0f, -1.0f, 0.0f); + hs.m_vertex1.Set(0.0f, 1.0f, 0.0f); + hs.m_vertex2.Set(0.0f, -1.0f, 0.0f); hs.m_radius = 3.0f; b3ShapeDef sdef; @@ -175,11 +175,22 @@ public: m_p12.Set(0.0f, 2.0f, 0.0f); m_p22.Set(-50.0f, 2.0f, 0.0f); } - + void CastRay(const b3Vec3 p1, const b3Vec3 p2) const { + class RayCastFilter : public b3RayCastFilter + { + public: + bool ShouldRayCast(b3Shape* shape) + { + return true; + } + }; + + RayCastFilter filter; + b3RayCastSingleOutput out; - if (m_world.RayCastSingle(&out, p1, p2)) + if (m_world.RayCastSingle(&out, &filter, p1, p2)) { g_draw->DrawSegment(p1, out.point, b3Color_green); @@ -194,7 +205,7 @@ public: void Step() { - float32 dt = g_testSettings->inv_hertz; + scalar dt = g_testSettings->inv_hertz; b3Quat dq = b3QuatRotationY(0.05f * B3_PI * dt); m_p1 = b3Mul(dq, m_p1); diff --git a/examples/testbed/tests/hinge_motor.h b/examples/testbed/tests/revolute_test.h similarity index 77% rename from examples/testbed/tests/hinge_motor.h rename to examples/testbed/tests/revolute_test.h index 0e8d01f..a923a6f 100644 --- a/examples/testbed/tests/hinge_motor.h +++ b/examples/testbed/tests/revolute_test.h @@ -16,13 +16,13 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef HINGE_MOTOR_H -#define HINGE_MOTOR_H +#ifndef REVOLUTE_TEST_H +#define REVOLUTE_TEST_H -class HingeMotor : public Test +class RevoluteTest : public Test { public: - HingeMotor() + RevoluteTest() { { b3BodyDef bd; @@ -46,8 +46,8 @@ public: hinge = m_world.CreateBody(bd); b3CapsuleShape shape; - shape.m_centers[0].Set(0.0f, 0.0f, -4.0f); - shape.m_centers[1].Set(0.0f, 0.0f, 4.0f); + shape.m_vertex1.Set(0.0f, 0.0f, -4.0f); + shape.m_vertex2.Set(0.0f, 0.0f, 4.0f); shape.m_radius = 0.5f; b3ShapeDef sd; @@ -66,7 +66,7 @@ public: door = m_world.CreateBody(bd); - m_doorBox.Set(1.0f, 0.5f, 4.0f); + m_doorBox.SetExtents(1.0f, 0.5f, 4.0f); b3HullShape hull; hull.m_hull = &m_doorBox; @@ -78,23 +78,34 @@ public: door->CreateShape(sdef); } + { b3Vec3 axis(0.0f, 0.0f, 1.0f); b3Vec3 anchor(0.0f, 7.0f, 0.0f); b3RevoluteJointDef jd; - jd.Initialize(hinge, door, axis, anchor, 0.0f, B3_PI); + jd.Initialize(hinge, door, axis, anchor, -0.25f * B3_PI, 0.5f * B3_PI); + jd.maxMotorTorque = 1000.0f; + jd.enableMotor = false; + jd.enableLimit = true; jd.motorSpeed = B3_PI; - jd.maxMotorTorque = door->GetMass() * 10000.0f; - jd.enableMotor = true; m_rj = (b3RevoluteJoint*)m_world.CreateJoint(jd); } // Invalidate the orientation - b3Vec3 axis(1.0f, 0.0f, 0.0f); - float32 angle = B3_PI; - door->SetTransform(door->GetPosition(), axis, angle); + b3Quat q = b3QuatRotationX(B3_PI); + door->SetTransform(door->GetPosition(), q); + } + + void Step() + { + Test::Step(); + + g_draw->DrawString(b3Color_white, "M - Motor"); + g_draw->DrawString(b3Color_white, "L - Limits"); + g_draw->DrawString(b3Color_white, "S/K/D - Static/kinematic/dynamic body"); + } void KeyDown(int button) @@ -127,7 +138,7 @@ public: static Test* Create() { - return new HingeMotor(); + return new RevoluteTest(); } b3BoxHull m_doorBox; diff --git a/examples/testbed/tests/rope_test.h b/examples/testbed/tests/rope_test.h index 389f517..6a244d8 100644 --- a/examples/testbed/tests/rope_test.h +++ b/examples/testbed/tests/rope_test.h @@ -30,7 +30,7 @@ public: Rope() { b3Vec3 vs[e_count]; - float32 ms[e_count]; + scalar ms[e_count]; vs[0].Set(0.0f, 0.0f, 0.0f); ms[0] = 0.0f; @@ -38,7 +38,7 @@ public: for (u32 i = 1; i < e_count; ++i) { ms[i] = 1.0f; - vs[i].Set(float32(i), 0.0f, 0.0f); + vs[i].Set(scalar(i), 0.0f, 0.0f); } b3RopeDef rd; diff --git a/examples/testbed/tests/sensor_test.h b/examples/testbed/tests/sensor_test.h index 3d8e64f..dce0b6d 100644 --- a/examples/testbed/tests/sensor_test.h +++ b/examples/testbed/tests/sensor_test.h @@ -63,8 +63,8 @@ public: m_character = m_world.CreateBody(bd); b3CapsuleShape cap; - cap.m_centers[0].Set(0.0f, 2.0f, 0.0f); - cap.m_centers[1].Set(0.0f, -2.0f, 0.0f); + cap.m_vertex1.Set(0.0f, 2.0f, 0.0f); + cap.m_vertex2.Set(0.0f, -2.0f, 0.0f); cap.m_radius = 0.5f; b3ShapeDef sd; @@ -129,7 +129,7 @@ public: bd.type = b3BodyType::e_dynamicBody; bd.position.Set(RandomFloat(-20.0f, 20.0f), RandomFloat(10.0f, 20.0f), RandomFloat(-20.0f, 20.0f)); - b3Vec3 n = m_character->GetTransform().position - bd.position; + b3Vec3 n = m_character->GetTransform().translation - bd.position; n.Normalize(); bd.linearVelocity = 60.0f * n; diff --git a/examples/testbed/tests/shape_cast.h b/examples/testbed/tests/shape_cast.h deleted file mode 100644 index f08e083..0000000 --- a/examples/testbed/tests/shape_cast.h +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef SHAPE_CAST_H -#define SHAPE_CAST_H - -class ShapeCast : public Test -{ -public: - ShapeCast() - { - m_shapeA.m_hull = &b3BoxHull_identity; - m_shapeA.m_radius = 0.0f; - - m_shapeB.m_hull = &b3BoxHull_identity; - m_shapeB.m_radius = 0.0f; - - m_xfA.position.Set(-5.0f, 0.0f, 0.0f); - m_xfA.rotation.SetIdentity(); - - m_xfB.position.Set(10.0f, 0.0f, 0.0f); - m_xfB.rotation.SetIdentity(); - - m_proxyA.Set(&m_shapeA, 0); - m_proxyB.Set(&m_shapeB, 0); - } - - void Step() - { - g_draw->DrawString(b3Color_white, "Left/Right/Up/Down Arrow - Translate shape"); - g_draw->DrawString(b3Color_white, "X/Y/Z - Rotate shape"); - - g_draw->DrawTransform(m_xfA); - g_draw->DrawTransform(m_xfB); - - m_world.DrawShape(m_xfA, &m_shapeA, b3Color_black); - m_world.DrawShape(m_xfB, &m_shapeB, b3Color_black); - - m_world.DrawSolidShape(m_xfA, &m_shapeA, b3Color_white); - m_world.DrawSolidShape(m_xfB, &m_shapeB, b3Color_white); - - b3Vec3 translationB = -100.0f * b3Vec3_x; - g_draw->DrawSegment(m_xfB.position, m_xfB.position + translationB, b3Color_white); - - b3GJKShapeCastOutput out; - bool hit = b3GJKShapeCast(&out, m_xfA, m_proxyA, m_xfB, m_proxyB, translationB); - - g_draw->DrawString(b3Color_white, "Iterations = %d", out.iterations); - - if (hit) - { - g_draw->DrawPoint(out.point, 4.0f, b3Color_green); - g_draw->DrawSegment(out.point, out.point + out.normal, b3Color_green); - - b3Transform xfB; - xfB.rotation = m_xfB.rotation; - xfB.position = m_xfB.position + out.t * translationB; - - m_world.DrawShape(xfB, &m_shapeB, b3Color_black); - } - } - - void KeyDown(int key) - { - if (key == GLFW_KEY_LEFT) - { - m_xfB.position.x -= 0.105f; - } - - if (key == GLFW_KEY_RIGHT) - { - m_xfB.position.x += 0.105f; - } - - if (key == GLFW_KEY_UP) - { - m_xfB.position.y += 0.105f; - } - - if (key == GLFW_KEY_DOWN) - { - m_xfB.position.y -= 0.105f; - } - - if (key == GLFW_KEY_X) - { - b3Quat qx(b3Vec3(1.0f, 0.0f, 0.0f), 0.05f * B3_PI); - b3Mat33 xfx = b3QuatMat33(qx); - - m_xfB.rotation = m_xfB.rotation * xfx; - } - - if (key == GLFW_KEY_Y) - { - b3Quat qy(b3Vec3(0.0f, 1.0f, 0.0f), 0.05f * B3_PI); - b3Mat33 xfy = b3QuatMat33(qy); - - m_xfB.rotation = m_xfB.rotation * xfy; - } - - if (key == GLFW_KEY_Z) - { - b3Quat qy(b3Vec3(0.0f, 0.0f, 1.0f), 0.05f * B3_PI); - b3Mat33 xfz = b3QuatMat33(qy); - - m_xfB.rotation = m_xfB.rotation * xfz; - } - } - - static Test* Create() - { - return new ShapeCast(); - } - - b3HullShape m_shapeA; - b3Transform m_xfA; - b3ShapeGJKProxy m_proxyA; - - b3HullShape m_shapeB; - b3Transform m_xfB; - b3ShapeGJKProxy m_proxyB; -}; - -#endif \ No newline at end of file diff --git a/examples/testbed/tests/shape_stack.h b/examples/testbed/tests/shape_stack.h index 8d6097c..2a36f83 100644 --- a/examples/testbed/tests/shape_stack.h +++ b/examples/testbed/tests/shape_stack.h @@ -26,7 +26,6 @@ public: { { b3BodyDef bd; - bd.orientation.Set(b3Vec3(0.0f, 1.0f, 0.0f), 0.18f * B3_PI); b3Body* ground = m_world.CreateBody(bd); b3MeshShape ms; @@ -39,7 +38,7 @@ public: } - for (float32 y = 2.5f; y < 20.0f; y += 2.5f) + for (scalar y = 2.5f; y < 20.0f; y += 2.5f) { b3BodyDef bd; bd.type = b3BodyType::e_dynamicBody; @@ -59,18 +58,18 @@ public: body->CreateShape(sdef); } - for (float32 y = 2.5f; y < 20.0f; y += 2.5f) + for (scalar y = 2.5f; y < 20.0f; y += 2.5f) { b3BodyDef bd; bd.type = b3BodyType::e_dynamicBody; bd.position.Set(0.0f, y, 0.0f); - bd.orientation = b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), 0.5f * B3_PI); + bd.orientation = b3QuatRotationZ(0.5f * B3_PI); b3Body* body = m_world.CreateBody(bd); b3CapsuleShape capsule; - capsule.m_centers[0].Set(0.0f, -1.0f, 0.0f); - capsule.m_centers[1].Set(0.0f, 1.0f, 0.0f); + capsule.m_vertex1.Set(0.0f, -1.0f, 0.0f); + capsule.m_vertex2.Set(0.0f, 1.0f, 0.0f); capsule.m_radius = 1.0f; b3ShapeDef sd; @@ -81,7 +80,7 @@ public: body->CreateShape(sd); } - for (float32 y = 2.5f; y < 20.0f; y += 2.5f) + for (scalar y = 2.5f; y < 20.0f; y += 2.5f) { b3BodyDef bd; bd.type = b3BodyType::e_dynamicBody; diff --git a/examples/testbed/tests/sheet.h b/examples/testbed/tests/sheet.h new file mode 100644 index 0000000..422ca17 --- /dev/null +++ b/examples/testbed/tests/sheet.h @@ -0,0 +1,136 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SHEET_H +#define SHEET_H + +class Sheet : public Test +{ +public: + enum + { + e_w = 10, + e_h = 1, + e_d = 10 + }; + + Sheet() + { + // Downscale the block along the y axis + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + m_mesh.vertices[i].y *= 0.5f; + } + + // Create soft body + b3SoftBodyDef def; + def.mesh = &m_mesh; + def.density = 0.3f; + def.E = 200.0f; + def.nu = 0.3f; + + m_body = new b3SoftBody(def); + + b3Vec3 gravity(0.0f, -9.8f, 0.0f); + m_body->SetGravity(gravity); + + for (u32 j = 0; j < e_w + 1; ++j) + { + u32 v = m_mesh.GetVertex(0, j, 0); + + b3SoftBodyNode* n = m_body->GetNode(v); + n->SetType(e_staticSoftBodyNode); + } + + m_bodyDragger = new b3SoftBodyDragger(&m_ray, m_body); + } + + ~Sheet() + { + delete m_bodyDragger; + delete m_body; + } + + void Step() + { + Test::Step(); + + if (m_bodyDragger->IsDragging()) + { + m_bodyDragger->Drag(); + } + + m_body->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); + + m_body->Draw(); + + if (m_bodyDragger->IsDragging()) + { + b3Vec3 pA = m_bodyDragger->GetPointA(); + b3Vec3 pB = m_bodyDragger->GetPointB(); + + g_draw->DrawPoint(pA, 4.0f, b3Color_green); + + g_draw->DrawPoint(pB, 4.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_softBodySolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_softBodySolverIterations); + + scalar E = m_body->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_bodyDragger->IsDragging() == false) + { + m_bodyDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_bodyDragger->IsDragging() == true) + { + m_bodyDragger->StopDragging(); + } + } + + static Test* Create() + { + return new Sheet(); + } + + b3BlockSoftBodyMesh m_mesh; + b3SoftBody* m_body; + b3SoftBodyDragger* m_bodyDragger; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/sheet_stack.h b/examples/testbed/tests/sheet_stack.h index 7a32e93..642c57c 100644 --- a/examples/testbed/tests/sheet_stack.h +++ b/examples/testbed/tests/sheet_stack.h @@ -24,17 +24,15 @@ class SheetStack : public Test public: enum { - e_rowCount = 1, - e_columnCount = 10, - e_depthCount = 1 + e_h = 5, + e_w = 1, + e_d = 1 }; SheetStack() { { b3BodyDef bdef; - bdef.type = b3BodyType::e_staticBody; - b3Body* body = m_world.CreateBody(bdef); b3HullShape hs; @@ -44,49 +42,87 @@ public: sdef.shape = &hs; sdef.friction = 1.0f; - b3Shape* shape = body->CreateShape(sdef); + body->CreateShape(sdef); } - - static b3Vec3 sheetExtents(4.05f, 2.0f * B3_LINEAR_SLOP, 4.05f); - static b3BoxHull sheetHull(sheetExtents.x, sheetExtents.y, sheetExtents.z); - b3Vec3 stackOrigin; - stackOrigin.Set(0.0f, 4.05f, 0.0f); + b3Vec3 e(4.0f, 2.0f * B3_LINEAR_SLOP, 4.0f); - for (u32 i = 0; i < e_rowCount; ++i) + m_boxHull.SetExtents(e.x, e.y, e.z); + + b3Vec3 separation; + separation.x = 1.0f; + separation.y = 1.0f; + separation.z = 1.0f; + + b3Vec3 scale; + scale.x = 2.0f * e.x + separation.x; + scale.y = 2.0f * e.y + separation.y; + scale.z = 2.0f * e.z + separation.z; + + b3Vec3 size; + size.x = 2.0f * e.x + scale.x * scalar(e_w - 1); + size.y = 2.0f * e.y + scale.y * scalar(e_h - 1); + size.z = 2.0f * e.z + scale.z * scalar(e_d - 1); + + b3Vec3 translation; + translation.x = e.x - 0.5f * size.x; + translation.y = e.y - 0.5f * size.y; + translation.z = e.z - 0.5f * size.z; + + translation.y += 9.0f; + + for (u32 i = 0; i < e_h; ++i) { - for (u32 j = 0; j < e_columnCount; ++j) + for (u32 j = 0; j < e_w; ++j) { - for (u32 k = 0; k < e_depthCount; ++k) + for (u32 k = 0; k < e_d; ++k) { b3BodyDef bdef; - bdef.type = b3BodyType::e_dynamicBody; + bdef.type = e_dynamicBody; - bdef.position.x = float32(i) * sheetExtents.x; - bdef.position.y = float32(j) * 50.0f * sheetExtents.y; - bdef.position.z = float32(k) * sheetExtents.z; - bdef.position += stackOrigin; + bdef.position.Set(scalar(j), scalar(i), scalar(k)); + + bdef.position.x *= scale.x; + bdef.position.y *= scale.y; + bdef.position.z *= scale.z; + + bdef.position += translation; b3Body* body = m_world.CreateBody(bdef); b3HullShape hs; - hs.m_hull = &sheetHull; + hs.m_hull = &m_boxHull; b3ShapeDef sdef; + sdef.density = 0.1f; + sdef.friction = 0.3f; sdef.shape = &hs; - sdef.density = 0.5f; - sdef.friction = 0.2f; - + body->CreateShape(sdef); + + u32 bodyIndex = GetBodyIndex(i, j, k); + + m_bodies[bodyIndex] = body; } } } } + u32 GetBodyIndex(u32 i, u32 j, u32 k) + { + B3_ASSERT(i < e_h); + B3_ASSERT(j < e_w); + B3_ASSERT(k < e_d); + return k + e_d * (j + e_w * i); + } + static Test* Create() { return new SheetStack(); } + + b3BoxHull m_boxHull; + b3Body* m_bodies[e_h * e_w * e_d]; }; #endif \ No newline at end of file diff --git a/examples/testbed/tests/smash_softbody.h b/examples/testbed/tests/smash_softbody.h index 544d5a2..e497761 100644 --- a/examples/testbed/tests/smash_softbody.h +++ b/examples/testbed/tests/smash_softbody.h @@ -19,8 +19,6 @@ #ifndef SMASH_SOFTBODY_H #define SMASH_SOFTBODY_H -#include - class SmashSoftBody : public Test { public: @@ -42,20 +40,13 @@ public: def.c_yield = 0.6f; def.c_creep = 1.0f; def.c_max = 1.0f; + def.radius = 0.05f; + def.friction = 0.2f; m_body = new b3SoftBody(def); b3Vec3 gravity(0.0f, -9.8f, 0.0f); m_body->SetGravity(gravity); - m_body->SetWorld(&m_world); - - for (u32 i = 0; i < m_mesh.vertexCount; ++i) - { - b3SoftBodyNode* n = m_body->GetVertexNode(i); - - n->SetRadius(0.05f); - n->SetFriction(0.2f); - } // Create ground { @@ -71,30 +62,14 @@ public: sd.shape = &groundShape; sd.friction = 0.3f; - b->CreateShape(sd); + b3Shape* s = b->CreateShape(sd); + + b3SoftBodyWorldShapeDef ssd; + ssd.shape = s; + + m_body->CreateWorldShape(ssd); } - // Create body - { - b3BodyDef bd; - bd.type = e_dynamicBody; - bd.position.y = 10.0f; - - b3Body* b = m_world.CreateBody(bd); - - static b3BoxHull boxHull(5.0f, 1.0f, 5.0f); - - b3HullShape boxShape; - boxShape.m_hull = &boxHull; - - b3ShapeDef sd; - sd.shape = &boxShape; - sd.density = 0.1f; - sd.friction = 0.3f; - - b->CreateShape(sd); - } - m_bodyDragger = new b3SoftBodyDragger(&m_ray, m_body); } @@ -122,9 +97,9 @@ public: b3Vec3 pA = m_bodyDragger->GetPointA(); b3Vec3 pB = m_bodyDragger->GetPointB(); - g_draw->DrawPoint(pA, 2.0f, b3Color_green); + g_draw->DrawPoint(pA, 4.0f, b3Color_green); - g_draw->DrawPoint(pB, 2.0f, b3Color_green); + g_draw->DrawPoint(pB, 4.0f, b3Color_green); g_draw->DrawSegment(pA, pB, b3Color_white); } @@ -132,7 +107,7 @@ public: extern u32 b3_softBodySolverIterations; g_draw->DrawString(b3Color_white, "Iterations = %d", b3_softBodySolverIterations); - float32 E = m_body->GetEnergy(); + scalar E = m_body->GetEnergy(); g_draw->DrawString(b3Color_white, "E = %f", E); } diff --git a/examples/testbed/tests/softbody_anchor.h b/examples/testbed/tests/softbody_anchor.h new file mode 100644 index 0000000..06d0a4c --- /dev/null +++ b/examples/testbed/tests/softbody_anchor.h @@ -0,0 +1,177 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SOFTBODY_ANCHOR_H +#define SOFTBODY_ANCHOR_H + +class SoftBodyAnchor : public Test +{ +public: + SoftBodyAnchor() + { + m_mesh.SetAsSphere(4.0f, 0); + + // Create soft body + b3SoftBodyDef def; + def.mesh = &m_mesh; + def.density = 0.2f; + def.E = 1000.0f; + def.nu = 0.33f; + def.c_yield = 0.1f; + def.c_creep = 0.5f; + def.c_max = 1.0f; + def.massDamping = 0.2f; + + m_body = new b3SoftBody(def); + + u32 pinIndex = ~0; + scalar pinDot = -B3_MAX_SCALAR; + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + scalar dot = b3Dot(m_mesh.vertices[i], b3Vec3_y); + if (dot > pinDot) + { + pinDot = dot; + pinIndex = i; + } + } + + b3SoftBodyNode* pinNode = m_body->GetNode(pinIndex); + pinNode->SetType(e_staticSoftBodyNode); + + u32 anchorIndex = ~0; + scalar anchorDot = -B3_MAX_SCALAR; + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + scalar dot = b3Dot(m_mesh.vertices[i], -b3Vec3_y); + if (dot > anchorDot) + { + anchorDot = dot; + anchorIndex = i; + } + } + + b3SoftBodyNode* anchorNode = m_body->GetNode(anchorIndex); + + { + b3BodyDef bd; + bd.type = e_dynamicBody; + bd.position.y = -10.0f; + + b3Body* b = m_world.CreateBody(bd); + + b3CapsuleShape cs; + cs.m_vertex1.Set(0.0f, -1.0f, 0.0f); + cs.m_vertex2.Set(0.0f, 1.0f, 0.0f); + cs.m_radius = 1.0f; + + b3ShapeDef sd; + sd.shape = &cs; + sd.density = 0.1f; + + m_shape = b->CreateShape(sd); + + // Create anchor + b3SoftBodyAnchorDef ad; + ad.Initialize(b, anchorNode, anchorNode->GetPosition()); + + m_body->CreateAnchor(ad); + } + + b3Vec3 gravity(0.0f, -9.8f, 0.0f); + m_body->SetGravity(gravity); + + m_bodyDragger = new b3SoftBodyDragger(&m_ray, m_body); + } + + ~SoftBodyAnchor() + { + delete m_bodyDragger; + delete m_body; + } + + void Step() + { + Test::Step(); + + if (m_bodyDragger->IsDragging()) + { + m_bodyDragger->Drag(); + } + + m_body->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); + + m_body->Draw(); + + if (m_bodyDragger->IsDragging()) + { + b3Vec3 pA = m_bodyDragger->GetPointA(); + b3Vec3 pB = m_bodyDragger->GetPointB(); + + g_draw->DrawPoint(pA, 4.0f, b3Color_green); + + g_draw->DrawPoint(pB, 4.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_softBodySolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_softBodySolverIterations); + + scalar E = m_body->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_bodyDragger->IsDragging() == false) + { + m_bodyDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_bodyDragger->IsDragging() == true) + { + m_bodyDragger->StopDragging(); + } + } + + static Test* Create() + { + return new SoftBodyAnchor(); + } + + b3QSoftBodyMesh m_mesh; + + b3SoftBody* m_body; + b3SoftBodyDragger* m_bodyDragger; + b3Shape* m_shape; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/sphere_stack.h b/examples/testbed/tests/sphere_stack.h index b5f48b6..28f1ded 100644 --- a/examples/testbed/tests/sphere_stack.h +++ b/examples/testbed/tests/sphere_stack.h @@ -24,70 +24,103 @@ class SphereStack : public Test public: enum { - e_rowCount = 1, - e_columnCount = 5, - e_depthCount = 1 + e_h = 5, + e_w = 1, + e_d = 1 }; SphereStack() { { - b3BodyDef bd; - bd.type = e_staticBody; - b3Body* ground = m_world.CreateBody(bd); + b3BodyDef bdef; + b3Body* body = m_world.CreateBody(bdef); b3HullShape hs; hs.m_hull = &m_groundHull; - - b3ShapeDef sd; - sd.shape = &hs; - sd.density = 0.0f; - sd.friction = 1.0f; - sd.restitution = 0.0f; - - b3Shape* groundShape = ground->CreateShape(sd); + + b3ShapeDef sdef; + sdef.shape = &hs; + sdef.friction = 1.0f; + + body->CreateShape(sdef); } - b3Vec3 stackOrigin; - stackOrigin.Set(0.0f, 5.0f, 0.0f); - float32 radius = 1.0f; - float32 diameter = 2.0f * radius; + scalar e = 1.0f; - for (u32 i = 0; i < e_rowCount; ++i) + b3SphereShape sphere; + sphere.m_center.SetZero(); + sphere.m_radius = e; + + b3Vec3 separation; + separation.x = 1.0f; + separation.y = 1.0f; + separation.z = 1.0f; + + b3Vec3 scale; + scale.x = 2.0f * e + separation.x; + scale.y = 2.0f * e + separation.y; + scale.z = 2.0f * e + separation.z; + + b3Vec3 size; + size.x = 2.0f * e + scale.x * scalar(e_w - 1); + size.y = 2.0f * e + scale.y * scalar(e_h - 1); + size.z = 2.0f * e + scale.z * scalar(e_d - 1); + + b3Vec3 translation; + translation.x = e - 0.5f * size.x; + translation.y = e - 0.5f * size.y; + translation.z = e - 0.5f * size.z; + + translation.y += 9.0f; + + for (u32 i = 0; i < e_h; ++i) { - for (u32 j = 0; j < e_columnCount; ++j) + for (u32 j = 0; j < e_w; ++j) { - for (u32 k = 0; k < e_depthCount; ++k) + for (u32 k = 0; k < e_d; ++k) { b3BodyDef bdef; - bdef.type = b3BodyType::e_dynamicBody; - bdef.position.x = float32(i) * diameter; - bdef.position.y = float32(j) * diameter; - bdef.position.z = float32(k) * diameter; - bdef.position += stackOrigin; - bdef.linearVelocity.Set(0.0f, -50.0f, 0.0f); + bdef.type = e_dynamicBody; + + bdef.position.Set(scalar(j), scalar(i), scalar(k)); + + bdef.position.x *= scale.x; + bdef.position.y *= scale.y; + bdef.position.z *= scale.z; + + bdef.position += translation; b3Body* body = m_world.CreateBody(bdef); - b3SphereShape sphere; - sphere.m_center.SetZero(); - sphere.m_radius = radius; - b3ShapeDef sdef; - sdef.shape = &sphere; - sdef.density = 1.0f; + sdef.density = 0.1f; sdef.friction = 0.3f; + sdef.shape = &sphere; - b3Shape* shape = body->CreateShape(sdef); + body->CreateShape(sdef); + + u32 bodyIndex = GetBodyIndex(i, j, k); + + m_bodies[bodyIndex] = body; } } } } + u32 GetBodyIndex(u32 i, u32 j, u32 k) + { + B3_ASSERT(i < e_h); + B3_ASSERT(j < e_w); + B3_ASSERT(k < e_d); + return k + e_d * (j + e_w * i); + } + static Test* Create() { return new SphereStack(); } + + b3Body* m_bodies[e_h * e_w * e_d]; }; -#endif +#endif \ No newline at end of file diff --git a/examples/testbed/tests/spring.h b/examples/testbed/tests/spring_test.h similarity index 82% rename from examples/testbed/tests/spring.h rename to examples/testbed/tests/spring_test.h index ad5df0b..b25fb7b 100644 --- a/examples/testbed/tests/spring.h +++ b/examples/testbed/tests/spring_test.h @@ -16,13 +16,13 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef SPRING_H -#define SPRING_H +#ifndef SPRING_TEST_H +#define SPRING_TEST_H -class Spring : public Test +class SpringTest : public Test { public: - Spring() + SpringTest() { { b3BodyDef bd; @@ -33,18 +33,12 @@ public: b3ShapeDef sd; sd.shape = &hs; - + ground->CreateShape(sd); } - - // Car frame shape - { - b3Transform xf; - xf.SetIdentity(); - xf.rotation = b3Diagonal(2.0f, 0.5f, 5.0f); - m_frameHull.SetTransform(xf); - } + // Car frame shape + m_frameHull.SetExtents(2.0f, 0.5f, 5.0f); b3HullShape box; box.m_hull = &m_frameHull; @@ -60,22 +54,22 @@ public: b3BodyDef bdef; bdef.type = e_dynamicBody; bdef.position.Set(0.0f, 10.0f, 0.0f); - + frame = m_world.CreateBody(bdef); b3ShapeDef sdef; sdef.density = 0.1f; sdef.friction = 0.3f; sdef.shape = &box; - + frame->CreateShape(sdef); } - + b3Body* wheelLF; { b3BodyDef bdef; bdef.type = e_dynamicBody; - bdef.position.Set(-1.0f, 7.0f, -4.5f); + bdef.position.Set(-1.0f, 7.0f, 4.5f); bdef.fixedRotationY = true; wheelLF = m_world.CreateBody(bdef); @@ -84,17 +78,17 @@ public: sdef.shape = &sphere; sdef.density = 0.1f; sdef.friction = 1.0f; - + wheelLF->CreateShape(sdef); } { b3SpringJointDef def; - def.Initialize(frame, wheelLF, b3Vec3(-1.0f, 9.0f, -4.5), b3Vec3(-1.0f, 9.0f, -4.5f)); + def.Initialize(frame, wheelLF, b3Vec3(-1.0f, 9.0f, 4.5), b3Vec3(-1.0f, 9.0f, 4.5f)); def.collideLinked = true; def.dampingRatio = 0.5f; def.frequencyHz = 4.0f; - + m_world.CreateJoint(def); } @@ -102,7 +96,7 @@ public: { b3BodyDef bdef; bdef.type = e_dynamicBody; - bdef.position.Set(1.0f, 7.0, -4.5f); + bdef.position.Set(1.0f, 7.0, 4.5f); bdef.fixedRotationY = true; wheelRF = m_world.CreateBody(bdef); @@ -111,25 +105,25 @@ public: sdef.density = 0.1f; sdef.friction = 1.0f; sdef.shape = &sphere; - + wheelRF->CreateShape(sdef); } - + { b3SpringJointDef def; - def.Initialize(frame, wheelRF, b3Vec3(1.0f, 9.0, -4.5), b3Vec3(1.0f, 9.0, -4.5f)); + def.Initialize(frame, wheelRF, b3Vec3(1.0f, 9.0, 4.5), b3Vec3(1.0f, 9.0, 4.5f)); def.collideLinked = true; def.dampingRatio = 0.5f; def.frequencyHz = 4.0f; m_world.CreateJoint(def); } - + b3Body* wheelLB; { b3BodyDef bdef; bdef.type = e_dynamicBody; - bdef.position.Set(-1.0f, 7.0f, 4.5f); + bdef.position.Set(-1.0f, 7.0f, -4.5f); bdef.fixedRotationY = true; wheelLB = m_world.CreateBody(bdef); @@ -144,7 +138,7 @@ public: { b3SpringJointDef def; - def.Initialize(frame, wheelLB, b3Vec3(-1.0f, 9.0f, 4.5f), b3Vec3(-1.0f, 9.0f, 4.5f)); + def.Initialize(frame, wheelLB, b3Vec3(-1.0f, 9.0f, -4.5f), b3Vec3(-1.0f, 9.0f, -4.5f)); def.collideLinked = true; def.dampingRatio = 0.8f; def.frequencyHz = 4.0f; @@ -156,7 +150,7 @@ public: { b3BodyDef bdef; bdef.type = e_dynamicBody; - bdef.position.Set(1.0f, 7.0f, 4.5f); + bdef.position.Set(1.0f, 7.0f, -4.5f); bdef.fixedRotationY = true; wheelRB = m_world.CreateBody(bdef); @@ -171,7 +165,7 @@ public: { b3SpringJointDef def; - def.Initialize(frame, wheelRB, b3Vec3(1.0f, 9.0f, 4.5f), b3Vec3(1.0f, 9.0f, 4.5f)); + def.Initialize(frame, wheelRB, b3Vec3(1.0f, 9.0f, -4.5f), b3Vec3(1.0f, 9.0f, -4.5f)); def.collideLinked = true; def.frequencyHz = 4.0f; def.dampingRatio = 0.8f; @@ -182,7 +176,7 @@ public: static Test* Create() { - return new Spring(); + return new SpringTest(); } b3BoxHull m_frameHull; diff --git a/examples/testbed/tests/table_cloth.h b/examples/testbed/tests/table_cloth.h index 9460ffe..608cb62 100644 --- a/examples/testbed/tests/table_cloth.h +++ b/examples/testbed/tests/table_cloth.h @@ -22,6 +22,12 @@ class TableCloth : public Test { public: + enum + { + e_w = 10, + e_h = 10 + }; + TableCloth() { // Translate the mesh @@ -35,15 +41,13 @@ public: def.mesh = &m_clothMesh; def.density = 0.2f; def.streching = 10000.0f; - //def.shearing = 10000.0f; - def.damping = 100.0f; + def.strechDamping = 100.0f; def.thickness = 0.2f; def.friction = 0.1f; m_cloth = new b3Cloth(def); m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); - m_cloth->SetWorld(&m_world); { b3BodyDef bd; @@ -51,7 +55,7 @@ public: b3Body* b = m_world.CreateBody(bd); - m_tableHull.SetAsCylinder(5.0f, 2.0f); + m_tableHull.SetExtents(5.0f, 2.0f); b3HullShape tableShape; tableShape.m_hull = &m_tableHull; @@ -60,7 +64,12 @@ public: sd.shape = &tableShape; sd.friction = 1.0f; - b->CreateShape(sd); + b3Shape* shape = b->CreateShape(sd); + + b3ClothWorldShapeDef csd; + csd.shape = shape; + + m_cloth->CreateWorldShape(csd); } m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); @@ -85,9 +94,9 @@ public: b3Vec3 pA = m_clothDragger->GetPointA(); b3Vec3 pB = m_clothDragger->GetPointB(); - g_draw->DrawPoint(pA, 2.0f, b3Color_green); + g_draw->DrawPoint(pA, 4.0f, b3Color_green); - g_draw->DrawPoint(pB, 2.0f, b3Color_green); + g_draw->DrawPoint(pB, 4.0f, b3Color_green); g_draw->DrawSegment(pA, pB, b3Color_white); } @@ -95,7 +104,7 @@ public: extern u32 b3_clothSolverIterations; g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); - float32 E = m_cloth->GetEnergy(); + scalar E = m_cloth->GetEnergy(); g_draw->DrawString(b3Color_white, "E = %f", E); } @@ -134,11 +143,11 @@ public: return new TableCloth(); } - b3GridClothMesh<10, 10> m_clothMesh; + b3GridClothMesh m_clothMesh; b3Cloth* m_cloth; b3ClothDragger* m_clothDragger; - b3QHull m_tableHull; + b3CylinderHull m_tableHull; }; #endif \ No newline at end of file diff --git a/examples/testbed/tests/tension_mapping.h b/examples/testbed/tests/tension_mapping.h index c328fd5..85e010e 100644 --- a/examples/testbed/tests/tension_mapping.h +++ b/examples/testbed/tests/tension_mapping.h @@ -21,11 +21,11 @@ // Hot/Cold color map // See http://paulbourke.net/miscellaneous/colourspace/ -static inline b3Color Color(float32 x, float32 a, float32 b) +static inline b3Color Color(scalar x, scalar a, scalar b) { x = b3Clamp(x, a, b); - float32 d = b - a; + scalar d = b - a; b3Color c(1.0f, 1.0f, 1.0f); @@ -58,6 +58,12 @@ static inline b3Color Color(float32 x, float32 a, float32 b) class TensionMapping : public Test { public: + enum + { + e_w = 10, + e_h = 10 + }; + TensionMapping() { // Create cloth @@ -65,24 +71,25 @@ public: def.mesh = &m_clothMesh; def.density = 0.2f; def.streching = 10000.0f; - def.shearing = 5000.0f; - def.damping = 100.0f; + def.strechDamping = 100.0f; + def.shearing = 1000.0f; + def.shearDamping = 10.0f; + def.bending = 1000.0f; + def.bendDamping = 10.0f; m_cloth = new b3Cloth(def); m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); - m_cloth->SetWorld(&m_world); // Freeze some particles - b3AABB3 aabb; - aabb.m_lower.Set(-5.0f, -1.0f, -6.0f); - aabb.m_upper.Set(5.0f, 1.0f, -4.0f); - - for (b3Particle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) + for (u32 i = 0; i < 2; ++i) { - if (aabb.Contains(p->GetPosition())) + for (u32 j = 0; j < e_w + 1; ++j) { - p->SetType(e_staticParticle); + u32 v = m_clothMesh.GetVertex(i, j); + + b3ClothParticle* p = m_cloth->GetParticle(v); + p->SetType(e_staticClothParticle); } } @@ -103,8 +110,7 @@ public: const b3ClothMesh* mesh = m_cloth->GetMesh(); - b3StackArray tension; - tension.Resize(mesh->vertexCount); + b3Vec3 tension[(e_h + 1) * (e_w + 1)]; for (u32 i = 0; i < mesh->vertexCount; ++i) { tension[i].SetZero(); @@ -112,66 +118,100 @@ public: for (b3Force* f = m_cloth->GetForceList().m_head; f; f = f->GetNext()) { - if (f->GetType() == e_strechForce) + if (f->GetType() == e_stretchForce) { - b3StrechForce* s = (b3StrechForce*)f; - - b3ClothTriangle* triangle = s->GetTriangle(); - u32 triangleIndex = triangle->GetTriangle(); - b3ClothMeshTriangle* mesh_triangle = m_clothMesh.triangles + triangleIndex; - - u32 v1 = mesh_triangle->v1; - u32 v2 = mesh_triangle->v2; - u32 v3 = mesh_triangle->v3; + b3StretchForce* s = (b3StretchForce*)f; b3Vec3 f1 = s->GetActionForce1(); b3Vec3 f2 = s->GetActionForce2(); b3Vec3 f3 = s->GetActionForce3(); + b3ClothParticle* p1 = s->GetParticle1(); + b3ClothParticle* p2 = s->GetParticle2(); + b3ClothParticle* p3 = s->GetParticle3(); + + u32 v1 = p1->GetMeshIndex(); + u32 v2 = p2->GetMeshIndex(); + u32 v3 = p3->GetMeshIndex(); + tension[v1] += f1; tension[v2] += f2; tension[v3] += f3; } } + + for (b3ClothParticle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) + { + if (p->GetType() == e_staticClothParticle) + { + b3Draw_draw->DrawPoint(p->GetPosition(), 4.0f, b3Color_white); + } + + if (p->GetType() == e_kinematicClothParticle) + { + b3Draw_draw->DrawPoint(p->GetPosition(), 4.0f, b3Color_blue); + } + + if (p->GetType() == e_dynamicClothParticle) + { + b3Draw_draw->DrawPoint(p->GetPosition(), 4.0f, b3Color_green); + } + } for (u32 i = 0; i < mesh->triangleCount; ++i) { - b3ClothMeshTriangle* t = mesh->triangles + i; + b3ClothMeshTriangle* triangle = mesh->triangles + i; + + b3Vec3 v1 = m_cloth->GetParticle(triangle->v1)->GetPosition(); + b3Vec3 v2 = m_cloth->GetParticle(triangle->v2)->GetPosition(); + b3Vec3 v3 = m_cloth->GetParticle(triangle->v3)->GetPosition(); - b3Vec3 v1 = m_cloth->GetParticle(t->v1)->GetPosition(); - b3Vec3 v2 = m_cloth->GetParticle(t->v2)->GetPosition(); - b3Vec3 v3 = m_cloth->GetParticle(t->v3)->GetPosition(); - g_draw->DrawTriangle(v1, v2, v3, b3Color_black); b3Vec3 c = (v1 + v2 + v3) / 3.0f; - float32 s = 0.9f; + scalar s = 0.9f; v1 = s * (v1 - c) + c; v2 = s * (v2 - c) + c; v3 = s * (v3 - c) + c; - b3Vec3 f1 = tension[t->v1]; - float32 L1 = b3Length(f1); + b3Vec3 f1 = tension[triangle->v1]; + scalar L1 = b3Length(f1); - b3Vec3 f2 = tension[t->v2]; - float32 L2 = b3Length(f2); + b3Vec3 f2 = tension[triangle->v2]; + scalar L2 = b3Length(f2); - b3Vec3 f3 = tension[t->v3]; - float32 L3 = b3Length(f3); + b3Vec3 f3 = tension[triangle->v3]; + scalar L3 = b3Length(f3); - float32 L = (L1 + L2 + L3) / 3.0f; + scalar L = (L1 + L2 + L3) / 3.0f; - const float32 kMaxT = 10000.0f; + const scalar kMaxT = 10000.0f; b3Color color = Color(L, 0.0f, kMaxT); b3Vec3 n1 = b3Cross(v2 - v1, v3 - v1); n1.Normalize(); - g_draw->DrawSolidTriangle(n1, v1, v2, v3, color); - b3Vec3 n2 = -n1; - g_draw->DrawSolidTriangle(n2, v3, v2, v1, color); + scalar r = 0.05f; + + { + b3Vec3 x1 = v1 + r * n1; + b3Vec3 x2 = v2 + r * n1; + b3Vec3 x3 = v3 + r * n1; + + g_draw->DrawSolidTriangle(n1, x1, x2, x3, color); + } + + { + b3Vec3 n2 = -n1; + + b3Vec3 x1 = v1 + r * n2; + b3Vec3 x2 = v2 + r * n2; + b3Vec3 x3 = v3 + r * n2; + + g_draw->DrawSolidTriangle(n2, x3, x2, x1, color); + } } if (m_clothDragger->IsDragging()) @@ -179,9 +219,9 @@ public: b3Vec3 pA = m_clothDragger->GetPointA(); b3Vec3 pB = m_clothDragger->GetPointB(); - g_draw->DrawPoint(pA, 2.0f, b3Color_green); + g_draw->DrawPoint(pA, 4.0f, b3Color_green); - g_draw->DrawPoint(pB, 2.0f, b3Color_green); + g_draw->DrawPoint(pB, 4.0f, b3Color_green); g_draw->DrawSegment(pA, pB, b3Color_white); } @@ -189,7 +229,7 @@ public: extern u32 b3_clothSolverIterations; g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); - float32 E = m_cloth->GetEnergy(); + scalar E = m_cloth->GetEnergy(); g_draw->DrawString(b3Color_white, "E = %f", E); } @@ -228,7 +268,7 @@ public: return new TensionMapping(); } - b3GridClothMesh<10, 10> m_clothMesh; + b3GridClothMesh m_clothMesh; b3Cloth* m_cloth; b3ClothDragger* m_clothDragger; }; diff --git a/examples/testbed/tests/tetgen_softbody.h b/examples/testbed/tests/tetgen_softbody.h new file mode 100644 index 0000000..b84aaf8 --- /dev/null +++ b/examples/testbed/tests/tetgen_softbody.h @@ -0,0 +1,413 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef TETGEN_SOFTBODY_H +#define TETGEN_SOFTBODY_H + +#include + +struct TetGenMesh : public b3SoftBodyMesh +{ + TetGenMesh() + { + vertexCount = 0; + vertices = nullptr; + triangleCount = 0; + triangles = nullptr; + tetrahedronCount = 0; + tetrahedrons = nullptr; + } + + ~TetGenMesh() + { + free(vertices); + free(triangles); + free(tetrahedrons); + } + + bool Load(const char* node_filename, const char* face_filename, const char* ele_filename) + { + { + std::ifstream file(node_filename); + if (!file.good()) + { + printf("Could not open %s \n", node_filename); + return false; + } + + int nodeCount, nodeDimensions, attributeCount, boundaryMarkCount; + + std::string line; + while (std::getline(file, line)) + { + if (line[0] == '#') + { + continue; + } + + if (line[0] == 0) + { + continue; + } + + std::stringstream line_stream(line); + + line_stream >> nodeCount >> nodeDimensions >> attributeCount >> boundaryMarkCount; + + break; + } + + if (nodeDimensions != 3) + { + printf(".node file: Only 3 dimensional nodes supported\n"); + return false; + } + + if (attributeCount != 0) + { + printf(".node file: Only nodes with 0 attributes supported\n"); + return false; + } + + if (boundaryMarkCount != 0) + { + printf(".node file: Only nodes with 0 markers supported\n"); + return false; + } + + assert(vertexCount == 0); + vertices = (b3Vec3*)malloc(sizeof(b3Vec3) * nodeCount); + + while (std::getline(file, line)) + { + if (line[0] == '#') + { + continue; + } + + if (line[0] == 0) + { + continue; + } + + int nodeId; + float x, y, z; + + std::stringstream line_stream(line); + + line_stream >> nodeId >> x >> y >> z; + + assert(nodeId > 0); + assert(nodeId <= nodeCount); + + assert(b3IsValid(x)); + assert(b3IsValid(y)); + assert(b3IsValid(z)); + + vertices[vertexCount].x = x; + vertices[vertexCount].y = y; + vertices[vertexCount].z = z; + + ++vertexCount; + } + + assert(vertexCount == nodeCount); + } + + { + std::ifstream file(face_filename); + if (!file.good()) + { + printf("Could not open %s \n", face_filename); + return false; + } + + int faceCount, boundaryMarkerCount; + + std::string line; + while (std::getline(file, line)) + { + if (line[0] == '#') + { + continue; + } + + if (line[0] == 0) + { + continue; + } + + std::stringstream line_stream(line); + + line_stream >> faceCount >> boundaryMarkerCount; + + break; + } + + assert(triangleCount == 0); + triangles = (b3SoftBodyMeshTriangle*)malloc(sizeof(b3SoftBodyMeshTriangle) * faceCount); + + while (std::getline(file, line)) + { + if (line[0] == '#') + { + continue; + } + + if (line[0] == 0) + { + continue; + } + + int faceId; + int v1, v2, v3; + int corner; + + std::stringstream line_stream(line); + + line_stream >> faceId >> v1 >> v2 >> v3 >> corner; + + assert(faceId > 0); + assert(faceId <= faceCount); + + // Make CCW + b3Swap(v2, v3); + + triangles[triangleCount].v1 = u32(v1 - 1); + triangles[triangleCount].v2 = u32(v2 - 1); + triangles[triangleCount].v3 = u32(v3 - 1); + + ++triangleCount; + } + + assert(triangleCount == faceCount); + } + + { + std::ifstream file(ele_filename); + if (!file.good()) + { + printf("Could not open %s \n", ele_filename); + return false; + } + + int tetCount, nodesPerTet, attributeCount; + + std::string line; + while (std::getline(file, line)) + { + if (line[0] == '#') + { + continue; + } + + if (line[0] == 0) + { + continue; + } + + std::stringstream line_stream(line); + + line_stream >> tetCount >> nodesPerTet >> attributeCount; + + break; + } + + if (nodesPerTet != 4) + { + printf(".ele file: Only 4 nodes per tetrahedran supported\n"); + return false; + } + + if (attributeCount != 0) + { + printf(".ele file: Only elements with 0 attributes supported\n"); + return false; + } + + assert(tetrahedronCount == 0); + tetrahedrons = (b3SoftBodyMeshTetrahedron*)malloc(sizeof(b3SoftBodyMeshTetrahedron) * tetCount); + + while (std::getline(file, line)) + { + if (line[0] == '#') + { + continue; + } + + if (line[0] == 0) + { + continue; + } + + int tetId; + int v1, v2, v3, v4; + + std::stringstream line_stream(line); + + line_stream >> tetId >> v1 >> v2 >> v3 >> v4; + + assert(tetId > 0); + assert(tetId <= tetCount); + + // Make CCW + b3Swap(v2, v3); + + tetrahedrons[tetrahedronCount].v1 = u32(v1 - 1); + tetrahedrons[tetrahedronCount].v2 = u32(v2 - 1); + tetrahedrons[tetrahedronCount].v3 = u32(v3 - 1); + tetrahedrons[tetrahedronCount].v4 = u32(v4 - 1); + + ++tetrahedronCount; + } + + assert(tetrahedronCount == tetCount); + } + + return true; + } +}; + +class TetGenSoftBody : public Test +{ +public: + TetGenSoftBody() + { + { + bool ok = m_mesh.Load("data/octopus.node", "data/octopus.face", "data/octopus.ele"); + assert(ok); + } + + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + m_mesh.vertices[i].y += 10.0f; + } + + // Create soft body + b3SoftBodyDef def; + def.mesh = &m_mesh; + def.density = 0.2f; + def.E = 1000.0f; + def.nu = 0.3f; + def.radius = 0.05f; + def.friction = 0.2f; + + m_body = new b3SoftBody(def); + + b3Vec3 gravity(0.0f, -9.8f, 0.0f); + m_body->SetGravity(gravity); + + // Create ground + { + b3BodyDef bd; + bd.type = e_staticBody; + + b3Body* b = m_world.CreateBody(bd); + + b3HullShape groundShape; + groundShape.m_hull = &m_groundHull; + + b3ShapeDef sd; + sd.shape = &groundShape; + sd.friction = 0.3f; + + b3Shape* s = b->CreateShape(sd); + + b3SoftBodyWorldShapeDef ssd; + ssd.shape = s; + + m_body->CreateWorldShape(ssd); + } + + m_bodyDragger = new b3SoftBodyDragger(&m_ray, m_body); + } + + ~TetGenSoftBody() + { + delete m_bodyDragger; + delete m_body; + } + + void Step() + { + Test::Step(); + + if (m_bodyDragger->IsDragging()) + { + m_bodyDragger->Drag(); + } + + m_body->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); + + m_body->Draw(); + + if (m_bodyDragger->IsDragging()) + { + b3Vec3 pA = m_bodyDragger->GetPointA(); + b3Vec3 pB = m_bodyDragger->GetPointB(); + + g_draw->DrawPoint(pA, 4.0f, b3Color_green); + + g_draw->DrawPoint(pB, 4.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_softBodySolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_softBodySolverIterations); + + scalar E = m_body->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_bodyDragger->IsDragging() == false) + { + m_bodyDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_bodyDragger->IsDragging() == true) + { + m_bodyDragger->StopDragging(); + } + } + + static Test* Create() + { + return new TetGenSoftBody(); + } + + TetGenMesh m_mesh; + b3SoftBody* m_body; + b3SoftBodyDragger* m_bodyDragger; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/time_of_impact.h b/examples/testbed/tests/time_of_impact.h new file mode 100644 index 0000000..9289113 --- /dev/null +++ b/examples/testbed/tests/time_of_impact.h @@ -0,0 +1,237 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef TIME_OF_IMPACT_H +#define TIME_OF_IMPACT_H + +class TimeOfImpact : public Test +{ +public: + TimeOfImpact() + { + m_shapeA.m_hull = &b3BoxHull_identity; + m_shapeA.m_radius = 0.0f; + + m_shapeB.m_hull = &b3BoxHull_identity; + m_shapeB.m_radius = 0.0f; + + m_sweepA.localCenter.SetZero(); + m_sweepA.worldCenter0.Set(0.0f, 0.0f, 5.0f); + m_sweepA.worldCenter.Set(0.0f, 0.0f, -5.0f); + m_sweepA.orientation0.SetIdentity(); + m_sweepA.orientation.SetIdentity(); + + m_sweepB.localCenter.SetZero(); + m_sweepB.worldCenter0.Set(5.0f, 0.0f, 0.0f); + m_sweepB.worldCenter.Set(-5.0f, 0.0f, 0.0f); + m_sweepB.orientation0.SetIdentity(); + m_sweepB.orientation.SetIdentity(); + + m_proxyA.Set(&m_shapeA, 0); + m_proxyB.Set(&m_shapeB, 0); + + m_time = 0.0f; + } + + void Step() + { + b3Color colorA0(1.0f, 0.0f, 0.0f, 1.0f); + b3Color colorB0(0.0f, 1.0f, 0.0f, 1.0f); + + // t0 + b3Transform xfA0 = m_sweepA.GetTransform(0.0f); + b3Transform xfB0 = m_sweepB.GetTransform(0.0f); + + g_draw->DrawTransform(xfA0); + g_draw->DrawTransform(xfB0); + + m_world.DrawShape(xfA0, &m_shapeA, b3Color_black); + m_world.DrawShape(xfB0, &m_shapeB, b3Color_black); + + m_world.DrawSolidShape(xfA0, &m_shapeA, colorA0); + m_world.DrawSolidShape(xfB0, &m_shapeB, colorB0); + + // t1 + b3Transform xfA1 = m_sweepA.GetTransform(1.0f); + b3Transform xfB1 = m_sweepB.GetTransform(1.0f); + + g_draw->DrawTransform(xfA1); + g_draw->DrawTransform(xfB1); + + m_world.DrawShape(xfA1, &m_shapeA, b3Color_black); + m_world.DrawShape(xfB1, &m_shapeB, b3Color_black); + + m_world.DrawSolidShape(xfA1, &m_shapeA, colorA0); + m_world.DrawSolidShape(xfB1, &m_shapeB, colorB0); + + // time + b3Color colorAt(1.0f, 0.0f, 0.0f, 0.5f); + b3Color colorBt(0.0f, 1.0f, 0.0f, 0.5f); + + b3Transform xfAx = m_sweepA.GetTransform(m_time); + b3Transform xfBx = m_sweepB.GetTransform(m_time); + + g_draw->DrawTransform(xfAx); + g_draw->DrawTransform(xfBx); + + m_world.DrawShape(xfAx, &m_shapeA, b3Color_black); + m_world.DrawShape(xfBx, &m_shapeB, b3Color_black); + + m_world.DrawSolidShape(xfAx, &m_shapeA, colorAt); + m_world.DrawSolidShape(xfBx, &m_shapeB, colorBt); + + b3TOIInput input; + input.proxyA = m_proxyA; + input.sweepA = m_sweepA; + input.proxyB = m_proxyB; + input.sweepB = m_sweepB; + input.tMax = 1.0f; + + b3TOIOutput output = b3TimeOfImpact(input); + + if (output.state == b3TOIOutput::e_touching) + { + b3Transform xfAt = m_sweepA.GetTransform(output.t); + b3Transform xfBt = m_sweepB.GetTransform(output.t); + + m_world.DrawShape(xfAt, &m_shapeA, b3Color_black); + m_world.DrawShape(xfBt, &m_shapeB, b3Color_black); + } + + g_draw->DrawString(b3Color_white, "Left/Right/Up/Down Arrow/W/S - Translate shape"); + g_draw->DrawString(b3Color_white, "X/Y/Z - Rotate shape"); + g_draw->DrawString(b3Color_white, "F/B - Advance Time Forwards/Backwards"); + g_draw->DrawString(b3Color_white, "Iterations = %d", output.iterations); + + if (output.state == b3TOIOutput::e_failed) + { + g_draw->DrawString(b3Color_white, "State = Failed"); + } + else if (output.state == b3TOIOutput::e_overlapped) + { + g_draw->DrawString(b3Color_white, "State = Overlapped"); + } + else if (output.state == b3TOIOutput::e_separated) + { + g_draw->DrawString(b3Color_white, "State = Separated!"); + } + else if (output.state == b3TOIOutput::e_touching) + { + g_draw->DrawString(b3Color_white, "State = Touching!"); + } + + g_draw->DrawString(b3Color_white, m_sweepA.worldCenter0, "t0"); + g_draw->DrawString(b3Color_white, m_sweepA.worldCenter, "t1"); + + g_draw->DrawString(b3Color_white, m_sweepB.worldCenter0, "t0"); + g_draw->DrawString(b3Color_white, m_sweepB.worldCenter, "t1"); + } + + void KeyDown(int key) + { + const scalar dt = 0.01f; + const scalar d = 0.15f; + const scalar theta = 0.05f * B3_PI; + + if (key == GLFW_KEY_F) + { + m_time += dt; + if (m_time > 1.0f) + { + m_time = 0.0f; + } + } + + if (key == GLFW_KEY_B) + { + m_time -= dt; + if (m_time < 0.0f) + { + m_time = 1.0f; + } + } + + if (key == GLFW_KEY_LEFT) + { + m_sweepB.worldCenter0.x -= d; + } + + if (key == GLFW_KEY_RIGHT) + { + m_sweepB.worldCenter0.x += d; + } + + if (key == GLFW_KEY_UP) + { + m_sweepB.worldCenter0.y += d; + } + + if (key == GLFW_KEY_DOWN) + { + m_sweepB.worldCenter0.y -= d; + } + + if (key == GLFW_KEY_S) + { + m_sweepB.worldCenter0.z += d; + } + + if (key == GLFW_KEY_W) + { + m_sweepB.worldCenter0.z -= d; + } + + if (key == GLFW_KEY_X) + { + b3Quat qx = b3QuatRotationX(theta); + + m_sweepB.orientation0 = m_sweepB.orientation0 * qx; + } + + if (key == GLFW_KEY_Y) + { + b3Quat qy = b3QuatRotationY(theta); + + m_sweepB.orientation0 = m_sweepB.orientation0 * qy; + } + + if (key == GLFW_KEY_Z) + { + b3Quat qz = b3QuatRotationZ(theta); + + m_sweepB.orientation0 = m_sweepB.orientation0 * qz; + } + } + + static Test* Create() + { + return new TimeOfImpact(); + } + + scalar m_time; + + b3HullShape m_shapeA; + b3Sweep m_sweepA; + b3ShapeGJKProxy m_proxyA; + + b3HullShape m_shapeB; + b3Sweep m_sweepB; + b3ShapeGJKProxy m_proxyB; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/degenerate_capsule.h b/examples/testbed/tests/triangle_contact_test.h similarity index 53% rename from examples/testbed/tests/degenerate_capsule.h rename to examples/testbed/tests/triangle_contact_test.h index 7a5b575..39a8edb 100644 --- a/examples/testbed/tests/degenerate_capsule.h +++ b/examples/testbed/tests/triangle_contact_test.h @@ -16,45 +16,55 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef DEGENERATE_CAPSULE_H -#define DEGENERATE_CAPSULE_H +#ifndef TRIANGLE_CONTACT_TEST_H +#define TRIANGLE_CONTACT_TEST_H -class DegenerateCapsule : public Collide +class TriangleContactTest : public Test { public: - DegenerateCapsule() + TriangleContactTest() { - m_xfA.position.Set(0.0f, 0.0f, 0.0f); - m_xfA.rotation = b3QuatMat33(b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), 0.55f * B3_PI)); + { + b3BodyDef bdef; + bdef.type = b3BodyType::e_staticBody; - m_sA.m_centers[0].Set(0.0f, 0.0f, 0.0f); - m_sA.m_centers[1].Set(0.0f, 0.0f, 0.0f); - m_sA.m_radius = 0.05f; + b3Body* body = m_world.CreateBody(bdef); - m_xfB.position.Set(0.f, 0.0f, 0.0f); - m_xfB.rotation = b3QuatMat33(b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), 0.0f * B3_PI)); + b3TriangleShape ts; + ts.m_vertex1.Set(-5.0f, 0.0f, 5.0f); + ts.m_vertex2.Set(5.0f, 0.0f, 5.0f); + ts.m_vertex3.Set(0.0f, 0.0f, -5.0f); - b3Transform xf; - xf.SetIdentity(); - xf.rotation = b3Diagonal(4.0f, 1.0f, 4.0f); + b3ShapeDef sdef; + sdef.shape = &ts; + sdef.friction = 1.0f; - m_box.SetTransform(xf); + body->CreateShape(sdef); + } - m_sB.m_hull = &m_box; + { + b3BodyDef bdef; + bdef.type = b3BodyType::e_dynamicBody; + bdef.position.Set(0.0f, 5.0f, 0.0f); - m_shapeA = &m_sA; - m_shapeB = &m_sB; - m_cache.count = 0; + b3Body* body = m_world.CreateBody(bdef); + + b3HullShape hs; + hs.m_hull = &b3BoxHull_identity; + + b3ShapeDef sdef; + sdef.density = 0.1f; + sdef.friction = 0.1f; + sdef.shape = &hs; + + body->CreateShape(sdef); + } } static Test* Create() { - return new DegenerateCapsule(); + return new TriangleContactTest(); } - - b3CapsuleShape m_sA; - b3HullShape m_sB; - b3BoxHull m_box; }; -#endif +#endif \ No newline at end of file diff --git a/examples/testbed/tests/tumbler.h b/examples/testbed/tests/tumbler.h index 471fb2c..22995a5 100644 --- a/examples/testbed/tests/tumbler.h +++ b/examples/testbed/tests/tumbler.h @@ -39,11 +39,10 @@ public: { static b3BoxHull box; - b3Transform m; - m.position.Set(0.0f, -45.0f, 0.0f); - m.rotation = b3Diagonal(50.0f, 1.0f, 200.0f); - - box.SetTransform(m); + box.SetExtents(50.0f, 1.0f, 200.0f); + + b3Vec3 translation(0.0f, -45.0f, 0.0f); + box.Translate(translation); b3HullShape hs; hs.m_hull = &box; @@ -58,11 +57,10 @@ public: { static b3BoxHull box; - b3Transform m; - m.position.Set(0.0f, 50.0f, 0.0f); - m.rotation = b3Diagonal(50.0f, 1.0f, 200.0f); - - box.SetTransform(m); + box.SetExtents(50.0f, 1.0f, 200.0f); + + b3Vec3 translation(0.0f, 50.0f, 0.0f); + box.Translate(translation); b3HullShape hs; hs.m_hull = &box; @@ -77,11 +75,10 @@ public: { static b3BoxHull box; - b3Transform m; - m.position.Set(0.0f, 5.0f, -200.0f); - m.rotation = b3Diagonal(50.0f, 50.0f, 1.0f); - - box.SetTransform(m); + box.SetExtents(50.0f, 50.0f, 1.0f); + + b3Vec3 translation(0.0f, 5.0f, -200.0f); + box.Translate(translation); b3HullShape hs; hs.m_hull = &box; @@ -96,11 +93,10 @@ public: { static b3BoxHull box; - b3Transform m; - m.position.Set(0.0f, 5.0f, 200.0f); - m.rotation = b3Diagonal(50.0f, 50.0f, 1.0f); - - box.SetTransform(m); + box.SetExtents(50.0f, 50.0f, 1.0f); + + b3Vec3 translation(0.0f, 5.0f, 200.0f); + box.Translate(translation); b3HullShape hs; hs.m_hull = &box; @@ -115,11 +111,10 @@ public: { static b3BoxHull box; - b3Transform m; - m.position.Set(-50.0f, 5.0f, 0.0f); - m.rotation = b3Diagonal(1.0f, 50.0f, 200.0f); - - box.SetTransform(m); + box.SetExtents(1.0f, 50.0f, 200.0f); + + b3Vec3 translation(-50.0f, 5.0f, 0.0f); + box.Translate(translation); b3HullShape hs; hs.m_hull = &box; @@ -134,11 +129,11 @@ public: { static b3BoxHull box; - b3Transform m; - m.position.Set(50.0f, 5.0f, 0.0f); - m.rotation = b3Diagonal(1.0f, 50.0f, 200.0f); + box.SetExtents(1.0f, 50.0f, 200.0f); + + b3Vec3 translation(50.0f, 5.0f, 0.0f); - box.SetTransform(m); + box.Translate(translation); b3HullShape hs; hs.m_hull = &box; @@ -161,8 +156,8 @@ public: } } - m_coneHull.SetAsCone(); - m_cylinderHull.SetAsCylinder(); + m_coneHull.SetExtents(1.0f, 1.0f); + m_cylinderHull.SetExtents(1.0f, 1.0f); m_count = 0; } @@ -204,8 +199,8 @@ public: b3Body* body = m_world.CreateBody(bdef); b3CapsuleShape capsule; - capsule.m_centers[0].Set(0.0f, 0.0f, -1.0f); - capsule.m_centers[1].Set(0.0f, 0.0f, 1.0f); + capsule.m_vertex1.Set(0.0f, 0.0f, -1.0f); + capsule.m_vertex2.Set(0.0f, 0.0f, 1.0f); capsule.m_radius = 1.0f; b3ShapeDef sdef; @@ -279,8 +274,8 @@ public: } u32 m_count; - b3QHull m_coneHull; - b3QHull m_cylinderHull; + b3ConeHull m_coneHull; + b3CylinderHull m_cylinderHull; }; #endif \ No newline at end of file diff --git a/examples/testbed/tests/varying_friction.h b/examples/testbed/tests/varying_friction.h index 30726a7..0c73c64 100644 --- a/examples/testbed/tests/varying_friction.h +++ b/examples/testbed/tests/varying_friction.h @@ -36,19 +36,12 @@ public: ground->CreateShape(sd); } - static b3BoxHull rampHull; - - { - b3Transform xf; - xf.position.SetZero(); - xf.rotation = b3Diagonal(25.0f, 0.5f, 25.0f); - rampHull.SetTransform(xf); - } + static b3BoxHull rampHull(25.0f, 0.5f, 25.0f); { b3BodyDef bdef; bdef.position.Set(-20.0f, 20.0f, 0.0f); - bdef.orientation = b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), -0.1f * B3_PI); + bdef.orientation = b3QuatRotationZ(-0.1f * B3_PI); b3Body* ramp = m_world.CreateBody(bdef); @@ -64,7 +57,7 @@ public: { b3BodyDef bdef; bdef.position.Set(20.0f, 30.0f, 0.0f); - bdef.orientation = b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), 0.1f * B3_PI); + bdef.orientation = b3QuatRotationZ(0.1f * B3_PI); b3Body* ramp = m_world.CreateBody(bdef); @@ -80,7 +73,7 @@ public: { b3BodyDef bdef; bdef.position.Set(-20.0f, 40.0f, 0.0f); - bdef.orientation = b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), -0.1f * B3_PI); + bdef.orientation = b3QuatRotationZ(-0.1f * B3_PI); b3Body* ramp = m_world.CreateBody(bdef); @@ -96,7 +89,7 @@ public: { b3BodyDef bdef; bdef.position.Set(20.0f, 50.0f, 0.0f); - bdef.orientation = b3Quat(b3Vec3(0.0f, 0.0f, 1.0f), 0.1f * B3_PI); + bdef.orientation = b3QuatRotationZ(0.1f * B3_PI); b3Body* ramp = m_world.CreateBody(bdef); diff --git a/examples/testbed/tests/weld_test.h b/examples/testbed/tests/weld_test.h index 1ca7aac..95ae92c 100644 --- a/examples/testbed/tests/weld_test.h +++ b/examples/testbed/tests/weld_test.h @@ -46,8 +46,8 @@ public: bA = m_world.CreateBody(bd); b3CapsuleShape shape; - shape.m_centers[0].Set(0.0f, -3.5f, 0.0f); - shape.m_centers[1].Set(0.0f, 3.5f, 0.0f); + shape.m_vertex1.Set(0.0f, -3.5f, 0.0f); + shape.m_vertex2.Set(0.0f, 3.5f, 0.0f); shape.m_radius = 0.5f; b3ShapeDef sd; @@ -64,13 +64,7 @@ public: bB = m_world.CreateBody(bd); - static b3BoxHull doorHull; - { - b3Transform xf; - xf.position.SetZero(); - xf.rotation = b3Diagonal(2.0f, 4.0f, 0.5f); - doorHull.SetTransform(xf); - } + static b3BoxHull doorHull(2.0f, 4.0f, 0.5f); b3HullShape hull; hull.m_hull = &doorHull; @@ -86,14 +80,15 @@ public: b3WeldJointDef jd; jd.Initialize(bA, bB, anchor); + jd.frequencyHz = 2.0f; + jd.dampingRatio = 0.3f; b3WeldJoint* wj = (b3WeldJoint*)m_world.CreateJoint(jd); } // Invalidate the orientation - b3Vec3 axis(1.0f, 0.0f, 0.0f); - float32 angle = B3_PI; - bB->SetTransform(bB->GetPosition(), axis, angle); + b3Quat q = b3QuatRotationX(B3_PI); + bB->SetTransform(bB->GetPosition(), q); } } diff --git a/examples/testbed/tests/wheel_test.h b/examples/testbed/tests/wheel_test.h new file mode 100644 index 0000000..2ffa488 --- /dev/null +++ b/examples/testbed/tests/wheel_test.h @@ -0,0 +1,204 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef WHEEL_TEST_H +#define WHEEL_TEST_H + +class WheelTest : public Test +{ +public: + WheelTest() + { + { + b3BodyDef bd; + b3Body* ground = m_world.CreateBody(bd); + + b3HullShape hs; + hs.m_hull = &m_groundHull; + + b3ShapeDef sd; + sd.shape = &hs; + + ground->CreateShape(sd); + } + + m_chassisHull.SetExtents(2.0f, 0.5f, 5.0f); + + b3HullShape chassisShape; + chassisShape.m_hull = &m_chassisHull; + + m_wheelHull.SetExtents(1.0f, 0.5f); + + b3HullShape wheelShape; + wheelShape.m_hull = &m_wheelHull; + + // Chassis + b3Body* chassis; + { + b3BodyDef bdef; + bdef.type = e_dynamicBody; + bdef.position.Set(0.0f, 10.0f, 0.0f); + + chassis = m_world.CreateBody(bdef); + + b3ShapeDef sdef; + sdef.density = 0.1f; + sdef.friction = 0.3f; + sdef.shape = &chassisShape; + + chassis->CreateShape(sdef); + } + + b3Quat orientation = b3QuatRotationZ(0.5f * B3_PI); + + b3Body* wheelLF; + { + b3BodyDef bdef; + bdef.type = e_dynamicBody; + bdef.position.Set(-1.0f, 7.0f, 4.5f); + bdef.orientation = orientation; + + wheelLF = m_world.CreateBody(bdef); + + b3ShapeDef sdef; + sdef.shape = &wheelShape; + sdef.density = 0.1f; + sdef.friction = 1.0f; + + wheelLF->CreateShape(sdef); + } + + { + b3WheelJointDef def; + def.Initialize(chassis, wheelLF, wheelLF->GetPosition(), b3Vec3_y, b3Vec3_x); + def.motorSpeed = 0.25f * B3_PI; + def.maxMotorTorque = 1000.0f; + + m_joint1 = (b3WheelJoint*)m_world.CreateJoint(def); + } + + b3Body* wheelRF; + { + b3BodyDef bdef; + bdef.type = e_dynamicBody; + bdef.position.Set(1.0f, 7.0, 4.5f); + bdef.orientation = orientation; + + wheelRF = m_world.CreateBody(bdef); + + b3ShapeDef sdef; + sdef.density = 0.1f; + sdef.friction = 1.0f; + sdef.shape = &wheelShape; + + wheelRF->CreateShape(sdef); + } + + { + b3WheelJointDef def; + def.Initialize(chassis, wheelRF, wheelRF->GetPosition(), b3Vec3_y, b3Vec3_x); + def.motorSpeed = 0.25f * B3_PI; + def.maxMotorTorque = 1000.0f; + + m_joint2 = (b3WheelJoint*)m_world.CreateJoint(def); + } + + b3Body* wheelLB; + { + b3BodyDef bdef; + bdef.type = e_dynamicBody; + bdef.position.Set(-1.0f, 7.0f, -4.5f); + bdef.orientation = orientation; + + wheelLB = m_world.CreateBody(bdef); + + b3ShapeDef sdef; + sdef.shape = &wheelShape; + sdef.density = 0.1f; + sdef.friction = 1.0f; + + wheelLB->CreateShape(sdef); + } + + { + b3WheelJointDef def; + def.Initialize(chassis, wheelLB, wheelLB->GetPosition(), b3Vec3_y, b3Vec3_x); + + m_world.CreateJoint(def); + } + + b3Body* wheelRB; + { + b3BodyDef bdef; + bdef.type = e_dynamicBody; + bdef.position.Set(1.0f, 7.0f, -4.5f); + bdef.orientation = orientation; + + wheelRB = m_world.CreateBody(bdef); + + b3ShapeDef sdef; + sdef.density = 0.1f; + sdef.friction = 1.0f; + sdef.shape = &wheelShape; + + wheelRB->CreateShape(sdef); + } + + { + b3WheelJointDef def; + def.Initialize(chassis, wheelRB, wheelRB->GetPosition(), b3Vec3_y, b3Vec3_x); + + m_world.CreateJoint(def); + } + } + + void Step() + { + Test::Step(); + + g_draw->DrawString(b3Color_white, "M - Enable Motor"); + g_draw->DrawString(b3Color_white, "S - Flip Motor Speed"); + } + + void KeyDown(int button) + { + if (button == GLFW_KEY_M) + { + m_joint1->EnableMotor(!m_joint1->IsMotorEnabled()); + m_joint2->EnableMotor(!m_joint2->IsMotorEnabled()); + } + + if (button == GLFW_KEY_S) + { + m_joint1->SetMotorSpeed(-m_joint1->GetMotorSpeed()); + m_joint2->SetMotorSpeed(-m_joint2->GetMotorSpeed()); + } + } + + static Test* Create() + { + return new WheelTest(); + } + + b3BoxHull m_chassisHull; + b3CylinderHull m_wheelHull; + b3WheelJoint* m_joint1; + b3WheelJoint* m_joint2; +}; + +#endif \ No newline at end of file diff --git a/external/glad_2/glad.c b/external/glad_2/glad.c index 96a75f5..96e06a5 100644 --- a/external/glad_2/glad.c +++ b/external/glad_2/glad.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include static void* get_proc(const char *namez); diff --git a/external/glfw/CMakeLists.txt b/external/glfw/CMakeLists.txt new file mode 100644 index 0000000..0913579 --- /dev/null +++ b/external/glfw/CMakeLists.txt @@ -0,0 +1,165 @@ + +set(common_HEADERS internal.h mappings.h + "${GLFW_BINARY_DIR}/src/glfw_config.h" + "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" + "${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h") +set(common_SOURCES context.c init.c input.c monitor.c vulkan.c window.c) + +if (_GLFW_COCOA) + set(glfw_HEADERS ${common_HEADERS} cocoa_platform.h cocoa_joystick.h + posix_thread.h nsgl_context.h egl_context.h osmesa_context.h) + set(glfw_SOURCES ${common_SOURCES} cocoa_init.m cocoa_joystick.m + cocoa_monitor.m cocoa_window.m cocoa_time.c posix_thread.c + nsgl_context.m egl_context.c osmesa_context.c) +elseif (_GLFW_WIN32) + set(glfw_HEADERS ${common_HEADERS} win32_platform.h win32_joystick.h + wgl_context.h egl_context.h osmesa_context.h) + set(glfw_SOURCES ${common_SOURCES} win32_init.c win32_joystick.c + win32_monitor.c win32_time.c win32_thread.c win32_window.c + wgl_context.c egl_context.c osmesa_context.c) +elseif (_GLFW_X11) + set(glfw_HEADERS ${common_HEADERS} x11_platform.h xkb_unicode.h posix_time.h + posix_thread.h glx_context.h egl_context.h osmesa_context.h) + set(glfw_SOURCES ${common_SOURCES} x11_init.c x11_monitor.c x11_window.c + xkb_unicode.c posix_time.c posix_thread.c glx_context.c + egl_context.c osmesa_context.c) +elseif (_GLFW_WAYLAND) + set(glfw_HEADERS ${common_HEADERS} wl_platform.h + posix_time.h posix_thread.h xkb_unicode.h egl_context.h + osmesa_context.h) + set(glfw_SOURCES ${common_SOURCES} wl_init.c wl_monitor.c wl_window.c + posix_time.c posix_thread.c xkb_unicode.c + egl_context.c osmesa_context.c) + + ecm_add_wayland_client_protocol(glfw_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/xdg-shell/xdg-shell.xml" + BASENAME xdg-shell) + ecm_add_wayland_client_protocol(glfw_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" + BASENAME xdg-decoration) + ecm_add_wayland_client_protocol(glfw_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/viewporter/viewporter.xml" + BASENAME viewporter) + ecm_add_wayland_client_protocol(glfw_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml" + BASENAME relative-pointer-unstable-v1) + ecm_add_wayland_client_protocol(glfw_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" + BASENAME pointer-constraints-unstable-v1) + ecm_add_wayland_client_protocol(glfw_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" + BASENAME idle-inhibit-unstable-v1) +elseif (_GLFW_OSMESA) + set(glfw_HEADERS ${common_HEADERS} null_platform.h null_joystick.h + posix_time.h posix_thread.h osmesa_context.h) + set(glfw_SOURCES ${common_SOURCES} null_init.c null_monitor.c null_window.c + null_joystick.c posix_time.c posix_thread.c osmesa_context.c) +endif() + +if (_GLFW_X11 OR _GLFW_WAYLAND) + if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + set(glfw_HEADERS ${glfw_HEADERS} linux_joystick.h) + set(glfw_SOURCES ${glfw_SOURCES} linux_joystick.c) + else() + set(glfw_HEADERS ${glfw_HEADERS} null_joystick.h) + set(glfw_SOURCES ${glfw_SOURCES} null_joystick.c) + endif() +endif() + +if (APPLE) + # For some reason, CMake doesn't know about .m + set_source_files_properties(${glfw_SOURCES} PROPERTIES LANGUAGE C) +endif() + +# Make GCC and Clang warn about declarations that VS 2010 and 2012 won't accept +# for all source files that VS will build +if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") + + if (WIN32) + set(windows_SOURCES ${glfw_SOURCES}) + else() + set(windows_SOURCES ${common_SOURCES}) + endif() + set_source_files_properties(${windows_SOURCES} PROPERTIES + COMPILE_FLAGS -Wdeclaration-after-statement) +endif() + +add_library(glfw ${glfw_SOURCES} ${glfw_HEADERS}) +set_target_properties(glfw PROPERTIES + OUTPUT_NAME ${GLFW_LIB_NAME} + VERSION ${GLFW_VERSION} + SOVERSION ${GLFW_VERSION_MAJOR} + POSITION_INDEPENDENT_CODE ON + FOLDER "GLFW3") + +target_compile_definitions(glfw PRIVATE _GLFW_USE_CONFIG_H) +target_include_directories(glfw PUBLIC + "$" + "$") +target_include_directories(glfw PRIVATE + "${GLFW_SOURCE_DIR}/src" + "${GLFW_BINARY_DIR}/src" + ${glfw_INCLUDE_DIRS}) + +# HACK: When building on MinGW, WINVER and UNICODE need to be defined before +# the inclusion of stddef.h (by glfw3.h), which is itself included before +# win32_platform.h. We define them here until a saner solution can be found +# NOTE: MinGW-w64 and Visual C++ do /not/ need this hack. +target_compile_definitions(glfw PRIVATE + "$<$:UNICODE;WINVER=0x0501>") + +# Enable a reasonable set of warnings (no, -Wextra is not reasonable) +target_compile_options(glfw PRIVATE + "$<$:-Wall>" + "$<$:-Wall>" + "$<$:-Wall>") + +if (BUILD_SHARED_LIBS) + if (WIN32) + if (MINGW) + # Remove the lib prefix on the DLL (but not the import library) + set_target_properties(glfw PROPERTIES PREFIX "") + + # Add a suffix to the import library to avoid naming conflicts + set_target_properties(glfw PROPERTIES IMPORT_SUFFIX "dll.a") + else() + # Add a suffix to the import library to avoid naming conflicts + set_target_properties(glfw PROPERTIES IMPORT_SUFFIX "dll.lib") + endif() + elseif (APPLE) + # Add -fno-common to work around a bug in Apple's GCC + target_compile_options(glfw PRIVATE "-fno-common") + + set_target_properties(glfw PROPERTIES + INSTALL_NAME_DIR "${CMAKE_INSTALL_LIBDIR}") + elseif (UNIX) + # Hide symbols not explicitly tagged for export from the shared library + target_compile_options(glfw PRIVATE "-fvisibility=hidden") + endif() + + target_compile_definitions(glfw INTERFACE GLFW_DLL) + target_link_libraries(glfw PRIVATE ${glfw_LIBRARIES}) +else() + target_link_libraries(glfw INTERFACE ${glfw_LIBRARIES}) +endif() + +if (MSVC) + target_compile_definitions(glfw PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() + +if (GLFW_INSTALL) + install(TARGETS glfw + EXPORT glfwTargets + RUNTIME DESTINATION "bin" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") +endif() + diff --git a/external/glfw/LICENSE.md b/external/glfw/LICENSE.md new file mode 100644 index 0000000..acdac20 --- /dev/null +++ b/external/glfw/LICENSE.md @@ -0,0 +1,22 @@ +Copyright (c) 2002-2006 Marcus Geelnard +Copyright (c) 2006-2016 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. + diff --git a/external/glfw/cocoa_init.m b/external/glfw/cocoa_init.m index 7c56071..41329b3 100644 --- a/external/glfw/cocoa_init.m +++ b/external/glfw/cocoa_init.m @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -27,8 +27,8 @@ #include "internal.h" #include // For MAXPATHLEN - -#if defined(_GLFW_USE_CHDIR) +// Needed for _NSGetProgname +#include // Change to our application bundle's resources directory, if present // @@ -66,7 +66,110 @@ static void changeToResourcesDirectory(void) chdir(resourcesPath); } -#endif /* _GLFW_USE_CHDIR */ +// Set up the menu bar (manually) +// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that +// could go away at any moment, lots of stuff that really should be +// localize(d|able), etc. Add a nib to save us this horror. +// +static void createMenuBar(void) +{ + size_t i; + NSString* appName = nil; + NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary]; + NSString* nameKeys[] = + { + @"CFBundleDisplayName", + @"CFBundleName", + @"CFBundleExecutable", + }; + + // Try to figure out what the calling application is called + + for (i = 0; i < sizeof(nameKeys) / sizeof(nameKeys[0]); i++) + { + id name = bundleInfo[nameKeys[i]]; + if (name && + [name isKindOfClass:[NSString class]] && + ![name isEqualToString:@""]) + { + appName = name; + break; + } + } + + if (!appName) + { + char** progname = _NSGetProgname(); + if (progname && *progname) + appName = @(*progname); + else + appName = @"GLFW Application"; + } + + NSMenu* bar = [[NSMenu alloc] init]; + [NSApp setMainMenu:bar]; + + NSMenuItem* appMenuItem = + [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; + NSMenu* appMenu = [[NSMenu alloc] init]; + [appMenuItem setSubmenu:appMenu]; + + [appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName] + action:@selector(orderFrontStandardAboutPanel:) + keyEquivalent:@""]; + [appMenu addItem:[NSMenuItem separatorItem]]; + NSMenu* servicesMenu = [[NSMenu alloc] init]; + [NSApp setServicesMenu:servicesMenu]; + [[appMenu addItemWithTitle:@"Services" + action:NULL + keyEquivalent:@""] setSubmenu:servicesMenu]; + [servicesMenu release]; + [appMenu addItem:[NSMenuItem separatorItem]]; + [appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName] + action:@selector(hide:) + keyEquivalent:@"h"]; + [[appMenu addItemWithTitle:@"Hide Others" + action:@selector(hideOtherApplications:) + keyEquivalent:@"h"] + setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand]; + [appMenu addItemWithTitle:@"Show All" + action:@selector(unhideAllApplications:) + keyEquivalent:@""]; + [appMenu addItem:[NSMenuItem separatorItem]]; + [appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName] + action:@selector(terminate:) + keyEquivalent:@"q"]; + + NSMenuItem* windowMenuItem = + [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; + [bar release]; + NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + [NSApp setWindowsMenu:windowMenu]; + [windowMenuItem setSubmenu:windowMenu]; + + [windowMenu addItemWithTitle:@"Minimize" + action:@selector(performMiniaturize:) + keyEquivalent:@"m"]; + [windowMenu addItemWithTitle:@"Zoom" + action:@selector(performZoom:) + keyEquivalent:@""]; + [windowMenu addItem:[NSMenuItem separatorItem]]; + [windowMenu addItemWithTitle:@"Bring All to Front" + action:@selector(arrangeInFront:) + keyEquivalent:@""]; + + // TODO: Make this appear at the bottom of the menu (for consistency) + [windowMenu addItem:[NSMenuItem separatorItem]]; + [[windowMenu addItemWithTitle:@"Enter Full Screen" + action:@selector(toggleFullScreen:) + keyEquivalent:@"f"] + setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand]; + + // Prior to Snow Leopard, we need to use this oddly-named semi-private API + // to get the application menu working properly. + SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:"); + [NSApp performSelector:setAppleMenuSelector withObject:appMenu]; +} // Create key code translation tables // @@ -74,129 +177,129 @@ static void createKeyTables(void) { int scancode; - memset(_glfw.ns.publicKeys, -1, sizeof(_glfw.ns.publicKeys)); - memset(_glfw.ns.nativeKeys, -1, sizeof(_glfw.ns.nativeKeys)); + memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes)); + memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes)); - _glfw.ns.publicKeys[0x1D] = GLFW_KEY_0; - _glfw.ns.publicKeys[0x12] = GLFW_KEY_1; - _glfw.ns.publicKeys[0x13] = GLFW_KEY_2; - _glfw.ns.publicKeys[0x14] = GLFW_KEY_3; - _glfw.ns.publicKeys[0x15] = GLFW_KEY_4; - _glfw.ns.publicKeys[0x17] = GLFW_KEY_5; - _glfw.ns.publicKeys[0x16] = GLFW_KEY_6; - _glfw.ns.publicKeys[0x1A] = GLFW_KEY_7; - _glfw.ns.publicKeys[0x1C] = GLFW_KEY_8; - _glfw.ns.publicKeys[0x19] = GLFW_KEY_9; - _glfw.ns.publicKeys[0x00] = GLFW_KEY_A; - _glfw.ns.publicKeys[0x0B] = GLFW_KEY_B; - _glfw.ns.publicKeys[0x08] = GLFW_KEY_C; - _glfw.ns.publicKeys[0x02] = GLFW_KEY_D; - _glfw.ns.publicKeys[0x0E] = GLFW_KEY_E; - _glfw.ns.publicKeys[0x03] = GLFW_KEY_F; - _glfw.ns.publicKeys[0x05] = GLFW_KEY_G; - _glfw.ns.publicKeys[0x04] = GLFW_KEY_H; - _glfw.ns.publicKeys[0x22] = GLFW_KEY_I; - _glfw.ns.publicKeys[0x26] = GLFW_KEY_J; - _glfw.ns.publicKeys[0x28] = GLFW_KEY_K; - _glfw.ns.publicKeys[0x25] = GLFW_KEY_L; - _glfw.ns.publicKeys[0x2E] = GLFW_KEY_M; - _glfw.ns.publicKeys[0x2D] = GLFW_KEY_N; - _glfw.ns.publicKeys[0x1F] = GLFW_KEY_O; - _glfw.ns.publicKeys[0x23] = GLFW_KEY_P; - _glfw.ns.publicKeys[0x0C] = GLFW_KEY_Q; - _glfw.ns.publicKeys[0x0F] = GLFW_KEY_R; - _glfw.ns.publicKeys[0x01] = GLFW_KEY_S; - _glfw.ns.publicKeys[0x11] = GLFW_KEY_T; - _glfw.ns.publicKeys[0x20] = GLFW_KEY_U; - _glfw.ns.publicKeys[0x09] = GLFW_KEY_V; - _glfw.ns.publicKeys[0x0D] = GLFW_KEY_W; - _glfw.ns.publicKeys[0x07] = GLFW_KEY_X; - _glfw.ns.publicKeys[0x10] = GLFW_KEY_Y; - _glfw.ns.publicKeys[0x06] = GLFW_KEY_Z; + _glfw.ns.keycodes[0x1D] = GLFW_KEY_0; + _glfw.ns.keycodes[0x12] = GLFW_KEY_1; + _glfw.ns.keycodes[0x13] = GLFW_KEY_2; + _glfw.ns.keycodes[0x14] = GLFW_KEY_3; + _glfw.ns.keycodes[0x15] = GLFW_KEY_4; + _glfw.ns.keycodes[0x17] = GLFW_KEY_5; + _glfw.ns.keycodes[0x16] = GLFW_KEY_6; + _glfw.ns.keycodes[0x1A] = GLFW_KEY_7; + _glfw.ns.keycodes[0x1C] = GLFW_KEY_8; + _glfw.ns.keycodes[0x19] = GLFW_KEY_9; + _glfw.ns.keycodes[0x00] = GLFW_KEY_A; + _glfw.ns.keycodes[0x0B] = GLFW_KEY_B; + _glfw.ns.keycodes[0x08] = GLFW_KEY_C; + _glfw.ns.keycodes[0x02] = GLFW_KEY_D; + _glfw.ns.keycodes[0x0E] = GLFW_KEY_E; + _glfw.ns.keycodes[0x03] = GLFW_KEY_F; + _glfw.ns.keycodes[0x05] = GLFW_KEY_G; + _glfw.ns.keycodes[0x04] = GLFW_KEY_H; + _glfw.ns.keycodes[0x22] = GLFW_KEY_I; + _glfw.ns.keycodes[0x26] = GLFW_KEY_J; + _glfw.ns.keycodes[0x28] = GLFW_KEY_K; + _glfw.ns.keycodes[0x25] = GLFW_KEY_L; + _glfw.ns.keycodes[0x2E] = GLFW_KEY_M; + _glfw.ns.keycodes[0x2D] = GLFW_KEY_N; + _glfw.ns.keycodes[0x1F] = GLFW_KEY_O; + _glfw.ns.keycodes[0x23] = GLFW_KEY_P; + _glfw.ns.keycodes[0x0C] = GLFW_KEY_Q; + _glfw.ns.keycodes[0x0F] = GLFW_KEY_R; + _glfw.ns.keycodes[0x01] = GLFW_KEY_S; + _glfw.ns.keycodes[0x11] = GLFW_KEY_T; + _glfw.ns.keycodes[0x20] = GLFW_KEY_U; + _glfw.ns.keycodes[0x09] = GLFW_KEY_V; + _glfw.ns.keycodes[0x0D] = GLFW_KEY_W; + _glfw.ns.keycodes[0x07] = GLFW_KEY_X; + _glfw.ns.keycodes[0x10] = GLFW_KEY_Y; + _glfw.ns.keycodes[0x06] = GLFW_KEY_Z; - _glfw.ns.publicKeys[0x27] = GLFW_KEY_APOSTROPHE; - _glfw.ns.publicKeys[0x2A] = GLFW_KEY_BACKSLASH; - _glfw.ns.publicKeys[0x2B] = GLFW_KEY_COMMA; - _glfw.ns.publicKeys[0x18] = GLFW_KEY_EQUAL; - _glfw.ns.publicKeys[0x32] = GLFW_KEY_GRAVE_ACCENT; - _glfw.ns.publicKeys[0x21] = GLFW_KEY_LEFT_BRACKET; - _glfw.ns.publicKeys[0x1B] = GLFW_KEY_MINUS; - _glfw.ns.publicKeys[0x2F] = GLFW_KEY_PERIOD; - _glfw.ns.publicKeys[0x1E] = GLFW_KEY_RIGHT_BRACKET; - _glfw.ns.publicKeys[0x29] = GLFW_KEY_SEMICOLON; - _glfw.ns.publicKeys[0x2C] = GLFW_KEY_SLASH; - _glfw.ns.publicKeys[0x0A] = GLFW_KEY_WORLD_1; + _glfw.ns.keycodes[0x27] = GLFW_KEY_APOSTROPHE; + _glfw.ns.keycodes[0x2A] = GLFW_KEY_BACKSLASH; + _glfw.ns.keycodes[0x2B] = GLFW_KEY_COMMA; + _glfw.ns.keycodes[0x18] = GLFW_KEY_EQUAL; + _glfw.ns.keycodes[0x32] = GLFW_KEY_GRAVE_ACCENT; + _glfw.ns.keycodes[0x21] = GLFW_KEY_LEFT_BRACKET; + _glfw.ns.keycodes[0x1B] = GLFW_KEY_MINUS; + _glfw.ns.keycodes[0x2F] = GLFW_KEY_PERIOD; + _glfw.ns.keycodes[0x1E] = GLFW_KEY_RIGHT_BRACKET; + _glfw.ns.keycodes[0x29] = GLFW_KEY_SEMICOLON; + _glfw.ns.keycodes[0x2C] = GLFW_KEY_SLASH; + _glfw.ns.keycodes[0x0A] = GLFW_KEY_WORLD_1; - _glfw.ns.publicKeys[0x33] = GLFW_KEY_BACKSPACE; - _glfw.ns.publicKeys[0x39] = GLFW_KEY_CAPS_LOCK; - _glfw.ns.publicKeys[0x75] = GLFW_KEY_DELETE; - _glfw.ns.publicKeys[0x7D] = GLFW_KEY_DOWN; - _glfw.ns.publicKeys[0x77] = GLFW_KEY_END; - _glfw.ns.publicKeys[0x24] = GLFW_KEY_ENTER; - _glfw.ns.publicKeys[0x35] = GLFW_KEY_ESCAPE; - _glfw.ns.publicKeys[0x7A] = GLFW_KEY_F1; - _glfw.ns.publicKeys[0x78] = GLFW_KEY_F2; - _glfw.ns.publicKeys[0x63] = GLFW_KEY_F3; - _glfw.ns.publicKeys[0x76] = GLFW_KEY_F4; - _glfw.ns.publicKeys[0x60] = GLFW_KEY_F5; - _glfw.ns.publicKeys[0x61] = GLFW_KEY_F6; - _glfw.ns.publicKeys[0x62] = GLFW_KEY_F7; - _glfw.ns.publicKeys[0x64] = GLFW_KEY_F8; - _glfw.ns.publicKeys[0x65] = GLFW_KEY_F9; - _glfw.ns.publicKeys[0x6D] = GLFW_KEY_F10; - _glfw.ns.publicKeys[0x67] = GLFW_KEY_F11; - _glfw.ns.publicKeys[0x6F] = GLFW_KEY_F12; - _glfw.ns.publicKeys[0x69] = GLFW_KEY_F13; - _glfw.ns.publicKeys[0x6B] = GLFW_KEY_F14; - _glfw.ns.publicKeys[0x71] = GLFW_KEY_F15; - _glfw.ns.publicKeys[0x6A] = GLFW_KEY_F16; - _glfw.ns.publicKeys[0x40] = GLFW_KEY_F17; - _glfw.ns.publicKeys[0x4F] = GLFW_KEY_F18; - _glfw.ns.publicKeys[0x50] = GLFW_KEY_F19; - _glfw.ns.publicKeys[0x5A] = GLFW_KEY_F20; - _glfw.ns.publicKeys[0x73] = GLFW_KEY_HOME; - _glfw.ns.publicKeys[0x72] = GLFW_KEY_INSERT; - _glfw.ns.publicKeys[0x7B] = GLFW_KEY_LEFT; - _glfw.ns.publicKeys[0x3A] = GLFW_KEY_LEFT_ALT; - _glfw.ns.publicKeys[0x3B] = GLFW_KEY_LEFT_CONTROL; - _glfw.ns.publicKeys[0x38] = GLFW_KEY_LEFT_SHIFT; - _glfw.ns.publicKeys[0x37] = GLFW_KEY_LEFT_SUPER; - _glfw.ns.publicKeys[0x6E] = GLFW_KEY_MENU; - _glfw.ns.publicKeys[0x47] = GLFW_KEY_NUM_LOCK; - _glfw.ns.publicKeys[0x79] = GLFW_KEY_PAGE_DOWN; - _glfw.ns.publicKeys[0x74] = GLFW_KEY_PAGE_UP; - _glfw.ns.publicKeys[0x7C] = GLFW_KEY_RIGHT; - _glfw.ns.publicKeys[0x3D] = GLFW_KEY_RIGHT_ALT; - _glfw.ns.publicKeys[0x3E] = GLFW_KEY_RIGHT_CONTROL; - _glfw.ns.publicKeys[0x3C] = GLFW_KEY_RIGHT_SHIFT; - _glfw.ns.publicKeys[0x36] = GLFW_KEY_RIGHT_SUPER; - _glfw.ns.publicKeys[0x31] = GLFW_KEY_SPACE; - _glfw.ns.publicKeys[0x30] = GLFW_KEY_TAB; - _glfw.ns.publicKeys[0x7E] = GLFW_KEY_UP; + _glfw.ns.keycodes[0x33] = GLFW_KEY_BACKSPACE; + _glfw.ns.keycodes[0x39] = GLFW_KEY_CAPS_LOCK; + _glfw.ns.keycodes[0x75] = GLFW_KEY_DELETE; + _glfw.ns.keycodes[0x7D] = GLFW_KEY_DOWN; + _glfw.ns.keycodes[0x77] = GLFW_KEY_END; + _glfw.ns.keycodes[0x24] = GLFW_KEY_ENTER; + _glfw.ns.keycodes[0x35] = GLFW_KEY_ESCAPE; + _glfw.ns.keycodes[0x7A] = GLFW_KEY_F1; + _glfw.ns.keycodes[0x78] = GLFW_KEY_F2; + _glfw.ns.keycodes[0x63] = GLFW_KEY_F3; + _glfw.ns.keycodes[0x76] = GLFW_KEY_F4; + _glfw.ns.keycodes[0x60] = GLFW_KEY_F5; + _glfw.ns.keycodes[0x61] = GLFW_KEY_F6; + _glfw.ns.keycodes[0x62] = GLFW_KEY_F7; + _glfw.ns.keycodes[0x64] = GLFW_KEY_F8; + _glfw.ns.keycodes[0x65] = GLFW_KEY_F9; + _glfw.ns.keycodes[0x6D] = GLFW_KEY_F10; + _glfw.ns.keycodes[0x67] = GLFW_KEY_F11; + _glfw.ns.keycodes[0x6F] = GLFW_KEY_F12; + _glfw.ns.keycodes[0x69] = GLFW_KEY_F13; + _glfw.ns.keycodes[0x6B] = GLFW_KEY_F14; + _glfw.ns.keycodes[0x71] = GLFW_KEY_F15; + _glfw.ns.keycodes[0x6A] = GLFW_KEY_F16; + _glfw.ns.keycodes[0x40] = GLFW_KEY_F17; + _glfw.ns.keycodes[0x4F] = GLFW_KEY_F18; + _glfw.ns.keycodes[0x50] = GLFW_KEY_F19; + _glfw.ns.keycodes[0x5A] = GLFW_KEY_F20; + _glfw.ns.keycodes[0x73] = GLFW_KEY_HOME; + _glfw.ns.keycodes[0x72] = GLFW_KEY_INSERT; + _glfw.ns.keycodes[0x7B] = GLFW_KEY_LEFT; + _glfw.ns.keycodes[0x3A] = GLFW_KEY_LEFT_ALT; + _glfw.ns.keycodes[0x3B] = GLFW_KEY_LEFT_CONTROL; + _glfw.ns.keycodes[0x38] = GLFW_KEY_LEFT_SHIFT; + _glfw.ns.keycodes[0x37] = GLFW_KEY_LEFT_SUPER; + _glfw.ns.keycodes[0x6E] = GLFW_KEY_MENU; + _glfw.ns.keycodes[0x47] = GLFW_KEY_NUM_LOCK; + _glfw.ns.keycodes[0x79] = GLFW_KEY_PAGE_DOWN; + _glfw.ns.keycodes[0x74] = GLFW_KEY_PAGE_UP; + _glfw.ns.keycodes[0x7C] = GLFW_KEY_RIGHT; + _glfw.ns.keycodes[0x3D] = GLFW_KEY_RIGHT_ALT; + _glfw.ns.keycodes[0x3E] = GLFW_KEY_RIGHT_CONTROL; + _glfw.ns.keycodes[0x3C] = GLFW_KEY_RIGHT_SHIFT; + _glfw.ns.keycodes[0x36] = GLFW_KEY_RIGHT_SUPER; + _glfw.ns.keycodes[0x31] = GLFW_KEY_SPACE; + _glfw.ns.keycodes[0x30] = GLFW_KEY_TAB; + _glfw.ns.keycodes[0x7E] = GLFW_KEY_UP; - _glfw.ns.publicKeys[0x52] = GLFW_KEY_KP_0; - _glfw.ns.publicKeys[0x53] = GLFW_KEY_KP_1; - _glfw.ns.publicKeys[0x54] = GLFW_KEY_KP_2; - _glfw.ns.publicKeys[0x55] = GLFW_KEY_KP_3; - _glfw.ns.publicKeys[0x56] = GLFW_KEY_KP_4; - _glfw.ns.publicKeys[0x57] = GLFW_KEY_KP_5; - _glfw.ns.publicKeys[0x58] = GLFW_KEY_KP_6; - _glfw.ns.publicKeys[0x59] = GLFW_KEY_KP_7; - _glfw.ns.publicKeys[0x5B] = GLFW_KEY_KP_8; - _glfw.ns.publicKeys[0x5C] = GLFW_KEY_KP_9; - _glfw.ns.publicKeys[0x45] = GLFW_KEY_KP_ADD; - _glfw.ns.publicKeys[0x41] = GLFW_KEY_KP_DECIMAL; - _glfw.ns.publicKeys[0x4B] = GLFW_KEY_KP_DIVIDE; - _glfw.ns.publicKeys[0x4C] = GLFW_KEY_KP_ENTER; - _glfw.ns.publicKeys[0x51] = GLFW_KEY_KP_EQUAL; - _glfw.ns.publicKeys[0x43] = GLFW_KEY_KP_MULTIPLY; - _glfw.ns.publicKeys[0x4E] = GLFW_KEY_KP_SUBTRACT; + _glfw.ns.keycodes[0x52] = GLFW_KEY_KP_0; + _glfw.ns.keycodes[0x53] = GLFW_KEY_KP_1; + _glfw.ns.keycodes[0x54] = GLFW_KEY_KP_2; + _glfw.ns.keycodes[0x55] = GLFW_KEY_KP_3; + _glfw.ns.keycodes[0x56] = GLFW_KEY_KP_4; + _glfw.ns.keycodes[0x57] = GLFW_KEY_KP_5; + _glfw.ns.keycodes[0x58] = GLFW_KEY_KP_6; + _glfw.ns.keycodes[0x59] = GLFW_KEY_KP_7; + _glfw.ns.keycodes[0x5B] = GLFW_KEY_KP_8; + _glfw.ns.keycodes[0x5C] = GLFW_KEY_KP_9; + _glfw.ns.keycodes[0x45] = GLFW_KEY_KP_ADD; + _glfw.ns.keycodes[0x41] = GLFW_KEY_KP_DECIMAL; + _glfw.ns.keycodes[0x4B] = GLFW_KEY_KP_DIVIDE; + _glfw.ns.keycodes[0x4C] = GLFW_KEY_KP_ENTER; + _glfw.ns.keycodes[0x51] = GLFW_KEY_KP_EQUAL; + _glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY; + _glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT; for (scancode = 0; scancode < 256; scancode++) { // Store the reverse translation for faster key name lookup - if (_glfw.ns.publicKeys[scancode] >= 0) - _glfw.ns.nativeKeys[_glfw.ns.publicKeys[scancode]] = scancode; + if (_glfw.ns.keycodes[scancode] >= 0) + _glfw.ns.scancodes[_glfw.ns.keycodes[scancode]] = scancode; } } @@ -219,8 +322,9 @@ static GLFWbool updateUnicodeDataNS(void) return GLFW_FALSE; } - _glfw.ns.unicodeData = TISGetInputSourceProperty(_glfw.ns.inputSource, - kTISPropertyUnicodeKeyLayoutData); + _glfw.ns.unicodeData = + TISGetInputSourceProperty(_glfw.ns.inputSource, + kTISPropertyUnicodeKeyLayoutData); if (!_glfw.ns.unicodeData) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -236,7 +340,8 @@ static GLFWbool updateUnicodeDataNS(void) static GLFWbool initializeTIS(void) { // This works only because Cocoa has already loaded it properly - _glfw.ns.tis.bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); + _glfw.ns.tis.bundle = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); if (!_glfw.ns.tis.bundle) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -247,9 +352,6 @@ static GLFWbool initializeTIS(void) CFStringRef* kPropertyUnicodeKeyLayoutData = CFBundleGetDataPointerForName(_glfw.ns.tis.bundle, CFSTR("kTISPropertyUnicodeKeyLayoutData")); - CFStringRef* kNotifySelectedKeyboardInputSourceChanged = - CFBundleGetDataPointerForName(_glfw.ns.tis.bundle, - CFSTR("kTISNotifySelectedKeyboardInputSourceChanged")); _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource = CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, CFSTR("TISCopyCurrentKeyboardLayoutInputSource")); @@ -261,7 +363,6 @@ static GLFWbool initializeTIS(void) CFSTR("LMGetKbdType")); if (!kPropertyUnicodeKeyLayoutData || - !kNotifySelectedKeyboardInputSourceChanged || !TISCopyCurrentKeyboardLayoutInputSource || !TISGetInputSourceProperty || !LMGetKbdType) @@ -273,24 +374,93 @@ static GLFWbool initializeTIS(void) _glfw.ns.tis.kPropertyUnicodeKeyLayoutData = *kPropertyUnicodeKeyLayoutData; - _glfw.ns.tis.kNotifySelectedKeyboardInputSourceChanged = - *kNotifySelectedKeyboardInputSourceChanged; return updateUnicodeDataNS(); } -@interface GLFWLayoutListener : NSObject +@interface GLFWHelper : NSObject @end -@implementation GLFWLayoutListener +@implementation GLFWHelper - (void)selectedKeyboardInputSourceChanged:(NSObject* )object { updateUnicodeDataNS(); } +- (void)doNothing:(id)object +{ +} + +@end // GLFWHelper + +@interface GLFWApplicationDelegate : NSObject @end +@implementation GLFWApplicationDelegate + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + _GLFWwindow* window; + + for (window = _glfw.windowListHead; window; window = window->next) + _glfwInputWindowCloseRequest(window); + + return NSTerminateCancel; +} + +- (void)applicationDidChangeScreenParameters:(NSNotification *) notification +{ + _GLFWwindow* window; + + for (window = _glfw.windowListHead; window; window = window->next) + { + if (window->context.client != GLFW_NO_API) + [window->context.nsgl.object update]; + } + + _glfwPollMonitorsNS(); +} + +- (void)applicationWillFinishLaunching:(NSNotification *)notification +{ + if (_glfw.hints.init.ns.menubar) + { + // In case we are unbundled, make us a proper UI application + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + + // Menu bar setup must go between sharedApplication above and + // finishLaunching below, in order to properly emulate the behavior + // of NSApplicationMain + + if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) + { + [[NSBundle mainBundle] loadNibNamed:@"MainMenu" + owner:NSApp + topLevelObjects:&_glfw.ns.nibObjects]; + } + else + createMenuBar(); + } +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification +{ + [NSApp stop:nil]; + + _glfwPlatformPostEmptyEvent(); +} + +- (void)applicationDidHide:(NSNotification *)notification +{ + int i; + + for (i = 0; i < _glfw.monitorCount; i++) + _glfwRestoreVideoModeNS(_glfw.monitors[i]); +} + +@end // GLFWApplicationDelegate + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -298,19 +468,54 @@ static GLFWbool initializeTIS(void) int _glfwPlatformInit(void) { - _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; + @autoreleasepool { - _glfw.ns.listener = [[GLFWLayoutListener alloc] init]; - [[NSDistributedNotificationCenter defaultCenter] - addObserver:_glfw.ns.listener + _glfw.ns.helper = [[GLFWHelper alloc] init]; + + [NSThread detachNewThreadSelector:@selector(doNothing:) + toTarget:_glfw.ns.helper + withObject:nil]; + + if (NSApp) + _glfw.ns.finishedLaunching = GLFW_TRUE; + + [NSApplication sharedApplication]; + + _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init]; + if (_glfw.ns.delegate == nil) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create application delegate"); + return GLFW_FALSE; + } + + [NSApp setDelegate:_glfw.ns.delegate]; + + NSEvent* (^block)(NSEvent*) = ^ NSEvent* (NSEvent* event) + { + if ([event modifierFlags] & NSEventModifierFlagCommand) + [[NSApp keyWindow] sendEvent:event]; + + return event; + }; + + _glfw.ns.keyUpMonitor = + [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp + handler:block]; + + if (_glfw.hints.init.ns.chdir) + changeToResourcesDirectory(); + + // Press and Hold prevents some keys from emitting repeated characters + NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO}; + [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; + + [[NSNotificationCenter defaultCenter] + addObserver:_glfw.ns.helper selector:@selector(selectedKeyboardInputSourceChanged:) - name:(__bridge NSString*)kTISNotifySelectedKeyboardInputSourceChanged + name:NSTextInputContextKeyboardSelectionDidChangeNotification object:nil]; -#if defined(_GLFW_USE_CHDIR) - changeToResourcesDirectory(); -#endif - createKeyTables(); _glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); @@ -322,17 +527,19 @@ int _glfwPlatformInit(void) if (!initializeTIS()) return GLFW_FALSE; - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - _glfwInitTimerNS(); _glfwInitJoysticksNS(); + _glfwPollMonitorsNS(); return GLFW_TRUE; + + } // autoreleasepool } void _glfwPlatformTerminate(void) { + @autoreleasepool { + if (_glfw.ns.inputSource) { CFRelease(_glfw.ns.inputSource); @@ -353,43 +560,32 @@ void _glfwPlatformTerminate(void) _glfw.ns.delegate = nil; } - if (_glfw.ns.listener) + if (_glfw.ns.helper) { - [[NSDistributedNotificationCenter defaultCenter] - removeObserver:_glfw.ns.listener - name:(__bridge NSString*)kTISNotifySelectedKeyboardInputSourceChanged + [[NSNotificationCenter defaultCenter] + removeObserver:_glfw.ns.helper + name:NSTextInputContextKeyboardSelectionDidChangeNotification object:nil]; - [[NSDistributedNotificationCenter defaultCenter] - removeObserver:_glfw.ns.listener]; - [_glfw.ns.listener release]; - _glfw.ns.listener = nil; + [[NSNotificationCenter defaultCenter] + removeObserver:_glfw.ns.helper]; + [_glfw.ns.helper release]; + _glfw.ns.helper = nil; } - [_glfw.ns.cursor release]; - _glfw.ns.cursor = nil; + if (_glfw.ns.keyUpMonitor) + [NSEvent removeMonitor:_glfw.ns.keyUpMonitor]; free(_glfw.ns.clipboardString); _glfwTerminateNSGL(); _glfwTerminateJoysticksNS(); - _glfwTerminateThreadLocalStoragePOSIX(); - [_glfw.ns.autoreleasePool release]; - _glfw.ns.autoreleasePool = nil; + } // autoreleasepool } const char* _glfwPlatformGetVersionString(void) { - return _GLFW_VERSION_NUMBER " Cocoa NSGL" -#if defined(_GLFW_USE_CHDIR) - " chdir" -#endif -#if defined(_GLFW_USE_MENUBAR) - " menubar" -#endif -#if defined(_GLFW_USE_RETINA) - " retina" -#endif + return _GLFW_VERSION_NUMBER " Cocoa NSGL EGL OSMesa" #if defined(_GLFW_BUILD_DLL) " dynamic" #endif diff --git a/external/glfw/cocoa_joystick.h b/external/glfw/cocoa_joystick.h index 9a59101..0ab8137 100644 --- a/external/glfw/cocoa_joystick.h +++ b/external/glfw/cocoa_joystick.h @@ -1,7 +1,7 @@ //======================================================================== // GLFW 3.3 Cocoa - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -24,37 +24,27 @@ // //======================================================================== -#ifndef _glfw3_cocoa_joystick_h_ -#define _glfw3_cocoa_joystick_h_ - #include #include #include #include -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \ - _GLFWjoystickNS ns_js[GLFW_JOYSTICK_LAST + 1] +#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickNS ns +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE +#define _GLFW_PLATFORM_MAPPING_NAME "Mac OS X" // Cocoa-specific per-joystick data // typedef struct _GLFWjoystickNS { - GLFWbool present; - char name[256]; - - IOHIDDeviceRef deviceRef; - - CFMutableArrayRef axisElements; - CFMutableArrayRef buttonElements; - CFMutableArrayRef hatElements; - - float* axes; - unsigned char* buttons; + IOHIDDeviceRef device; + CFMutableArrayRef axes; + CFMutableArrayRef buttons; + CFMutableArrayRef hats; } _GLFWjoystickNS; void _glfwInitJoysticksNS(void); void _glfwTerminateJoysticksNS(void); -#endif // _glfw3_cocoa_joystick_h_ diff --git a/external/glfw/cocoa_joystick.m b/external/glfw/cocoa_joystick.m index 439918f..db4427d 100644 --- a/external/glfw/cocoa_joystick.m +++ b/external/glfw/cocoa_joystick.m @@ -1,7 +1,7 @@ //======================================================================== // GLFW 3.3 Cocoa - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2019 Camilla Löwy // Copyright (c) 2012 Torsten Walluhn // // This software is provided 'as-is', without any express or implied @@ -42,42 +42,167 @@ // typedef struct _GLFWjoyelementNS { - IOHIDElementRef elementRef; - - long min; - long max; - - long minReport; - long maxReport; + IOHIDElementRef native; + uint32_t usage; + int index; + long minimum; + long maximum; } _GLFWjoyelementNS; -static void getElementsCFArrayHandler(const void* value, void* parameter); - -// Adds an element to the specified joystick +// Returns the value of the specified element of the specified joystick // -static void addJoystickElement(_GLFWjoystickNS* js, - IOHIDElementRef elementRef) +static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element) { - IOHIDElementType elementType; - long usagePage, usage; - CFMutableArrayRef elementsArray = NULL; + IOHIDValueRef valueRef; + long value = 0; - elementType = IOHIDElementGetType(elementRef); - usagePage = IOHIDElementGetUsagePage(elementRef); - usage = IOHIDElementGetUsage(elementRef); - - if ((elementType != kIOHIDElementTypeInput_Axis) && - (elementType != kIOHIDElementTypeInput_Button) && - (elementType != kIOHIDElementTypeInput_Misc)) + if (js->ns.device) { - return; + if (IOHIDDeviceGetValue(js->ns.device, + element->native, + &valueRef) == kIOReturnSuccess) + { + value = IOHIDValueGetIntegerValue(valueRef); + } } - switch (usagePage) + return value; +} + +// Comparison function for matching the SDL element order +// +static CFComparisonResult compareElements(const void* fp, + const void* sp, + void* user) +{ + const _GLFWjoyelementNS* fe = fp; + const _GLFWjoyelementNS* se = sp; + if (fe->usage < se->usage) + return kCFCompareLessThan; + if (fe->usage > se->usage) + return kCFCompareGreaterThan; + if (fe->index < se->index) + return kCFCompareLessThan; + if (fe->index > se->index) + return kCFCompareGreaterThan; + return kCFCompareEqualTo; +} + +// Removes the specified joystick +// +static void closeJoystick(_GLFWjoystick* js) +{ + int i; + + if (!js->present) + return; + + for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) + free((void*) CFArrayGetValueAtIndex(js->ns.axes, i)); + CFRelease(js->ns.axes); + + for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) + free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i)); + CFRelease(js->ns.buttons); + + for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) + free((void*) CFArrayGetValueAtIndex(js->ns.hats, i)); + CFRelease(js->ns.hats); + + _glfwFreeJoystick(js); + _glfwInputJoystick(js, GLFW_DISCONNECTED); +} + +// Callback for user-initiated joystick addition +// +static void matchCallback(void* context, + IOReturn result, + void* sender, + IOHIDDeviceRef device) +{ + int jid; + char name[256]; + char guid[33]; + CFIndex i; + CFTypeRef property; + uint32_t vendor = 0, product = 0, version = 0; + _GLFWjoystick* js; + CFMutableArrayRef axes, buttons, hats; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - case kHIDPage_GenericDesktop: + if (_glfw.joysticks[jid].ns.device == device) + return; + } + + axes = CFArrayCreateMutable(NULL, 0, NULL); + buttons = CFArrayCreateMutable(NULL, 0, NULL); + hats = CFArrayCreateMutable(NULL, 0, NULL); + + property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + if (property) + { + CFStringGetCString(property, + name, + sizeof(name), + kCFStringEncodingUTF8); + } + else + strncpy(name, "Unknown", sizeof(name)); + + property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)); + if (property) + CFNumberGetValue(property, kCFNumberSInt32Type, &vendor); + + property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)); + if (property) + CFNumberGetValue(property, kCFNumberSInt32Type, &product); + + property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVersionNumberKey)); + if (property) + CFNumberGetValue(property, kCFNumberSInt32Type, &version); + + // Generate a joystick GUID that matches the SDL 2.0.5+ one + if (vendor && product) + { + sprintf(guid, "03000000%02x%02x0000%02x%02x0000%02x%02x0000", + (uint8_t) vendor, (uint8_t) (vendor >> 8), + (uint8_t) product, (uint8_t) (product >> 8), + (uint8_t) version, (uint8_t) (version >> 8)); + } + else + { + sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", + name[0], name[1], name[2], name[3], + name[4], name[5], name[6], name[7], + name[8], name[9], name[10]); + } + + CFArrayRef elements = + IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); + + for (i = 0; i < CFArrayGetCount(elements); i++) + { + IOHIDElementRef native = (IOHIDElementRef) + CFArrayGetValueAtIndex(elements, i); + if (CFGetTypeID(native) != IOHIDElementGetTypeID()) + continue; + + const IOHIDElementType type = IOHIDElementGetType(native); + if ((type != kIOHIDElementTypeInput_Axis) && + (type != kIOHIDElementTypeInput_Button) && + (type != kIOHIDElementTypeInput_Misc)) + { + continue; + } + + CFMutableArrayRef target = NULL; + + const uint32_t usage = IOHIDElementGetUsage(native); + const uint32_t page = IOHIDElementGetUsagePage(native); + if (page == kHIDPage_GenericDesktop) { switch (usage) { @@ -90,241 +215,70 @@ static void addJoystickElement(_GLFWjoystickNS* js, case kHIDUsage_GD_Slider: case kHIDUsage_GD_Dial: case kHIDUsage_GD_Wheel: - elementsArray = js->axisElements; + target = axes; break; case kHIDUsage_GD_Hatswitch: - elementsArray = js->hatElements; + target = hats; + break; + case kHIDUsage_GD_DPadUp: + case kHIDUsage_GD_DPadRight: + case kHIDUsage_GD_DPadDown: + case kHIDUsage_GD_DPadLeft: + case kHIDUsage_GD_SystemMainMenu: + case kHIDUsage_GD_Select: + case kHIDUsage_GD_Start: + target = buttons; break; } - - break; } - - case kHIDPage_Button: - elementsArray = js->buttonElements; - break; - default: - break; - } - - if (elementsArray) - { - _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); - - CFArrayAppendValue(elementsArray, element); - - element->elementRef = elementRef; - - element->minReport = IOHIDElementGetLogicalMin(elementRef); - element->maxReport = IOHIDElementGetLogicalMax(elementRef); - } -} - -// Adds an element to the specified joystick -// -static void getElementsCFArrayHandler(const void* value, void* parameter) -{ - if (CFGetTypeID(value) == IOHIDElementGetTypeID()) - { - addJoystickElement((_GLFWjoystickNS*) parameter, - (IOHIDElementRef) value); - } -} - -// Returns the value of the specified element of the specified joystick -// -static long getElementValue(_GLFWjoystickNS* js, _GLFWjoyelementNS* element) -{ - IOReturn result = kIOReturnSuccess; - IOHIDValueRef valueRef; - long value = 0; - - if (js && element && js->deviceRef) - { - result = IOHIDDeviceGetValue(js->deviceRef, - element->elementRef, - &valueRef); - - if (kIOReturnSuccess == result) + else if (page == kHIDPage_Simulation) { - value = IOHIDValueGetIntegerValue(valueRef); - - // Record min and max for auto calibration - if (value < element->minReport) - element->minReport = value; - if (value > element->maxReport) - element->maxReport = value; + switch (usage) + { + case kHIDUsage_Sim_Accelerator: + case kHIDUsage_Sim_Brake: + case kHIDUsage_Sim_Throttle: + case kHIDUsage_Sim_Rudder: + case kHIDUsage_Sim_Steering: + target = axes; + break; + } } - } + else if (page == kHIDPage_Button || page == kHIDPage_Consumer) + target = buttons; - // Auto user scale - return value; -} - -// Removes the specified joystick -// -static void removeJoystick(_GLFWjoystickNS* js) -{ - int i; - - if (!js->present) - return; - - for (i = 0; i < CFArrayGetCount(js->axisElements); i++) - free((void*) CFArrayGetValueAtIndex(js->axisElements, i)); - CFArrayRemoveAllValues(js->axisElements); - CFRelease(js->axisElements); - - for (i = 0; i < CFArrayGetCount(js->buttonElements); i++) - free((void*) CFArrayGetValueAtIndex(js->buttonElements, i)); - CFArrayRemoveAllValues(js->buttonElements); - CFRelease(js->buttonElements); - - for (i = 0; i < CFArrayGetCount(js->hatElements); i++) - free((void*) CFArrayGetValueAtIndex(js->hatElements, i)); - CFArrayRemoveAllValues(js->hatElements); - CFRelease(js->hatElements); - - free(js->axes); - free(js->buttons); - - memset(js, 0, sizeof(_GLFWjoystickNS)); - - _glfwInputJoystickChange(js - _glfw.ns_js, GLFW_DISCONNECTED); -} - -// Polls for joystick axis events and updates GLFW state -// -static GLFWbool pollJoystickAxisEvents(_GLFWjoystickNS* js) -{ - CFIndex i; - - if (!js->present) - return GLFW_FALSE; - - for (i = 0; i < CFArrayGetCount(js->axisElements); i++) - { - _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) - CFArrayGetValueAtIndex(js->axisElements, i); - - long value = getElementValue(js, axis); - long readScale = axis->maxReport - axis->minReport; - - if (readScale == 0) - js->axes[i] = value; - else - js->axes[i] = (2.f * (value - axis->minReport) / readScale) - 1.f; - } - - return GLFW_TRUE; -} - -// Polls for joystick button events and updates GLFW state -// -static GLFWbool pollJoystickButtonEvents(_GLFWjoystickNS* js) -{ - CFIndex i; - int buttonIndex = 0; - - if (!js->present) - return GLFW_FALSE; - - for (i = 0; i < CFArrayGetCount(js->buttonElements); i++) - { - _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) - CFArrayGetValueAtIndex(js->buttonElements, i); - - if (getElementValue(js, button)) - js->buttons[buttonIndex++] = GLFW_PRESS; - else - js->buttons[buttonIndex++] = GLFW_RELEASE; - } - - for (i = 0; i < CFArrayGetCount(js->hatElements); i++) - { - _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*) - CFArrayGetValueAtIndex(js->hatElements, i); - - // Bit fields of button presses for each direction, including nil - const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; - - long j, value = getElementValue(js, hat); - if (value < 0 || value > 8) - value = 8; - - for (j = 0; j < 4; j++) + if (target) { - if (directions[value] & (1 << j)) - js->buttons[buttonIndex++] = GLFW_PRESS; - else - js->buttons[buttonIndex++] = GLFW_RELEASE; + _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); + element->native = native; + element->usage = usage; + element->index = (int) CFArrayGetCount(target); + element->minimum = IOHIDElementGetLogicalMin(native); + element->maximum = IOHIDElementGetLogicalMax(native); + CFArrayAppendValue(target, element); } } - return GLFW_TRUE; -} + CFRelease(elements); -// Callback for user-initiated joystick addition -// -static void matchCallback(void* context, - IOReturn result, - void* sender, - IOHIDDeviceRef deviceRef) -{ - _GLFWjoystickNS* js; - int joy; + CFArraySortValues(axes, CFRangeMake(0, CFArrayGetCount(axes)), + compareElements, NULL); + CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)), + compareElements, NULL); + CFArraySortValues(hats, CFRangeMake(0, CFArrayGetCount(hats)), + compareElements, NULL); - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - { - if (_glfw.ns_js[joy].present && _glfw.ns_js[joy].deviceRef == deviceRef) - return; - } + js = _glfwAllocJoystick(name, guid, + (int) CFArrayGetCount(axes), + (int) CFArrayGetCount(buttons), + (int) CFArrayGetCount(hats)); - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - { - if (!_glfw.ns_js[joy].present) - break; - } + js->ns.device = device; + js->ns.axes = axes; + js->ns.buttons = buttons; + js->ns.hats = hats; - if (joy > GLFW_JOYSTICK_LAST) - return; - - js = _glfw.ns_js + joy; - js->present = GLFW_TRUE; - js->deviceRef = deviceRef; - - CFStringRef name = IOHIDDeviceGetProperty(deviceRef, - CFSTR(kIOHIDProductKey)); - if (name) - { - CFStringGetCString(name, - js->name, - sizeof(js->name), - kCFStringEncodingUTF8); - } - else - strncpy(js->name, "Unknown", sizeof(js->name)); - - js->axisElements = CFArrayCreateMutable(NULL, 0, NULL); - js->buttonElements = CFArrayCreateMutable(NULL, 0, NULL); - js->hatElements = CFArrayCreateMutable(NULL, 0, NULL); - - CFArrayRef arrayRef = IOHIDDeviceCopyMatchingElements(deviceRef, - NULL, - kIOHIDOptionsTypeNone); - CFRange range = { 0, CFArrayGetCount(arrayRef) }; - CFArrayApplyFunction(arrayRef, - range, - getElementsCFArrayHandler, - (void*) js); - - CFRelease(arrayRef); - - js->axes = calloc(CFArrayGetCount(js->axisElements), sizeof(float)); - js->buttons = calloc(CFArrayGetCount(js->buttonElements) + - CFArrayGetCount(js->hatElements) * 4, 1); - - _glfwInputJoystickChange(joy, GLFW_CONNECTED); + _glfwInputJoystick(js, GLFW_CONNECTED); } // Callback for user-initiated joystick removal @@ -332,60 +286,20 @@ static void matchCallback(void* context, static void removeCallback(void* context, IOReturn result, void* sender, - IOHIDDeviceRef deviceRef) + IOHIDDeviceRef device) { - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (_glfw.ns_js[joy].deviceRef == deviceRef) + if (_glfw.joysticks[jid].ns.device == device) { - removeJoystick(_glfw.ns_js + joy); + closeJoystick(_glfw.joysticks + jid); break; } } } -// Creates a dictionary to match against devices with the specified usage page -// and usage -// -static CFMutableDictionaryRef createMatchingDictionary(long usagePage, - long usage) -{ - CFMutableDictionaryRef result = - CFDictionaryCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - if (result) - { - CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault, - kCFNumberLongType, - &usagePage); - if (pageRef) - { - CFDictionarySetValue(result, - CFSTR(kIOHIDDeviceUsagePageKey), - pageRef); - CFRelease(pageRef); - - CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault, - kCFNumberLongType, - &usage); - if (usageRef) - { - CFDictionarySetValue(result, - CFSTR(kIOHIDDeviceUsageKey), - usageRef); - CFRelease(usageRef); - } - } - } - - return result; -} - ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -395,56 +309,73 @@ static CFMutableDictionaryRef createMatchingDictionary(long usagePage, // void _glfwInitJoysticksNS(void) { - CFMutableArrayRef matchingCFArrayRef; + CFMutableArrayRef matching; + const long usages[] = + { + kHIDUsage_GD_Joystick, + kHIDUsage_GD_GamePad, + kHIDUsage_GD_MultiAxisController + }; _glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - matchingCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeArrayCallBacks); - if (matchingCFArrayRef) + matching = CFArrayCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeArrayCallBacks); + if (!matching) { - CFDictionaryRef matchingCFDictRef = - createMatchingDictionary(kHIDPage_GenericDesktop, - kHIDUsage_GD_Joystick); - if (matchingCFDictRef) - { - CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef); - CFRelease(matchingCFDictRef); - } - - matchingCFDictRef = createMatchingDictionary(kHIDPage_GenericDesktop, - kHIDUsage_GD_GamePad); - if (matchingCFDictRef) - { - CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef); - CFRelease(matchingCFDictRef); - } - - matchingCFDictRef = - createMatchingDictionary(kHIDPage_GenericDesktop, - kHIDUsage_GD_MultiAxisController); - if (matchingCFDictRef) - { - CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef); - CFRelease(matchingCFDictRef); - } - - IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, - matchingCFArrayRef); - CFRelease(matchingCFArrayRef); + _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array"); + return; } + for (int i = 0; i < sizeof(usages) / sizeof(long); i++) + { + const long page = kHIDPage_GenericDesktop; + + CFMutableDictionaryRef dict = + CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (!dict) + continue; + + CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault, + kCFNumberLongType, + &page); + CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault, + kCFNumberLongType, + &usages[i]); + if (pageRef && usageRef) + { + CFDictionarySetValue(dict, + CFSTR(kIOHIDDeviceUsagePageKey), + pageRef); + CFDictionarySetValue(dict, + CFSTR(kIOHIDDeviceUsageKey), + usageRef); + CFArrayAppendValue(matching, dict); + } + + if (pageRef) + CFRelease(pageRef); + if (usageRef) + CFRelease(usageRef); + + CFRelease(dict); + } + + IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, matching); + CFRelease(matching); + IOHIDManagerRegisterDeviceMatchingCallback(_glfw.ns.hidManager, &matchCallback, NULL); IOHIDManagerRegisterDeviceRemovalCallback(_glfw.ns.hidManager, &removeCallback, NULL); - IOHIDManagerScheduleWithRunLoop(_glfw.ns.hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode); - IOHIDManagerOpen(_glfw.ns.hidManager, kIOHIDOptionsTypeNone); // Execute the run loop once in order to register any initially-attached @@ -456,13 +387,10 @@ void _glfwInitJoysticksNS(void) // void _glfwTerminateJoysticksNS(void) { - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - { - _GLFWjoystickNS* js = _glfw.ns_js + joy; - removeJoystick(js); - } + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + closeJoystick(_glfw.joysticks + jid); CFRelease(_glfw.ns.hidManager); _glfw.ns.hidManager = NULL; @@ -473,39 +401,85 @@ void _glfwTerminateJoysticksNS(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformJoystickPresent(int joy) +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) { - _GLFWjoystickNS* js = _glfw.ns_js + joy; + if (mode & _GLFW_POLL_AXES) + { + CFIndex i; + + for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) + { + _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->ns.axes, i); + + const long raw = getElementValue(js, axis); + // Perform auto calibration + if (raw < axis->minimum) + axis->minimum = raw; + if (raw > axis->maximum) + axis->maximum = raw; + + const long size = axis->maximum - axis->minimum; + if (size == 0) + _glfwInputJoystickAxis(js, (int) i, 0.f); + else + { + const float value = (2.f * (raw - axis->minimum) / size) - 1.f; + _glfwInputJoystickAxis(js, (int) i, value); + } + } + } + + if (mode & _GLFW_POLL_BUTTONS) + { + CFIndex i; + + for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) + { + _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->ns.buttons, i); + const char value = getElementValue(js, button) - button->minimum; + const int state = (value > 0) ? GLFW_PRESS : GLFW_RELEASE; + _glfwInputJoystickButton(js, (int) i, state); + } + + for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) + { + const int states[9] = + { + GLFW_HAT_UP, + GLFW_HAT_RIGHT_UP, + GLFW_HAT_RIGHT, + GLFW_HAT_RIGHT_DOWN, + GLFW_HAT_DOWN, + GLFW_HAT_LEFT_DOWN, + GLFW_HAT_LEFT, + GLFW_HAT_LEFT_UP, + GLFW_HAT_CENTERED + }; + + _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->ns.hats, i); + long state = getElementValue(js, hat) - hat->minimum; + if (state < 0 || state > 8) + state = 8; + + _glfwInputJoystickHat(js, (int) i, states[state]); + } + } + return js->present; } -const float* _glfwPlatformGetJoystickAxes(int joy, int* count) +void _glfwPlatformUpdateGamepadGUID(char* guid) { - _GLFWjoystickNS* js = _glfw.ns_js + joy; - if (!pollJoystickAxisEvents(js)) - return NULL; - - *count = (int) CFArrayGetCount(js->axisElements); - return js->axes; -} - -const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) -{ - _GLFWjoystickNS* js = _glfw.ns_js + joy; - if (!pollJoystickButtonEvents(js)) - return NULL; - - *count = (int) CFArrayGetCount(js->buttonElements) + - (int) CFArrayGetCount(js->hatElements) * 4; - return js->buttons; -} - -const char* _glfwPlatformGetJoystickName(int joy) -{ - _GLFWjoystickNS* js = _glfw.ns_js + joy; - if (!js->present) - return NULL; - - return js->name; + if ((strncmp(guid + 4, "000000000000", 12) == 0) && + (strncmp(guid + 20, "000000000000", 12) == 0)) + { + char original[33]; + strncpy(original, guid, sizeof(original) - 1); + sprintf(guid, "03000000%.4s0000%.4s000000000000", + original, original + 16); + } } diff --git a/external/glfw/cocoa_monitor.m b/external/glfw/cocoa_monitor.m index 9a384b6..e327c62 100644 --- a/external/glfw/cocoa_monitor.m +++ b/external/glfw/cocoa_monitor.m @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -29,46 +29,86 @@ #include #include +#include #include -#include -#include #include -// Get the name of the specified display +// Get the name of the specified display, or NULL // static char* getDisplayName(CGDirectDisplayID displayID) { - char* name; - CFDictionaryRef info, names; - CFStringRef value; - CFIndex size; + io_iterator_t it; + io_service_t service; + CFDictionaryRef info; - // NOTE: This uses a deprecated function because Apple has - // (as of January 2015) not provided any alternative - info = IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), - kIODisplayOnlyPreferredName); - names = CFDictionaryGetValue(info, CFSTR(kDisplayProductName)); - - if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"), - (const void**) &value)) + if (IOServiceGetMatchingServices(kIOMasterPortDefault, + IOServiceMatching("IODisplayConnect"), + &it) != 0) { // This may happen if a desktop Mac is running headless - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to retrieve display name"); - - CFRelease(info); - return strdup("Unknown"); + return NULL; } - size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), - kCFStringEncodingUTF8); - name = calloc(size + 1, 1); - CFStringGetCString(value, name, size, kCFStringEncodingUTF8); + while ((service = IOIteratorNext(it)) != 0) + { + info = IODisplayCreateInfoDictionary(service, + kIODisplayOnlyPreferredName); + + CFNumberRef vendorIDRef = + CFDictionaryGetValue(info, CFSTR(kDisplayVendorID)); + CFNumberRef productIDRef = + CFDictionaryGetValue(info, CFSTR(kDisplayProductID)); + if (!vendorIDRef || !productIDRef) + { + CFRelease(info); + continue; + } + + unsigned int vendorID, productID; + CFNumberGetValue(vendorIDRef, kCFNumberIntType, &vendorID); + CFNumberGetValue(productIDRef, kCFNumberIntType, &productID); + + if (CGDisplayVendorNumber(displayID) == vendorID && + CGDisplayModelNumber(displayID) == productID) + { + // Info dictionary is used and freed below + break; + } + + CFRelease(info); + } + + IOObjectRelease(it); + + if (!service) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to find service port for display"); + return NULL; + } + + CFDictionaryRef names = + CFDictionaryGetValue(info, CFSTR(kDisplayProductName)); + + CFStringRef nameRef; + + if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"), + (const void**) &nameRef)) + { + // This may happen if a desktop Mac is running headless + CFRelease(info); + return NULL; + } + + const CFIndex size = + CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef), + kCFStringEncodingUTF8); + char* name = calloc(size + 1, 1); + CFStringGetCString(nameRef, name, size, kCFStringEncodingUTF8); CFRelease(info); - return name; } @@ -77,15 +117,15 @@ static char* getDisplayName(CGDirectDisplayID displayID) static GLFWbool modeIsGood(CGDisplayModeRef mode) { uint32_t flags = CGDisplayModeGetIOFlags(mode); + if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag)) return GLFW_FALSE; - if (flags & kDisplayModeInterlacedFlag) return GLFW_FALSE; - if (flags & kDisplayModeStretchedFlag) return GLFW_FALSE; +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) && CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0)) @@ -95,6 +135,7 @@ static GLFWbool modeIsGood(CGDisplayModeRef mode) } CFRelease(format); +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ return GLFW_TRUE; } @@ -106,7 +147,7 @@ static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode, GLFWvidmode result; result.width = (int) CGDisplayModeGetWidth(mode); result.height = (int) CGDisplayModeGetHeight(mode); - result.refreshRate = (int) CGDisplayModeGetRefreshRate(mode); + result.refreshRate = (int) round(CGDisplayModeGetRefreshRate(mode)); if (result.refreshRate == 0) { @@ -115,8 +156,8 @@ static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode, result.refreshRate = (int) (time.timeScale / (double) time.timeValue); } +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); - if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) == 0) { result.redBits = 5; @@ -124,13 +165,16 @@ static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode, result.blueBits = 5; } else +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ { result.redBits = 8; result.greenBits = 8; result.blueBits = 8; } +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 CFRelease(format); +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ return result; } @@ -141,7 +185,13 @@ static CGDisplayFadeReservationToken beginFadeReservation(void) CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken; if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess) - CGDisplayFade(token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); + { + CGDisplayFade(token, 0.3, + kCGDisplayBlendNormal, + kCGDisplayBlendSolidColor, + 0.0, 0.0, 0.0, + TRUE); + } return token; } @@ -152,19 +202,116 @@ static void endFadeReservation(CGDisplayFadeReservationToken token) { if (token != kCGDisplayFadeReservationInvalidToken) { - CGDisplayFade(token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGDisplayFade(token, 0.5, + kCGDisplayBlendSolidColor, + kCGDisplayBlendNormal, + 0.0, 0.0, 0.0, + FALSE); CGReleaseDisplayFadeReservation(token); } } +// Finds and caches the NSScreen corresponding to the specified monitor +// +GLFWbool refreshMonitorScreen(_GLFWmonitor* monitor) +{ + if (monitor->ns.screen) + return GLFW_TRUE; + + for (NSScreen* screen in [NSScreen screens]) + { + NSNumber* displayID = [screen deviceDescription][@"NSScreenNumber"]; + + // HACK: Compare unit numbers instead of display IDs to work around + // display replacement on machines with automatic graphics + // switching + if (monitor->ns.unitNumber == CGDisplayUnitNumber([displayID unsignedIntValue])) + { + monitor->ns.screen = screen; + return GLFW_TRUE; + } + } + + _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to find a screen for monitor"); + return GLFW_FALSE; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// +// Poll for changes in the set of connected monitors +// +void _glfwPollMonitorsNS(void) +{ + uint32_t i, j, displayCount, disconnectedCount; + CGDirectDisplayID* displays; + _GLFWmonitor** disconnected = NULL; + + CGGetOnlineDisplayList(0, NULL, &displayCount); + displays = calloc(displayCount, sizeof(CGDirectDisplayID)); + CGGetOnlineDisplayList(displayCount, displays, &displayCount); + + for (i = 0; i < _glfw.monitorCount; i++) + _glfw.monitors[i]->ns.screen = nil; + + disconnectedCount = _glfw.monitorCount; + if (disconnectedCount) + { + disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + memcpy(disconnected, + _glfw.monitors, + _glfw.monitorCount * sizeof(_GLFWmonitor*)); + } + + for (i = 0; i < displayCount; i++) + { + _GLFWmonitor* monitor; + const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]); + + if (CGDisplayIsAsleep(displays[i])) + continue; + + for (j = 0; j < disconnectedCount; j++) + { + // HACK: Compare unit numbers instead of display IDs to work around + // display replacement on machines with automatic graphics + // switching + if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber) + { + disconnected[j] = NULL; + break; + } + } + + const CGSize size = CGDisplayScreenSize(displays[i]); + char* name = getDisplayName(displays[i]); + if (!name) + name = _glfw_strdup("Unknown"); + + monitor = _glfwAllocMonitor(name, size.width, size.height); + monitor->ns.displayID = displays[i]; + monitor->ns.unitNumber = unitNumber; + + free(name); + + _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); + } + + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i]) + _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); + } + + free(disconnected); + free(displays); +} + // Change the current video mode // -GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) +void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) { CFArrayRef modes; CFIndex count, i; @@ -176,7 +323,7 @@ GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) best = _glfwChooseVideoMode(monitor, desired); _glfwPlatformGetVideoMode(monitor, ¤t); if (_glfwCompareVideoModes(¤t, best) == 0) - return GLFW_TRUE; + return; CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link); @@ -209,15 +356,6 @@ GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) CFRelease(modes); CVDisplayLinkRelease(link); - - if (!native) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Monitor mode list changed"); - return GLFW_FALSE; - } - - return GLFW_TRUE; } // Restore the previously saved (original) video mode @@ -241,65 +379,70 @@ void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -_GLFWmonitor** _glfwPlatformGetMonitors(int* count) +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) { - uint32_t i, found = 0, displayCount; - _GLFWmonitor** monitors; - CGDirectDisplayID* displays; - - *count = 0; - - CGGetOnlineDisplayList(0, NULL, &displayCount); - displays = calloc(displayCount, sizeof(CGDirectDisplayID)); - monitors = calloc(displayCount, sizeof(_GLFWmonitor*)); - - CGGetOnlineDisplayList(displayCount, displays, &displayCount); - - for (i = 0; i < displayCount; i++) - { - _GLFWmonitor* monitor; - - if (CGDisplayIsAsleep(displays[i])) - continue; - - const CGSize size = CGDisplayScreenSize(displays[i]); - char* name = getDisplayName(displays[i]); - - monitor = _glfwAllocMonitor(name, size.width, size.height); - monitor->ns.displayID = displays[i]; - monitor->ns.unitNumber = CGDisplayUnitNumber(displays[i]); - - free(name); - - found++; - monitors[found - 1] = monitor; - } - - free(displays); - - *count = found; - return monitors; -} - -GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) -{ - // HACK: Compare unit numbers instead of display IDs to work around display - // replacement on machines with automatic graphics switching - return first->ns.unitNumber == second->ns.unitNumber; } void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { + @autoreleasepool { + const CGRect bounds = CGDisplayBounds(monitor->ns.displayID); if (xpos) *xpos = (int) bounds.origin.x; if (ypos) *ypos = (int) bounds.origin.y; + + } // autoreleasepool +} + +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + @autoreleasepool { + + if (!refreshMonitorScreen(monitor)) + return; + + const NSRect points = [monitor->ns.screen frame]; + const NSRect pixels = [monitor->ns.screen convertRectToBacking:points]; + + if (xscale) + *xscale = (float) (pixels.size.width / points.size.width); + if (yscale) + *yscale = (float) (pixels.size.height / points.size.height); + + } // autoreleasepool +} + +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) +{ + @autoreleasepool { + + if (!refreshMonitorScreen(monitor)) + return; + + const NSRect frameRect = [monitor->ns.screen visibleFrame]; + + if (xpos) + *xpos = frameRect.origin.x; + if (ypos) + *ypos = _glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1); + if (width) + *width = frameRect.size.width; + if (height) + *height = frameRect.size.height; + + } // autoreleasepool } GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { + @autoreleasepool { + CFArrayRef modes; CFIndex found, i, j; GLFWvidmode* result; @@ -338,10 +481,14 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) CFRelease(modes); CVDisplayLinkRelease(link); return result; + + } // autoreleasepool } void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode) { + @autoreleasepool { + CGDisplayModeRef displayMode; CVDisplayLinkRef link; @@ -352,10 +499,14 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode) CGDisplayModeRelease(displayMode); CVDisplayLinkRelease(link); + + } // autoreleasepool } -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { + @autoreleasepool { + uint32_t i, size = CGDisplayGammaTableCapacity(monitor->ns.displayID); CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue)); @@ -376,10 +527,15 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) } free(values); + return GLFW_TRUE; + + } // autoreleasepool } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { + @autoreleasepool { + int i; CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue)); @@ -397,6 +553,8 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) values + ramp->size * 2); free(values); + + } // autoreleasepool } diff --git a/external/glfw/cocoa_platform.h b/external/glfw/cocoa_platform.h index 891b533..2847f36 100644 --- a/external/glfw/cocoa_platform.h +++ b/external/glfw/cocoa_platform.h @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -24,41 +24,74 @@ // //======================================================================== -#ifndef _glfw3_cocoa_platform_h_ -#define _glfw3_cocoa_platform_h_ - #include #include +#include +#include +#include + +// NOTE: All of NSGL was deprecated in the 10.14 SDK +// This disables the pointless warnings for every symbol we use +#define GL_SILENCE_DEPRECATION + #if defined(__OBJC__) -#import #import #else -#include -#include typedef void* id; #endif -#include "posix_tls.h" +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 + #define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat + #define NSEventMaskAny NSAnyEventMask + #define NSEventMaskKeyUp NSKeyUpMask + #define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask + #define NSEventModifierFlagCommand NSCommandKeyMask + #define NSEventModifierFlagControl NSControlKeyMask + #define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask + #define NSEventModifierFlagOption NSAlternateKeyMask + #define NSEventModifierFlagShift NSShiftKeyMask + #define NSEventTypeApplicationDefined NSApplicationDefined + #define NSWindowStyleMaskBorderless NSBorderlessWindowMask + #define NSWindowStyleMaskClosable NSClosableWindowMask + #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask + #define NSWindowStyleMaskResizable NSResizableWindowMask + #define NSWindowStyleMaskTitled NSTitledWindowMask +#endif + +typedef VkFlags VkMacOSSurfaceCreateFlagsMVK; + +typedef struct VkMacOSSurfaceCreateInfoMVK +{ + VkStructureType sType; + const void* pNext; + VkMacOSSurfaceCreateFlagsMVK flags; + const void* pView; +} VkMacOSSurfaceCreateInfoMVK; + +typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*); + +#include "posix_thread.h" #include "cocoa_joystick.h" #include "nsgl_context.h" +#include "egl_context.h" +#include "osmesa_context.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) #define _glfw_dlsym(handle, name) dlsym(handle, name) +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->ns.view) +#define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY + #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns -#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimeNS ns_time +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerNS ns #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns -#define _GLFW_EGL_CONTEXT_STATE -#define _GLFW_EGL_LIBRARY_CONTEXT_STATE - // HIToolbox.framework pointer typedefs #define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData -#define kTISNotifySelectedKeyboardInputSourceChanged _glfw.ns.tis.kNotifySelectedKeyboardInputSourceChanged typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void); #define TISCopyCurrentKeyboardLayoutInputSource _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource typedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef); @@ -74,6 +107,15 @@ typedef struct _GLFWwindowNS id object; id delegate; id view; + id layer; + + GLFWbool maximized; + GLFWbool retina; + + // Cached window properties to filter out duplicate events + int width, height; + int fbWidth, fbHeight; + float xscale, yscale; // The total sum of the distances the cursor has been warped // since the last cursor motion event was processed @@ -88,17 +130,20 @@ typedef struct _GLFWlibraryNS { CGEventSourceRef eventSource; id delegate; - id autoreleasePool; - id cursor; + GLFWbool finishedLaunching; + GLFWbool cursorHidden; TISInputSourceRef inputSource; IOHIDManagerRef hidManager; id unicodeData; - id listener; + id helper; + id keyUpMonitor; + id nibObjects; char keyName[64]; - short int publicKeys[256]; - short int nativeKeys[GLFW_KEY_LAST + 1]; + short int keycodes[256]; + short int scancodes[GLFW_KEY_LAST + 1]; char* clipboardString; + CGPoint cascadePoint; // Where to place the cursor when re-enabled double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active @@ -110,7 +155,6 @@ typedef struct _GLFWlibraryNS PFN_TISGetInputSourceProperty GetInputSourceProperty; PFN_LMGetKbdType GetKbdType; CFStringRef kPropertyUnicodeKeyLayoutData; - CFStringRef kNotifySelectedKeyboardInputSourceChanged; } tis; } _GLFWlibraryNS; @@ -122,6 +166,7 @@ typedef struct _GLFWmonitorNS CGDirectDisplayID displayID; CGDisplayModeRef previousMode; uint32_t unitNumber; + id screen; } _GLFWmonitorNS; @@ -135,16 +180,18 @@ typedef struct _GLFWcursorNS // Cocoa-specific global timer data // -typedef struct _GLFWtimeNS +typedef struct _GLFWtimerNS { uint64_t frequency; -} _GLFWtimeNS; +} _GLFWtimerNS; void _glfwInitTimerNS(void); -GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired); +void _glfwPollMonitorsNS(void); +void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor); -#endif // _glfw3_cocoa_platform_h_ +float _glfwTransformYNS(float y); + diff --git a/external/glfw/cocoa_time.c b/external/glfw/cocoa_time.c index 9a0bf6a..3b27035 100644 --- a/external/glfw/cocoa_time.c +++ b/external/glfw/cocoa_time.c @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -40,7 +40,7 @@ void _glfwInitTimerNS(void) mach_timebase_info_data_t info; mach_timebase_info(&info); - _glfw.ns_time.frequency = (info.denom * 1e9) / info.numer; + _glfw.timer.ns.frequency = (info.denom * 1e9) / info.numer; } @@ -55,6 +55,6 @@ uint64_t _glfwPlatformGetTimerValue(void) uint64_t _glfwPlatformGetTimerFrequency(void) { - return _glfw.ns_time.frequency; + return _glfw.timer.ns.frequency; } diff --git a/external/glfw/cocoa_window.m b/external/glfw/cocoa_window.m index 5230786..d648096 100644 --- a/external/glfw/cocoa_window.m +++ b/external/glfw/cocoa_window.m @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -29,33 +29,6 @@ #include #include -// Needed for _NSGetProgname -#include - - -// Returns the specified standard cursor -// -static NSCursor* getStandardCursor(int shape) -{ - switch (shape) - { - case GLFW_ARROW_CURSOR: - return [NSCursor arrowCursor]; - case GLFW_IBEAM_CURSOR: - return [NSCursor IBeamCursor]; - case GLFW_CROSSHAIR_CURSOR: - return [NSCursor crosshairCursor]; - case GLFW_HAND_CURSOR: - return [NSCursor pointingHandCursor]; - case GLFW_HRESIZE_CURSOR: - return [NSCursor resizeLeftRightCursor]; - case GLFW_VRESIZE_CURSOR: - return [NSCursor resizeUpDownCursor]; - } - - return nil; -} - // Returns the style mask corresponding to the window settings // static NSUInteger getStyleMask(_GLFWwindow* window) @@ -63,74 +36,107 @@ static NSUInteger getStyleMask(_GLFWwindow* window) NSUInteger styleMask = 0; if (window->monitor || !window->decorated) - styleMask |= NSBorderlessWindowMask; + styleMask |= NSWindowStyleMaskBorderless; else { - styleMask |= NSTitledWindowMask | NSClosableWindowMask | - NSMiniaturizableWindowMask; + styleMask |= NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable; if (window->resizable) - styleMask |= NSResizableWindowMask; + styleMask |= NSWindowStyleMaskResizable; } return styleMask; } -// Center the cursor in the view of the window +// Returns whether the cursor is in the content area of the specified window // -static void centerCursor(_GLFWwindow *window) -{ - int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); - _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); -} - -// Returns whether the cursor is in the client area of the specified window -// -static GLFWbool cursorInClientArea(_GLFWwindow* window) +static GLFWbool cursorInContentArea(_GLFWwindow* window) { const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; return [window->ns.view mouse:pos inRect:[window->ns.view frame]]; } +// Hides the cursor if not already hidden +// +static void hideCursor(_GLFWwindow* window) +{ + if (!_glfw.ns.cursorHidden) + { + [NSCursor hide]; + _glfw.ns.cursorHidden = GLFW_TRUE; + } +} + +// Shows the cursor if not already shown +// +static void showCursor(_GLFWwindow* window) +{ + if (_glfw.ns.cursorHidden) + { + [NSCursor unhide]; + _glfw.ns.cursorHidden = GLFW_FALSE; + } +} + // Updates the cursor image according to its cursor mode // static void updateCursorImage(_GLFWwindow* window) { if (window->cursorMode == GLFW_CURSOR_NORMAL) { + showCursor(window); + if (window->cursor) [(NSCursor*) window->cursor->ns.object set]; else [[NSCursor arrowCursor] set]; } else - [(NSCursor*) _glfw.ns.cursor set]; + hideCursor(window); } -// Transforms the specified y-coordinate between the CG display and NS screen -// coordinate systems +// Apply chosen cursor mode to a focused window // -static float transformY(float y) +static void updateCursorMode(_GLFWwindow* window) { - return CGDisplayBounds(CGMainDisplayID()).size.height - y; + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + _glfw.ns.disabledCursorWindow = window; + _glfwPlatformGetCursorPos(window, + &_glfw.ns.restoreCursorPosX, + &_glfw.ns.restoreCursorPosY); + _glfwCenterCursorInContentArea(window); + CGAssociateMouseAndMouseCursorPosition(false); + } + else if (_glfw.ns.disabledCursorWindow == window) + { + _glfw.ns.disabledCursorWindow = NULL; + CGAssociateMouseAndMouseCursorPosition(true); + _glfwPlatformSetCursorPos(window, + _glfw.ns.restoreCursorPosX, + _glfw.ns.restoreCursorPosY); + } + + if (cursorInContentArea(window)) + updateCursorImage(window); } // Make the specified window and its video mode active on its monitor // -static GLFWbool acquireMonitor(_GLFWwindow* window) +static void acquireMonitor(_GLFWwindow* window) { - const GLFWbool status = _glfwSetVideoModeNS(window->monitor, &window->videoMode); + _glfwSetVideoModeNS(window->monitor, &window->videoMode); const CGRect bounds = CGDisplayBounds(window->monitor->ns.displayID); const NSRect frame = NSMakeRect(bounds.origin.x, - transformY(bounds.origin.y + bounds.size.height), + _glfwTransformYNS(bounds.origin.y + bounds.size.height - 1), bounds.size.width, bounds.size.height); [window->ns.object setFrame:frame display:YES]; - _glfwInputMonitorWindowChange(window->monitor, window); - return status; + _glfwInputMonitorWindow(window->monitor, window); } // Remove the window and restore the original video mode @@ -140,36 +146,38 @@ static void releaseMonitor(_GLFWwindow* window) if (window->monitor->window != window) return; - _glfwInputMonitorWindowChange(window->monitor, NULL); + _glfwInputMonitorWindow(window->monitor, NULL); _glfwRestoreVideoModeNS(window->monitor); } -// Translates OS X key modifiers into GLFW ones +// Translates macOS key modifiers into GLFW ones // static int translateFlags(NSUInteger flags) { int mods = 0; - if (flags & NSShiftKeyMask) + if (flags & NSEventModifierFlagShift) mods |= GLFW_MOD_SHIFT; - if (flags & NSControlKeyMask) + if (flags & NSEventModifierFlagControl) mods |= GLFW_MOD_CONTROL; - if (flags & NSAlternateKeyMask) + if (flags & NSEventModifierFlagOption) mods |= GLFW_MOD_ALT; - if (flags & NSCommandKeyMask) + if (flags & NSEventModifierFlagCommand) mods |= GLFW_MOD_SUPER; + if (flags & NSEventModifierFlagCapsLock) + mods |= GLFW_MOD_CAPS_LOCK; return mods; } -// Translates a OS X keycode to a GLFW keycode +// Translates a macOS keycode to a GLFW keycode // static int translateKey(unsigned int key) { - if (key >= sizeof(_glfw.ns.publicKeys) / sizeof(_glfw.ns.publicKeys[0])) + if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0])) return GLFW_KEY_UNKNOWN; - return _glfw.ns.publicKeys[key]; + return _glfw.ns.keycodes[key]; } // Translate a GLFW keycode to a Cocoa modifier flag @@ -180,16 +188,18 @@ static NSUInteger translateKeyToModifierFlag(int key) { case GLFW_KEY_LEFT_SHIFT: case GLFW_KEY_RIGHT_SHIFT: - return NSShiftKeyMask; + return NSEventModifierFlagShift; case GLFW_KEY_LEFT_CONTROL: case GLFW_KEY_RIGHT_CONTROL: - return NSControlKeyMask; + return NSEventModifierFlagControl; case GLFW_KEY_LEFT_ALT: case GLFW_KEY_RIGHT_ALT: - return NSAlternateKeyMask; + return NSEventModifierFlagOption; case GLFW_KEY_LEFT_SUPER: case GLFW_KEY_RIGHT_SUPER: - return NSCommandKeyMask; + return NSEventModifierFlagCommand; + case GLFW_KEY_CAPS_LOCK: + return NSEventModifierFlagCapsLock; } return 0; @@ -209,13 +219,13 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; _GLFWwindow* window; } -- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow; +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow; @end @implementation GLFWWindowDelegate -- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow { self = [super init]; if (self != nil) @@ -236,13 +246,33 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; [window->context.nsgl.object update]; if (_glfw.ns.disabledCursorWindow == window) - centerCursor(window); + _glfwCenterCursorInContentArea(window); + + const int maximized = [window->ns.object isZoomed]; + if (window->ns.maximized != maximized) + { + window->ns.maximized = maximized; + _glfwInputWindowMaximize(window, maximized); + } const NSRect contentRect = [window->ns.view frame]; const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; - _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); - _glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height); + if (fbRect.size.width != window->ns.fbWidth || + fbRect.size.height != window->ns.fbHeight) + { + window->ns.fbWidth = fbRect.size.width; + window->ns.fbHeight = fbRect.size.height; + _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); + } + + if (contentRect.size.width != window->ns.width || + contentRect.size.height != window->ns.height) + { + window->ns.width = contentRect.size.width; + window->ns.height = contentRect.size.height; + _glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height); + } } - (void)windowDidMove:(NSNotification *)notification @@ -251,7 +281,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; [window->context.nsgl.object update]; if (_glfw.ns.disabledCursorWindow == window) - centerCursor(window); + _glfwCenterCursorInContentArea(window); int x, y; _glfwPlatformGetWindowPos(window, &x, &y); @@ -277,10 +307,10 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)windowDidBecomeKey:(NSNotification *)notification { if (_glfw.ns.disabledCursorWindow == window) - centerCursor(window); + _glfwCenterCursorInContentArea(window); _glfwInputWindowFocus(window, GLFW_TRUE); - _glfwPlatformSetCursorMode(window, window->cursorMode); + updateCursorMode(window); } - (void)windowDidResignKey:(NSNotification *)notification @@ -291,46 +321,10 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; _glfwInputWindowFocus(window, GLFW_FALSE); } -@end - - -//------------------------------------------------------------------------ -// Delegate for application related notifications -//------------------------------------------------------------------------ - -@interface GLFWApplicationDelegate : NSObject -@end - -@implementation GLFWApplicationDelegate - -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +- (void)windowDidChangeScreen:(NSNotification *)notification { - _GLFWwindow* window; - - for (window = _glfw.windowListHead; window; window = window->next) - _glfwInputWindowCloseRequest(window); - - return NSTerminateCancel; -} - -- (void)applicationDidChangeScreenParameters:(NSNotification *) notification -{ - _glfwInputMonitorChange(); -} - -- (void)applicationDidFinishLaunching:(NSNotification *)notification -{ - [NSApp stop:nil]; - - _glfwPlatformPostEmptyEvent(); -} - -- (void)applicationDidHide:(NSNotification *)notification -{ - int i; - - for (i = 0; i < _glfw.monitorCount; i++) - _glfwRestoreVideoModeNS(_glfw.monitors[i]); + if (window->context.source == GLFW_NATIVE_CONTEXT_API) + _glfwUpdateDisplayLinkDisplayNSGL(window); } @end @@ -347,27 +341,13 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; NSMutableAttributedString* markedText; } -- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow; +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow; @end @implementation GLFWContentView -+ (void)initialize -{ - if (self == [GLFWContentView class]) - { - if (_glfw.ns.cursor == nil) - { - NSImage* data = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]; - _glfw.ns.cursor = [[NSCursor alloc] initWithImage:data - hotSpot:NSZeroPoint]; - [data release]; - } - } -} - -- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow { self = [super init]; if (self != nil) @@ -377,8 +357,9 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; markedText = [[NSMutableAttributedString alloc] init]; [self updateTrackingAreas]; - [self registerForDraggedTypes:[NSArray arrayWithObjects: - NSFilenamesPboardType, nil]]; + // NOTE: kUTTypeURL corresponds to NSPasteboardTypeURL but is available + // on 10.7 without having been deprecated yet + [self registerForDraggedTypes:@[(__bridge NSString*) kUTTypeURL]]; } return self; @@ -393,7 +374,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (BOOL)isOpaque { - return YES; + return [window->ns.object isOpaque]; } - (BOOL)canBecomeKeyView @@ -406,11 +387,29 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; return YES; } +- (BOOL)wantsUpdateLayer +{ + return YES; +} + +- (void)updateLayer +{ + if (window->context.client != GLFW_NO_API) + [window->context.nsgl.object update]; + + _glfwInputWindowDamage(window); +} + - (void)cursorUpdate:(NSEvent *)event { updateCursorImage(window); } +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + return YES; +} + - (void)mouseDown:(NSEvent *)event { _glfwInputMouseClick(window, @@ -446,6 +445,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; else { const NSRect contentRect = [window->ns.view frame]; + // NOTE: The returned location uses base 0,1 not 0,0 const NSPoint pos = [event locationInWindow]; _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y); @@ -499,11 +499,17 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)mouseExited:(NSEvent *)event { + if (window->cursorMode == GLFW_CURSOR_HIDDEN) + showCursor(window); + _glfwInputCursorEnter(window, GLFW_FALSE); } - (void)mouseEntered:(NSEvent *)event { + if (window->cursorMode == GLFW_CURSOR_HIDDEN) + hideCursor(window); + _glfwInputCursorEnter(window, GLFW_TRUE); } @@ -512,7 +518,26 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; const NSRect contentRect = [window->ns.view frame]; const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; - _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); + if (fbRect.size.width != window->ns.fbWidth || + fbRect.size.height != window->ns.fbHeight) + { + window->ns.fbWidth = fbRect.size.width; + window->ns.fbHeight = fbRect.size.height; + _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); + } + + const float xscale = fbRect.size.width / contentRect.size.width; + const float yscale = fbRect.size.height / contentRect.size.height; + + if (xscale != window->ns.xscale || yscale != window->ns.yscale) + { + window->ns.xscale = xscale; + window->ns.yscale = yscale; + _glfwInputWindowContentScale(window, xscale, yscale); + + if (window->ns.retina && window->ns.layer) + [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; + } } - (void)drawRect:(NSRect)rect @@ -551,14 +576,14 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; _glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods); - [self interpretKeyEvents:[NSArray arrayWithObject:event]]; + [self interpretKeyEvents:@[event]]; } - (void)flagsChanged:(NSEvent *)event { int action; const unsigned int modifierFlags = - [event modifierFlags] & NSDeviceIndependentModifierFlagsMask; + [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; const int key = translateKey([event keyCode]); const int mods = translateFlags(modifierFlags); const NSUInteger keyFlag = translateKeyToModifierFlag(key); @@ -602,45 +627,33 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (NSDragOperation)draggingEntered:(id )sender { - if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) - == NSDragOperationGeneric) - { - [self setNeedsDisplay:YES]; - return NSDragOperationGeneric; - } - - return NSDragOperationNone; -} - -- (BOOL)prepareForDragOperation:(id )sender -{ - [self setNeedsDisplay:YES]; - return YES; + // HACK: We don't know what to say here because we don't know what the + // application wants to do with the paths + return NSDragOperationGeneric; } - (BOOL)performDragOperation:(id )sender { - NSPasteboard* pasteboard = [sender draggingPasteboard]; - NSArray* files = [pasteboard propertyListForType:NSFilenamesPboardType]; - const NSRect contentRect = [window->ns.view frame]; - _glfwInputCursorPos(window, - [sender draggingLocation].x, - contentRect.size.height - [sender draggingLocation].y); + // NOTE: The returned location uses base 0,1 not 0,0 + const NSPoint pos = [sender draggingLocation]; + _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y); - const int count = [files count]; + NSPasteboard* pasteboard = [sender draggingPasteboard]; + NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES}; + NSArray* urls = [pasteboard readObjectsForClasses:@[[NSURL class]] + options:options]; + const NSUInteger count = [urls count]; if (count) { - NSEnumerator* e = [files objectEnumerator]; char** paths = calloc(count, sizeof(char*)); - int i; - for (i = 0; i < count; i++) - paths[i] = strdup([[e nextObject] UTF8String]); + for (NSUInteger i = 0; i < count; i++) + paths[i] = _glfw_strdup([urls[i] fileSystemRepresentation]); - _glfwInputDrop(window, count, (const char**) paths); + _glfwInputDrop(window, (int) count, (const char**) paths); - for (i = 0; i < count; i++) + for (NSUInteger i = 0; i < count; i++) free(paths[i]); free(paths); } @@ -648,11 +661,6 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; return YES; } -- (void)concludeDragOperation:(id )sender -{ - [self setNeedsDisplay:YES]; -} - - (BOOL)hasMarkedText { return [markedText length] > 0; @@ -675,10 +683,11 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange { + [markedText release]; if ([string isKindOfClass:[NSAttributedString class]]) - [markedText initWithAttributedString:string]; + markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string]; else - [markedText initWithString:string]; + markedText = [[NSMutableAttributedString alloc] initWithString:string]; } - (void)unmarkText @@ -705,10 +714,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange { - int xpos, ypos; - _glfwPlatformGetWindowPos(window, &xpos, &ypos); - const NSRect contentRect = [window->ns.view frame]; - return NSMakeRect(xpos, transformY(ypos + contentRect.size.height), 0.0, 0.0); + const NSRect frame = [window->ns.view frame]; + return NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0); } - (void)insertText:(id)string replacementRange:(NSRange)replacementRange @@ -753,199 +760,23 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (BOOL)canBecomeKeyWindow { - // Required for NSBorderlessWindowMask windows + // Required for NSWindowStyleMaskBorderless windows + return YES; +} + +- (BOOL)canBecomeMainWindow +{ return YES; } @end -//------------------------------------------------------------------------ -// GLFW application class -//------------------------------------------------------------------------ - -@interface GLFWApplication : NSApplication -@end - -@implementation GLFWApplication - -// From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost -// This works around an AppKit bug, where key up events while holding -// down the command key don't get sent to the key window. -- (void)sendEvent:(NSEvent *)event -{ - if ([event type] == NSKeyUp && ([event modifierFlags] & NSCommandKeyMask)) - [[self keyWindow] sendEvent:event]; - else - [super sendEvent:event]; -} - - -// No-op thread entry point -// -- (void)doNothing:(id)object -{ -} -@end - -#if defined(_GLFW_USE_MENUBAR) - -// Try to figure out what the calling application is called -// -static NSString* findAppName(void) -{ - size_t i; - NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary]; - - // Keys to search for as potential application names - NSString* GLFWNameKeys[] = - { - @"CFBundleDisplayName", - @"CFBundleName", - @"CFBundleExecutable", - }; - - for (i = 0; i < sizeof(GLFWNameKeys) / sizeof(GLFWNameKeys[0]); i++) - { - id name = [infoDictionary objectForKey:GLFWNameKeys[i]]; - if (name && - [name isKindOfClass:[NSString class]] && - ![name isEqualToString:@""]) - { - return name; - } - } - - char** progname = _NSGetProgname(); - if (progname && *progname) - return [NSString stringWithUTF8String:*progname]; - - // Really shouldn't get here - return @"GLFW Application"; -} - -// Set up the menu bar (manually) -// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that -// could go away at any moment, lots of stuff that really should be -// localize(d|able), etc. Loading a nib would save us this horror, but that -// doesn't seem like a good thing to require of GLFW users. -// -static void createMenuBar(void) -{ - NSString* appName = findAppName(); - - NSMenu* bar = [[NSMenu alloc] init]; - [NSApp setMainMenu:bar]; - - NSMenuItem* appMenuItem = - [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; - NSMenu* appMenu = [[NSMenu alloc] init]; - [appMenuItem setSubmenu:appMenu]; - - [appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName] - action:@selector(orderFrontStandardAboutPanel:) - keyEquivalent:@""]; - [appMenu addItem:[NSMenuItem separatorItem]]; - NSMenu* servicesMenu = [[NSMenu alloc] init]; - [NSApp setServicesMenu:servicesMenu]; - [[appMenu addItemWithTitle:@"Services" - action:NULL - keyEquivalent:@""] setSubmenu:servicesMenu]; - [servicesMenu release]; - [appMenu addItem:[NSMenuItem separatorItem]]; - [appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName] - action:@selector(hide:) - keyEquivalent:@"h"]; - [[appMenu addItemWithTitle:@"Hide Others" - action:@selector(hideOtherApplications:) - keyEquivalent:@"h"] - setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask]; - [appMenu addItemWithTitle:@"Show All" - action:@selector(unhideAllApplications:) - keyEquivalent:@""]; - [appMenu addItem:[NSMenuItem separatorItem]]; - [appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName] - action:@selector(terminate:) - keyEquivalent:@"q"]; - - NSMenuItem* windowMenuItem = - [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; - [bar release]; - NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; - [NSApp setWindowsMenu:windowMenu]; - [windowMenuItem setSubmenu:windowMenu]; - - [windowMenu addItemWithTitle:@"Minimize" - action:@selector(performMiniaturize:) - keyEquivalent:@"m"]; - [windowMenu addItemWithTitle:@"Zoom" - action:@selector(performZoom:) - keyEquivalent:@""]; - [windowMenu addItem:[NSMenuItem separatorItem]]; - [windowMenu addItemWithTitle:@"Bring All to Front" - action:@selector(arrangeInFront:) - keyEquivalent:@""]; - - // TODO: Make this appear at the bottom of the menu (for consistency) - [windowMenu addItem:[NSMenuItem separatorItem]]; - [[windowMenu addItemWithTitle:@"Enter Full Screen" - action:@selector(toggleFullScreen:) - keyEquivalent:@"f"] - setKeyEquivalentModifierMask:NSControlKeyMask | NSCommandKeyMask]; - - // Prior to Snow Leopard, we need to use this oddly-named semi-private API - // to get the application menu working properly. - SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:"); - [NSApp performSelector:setAppleMenuSelector withObject:appMenu]; -} - -#endif /* _GLFW_USE_MENUBAR */ - -// Initialize the Cocoa Application Kit -// -static GLFWbool initializeAppKit(void) -{ - if (NSApp) - return GLFW_TRUE; - - // Implicitly create shared NSApplication instance - [GLFWApplication sharedApplication]; - - // Make Cocoa enter multi-threaded mode - [NSThread detachNewThreadSelector:@selector(doNothing:) - toTarget:NSApp - withObject:nil]; - - // In case we are unbundled, make us a proper UI application - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - -#if defined(_GLFW_USE_MENUBAR) - // Menu bar setup must go between sharedApplication above and - // finishLaunching below, in order to properly emulate the behavior - // of NSApplicationMain - createMenuBar(); -#endif - - // There can only be one application delegate, but we allocate it the - // first time a window is created to keep all window code in this file - _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init]; - if (_glfw.ns.delegate == nil) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to create application delegate"); - return GLFW_FALSE; - } - - [NSApp setDelegate:_glfw.ns.delegate]; - [NSApp run]; - - return GLFW_TRUE; -} - // Create the Cocoa window // static GLFWbool createNativeWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig) + const _GLFWwndconfig* wndconfig, + const _GLFWfbconfig* fbconfig) { window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window]; if (window->ns.delegate == nil) @@ -987,9 +818,17 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, else { [window->ns.object center]; + _glfw.ns.cascadePoint = + NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint: + NSPointFromCGPoint(_glfw.ns.cascadePoint)]); if (wndconfig->resizable) - [window->ns.object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + { + const NSWindowCollectionBehavior behavior = + NSWindowCollectionBehaviorFullScreenPrimary | + NSWindowCollectionBehaviorManaged; + [window->ns.object setCollectionBehavior:behavior]; + } if (wndconfig->floating) [window->ns.object setLevel:NSFloatingWindowLevel]; @@ -998,23 +837,50 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, [window->ns.object zoom:nil]; } + if (strlen(wndconfig->ns.frameName)) + [window->ns.object setFrameAutosaveName:@(wndconfig->ns.frameName)]; + window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window]; + window->ns.retina = wndconfig->ns.retina; -#if defined(_GLFW_USE_RETINA) - [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; -#endif /*_GLFW_USE_RETINA*/ + if (fbconfig->transparent) + { + [window->ns.object setOpaque:NO]; + [window->ns.object setHasShadow:NO]; + [window->ns.object setBackgroundColor:[NSColor clearColor]]; + } + [window->ns.object setContentView:window->ns.view]; [window->ns.object makeFirstResponder:window->ns.view]; - [window->ns.object setTitle:[NSString stringWithUTF8String:wndconfig->title]]; + [window->ns.object setTitle:@(wndconfig->title)]; [window->ns.object setDelegate:window->ns.delegate]; [window->ns.object setAcceptsMouseMovedEvents:YES]; - [window->ns.object setContentView:window->ns.view]; [window->ns.object setRestorable:NO]; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 + if ([window->ns.object respondsToSelector:@selector(setTabbingMode:)]) + [window->ns.object setTabbingMode:NSWindowTabbingModeDisallowed]; +#endif + + _glfwPlatformGetWindowSize(window, &window->ns.width, &window->ns.height); + _glfwPlatformGetFramebufferSize(window, &window->ns.fbWidth, &window->ns.fbHeight); + return GLFW_TRUE; } +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Transforms a y-coordinate between the CG display and NS screen spaces +// +float _glfwTransformYNS(float y) +{ + return CGDisplayBounds(CGMainDisplayID()).size.height - y - 1; +} + + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// @@ -1024,10 +890,15 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { - if (!initializeAppKit()) - return GLFW_FALSE; + @autoreleasepool { - if (!createNativeWindow(window, wndconfig)) + if (!_glfw.ns.finishedLaunching) + { + [NSApp run]; + _glfw.ns.finishedLaunching = GLFW_TRUE; + } + + if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) @@ -1039,10 +910,19 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { - _glfwInputError(GLFW_API_UNAVAILABLE, "Cocoa: EGL not available"); - return GLFW_FALSE; + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; } } @@ -1050,17 +930,18 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, { _glfwPlatformShowWindow(window); _glfwPlatformFocusWindow(window); - if (!acquireMonitor(window)) - return GLFW_FALSE; - - centerCursor(window); + acquireMonitor(window); } return GLFW_TRUE; + + } // autoreleasepool } void _glfwPlatformDestroyWindow(_GLFWwindow* window) { + @autoreleasepool { + if (_glfw.ns.disabledCursorWindow == window) _glfw.ns.disabledCursorWindow = NULL; @@ -1082,13 +963,17 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) [window->ns.object close]; window->ns.object = nil; - [_glfw.ns.autoreleasePool drain]; - _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; + } // autoreleasepool } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title) { - [window->ns.object setTitle:[NSString stringWithUTF8String:title]]; + @autoreleasepool { + [window->ns.object setTitle:@(title)]; + // HACK: Set the miniwindow title explicitly as setTitle: doesn't update it + // if the window lacks NSWindowStyleMaskTitled + [window->ns.object setMiniwindowTitle:@(title)]; + } // autoreleasepool } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, @@ -1099,35 +984,49 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window, void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) { + @autoreleasepool { + const NSRect contentRect = [window->ns.object contentRectForFrameRect:[window->ns.object frame]]; if (xpos) *xpos = contentRect.origin.x; if (ypos) - *ypos = transformY(contentRect.origin.y + contentRect.size.height); + *ypos = _glfwTransformYNS(contentRect.origin.y + contentRect.size.height - 1); + + } // autoreleasepool } void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y) { + @autoreleasepool { + const NSRect contentRect = [window->ns.view frame]; - const NSRect dummyRect = NSMakeRect(x, transformY(y + contentRect.size.height), 0, 0); + const NSRect dummyRect = NSMakeRect(x, _glfwTransformYNS(y + contentRect.size.height - 1), 0, 0); const NSRect frameRect = [window->ns.object frameRectForContentRect:dummyRect]; [window->ns.object setFrameOrigin:frameRect.origin]; + + } // autoreleasepool } void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) { + @autoreleasepool { + const NSRect contentRect = [window->ns.view frame]; if (width) *width = contentRect.size.width; if (height) *height = contentRect.size.height; + + } // autoreleasepool } void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { + @autoreleasepool { + if (window->monitor) { if (window->monitor->window == window) @@ -1135,12 +1034,16 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) } else [window->ns.object setContentSize:NSMakeSize(width, height)]; + + } // autoreleasepool } void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) { + @autoreleasepool { + if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) [window->ns.object setContentMinSize:NSMakeSize(0, 0)]; else @@ -1150,18 +1053,24 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, [window->ns.object setContentMaxSize:NSMakeSize(DBL_MAX, DBL_MAX)]; else [window->ns.object setContentMaxSize:NSMakeSize(maxwidth, maxheight)]; + + } // autoreleasepool } void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) { + @autoreleasepool { if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) - [window->ns.object setContentAspectRatio:NSMakeSize(0, 0)]; + [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)]; else [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)]; + } // autoreleasepool } void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) { + @autoreleasepool { + const NSRect contentRect = [window->ns.view frame]; const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; @@ -1169,12 +1078,16 @@ void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* heigh *width = (int) fbRect.size.width; if (height) *height = (int) fbRect.size.height; + + } // autoreleasepool } void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom) { + @autoreleasepool { + const NSRect contentRect = [window->ns.view frame]; const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect]; @@ -1188,46 +1101,82 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, contentRect.origin.x - contentRect.size.width; if (bottom) *bottom = contentRect.origin.y - frameRect.origin.y; + + } // autoreleasepool +} + +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + @autoreleasepool { + + const NSRect points = [window->ns.view frame]; + const NSRect pixels = [window->ns.view convertRectToBacking:points]; + + if (xscale) + *xscale = (float) (pixels.size.width / points.size.width); + if (yscale) + *yscale = (float) (pixels.size.height / points.size.height); + + } // autoreleasepool } void _glfwPlatformIconifyWindow(_GLFWwindow* window) { + @autoreleasepool { [window->ns.object miniaturize:nil]; + } // autoreleasepool } void _glfwPlatformRestoreWindow(_GLFWwindow* window) { + @autoreleasepool { if ([window->ns.object isMiniaturized]) [window->ns.object deminiaturize:nil]; else if ([window->ns.object isZoomed]) [window->ns.object zoom:nil]; + } // autoreleasepool } void _glfwPlatformMaximizeWindow(_GLFWwindow* window) { + @autoreleasepool { if (![window->ns.object isZoomed]) [window->ns.object zoom:nil]; + } // autoreleasepool } void _glfwPlatformShowWindow(_GLFWwindow* window) { + @autoreleasepool { [window->ns.object orderFront:nil]; + } // autoreleasepool } void _glfwPlatformHideWindow(_GLFWwindow* window) { + @autoreleasepool { [window->ns.object orderOut:nil]; + } // autoreleasepool +} + +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + @autoreleasepool { + [NSApp requestUserAttention:NSInformationalRequest]; + } // autoreleasepool } void _glfwPlatformFocusWindow(_GLFWwindow* window) { + @autoreleasepool { // Make us the active application - // HACK: This has been moved here from initializeAppKit to prevent - // applications using only hidden windows from being activated, but - // should probably not be done every time any window is shown + // HACK: This is here to prevent applications using only hidden windows from + // being activated, but should probably not be done every time any + // window is shown [NSApp activateIgnoringOtherApps:YES]; - [window->ns.object makeKeyAndOrderFront:nil]; + } // autoreleasepool } void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, @@ -1236,6 +1185,8 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, int width, int height, int refreshRate) { + @autoreleasepool { + if (window->monitor == monitor) { if (monitor) @@ -1246,7 +1197,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, else { const NSRect contentRect = - NSMakeRect(xpos, transformY(ypos + height), width, height); + NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), width, height); const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect styleMask:getStyleMask(window)]; @@ -1260,34 +1211,17 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (window->monitor) releaseMonitor(window); - _glfwInputWindowMonitorChange(window, monitor); + _glfwInputWindowMonitor(window, monitor); + + // HACK: Allow the state cached in Cocoa to catch up to reality + // TODO: Solve this in a less terrible way + _glfwPlatformPollEvents(); const NSUInteger styleMask = getStyleMask(window); [window->ns.object setStyleMask:styleMask]; + // HACK: Changing the style mask can cause the first responder to be cleared [window->ns.object makeFirstResponder:window->ns.view]; - NSRect contentRect; - - if (monitor) - { - GLFWvidmode mode; - - _glfwPlatformGetVideoMode(window->monitor, &mode); - _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); - - contentRect = NSMakeRect(xpos, transformY(ypos + mode.height), - mode.width, mode.height); - } - else - { - contentRect = NSMakeRect(xpos, transformY(ypos + height), - width, height); - } - - NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect - styleMask:styleMask]; - [window->ns.object setFrame:frameRect display:YES]; - if (monitor) { [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; @@ -1297,6 +1231,12 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, } else { + NSRect contentRect = NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), + width, height); + NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect + styleMask:styleMask]; + [window->ns.object setFrame:frameRect display:YES]; + if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) { @@ -1324,34 +1264,122 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, [window->ns.object setLevel:NSNormalWindowLevel]; [window->ns.object setHasShadow:YES]; + // HACK: Clearing NSWindowStyleMaskTitled resets and disables the window + // title property but the miniwindow title property is unaffected + [window->ns.object setTitle:[window->ns.object miniwindowTitle]]; } + + } // autoreleasepool } int _glfwPlatformWindowFocused(_GLFWwindow* window) { + @autoreleasepool { return [window->ns.object isKeyWindow]; + } // autoreleasepool } int _glfwPlatformWindowIconified(_GLFWwindow* window) { + @autoreleasepool { return [window->ns.object isMiniaturized]; + } // autoreleasepool } int _glfwPlatformWindowVisible(_GLFWwindow* window) { + @autoreleasepool { return [window->ns.object isVisible]; + } // autoreleasepool } int _glfwPlatformWindowMaximized(_GLFWwindow* window) { + @autoreleasepool { return [window->ns.object isZoomed]; + } // autoreleasepool +} + +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + @autoreleasepool { + + const NSPoint point = [NSEvent mouseLocation]; + + if ([NSWindow windowNumberAtPoint:point belowWindowWithWindowNumber:0] != + [window->ns.object windowNumber]) + { + return GLFW_FALSE; + } + + return NSMouseInRect(point, + [window->ns.object convertRectToScreen:[window->ns.view frame]], NO); + + } // autoreleasepool +} + +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + @autoreleasepool { + return ![window->ns.object isOpaque] && ![window->ns.view isOpaque]; + } // autoreleasepool +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + @autoreleasepool { + [window->ns.object setStyleMask:getStyleMask(window)]; + } // autoreleasepool +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + @autoreleasepool { + [window->ns.object setStyleMask:getStyleMask(window)]; + [window->ns.object makeFirstResponder:window->ns.view]; + } // autoreleasepool +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + @autoreleasepool { + if (enabled) + [window->ns.object setLevel:NSFloatingWindowLevel]; + else + [window->ns.object setLevel:NSNormalWindowLevel]; + } // autoreleasepool +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + @autoreleasepool { + return (float) [window->ns.object alphaValue]; + } // autoreleasepool +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ + @autoreleasepool { + [window->ns.object setAlphaValue:opacity]; + } // autoreleasepool +} + +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +{ +} + +GLFWbool _glfwPlatformRawMouseMotionSupported(void) +{ + return GLFW_FALSE; } void _glfwPlatformPollEvents(void) { + @autoreleasepool { + for (;;) { - NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; @@ -1361,28 +1389,33 @@ void _glfwPlatformPollEvents(void) [NSApp sendEvent:event]; } - [_glfw.ns.autoreleasePool drain]; - _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; + } // autoreleasepool } void _glfwPlatformWaitEvents(void) { + @autoreleasepool { + // I wanted to pass NO to dequeue:, and rely on PollEvents to // dequeue and send. For reasons not at all clear to me, passing // NO to dequeue: causes this method never to return. - NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask + NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES]; [NSApp sendEvent:event]; _glfwPlatformPollEvents(); + + } // autoreleasepool } void _glfwPlatformWaitEventsTimeout(double timeout) { + @autoreleasepool { + NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout]; - NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:date inMode:NSDefaultRunLoopMode dequeue:YES]; @@ -1390,12 +1423,15 @@ void _glfwPlatformWaitEventsTimeout(double timeout) [NSApp sendEvent:event]; _glfwPlatformPollEvents(); + + } // autoreleasepool } void _glfwPlatformPostEmptyEvent(void) { - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined + @autoreleasepool { + + NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 @@ -1405,25 +1441,34 @@ void _glfwPlatformPostEmptyEvent(void) data1:0 data2:0]; [NSApp postEvent:event atStart:YES]; - [pool drain]; + + } // autoreleasepool } void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { + @autoreleasepool { + const NSRect contentRect = [window->ns.view frame]; + // NOTE: The returned location uses base 0,1 not 0,0 const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; if (xpos) *xpos = pos.x; if (ypos) - *ypos = contentRect.size.height - pos.y - 1; + *ypos = contentRect.size.height - pos.y; + + } // autoreleasepool } void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) { + @autoreleasepool { + updateCursorImage(window); const NSRect contentRect = [window->ns.view frame]; + // NOTE: The returned location uses base 0,1 not 0,0 const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; window->ns.cursorWarpDeltaX += x - pos.x; @@ -1441,41 +1486,23 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) const NSPoint globalPoint = globalRect.origin; CGWarpMouseCursorPosition(CGPointMake(globalPoint.x, - transformY(globalPoint.y))); + _glfwTransformYNS(globalPoint.y))); } + + } // autoreleasepool } void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { - if (mode == GLFW_CURSOR_DISABLED) - { - _glfw.ns.disabledCursorWindow = window; - _glfwPlatformGetCursorPos(window, - &_glfw.ns.restoreCursorPosX, - &_glfw.ns.restoreCursorPosY); - centerCursor(window); - CGAssociateMouseAndMouseCursorPosition(false); - } - else if (_glfw.ns.disabledCursorWindow == window) - { - _glfw.ns.disabledCursorWindow = NULL; - CGAssociateMouseAndMouseCursorPosition(true); - _glfwPlatformSetCursorPos(window, - _glfw.ns.restoreCursorPosX, - _glfw.ns.restoreCursorPosY); - } - - if (cursorInClientArea(window)) - updateCursorImage(window); + @autoreleasepool { + if (_glfwPlatformWindowFocused(window)) + updateCursorMode(window); + } // autoreleasepool } -const char* _glfwPlatformGetKeyName(int key, int scancode) +const char* _glfwPlatformGetScancodeName(int scancode) { - if (key != GLFW_KEY_UNKNOWN) - scancode = _glfw.ns.nativeKeys[key]; - - if (!_glfwIsPrintable(_glfw.ns.publicKeys[scancode])) - return NULL; + @autoreleasepool { UInt32 deadKeyState = 0; UniChar characters[8]; @@ -1509,18 +1536,24 @@ const char* _glfwPlatformGetKeyName(int key, int scancode) CFRelease(string); return _glfw.ns.keyName; + + } // autoreleasepool +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.ns.scancodes[key]; } int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) { + @autoreleasepool { + NSImage* native; NSBitmapImageRep* rep; - if (!initializeAppKit()) - return GLFW_FALSE; - rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:image->width @@ -1530,7 +1563,7 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, hasAlpha:YES isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace - bitmapFormat:NSAlphaNonpremultipliedBitmapFormat + bitmapFormat:NSBitmapFormatAlphaNonpremultiplied bytesPerRow:image->width * 4 bitsPerPixel:32]; @@ -1552,14 +1585,27 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, return GLFW_FALSE; return GLFW_TRUE; + + } // autoreleasepool } int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { - if (!initializeAppKit()) - return GLFW_FALSE; + @autoreleasepool { + + if (shape == GLFW_ARROW_CURSOR) + cursor->ns.object = [NSCursor arrowCursor]; + else if (shape == GLFW_IBEAM_CURSOR) + cursor->ns.object = [NSCursor IBeamCursor]; + else if (shape == GLFW_CROSSHAIR_CURSOR) + cursor->ns.object = [NSCursor crosshairCursor]; + else if (shape == GLFW_HAND_CURSOR) + cursor->ns.object = [NSCursor pointingHandCursor]; + else if (shape == GLFW_HRESIZE_CURSOR) + cursor->ns.object = [NSCursor resizeLeftRightCursor]; + else if (shape == GLFW_VRESIZE_CURSOR) + cursor->ns.object = [NSCursor resizeUpDownCursor]; - cursor->ns.object = getStandardCursor(shape); if (!cursor->ns.object) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -1569,42 +1615,49 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) [cursor->ns.object retain]; return GLFW_TRUE; + + } // autoreleasepool } void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { + @autoreleasepool { if (cursor->ns.object) [(NSCursor*) cursor->ns.object release]; + } // autoreleasepool } void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { - if (cursorInClientArea(window)) + @autoreleasepool { + if (cursorInContentArea(window)) updateCursorImage(window); + } // autoreleasepool } -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +void _glfwPlatformSetClipboardString(const char* string) { - NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil]; - + @autoreleasepool { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard declareTypes:types owner:nil]; - [pasteboard setString:[NSString stringWithUTF8String:string] - forType:NSStringPboardType]; + [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; + [pasteboard setString:@(string) forType:NSPasteboardTypeString]; + } // autoreleasepool } -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +const char* _glfwPlatformGetClipboardString(void) { + @autoreleasepool { + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - if (![[pasteboard types] containsObject:NSStringPboardType]) + if (![[pasteboard types] containsObject:NSPasteboardTypeString]) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "Cocoa: Failed to retrieve string from pasteboard"); return NULL; } - NSString* object = [pasteboard stringForType:NSStringPboardType]; + NSString* object = [pasteboard stringForType:NSPasteboardTypeString]; if (!object) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -1613,22 +1666,27 @@ const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) } free(_glfw.ns.clipboardString); - _glfw.ns.clipboardString = strdup([object UTF8String]); + _glfw.ns.clipboardString = _glfw_strdup([object UTF8String]); return _glfw.ns.clipboardString; + + } // autoreleasepool } -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { - *count = 0; - return NULL; + if (!_glfw.vk.KHR_surface || !_glfw.vk.MVK_macos_surface) + return; + + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_MVK_macos_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { - return GLFW_FALSE; + return GLFW_TRUE; } VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, @@ -1636,7 +1694,65 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { + @autoreleasepool { + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 + VkResult err; + VkMacOSSurfaceCreateInfoMVK sci; + PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK; + + vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK) + vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK"); + if (!vkCreateMacOSSurfaceMVK) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + // HACK: Dynamically load Core Animation to avoid adding an extra + // dependency for the majority who don't use MoltenVK + NSBundle* bundle = [NSBundle bundleWithPath:@"/System/Library/Frameworks/QuartzCore.framework"]; + if (!bundle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to find QuartzCore.framework"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + // NOTE: Create the layer here as makeBackingLayer should not return nil + window->ns.layer = [[bundle classNamed:@"CAMetalLayer"] layer]; + if (!window->ns.layer) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create layer for view"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + if (window->ns.retina) + [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; + + [window->ns.view setLayer:window->ns.layer]; + [window->ns.view setWantsLayer:YES]; + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + sci.pView = window->ns.view; + + err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create Vulkan surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; +#else return VK_ERROR_EXTENSION_NOT_PRESENT; +#endif + + } // autoreleasepool } diff --git a/external/glfw/context.c b/external/glfw/context.c index e633487..3850852 100644 --- a/external/glfw/context.c +++ b/external/glfw/context.c @@ -2,7 +2,7 @@ // GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -38,13 +38,30 @@ ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// +// Checks whether the desired context attributes are valid +// +// This function checks things like whether the specified client API version +// exists and whether all relevant options have supported and non-conflicting +// values +// GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) { + if (ctxconfig->share) + { + if (ctxconfig->client == GLFW_NO_API || + ctxconfig->share->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return GLFW_FALSE; + } + } + if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && - ctxconfig->source != GLFW_EGL_CONTEXT_API) + ctxconfig->source != GLFW_EGL_CONTEXT_API && + ctxconfig->source != GLFW_OSMESA_CONTEXT_API) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid context creation API %i", + "Invalid context creation API 0x%08X", ctxconfig->source); return GLFW_FALSE; } @@ -54,7 +71,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) ctxconfig->client != GLFW_OPENGL_ES_API) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid client API %i", + "Invalid client API 0x%08X", ctxconfig->client); return GLFW_FALSE; } @@ -84,7 +101,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid OpenGL profile %i", + "Invalid OpenGL profile 0x%08X", ctxconfig->profile); return GLFW_FALSE; } @@ -133,7 +150,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid context robustness mode %i", + "Invalid context robustness mode 0x%08X", ctxconfig->robustness); return GLFW_FALSE; } @@ -145,7 +162,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid context release behavior %i", + "Invalid context release behavior 0x%08X", ctxconfig->release); return GLFW_FALSE; } @@ -154,6 +171,8 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) return GLFW_TRUE; } +// Chooses the framebuffer config that best matches the desired one +// const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, const _GLFWfbconfig* alternatives, unsigned int count) @@ -207,6 +226,9 @@ const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, // not important to us here, so we count them as one missing++; } + + if (desired->transparent != current->transparent) + missing++; } // These polynomials make many small channel size differences matter @@ -317,10 +339,13 @@ const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, return closest; } -GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) +// Retrieves the attributes of the current context +// +GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig) { int i; - _GLFWwindow* window; + _GLFWwindow* previous; const char* version; const char* prefixes[] = { @@ -330,11 +355,12 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) NULL }; - window = _glfwPlatformGetCurrentContext(); - window->context.source = ctxconfig->source; window->context.client = GLFW_OPENGL_API; + previous = _glfwPlatformGetTls(&_glfw.contextSlot); + glfwMakeContextCurrent((GLFWwindow*) window); + window->context.GetIntegerv = (PFNGLGETINTEGERVPROC) window->context.getProcAddress("glGetIntegerv"); window->context.GetString = (PFNGLGETSTRINGPROC) @@ -342,6 +368,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) if (!window->context.GetIntegerv || !window->context.GetString) { _glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken"); + glfwMakeContextCurrent((GLFWwindow*) previous); return GLFW_FALSE; } @@ -359,6 +386,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) "OpenGL ES version string retrieval is broken"); } + glfwMakeContextCurrent((GLFWwindow*) previous); return GLFW_FALSE; } @@ -390,6 +418,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) "No version found in OpenGL ES version string"); } + glfwMakeContextCurrent((GLFWwindow*) previous); return GLFW_FALSE; } @@ -419,6 +448,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) window->context.major, window->context.minor); } + glfwMakeContextCurrent((GLFWwindow*) previous); return GLFW_FALSE; } @@ -434,6 +464,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) { _glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken"); + glfwMakeContextCurrent((GLFWwindow*) previous); return GLFW_FALSE; } } @@ -540,9 +571,12 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) window->context.swapBuffers(window); } + glfwMakeContextCurrent((GLFWwindow*) previous); return GLFW_TRUE; } +// Searches an extension string for the specified extension +// GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions) { const char* start = extensions; @@ -577,13 +611,14 @@ GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFWwindow* previous = _glfwPlatformGetCurrentContext(); + _GLFWwindow* previous = _glfwPlatformGetTls(&_glfw.contextSlot); _GLFW_REQUIRE_INIT(); if (window && window->context.client == GLFW_NO_API) { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, + "Cannot make current with a window that has no OpenGL or OpenGL ES context"); return; } @@ -600,7 +635,7 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) GLFWAPI GLFWwindow* glfwGetCurrentContext(void) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return (GLFWwindow*) _glfwPlatformGetCurrentContext(); + return _glfwPlatformGetTls(&_glfw.contextSlot); } GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) @@ -612,7 +647,8 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) if (window->context.client == GLFW_NO_API) { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, + "Cannot swap buffers of a window that has no OpenGL or OpenGL ES context"); return; } @@ -625,10 +661,11 @@ GLFWAPI void glfwSwapInterval(int interval) _GLFW_REQUIRE_INIT(); - window = _glfwPlatformGetCurrentContext(); + window = _glfwPlatformGetTls(&_glfw.contextSlot); if (!window) { - _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); + _glfwInputError(GLFW_NO_CURRENT_CONTEXT, + "Cannot set swap interval without a current OpenGL or OpenGL ES context"); return; } @@ -638,21 +675,21 @@ GLFWAPI void glfwSwapInterval(int interval) GLFWAPI int glfwExtensionSupported(const char* extension) { _GLFWwindow* window; - assert(extension != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - window = _glfwPlatformGetCurrentContext(); + window = _glfwPlatformGetTls(&_glfw.contextSlot); if (!window) { - _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); + _glfwInputError(GLFW_NO_CURRENT_CONTEXT, + "Cannot query extension without a current OpenGL or OpenGL ES context"); return GLFW_FALSE; } if (*extension == '\0') { - _glfwInputError(GLFW_INVALID_VALUE, "Extension name is empty string"); + _glfwInputError(GLFW_INVALID_VALUE, "Extension name cannot be an empty string"); return GLFW_FALSE; } @@ -708,10 +745,11 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - window = _glfwPlatformGetCurrentContext(); + window = _glfwPlatformGetTls(&_glfw.contextSlot); if (!window) { - _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); + _glfwInputError(GLFW_NO_CURRENT_CONTEXT, + "Cannot query entry point without a current OpenGL or OpenGL ES context"); return NULL; } diff --git a/external/glfw/egl_context.c b/external/glfw/egl_context.c index 8915a6e..1952527 100644 --- a/external/glfw/egl_context.c +++ b/external/glfw/egl_context.c @@ -2,7 +2,7 @@ // GLFW 3.3 EGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -113,7 +113,7 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, _GLFWfbconfig* u = usableConfigs + usableCount; // Only consider RGB(A) EGLConfigs - if (!(getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) & EGL_RGB_BUFFER)) + if (getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER) continue; // Only consider window EGLConfigs @@ -121,9 +121,25 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, continue; #if defined(_GLFW_X11) + XVisualInfo vi = {0}; + // Only consider EGLConfigs with associated Visuals - if (!getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID)) + vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); + if (!vi.visualid) continue; + + if (desired->transparent) + { + int count; + XVisualInfo* vis = XGetVisualInfo(_glfw.x11.display, + VisualIDMask, &vi, + &count); + if (vis) + { + u->transparent = _glfwIsVisualTransparentX11(vis[0].visual); + XFree(vis); + } + } #endif // _GLFW_X11 if (ctxconfig->client == GLFW_OPENGL_ES_API) @@ -199,12 +215,12 @@ static void makeContextCurrentEGL(_GLFWwindow* window) } } - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.contextSlot, window); } static void swapBuffersEGL(_GLFWwindow* window) { - if (window != _glfwPlatformGetCurrentContext()) + if (window != _glfwPlatformGetTls(&_glfw.contextSlot)) { _glfwInputError(GLFW_PLATFORM_ERROR, "EGL: The context must be current on the calling thread when swapping buffers"); @@ -233,7 +249,7 @@ static int extensionSupportedEGL(const char* extension) static GLFWglproc getProcAddressEGL(const char* procname) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); if (window->context.egl.client) { @@ -286,11 +302,15 @@ GLFWbool _glfwInitEGL(void) int i; const char* sonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_GLFW_EGL_LIBRARY) + _GLFW_EGL_LIBRARY, +#elif defined(_GLFW_WIN32) "libEGL.dll", "EGL.dll", #elif defined(_GLFW_COCOA) "libEGL.dylib", +#elif defined(__CYGWIN__) + "libEGL-1.so", #else "libEGL.so.1", #endif @@ -315,37 +335,37 @@ GLFWbool _glfwInitEGL(void) _glfw.egl.prefix = (strncmp(sonames[i], "lib", 3) == 0); - _glfw.egl.GetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC) + _glfw.egl.GetConfigAttrib = (PFN_eglGetConfigAttrib) _glfw_dlsym(_glfw.egl.handle, "eglGetConfigAttrib"); - _glfw.egl.GetConfigs = (PFNEGLGETCONFIGSPROC) + _glfw.egl.GetConfigs = (PFN_eglGetConfigs) _glfw_dlsym(_glfw.egl.handle, "eglGetConfigs"); - _glfw.egl.GetDisplay = (PFNEGLGETDISPLAYPROC) + _glfw.egl.GetDisplay = (PFN_eglGetDisplay) _glfw_dlsym(_glfw.egl.handle, "eglGetDisplay"); - _glfw.egl.GetError = (PFNEGLGETERRORPROC) + _glfw.egl.GetError = (PFN_eglGetError) _glfw_dlsym(_glfw.egl.handle, "eglGetError"); - _glfw.egl.Initialize = (PFNEGLINITIALIZEPROC) + _glfw.egl.Initialize = (PFN_eglInitialize) _glfw_dlsym(_glfw.egl.handle, "eglInitialize"); - _glfw.egl.Terminate = (PFNEGLTERMINATEPROC) + _glfw.egl.Terminate = (PFN_eglTerminate) _glfw_dlsym(_glfw.egl.handle, "eglTerminate"); - _glfw.egl.BindAPI = (PFNEGLBINDAPIPROC) + _glfw.egl.BindAPI = (PFN_eglBindAPI) _glfw_dlsym(_glfw.egl.handle, "eglBindAPI"); - _glfw.egl.CreateContext = (PFNEGLCREATECONTEXTPROC) + _glfw.egl.CreateContext = (PFN_eglCreateContext) _glfw_dlsym(_glfw.egl.handle, "eglCreateContext"); - _glfw.egl.DestroySurface = (PFNEGLDESTROYSURFACEPROC) + _glfw.egl.DestroySurface = (PFN_eglDestroySurface) _glfw_dlsym(_glfw.egl.handle, "eglDestroySurface"); - _glfw.egl.DestroyContext = (PFNEGLDESTROYCONTEXTPROC) + _glfw.egl.DestroyContext = (PFN_eglDestroyContext) _glfw_dlsym(_glfw.egl.handle, "eglDestroyContext"); - _glfw.egl.CreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) + _glfw.egl.CreateWindowSurface = (PFN_eglCreateWindowSurface) _glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface"); - _glfw.egl.MakeCurrent = (PFNEGLMAKECURRENTPROC) + _glfw.egl.MakeCurrent = (PFN_eglMakeCurrent) _glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent"); - _glfw.egl.SwapBuffers = (PFNEGLSWAPBUFFERSPROC) + _glfw.egl.SwapBuffers = (PFN_eglSwapBuffers) _glfw_dlsym(_glfw.egl.handle, "eglSwapBuffers"); - _glfw.egl.SwapInterval = (PFNEGLSWAPINTERVALPROC) + _glfw.egl.SwapInterval = (PFN_eglSwapInterval) _glfw_dlsym(_glfw.egl.handle, "eglSwapInterval"); - _glfw.egl.QueryString = (PFNEGLQUERYSTRINGPROC) + _glfw.egl.QueryString = (PFN_eglQueryString) _glfw_dlsym(_glfw.egl.handle, "eglQueryString"); - _glfw.egl.GetProcAddress = (PFNEGLGETPROCADDRESSPROC) + _glfw.egl.GetProcAddress = (PFN_eglGetProcAddress) _glfw_dlsym(_glfw.egl.handle, "eglGetProcAddress"); if (!_glfw.egl.GetConfigAttrib || @@ -399,6 +419,10 @@ GLFWbool _glfwInitEGL(void) extensionSupportedEGL("EGL_KHR_create_context_no_error"); _glfw.egl.KHR_gl_colorspace = extensionSupportedEGL("EGL_KHR_gl_colorspace"); + _glfw.egl.KHR_get_all_proc_addresses = + extensionSupportedEGL("EGL_KHR_get_all_proc_addresses"); + _glfw.egl.KHR_context_flush_control = + extensionSupportedEGL("EGL_KHR_context_flush_control"); return GLFW_TRUE; } @@ -420,11 +444,11 @@ void _glfwTerminateEGL(void) } } -#define setEGLattrib(attribName, attribValue) \ +#define setAttrib(a, v) \ { \ - attribs[index++] = attribName; \ - attribs[index++] = attribValue; \ - assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ + assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ } // Create the OpenGL or OpenGL ES context @@ -436,6 +460,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, EGLint attribs[40]; EGLConfig config; EGLContext share = NULL; + int index = 0; if (!_glfw.egl.display) { @@ -476,7 +501,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (_glfw.egl.KHR_create_context) { - int index = 0, mask = 0, flags = 0; + int mask = 0, flags = 0; if (ctxconfig->client == GLFW_OPENGL_API) { @@ -487,12 +512,6 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; - - if (_glfw.egl.KHR_create_context_no_error) - { - if (ctxconfig->noerror) - flags |= EGL_CONTEXT_OPENGL_NO_ERROR_KHR; - } } if (ctxconfig->debug) @@ -502,44 +521,57 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { - setEGLattrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, - EGL_NO_RESET_NOTIFICATION_KHR); + setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_NO_RESET_NOTIFICATION_KHR); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { - setEGLattrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, - EGL_LOSE_CONTEXT_ON_RESET_KHR); + setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_LOSE_CONTEXT_ON_RESET_KHR); } flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; } + if (ctxconfig->noerror) + { + if (_glfw.egl.KHR_create_context_no_error) + setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE); + } + if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setEGLattrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); - setEGLattrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); + setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); + setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); } if (mask) - setEGLattrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); + setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); if (flags) - setEGLattrib(EGL_CONTEXT_FLAGS_KHR, flags); - - setEGLattrib(EGL_NONE, EGL_NONE); + setAttrib(EGL_CONTEXT_FLAGS_KHR, flags); } else { - int index = 0; - if (ctxconfig->client == GLFW_OPENGL_ES_API) - setEGLattrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); - - setEGLattrib(EGL_NONE, EGL_NONE); + setAttrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); } - // Context release behaviors (GL_KHR_context_flush_control) are not yet - // supported on EGL but are not a hard constraint, so ignore and continue + if (_glfw.egl.KHR_context_flush_control) + { + if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) + { + setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, + EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); + } + else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) + { + setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, + EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); + } + } + + setAttrib(EGL_NONE, EGL_NONE); window->context.egl.handle = eglCreateContext(_glfw.egl.display, config, share, attribs); @@ -559,12 +591,10 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (fbconfig->sRGB) { if (_glfw.egl.KHR_gl_colorspace) - { - setEGLattrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); - } + setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); } - setEGLattrib(EGL_NONE, EGL_NONE); + setAttrib(EGL_NONE, EGL_NONE); } window->context.egl.surface = @@ -583,12 +613,15 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, window->context.egl.config = config; // Load the appropriate client library + if (!_glfw.egl.KHR_get_all_proc_addresses) { int i; const char** sonames; const char* es1sonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_GLFW_GLESV1_LIBRARY) + _GLFW_GLESV1_LIBRARY, +#elif defined(_GLFW_WIN32) "GLESv1_CM.dll", "libGLES_CM.dll", #elif defined(_GLFW_COCOA) @@ -601,11 +634,15 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, }; const char* es2sonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_GLFW_GLESV2_LIBRARY) + _GLFW_GLESV2_LIBRARY, +#elif defined(_GLFW_WIN32) "GLESv2.dll", "libGLESv2.dll", #elif defined(_GLFW_COCOA) "libGLESv2.dylib", +#elif defined(__CYGWIN__) + "libGLESv2-2.so", #else "libGLESv2.so.2", #endif @@ -613,7 +650,9 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, }; const char* glsonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_GLFW_OPENGL_LIBRARY) + _GLFW_OPENGL_LIBRARY, +#elif defined(_GLFW_WIN32) #elif defined(_GLFW_COCOA) #else "libGL.so.1", @@ -661,12 +700,13 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, return GLFW_TRUE; } -#undef setEGLattrib +#undef setAttrib // Returns the Visual and depth of the chosen EGLConfig // #if defined(_GLFW_X11) -GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, +GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth) { diff --git a/external/glfw/egl_context.h b/external/glfw/egl_context.h index 39bd575..7def043 100644 --- a/external/glfw/egl_context.h +++ b/external/glfw/egl_context.h @@ -2,7 +2,7 @@ // GLFW 3.3 EGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,15 +25,16 @@ // //======================================================================== -#ifndef _glfw3_egl_context_h_ -#define _glfw3_egl_context_h_ - #if defined(_GLFW_USE_EGLPLATFORM_H) #include #elif defined(_GLFW_WIN32) #define EGLAPIENTRY __stdcall typedef HDC EGLNativeDisplayType; typedef HWND EGLNativeWindowType; +#elif defined(_GLFW_COCOA) + #define EGLAPIENTRY +typedef void* EGLNativeDisplayType; +typedef id EGLNativeWindowType; #elif defined(_GLFW_X11) #define EGLAPIENTRY typedef Display* EGLNativeDisplayType; @@ -42,10 +43,6 @@ typedef Window EGLNativeWindowType; #define EGLAPIENTRY typedef struct wl_display* EGLNativeDisplayType; typedef struct wl_egl_window* EGLNativeWindowType; -#elif defined(_GLFW_MIR) - #define EGLAPIENTRY -typedef MirEGLNativeDisplayType EGLNativeDisplayType; -typedef MirEGLNativeWindowType EGLNativeWindowType; #else #error "No supported EGL platform selected" #endif @@ -106,6 +103,9 @@ typedef MirEGLNativeWindowType EGLNativeWindowType; #define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 #define EGL_GL_COLORSPACE_KHR 0x309d #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 typedef int EGLint; typedef unsigned int EGLBoolean; @@ -116,22 +116,22 @@ typedef void* EGLDisplay; typedef void* EGLSurface; // EGL function pointer typedefs -typedef EGLBoolean (EGLAPIENTRY * PFNEGLGETCONFIGATTRIBPROC)(EGLDisplay,EGLConfig,EGLint,EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLGETCONFIGSPROC)(EGLDisplay,EGLConfig*,EGLint,EGLint*); -typedef EGLDisplay (EGLAPIENTRY * PFNEGLGETDISPLAYPROC)(EGLNativeDisplayType); -typedef EGLint (EGLAPIENTRY * PFNEGLGETERRORPROC)(void); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLINITIALIZEPROC)(EGLDisplay,EGLint*,EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLTERMINATEPROC)(EGLDisplay); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLBINDAPIPROC)(EGLenum); -typedef EGLContext (EGLAPIENTRY * PFNEGLCREATECONTEXTPROC)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLDESTROYSURFACEPROC)(EGLDisplay,EGLSurface); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLDESTROYCONTEXTPROC)(EGLDisplay,EGLContext); -typedef EGLSurface (EGLAPIENTRY * PFNEGLCREATEWINDOWSURFACEPROC)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLMAKECURRENTPROC)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLSWAPBUFFERSPROC)(EGLDisplay,EGLSurface); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLSWAPINTERVALPROC)(EGLDisplay,EGLint); -typedef const char* (EGLAPIENTRY * PFNEGLQUERYSTRINGPROC)(EGLDisplay,EGLint); -typedef GLFWglproc (EGLAPIENTRY * PFNEGLGETPROCADDRESSPROC)(const char*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*); +typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType); +typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum); +typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext); +typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); +typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); +typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); #define eglGetConfigAttrib _glfw.egl.GetConfigAttrib #define eglGetConfigs _glfw.egl.GetConfigs #define eglGetDisplay _glfw.egl.GetDisplay @@ -176,25 +176,27 @@ typedef struct _GLFWlibraryEGL GLFWbool KHR_create_context; GLFWbool KHR_create_context_no_error; GLFWbool KHR_gl_colorspace; + GLFWbool KHR_get_all_proc_addresses; + GLFWbool KHR_context_flush_control; void* handle; - PFNEGLGETCONFIGATTRIBPROC GetConfigAttrib; - PFNEGLGETCONFIGSPROC GetConfigs; - PFNEGLGETDISPLAYPROC GetDisplay; - PFNEGLGETERRORPROC GetError; - PFNEGLINITIALIZEPROC Initialize; - PFNEGLTERMINATEPROC Terminate; - PFNEGLBINDAPIPROC BindAPI; - PFNEGLCREATECONTEXTPROC CreateContext; - PFNEGLDESTROYSURFACEPROC DestroySurface; - PFNEGLDESTROYCONTEXTPROC DestroyContext; - PFNEGLCREATEWINDOWSURFACEPROC CreateWindowSurface; - PFNEGLMAKECURRENTPROC MakeCurrent; - PFNEGLSWAPBUFFERSPROC SwapBuffers; - PFNEGLSWAPINTERVALPROC SwapInterval; - PFNEGLQUERYSTRINGPROC QueryString; - PFNEGLGETPROCADDRESSPROC GetProcAddress; + PFN_eglGetConfigAttrib GetConfigAttrib; + PFN_eglGetConfigs GetConfigs; + PFN_eglGetDisplay GetDisplay; + PFN_eglGetError GetError; + PFN_eglInitialize Initialize; + PFN_eglTerminate Terminate; + PFN_eglBindAPI BindAPI; + PFN_eglCreateContext CreateContext; + PFN_eglDestroySurface DestroySurface; + PFN_eglDestroyContext DestroyContext; + PFN_eglCreateWindowSurface CreateWindowSurface; + PFN_eglMakeCurrent MakeCurrent; + PFN_eglSwapBuffers SwapBuffers; + PFN_eglSwapInterval SwapInterval; + PFN_eglQueryString QueryString; + PFN_eglGetProcAddress GetProcAddress; } _GLFWlibraryEGL; @@ -205,9 +207,9 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #if defined(_GLFW_X11) -GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, +GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); #endif /*_GLFW_X11*/ -#endif // _glfw3_egl_context_h_ diff --git a/external/glfw/glfw3.h b/external/glfw/glfw3.h index d0c662e..0521d19 100644 --- a/external/glfw/glfw3.h +++ b/external/glfw/glfw3.h @@ -3,7 +3,7 @@ * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard - * Copyright (c) 2006-2016 Camilla Berglund + * Copyright (c) 2006-2019 Camilla Löwy * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -47,32 +47,38 @@ extern "C" { * For more information about how to use this file, see @ref build_include. */ /*! @defgroup context Context reference + * @brief Functions and types related to OpenGL and OpenGL ES contexts. * * This is the reference documentation for OpenGL and OpenGL ES context related * functions. For more task-oriented information, see the @ref context_guide. */ /*! @defgroup vulkan Vulkan reference + * @brief Functions and types related to Vulkan. * * This is the reference documentation for Vulkan related functions and types. * For more task-oriented information, see the @ref vulkan_guide. */ /*! @defgroup init Initialization, version and error reference + * @brief Functions and types related to initialization and error handling. * * This is the reference documentation for initialization and termination of * the library, version management and error handling. For more task-oriented * information, see the @ref intro_guide. */ /*! @defgroup input Input reference + * @brief Functions and types related to input handling. * * This is the reference documentation for input related functions and types. * For more task-oriented information, see the @ref input_guide. */ /*! @defgroup monitor Monitor reference + * @brief Functions and types related to monitors. * * This is the reference documentation for monitor related functions and types. * For more task-oriented information, see the @ref monitor_guide. */ /*! @defgroup window Window reference + * @brief Functions and types related to windows. * * This is the reference documentation for window related functions and types, * including creation, deletion and event polling. For more task-oriented @@ -99,6 +105,7 @@ extern "C" { #else #define APIENTRY #endif + #define GLFW_APIENTRY_DEFINED #endif /* APIENTRY */ /* Some Windows OpenGL headers need this. @@ -116,67 +123,97 @@ extern "C" { #endif /* CALLBACK */ /* Include because most Windows GLU headers need wchar_t and - * the OS X OpenGL header blocks the definition of ptrdiff_t by glext.h. + * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. * Include it unconditionally to avoid surprising side-effects. */ #include /* Include because it is needed by Vulkan and related functions. + * Include it unconditionally to avoid surprising side-effects. */ #include -/* Include the chosen client API headers. +/* Include the chosen OpenGL or OpenGL ES headers. */ -#if defined(__APPLE__) - #if defined(GLFW_INCLUDE_GLCOREARB) +#if defined(GLFW_INCLUDE_ES1) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES2) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES3) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES31) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES32) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_GLCOREARB) + + #if defined(__APPLE__) + #include #if defined(GLFW_INCLUDE_GLEXT) #include - #endif - #elif !defined(GLFW_INCLUDE_NONE) + #endif /*GLFW_INCLUDE_GLEXT*/ + + #else /*__APPLE__*/ + + #include + + #endif /*__APPLE__*/ + +#elif !defined(GLFW_INCLUDE_NONE) + + #if defined(__APPLE__) + #if !defined(GLFW_INCLUDE_GLEXT) #define GL_GLEXT_LEGACY #endif #include - #endif - #if defined(GLFW_INCLUDE_GLU) - #include - #endif -#else - #if defined(GLFW_INCLUDE_GLCOREARB) - #include - #elif defined(GLFW_INCLUDE_ES1) - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include + #if defined(GLFW_INCLUDE_GLU) + #include #endif - #elif defined(GLFW_INCLUDE_ES2) - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - #elif defined(GLFW_INCLUDE_ES3) - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - #elif defined(GLFW_INCLUDE_ES31) - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - #elif defined(GLFW_INCLUDE_VULKAN) - #include - #elif !defined(GLFW_INCLUDE_NONE) + + #else /*__APPLE__*/ + #include #if defined(GLFW_INCLUDE_GLEXT) #include #endif - #endif - #if defined(GLFW_INCLUDE_GLU) - #include - #endif -#endif + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #endif /*__APPLE__*/ + +#endif /* OpenGL and OpenGL ES headers */ + +#if defined(GLFW_INCLUDE_VULKAN) + #include +#endif /* Vulkan header */ #if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) /* GLFW_DLL must be defined by applications that are linking against the DLL @@ -232,23 +269,24 @@ extern "C" { #define GLFW_VERSION_REVISION 0 /*! @} */ -/*! @name Boolean values - * @{ */ /*! @brief One. * - * One. Seriously. You don't _need_ to use this symbol in your code. It's - * just semantic sugar for the number 1. You can use `1` or `true` or `_True` - * or `GL_TRUE` or whatever you want. + * This is only semantic sugar for the number 1. You can instead use `1` or + * `true` or `_True` or `GL_TRUE` or `VK_TRUE` or anything else that is equal + * to one. + * + * @ingroup init */ #define GLFW_TRUE 1 /*! @brief Zero. * - * Zero. Seriously. You don't _need_ to use this symbol in your code. It's - * just just semantic sugar for the number 0. You can use `0` or `false` or - * `_False` or `GL_FALSE` or whatever you want. + * This is only semantic sugar for the number 0. You can instead use `0` or + * `false` or `_False` or `GL_FALSE` or `VK_FALSE` or anything else that is + * equal to zero. + * + * @ingroup init */ #define GLFW_FALSE 0 -/*! @} */ /*! @name Key and button actions * @{ */ @@ -275,7 +313,26 @@ extern "C" { #define GLFW_REPEAT 2 /*! @} */ +/*! @defgroup hat_state Joystick hat states + * @brief Joystick hat states. + * + * See [joystick hat input](@ref joystick_hat) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_HAT_CENTERED 0 +#define GLFW_HAT_UP 1 +#define GLFW_HAT_RIGHT 2 +#define GLFW_HAT_DOWN 4 +#define GLFW_HAT_LEFT 8 +#define GLFW_HAT_RIGHT_UP (GLFW_HAT_RIGHT | GLFW_HAT_UP) +#define GLFW_HAT_RIGHT_DOWN (GLFW_HAT_RIGHT | GLFW_HAT_DOWN) +#define GLFW_HAT_LEFT_UP (GLFW_HAT_LEFT | GLFW_HAT_UP) +#define GLFW_HAT_LEFT_DOWN (GLFW_HAT_LEFT | GLFW_HAT_DOWN) +/*! @} */ + /*! @defgroup keys Keyboard keys + * @brief Keyboard key IDs. * * See [key input](@ref input_key) for how these are used. * @@ -430,6 +487,7 @@ extern "C" { /*! @} */ /*! @defgroup mods Modifier key flags + * @brief Modifier key flags. * * See [key input](@ref input_key) for how these are used. * @@ -437,21 +495,42 @@ extern "C" { * @{ */ /*! @brief If this bit is set one or more Shift keys were held down. + * + * If this bit is set one or more Shift keys were held down. */ #define GLFW_MOD_SHIFT 0x0001 /*! @brief If this bit is set one or more Control keys were held down. + * + * If this bit is set one or more Control keys were held down. */ #define GLFW_MOD_CONTROL 0x0002 /*! @brief If this bit is set one or more Alt keys were held down. + * + * If this bit is set one or more Alt keys were held down. */ #define GLFW_MOD_ALT 0x0004 /*! @brief If this bit is set one or more Super keys were held down. + * + * If this bit is set one or more Super keys were held down. */ #define GLFW_MOD_SUPER 0x0008 +/*! @brief If this bit is set the Caps Lock key is enabled. + * + * If this bit is set the Caps Lock key is enabled and the @ref + * GLFW_LOCK_KEY_MODS input mode is set. + */ +#define GLFW_MOD_CAPS_LOCK 0x0010 +/*! @brief If this bit is set the Num Lock key is enabled. + * + * If this bit is set the Num Lock key is enabled and the @ref + * GLFW_LOCK_KEY_MODS input mode is set. + */ +#define GLFW_MOD_NUM_LOCK 0x0020 /*! @} */ /*! @defgroup buttons Mouse buttons + * @brief Mouse button IDs. * * See [mouse button input](@ref input_mouse_button) for how these are used. * @@ -472,6 +551,7 @@ extern "C" { /*! @} */ /*! @defgroup joysticks Joysticks + * @brief Joystick IDs. * * See [joystick input](@ref joystick) for how these are used. * @@ -496,12 +576,66 @@ extern "C" { #define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16 /*! @} */ +/*! @defgroup gamepad_buttons Gamepad buttons + * @brief Gamepad buttons. + * + * See @ref gamepad for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_GAMEPAD_BUTTON_A 0 +#define GLFW_GAMEPAD_BUTTON_B 1 +#define GLFW_GAMEPAD_BUTTON_X 2 +#define GLFW_GAMEPAD_BUTTON_Y 3 +#define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER 4 +#define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER 5 +#define GLFW_GAMEPAD_BUTTON_BACK 6 +#define GLFW_GAMEPAD_BUTTON_START 7 +#define GLFW_GAMEPAD_BUTTON_GUIDE 8 +#define GLFW_GAMEPAD_BUTTON_LEFT_THUMB 9 +#define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB 10 +#define GLFW_GAMEPAD_BUTTON_DPAD_UP 11 +#define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT 12 +#define GLFW_GAMEPAD_BUTTON_DPAD_DOWN 13 +#define GLFW_GAMEPAD_BUTTON_DPAD_LEFT 14 +#define GLFW_GAMEPAD_BUTTON_LAST GLFW_GAMEPAD_BUTTON_DPAD_LEFT + +#define GLFW_GAMEPAD_BUTTON_CROSS GLFW_GAMEPAD_BUTTON_A +#define GLFW_GAMEPAD_BUTTON_CIRCLE GLFW_GAMEPAD_BUTTON_B +#define GLFW_GAMEPAD_BUTTON_SQUARE GLFW_GAMEPAD_BUTTON_X +#define GLFW_GAMEPAD_BUTTON_TRIANGLE GLFW_GAMEPAD_BUTTON_Y +/*! @} */ + +/*! @defgroup gamepad_axes Gamepad axes + * @brief Gamepad axes. + * + * See @ref gamepad for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_GAMEPAD_AXIS_LEFT_X 0 +#define GLFW_GAMEPAD_AXIS_LEFT_Y 1 +#define GLFW_GAMEPAD_AXIS_RIGHT_X 2 +#define GLFW_GAMEPAD_AXIS_RIGHT_Y 3 +#define GLFW_GAMEPAD_AXIS_LEFT_TRIGGER 4 +#define GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER 5 +#define GLFW_GAMEPAD_AXIS_LAST GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER +/*! @} */ + /*! @defgroup errors Error codes + * @brief Error codes. * * See [error handling](@ref error_handling) for how these are used. * * @ingroup init * @{ */ +/*! @brief No error has occurred. + * + * No error has occurred. + * + * @analysis Yay. + */ +#define GLFW_NO_ERROR 0 /*! @brief GLFW has not been initialized. * * This occurs if a GLFW function was called that must not be called unless the @@ -524,8 +658,7 @@ extern "C" { /*! @brief One of the arguments to the function was an invalid enum value. * * One of the arguments to the function was an invalid enum value, for example - * requesting [GLFW_RED_BITS](@ref window_hints_fb) with @ref - * glfwGetWindowAttrib. + * requesting @ref GLFW_RED_BITS with @ref glfwGetWindowAttrib. * * @analysis Application programmer error. Fix the offending call. */ @@ -560,7 +693,7 @@ extern "C" { * @par * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only * supports OpenGL ES via EGL, while Nvidia and Intel only support it via - * a WGL or GLX extension. OS X does not provide OpenGL ES at all. The Mesa + * a WGL or GLX extension. macOS does not provide OpenGL ES at all. The Mesa * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary * driver. Older graphics drivers do not support Vulkan. */ @@ -622,43 +755,251 @@ extern "C" { #define GLFW_NO_WINDOW_CONTEXT 0x0001000A /*! @} */ +/*! @addtogroup window + * @{ */ +/*! @brief Input focus window hint and attribute + * + * Input focus [window hint](@ref GLFW_FOCUSED_hint) or + * [window attribute](@ref GLFW_FOCUSED_attrib). + */ #define GLFW_FOCUSED 0x00020001 +/*! @brief Window iconification window attribute + * + * Window iconification [window attribute](@ref GLFW_ICONIFIED_attrib). + */ #define GLFW_ICONIFIED 0x00020002 +/*! @brief Window resize-ability window hint and attribute + * + * Window resize-ability [window hint](@ref GLFW_RESIZABLE_hint) and + * [window attribute](@ref GLFW_RESIZABLE_attrib). + */ #define GLFW_RESIZABLE 0x00020003 +/*! @brief Window visibility window hint and attribute + * + * Window visibility [window hint](@ref GLFW_VISIBLE_hint) and + * [window attribute](@ref GLFW_VISIBLE_attrib). + */ #define GLFW_VISIBLE 0x00020004 +/*! @brief Window decoration window hint and attribute + * + * Window decoration [window hint](@ref GLFW_DECORATED_hint) and + * [window attribute](@ref GLFW_DECORATED_attrib). + */ #define GLFW_DECORATED 0x00020005 +/*! @brief Window auto-iconification window hint and attribute + * + * Window auto-iconification [window hint](@ref GLFW_AUTO_ICONIFY_hint) and + * [window attribute](@ref GLFW_AUTO_ICONIFY_attrib). + */ #define GLFW_AUTO_ICONIFY 0x00020006 +/*! @brief Window decoration window hint and attribute + * + * Window decoration [window hint](@ref GLFW_FLOATING_hint) and + * [window attribute](@ref GLFW_FLOATING_attrib). + */ #define GLFW_FLOATING 0x00020007 +/*! @brief Window maximization window hint and attribute + * + * Window maximization [window hint](@ref GLFW_MAXIMIZED_hint) and + * [window attribute](@ref GLFW_MAXIMIZED_attrib). + */ #define GLFW_MAXIMIZED 0x00020008 +/*! @brief Cursor centering window hint + * + * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint). + */ +#define GLFW_CENTER_CURSOR 0x00020009 +/*! @brief Window framebuffer transparency hint and attribute + * + * Window framebuffer transparency + * [window hint](@ref GLFW_TRANSPARENT_FRAMEBUFFER_hint) and + * [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib). + */ +#define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A +/*! @brief Mouse cursor hover window attribute. + * + * Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib). + */ +#define GLFW_HOVERED 0x0002000B +/*! @brief Input focus on calling show window hint and attribute + * + * Input focus [window hint](@ref GLFW_FOCUS_ON_SHOW_hint) or + * [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib). + */ +#define GLFW_FOCUS_ON_SHOW 0x0002000C +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_RED_BITS). + */ #define GLFW_RED_BITS 0x00021001 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_GREEN_BITS). + */ #define GLFW_GREEN_BITS 0x00021002 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_BLUE_BITS). + */ #define GLFW_BLUE_BITS 0x00021003 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ALPHA_BITS). + */ #define GLFW_ALPHA_BITS 0x00021004 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_DEPTH_BITS). + */ #define GLFW_DEPTH_BITS 0x00021005 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_STENCIL_BITS). + */ #define GLFW_STENCIL_BITS 0x00021006 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_RED_BITS). + */ #define GLFW_ACCUM_RED_BITS 0x00021007 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_GREEN_BITS). + */ #define GLFW_ACCUM_GREEN_BITS 0x00021008 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_BLUE_BITS). + */ #define GLFW_ACCUM_BLUE_BITS 0x00021009 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_ALPHA_BITS). + */ #define GLFW_ACCUM_ALPHA_BITS 0x0002100A +/*! @brief Framebuffer auxiliary buffer hint. + * + * Framebuffer auxiliary buffer [hint](@ref GLFW_AUX_BUFFERS). + */ #define GLFW_AUX_BUFFERS 0x0002100B +/*! @brief OpenGL stereoscopic rendering hint. + * + * OpenGL stereoscopic rendering [hint](@ref GLFW_STEREO). + */ #define GLFW_STEREO 0x0002100C +/*! @brief Framebuffer MSAA samples hint. + * + * Framebuffer MSAA samples [hint](@ref GLFW_SAMPLES). + */ #define GLFW_SAMPLES 0x0002100D +/*! @brief Framebuffer sRGB hint. + * + * Framebuffer sRGB [hint](@ref GLFW_SRGB_CAPABLE). + */ #define GLFW_SRGB_CAPABLE 0x0002100E +/*! @brief Monitor refresh rate hint. + * + * Monitor refresh rate [hint](@ref GLFW_REFRESH_RATE). + */ #define GLFW_REFRESH_RATE 0x0002100F +/*! @brief Framebuffer double buffering hint. + * + * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER). + */ #define GLFW_DOUBLEBUFFER 0x00021010 +/*! @brief Context client API hint and attribute. + * + * Context client API [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CLIENT_API 0x00022001 +/*! @brief Context client API major version hint and attribute. + * + * Context client API major version [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 +/*! @brief Context client API minor version hint and attribute. + * + * Context client API minor version [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_VERSION_MINOR 0x00022003 +/*! @brief Context client API revision number hint and attribute. + * + * Context client API revision number [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_REVISION 0x00022004 +/*! @brief Context robustness hint and attribute. + * + * Context client API revision number [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_ROBUSTNESS 0x00022005 +/*! @brief OpenGL forward-compatibility hint and attribute. + * + * OpenGL forward-compatibility [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 +/*! @brief OpenGL debug context hint and attribute. + * + * OpenGL debug context [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 +/*! @brief OpenGL profile hint and attribute. + * + * OpenGL profile [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_OPENGL_PROFILE 0x00022008 +/*! @brief Context flush-on-release hint and attribute. + * + * Context flush-on-release [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 +/*! @brief Context error suppression hint and attribute. + * + * Context error suppression [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_NO_ERROR 0x0002200A +/*! @brief Context creation API hint and attribute. + * + * Context creation API [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_CREATION_API 0x0002200B +/*! @brief Window content area scaling window + * [window hint](@ref GLFW_SCALE_TO_MONITOR). + */ +#define GLFW_SCALE_TO_MONITOR 0x0002200C +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint). + */ +#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_FRAME_NAME_hint). + */ +#define GLFW_COCOA_FRAME_NAME 0x00023002 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint). + */ +#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003 +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ +#define GLFW_X11_CLASS_NAME 0x00024001 +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ +#define GLFW_X11_INSTANCE_NAME 0x00024002 +/*! @} */ #define GLFW_NO_API 0 #define GLFW_OPENGL_API 0x00030001 @@ -675,6 +1016,8 @@ extern "C" { #define GLFW_CURSOR 0x00033001 #define GLFW_STICKY_KEYS 0x00033002 #define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 +#define GLFW_LOCK_KEY_MODS 0x00033004 +#define GLFW_RAW_MOUSE_MOTION 0x00033005 #define GLFW_CURSOR_NORMAL 0x00034001 #define GLFW_CURSOR_HIDDEN 0x00034002 @@ -686,8 +1029,10 @@ extern "C" { #define GLFW_NATIVE_CONTEXT_API 0x00036001 #define GLFW_EGL_CONTEXT_API 0x00036002 +#define GLFW_OSMESA_CONTEXT_API 0x00036003 /*! @defgroup shapes Standard cursor shapes + * @brief Standard system cursor shapes. * * See [standard cursor creation](@ref cursor_standard) for how these are used. * @@ -729,6 +1074,25 @@ extern "C" { #define GLFW_CONNECTED 0x00040001 #define GLFW_DISCONNECTED 0x00040002 +/*! @addtogroup init + * @{ */ +/*! @brief Joystick hat buttons init hint. + * + * Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS). + */ +#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint). + */ +#define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint). + */ +#define GLFW_COCOA_MENUBAR 0x00051002 +/*! @} */ + #define GLFW_DONT_CARE -1 @@ -742,10 +1106,10 @@ extern "C" { * without forcing a cast from a regular pointer. * * @sa @ref context_glext - * @sa glfwGetProcAddress + * @sa @ref glfwGetProcAddress * * @since Added in version 3.0. - + * * @ingroup context */ typedef void (*GLFWglproc)(void); @@ -756,7 +1120,7 @@ typedef void (*GLFWglproc)(void); * without forcing a cast from a regular pointer. * * @sa @ref vulkan_proc - * @sa glfwGetInstanceProcAddress + * @sa @ref glfwGetInstanceProcAddress * * @since Added in version 3.2. * @@ -796,7 +1160,7 @@ typedef struct GLFWwindow GLFWwindow; * * @since Added in version 3.1. * - * @ingroup cursor + * @ingroup input */ typedef struct GLFWcursor GLFWcursor; @@ -808,7 +1172,7 @@ typedef struct GLFWcursor GLFWcursor; * @param[in] description A UTF-8 encoded string describing the error. * * @sa @ref error_handling - * @sa glfwSetErrorCallback + * @sa @ref glfwSetErrorCallback * * @since Added in version 3.0. * @@ -822,12 +1186,12 @@ typedef void (* GLFWerrorfun)(int,const char*); * * @param[in] window The window that was moved. * @param[in] xpos The new x-coordinate, in screen coordinates, of the - * upper-left corner of the client area of the window. + * upper-left corner of the content area of the window. * @param[in] ypos The new y-coordinate, in screen coordinates, of the - * upper-left corner of the client area of the window. + * upper-left corner of the content area of the window. * * @sa @ref window_pos - * @sa glfwSetWindowPosCallback + * @sa @ref glfwSetWindowPosCallback * * @since Added in version 3.0. * @@ -844,7 +1208,7 @@ typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); * @param[in] height The new height, in screen coordinates, of the window. * * @sa @ref window_size - * @sa glfwSetWindowSizeCallback + * @sa @ref glfwSetWindowSizeCallback * * @since Added in version 1.0. * @glfw3 Added window handle parameter. @@ -860,7 +1224,7 @@ typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); * @param[in] window The window that the user attempted to close. * * @sa @ref window_close - * @sa glfwSetWindowCloseCallback + * @sa @ref glfwSetWindowCloseCallback * * @since Added in version 2.5. * @glfw3 Added window handle parameter. @@ -876,7 +1240,7 @@ typedef void (* GLFWwindowclosefun)(GLFWwindow*); * @param[in] window The window whose content needs to be refreshed. * * @sa @ref window_refresh - * @sa glfwSetWindowRefreshCallback + * @sa @ref glfwSetWindowRefreshCallback * * @since Added in version 2.5. * @glfw3 Added window handle parameter. @@ -894,7 +1258,7 @@ typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); * `GLFW_FALSE` if it lost it. * * @sa @ref window_focus - * @sa glfwSetWindowFocusCallback + * @sa @ref glfwSetWindowFocusCallback * * @since Added in version 3.0. * @@ -912,7 +1276,7 @@ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); * `GLFW_FALSE` if it was restored. * * @sa @ref window_iconify - * @sa glfwSetWindowIconifyCallback + * @sa @ref glfwSetWindowIconifyCallback * * @since Added in version 3.0. * @@ -920,6 +1284,24 @@ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); */ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); +/*! @brief The function signature for window maximize/restore callbacks. + * + * This is the function signature for window maximize/restore callback + * functions. + * + * @param[in] window The window that was maximized or restored. + * @param[in] iconified `GLFW_TRUE` if the window was maximized, or + * `GLFW_FALSE` if it was restored. + * + * @sa @ref window_maximize + * @sa glfwSetWindowMaximizeCallback + * + * @since Added in version 3.3. + * + * @ingroup window + */ +typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); + /*! @brief The function signature for framebuffer resize callbacks. * * This is the function signature for framebuffer resize callback @@ -930,7 +1312,7 @@ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); * @param[in] height The new height, in pixels, of the framebuffer. * * @sa @ref window_fbsize - * @sa glfwSetFramebufferSizeCallback + * @sa @ref glfwSetFramebufferSizeCallback * * @since Added in version 3.0. * @@ -938,6 +1320,24 @@ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); */ typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); +/*! @brief The function signature for window content scale callbacks. + * + * This is the function signature for window content scale callback + * functions. + * + * @param[in] window The window whose content scale changed. + * @param[in] xscale The new x-axis content scale of the window. + * @param[in] yscale The new y-axis content scale of the window. + * + * @sa @ref window_scale + * @sa @ref glfwSetWindowContentScaleCallback + * + * @since Added in version 3.3. + * + * @ingroup window + */ +typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float); + /*! @brief The function signature for mouse button callbacks. * * This is the function signature for mouse button callback functions. @@ -950,7 +1350,7 @@ typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); * held down. * * @sa @ref input_mouse_button - * @sa glfwSetMouseButtonCallback + * @sa @ref glfwSetMouseButtonCallback * * @since Added in version 1.0. * @glfw3 Added window handle and modifier mask parameters. @@ -965,12 +1365,12 @@ typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); * * @param[in] window The window that received the event. * @param[in] xpos The new cursor x-coordinate, relative to the left edge of - * the client area. + * the content area. * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the - * client area. + * content area. * * @sa @ref cursor_pos - * @sa glfwSetCursorPosCallback + * @sa @ref glfwSetCursorPosCallback * * @since Added in version 3.0. Replaces `GLFWmouseposfun`. * @@ -983,11 +1383,11 @@ typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); * This is the function signature for cursor enter/leave callback functions. * * @param[in] window The window that received the event. - * @param[in] entered `GLFW_TRUE` if the cursor entered the window's client + * @param[in] entered `GLFW_TRUE` if the cursor entered the window's content * area, or `GLFW_FALSE` if it left it. * * @sa @ref cursor_enter - * @sa glfwSetCursorEnterCallback + * @sa @ref glfwSetCursorEnterCallback * * @since Added in version 3.0. * @@ -1004,7 +1404,7 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); * @param[in] yoffset The scroll offset along the y-axis. * * @sa @ref scrolling - * @sa glfwSetScrollCallback + * @sa @ref glfwSetScrollCallback * * @since Added in version 3.0. Replaces `GLFWmousewheelfun`. * @@ -1024,7 +1424,7 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); * held down. * * @sa @ref input_key - * @sa glfwSetKeyCallback + * @sa @ref glfwSetKeyCallback * * @since Added in version 1.0. * @glfw3 Added window handle, scancode and modifier mask parameters. @@ -1041,7 +1441,7 @@ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); * @param[in] codepoint The Unicode code point of the character. * * @sa @ref input_char - * @sa glfwSetCharCallback + * @sa @ref glfwSetCharCallback * * @since Added in version 2.4. * @glfw3 Added window handle parameter. @@ -1063,7 +1463,9 @@ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); * held down. * * @sa @ref input_char - * @sa glfwSetCharModsCallback + * @sa @ref glfwSetCharModsCallback + * + * @deprecated Scheduled for removal in version 4.0. * * @since Added in version 3.1. * @@ -1080,7 +1482,7 @@ typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); * @param[in] paths The UTF-8 encoded file and/or directory path names. * * @sa @ref path_drop - * @sa glfwSetDropCallback + * @sa @ref glfwSetDropCallback * * @since Added in version 3.1. * @@ -1093,10 +1495,11 @@ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); * This is the function signature for monitor configuration callback functions. * * @param[in] monitor The monitor that was connected or disconnected. - * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining + * values reserved for future use. * * @sa @ref monitor_event - * @sa glfwSetMonitorCallback + * @sa @ref glfwSetMonitorCallback * * @since Added in version 3.0. * @@ -1109,11 +1512,12 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); * This is the function signature for joystick configuration callback * functions. * - * @param[in] joy The joystick that was connected or disconnected. - * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. + * @param[in] jid The joystick that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining + * values reserved for future use. * * @sa @ref joystick_event - * @sa glfwSetJoystickCallback + * @sa @ref glfwSetJoystickCallback * * @since Added in version 3.2. * @@ -1126,7 +1530,8 @@ typedef void (* GLFWjoystickfun)(int,int); * This describes a single video mode. * * @sa @ref monitor_modes - * @sa glfwGetVideoMode glfwGetVideoModes + * @sa @ref glfwGetVideoMode + * @sa @ref glfwGetVideoModes * * @since Added in version 1.0. * @glfw3 Added refresh rate member. @@ -1160,7 +1565,8 @@ typedef struct GLFWvidmode * This describes the gamma ramp for a monitor. * * @sa @ref monitor_gamma - * @sa glfwGetGammaRamp glfwSetGammaRamp + * @sa @ref glfwGetGammaRamp + * @sa @ref glfwSetGammaRamp * * @since Added in version 3.0. * @@ -1183,12 +1589,17 @@ typedef struct GLFWgammaramp } GLFWgammaramp; /*! @brief Image data. + * + * This describes a single 2D image. See the documentation for each related + * function what the expected pixel format is. * * @sa @ref cursor_custom * @sa @ref window_icon * * @since Added in version 2.1. * @glfw3 Removed format and bytes-per-pixel members. + * + * @ingroup window */ typedef struct GLFWimage { @@ -1203,6 +1614,29 @@ typedef struct GLFWimage unsigned char* pixels; } GLFWimage; +/*! @brief Gamepad input state + * + * This describes the input state of a gamepad. + * + * @sa @ref gamepad + * @sa @ref glfwGetGamepadState + * + * @since Added in version 3.3. + * + * @ingroup input + */ +typedef struct GLFWgamepadstate +{ + /*! The states of each [gamepad button](@ref gamepad_buttons), `GLFW_PRESS` + * or `GLFW_RELEASE`. + */ + unsigned char buttons[15]; + /*! The states of each [gamepad axis](@ref gamepad_axes), in the range -1.0 + * to 1.0 inclusive. + */ + float axes[6]; +} GLFWgamepadstate; + /************************************************************************* * GLFW API functions @@ -1226,15 +1660,15 @@ typedef struct GLFWimage * * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. * - * @remark @osx This function will change the current directory of the + * @remark @macos This function will change the current directory of the * application to the `Contents/Resources` subdirectory of the application's - * bundle, if present. This can be disabled with a - * [compile-time option](@ref compile_options_osx). + * bundle, if present. This can be disabled with the @ref + * GLFW_COCOA_CHDIR_RESOURCES init hint. * * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init - * @sa glfwTerminate + * @sa @ref glfwTerminate * * @since Added in version 1.0. * @@ -1266,7 +1700,7 @@ GLFWAPI int glfwInit(void); * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init - * @sa glfwInit + * @sa @ref glfwInit * * @since Added in version 1.0. * @@ -1274,6 +1708,38 @@ GLFWAPI int glfwInit(void); */ GLFWAPI void glfwTerminate(void); +/*! @brief Sets the specified init hint to the desired value. + * + * This function sets hints for the next initialization of GLFW. + * + * The values you set hints to are never reset by GLFW, but they only take + * effect during initialization. Once GLFW has been initialized, any values + * you set will be ignored until the library is terminated and initialized + * again. + * + * Some hints are platform specific. These may be set on any platform but they + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. + * + * @param[in] hint The [init hint](@ref init_hints) to set. + * @param[in] value The new value of the init hint. + * + * @errors Possible errors include @ref GLFW_INVALID_ENUM and @ref + * GLFW_INVALID_VALUE. + * + * @remarks This function may be called before @ref glfwInit. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa init_hints + * @sa glfwInit + * + * @since Added in version 3.3. + * + * @ingroup init + */ +GLFWAPI void glfwInitHint(int hint, int value); + /*! @brief Retrieves the version of the GLFW library. * * This function retrieves the major, minor and revision numbers of the GLFW @@ -1293,7 +1759,7 @@ GLFWAPI void glfwTerminate(void); * @thread_safety This function may be called from any thread. * * @sa @ref intro_version - * @sa glfwGetVersionString + * @sa @ref glfwGetVersionString * * @since Added in version 1.0. * @@ -1324,7 +1790,7 @@ GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); * @thread_safety This function may be called from any thread. * * @sa @ref intro_version - * @sa glfwGetVersion + * @sa @ref glfwGetVersion * * @since Added in version 3.0. * @@ -1332,11 +1798,46 @@ GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); */ GLFWAPI const char* glfwGetVersionString(void); +/*! @brief Returns and clears the last error for the calling thread. + * + * This function returns and clears the [error code](@ref errors) of the last + * error that occurred on the calling thread, and optionally a UTF-8 encoded + * human-readable description of it. If no error has occurred since the last + * call, it returns @ref GLFW_NO_ERROR (zero) and the description pointer is + * set to `NULL`. + * + * @param[in] description Where to store the error description pointer, or `NULL`. + * @return The last error code for the calling thread, or @ref GLFW_NO_ERROR + * (zero). + * + * @errors None. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * next error occurs or the library is terminated. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref error_handling + * @sa @ref glfwSetErrorCallback + * + * @since Added in version 3.3. + * + * @ingroup init + */ +GLFWAPI int glfwGetError(const char** description); + /*! @brief Sets the error callback. * * This function sets the error callback, which is called with an error code * and a human-readable description each time a GLFW error occurs. * + * The error code is set before the callback is called. Calling @ref + * glfwGetError from the error callback will return the same value as the error + * code argument. + * * The error callback is called on the thread where the error occurred. If you * are using GLFW from multiple threads, your error callback needs to be * written accordingly. @@ -1359,6 +1860,7 @@ GLFWAPI const char* glfwGetVersionString(void); * @thread_safety This function must only be called from the main thread. * * @sa @ref error_handling + * @sa @ref glfwGetError * * @since Added in version 3.0. * @@ -1387,7 +1889,7 @@ GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun); * * @sa @ref monitor_monitors * @sa @ref monitor_event - * @sa glfwGetPrimaryMonitor + * @sa @ref glfwGetPrimaryMonitor * * @since Added in version 3.0. * @@ -1411,7 +1913,7 @@ GLFWAPI GLFWmonitor** glfwGetMonitors(int* count); * glfwGetMonitors. * * @sa @ref monitor_monitors - * @sa glfwGetMonitors + * @sa @ref glfwGetMonitors * * @since Added in version 3.0. * @@ -1444,6 +1946,37 @@ GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); */ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); +/*! @brief Retrives the work area of the monitor. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the work area of the specified monitor along with the work area + * size in screen coordinates. The work area is defined as the area of the + * monitor not occluded by the operating system task bar where present. If no + * task bar exists then the work area is the monitor resolution in screen + * coordinates. + * + * Any or all of the position and size arguments may be `NULL`. If an error + * occurs, all non-`NULL` position and size arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * @param[out] width Where to store the monitor width, or `NULL`. + * @param[out] height Where to store the monitor height, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_workarea + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); + /*! @brief Returns the physical size of the monitor. * * This function returns the size, in millimetres, of the display area of the @@ -1478,6 +2011,38 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); */ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM); +/*! @brief Retrieves the content scale for the specified monitor. + * + * This function retrieves the content scale for the specified monitor. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. + * + * The content scale may depend on both the monitor resolution and pixel + * density and on user settings. It may be very different from the raw DPI + * calculated from the physical size and current resolution. + * + * @param[in] monitor The monitor to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_scale + * @sa @ref glfwGetWindowContentScale + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, float* yscale); + /*! @brief Returns the name of the specified monitor. * * This function returns a human-readable name, encoded as UTF-8, of the @@ -1504,6 +2069,56 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* */ GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor); +/*! @brief Sets the user pointer of the specified monitor. + * + * This function sets the user-defined pointer of the specified monitor. The + * current value is retained until the monitor is disconnected. The initial + * value is `NULL`. + * + * This function may be called from the monitor callback, even for a monitor + * that is being disconnected. + * + * @param[in] monitor The monitor whose pointer to set. + * @param[in] pointer The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref monitor_userptr + * @sa @ref glfwGetMonitorUserPointer + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* monitor, void* pointer); + +/*! @brief Returns the user pointer of the specified monitor. + * + * This function returns the current value of the user-defined pointer of the + * specified monitor. The initial value is `NULL`. + * + * This function may be called from the monitor callback, even for a monitor + * that is being disconnected. + * + * @param[in] monitor The monitor whose pointer to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref monitor_userptr + * @sa @ref glfwSetMonitorUserPointer + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor); + /*! @brief Sets the monitor configuration callback. * * This function sets the monitor configuration callback, or removes the @@ -1551,7 +2166,7 @@ GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun); * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_modes - * @sa glfwGetVideoMode + * @sa @ref glfwGetVideoMode * * @since Added in version 1.0. * @glfw3 Changed to return an array of modes for a specific monitor. @@ -1580,7 +2195,7 @@ GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count); * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_modes - * @sa glfwGetVideoModes + * @sa @ref glfwGetVideoModes * * @since Added in version 3.0. Replaces `glfwGetDesktopMode`. * @@ -1590,9 +2205,17 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); /*! @brief Generates a gamma ramp and sets it for the specified monitor. * - * This function generates a 256-element gamma ramp from the specified exponent - * and then calls @ref glfwSetGammaRamp with it. The value must be a finite - * number greater than zero. + * This function generates an appropriately sized gamma ramp from the specified + * exponent and then calls @ref glfwSetGammaRamp with it. The value must be + * a finite number greater than zero. + * + * The software controlled gamma ramp is applied _in addition_ to the hardware + * gamma correction, which today is usually an approximation of sRGB gamma. + * This means that setting a perfectly linear ramp, or gamma 1.0, will produce + * the default (usually sRGB-like) behavior. + * + * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref + * GLFW_SRGB_CAPABLE hint. * * @param[in] monitor The monitor whose gamma ramp to set. * @param[in] gamma The desired exponent. @@ -1600,6 +2223,9 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * + * @remark @wayland Gamma handling is a priviledged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_gamma @@ -1621,6 +2247,10 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland Gamma handling is a priviledged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while + * returning `NULL`. + * * @pointer_lifetime The returned structure and its arrays are allocated and * freed by GLFW. You should not free them yourself. They are valid until the * specified monitor is disconnected, this function is called again for that @@ -1642,17 +2272,28 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); * original gamma ramp for that monitor is saved by GLFW the first time this * function is called and is restored by @ref glfwTerminate. * + * The software controlled gamma ramp is applied _in addition_ to the hardware + * gamma correction, which today is usually an approximation of sRGB gamma. + * This means that setting a perfectly linear ramp, or gamma 1.0, will produce + * the default (usually sRGB-like) behavior. + * + * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref + * GLFW_SRGB_CAPABLE hint. + * * @param[in] monitor The monitor whose gamma ramp to set. * @param[in] ramp The gamma ramp to use. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark Gamma ramp sizes other than 256 are not supported by all platforms - * or graphics hardware. + * @remark The size of the specified gamma ramp should match the size of the + * current ramp for that monitor. * * @remark @win32 The gamma ramp size must be 256. * + * @remark @wayland Gamma handling is a priviledged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. + * * @pointer_lifetime The specified gamma ramp is copied before this function * returns. * @@ -1676,7 +2317,8 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hints - * @sa glfwWindowHint + * @sa @ref glfwWindowHint + * @sa @ref glfwWindowHintString * * @since Added in version 3.0. * @@ -1687,14 +2329,20 @@ GLFWAPI void glfwDefaultWindowHints(void); /*! @brief Sets the specified window hint to the desired value. * * This function sets hints for the next call to @ref glfwCreateWindow. The - * hints, once set, retain their values until changed by a call to @ref - * glfwWindowHint or @ref glfwDefaultWindowHints, or until the library is - * terminated. + * hints, once set, retain their values until changed by a call to this + * function or @ref glfwDefaultWindowHints, or until the library is terminated. + * + * Only integer value hints can be set with this function. String value hints + * are set with @ref glfwWindowHintString. * * This function does not check whether the specified hint values are valid. * If you set hints to invalid values this will instead be reported by the next * call to @ref glfwCreateWindow. * + * Some hints are platform specific. These may be set on any platform but they + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. + * * @param[in] hint The [window hint](@ref window_hints) to set. * @param[in] value The new value of the window hint. * @@ -1704,7 +2352,8 @@ GLFWAPI void glfwDefaultWindowHints(void); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hints - * @sa glfwDefaultWindowHints + * @sa @ref glfwWindowHintString + * @sa @ref glfwDefaultWindowHints * * @since Added in version 3.0. Replaces `glfwOpenWindowHint`. * @@ -1712,6 +2361,44 @@ GLFWAPI void glfwDefaultWindowHints(void); */ GLFWAPI void glfwWindowHint(int hint, int value); +/*! @brief Sets the specified window hint to the desired value. + * + * This function sets hints for the next call to @ref glfwCreateWindow. The + * hints, once set, retain their values until changed by a call to this + * function or @ref glfwDefaultWindowHints, or until the library is terminated. + * + * Only string type hints can be set with this function. Integer value hints + * are set with @ref glfwWindowHint. + * + * This function does not check whether the specified hint values are valid. + * If you set hints to invalid values this will instead be reported by the next + * call to @ref glfwCreateWindow. + * + * Some hints are platform specific. These may be set on any platform but they + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. + * + * @param[in] hint The [window hint](@ref window_hints) to set. + * @param[in] value The new value of the window hint. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @pointer_lifetime The specified string is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hints + * @sa @ref glfwWindowHint + * @sa @ref glfwDefaultWindowHints + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwWindowHintString(int hint, const char* value); + /*! @brief Creates a window and its associated context. * * This function creates a window and its associated OpenGL or OpenGL ES @@ -1744,12 +2431,12 @@ GLFWAPI void glfwWindowHint(int hint, int value); * or _borderless full screen_ windows, see @ref window_windowed_full_screen. * * Once you have created the window, you can switch it between windowed and - * full screen mode with @ref glfwSetWindowMonitor. If the window has an - * OpenGL or OpenGL ES context, it will be unaffected. + * full screen mode with @ref glfwSetWindowMonitor. This will not affect its + * OpenGL or OpenGL ES context. * * By default, newly created windows use the placement recommended by the * window system. To create the window at a specific position, make it - * initially invisible using the [GLFW_VISIBLE](@ref window_hints_wnd) window + * initially invisible using the [GLFW_VISIBLE](@ref GLFW_VISIBLE_hint) window * hint, set its [position](@ref window_pos) and then [show](@ref window_hide) * it. * @@ -1785,33 +2472,46 @@ GLFWAPI void glfwWindowHint(int hint, int value); * * @remark @win32 If the executable has an icon resource named `GLFW_ICON,` it * will be set as the initial icon for the window. If no such icon is present, - * the `IDI_WINLOGO` icon will be used instead. To set a different icon, see - * @ref glfwSetWindowIcon. + * the `IDI_APPLICATION` icon will be used instead. To set a different icon, + * see @ref glfwSetWindowIcon. * * @remark @win32 The context to share resources with must not be current on * any other thread. * - * @remark @osx The GLFW window has no icon, as it is not a document + * @remark @macos The OS only supports forward-compatible core profile contexts + * for OpenGL versions 3.2 and later. Before creating an OpenGL context of + * version 3.2 or later you must set the + * [GLFW_OPENGL_FORWARD_COMPAT](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) and + * [GLFW_OPENGL_PROFILE](@ref GLFW_OPENGL_PROFILE_hint) hints accordingly. + * OpenGL 3.0 and 3.1 contexts are not supported at all on macOS. + * + * @remark @macos The GLFW window has no icon, as it is not a document * window, but the dock icon will be the same as the application bundle's icon. * For more information on bundles, see the * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) * in the Mac Developer Library. * - * @remark @osx The first time a window is created the menu bar is populated - * with common commands like Hide, Quit and About. The About entry opens - * a minimal about dialog with information from the application's bundle. The - * menu bar can be disabled with a - * [compile-time option](@ref compile_options_osx). + * @remark @macos The first time a window is created the menu bar is created. + * If GLFW finds a `MainMenu.nib` it is loaded and assumed to contain a menu + * bar. Otherwise a minimal menu bar is created manually with common commands + * like Hide, Quit and About. The About entry opens a minimal about dialog + * with information from the application's bundle. Menu bar creation can be + * disabled entirely with the @ref GLFW_COCOA_MENUBAR init hint. * - * @remark @osx On OS X 10.10 and later the window frame will not be rendered - * at full resolution on Retina displays unless the `NSHighResolutionCapable` - * key is enabled in the application bundle's `Info.plist`. For more - * information, see + * @remark @macos On OS X 10.10 and later the window frame will not be rendered + * at full resolution on Retina displays unless the + * [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint) + * hint is `GLFW_TRUE` and the `NSHighResolutionCapable` key is enabled in the + * application bundle's `Info.plist`. For more information, see * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html) * in the Mac Developer Library. The GLFW test and example programs use * a custom `Info.plist` template for this, which can be found as * `CMake/MacOSXBundleInfo.plist.in` in the source tree. * + * @remark @macos When activating frame autosaving with + * [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified + * window size and position may be overriden by previously saved values. + * * @remark @x11 Some window managers will not respect the placement of * initially hidden windows. * @@ -1820,12 +2520,32 @@ GLFWAPI void glfwWindowHint(int hint, int value); * query the final size, position or other attributes directly after window * creation. * - * @reentrancy This function must not be called from a callback. + * @remark @x11 The class part of the `WM_CLASS` window property will by + * default be set to the window title passed to this function. The instance + * part will use the contents of the `RESOURCE_NAME` environment variable, if + * present and not empty, or fall back to the window title. Set the + * [GLFW_X11_CLASS_NAME](@ref GLFW_X11_CLASS_NAME_hint) and + * [GLFW_X11_INSTANCE_NAME](@ref GLFW_X11_INSTANCE_NAME_hint) window hints to + * override this. + * + * @remark @wayland Compositors should implement the xdg-decoration protocol + * for GLFW to decorate the window properly. If this protocol isn't + * supported, or if the compositor prefers client-side decorations, a very + * simple fallback frame will be drawn using the wp_viewporter protocol. A + * compositor can still emit close, maximize or fullscreen events, using for + * instance a keybind mechanism. If neither of these protocols is supported, + * the window won't be decorated. + * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size or refresh rate. + * + * @remark @wayland Screensaver inhibition requires the idle-inhibit protocol + * to be implemented in the user's compositor. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_creation - * @sa glfwDestroyWindow + * @sa @ref glfwDestroyWindow * * @since Added in version 3.0. Replaces `glfwOpenWindow`. * @@ -1854,7 +2574,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, G * @thread_safety This function must only be called from the main thread. * * @sa @ref window_creation - * @sa glfwCreateWindow + * @sa @ref glfwCreateWindow * * @since Added in version 3.0. Replaces `glfwCloseWindow`. * @@ -1915,7 +2635,7 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @osx The window title will not be updated until the next time you + * @remark @macos The window title will not be updated until the next time you * process events. * * @thread_safety This function must only be called from the main thread. @@ -1936,6 +2656,10 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * selected. If no images are specified, the window reverts to its default * icon. * + * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight + * bits per channel with the red channel first. They are arranged canonically + * as packed sequential rows, starting from the top-left corner. + * * The desired image sizes varies depending on platform and system settings. * The selected images will be rescaled as needed. Good sizes include 16x16, * 32x32 and 48x48. @@ -1952,12 +2676,16 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * @pointer_lifetime The specified image data is copied before this function * returns. * - * @remark @osx The GLFW window has no icon, as it is not a document + * @remark @macos The GLFW window has no icon, as it is not a document * window, so this function does nothing. The dock icon will be the same as * the application bundle's icon. For more information on bundles, see the * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) * in the Mac Developer Library. * + * @remark @wayland There is no existing protocol to change an icon, the + * window will thus inherit the one defined in the application's desktop file. + * This function always emits @ref GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_icon @@ -1968,27 +2696,31 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); */ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images); -/*! @brief Retrieves the position of the client area of the specified window. +/*! @brief Retrieves the position of the content area of the specified window. * * This function retrieves the position, in screen coordinates, of the - * upper-left corner of the client area of the specified window. + * upper-left corner of the content area of the specified window. * * Any or all of the position arguments may be `NULL`. If an error occurs, all * non-`NULL` position arguments will be set to zero. * * @param[in] window The window to query. * @param[out] xpos Where to store the x-coordinate of the upper-left corner of - * the client area, or `NULL`. + * the content area, or `NULL`. * @param[out] ypos Where to store the y-coordinate of the upper-left corner of - * the client area, or `NULL`. + * the content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland There is no way for an application to retrieve the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos - * @sa glfwSetWindowPos + * @sa @ref glfwSetWindowPos * * @since Added in version 3.0. * @@ -1996,10 +2728,10 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* i */ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); -/*! @brief Sets the position of the client area of the specified window. +/*! @brief Sets the position of the content area of the specified window. * * This function sets the position, in screen coordinates, of the upper-left - * corner of the client area of the specified windowed mode window. If the + * corner of the content area of the specified windowed mode window. If the * window is a full screen window, this function does nothing. * * __Do not use this function__ to move an already visible window unless you @@ -2009,16 +2741,20 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); * cannot and should not override these limits. * * @param[in] window The window to query. - * @param[in] xpos The x-coordinate of the upper-left corner of the client area. - * @param[in] ypos The y-coordinate of the upper-left corner of the client area. + * @param[in] xpos The x-coordinate of the upper-left corner of the content area. + * @param[in] ypos The y-coordinate of the upper-left corner of the content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland There is no way for an application to set the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos - * @sa glfwGetWindowPos + * @sa @ref glfwGetWindowPos * * @since Added in version 1.0. * @glfw3 Added window handle parameter. @@ -2027,9 +2763,9 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); */ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); -/*! @brief Retrieves the size of the client area of the specified window. +/*! @brief Retrieves the size of the content area of the specified window. * - * This function retrieves the size, in screen coordinates, of the client area + * This function retrieves the size, in screen coordinates, of the content area * of the specified window. If you wish to retrieve the size of the * framebuffer of the window in pixels, see @ref glfwGetFramebufferSize. * @@ -2038,9 +2774,9 @@ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); * * @param[in] window The window whose size to retrieve. * @param[out] width Where to store the width, in screen coordinates, of the - * client area, or `NULL`. + * content area, or `NULL`. * @param[out] height Where to store the height, in screen coordinates, of the - * client area, or `NULL`. + * content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2048,7 +2784,7 @@ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size - * @sa glfwSetWindowSize + * @sa @ref glfwSetWindowSize * * @since Added in version 1.0. * @glfw3 Added window handle parameter. @@ -2059,7 +2795,7 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); /*! @brief Sets the size limits of the specified window. * - * This function sets the size limits of the client area of the specified + * This function sets the size limits of the content area of the specified * window. If the window is full screen, the size limits only take effect * once it is made windowed. If the window is not resizable, this function * does nothing. @@ -2071,14 +2807,14 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); * dimensions and all must be greater than or equal to zero. * * @param[in] window The window to set limits for. - * @param[in] minwidth The minimum width, in screen coordinates, of the client + * @param[in] minwidth The minimum width, in screen coordinates, of the content * area, or `GLFW_DONT_CARE`. * @param[in] minheight The minimum height, in screen coordinates, of the - * client area, or `GLFW_DONT_CARE`. - * @param[in] maxwidth The maximum width, in screen coordinates, of the client + * content area, or `GLFW_DONT_CARE`. + * @param[in] maxwidth The maximum width, in screen coordinates, of the content * area, or `GLFW_DONT_CARE`. * @param[in] maxheight The maximum height, in screen coordinates, of the - * client area, or `GLFW_DONT_CARE`. + * content area, or `GLFW_DONT_CARE`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. @@ -2086,10 +2822,13 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); * @remark If you set size limits and an aspect ratio that conflict, the * results are undefined. * + * @remark @wayland The size limits will not be applied until the window is + * actually resized, either by the user or by the compositor. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_sizelimits - * @sa glfwSetWindowAspectRatio + * @sa @ref glfwSetWindowAspectRatio * * @since Added in version 3.2. * @@ -2099,7 +2838,7 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe /*! @brief Sets the aspect ratio of the specified window. * - * This function sets the required aspect ratio of the client area of the + * This function sets the required aspect ratio of the content area of the * specified window. If the window is full screen, the aspect ratio only takes * effect once it is made windowed. If the window is not resizable, this * function does nothing. @@ -2126,10 +2865,13 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe * @remark If you set size limits and an aspect ratio that conflict, the * results are undefined. * + * @remark @wayland The aspect ratio will not be applied until the window is + * actually resized, either by the user or by the compositor. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_sizelimits - * @sa glfwSetWindowSizeLimits + * @sa @ref glfwSetWindowSizeLimits * * @since Added in version 3.2. * @@ -2137,9 +2879,9 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe */ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); -/*! @brief Sets the size of the client area of the specified window. +/*! @brief Sets the size of the content area of the specified window. * - * This function sets the size, in screen coordinates, of the client area of + * This function sets the size, in screen coordinates, of the content area of * the specified window. * * For full screen windows, this function updates the resolution of its desired @@ -2155,18 +2897,21 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); * * @param[in] window The window to resize. * @param[in] width The desired width, in screen coordinates, of the window - * client area. + * content area. * @param[in] height The desired height, in screen coordinates, of the window - * client area. + * content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size - * @sa glfwGetWindowSize - * @sa glfwSetWindowMonitor + * @sa @ref glfwGetWindowSize + * @sa @ref glfwSetWindowMonitor * * @since Added in version 1.0. * @glfw3 Added window handle parameter. @@ -2196,7 +2941,7 @@ GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_fbsize - * @sa glfwSetFramebufferSizeCallback + * @sa @ref glfwSetFramebufferSizeCallback * * @since Added in version 3.0. * @@ -2241,6 +2986,95 @@ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height) */ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom); +/*! @brief Retrieves the content scale for the specified window. + * + * This function retrieves the content scale for the specified window. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. + * + * On systems where each monitors can have its own content scale, the window + * content scale will depend on which monitor the system considers the window + * to be on. + * + * @param[in] window The window to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_scale + * @sa @ref glfwSetWindowContentScaleCallback + * @sa @ref glfwGetMonitorContentScale + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowContentScale(GLFWwindow* window, float* xscale, float* yscale); + +/*! @brief Returns the opacity of the whole window. + * + * This function returns the opacity of the window, including any decorations. + * + * The opacity (or alpha) value is a positive finite number between zero and + * one, where zero is fully transparent and one is fully opaque. If the system + * does not support whole window transparency, this function always returns one. + * + * The initial opacity value for newly created windows is one. + * + * @param[in] window The window to query. + * @return The opacity value of the specified window. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_transparency + * @sa @ref glfwSetWindowOpacity + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI float glfwGetWindowOpacity(GLFWwindow* window); + +/*! @brief Sets the opacity of the whole window. + * + * This function sets the opacity of the window, including any decorations. + * + * The opacity (or alpha) value is a positive finite number between zero and + * one, where zero is fully transparent and one is fully opaque. + * + * The initial opacity value for newly created windows is one. + * + * A window created with framebuffer transparency may not use whole window + * transparency. The results of doing this are undefined. + * + * @param[in] window The window to set the opacity for. + * @param[in] opacity The desired opacity of the specified window. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_transparency + * @sa @ref glfwGetWindowOpacity + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity); + /*! @brief Iconifies the specified window. * * This function iconifies (minimizes) the specified window if it was @@ -2255,11 +3089,15 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland There is no concept of iconification in wl_shell, this + * function will emit @ref GLFW_PLATFORM_ERROR when using this deprecated + * protocol. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify - * @sa glfwRestoreWindow - * @sa glfwMaximizeWindow + * @sa @ref glfwRestoreWindow + * @sa @ref glfwMaximizeWindow * * @since Added in version 2.1. * @glfw3 Added window handle parameter. @@ -2285,8 +3123,8 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify - * @sa glfwIconifyWindow - * @sa glfwMaximizeWindow + * @sa @ref glfwIconifyWindow + * @sa @ref glfwMaximizeWindow * * @since Added in version 2.1. * @glfw3 Added window handle parameter. @@ -2311,8 +3149,8 @@ GLFWAPI void glfwRestoreWindow(GLFWwindow* window); * This function may only be called from the main thread. * * @sa @ref window_iconify - * @sa glfwIconifyWindow - * @sa glfwRestoreWindow + * @sa @ref glfwIconifyWindow + * @sa @ref glfwRestoreWindow * * @since Added in GLFW 3.2. * @@ -2326,6 +3164,11 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); * hidden. If the window is already visible or is in full screen mode, this * function does nothing. * + * By default, windowed mode windows are focused when shown + * Set the [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) window hint + * to change this behavior for all newly created windows, or change the + * behavior for an existing window with @ref glfwSetWindowAttrib. + * * @param[in] window The window to make visible. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref @@ -2334,7 +3177,7 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hide - * @sa glfwHideWindow + * @sa @ref glfwHideWindow * * @since Added in version 3.0. * @@ -2356,7 +3199,7 @@ GLFWAPI void glfwShowWindow(GLFWwindow* window); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hide - * @sa glfwShowWindow + * @sa @ref glfwShowWindow * * @since Added in version 3.0. * @@ -2370,21 +3213,32 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window); * The window should already be visible and not iconified. * * By default, both windowed and full screen mode windows are focused when - * initially created. Set the [GLFW_FOCUSED](@ref window_hints_wnd) to disable - * this behavior. + * initially created. Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to + * disable this behavior. + * + * Also by default, windowed mode windows are focused when shown + * with @ref glfwShowWindow. Set the + * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) to disable this behavior. * * __Do not use this function__ to steal focus from other applications unless * you are certain that is what the user wants. Focus stealing can be * extremely disruptive. * + * For a less disruptive way of getting the user's attention, see + * [attention requests](@ref window_attention). + * * @param[in] window The window to give input focus. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland It is not possible for an application to bring its windows + * to front, this function will always emit @ref GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_focus + * @sa @ref window_attention * * @since Added in version 3.2. * @@ -2392,6 +3246,33 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window); */ GLFWAPI void glfwFocusWindow(GLFWwindow* window); +/*! @brief Requests user attention to the specified window. + * + * This function requests user attention to the specified window. On + * platforms where this is not supported, attention is requested to the + * application as a whole. + * + * Once the user has given attention, usually by focusing the window or + * application, the system will end the request automatically. + * + * @param[in] window The window to request attention to. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @macos Attention is requested to the application as a whole, not the + * specific window. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attention + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window); + /*! @brief Returns the monitor that the window uses for full screen mode. * * This function returns the handle of the monitor that the specified window is @@ -2406,7 +3287,7 @@ GLFWAPI void glfwFocusWindow(GLFWwindow* window); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_monitor - * @sa glfwSetWindowMonitor + * @sa @ref glfwSetWindowMonitor * * @since Added in version 3.0. * @@ -2424,7 +3305,7 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * The window position is ignored when setting a monitor. * * When the monitor is `NULL`, the position, width and height are used to - * place the window client area. The refresh rate is ignored when no monitor + * place the window content area. The refresh rate is ignored when no monitor * is specified. * * If you only wish to update the resolution of a full screen window or the @@ -2432,17 +3313,17 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * * When a window transitions from full screen to windowed mode, this function * restores any previous window settings such as whether it is decorated, - * floating, resizable, has size or aspect ratio limits, etc.. + * floating, resizable, has size or aspect ratio limits, etc. * * @param[in] window The window whose monitor, size or video mode to set. * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. * @param[in] xpos The desired x-coordinate of the upper-left corner of the - * client area. + * content area. * @param[in] ypos The desired y-coordinate of the upper-left corner of the - * client area. - * @param[in] width The desired with, in screen coordinates, of the client area - * or video mode. - * @param[in] height The desired height, in screen coordinates, of the client + * content area. + * @param[in] width The desired with, in screen coordinates, of the content + * area or video mode. + * @param[in] height The desired height, in screen coordinates, of the content * area or video mode. * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode, * or `GLFW_DONT_CARE`. @@ -2450,12 +3331,22 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark The OpenGL or OpenGL ES context will not be destroyed or otherwise + * affected by any resizing or mode switching, although you may need to update + * your viewport if the framebuffer size has changed. + * + * @remark @wayland The desired window position is ignored, as there is no way + * for an application to set this property. + * + * @remark @wayland Setting the window to full screen will not attempt to + * change the mode, no matter what the requested size or refresh rate. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_monitor * @sa @ref window_full_screen - * @sa glfwGetWindowMonitor - * @sa glfwSetWindowSize + * @sa @ref glfwGetWindowMonitor + * @sa @ref glfwSetWindowSize * * @since Added in version 3.2. * @@ -2488,6 +3379,7 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int * @thread_safety This function must only be called from the main thread. * * @sa @ref window_attribs + * @sa @ref glfwSetWindowAttrib * * @since Added in version 3.0. Replaces `glfwGetWindowParam` and * `glfwGetGLVersion`. @@ -2496,6 +3388,43 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int */ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); +/*! @brief Sets an attribute of the specified window. + * + * This function sets the value of an attribute of the specified window. + * + * The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), + * [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib), + * [GLFW_FLOATING](@ref GLFW_FLOATING_attrib), + * [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and + * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib). + * + * Some of these attributes are ignored for full screen windows. The new + * value will take effect if the window is later made windowed. + * + * Some of these attributes are ignored for windowed mode windows. The new + * value will take effect if the window is later made full screen. + * + * @param[in] window The window to set the attribute for. + * @param[in] attrib A supported window attribute. + * @param[in] value `GLFW_TRUE` or `GLFW_FALSE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark Calling @ref glfwGetWindowAttrib will always return the latest + * value, even if that value is ignored by the current mode of the window. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attribs + * @sa @ref glfwGetWindowAttrib + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowAttrib(GLFWwindow* window, int attrib, int value); + /*! @brief Sets the user pointer of the specified window. * * This function sets the user-defined pointer of the specified window. The @@ -2511,7 +3440,7 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); * synchronized. * * @sa @ref window_userptr - * @sa glfwGetWindowUserPointer + * @sa @ref glfwGetWindowUserPointer * * @since Added in version 3.0. * @@ -2532,7 +3461,7 @@ GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer); * synchronized. * * @sa @ref window_userptr - * @sa glfwSetWindowUserPointer + * @sa @ref glfwSetWindowUserPointer * * @since Added in version 3.0. * @@ -2543,8 +3472,9 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); /*! @brief Sets the position callback for the specified window. * * This function sets the position callback of the specified window, which is - * called when the window is moved. The callback is provided with the screen - * position of the upper-left corner of the client area of the window. + * called when the window is moved. The callback is provided with the + * position, in screen coordinates, of the upper-left corner of the content + * area of the window. * * @param[in] window The window whose callback to set. * @param[in] cbfun The new callback, or `NULL` to remove the currently set @@ -2554,6 +3484,9 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * + * @remark @wayland This callback will never be called, as there is no way for + * an application to know its global position. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos @@ -2568,7 +3501,7 @@ GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindow * * This function sets the size callback of the specified window, which is * called when the window is resized. The callback is provided with the size, - * in screen coordinates, of the client area of the window. + * in screen coordinates, of the content area of the window. * * @param[in] window The window whose callback to set. * @param[in] cbfun The new callback, or `NULL` to remove the currently set @@ -2608,8 +3541,8 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @remark @osx Selecting Quit from the application menu will trigger the close - * callback for all windows. + * @remark @macos Selecting Quit from the application menu will trigger the + * close callback for all windows. * * @thread_safety This function must only be called from the main thread. * @@ -2625,12 +3558,12 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwi /*! @brief Sets the refresh callback for the specified window. * * This function sets the refresh callback of the specified window, which is - * called when the client area of the window needs to be redrawn, for example + * called when the content area of the window needs to be redrawn, for example * if the window has been exposed after having been covered by another window. * - * On compositing window systems such as Aero, Compiz or Aqua, where the window - * contents are saved off-screen, this callback may be called only very - * infrequently or never at all. + * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where + * the window contents are saved off-screen, this callback may be called only + * very infrequently or never at all. * * @param[in] window The window whose callback to set. * @param[in] cbfun The new callback, or `NULL` to remove the currently set @@ -2692,6 +3625,9 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * + * @remark @wayland The wl_shell protocol has no concept of iconification, + * this callback will never be called when using this deprecated protocol. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify @@ -2702,6 +3638,29 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi */ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun cbfun); +/*! @brief Sets the maximize callback for the specified window. + * + * This function sets the maximization callback of the specified window, which + * is called when the window is maximized or restored. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_maximize + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun cbfun); + /*! @brief Sets the framebuffer resize callback for the specified window. * * This function sets the framebuffer resize callback of the specified window, @@ -2725,6 +3684,30 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GL */ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun cbfun); +/*! @brief Sets the window content scale callback for the specified window. + * + * This function sets the window content scale callback of the specified window, + * which is called when the content scale of the specified window changes. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_scale + * @sa @ref glfwGetWindowContentScale + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun cbfun); + /*! @brief Processes all pending events. * * This function processes only those events that are already in the event @@ -2737,9 +3720,12 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * [window refresh callback](@ref window_refresh) to redraw the contents of * your window when necessary during such operations. * - * On some platforms, certain events are sent directly to the application - * without going through the event queue, causing callbacks to be called - * outside of a call to one of the event processing functions. + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. * * Event processing is not required for joystick input to work. * @@ -2751,8 +3737,8 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * @thread_safety This function must only be called from the main thread. * * @sa @ref events - * @sa glfwWaitEvents - * @sa glfwWaitEventsTimeout + * @sa @ref glfwWaitEvents + * @sa @ref glfwWaitEventsTimeout * * @since Added in version 1.0. * @@ -2779,12 +3765,12 @@ GLFWAPI void glfwPollEvents(void); * [window refresh callback](@ref window_refresh) to redraw the contents of * your window when necessary during such operations. * - * On some platforms, certain callbacks may be called outside of a call to one - * of the event processing functions. - * - * If no windows exist, this function returns immediately. For synchronization - * of threads in applications that do not create windows, use your threading - * library of choice. + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. * * Event processing is not required for joystick input to work. * @@ -2796,8 +3782,8 @@ GLFWAPI void glfwPollEvents(void); * @thread_safety This function must only be called from the main thread. * * @sa @ref events - * @sa glfwPollEvents - * @sa glfwWaitEventsTimeout + * @sa @ref glfwPollEvents + * @sa @ref glfwWaitEventsTimeout * * @since Added in version 2.5. * @@ -2826,24 +3812,27 @@ GLFWAPI void glfwWaitEvents(void); * [window refresh callback](@ref window_refresh) to redraw the contents of * your window when necessary during such operations. * - * On some platforms, certain callbacks may be called outside of a call to one - * of the event processing functions. - * - * If no windows exist, this function returns immediately. For synchronization - * of threads in applications that do not create windows, use your threading - * library of choice. + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. * * Event processing is not required for joystick input to work. * * @param[in] timeout The maximum amount of time, in seconds, to wait. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * * @reentrancy This function must not be called from a callback. * * @thread_safety This function must only be called from the main thread. * * @sa @ref events - * @sa glfwPollEvents - * @sa glfwWaitEvents + * @sa @ref glfwPollEvents + * @sa @ref glfwWaitEvents * * @since Added in version 3.2. * @@ -2856,18 +3845,14 @@ GLFWAPI void glfwWaitEventsTimeout(double timeout); * This function posts an empty event from the current thread to the event * queue, causing @ref glfwWaitEvents or @ref glfwWaitEventsTimeout to return. * - * If no windows exist, this function returns immediately. For synchronization - * of threads in applications that do not create windows, use your threading - * library of choice. - * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function may be called from any thread. * * @sa @ref events - * @sa glfwWaitEvents - * @sa glfwWaitEventsTimeout + * @sa @ref glfwWaitEvents + * @sa @ref glfwWaitEventsTimeout * * @since Added in version 3.1. * @@ -2878,19 +3863,21 @@ GLFWAPI void glfwPostEmptyEvent(void); /*! @brief Returns the value of an input option for the specified window. * * This function returns the value of an input option for the specified window. - * The mode must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or - * `GLFW_STICKY_MOUSE_BUTTONS`. + * The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. * * @param[in] window The window to query. - * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or - * `GLFW_STICKY_MOUSE_BUTTONS`. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. * * @thread_safety This function must only be called from the main thread. * - * @sa glfwSetInputMode + * @sa @ref glfwSetInputMode * * @since Added in version 3.0. * @@ -2901,14 +3888,15 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); /*! @brief Sets an input option for the specified window. * * This function sets an input mode option for the specified window. The mode - * must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or - * `GLFW_STICKY_MOUSE_BUTTONS`. + * must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. * * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor * modes: * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally. - * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the client - * area of the window but does not restrict the cursor from leaving. + * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the + * content area of the window but does not restrict the cursor from leaving. * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual * and unlimited cursor movement. This is useful for implementing for * example 3D camera controls. @@ -2928,9 +3916,22 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * you are only interested in whether mouse buttons have been pressed but not * when or in which order. * + * If the mode is `GLFW_LOCK_KEY_MODS`, the value must be either `GLFW_TRUE` to + * enable lock key modifier bits, or `GLFW_FALSE` to disable them. If enabled, + * callbacks that receive modifier bits will also have the @ref + * GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on, + * and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on. + * + * If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `GLFW_TRUE` + * to enable raw (unscaled and unaccelerated) mouse motion when the cursor is + * disabled, or `GLFW_FALSE` to disable it. If raw motion is not supported, + * attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref + * glfwRawMouseMotionSupported to check for support. + * * @param[in] window The window whose input mode to set. - * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or - * `GLFW_STICKY_MOUSE_BUTTONS`. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. * @param[in] value The new value of the specified input mode. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref @@ -2938,7 +3939,7 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * * @thread_safety This function must only be called from the main thread. * - * @sa glfwGetInputMode + * @sa @ref glfwGetInputMode * * @since Added in version 3.0. Replaces `glfwEnable` and `glfwDisable`. * @@ -2946,17 +3947,51 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); */ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); -/*! @brief Returns the localized name of the specified printable key. +/*! @brief Returns whether raw mouse motion is supported. * - * This function returns the localized name of the specified printable key. - * This is intended for displaying key bindings to the user. + * This function returns whether raw mouse motion is supported on the current + * system. This status does not change after GLFW has been initialized so you + * only need to check this once. If you attempt to enable raw motion on + * a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted. * - * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used instead, otherwise - * the scancode is ignored. If a non-printable key or (if the key is - * `GLFW_KEY_UNKNOWN`) a scancode that maps to a non-printable key is - * specified, this function returns `NULL`. + * Raw mouse motion is closer to the actual motion of the mouse across + * a surface. It is not affected by the scaling and acceleration applied to + * the motion of the desktop cursor. That processing is suitable for a cursor + * while raw motion is better for controlling for example a 3D camera. Because + * of this, raw mouse motion is only provided when the cursor is disabled. * - * This behavior allows you to pass in the arguments passed to the + * @return `GLFW_TRUE` if raw mouse motion is supported on the current machine, + * or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref raw_mouse_motion + * @sa @ref glfwSetInputMode + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwRawMouseMotionSupported(void); + +/*! @brief Returns the layout-specific name of the specified printable key. + * + * This function returns the name of the specified printable key, encoded as + * UTF-8. This is typically the character that key would produce without any + * modifier keys, intended for displaying key bindings to the user. For dead + * keys, it is typically the diacritic it would add to a character. + * + * __Do not use this function__ for [text input](@ref input_char). You will + * break text input for many languages even if it happens to work for yours. + * + * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used to identify the key, + * otherwise the scancode is ignored. If you specify a non-printable key, or + * `GLFW_KEY_UNKNOWN` and a scancode that maps to a non-printable key, this + * function returns `NULL` but does not emit an error. + * + * This behavior allows you to always pass in the arguments in the * [key callback](@ref input_key) without modification. * * The printable keys are: @@ -2982,9 +4017,13 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); * - `GLFW_KEY_KP_ADD` * - `GLFW_KEY_KP_EQUAL` * + * Names for printable keys depend on keyboard layout, while names for + * non-printable keys are the same across layouts but depend on the application + * language and should be localized along with other user interface text. + * * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`. * @param[in] scancode The scancode of the key to query. - * @return The localized name of the key, or `NULL`. + * @return The UTF-8 encoded, layout-specific name of the key, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -3003,6 +4042,30 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); */ GLFWAPI const char* glfwGetKeyName(int key, int scancode); +/*! @brief Returns the platform-specific scancode of the specified key. + * + * This function returns the platform-specific scancode of the specified key. + * + * If the key is `GLFW_KEY_UNKNOWN` or does not exist on the keyboard this + * method will return `-1`. + * + * @param[in] key Any [named key](@ref keys). + * @return The platform-specific scancode for the key, or `-1` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref input_key + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwGetKeyScancode(int key); + /*! @brief Returns the last reported state of a keyboard key for the specified * window. * @@ -3011,7 +4074,7 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode); * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to * the key callback. * - * If the `GLFW_STICKY_KEYS` input mode is enabled, this function returns + * If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns * `GLFW_PRESS` the first time you call it for a key that was pressed, even if * that key has already been released. * @@ -3050,9 +4113,9 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); * to the specified window. The returned state is one of `GLFW_PRESS` or * `GLFW_RELEASE`. * - * If the `GLFW_STICKY_MOUSE_BUTTONS` input mode is enabled, this function - * `GLFW_PRESS` the first time you call it for a mouse button that was pressed, - * even if that mouse button has already been released. + * If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function + * returns `GLFW_PRESS` the first time you call it for a mouse button that was + * pressed, even if that mouse button has already been released. * * @param[in] window The desired window. * @param[in] button The desired [mouse button](@ref buttons). @@ -3072,11 +4135,11 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); */ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); -/*! @brief Retrieves the position of the cursor relative to the client area of +/*! @brief Retrieves the position of the cursor relative to the content area of * the window. * * This function returns the position of the cursor, in screen coordinates, - * relative to the upper-left corner of the client area of the specified + * relative to the upper-left corner of the content area of the specified * window. * * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor @@ -3092,9 +4155,9 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); * * @param[in] window The desired window. * @param[out] xpos Where to store the cursor x-coordinate, relative to the - * left edge of the client area, or `NULL`. + * left edge of the content area, or `NULL`. * @param[out] ypos Where to store the cursor y-coordinate, relative to the to - * top edge of the client area, or `NULL`. + * top edge of the content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -3102,7 +4165,7 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_pos - * @sa glfwSetCursorPos + * @sa @ref glfwSetCursorPos * * @since Added in version 3.0. Replaces `glfwGetMousePos`. * @@ -3110,11 +4173,11 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); */ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); -/*! @brief Sets the position of the cursor, relative to the client area of the +/*! @brief Sets the position of the cursor, relative to the content area of the * window. * * This function sets the position, in screen coordinates, of the cursor - * relative to the upper-left corner of the client area of the specified + * relative to the upper-left corner of the content area of the specified * window. The window must have input focus. If the window does not have * input focus when this function is called, it fails silently. * @@ -3129,17 +4192,20 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); * * @param[in] window The desired window. * @param[in] xpos The desired x-coordinate, relative to the left edge of the - * client area. + * content area. * @param[in] ypos The desired y-coordinate, relative to the top edge of the - * client area. + * content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland This function will only work when the cursor mode is + * `GLFW_CURSOR_DISABLED`, otherwise it will do nothing. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_pos - * @sa glfwGetCursorPos + * @sa @ref glfwGetCursorPos * * @since Added in version 3.0. Replaces `glfwSetMousePos`. * @@ -3154,8 +4220,8 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * Any remaining cursors are destroyed by @ref glfwTerminate. * * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight - * bits per channel. They are arranged canonically as packed sequential rows, - * starting from the top-left corner. + * bits per channel with the red channel first. They are arranged canonically + * as packed sequential rows, starting from the top-left corner. * * The cursor hotspot is specified in pixels, relative to the upper-left corner * of the cursor image. Like all other coordinate systems in GLFW, the X-axis @@ -3173,13 +4239,11 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * @pointer_lifetime The specified image data is copied before this function * returns. * - * @reentrancy This function must not be called from a callback. - * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object - * @sa glfwDestroyCursor - * @sa glfwCreateStandardCursor + * @sa @ref glfwDestroyCursor + * @sa @ref glfwCreateStandardCursor * * @since Added in version 3.1. * @@ -3199,12 +4263,10 @@ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * - * @reentrancy This function must not be called from a callback. - * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object - * @sa glfwCreateCursor + * @sa @ref glfwCreateCursor * * @since Added in version 3.1. * @@ -3218,6 +4280,9 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); * glfwCreateCursor. Any remaining cursors will be destroyed by @ref * glfwTerminate. * + * If the specified cursor is current for any window, that window will be + * reverted to the default cursor. This does not affect the cursor mode. + * * @param[in] cursor The cursor object to destroy. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref @@ -3228,7 +4293,7 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object - * @sa glfwCreateCursor + * @sa @ref glfwCreateCursor * * @since Added in version 3.1. * @@ -3239,7 +4304,7 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); /*! @brief Sets the cursor for the window. * * This function sets the cursor image to be used when the cursor is over the - * client area of the specified window. The set cursor will only be visible + * content area of the specified window. The set cursor will only be visible * when the [cursor mode](@ref cursor_mode) of the window is * `GLFW_CURSOR_NORMAL`. * @@ -3320,10 +4385,8 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * * The character callback behaves as system text input normally does and will * not be called if modifier keys are held down that would prevent normal text - * input on that platform, for example a Super (Command) key on OS X or Alt key - * on Windows. There is a - * [character with modifiers callback](@ref glfwSetCharModsCallback) that - * receives these events. + * input on that platform, for example a Super (Command) key on macOS or Alt key + * on Windows. * * @param[in] window The window whose callback to set. * @param[in] cbfun The new callback, or `NULL` to remove the currently set @@ -3365,6 +4428,8 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); * @return The previously set callback, or `NULL` if no callback was set or an * [error](@ref error_handling) occurred. * + * @deprecated Scheduled for removal in version 4.0. + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3412,7 +4477,7 @@ GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmo * This function sets the cursor position callback of the specified window, * which is called when the cursor is moved. The callback is provided with the * position, in screen coordinates, relative to the upper-left corner of the - * client area of the window. + * content area of the window. * * @param[in] window The window whose callback to set. * @param[in] cbfun The new callback, or `NULL` to remove the currently set @@ -3435,7 +4500,7 @@ GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursor /*! @brief Sets the cursor enter/exit callback. * * This function sets the cursor boundary crossing callback of the specified - * window, which is called when the cursor enters or leaves the client area of + * window, which is called when the cursor enters or leaves the content area of * the window. * * @param[in] window The window whose callback to set. @@ -3501,6 +4566,8 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cb * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * + * @remark @wayland File drop is currently unimplemented. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref path_drop @@ -3515,7 +4582,11 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); * * This function returns whether the specified joystick is present. * - * @param[in] joy The [joystick](@ref joysticks) to query. + * There is no need to call this function before other functions that accept + * a joystick ID, as they all check for presence before performing any other + * work. + * + * @param[in] jid The [joystick](@ref joysticks) to query. * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref @@ -3529,18 +4600,18 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); * * @ingroup input */ -GLFWAPI int glfwJoystickPresent(int joy); +GLFWAPI int glfwJoystickPresent(int jid); /*! @brief Returns the values of all axes of the specified joystick. * * This function returns the values of all axes of the specified joystick. * Each element in the array is a value between -1.0 and 1.0. * - * Querying a joystick slot with no device present is not an error, but will - * cause this function to return `NULL`. Call @ref glfwJoystickPresent to - * check device presence. + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. * - * @param[in] joy The [joystick](@ref joysticks) to query. + * @param[in] jid The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of axis values in the returned * array. This is set to zero if the joystick is not present or an error * occurred. @@ -3552,8 +4623,7 @@ GLFWAPI int glfwJoystickPresent(int joy); * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is - * disconnected, this function is called again for that joystick or the library - * is terminated. + * disconnected or the library is terminated. * * @thread_safety This function must only be called from the main thread. * @@ -3563,18 +4633,25 @@ GLFWAPI int glfwJoystickPresent(int joy); * * @ingroup input */ -GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count); +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count); /*! @brief Returns the state of all buttons of the specified joystick. * * This function returns the state of all buttons of the specified joystick. * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. * - * Querying a joystick slot with no device present is not an error, but will - * cause this function to return `NULL`. Call @ref glfwJoystickPresent to - * check device presence. + * For backward compatibility with earlier versions that did not have @ref + * glfwGetJoystickHats, the button array also includes all hats, each + * represented as four buttons. The hats are in the same order as returned by + * __glfwGetJoystickHats__ and are in the order _up_, _right_, _down_ and + * _left_. To disable these extra buttons, set the @ref + * GLFW_JOYSTICK_HAT_BUTTONS init hint before initialization. * - * @param[in] joy The [joystick](@ref joysticks) to query. + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of button states in the returned * array. This is set to zero if the joystick is not present or an error * occurred. @@ -3586,19 +4663,75 @@ GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count); * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_button + * + * @since Added in version 2.2. + * @glfw3 Changed to return a dynamic array. + * + * @ingroup input + */ +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); + +/*! @brief Returns the state of all hats of the specified joystick. + * + * This function returns the state of all hats of the specified joystick. + * Each element in the array is one of the following values: + * + * Name | Value + * ---- | ----- + * `GLFW_HAT_CENTERED` | 0 + * `GLFW_HAT_UP` | 1 + * `GLFW_HAT_RIGHT` | 2 + * `GLFW_HAT_DOWN` | 4 + * `GLFW_HAT_LEFT` | 8 + * `GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP` + * `GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_DOWN` + * `GLFW_HAT_LEFT_UP` | `GLFW_HAT_LEFT` \| `GLFW_HAT_UP` + * `GLFW_HAT_LEFT_DOWN` | `GLFW_HAT_LEFT` \| `GLFW_HAT_DOWN` + * + * The diagonal directions are bitwise combinations of the primary (up, right, + * down and left) directions and you can test for these individually by ANDing + * it with the corresponding direction. + * + * @code + * if (hats[2] & GLFW_HAT_RIGHT) + * { + * // State of hat 2 could be right-up, right or right-down + * } + * @endcode + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of hat states in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of hat states, or `NULL` if the joystick is not present + * or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is * disconnected, this function is called again for that joystick or the library * is terminated. * * @thread_safety This function must only be called from the main thread. * - * @sa @ref joystick_button + * @sa @ref joystick_hat * - * @since Added in version 2.2. - * @glfw3 Changed to return a dynamic array. + * @since Added in version 3.3. * * @ingroup input */ -GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); +GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); /*! @brief Returns the name of the specified joystick. * @@ -3606,11 +4739,11 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); * The returned string is allocated and freed by GLFW. You should not free it * yourself. * - * Querying a joystick slot with no device present is not an error, but will - * cause this function to return `NULL`. Call @ref glfwJoystickPresent to - * check device presence. + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. * - * @param[in] joy The [joystick](@ref joysticks) to query. + * @param[in] jid The [joystick](@ref joysticks) to query. * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick * is not present or an [error](@ref error_handling) occurred. * @@ -3619,8 +4752,7 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is - * disconnected, this function is called again for that joystick or the library - * is terminated. + * disconnected or the library is terminated. * * @thread_safety This function must only be called from the main thread. * @@ -3630,7 +4762,126 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); * * @ingroup input */ -GLFWAPI const char* glfwGetJoystickName(int joy); +GLFWAPI const char* glfwGetJoystickName(int jid); + +/*! @brief Returns the SDL comaptible GUID of the specified joystick. + * + * This function returns the SDL compatible GUID, as a UTF-8 encoded + * hexadecimal string, of the specified joystick. The returned string is + * allocated and freed by GLFW. You should not free it yourself. + * + * The GUID is what connects a joystick to a gamepad mapping. A connected + * joystick will always have a GUID even if there is no gamepad mapping + * assigned to it. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * The GUID uses the format introduced in SDL 2.0.5. This GUID tries to + * uniquely identify the make and model of a joystick but does not identify + * a specific unit, e.g. all wired Xbox 360 controllers will have the same + * GUID on that platform. The GUID for a unit may vary between platforms + * depending on what hardware information the platform specific APIs provide. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded GUID of the joystick, or `NULL` if the joystick + * is not present or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetJoystickGUID(int jid); + +/*! @brief Sets the user pointer of the specified joystick. + * + * This function sets the user-defined pointer of the specified joystick. The + * current value is retained until the joystick is disconnected. The initial + * value is `NULL`. + * + * This function may be called from the joystick callback, even for a joystick + * that is being disconnected. + * + * @param[in] jid The joystick whose pointer to set. + * @param[in] pointer The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref joystick_userptr + * @sa @ref glfwGetJoystickUserPointer + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer); + +/*! @brief Returns the user pointer of the specified joystick. + * + * This function returns the current value of the user-defined pointer of the + * specified joystick. The initial value is `NULL`. + * + * This function may be called from the joystick callback, even for a joystick + * that is being disconnected. + * + * @param[in] jid The joystick whose pointer to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref joystick_userptr + * @sa @ref glfwSetJoystickUserPointer + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI void* glfwGetJoystickUserPointer(int jid); + +/*! @brief Returns whether the specified joystick has a gamepad mapping. + * + * This function returns whether the specified joystick is both present and has + * a gamepad mapping. + * + * If the specified joystick is present but does not have a gamepad mapping + * this function will return `GLFW_FALSE` but will not generate an error. Call + * @ref glfwJoystickPresent to check if a joystick is present regardless of + * whether it has a mapping. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return `GLFW_TRUE` if a joystick is both present and has a gamepad mapping, + * or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwGetGamepadState + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwJoystickIsGamepad(int jid); /*! @brief Sets the joystick configuration callback. * @@ -3638,6 +4889,12 @@ GLFWAPI const char* glfwGetJoystickName(int joy); * currently set callback. This is called when a joystick is connected to or * disconnected from the system. * + * For joystick connection and disconnection events to be delivered on all + * platforms, you need to call one of the [event processing](@ref events) + * functions. Joystick disconnection may also be detected and the callback + * called by joystick functions. The function will then return whatever it + * returns if the joystick is not present. + * * @param[in] cbfun The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the @@ -3655,12 +4912,114 @@ GLFWAPI const char* glfwGetJoystickName(int joy); */ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun); +/*! @brief Adds the specified SDL_GameControllerDB gamepad mappings. + * + * This function parses the specified ASCII encoded string and updates the + * internal list with any gamepad mappings it finds. This string may + * contain either a single gamepad mapping or many mappings separated by + * newlines. The parser supports the full format of the `gamecontrollerdb.txt` + * source file including empty lines and comments. + * + * See @ref gamepad_mapping for a description of the format. + * + * If there is already a gamepad mapping for a given GUID in the internal list, + * it will be replaced by the one passed to this function. If the library is + * terminated and re-initialized the internal list will revert to the built-in + * default. + * + * @param[in] string The string containing the gamepad mappings. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_VALUE. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwJoystickIsGamepad + * @sa @ref glfwGetGamepadName + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwUpdateGamepadMappings(const char* string); + +/*! @brief Returns the human-readable gamepad name for the specified joystick. + * + * This function returns the human-readable name of the gamepad from the + * gamepad mapping assigned to the specified joystick. + * + * If the specified joystick is not present or does not have a gamepad mapping + * this function will return `NULL` but will not generate an error. Call + * @ref glfwJoystickPresent to check whether it is present regardless of + * whether it has a mapping. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded name of the gamepad, or `NULL` if the + * joystick is not present, does not have a mapping or an + * [error](@ref error_handling) occurred. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, the gamepad mappings are updated or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwJoystickIsGamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetGamepadName(int jid); + +/*! @brief Retrieves the state of the specified joystick remapped as a gamepad. + * + * This function retrives the state of the specified joystick remapped to + * an Xbox-like gamepad. + * + * If the specified joystick is not present or does not have a gamepad mapping + * this function will return `GLFW_FALSE` but will not generate an error. Call + * @ref glfwJoystickPresent to check whether it is present regardless of + * whether it has a mapping. + * + * The Guide button may not be available for input as it is often hooked by the + * system or the Steam client. + * + * Not all devices have all the buttons or axes provided by @ref + * GLFWgamepadstate. Unavailable buttons and axes will always report + * `GLFW_RELEASE` and 0.0 respectively. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] state The gamepad input state of the joystick. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if no joystick is + * connected, it has no gamepad mapping or an [error](@ref error_handling) + * occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwUpdateGamepadMappings + * @sa @ref glfwJoystickIsGamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); + /*! @brief Sets the clipboard to the specified string. * * This function sets the system clipboard to the specified, UTF-8 encoded * string. * - * @param[in] window The window that will own the clipboard contents. + * @param[in] window Deprecated. Any valid window or `NULL`. * @param[in] string A UTF-8 encoded string. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref @@ -3672,7 +5031,7 @@ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun); * @thread_safety This function must only be called from the main thread. * * @sa @ref clipboard - * @sa glfwGetClipboardString + * @sa @ref glfwGetClipboardString * * @since Added in version 3.0. * @@ -3687,7 +5046,7 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); * if its contents cannot be converted, `NULL` is returned and a @ref * GLFW_FORMAT_UNAVAILABLE error is generated. * - * @param[in] window The window that will request the clipboard contents. + * @param[in] window Deprecated. Any valid window or `NULL`. * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` * if an [error](@ref error_handling) occurred. * @@ -3702,7 +5061,7 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); * @thread_safety This function must only be called from the main thread. * * @sa @ref clipboard - * @sa glfwSetClipboardString + * @sa @ref glfwSetClipboardString * * @since Added in version 3.0. * @@ -3770,7 +5129,7 @@ GLFWAPI void glfwSetTime(double time); * 1 / frequency seconds. To get the frequency, call @ref * glfwGetTimerFrequency. * - * @return The value of the timer, or zero if an + * @return The value of the timer, or zero if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. @@ -3778,7 +5137,7 @@ GLFWAPI void glfwSetTime(double time); * @thread_safety This function may be called from any thread. * * @sa @ref time - * @sa glfwGetTimerFrequency + * @sa @ref glfwGetTimerFrequency * * @since Added in version 3.2. * @@ -3798,7 +5157,7 @@ GLFWAPI uint64_t glfwGetTimerValue(void); * @thread_safety This function may be called from any thread. * * @sa @ref time - * @sa glfwGetTimerValue + * @sa @ref glfwGetTimerValue * * @since Added in version 3.2. * @@ -3810,14 +5169,18 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void); * thread. * * This function makes the OpenGL or OpenGL ES context of the specified window - * current on the calling thread. A context can only be made current on + * current on the calling thread. A context must only be made current on * a single thread at a time and each thread can have only a single current * context at a time. * + * When moving a context between threads, you must make it non-current on the + * old thread before making it current on the new one. + * * By default, making a context non-current implicitly forces a pipeline flush. * On machines that support `GL_KHR_context_flush_control`, you can control * whether a context performs this flush by setting the - * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref window_hints_ctx) window hint. + * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) + * hint. * * The specified window must have an OpenGL or OpenGL ES context. Specifying * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT @@ -3832,7 +5195,7 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void); * @thread_safety This function may be called from any thread. * * @sa @ref context_current - * @sa glfwGetCurrentContext + * @sa @ref glfwGetCurrentContext * * @since Added in version 3.0. * @@ -3853,7 +5216,7 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window); * @thread_safety This function may be called from any thread. * * @sa @ref context_current - * @sa glfwMakeContextCurrent + * @sa @ref glfwMakeContextCurrent * * @since Added in version 3.0. * @@ -3886,7 +5249,7 @@ GLFWAPI GLFWwindow* glfwGetCurrentContext(void); * @thread_safety This function may be called from any thread. * * @sa @ref buffer_swap - * @sa glfwSwapInterval + * @sa @ref glfwSwapInterval * * @since Added in version 1.0. * @glfw3 Added window handle parameter. @@ -3903,12 +5266,11 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); * is sometimes called _vertical synchronization_, _vertical retrace * synchronization_ or just _vsync_. * - * Contexts that support either of the `WGL_EXT_swap_control_tear` and - * `GLX_EXT_swap_control_tear` extensions also accept negative swap intervals, - * which allow the driver to swap even if a frame arrives a little bit late. - * You can check for the presence of these extensions using @ref - * glfwExtensionSupported. For more information about swap tearing, see the - * extension specifications. + * A context that supports either of the `WGL_EXT_swap_control_tear` and + * `GLX_EXT_swap_control_tear` extensions also accepts _negative_ swap + * intervals, which allows the driver to swap immediately even if a frame + * arrives a little bit late. You can check for these extensions with @ref + * glfwExtensionSupported. * * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. @@ -3934,7 +5296,7 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); * @thread_safety This function may be called from any thread. * * @sa @ref buffer_swap - * @sa glfwSwapBuffers + * @sa @ref glfwSwapBuffers * * @since Added in version 1.0. * @@ -3972,7 +5334,7 @@ GLFWAPI void glfwSwapInterval(int interval); * @thread_safety This function may be called from any thread. * * @sa @ref context_glext - * @sa glfwGetProcAddress + * @sa @ref glfwGetProcAddress * * @since Added in version 1.0. * @@ -4014,7 +5376,7 @@ GLFWAPI int glfwExtensionSupported(const char* extension); * @thread_safety This function may be called from any thread. * * @sa @ref context_glext - * @sa glfwExtensionSupported + * @sa @ref glfwExtensionSupported * * @since Added in version 1.0. * @@ -4022,19 +5384,21 @@ GLFWAPI int glfwExtensionSupported(const char* extension); */ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); -/*! @brief Returns whether the Vulkan loader has been found. +/*! @brief Returns whether the Vulkan loader and an ICD have been found. * - * This function returns whether the Vulkan loader has been found. This check - * is performed by @ref glfwInit. + * This function returns whether the Vulkan loader and any minimally functional + * ICD have been found. * - * The availability of a Vulkan loader does not by itself guarantee that window - * surface creation or even device creation is possible. Call @ref - * glfwGetRequiredInstanceExtensions to check whether the extensions necessary - * for Vulkan surface creation are available and @ref - * glfwGetPhysicalDevicePresentationSupport to check whether a queue family of - * a physical device supports image presentation. + * The availability of a Vulkan loader and even an ICD does not by itself + * guarantee that surface creation or even instance creation is possible. + * For example, on Fermi systems Nvidia will install an ICD that provides no + * actual Vulkan support. Call @ref glfwGetRequiredInstanceExtensions to check + * whether the extensions necessary for Vulkan surface creation are available + * and @ref glfwGetPhysicalDevicePresentationSupport to check whether a queue + * family of a physical device supports image presentation. * - * @return `GLFW_TRUE` if Vulkan is available, or `GLFW_FALSE` otherwise. + * @return `GLFW_TRUE` if Vulkan is minimally available, or `GLFW_FALSE` + * otherwise. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * @@ -4058,7 +5422,7 @@ GLFWAPI int glfwVulkanSupported(void); * * If Vulkan is not available on the machine, this function returns `NULL` and * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported - * to check whether Vulkan is available. + * to check whether Vulkan is at least minimally available. * * If Vulkan is available but no set of extensions allowing window surface * creation was found, this function returns `NULL`. You may still use Vulkan @@ -4072,11 +5436,14 @@ GLFWAPI int glfwVulkanSupported(void); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_API_UNAVAILABLE. * - * @remarks Additional extensions may be required by future versions of GLFW. + * @remark Additional extensions may be required by future versions of GLFW. * You should check if any extensions you wish to enable are already in the * returned array, as it is an error to specify an extension more than once in * the `VkInstanceCreateInfo` struct. * + * @remark @macos This function currently only supports the + * `VK_MVK_macos_surface` extension from MoltenVK. + * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is guaranteed to be valid only until the * library is terminated. @@ -4084,7 +5451,7 @@ GLFWAPI int glfwVulkanSupported(void); * @thread_safety This function may be called from any thread. * * @sa @ref vulkan_ext - * @sa glfwCreateWindowSurface + * @sa @ref glfwCreateWindowSurface * * @since Added in version 3.2. * @@ -4108,7 +5475,7 @@ GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count); * * If Vulkan is not available on the machine, this function returns `NULL` and * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported - * to check whether Vulkan is available. + * to check whether Vulkan is at least minimally available. * * This function is equivalent to calling `vkGetInstanceProcAddr` with * a platform-specific query of the Vulkan loader as a fallback. @@ -4144,7 +5511,7 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* p * not available on the machine, or if the specified instance was not created * with the required extensions, this function returns `GLFW_FALSE` and * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported - * to check whether Vulkan is available and @ref + * to check whether Vulkan is at least minimally available and @ref * glfwGetRequiredInstanceExtensions to check what instance extensions are * required. * @@ -4157,6 +5524,10 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* p * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * + * @remark @macos This function currently always returns `GLFW_TRUE`, as the + * `VK_MVK_macos_surface` extension does not provide + * a `vkGetPhysicalDevice*PresentationSupport` type function. + * * @thread_safety This function may be called from any thread. For * synchronization details of Vulkan objects, see the Vulkan specification. * @@ -4172,10 +5543,10 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys * * This function creates a Vulkan surface for the specified window. * - * If the Vulkan loader was not found at initialization, this function returns - * `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref GLFW_API_UNAVAILABLE - * error. Call @ref glfwVulkanSupported to check whether the Vulkan loader was - * found. + * If the Vulkan loader or at least one minimally functional ICD were not found, + * this function returns `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref + * GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported to check whether + * Vulkan is at least minimally available. * * If the required window surface creation instance extensions are not * available or if the specified instance was not created with these extensions @@ -4184,6 +5555,11 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys * glfwGetRequiredInstanceExtensions to check what instance extensions are * required. * + * The window surface cannot be shared with another API so the window must + * have been created with the [client api hint](@ref GLFW_CLIENT_API_attrib) + * set to `GLFW_NO_API` otherwise it generates a @ref GLFW_INVALID_VALUE error + * and returns `VK_ERROR_NATIVE_WINDOW_IN_USE_KHR`. + * * The window surface must be destroyed before the specified Vulkan instance. * It is the responsibility of the caller to destroy the window surface. GLFW * does not destroy it for you. Call `vkDestroySurfaceKHR` to destroy the @@ -4199,18 +5575,24 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. + * GLFW_API_UNAVAILABLE, @ref GLFW_PLATFORM_ERROR and @ref GLFW_INVALID_VALUE * - * @remarks If an error occurs before the creation call is made, GLFW returns + * @remark If an error occurs before the creation call is made, GLFW returns * the Vulkan error code most appropriate for the error. Appropriate use of * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should * eliminate almost all occurrences of these errors. * + * @remark @macos This function currently only supports the + * `VK_MVK_macos_surface` extension from MoltenVK. + * + * @remark @macos This function creates and sets a `CAMetalLayer` instance for + * the window content view, which is required for MoltenVK to function. + * * @thread_safety This function may be called from any thread. For * synchronization details of Vulkan objects, see the Vulkan specification. * * @sa @ref vulkan_surface - * @sa glfwGetRequiredInstanceExtensions + * @sa @ref glfwGetRequiredInstanceExtensions * * @since Added in version 3.2. * @@ -4237,6 +5619,13 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window #undef GLFW_CALLBACK_DEFINED #endif +/* Some OpenGL related headers need GLAPIENTRY, but it is unconditionally + * defined by some gl.h variants (OpenBSD) so define it after if needed. + */ +#ifndef GLAPIENTRY + #define GLAPIENTRY APIENTRY +#endif + /* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ diff --git a/external/glfw/glfw3.pc.in b/external/glfw/glfw3.pc.in index f2e4d97..87423e1 100644 --- a/external/glfw/glfw3.pc.in +++ b/external/glfw/glfw3.pc.in @@ -1,12 +1,12 @@ prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} -includedir=${prefix}/include -libdir=${exec_prefix}/lib@LIB_SUFFIX@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ Name: GLFW Description: A multi-platform library for OpenGL, window and input Version: @GLFW_VERSION_FULL@ -URL: http://www.glfw.org/ +URL: https://www.glfw.org/ Requires.private: @GLFW_PKG_DEPS@ Libs: -L${libdir} -l@GLFW_LIB_NAME@ Libs.private: @GLFW_PKG_LIBS@ diff --git a/external/glfw/glfw3native.h b/external/glfw/glfw3native.h index 056bc82..267e75c 100644 --- a/external/glfw/glfw3native.h +++ b/external/glfw/glfw3native.h @@ -3,7 +3,7 @@ * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard - * Copyright (c) 2006-2016 Camilla Berglund + * Copyright (c) 2006-2018 Camilla Löwy * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -45,12 +45,13 @@ extern "C" { * more information. */ /*! @defgroup native Native access + * @brief Functions related to accessing native handles. * * **By using the native access functions you assert that you know what you're * doing and how to fix problems caused by using them. If you don't, you * shouldn't be using them.** * - * Before the inclusion of @ref glfw3native.h, you may define exactly one + * Before the inclusion of @ref glfw3native.h, you may define zero or more * window system API macro and zero or more context creation API macros. * * The chosen backends must match those the library was compiled for. Failure @@ -61,13 +62,13 @@ extern "C" { * * `GLFW_EXPOSE_NATIVE_COCOA` * * `GLFW_EXPOSE_NATIVE_X11` * * `GLFW_EXPOSE_NATIVE_WAYLAND` - * * `GLFW_EXPOSE_NATIVE_MIR` * * The available context API macros are: * * `GLFW_EXPOSE_NATIVE_WGL` * * `GLFW_EXPOSE_NATIVE_NSGL` * * `GLFW_EXPOSE_NATIVE_GLX` * * `GLFW_EXPOSE_NATIVE_EGL` + * * `GLFW_EXPOSE_NATIVE_OSMESA` * * These macros select which of the native access functions that are declared * and which platform-specific headers to include. It is then up your (by @@ -80,26 +81,27 @@ extern "C" { * System headers and types *************************************************************************/ -#if defined(GLFW_EXPOSE_NATIVE_WIN32) +#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for // example to allow applications to correctly declare a GL_ARB_debug_output // callback) but windows.h assumes no one will define APIENTRY before it does - #undef APIENTRY + #if defined(GLFW_APIENTRY_DEFINED) + #undef APIENTRY + #undef GLFW_APIENTRY_DEFINED + #endif #include -#elif defined(GLFW_EXPOSE_NATIVE_COCOA) - #include +#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) #if defined(__OBJC__) #import #else + #include typedef void* id; #endif -#elif defined(GLFW_EXPOSE_NATIVE_X11) +#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) #include #include #elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) #include -#elif defined(GLFW_EXPOSE_NATIVE_MIR) - #include #endif #if defined(GLFW_EXPOSE_NATIVE_WGL) @@ -114,6 +116,9 @@ extern "C" { #if defined(GLFW_EXPOSE_NATIVE_EGL) #include #endif +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) + #include +#endif /************************************************************************* @@ -284,6 +289,56 @@ GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); * @ingroup native */ GLFWAPI Window glfwGetX11Window(GLFWwindow* window); + +/*! @brief Sets the current primary selection to the specified string. + * + * @param[in] string A UTF-8 encoded string. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified string is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa glfwGetX11SelectionString + * @sa glfwSetClipboardString + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI void glfwSetX11SelectionString(const char* string); + +/*! @brief Returns the contents of the current primary selection as a string. + * + * If the selection is empty or if its contents cannot be converted, `NULL` + * is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated. + * + * @return The contents of the selection as a UTF-8 encoded string, or `NULL` + * if an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the next call to @ref + * glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the + * library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa glfwSetX11SelectionString + * @sa glfwGetClipboardString + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI const char* glfwGetX11SelectionString(void); #endif #if defined(GLFW_EXPOSE_NATIVE_GLX) @@ -360,50 +415,6 @@ GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); #endif -#if defined(GLFW_EXPOSE_NATIVE_MIR) -/*! @brief Returns the `MirConnection*` used by GLFW. - * - * @return The `MirConnection*` used by GLFW, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI MirConnection* glfwGetMirDisplay(void); - -/*! @brief Returns the Mir output ID of the specified monitor. - * - * @return The Mir output ID of the specified monitor, or zero if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor); - -/*! @brief Returns the `MirSurface*` of the specified window. - * - * @return The `MirSurface*` of the specified window, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* window); -#endif - #if defined(GLFW_EXPOSE_NATIVE_EGL) /*! @brief Returns the `EGLDisplay` used by GLFW. * @@ -448,6 +459,64 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); #endif +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) +/*! @brief Retrieves the color buffer associated with the specified window. + * + * @param[in] window The window whose color buffer to retrieve. + * @param[out] width Where to store the width of the color buffer, or `NULL`. + * @param[out] height Where to store the height of the color buffer, or `NULL`. + * @param[out] format Where to store the OSMesa pixel format of the color + * buffer, or `NULL`. + * @param[out] buffer Where to store the address of the color buffer, or + * `NULL`. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer); + +/*! @brief Retrieves the depth buffer associated with the specified window. + * + * @param[in] window The window whose depth buffer to retrieve. + * @param[out] width Where to store the width of the depth buffer, or `NULL`. + * @param[out] height Where to store the height of the depth buffer, or `NULL`. + * @param[out] bytesPerValue Where to store the number of bytes per depth + * buffer element, or `NULL`. + * @param[out] buffer Where to store the address of the depth buffer, or + * `NULL`. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer); + +/*! @brief Returns the `OSMesaContext` of the specified window. + * + * @return The `OSMesaContext` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window); +#endif + #ifdef __cplusplus } #endif diff --git a/external/glfw/glfw_config.h b/external/glfw/glfw_config.h index 2250ff0..5d93aa1 100644 --- a/external/glfw/glfw_config.h +++ b/external/glfw/glfw_config.h @@ -75,4 +75,4 @@ #endif #define _GLFW_USE_MENUBAR -#define _GLFW_USE_OPENGL +#define _GLFW_USE_OPENGL \ No newline at end of file diff --git a/external/glfw/glfw_config.h.in b/external/glfw/glfw_config.h.in index f709726..f418c99 100644 --- a/external/glfw/glfw_config.h.in +++ b/external/glfw/glfw_config.h.in @@ -1,7 +1,7 @@ //======================================================================== // GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2010-2016 Camilla Berglund +// Copyright (c) 2010-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -42,8 +42,8 @@ #cmakedefine _GLFW_COCOA // Define this to 1 if building GLFW for Wayland #cmakedefine _GLFW_WAYLAND -// Define this to 1 if building GLFW for Mir -#cmakedefine _GLFW_MIR +// Define this to 1 if building GLFW for OSMesa +#cmakedefine _GLFW_OSMESA // Define this to 1 if building as a shared library / dynamic library / DLL #cmakedefine _GLFW_BUILD_DLL @@ -53,13 +53,8 @@ // Define this to 1 to force use of high-performance GPU on hybrid systems #cmakedefine _GLFW_USE_HYBRID_HPG -// Define this to 1 if the Xxf86vm X11 extension is available -#cmakedefine _GLFW_HAS_XF86VM - -// Define this to 1 if glfwInit should change the current directory -#cmakedefine _GLFW_USE_CHDIR -// Define this to 1 if glfwCreateWindow should populate the menu bar -#cmakedefine _GLFW_USE_MENUBAR -// Define this to 1 if windows should use full resolution on Retina displays -#cmakedefine _GLFW_USE_RETINA +// Define this to 1 if xkbcommon supports the compose key +#cmakedefine HAVE_XKBCOMMON_COMPOSE_H +// Define this to 1 if the libc supports memfd_create() +#cmakedefine HAVE_MEMFD_CREATE diff --git a/external/glfw/glx_context.c b/external/glfw/glx_context.c index 6f2f276..b03a048 100644 --- a/external/glfw/glx_context.c +++ b/external/glfw/glx_context.c @@ -2,7 +2,7 @@ // GLFW 3.3 GLX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -47,7 +47,8 @@ static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib) // Return the GLXFBConfig most closely matching the specified hints // -static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* result) +static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, + GLXFBConfig* result) { GLXFBConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; @@ -59,12 +60,12 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res // HACK: This is a (hopefully temporary) workaround for Chromium // (VirtualBox GL) not setting the window bit on any GLXFBConfigs vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR); - if (strcmp(vendor, "Chromium") == 0) + if (vendor && strcmp(vendor, "Chromium") == 0) trustWindowBit = GLFW_FALSE; nativeConfigs = glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount); - if (!nativeCount) + if (!nativeConfigs || !nativeCount) { _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned"); return GLFW_FALSE; @@ -89,6 +90,16 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res continue; } + if (desired->transparent) + { + XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n); + if (vi) + { + u->transparent = _glfwIsVisualTransparentX11(vi->visual); + XFree(vi); + } + } + u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE); u->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE); u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE); @@ -165,7 +176,7 @@ static void makeContextCurrentGLX(_GLFWwindow* window) } } - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.contextSlot, window); } static void swapBuffersGLX(_GLFWwindow* window) @@ -175,7 +186,7 @@ static void swapBuffersGLX(_GLFWwindow* window) static void swapIntervalGLX(int interval) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); if (_glfw.glx.EXT_swap_control) { @@ -212,7 +223,7 @@ static GLFWglproc getProcAddressGLX(const char* procname) else if (_glfw.glx.GetProcAddressARB) return _glfw.glx.GetProcAddressARB((const GLubyte*) procname); else - return dlsym(_glfw.glx.handle, procname); + return _glfw_dlsym(_glfw.glx.handle, procname); } // Destroy the OpenGL context @@ -244,7 +255,9 @@ GLFWbool _glfwInitGLX(void) int i; const char* sonames[] = { -#if defined(__CYGWIN__) +#if defined(_GLFW_GLX_LIBRARY) + _GLFW_GLX_LIBRARY, +#elif defined(__CYGWIN__) "libGL-1.so", #else "libGL.so.1", @@ -258,7 +271,7 @@ GLFWbool _glfwInitGLX(void) for (i = 0; sonames[i]; i++) { - _glfw.glx.handle = dlopen(sonames[i], RTLD_LAZY | RTLD_GLOBAL); + _glfw.glx.handle = _glfw_dlopen(sonames[i]); if (_glfw.glx.handle) break; } @@ -270,35 +283,35 @@ GLFWbool _glfwInitGLX(void) } _glfw.glx.GetFBConfigs = - dlsym(_glfw.glx.handle, "glXGetFBConfigs"); + _glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigs"); _glfw.glx.GetFBConfigAttrib = - dlsym(_glfw.glx.handle, "glXGetFBConfigAttrib"); + _glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigAttrib"); _glfw.glx.GetClientString = - dlsym(_glfw.glx.handle, "glXGetClientString"); + _glfw_dlsym(_glfw.glx.handle, "glXGetClientString"); _glfw.glx.QueryExtension = - dlsym(_glfw.glx.handle, "glXQueryExtension"); + _glfw_dlsym(_glfw.glx.handle, "glXQueryExtension"); _glfw.glx.QueryVersion = - dlsym(_glfw.glx.handle, "glXQueryVersion"); + _glfw_dlsym(_glfw.glx.handle, "glXQueryVersion"); _glfw.glx.DestroyContext = - dlsym(_glfw.glx.handle, "glXDestroyContext"); + _glfw_dlsym(_glfw.glx.handle, "glXDestroyContext"); _glfw.glx.MakeCurrent = - dlsym(_glfw.glx.handle, "glXMakeCurrent"); + _glfw_dlsym(_glfw.glx.handle, "glXMakeCurrent"); _glfw.glx.SwapBuffers = - dlsym(_glfw.glx.handle, "glXSwapBuffers"); + _glfw_dlsym(_glfw.glx.handle, "glXSwapBuffers"); _glfw.glx.QueryExtensionsString = - dlsym(_glfw.glx.handle, "glXQueryExtensionsString"); + _glfw_dlsym(_glfw.glx.handle, "glXQueryExtensionsString"); _glfw.glx.CreateNewContext = - dlsym(_glfw.glx.handle, "glXCreateNewContext"); + _glfw_dlsym(_glfw.glx.handle, "glXCreateNewContext"); _glfw.glx.CreateWindow = - dlsym(_glfw.glx.handle, "glXCreateWindow"); + _glfw_dlsym(_glfw.glx.handle, "glXCreateWindow"); _glfw.glx.DestroyWindow = - dlsym(_glfw.glx.handle, "glXDestroyWindow"); + _glfw_dlsym(_glfw.glx.handle, "glXDestroyWindow"); _glfw.glx.GetProcAddress = - dlsym(_glfw.glx.handle, "glXGetProcAddress"); + _glfw_dlsym(_glfw.glx.handle, "glXGetProcAddress"); _glfw.glx.GetProcAddressARB = - dlsym(_glfw.glx.handle, "glXGetProcAddressARB"); + _glfw_dlsym(_glfw.glx.handle, "glXGetProcAddressARB"); _glfw.glx.GetVisualFromFBConfig = - dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig"); + _glfw_dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig"); if (!_glfw.glx.GetFBConfigs || !_glfw.glx.GetFBConfigAttrib || @@ -397,6 +410,9 @@ GLFWbool _glfwInitGLX(void) if (extensionSupportedGLX("GLX_EXT_create_context_es2_profile")) _glfw.glx.EXT_create_context_es2_profile = GLFW_TRUE; + if (extensionSupportedGLX("GLX_ARB_create_context_no_error")) + _glfw.glx.ARB_create_context_no_error = GLFW_TRUE; + if (extensionSupportedGLX("GLX_ARB_context_flush_control")) _glfw.glx.ARB_context_flush_control = GLFW_TRUE; @@ -412,16 +428,16 @@ void _glfwTerminateGLX(void) if (_glfw.glx.handle) { - dlclose(_glfw.glx.handle); + _glfw_dlclose(_glfw.glx.handle); _glfw.glx.handle = NULL; } } -#define setGLXattrib(attribName, attribValue) \ +#define setAttrib(a, v) \ { \ - attribs[index++] = attribName; \ - attribs[index++] = attribValue; \ - assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ + assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ } // Create the OpenGL or OpenGL ES context @@ -498,8 +514,6 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, if (ctxconfig->debug) flags |= GLX_CONTEXT_DEBUG_BIT_ARB; - if (ctxconfig->noerror) - flags |= GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR; if (ctxconfig->robustness) { @@ -507,13 +521,13 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { - setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - GLX_NO_RESET_NOTIFICATION_ARB); + setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + GLX_NO_RESET_NOTIFICATION_ARB); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { - setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - GLX_LOSE_CONTEXT_ON_RESET_ARB); + setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + GLX_LOSE_CONTEXT_ON_RESET_ARB); } flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; @@ -526,33 +540,39 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { - setGLXattrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, - GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, + GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { - setGLXattrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, - GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); + setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, + GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); } } } + if (ctxconfig->noerror) + { + if (_glfw.glx.ARB_create_context_no_error) + setAttrib(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); + } + // NOTE: Only request an explicitly versioned context when necessary, as // explicitly requesting version 1.0 does not always return the // highest version supported by the driver if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setGLXattrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); - setGLXattrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); + setAttrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); + setAttrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); } if (mask) - setGLXattrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); + setAttrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); if (flags) - setGLXattrib(GLX_CONTEXT_FLAGS_ARB, flags); + setAttrib(GLX_CONTEXT_FLAGS_ARB, flags); - setGLXattrib(None, None); + setAttrib(None, None); window->context.glx.handle = _glfw.glx.CreateContextAttribsARB(_glfw.x11.display, @@ -609,11 +629,12 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, return GLFW_TRUE; } -#undef setGLXattrib +#undef setAttrib // Returns the Visual and depth of the chosen GLXFBConfig // -GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, +GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth) { @@ -636,7 +657,7 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, } *visual = result->visual; - *depth = result->depth; + *depth = result->depth; XFree(result); return GLFW_TRUE; diff --git a/external/glfw/glx_context.h b/external/glfw/glx_context.h index 15e4f9b..e63684f 100644 --- a/external/glfw/glx_context.h +++ b/external/glfw/glx_context.h @@ -2,7 +2,7 @@ // GLFW 3.3 GLX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,9 +25,6 @@ // //======================================================================== -#ifndef _glfw3_glx_context_h_ -#define _glfw3_glx_context_h_ - #define GLX_VENDOR 1 #define GLX_RGBA_BIT 0x00000001 #define GLX_WINDOW_BIT 0x00000001 @@ -67,6 +64,7 @@ #define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 #define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 #define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 typedef XID GLXWindow; typedef XID GLXDrawable; @@ -85,14 +83,15 @@ typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool); typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName); -typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); -typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int); typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); -typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); +typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); +typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int); +typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); + // libGL.so function pointer typedefs #define glXGetFBConfigs _glfw.glx.GetFBConfigs #define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib @@ -164,19 +163,19 @@ typedef struct _GLFWlibraryGLX GLFWbool ARB_create_context_profile; GLFWbool ARB_create_context_robustness; GLFWbool EXT_create_context_es2_profile; + GLFWbool ARB_create_context_no_error; GLFWbool ARB_context_flush_control; } _GLFWlibraryGLX; - GLFWbool _glfwInitGLX(void); void _glfwTerminateGLX(void); GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); void _glfwDestroyContextGLX(_GLFWwindow* window); -GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, +GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); -#endif // _glfw3_glx_context_h_ diff --git a/external/glfw/init.c b/external/glfw/init.c index e0ebfad..3d0f9cf 100644 --- a/external/glfw/init.c +++ b/external/glfw/init.c @@ -2,7 +2,7 @@ // GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2018 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -26,128 +26,43 @@ //======================================================================== #include "internal.h" +#include "mappings.h" #include #include #include #include +#include -// The three global variables below comprise all global data in GLFW. -// Any other global variable is a bug. +// The global variables below comprise all mutable global data in GLFW +// +// Any other global variable is a bug // Global state shared between compilation units of GLFW -// These are documented in internal.h // -GLFWbool _glfwInitialized = GLFW_FALSE; -_GLFWlibrary _glfw; +_GLFWlibrary _glfw = { GLFW_FALSE }; -// This is outside of _glfw so it can be initialized and usable before -// glfwInit is called, which lets that function report errors +// These are outside of _glfw so they can be used before initialization and +// after termination // -static GLFWerrorfun _glfwErrorCallback = NULL; +static _GLFWerror _glfwMainThreadError; +static GLFWerrorfun _glfwErrorCallback; +static _GLFWinitconfig _glfwInitHints = +{ + GLFW_TRUE, // hat buttons + { + GLFW_TRUE, // macOS menu bar + GLFW_TRUE // macOS bundle chdir + } +}; - -// Returns a generic string representation of the specified error +// Terminate the library // -static const char* getErrorString(int error) -{ - switch (error) - { - case GLFW_NOT_INITIALIZED: - return "The GLFW library is not initialized"; - case GLFW_NO_CURRENT_CONTEXT: - return "There is no current context"; - case GLFW_INVALID_ENUM: - return "Invalid argument for enum parameter"; - case GLFW_INVALID_VALUE: - return "Invalid value for parameter"; - case GLFW_OUT_OF_MEMORY: - return "Out of memory"; - case GLFW_API_UNAVAILABLE: - return "The requested API is unavailable"; - case GLFW_VERSION_UNAVAILABLE: - return "The requested API version is unavailable"; - case GLFW_PLATFORM_ERROR: - return "A platform-specific error occurred"; - case GLFW_FORMAT_UNAVAILABLE: - return "The requested format is unavailable"; - case GLFW_NO_WINDOW_CONTEXT: - return "The specified window has no context"; - default: - return "ERROR: UNKNOWN GLFW ERROR"; - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW event API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwInputError(int error, const char* format, ...) -{ - if (_glfwErrorCallback) - { - char buffer[8192]; - const char* description; - - if (format) - { - int count; - va_list vl; - - va_start(vl, format); - count = vsnprintf(buffer, sizeof(buffer), format, vl); - va_end(vl); - - if (count < 0) - buffer[sizeof(buffer) - 1] = '\0'; - - description = buffer; - } - else - description = getErrorString(error); - - _glfwErrorCallback(error, description); - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW public API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI int glfwInit(void) -{ - if (_glfwInitialized) - return GLFW_TRUE; - - memset(&_glfw, 0, sizeof(_glfw)); - - if (!_glfwPlatformInit()) - { - _glfwPlatformTerminate(); - return GLFW_FALSE; - } - - _glfw.monitors = _glfwPlatformGetMonitors(&_glfw.monitorCount); - _glfwInitialized = GLFW_TRUE; - - _glfw.timerOffset = _glfwPlatformGetTimerValue(); - - // Not all window hints have zero as their default value - glfwDefaultWindowHints(); - - return GLFW_TRUE; -} - -GLFWAPI void glfwTerminate(void) +static void terminate(void) { int i; - if (!_glfwInitialized) - return; - memset(&_glfw.callbacks, 0, sizeof(_glfw.callbacks)); while (_glfw.windowListHead) @@ -161,28 +76,227 @@ GLFWAPI void glfwTerminate(void) _GLFWmonitor* monitor = _glfw.monitors[i]; if (monitor->originalRamp.size) _glfwPlatformSetGammaRamp(monitor, &monitor->originalRamp); + _glfwFreeMonitor(monitor); } - _glfwTerminateVulkan(); - - _glfwFreeMonitors(_glfw.monitors, _glfw.monitorCount); + free(_glfw.monitors); _glfw.monitors = NULL; _glfw.monitorCount = 0; + free(_glfw.mappings); + _glfw.mappings = NULL; + _glfw.mappingCount = 0; + + _glfwTerminateVulkan(); _glfwPlatformTerminate(); + _glfw.initialized = GLFW_FALSE; + + while (_glfw.errorListHead) + { + _GLFWerror* error = _glfw.errorListHead; + _glfw.errorListHead = error->next; + free(error); + } + + _glfwPlatformDestroyTls(&_glfw.contextSlot); + _glfwPlatformDestroyTls(&_glfw.errorSlot); + _glfwPlatformDestroyMutex(&_glfw.errorLock); + memset(&_glfw, 0, sizeof(_glfw)); - _glfwInitialized = GLFW_FALSE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +char* _glfw_strdup(const char* source) +{ + const size_t length = strlen(source); + char* result = calloc(length + 1, 1); + strcpy(result, source); + return result; +} + +float _glfw_fminf(float a, float b) +{ + if (a != a) + return b; + else if (b != b) + return a; + else if (a < b) + return a; + else + return b; +} + +float _glfw_fmaxf(float a, float b) +{ + if (a != a) + return b; + else if (b != b) + return a; + else if (a > b) + return a; + else + return b; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +// Notifies shared code of an error +// +void _glfwInputError(int code, const char* format, ...) +{ + _GLFWerror* error; + char description[_GLFW_MESSAGE_SIZE]; + + if (format) + { + va_list vl; + + va_start(vl, format); + vsnprintf(description, sizeof(description), format, vl); + va_end(vl); + + description[sizeof(description) - 1] = '\0'; + } + else + { + if (code == GLFW_NOT_INITIALIZED) + strcpy(description, "The GLFW library is not initialized"); + else if (code == GLFW_NO_CURRENT_CONTEXT) + strcpy(description, "There is no current context"); + else if (code == GLFW_INVALID_ENUM) + strcpy(description, "Invalid argument for enum parameter"); + else if (code == GLFW_INVALID_VALUE) + strcpy(description, "Invalid value for parameter"); + else if (code == GLFW_OUT_OF_MEMORY) + strcpy(description, "Out of memory"); + else if (code == GLFW_API_UNAVAILABLE) + strcpy(description, "The requested API is unavailable"); + else if (code == GLFW_VERSION_UNAVAILABLE) + strcpy(description, "The requested API version is unavailable"); + else if (code == GLFW_PLATFORM_ERROR) + strcpy(description, "A platform-specific error occurred"); + else if (code == GLFW_FORMAT_UNAVAILABLE) + strcpy(description, "The requested format is unavailable"); + else if (code == GLFW_NO_WINDOW_CONTEXT) + strcpy(description, "The specified window has no context"); + else + strcpy(description, "ERROR: UNKNOWN GLFW ERROR"); + } + + if (_glfw.initialized) + { + error = _glfwPlatformGetTls(&_glfw.errorSlot); + if (!error) + { + error = calloc(1, sizeof(_GLFWerror)); + _glfwPlatformSetTls(&_glfw.errorSlot, error); + _glfwPlatformLockMutex(&_glfw.errorLock); + error->next = _glfw.errorListHead; + _glfw.errorListHead = error; + _glfwPlatformUnlockMutex(&_glfw.errorLock); + } + } + else + error = &_glfwMainThreadError; + + error->code = code; + strcpy(error->description, description); + + if (_glfwErrorCallback) + _glfwErrorCallback(code, description); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwInit(void) +{ + if (_glfw.initialized) + return GLFW_TRUE; + + memset(&_glfw, 0, sizeof(_glfw)); + _glfw.hints.init = _glfwInitHints; + + if (!_glfwPlatformInit()) + { + terminate(); + return GLFW_FALSE; + } + + if (!_glfwPlatformCreateMutex(&_glfw.errorLock) || + !_glfwPlatformCreateTls(&_glfw.errorSlot) || + !_glfwPlatformCreateTls(&_glfw.contextSlot)) + { + terminate(); + return GLFW_FALSE; + } + + _glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError); + + _glfw.initialized = GLFW_TRUE; + _glfw.timer.offset = _glfwPlatformGetTimerValue(); + + glfwDefaultWindowHints(); + + { + int i; + + for (i = 0; _glfwDefaultMappings[i]; i++) + { + if (!glfwUpdateGamepadMappings(_glfwDefaultMappings[i])) + { + terminate(); + return GLFW_FALSE; + } + } + } + + return GLFW_TRUE; +} + +GLFWAPI void glfwTerminate(void) +{ + if (!_glfw.initialized) + return; + + terminate(); +} + +GLFWAPI void glfwInitHint(int hint, int value) +{ + switch (hint) + { + case GLFW_JOYSTICK_HAT_BUTTONS: + _glfwInitHints.hatButtons = value; + return; + case GLFW_COCOA_CHDIR_RESOURCES: + _glfwInitHints.ns.chdir = value; + return; + case GLFW_COCOA_MENUBAR: + _glfwInitHints.ns.menubar = value; + return; + } + + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid init hint 0x%08X", hint); } GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) { if (major != NULL) *major = GLFW_VERSION_MAJOR; - if (minor != NULL) *minor = GLFW_VERSION_MINOR; - if (rev != NULL) *rev = GLFW_VERSION_REVISION; } @@ -192,6 +306,30 @@ GLFWAPI const char* glfwGetVersionString(void) return _glfwPlatformGetVersionString(); } +GLFWAPI int glfwGetError(const char** description) +{ + _GLFWerror* error; + int code = GLFW_NO_ERROR; + + if (description) + *description = NULL; + + if (_glfw.initialized) + error = _glfwPlatformGetTls(&_glfw.errorSlot); + else + error = &_glfwMainThreadError; + + if (error) + { + code = error->code; + error->code = GLFW_NO_ERROR; + if (description && code) + *description = error->description; + } + + return code; +} + GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) { _GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun); diff --git a/external/glfw/input.c b/external/glfw/input.c index 37c98c6..a2f42ef 100644 --- a/external/glfw/input.c +++ b/external/glfw/input.c @@ -2,7 +2,7 @@ // GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -29,16 +29,233 @@ #include #include +#include #include +#include // Internal key state used for sticky keys #define _GLFW_STICK 3 +// Internal constants for gamepad mapping source types +#define _GLFW_JOYSTICK_AXIS 1 +#define _GLFW_JOYSTICK_BUTTON 2 +#define _GLFW_JOYSTICK_HATBIT 3 + +// Finds a mapping based on joystick GUID +// +static _GLFWmapping* findMapping(const char* guid) +{ + int i; + + for (i = 0; i < _glfw.mappingCount; i++) + { + if (strcmp(_glfw.mappings[i].guid, guid) == 0) + return _glfw.mappings + i; + } + + return NULL; +} + +// Checks whether a gamepad mapping element is present in the hardware +// +static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e, + const _GLFWjoystick* js) +{ + if (e->type == _GLFW_JOYSTICK_HATBIT && (e->index >> 4) >= js->hatCount) + return GLFW_FALSE; + else if (e->type == _GLFW_JOYSTICK_BUTTON && e->index >= js->buttonCount) + return GLFW_FALSE; + else if (e->type == _GLFW_JOYSTICK_AXIS && e->index >= js->axisCount) + return GLFW_FALSE; + + return GLFW_TRUE; +} + +// Finds a mapping based on joystick GUID and verifies element indices +// +static _GLFWmapping* findValidMapping(const _GLFWjoystick* js) +{ + _GLFWmapping* mapping = findMapping(js->guid); + if (mapping) + { + int i; + + for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) + { + if (!isValidElementForJoystick(mapping->buttons + i, js)) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid button in gamepad mapping %s (%s)", + mapping->guid, + mapping->name); + return NULL; + } + } + + for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) + { + if (!isValidElementForJoystick(mapping->axes + i, js)) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid axis in gamepad mapping %s (%s)", + mapping->guid, + mapping->name); + return NULL; + } + } + } + + return mapping; +} + +// Parses an SDL_GameControllerDB line and adds it to the mapping list +// +static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string) +{ + const char* c = string; + size_t i, length; + struct + { + const char* name; + _GLFWmapelement* element; + } fields[] = + { + { "platform", NULL }, + { "a", mapping->buttons + GLFW_GAMEPAD_BUTTON_A }, + { "b", mapping->buttons + GLFW_GAMEPAD_BUTTON_B }, + { "x", mapping->buttons + GLFW_GAMEPAD_BUTTON_X }, + { "y", mapping->buttons + GLFW_GAMEPAD_BUTTON_Y }, + { "back", mapping->buttons + GLFW_GAMEPAD_BUTTON_BACK }, + { "start", mapping->buttons + GLFW_GAMEPAD_BUTTON_START }, + { "guide", mapping->buttons + GLFW_GAMEPAD_BUTTON_GUIDE }, + { "leftshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_BUMPER }, + { "rightshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER }, + { "leftstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_THUMB }, + { "rightstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_THUMB }, + { "dpup", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_UP }, + { "dpright", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_RIGHT }, + { "dpdown", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_DOWN }, + { "dpleft", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_LEFT }, + { "lefttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_TRIGGER }, + { "righttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER }, + { "leftx", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_X }, + { "lefty", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_Y }, + { "rightx", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_X }, + { "righty", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_Y } + }; + + length = strcspn(c, ","); + if (length != 32 || c[length] != ',') + { + _glfwInputError(GLFW_INVALID_VALUE, NULL); + return GLFW_FALSE; + } + + memcpy(mapping->guid, c, length); + c += length + 1; + + length = strcspn(c, ","); + if (length >= sizeof(mapping->name) || c[length] != ',') + { + _glfwInputError(GLFW_INVALID_VALUE, NULL); + return GLFW_FALSE; + } + + memcpy(mapping->name, c, length); + c += length + 1; + + while (*c) + { + // TODO: Implement output modifiers + if (*c == '+' || *c == '-') + return GLFW_FALSE; + + for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) + { + length = strlen(fields[i].name); + if (strncmp(c, fields[i].name, length) != 0 || c[length] != ':') + continue; + + c += length + 1; + + if (fields[i].element) + { + _GLFWmapelement* e = fields[i].element; + int8_t minimum = -1; + int8_t maximum = 1; + + if (*c == '+') + { + minimum = 0; + c += 1; + } + else if (*c == '-') + { + maximum = 0; + c += 1; + } + + if (*c == 'a') + e->type = _GLFW_JOYSTICK_AXIS; + else if (*c == 'b') + e->type = _GLFW_JOYSTICK_BUTTON; + else if (*c == 'h') + e->type = _GLFW_JOYSTICK_HATBIT; + else + break; + + if (e->type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned long hat = strtoul(c + 1, (char**) &c, 10); + const unsigned long bit = strtoul(c + 1, (char**) &c, 10); + e->index = (uint8_t) ((hat << 4) | bit); + } + else + e->index = (uint8_t) strtoul(c + 1, (char**) &c, 10); + + if (e->type == _GLFW_JOYSTICK_AXIS) + { + e->axisScale = 2 / (maximum - minimum); + e->axisOffset = -(maximum + minimum); + + if (*c == '~') + { + e->axisScale = -e->axisScale; + e->axisOffset = -e->axisOffset; + } + } + } + else + { + length = strlen(_GLFW_PLATFORM_MAPPING_NAME); + if (strncmp(c, _GLFW_PLATFORM_MAPPING_NAME, length) != 0) + return GLFW_FALSE; + } + + break; + } + + c += strcspn(c, ","); + c += strspn(c, ","); + } + + for (i = 0; i < 32; i++) + { + if (mapping->guid[i] >= 'A' && mapping->guid[i] <= 'F') + mapping->guid[i] += 'a' - 'A'; + } + + _glfwPlatformUpdateGamepadGUID(mapping->guid); + return GLFW_TRUE; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW event API ////// ////////////////////////////////////////////////////////////////////////// +// Notifies shared code of a physical key event +// void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods) { if (key >= 0 && key <= GLFW_KEY_LAST) @@ -60,15 +277,24 @@ void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int m action = GLFW_REPEAT; } + if (!window->lockKeyMods) + mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); + if (window->callbacks.key) window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods); } +// Notifies shared code of a Unicode codepoint input event +// The 'plain' parameter determines whether to emit a regular character event +// void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain) { if (codepoint < 32 || (codepoint > 126 && codepoint < 160)) return; + if (!window->lockKeyMods) + mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); + if (window->callbacks.charmods) window->callbacks.charmods((GLFWwindow*) window, codepoint, mods); @@ -79,18 +305,24 @@ void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWb } } +// Notifies shared code of a scroll event +// void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset) { if (window->callbacks.scroll) window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset); } +// Notifies shared code of a mouse button click event +// void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods) { if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST) return; - // Register mouse button action + if (!window->lockKeyMods) + mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); + if (action == GLFW_RELEASE && window->stickyMouseButtons) window->mouseButtons[button] = _GLFW_STICK; else @@ -100,6 +332,9 @@ void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods) window->callbacks.mouseButton((GLFWwindow*) window, button, action, mods); } +// Notifies shared code of a cursor motion event +// The position is specified in content area relative screen coordinates +// void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos) { if (window->virtualCursorPosX == xpos && window->virtualCursorPosY == ypos) @@ -112,22 +347,58 @@ void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos) window->callbacks.cursorPos((GLFWwindow*) window, xpos, ypos); } +// Notifies shared code of a cursor enter/leave event +// void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered) { if (window->callbacks.cursorEnter) window->callbacks.cursorEnter((GLFWwindow*) window, entered); } +// Notifies shared code of files or directories dropped on a window +// void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) { if (window->callbacks.drop) window->callbacks.drop((GLFWwindow*) window, count, paths); } -void _glfwInputJoystickChange(int joy, int event) +// Notifies shared code of a joystick connection or disconnection +// +void _glfwInputJoystick(_GLFWjoystick* js, int event) { + const int jid = (int) (js - _glfw.joysticks); + if (_glfw.callbacks.joystick) - _glfw.callbacks.joystick(joy, event); + _glfw.callbacks.joystick(jid, event); +} + +// Notifies shared code of the new value of a joystick axis +// +void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value) +{ + js->axes[axis] = value; +} + +// Notifies shared code of the new value of a joystick button +// +void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value) +{ + js->buttons[button] = value; +} + +// Notifies shared code of the new value of a joystick hat +// +void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) +{ + const int base = js->buttonCount + hat * 4; + + js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 1] = (value & 0x02) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 2] = (value & 0x04) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 3] = (value & 0x08) ? GLFW_PRESS : GLFW_RELEASE; + + js->hats[hat] = value; } @@ -135,11 +406,61 @@ void _glfwInputJoystickChange(int joy, int event) ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -GLFWbool _glfwIsPrintable(int key) +// Returns an available joystick object with arrays and name allocated +// +_GLFWjoystick* _glfwAllocJoystick(const char* name, + const char* guid, + int axisCount, + int buttonCount, + int hatCount) { - return (key >= GLFW_KEY_APOSTROPHE && key <= GLFW_KEY_WORLD_2) || - (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD) || - key == GLFW_KEY_KP_EQUAL; + int jid; + _GLFWjoystick* js; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (!_glfw.joysticks[jid].present) + break; + } + + if (jid > GLFW_JOYSTICK_LAST) + return NULL; + + js = _glfw.joysticks + jid; + js->present = GLFW_TRUE; + js->name = _glfw_strdup(name); + js->axes = calloc(axisCount, sizeof(float)); + js->buttons = calloc(buttonCount + (size_t) hatCount * 4, 1); + js->hats = calloc(hatCount, 1); + js->axisCount = axisCount; + js->buttonCount = buttonCount; + js->hatCount = hatCount; + + strncpy(js->guid, guid, sizeof(js->guid) - 1); + js->mapping = findValidMapping(js); + + return js; +} + +// Frees arrays and name and flags the joystick object as unused +// +void _glfwFreeJoystick(_GLFWjoystick* js) +{ + free(js->name); + free(js->axes); + free(js->buttons); + free(js->hats); + memset(js, 0, sizeof(_GLFWjoystick)); +} + +// Center the cursor in the content area of the specified window +// +void _glfwCenterCursorInContentArea(_GLFWwindow* window) +{ + int width, height; + + _glfwPlatformGetWindowSize(window, &width, &height); + _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); } @@ -162,10 +483,14 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) return window->stickyKeys; case GLFW_STICKY_MOUSE_BUTTONS: return window->stickyMouseButtons; - default: - _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode); - return 0; + case GLFW_LOCK_KEY_MODS: + return window->lockKeyMods; + case GLFW_RAW_MOUSE_MOTION: + return window->rawMouseMotion; } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); + return 0; } GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) @@ -175,85 +500,128 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) _GLFW_REQUIRE_INIT(); - switch (mode) + if (mode == GLFW_CURSOR) { - case GLFW_CURSOR: + if (value != GLFW_CURSOR_NORMAL && + value != GLFW_CURSOR_HIDDEN && + value != GLFW_CURSOR_DISABLED) { - if (value != GLFW_CURSOR_NORMAL && - value != GLFW_CURSOR_HIDDEN && - value != GLFW_CURSOR_DISABLED) - { - _glfwInputError(GLFW_INVALID_ENUM, - "Invalid cursor mode %i", - value); - return; - } - - if (window->cursorMode == value) - return; - - window->cursorMode = value; - - _glfwPlatformGetCursorPos(window, - &window->virtualCursorPosX, - &window->virtualCursorPosY); - - if (_glfwPlatformWindowFocused(window)) - _glfwPlatformSetCursorMode(window, value); - + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid cursor mode 0x%08X", + value); return; } - case GLFW_STICKY_KEYS: - { - if (window->stickyKeys == value) - return; - - if (!value) - { - int i; - - // Release all sticky keys - for (i = 0; i <= GLFW_KEY_LAST; i++) - { - if (window->keys[i] == _GLFW_STICK) - window->keys[i] = GLFW_RELEASE; - } - } - - window->stickyKeys = value ? GLFW_TRUE : GLFW_FALSE; + if (window->cursorMode == value) return; - } - case GLFW_STICKY_MOUSE_BUTTONS: - { - if (window->stickyMouseButtons == value) - return; + window->cursorMode = value; - if (!value) - { - int i; - - // Release all sticky mouse buttons - for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) - { - if (window->mouseButtons[i] == _GLFW_STICK) - window->mouseButtons[i] = GLFW_RELEASE; - } - } - - window->stickyMouseButtons = value ? GLFW_TRUE : GLFW_FALSE; - return; - } + _glfwPlatformGetCursorPos(window, + &window->virtualCursorPosX, + &window->virtualCursorPosY); + _glfwPlatformSetCursorMode(window, value); } + else if (mode == GLFW_STICKY_KEYS) + { + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->stickyKeys == value) + return; - _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode); + if (!value) + { + int i; + + // Release all sticky keys + for (i = 0; i <= GLFW_KEY_LAST; i++) + { + if (window->keys[i] == _GLFW_STICK) + window->keys[i] = GLFW_RELEASE; + } + } + + window->stickyKeys = value; + } + else if (mode == GLFW_STICKY_MOUSE_BUTTONS) + { + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->stickyMouseButtons == value) + return; + + if (!value) + { + int i; + + // Release all sticky mouse buttons + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == _GLFW_STICK) + window->mouseButtons[i] = GLFW_RELEASE; + } + } + + window->stickyMouseButtons = value; + } + else if (mode == GLFW_LOCK_KEY_MODS) + { + window->lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE; + } + else if (mode == GLFW_RAW_MOUSE_MOTION) + { + if (!_glfwPlatformRawMouseMotionSupported()) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Raw mouse motion is not supported on this system"); + return; + } + + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->rawMouseMotion == value) + return; + + window->rawMouseMotion = value; + _glfwPlatformSetRawMouseMotion(window, value); + } + else + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); +} + +GLFWAPI int glfwRawMouseMotionSupported(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + return _glfwPlatformRawMouseMotionSupported(); } GLFWAPI const char* glfwGetKeyName(int key, int scancode) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfwPlatformGetKeyName(key, scancode); + + if (key != GLFW_KEY_UNKNOWN) + { + if (key != GLFW_KEY_KP_EQUAL && + (key < GLFW_KEY_KP_0 || key > GLFW_KEY_KP_ADD) && + (key < GLFW_KEY_APOSTROPHE || key > GLFW_KEY_WORLD_2)) + { + return NULL; + } + + scancode = _glfwPlatformGetKeyScancode(key); + } + + return _glfwPlatformGetScancodeName(scancode); +} + +GLFWAPI int glfwGetKeyScancode(int key) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(-1); + + if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); + return GLFW_RELEASE; + } + + return _glfwPlatformGetKeyScancode(key); } GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) @@ -391,7 +759,7 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape) shape != GLFW_HRESIZE_CURSOR && shape != GLFW_VRESIZE_CURSOR) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor %i", shape); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape); return NULL; } @@ -540,62 +908,199 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) return cbfun; } -GLFWAPI int glfwJoystickPresent(int joy) +GLFWAPI int glfwJoystickPresent(int jid) { - _GLFW_REQUIRE_INIT_OR_RETURN(0); + _GLFWjoystick* js; - if (joy < 0 || joy > GLFW_JOYSTICK_LAST) + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); - return 0; + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return GLFW_FALSE; } - return _glfwPlatformJoystickPresent(joy); + js = _glfw.joysticks + jid; + if (!js->present) + return GLFW_FALSE; + + return _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); } -GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count) +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) { + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); assert(count != NULL); + *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (joy < 0 || joy > GLFW_JOYSTICK_LAST) + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return NULL; } - return _glfwPlatformGetJoystickAxes(joy, count); + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_AXES)) + return NULL; + + *count = js->axisCount; + return js->axes; } -GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count) +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) { + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); assert(count != NULL); + *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (joy < 0 || joy > GLFW_JOYSTICK_LAST) + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return NULL; } - return _glfwPlatformGetJoystickButtons(joy, count); + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS)) + return NULL; + + if (_glfw.hints.init.hatButtons) + *count = js->buttonCount + js->hatCount * 4; + else + *count = js->buttonCount; + + return js->buttons; } -GLFWAPI const char* glfwGetJoystickName(int joy) +GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) { + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + assert(count != NULL); + + *count = 0; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (joy < 0 || joy > GLFW_JOYSTICK_LAST) + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return NULL; } - return _glfwPlatformGetJoystickName(joy); + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS)) + return NULL; + + *count = js->hatCount; + return js->hats; +} + +GLFWAPI const char* glfwGetJoystickName(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return NULL; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + return NULL; + + return js->name; +} + +GLFWAPI const char* glfwGetJoystickGUID(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return NULL; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + return NULL; + + return js->guid; +} + +GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT(); + + js = _glfw.joysticks + jid; + if (!js->present) + return; + + js->userPointer = pointer; +} + +GLFWAPI void* glfwGetJoystickUserPointer(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + return js->userPointer; } GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) @@ -605,29 +1110,220 @@ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) return cbfun; } +GLFWAPI int glfwUpdateGamepadMappings(const char* string) +{ + int jid; + const char* c = string; + + assert(string != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + while (*c) + { + if ((*c >= '0' && *c <= '9') || + (*c >= 'a' && *c <= 'f') || + (*c >= 'A' && *c <= 'F')) + { + char line[1024]; + + const size_t length = strcspn(c, "\r\n"); + if (length < sizeof(line)) + { + _GLFWmapping mapping = {{0}}; + + memcpy(line, c, length); + line[length] = '\0'; + + if (parseMapping(&mapping, line)) + { + _GLFWmapping* previous = findMapping(mapping.guid); + if (previous) + *previous = mapping; + else + { + _glfw.mappingCount++; + _glfw.mappings = + realloc(_glfw.mappings, + sizeof(_GLFWmapping) * _glfw.mappingCount); + _glfw.mappings[_glfw.mappingCount - 1] = mapping; + } + } + } + + c += length; + } + else + { + c += strcspn(c, "\r\n"); + c += strspn(c, "\r\n"); + } + } + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + js->mapping = findValidMapping(js); + } + + return GLFW_TRUE; +} + +GLFWAPI int glfwJoystickIsGamepad(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return GLFW_FALSE; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return GLFW_FALSE; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + return GLFW_FALSE; + + return js->mapping != NULL; +} + +GLFWAPI const char* glfwGetGamepadName(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return NULL; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + return NULL; + + if (!js->mapping) + return NULL; + + return js->mapping->name; +} + +GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state) +{ + int i; + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + assert(state != NULL); + + memset(state, 0, sizeof(GLFWgamepadstate)); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return GLFW_FALSE; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return GLFW_FALSE; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_ALL)) + return GLFW_FALSE; + + if (!js->mapping) + return GLFW_FALSE; + + for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) + { + const _GLFWmapelement* e = js->mapping->buttons + i; + if (e->type == _GLFW_JOYSTICK_AXIS) + { + const float value = js->axes[e->index] * e->axisScale + e->axisOffset; + // HACK: This should be baked into the value transform + // TODO: Bake into transform when implementing output modifiers + if (e->axisOffset < 0 || (e->axisOffset == 0 && e->axisScale > 0)) + { + if (value >= 0.f) + state->buttons[i] = GLFW_PRESS; + } + else + { + if (value <= 0.f) + state->buttons[i] = GLFW_PRESS; + } + } + else if (e->type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned int hat = e->index >> 4; + const unsigned int bit = e->index & 0xf; + if (js->hats[hat] & bit) + state->buttons[i] = GLFW_PRESS; + } + else if (e->type == _GLFW_JOYSTICK_BUTTON) + state->buttons[i] = js->buttons[e->index]; + } + + for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) + { + const _GLFWmapelement* e = js->mapping->axes + i; + if (e->type == _GLFW_JOYSTICK_AXIS) + { + const float value = js->axes[e->index] * e->axisScale + e->axisOffset; + state->axes[i] = _glfw_fminf(_glfw_fmaxf(value, -1.f), 1.f); + } + else if (e->type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned int hat = e->index >> 4; + const unsigned int bit = e->index & 0xf; + if (js->hats[hat] & bit) + state->axes[i] = 1.f; + else + state->axes[i] = -1.f; + } + else if (e->type == _GLFW_JOYSTICK_BUTTON) + state->axes[i] = js->buttons[e->index] * 2.f - 1.f; + } + + return GLFW_TRUE; +} + GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) { - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); assert(string != NULL); _GLFW_REQUIRE_INIT(); - _glfwPlatformSetClipboardString(window, string); + _glfwPlatformSetClipboardString(string); } GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) { - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfwPlatformGetClipboardString(window); + return _glfwPlatformGetClipboardString(); } GLFWAPI double glfwGetTime(void) { _GLFW_REQUIRE_INIT_OR_RETURN(0.0); - return (double) (_glfwPlatformGetTimerValue() - _glfw.timerOffset) / + return (double) (_glfwPlatformGetTimerValue() - _glfw.timer.offset) / _glfwPlatformGetTimerFrequency(); } @@ -641,7 +1337,7 @@ GLFWAPI void glfwSetTime(double time) return; } - _glfw.timerOffset = _glfwPlatformGetTimerValue() - + _glfw.timer.offset = _glfwPlatformGetTimerValue() - (uint64_t) (time * _glfwPlatformGetTimerFrequency()); } @@ -656,4 +1352,3 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void) _GLFW_REQUIRE_INIT_OR_RETURN(0); return _glfwPlatformGetTimerFrequency(); } - diff --git a/external/glfw/internal.h b/external/glfw/internal.h index 73d8927..02b05b4 100644 --- a/external/glfw/internal.h +++ b/external/glfw/internal.h @@ -2,7 +2,7 @@ // GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,9 +25,7 @@ // //======================================================================== -#ifndef _glfw3_internal_h_ -#define _glfw3_internal_h_ - +#pragma once #if defined(_GLFW_USE_CONFIG_H) #include "glfw_config.h" @@ -37,6 +35,8 @@ defined(GLFW_INCLUDE_ES1) || \ defined(GLFW_INCLUDE_ES2) || \ defined(GLFW_INCLUDE_ES3) || \ + defined(GLFW_INCLUDE_ES31) || \ + defined(GLFW_INCLUDE_ES32) || \ defined(GLFW_INCLUDE_NONE) || \ defined(GLFW_INCLUDE_GLEXT) || \ defined(GLFW_INCLUDE_GLU) || \ @@ -48,8 +48,20 @@ #define GLFW_INCLUDE_NONE #include "glfw3.h" +#define _GLFW_INSERT_FIRST 0 +#define _GLFW_INSERT_LAST 1 + +#define _GLFW_POLL_PRESENCE 0 +#define _GLFW_POLL_AXES 1 +#define _GLFW_POLL_BUTTONS 2 +#define _GLFW_POLL_ALL (_GLFW_POLL_AXES | _GLFW_POLL_BUTTONS) + +#define _GLFW_MESSAGE_SIZE 1024 + typedef int GLFWbool; +typedef struct _GLFWerror _GLFWerror; +typedef struct _GLFWinitconfig _GLFWinitconfig; typedef struct _GLFWwndconfig _GLFWwndconfig; typedef struct _GLFWctxconfig _GLFWctxconfig; typedef struct _GLFWfbconfig _GLFWfbconfig; @@ -58,6 +70,11 @@ typedef struct _GLFWwindow _GLFWwindow; typedef struct _GLFWlibrary _GLFWlibrary; typedef struct _GLFWmonitor _GLFWmonitor; typedef struct _GLFWcursor _GLFWcursor; +typedef struct _GLFWmapelement _GLFWmapelement; +typedef struct _GLFWmapping _GLFWmapping; +typedef struct _GLFWjoystick _GLFWjoystick; +typedef struct _GLFWtls _GLFWtls; +typedef struct _GLFWmutex _GLFWmutex; typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); @@ -69,6 +86,7 @@ typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*); #define GL_VERSION 0x1f02 #define GL_NONE 0 #define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_UNSIGNED_BYTE 0x1401 #define GL_EXTENSIONS 0x1f03 #define GL_NUM_EXTENSIONS 0x821d #define GL_CONTEXT_FLAGS 0x821e @@ -108,8 +126,8 @@ typedef enum VkStructureType VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, - VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, + VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkStructureType; @@ -169,44 +187,12 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void); #include "x11_platform.h" #elif defined(_GLFW_WAYLAND) #include "wl_platform.h" -#elif defined(_GLFW_MIR) - #include "mir_platform.h" +#elif defined(_GLFW_OSMESA) + #include "null_platform.h" #else #error "No supported window creation API selected" #endif - -//======================================================================== -// Doxygen group definitions -//======================================================================== - -/*! @defgroup platform Platform interface - * @brief The interface implemented by the platform-specific code. - * - * The platform API is the interface exposed by the platform-specific code for - * each platform and is called by the shared code of the public API It mirrors - * the public API except it uses objects instead of handles. - */ -/*! @defgroup event Event interface - * @brief The interface used by the platform-specific code to report events. - * - * The event API is used by the platform-specific code to notify the shared - * code of events that can be translated into state changes and/or callback - * calls. - */ -/*! @defgroup utility Utility functions - * @brief Various utility functions for internal use. - * - * These functions are shared code and may be used by any part of GLFW - * Each platform may add its own utility functions, but those must only be - * called by the platform-specific code - */ - - -//======================================================================== -// Helper macros -//======================================================================== - // Constructs a version number string from the public header macros #define _GLFW_CONCAT_VERSION(m, n, r) #m "." #n "." #r #define _GLFW_MAKE_VERSION(m, n, r) _GLFW_CONCAT_VERSION(m, n, r) @@ -216,13 +202,13 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void); // Checks for whether the library has been initialized #define _GLFW_REQUIRE_INIT() \ - if (!_glfwInitialized) \ + if (!_glfw.initialized) \ { \ _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ return; \ } #define _GLFW_REQUIRE_INIT_OR_RETURN(x) \ - if (!_glfwInitialized) \ + if (!_glfw.initialized) \ { \ _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ return x; \ @@ -237,17 +223,34 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void); y = t; \ } +// Per-thread error structure +// +struct _GLFWerror +{ + _GLFWerror* next; + int code; + char description[_GLFW_MESSAGE_SIZE]; +}; -//======================================================================== -// Platform-independent structures -//======================================================================== +// Initialization configuration +// +// Parameters relating to the initialization of the library +// +struct _GLFWinitconfig +{ + GLFWbool hatButtons; + struct { + GLFWbool menubar; + GLFWbool chdir; + } ns; +}; -/*! @brief Window configuration. - * - * Parameters relating to the creation of the window but not directly related - * to the framebuffer. This is used to pass window creation parameters from - * shared code to the platform API. - */ +// Window configuration +// +// Parameters relating to the creation of the window but not directly related +// to the framebuffer. This is used to pass window creation parameters from +// shared code to the platform API. +// struct _GLFWwndconfig { int width; @@ -260,14 +263,25 @@ struct _GLFWwndconfig GLFWbool autoIconify; GLFWbool floating; GLFWbool maximized; + GLFWbool centerCursor; + GLFWbool focusOnShow; + GLFWbool scaleToMonitor; + struct { + GLFWbool retina; + char frameName[256]; + } ns; + struct { + char className[256]; + char instanceName[256]; + } x11; }; -/*! @brief Context configuration. - * - * Parameters relating to the creation of the context but not directly related - * to the framebuffer. This is used to pass context creation parameters from - * shared code to the platform API. - */ +// Context configuration +// +// Parameters relating to the creation of the context but not directly related +// to the framebuffer. This is used to pass context creation parameters from +// shared code to the platform API. +// struct _GLFWctxconfig { int client; @@ -281,16 +295,19 @@ struct _GLFWctxconfig int robustness; int release; _GLFWwindow* share; + struct { + GLFWbool offline; + } nsgl; }; -/*! @brief Framebuffer configuration. - * - * This describes buffers and their sizes. It also contains - * a platform-specific ID used to map back to the backend API object. - * - * It is used to pass framebuffer parameters from shared code to the platform - * API and also to enumerate and select available framebuffer configs. - */ +// Framebuffer configuration +// +// This describes buffers and their sizes. It also contains +// a platform-specific ID used to map back to the backend API object. +// +// It is used to pass framebuffer parameters from shared code to the platform +// API and also to enumerate and select available framebuffer configs. +// struct _GLFWfbconfig { int redBits; @@ -308,11 +325,12 @@ struct _GLFWfbconfig int samples; GLFWbool sRGB; GLFWbool doublebuffer; + GLFWbool transparent; uintptr_t handle; }; -/*! @brief Context structure. - */ +// Context structure +// struct _GLFWcontext { int client; @@ -338,10 +356,12 @@ struct _GLFWcontext _GLFW_PLATFORM_CONTEXT_STATE; // This is defined in egl_context.h _GLFW_EGL_CONTEXT_STATE; + // This is defined in osmesa_context.h + _GLFW_OSMESA_CONTEXT_STATE; }; -/*! @brief Window and context structure. - */ +// Window and context structure +// struct _GLFWwindow { struct _GLFWwindow* next; @@ -351,7 +371,8 @@ struct _GLFWwindow GLFWbool decorated; GLFWbool autoIconify; GLFWbool floating; - GLFWbool closed; + GLFWbool focusOnShow; + GLFWbool shouldClose; void* userPointer; GLFWvidmode videoMode; _GLFWmonitor* monitor; @@ -363,11 +384,13 @@ struct _GLFWwindow GLFWbool stickyKeys; GLFWbool stickyMouseButtons; + GLFWbool lockKeyMods; int cursorMode; char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1]; char keys[GLFW_KEY_LAST + 1]; // Virtual cursor position when cursor is disabled double virtualCursorPosX, virtualCursorPosY; + GLFWbool rawMouseMotion; _GLFWcontext context; @@ -378,7 +401,9 @@ struct _GLFWwindow GLFWwindowrefreshfun refresh; GLFWwindowfocusfun focus; GLFWwindowiconifyfun iconify; + GLFWwindowmaximizefun maximize; GLFWframebuffersizefun fbsize; + GLFWwindowcontentscalefun scale; GLFWmousebuttonfun mouseButton; GLFWcursorposfun cursorPos; GLFWcursorenterfun cursorEnter; @@ -393,11 +418,12 @@ struct _GLFWwindow _GLFW_PLATFORM_WINDOW_STATE; }; -/*! @brief Monitor structure. - */ +// Monitor structure +// struct _GLFWmonitor { char* name; + void* userPointer; // Physical dimensions in millimeters. int widthMM, heightMM; @@ -416,8 +442,8 @@ struct _GLFWmonitor _GLFW_PLATFORM_MONITOR_STATE; }; -/*! @brief Cursor structure - */ +// Cursor structure +// struct _GLFWcursor { _GLFWcursor* next; @@ -426,41 +452,116 @@ struct _GLFWcursor _GLFW_PLATFORM_CURSOR_STATE; }; -/*! @brief Library global data. - */ +// Gamepad mapping element structure +// +struct _GLFWmapelement +{ + uint8_t type; + uint8_t index; + int8_t axisScale; + int8_t axisOffset; +}; + +// Gamepad mapping structure +// +struct _GLFWmapping +{ + char name[128]; + char guid[33]; + _GLFWmapelement buttons[15]; + _GLFWmapelement axes[6]; +}; + +// Joystick structure +// +struct _GLFWjoystick +{ + GLFWbool present; + float* axes; + int axisCount; + unsigned char* buttons; + int buttonCount; + unsigned char* hats; + int hatCount; + char* name; + void* userPointer; + char guid[33]; + _GLFWmapping* mapping; + + // This is defined in the joystick API's joystick.h + _GLFW_PLATFORM_JOYSTICK_STATE; +}; + +// Thread local storage structure +// +struct _GLFWtls +{ + // This is defined in the platform's thread.h + _GLFW_PLATFORM_TLS_STATE; +}; + +// Mutex structure +// +struct _GLFWmutex +{ + // This is defined in the platform's thread.h + _GLFW_PLATFORM_MUTEX_STATE; +}; + +// Library global data +// struct _GLFWlibrary { + GLFWbool initialized; + struct { + _GLFWinitconfig init; _GLFWfbconfig framebuffer; _GLFWwndconfig window; _GLFWctxconfig context; int refreshRate; } hints; + _GLFWerror* errorListHead; _GLFWcursor* cursorListHead; - _GLFWwindow* windowListHead; _GLFWmonitor** monitors; int monitorCount; - uint64_t timerOffset; + _GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1]; + _GLFWmapping* mappings; + int mappingCount; + + _GLFWtls errorSlot; + _GLFWtls contextSlot; + _GLFWmutex errorLock; + + struct { + uint64_t offset; + // This is defined in the platform's time.h + _GLFW_PLATFORM_LIBRARY_TIMER_STATE; + } timer; struct { GLFWbool available; void* handle; - char** extensions; - uint32_t extensionCount; + char* extensions[2]; #if !defined(_GLFW_VULKAN_STATIC) PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; PFN_vkGetInstanceProcAddr GetInstanceProcAddr; #endif GLFWbool KHR_surface; +#if defined(_GLFW_WIN32) GLFWbool KHR_win32_surface; +#elif defined(_GLFW_COCOA) + GLFWbool MVK_macos_surface; +#elif defined(_GLFW_X11) GLFWbool KHR_xlib_surface; GLFWbool KHR_xcb_surface; +#elif defined(_GLFW_WAYLAND) GLFWbool KHR_wayland_surface; - GLFWbool KHR_mir_surface; +#endif } vk; struct { @@ -472,584 +573,204 @@ struct _GLFWlibrary _GLFW_PLATFORM_LIBRARY_WINDOW_STATE; // This is defined in the context API's context.h _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE; - // This is defined in the platform's time.h - _GLFW_PLATFORM_LIBRARY_TIME_STATE; // This is defined in the platform's joystick.h _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE; - // This is defined in the platform's tls.h - _GLFW_PLATFORM_LIBRARY_TLS_STATE; // This is defined in egl_context.h _GLFW_EGL_LIBRARY_CONTEXT_STATE; + // This is defined in osmesa_context.h + _GLFW_OSMESA_LIBRARY_CONTEXT_STATE; }; - -//======================================================================== // Global state shared between compilation units of GLFW -//======================================================================== - -/*! @brief Flag indicating whether GLFW has been successfully initialized. - */ -extern GLFWbool _glfwInitialized; - -/*! @brief All global data protected by @ref _glfwInitialized. - * This should only be touched after a call to @ref glfwInit that has not been - * followed by a call to @ref glfwTerminate. - */ +// extern _GLFWlibrary _glfw; -//======================================================================== -// Platform API functions -//======================================================================== +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// -/*! @brief Initializes the platform-specific part of the library. - * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an error occurred. - * @ingroup platform - */ int _glfwPlatformInit(void); - -/*! @brief Terminates the platform-specific part of the library. - * @ingroup platform - */ void _glfwPlatformTerminate(void); - -/*! @copydoc glfwGetVersionString - * @ingroup platform - * - * @note The returned string must be available for the duration of the program. - * - * @note The returned string must not change for the duration of the program. - */ const char* _glfwPlatformGetVersionString(void); -/*! @copydoc glfwGetCursorPos - * @ingroup platform - */ void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos); - -/*! @copydoc glfwSetCursorPos - * @ingroup platform - */ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos); - -/*! @brief Sets the specified cursor mode of the specified window. - * @param[in] window The window whose cursor mode to set. - * @ingroup platform - */ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode); +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled); +GLFWbool _glfwPlatformRawMouseMotionSupported(void); +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, int xhot, int yhot); +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape); +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor); +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor); -/*! @copydoc glfwGetKeyName - * @ingroup platform - */ -const char* _glfwPlatformGetKeyName(int key, int scancode); +const char* _glfwPlatformGetScancodeName(int scancode); +int _glfwPlatformGetKeyScancode(int key); -/*! @copydoc glfwGetMonitors - * @ingroup platform - */ -_GLFWmonitor** _glfwPlatformGetMonitors(int* count); - -/*! @brief Checks whether two monitor objects represent the same monitor. - * - * @param[in] first The first monitor. - * @param[in] second The second monitor. - * @return @c GLFW_TRUE if the monitor objects represent the same monitor, or - * @c GLFW_FALSE otherwise. - * @ingroup platform - */ -GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second); - -/*! @copydoc glfwGetMonitorPos - * @ingroup platform - */ +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor); void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos); - -/*! @copydoc glfwGetVideoModes - * @ingroup platform - */ +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale); +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height); GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); - -/*! @ingroup platform - */ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); - -/*! @copydoc glfwGetGammaRamp - * @ingroup platform - */ -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); - -/*! @copydoc glfwSetGammaRamp - * @ingroup platform - */ +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); -/*! @copydoc glfwSetClipboardString - * @ingroup platform - */ -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string); +void _glfwPlatformSetClipboardString(const char* string); +const char* _glfwPlatformGetClipboardString(void); -/*! @copydoc glfwGetClipboardString - * @ingroup platform - * - * @note The returned string must be valid until the next call to @ref - * _glfwPlatformGetClipboardString or @ref _glfwPlatformSetClipboardString. - */ -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window); +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode); +void _glfwPlatformUpdateGamepadGUID(char* guid); -/*! @copydoc glfwJoystickPresent - * @ingroup platform - */ -int _glfwPlatformJoystickPresent(int joy); - -/*! @copydoc glfwGetJoystickAxes - * @ingroup platform - */ -const float* _glfwPlatformGetJoystickAxes(int joy, int* count); - -/*! @copydoc glfwGetJoystickButtons - * @ingroup platform - */ -const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count); - -/*! @copydoc glfwGetJoystickName - * @ingroup platform - */ -const char* _glfwPlatformGetJoystickName(int joy); - -/*! @copydoc glfwGetTimerValue - * @ingroup platform - */ uint64_t _glfwPlatformGetTimerValue(void); - -/*! @copydoc glfwGetTimerFrequency - * @ingroup platform - */ uint64_t _glfwPlatformGetTimerFrequency(void); -/*! @ingroup platform - */ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); - -/*! @ingroup platform - */ void _glfwPlatformDestroyWindow(_GLFWwindow* window); - -/*! @copydoc glfwSetWindowTitle - * @ingroup platform - */ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title); - -/*! @copydoc glfwSetWindowIcon - * @ingroup platform - */ -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images); - -/*! @copydoc glfwGetWindowPos - * @ingroup platform - */ +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images); void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos); - -/*! @copydoc glfwSetWindowPos - * @ingroup platform - */ void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos); - -/*! @copydoc glfwGetWindowSize - * @ingroup platform - */ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height); - -/*! @copydoc glfwSetWindowSize - * @ingroup platform - */ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height); - -/*! @copydoc glfwSetWindowSizeLimits - * @ingroup platform - */ -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); - -/*! @copydoc glfwSetWindowAspectRatio - * @ingroup platform - */ +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight); void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom); - -/*! @copydoc glfwGetFramebufferSize - * @ingroup platform - */ void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); - -/*! @copydoc glfwGetWindowFrameSize - * @ingroup platform - */ -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); - -/*! @copydoc glfwIconifyWindow - * @ingroup platform - */ +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom); +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale); void _glfwPlatformIconifyWindow(_GLFWwindow* window); - -/*! @copydoc glfwRestoreWindow - * @ingroup platform - */ void _glfwPlatformRestoreWindow(_GLFWwindow* window); - -/*! @copydoc glfwMaximizeWindow - * @ingroup platform - */ void _glfwPlatformMaximizeWindow(_GLFWwindow* window); - -/*! @copydoc glfwShowWindow - * @ingroup platform - */ void _glfwPlatformShowWindow(_GLFWwindow* window); - -/*! @copydoc glfwHideWindow - * @ingroup platform - */ void _glfwPlatformHideWindow(_GLFWwindow* window); - -/*! @copydoc glfwFocusWindow - * @ingroup platform - */ +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window); void _glfwPlatformFocusWindow(_GLFWwindow* window); - -/*! @copydoc glfwSetWindowMonitor - * @ingroup platform - */ -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); - -/*! @brief Returns whether the window is focused. - * @ingroup platform - */ +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, + int xpos, int ypos, int width, int height, + int refreshRate); int _glfwPlatformWindowFocused(_GLFWwindow* window); - -/*! @brief Returns whether the window is iconified. - * @ingroup platform - */ int _glfwPlatformWindowIconified(_GLFWwindow* window); - -/*! @brief Returns whether the window is visible. - * @ingroup platform - */ int _glfwPlatformWindowVisible(_GLFWwindow* window); - -/*! @brief Returns whether the window is maximized. - * @ingroup platform - */ int _glfwPlatformWindowMaximized(_GLFWwindow* window); +int _glfwPlatformWindowHovered(_GLFWwindow* window); +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window); +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window); +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity); -/*! @copydoc glfwPollEvents - * @ingroup platform - */ void _glfwPlatformPollEvents(void); - -/*! @copydoc glfwWaitEvents - * @ingroup platform - */ void _glfwPlatformWaitEvents(void); - -/*! @copydoc glfwWaitEventsTimeout - * @ingroup platform - */ void _glfwPlatformWaitEventsTimeout(double timeout); - -/*! @copydoc glfwPostEmptyEvent - * @ingroup platform - */ void _glfwPlatformPostEmptyEvent(void); -/*! @ingroup platform - */ -void _glfwPlatformSetCurrentContext(_GLFWwindow* context); +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions); +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily); +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface); -/*! @copydoc glfwGetCurrentContext - * @ingroup platform - */ -_GLFWwindow* _glfwPlatformGetCurrentContext(void); +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls); +void _glfwPlatformDestroyTls(_GLFWtls* tls); +void* _glfwPlatformGetTls(_GLFWtls* tls); +void _glfwPlatformSetTls(_GLFWtls* tls, void* value); -/*! @copydoc glfwCreateCursor - * @ingroup platform - */ -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot); - -/*! @copydoc glfwCreateStandardCursor - * @ingroup platform - */ -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape); - -/*! @copydoc glfwDestroyCursor - * @ingroup platform - */ -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor); - -/*! @copydoc glfwSetCursor - * @ingroup platform - */ -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor); - -/*! @ingroup platform - */ -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count); - -/*! @ingroup platform - */ -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); - -/*! @ingroup platform - */ -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); +GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex); +void _glfwPlatformDestroyMutex(_GLFWmutex* mutex); +void _glfwPlatformLockMutex(_GLFWmutex* mutex); +void _glfwPlatformUnlockMutex(_GLFWmutex* mutex); -//======================================================================== -// Event API functions -//======================================================================== +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// -/*! @brief Notifies shared code of a window focus event. - * @param[in] window The window that received the event. - * @param[in] focused `GLFW_TRUE` if the window received focus, or `GLFW_FALSE` - * if it lost focus. - * @ingroup event - */ void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused); - -/*! @brief Notifies shared code of a window movement event. - * @param[in] window The window that received the event. - * @param[in] xpos The new x-coordinate of the client area of the window. - * @param[in] ypos The new y-coordinate of the client area of the window. - * @ingroup event - */ void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos); - -/*! @brief Notifies shared code of a window resize event. - * @param[in] window The window that received the event. - * @param[in] width The new width of the client area of the window. - * @param[in] height The new height of the client area of the window. - * @ingroup event - */ void _glfwInputWindowSize(_GLFWwindow* window, int width, int height); - -/*! @brief Notifies shared code of a framebuffer resize event. - * @param[in] window The window that received the event. - * @param[in] width The new width, in pixels, of the framebuffer. - * @param[in] height The new height, in pixels, of the framebuffer. - * @ingroup event - */ void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height); - -/*! @brief Notifies shared code of a window iconification event. - * @param[in] window The window that received the event. - * @param[in] iconified `GLFW_TRUE` if the window was iconified, or - * `GLFW_FALSE` if it was restored. - * @ingroup event - */ +void _glfwInputWindowContentScale(_GLFWwindow* window, + float xscale, float yscale); void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified); - -/*! @brief Notifies shared code of a window damage event. - * @param[in] window The window that received the event. - */ +void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized); void _glfwInputWindowDamage(_GLFWwindow* window); - -/*! @brief Notifies shared code of a window close request event - * @param[in] window The window that received the event. - * @ingroup event - */ void _glfwInputWindowCloseRequest(_GLFWwindow* window); +void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor); -void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor); - -/*! @brief Notifies shared code of a physical key event. - * @param[in] window The window that received the event. - * @param[in] key The key that was pressed or released. - * @param[in] scancode The system-specific scan code of the key. - * @param[in] action @ref GLFW_PRESS or @ref GLFW_RELEASE. - * @param[in] mods The modifiers pressed when the event was generated. - * @ingroup event - */ -void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods); - -/*! @brief Notifies shared code of a Unicode character input event. - * @param[in] window The window that received the event. - * @param[in] codepoint The Unicode code point of the input character. - * @param[in] mods Bit field describing which modifier keys were held down. - * @param[in] plain `GLFW_TRUE` if the character is regular text input, or - * `GLFW_FALSE` otherwise. - * @ingroup event - */ -void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain); - -/*! @brief Notifies shared code of a scroll event. - * @param[in] window The window that received the event. - * @param[in] xoffset The scroll offset along the x-axis. - * @param[in] yoffset The scroll offset along the y-axis. - * @ingroup event - */ +void _glfwInputKey(_GLFWwindow* window, + int key, int scancode, int action, int mods); +void _glfwInputChar(_GLFWwindow* window, + unsigned int codepoint, int mods, GLFWbool plain); void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset); - -/*! @brief Notifies shared code of a mouse button click event. - * @param[in] window The window that received the event. - * @param[in] button The button that was pressed or released. - * @param[in] action @ref GLFW_PRESS or @ref GLFW_RELEASE. - * @ingroup event - */ void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods); - -/*! @brief Notifies shared code of a cursor motion event. - * @param[in] window The window that received the event. - * @param[in] xpos The new x-coordinate of the cursor, relative to the left - * edge of the client area of the window. - * @param[in] ypos The new y-coordinate of the cursor, relative to the top edge - * of the client area of the window. - * @ingroup event - */ void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos); - -/*! @brief Notifies shared code of a cursor enter/leave event. - * @param[in] window The window that received the event. - * @param[in] entered `GLFW_TRUE` if the cursor entered the client area of the - * window, or `GLFW_FALSE` if it left it. - * @ingroup event - */ void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered); +void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); +void _glfwInputJoystick(_GLFWjoystick* js, int event); +void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value); +void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value); +void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value); -/*! @ingroup event - */ -void _glfwInputMonitorChange(void); +void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement); +void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window); -/*! @ingroup event - */ -void _glfwInputMonitorWindowChange(_GLFWmonitor* monitor, _GLFWwindow* window); - -/*! @brief Notifies shared code of an error. - * @param[in] error The error code most suitable for the error. - * @param[in] format The `printf` style format string of the error - * description. - * @ingroup event - */ #if defined(__GNUC__) -void _glfwInputError(int error, const char* format, ...) __attribute__((format(printf, 2, 3))); +void _glfwInputError(int code, const char* format, ...) + __attribute__((format(printf, 2, 3))); #else -void _glfwInputError(int error, const char* format, ...); +void _glfwInputError(int code, const char* format, ...); #endif -/*! @brief Notifies dropped object over window. - * @param[in] window The window that received the event. - * @param[in] count The number of dropped objects. - * @param[in] names The names of the dropped objects. - * @ingroup event - */ -void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); -/*! @brief Notifies shared code of a joystick connection/disconnection event. - * @param[in] joy The joystick that was connected or disconnected. - * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. - * @ingroup event - */ -void _glfwInputJoystickChange(int joy, int event); +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// - -//======================================================================== -// Utility functions -//======================================================================== - -/*! @ingroup utility - */ -const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, - const GLFWvidmode* desired); - -/*! @brief Performs lexical comparison between two @ref GLFWvidmode structures. - * @ingroup utility - */ -int _glfwCompareVideoModes(const GLFWvidmode* first, const GLFWvidmode* second); - -/*! @brief Splits a color depth into red, green and blue bit depths. - * @ingroup utility - */ -void _glfwSplitBPP(int bpp, int* red, int* green, int* blue); - -/*! @brief Searches an extension string for the specified extension. - * @param[in] string The extension string to search. - * @param[in] extensions The extension to search for. - * @return `GLFW_TRUE` if the extension was found, or `GLFW_FALSE` otherwise. - * @ingroup utility - */ GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions); - -/*! @brief Chooses the framebuffer config that best matches the desired one. - * @param[in] desired The desired framebuffer config. - * @param[in] alternatives The framebuffer configs supported by the system. - * @param[in] count The number of entries in the alternatives array. - * @return The framebuffer config most closely matching the desired one, or @c - * NULL if none fulfilled the hard constraints of the desired values. - * @ingroup utility - */ const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, const _GLFWfbconfig* alternatives, unsigned int count); - -/*! @brief Retrieves the attributes of the current context. - * @param[in] ctxconfig The desired context attributes. - * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if the context is - * unusable. - * @ingroup utility - */ -GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig); - -/*! @brief Checks whether the desired context attributes are valid. - * @param[in] ctxconfig The context attributes to check. - * @return `GLFW_TRUE` if the context attributes are valid, or `GLFW_FALSE` - * otherwise. - * @ingroup utility - * - * This function checks things like whether the specified client API version - * exists and whether all relevant options have supported and non-conflicting - * values. - */ +GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig); GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig); -/*! @ingroup utility - */ -void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size); - -/*! @ingroup utility - */ -void _glfwFreeGammaArrays(GLFWgammaramp* ramp); - -/*! @brief Allocates and returns a monitor object with the specified name - * and dimensions. - * @param[in] name The name of the monitor. - * @param[in] widthMM The width, in mm, of the monitor's display area. - * @param[in] heightMM The height, in mm, of the monitor's display area. - * @return The newly created object. - * @ingroup utility - */ +const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, + const GLFWvidmode* desired); +int _glfwCompareVideoModes(const GLFWvidmode* first, const GLFWvidmode* second); _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM); - -/*! @brief Frees a monitor object and any data associated with it. - * @ingroup utility - */ void _glfwFreeMonitor(_GLFWmonitor* monitor); +void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size); +void _glfwFreeGammaArrays(GLFWgammaramp* ramp); +void _glfwSplitBPP(int bpp, int* red, int* green, int* blue); -/*! @ingroup utility - */ -void _glfwFreeMonitors(_GLFWmonitor** monitors, int count); +_GLFWjoystick* _glfwAllocJoystick(const char* name, + const char* guid, + int axisCount, + int buttonCount, + int hatCount); +void _glfwFreeJoystick(_GLFWjoystick* js); +void _glfwCenterCursorInContentArea(_GLFWwindow* window); -/*! @ingroup utility - */ -GLFWbool _glfwIsPrintable(int key); - -/*! @ingroup utility - */ -GLFWbool _glfwInitVulkan(void); - -/*! @ingroup utility - */ +GLFWbool _glfwInitVulkan(int mode); void _glfwTerminateVulkan(void); - -/*! @ingroup utility - */ const char* _glfwGetVulkanResultString(VkResult result); -#endif // _glfw3_internal_h_ +char* _glfw_strdup(const char* source); +float _glfw_fminf(float a, float b); +float _glfw_fmaxf(float a, float b); + diff --git a/external/glfw/linux_joystick.c b/external/glfw/linux_joystick.c index ad24995..42e457f 100644 --- a/external/glfw/linux_joystick.c +++ b/external/glfw/linux_joystick.c @@ -2,7 +2,7 @@ // GLFW 3.3 Linux - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -27,9 +27,6 @@ #include "internal.h" -#if defined(__linux__) -#include - #include #include #include @@ -40,129 +37,225 @@ #include #include #include -#endif // __linux__ +#ifndef SYN_DROPPED // < v2.6.39 kernel headers +// Workaround for CentOS-6, which is supported till 2020-11-30, but still on v2.6.32 +#define SYN_DROPPED 3 +#endif + +// Apply an EV_KEY event to the specified joystick +// +static void handleKeyEvent(_GLFWjoystick* js, int code, int value) +{ + _glfwInputJoystickButton(js, + js->linjs.keyMap[code - BTN_MISC], + value ? GLFW_PRESS : GLFW_RELEASE); +} + +// Apply an EV_ABS event to the specified joystick +// +static void handleAbsEvent(_GLFWjoystick* js, int code, int value) +{ + const int index = js->linjs.absMap[code]; + + if (code >= ABS_HAT0X && code <= ABS_HAT3Y) + { + static const char stateMap[3][3] = + { + { GLFW_HAT_CENTERED, GLFW_HAT_UP, GLFW_HAT_DOWN }, + { GLFW_HAT_LEFT, GLFW_HAT_LEFT_UP, GLFW_HAT_LEFT_DOWN }, + { GLFW_HAT_RIGHT, GLFW_HAT_RIGHT_UP, GLFW_HAT_RIGHT_DOWN }, + }; + + const int hat = (code - ABS_HAT0X) / 2; + const int axis = (code - ABS_HAT0X) % 2; + int* state = js->linjs.hats[hat]; + + // NOTE: Looking at several input drivers, it seems all hat events use + // -1 for left / up, 0 for centered and 1 for right / down + if (value == 0) + state[axis] = 0; + else if (value < 0) + state[axis] = 1; + else if (value > 0) + state[axis] = 2; + + _glfwInputJoystickHat(js, index, stateMap[state[0]][state[1]]); + } + else + { + const struct input_absinfo* info = &js->linjs.absInfo[code]; + float normalized = value; + + const int range = info->maximum - info->minimum; + if (range) + { + // Normalize to 0.0 -> 1.0 + normalized = (normalized - info->minimum) / range; + // Normalize to -1.0 -> 1.0 + normalized = normalized * 2.0f - 1.0f; + } + + _glfwInputJoystickAxis(js, index, normalized); + } +} + +// Poll state of absolute axes +// +static void pollAbsState(_GLFWjoystick* js) +{ + int code; + + for (code = 0; code < ABS_CNT; code++) + { + if (js->linjs.absMap[code] < 0) + continue; + + struct input_absinfo* info = &js->linjs.absInfo[code]; + + if (ioctl(js->linjs.fd, EVIOCGABS(code), info) < 0) + continue; + + handleAbsEvent(js, code, info->value); + } +} + +#define isBitSet(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8))) // Attempt to open the specified joystick device // -#if defined(__linux__) static GLFWbool openJoystickDevice(const char* path) { - char axisCount, buttonCount; + int jid, code; char name[256] = ""; - int joy, fd, version; - _GLFWjoystickLinux* js; + char guid[33] = ""; + char evBits[(EV_CNT + 7) / 8] = {0}; + char keyBits[(KEY_CNT + 7) / 8] = {0}; + char absBits[(ABS_CNT + 7) / 8] = {0}; + int axisCount = 0, buttonCount = 0, hatCount = 0; + struct input_id id; + _GLFWjoystickLinux linjs = {0}; + _GLFWjoystick* js = NULL; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (!_glfw.linux_js.js[joy].present) + if (!_glfw.joysticks[jid].present) continue; - - if (strcmp(_glfw.linux_js.js[joy].path, path) == 0) + if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0) return GLFW_FALSE; } - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - { - if (!_glfw.linux_js.js[joy].present) - break; - } - - if (joy > GLFW_JOYSTICK_LAST) + linjs.fd = open(path, O_RDONLY | O_NONBLOCK); + if (linjs.fd == -1) return GLFW_FALSE; - fd = open(path, O_RDONLY | O_NONBLOCK); - if (fd == -1) - return GLFW_FALSE; - - // Verify that the joystick driver version is at least 1.0 - ioctl(fd, JSIOCGVERSION, &version); - if (version < 0x010000) + if (ioctl(linjs.fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 || + ioctl(linjs.fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 || + ioctl(linjs.fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 || + ioctl(linjs.fd, EVIOCGID, &id) < 0) { - // It's an old 0.x interface (we don't support it) - close(fd); + _glfwInputError(GLFW_PLATFORM_ERROR, + "Linux: Failed to query input device: %s", + strerror(errno)); + close(linjs.fd); return GLFW_FALSE; } - if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0) + // Ensure this device supports the events expected of a joystick + if (!isBitSet(EV_KEY, evBits) || !isBitSet(EV_ABS, evBits)) + { + close(linjs.fd); + return GLFW_FALSE; + } + + if (ioctl(linjs.fd, EVIOCGNAME(sizeof(name)), name) < 0) strncpy(name, "Unknown", sizeof(name)); - js = _glfw.linux_js.js + joy; - js->present = GLFW_TRUE; - js->name = strdup(name); - js->path = strdup(path); - js->fd = fd; + // Generate a joystick GUID that matches the SDL 2.0.5+ one + if (id.vendor && id.product && id.version) + { + sprintf(guid, "%02x%02x0000%02x%02x0000%02x%02x0000%02x%02x0000", + id.bustype & 0xff, id.bustype >> 8, + id.vendor & 0xff, id.vendor >> 8, + id.product & 0xff, id.product >> 8, + id.version & 0xff, id.version >> 8); + } + else + { + sprintf(guid, "%02x%02x0000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", + id.bustype & 0xff, id.bustype >> 8, + name[0], name[1], name[2], name[3], + name[4], name[5], name[6], name[7], + name[8], name[9], name[10]); + } - ioctl(fd, JSIOCGAXES, &axisCount); - js->axisCount = (int) axisCount; - js->axes = calloc(axisCount, sizeof(float)); + for (code = BTN_MISC; code < KEY_CNT; code++) + { + if (!isBitSet(code, keyBits)) + continue; - ioctl(fd, JSIOCGBUTTONS, &buttonCount); - js->buttonCount = (int) buttonCount; - js->buttons = calloc(buttonCount, 1); + linjs.keyMap[code - BTN_MISC] = buttonCount; + buttonCount++; + } - _glfwInputJoystickChange(joy, GLFW_CONNECTED); + for (code = 0; code < ABS_CNT; code++) + { + linjs.absMap[code] = -1; + if (!isBitSet(code, absBits)) + continue; + + if (code >= ABS_HAT0X && code <= ABS_HAT3Y) + { + linjs.absMap[code] = hatCount; + hatCount++; + // Skip the Y axis + code++; + } + else + { + if (ioctl(linjs.fd, EVIOCGABS(code), &linjs.absInfo[code]) < 0) + continue; + + linjs.absMap[code] = axisCount; + axisCount++; + } + } + + js = _glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount); + if (!js) + { + close(linjs.fd); + return GLFW_FALSE; + } + + strncpy(linjs.path, path, sizeof(linjs.path) - 1); + memcpy(&js->linjs, &linjs, sizeof(linjs)); + + pollAbsState(js); + + _glfwInputJoystick(js, GLFW_CONNECTED); return GLFW_TRUE; } -#endif // __linux__ -// Polls for and processes events the specified joystick +#undef isBitSet + +// Frees all resources associated with the specified joystick // -static GLFWbool pollJoystickEvents(_GLFWjoystickLinux* js) +static void closeJoystick(_GLFWjoystick* js) { -#if defined(__linux__) - _glfwPollJoystickEvents(); - - if (!js->present) - return GLFW_FALSE; - - // Read all queued events (non-blocking) - for (;;) - { - struct js_event e; - - errno = 0; - if (read(js->fd, &e, sizeof(e)) < 0) - { - // Reset the joystick slot if the device was disconnected - if (errno == ENODEV) - { - free(js->axes); - free(js->buttons); - free(js->name); - free(js->path); - - memset(js, 0, sizeof(_GLFWjoystickLinux)); - - _glfwInputJoystickChange(js - _glfw.linux_js.js, - GLFW_DISCONNECTED); - } - - break; - } - - // Clear the initial-state bit - e.type &= ~JS_EVENT_INIT; - - if (e.type == JS_EVENT_AXIS) - js->axes[e.number] = (float) e.value / 32767.0f; - else if (e.type == JS_EVENT_BUTTON) - js->buttons[e.number] = e.value ? GLFW_PRESS : GLFW_RELEASE; - } -#endif // __linux__ - return js->present; + close(js->linjs.fd); + _glfwFreeJoystick(js); + _glfwInputJoystick(js, GLFW_DISCONNECTED); } // Lexically compare joysticks by name; used by qsort // -#if defined(__linux__) static int compareJoysticks(const void* fp, const void* sp) { - const _GLFWjoystickLinux* fj = fp; - const _GLFWjoystickLinux* sj = sp; - return strcmp(fj->path, sj->path); + const _GLFWjoystick* fj = fp; + const _GLFWjoystick* sj = sp; + return strcmp(fj->linjs.path, sj->linjs.path); } -#endif // __linux__ ////////////////////////////////////////////////////////////////////////// @@ -173,36 +266,24 @@ static int compareJoysticks(const void* fp, const void* sp) // GLFWbool _glfwInitJoysticksLinux(void) { -#if defined(__linux__) DIR* dir; int count = 0; const char* dirname = "/dev/input"; - _glfw.linux_js.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); - if (_glfw.linux_js.inotify == -1) + _glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (_glfw.linjs.inotify > 0) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Linux: Failed to initialize inotify: %s", - strerror(errno)); - return GLFW_FALSE; + // HACK: Register for IN_ATTRIB to get notified when udev is done + // This works well in practice but the true way is libudev + + _glfw.linjs.watch = inotify_add_watch(_glfw.linjs.inotify, + dirname, + IN_CREATE | IN_ATTRIB | IN_DELETE); } - // HACK: Register for IN_ATTRIB as well to get notified when udev is done - // This works well in practice but the true way is libudev + // Continue without device connection notifications if inotify fails - _glfw.linux_js.watch = inotify_add_watch(_glfw.linux_js.inotify, - dirname, - IN_CREATE | IN_ATTRIB); - if (_glfw.linux_js.watch == -1) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Linux: Failed to watch for joystick connections in %s: %s", - dirname, - strerror(errno)); - // Continue without device connection notifications - } - - if (regcomp(&_glfw.linux_js.regex, "^js[0-9]\\+$", 0) != 0) + if (regcomp(&_glfw.linjs.regex, "^event[0-9]\\+$", 0) != 0) { _glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to compile regex"); return GLFW_FALSE; @@ -215,31 +296,25 @@ GLFWbool _glfwInitJoysticksLinux(void) while ((entry = readdir(dir))) { - char path[20]; regmatch_t match; - if (regexec(&_glfw.linux_js.regex, entry->d_name, 1, &match, 0) != 0) + if (regexec(&_glfw.linjs.regex, entry->d_name, 1, &match, 0) != 0) continue; + char path[PATH_MAX]; + snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name); + if (openJoystickDevice(path)) count++; } closedir(dir); } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Linux: Failed to open joystick device directory %s: %s", - dirname, - strerror(errno)); - // Continue with no joysticks detected - } - qsort(_glfw.linux_js.js, count, sizeof(_GLFWjoystickLinux), compareJoysticks); -#endif // __linux__ + // Continue with no joysticks if enumeration fails + qsort(_glfw.joysticks, count, sizeof(_GLFWjoystick), compareJoysticks); return GLFW_TRUE; } @@ -247,56 +322,65 @@ GLFWbool _glfwInitJoysticksLinux(void) // void _glfwTerminateJoysticksLinux(void) { -#if defined(__linux__) - int i; + int jid; - for (i = 0; i <= GLFW_JOYSTICK_LAST; i++) + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (_glfw.linux_js.js[i].present) - { - close(_glfw.linux_js.js[i].fd); - free(_glfw.linux_js.js[i].axes); - free(_glfw.linux_js.js[i].buttons); - free(_glfw.linux_js.js[i].name); - free(_glfw.linux_js.js[i].path); - } + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + closeJoystick(js); } - regfree(&_glfw.linux_js.regex); + regfree(&_glfw.linjs.regex); - if (_glfw.linux_js.inotify > 0) + if (_glfw.linjs.inotify > 0) { - if (_glfw.linux_js.watch > 0) - inotify_rm_watch(_glfw.linux_js.inotify, _glfw.linux_js.watch); + if (_glfw.linjs.watch > 0) + inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch); - close(_glfw.linux_js.inotify); + close(_glfw.linjs.inotify); } -#endif // __linux__ } -void _glfwPollJoystickEvents(void) +void _glfwDetectJoystickConnectionLinux(void) { -#if defined(__linux__) ssize_t offset = 0; char buffer[16384]; - const ssize_t size = read(_glfw.linux_js.inotify, buffer, sizeof(buffer)); + if (_glfw.linjs.inotify <= 0) + return; + + const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer)); while (size > offset) { regmatch_t match; const struct inotify_event* e = (struct inotify_event*) (buffer + offset); - if (regexec(&_glfw.linux_js.regex, e->name, 1, &match, 0) == 0) - { - char path[20]; - snprintf(path, sizeof(path), "/dev/input/%s", e->name); - openJoystickDevice(path); - } - offset += sizeof(struct inotify_event) + e->len; + + if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) != 0) + continue; + + char path[PATH_MAX]; + snprintf(path, sizeof(path), "/dev/input/%s", e->name); + + if (e->mask & (IN_CREATE | IN_ATTRIB)) + openJoystickDevice(path); + else if (e->mask & IN_DELETE) + { + int jid; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0) + { + closeJoystick(_glfw.joysticks + jid); + break; + } + } + } } -#endif } @@ -304,38 +388,47 @@ void _glfwPollJoystickEvents(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformJoystickPresent(int joy) +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) { - _GLFWjoystickLinux* js = _glfw.linux_js.js + joy; - return pollJoystickEvents(js); + // Read all queued events (non-blocking) + for (;;) + { + struct input_event e; + + errno = 0; + if (read(js->linjs.fd, &e, sizeof(e)) < 0) + { + // Reset the joystick slot if the device was disconnected + if (errno == ENODEV) + closeJoystick(js); + + break; + } + + if (e.type == EV_SYN) + { + if (e.code == SYN_DROPPED) + _glfw.linjs.dropped = GLFW_TRUE; + else if (e.code == SYN_REPORT) + { + _glfw.linjs.dropped = GLFW_FALSE; + pollAbsState(js); + } + } + + if (_glfw.linjs.dropped) + continue; + + if (e.type == EV_KEY) + handleKeyEvent(js, e.code, e.value); + else if (e.type == EV_ABS) + handleAbsEvent(js, e.code, e.value); + } + + return js->present; } -const float* _glfwPlatformGetJoystickAxes(int joy, int* count) +void _glfwPlatformUpdateGamepadGUID(char* guid) { - _GLFWjoystickLinux* js = _glfw.linux_js.js + joy; - if (!pollJoystickEvents(js)) - return NULL; - - *count = js->axisCount; - return js->axes; -} - -const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) -{ - _GLFWjoystickLinux* js = _glfw.linux_js.js + joy; - if (!pollJoystickEvents(js)) - return NULL; - - *count = js->buttonCount; - return js->buttons; -} - -const char* _glfwPlatformGetJoystickName(int joy) -{ - _GLFWjoystickLinux* js = _glfw.linux_js.js + joy; - if (!pollJoystickEvents(js)) - return NULL; - - return js->name; } diff --git a/external/glfw/linux_joystick.h b/external/glfw/linux_joystick.h index 4187b13..2eabfa1 100644 --- a/external/glfw/linux_joystick.h +++ b/external/glfw/linux_joystick.h @@ -24,45 +24,39 @@ // //======================================================================== -#ifndef _glfw3_linux_joystick_h_ -#define _glfw3_linux_joystick_h_ - +#include +#include #include -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWjoylistLinux linux_js +#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickLinux linjs +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs +#define _GLFW_PLATFORM_MAPPING_NAME "Linux" // Linux-specific joystick data // typedef struct _GLFWjoystickLinux { - GLFWbool present; - int fd; - float* axes; - int axisCount; - unsigned char* buttons; - int buttonCount; - char* name; - char* path; + int fd; + char path[PATH_MAX]; + int keyMap[KEY_CNT - BTN_MISC]; + int absMap[ABS_CNT]; + struct input_absinfo absInfo[ABS_CNT]; + int hats[4][2]; } _GLFWjoystickLinux; // Linux-specific joystick API data // -typedef struct _GLFWjoylistLinux +typedef struct _GLFWlibraryLinux { - _GLFWjoystickLinux js[GLFW_JOYSTICK_LAST + 1]; - -#if defined(__linux__) - int inotify; - int watch; - regex_t regex; -#endif /*__linux__*/ -} _GLFWjoylistLinux; + int inotify; + int watch; + regex_t regex; + GLFWbool dropped; +} _GLFWlibraryLinux; GLFWbool _glfwInitJoysticksLinux(void); void _glfwTerminateJoysticksLinux(void); +void _glfwDetectJoystickConnectionLinux(void); -void _glfwPollJoystickEvents(void); - -#endif // _glfw3_linux_joystick_h_ diff --git a/external/glfw/mappings.h b/external/glfw/mappings.h new file mode 100644 index 0000000..94a279a --- /dev/null +++ b/external/glfw/mappings.h @@ -0,0 +1,478 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2018 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// As mappings.h.in, this file is used by CMake to produce the mappings.h +// header file. If you are adding a GLFW specific gamepad mapping, this is +// where to put it. +//======================================================================== +// As mappings.h, this provides all pre-defined gamepad mappings, including +// all available in SDL_GameControllerDB. Do not edit this file. Any gamepad +// mappings not specific to GLFW should be submitted to SDL_GameControllerDB. +// This file can be re-generated from mappings.h.in and the upstream +// gamecontrollerdb.txt with the GenerateMappings.cmake script. +//======================================================================== + +// All gamepad mappings not labeled GLFW are copied from the +// SDL_GameControllerDB project under the following license: +// +// Simple DirectMedia Layer +// Copyright (C) 1997-2013 Sam Lantinga +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the +// use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +const char* _glfwDefaultMappings[] = +{ +"03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,", +"03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", +"03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", +"030000008f0e00001200000000000000,Acme,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", +"03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,", +"030000006b1400000055000000000000,bigben ps3padstreetnew,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,", +"03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,", +"03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"030000004f04000023b3000000000000,Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", +"030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,", +"03000000790000000600000000000000,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", +"03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,", +"030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000451300000010000000000000,Generic USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00004d00000000000000,HORIPAD3 A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,", +"030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows,", +"03000000b50700001403000000000000,IMPACT BLACK,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"030000006f0e00002401000000000000,INJUSTICE FightStick for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,", +"030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008433000000000000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008483000000000000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b6,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000008305000031b0000000000000,MaxfireBlaze3,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,", +"03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,", +"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", +"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows,", +"03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", +"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,leftx:h0.6,lefty:h0.12,rightshoulder:b5,rightstick:a2,righttrigger:b7,rightx:h0.9,righty:h0.4,start:b9,x:b2,y:b3,platform:Windows,", +"03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,", +"03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000008f0e00007530000000000000,PS (R) Gamepad,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000100800000100000000000000,PS1 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000100800000300000000000000,PS2 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", +"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,", +"03000000250900000500000000000000,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,", +"03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,", +"03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", +"03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", +"03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,", +"03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", +"0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", +"0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", +"030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000006f0e00001e01000000000000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,x:b0,y:b1,platform:Windows,", +"03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,", +"03000000300f00001101000000000000,saitek rumble pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", +"030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,", +"030000008f0e00000800000000000000,SpeedLink Strike FX Wireless,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows,", +"03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"030000004f04000015b3000000000000,Thrustmaster Dual Analog 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", +"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,", +"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", +"03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000380700006652000000000000,UnKnown,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,", +"03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000790000000600000000000000,G-Shark GP-702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", +"030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,", +"03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", +"03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", +"030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,", +"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,", +"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,", +"03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", +"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,", +"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,", +"030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,", +"03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,", +"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", +"030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000005e0400008e02000001000000,Steam Virtual GamePad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,", +"03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", +"03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", +"03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,", +"03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,", +"030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,", +"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,", +"03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,", +"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,", +"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,", +"030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", +"03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", +"05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", +"030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,", +"03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,", +"03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,", +"03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,", +"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,", +"030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", +"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e00001f01000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,", +"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", +"030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,", +"03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,", +"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,", +"03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,", +"050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", +"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,", +"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,", +"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", +"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d04000015c2000010010000,Logitech Logitech Extreme 3D,a:b0,b:b4,back:b6,guide:b8,leftshoulder:b9,leftstick:h0.8,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:h0.2,start:b7,x:b2,y:b5,platform:Linux,", +"030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,", +"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,", +"03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", +"03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", +"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", +"030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Linux,", +"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", +"05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,", +"03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", +"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", +"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", +"030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", +"050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", +"030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", +"060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", +"05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", +"050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,", +"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,", +"030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", +"0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", +"0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", +"030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,", +"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,", +"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,", +"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,", +"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux,", +"03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", +"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", +"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", +"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", +"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,", +"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", +"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,", +"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,", +"05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,", +"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,", +"03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,", +"61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", +"4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", +"37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", +"35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,", +"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,", +"5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android,", +"34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android,", +"4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,", +"4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,", +"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,", + +"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +NULL +}; + diff --git a/external/glfw/mappings.h.in b/external/glfw/mappings.h.in new file mode 100644 index 0000000..72460b0 --- /dev/null +++ b/external/glfw/mappings.h.in @@ -0,0 +1,73 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2018 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// As mappings.h.in, this file is used by CMake to produce the mappings.h +// header file. If you are adding a GLFW specific gamepad mapping, this is +// where to put it. +//======================================================================== +// As mappings.h, this provides all pre-defined gamepad mappings, including +// all available in SDL_GameControllerDB. Do not edit this file. Any gamepad +// mappings not specific to GLFW should be submitted to SDL_GameControllerDB. +// This file can be re-generated from mappings.h.in and the upstream +// gamecontrollerdb.txt with the GenerateMappings.cmake script. +//======================================================================== + +// All gamepad mappings not labeled GLFW are copied from the +// SDL_GameControllerDB project under the following license: +// +// Simple DirectMedia Layer +// Copyright (C) 1997-2013 Sam Lantinga +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the +// use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +const char* _glfwDefaultMappings[] = +{ +@GLFW_GAMEPAD_MAPPINGS@ +"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +NULL +}; + diff --git a/external/glfw/mir_init.c b/external/glfw/mir_init.c deleted file mode 100644 index 7385bb7..0000000 --- a/external/glfw/mir_init.c +++ /dev/null @@ -1,238 +0,0 @@ -//======================================================================== -// GLFW 3.3 Mir - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014-2015 Brandon Schaefer -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#include "internal.h" - -#include -#include -#include - - -// Create key code translation tables -// -static void createKeyTables(void) -{ - memset(_glfw.mir.publicKeys, -1, sizeof(_glfw.mir.publicKeys)); - - _glfw.mir.publicKeys[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; - _glfw.mir.publicKeys[KEY_1] = GLFW_KEY_1; - _glfw.mir.publicKeys[KEY_2] = GLFW_KEY_2; - _glfw.mir.publicKeys[KEY_3] = GLFW_KEY_3; - _glfw.mir.publicKeys[KEY_4] = GLFW_KEY_4; - _glfw.mir.publicKeys[KEY_5] = GLFW_KEY_5; - _glfw.mir.publicKeys[KEY_6] = GLFW_KEY_6; - _glfw.mir.publicKeys[KEY_7] = GLFW_KEY_7; - _glfw.mir.publicKeys[KEY_8] = GLFW_KEY_8; - _glfw.mir.publicKeys[KEY_9] = GLFW_KEY_9; - _glfw.mir.publicKeys[KEY_0] = GLFW_KEY_0; - _glfw.mir.publicKeys[KEY_MINUS] = GLFW_KEY_MINUS; - _glfw.mir.publicKeys[KEY_EQUAL] = GLFW_KEY_EQUAL; - _glfw.mir.publicKeys[KEY_Q] = GLFW_KEY_Q; - _glfw.mir.publicKeys[KEY_W] = GLFW_KEY_W; - _glfw.mir.publicKeys[KEY_E] = GLFW_KEY_E; - _glfw.mir.publicKeys[KEY_R] = GLFW_KEY_R; - _glfw.mir.publicKeys[KEY_T] = GLFW_KEY_T; - _glfw.mir.publicKeys[KEY_Y] = GLFW_KEY_Y; - _glfw.mir.publicKeys[KEY_U] = GLFW_KEY_U; - _glfw.mir.publicKeys[KEY_I] = GLFW_KEY_I; - _glfw.mir.publicKeys[KEY_O] = GLFW_KEY_O; - _glfw.mir.publicKeys[KEY_P] = GLFW_KEY_P; - _glfw.mir.publicKeys[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; - _glfw.mir.publicKeys[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; - _glfw.mir.publicKeys[KEY_A] = GLFW_KEY_A; - _glfw.mir.publicKeys[KEY_S] = GLFW_KEY_S; - _glfw.mir.publicKeys[KEY_D] = GLFW_KEY_D; - _glfw.mir.publicKeys[KEY_F] = GLFW_KEY_F; - _glfw.mir.publicKeys[KEY_G] = GLFW_KEY_G; - _glfw.mir.publicKeys[KEY_H] = GLFW_KEY_H; - _glfw.mir.publicKeys[KEY_J] = GLFW_KEY_J; - _glfw.mir.publicKeys[KEY_K] = GLFW_KEY_K; - _glfw.mir.publicKeys[KEY_L] = GLFW_KEY_L; - _glfw.mir.publicKeys[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; - _glfw.mir.publicKeys[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; - _glfw.mir.publicKeys[KEY_Z] = GLFW_KEY_Z; - _glfw.mir.publicKeys[KEY_X] = GLFW_KEY_X; - _glfw.mir.publicKeys[KEY_C] = GLFW_KEY_C; - _glfw.mir.publicKeys[KEY_V] = GLFW_KEY_V; - _glfw.mir.publicKeys[KEY_B] = GLFW_KEY_B; - _glfw.mir.publicKeys[KEY_N] = GLFW_KEY_N; - _glfw.mir.publicKeys[KEY_M] = GLFW_KEY_M; - _glfw.mir.publicKeys[KEY_COMMA] = GLFW_KEY_COMMA; - _glfw.mir.publicKeys[KEY_DOT] = GLFW_KEY_PERIOD; - _glfw.mir.publicKeys[KEY_SLASH] = GLFW_KEY_SLASH; - _glfw.mir.publicKeys[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; - _glfw.mir.publicKeys[KEY_ESC] = GLFW_KEY_ESCAPE; - _glfw.mir.publicKeys[KEY_TAB] = GLFW_KEY_TAB; - _glfw.mir.publicKeys[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; - _glfw.mir.publicKeys[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; - _glfw.mir.publicKeys[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; - _glfw.mir.publicKeys[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; - _glfw.mir.publicKeys[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; - _glfw.mir.publicKeys[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; - _glfw.mir.publicKeys[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; - _glfw.mir.publicKeys[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; - _glfw.mir.publicKeys[KEY_MENU] = GLFW_KEY_MENU; - _glfw.mir.publicKeys[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; - _glfw.mir.publicKeys[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; - _glfw.mir.publicKeys[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; - _glfw.mir.publicKeys[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; - _glfw.mir.publicKeys[KEY_PAUSE] = GLFW_KEY_PAUSE; - _glfw.mir.publicKeys[KEY_DELETE] = GLFW_KEY_DELETE; - _glfw.mir.publicKeys[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; - _glfw.mir.publicKeys[KEY_ENTER] = GLFW_KEY_ENTER; - _glfw.mir.publicKeys[KEY_HOME] = GLFW_KEY_HOME; - _glfw.mir.publicKeys[KEY_END] = GLFW_KEY_END; - _glfw.mir.publicKeys[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; - _glfw.mir.publicKeys[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; - _glfw.mir.publicKeys[KEY_INSERT] = GLFW_KEY_INSERT; - _glfw.mir.publicKeys[KEY_LEFT] = GLFW_KEY_LEFT; - _glfw.mir.publicKeys[KEY_RIGHT] = GLFW_KEY_RIGHT; - _glfw.mir.publicKeys[KEY_DOWN] = GLFW_KEY_DOWN; - _glfw.mir.publicKeys[KEY_UP] = GLFW_KEY_UP; - _glfw.mir.publicKeys[KEY_F1] = GLFW_KEY_F1; - _glfw.mir.publicKeys[KEY_F2] = GLFW_KEY_F2; - _glfw.mir.publicKeys[KEY_F3] = GLFW_KEY_F3; - _glfw.mir.publicKeys[KEY_F4] = GLFW_KEY_F4; - _glfw.mir.publicKeys[KEY_F5] = GLFW_KEY_F5; - _glfw.mir.publicKeys[KEY_F6] = GLFW_KEY_F6; - _glfw.mir.publicKeys[KEY_F7] = GLFW_KEY_F7; - _glfw.mir.publicKeys[KEY_F8] = GLFW_KEY_F8; - _glfw.mir.publicKeys[KEY_F9] = GLFW_KEY_F9; - _glfw.mir.publicKeys[KEY_F10] = GLFW_KEY_F10; - _glfw.mir.publicKeys[KEY_F11] = GLFW_KEY_F11; - _glfw.mir.publicKeys[KEY_F12] = GLFW_KEY_F12; - _glfw.mir.publicKeys[KEY_F13] = GLFW_KEY_F13; - _glfw.mir.publicKeys[KEY_F14] = GLFW_KEY_F14; - _glfw.mir.publicKeys[KEY_F15] = GLFW_KEY_F15; - _glfw.mir.publicKeys[KEY_F16] = GLFW_KEY_F16; - _glfw.mir.publicKeys[KEY_F17] = GLFW_KEY_F17; - _glfw.mir.publicKeys[KEY_F18] = GLFW_KEY_F18; - _glfw.mir.publicKeys[KEY_F19] = GLFW_KEY_F19; - _glfw.mir.publicKeys[KEY_F20] = GLFW_KEY_F20; - _glfw.mir.publicKeys[KEY_F21] = GLFW_KEY_F21; - _glfw.mir.publicKeys[KEY_F22] = GLFW_KEY_F22; - _glfw.mir.publicKeys[KEY_F23] = GLFW_KEY_F23; - _glfw.mir.publicKeys[KEY_F24] = GLFW_KEY_F24; - _glfw.mir.publicKeys[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; - _glfw.mir.publicKeys[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; - _glfw.mir.publicKeys[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; - _glfw.mir.publicKeys[KEY_KPPLUS] = GLFW_KEY_KP_ADD; - _glfw.mir.publicKeys[KEY_KP0] = GLFW_KEY_KP_0; - _glfw.mir.publicKeys[KEY_KP1] = GLFW_KEY_KP_1; - _glfw.mir.publicKeys[KEY_KP2] = GLFW_KEY_KP_2; - _glfw.mir.publicKeys[KEY_KP3] = GLFW_KEY_KP_3; - _glfw.mir.publicKeys[KEY_KP4] = GLFW_KEY_KP_4; - _glfw.mir.publicKeys[KEY_KP5] = GLFW_KEY_KP_5; - _glfw.mir.publicKeys[KEY_KP6] = GLFW_KEY_KP_6; - _glfw.mir.publicKeys[KEY_KP7] = GLFW_KEY_KP_7; - _glfw.mir.publicKeys[KEY_KP8] = GLFW_KEY_KP_8; - _glfw.mir.publicKeys[KEY_KP9] = GLFW_KEY_KP_9; - _glfw.mir.publicKeys[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; - _glfw.mir.publicKeys[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; - _glfw.mir.publicKeys[KEY_KPENTER] = GLFW_KEY_KP_ENTER; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformInit(void) -{ - int error; - - _glfw.mir.connection = mir_connect_sync(NULL, __PRETTY_FUNCTION__); - - if (!mir_connection_is_valid(_glfw.mir.connection)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unable to connect to server: %s", - mir_connection_get_error_message(_glfw.mir.connection)); - - return GLFW_FALSE; - } - - _glfw.mir.display = - mir_connection_get_egl_native_display(_glfw.mir.connection); - - createKeyTables(); - - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - - if (!_glfwInitJoysticksLinux()) - return GLFW_FALSE; - - _glfwInitTimerPOSIX(); - - // Need the default conf for when we set a NULL cursor - _glfw.mir.default_conf = mir_cursor_configuration_from_name(mir_arrow_cursor_name); - - _glfw.mir.event_queue = calloc(1, sizeof(EventQueue)); - _glfwInitEventQueueMir(_glfw.mir.event_queue); - - error = pthread_mutex_init(&_glfw.mir.event_mutex, NULL); - if (error) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Failed to create event mutex: %s", - strerror(error)); - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -void _glfwPlatformTerminate(void) -{ - _glfwTerminateEGL(); - _glfwTerminateJoysticksLinux(); - _glfwTerminateThreadLocalStoragePOSIX(); - - _glfwDeleteEventQueueMir(_glfw.mir.event_queue); - - pthread_mutex_destroy(&_glfw.mir.event_mutex); - - mir_connection_release(_glfw.mir.connection); -} - -const char* _glfwPlatformGetVersionString(void) -{ - return _GLFW_VERSION_NUMBER " Mir EGL" -#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) - " clock_gettime" -#else - " gettimeofday" -#endif -#if defined(__linux__) - " /dev/js" -#endif -#if defined(_GLFW_BUILD_DLL) - " shared" -#endif - ; -} - diff --git a/external/glfw/mir_monitor.c b/external/glfw/mir_monitor.c deleted file mode 100644 index 94c1313..0000000 --- a/external/glfw/mir_monitor.c +++ /dev/null @@ -1,182 +0,0 @@ -//======================================================================== -// GLFW 3.3 Mir - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014-2015 Brandon Schaefer -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#include "internal.h" - -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -_GLFWmonitor** _glfwPlatformGetMonitors(int* count) -{ - int i, found = 0; - _GLFWmonitor** monitors = NULL; - MirDisplayConfiguration* displayConfig = - mir_connection_create_display_config(_glfw.mir.connection); - - *count = 0; - - for (i = 0; i < displayConfig->num_outputs; i++) - { - const MirDisplayOutput* out = displayConfig->outputs + i; - - if (out->used && - out->connected && - out->num_modes && - out->current_mode < out->num_modes) - { - found++; - monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found); - monitors[i] = _glfwAllocMonitor("Unknown", - out->physical_width_mm, - out->physical_height_mm); - - monitors[i]->mir.x = out->position_x; - monitors[i]->mir.y = out->position_y; - monitors[i]->mir.output_id = out->output_id; - monitors[i]->mir.cur_mode = out->current_mode; - - monitors[i]->modes = _glfwPlatformGetVideoModes(monitors[i], - &monitors[i]->modeCount); - } - } - - mir_display_config_destroy(displayConfig); - - *count = found; - return monitors; -} - -GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) -{ - return first->mir.output_id == second->mir.output_id; -} - -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) -{ - if (xpos) - *xpos = monitor->mir.x; - if (ypos) - *ypos = monitor->mir.y; -} - -void FillInRGBBitsFromPixelFormat(GLFWvidmode* mode, const MirPixelFormat pf) -{ - switch (pf) - { - case mir_pixel_format_rgb_565: - mode->redBits = 5; - mode->greenBits = 6; - mode->blueBits = 5; - break; - case mir_pixel_format_rgba_5551: - mode->redBits = 5; - mode->greenBits = 5; - mode->blueBits = 5; - break; - case mir_pixel_format_rgba_4444: - mode->redBits = 4; - mode->greenBits = 4; - mode->blueBits = 4; - break; - case mir_pixel_format_abgr_8888: - case mir_pixel_format_xbgr_8888: - case mir_pixel_format_argb_8888: - case mir_pixel_format_xrgb_8888: - case mir_pixel_format_bgr_888: - case mir_pixel_format_rgb_888: - default: - mode->redBits = 8; - mode->greenBits = 8; - mode->blueBits = 8; - break; - } -} - -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) -{ - int i; - GLFWvidmode* modes = NULL; - MirDisplayConfiguration* displayConfig = - mir_connection_create_display_config(_glfw.mir.connection); - - for (i = 0; i < displayConfig->num_outputs; i++) - { - const MirDisplayOutput* out = displayConfig->outputs + i; - if (out->output_id != monitor->mir.output_id) - continue; - - modes = calloc(out->num_modes, sizeof(GLFWvidmode)); - - for (*found = 0; *found < out->num_modes; (*found)++) - { - modes[*found].width = out->modes[*found].horizontal_resolution; - modes[*found].height = out->modes[*found].vertical_resolution; - modes[*found].refreshRate = out->modes[*found].refresh_rate; - - FillInRGBBitsFromPixelFormat(&modes[*found], out->output_formats[*found]); - } - - break; - } - - mir_display_config_destroy(displayConfig); - - return modes; -} - -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) -{ - *mode = monitor->modes[monitor->mir.cur_mode]; -} - -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI int glfwGetMirMonitor(GLFWmonitor* handle) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(0); - return monitor->mir.output_id; -} - diff --git a/external/glfw/mir_platform.h b/external/glfw/mir_platform.h deleted file mode 100644 index 2bd9b62..0000000 --- a/external/glfw/mir_platform.h +++ /dev/null @@ -1,130 +0,0 @@ -//======================================================================== -// GLFW 3.3 Mir - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014-2015 Brandon Schaefer -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#ifndef _glfw3_mir_platform_h_ -#define _glfw3_mir_platform_h_ - -#include -#include -#include - -#include - -typedef VkFlags VkMirSurfaceCreateFlagsKHR; - -typedef struct VkMirSurfaceCreateInfoKHR -{ - VkStructureType sType; - const void* pNext; - VkMirSurfaceCreateFlagsKHR flags; - MirConnection* connection; - MirSurface* mirSurface; -} VkMirSurfaceCreateInfoKHR; - -typedef VkResult (APIENTRY *PFN_vkCreateMirSurfaceKHR)(VkInstance,const VkMirSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); -typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(VkPhysicalDevice,uint32_t,MirConnection*); - -#include "posix_tls.h" -#include "posix_time.h" -#include "linux_joystick.h" -#include "xkb_unicode.h" -#include "egl_context.h" - -#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) -#define _glfw_dlclose(handle) dlclose(handle) -#define _glfw_dlsym(handle, name) dlsym(handle, name) - -#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->mir.window) -#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.mir.display) - -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowMir mir -#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorMir mir -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryMir mir -#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorMir mir - -#define _GLFW_PLATFORM_CONTEXT_STATE -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE - - -// Mir-specific Event Queue -// -typedef struct EventQueue -{ - TAILQ_HEAD(, EventNode) head; -} EventQueue; - -// Mir-specific per-window data -// -typedef struct _GLFWwindowMir -{ - MirSurface* surface; - int width; - int height; - MirEGLNativeWindowType window; - -} _GLFWwindowMir; - -// Mir-specific per-monitor data -// -typedef struct _GLFWmonitorMir -{ - int cur_mode; - int output_id; - int x; - int y; - -} _GLFWmonitorMir; - -// Mir-specific global data -// -typedef struct _GLFWlibraryMir -{ - MirConnection* connection; - MirEGLNativeDisplayType display; - MirCursorConfiguration* default_conf; - EventQueue* event_queue; - - short int publicKeys[256]; - - pthread_mutex_t event_mutex; - pthread_cond_t event_cond; - -} _GLFWlibraryMir; - -// Mir-specific per-cursor data -// TODO: Only system cursors are implemented in Mir atm. Need to wait for support. -// -typedef struct _GLFWcursorMir -{ - MirCursorConfiguration* conf; - MirBufferStream* custom_cursor; -} _GLFWcursorMir; - - -extern void _glfwInitEventQueueMir(EventQueue* queue); -extern void _glfwDeleteEventQueueMir(EventQueue* queue); - -#endif // _glfw3_mir_platform_h_ diff --git a/external/glfw/mir_window.c b/external/glfw/mir_window.c deleted file mode 100644 index 7d7f632..0000000 --- a/external/glfw/mir_window.c +++ /dev/null @@ -1,848 +0,0 @@ -//======================================================================== -// GLFW 3.3 Mir - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014-2015 Brandon Schaefer -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#include "internal.h" - -#include -#include -#include - - -typedef struct EventNode -{ - TAILQ_ENTRY(EventNode) entries; - const MirEvent* event; - _GLFWwindow* window; -} EventNode; - -static void deleteNode(EventQueue* queue, EventNode* node) -{ - mir_event_unref(node->event); - free(node); -} - -static GLFWbool emptyEventQueue(EventQueue* queue) -{ - return queue->head.tqh_first == NULL; -} - -// TODO The mir_event_ref is not supposed to be used but ... its needed -// in this case. Need to wait until we can read from an FD set up by mir -// for single threaded event handling. -static EventNode* newEventNode(const MirEvent* event, _GLFWwindow* context) -{ - EventNode* new_node = calloc(1, sizeof(EventNode)); - new_node->event = mir_event_ref(event); - new_node->window = context; - - return new_node; -} - -static void enqueueEvent(const MirEvent* event, _GLFWwindow* context) -{ - pthread_mutex_lock(&_glfw.mir.event_mutex); - - EventNode* new_node = newEventNode(event, context); - TAILQ_INSERT_TAIL(&_glfw.mir.event_queue->head, new_node, entries); - - pthread_cond_signal(&_glfw.mir.event_cond); - - pthread_mutex_unlock(&_glfw.mir.event_mutex); -} - -static EventNode* dequeueEvent(EventQueue* queue) -{ - EventNode* node = NULL; - - pthread_mutex_lock(&_glfw.mir.event_mutex); - - node = queue->head.tqh_first; - - if (node) - TAILQ_REMOVE(&queue->head, node, entries); - - pthread_mutex_unlock(&_glfw.mir.event_mutex); - - return node; -} - -/* FIXME Soon to be changed upstream mir! So we can use an egl config to figure out - the best pixel format! -*/ -static MirPixelFormat findValidPixelFormat(void) -{ - unsigned int i, validFormats, mirPixelFormats = 32; - MirPixelFormat formats[mir_pixel_formats]; - - mir_connection_get_available_surface_formats(_glfw.mir.connection, formats, - mirPixelFormats, &validFormats); - - for (i = 0; i < validFormats; i++) - { - if (formats[i] == mir_pixel_format_abgr_8888 || - formats[i] == mir_pixel_format_xbgr_8888 || - formats[i] == mir_pixel_format_argb_8888 || - formats[i] == mir_pixel_format_xrgb_8888) - { - return formats[i]; - } - } - - return mir_pixel_format_invalid; -} - -static int mirModToGLFWMod(uint32_t mods) -{ - int publicMods = 0x0; - - if (mods & mir_input_event_modifier_alt) - publicMods |= GLFW_MOD_ALT; - else if (mods & mir_input_event_modifier_shift) - publicMods |= GLFW_MOD_SHIFT; - else if (mods & mir_input_event_modifier_ctrl) - publicMods |= GLFW_MOD_CONTROL; - else if (mods & mir_input_event_modifier_meta) - publicMods |= GLFW_MOD_SUPER; - - return publicMods; -} - -static int toGLFWKeyCode(uint32_t key) -{ - if (key < sizeof(_glfw.mir.publicKeys) / sizeof(_glfw.mir.publicKeys[0])) - return _glfw.mir.publicKeys[key]; - - return GLFW_KEY_UNKNOWN; -} - -static void handleKeyEvent(const MirKeyboardEvent* key_event, _GLFWwindow* window) -{ - const int action = mir_keyboard_event_action (key_event); - const int scan_code = mir_keyboard_event_scan_code(key_event); - const int key_code = mir_keyboard_event_key_code (key_event); - const int modifiers = mir_keyboard_event_modifiers(key_event); - - const int pressed = action == mir_keyboard_action_up ? GLFW_RELEASE : GLFW_PRESS; - const int mods = mirModToGLFWMod(modifiers); - const long text = _glfwKeySym2Unicode(key_code); - const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); - - _glfwInputKey(window, toGLFWKeyCode(scan_code), scan_code, pressed, mods); - - if (text != -1) - _glfwInputChar(window, text, mods, plain); -} - -static void handlePointerButton(_GLFWwindow* window, - int pressed, - const MirPointerEvent* pointer_event) -{ - int mods = mir_pointer_event_modifiers(pointer_event); - const int publicMods = mirModToGLFWMod(mods); - MirPointerButton button = mir_pointer_button_primary; - static uint32_t oldButtonStates = 0; - uint32_t newButtonStates = mir_pointer_event_buttons(pointer_event); - int publicButton = GLFW_MOUSE_BUTTON_LEFT; - - // XOR our old button states our new states to figure out what was added or removed - button = newButtonStates ^ oldButtonStates; - - switch (button) - { - case mir_pointer_button_primary: - publicButton = GLFW_MOUSE_BUTTON_LEFT; - break; - case mir_pointer_button_secondary: - publicButton = GLFW_MOUSE_BUTTON_RIGHT; - break; - case mir_pointer_button_tertiary: - publicButton = GLFW_MOUSE_BUTTON_MIDDLE; - break; - case mir_pointer_button_forward: - // FIXME What is the forward button? - publicButton = GLFW_MOUSE_BUTTON_4; - break; - case mir_pointer_button_back: - // FIXME What is the back button? - publicButton = GLFW_MOUSE_BUTTON_5; - break; - default: - break; - } - - oldButtonStates = newButtonStates; - - _glfwInputMouseClick(window, publicButton, pressed, publicMods); -} - -static void handlePointerMotion(_GLFWwindow* window, - const MirPointerEvent* pointer_event) -{ - int current_x = window->virtualCursorPosX; - int current_y = window->virtualCursorPosY; - int x = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_x); - int y = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_y); - int dx = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_hscroll); - int dy = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_vscroll); - - _glfwInputCursorPos(window, x, y); - if (dx != 0 || dy != 0) - _glfwInputScroll(window, dx, dy); -} - -static void handlePointerEvent(const MirPointerEvent* pointer_event, - _GLFWwindow* window) -{ - int action = mir_pointer_event_action(pointer_event); - - switch (action) - { - case mir_pointer_action_button_down: - handlePointerButton(window, GLFW_PRESS, pointer_event); - break; - case mir_pointer_action_button_up: - handlePointerButton(window, GLFW_RELEASE, pointer_event); - break; - case mir_pointer_action_motion: - handlePointerMotion(window, pointer_event); - break; - case mir_pointer_action_enter: - case mir_pointer_action_leave: - break; - default: - break; - - } -} - -static void handleInput(const MirInputEvent* input_event, _GLFWwindow* window) -{ - int type = mir_input_event_get_type(input_event); - - switch (type) - { - case mir_input_event_type_key: - handleKeyEvent(mir_input_event_get_keyboard_event(input_event), window); - break; - case mir_input_event_type_pointer: - handlePointerEvent(mir_input_event_get_pointer_event(input_event), window); - break; - default: - break; - } -} - -static void handleEvent(const MirEvent* event, _GLFWwindow* window) -{ - int type = mir_event_get_type(event); - - switch (type) - { - case mir_event_type_input: - handleInput(mir_event_get_input_event(event), window); - break; - default: - break; - } -} - -static void addNewEvent(MirSurface* surface, const MirEvent* event, void* context) -{ - enqueueEvent(event, context); -} - -static GLFWbool createSurface(_GLFWwindow* window) -{ - MirSurfaceSpec* spec; - MirBufferUsage buffer_usage = mir_buffer_usage_hardware; - MirPixelFormat pixel_format = findValidPixelFormat(); - - if (pixel_format == mir_pixel_format_invalid) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unable to find a correct pixel format"); - return GLFW_FALSE; - } - - spec = mir_connection_create_spec_for_normal_surface(_glfw.mir.connection, - window->mir.width, - window->mir.height, - pixel_format); - - mir_surface_spec_set_buffer_usage(spec, buffer_usage); - mir_surface_spec_set_name(spec, "MirSurface"); - - window->mir.surface = mir_surface_create_sync(spec); - mir_surface_spec_release(spec); - - if (!mir_surface_is_valid(window->mir.surface)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unable to create surface: %s", - mir_surface_get_error_message(window->mir.surface)); - - return GLFW_FALSE; - } - - mir_surface_set_event_handler(window->mir.surface, addNewEvent, window); - - return GLFW_TRUE; -} - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwInitEventQueueMir(EventQueue* queue) -{ - TAILQ_INIT(&queue->head); -} - -void _glfwDeleteEventQueueMir(EventQueue* queue) -{ - if (queue) - { - EventNode* node, *node_next; - node = queue->head.tqh_first; - - while (node != NULL) - { - node_next = node->entries.tqe_next; - - TAILQ_REMOVE(&queue->head, node, entries); - deleteNode(queue, node); - - node = node_next; - } - - free(queue); - } -} - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - if (window->monitor) - { - GLFWvidmode mode; - _glfwPlatformGetVideoMode(window->monitor, &mode); - - mir_surface_set_state(window->mir.surface, mir_surface_state_fullscreen); - - if (wndconfig->width > mode.width || wndconfig->height > mode.height) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Requested surface size too large: %ix%i", - wndconfig->width, wndconfig->height); - - return GLFW_FALSE; - } - } - - window->mir.width = wndconfig->width; - window->mir.height = wndconfig->height; - - if (!createSurface(window)) - return GLFW_FALSE; - - window->mir.window = mir_buffer_stream_get_egl_native_window( - mir_surface_get_buffer_stream(window->mir.surface)); - - if (ctxconfig->client != GLFW_NO_API) - { - if (!_glfwInitEGL()) - return GLFW_FALSE; - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -void _glfwPlatformDestroyWindow(_GLFWwindow* window) -{ - if (mir_surface_is_valid(window->mir.surface)) - { - mir_surface_release_sync(window->mir.surface); - window->mir.surface = NULL; - } - - if (window->context.destroy) - window->context.destroy(window); -} - -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) -{ - MirSurfaceSpec* spec; - const char* e_title = title ? title : ""; - - spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); - mir_surface_spec_set_name(spec, e_title); - - mir_surface_apply_spec(window->mir.surface, spec); - mir_surface_spec_release(spec); -} - -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, - int count, const GLFWimage* images) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) -{ - MirSurfaceSpec* spec; - - spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); - mir_surface_spec_set_width (spec, width); - mir_surface_spec_set_height(spec, height); - - mir_surface_apply_spec(window->mir.surface, spec); - mir_surface_spec_release(spec); -} - -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) -{ - if (width) - *width = window->mir.width; - if (height) - *height = window->mir.height; -} - -void _glfwPlatformIconifyWindow(_GLFWwindow* window) -{ - mir_surface_set_state(window->mir.surface, mir_surface_state_minimized); -} - -void _glfwPlatformRestoreWindow(_GLFWwindow* window) -{ - mir_surface_set_state(window->mir.surface, mir_surface_state_restored); -} - -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformHideWindow(_GLFWwindow* window) -{ - MirSurfaceSpec* spec; - - spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); - mir_surface_spec_set_state(spec, mir_surface_state_hidden); - - mir_surface_apply_spec(window->mir.surface, spec); - mir_surface_spec_release(spec); -} - -void _glfwPlatformShowWindow(_GLFWwindow* window) -{ - MirSurfaceSpec* spec; - - spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); - mir_surface_spec_set_state(spec, mir_surface_state_restored); - - mir_surface_apply_spec(window->mir.surface, spec); - mir_surface_spec_release(spec); -} - -void _glfwPlatformFocusWindow(_GLFWwindow* window) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, - _GLFWmonitor* monitor, - int xpos, int ypos, - int width, int height, - int refreshRate) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -int _glfwPlatformWindowFocused(_GLFWwindow* window) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); - return GLFW_FALSE; -} - -int _glfwPlatformWindowIconified(_GLFWwindow* window) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); - return GLFW_FALSE; -} - -int _glfwPlatformWindowVisible(_GLFWwindow* window) -{ - return mir_surface_get_visibility(window->mir.surface) == mir_surface_visibility_exposed; -} - -int _glfwPlatformWindowMaximized(_GLFWwindow* window) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); - return GLFW_FALSE; -} - -void _glfwPlatformPollEvents(void) -{ - EventNode* node = NULL; - - while ((node = dequeueEvent(_glfw.mir.event_queue))) - { - handleEvent(node->event, node->window); - deleteNode(_glfw.mir.event_queue, node); - } -} - -void _glfwPlatformWaitEvents(void) -{ - pthread_mutex_lock(&_glfw.mir.event_mutex); - - if (emptyEventQueue(_glfw.mir.event_queue)) - pthread_cond_wait(&_glfw.mir.event_cond, &_glfw.mir.event_mutex); - - pthread_mutex_unlock(&_glfw.mir.event_mutex); - - _glfwPlatformPollEvents(); -} - -void _glfwPlatformWaitEventsTimeout(double timeout) -{ - pthread_mutex_lock(&_glfw.mir.event_mutex); - - if (emptyEventQueue(_glfw.mir.event_queue)) - { - struct timespec time; - clock_gettime(CLOCK_REALTIME, &time); - time.tv_sec += (long) timeout; - time.tv_nsec += (long) ((timeout - (long) timeout) * 1e9); - pthread_cond_timedwait(&_glfw.mir.event_cond, &_glfw.mir.event_mutex, &time); - } - - pthread_mutex_unlock(&_glfw.mir.event_mutex); - - _glfwPlatformPollEvents(); -} - -void _glfwPlatformPostEmptyEvent(void) -{ -} - -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) -{ - if (width) - *width = window->mir.width; - if (height) - *height = window->mir.height; -} - -// FIXME implement -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, - int xhot, int yhot) -{ - MirBufferStream* stream; - MirPixelFormat pixel_format = findValidPixelFormat(); - - int i_w = image->width; - int i_h = image->height; - - if (pixel_format == mir_pixel_format_invalid) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unable to find a correct pixel format"); - return GLFW_FALSE; - } - - stream = mir_connection_create_buffer_stream_sync(_glfw.mir.connection, - i_w, i_h, - pixel_format, - mir_buffer_usage_software); - - cursor->mir.conf = mir_cursor_configuration_from_buffer_stream(stream, xhot, yhot); - - char* dest; - unsigned char *pixels; - int i, r_stride, bytes_per_pixel, bytes_per_row; - - MirGraphicsRegion region; - mir_buffer_stream_get_graphics_region(stream, ®ion); - - // FIXME Figure this out based on the current_pf - bytes_per_pixel = 4; - bytes_per_row = bytes_per_pixel * i_w; - - dest = region.vaddr; - pixels = image->pixels; - - r_stride = region.stride; - - for (i = 0; i < i_h; i++) - { - memcpy(dest, pixels, bytes_per_row); - dest += r_stride; - pixels += r_stride; - } - - cursor->mir.custom_cursor = stream; - - return GLFW_TRUE; -} - -const char* getSystemCursorName(int shape) -{ - switch (shape) - { - case GLFW_ARROW_CURSOR: - return mir_arrow_cursor_name; - case GLFW_IBEAM_CURSOR: - return mir_caret_cursor_name; - case GLFW_CROSSHAIR_CURSOR: - return mir_crosshair_cursor_name; - case GLFW_HAND_CURSOR: - return mir_open_hand_cursor_name; - case GLFW_HRESIZE_CURSOR: - return mir_horizontal_resize_cursor_name; - case GLFW_VRESIZE_CURSOR: - return mir_vertical_resize_cursor_name; - } - - return NULL; -} - -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) -{ - const char* cursor_name = getSystemCursorName(shape); - - if (cursor_name) - { - cursor->mir.conf = mir_cursor_configuration_from_name(cursor_name); - cursor->mir.custom_cursor = NULL; - - return GLFW_TRUE; - } - - return GLFW_FALSE; -} - -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) -{ - if (cursor->mir.conf) - mir_cursor_configuration_destroy(cursor->mir.conf); - if (cursor->mir.custom_cursor) - mir_buffer_stream_release_sync(cursor->mir.custom_cursor); -} - -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) -{ - if (cursor && cursor->mir.conf) - { - mir_wait_for(mir_surface_configure_cursor(window->mir.surface, cursor->mir.conf)); - if (cursor->mir.custom_cursor) - { - mir_buffer_stream_swap_buffers_sync(cursor->mir.custom_cursor); - } - } - else - { - mir_wait_for(mir_surface_configure_cursor(window->mir.surface, _glfw.mir.default_conf)); - } -} - -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -const char* _glfwPlatformGetKeyName(int key, int scancode) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); - return NULL; -} - -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); - - return NULL; -} - -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) -{ - char** extensions; - - *count = 0; - - if (!_glfw.vk.KHR_mir_surface) - return NULL; - - extensions = calloc(2, sizeof(char*)); - extensions[0] = strdup("VK_KHR_surface"); - extensions[1] = strdup("VK_KHR_mir_surface"); - - *count = 2; - return extensions; -} - -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily) -{ - PFN_vkGetPhysicalDeviceMirPresentationSupportKHR vkGetPhysicalDeviceMirPresentationSupportKHR = - (PFN_vkGetPhysicalDeviceMirPresentationSupportKHR) - vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceMirPresentationSupportKHR"); - if (!vkGetPhysicalDeviceMirPresentationSupportKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Mir: Vulkan instance missing VK_KHR_mir_surface extension"); - return GLFW_FALSE; - } - - return vkGetPhysicalDeviceMirPresentationSupportKHR(device, - queuefamily, - _glfw.mir.connection); -} - -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) -{ - VkResult err; - VkMirSurfaceCreateInfoKHR sci; - PFN_vkCreateMirSurfaceKHR vkCreateMirSurfaceKHR; - - vkCreateMirSurfaceKHR = (PFN_vkCreateMirSurfaceKHR) - vkGetInstanceProcAddr(instance, "vkCreateMirSurfaceKHR"); - if (!vkCreateMirSurfaceKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Mir: Vulkan instance missing VK_KHR_mir_surface extension"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - memset(&sci, 0, sizeof(sci)); - sci.sType = VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR; - sci.connection = _glfw.mir.connection; - sci.mirSurface = window->mir.surface; - - err = vkCreateMirSurfaceKHR(instance, &sci, allocator, surface); - if (err) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Failed to create Vulkan surface: %s", - _glfwGetVulkanResultString(err)); - } - - return err; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI MirConnection* glfwGetMirDisplay(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfw.mir.connection; -} - -GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return window->mir.surface; -} - diff --git a/external/glfw/monitor.c b/external/glfw/monitor.c index df32a3c..d390a1c 100644 --- a/external/glfw/monitor.c +++ b/external/glfw/monitor.c @@ -2,7 +2,7 @@ // GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -86,83 +86,67 @@ static GLFWbool refreshVideoModes(_GLFWmonitor* monitor) ////// GLFW event API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwInputMonitorChange(void) +// Notifies shared code of a monitor connection or disconnection +// +void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) { - int i, j, monitorCount = _glfw.monitorCount; - _GLFWmonitor** monitors = _glfw.monitors; - - _glfw.monitors = _glfwPlatformGetMonitors(&_glfw.monitorCount); - - // Re-use still connected monitor objects - - for (i = 0; i < _glfw.monitorCount; i++) + if (action == GLFW_CONNECTED) { - for (j = 0; j < monitorCount; j++) + _glfw.monitorCount++; + _glfw.monitors = + realloc(_glfw.monitors, sizeof(_GLFWmonitor*) * _glfw.monitorCount); + + if (placement == _GLFW_INSERT_FIRST) { - if (_glfwPlatformIsSameMonitor(_glfw.monitors[i], monitors[j])) - { - _glfwFreeMonitor(_glfw.monitors[i]); - _glfw.monitors[i] = monitors[j]; - break; - } + memmove(_glfw.monitors + 1, + _glfw.monitors, + ((size_t) _glfw.monitorCount - 1) * sizeof(_GLFWmonitor*)); + _glfw.monitors[0] = monitor; } + else + _glfw.monitors[_glfw.monitorCount - 1] = monitor; } - - // Find and report disconnected monitors (not in the new list) - - for (i = 0; i < monitorCount; i++) + else if (action == GLFW_DISCONNECTED) { + int i; _GLFWwindow* window; - for (j = 0; j < _glfw.monitorCount; j++) - { - if (monitors[i] == _glfw.monitors[j]) - break; - } - - if (j < _glfw.monitorCount) - continue; - for (window = _glfw.windowListHead; window; window = window->next) { - if (window->monitor == monitors[i]) + if (window->monitor == monitor) { - int width, height; + int width, height, xoff, yoff; _glfwPlatformGetWindowSize(window, &width, &height); _glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0); + _glfwPlatformGetWindowFrameSize(window, &xoff, &yoff, NULL, NULL); + _glfwPlatformSetWindowPos(window, xoff, yoff); } } - if (_glfw.callbacks.monitor) - _glfw.callbacks.monitor((GLFWmonitor*) monitors[i], GLFW_DISCONNECTED); - } - - // Find and report newly connected monitors (not in the old list) - // Re-used monitor objects are then removed from the old list to avoid - // having them destroyed at the end of this function - - for (i = 0; i < _glfw.monitorCount; i++) - { - for (j = 0; j < monitorCount; j++) + for (i = 0; i < _glfw.monitorCount; i++) { - if (_glfw.monitors[i] == monitors[j]) + if (_glfw.monitors[i] == monitor) { - monitors[j] = NULL; + _glfw.monitorCount--; + memmove(_glfw.monitors + i, + _glfw.monitors + i + 1, + ((size_t) _glfw.monitorCount - i) * sizeof(_GLFWmonitor*)); break; } } - - if (j < monitorCount) - continue; - - if (_glfw.callbacks.monitor) - _glfw.callbacks.monitor((GLFWmonitor*) _glfw.monitors[i], GLFW_CONNECTED); } - _glfwFreeMonitors(monitors, monitorCount); + if (_glfw.callbacks.monitor) + _glfw.callbacks.monitor((GLFWmonitor*) monitor, action); + + if (action == GLFW_DISCONNECTED) + _glfwFreeMonitor(monitor); } -void _glfwInputMonitorWindowChange(_GLFWmonitor* monitor, _GLFWwindow* window) +// Notifies shared code that a full screen window has acquired or released +// a monitor +// +void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window) { monitor->window = window; } @@ -172,21 +156,29 @@ void _glfwInputMonitorWindowChange(_GLFWmonitor* monitor, _GLFWwindow* window) ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// +// Allocates and returns a monitor object with the specified name and dimensions +// _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM) { _GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor)); - monitor->name = strdup(name); monitor->widthMM = widthMM; monitor->heightMM = heightMM; + if (name) + monitor->name = _glfw_strdup(name); + return monitor; } +// Frees a monitor object and any data associated with it +// void _glfwFreeMonitor(_GLFWmonitor* monitor) { if (monitor == NULL) return; + _glfwPlatformFreeMonitor(monitor); + _glfwFreeGammaArrays(&monitor->originalRamp); _glfwFreeGammaArrays(&monitor->currentRamp); @@ -195,6 +187,8 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor) free(monitor); } +// Allocates red, green and blue value arrays of the specified size +// void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size) { ramp->red = calloc(size, sizeof(unsigned short)); @@ -203,6 +197,8 @@ void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size) ramp->size = size; } +// Frees the red, green and blue value arrays and clears the struct +// void _glfwFreeGammaArrays(GLFWgammaramp* ramp) { free(ramp->red); @@ -212,16 +208,8 @@ void _glfwFreeGammaArrays(GLFWgammaramp* ramp) memset(ramp, 0, sizeof(GLFWgammaramp)); } -void _glfwFreeMonitors(_GLFWmonitor** monitors, int count) -{ - int i; - - for (i = 0; i < count; i++) - _glfwFreeMonitor(monitors[i]); - - free(monitors); -} - +// Chooses the video mode most closely matching the desired one +// const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, const GLFWvidmode* desired) { @@ -272,11 +260,15 @@ const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, return closest; } +// Performs lexical comparison between two @ref GLFWvidmode structures +// int _glfwCompareVideoModes(const GLFWvidmode* fm, const GLFWvidmode* sm) { return compareVideoModes(fm, sm); } +// Splits a color depth into red, green and blue bit depths +// void _glfwSplitBPP(int bpp, int* red, int* green, int* blue) { int delta; @@ -304,6 +296,7 @@ void _glfwSplitBPP(int bpp, int* red, int* green, int* blue) GLFWAPI GLFWmonitor** glfwGetMonitors(int* count) { assert(count != NULL); + *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); @@ -337,6 +330,27 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos) _glfwPlatformGetMonitorPos(monitor, xpos, ypos); } +GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle, + int* xpos, int* ypos, + int* width, int* height) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + if (width) + *width = 0; + if (height) + *height = 0; + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformGetMonitorWorkarea(monitor, xpos, ypos, width, height); +} + GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; @@ -355,6 +369,21 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* *heightMM = monitor->heightMM; } +GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle, + float* xscale, float* yscale) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (xscale) + *xscale = 0.f; + if (yscale) + *yscale = 0.f; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetMonitorContentScale(monitor, xscale, yscale); +} + GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; @@ -364,6 +393,24 @@ GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) return monitor->name; } +GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT(); + monitor->userPointer = pointer; +} + +GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return monitor->userPointer; +} + GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); @@ -401,9 +448,13 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle) GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) { - int i; - unsigned short values[256]; + unsigned int i; + unsigned short* values; GLFWgammaramp ramp; + const GLFWgammaramp* original; + assert(handle != NULL); + assert(gamma > 0.f); + assert(gamma <= FLT_MAX); _GLFW_REQUIRE_INIT(); @@ -413,18 +464,22 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) return; } - for (i = 0; i < 256; i++) + original = glfwGetGammaRamp(handle); + if (!original) + return; + + values = calloc(original->size, sizeof(unsigned short)); + + for (i = 0; i < original->size; i++) { - double value; + float value; // Calculate intensity - value = i / 255.0; + value = i / (float) (original->size - 1); // Apply gamma curve - value = pow(value, 1.0 / gamma) * 65535.0 + 0.5; - + value = powf(value, 1.f / gamma) * 65535.f + 0.5f; // Clamp to value range - if (value > 65535.0) - value = 65535.0; + value = _glfw_fminf(value, 65535.f); values[i] = (unsigned short) value; } @@ -432,9 +487,10 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) ramp.red = values; ramp.green = values; ramp.blue = values; - ramp.size = 256; + ramp.size = original->size; glfwSetGammaRamp(handle, &ramp); + free(values); } GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) @@ -445,7 +501,8 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _glfwFreeGammaArrays(&monitor->currentRamp); - _glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp); + if (!_glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp)) + return NULL; return &monitor->currentRamp; } @@ -455,6 +512,7 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); assert(ramp != NULL); + assert(ramp->size > 0); assert(ramp->red != NULL); assert(ramp->green != NULL); assert(ramp->blue != NULL); @@ -470,7 +528,10 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) _GLFW_REQUIRE_INIT(); if (!monitor->originalRamp.size) - _glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp); + { + if (!_glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp)) + return; + } _glfwPlatformSetGammaRamp(monitor, ramp); } diff --git a/external/glfw/nsgl_context.h b/external/glfw/nsgl_context.h index aa42d23..2485b18 100644 --- a/external/glfw/nsgl_context.h +++ b/external/glfw/nsgl_context.h @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -24,19 +24,27 @@ // //======================================================================== -#ifndef _glfw3_nsgl_context_h_ -#define _glfw3_nsgl_context_h_ +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101400 + #define NSOpenGLContextParameterSwapInterval NSOpenGLCPSwapInterval + #define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity +#endif #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl +#include + // NSGL-specific per-context data // typedef struct _GLFWcontextNSGL { - id pixelFormat; - id object; + id pixelFormat; + id object; + CVDisplayLinkRef displayLink; + atomic_int swapInterval; + int swapIntervalsPassed; + id swapIntervalCond; } _GLFWcontextNSGL; @@ -56,5 +64,5 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); void _glfwDestroyContextNSGL(_GLFWwindow* window); +void _glfwUpdateDisplayLinkDisplayNSGL(_GLFWwindow* window); -#endif // _glfw3_nsgl_context_h_ diff --git a/external/glfw/nsgl_context.m b/external/glfw/nsgl_context.m index 454a773..bbbbd36 100644 --- a/external/glfw/nsgl_context.m +++ b/external/glfw/nsgl_context.m @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -26,30 +26,75 @@ #include "internal.h" +// Display link callback for manual swap interval implementation +// This is based on a similar workaround added to SDL2 +// +static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, + const CVTimeStamp* now, + const CVTimeStamp* outputTime, + CVOptionFlags flagsIn, + CVOptionFlags* flagsOut, + void* userInfo) +{ + _GLFWwindow* window = (_GLFWwindow *) userInfo; + + const int interval = atomic_load(&window->context.nsgl.swapInterval); + if (interval > 0) + { + [window->context.nsgl.swapIntervalCond lock]; + window->context.nsgl.swapIntervalsPassed++; + [window->context.nsgl.swapIntervalCond signal]; + [window->context.nsgl.swapIntervalCond unlock]; + } + + return kCVReturnSuccess; +} static void makeContextCurrentNSGL(_GLFWwindow* window) { + @autoreleasepool { + if (window) [window->context.nsgl.object makeCurrentContext]; else [NSOpenGLContext clearCurrentContext]; - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.contextSlot, window); + + } // autoreleasepool } static void swapBuffersNSGL(_GLFWwindow* window) { + @autoreleasepool { + + const int interval = atomic_load(&window->context.nsgl.swapInterval); + if (interval > 0) + { + [window->context.nsgl.swapIntervalCond lock]; + do + { + [window->context.nsgl.swapIntervalCond wait]; + } while (window->context.nsgl.swapIntervalsPassed % interval != 0); + window->context.nsgl.swapIntervalsPassed = 0; + [window->context.nsgl.swapIntervalCond unlock]; + } + // ARP appears to be unnecessary, but this is future-proof [window->context.nsgl.object flushBuffer]; + + } // autoreleasepool } static void swapIntervalNSGL(int interval) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); - - GLint sync = interval; - [window->context.nsgl.object setValues:&sync - forParameter:NSOpenGLCPSwapInterval]; + @autoreleasepool { + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); + atomic_store(&window->context.nsgl.swapInterval, interval); + [window->context.nsgl.swapIntervalCond lock]; + window->context.nsgl.swapIntervalsPassed = 0; + [window->context.nsgl.swapIntervalCond unlock]; + } // autoreleasepool } static int extensionSupportedNSGL(const char* extension) @@ -76,11 +121,26 @@ static GLFWglproc getProcAddressNSGL(const char* procname) // static void destroyContextNSGL(_GLFWwindow* window) { + @autoreleasepool { + + if (window->context.nsgl.displayLink) + { + if (CVDisplayLinkIsRunning(window->context.nsgl.displayLink)) + CVDisplayLinkStop(window->context.nsgl.displayLink); + + CVDisplayLinkRelease(window->context.nsgl.displayLink); + } + + [window->context.nsgl.swapIntervalCond release]; + window->context.nsgl.swapIntervalCond = nil; + [window->context.nsgl.pixelFormat release]; window->context.nsgl.pixelFormat = nil; [window->context.nsgl.object release]; window->context.nsgl.object = nil; + + } // autoreleasepool } @@ -119,70 +179,81 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { - unsigned int attributeCount = 0; - if (ctxconfig->client == GLFW_OPENGL_ES_API) { _glfwInputError(GLFW_API_UNAVAILABLE, - "NSGL: OpenGL ES is not available on OS X"); - return GLFW_FALSE; - } - - if (ctxconfig->major == 3 && ctxconfig->minor < 2) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "NSGL: The targeted version of OS X does not support OpenGL 3.0 or 3.1"); + "NSGL: OpenGL ES is not available on macOS"); return GLFW_FALSE; } if (ctxconfig->major > 2) { - if (!ctxconfig->forward) + if (ctxconfig->major == 3 && ctxconfig->minor < 2) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "NSGL: The targeted version of OS X only supports forward-compatible contexts for OpenGL 3.2 and above"); + "NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1 but may support 3.2 and above"); return GLFW_FALSE; } - if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE) + if (!ctxconfig->forward || ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "NSGL: The targeted version of OS X only supports core profile contexts for OpenGL 3.2 and above"); + "NSGL: The targeted version of macOS only supports forward-compatible core profile contexts for OpenGL 3.2 and above"); return GLFW_FALSE; } } - // Context robustness modes (GL_KHR_robustness) are not yet supported on - // OS X but are not a hard constraint, so ignore and continue + // Context robustness modes (GL_KHR_robustness) are not yet supported by + // macOS but are not a hard constraint, so ignore and continue // Context release behaviors (GL_KHR_context_flush_control) are not yet - // supported on OS X but are not a hard constraint, so ignore and continue + // supported by macOS but are not a hard constraint, so ignore and continue -#define ADD_ATTR(x) { attributes[attributeCount++] = x; } -#define ADD_ATTR2(x, y) { ADD_ATTR(x); ADD_ATTR(y); } + // Debug contexts (GL_KHR_debug) are not yet supported by macOS but are not + // a hard constraint, so ignore and continue - // Arbitrary array size here - NSOpenGLPixelFormatAttribute attributes[40]; + // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but + // are not a hard constraint, so ignore and continue - ADD_ATTR(NSOpenGLPFAAccelerated); - ADD_ATTR(NSOpenGLPFAClosestPolicy); +#define addAttrib(a) \ +{ \ + assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ +} +#define setAttrib(a, v) { addAttrib(a); addAttrib(v); } + + NSOpenGLPixelFormatAttribute attribs[40]; + int index = 0; + + addAttrib(NSOpenGLPFAAccelerated); + addAttrib(NSOpenGLPFAClosestPolicy); + + if (ctxconfig->nsgl.offline) + { + addAttrib(NSOpenGLPFAAllowOfflineRenderers); + // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in + // Info.plist for unbundled applications + // HACK: This assumes that NSOpenGLPixelFormat will remain + // a straightforward wrapper of its CGL counterpart + addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching); + } #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 if (ctxconfig->major >= 4) { - ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); + setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); } else #endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ if (ctxconfig->major >= 3) { - ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); + setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); } if (ctxconfig->major <= 2) { if (fbconfig->auxBuffers != GLFW_DONT_CARE) - ADD_ATTR2(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); + setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); if (fbconfig->accumRedBits != GLFW_DONT_CARE && fbconfig->accumGreenBits != GLFW_DONT_CARE && @@ -194,7 +265,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, fbconfig->accumBlueBits + fbconfig->accumAlphaBits; - ADD_ATTR2(NSOpenGLPFAAccumSize, accumBits); + setAttrib(NSOpenGLPFAAccumSize, accumBits); } } @@ -206,53 +277,61 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, fbconfig->greenBits + fbconfig->blueBits; - // OS X needs non-zero color size, so set reasonable values + // macOS needs non-zero color size, so set reasonable values if (colorBits == 0) colorBits = 24; else if (colorBits < 15) colorBits = 15; - ADD_ATTR2(NSOpenGLPFAColorSize, colorBits); + setAttrib(NSOpenGLPFAColorSize, colorBits); } if (fbconfig->alphaBits != GLFW_DONT_CARE) - ADD_ATTR2(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); + setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); if (fbconfig->depthBits != GLFW_DONT_CARE) - ADD_ATTR2(NSOpenGLPFADepthSize, fbconfig->depthBits); + setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits); if (fbconfig->stencilBits != GLFW_DONT_CARE) - ADD_ATTR2(NSOpenGLPFAStencilSize, fbconfig->stencilBits); + setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits); if (fbconfig->stereo) - ADD_ATTR(NSOpenGLPFAStereo); + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "NSGL: Stereo rendering is deprecated"); + return GLFW_FALSE; +#else + addAttrib(NSOpenGLPFAStereo); +#endif + } if (fbconfig->doublebuffer) - ADD_ATTR(NSOpenGLPFADoubleBuffer); + addAttrib(NSOpenGLPFADoubleBuffer); if (fbconfig->samples != GLFW_DONT_CARE) { if (fbconfig->samples == 0) { - ADD_ATTR2(NSOpenGLPFASampleBuffers, 0); + setAttrib(NSOpenGLPFASampleBuffers, 0); } else { - ADD_ATTR2(NSOpenGLPFASampleBuffers, 1); - ADD_ATTR2(NSOpenGLPFASamples, fbconfig->samples); + setAttrib(NSOpenGLPFASampleBuffers, 1); + setAttrib(NSOpenGLPFASamples, fbconfig->samples); } } // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB // framebuffer, so there's no need (and no way) to request it - ADD_ATTR(0); + addAttrib(0); -#undef ADD_ATTR -#undef ADD_ATTR2 +#undef addAttrib +#undef setAttrib window->context.nsgl.pixelFormat = - [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; if (window->context.nsgl.pixelFormat == nil) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, @@ -275,8 +354,24 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, return GLFW_FALSE; } + if (fbconfig->transparent) + { + GLint opaque = 0; + [window->context.nsgl.object setValues:&opaque + forParameter:NSOpenGLContextParameterSurfaceOpacity]; + } + + if (window->ns.retina) + [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; + + GLint interval = 0; + [window->context.nsgl.object setValues:&interval + forParameter:NSOpenGLContextParameterSwapInterval]; + [window->context.nsgl.object setView:window->ns.view]; + window->context.nsgl.swapIntervalCond = [NSCondition new]; + window->context.makeCurrent = makeContextCurrentNSGL; window->context.swapBuffers = swapBuffersNSGL; window->context.swapInterval = swapIntervalNSGL; @@ -284,9 +379,26 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, window->context.getProcAddress = getProcAddressNSGL; window->context.destroy = destroyContextNSGL; + CVDisplayLinkCreateWithActiveCGDisplays(&window->context.nsgl.displayLink); + CVDisplayLinkSetOutputCallback(window->context.nsgl.displayLink, + &displayLinkCallback, + window); + CVDisplayLinkStart(window->context.nsgl.displayLink); + + _glfwUpdateDisplayLinkDisplayNSGL(window); return GLFW_TRUE; } +void _glfwUpdateDisplayLinkDisplayNSGL(_GLFWwindow* window) +{ + CGDirectDisplayID displayID = + [[[window->ns.object screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue]; + if (!displayID) + return; + + CVDisplayLinkSetCurrentCGDisplay(window->context.nsgl.displayLink, displayID); +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// diff --git a/external/glfw/posix_tls.c b/external/glfw/null_init.c similarity index 57% rename from external/glfw/posix_tls.c rename to external/glfw/null_init.c index 3edade5..b48477b 100644 --- a/external/glfw/posix_tls.c +++ b/external/glfw/null_init.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.3 POSIX - www.glfw.org +// GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2016-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -28,41 +28,23 @@ #include "internal.h" -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWbool _glfwInitThreadLocalStoragePOSIX(void) -{ - if (pthread_key_create(&_glfw.posix_tls.context, NULL) != 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "POSIX: Failed to create context TLS"); - return GLFW_FALSE; - } - - _glfw.posix_tls.allocated = GLFW_TRUE; - return GLFW_TRUE; -} - -void _glfwTerminateThreadLocalStoragePOSIX(void) -{ - if (_glfw.posix_tls.allocated) - pthread_key_delete(_glfw.posix_tls.context); -} - - ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwPlatformSetCurrentContext(_GLFWwindow* context) +int _glfwPlatformInit(void) { - pthread_setspecific(_glfw.posix_tls.context, context); + _glfwInitTimerPOSIX(); + return GLFW_TRUE; } -_GLFWwindow* _glfwPlatformGetCurrentContext(void) +void _glfwPlatformTerminate(void) { - return pthread_getspecific(_glfw.posix_tls.context); + _glfwTerminateOSMesa(); +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " null OSMesa"; } diff --git a/external/glfw/null_joystick.c b/external/glfw/null_joystick.c new file mode 100644 index 0000000..60180bc --- /dev/null +++ b/external/glfw/null_joystick.c @@ -0,0 +1,42 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) +{ + return GLFW_FALSE; +} + +void _glfwPlatformUpdateGamepadGUID(char* guid) +{ +} + diff --git a/external/glfw/null_joystick.h b/external/glfw/null_joystick.h new file mode 100644 index 0000000..5a5c558 --- /dev/null +++ b/external/glfw/null_joystick.h @@ -0,0 +1,31 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#define _GLFW_PLATFORM_JOYSTICK_STATE int nulljs +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE int nulljs + +#define _GLFW_PLATFORM_MAPPING_NAME "" + diff --git a/external/glfw/win32_tls.c b/external/glfw/null_monitor.c similarity index 56% rename from external/glfw/win32_tls.c rename to external/glfw/null_monitor.c index 0607ca6..f5cb092 100644 --- a/external/glfw/win32_tls.c +++ b/external/glfw/null_monitor.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.3 Win32 - www.glfw.org +// GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2016-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -28,42 +28,48 @@ #include "internal.h" -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWbool _glfwInitThreadLocalStorageWin32(void) -{ - _glfw.win32_tls.context = TlsAlloc(); - if (_glfw.win32_tls.context == TLS_OUT_OF_INDEXES) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to allocate TLS index"); - return GLFW_FALSE; - } - - _glfw.win32_tls.allocated = GLFW_TRUE; - return GLFW_TRUE; -} - -void _glfwTerminateThreadLocalStorageWin32(void) -{ - if (_glfw.win32_tls.allocated) - TlsFree(_glfw.win32_tls.context); -} - - ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwPlatformSetCurrentContext(_GLFWwindow* context) +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) { - TlsSetValue(_glfw.win32_tls.context, context); } -_GLFWwindow* _glfwPlatformGetCurrentContext(void) +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +{ +} + +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) +{ +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) +{ + return NULL; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +{ +} + +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ + return GLFW_FALSE; +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { - return TlsGetValue(_glfw.win32_tls.context); } diff --git a/external/glfw/null_platform.h b/external/glfw/null_platform.h new file mode 100644 index 0000000..7871683 --- /dev/null +++ b/external/glfw/null_platform.h @@ -0,0 +1,62 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2016-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNull null + +#define _GLFW_PLATFORM_CONTEXT_STATE +#define _GLFW_PLATFORM_MONITOR_STATE +#define _GLFW_PLATFORM_CURSOR_STATE +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE +#define _GLFW_EGL_CONTEXT_STATE +#define _GLFW_EGL_LIBRARY_CONTEXT_STATE + +#include "osmesa_context.h" +#include "posix_time.h" +#include "posix_thread.h" +#include "null_joystick.h" + +#if defined(_GLFW_WIN32) + #define _glfw_dlopen(name) LoadLibraryA(name) + #define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) + #define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) +#else + #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) + #define _glfw_dlclose(handle) dlclose(handle) + #define _glfw_dlsym(handle, name) dlsym(handle, name) +#endif + +// Null-specific per-window data +// +typedef struct _GLFWwindowNull +{ + int width; + int height; +} _GLFWwindowNull; + diff --git a/external/glfw/null_window.c b/external/glfw/null_window.c new file mode 100644 index 0000000..67021ab --- /dev/null +++ b/external/glfw/null_window.c @@ -0,0 +1,330 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2016-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + + +static int createNativeWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig) +{ + window->null.width = wndconfig->width; + window->null.height = wndconfig->height; + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + if (!createNativeWindow(window, wndconfig)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API || + ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else + { + _glfwInputError(GLFW_API_UNAVAILABLE, "Null: EGL not available"); + return GLFW_FALSE; + } + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (window->context.destroy) + window->context.destroy(window); +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, + const GLFWimage* images) +{ +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = window->null.width; + if (height) + *height = window->null.height; +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + window->null.width = width; + window->null.height = height; +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n, int d) +{ +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = window->null.width; + if (height) + *height = window->null.height; +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ +} + +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + +void _glfwPlatformIconifyWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + return 1.f; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ +} + +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +{ +} + +GLFWbool _glfwPlatformRawMouseMotionSupported(void) +{ + return GLFW_FALSE; +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ +} + + +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ +} + +void _glfwPlatformUnhideWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +void _glfwPlatformPollEvents(void) +{ +} + +void _glfwPlatformWaitEvents(void) +{ +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ +} + +void _glfwPlatformPostEmptyEvent(void) +{ +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +{ +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + return GLFW_TRUE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + return GLFW_TRUE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ +} + +void _glfwPlatformSetClipboardString(const char* string) +{ +} + +const char* _glfwPlatformGetClipboardString(void) +{ + return NULL; +} + +const char* _glfwPlatformGetScancodeName(int scancode) +{ + return ""; +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return -1; +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + return GLFW_FALSE; +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + // This seems like the most appropriate error to return here + return VK_ERROR_INITIALIZATION_FAILED; +} + diff --git a/external/glfw/osmesa_context.c b/external/glfw/osmesa_context.c new file mode 100644 index 0000000..b45bb2e --- /dev/null +++ b/external/glfw/osmesa_context.c @@ -0,0 +1,370 @@ +//======================================================================== +// GLFW 3.3 OSMesa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2016-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include +#include +#include + +#include "internal.h" + + +static void makeContextCurrentOSMesa(_GLFWwindow* window) +{ + if (window) + { + int width, height; + _glfwPlatformGetFramebufferSize(window, &width, &height); + + // Check to see if we need to allocate a new buffer + if ((window->context.osmesa.buffer == NULL) || + (width != window->context.osmesa.width) || + (height != window->context.osmesa.height)) + { + free(window->context.osmesa.buffer); + + // Allocate the new buffer (width * height * 8-bit RGBA) + window->context.osmesa.buffer = calloc(4, (size_t) width * height); + window->context.osmesa.width = width; + window->context.osmesa.height = height; + } + + if (!OSMesaMakeCurrent(window->context.osmesa.handle, + window->context.osmesa.buffer, + GL_UNSIGNED_BYTE, + width, height)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to make context current"); + return; + } + } + + _glfwPlatformSetTls(&_glfw.contextSlot, window); +} + +static GLFWglproc getProcAddressOSMesa(const char* procname) +{ + return (GLFWglproc) OSMesaGetProcAddress(procname); +} + +static void destroyContextOSMesa(_GLFWwindow* window) +{ + if (window->context.osmesa.handle) + { + OSMesaDestroyContext(window->context.osmesa.handle); + window->context.osmesa.handle = NULL; + } + + if (window->context.osmesa.buffer) + { + free(window->context.osmesa.buffer); + window->context.osmesa.width = 0; + window->context.osmesa.height = 0; + } +} + +static void swapBuffersOSMesa(_GLFWwindow* window) +{ + // No double buffering on OSMesa +} + +static void swapIntervalOSMesa(int interval) +{ + // No swap interval on OSMesa +} + +static int extensionSupportedOSMesa(const char* extension) +{ + // OSMesa does not have extensions + return GLFW_FALSE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwInitOSMesa(void) +{ + int i; + const char* sonames[] = + { +#if defined(_GLFW_OSMESA_LIBRARY) + _GLFW_OSMESA_LIBRARY, +#elif defined(_WIN32) + "libOSMesa.dll", + "OSMesa.dll", +#elif defined(__APPLE__) + "libOSMesa.8.dylib", +#elif defined(__CYGWIN__) + "libOSMesa-8.so", +#else + "libOSMesa.so.8", + "libOSMesa.so.6", +#endif + NULL + }; + + if (_glfw.osmesa.handle) + return GLFW_TRUE; + + for (i = 0; sonames[i]; i++) + { + _glfw.osmesa.handle = _glfw_dlopen(sonames[i]); + if (_glfw.osmesa.handle) + break; + } + + if (!_glfw.osmesa.handle) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: Library not found"); + return GLFW_FALSE; + } + + _glfw.osmesa.CreateContextExt = (PFN_OSMesaCreateContextExt) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextExt"); + _glfw.osmesa.CreateContextAttribs = (PFN_OSMesaCreateContextAttribs) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextAttribs"); + _glfw.osmesa.DestroyContext = (PFN_OSMesaDestroyContext) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaDestroyContext"); + _glfw.osmesa.MakeCurrent = (PFN_OSMesaMakeCurrent) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaMakeCurrent"); + _glfw.osmesa.GetColorBuffer = (PFN_OSMesaGetColorBuffer) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetColorBuffer"); + _glfw.osmesa.GetDepthBuffer = (PFN_OSMesaGetDepthBuffer) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetDepthBuffer"); + _glfw.osmesa.GetProcAddress = (PFN_OSMesaGetProcAddress) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress"); + + if (!_glfw.osmesa.CreateContextExt || + !_glfw.osmesa.DestroyContext || + !_glfw.osmesa.MakeCurrent || + !_glfw.osmesa.GetColorBuffer || + !_glfw.osmesa.GetDepthBuffer || + !_glfw.osmesa.GetProcAddress) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to load required entry points"); + + _glfwTerminateOSMesa(); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +void _glfwTerminateOSMesa(void) +{ + if (_glfw.osmesa.handle) + { + _glfw_dlclose(_glfw.osmesa.handle); + _glfw.osmesa.handle = NULL; + } +} + +#define setAttrib(a, v) \ +{ \ + assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ +} + +GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + OSMesaContext share = NULL; + const int accumBits = fbconfig->accumRedBits + + fbconfig->accumGreenBits + + fbconfig->accumBlueBits + + fbconfig->accumAlphaBits; + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "OSMesa: OpenGL ES is not available on OSMesa"); + return GLFW_FALSE; + } + + if (ctxconfig->share) + share = ctxconfig->share->context.osmesa.handle; + + if (OSMesaCreateContextAttribs) + { + int index = 0, attribs[40]; + + setAttrib(OSMESA_FORMAT, OSMESA_RGBA); + setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits); + setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits); + setAttrib(OSMESA_ACCUM_BITS, accumBits); + + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + { + setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE); + } + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + { + setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); + } + + if (ctxconfig->major != 1 || ctxconfig->minor != 0) + { + setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); + setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); + } + + if (ctxconfig->forward) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: Forward-compatible contexts not supported"); + return GLFW_FALSE; + } + + setAttrib(0, 0); + + window->context.osmesa.handle = + OSMesaCreateContextAttribs(attribs, share); + } + else + { + if (ctxconfig->profile) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: OpenGL profiles unavailable"); + return GLFW_FALSE; + } + + window->context.osmesa.handle = + OSMesaCreateContextExt(OSMESA_RGBA, + fbconfig->depthBits, + fbconfig->stencilBits, + accumBits, + share); + } + + if (window->context.osmesa.handle == NULL) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: Failed to create context"); + return GLFW_FALSE; + } + + window->context.makeCurrent = makeContextCurrentOSMesa; + window->context.swapBuffers = swapBuffersOSMesa; + window->context.swapInterval = swapIntervalOSMesa; + window->context.extensionSupported = extensionSupportedOSMesa; + window->context.getProcAddress = getProcAddressOSMesa; + window->context.destroy = destroyContextOSMesa; + + return GLFW_TRUE; +} + +#undef setAttrib + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width, + int* height, int* format, void** buffer) +{ + void* mesaBuffer; + GLint mesaWidth, mesaHeight, mesaFormat; + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!OSMesaGetColorBuffer(window->context.osmesa.handle, + &mesaWidth, &mesaHeight, + &mesaFormat, &mesaBuffer)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to retrieve color buffer"); + return GLFW_FALSE; + } + + if (width) + *width = mesaWidth; + if (height) + *height = mesaHeight; + if (format) + *format = mesaFormat; + if (buffer) + *buffer = mesaBuffer; + + return GLFW_TRUE; +} + +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle, + int* width, int* height, + int* bytesPerValue, + void** buffer) +{ + void* mesaBuffer; + GLint mesaWidth, mesaHeight, mesaBytes; + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!OSMesaGetDepthBuffer(window->context.osmesa.handle, + &mesaWidth, &mesaHeight, + &mesaBytes, &mesaBuffer)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to retrieve depth buffer"); + return GLFW_FALSE; + } + + if (width) + *width = mesaWidth; + if (height) + *height = mesaHeight; + if (bytesPerValue) + *bytesPerValue = mesaBytes; + if (buffer) + *buffer = mesaBuffer; + + return GLFW_TRUE; +} + +GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return NULL; + } + + return window->context.osmesa.handle; +} + diff --git a/external/glfw/osmesa_context.h b/external/glfw/osmesa_context.h new file mode 100644 index 0000000..2413188 --- /dev/null +++ b/external/glfw/osmesa_context.h @@ -0,0 +1,94 @@ +//======================================================================== +// GLFW 3.3 OSMesa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2016-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#define OSMESA_RGBA 0x1908 +#define OSMESA_FORMAT 0x22 +#define OSMESA_DEPTH_BITS 0x30 +#define OSMESA_STENCIL_BITS 0x31 +#define OSMESA_ACCUM_BITS 0x32 +#define OSMESA_PROFILE 0x33 +#define OSMESA_CORE_PROFILE 0x34 +#define OSMESA_COMPAT_PROFILE 0x35 +#define OSMESA_CONTEXT_MAJOR_VERSION 0x36 +#define OSMESA_CONTEXT_MINOR_VERSION 0x37 + +typedef void* OSMesaContext; +typedef void (*OSMESAproc)(void); + +typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); +typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); +typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext); +typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int); +typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**); +typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**); +typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); +#define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt +#define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs +#define OSMesaDestroyContext _glfw.osmesa.DestroyContext +#define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent +#define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer +#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer +#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress + +#define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa +#define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa + + +// OSMesa-specific per-context data +// +typedef struct _GLFWcontextOSMesa +{ + OSMesaContext handle; + int width; + int height; + void* buffer; + +} _GLFWcontextOSMesa; + +// OSMesa-specific global data +// +typedef struct _GLFWlibraryOSMesa +{ + void* handle; + + PFN_OSMesaCreateContextExt CreateContextExt; + PFN_OSMesaCreateContextAttribs CreateContextAttribs; + PFN_OSMesaDestroyContext DestroyContext; + PFN_OSMesaMakeCurrent MakeCurrent; + PFN_OSMesaGetColorBuffer GetColorBuffer; + PFN_OSMesaGetDepthBuffer GetDepthBuffer; + PFN_OSMesaGetProcAddress GetProcAddress; + +} _GLFWlibraryOSMesa; + + +GLFWbool _glfwInitOSMesa(void); +void _glfwTerminateOSMesa(void); +GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); + diff --git a/external/glfw/posix_thread.c b/external/glfw/posix_thread.c new file mode 100644 index 0000000..ff4ea60 --- /dev/null +++ b/external/glfw/posix_thread.c @@ -0,0 +1,103 @@ +//======================================================================== +// GLFW 3.3 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) +{ + assert(tls->posix.allocated == GLFW_FALSE); + + if (pthread_key_create(&tls->posix.key, NULL) != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "POSIX: Failed to create context TLS"); + return GLFW_FALSE; + } + + tls->posix.allocated = GLFW_TRUE; + return GLFW_TRUE; +} + +void _glfwPlatformDestroyTls(_GLFWtls* tls) +{ + if (tls->posix.allocated) + pthread_key_delete(tls->posix.key); + memset(tls, 0, sizeof(_GLFWtls)); +} + +void* _glfwPlatformGetTls(_GLFWtls* tls) +{ + assert(tls->posix.allocated == GLFW_TRUE); + return pthread_getspecific(tls->posix.key); +} + +void _glfwPlatformSetTls(_GLFWtls* tls, void* value) +{ + assert(tls->posix.allocated == GLFW_TRUE); + pthread_setspecific(tls->posix.key, value); +} + +GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex) +{ + assert(mutex->posix.allocated == GLFW_FALSE); + + if (pthread_mutex_init(&mutex->posix.handle, NULL) != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "POSIX: Failed to create mutex"); + return GLFW_FALSE; + } + + return mutex->posix.allocated = GLFW_TRUE; +} + +void _glfwPlatformDestroyMutex(_GLFWmutex* mutex) +{ + if (mutex->posix.allocated) + pthread_mutex_destroy(&mutex->posix.handle); + memset(mutex, 0, sizeof(_GLFWmutex)); +} + +void _glfwPlatformLockMutex(_GLFWmutex* mutex) +{ + assert(mutex->posix.allocated == GLFW_TRUE); + pthread_mutex_lock(&mutex->posix.handle); +} + +void _glfwPlatformUnlockMutex(_GLFWmutex* mutex) +{ + assert(mutex->posix.allocated == GLFW_TRUE); + pthread_mutex_unlock(&mutex->posix.handle); +} + diff --git a/external/glfw/posix_tls.h b/external/glfw/posix_thread.h similarity index 76% rename from external/glfw/posix_tls.h rename to external/glfw/posix_thread.h index f79ba98..24452ba 100644 --- a/external/glfw/posix_tls.h +++ b/external/glfw/posix_thread.h @@ -2,7 +2,7 @@ // GLFW 3.3 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,25 +25,27 @@ // //======================================================================== -#ifndef _glfw3_posix_tls_h_ -#define _glfw3_posix_tls_h_ - #include -#define _GLFW_PLATFORM_LIBRARY_TLS_STATE _GLFWtlsPOSIX posix_tls +#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix +#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexPOSIX posix -// POSIX-specific global TLS data +// POSIX-specific thread local storage data // typedef struct _GLFWtlsPOSIX { GLFWbool allocated; - pthread_key_t context; + pthread_key_t key; } _GLFWtlsPOSIX; +// POSIX-specific mutex data +// +typedef struct _GLFWmutexPOSIX +{ + GLFWbool allocated; + pthread_mutex_t handle; -GLFWbool _glfwInitThreadLocalStoragePOSIX(void); -void _glfwTerminateThreadLocalStoragePOSIX(void); +} _GLFWmutexPOSIX; -#endif // _glfw3_posix_tls_h_ diff --git a/external/glfw/posix_time.c b/external/glfw/posix_time.c index 368cbdf..53f856c 100644 --- a/external/glfw/posix_time.c +++ b/external/glfw/posix_time.c @@ -2,7 +2,7 @@ // GLFW 3.3 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -44,14 +44,14 @@ void _glfwInitTimerPOSIX(void) if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { - _glfw.posix_time.monotonic = GLFW_TRUE; - _glfw.posix_time.frequency = 1000000000; + _glfw.timer.posix.monotonic = GLFW_TRUE; + _glfw.timer.posix.frequency = 1000000000; } else #endif { - _glfw.posix_time.monotonic = GLFW_FALSE; - _glfw.posix_time.frequency = 1000000; + _glfw.timer.posix.monotonic = GLFW_FALSE; + _glfw.timer.posix.frequency = 1000000; } } @@ -63,7 +63,7 @@ void _glfwInitTimerPOSIX(void) uint64_t _glfwPlatformGetTimerValue(void) { #if defined(CLOCK_MONOTONIC) - if (_glfw.posix_time.monotonic) + if (_glfw.timer.posix.monotonic) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -80,6 +80,6 @@ uint64_t _glfwPlatformGetTimerValue(void) uint64_t _glfwPlatformGetTimerFrequency(void) { - return _glfw.posix_time.frequency; + return _glfw.timer.posix.frequency; } diff --git a/external/glfw/posix_time.h b/external/glfw/posix_time.h index 4a23bfa..08cf4fc 100644 --- a/external/glfw/posix_time.h +++ b/external/glfw/posix_time.h @@ -2,7 +2,7 @@ // GLFW 3.3 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,24 +25,20 @@ // //======================================================================== -#ifndef _glfw3_posix_time_h_ -#define _glfw3_posix_time_h_ - -#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimePOSIX posix_time +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix #include // POSIX-specific global timer data // -typedef struct _GLFWtimePOSIX +typedef struct _GLFWtimerPOSIX { GLFWbool monotonic; uint64_t frequency; -} _GLFWtimePOSIX; +} _GLFWtimerPOSIX; void _glfwInitTimerPOSIX(void); -#endif // _glfw3_posix_time_h_ diff --git a/external/glfw/vulkan.c b/external/glfw/vulkan.c index a2d10b6..cb32673 100644 --- a/external/glfw/vulkan.c +++ b/external/glfw/vulkan.c @@ -2,7 +2,7 @@ // GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2018 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -31,30 +31,40 @@ #include #include +#define _GLFW_FIND_LOADER 1 +#define _GLFW_REQUIRE_LOADER 2 + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -GLFWbool _glfwInitVulkan(void) +GLFWbool _glfwInitVulkan(int mode) { VkResult err; VkExtensionProperties* ep; uint32_t i, count; -#if !defined(_GLFW_VULKAN_STATIC) -#if defined(_GLFW_WIN32) - const char* name = "vulkan-1.dll"; -#else - const char* name = "libvulkan.so.1"; -#endif - if (_glfw.vk.available) return GLFW_TRUE; - _glfw.vk.handle = _glfw_dlopen(name); +#if !defined(_GLFW_VULKAN_STATIC) +#if defined(_GLFW_VULKAN_LIBRARY) + _glfw.vk.handle = _glfw_dlopen(_GLFW_VULKAN_LIBRARY); +#elif defined(_GLFW_WIN32) + _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll"); +#elif defined(_GLFW_COCOA) + _glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib"); +#else + _glfw.vk.handle = _glfw_dlopen("libvulkan.so.1"); +#endif if (!_glfw.vk.handle) + { + if (mode == _GLFW_REQUIRE_LOADER) + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader not found"); + return GLFW_FALSE; + } _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr"); @@ -82,9 +92,13 @@ GLFWbool _glfwInitVulkan(void) err = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL); if (err) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Vulkan: Failed to query instance extension count: %s", - _glfwGetVulkanResultString(err)); + // NOTE: This happens on systems with a loader but without any Vulkan ICD + if (mode == _GLFW_REQUIRE_LOADER) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Failed to query instance extension count: %s", + _glfwGetVulkanResultString(err)); + } _glfwTerminateVulkan(); return GLFW_FALSE; @@ -95,7 +109,7 @@ GLFWbool _glfwInitVulkan(void) err = vkEnumerateInstanceExtensionProperties(NULL, &count, ep); if (err) { - _glfwInputError(GLFW_PLATFORM_ERROR, + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Failed to query instance extensions: %s", _glfwGetVulkanResultString(err)); @@ -108,41 +122,38 @@ GLFWbool _glfwInitVulkan(void) { if (strcmp(ep[i].extensionName, "VK_KHR_surface") == 0) _glfw.vk.KHR_surface = GLFW_TRUE; - if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) +#if defined(_GLFW_WIN32) + else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) _glfw.vk.KHR_win32_surface = GLFW_TRUE; - if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) +#elif defined(_GLFW_COCOA) + else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0) + _glfw.vk.MVK_macos_surface = GLFW_TRUE; +#elif defined(_GLFW_X11) + else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) _glfw.vk.KHR_xlib_surface = GLFW_TRUE; - if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0) + else if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0) _glfw.vk.KHR_xcb_surface = GLFW_TRUE; - if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) +#elif defined(_GLFW_WAYLAND) + else if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) _glfw.vk.KHR_wayland_surface = GLFW_TRUE; - if (strcmp(ep[i].extensionName, "VK_KHR_mir_surface") == 0) - _glfw.vk.KHR_mir_surface = GLFW_TRUE; +#endif } free(ep); _glfw.vk.available = GLFW_TRUE; - if (_glfw.vk.KHR_surface) - { - _glfw.vk.extensions = - _glfwPlatformGetRequiredInstanceExtensions(&_glfw.vk.extensionCount); - } + _glfwPlatformGetRequiredInstanceExtensions(_glfw.vk.extensions); return GLFW_TRUE; } void _glfwTerminateVulkan(void) { - uint32_t i; - - for (i = 0; i < _glfw.vk.extensionCount; i++) - free(_glfw.vk.extensions[i]); - free(_glfw.vk.extensions); - +#if !defined(_GLFW_VULKAN_STATIC) if (_glfw.vk.handle) _glfw_dlclose(_glfw.vk.handle); +#endif } const char* _glfwGetVulkanResultString(VkResult result) @@ -208,22 +219,24 @@ const char* _glfwGetVulkanResultString(VkResult result) GLFWAPI int glfwVulkanSupported(void) { _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - return _glfwInitVulkan(); + return _glfwInitVulkan(_GLFW_FIND_LOADER); } GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count) { + assert(count != NULL); + *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (!_glfwInitVulkan()) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return NULL; - } - *count = _glfw.vk.extensionCount; + if (!_glfw.vk.extensions[0]) + return NULL; + + *count = 2; return (const char**) _glfw.vk.extensions; } @@ -231,18 +244,24 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname) { GLFWvkproc proc; + assert(procname != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (!_glfwInitVulkan()) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return NULL; - } proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname); +#if defined(_GLFW_VULKAN_STATIC) + if (!proc) + { + if (strcmp(procname, "vkGetInstanceProcAddr") == 0) + return (GLFWvkproc) vkGetInstanceProcAddr; + } +#else if (!proc) proc = (GLFWvkproc) _glfw_dlsym(_glfw.vk.handle, procname); +#endif return proc; } @@ -251,15 +270,15 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { + assert(instance != VK_NULL_HANDLE); + assert(device != VK_NULL_HANDLE); + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - if (!_glfwInitVulkan()) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return GLFW_FALSE; - } - if (!_glfw.vk.extensions) + if (!_glfw.vk.extensions[0]) { _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Window surface creation extensions not found"); @@ -277,6 +296,7 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, VkSurfaceKHR* surface) { _GLFWwindow* window = (_GLFWwindow*) handle; + assert(instance != VK_NULL_HANDLE); assert(window != NULL); assert(surface != NULL); @@ -284,19 +304,23 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, _GLFW_REQUIRE_INIT_OR_RETURN(VK_ERROR_INITIALIZATION_FAILED); - if (!_glfwInitVulkan()) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return VK_ERROR_INITIALIZATION_FAILED; - } - if (!_glfw.vk.extensions) + if (!_glfw.vk.extensions[0]) { _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Window surface creation extensions not found"); return VK_ERROR_EXTENSION_NOT_PRESENT; } + if (window->context.client != GLFW_NO_API) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Vulkan: Window surface creation requires the window to have the client API set to GLFW_NO_API"); + return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR; + } + return _glfwPlatformCreateWindowSurface(instance, window, allocator, surface); } diff --git a/external/glfw/wgl_context.c b/external/glfw/wgl_context.c index 677860d..5b0d09b 100644 --- a/external/glfw/wgl_context.c +++ b/external/glfw/wgl_context.c @@ -2,7 +2,7 @@ // GLFW 3.3 WGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -31,41 +31,94 @@ #include #include - -// Returns the specified attribute of the specified pixel format +// Return the value corresponding to the specified attribute // -static int getPixelFormatAttrib(_GLFWwindow* window, int pixelFormat, int attrib) +static int findPixelFormatAttribValue(const int* attribs, + int attribCount, + const int* values, + int attrib) { - int value = 0; + int i; - assert(_glfw.wgl.ARB_pixel_format); - - if (!_glfw.wgl.GetPixelFormatAttribivARB(window->context.wgl.dc, - pixelFormat, - 0, 1, &attrib, &value)) + for (i = 0; i < attribCount; i++) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to retrieve pixel format attribute %i", - attrib); - return 0; + if (attribs[i] == attrib) + return values[i]; } - return value; + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Unknown pixel format attribute requested"); + return 0; } +#define addAttrib(a) \ +{ \ + assert((size_t) attribCount < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[attribCount++] = a; \ +} +#define findAttribValue(a) \ + findPixelFormatAttribValue(attribs, attribCount, values, a) + // Return a list of available and usable framebuffer configs // -static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired) +static int choosePixelFormat(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) { _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; - int i, pixelFormat, nativeCount, usableCount; + int i, pixelFormat, nativeCount, usableCount = 0, attribCount = 0; + int attribs[40]; + int values[sizeof(attribs) / sizeof(attribs[0])]; if (_glfw.wgl.ARB_pixel_format) { - nativeCount = getPixelFormatAttrib(window, - 1, - WGL_NUMBER_PIXEL_FORMATS_ARB); + const int attrib = WGL_NUMBER_PIXEL_FORMATS_ARB; + + if (!_glfw.wgl.GetPixelFormatAttribivARB(window->context.wgl.dc, + 1, 0, 1, &attrib, &nativeCount)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve pixel format attribute"); + return 0; + } + + addAttrib(WGL_SUPPORT_OPENGL_ARB); + addAttrib(WGL_DRAW_TO_WINDOW_ARB); + addAttrib(WGL_PIXEL_TYPE_ARB); + addAttrib(WGL_ACCELERATION_ARB); + addAttrib(WGL_RED_BITS_ARB); + addAttrib(WGL_RED_SHIFT_ARB); + addAttrib(WGL_GREEN_BITS_ARB); + addAttrib(WGL_GREEN_SHIFT_ARB); + addAttrib(WGL_BLUE_BITS_ARB); + addAttrib(WGL_BLUE_SHIFT_ARB); + addAttrib(WGL_ALPHA_BITS_ARB); + addAttrib(WGL_ALPHA_SHIFT_ARB); + addAttrib(WGL_DEPTH_BITS_ARB); + addAttrib(WGL_STENCIL_BITS_ARB); + addAttrib(WGL_ACCUM_BITS_ARB); + addAttrib(WGL_ACCUM_RED_BITS_ARB); + addAttrib(WGL_ACCUM_GREEN_BITS_ARB); + addAttrib(WGL_ACCUM_BLUE_BITS_ARB); + addAttrib(WGL_ACCUM_ALPHA_BITS_ARB); + addAttrib(WGL_AUX_BUFFERS_ARB); + addAttrib(WGL_STEREO_ARB); + addAttrib(WGL_DOUBLE_BUFFER_ARB); + + if (_glfw.wgl.ARB_multisample) + addAttrib(WGL_SAMPLES_ARB); + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if (_glfw.wgl.ARB_framebuffer_sRGB || _glfw.wgl.EXT_framebuffer_sRGB) + addAttrib(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); + } + else + { + if (_glfw.wgl.EXT_colorspace) + addAttrib(WGL_COLORSPACE_EXT); + } } else { @@ -76,77 +129,97 @@ static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired) } usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); - usableCount = 0; for (i = 0; i < nativeCount; i++) { - const int n = i + 1; _GLFWfbconfig* u = usableConfigs + usableCount; + pixelFormat = i + 1; if (_glfw.wgl.ARB_pixel_format) { // Get pixel format attributes through "modern" extension - if (!getPixelFormatAttrib(window, n, WGL_SUPPORT_OPENGL_ARB) || - !getPixelFormatAttrib(window, n, WGL_DRAW_TO_WINDOW_ARB)) + if (!_glfw.wgl.GetPixelFormatAttribivARB(window->context.wgl.dc, + pixelFormat, 0, + attribCount, + attribs, values)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve pixel format attributes"); + + free(usableConfigs); + return 0; + } + + if (!findAttribValue(WGL_SUPPORT_OPENGL_ARB) || + !findAttribValue(WGL_DRAW_TO_WINDOW_ARB)) { continue; } - if (getPixelFormatAttrib(window, n, WGL_PIXEL_TYPE_ARB) != - WGL_TYPE_RGBA_ARB) - { + if (findAttribValue(WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) continue; - } - if (getPixelFormatAttrib(window, n, WGL_ACCELERATION_ARB) == - WGL_NO_ACCELERATION_ARB) - { + if (findAttribValue(WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) continue; - } - u->redBits = getPixelFormatAttrib(window, n, WGL_RED_BITS_ARB); - u->greenBits = getPixelFormatAttrib(window, n, WGL_GREEN_BITS_ARB); - u->blueBits = getPixelFormatAttrib(window, n, WGL_BLUE_BITS_ARB); - u->alphaBits = getPixelFormatAttrib(window, n, WGL_ALPHA_BITS_ARB); + u->redBits = findAttribValue(WGL_RED_BITS_ARB); + u->greenBits = findAttribValue(WGL_GREEN_BITS_ARB); + u->blueBits = findAttribValue(WGL_BLUE_BITS_ARB); + u->alphaBits = findAttribValue(WGL_ALPHA_BITS_ARB); - u->depthBits = getPixelFormatAttrib(window, n, WGL_DEPTH_BITS_ARB); - u->stencilBits = getPixelFormatAttrib(window, n, WGL_STENCIL_BITS_ARB); + u->depthBits = findAttribValue(WGL_DEPTH_BITS_ARB); + u->stencilBits = findAttribValue(WGL_STENCIL_BITS_ARB); - u->accumRedBits = getPixelFormatAttrib(window, n, WGL_ACCUM_RED_BITS_ARB); - u->accumGreenBits = getPixelFormatAttrib(window, n, WGL_ACCUM_GREEN_BITS_ARB); - u->accumBlueBits = getPixelFormatAttrib(window, n, WGL_ACCUM_BLUE_BITS_ARB); - u->accumAlphaBits = getPixelFormatAttrib(window, n, WGL_ACCUM_ALPHA_BITS_ARB); + u->accumRedBits = findAttribValue(WGL_ACCUM_RED_BITS_ARB); + u->accumGreenBits = findAttribValue(WGL_ACCUM_GREEN_BITS_ARB); + u->accumBlueBits = findAttribValue(WGL_ACCUM_BLUE_BITS_ARB); + u->accumAlphaBits = findAttribValue(WGL_ACCUM_ALPHA_BITS_ARB); - u->auxBuffers = getPixelFormatAttrib(window, n, WGL_AUX_BUFFERS_ARB); + u->auxBuffers = findAttribValue(WGL_AUX_BUFFERS_ARB); - if (getPixelFormatAttrib(window, n, WGL_STEREO_ARB)) + if (findAttribValue(WGL_STEREO_ARB)) u->stereo = GLFW_TRUE; - if (getPixelFormatAttrib(window, n, WGL_DOUBLE_BUFFER_ARB)) + if (findAttribValue(WGL_DOUBLE_BUFFER_ARB)) u->doublebuffer = GLFW_TRUE; if (_glfw.wgl.ARB_multisample) - u->samples = getPixelFormatAttrib(window, n, WGL_SAMPLES_ARB); + u->samples = findAttribValue(WGL_SAMPLES_ARB); - if (_glfw.wgl.ARB_framebuffer_sRGB || - _glfw.wgl.EXT_framebuffer_sRGB) + if (ctxconfig->client == GLFW_OPENGL_API) { - if (getPixelFormatAttrib(window, n, WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)) - u->sRGB = GLFW_TRUE; + if (_glfw.wgl.ARB_framebuffer_sRGB || + _glfw.wgl.EXT_framebuffer_sRGB) + { + if (findAttribValue(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)) + u->sRGB = GLFW_TRUE; + } + } + else + { + if (_glfw.wgl.EXT_colorspace) + { + if (findAttribValue(WGL_COLORSPACE_EXT) == WGL_COLORSPACE_SRGB_EXT) + u->sRGB = GLFW_TRUE; + } } } else { - PIXELFORMATDESCRIPTOR pfd; - // Get pixel format attributes through legacy PFDs + PIXELFORMATDESCRIPTOR pfd; + if (!DescribePixelFormat(window->context.wgl.dc, - n, + pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) { - continue; + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to describe pixel format"); + + free(usableConfigs); + return 0; } if (!(pfd.dwFlags & PFD_DRAW_TO_WINDOW) || @@ -185,7 +258,7 @@ static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired) u->doublebuffer = GLFW_TRUE; } - u->handle = n; + u->handle = pixelFormat; usableCount++; } @@ -198,7 +271,7 @@ static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired) return 0; } - closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); + closest = _glfwChooseFBConfig(fbconfig, usableConfigs, usableCount); if (!closest) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, @@ -214,54 +287,52 @@ static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired) return pixelFormat; } -// Returns whether desktop compositing is enabled -// -static GLFWbool isCompositionEnabled(void) -{ - BOOL enabled; - - if (!_glfw_DwmIsCompositionEnabled) - return FALSE; - - if (_glfw_DwmIsCompositionEnabled(&enabled) != S_OK) - return FALSE; - - return enabled; -} +#undef addAttrib +#undef findAttribValue static void makeContextCurrentWGL(_GLFWwindow* window) { if (window) { if (wglMakeCurrent(window->context.wgl.dc, window->context.wgl.handle)) - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.contextSlot, window); else { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to make context current"); - _glfwPlatformSetCurrentContext(NULL); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to make context current"); + _glfwPlatformSetTls(&_glfw.contextSlot, NULL); } } else { if (!wglMakeCurrent(NULL, NULL)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to clear current context"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to clear current context"); } - _glfwPlatformSetCurrentContext(NULL); + _glfwPlatformSetTls(&_glfw.contextSlot, NULL); } } static void swapBuffersWGL(_GLFWwindow* window) { - // HACK: Use DwmFlush when desktop composition is enabled - if (isCompositionEnabled() && !window->monitor) + if (!window->monitor) { - int count = abs(window->context.wgl.interval); - while (count--) - _glfw_DwmFlush(); + if (IsWindowsVistaOrGreater()) + { + // DWM Composition is always enabled on Win8+ + BOOL enabled = IsWindows8OrGreater(); + + // HACK: Use DwmFlush when desktop composition is enabled + if (enabled || + (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)) + { + int count = abs(window->context.wgl.interval); + while (count--) + DwmFlush(); + } + } } SwapBuffers(window->context.wgl.dc); @@ -269,14 +340,24 @@ static void swapBuffersWGL(_GLFWwindow* window) static void swapIntervalWGL(int interval) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); window->context.wgl.interval = interval; - // HACK: Disable WGL swap interval when desktop composition is enabled to - // avoid interfering with DWM vsync - if (isCompositionEnabled() && !window->monitor) - interval = 0; + if (!window->monitor) + { + if (IsWindowsVistaOrGreater()) + { + // DWM Composition is always enabled on Win8+ + BOOL enabled = IsWindows8OrGreater(); + + // HACK: Disable WGL swap interval when desktop composition is enabled to + // avoid interfering with DWM vsync + if (enabled || + (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)) + interval = 0; + } + } if (_glfw.wgl.EXT_swap_control) _glfw.wgl.SwapIntervalEXT(interval); @@ -284,29 +365,17 @@ static void swapIntervalWGL(int interval) static int extensionSupportedWGL(const char* extension) { - const char* extensions; - - if (_glfw.wgl.GetExtensionsStringEXT) - { - extensions = _glfw.wgl.GetExtensionsStringEXT(); - if (extensions) - { - if (_glfwStringInExtensionString(extension, extensions)) - return GLFW_TRUE; - } - } + const char* extensions = NULL; if (_glfw.wgl.GetExtensionsStringARB) - { extensions = _glfw.wgl.GetExtensionsStringARB(wglGetCurrentDC()); - if (extensions) - { - if (_glfwStringInExtensionString(extension, extensions)) - return GLFW_TRUE; - } - } + else if (_glfw.wgl.GetExtensionsStringEXT) + extensions = _glfw.wgl.GetExtensionsStringEXT(); - return GLFW_FALSE; + if (!extensions) + return GLFW_FALSE; + + return _glfwStringInExtensionString(extension, extensions); } static GLFWglproc getProcAddressWGL(const char* procname) @@ -329,21 +398,52 @@ static void destroyContextWGL(_GLFWwindow* window) } } -// Initialize WGL-specific extensions + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize WGL // -static void loadWGLExtensions(void) +GLFWbool _glfwInitWGL(void) { PIXELFORMATDESCRIPTOR pfd; - HGLRC rc; - HDC dc = GetDC(_glfw.win32.helperWindowHandle);; + HGLRC prc, rc; + HDC pdc, dc; - _glfw.wgl.extensionsLoaded = GLFW_TRUE; + if (_glfw.wgl.instance) + return GLFW_TRUE; + + _glfw.wgl.instance = LoadLibraryA("opengl32.dll"); + if (!_glfw.wgl.instance) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to load opengl32.dll"); + return GLFW_FALSE; + } + + _glfw.wgl.CreateContext = (PFN_wglCreateContext) + GetProcAddress(_glfw.wgl.instance, "wglCreateContext"); + _glfw.wgl.DeleteContext = (PFN_wglDeleteContext) + GetProcAddress(_glfw.wgl.instance, "wglDeleteContext"); + _glfw.wgl.GetProcAddress = (PFN_wglGetProcAddress) + GetProcAddress(_glfw.wgl.instance, "wglGetProcAddress"); + _glfw.wgl.GetCurrentDC = (PFN_wglGetCurrentDC) + GetProcAddress(_glfw.wgl.instance, "wglGetCurrentDC"); + _glfw.wgl.GetCurrentContext = (PFN_wglGetCurrentContext) + GetProcAddress(_glfw.wgl.instance, "wglGetCurrentContext"); + _glfw.wgl.MakeCurrent = (PFN_wglMakeCurrent) + GetProcAddress(_glfw.wgl.instance, "wglMakeCurrent"); + _glfw.wgl.ShareLists = (PFN_wglShareLists) + GetProcAddress(_glfw.wgl.instance, "wglShareLists"); // NOTE: A dummy context has to be created for opengl32.dll to load the // OpenGL ICD, from which we can then query WGL extensions // NOTE: This code will accept the Microsoft GDI ICD; accelerated context // creation failure occurs during manual pixel format enumeration + dc = GetDC(_glfw.win32.helperWindowHandle); + ZeroMemory(&pfd, sizeof(pfd)); pfd.nSize = sizeof(pfd); pfd.nVersion = 1; @@ -353,26 +453,29 @@ static void loadWGLExtensions(void) if (!SetPixelFormat(dc, ChoosePixelFormat(dc, &pfd), &pfd)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to set pixel format for dummy context"); - return; + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to set pixel format for dummy context"); + return GLFW_FALSE; } rc = wglCreateContext(dc); if (!rc) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to create dummy context"); - return; + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to create dummy context"); + return GLFW_FALSE; } + pdc = wglGetCurrentDC(); + prc = wglGetCurrentContext(); + if (!wglMakeCurrent(dc, rc)) { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to make dummy context current"); + wglMakeCurrent(pdc, prc); wglDeleteContext(rc); - - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to make dummy context current"); - return; + return GLFW_FALSE; } // NOTE: Functions must be loaded first as they're needed to retrieve the @@ -404,49 +507,19 @@ static void loadWGLExtensions(void) extensionSupportedWGL("WGL_EXT_create_context_es2_profile"); _glfw.wgl.ARB_create_context_robustness = extensionSupportedWGL("WGL_ARB_create_context_robustness"); + _glfw.wgl.ARB_create_context_no_error = + extensionSupportedWGL("WGL_ARB_create_context_no_error"); _glfw.wgl.EXT_swap_control = extensionSupportedWGL("WGL_EXT_swap_control"); + _glfw.wgl.EXT_colorspace = + extensionSupportedWGL("WGL_EXT_colorspace"); _glfw.wgl.ARB_pixel_format = extensionSupportedWGL("WGL_ARB_pixel_format"); _glfw.wgl.ARB_context_flush_control = extensionSupportedWGL("WGL_ARB_context_flush_control"); - wglMakeCurrent(dc, NULL); + wglMakeCurrent(pdc, prc); wglDeleteContext(rc); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialize WGL -// -GLFWbool _glfwInitWGL(void) -{ - if (_glfw.wgl.instance) - return GLFW_TRUE; - - _glfw.wgl.instance = LoadLibraryA("opengl32.dll"); - if (!_glfw.wgl.instance) - { - _glfwInputError(GLFW_PLATFORM_ERROR, "WGL: Failed to load opengl32.dll"); - return GLFW_FALSE; - } - - _glfw.wgl.CreateContext = (WGLCREATECONTEXT_T) - GetProcAddress(_glfw.wgl.instance, "wglCreateContext"); - _glfw.wgl.DeleteContext = (WGLDELETECONTEXT_T) - GetProcAddress(_glfw.wgl.instance, "wglDeleteContext"); - _glfw.wgl.GetProcAddress = (WGLGETPROCADDRESS_T) - GetProcAddress(_glfw.wgl.instance, "wglGetProcAddress"); - _glfw.wgl.GetCurrentDC = (WGLGETCURRENTDC_T) - GetProcAddress(_glfw.wgl.instance, "wglGetCurrentDC"); - _glfw.wgl.MakeCurrent = (WGLMAKECURRENT_T) - GetProcAddress(_glfw.wgl.instance, "wglMakeCurrent"); - _glfw.wgl.ShareLists = (WGLSHARELISTS_T) - GetProcAddress(_glfw.wgl.instance, "wglShareLists"); - return GLFW_TRUE; } @@ -458,11 +531,11 @@ void _glfwTerminateWGL(void) FreeLibrary(_glfw.wgl.instance); } -#define setWGLattrib(attribName, attribValue) \ +#define setAttrib(a, v) \ { \ - attribs[index++] = attribName; \ - attribs[index++] = attribValue; \ - assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ + assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ } // Create the OpenGL or OpenGL ES context @@ -476,9 +549,6 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, PIXELFORMATDESCRIPTOR pfd; HGLRC share = NULL; - if (!_glfw.wgl.extensionsLoaded) - loadWGLExtensions(); - if (ctxconfig->share) share = ctxconfig->share->context.wgl.handle; @@ -490,22 +560,22 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, return GLFW_FALSE; } - pixelFormat = choosePixelFormat(window, fbconfig); + pixelFormat = choosePixelFormat(window, ctxconfig, fbconfig); if (!pixelFormat) return GLFW_FALSE; if (!DescribePixelFormat(window->context.wgl.dc, pixelFormat, sizeof(pfd), &pfd)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to retrieve PFD for selected pixel format"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve PFD for selected pixel format"); return GLFW_FALSE; } if (!SetPixelFormat(window->context.wgl.dc, pixelFormat, &pfd)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to set selected pixel format"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to set selected pixel format"); return GLFW_FALSE; } @@ -562,8 +632,6 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, if (ctxconfig->debug) flags |= WGL_CONTEXT_DEBUG_BIT_ARB; - if (ctxconfig->noerror) - flags |= GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR; if (ctxconfig->robustness) { @@ -571,13 +639,13 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { - setWGLattrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - WGL_NO_RESET_NOTIFICATION_ARB); + setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + WGL_NO_RESET_NOTIFICATION_ARB); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { - setWGLattrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - WGL_LOSE_CONTEXT_ON_RESET_ARB); + setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + WGL_LOSE_CONTEXT_ON_RESET_ARB); } flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; @@ -590,33 +658,39 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { - setWGLattrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, - WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, + WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { - setWGLattrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, - WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); + setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, + WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); } } } + if (ctxconfig->noerror) + { + if (_glfw.wgl.ARB_create_context_no_error) + setAttrib(WGL_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); + } + // NOTE: Only request an explicitly versioned context when necessary, as // explicitly requesting version 1.0 does not always return the // highest version supported by the driver if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setWGLattrib(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); - setWGLattrib(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); + setAttrib(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); + setAttrib(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); } if (flags) - setWGLattrib(WGL_CONTEXT_FLAGS_ARB, flags); + setAttrib(WGL_CONTEXT_FLAGS_ARB, flags); if (mask) - setWGLattrib(WGL_CONTEXT_PROFILE_MASK_ARB, mask); + setAttrib(WGL_CONTEXT_PROFILE_MASK_ARB, mask); - setWGLattrib(0, 0); + setAttrib(0, 0); window->context.wgl.handle = _glfw.wgl.CreateContextAttribsARB(window->context.wgl.dc, @@ -647,6 +721,11 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, _glfwInputError(GLFW_VERSION_UNAVAILABLE, "WGL: Driver does not support the requested OpenGL profile"); } + else if (error == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) + { + _glfwInputError(GLFW_INVALID_VALUE, + "WGL: The share context is not compatible with the requested context"); + } else { if (ctxconfig->client == GLFW_OPENGL_API) @@ -669,8 +748,8 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, window->context.wgl.handle = wglCreateContext(window->context.wgl.dc); if (!window->context.wgl.handle) { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "WGL: Failed to create OpenGL context"); + _glfwInputErrorWin32(GLFW_VERSION_UNAVAILABLE, + "WGL: Failed to create OpenGL context"); return GLFW_FALSE; } @@ -678,8 +757,8 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, { if (!wglShareLists(share, window->context.wgl.handle)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to enable sharing with specified OpenGL context"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to enable sharing with specified OpenGL context"); return GLFW_FALSE; } } @@ -695,7 +774,7 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, return GLFW_TRUE; } -#undef setWGLattrib +#undef setAttrib ////////////////////////////////////////////////////////////////////////// diff --git a/external/glfw/wgl_context.h b/external/glfw/wgl_context.h index 6d98923..fa6605b 100644 --- a/external/glfw/wgl_context.h +++ b/external/glfw/wgl_context.h @@ -2,7 +2,7 @@ // GLFW 3.3 WGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2018 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,10 +25,6 @@ // //======================================================================== -#ifndef _glfw3_wgl_context_h_ -#define _glfw3_wgl_context_h_ - - #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 #define WGL_SUPPORT_OPENGL_ARB 0x2010 #define WGL_DRAW_TO_WINDOW_ARB 0x2001 @@ -72,9 +68,13 @@ #define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 #define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 #define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 +#define WGL_COLORSPACE_EXT 0x309d +#define WGL_COLORSPACE_SRGB_EXT 0x3089 #define ERROR_INVALID_VERSION_ARB 0x2095 #define ERROR_INVALID_PROFILE_ARB 0x2096 +#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); @@ -82,18 +82,20 @@ typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); -typedef HGLRC (WINAPI * WGLCREATECONTEXT_T)(HDC); -typedef BOOL (WINAPI * WGLDELETECONTEXT_T)(HGLRC); -typedef PROC (WINAPI * WGLGETPROCADDRESS_T)(LPCSTR); -typedef HDC (WINAPI * WGLGETCURRENTDC_T)(void); -typedef BOOL (WINAPI * WGLMAKECURRENT_T)(HDC,HGLRC); -typedef BOOL (WINAPI * WGLSHARELISTS_T)(HGLRC,HGLRC); +typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); +typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); +typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); +typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); +typedef HGLRC (WINAPI * PFN_wglGetCurrentContext)(void); +typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); +typedef BOOL (WINAPI * PFN_wglShareLists)(HGLRC,HGLRC); // opengl32.dll function pointer typedefs #define wglCreateContext _glfw.wgl.CreateContext #define wglDeleteContext _glfw.wgl.DeleteContext #define wglGetProcAddress _glfw.wgl.GetProcAddress #define wglGetCurrentDC _glfw.wgl.GetCurrentDC +#define wglGetCurrentContext _glfw.wgl.GetCurrentContext #define wglMakeCurrent _glfw.wgl.MakeCurrent #define wglShareLists _glfw.wgl.ShareLists @@ -120,14 +122,13 @@ typedef struct _GLFWcontextWGL typedef struct _GLFWlibraryWGL { HINSTANCE instance; - WGLCREATECONTEXT_T CreateContext; - WGLDELETECONTEXT_T DeleteContext; - WGLGETPROCADDRESS_T GetProcAddress; - WGLGETCURRENTDC_T GetCurrentDC; - WGLMAKECURRENT_T MakeCurrent; - WGLSHARELISTS_T ShareLists; - - GLFWbool extensionsLoaded; + PFN_wglCreateContext CreateContext; + PFN_wglDeleteContext DeleteContext; + PFN_wglGetProcAddress GetProcAddress; + PFN_wglGetCurrentDC GetCurrentDC; + PFN_wglGetCurrentContext GetCurrentContext; + PFN_wglMakeCurrent MakeCurrent; + PFN_wglShareLists ShareLists; PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; @@ -135,6 +136,7 @@ typedef struct _GLFWlibraryWGL PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; GLFWbool EXT_swap_control; + GLFWbool EXT_colorspace; GLFWbool ARB_multisample; GLFWbool ARB_framebuffer_sRGB; GLFWbool EXT_framebuffer_sRGB; @@ -143,6 +145,7 @@ typedef struct _GLFWlibraryWGL GLFWbool ARB_create_context_profile; GLFWbool EXT_create_context_es2_profile; GLFWbool ARB_create_context_robustness; + GLFWbool ARB_create_context_no_error; GLFWbool ARB_context_flush_control; } _GLFWlibraryWGL; @@ -154,4 +157,3 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); -#endif // _glfw3_wgl_context_h_ diff --git a/external/glfw/win32_init.c b/external/glfw/win32_init.c index 12e98a0..e28868f 100644 --- a/external/glfw/win32_init.c +++ b/external/glfw/win32_init.c @@ -2,7 +2,7 @@ // GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -30,20 +30,22 @@ #include #include -#include -DEFINE_GUID(GUID_DEVINTERFACE_HID,0x4d1e55b2,0xf16f,0x11cf,0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30); +static const GUID _glfw_GUID_DEVINTERFACE_HID = + {0x4d1e55b2,0xf16f,0x11cf,{0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30}}; + +#define GUID_DEVINTERFACE_HID _glfw_GUID_DEVINTERFACE_HID #if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) -// Applications exporting this symbol with this value will be automatically -// directed to the high-performance GPU on Nvidia Optimus systems with -// up-to-date drivers +// Executables (but not DLLs) exporting this symbol with this value will be +// automatically directed to the high-performance GPU on Nvidia Optimus systems +// with up-to-date drivers // __declspec(dllexport) DWORD NvOptimusEnablement = 1; -// Applications exporting this symbol with this value will be automatically -// directed to the high-performance GPU on AMD PowerXpress systems with -// up-to-date drivers +// Executables (but not DLLs) exporting this symbol with this value will be +// automatically directed to the high-performance GPU on AMD PowerXpress systems +// with up-to-date drivers // __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; @@ -67,29 +69,39 @@ static GLFWbool loadLibraries(void) _glfw.win32.winmm.instance = LoadLibraryA("winmm.dll"); if (!_glfw.win32.winmm.instance) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to load winmm.dll"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to load winmm.dll"); return GLFW_FALSE; } - _glfw.win32.winmm.timeGetTime = (TIMEGETTIME_T) + _glfw.win32.winmm.GetTime = (PFN_timeGetTime) GetProcAddress(_glfw.win32.winmm.instance, "timeGetTime"); _glfw.win32.user32.instance = LoadLibraryA("user32.dll"); if (!_glfw.win32.user32.instance) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to load user32.dll"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to load user32.dll"); return GLFW_FALSE; } - _glfw.win32.user32.SetProcessDPIAware = (SETPROCESSDPIAWARE_T) + _glfw.win32.user32.SetProcessDPIAware_ = (PFN_SetProcessDPIAware) GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware"); - _glfw.win32.user32.ChangeWindowMessageFilterEx = (CHANGEWINDOWMESSAGEFILTEREX_T) + _glfw.win32.user32.ChangeWindowMessageFilterEx_ = (PFN_ChangeWindowMessageFilterEx) GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); + _glfw.win32.user32.EnableNonClientDpiScaling_ = (PFN_EnableNonClientDpiScaling) + GetProcAddress(_glfw.win32.user32.instance, "EnableNonClientDpiScaling"); + _glfw.win32.user32.SetProcessDpiAwarenessContext_ = (PFN_SetProcessDpiAwarenessContext) + GetProcAddress(_glfw.win32.user32.instance, "SetProcessDpiAwarenessContext"); + _glfw.win32.user32.GetDpiForWindow_ = (PFN_GetDpiForWindow) + GetProcAddress(_glfw.win32.user32.instance, "GetDpiForWindow"); + _glfw.win32.user32.AdjustWindowRectExForDpi_ = (PFN_AdjustWindowRectExForDpi) + GetProcAddress(_glfw.win32.user32.instance, "AdjustWindowRectExForDpi"); _glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll"); if (_glfw.win32.dinput8.instance) { - _glfw.win32.dinput8.DirectInput8Create = (DIRECTINPUT8CREATE_T) + _glfw.win32.dinput8.Create = (PFN_DirectInput8Create) GetProcAddress(_glfw.win32.dinput8.instance, "DirectInput8Create"); } @@ -110,9 +122,9 @@ static GLFWbool loadLibraries(void) _glfw.win32.xinput.instance = LoadLibraryA(names[i]); if (_glfw.win32.xinput.instance) { - _glfw.win32.xinput.XInputGetCapabilities = (XINPUTGETCAPABILITIES_T) + _glfw.win32.xinput.GetCapabilities = (PFN_XInputGetCapabilities) GetProcAddress(_glfw.win32.xinput.instance, "XInputGetCapabilities"); - _glfw.win32.xinput.XInputGetState = (XINPUTGETSTATE_T) + _glfw.win32.xinput.GetState = (PFN_XInputGetState) GetProcAddress(_glfw.win32.xinput.instance, "XInputGetState"); break; @@ -123,17 +135,28 @@ static GLFWbool loadLibraries(void) _glfw.win32.dwmapi.instance = LoadLibraryA("dwmapi.dll"); if (_glfw.win32.dwmapi.instance) { - _glfw.win32.dwmapi.DwmIsCompositionEnabled = (DWMISCOMPOSITIONENABLED_T) + _glfw.win32.dwmapi.IsCompositionEnabled = (PFN_DwmIsCompositionEnabled) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); - _glfw.win32.dwmapi.DwmFlush = (DWMFLUSH_T) + _glfw.win32.dwmapi.Flush = (PFN_DwmFlush) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush"); + _glfw.win32.dwmapi.EnableBlurBehindWindow = (PFN_DwmEnableBlurBehindWindow) + GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); } _glfw.win32.shcore.instance = LoadLibraryA("shcore.dll"); if (_glfw.win32.shcore.instance) { - _glfw.win32.shcore.SetProcessDpiAwareness = (SETPROCESSDPIAWARENESS_T) + _glfw.win32.shcore.SetProcessDpiAwareness_ = (PFN_SetProcessDpiAwareness) GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); + _glfw.win32.shcore.GetDpiForMonitor_ = (PFN_GetDpiForMonitor) + GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor"); + } + + _glfw.win32.ntdll.instance = LoadLibraryA("ntdll.dll"); + if (_glfw.win32.ntdll.instance) + { + _glfw.win32.ntdll.RtlVerifyVersionInfo_ = (PFN_RtlVerifyVersionInfo) + GetProcAddress(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); } return GLFW_TRUE; @@ -160,6 +183,9 @@ static void freeLibraries(void) if (_glfw.win32.shcore.instance) FreeLibrary(_glfw.win32.shcore.instance); + + if (_glfw.win32.ntdll.instance) + FreeLibrary(_glfw.win32.ntdll.instance); } // Create key code translation tables @@ -168,160 +194,165 @@ static void createKeyTables(void) { int scancode; - memset(_glfw.win32.publicKeys, -1, sizeof(_glfw.win32.publicKeys)); - memset(_glfw.win32.nativeKeys, -1, sizeof(_glfw.win32.nativeKeys)); + memset(_glfw.win32.keycodes, -1, sizeof(_glfw.win32.keycodes)); + memset(_glfw.win32.scancodes, -1, sizeof(_glfw.win32.scancodes)); - _glfw.win32.publicKeys[0x00B] = GLFW_KEY_0; - _glfw.win32.publicKeys[0x002] = GLFW_KEY_1; - _glfw.win32.publicKeys[0x003] = GLFW_KEY_2; - _glfw.win32.publicKeys[0x004] = GLFW_KEY_3; - _glfw.win32.publicKeys[0x005] = GLFW_KEY_4; - _glfw.win32.publicKeys[0x006] = GLFW_KEY_5; - _glfw.win32.publicKeys[0x007] = GLFW_KEY_6; - _glfw.win32.publicKeys[0x008] = GLFW_KEY_7; - _glfw.win32.publicKeys[0x009] = GLFW_KEY_8; - _glfw.win32.publicKeys[0x00A] = GLFW_KEY_9; - _glfw.win32.publicKeys[0x01E] = GLFW_KEY_A; - _glfw.win32.publicKeys[0x030] = GLFW_KEY_B; - _glfw.win32.publicKeys[0x02E] = GLFW_KEY_C; - _glfw.win32.publicKeys[0x020] = GLFW_KEY_D; - _glfw.win32.publicKeys[0x012] = GLFW_KEY_E; - _glfw.win32.publicKeys[0x021] = GLFW_KEY_F; - _glfw.win32.publicKeys[0x022] = GLFW_KEY_G; - _glfw.win32.publicKeys[0x023] = GLFW_KEY_H; - _glfw.win32.publicKeys[0x017] = GLFW_KEY_I; - _glfw.win32.publicKeys[0x024] = GLFW_KEY_J; - _glfw.win32.publicKeys[0x025] = GLFW_KEY_K; - _glfw.win32.publicKeys[0x026] = GLFW_KEY_L; - _glfw.win32.publicKeys[0x032] = GLFW_KEY_M; - _glfw.win32.publicKeys[0x031] = GLFW_KEY_N; - _glfw.win32.publicKeys[0x018] = GLFW_KEY_O; - _glfw.win32.publicKeys[0x019] = GLFW_KEY_P; - _glfw.win32.publicKeys[0x010] = GLFW_KEY_Q; - _glfw.win32.publicKeys[0x013] = GLFW_KEY_R; - _glfw.win32.publicKeys[0x01F] = GLFW_KEY_S; - _glfw.win32.publicKeys[0x014] = GLFW_KEY_T; - _glfw.win32.publicKeys[0x016] = GLFW_KEY_U; - _glfw.win32.publicKeys[0x02F] = GLFW_KEY_V; - _glfw.win32.publicKeys[0x011] = GLFW_KEY_W; - _glfw.win32.publicKeys[0x02D] = GLFW_KEY_X; - _glfw.win32.publicKeys[0x015] = GLFW_KEY_Y; - _glfw.win32.publicKeys[0x02C] = GLFW_KEY_Z; + _glfw.win32.keycodes[0x00B] = GLFW_KEY_0; + _glfw.win32.keycodes[0x002] = GLFW_KEY_1; + _glfw.win32.keycodes[0x003] = GLFW_KEY_2; + _glfw.win32.keycodes[0x004] = GLFW_KEY_3; + _glfw.win32.keycodes[0x005] = GLFW_KEY_4; + _glfw.win32.keycodes[0x006] = GLFW_KEY_5; + _glfw.win32.keycodes[0x007] = GLFW_KEY_6; + _glfw.win32.keycodes[0x008] = GLFW_KEY_7; + _glfw.win32.keycodes[0x009] = GLFW_KEY_8; + _glfw.win32.keycodes[0x00A] = GLFW_KEY_9; + _glfw.win32.keycodes[0x01E] = GLFW_KEY_A; + _glfw.win32.keycodes[0x030] = GLFW_KEY_B; + _glfw.win32.keycodes[0x02E] = GLFW_KEY_C; + _glfw.win32.keycodes[0x020] = GLFW_KEY_D; + _glfw.win32.keycodes[0x012] = GLFW_KEY_E; + _glfw.win32.keycodes[0x021] = GLFW_KEY_F; + _glfw.win32.keycodes[0x022] = GLFW_KEY_G; + _glfw.win32.keycodes[0x023] = GLFW_KEY_H; + _glfw.win32.keycodes[0x017] = GLFW_KEY_I; + _glfw.win32.keycodes[0x024] = GLFW_KEY_J; + _glfw.win32.keycodes[0x025] = GLFW_KEY_K; + _glfw.win32.keycodes[0x026] = GLFW_KEY_L; + _glfw.win32.keycodes[0x032] = GLFW_KEY_M; + _glfw.win32.keycodes[0x031] = GLFW_KEY_N; + _glfw.win32.keycodes[0x018] = GLFW_KEY_O; + _glfw.win32.keycodes[0x019] = GLFW_KEY_P; + _glfw.win32.keycodes[0x010] = GLFW_KEY_Q; + _glfw.win32.keycodes[0x013] = GLFW_KEY_R; + _glfw.win32.keycodes[0x01F] = GLFW_KEY_S; + _glfw.win32.keycodes[0x014] = GLFW_KEY_T; + _glfw.win32.keycodes[0x016] = GLFW_KEY_U; + _glfw.win32.keycodes[0x02F] = GLFW_KEY_V; + _glfw.win32.keycodes[0x011] = GLFW_KEY_W; + _glfw.win32.keycodes[0x02D] = GLFW_KEY_X; + _glfw.win32.keycodes[0x015] = GLFW_KEY_Y; + _glfw.win32.keycodes[0x02C] = GLFW_KEY_Z; - _glfw.win32.publicKeys[0x028] = GLFW_KEY_APOSTROPHE; - _glfw.win32.publicKeys[0x02B] = GLFW_KEY_BACKSLASH; - _glfw.win32.publicKeys[0x033] = GLFW_KEY_COMMA; - _glfw.win32.publicKeys[0x00D] = GLFW_KEY_EQUAL; - _glfw.win32.publicKeys[0x029] = GLFW_KEY_GRAVE_ACCENT; - _glfw.win32.publicKeys[0x01A] = GLFW_KEY_LEFT_BRACKET; - _glfw.win32.publicKeys[0x00C] = GLFW_KEY_MINUS; - _glfw.win32.publicKeys[0x034] = GLFW_KEY_PERIOD; - _glfw.win32.publicKeys[0x01B] = GLFW_KEY_RIGHT_BRACKET; - _glfw.win32.publicKeys[0x027] = GLFW_KEY_SEMICOLON; - _glfw.win32.publicKeys[0x035] = GLFW_KEY_SLASH; - _glfw.win32.publicKeys[0x056] = GLFW_KEY_WORLD_2; + _glfw.win32.keycodes[0x028] = GLFW_KEY_APOSTROPHE; + _glfw.win32.keycodes[0x02B] = GLFW_KEY_BACKSLASH; + _glfw.win32.keycodes[0x033] = GLFW_KEY_COMMA; + _glfw.win32.keycodes[0x00D] = GLFW_KEY_EQUAL; + _glfw.win32.keycodes[0x029] = GLFW_KEY_GRAVE_ACCENT; + _glfw.win32.keycodes[0x01A] = GLFW_KEY_LEFT_BRACKET; + _glfw.win32.keycodes[0x00C] = GLFW_KEY_MINUS; + _glfw.win32.keycodes[0x034] = GLFW_KEY_PERIOD; + _glfw.win32.keycodes[0x01B] = GLFW_KEY_RIGHT_BRACKET; + _glfw.win32.keycodes[0x027] = GLFW_KEY_SEMICOLON; + _glfw.win32.keycodes[0x035] = GLFW_KEY_SLASH; + _glfw.win32.keycodes[0x056] = GLFW_KEY_WORLD_2; - _glfw.win32.publicKeys[0x00E] = GLFW_KEY_BACKSPACE; - _glfw.win32.publicKeys[0x153] = GLFW_KEY_DELETE; - _glfw.win32.publicKeys[0x14F] = GLFW_KEY_END; - _glfw.win32.publicKeys[0x01C] = GLFW_KEY_ENTER; - _glfw.win32.publicKeys[0x001] = GLFW_KEY_ESCAPE; - _glfw.win32.publicKeys[0x147] = GLFW_KEY_HOME; - _glfw.win32.publicKeys[0x152] = GLFW_KEY_INSERT; - _glfw.win32.publicKeys[0x15D] = GLFW_KEY_MENU; - _glfw.win32.publicKeys[0x151] = GLFW_KEY_PAGE_DOWN; - _glfw.win32.publicKeys[0x149] = GLFW_KEY_PAGE_UP; - _glfw.win32.publicKeys[0x045] = GLFW_KEY_PAUSE; - _glfw.win32.publicKeys[0x146] = GLFW_KEY_PAUSE; - _glfw.win32.publicKeys[0x039] = GLFW_KEY_SPACE; - _glfw.win32.publicKeys[0x00F] = GLFW_KEY_TAB; - _glfw.win32.publicKeys[0x03A] = GLFW_KEY_CAPS_LOCK; - _glfw.win32.publicKeys[0x145] = GLFW_KEY_NUM_LOCK; - _glfw.win32.publicKeys[0x046] = GLFW_KEY_SCROLL_LOCK; - _glfw.win32.publicKeys[0x03B] = GLFW_KEY_F1; - _glfw.win32.publicKeys[0x03C] = GLFW_KEY_F2; - _glfw.win32.publicKeys[0x03D] = GLFW_KEY_F3; - _glfw.win32.publicKeys[0x03E] = GLFW_KEY_F4; - _glfw.win32.publicKeys[0x03F] = GLFW_KEY_F5; - _glfw.win32.publicKeys[0x040] = GLFW_KEY_F6; - _glfw.win32.publicKeys[0x041] = GLFW_KEY_F7; - _glfw.win32.publicKeys[0x042] = GLFW_KEY_F8; - _glfw.win32.publicKeys[0x043] = GLFW_KEY_F9; - _glfw.win32.publicKeys[0x044] = GLFW_KEY_F10; - _glfw.win32.publicKeys[0x057] = GLFW_KEY_F11; - _glfw.win32.publicKeys[0x058] = GLFW_KEY_F12; - _glfw.win32.publicKeys[0x064] = GLFW_KEY_F13; - _glfw.win32.publicKeys[0x065] = GLFW_KEY_F14; - _glfw.win32.publicKeys[0x066] = GLFW_KEY_F15; - _glfw.win32.publicKeys[0x067] = GLFW_KEY_F16; - _glfw.win32.publicKeys[0x068] = GLFW_KEY_F17; - _glfw.win32.publicKeys[0x069] = GLFW_KEY_F18; - _glfw.win32.publicKeys[0x06A] = GLFW_KEY_F19; - _glfw.win32.publicKeys[0x06B] = GLFW_KEY_F20; - _glfw.win32.publicKeys[0x06C] = GLFW_KEY_F21; - _glfw.win32.publicKeys[0x06D] = GLFW_KEY_F22; - _glfw.win32.publicKeys[0x06E] = GLFW_KEY_F23; - _glfw.win32.publicKeys[0x076] = GLFW_KEY_F24; - _glfw.win32.publicKeys[0x038] = GLFW_KEY_LEFT_ALT; - _glfw.win32.publicKeys[0x01D] = GLFW_KEY_LEFT_CONTROL; - _glfw.win32.publicKeys[0x02A] = GLFW_KEY_LEFT_SHIFT; - _glfw.win32.publicKeys[0x15B] = GLFW_KEY_LEFT_SUPER; - _glfw.win32.publicKeys[0x137] = GLFW_KEY_PRINT_SCREEN; - _glfw.win32.publicKeys[0x138] = GLFW_KEY_RIGHT_ALT; - _glfw.win32.publicKeys[0x11D] = GLFW_KEY_RIGHT_CONTROL; - _glfw.win32.publicKeys[0x036] = GLFW_KEY_RIGHT_SHIFT; - _glfw.win32.publicKeys[0x15C] = GLFW_KEY_RIGHT_SUPER; - _glfw.win32.publicKeys[0x150] = GLFW_KEY_DOWN; - _glfw.win32.publicKeys[0x14B] = GLFW_KEY_LEFT; - _glfw.win32.publicKeys[0x14D] = GLFW_KEY_RIGHT; - _glfw.win32.publicKeys[0x148] = GLFW_KEY_UP; + _glfw.win32.keycodes[0x00E] = GLFW_KEY_BACKSPACE; + _glfw.win32.keycodes[0x153] = GLFW_KEY_DELETE; + _glfw.win32.keycodes[0x14F] = GLFW_KEY_END; + _glfw.win32.keycodes[0x01C] = GLFW_KEY_ENTER; + _glfw.win32.keycodes[0x001] = GLFW_KEY_ESCAPE; + _glfw.win32.keycodes[0x147] = GLFW_KEY_HOME; + _glfw.win32.keycodes[0x152] = GLFW_KEY_INSERT; + _glfw.win32.keycodes[0x15D] = GLFW_KEY_MENU; + _glfw.win32.keycodes[0x151] = GLFW_KEY_PAGE_DOWN; + _glfw.win32.keycodes[0x149] = GLFW_KEY_PAGE_UP; + _glfw.win32.keycodes[0x045] = GLFW_KEY_PAUSE; + _glfw.win32.keycodes[0x146] = GLFW_KEY_PAUSE; + _glfw.win32.keycodes[0x039] = GLFW_KEY_SPACE; + _glfw.win32.keycodes[0x00F] = GLFW_KEY_TAB; + _glfw.win32.keycodes[0x03A] = GLFW_KEY_CAPS_LOCK; + _glfw.win32.keycodes[0x145] = GLFW_KEY_NUM_LOCK; + _glfw.win32.keycodes[0x046] = GLFW_KEY_SCROLL_LOCK; + _glfw.win32.keycodes[0x03B] = GLFW_KEY_F1; + _glfw.win32.keycodes[0x03C] = GLFW_KEY_F2; + _glfw.win32.keycodes[0x03D] = GLFW_KEY_F3; + _glfw.win32.keycodes[0x03E] = GLFW_KEY_F4; + _glfw.win32.keycodes[0x03F] = GLFW_KEY_F5; + _glfw.win32.keycodes[0x040] = GLFW_KEY_F6; + _glfw.win32.keycodes[0x041] = GLFW_KEY_F7; + _glfw.win32.keycodes[0x042] = GLFW_KEY_F8; + _glfw.win32.keycodes[0x043] = GLFW_KEY_F9; + _glfw.win32.keycodes[0x044] = GLFW_KEY_F10; + _glfw.win32.keycodes[0x057] = GLFW_KEY_F11; + _glfw.win32.keycodes[0x058] = GLFW_KEY_F12; + _glfw.win32.keycodes[0x064] = GLFW_KEY_F13; + _glfw.win32.keycodes[0x065] = GLFW_KEY_F14; + _glfw.win32.keycodes[0x066] = GLFW_KEY_F15; + _glfw.win32.keycodes[0x067] = GLFW_KEY_F16; + _glfw.win32.keycodes[0x068] = GLFW_KEY_F17; + _glfw.win32.keycodes[0x069] = GLFW_KEY_F18; + _glfw.win32.keycodes[0x06A] = GLFW_KEY_F19; + _glfw.win32.keycodes[0x06B] = GLFW_KEY_F20; + _glfw.win32.keycodes[0x06C] = GLFW_KEY_F21; + _glfw.win32.keycodes[0x06D] = GLFW_KEY_F22; + _glfw.win32.keycodes[0x06E] = GLFW_KEY_F23; + _glfw.win32.keycodes[0x076] = GLFW_KEY_F24; + _glfw.win32.keycodes[0x038] = GLFW_KEY_LEFT_ALT; + _glfw.win32.keycodes[0x01D] = GLFW_KEY_LEFT_CONTROL; + _glfw.win32.keycodes[0x02A] = GLFW_KEY_LEFT_SHIFT; + _glfw.win32.keycodes[0x15B] = GLFW_KEY_LEFT_SUPER; + _glfw.win32.keycodes[0x137] = GLFW_KEY_PRINT_SCREEN; + _glfw.win32.keycodes[0x138] = GLFW_KEY_RIGHT_ALT; + _glfw.win32.keycodes[0x11D] = GLFW_KEY_RIGHT_CONTROL; + _glfw.win32.keycodes[0x036] = GLFW_KEY_RIGHT_SHIFT; + _glfw.win32.keycodes[0x15C] = GLFW_KEY_RIGHT_SUPER; + _glfw.win32.keycodes[0x150] = GLFW_KEY_DOWN; + _glfw.win32.keycodes[0x14B] = GLFW_KEY_LEFT; + _glfw.win32.keycodes[0x14D] = GLFW_KEY_RIGHT; + _glfw.win32.keycodes[0x148] = GLFW_KEY_UP; - _glfw.win32.publicKeys[0x052] = GLFW_KEY_KP_0; - _glfw.win32.publicKeys[0x04F] = GLFW_KEY_KP_1; - _glfw.win32.publicKeys[0x050] = GLFW_KEY_KP_2; - _glfw.win32.publicKeys[0x051] = GLFW_KEY_KP_3; - _glfw.win32.publicKeys[0x04B] = GLFW_KEY_KP_4; - _glfw.win32.publicKeys[0x04C] = GLFW_KEY_KP_5; - _glfw.win32.publicKeys[0x04D] = GLFW_KEY_KP_6; - _glfw.win32.publicKeys[0x047] = GLFW_KEY_KP_7; - _glfw.win32.publicKeys[0x048] = GLFW_KEY_KP_8; - _glfw.win32.publicKeys[0x049] = GLFW_KEY_KP_9; - _glfw.win32.publicKeys[0x04E] = GLFW_KEY_KP_ADD; - _glfw.win32.publicKeys[0x053] = GLFW_KEY_KP_DECIMAL; - _glfw.win32.publicKeys[0x135] = GLFW_KEY_KP_DIVIDE; - _glfw.win32.publicKeys[0x11C] = GLFW_KEY_KP_ENTER; - _glfw.win32.publicKeys[0x037] = GLFW_KEY_KP_MULTIPLY; - _glfw.win32.publicKeys[0x04A] = GLFW_KEY_KP_SUBTRACT; + _glfw.win32.keycodes[0x052] = GLFW_KEY_KP_0; + _glfw.win32.keycodes[0x04F] = GLFW_KEY_KP_1; + _glfw.win32.keycodes[0x050] = GLFW_KEY_KP_2; + _glfw.win32.keycodes[0x051] = GLFW_KEY_KP_3; + _glfw.win32.keycodes[0x04B] = GLFW_KEY_KP_4; + _glfw.win32.keycodes[0x04C] = GLFW_KEY_KP_5; + _glfw.win32.keycodes[0x04D] = GLFW_KEY_KP_6; + _glfw.win32.keycodes[0x047] = GLFW_KEY_KP_7; + _glfw.win32.keycodes[0x048] = GLFW_KEY_KP_8; + _glfw.win32.keycodes[0x049] = GLFW_KEY_KP_9; + _glfw.win32.keycodes[0x04E] = GLFW_KEY_KP_ADD; + _glfw.win32.keycodes[0x053] = GLFW_KEY_KP_DECIMAL; + _glfw.win32.keycodes[0x135] = GLFW_KEY_KP_DIVIDE; + _glfw.win32.keycodes[0x11C] = GLFW_KEY_KP_ENTER; + _glfw.win32.keycodes[0x059] = GLFW_KEY_KP_EQUAL; + _glfw.win32.keycodes[0x037] = GLFW_KEY_KP_MULTIPLY; + _glfw.win32.keycodes[0x04A] = GLFW_KEY_KP_SUBTRACT; for (scancode = 0; scancode < 512; scancode++) { - if (_glfw.win32.publicKeys[scancode] > 0) - _glfw.win32.nativeKeys[_glfw.win32.publicKeys[scancode]] = scancode; + if (_glfw.win32.keycodes[scancode] > 0) + _glfw.win32.scancodes[_glfw.win32.keycodes[scancode]] = scancode; } } // Creates a dummy window for behind-the-scenes work // -static HWND createHelperWindow(void) +static GLFWbool createHelperWindow(void) { - HWND window = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, - _GLFW_WNDCLASSNAME, - L"GLFW helper window", - WS_CLIPSIBLINGS | WS_CLIPCHILDREN, - 0, 0, 1, 1, - HWND_MESSAGE, NULL, - GetModuleHandleW(NULL), - NULL); - if (!window) + MSG msg; + + _glfw.win32.helperWindowHandle = + CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, + _GLFW_WNDCLASSNAME, + L"GLFW message window", + WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, 0, 1, 1, + NULL, NULL, + GetModuleHandleW(NULL), + NULL); + + if (!_glfw.win32.helperWindowHandle) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to create helper window"); - return NULL; + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create helper window"); + return GLFW_FALSE; } - // HACK: The first call to ShowWindow is ignored if the parent process - // passed along a STARTUPINFO, so clear that flag with a no-op call - ShowWindow(window, SW_HIDE); + // HACK: The command to the first ShowWindow call is ignored if the parent + // process passed along a STARTUPINFO, so clear that with a no-op call + ShowWindow(_glfw.win32.helperWindowHandle, SW_HIDE); // Register for HID device notifications { @@ -331,12 +362,19 @@ static HWND createHelperWindow(void) dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; dbi.dbcc_classguid = GUID_DEVINTERFACE_HID; - RegisterDeviceNotificationW(window, - (DEV_BROADCAST_HDR*) &dbi, - DEVICE_NOTIFY_WINDOW_HANDLE); + _glfw.win32.deviceNotificationHandle = + RegisterDeviceNotificationW(_glfw.win32.helperWindowHandle, + (DEV_BROADCAST_HDR*) &dbi, + DEVICE_NOTIFY_WINDOW_HANDLE); } - return window; + while (PeekMessageW(&msg, _glfw.win32.helperWindowHandle, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + return GLFW_TRUE; } @@ -349,16 +387,22 @@ static HWND createHelperWindow(void) WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) { WCHAR* target; - int length; + int count; - length = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0); - if (!length) - return NULL; - - target = calloc(length, sizeof(WCHAR)); - - if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, length)) + count = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0); + if (!count) { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string from UTF-8"); + return NULL; + } + + target = calloc(count, sizeof(WCHAR)); + + if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, count)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string from UTF-8"); free(target); return NULL; } @@ -371,16 +415,22 @@ WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) { char* target; - int length; + int size; - length = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); - if (!length) - return NULL; - - target = calloc(length, 1); - - if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, length, NULL, NULL)) + size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); + if (!size) { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string to UTF-8"); + return NULL; + } + + target = calloc(size, 1); + + if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string to UTF-8"); free(target); return NULL; } @@ -388,6 +438,111 @@ char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) return target; } +// Reports the specified error, appending information about the last Win32 error +// +void _glfwInputErrorWin32(int error, const char* description) +{ + WCHAR buffer[_GLFW_MESSAGE_SIZE] = L""; + char message[_GLFW_MESSAGE_SIZE] = ""; + + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, + GetLastError() & 0xffff, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buffer, + sizeof(buffer) / sizeof(WCHAR), + NULL); + WideCharToMultiByte(CP_UTF8, 0, buffer, -1, message, sizeof(message), NULL, NULL); + + _glfwInputError(error, "%s: %s", description, message); +} + +// Updates key names according to the current keyboard layout +// +void _glfwUpdateKeyNamesWin32(void) +{ + int key; + BYTE state[256] = {0}; + + memset(_glfw.win32.keynames, 0, sizeof(_glfw.win32.keynames)); + + for (key = GLFW_KEY_SPACE; key <= GLFW_KEY_LAST; key++) + { + UINT vk; + int scancode, length; + WCHAR chars[16]; + + scancode = _glfw.win32.scancodes[key]; + if (scancode == -1) + continue; + + if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD) + { + const UINT vks[] = { + VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, + VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, + VK_NUMPAD8, VK_NUMPAD9, VK_DECIMAL, VK_DIVIDE, + VK_MULTIPLY, VK_SUBTRACT, VK_ADD + }; + + vk = vks[key - GLFW_KEY_KP_0]; + } + else + vk = MapVirtualKey(scancode, MAPVK_VSC_TO_VK); + + length = ToUnicode(vk, scancode, state, + chars, sizeof(chars) / sizeof(WCHAR), + 0); + + if (length == -1) + { + length = ToUnicode(vk, scancode, state, + chars, sizeof(chars) / sizeof(WCHAR), + 0); + } + + if (length < 1) + continue; + + WideCharToMultiByte(CP_UTF8, 0, chars, 1, + _glfw.win32.keynames[key], + sizeof(_glfw.win32.keynames[key]), + NULL, NULL); + } +} + +// Replacement for IsWindowsVersionOrGreater as MinGW lacks versionhelpers.h +// +BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp }; + DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; + ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the + // latter lies unless the user knew to embedd a non-default manifest + // announcing support for Windows 10 via supportedOS GUID + return RtlVerifyVersionInfo(&osvi, mask, cond) == 0; +} + +// Checks whether we are on at least the specified build of Windows 10 +// +BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), 10, 0, build }; + DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER; + ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_BUILDNUMBER, VER_GREATER_EQUAL); + // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the + // latter lies unless the user knew to embedd a non-default manifest + // announcing support for Windows 10 via supportedOS GUID + return RtlVerifyVersionInfo(&osvi, mask, cond) == 0; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -395,9 +550,6 @@ char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) int _glfwPlatformInit(void) { - if (!_glfwInitThreadLocalStorageWin32()) - return GLFW_FALSE; - // To make SetForegroundWindow work as we want, we need to fiddle // with the FOREGROUNDLOCKTIMEOUT system setting (we do this as early // as possible in the hope of still being the foreground process) @@ -410,29 +562,33 @@ int _glfwPlatformInit(void) return GLFW_FALSE; createKeyTables(); + _glfwUpdateKeyNamesWin32(); - if (_glfw_SetProcessDpiAwareness) - _glfw_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - else if (_glfw_SetProcessDPIAware) - _glfw_SetProcessDPIAware(); + if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + else if (IsWindows8Point1OrGreater()) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + else if (IsWindowsVistaOrGreater()) + SetProcessDPIAware(); if (!_glfwRegisterWindowClassWin32()) return GLFW_FALSE; - _glfw.win32.helperWindowHandle = createHelperWindow(); - if (!_glfw.win32.helperWindowHandle) + if (!createHelperWindow()) return GLFW_FALSE; - _glfwPlatformPollEvents(); - _glfwInitTimerWin32(); _glfwInitJoysticksWin32(); + _glfwPollMonitorsWin32(); return GLFW_TRUE; } void _glfwPlatformTerminate(void) { + if (_glfw.win32.deviceNotificationHandle) + UnregisterDeviceNotification(_glfw.win32.deviceNotificationHandle); + if (_glfw.win32.helperWindowHandle) DestroyWindow(_glfw.win32.helperWindowHandle); @@ -444,19 +600,19 @@ void _glfwPlatformTerminate(void) SPIF_SENDCHANGE); free(_glfw.win32.clipboardString); + free(_glfw.win32.rawInput); _glfwTerminateWGL(); _glfwTerminateEGL(); _glfwTerminateJoysticksWin32(); - _glfwTerminateThreadLocalStorageWin32(); freeLibraries(); } const char* _glfwPlatformGetVersionString(void) { - return _GLFW_VERSION_NUMBER " Win32 WGL EGL" + return _GLFW_VERSION_NUMBER " Win32 WGL EGL OSMesa" #if defined(__MINGW32__) " MinGW" #elif defined(_MSC_VER) diff --git a/external/glfw/win32_joystick.c b/external/glfw/win32_joystick.c index 49f3b87..5c3e87d 100644 --- a/external/glfw/win32_joystick.c +++ b/external/glfw/win32_joystick.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.1 Win32 - www.glfw.org +// GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -27,13 +27,9 @@ #include "internal.h" +#include #include -#include - -#define _GLFW_PRESENCE_ONLY 1 -#define _GLFW_UPDATE_STATE 2 - #define _GLFW_TYPE_AXIS 0 #define _GLFW_TYPE_SLIDER 1 #define _GLFW_TYPE_BUTTON 2 @@ -52,18 +48,36 @@ typedef struct _GLFWobjenumWin32 int povCount; } _GLFWobjenumWin32; -// Define only the necessary GUIDs (it's bad enough that we're exporting these) +// Define local copies of the necessary GUIDs // -DEFINE_GUID(IID_IDirectInput8W,0xbf798031,0x483a,0x4da2,0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00); -DEFINE_GUID(GUID_XAxis,0xa36d02e0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_YAxis,0xa36d02e1,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_ZAxis,0xa36d02e2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_RxAxis,0xa36d02f4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_RyAxis,0xa36d02f5,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_RzAxis,0xa36d02e3,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_Slider,0xa36d02e4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_Button,0xa36d02f0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_POV,0xa36d02f2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +static const GUID _glfw_IID_IDirectInput8W = + {0xbf798031,0x483a,0x4da2,{0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00}}; +static const GUID _glfw_GUID_XAxis = + {0xa36d02e0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_YAxis = + {0xa36d02e1,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_ZAxis = + {0xa36d02e2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_RxAxis = + {0xa36d02f4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_RyAxis = + {0xa36d02f5,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_RzAxis = + {0xa36d02e3,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_Slider = + {0xa36d02e4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_POV = + {0xa36d02f2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; + +#define IID_IDirectInput8W _glfw_IID_IDirectInput8W +#define GUID_XAxis _glfw_GUID_XAxis +#define GUID_YAxis _glfw_GUID_YAxis +#define GUID_ZAxis _glfw_GUID_ZAxis +#define GUID_RxAxis _glfw_GUID_RxAxis +#define GUID_RyAxis _glfw_GUID_RyAxis +#define GUID_RzAxis _glfw_GUID_RzAxis +#define GUID_Slider _glfw_GUID_Slider +#define GUID_POV _glfw_GUID_POV // Object data array for our clone of c_dfDIJoystick // Generated with https://github.com/elmindreda/c_dfDIJoystick2 @@ -149,9 +163,9 @@ static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic) case XINPUT_DEVSUBTYPE_GAMEPAD: { if (xic->Flags & XINPUT_CAPS_WIRELESS) - return "Wireless Xbox 360 Controller"; + return "Wireless Xbox Controller"; else - return "Xbox 360 Controller"; + return "Xbox Controller"; } } @@ -238,25 +252,22 @@ static GLFWbool supportsXInput(const GUID* guid) // Frees all resources associated with the specified joystick // -static void closeJoystick(_GLFWjoystickWin32* js) +static void closeJoystick(_GLFWjoystick* js) { - if (js->device) + if (js->win32.device) { - IDirectInputDevice8_Unacquire(js->device); - IDirectInputDevice8_Release(js->device); + IDirectInputDevice8_Unacquire(js->win32.device); + IDirectInputDevice8_Release(js->win32.device); } - free(js->name); - free(js->axes); - free(js->buttons); - free(js->objects); - memset(js, 0, sizeof(_GLFWjoystickWin32)); + free(js->win32.objects); - _glfwInputJoystickChange((int) (js - _glfw.win32_js), GLFW_DISCONNECTED); + _glfwFreeJoystick(js); + _glfwInputJoystick(js, GLFW_DISCONNECTED); } // DirectInput device object enumeration callback -// Insights gleaned from SDL2 +// Insights gleaned from SDL // static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi, void* user) @@ -332,28 +343,25 @@ static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi, // static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) { - int joy = 0; + int jid = 0; DIDEVCAPS dc; DIPROPDWORD dipd; IDirectInputDevice8* device; _GLFWobjenumWin32 data; - _GLFWjoystickWin32* js; + _GLFWjoystick* js; + char guid[33]; + char name[256]; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (memcmp(&_glfw.win32_js[joy].guid, &di->guidInstance, sizeof(GUID)) == 0) - return DIENUM_CONTINUE; + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + { + if (memcmp(&js->win32.guid, &di->guidInstance, sizeof(GUID)) == 0) + return DIENUM_CONTINUE; + } } - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - { - if (!_glfw.win32_js[joy].present) - break; - } - - if (joy > GLFW_JOYSTICK_LAST) - return DIENUM_STOP; - if (supportsXInput(&di->guidProduct)) return DIENUM_CONTINUE; @@ -362,14 +370,14 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) &device, NULL))) { - _glfwInputError(GLFW_PLATFORM_ERROR, "DI: Failed to create device"); + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create device"); return DIENUM_CONTINUE; } if (FAILED(IDirectInputDevice8_SetDataFormat(device, &_glfwDataFormat))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to set device data format"); + "Win32: Failed to set device data format"); IDirectInputDevice8_Release(device); return DIENUM_CONTINUE; @@ -381,7 +389,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) if (FAILED(IDirectInputDevice8_GetCapabilities(device, &dc))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to query device capabilities"); + "Win32: Failed to query device capabilities"); IDirectInputDevice8_Release(device); return DIENUM_CONTINUE; @@ -398,7 +406,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) &dipd.diph))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to set device axis mode"); + "Win32: Failed to set device axis mode"); IDirectInputDevice8_Release(device); return DIENUM_CONTINUE; @@ -406,7 +414,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) memset(&data, 0, sizeof(data)); data.device = device; - data.objects = calloc(dc.dwAxes + dc.dwButtons + dc.dwPOVs, + data.objects = calloc(dc.dwAxes + (size_t) dc.dwButtons + dc.dwPOVs, sizeof(_GLFWjoyobjectWin32)); if (FAILED(IDirectInputDevice8_EnumObjects(device, @@ -415,7 +423,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to enumerate device objects"); + "Win32: Failed to enumerate device objects"); IDirectInputDevice8_Release(device); free(data.objects); @@ -426,224 +434,54 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) sizeof(_GLFWjoyobjectWin32), compareJoystickObjects); - js = _glfw.win32_js + joy; - js->device = device; - js->guid = di->guidInstance; - js->axisCount = data.axisCount + data.sliderCount; - js->axes = calloc(js->axisCount, sizeof(float)); - js->buttonCount += data.buttonCount + data.povCount * 4; - js->buttons = calloc(js->buttonCount, 1); - js->objects = data.objects; - js->objectCount = data.objectCount; - js->name = _glfwCreateUTF8FromWideStringWin32(di->tszInstanceName); - js->present = GLFW_TRUE; - - _glfwInputJoystickChange(joy, GLFW_CONNECTED); - return DIENUM_CONTINUE; -} - -// Attempt to open the specified joystick device -// TODO: Pack state arrays for non-gamepad devices -// -static GLFWbool openXinputDevice(DWORD index) -{ - int joy; - XINPUT_CAPABILITIES xic; - _GLFWjoystickWin32* js; - - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + if (!WideCharToMultiByte(CP_UTF8, 0, + di->tszInstanceName, -1, + name, sizeof(name), + NULL, NULL)) { - if (_glfw.win32_js[joy].present && - _glfw.win32_js[joy].device == NULL && - _glfw.win32_js[joy].index == index) - { - return GLFW_FALSE; - } + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert joystick name to UTF-8"); + + IDirectInputDevice8_Release(device); + free(data.objects); + return DIENUM_STOP; } - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + // Generate a joystick GUID that matches the SDL 2.0.5+ one + if (memcmp(&di->guidProduct.Data4[2], "PIDVID", 6) == 0) { - if (!_glfw.win32_js[joy].present) - break; - } - - if (joy > GLFW_JOYSTICK_LAST) - return GLFW_FALSE; - - if (_glfw_XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) - return GLFW_FALSE; - - js = _glfw.win32_js + joy; - js->axisCount = 6; - js->axes = calloc(js->axisCount, sizeof(float)); - js->buttonCount = 14; - js->buttons = calloc(js->buttonCount, 1); - js->present = GLFW_TRUE; - js->name = strdup(getDeviceDescription(&xic)); - js->index = index; - - _glfwInputJoystickChange(joy, GLFW_CONNECTED); - - return GLFW_TRUE; -} - -// Polls for and processes events the specified joystick -// -static GLFWbool pollJoystickState(_GLFWjoystickWin32* js, int mode) -{ - if (!js->present) - return GLFW_FALSE; - - if (js->device) - { - int i, j, ai = 0, bi = 0; - HRESULT result; - DIJOYSTATE state; - - IDirectInputDevice8_Poll(js->device); - result = IDirectInputDevice8_GetDeviceState(js->device, - sizeof(state), - &state); - if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST) - { - IDirectInputDevice8_Acquire(js->device); - IDirectInputDevice8_Poll(js->device); - result = IDirectInputDevice8_GetDeviceState(js->device, - sizeof(state), - &state); - } - - if (FAILED(result)) - { - closeJoystick(js); - return GLFW_FALSE; - } - - if (mode == _GLFW_PRESENCE_ONLY) - return GLFW_TRUE; - - for (i = 0; i < js->objectCount; i++) - { - const void* data = (char*) &state + js->objects[i].offset; - - switch (js->objects[i].type) - { - case _GLFW_TYPE_AXIS: - case _GLFW_TYPE_SLIDER: - { - js->axes[ai++] = (*((LONG*) data) + 0.5f) / 32767.5f; - break; - } - - case _GLFW_TYPE_BUTTON: - { - if (*((BYTE*) data) & 0x80) - js->buttons[bi++] = GLFW_PRESS; - else - js->buttons[bi++] = GLFW_RELEASE; - - break; - } - - case _GLFW_TYPE_POV: - { - const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; - // Screams of horror are appropriate at this point - int value = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); - if (value < 0 || value > 8) - value = 8; - - for (j = 0; j < 4; j++) - { - if (directions[value] & (1 << j)) - js->buttons[bi++] = GLFW_PRESS; - else - js->buttons[bi++] = GLFW_RELEASE; - } - - break; - } - } - } - - return GLFW_TRUE; + sprintf(guid, "03000000%02x%02x0000%02x%02x000000000000", + (uint8_t) di->guidProduct.Data1, + (uint8_t) (di->guidProduct.Data1 >> 8), + (uint8_t) (di->guidProduct.Data1 >> 16), + (uint8_t) (di->guidProduct.Data1 >> 24)); } else { - int i; - DWORD result; - XINPUT_STATE xis; - const WORD buttons[14] = - { - XINPUT_GAMEPAD_A, - XINPUT_GAMEPAD_B, - XINPUT_GAMEPAD_X, - XINPUT_GAMEPAD_Y, - XINPUT_GAMEPAD_LEFT_SHOULDER, - XINPUT_GAMEPAD_RIGHT_SHOULDER, - XINPUT_GAMEPAD_BACK, - XINPUT_GAMEPAD_START, - XINPUT_GAMEPAD_LEFT_THUMB, - XINPUT_GAMEPAD_RIGHT_THUMB, - XINPUT_GAMEPAD_DPAD_UP, - XINPUT_GAMEPAD_DPAD_RIGHT, - XINPUT_GAMEPAD_DPAD_DOWN, - XINPUT_GAMEPAD_DPAD_LEFT - }; - - result = _glfw_XInputGetState(js->index, &xis); - if (result != ERROR_SUCCESS) - { - if (result == ERROR_DEVICE_NOT_CONNECTED) - closeJoystick(js); - - return GLFW_FALSE; - } - - if (mode == _GLFW_PRESENCE_ONLY) - return GLFW_TRUE; - - if (sqrt((double) (xis.Gamepad.sThumbLX * xis.Gamepad.sThumbLX + - xis.Gamepad.sThumbLY * xis.Gamepad.sThumbLY)) > - (double) XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) - { - js->axes[0] = (xis.Gamepad.sThumbLX + 0.5f) / 32767.f; - js->axes[1] = (xis.Gamepad.sThumbLY + 0.5f) / 32767.f; - } - else - { - js->axes[0] = 0.f; - js->axes[1] = 0.f; - } - - if (sqrt((double) (xis.Gamepad.sThumbRX * xis.Gamepad.sThumbRX + - xis.Gamepad.sThumbRY * xis.Gamepad.sThumbRY)) > - (double) XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) - { - js->axes[2] = (xis.Gamepad.sThumbRX + 0.5f) / 32767.f; - js->axes[3] = (xis.Gamepad.sThumbRY + 0.5f) / 32767.f; - } - else - { - js->axes[2] = 0.f; - js->axes[3] = 0.f; - } - - if (xis.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) - js->axes[4] = xis.Gamepad.bLeftTrigger / 127.5f - 1.f; - else - js->axes[4] = -1.f; - - if (xis.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) - js->axes[5] = xis.Gamepad.bRightTrigger / 127.5f - 1.f; - else - js->axes[5] = -1.f; - - for (i = 0; i < 14; i++) - js->buttons[i] = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; - - return GLFW_TRUE; + sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", + name[0], name[1], name[2], name[3], + name[4], name[5], name[6], name[7], + name[8], name[9], name[10]); } + + js = _glfwAllocJoystick(name, guid, + data.axisCount + data.sliderCount, + data.buttonCount, + data.povCount); + if (!js) + { + IDirectInputDevice8_Release(device); + free(data.objects); + return DIENUM_STOP; + } + + js->win32.device = device; + js->win32.guid = di->guidInstance; + js->win32.objects = data.objects; + js->win32.objectCount = data.objectCount; + + _glfwInputJoystick(js, GLFW_CONNECTED); + return DIENUM_CONTINUE; } @@ -657,14 +495,14 @@ void _glfwInitJoysticksWin32(void) { if (_glfw.win32.dinput8.instance) { - if (FAILED(_glfw_DirectInput8Create(GetModuleHandle(NULL), - DIRECTINPUT_VERSION, - &IID_IDirectInput8W, - (void**) &_glfw.win32.dinput8.api, - NULL))) + if (FAILED(DirectInput8Create(GetModuleHandle(NULL), + DIRECTINPUT_VERSION, + &IID_IDirectInput8W, + (void**) &_glfw.win32.dinput8.api, + NULL))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to create interface"); + "Win32: Failed to create interface"); } } @@ -675,10 +513,10 @@ void _glfwInitJoysticksWin32(void) // void _glfwTerminateJoysticksWin32(void) { - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - closeJoystick(_glfw.win32_js + joy); + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + closeJoystick(_glfw.joysticks + jid); if (_glfw.win32.dinput8.api) IDirectInput8_Release(_glfw.win32.dinput8.api); @@ -690,10 +528,43 @@ void _glfwDetectJoystickConnectionWin32(void) { if (_glfw.win32.xinput.instance) { - DWORD i; + DWORD index; - for (i = 0; i < XUSER_MAX_COUNT; i++) - openXinputDevice(i); + for (index = 0; index < XUSER_MAX_COUNT; index++) + { + int jid; + char guid[33]; + XINPUT_CAPABILITIES xic; + _GLFWjoystick* js; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (_glfw.joysticks[jid].present && + _glfw.joysticks[jid].win32.device == NULL && + _glfw.joysticks[jid].win32.index == index) + { + break; + } + } + + if (jid <= GLFW_JOYSTICK_LAST) + continue; + + if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) + continue; + + // Generate a joystick GUID that matches the SDL 2.0.5+ one + sprintf(guid, "78696e707574%02x000000000000000000", + xic.SubType & 0xff); + + js = _glfwAllocJoystick(getDeviceDescription(&xic), guid, 6, 10, 1); + if (!js) + continue; + + js->win32.index = index; + + _glfwInputJoystick(js, GLFW_CONNECTED); + } } if (_glfw.win32.dinput8.api) @@ -715,10 +586,14 @@ void _glfwDetectJoystickConnectionWin32(void) // void _glfwDetectJoystickDisconnectionWin32(void) { - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - pollJoystickState(_glfw.win32_js + joy, _GLFW_PRESENCE_ONLY); + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); + } } @@ -726,38 +601,153 @@ void _glfwDetectJoystickDisconnectionWin32(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformJoystickPresent(int joy) +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) { - _GLFWjoystickWin32* js = _glfw.win32_js + joy; - return pollJoystickState(js, _GLFW_PRESENCE_ONLY); + if (js->win32.device) + { + int i, ai = 0, bi = 0, pi = 0; + HRESULT result; + DIJOYSTATE state; + + IDirectInputDevice8_Poll(js->win32.device); + result = IDirectInputDevice8_GetDeviceState(js->win32.device, + sizeof(state), + &state); + if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST) + { + IDirectInputDevice8_Acquire(js->win32.device); + IDirectInputDevice8_Poll(js->win32.device); + result = IDirectInputDevice8_GetDeviceState(js->win32.device, + sizeof(state), + &state); + } + + if (FAILED(result)) + { + closeJoystick(js); + return GLFW_FALSE; + } + + if (mode == _GLFW_POLL_PRESENCE) + return GLFW_TRUE; + + for (i = 0; i < js->win32.objectCount; i++) + { + const void* data = (char*) &state + js->win32.objects[i].offset; + + switch (js->win32.objects[i].type) + { + case _GLFW_TYPE_AXIS: + case _GLFW_TYPE_SLIDER: + { + const float value = (*((LONG*) data) + 0.5f) / 32767.5f; + _glfwInputJoystickAxis(js, ai, value); + ai++; + break; + } + + case _GLFW_TYPE_BUTTON: + { + const char value = (*((BYTE*) data) & 0x80) != 0; + _glfwInputJoystickButton(js, bi, value); + bi++; + break; + } + + case _GLFW_TYPE_POV: + { + const int states[9] = + { + GLFW_HAT_UP, + GLFW_HAT_RIGHT_UP, + GLFW_HAT_RIGHT, + GLFW_HAT_RIGHT_DOWN, + GLFW_HAT_DOWN, + GLFW_HAT_LEFT_DOWN, + GLFW_HAT_LEFT, + GLFW_HAT_LEFT_UP, + GLFW_HAT_CENTERED + }; + + // Screams of horror are appropriate at this point + int state = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); + if (state < 0 || state > 8) + state = 8; + + _glfwInputJoystickHat(js, pi, states[state]); + pi++; + break; + } + } + } + } + else + { + int i, dpad = 0; + DWORD result; + XINPUT_STATE xis; + const WORD buttons[10] = + { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_BACK, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB + }; + + result = XInputGetState(js->win32.index, &xis); + if (result != ERROR_SUCCESS) + { + if (result == ERROR_DEVICE_NOT_CONNECTED) + closeJoystick(js); + + return GLFW_FALSE; + } + + if (mode == _GLFW_POLL_PRESENCE) + return GLFW_TRUE; + + _glfwInputJoystickAxis(js, 0, (xis.Gamepad.sThumbLX + 0.5f) / 32767.5f); + _glfwInputJoystickAxis(js, 1, -(xis.Gamepad.sThumbLY + 0.5f) / 32767.5f); + _glfwInputJoystickAxis(js, 2, (xis.Gamepad.sThumbRX + 0.5f) / 32767.5f); + _glfwInputJoystickAxis(js, 3, -(xis.Gamepad.sThumbRY + 0.5f) / 32767.5f); + _glfwInputJoystickAxis(js, 4, xis.Gamepad.bLeftTrigger / 127.5f - 1.f); + _glfwInputJoystickAxis(js, 5, xis.Gamepad.bRightTrigger / 127.5f - 1.f); + + for (i = 0; i < 10; i++) + { + const char value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; + _glfwInputJoystickButton(js, i, value); + } + + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) + dpad |= GLFW_HAT_UP; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) + dpad |= GLFW_HAT_RIGHT; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) + dpad |= GLFW_HAT_DOWN; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) + dpad |= GLFW_HAT_LEFT; + + _glfwInputJoystickHat(js, 0, dpad); + } + + return GLFW_TRUE; } -const float* _glfwPlatformGetJoystickAxes(int joy, int* count) +void _glfwPlatformUpdateGamepadGUID(char* guid) { - _GLFWjoystickWin32* js = _glfw.win32_js + joy; - if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) - return NULL; - - *count = js->axisCount; - return js->axes; -} - -const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) -{ - _GLFWjoystickWin32* js = _glfw.win32_js + joy; - if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) - return NULL; - - *count = js->buttonCount; - return js->buttons; -} - -const char* _glfwPlatformGetJoystickName(int joy) -{ - _GLFWjoystickWin32* js = _glfw.win32_js + joy; - if (!pollJoystickState(js, _GLFW_PRESENCE_ONLY)) - return NULL; - - return js->name; + if (strcmp(guid + 20, "504944564944") == 0) + { + char original[33]; + strncpy(original, guid, sizeof(original) - 1); + sprintf(guid, "03000000%.4s0000%.4s000000000000", + original, original + 4); + } } diff --git a/external/glfw/win32_joystick.h b/external/glfw/win32_joystick.h index 44ce953..22bcded 100644 --- a/external/glfw/win32_joystick.h +++ b/external/glfw/win32_joystick.h @@ -1,7 +1,7 @@ //======================================================================== // GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -24,11 +24,10 @@ // //======================================================================== -#ifndef _glfw3_win32_joystick_h_ -#define _glfw3_win32_joystick_h_ +#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickWin32 win32 +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE int dummy -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \ - _GLFWjoystickWin32 win32_js[GLFW_JOYSTICK_LAST + 1] +#define _GLFW_PLATFORM_MAPPING_NAME "Windows" // Joystick element (axis, button or slider) // @@ -42,14 +41,8 @@ typedef struct _GLFWjoyobjectWin32 // typedef struct _GLFWjoystickWin32 { - GLFWbool present; - float* axes; - int axisCount; - unsigned char* buttons; - int buttonCount; _GLFWjoyobjectWin32* objects; int objectCount; - char* name; IDirectInputDevice8W* device; DWORD index; GUID guid; @@ -61,4 +54,3 @@ void _glfwTerminateJoysticksWin32(void); void _glfwDetectJoystickConnectionWin32(void); void _glfwDetectJoystickDisconnectionWin32(void); -#endif // _glfw3_win32_joystick_h_ diff --git a/external/glfw/win32_monitor.c b/external/glfw/win32_monitor.c index dc29f7c..e6875db 100644 --- a/external/glfw/win32_monitor.c +++ b/external/glfw/win32_monitor.c @@ -2,7 +2,7 @@ // GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -33,33 +33,66 @@ #include +// Callback for EnumDisplayMonitors in createMonitor +// +static BOOL CALLBACK monitorCallback(HMONITOR handle, + HDC dc, + RECT* rect, + LPARAM data) +{ + MONITORINFOEXW mi; + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + + if (GetMonitorInfoW(handle, (MONITORINFO*) &mi)) + { + _GLFWmonitor* monitor = (_GLFWmonitor*) data; + if (wcscmp(mi.szDevice, monitor->win32.adapterName) == 0) + monitor->win32.handle = handle; + } + + return TRUE; +} + // Create monitor from an adapter and (optionally) a display // static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* display) { _GLFWmonitor* monitor; + int widthMM, heightMM; char* name; HDC dc; + DEVMODEW dm; + RECT rect; if (display) name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString); else name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString); if (!name) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert string to UTF-8"); return NULL; - } + + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm); dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL); - monitor = _glfwAllocMonitor(name, - GetDeviceCaps(dc, HORZSIZE), - GetDeviceCaps(dc, VERTSIZE)); + if (IsWindows8Point1OrGreater()) + { + widthMM = GetDeviceCaps(dc, HORZSIZE); + heightMM = GetDeviceCaps(dc, VERTSIZE); + } + else + { + widthMM = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX)); + heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY)); + } DeleteDC(dc); + + monitor = _glfwAllocMonitor(name, widthMM, heightMM); free(name); if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) @@ -82,6 +115,12 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, NULL, NULL); } + rect.left = dm.dmPosition.x; + rect.top = dm.dmPosition.y; + rect.right = dm.dmPosition.x + dm.dmPelsWidth; + rect.bottom = dm.dmPosition.y + dm.dmPelsHeight; + + EnumDisplayMonitors(NULL, &rect, monitorCallback, (LPARAM) monitor); return monitor; } @@ -90,21 +129,132 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// +// Poll for changes in the set of connected monitors +// +void _glfwPollMonitorsWin32(void) +{ + int i, disconnectedCount; + _GLFWmonitor** disconnected = NULL; + DWORD adapterIndex, displayIndex; + DISPLAY_DEVICEW adapter, display; + _GLFWmonitor* monitor; + + disconnectedCount = _glfw.monitorCount; + if (disconnectedCount) + { + disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + memcpy(disconnected, + _glfw.monitors, + _glfw.monitorCount * sizeof(_GLFWmonitor*)); + } + + for (adapterIndex = 0; ; adapterIndex++) + { + int type = _GLFW_INSERT_LAST; + + ZeroMemory(&adapter, sizeof(adapter)); + adapter.cb = sizeof(adapter); + + if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) + break; + + if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) + type = _GLFW_INSERT_FIRST; + + for (displayIndex = 0; ; displayIndex++) + { + ZeroMemory(&display, sizeof(display)); + display.cb = sizeof(display); + + if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) + break; + + if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i] && + wcscmp(disconnected[i]->win32.displayName, + display.DeviceName) == 0) + { + disconnected[i] = NULL; + break; + } + } + + if (i < disconnectedCount) + continue; + + monitor = createMonitor(&adapter, &display); + if (!monitor) + { + free(disconnected); + return; + } + + _glfwInputMonitor(monitor, GLFW_CONNECTED, type); + + type = _GLFW_INSERT_LAST; + } + + // HACK: If an active adapter does not have any display devices + // (as sometimes happens), add it directly as a monitor + if (displayIndex == 0) + { + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i] && + wcscmp(disconnected[i]->win32.adapterName, + adapter.DeviceName) == 0) + { + disconnected[i] = NULL; + break; + } + } + + if (i < disconnectedCount) + continue; + + monitor = createMonitor(&adapter, NULL); + if (!monitor) + { + free(disconnected); + return; + } + + _glfwInputMonitor(monitor, GLFW_CONNECTED, type); + } + } + + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i]) + _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); + } + + free(disconnected); +} + // Change the current video mode // -GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired) +void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired) { GLFWvidmode current; const GLFWvidmode* best; DEVMODEW dm; + LONG result; best = _glfwChooseVideoMode(monitor, desired); _glfwPlatformGetVideoMode(monitor, ¤t); if (_glfwCompareVideoModes(¤t, best) == 0) - return GLFW_TRUE; + return; ZeroMemory(&dm, sizeof(dm)); - dm.dmSize = sizeof(DEVMODEW); + dm.dmSize = sizeof(dm); dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; dm.dmPelsWidth = best->width; @@ -115,18 +265,36 @@ GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desire if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24) dm.dmBitsPerPel = 32; - if (ChangeDisplaySettingsExW(monitor->win32.adapterName, - &dm, - NULL, - CDS_FULLSCREEN, - NULL) != DISP_CHANGE_SUCCESSFUL) + result = ChangeDisplaySettingsExW(monitor->win32.adapterName, + &dm, + NULL, + CDS_FULLSCREEN, + NULL); + if (result == DISP_CHANGE_SUCCESSFUL) + monitor->win32.modeChanged = GLFW_TRUE; + else { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to set video mode"); - return GLFW_FALSE; - } + const char* description = "Unknown error"; - monitor->win32.modeChanged = GLFW_TRUE; - return GLFW_TRUE; + if (result == DISP_CHANGE_BADDUALVIEW) + description = "The system uses DualView"; + else if (result == DISP_CHANGE_BADFLAGS) + description = "Invalid flags"; + else if (result == DISP_CHANGE_BADMODE) + description = "Graphics mode not supported"; + else if (result == DISP_CHANGE_BADPARAM) + description = "Invalid parameter"; + else if (result == DISP_CHANGE_FAILED) + description = "Graphics mode failed"; + else if (result == DISP_CHANGE_NOTUPDATED) + description = "Failed to write to registry"; + else if (result == DISP_CHANGE_RESTART) + description = "Computer restart required"; + + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to set video mode: %s", + description); + } } // Restore the previously saved (original) video mode @@ -141,111 +309,73 @@ void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) } } +void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale) +{ + UINT xdpi, ydpi; + + if (IsWindows8Point1OrGreater()) + GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + else + { + const HDC dc = GetDC(NULL); + xdpi = GetDeviceCaps(dc, LOGPIXELSX); + ydpi = GetDeviceCaps(dc, LOGPIXELSY); + ReleaseDC(NULL, dc); + } + + if (xscale) + *xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI; + if (yscale) + *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -_GLFWmonitor** _glfwPlatformGetMonitors(int* count) +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) { - int found = 0; - DWORD adapterIndex, displayIndex, primaryIndex = 0; - DISPLAY_DEVICEW adapter, display; - GLFWbool hasDisplays = GLFW_FALSE; - _GLFWmonitor** monitors = NULL; - - *count = 0; - - // HACK: Check if any active adapters have connected displays - // If not, this is a headless system or a VMware guest - - for (adapterIndex = 0; ; adapterIndex++) - { - ZeroMemory(&adapter, sizeof(DISPLAY_DEVICEW)); - adapter.cb = sizeof(DISPLAY_DEVICEW); - - if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) - break; - - if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) - continue; - - ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); - display.cb = sizeof(DISPLAY_DEVICEW); - - if (EnumDisplayDevicesW(adapter.DeviceName, 0, &display, 0)) - { - hasDisplays = GLFW_TRUE; - break; - } - } - - for (adapterIndex = 0; ; adapterIndex++) - { - ZeroMemory(&adapter, sizeof(DISPLAY_DEVICEW)); - adapter.cb = sizeof(DISPLAY_DEVICEW); - - if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) - break; - - if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) - continue; - - if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) - primaryIndex = found; - - if (hasDisplays) - { - for (displayIndex = 0; ; displayIndex++) - { - ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); - display.cb = sizeof(DISPLAY_DEVICEW); - - if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) - break; - - found++; - monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found); - monitors[found - 1] = createMonitor(&adapter, &display); - } - } - else - { - found++; - monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found); - monitors[found - 1] = createMonitor(&adapter, NULL); - } - } - - _GLFW_SWAP_POINTERS(monitors[0], monitors[primaryIndex]); - - *count = found; - return monitors; -} - -GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) -{ - if (wcslen(first->win32.displayName)) - return wcscmp(first->win32.displayName, second->win32.displayName) == 0; - else - return wcscmp(first->win32.adapterName, second->win32.adapterName) == 0; } void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { - DEVMODEW settings; - ZeroMemory(&settings, sizeof(DEVMODEW)); - settings.dmSize = sizeof(DEVMODEW); + DEVMODEW dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); EnumDisplaySettingsExW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, - &settings, + &dm, EDS_ROTATEDMODE); if (xpos) - *xpos = settings.dmPosition.x; + *xpos = dm.dmPosition.x; if (ypos) - *ypos = settings.dmPosition.y; + *ypos = dm.dmPosition.y; +} + +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale); +} + +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) +{ + MONITORINFO mi = { sizeof(mi) }; + GetMonitorInfo(monitor->win32.handle, &mi); + + if (xpos) + *xpos = mi.rcWork.left; + if (ypos) + *ypos = mi.rcWork.top; + if (width) + *width = mi.rcWork.right - mi.rcWork.left; + if (height) + *height = mi.rcWork.bottom - mi.rcWork.top; } GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) @@ -261,8 +391,8 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) GLFWvidmode mode; DEVMODEW dm; - ZeroMemory(&dm, sizeof(DEVMODEW)); - dm.dmSize = sizeof(DEVMODEW); + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm)) break; @@ -328,9 +458,8 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) { DEVMODEW dm; - - ZeroMemory(&dm, sizeof(DEVMODEW)); - dm.dmSize = sizeof(DEVMODEW); + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm); @@ -343,7 +472,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) &mode->blueBits); } -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { HDC dc; WORD values[768]; @@ -357,6 +486,8 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) memcpy(ramp->red, values + 0, 256 * sizeof(unsigned short)); memcpy(ramp->green, values + 256, 256 * sizeof(unsigned short)); memcpy(ramp->blue, values + 512, 256 * sizeof(unsigned short)); + + return GLFW_TRUE; } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) diff --git a/external/glfw/win32_platform.h b/external/glfw/win32_platform.h index d742f3b..07e4377 100644 --- a/external/glfw/win32_platform.h +++ b/external/glfw/win32_platform.h @@ -2,7 +2,7 @@ // GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,9 +25,6 @@ // //======================================================================== -#ifndef _glfw3_win32_platform_h_ -#define _glfw3_win32_platform_h_ - // We don't need all the fancy stuff #ifndef NOMINMAX #define NOMINMAX @@ -64,18 +61,15 @@ // GLFW uses DirectInput8 interfaces #define DIRECTINPUT_VERSION 0x0800 +// GLFW uses OEM cursor resources +#define OEMRESOURCE + #include #include -#include #include #include #include -#if defined(_MSC_VER) - #include - #define strdup _strdup -#endif - // HACK: Define macros that some windows.h variants don't #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL 0x020E @@ -104,28 +98,89 @@ #ifndef DISPLAY_DEVICE_ACTIVE #define DISPLAY_DEVICE_ACTIVE 0x00000001 #endif +#ifndef _WIN32_WINNT_WINBLUE + #define _WIN32_WINNT_WINBLUE 0x0602 +#endif +#ifndef _WIN32_WINNT_WIN8 + #define _WIN32_WINNT_WIN8 0x0602 +#endif +#ifndef WM_GETDPISCALEDSIZE + #define WM_GETDPISCALEDSIZE 0x02e4 +#endif +#ifndef USER_DEFAULT_SCREEN_DPI + #define USER_DEFAULT_SCREEN_DPI 96 +#endif +#ifndef OCR_HAND + #define OCR_HAND 32649 +#endif #if WINVER < 0x0601 -typedef struct tagCHANGEFILTERSTRUCT +typedef struct { DWORD cbSize; DWORD ExtStatus; - -} CHANGEFILTERSTRUCT, *PCHANGEFILTERSTRUCT; +} CHANGEFILTERSTRUCT; #ifndef MSGFLT_ALLOW #define MSGFLT_ALLOW 1 #endif #endif /*Windows 7*/ +#if WINVER < 0x0600 +#define DWM_BB_ENABLE 0x00000001 +#define DWM_BB_BLURREGION 0x00000002 +typedef struct +{ + DWORD dwFlags; + BOOL fEnable; + HRGN hRgnBlur; + BOOL fTransitionOnMaximized; +} DWM_BLURBEHIND; +#else + #include +#endif /*Windows Vista*/ + #ifndef DPI_ENUMS_DECLARED -typedef enum PROCESS_DPI_AWARENESS +typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; +typedef enum +{ + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; #endif /*DPI_ENUMS_DECLARED*/ +#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 +#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE) -4) +#endif /*DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2*/ + +// HACK: Define versionhelpers.h functions manually as MinGW lacks the header +#define IsWindowsXPOrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINXP), \ + LOBYTE(_WIN32_WINNT_WINXP), 0) +#define IsWindowsVistaOrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_VISTA), \ + LOBYTE(_WIN32_WINNT_VISTA), 0) +#define IsWindows7OrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WIN7), \ + LOBYTE(_WIN32_WINNT_WIN7), 0) +#define IsWindows8OrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WIN8), \ + LOBYTE(_WIN32_WINNT_WIN8), 0) +#define IsWindows8Point1OrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINBLUE), \ + LOBYTE(_WIN32_WINNT_WINBLUE), 0) + +#define _glfwIsWindows10AnniversaryUpdateOrGreaterWin32() \ + _glfwIsWindows10BuildOrGreaterWin32(14393) +#define _glfwIsWindows10CreatorsUpdateOrGreaterWin32() \ + _glfwIsWindows10BuildOrGreaterWin32(15063) + // HACK: Define macros that some xinput.h variants don't #ifndef XINPUT_CAPS_WIRELESS #define XINPUT_CAPS_WIRELESS 0x0002 @@ -161,34 +216,50 @@ typedef enum PROCESS_DPI_AWARENESS #endif // winmm.dll function pointer typedefs -typedef DWORD (WINAPI * TIMEGETTIME_T)(void); -#define _glfw_timeGetTime _glfw.win32.winmm.timeGetTime +typedef DWORD (WINAPI * PFN_timeGetTime)(void); +#define timeGetTime _glfw.win32.winmm.GetTime // xinput.dll function pointer typedefs -typedef DWORD (WINAPI * XINPUTGETCAPABILITIES_T)(DWORD,DWORD,XINPUT_CAPABILITIES*); -typedef DWORD (WINAPI * XINPUTGETSTATE_T)(DWORD,XINPUT_STATE*); -#define _glfw_XInputGetCapabilities _glfw.win32.xinput.XInputGetCapabilities -#define _glfw_XInputGetState _glfw.win32.xinput.XInputGetState +typedef DWORD (WINAPI * PFN_XInputGetCapabilities)(DWORD,DWORD,XINPUT_CAPABILITIES*); +typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*); +#define XInputGetCapabilities _glfw.win32.xinput.GetCapabilities +#define XInputGetState _glfw.win32.xinput.GetState // dinput8.dll function pointer typedefs -typedef HRESULT (WINAPI * DIRECTINPUT8CREATE_T)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN); -#define _glfw_DirectInput8Create _glfw.win32.dinput8.DirectInput8Create +typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN); +#define DirectInput8Create _glfw.win32.dinput8.Create // user32.dll function pointer typedefs -typedef BOOL (WINAPI * SETPROCESSDPIAWARE_T)(void); -typedef BOOL (WINAPI * CHANGEWINDOWMESSAGEFILTEREX_T)(HWND,UINT,DWORD,PCHANGEFILTERSTRUCT); -#define _glfw_SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware -#define _glfw_ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx +typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void); +typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,CHANGEFILTERSTRUCT*); +typedef BOOL (WINAPI * PFN_EnableNonClientDpiScaling)(HWND); +typedef BOOL (WINAPI * PFN_SetProcessDpiAwarenessContext)(HANDLE); +typedef UINT (WINAPI * PFN_GetDpiForWindow)(HWND); +typedef BOOL (WINAPI * PFN_AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT); +#define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_ +#define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_ +#define EnableNonClientDpiScaling _glfw.win32.user32.EnableNonClientDpiScaling_ +#define SetProcessDpiAwarenessContext _glfw.win32.user32.SetProcessDpiAwarenessContext_ +#define GetDpiForWindow _glfw.win32.user32.GetDpiForWindow_ +#define AdjustWindowRectExForDpi _glfw.win32.user32.AdjustWindowRectExForDpi_ // dwmapi.dll function pointer typedefs -typedef HRESULT (WINAPI * DWMISCOMPOSITIONENABLED_T)(BOOL*); -typedef HRESULT (WINAPI * DWMFLUSH_T)(VOID); -#define _glfw_DwmIsCompositionEnabled _glfw.win32.dwmapi.DwmIsCompositionEnabled -#define _glfw_DwmFlush _glfw.win32.dwmapi.DwmFlush +typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); +typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); +typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); +#define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled +#define DwmFlush _glfw.win32.dwmapi.Flush +#define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow // shcore.dll function pointer typedefs -typedef HRESULT (WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS); -#define _glfw_SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness +typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); +typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); +#define SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_ +#define GetDpiForMonitor _glfw.win32.shcore.GetDpiForMonitor_ + +// ntdll.dll function pointer typedefs +typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLONG); +#define RtlVerifyVersionInfo _glfw.win32.ntdll.RtlVerifyVersionInfo_ typedef VkFlags VkWin32SurfaceCreateFlagsKHR; @@ -207,8 +278,11 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( #include "win32_joystick.h" #include "wgl_context.h" #include "egl_context.h" +#include "osmesa_context.h" -#define _GLFW_WNDCLASSNAME L"GLFW30" +#if !defined(_GLFW_WNDCLASSNAME) + #define _GLFW_WNDCLASSNAME L"GLFW30" +#endif #define _glfw_dlopen(name) LoadLibraryA(name) #define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) @@ -219,10 +293,11 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32 #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32 -#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimeWin32 win32_time -#define _GLFW_PLATFORM_LIBRARY_TLS_STATE _GLFWtlsWin32 win32_tls +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerWin32 win32 #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWin32 win32 #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWin32 win32 +#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsWin32 win32 +#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexWin32 win32 // Win32-specific per-window data @@ -234,7 +309,12 @@ typedef struct _GLFWwindowWin32 HICON smallIcon; GLFWbool cursorTracked; + GLFWbool frameAction; GLFWbool iconified; + GLFWbool maximized; + // Whether to enable framebuffer transparency on DWM + GLFWbool transparent; + GLFWbool scaleToMonitor; // The last received cursor position, regardless of source int lastCursorPosX, lastCursorPosY; @@ -246,61 +326,78 @@ typedef struct _GLFWwindowWin32 typedef struct _GLFWlibraryWin32 { HWND helperWindowHandle; + HDEVNOTIFY deviceNotificationHandle; DWORD foregroundLockTimeout; + int acquiredMonitorCount; char* clipboardString; - char keyName[64]; - short int publicKeys[512]; - short int nativeKeys[GLFW_KEY_LAST + 1]; + short int keycodes[512]; + short int scancodes[GLFW_KEY_LAST + 1]; + char keynames[GLFW_KEY_LAST + 1][5]; // Where to place the cursor when re-enabled double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active _GLFWwindow* disabledCursorWindow; + RAWINPUT* rawInput; + int rawInputSize; + UINT mouseTrailSize; struct { - HINSTANCE instance; - TIMEGETTIME_T timeGetTime; + HINSTANCE instance; + PFN_timeGetTime GetTime; } winmm; struct { - HINSTANCE instance; - DIRECTINPUT8CREATE_T DirectInput8Create; - IDirectInput8W* api; + HINSTANCE instance; + PFN_DirectInput8Create Create; + IDirectInput8W* api; } dinput8; struct { - HINSTANCE instance; - XINPUTGETCAPABILITIES_T XInputGetCapabilities; - XINPUTGETSTATE_T XInputGetState; + HINSTANCE instance; + PFN_XInputGetCapabilities GetCapabilities; + PFN_XInputGetState GetState; } xinput; struct { - HINSTANCE instance; - SETPROCESSDPIAWARE_T SetProcessDPIAware; - CHANGEWINDOWMESSAGEFILTEREX_T ChangeWindowMessageFilterEx; + HINSTANCE instance; + PFN_SetProcessDPIAware SetProcessDPIAware_; + PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx_; + PFN_EnableNonClientDpiScaling EnableNonClientDpiScaling_; + PFN_SetProcessDpiAwarenessContext SetProcessDpiAwarenessContext_; + PFN_GetDpiForWindow GetDpiForWindow_; + PFN_AdjustWindowRectExForDpi AdjustWindowRectExForDpi_; } user32; struct { - HINSTANCE instance; - DWMISCOMPOSITIONENABLED_T DwmIsCompositionEnabled; - DWMFLUSH_T DwmFlush; + HINSTANCE instance; + PFN_DwmIsCompositionEnabled IsCompositionEnabled; + PFN_DwmFlush Flush; + PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; } dwmapi; struct { - HINSTANCE instance; - SETPROCESSDPIAWARENESS_T SetProcessDpiAwareness; + HINSTANCE instance; + PFN_SetProcessDpiAwareness SetProcessDpiAwareness_; + PFN_GetDpiForMonitor GetDpiForMonitor_; } shcore; + struct { + HINSTANCE instance; + PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_; + } ntdll; + } _GLFWlibraryWin32; // Win32-specific per-monitor data // typedef struct _GLFWmonitorWin32 { + HMONITOR handle; // This size matches the static size of DISPLAY_DEVICE.DeviceName WCHAR adapterName[32]; WCHAR displayName[32]; - char publicAdapterName[64]; - char publicDisplayName[64]; + char publicAdapterName[32]; + char publicDisplayName[32]; GLFWbool modesPruned; GLFWbool modeChanged; @@ -310,41 +407,52 @@ typedef struct _GLFWmonitorWin32 // typedef struct _GLFWcursorWin32 { - HCURSOR handle; + HCURSOR handle; } _GLFWcursorWin32; // Win32-specific global timer data // -typedef struct _GLFWtimeWin32 +typedef struct _GLFWtimerWin32 { GLFWbool hasPC; uint64_t frequency; -} _GLFWtimeWin32; +} _GLFWtimerWin32; -// Win32-specific global TLS data +// Win32-specific thread local storage data // typedef struct _GLFWtlsWin32 { - GLFWbool allocated; - DWORD context; + GLFWbool allocated; + DWORD index; } _GLFWtlsWin32; +// Win32-specific mutex data +// +typedef struct _GLFWmutexWin32 +{ + GLFWbool allocated; + CRITICAL_SECTION section; + +} _GLFWmutexWin32; + GLFWbool _glfwRegisterWindowClassWin32(void); void _glfwUnregisterWindowClassWin32(void); -GLFWbool _glfwInitThreadLocalStorageWin32(void); -void _glfwTerminateThreadLocalStorageWin32(void); - WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); +BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp); +BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build); +void _glfwInputErrorWin32(int error, const char* description); +void _glfwUpdateKeyNamesWin32(void); void _glfwInitTimerWin32(void); -GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); +void _glfwPollMonitorsWin32(void); +void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor); +void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale); -#endif // _glfw3_win32_platform_h_ diff --git a/external/glfw/win32_thread.c b/external/glfw/win32_thread.c new file mode 100644 index 0000000..9391fc9 --- /dev/null +++ b/external/glfw/win32_thread.c @@ -0,0 +1,97 @@ +//======================================================================== +// GLFW 3.3 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) +{ + assert(tls->win32.allocated == GLFW_FALSE); + + tls->win32.index = TlsAlloc(); + if (tls->win32.index == TLS_OUT_OF_INDEXES) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to allocate TLS index"); + return GLFW_FALSE; + } + + tls->win32.allocated = GLFW_TRUE; + return GLFW_TRUE; +} + +void _glfwPlatformDestroyTls(_GLFWtls* tls) +{ + if (tls->win32.allocated) + TlsFree(tls->win32.index); + memset(tls, 0, sizeof(_GLFWtls)); +} + +void* _glfwPlatformGetTls(_GLFWtls* tls) +{ + assert(tls->win32.allocated == GLFW_TRUE); + return TlsGetValue(tls->win32.index); +} + +void _glfwPlatformSetTls(_GLFWtls* tls, void* value) +{ + assert(tls->win32.allocated == GLFW_TRUE); + TlsSetValue(tls->win32.index, value); +} + +GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex) +{ + assert(mutex->win32.allocated == GLFW_FALSE); + InitializeCriticalSection(&mutex->win32.section); + return mutex->win32.allocated = GLFW_TRUE; +} + +void _glfwPlatformDestroyMutex(_GLFWmutex* mutex) +{ + if (mutex->win32.allocated) + DeleteCriticalSection(&mutex->win32.section); + memset(mutex, 0, sizeof(_GLFWmutex)); +} + +void _glfwPlatformLockMutex(_GLFWmutex* mutex) +{ + assert(mutex->win32.allocated == GLFW_TRUE); + EnterCriticalSection(&mutex->win32.section); +} + +void _glfwPlatformUnlockMutex(_GLFWmutex* mutex) +{ + assert(mutex->win32.allocated == GLFW_TRUE); + LeaveCriticalSection(&mutex->win32.section); +} + diff --git a/external/glfw/win32_time.c b/external/glfw/win32_time.c index add55c5..29670f9 100644 --- a/external/glfw/win32_time.c +++ b/external/glfw/win32_time.c @@ -2,7 +2,7 @@ // GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -40,13 +40,13 @@ void _glfwInitTimerWin32(void) if (QueryPerformanceFrequency((LARGE_INTEGER*) &frequency)) { - _glfw.win32_time.hasPC = GLFW_TRUE; - _glfw.win32_time.frequency = frequency; + _glfw.timer.win32.hasPC = GLFW_TRUE; + _glfw.timer.win32.frequency = frequency; } else { - _glfw.win32_time.hasPC = GLFW_FALSE; - _glfw.win32_time.frequency = 1000; + _glfw.timer.win32.hasPC = GLFW_FALSE; + _glfw.timer.win32.frequency = 1000; } } @@ -57,18 +57,18 @@ void _glfwInitTimerWin32(void) uint64_t _glfwPlatformGetTimerValue(void) { - if (_glfw.win32_time.hasPC) + if (_glfw.timer.win32.hasPC) { uint64_t value; QueryPerformanceCounter((LARGE_INTEGER*) &value); return value; } else - return (uint64_t) _glfw_timeGetTime(); + return (uint64_t) timeGetTime(); } uint64_t _glfwPlatformGetTimerFrequency(void) { - return _glfw.win32_time.frequency; + return _glfw.timer.win32.frequency; } diff --git a/external/glfw/win32_window.c b/external/glfw/win32_window.c index 64c3d71..48b3dd5 100644 --- a/external/glfw/win32_window.c +++ b/external/glfw/win32_window.c @@ -2,7 +2,7 @@ // GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -46,9 +46,11 @@ static DWORD getWindowStyle(const _GLFWwindow* window) style |= WS_POPUP; else { + style |= WS_SYSMENU | WS_MINIMIZEBOX; + if (window->decorated) { - style |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + style |= WS_CAPTION; if (window->resizable) style |= WS_MAXIMIZEBOX | WS_THICKFRAME; @@ -109,7 +111,7 @@ static HICON createIcon(const GLFWimage* image, unsigned char* source = image->pixels; ZeroMemory(&bi, sizeof(bi)); - bi.bV5Size = sizeof(BITMAPV5HEADER); + bi.bV5Size = sizeof(bi); bi.bV5Width = image->width; bi.bV5Height = -image->height; bi.bV5Planes = 1; @@ -131,16 +133,16 @@ static HICON createIcon(const GLFWimage* image, if (!color) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to create RGBA bitmap"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create RGBA bitmap"); return NULL; } mask = CreateBitmap(image->width, image->height, 1, 1, NULL); if (!mask) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to create mask bitmap"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create mask bitmap"); DeleteObject(color); return NULL; } @@ -170,35 +172,51 @@ static HICON createIcon(const GLFWimage* image, if (!handle) { if (icon) - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create icon"); + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create icon"); + } else - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create cursor"); + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create cursor"); + } } return handle; } -// Translate client window size to full window size according to styles +// Translate content area size to full window size according to styles and DPI // static void getFullWindowSize(DWORD style, DWORD exStyle, - int clientWidth, int clientHeight, - int* fullWidth, int* fullHeight) + int contentWidth, int contentHeight, + int* fullWidth, int* fullHeight, + UINT dpi) { - RECT rect = { 0, 0, clientWidth, clientHeight }; - AdjustWindowRectEx(&rect, style, FALSE, exStyle); + RECT rect = { 0, 0, contentWidth, contentHeight }; + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); + else + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + *fullWidth = rect.right - rect.left; *fullHeight = rect.bottom - rect.top; } -// Enforce the client rect aspect ratio based on which edge is being dragged +// Enforce the content area aspect ratio based on which edge is being dragged // static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) { int xoff, yoff; + UINT dpi = USER_DEFAULT_SCREEN_DPI; const float ratio = (float) window->numer / (float) window->denom; + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + dpi = GetDpiForWindow(window->win32.handle); + getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), - 0, 0, &xoff, &yoff); + 0, 0, &xoff, &yoff, dpi); if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT || edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT) @@ -218,35 +236,6 @@ static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) } } -// Centers the cursor over the window client area -// -static void centerCursor(_GLFWwindow* window) -{ - int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); - _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); -} - -// Returns whether the cursor is in the client area of the specified window -// -static GLFWbool cursorInClientArea(_GLFWwindow* window) -{ - RECT area; - POINT pos; - - if (!GetCursorPos(&pos)) - return GLFW_FALSE; - - if (WindowFromPoint(pos) != window->win32.handle) - return GLFW_FALSE; - - GetClientRect(window->win32.handle, &area); - ClientToScreen(window->win32.handle, (POINT*) &area.left); - ClientToScreen(window->win32.handle, (POINT*) &area.right); - - return PtInRect(&area, pos); -} - // Updates the cursor image according to its cursor mode // static void updateCursorImage(_GLFWwindow* window) @@ -278,27 +267,161 @@ static void updateClipRect(_GLFWwindow* window) ClipCursor(NULL); } -// Translates a GLFW standard cursor to a resource ID +// Enables WM_INPUT messages for the mouse for the specified window // -static LPWSTR translateCursorShape(int shape) +static void enableRawMouseMotion(_GLFWwindow* window) { - switch (shape) - { - case GLFW_ARROW_CURSOR: - return IDC_ARROW; - case GLFW_IBEAM_CURSOR: - return IDC_IBEAM; - case GLFW_CROSSHAIR_CURSOR: - return IDC_CROSS; - case GLFW_HAND_CURSOR: - return IDC_HAND; - case GLFW_HRESIZE_CURSOR: - return IDC_SIZEWE; - case GLFW_VRESIZE_CURSOR: - return IDC_SIZENS; - } + const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle }; - return NULL; + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to register raw input device"); + } +} + +// Disables WM_INPUT messages for the mouse +// +static void disableRawMouseMotion(_GLFWwindow* window) +{ + const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; + + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to remove raw input device"); + } +} + +// Apply disabled cursor mode to a focused window +// +static void disableCursor(_GLFWwindow* window) +{ + _glfw.win32.disabledCursorWindow = window; + _glfwPlatformGetCursorPos(window, + &_glfw.win32.restoreCursorPosX, + &_glfw.win32.restoreCursorPosY); + updateCursorImage(window); + _glfwCenterCursorInContentArea(window); + updateClipRect(window); + + if (window->rawMouseMotion) + enableRawMouseMotion(window); +} + +// Exit disabled cursor mode for the specified window +// +static void enableCursor(_GLFWwindow* window) +{ + if (window->rawMouseMotion) + disableRawMouseMotion(window); + + _glfw.win32.disabledCursorWindow = NULL; + updateClipRect(NULL); + _glfwPlatformSetCursorPos(window, + _glfw.win32.restoreCursorPosX, + _glfw.win32.restoreCursorPosY); + updateCursorImage(window); +} + +// Returns whether the cursor is in the content area of the specified window +// +static GLFWbool cursorInContentArea(_GLFWwindow* window) +{ + RECT area; + POINT pos; + + if (!GetCursorPos(&pos)) + return GLFW_FALSE; + + if (WindowFromPoint(pos) != window->win32.handle) + return GLFW_FALSE; + + GetClientRect(window->win32.handle, &area); + ClientToScreen(window->win32.handle, (POINT*) &area.left); + ClientToScreen(window->win32.handle, (POINT*) &area.right); + + return PtInRect(&area, pos); +} + +// Update native window styles to match attributes +// +static void updateWindowStyles(const _GLFWwindow* window) +{ + RECT rect; + DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); + style &= ~(WS_OVERLAPPEDWINDOW | WS_POPUP); + style |= getWindowStyle(window); + + GetClientRect(window->win32.handle, &rect); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, style, FALSE, + getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window)); + + ClientToScreen(window->win32.handle, (POINT*) &rect.left); + ClientToScreen(window->win32.handle, (POINT*) &rect.right); + SetWindowLongW(window->win32.handle, GWL_STYLE, style); + SetWindowPos(window->win32.handle, HWND_TOP, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER); +} + +// Update window framebuffer transparency +// +static void updateFramebufferTransparency(const _GLFWwindow* window) +{ + BOOL enabled; + + if (!IsWindowsVistaOrGreater()) + return; + + if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled) + { + HRGN region = CreateRectRgn(0, 0, -1, -1); + DWM_BLURBEHIND bb = {0}; + bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + bb.hRgnBlur = region; + bb.fEnable = TRUE; + + if (SUCCEEDED(DwmEnableBlurBehindWindow(window->win32.handle, &bb))) + { + // Decorated windows don't repaint the transparent background + // leaving a trail behind animations + // HACK: Making the window layered with a transparency color key + // seems to fix this. Normally, when specifying + // a transparency color key to be used when composing the + // layered window, all pixels painted by the window in this + // color will be transparent. That doesn't seem to be the + // case anymore, at least when used with blur behind window + // plus negative region. + LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + exStyle |= WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); + + // Using a color key not equal to black to fix the trailing + // issue. When set to black, something is making the hit test + // not resize with the window frame. + SetLayeredWindowAttributes(window->win32.handle, + RGB(0, 193, 48), 255, LWA_COLORKEY); + } + + DeleteObject(region); + } + else + { + LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + exStyle &= ~WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); + RedrawWindow(window->win32.handle, NULL, NULL, + RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); + } } // Retrieves and translates modifier keys @@ -307,14 +430,18 @@ static int getKeyMods(void) { int mods = 0; - if (GetKeyState(VK_SHIFT) & (1 << 31)) + if (GetKeyState(VK_SHIFT) & 0x8000) mods |= GLFW_MOD_SHIFT; - if (GetKeyState(VK_CONTROL) & (1 << 31)) + if (GetKeyState(VK_CONTROL) & 0x8000) mods |= GLFW_MOD_CONTROL; - if (GetKeyState(VK_MENU) & (1 << 31)) + if (GetKeyState(VK_MENU) & 0x8000) mods |= GLFW_MOD_ALT; - if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1 << 31)) + if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000) mods |= GLFW_MOD_SUPER; + if (GetKeyState(VK_CAPITAL) & 1) + mods |= GLFW_MOD_CAPS_LOCK; + if (GetKeyState(VK_NUMLOCK) & 1) + mods |= GLFW_MOD_NUM_LOCK; return mods; } @@ -325,14 +452,18 @@ static int getAsyncKeyMods(void) { int mods = 0; - if (GetAsyncKeyState(VK_SHIFT) & (1 << 31)) + if (GetAsyncKeyState(VK_SHIFT) & 0x8000) mods |= GLFW_MOD_SHIFT; - if (GetAsyncKeyState(VK_CONTROL) & (1 << 31)) + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) mods |= GLFW_MOD_CONTROL; - if (GetAsyncKeyState(VK_MENU) & (1 << 31)) + if (GetAsyncKeyState(VK_MENU) & 0x8000) mods |= GLFW_MOD_ALT; - if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & (1 << 31)) + if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & 0x8000) mods |= GLFW_MOD_SUPER; + if (GetAsyncKeyState(VK_CAPITAL) & 1) + mods |= GLFW_MOD_CAPS_LOCK; + if (GetAsyncKeyState(VK_NUMLOCK) & 1) + mods |= GLFW_MOD_NUM_LOCK; return mods; } @@ -341,20 +472,19 @@ static int getAsyncKeyMods(void) // static int translateKey(WPARAM wParam, LPARAM lParam) { + // The Ctrl keys require special handling if (wParam == VK_CONTROL) { - // The CTRL keys require special handling - MSG next; DWORD time; - // Is this an extended key (i.e. right key)? + // Right side keys have the extended key bit set if (lParam & 0x01000000) return GLFW_KEY_RIGHT_CONTROL; - // Here is a trick: "Alt Gr" sends LCTRL, then RALT. We only - // want the RALT message, so we try to see if the next message - // is a RALT message. In that case, this is a false LCTRL! + // HACK: Alt Gr sends Left Ctrl and then Right Alt in close sequence + // We only want the Right Alt message, so if the next message is + // Right Alt we ignore this (synthetic) Left Ctrl message time = GetMessageTime(); if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE)) @@ -368,8 +498,7 @@ static int translateKey(WPARAM wParam, LPARAM lParam) (next.lParam & 0x01000000) && next.time == time) { - // Next message is a RALT down message, which - // means that this is not a proper LCTRL message + // Next message is Right Alt down so discard this return _GLFW_KEY_INVALID; } } @@ -385,28 +514,43 @@ static int translateKey(WPARAM wParam, LPARAM lParam) return _GLFW_KEY_INVALID; } - return _glfw.win32.publicKeys[HIWORD(lParam) & 0x1FF]; + return _glfw.win32.keycodes[HIWORD(lParam) & 0x1FF]; +} + +static void fitToMonitor(_GLFWwindow* window) +{ + MONITORINFO mi = { sizeof(mi) }; + GetMonitorInfo(window->monitor->win32.handle, &mi); + SetWindowPos(window->win32.handle, HWND_TOPMOST, + mi.rcMonitor.left, + mi.rcMonitor.top, + mi.rcMonitor.right - mi.rcMonitor.left, + mi.rcMonitor.bottom - mi.rcMonitor.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS); } // Make the specified window and its video mode active on its monitor // -static GLFWbool acquireMonitor(_GLFWwindow* window) +static void acquireMonitor(_GLFWwindow* window) { - GLFWvidmode mode; - GLFWbool status; - int xpos, ypos; + if (!_glfw.win32.acquiredMonitorCount) + { + SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); - status = _glfwSetVideoModeWin32(window->monitor, &window->videoMode); + // HACK: When mouse trails are enabled the cursor becomes invisible when + // the OpenGL ICD switches to page flipping + if (IsWindowsXPOrGreater()) + { + SystemParametersInfo(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0); + SystemParametersInfo(SPI_SETMOUSETRAILS, 0, 0, 0); + } + } - _glfwPlatformGetVideoMode(window->monitor, &mode); - _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); + if (!window->monitor->window) + _glfw.win32.acquiredMonitorCount++; - SetWindowPos(window->win32.handle, HWND_TOPMOST, - xpos, ypos, mode.width, mode.height, - SWP_NOACTIVATE | SWP_NOCOPYBITS); - - _glfwInputMonitorWindowChange(window->monitor, window); - return status; + _glfwSetVideoModeWin32(window->monitor, &window->videoMode); + _glfwInputMonitorWindow(window->monitor, window); } // Remove the window and restore the original video mode @@ -416,7 +560,17 @@ static void releaseMonitor(_GLFWwindow* window) if (window->monitor->window != window) return; - _glfwInputMonitorWindowChange(window->monitor, NULL); + _glfw.win32.acquiredMonitorCount--; + if (!_glfw.win32.acquiredMonitorCount) + { + SetThreadExecutionState(ES_CONTINUOUS); + + // HACK: Restore mouse trail length saved in acquireMonitor + if (IsWindowsXPOrGreater()) + SystemParametersInfo(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0); + } + + _glfwInputMonitorWindow(window->monitor, NULL); _glfwRestoreVideoModeWin32(window->monitor); } @@ -429,33 +583,35 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, if (!window) { // This is the message handling for the hidden helper window + // and for a regular window during its initial creation switch (uMsg) { + case WM_NCCREATE: + { + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + EnableNonClientDpiScaling(hWnd); + + break; + } + + case WM_DISPLAYCHANGE: + _glfwPollMonitorsWin32(); + break; + case WM_DEVICECHANGE: { - if (wParam == DBT_DEVNODES_CHANGED) - { - _glfwInputMonitorChange(); - return TRUE; - } - else if (wParam == DBT_DEVICEARRIVAL) + if (wParam == DBT_DEVICEARRIVAL) { DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; - if (dbh) - { - if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) - _glfwDetectJoystickConnectionWin32(); - } + if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickConnectionWin32(); } else if (wParam == DBT_DEVICEREMOVECOMPLETE) { DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; - if (dbh) - { - if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) - _glfwDetectJoystickDisconnectionWin32(); - } + if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickDisconnectionWin32(); } break; @@ -467,12 +623,49 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, switch (uMsg) { + case WM_MOUSEACTIVATE: + { + // HACK: Postpone cursor disabling when the window was activated by + // clicking a caption button + if (HIWORD(lParam) == WM_LBUTTONDOWN) + { + if (LOWORD(lParam) == HTCLOSE || + LOWORD(lParam) == HTMINBUTTON || + LOWORD(lParam) == HTMAXBUTTON) + { + window->win32.frameAction = GLFW_TRUE; + } + } + + break; + } + + case WM_CAPTURECHANGED: + { + // HACK: Disable the cursor once the caption button action has been + // completed or cancelled + if (lParam == 0 && window->win32.frameAction) + { + if (window->cursorMode == GLFW_CURSOR_DISABLED) + disableCursor(window); + + window->win32.frameAction = GLFW_FALSE; + } + + break; + } + case WM_SETFOCUS: { _glfwInputWindowFocus(window, GLFW_TRUE); + // HACK: Do not disable cursor while the user is interacting with + // a caption button + if (window->win32.frameAction) + break; + if (window->cursorMode == GLFW_CURSOR_DISABLED) - _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); + disableCursor(window); return 0; } @@ -480,7 +673,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_KILLFOCUS: { if (window->cursorMode == GLFW_CURSOR_DISABLED) - _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL); + enableCursor(window); if (window->monitor && window->autoIconify) _glfwPlatformIconifyWindow(window); @@ -519,6 +712,12 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return 0; } + case WM_INPUTLANGCHANGE: + { + _glfwUpdateKeyNamesWin32(); + break; + } + case WM_CHAR: case WM_SYSCHAR: case WM_UNICHAR: @@ -552,14 +751,15 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, if (action == GLFW_RELEASE && wParam == VK_SHIFT) { - // Release both Shift keys on Shift up event, as only one event - // is sent even if both keys are released + // HACK: Release both Shift keys on Shift up event, as when both + // are pressed the first release does not emit any event + // NOTE: The other half of this is in _glfwPlatformPollEvents _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods); _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods); } else if (wParam == VK_SNAPSHOT) { - // Key down is not reported for the Print Screen key + // HACK: Key down is not reported for the Print Screen key _glfwInputKey(window, key, scancode, GLFW_PRESS, mods); _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods); } @@ -578,7 +778,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_MBUTTONUP: case WM_XBUTTONUP: { - int button, action; + int i, button, action; if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) button = GLFW_MOUSE_BUTTON_LEFT; @@ -595,16 +795,30 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN) { action = GLFW_PRESS; - SetCapture(hWnd); } else - { action = GLFW_RELEASE; - ReleaseCapture(); + + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == GLFW_PRESS) + break; } + if (i > GLFW_MOUSE_BUTTON_LAST) + SetCapture(hWnd); + _glfwInputMouseClick(window, button, action, getKeyMods()); + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == GLFW_PRESS) + break; + } + + if (i > GLFW_MOUSE_BUTTON_LAST) + ReleaseCapture(); + if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP) return TRUE; @@ -616,6 +830,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, const int x = GET_X_LPARAM(lParam); const int y = GET_Y_LPARAM(lParam); + // Disabled cursor motion input is provided by WM_INPUT if (window->cursorMode == GLFW_CURSOR_DISABLED) { const int dx = x - window->win32.lastCursorPosX; @@ -623,6 +838,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, if (_glfw.win32.disabledCursorWindow != window) break; + if (window->rawMouseMotion) + break; _glfwInputCursorPos(window, window->virtualCursorPosX + dx, @@ -650,6 +867,57 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return 0; } + case WM_INPUT: + { + UINT size = 0; + HRAWINPUT ri = (HRAWINPUT) lParam; + RAWINPUT* data = NULL; + int dx, dy; + + if (_glfw.win32.disabledCursorWindow != window) + break; + if (!window->rawMouseMotion) + break; + + GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); + if (size > (UINT) _glfw.win32.rawInputSize) + { + free(_glfw.win32.rawInput); + _glfw.win32.rawInput = calloc(size, 1); + _glfw.win32.rawInputSize = size; + } + + size = _glfw.win32.rawInputSize; + if (GetRawInputData(ri, RID_INPUT, + _glfw.win32.rawInput, &size, + sizeof(RAWINPUTHEADER)) == (UINT) -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to retrieve raw input data"); + break; + } + + data = _glfw.win32.rawInput; + if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) + { + dx = data->data.mouse.lLastX - window->win32.lastCursorPosX; + dy = data->data.mouse.lLastY - window->win32.lastCursorPosY; + } + else + { + dx = data->data.mouse.lLastX; + dy = data->data.mouse.lLastY; + } + + _glfwInputCursorPos(window, + window->virtualCursorPosX + dx, + window->virtualCursorPosY + dy); + + window->win32.lastCursorPosX += dx; + window->win32.lastCursorPosY += dy; + break; + } + case WM_MOUSELEAVE: { window->win32.cursorTracked = GLFW_FALSE; @@ -666,7 +934,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_MOUSEHWHEEL: { // This message is only sent on Windows Vista and later - // NOTE: The X-axis is inverted for consistency with OS X and X11. + // NOTE: The X-axis is inverted for consistency with macOS and X11 _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0); return 0; } @@ -674,8 +942,10 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_ENTERSIZEMOVE: case WM_ENTERMENULOOP: { + // HACK: Enable the cursor while the user is moving or + // resizing the window or using the window menu if (window->cursorMode == GLFW_CURSOR_DISABLED) - _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL); + enableCursor(window); break; } @@ -683,44 +953,46 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_EXITSIZEMOVE: case WM_EXITMENULOOP: { + // HACK: Disable the cursor once the user is done moving or + // resizing the window or using the menu if (window->cursorMode == GLFW_CURSOR_DISABLED) - _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); + disableCursor(window); break; } case WM_SIZE: { - const GLFWbool iconified = - !window->win32.iconified && wParam == SIZE_MINIMIZED; - const GLFWbool restored = - window->win32.iconified && - (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED); + const GLFWbool iconified = wParam == SIZE_MINIMIZED; + const GLFWbool maximized = wParam == SIZE_MAXIMIZED || + (window->win32.maximized && + wParam != SIZE_RESTORED); if (_glfw.win32.disabledCursorWindow == window) updateClipRect(window); - if (iconified) - _glfwInputWindowIconify(window, GLFW_TRUE); - else if (restored) - _glfwInputWindowIconify(window, GLFW_FALSE); + if (window->win32.iconified != iconified) + _glfwInputWindowIconify(window, iconified); + + if (window->win32.maximized != maximized) + _glfwInputWindowMaximize(window, maximized); _glfwInputFramebufferSize(window, LOWORD(lParam), HIWORD(lParam)); _glfwInputWindowSize(window, LOWORD(lParam), HIWORD(lParam)); - if (iconified) + if (window->monitor && window->win32.iconified != iconified) { - window->win32.iconified = GLFW_TRUE; - if (window->monitor) + if (iconified) releaseMonitor(window); - } - else if (restored) - { - window->win32.iconified = GLFW_FALSE; - if (window->monitor) + else + { acquireMonitor(window); + fitToMonitor(window); + } } + window->win32.iconified = iconified; + window->win32.maximized = maximized; return 0; } @@ -752,13 +1024,17 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_GETMINMAXINFO: { int xoff, yoff; + UINT dpi = USER_DEFAULT_SCREEN_DPI; MINMAXINFO* mmi = (MINMAXINFO*) lParam; if (window->monitor) break; + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + dpi = GetDpiForWindow(window->win32.handle); + getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), - 0, 0, &xoff, &yoff); + 0, 0, &xoff, &yoff, dpi); if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE) @@ -774,6 +1050,22 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, mmi->ptMaxTrackSize.y = window->maxheight + yoff; } + if (!window->decorated) + { + MONITORINFO mi; + const HMONITOR mh = MonitorFromWindow(window->win32.handle, + MONITOR_DEFAULTTONEAREST); + + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + GetMonitorInfo(mh, &mi); + + mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left; + mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top; + mmi->ptMaxSize.x = mi.rcWork.right - mi.rcWork.left; + mmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top; + } + return 0; } @@ -788,11 +1080,46 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return TRUE; } - case WM_SETCURSOR: + case WM_NCACTIVATE: + case WM_NCPAINT: { - if (LOWORD(lParam) == HTCLIENT) + // Prevent title bar from being drawn after restoring a minimized + // undecorated window + if (!window->decorated) + return TRUE; + + break; + } + + case WM_DWMCOMPOSITIONCHANGED: + { + if (window->win32.transparent) + updateFramebufferTransparency(window); + return 0; + } + + case WM_GETDPISCALEDSIZE: + { + if (window->win32.scaleToMonitor) + break; + + // Adjust the window size to keep the content area size constant + if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) { - updateCursorImage(window); + RECT source = {0}, target = {0}; + SIZE* size = (SIZE*) lParam; + + AdjustWindowRectExForDpi(&source, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + AdjustWindowRectExForDpi(&target, getWindowStyle(window), + FALSE, getWindowExStyle(window), + LOWORD(wParam)); + + size->cx += (target.right - target.left) - + (source.right - source.left); + size->cy += (target.bottom - target.top) - + (source.bottom - source.top); return TRUE; } @@ -801,14 +1128,34 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_DPICHANGED: { - RECT* rect = (RECT*) lParam; - SetWindowPos(window->win32.handle, - HWND_TOP, - rect->left, - rect->top, - rect->right - rect->left, - rect->bottom - rect->top, - SWP_NOACTIVATE | SWP_NOZORDER); + const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; + const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; + + // Only apply the suggested size if the OS is new enough to have + // sent a WM_GETDPISCALEDSIZE before this + if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + { + RECT* suggested = (RECT*) lParam; + SetWindowPos(window->win32.handle, HWND_TOP, + suggested->left, + suggested->top, + suggested->right - suggested->left, + suggested->bottom - suggested->top, + SWP_NOACTIVATE | SWP_NOZORDER); + } + + _glfwInputWindowContentScale(window, xscale, yscale); + break; + } + + case WM_SETCURSOR: + { + if (LOWORD(lParam) == HTCLIENT) + { + updateCursorImage(window); + return TRUE; + } + break; } @@ -828,7 +1175,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, for (i = 0; i < count; i++) { const UINT length = DragQueryFileW(drop, i, NULL, 0); - WCHAR* buffer = calloc(length + 1, sizeof(WCHAR)); + WCHAR* buffer = calloc((size_t) length + 1, sizeof(WCHAR)); DragQueryFileW(drop, i, buffer, length + 1); paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer); @@ -853,7 +1200,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, // Creates the GLFW window // static int createNativeWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig) + const _GLFWwndconfig* wndconfig, + const _GLFWfbconfig* fbconfig) { int xpos, ypos, fullWidth, fullHeight; WCHAR* wideTitle; @@ -866,7 +1214,7 @@ static int createNativeWindow(_GLFWwindow* window, // NOTE: This window placement is temporary and approximate, as the // correct position and size cannot be known until the monitor - // video mode has been set + // video mode has been picked in _glfwSetVideoModeWin32 _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); _glfwPlatformGetVideoMode(window->monitor, &mode); fullWidth = mode.width; @@ -882,16 +1230,13 @@ static int createNativeWindow(_GLFWwindow* window, getFullWindowSize(style, exStyle, wndconfig->width, wndconfig->height, - &fullWidth, &fullHeight); + &fullWidth, &fullHeight, + USER_DEFAULT_SCREEN_DPI); } wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title); if (!wideTitle) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert window title to UTF-16"); return GLFW_FALSE; - } window->win32.handle = CreateWindowExW(exStyle, _GLFW_WNDCLASSNAME, @@ -908,24 +1253,65 @@ static int createNativeWindow(_GLFWwindow* window, if (!window->win32.handle) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create window"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create window"); return GLFW_FALSE; } SetPropW(window->win32.handle, L"GLFW", window); - if (_glfw_ChangeWindowMessageFilterEx) + if (IsWindows7OrGreater()) { - _glfw_ChangeWindowMessageFilterEx(window->win32.handle, - WM_DROPFILES, MSGFLT_ALLOW, NULL); - _glfw_ChangeWindowMessageFilterEx(window->win32.handle, - WM_COPYDATA, MSGFLT_ALLOW, NULL); - _glfw_ChangeWindowMessageFilterEx(window->win32.handle, - WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); + ChangeWindowMessageFilterEx(window->win32.handle, + WM_DROPFILES, MSGFLT_ALLOW, NULL); + ChangeWindowMessageFilterEx(window->win32.handle, + WM_COPYDATA, MSGFLT_ALLOW, NULL); + ChangeWindowMessageFilterEx(window->win32.handle, + WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); + } + + window->win32.scaleToMonitor = wndconfig->scaleToMonitor; + + // Adjust window size to account for DPI scaling of the window frame and + // optionally DPI scaling of the content area + // This cannot be done until we know what monitor it was placed on + if (!window->monitor) + { + RECT rect = { 0, 0, wndconfig->width, wndconfig->height }; + + if (wndconfig->scaleToMonitor) + { + float xscale, yscale; + _glfwPlatformGetWindowContentScale(window, &xscale, &yscale); + rect.right = (int) (rect.right * xscale); + rect.bottom = (int) (rect.bottom * yscale); + } + + ClientToScreen(window->win32.handle, (POINT*) &rect.left); + ClientToScreen(window->win32.handle, (POINT*) &rect.right); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, + GetDpiForWindow(window->win32.handle)); + } + else + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + + SetWindowPos(window->win32.handle, NULL, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOZORDER); } DragAcceptFiles(window->win32.handle, TRUE); + if (fbconfig->transparent) + { + updateFramebufferTransparency(window); + window->win32.transparent = GLFW_TRUE; + } + return GLFW_TRUE; } @@ -962,8 +1348,8 @@ GLFWbool _glfwRegisterWindowClassWin32(void) if (!RegisterClassExW(&wc)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to register window class"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to register window class"); return GLFW_FALSE; } @@ -987,7 +1373,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { - if (!createNativeWindow(window, wndconfig)) + if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) @@ -999,23 +1385,28 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwInitEGL()) return GLFW_FALSE; if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } if (window->monitor) { _glfwPlatformShowWindow(window); _glfwPlatformFocusWindow(window); - if (!acquireMonitor(window)) - return GLFW_FALSE; - - centerCursor(window); + acquireMonitor(window); + fitToMonitor(window); } return GLFW_TRUE; @@ -1050,11 +1441,7 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title); if (!wideTitle) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert window title to UTF-16"); return; - } SetWindowTextW(window->win32.handle, wideTitle); free(wideTitle); @@ -1113,8 +1500,19 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) { RECT rect = { xpos, ypos, xpos, ypos }; - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE); } @@ -1135,13 +1533,27 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) if (window->monitor) { if (window->monitor->window == window) + { acquireMonitor(window); + fitToMonitor(window); + } } else { RECT rect = { 0, 0, width, height }; - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + SetWindowPos(window->win32.handle, HWND_TOP, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER); @@ -1196,8 +1608,18 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, _glfwPlatformGetWindowSize(window, &width, &height); SetRect(&rect, 0, 0, width, height); - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } if (left) *left = -rect.left; @@ -1209,6 +1631,14 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, *bottom = rect.bottom - height; } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + const HANDLE handle = MonitorFromWindow(window->win32.handle, + MONITOR_DEFAULTTONEAREST); + _glfwGetMonitorContentScaleWin32(handle, xscale, yscale); +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_MINIMIZE); @@ -1226,7 +1656,7 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window) void _glfwPlatformShowWindow(_GLFWwindow* window) { - ShowWindow(window->win32.handle, SW_SHOW); + ShowWindow(window->win32.handle, SW_SHOWNA); } void _glfwPlatformHideWindow(_GLFWwindow* window) @@ -1234,6 +1664,11 @@ void _glfwPlatformHideWindow(_GLFWwindow* window) ShowWindow(window->win32.handle, SW_HIDE); } +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + FlashWindow(window->win32.handle, TRUE); +} + void _glfwPlatformFocusWindow(_GLFWwindow* window) { BringWindowToTop(window->win32.handle); @@ -1252,13 +1687,27 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (monitor) { if (monitor->window == window) + { acquireMonitor(window); + fitToMonitor(window); + } } else { RECT rect = { xpos, ypos, xpos + width, ypos + height }; - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + SetWindowPos(window->win32.handle, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, @@ -1271,31 +1720,31 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (window->monitor) releaseMonitor(window); - _glfwInputWindowMonitorChange(window, monitor); + _glfwInputWindowMonitor(window, monitor); if (monitor) { - GLFWvidmode mode; - DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); + MONITORINFO mi = { sizeof(mi) }; UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS; if (window->decorated) { + DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); style &= ~WS_OVERLAPPEDWINDOW; style |= getWindowStyle(window); SetWindowLongW(window->win32.handle, GWL_STYLE, style); - flags |= SWP_FRAMECHANGED; } - _glfwPlatformGetVideoMode(monitor, &mode); - _glfwPlatformGetMonitorPos(monitor, &xpos, &ypos); - - SetWindowPos(window->win32.handle, HWND_TOPMOST, - xpos, ypos, mode.width, mode.height, - flags); - acquireMonitor(window); + + GetMonitorInfo(window->monitor->win32.handle, &mi); + SetWindowPos(window->win32.handle, HWND_TOPMOST, + mi.rcMonitor.left, + mi.rcMonitor.top, + mi.rcMonitor.right - mi.rcMonitor.left, + mi.rcMonitor.bottom - mi.rcMonitor.top, + flags); } else { @@ -1318,8 +1767,18 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, else after = HWND_NOTOPMOST; - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + SetWindowPos(window->win32.handle, after, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, @@ -1347,6 +1806,90 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return IsZoomed(window->win32.handle); } +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + return cursorInContentArea(window); +} + +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + BOOL enabled; + + if (!window->win32.transparent) + return GLFW_FALSE; + + if (!IsWindowsVistaOrGreater()) + return GLFW_FALSE; + + return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled; +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + updateWindowStyles(window); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + updateWindowStyles(window); +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST; + SetWindowPos(window->win32.handle, after, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + BYTE alpha; + DWORD flags; + + if ((GetWindowLongW(window->win32.handle, GWL_EXSTYLE) & WS_EX_LAYERED) && + GetLayeredWindowAttributes(window->win32.handle, NULL, &alpha, &flags)) + { + if (flags & LWA_ALPHA) + return alpha / 255.f; + } + + return 1.f; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ + if (opacity < 1.f) + { + const BYTE alpha = (BYTE) (255 * opacity); + DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + style |= WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); + SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA); + } + else + { + DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + style &= ~WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); + } +} + +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +{ + if (_glfw.win32.disabledCursorWindow != window) + return; + + if (enabled) + enableRawMouseMotion(window); + else + disableRawMouseMotion(window); +} + +GLFWbool _glfwPlatformRawMouseMotionSupported(void) +{ + return GLFW_TRUE; +} + void _glfwPlatformPollEvents(void) { MSG msg; @@ -1357,9 +1900,9 @@ void _glfwPlatformPollEvents(void) { if (msg.message == WM_QUIT) { - // Treat WM_QUIT as a close on all windows - // While GLFW does not itself post WM_QUIT, other processes may post - // it to this one, for example Task Manager + // NOTE: While GLFW does not itself post WM_QUIT, other processes + // may post it to this one, for example Task Manager + // HACK: Treat WM_QUIT as a close on all windows window = _glfw.windowListHead; while (window) @@ -1378,25 +1921,28 @@ void _glfwPlatformPollEvents(void) handle = GetActiveWindow(); if (handle) { - // LSHIFT/RSHIFT fixup (keys tend to "stick" without this fix) - // This is the only async event handling in GLFW, but it solves some - // nasty problems + // NOTE: Shift keys on Windows tend to "stick" when both are pressed as + // no key up message is generated by the first key release + // The other half of this is in the handling of WM_KEYUP + // HACK: Query actual key state and synthesize release events as needed window = GetPropW(handle, L"GLFW"); if (window) { - const int mods = getAsyncKeyMods(); + const GLFWbool lshift = (GetAsyncKeyState(VK_LSHIFT) >> 15) & 1; + const GLFWbool rshift = (GetAsyncKeyState(VK_RSHIFT) >> 15) & 1; - // Get current state of left and right shift keys - const int lshiftDown = (GetAsyncKeyState(VK_LSHIFT) >> 15) & 1; - const int rshiftDown = (GetAsyncKeyState(VK_RSHIFT) >> 15) & 1; - - // See if this differs from our belief of what has happened - // (we only have to check for lost key up events) - if (!lshiftDown && window->keys[GLFW_KEY_LEFT_SHIFT] == 1) - _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, 0, GLFW_RELEASE, mods); - - if (!rshiftDown && window->keys[GLFW_KEY_RIGHT_SHIFT] == 1) - _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, 0, GLFW_RELEASE, mods); + if (!lshift && window->keys[GLFW_KEY_LEFT_SHIFT] == GLFW_PRESS) + { + const int mods = getAsyncKeyMods(); + const int scancode = _glfw.win32.scancodes[GLFW_KEY_LEFT_SHIFT]; + _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, GLFW_RELEASE, mods); + } + else if (!rshift && window->keys[GLFW_KEY_RIGHT_SHIFT] == GLFW_PRESS) + { + const int mods = getAsyncKeyMods(); + const int scancode = _glfw.win32.scancodes[GLFW_KEY_RIGHT_SHIFT]; + _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, GLFW_RELEASE, mods); + } } } @@ -1432,8 +1978,7 @@ void _glfwPlatformWaitEventsTimeout(double timeout) void _glfwPlatformPostEmptyEvent(void) { - _GLFWwindow* window = _glfw.windowListHead; - PostMessage(window->win32.handle, WM_NULL, 0, 0); + PostMessage(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); } void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) @@ -1467,48 +2012,23 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { if (mode == GLFW_CURSOR_DISABLED) { - _glfw.win32.disabledCursorWindow = window; - _glfwPlatformGetCursorPos(window, - &_glfw.win32.restoreCursorPosX, - &_glfw.win32.restoreCursorPosY); - centerCursor(window); - updateClipRect(window); + if (_glfwPlatformWindowFocused(window)) + disableCursor(window); } else if (_glfw.win32.disabledCursorWindow == window) - { - _glfw.win32.disabledCursorWindow = NULL; - updateClipRect(NULL); - _glfwPlatformSetCursorPos(window, - _glfw.win32.restoreCursorPosX, - _glfw.win32.restoreCursorPosY); - } - - if (cursorInClientArea(window)) + enableCursor(window); + else if (cursorInContentArea(window)) updateCursorImage(window); } -const char* _glfwPlatformGetKeyName(int key, int scancode) +const char* _glfwPlatformGetScancodeName(int scancode) { - WCHAR name[16]; + return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]]; +} - if (key != GLFW_KEY_UNKNOWN) - scancode = _glfw.win32.nativeKeys[key]; - - if (!_glfwIsPrintable(_glfw.win32.publicKeys[scancode])) - return NULL; - - if (!GetKeyNameTextW(scancode << 16, name, sizeof(name) / sizeof(WCHAR))) - return NULL; - - if (!WideCharToMultiByte(CP_UTF8, 0, name, -1, - _glfw.win32.keyName, - sizeof(_glfw.win32.keyName), - NULL, NULL)) - { - return NULL; - } - - return _glfw.win32.keyName; +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.win32.scancodes[key]; } int _glfwPlatformCreateCursor(_GLFWcursor* cursor, @@ -1524,12 +2044,30 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { - cursor->win32.handle = - CopyCursor(LoadCursorW(NULL, translateCursorShape(shape))); + int id = 0; + + if (shape == GLFW_ARROW_CURSOR) + id = OCR_NORMAL; + else if (shape == GLFW_IBEAM_CURSOR) + id = OCR_IBEAM; + else if (shape == GLFW_CROSSHAIR_CURSOR) + id = OCR_CROSS; + else if (shape == GLFW_HAND_CURSOR) + id = OCR_HAND; + else if (shape == GLFW_HRESIZE_CURSOR) + id = OCR_SIZEWE; + else if (shape == GLFW_VRESIZE_CURSOR) + id = OCR_SIZENS; + else + return GLFW_FALSE; + + cursor->win32.handle = LoadImageW(NULL, + MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0, + LR_DEFAULTSIZE | LR_SHARED); if (!cursor->win32.handle) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to create standard cursor"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create standard cursor"); return GLFW_FALSE; } @@ -1544,11 +2082,11 @@ void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { - if (cursorInClientArea(window)) + if (cursorInContentArea(window)) updateCursorImage(window); } -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +void _glfwPlatformSetClipboardString(const char* string) { int characterCount; HANDLE object; @@ -1556,26 +2094,22 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0); if (!characterCount) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert clipboard string to UTF-16"); return; - } object = GlobalAlloc(GMEM_MOVEABLE, characterCount * sizeof(WCHAR)); if (!object) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to allocate global handle for clipboard"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to allocate global handle for clipboard"); return; } buffer = GlobalLock(object); if (!buffer) { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to lock global handle"); GlobalFree(object); - - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to lock global handle"); return; } @@ -1584,9 +2118,9 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) if (!OpenClipboard(_glfw.win32.helperWindowHandle)) { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to open clipboard"); GlobalFree(object); - - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard"); return; } @@ -1595,75 +2129,60 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) CloseClipboard(); } -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +const char* _glfwPlatformGetClipboardString(void) { HANDLE object; WCHAR* buffer; if (!OpenClipboard(_glfw.win32.helperWindowHandle)) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to open clipboard"); return NULL; } object = GetClipboardData(CF_UNICODETEXT); if (!object) { + _glfwInputErrorWin32(GLFW_FORMAT_UNAVAILABLE, + "Win32: Failed to convert clipboard to string"); CloseClipboard(); - - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "Win32: Failed to convert clipboard to string"); return NULL; } buffer = GlobalLock(object); if (!buffer) { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to lock global handle"); CloseClipboard(); - - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to lock global handle"); return NULL; } free(_glfw.win32.clipboardString); - _glfw.win32.clipboardString = - _glfwCreateUTF8FromWideStringWin32(buffer); + _glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer); GlobalUnlock(object); CloseClipboard(); - if (!_glfw.win32.clipboardString) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert wide string to UTF-8"); - return NULL; - } - return _glfw.win32.clipboardString; } -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { - char** extensions; + if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface) + return; - *count = 0; - - if (!_glfw.vk.KHR_win32_surface) - return NULL; - - extensions = calloc(2, sizeof(char*)); - extensions[0] = strdup("VK_KHR_surface"); - extensions[1] = strdup("VK_KHR_win32_surface"); - - *count = 2; - return extensions; + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_KHR_win32_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { - PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = + PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR + vkGetPhysicalDeviceWin32PresentationSupportKHR = (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR"); if (!vkGetPhysicalDeviceWin32PresentationSupportKHR) diff --git a/external/glfw/window.c b/external/glfw/window.c index fe741dc..cf403dd 100644 --- a/external/glfw/window.c +++ b/external/glfw/window.c @@ -2,7 +2,7 @@ // GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // Copyright (c) 2012 Torsten Walluhn // // This software is provided 'as-is', without any express or implied @@ -38,80 +38,111 @@ ////// GLFW event API ////// ////////////////////////////////////////////////////////////////////////// +// Notifies shared code that a window has lost or received input focus +// void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused) { - if (focused) - { - if (window->callbacks.focus) - window->callbacks.focus((GLFWwindow*) window, focused); - } - else - { - int i; + if (window->callbacks.focus) + window->callbacks.focus((GLFWwindow*) window, focused); - if (window->callbacks.focus) - window->callbacks.focus((GLFWwindow*) window, focused); + if (!focused) + { + int key, button; - // Release all pressed keyboard keys - for (i = 0; i <= GLFW_KEY_LAST; i++) + for (key = 0; key <= GLFW_KEY_LAST; key++) { - if (window->keys[i] == GLFW_PRESS) - _glfwInputKey(window, i, 0, GLFW_RELEASE, 0); + if (window->keys[key] == GLFW_PRESS) + { + const int scancode = _glfwPlatformGetKeyScancode(key); + _glfwInputKey(window, key, scancode, GLFW_RELEASE, 0); + } } - // Release all pressed mouse buttons - for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + for (button = 0; button <= GLFW_MOUSE_BUTTON_LAST; button++) { - if (window->mouseButtons[i] == GLFW_PRESS) - _glfwInputMouseClick(window, i, GLFW_RELEASE, 0); + if (window->mouseButtons[button] == GLFW_PRESS) + _glfwInputMouseClick(window, button, GLFW_RELEASE, 0); } } } +// Notifies shared code that a window has moved +// The position is specified in content area relative screen coordinates +// void _glfwInputWindowPos(_GLFWwindow* window, int x, int y) { if (window->callbacks.pos) window->callbacks.pos((GLFWwindow*) window, x, y); } +// Notifies shared code that a window has been resized +// The size is specified in screen coordinates +// void _glfwInputWindowSize(_GLFWwindow* window, int width, int height) { if (window->callbacks.size) window->callbacks.size((GLFWwindow*) window, width, height); } +// Notifies shared code that a window has been iconified or restored +// void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified) { if (window->callbacks.iconify) window->callbacks.iconify((GLFWwindow*) window, iconified); } +// Notifies shared code that a window has been maximized or restored +// +void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized) +{ + if (window->callbacks.maximize) + window->callbacks.maximize((GLFWwindow*) window, maximized); +} + +// Notifies shared code that a window framebuffer has been resized +// The size is specified in pixels +// void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height) { if (window->callbacks.fbsize) window->callbacks.fbsize((GLFWwindow*) window, width, height); } +// Notifies shared code that a window content scale has changed +// The scale is specified as the ratio between the current and default DPI +// +void _glfwInputWindowContentScale(_GLFWwindow* window, float xscale, float yscale) +{ + if (window->callbacks.scale) + window->callbacks.scale((GLFWwindow*) window, xscale, yscale); +} + +// Notifies shared code that the window contents needs updating +// void _glfwInputWindowDamage(_GLFWwindow* window) { if (window->callbacks.refresh) window->callbacks.refresh((GLFWwindow*) window); } +// Notifies shared code that the user wishes to close a window +// void _glfwInputWindowCloseRequest(_GLFWwindow* window) { - window->closed = GLFW_TRUE; + window->shouldClose = GLFW_TRUE; if (window->callbacks.close) window->callbacks.close((GLFWwindow*) window); } -void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor) +// Notifies shared code that a window has changed its desired monitor +// +void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor) { window->monitor = monitor; } - ////////////////////////////////////////////////////////////////////////// ////// GLFW public API ////// ////////////////////////////////////////////////////////////////////////// @@ -125,9 +156,10 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, _GLFWctxconfig ctxconfig; _GLFWwndconfig wndconfig; _GLFWwindow* window; - _GLFWwindow* previous; assert(title != NULL); + assert(width >= 0); + assert(height >= 0); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); @@ -149,16 +181,6 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, wndconfig.title = title; ctxconfig.share = (_GLFWwindow*) share; - if (ctxconfig.share) - { - if (ctxconfig.client == GLFW_NO_API || - ctxconfig.share->context.client == GLFW_NO_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return NULL; - } - } - if (!_glfwIsValidContextConfig(&ctxconfig)) return NULL; @@ -178,6 +200,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->decorated = wndconfig.decorated; window->autoIconify = wndconfig.autoIconify; window->floating = wndconfig.floating; + window->focusOnShow = wndconfig.focusOnShow; window->cursorMode = GLFW_CURSOR_NORMAL; window->minwidth = GLFW_DONT_CARE; @@ -187,36 +210,28 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->numer = GLFW_DONT_CARE; window->denom = GLFW_DONT_CARE; - // Save the currently current context so it can be restored later - previous = _glfwPlatformGetCurrentContext(); - if (ctxconfig.client != GLFW_NO_API) - glfwMakeContextCurrent(NULL); - // Open the actual window and create its context if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig)) { - glfwMakeContextCurrent((GLFWwindow*) previous); glfwDestroyWindow((GLFWwindow*) window); return NULL; } if (ctxconfig.client != GLFW_NO_API) { - window->context.makeCurrent(window); - - // Retrieve the actual (as opposed to requested) context attributes - if (!_glfwRefreshContextAttribs(&ctxconfig)) + if (!_glfwRefreshContextAttribs(window, &ctxconfig)) { - glfwMakeContextCurrent((GLFWwindow*) previous); glfwDestroyWindow((GLFWwindow*) window); return NULL; } - - // Restore the previously current context (or NULL) - glfwMakeContextCurrent((GLFWwindow*) previous); } - if (!window->monitor) + if (window->monitor) + { + if (wndconfig.centerCursor) + _glfwCenterCursorInContentArea(window); + } + else { if (wndconfig.visible) { @@ -233,23 +248,26 @@ void glfwDefaultWindowHints(void) { _GLFW_REQUIRE_INIT(); - memset(&_glfw.hints, 0, sizeof(_glfw.hints)); - // The default is OpenGL with minimum version 1.0 + memset(&_glfw.hints.context, 0, sizeof(_glfw.hints.context)); _glfw.hints.context.client = GLFW_OPENGL_API; _glfw.hints.context.source = GLFW_NATIVE_CONTEXT_API; _glfw.hints.context.major = 1; _glfw.hints.context.minor = 0; // The default is a focused, visible, resizable window with decorations - _glfw.hints.window.resizable = GLFW_TRUE; - _glfw.hints.window.visible = GLFW_TRUE; - _glfw.hints.window.decorated = GLFW_TRUE; - _glfw.hints.window.focused = GLFW_TRUE; - _glfw.hints.window.autoIconify = GLFW_TRUE; + memset(&_glfw.hints.window, 0, sizeof(_glfw.hints.window)); + _glfw.hints.window.resizable = GLFW_TRUE; + _glfw.hints.window.visible = GLFW_TRUE; + _glfw.hints.window.decorated = GLFW_TRUE; + _glfw.hints.window.focused = GLFW_TRUE; + _glfw.hints.window.autoIconify = GLFW_TRUE; + _glfw.hints.window.centerCursor = GLFW_TRUE; + _glfw.hints.window.focusOnShow = GLFW_TRUE; // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, // double buffered + memset(&_glfw.hints.framebuffer, 0, sizeof(_glfw.hints.framebuffer)); _glfw.hints.framebuffer.redBits = 8; _glfw.hints.framebuffer.greenBits = 8; _glfw.hints.framebuffer.blueBits = 8; @@ -260,6 +278,9 @@ void glfwDefaultWindowHints(void) // The default is to select the highest available refresh rate _glfw.hints.refreshRate = GLFW_DONT_CARE; + + // The default is to use full Retina resolution framebuffers + _glfw.hints.window.ns.retina = GLFW_TRUE; } GLFWAPI void glfwWindowHint(int hint, int value) @@ -270,107 +291,149 @@ GLFWAPI void glfwWindowHint(int hint, int value) { case GLFW_RED_BITS: _glfw.hints.framebuffer.redBits = value; - break; + return; case GLFW_GREEN_BITS: _glfw.hints.framebuffer.greenBits = value; - break; + return; case GLFW_BLUE_BITS: _glfw.hints.framebuffer.blueBits = value; - break; + return; case GLFW_ALPHA_BITS: _glfw.hints.framebuffer.alphaBits = value; - break; + return; case GLFW_DEPTH_BITS: _glfw.hints.framebuffer.depthBits = value; - break; + return; case GLFW_STENCIL_BITS: _glfw.hints.framebuffer.stencilBits = value; - break; + return; case GLFW_ACCUM_RED_BITS: _glfw.hints.framebuffer.accumRedBits = value; - break; + return; case GLFW_ACCUM_GREEN_BITS: _glfw.hints.framebuffer.accumGreenBits = value; - break; + return; case GLFW_ACCUM_BLUE_BITS: _glfw.hints.framebuffer.accumBlueBits = value; - break; + return; case GLFW_ACCUM_ALPHA_BITS: _glfw.hints.framebuffer.accumAlphaBits = value; - break; + return; case GLFW_AUX_BUFFERS: _glfw.hints.framebuffer.auxBuffers = value; - break; + return; case GLFW_STEREO: _glfw.hints.framebuffer.stereo = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_DOUBLEBUFFER: _glfw.hints.framebuffer.doublebuffer = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; + case GLFW_TRANSPARENT_FRAMEBUFFER: + _glfw.hints.framebuffer.transparent = value ? GLFW_TRUE : GLFW_FALSE; + return; case GLFW_SAMPLES: _glfw.hints.framebuffer.samples = value; - break; + return; case GLFW_SRGB_CAPABLE: _glfw.hints.framebuffer.sRGB = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_RESIZABLE: _glfw.hints.window.resizable = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_DECORATED: _glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_FOCUSED: _glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_AUTO_ICONIFY: _glfw.hints.window.autoIconify = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_FLOATING: _glfw.hints.window.floating = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_MAXIMIZED: _glfw.hints.window.maximized = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_VISIBLE: _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; + case GLFW_COCOA_RETINA_FRAMEBUFFER: + _glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_COCOA_GRAPHICS_SWITCHING: + _glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_SCALE_TO_MONITOR: + _glfw.hints.window.scaleToMonitor = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_CENTER_CURSOR: + _glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_FOCUS_ON_SHOW: + _glfw.hints.window.focusOnShow = value ? GLFW_TRUE : GLFW_FALSE; + return; case GLFW_CLIENT_API: _glfw.hints.context.client = value; - break; + return; case GLFW_CONTEXT_CREATION_API: _glfw.hints.context.source = value; - break; + return; case GLFW_CONTEXT_VERSION_MAJOR: _glfw.hints.context.major = value; - break; + return; case GLFW_CONTEXT_VERSION_MINOR: _glfw.hints.context.minor = value; - break; + return; case GLFW_CONTEXT_ROBUSTNESS: _glfw.hints.context.robustness = value; - break; + return; case GLFW_OPENGL_FORWARD_COMPAT: _glfw.hints.context.forward = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_OPENGL_DEBUG_CONTEXT: _glfw.hints.context.debug = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_CONTEXT_NO_ERROR: _glfw.hints.context.noerror = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_OPENGL_PROFILE: _glfw.hints.context.profile = value; - break; + return; case GLFW_CONTEXT_RELEASE_BEHAVIOR: _glfw.hints.context.release = value; - break; + return; case GLFW_REFRESH_RATE: _glfw.hints.refreshRate = value; - break; - default: - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint %i", hint); - break; + return; } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint 0x%08X", hint); +} + +GLFWAPI void glfwWindowHintString(int hint, const char* value) +{ + assert(value != NULL); + + _GLFW_REQUIRE_INIT(); + + switch (hint) + { + case GLFW_COCOA_FRAME_NAME: + strncpy(_glfw.hints.window.ns.frameName, value, + sizeof(_glfw.hints.window.ns.frameName) - 1); + return; + case GLFW_X11_CLASS_NAME: + strncpy(_glfw.hints.window.x11.className, value, + sizeof(_glfw.hints.window.x11.className) - 1); + return; + case GLFW_X11_INSTANCE_NAME: + strncpy(_glfw.hints.window.x11.instanceName, value, + sizeof(_glfw.hints.window.x11.instanceName) - 1); + return; + } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint string 0x%08X", hint); } GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) @@ -388,7 +451,7 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) // The window's context must not be current on another thread when the // window is destroyed - if (window == _glfwPlatformGetCurrentContext()) + if (window == _glfwPlatformGetTls(&_glfw.contextSlot)) glfwMakeContextCurrent(NULL); _glfwPlatformDestroyWindow(window); @@ -412,7 +475,7 @@ GLFWAPI int glfwWindowShouldClose(GLFWwindow* handle) assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(0); - return window->closed; + return window->shouldClose; } GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value) @@ -421,14 +484,13 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value) assert(window != NULL); _GLFW_REQUIRE_INIT(); - window->closed = value; + window->shouldClose = value; } GLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); - assert(title != NULL); _GLFW_REQUIRE_INIT(); @@ -492,6 +554,8 @@ GLFWAPI void glfwSetWindowSize(GLFWwindow* handle, int width, int height) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); + assert(width >= 0); + assert(height >= 0); _GLFW_REQUIRE_INIT(); @@ -550,6 +614,8 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* handle, int numer, int denom) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); + assert(numer != 0); + assert(denom != 0); _GLFW_REQUIRE_INIT(); @@ -607,6 +673,49 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, _glfwPlatformGetWindowFrameSize(window, left, top, right, bottom); } +GLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle, + float* xscale, float* yscale) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (xscale) + *xscale = 0.f; + if (yscale) + *yscale = 0.f; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowContentScale(window, xscale, yscale); +} + +GLFWAPI float glfwGetWindowOpacity(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(1.f); + return _glfwPlatformGetWindowOpacity(window); +} + +GLFWAPI void glfwSetWindowOpacity(GLFWwindow* handle, float opacity) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(opacity == opacity); + assert(opacity >= 0.f); + assert(opacity <= 1.f); + + _GLFW_REQUIRE_INIT(); + + if (opacity != opacity || opacity < 0.f || opacity > 1.f) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid window opacity %f", opacity); + return; + } + + _glfwPlatformSetWindowOpacity(window, opacity); +} + GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; @@ -649,7 +758,19 @@ GLFWAPI void glfwShowWindow(GLFWwindow* handle) return; _glfwPlatformShowWindow(window); - _glfwPlatformFocusWindow(window); + + if (window->focusOnShow) + _glfwPlatformFocusWindow(window); +} + +GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformRequestWindowAttention(window); } GLFWAPI void glfwHideWindow(GLFWwindow* handle) @@ -692,12 +813,20 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return _glfwPlatformWindowVisible(window); case GLFW_MAXIMIZED: return _glfwPlatformWindowMaximized(window); + case GLFW_HOVERED: + return _glfwPlatformWindowHovered(window); + case GLFW_FOCUS_ON_SHOW: + return window->focusOnShow; + case GLFW_TRANSPARENT_FRAMEBUFFER: + return _glfwPlatformFramebufferTransparent(window); case GLFW_RESIZABLE: return window->resizable; case GLFW_DECORATED: return window->decorated; case GLFW_FLOATING: return window->floating; + case GLFW_AUTO_ICONIFY: + return window->autoIconify; case GLFW_CLIENT_API: return window->context.client; case GLFW_CONTEXT_CREATION_API: @@ -722,10 +851,54 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return window->context.noerror; } - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute %i", attrib); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); return 0; } +GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + value = value ? GLFW_TRUE : GLFW_FALSE; + + if (attrib == GLFW_AUTO_ICONIFY) + window->autoIconify = value; + else if (attrib == GLFW_RESIZABLE) + { + if (window->resizable == value) + return; + + window->resizable = value; + if (!window->monitor) + _glfwPlatformSetWindowResizable(window, value); + } + else if (attrib == GLFW_DECORATED) + { + if (window->decorated == value) + return; + + window->decorated = value; + if (!window->monitor) + _glfwPlatformSetWindowDecorated(window, value); + } + else if (attrib == GLFW_FLOATING) + { + if (window->floating == value) + return; + + window->floating = value; + if (!window->monitor) + _glfwPlatformSetWindowFloating(window, value); + } + else if (attrib == GLFW_FOCUS_ON_SHOW) + window->focusOnShow = value; + else + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); +} + GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; @@ -744,6 +917,8 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* wh, _GLFWwindow* window = (_GLFWwindow*) wh; _GLFWmonitor* monitor = (_GLFWmonitor*) mh; assert(window != NULL); + assert(width >= 0); + assert(height >= 0); _GLFW_REQUIRE_INIT(); @@ -856,6 +1031,17 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* handle, return cbfun; } +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* handle, + GLFWwindowmaximizefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.maximize, cbfun); + return cbfun; +} + GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle, GLFWframebuffersizefun cbfun) { @@ -867,6 +1053,17 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle return cbfun; } +GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* handle, + GLFWwindowcontentscalefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.scale, cbfun); + return cbfun; +} + GLFWAPI void glfwPollEvents(void) { _GLFW_REQUIRE_INIT(); @@ -876,16 +1073,15 @@ GLFWAPI void glfwPollEvents(void) GLFWAPI void glfwWaitEvents(void) { _GLFW_REQUIRE_INIT(); - - if (!_glfw.windowListHead) - return; - _glfwPlatformWaitEvents(); } GLFWAPI void glfwWaitEventsTimeout(double timeout) { _GLFW_REQUIRE_INIT(); + assert(timeout == timeout); + assert(timeout >= 0.0); + assert(timeout <= DBL_MAX); if (timeout != timeout || timeout < 0.0 || timeout > DBL_MAX) { @@ -899,10 +1095,5 @@ GLFWAPI void glfwWaitEventsTimeout(double timeout) GLFWAPI void glfwPostEmptyEvent(void) { _GLFW_REQUIRE_INIT(); - - if (!_glfw.windowListHead) - return; - _glfwPlatformPostEmptyEvent(); } - diff --git a/external/glfw/wl_init.c b/external/glfw/wl_init.c index 1b59009..8a6b918 100644 --- a/external/glfw/wl_init.c +++ b/external/glfw/wl_init.c @@ -26,14 +26,17 @@ #include "internal.h" +#include +#include +#include #include #include #include #include #include +#include #include #include -#include static inline int min(int n1, int n2) @@ -41,6 +44,40 @@ static inline int min(int n1, int n2) return n1 < n2 ? n1 : n2; } +static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, + int* which) +{ + int focus; + _GLFWwindow* window = _glfw.windowListHead; + if (!which) + which = &focus; + while (window) + { + if (surface == window->wl.decorations.top.surface) + { + *which = topDecoration; + break; + } + if (surface == window->wl.decorations.left.surface) + { + *which = leftDecoration; + break; + } + if (surface == window->wl.decorations.right.surface) + { + *which = rightDecoration; + break; + } + if (surface == window->wl.decorations.bottom.surface) + { + *which = bottomDecoration; + break; + } + window = window->next; + } + return window; +} + static void pointerHandleEnter(void* data, struct wl_pointer* pointer, uint32_t serial, @@ -48,11 +85,25 @@ static void pointerHandleEnter(void* data, wl_fixed_t sx, wl_fixed_t sy) { - _GLFWwindow* window = wl_surface_get_user_data(surface); + // Happens in the case we just destroyed the surface. + if (!surface) + return; - _glfw.wl.pointerSerial = serial; + int focus = 0; + _GLFWwindow* window = wl_surface_get_user_data(surface); + if (!window) + { + window = findWindowFromDecorationSurface(surface, &focus); + if (!window) + return; + } + + window->wl.decorations.focus = focus; + _glfw.wl.serial = serial; _glfw.wl.pointerFocus = window; + window->wl.hovered = GLFW_TRUE; + _glfwPlatformSetCursor(window, window->wl.currentCursor); _glfwInputCursorEnter(window, GLFW_TRUE); } @@ -67,11 +118,57 @@ static void pointerHandleLeave(void* data, if (!window) return; - _glfw.wl.pointerSerial = serial; + window->wl.hovered = GLFW_FALSE; + + _glfw.wl.serial = serial; _glfw.wl.pointerFocus = NULL; _glfwInputCursorEnter(window, GLFW_FALSE); } +static void setCursor(_GLFWwindow* window, const char* name) +{ + struct wl_buffer* buffer; + struct wl_cursor* cursor; + struct wl_cursor_image* image; + struct wl_surface* surface = _glfw.wl.cursorSurface; + struct wl_cursor_theme* theme = _glfw.wl.cursorTheme; + int scale = 1; + + if (window->wl.scale > 1 && _glfw.wl.cursorThemeHiDPI) + { + // We only support up to scale=2 for now, since libwayland-cursor + // requires us to load a different theme for each size. + scale = 2; + theme = _glfw.wl.cursorThemeHiDPI; + } + + cursor = wl_cursor_theme_get_cursor(theme, name); + if (!cursor) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Standard cursor not found"); + return; + } + // TODO: handle animated cursors too. + image = cursor->images[0]; + + if (!image) + return; + + buffer = wl_cursor_image_get_buffer(image); + if (!buffer) + return; + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, + surface, + image->hotspot_x / scale, + image->hotspot_y / scale); + wl_surface_set_buffer_scale(surface, scale); + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_damage(surface, 0, 0, + image->width, image->height); + wl_surface_commit(surface); +} + static void pointerHandleMotion(void* data, struct wl_pointer* pointer, uint32_t time, @@ -79,6 +176,7 @@ static void pointerHandleMotion(void* data, wl_fixed_t sy) { _GLFWwindow* window = _glfw.wl.pointerFocus; + const char* cursorName; if (!window) return; @@ -91,13 +189,47 @@ static void pointerHandleMotion(void* data, window->wl.cursorPosY = wl_fixed_to_double(sy); } - _glfwInputCursorPos(window, - wl_fixed_to_double(sx), - wl_fixed_to_double(sy)); + switch (window->wl.decorations.focus) + { + case mainWindow: + _glfwInputCursorPos(window, + wl_fixed_to_double(sx), + wl_fixed_to_double(sy)); + return; + case topDecoration: + if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + cursorName = "n-resize"; + else + cursorName = "left_ptr"; + break; + case leftDecoration: + if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + cursorName = "nw-resize"; + else + cursorName = "w-resize"; + break; + case rightDecoration: + if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + cursorName = "ne-resize"; + else + cursorName = "e-resize"; + break; + case bottomDecoration: + if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH) + cursorName = "sw-resize"; + else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH) + cursorName = "se-resize"; + else + cursorName = "s-resize"; + break; + default: + assert(0); + } + setCursor(window, cursorName); } static void pointerHandleButton(void* data, - struct wl_pointer* wl_pointer, + struct wl_pointer* pointer, uint32_t serial, uint32_t time, uint32_t button, @@ -106,8 +238,78 @@ static void pointerHandleButton(void* data, _GLFWwindow* window = _glfw.wl.pointerFocus; int glfwButton; + // Both xdg-shell and wl_shell use the same values. + uint32_t edges = WL_SHELL_SURFACE_RESIZE_NONE; + if (!window) return; + if (button == BTN_LEFT) + { + switch (window->wl.decorations.focus) + { + case mainWindow: + break; + case topDecoration: + if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + edges = WL_SHELL_SURFACE_RESIZE_TOP; + else + { + if (window->wl.xdg.toplevel) + xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial); + else + wl_shell_surface_move(window->wl.shellSurface, _glfw.wl.seat, serial); + } + break; + case leftDecoration: + if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + edges = WL_SHELL_SURFACE_RESIZE_TOP_LEFT; + else + edges = WL_SHELL_SURFACE_RESIZE_LEFT; + break; + case rightDecoration: + if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + edges = WL_SHELL_SURFACE_RESIZE_TOP_RIGHT; + else + edges = WL_SHELL_SURFACE_RESIZE_RIGHT; + break; + case bottomDecoration: + if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH) + edges = WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT; + else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH) + edges = WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT; + else + edges = WL_SHELL_SURFACE_RESIZE_BOTTOM; + break; + default: + assert(0); + } + if (edges != WL_SHELL_SURFACE_RESIZE_NONE) + { + if (window->wl.xdg.toplevel) + xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, + serial, edges); + else + wl_shell_surface_resize(window->wl.shellSurface, _glfw.wl.seat, + serial, edges); + } + } + else if (button == BTN_RIGHT) + { + if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel) + { + xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, + _glfw.wl.seat, serial, + window->wl.cursorPosX, + window->wl.cursorPosY); + return; + } + } + + // Don’t pass the button to the user if it was related to a decoration. + if (window->wl.decorations.focus != mainWindow) + return; + + _glfw.wl.serial = serial; /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev * codes. */ @@ -122,36 +324,28 @@ static void pointerHandleButton(void* data, } static void pointerHandleAxis(void* data, - struct wl_pointer* wl_pointer, + struct wl_pointer* pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { _GLFWwindow* window = _glfw.wl.pointerFocus; - double scroll_factor; - double x, y; + double x = 0.0, y = 0.0; + // Wayland scroll events are in pointer motion coordinate space (think two + // finger scroll). The factor 10 is commonly used to convert to "scroll + // step means 1.0. + const double scrollFactor = 1.0 / 10.0; if (!window) return; - /* Wayland scroll events are in pointer motion coordinate space (think - * two finger scroll). The factor 10 is commonly used to convert to - * "scroll step means 1.0. */ - scroll_factor = 1.0/10.0; + assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL || + axis == WL_POINTER_AXIS_VERTICAL_SCROLL); - switch (axis) - { - case WL_POINTER_AXIS_HORIZONTAL_SCROLL: - x = wl_fixed_to_double(value) * scroll_factor; - y = 0.0; - break; - case WL_POINTER_AXIS_VERTICAL_SCROLL: - x = 0.0; - y = wl_fixed_to_double(value) * scroll_factor; - break; - default: - break; - } + if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) + x = wl_fixed_to_double(value) * scrollFactor; + else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) + y = wl_fixed_to_double(value) * scrollFactor; _glfwInputScroll(window, x, y); } @@ -172,7 +366,14 @@ static void keyboardHandleKeymap(void* data, { struct xkb_keymap* keymap; struct xkb_state* state; + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + struct xkb_compose_table* composeTable; + struct xkb_compose_state* composeState; +#endif + char* mapStr; + const char* locale; if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { @@ -186,10 +387,10 @@ static void keyboardHandleKeymap(void* data, return; } - keymap = xkb_map_new_from_string(_glfw.wl.xkb.context, - mapStr, - XKB_KEYMAP_FORMAT_TEXT_V1, - 0); + keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, + mapStr, + XKB_KEYMAP_FORMAT_TEXT_V1, + 0); munmap(mapStr, size); close(fd); @@ -205,23 +406,58 @@ static void keyboardHandleKeymap(void* data, { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create XKB state"); - xkb_map_unref(keymap); + xkb_keymap_unref(keymap); return; } + // Look up the preferred locale, falling back to "C" as default. + locale = getenv("LC_ALL"); + if (!locale) + locale = getenv("LC_CTYPE"); + if (!locale) + locale = getenv("LANG"); + if (!locale) + locale = "C"; + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + composeTable = + xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, + XKB_COMPOSE_COMPILE_NO_FLAGS); + if (composeTable) + { + composeState = + xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); + xkb_compose_table_unref(composeTable); + if (composeState) + _glfw.wl.xkb.composeState = composeState; + else + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose state"); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose table"); + } +#endif + xkb_keymap_unref(_glfw.wl.xkb.keymap); xkb_state_unref(_glfw.wl.xkb.state); _glfw.wl.xkb.keymap = keymap; _glfw.wl.xkb.state = state; - _glfw.wl.xkb.control_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Control"); - _glfw.wl.xkb.alt_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); - _glfw.wl.xkb.shift_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); - _glfw.wl.xkb.super_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); + _glfw.wl.xkb.controlMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); + _glfw.wl.xkb.altMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); + _glfw.wl.xkb.shiftMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); + _glfw.wl.xkb.superMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); + _glfw.wl.xkb.capsLockMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock"); + _glfw.wl.xkb.numLockMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2"); } static void keyboardHandleEnter(void* data, @@ -230,8 +466,19 @@ static void keyboardHandleEnter(void* data, struct wl_surface* surface, struct wl_array* keys) { - _GLFWwindow* window = wl_surface_get_user_data(surface); + // Happens in the case we just destroyed the surface. + if (!surface) + return; + _GLFWwindow* window = wl_surface_get_user_data(surface); + if (!window) + { + window = findWindowFromDecorationSurface(surface, NULL); + if (!window) + return; + } + + _glfw.wl.serial = serial; _glfw.wl.keyboardFocus = window; _glfwInputWindowFocus(window, GLFW_TRUE); } @@ -246,18 +493,70 @@ static void keyboardHandleLeave(void* data, if (!window) return; + _glfw.wl.serial = serial; _glfw.wl.keyboardFocus = NULL; _glfwInputWindowFocus(window, GLFW_FALSE); } static int toGLFWKeyCode(uint32_t key) { - if (key < sizeof(_glfw.wl.publicKeys) / sizeof(_glfw.wl.publicKeys[0])) - return _glfw.wl.publicKeys[key]; + if (key < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0])) + return _glfw.wl.keycodes[key]; return GLFW_KEY_UNKNOWN; } +#ifdef HAVE_XKBCOMMON_COMPOSE_H +static xkb_keysym_t composeSymbol(xkb_keysym_t sym) +{ + if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState) + return sym; + if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) + != XKB_COMPOSE_FEED_ACCEPTED) + return sym; + switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) + { + case XKB_COMPOSE_COMPOSED: + return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); + case XKB_COMPOSE_COMPOSING: + case XKB_COMPOSE_CANCELLED: + return XKB_KEY_NoSymbol; + case XKB_COMPOSE_NOTHING: + default: + return sym; + } +} +#endif + +static GLFWbool inputChar(_GLFWwindow* window, uint32_t key) +{ + uint32_t code, numSyms; + long cp; + const xkb_keysym_t *syms; + xkb_keysym_t sym; + + code = key + 8; + numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms); + + if (numSyms == 1) + { +#ifdef HAVE_XKBCOMMON_COMPOSE_H + sym = composeSymbol(syms[0]); +#else + sym = syms[0]; +#endif + cp = _glfwKeySym2Unicode(sym); + if (cp != -1) + { + const int mods = _glfw.wl.xkb.modifiers; + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + _glfwInputChar(window, cp, mods, plain); + } + } + + return xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, syms[0]); +} + static void keyboardHandleKey(void* data, struct wl_keyboard* keyboard, uint32_t serial, @@ -265,12 +564,11 @@ static void keyboardHandleKey(void* data, uint32_t key, uint32_t state) { - uint32_t code, num_syms; - long cp; int keyCode; int action; - const xkb_keysym_t *syms; _GLFWwindow* window = _glfw.wl.keyboardFocus; + GLFWbool shouldRepeat; + struct itimerspec timer = {}; if (!window) return; @@ -279,22 +577,27 @@ static void keyboardHandleKey(void* data, action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE; + _glfw.wl.serial = serial; _glfwInputKey(window, keyCode, key, action, _glfw.wl.xkb.modifiers); - code = key + 8; - num_syms = xkb_key_get_syms(_glfw.wl.xkb.state, code, &syms); - - if (num_syms == 1) + if (action == GLFW_PRESS) { - cp = _glfwKeySym2Unicode(syms[0]); - if (cp != -1) + shouldRepeat = inputChar(window, key); + + if (shouldRepeat && _glfw.wl.keyboardRepeatRate > 0) { - const int mods = _glfw.wl.xkb.modifiers; - const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); - _glfwInputChar(window, cp, mods, plain); + _glfw.wl.keyboardLastKey = keyCode; + _glfw.wl.keyboardLastScancode = key; + if (_glfw.wl.keyboardRepeatRate > 1) + timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyboardRepeatRate; + else + timer.it_interval.tv_sec = 1; + timer.it_value.tv_sec = _glfw.wl.keyboardRepeatDelay / 1000; + timer.it_value.tv_nsec = (_glfw.wl.keyboardRepeatDelay % 1000) * 1000000; } } + timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); } static void keyboardHandleModifiers(void* data, @@ -308,6 +611,8 @@ static void keyboardHandleModifiers(void* data, xkb_mod_mask_t mask; unsigned int modifiers = 0; + _glfw.wl.serial = serial; + if (!_glfw.wl.xkb.keymap) return; @@ -320,25 +625,48 @@ static void keyboardHandleModifiers(void* data, group); mask = xkb_state_serialize_mods(_glfw.wl.xkb.state, - XKB_STATE_DEPRESSED | - XKB_STATE_LATCHED); - if (mask & _glfw.wl.xkb.control_mask) + XKB_STATE_MODS_DEPRESSED | + XKB_STATE_LAYOUT_DEPRESSED | + XKB_STATE_MODS_LATCHED | + XKB_STATE_LAYOUT_LATCHED); + if (mask & _glfw.wl.xkb.controlMask) modifiers |= GLFW_MOD_CONTROL; - if (mask & _glfw.wl.xkb.alt_mask) + if (mask & _glfw.wl.xkb.altMask) modifiers |= GLFW_MOD_ALT; - if (mask & _glfw.wl.xkb.shift_mask) + if (mask & _glfw.wl.xkb.shiftMask) modifiers |= GLFW_MOD_SHIFT; - if (mask & _glfw.wl.xkb.super_mask) + if (mask & _glfw.wl.xkb.superMask) modifiers |= GLFW_MOD_SUPER; + if (mask & _glfw.wl.xkb.capsLockMask) + modifiers |= GLFW_MOD_CAPS_LOCK; + if (mask & _glfw.wl.xkb.numLockMask) + modifiers |= GLFW_MOD_NUM_LOCK; _glfw.wl.xkb.modifiers = modifiers; } +#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION +static void keyboardHandleRepeatInfo(void* data, + struct wl_keyboard* keyboard, + int32_t rate, + int32_t delay) +{ + if (keyboard != _glfw.wl.keyboard) + return; + + _glfw.wl.keyboardRepeatRate = rate; + _glfw.wl.keyboardRepeatDelay = delay; +} +#endif + static const struct wl_keyboard_listener keyboardListener = { keyboardHandleKeymap, keyboardHandleEnter, keyboardHandleLeave, keyboardHandleKey, keyboardHandleModifiers, +#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION + keyboardHandleRepeatInfo, +#endif }; static void seatHandleCapabilities(void* data, @@ -368,8 +696,90 @@ static void seatHandleCapabilities(void* data, } } +static void seatHandleName(void* data, + struct wl_seat* seat, + const char* name) +{ +} + static const struct wl_seat_listener seatListener = { - seatHandleCapabilities + seatHandleCapabilities, + seatHandleName, +}; + +static void dataOfferHandleOffer(void* data, + struct wl_data_offer* dataOffer, + const char* mimeType) +{ +} + +static const struct wl_data_offer_listener dataOfferListener = { + dataOfferHandleOffer, +}; + +static void dataDeviceHandleDataOffer(void* data, + struct wl_data_device* dataDevice, + struct wl_data_offer* id) +{ + if (_glfw.wl.dataOffer) + wl_data_offer_destroy(_glfw.wl.dataOffer); + + _glfw.wl.dataOffer = id; + wl_data_offer_add_listener(_glfw.wl.dataOffer, &dataOfferListener, NULL); +} + +static void dataDeviceHandleEnter(void* data, + struct wl_data_device* dataDevice, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t x, + wl_fixed_t y, + struct wl_data_offer *id) +{ +} + +static void dataDeviceHandleLeave(void* data, + struct wl_data_device* dataDevice) +{ +} + +static void dataDeviceHandleMotion(void* data, + struct wl_data_device* dataDevice, + uint32_t time, + wl_fixed_t x, + wl_fixed_t y) +{ +} + +static void dataDeviceHandleDrop(void* data, + struct wl_data_device* dataDevice) +{ +} + +static void dataDeviceHandleSelection(void* data, + struct wl_data_device* dataDevice, + struct wl_data_offer* id) +{ +} + +static const struct wl_data_device_listener dataDeviceListener = { + dataDeviceHandleDataOffer, + dataDeviceHandleEnter, + dataDeviceHandleLeave, + dataDeviceHandleMotion, + dataDeviceHandleDrop, + dataDeviceHandleSelection, +}; + +static void wmBaseHandlePing(void* data, + struct xdg_wm_base* wmBase, + uint32_t serial) +{ + xdg_wm_base_pong(wmBase, serial); +} + +static const struct xdg_wm_base_listener wmBaseListener = { + wmBaseHandlePing }; static void registryHandleGlobal(void* data, @@ -380,10 +790,15 @@ static void registryHandleGlobal(void* data, { if (strcmp(interface, "wl_compositor") == 0) { - _glfw.wl.wl_compositor_version = min(3, version); + _glfw.wl.compositorVersion = min(3, version); _glfw.wl.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, - _glfw.wl.wl_compositor_version); + _glfw.wl.compositorVersion); + } + else if (strcmp(interface, "wl_subcompositor") == 0) + { + _glfw.wl.subcompositor = + wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); } else if (strcmp(interface, "wl_shm") == 0) { @@ -403,11 +818,40 @@ static void registryHandleGlobal(void* data, { if (!_glfw.wl.seat) { + _glfw.wl.seatVersion = min(4, version); _glfw.wl.seat = - wl_registry_bind(registry, name, &wl_seat_interface, 1); + wl_registry_bind(registry, name, &wl_seat_interface, + _glfw.wl.seatVersion); wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL); } } + else if (strcmp(interface, "wl_data_device_manager") == 0) + { + if (!_glfw.wl.dataDeviceManager) + { + _glfw.wl.dataDeviceManager = + wl_registry_bind(registry, name, + &wl_data_device_manager_interface, 1); + } + } + else if (strcmp(interface, "xdg_wm_base") == 0) + { + _glfw.wl.wmBase = + wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL); + } + else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) + { + _glfw.wl.decorationManager = + wl_registry_bind(registry, name, + &zxdg_decoration_manager_v1_interface, + 1); + } + else if (strcmp(interface, "wp_viewporter") == 0) + { + _glfw.wl.viewporter = + wl_registry_bind(registry, name, &wp_viewporter_interface, 1); + } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) { _glfw.wl.relativePointerManager = @@ -422,12 +866,31 @@ static void registryHandleGlobal(void* data, &zwp_pointer_constraints_v1_interface, 1); } + else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) + { + _glfw.wl.idleInhibitManager = + wl_registry_bind(registry, name, + &zwp_idle_inhibit_manager_v1_interface, + 1); + } } static void registryHandleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name) { + int i; + _GLFWmonitor* monitor; + + for (i = 0; i < _glfw.monitorCount; ++i) + { + monitor = _glfw.monitors[i]; + if (monitor->wl.name == name) + { + _glfwInputMonitor(monitor, GLFW_DISCONNECTED, 0); + return; + } + } } @@ -440,124 +903,134 @@ static const struct wl_registry_listener registryListener = { // static void createKeyTables(void) { - memset(_glfw.wl.publicKeys, -1, sizeof(_glfw.wl.publicKeys)); + int scancode; - _glfw.wl.publicKeys[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; - _glfw.wl.publicKeys[KEY_1] = GLFW_KEY_1; - _glfw.wl.publicKeys[KEY_2] = GLFW_KEY_2; - _glfw.wl.publicKeys[KEY_3] = GLFW_KEY_3; - _glfw.wl.publicKeys[KEY_4] = GLFW_KEY_4; - _glfw.wl.publicKeys[KEY_5] = GLFW_KEY_5; - _glfw.wl.publicKeys[KEY_6] = GLFW_KEY_6; - _glfw.wl.publicKeys[KEY_7] = GLFW_KEY_7; - _glfw.wl.publicKeys[KEY_8] = GLFW_KEY_8; - _glfw.wl.publicKeys[KEY_9] = GLFW_KEY_9; - _glfw.wl.publicKeys[KEY_0] = GLFW_KEY_0; - _glfw.wl.publicKeys[KEY_MINUS] = GLFW_KEY_MINUS; - _glfw.wl.publicKeys[KEY_EQUAL] = GLFW_KEY_EQUAL; - _glfw.wl.publicKeys[KEY_Q] = GLFW_KEY_Q; - _glfw.wl.publicKeys[KEY_W] = GLFW_KEY_W; - _glfw.wl.publicKeys[KEY_E] = GLFW_KEY_E; - _glfw.wl.publicKeys[KEY_R] = GLFW_KEY_R; - _glfw.wl.publicKeys[KEY_T] = GLFW_KEY_T; - _glfw.wl.publicKeys[KEY_Y] = GLFW_KEY_Y; - _glfw.wl.publicKeys[KEY_U] = GLFW_KEY_U; - _glfw.wl.publicKeys[KEY_I] = GLFW_KEY_I; - _glfw.wl.publicKeys[KEY_O] = GLFW_KEY_O; - _glfw.wl.publicKeys[KEY_P] = GLFW_KEY_P; - _glfw.wl.publicKeys[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; - _glfw.wl.publicKeys[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; - _glfw.wl.publicKeys[KEY_A] = GLFW_KEY_A; - _glfw.wl.publicKeys[KEY_S] = GLFW_KEY_S; - _glfw.wl.publicKeys[KEY_D] = GLFW_KEY_D; - _glfw.wl.publicKeys[KEY_F] = GLFW_KEY_F; - _glfw.wl.publicKeys[KEY_G] = GLFW_KEY_G; - _glfw.wl.publicKeys[KEY_H] = GLFW_KEY_H; - _glfw.wl.publicKeys[KEY_J] = GLFW_KEY_J; - _glfw.wl.publicKeys[KEY_K] = GLFW_KEY_K; - _glfw.wl.publicKeys[KEY_L] = GLFW_KEY_L; - _glfw.wl.publicKeys[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; - _glfw.wl.publicKeys[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; - _glfw.wl.publicKeys[KEY_Z] = GLFW_KEY_Z; - _glfw.wl.publicKeys[KEY_X] = GLFW_KEY_X; - _glfw.wl.publicKeys[KEY_C] = GLFW_KEY_C; - _glfw.wl.publicKeys[KEY_V] = GLFW_KEY_V; - _glfw.wl.publicKeys[KEY_B] = GLFW_KEY_B; - _glfw.wl.publicKeys[KEY_N] = GLFW_KEY_N; - _glfw.wl.publicKeys[KEY_M] = GLFW_KEY_M; - _glfw.wl.publicKeys[KEY_COMMA] = GLFW_KEY_COMMA; - _glfw.wl.publicKeys[KEY_DOT] = GLFW_KEY_PERIOD; - _glfw.wl.publicKeys[KEY_SLASH] = GLFW_KEY_SLASH; - _glfw.wl.publicKeys[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; - _glfw.wl.publicKeys[KEY_ESC] = GLFW_KEY_ESCAPE; - _glfw.wl.publicKeys[KEY_TAB] = GLFW_KEY_TAB; - _glfw.wl.publicKeys[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; - _glfw.wl.publicKeys[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; - _glfw.wl.publicKeys[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; - _glfw.wl.publicKeys[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; - _glfw.wl.publicKeys[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; - _glfw.wl.publicKeys[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; - _glfw.wl.publicKeys[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; - _glfw.wl.publicKeys[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; - _glfw.wl.publicKeys[KEY_MENU] = GLFW_KEY_MENU; - _glfw.wl.publicKeys[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; - _glfw.wl.publicKeys[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; - _glfw.wl.publicKeys[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; - _glfw.wl.publicKeys[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; - _glfw.wl.publicKeys[KEY_PAUSE] = GLFW_KEY_PAUSE; - _glfw.wl.publicKeys[KEY_DELETE] = GLFW_KEY_DELETE; - _glfw.wl.publicKeys[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; - _glfw.wl.publicKeys[KEY_ENTER] = GLFW_KEY_ENTER; - _glfw.wl.publicKeys[KEY_HOME] = GLFW_KEY_HOME; - _glfw.wl.publicKeys[KEY_END] = GLFW_KEY_END; - _glfw.wl.publicKeys[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; - _glfw.wl.publicKeys[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; - _glfw.wl.publicKeys[KEY_INSERT] = GLFW_KEY_INSERT; - _glfw.wl.publicKeys[KEY_LEFT] = GLFW_KEY_LEFT; - _glfw.wl.publicKeys[KEY_RIGHT] = GLFW_KEY_RIGHT; - _glfw.wl.publicKeys[KEY_DOWN] = GLFW_KEY_DOWN; - _glfw.wl.publicKeys[KEY_UP] = GLFW_KEY_UP; - _glfw.wl.publicKeys[KEY_F1] = GLFW_KEY_F1; - _glfw.wl.publicKeys[KEY_F2] = GLFW_KEY_F2; - _glfw.wl.publicKeys[KEY_F3] = GLFW_KEY_F3; - _glfw.wl.publicKeys[KEY_F4] = GLFW_KEY_F4; - _glfw.wl.publicKeys[KEY_F5] = GLFW_KEY_F5; - _glfw.wl.publicKeys[KEY_F6] = GLFW_KEY_F6; - _glfw.wl.publicKeys[KEY_F7] = GLFW_KEY_F7; - _glfw.wl.publicKeys[KEY_F8] = GLFW_KEY_F8; - _glfw.wl.publicKeys[KEY_F9] = GLFW_KEY_F9; - _glfw.wl.publicKeys[KEY_F10] = GLFW_KEY_F10; - _glfw.wl.publicKeys[KEY_F11] = GLFW_KEY_F11; - _glfw.wl.publicKeys[KEY_F12] = GLFW_KEY_F12; - _glfw.wl.publicKeys[KEY_F13] = GLFW_KEY_F13; - _glfw.wl.publicKeys[KEY_F14] = GLFW_KEY_F14; - _glfw.wl.publicKeys[KEY_F15] = GLFW_KEY_F15; - _glfw.wl.publicKeys[KEY_F16] = GLFW_KEY_F16; - _glfw.wl.publicKeys[KEY_F17] = GLFW_KEY_F17; - _glfw.wl.publicKeys[KEY_F18] = GLFW_KEY_F18; - _glfw.wl.publicKeys[KEY_F19] = GLFW_KEY_F19; - _glfw.wl.publicKeys[KEY_F20] = GLFW_KEY_F20; - _glfw.wl.publicKeys[KEY_F21] = GLFW_KEY_F21; - _glfw.wl.publicKeys[KEY_F22] = GLFW_KEY_F22; - _glfw.wl.publicKeys[KEY_F23] = GLFW_KEY_F23; - _glfw.wl.publicKeys[KEY_F24] = GLFW_KEY_F24; - _glfw.wl.publicKeys[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; - _glfw.wl.publicKeys[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; - _glfw.wl.publicKeys[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; - _glfw.wl.publicKeys[KEY_KPPLUS] = GLFW_KEY_KP_ADD; - _glfw.wl.publicKeys[KEY_KP0] = GLFW_KEY_KP_0; - _glfw.wl.publicKeys[KEY_KP1] = GLFW_KEY_KP_1; - _glfw.wl.publicKeys[KEY_KP2] = GLFW_KEY_KP_2; - _glfw.wl.publicKeys[KEY_KP3] = GLFW_KEY_KP_3; - _glfw.wl.publicKeys[KEY_KP4] = GLFW_KEY_KP_4; - _glfw.wl.publicKeys[KEY_KP5] = GLFW_KEY_KP_5; - _glfw.wl.publicKeys[KEY_KP6] = GLFW_KEY_KP_6; - _glfw.wl.publicKeys[KEY_KP7] = GLFW_KEY_KP_7; - _glfw.wl.publicKeys[KEY_KP8] = GLFW_KEY_KP_8; - _glfw.wl.publicKeys[KEY_KP9] = GLFW_KEY_KP_9; - _glfw.wl.publicKeys[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; - _glfw.wl.publicKeys[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; - _glfw.wl.publicKeys[KEY_KPENTER] = GLFW_KEY_KP_ENTER; + memset(_glfw.wl.keycodes, -1, sizeof(_glfw.wl.keycodes)); + memset(_glfw.wl.scancodes, -1, sizeof(_glfw.wl.scancodes)); + + _glfw.wl.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; + _glfw.wl.keycodes[KEY_1] = GLFW_KEY_1; + _glfw.wl.keycodes[KEY_2] = GLFW_KEY_2; + _glfw.wl.keycodes[KEY_3] = GLFW_KEY_3; + _glfw.wl.keycodes[KEY_4] = GLFW_KEY_4; + _glfw.wl.keycodes[KEY_5] = GLFW_KEY_5; + _glfw.wl.keycodes[KEY_6] = GLFW_KEY_6; + _glfw.wl.keycodes[KEY_7] = GLFW_KEY_7; + _glfw.wl.keycodes[KEY_8] = GLFW_KEY_8; + _glfw.wl.keycodes[KEY_9] = GLFW_KEY_9; + _glfw.wl.keycodes[KEY_0] = GLFW_KEY_0; + _glfw.wl.keycodes[KEY_SPACE] = GLFW_KEY_SPACE; + _glfw.wl.keycodes[KEY_MINUS] = GLFW_KEY_MINUS; + _glfw.wl.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL; + _glfw.wl.keycodes[KEY_Q] = GLFW_KEY_Q; + _glfw.wl.keycodes[KEY_W] = GLFW_KEY_W; + _glfw.wl.keycodes[KEY_E] = GLFW_KEY_E; + _glfw.wl.keycodes[KEY_R] = GLFW_KEY_R; + _glfw.wl.keycodes[KEY_T] = GLFW_KEY_T; + _glfw.wl.keycodes[KEY_Y] = GLFW_KEY_Y; + _glfw.wl.keycodes[KEY_U] = GLFW_KEY_U; + _glfw.wl.keycodes[KEY_I] = GLFW_KEY_I; + _glfw.wl.keycodes[KEY_O] = GLFW_KEY_O; + _glfw.wl.keycodes[KEY_P] = GLFW_KEY_P; + _glfw.wl.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; + _glfw.wl.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; + _glfw.wl.keycodes[KEY_A] = GLFW_KEY_A; + _glfw.wl.keycodes[KEY_S] = GLFW_KEY_S; + _glfw.wl.keycodes[KEY_D] = GLFW_KEY_D; + _glfw.wl.keycodes[KEY_F] = GLFW_KEY_F; + _glfw.wl.keycodes[KEY_G] = GLFW_KEY_G; + _glfw.wl.keycodes[KEY_H] = GLFW_KEY_H; + _glfw.wl.keycodes[KEY_J] = GLFW_KEY_J; + _glfw.wl.keycodes[KEY_K] = GLFW_KEY_K; + _glfw.wl.keycodes[KEY_L] = GLFW_KEY_L; + _glfw.wl.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; + _glfw.wl.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; + _glfw.wl.keycodes[KEY_Z] = GLFW_KEY_Z; + _glfw.wl.keycodes[KEY_X] = GLFW_KEY_X; + _glfw.wl.keycodes[KEY_C] = GLFW_KEY_C; + _glfw.wl.keycodes[KEY_V] = GLFW_KEY_V; + _glfw.wl.keycodes[KEY_B] = GLFW_KEY_B; + _glfw.wl.keycodes[KEY_N] = GLFW_KEY_N; + _glfw.wl.keycodes[KEY_M] = GLFW_KEY_M; + _glfw.wl.keycodes[KEY_COMMA] = GLFW_KEY_COMMA; + _glfw.wl.keycodes[KEY_DOT] = GLFW_KEY_PERIOD; + _glfw.wl.keycodes[KEY_SLASH] = GLFW_KEY_SLASH; + _glfw.wl.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; + _glfw.wl.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE; + _glfw.wl.keycodes[KEY_TAB] = GLFW_KEY_TAB; + _glfw.wl.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; + _glfw.wl.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; + _glfw.wl.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; + _glfw.wl.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; + _glfw.wl.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; + _glfw.wl.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; + _glfw.wl.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; + _glfw.wl.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; + _glfw.wl.keycodes[KEY_MENU] = GLFW_KEY_MENU; + _glfw.wl.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; + _glfw.wl.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; + _glfw.wl.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; + _glfw.wl.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; + _glfw.wl.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE; + _glfw.wl.keycodes[KEY_DELETE] = GLFW_KEY_DELETE; + _glfw.wl.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; + _glfw.wl.keycodes[KEY_ENTER] = GLFW_KEY_ENTER; + _glfw.wl.keycodes[KEY_HOME] = GLFW_KEY_HOME; + _glfw.wl.keycodes[KEY_END] = GLFW_KEY_END; + _glfw.wl.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; + _glfw.wl.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; + _glfw.wl.keycodes[KEY_INSERT] = GLFW_KEY_INSERT; + _glfw.wl.keycodes[KEY_LEFT] = GLFW_KEY_LEFT; + _glfw.wl.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT; + _glfw.wl.keycodes[KEY_DOWN] = GLFW_KEY_DOWN; + _glfw.wl.keycodes[KEY_UP] = GLFW_KEY_UP; + _glfw.wl.keycodes[KEY_F1] = GLFW_KEY_F1; + _glfw.wl.keycodes[KEY_F2] = GLFW_KEY_F2; + _glfw.wl.keycodes[KEY_F3] = GLFW_KEY_F3; + _glfw.wl.keycodes[KEY_F4] = GLFW_KEY_F4; + _glfw.wl.keycodes[KEY_F5] = GLFW_KEY_F5; + _glfw.wl.keycodes[KEY_F6] = GLFW_KEY_F6; + _glfw.wl.keycodes[KEY_F7] = GLFW_KEY_F7; + _glfw.wl.keycodes[KEY_F8] = GLFW_KEY_F8; + _glfw.wl.keycodes[KEY_F9] = GLFW_KEY_F9; + _glfw.wl.keycodes[KEY_F10] = GLFW_KEY_F10; + _glfw.wl.keycodes[KEY_F11] = GLFW_KEY_F11; + _glfw.wl.keycodes[KEY_F12] = GLFW_KEY_F12; + _glfw.wl.keycodes[KEY_F13] = GLFW_KEY_F13; + _glfw.wl.keycodes[KEY_F14] = GLFW_KEY_F14; + _glfw.wl.keycodes[KEY_F15] = GLFW_KEY_F15; + _glfw.wl.keycodes[KEY_F16] = GLFW_KEY_F16; + _glfw.wl.keycodes[KEY_F17] = GLFW_KEY_F17; + _glfw.wl.keycodes[KEY_F18] = GLFW_KEY_F18; + _glfw.wl.keycodes[KEY_F19] = GLFW_KEY_F19; + _glfw.wl.keycodes[KEY_F20] = GLFW_KEY_F20; + _glfw.wl.keycodes[KEY_F21] = GLFW_KEY_F21; + _glfw.wl.keycodes[KEY_F22] = GLFW_KEY_F22; + _glfw.wl.keycodes[KEY_F23] = GLFW_KEY_F23; + _glfw.wl.keycodes[KEY_F24] = GLFW_KEY_F24; + _glfw.wl.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; + _glfw.wl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; + _glfw.wl.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; + _glfw.wl.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; + _glfw.wl.keycodes[KEY_KP0] = GLFW_KEY_KP_0; + _glfw.wl.keycodes[KEY_KP1] = GLFW_KEY_KP_1; + _glfw.wl.keycodes[KEY_KP2] = GLFW_KEY_KP_2; + _glfw.wl.keycodes[KEY_KP3] = GLFW_KEY_KP_3; + _glfw.wl.keycodes[KEY_KP4] = GLFW_KEY_KP_4; + _glfw.wl.keycodes[KEY_KP5] = GLFW_KEY_KP_5; + _glfw.wl.keycodes[KEY_KP6] = GLFW_KEY_KP_6; + _glfw.wl.keycodes[KEY_KP7] = GLFW_KEY_KP_7; + _glfw.wl.keycodes[KEY_KP8] = GLFW_KEY_KP_8; + _glfw.wl.keycodes[KEY_KP9] = GLFW_KEY_KP_9; + _glfw.wl.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; + _glfw.wl.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; + _glfw.wl.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; + + for (scancode = 0; scancode < 256; scancode++) + { + if (_glfw.wl.keycodes[scancode] > 0) + _glfw.wl.scancodes[_glfw.wl.keycodes[scancode]] = scancode; + } } @@ -567,6 +1040,92 @@ static void createKeyTables(void) int _glfwPlatformInit(void) { + const char *cursorTheme; + const char *cursorSizeStr; + char *cursorSizeEnd; + long cursorSizeLong; + int cursorSize; + + _glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0"); + if (!_glfw.wl.cursor.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to open libwayland-cursor"); + return GLFW_FALSE; + } + + _glfw.wl.cursor.theme_load = (PFN_wl_cursor_theme_load) + _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_load"); + _glfw.wl.cursor.theme_destroy = (PFN_wl_cursor_theme_destroy) + _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_destroy"); + _glfw.wl.cursor.theme_get_cursor = (PFN_wl_cursor_theme_get_cursor) + _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_get_cursor"); + _glfw.wl.cursor.image_get_buffer = (PFN_wl_cursor_image_get_buffer) + _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_image_get_buffer"); + + _glfw.wl.egl.handle = _glfw_dlopen("libwayland-egl.so.1"); + if (!_glfw.wl.egl.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to open libwayland-egl"); + return GLFW_FALSE; + } + + _glfw.wl.egl.window_create = (PFN_wl_egl_window_create) + _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_create"); + _glfw.wl.egl.window_destroy = (PFN_wl_egl_window_destroy) + _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_destroy"); + _glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize) + _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize"); + + _glfw.wl.xkb.handle = _glfw_dlopen("libxkbcommon.so.0"); + if (!_glfw.wl.xkb.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to open libxkbcommon"); + return GLFW_FALSE; + } + + _glfw.wl.xkb.context_new = (PFN_xkb_context_new) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_new"); + _glfw.wl.xkb.context_unref = (PFN_xkb_context_unref) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_unref"); + _glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string"); + _glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref"); + _glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index"); + _glfw.wl.xkb.keymap_key_repeats = (PFN_xkb_keymap_key_repeats) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_key_repeats"); + _glfw.wl.xkb.state_new = (PFN_xkb_state_new) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_new"); + _glfw.wl.xkb.state_unref = (PFN_xkb_state_unref) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_unref"); + _glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms"); + _glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask"); + _glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods"); + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + _glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale"); + _glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref"); + _glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new"); + _glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref"); + _glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed"); + _glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status"); + _glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym"); +#endif + _glfw.wl.display = wl_display_connect(NULL); if (!_glfw.wl.display) { @@ -578,9 +1137,6 @@ int _glfwPlatformInit(void) _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL); - _glfw.wl.monitors = calloc(4, sizeof(_GLFWmonitor*)); - _glfw.wl.monitorsSize = 4; - createKeyTables(); _glfw.wl.xkb.context = xkb_context_new(0); @@ -597,25 +1153,59 @@ int _glfwPlatformInit(void) // Sync so we got all initial output events wl_display_roundtrip(_glfw.wl.display); - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - +#ifdef __linux__ if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; +#endif _glfwInitTimerPOSIX(); + _glfw.wl.timerfd = -1; + if (_glfw.wl.seatVersion >= 4) + _glfw.wl.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + if (_glfw.wl.pointer && _glfw.wl.shm) { - _glfw.wl.cursorTheme = wl_cursor_theme_load(NULL, 32, _glfw.wl.shm); + cursorTheme = getenv("XCURSOR_THEME"); + cursorSizeStr = getenv("XCURSOR_SIZE"); + cursorSize = 32; + if (cursorSizeStr) + { + errno = 0; + cursorSizeLong = strtol(cursorSizeStr, &cursorSizeEnd, 10); + if (!*cursorSizeEnd && !errno && cursorSizeLong > 0 && cursorSizeLong <= INT_MAX) + cursorSize = (int)cursorSizeLong; + } + _glfw.wl.cursorTheme = + wl_cursor_theme_load(cursorTheme, cursorSize, _glfw.wl.shm); if (!_glfw.wl.cursorTheme) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Unable to load default cursor theme\n"); + "Wayland: Unable to load default cursor theme"); return GLFW_FALSE; } + // If this happens to be NULL, we just fallback to the scale=1 version. + _glfw.wl.cursorThemeHiDPI = + wl_cursor_theme_load(cursorTheme, 2 * cursorSize, _glfw.wl.shm); _glfw.wl.cursorSurface = wl_compositor_create_surface(_glfw.wl.compositor); + _glfw.wl.cursorTimerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + } + + if (_glfw.wl.seat && _glfw.wl.dataDeviceManager) + { + _glfw.wl.dataDevice = + wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, + _glfw.wl.seat); + wl_data_device_add_listener(_glfw.wl.dataDevice, &dataDeviceListener, NULL); + _glfw.wl.clipboardString = malloc(4096); + if (!_glfw.wl.clipboardString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unable to allocate clipboard memory"); + return GLFW_FALSE; + } + _glfw.wl.clipboardSize = 4096; } return GLFW_TRUE; @@ -623,36 +1213,108 @@ int _glfwPlatformInit(void) void _glfwPlatformTerminate(void) { - _glfwTerminateEGL(); +#ifdef __linux__ _glfwTerminateJoysticksLinux(); - _glfwTerminateThreadLocalStoragePOSIX(); +#endif + _glfwTerminateEGL(); + if (_glfw.wl.egl.handle) + { + _glfw_dlclose(_glfw.wl.egl.handle); + _glfw.wl.egl.handle = NULL; + } + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + if (_glfw.wl.xkb.composeState) + xkb_compose_state_unref(_glfw.wl.xkb.composeState); +#endif + if (_glfw.wl.xkb.keymap) + xkb_keymap_unref(_glfw.wl.xkb.keymap); + if (_glfw.wl.xkb.state) + xkb_state_unref(_glfw.wl.xkb.state); + if (_glfw.wl.xkb.context) + xkb_context_unref(_glfw.wl.xkb.context); + if (_glfw.wl.xkb.handle) + { + _glfw_dlclose(_glfw.wl.xkb.handle); + _glfw.wl.xkb.handle = NULL; + } if (_glfw.wl.cursorTheme) wl_cursor_theme_destroy(_glfw.wl.cursorTheme); + if (_glfw.wl.cursorThemeHiDPI) + wl_cursor_theme_destroy(_glfw.wl.cursorThemeHiDPI); + if (_glfw.wl.cursor.handle) + { + _glfw_dlclose(_glfw.wl.cursor.handle); + _glfw.wl.cursor.handle = NULL; + } + if (_glfw.wl.cursorSurface) wl_surface_destroy(_glfw.wl.cursorSurface); + if (_glfw.wl.subcompositor) + wl_subcompositor_destroy(_glfw.wl.subcompositor); + if (_glfw.wl.compositor) + wl_compositor_destroy(_glfw.wl.compositor); + if (_glfw.wl.shm) + wl_shm_destroy(_glfw.wl.shm); + if (_glfw.wl.shell) + wl_shell_destroy(_glfw.wl.shell); + if (_glfw.wl.viewporter) + wp_viewporter_destroy(_glfw.wl.viewporter); + if (_glfw.wl.decorationManager) + zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager); + if (_glfw.wl.wmBase) + xdg_wm_base_destroy(_glfw.wl.wmBase); + if (_glfw.wl.dataSource) + wl_data_source_destroy(_glfw.wl.dataSource); + if (_glfw.wl.dataDevice) + wl_data_device_destroy(_glfw.wl.dataDevice); + if (_glfw.wl.dataOffer) + wl_data_offer_destroy(_glfw.wl.dataOffer); + if (_glfw.wl.dataDeviceManager) + wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager); + if (_glfw.wl.pointer) + wl_pointer_destroy(_glfw.wl.pointer); + if (_glfw.wl.keyboard) + wl_keyboard_destroy(_glfw.wl.keyboard); + if (_glfw.wl.seat) + wl_seat_destroy(_glfw.wl.seat); + if (_glfw.wl.relativePointerManager) + zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager); + if (_glfw.wl.pointerConstraints) + zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints); + if (_glfw.wl.idleInhibitManager) + zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager); if (_glfw.wl.registry) wl_registry_destroy(_glfw.wl.registry); if (_glfw.wl.display) + { wl_display_flush(_glfw.wl.display); - if (_glfw.wl.display) wl_display_disconnect(_glfw.wl.display); + } + + if (_glfw.wl.timerfd >= 0) + close(_glfw.wl.timerfd); + if (_glfw.wl.cursorTimerfd >= 0) + close(_glfw.wl.cursorTimerfd); + + if (_glfw.wl.clipboardString) + free(_glfw.wl.clipboardString); + if (_glfw.wl.clipboardSendString) + free(_glfw.wl.clipboardSendString); } const char* _glfwPlatformGetVersionString(void) { - return _GLFW_VERSION_NUMBER " Wayland EGL" + return _GLFW_VERSION_NUMBER " Wayland EGL OSMesa" #if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) " clock_gettime" #else " gettimeofday" #endif -#if defined(__linux__) - " /dev/js" -#endif + " evdev" #if defined(_GLFW_BUILD_DLL) " shared" #endif ; } - diff --git a/external/glfw/wl_monitor.c b/external/glfw/wl_monitor.c index d6a5a3f..223c3b8 100644 --- a/external/glfw/wl_monitor.c +++ b/external/glfw/wl_monitor.c @@ -30,83 +30,79 @@ #include #include #include +#include -struct _GLFWvidmodeWayland -{ - GLFWvidmode base; - uint32_t flags; -}; - -static void geometry(void* data, - struct wl_output* output, - int32_t x, - int32_t y, - int32_t physicalWidth, - int32_t physicalHeight, - int32_t subpixel, - const char* make, - const char* model, - int32_t transform) +static void outputHandleGeometry(void* data, + struct wl_output* output, + int32_t x, + int32_t y, + int32_t physicalWidth, + int32_t physicalHeight, + int32_t subpixel, + const char* make, + const char* model, + int32_t transform) { struct _GLFWmonitor *monitor = data; + char name[1024]; monitor->wl.x = x; monitor->wl.y = y; monitor->widthMM = physicalWidth; monitor->heightMM = physicalHeight; + + snprintf(name, sizeof(name), "%s %s", make, model); + monitor->name = _glfw_strdup(name); } -static void mode(void* data, - struct wl_output* output, - uint32_t flags, - int32_t width, - int32_t height, - int32_t refresh) +static void outputHandleMode(void* data, + struct wl_output* output, + uint32_t flags, + int32_t width, + int32_t height, + int32_t refresh) { struct _GLFWmonitor *monitor = data; - _GLFWvidmodeWayland mode = { { 0 }, }; + GLFWvidmode mode; - mode.base.width = width; - mode.base.height = height; - mode.base.refreshRate = refresh / 1000; - mode.flags = flags; + mode.width = width; + mode.height = height; + mode.redBits = 8; + mode.greenBits = 8; + mode.blueBits = 8; + mode.refreshRate = (int) round(refresh / 1000.0); - if (monitor->wl.modesCount + 1 >= monitor->wl.modesSize) - { - int size = monitor->wl.modesSize * 2; - _GLFWvidmodeWayland* modes = - realloc(monitor->wl.modes, - size * sizeof(_GLFWvidmodeWayland)); - monitor->wl.modes = modes; - monitor->wl.modesSize = size; - } + monitor->modeCount++; + monitor->modes = + realloc(monitor->modes, monitor->modeCount * sizeof(GLFWvidmode)); + monitor->modes[monitor->modeCount - 1] = mode; - monitor->wl.modes[monitor->wl.modesCount++] = mode; + if (flags & WL_OUTPUT_MODE_CURRENT) + monitor->wl.currentMode = monitor->modeCount - 1; } -static void done(void* data, - struct wl_output* output) +static void outputHandleDone(void* data, struct wl_output* output) { struct _GLFWmonitor *monitor = data; - monitor->wl.done = GLFW_TRUE; + _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); } -static void scale(void* data, - struct wl_output* output, - int32_t factor) +static void outputHandleScale(void* data, + struct wl_output* output, + int32_t factor) { struct _GLFWmonitor *monitor = data; monitor->wl.scale = factor; } -static const struct wl_output_listener output_listener = { - geometry, - mode, - done, - scale, +static const struct wl_output_listener outputListener = { + outputHandleGeometry, + outputHandleMode, + outputHandleDone, + outputHandleScale, }; @@ -118,10 +114,6 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) { _GLFWmonitor *monitor; struct wl_output *output; - char name_str[80]; - - memset(name_str, 0, sizeof(name_str)); - snprintf(name_str, 79, "wl_output@%u", name); if (version < 2) { @@ -130,7 +122,8 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) return; } - monitor = _glfwAllocMonitor(name_str, 0, 0); + // The actual name of this output will be set in the geometry handler. + monitor = _glfwAllocMonitor(NULL, 0, 0); output = wl_registry_bind(_glfw.wl.registry, name, @@ -142,26 +135,11 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) return; } - monitor->wl.modes = calloc(4, sizeof(_GLFWvidmodeWayland)); - monitor->wl.modesSize = 4; - monitor->wl.scale = 1; - monitor->wl.output = output; - wl_output_add_listener(output, &output_listener, monitor); + monitor->wl.name = name; - if (_glfw.wl.monitorsCount + 1 >= _glfw.wl.monitorsSize) - { - _GLFWmonitor** monitors = _glfw.wl.monitors; - int size = _glfw.wl.monitorsSize * 2; - - monitors = realloc(monitors, size * sizeof(_GLFWmonitor*)); - - _glfw.wl.monitors = monitors; - _glfw.wl.monitorsSize = size; - } - - _glfw.wl.monitors[_glfw.wl.monitorsCount++] = monitor; + wl_output_add_listener(output, &outputListener, monitor); } @@ -169,40 +147,10 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -_GLFWmonitor** _glfwPlatformGetMonitors(int* count) +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) { - _GLFWmonitor** monitors; - _GLFWmonitor* monitor; - int i, monitorsCount = _glfw.wl.monitorsCount; - - if (_glfw.wl.monitorsCount == 0) - goto err; - - monitors = calloc(monitorsCount, sizeof(_GLFWmonitor*)); - - for (i = 0; i < monitorsCount; i++) - { - _GLFWmonitor* origMonitor = _glfw.wl.monitors[i]; - monitor = calloc(1, sizeof(_GLFWmonitor)); - - monitor->modes = - _glfwPlatformGetVideoModes(origMonitor, - &origMonitor->wl.modesCount); - *monitor = *_glfw.wl.monitors[i]; - monitors[i] = monitor; - } - - *count = monitorsCount; - return monitors; - -err: - *count = 0; - return NULL; -} - -GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) -{ - return first->wl.output == second->wl.output; + if (monitor->wl.output) + wl_output_destroy(monitor->wl.output); } void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) @@ -213,46 +161,52 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = monitor->wl.y; } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = (float) monitor->wl.scale; + if (yscale) + *yscale = (float) monitor->wl.scale; +} + +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) +{ + if (xpos) + *xpos = monitor->wl.x; + if (ypos) + *ypos = monitor->wl.y; + if (width) + *width = monitor->modes[monitor->wl.currentMode].width; + if (height) + *height = monitor->modes[monitor->wl.currentMode].height; +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) { - GLFWvidmode *modes; - int i, modesCount = monitor->wl.modesCount; - - modes = calloc(modesCount, sizeof(GLFWvidmode)); - - for (i = 0; i < modesCount; i++) - modes[i] = monitor->wl.modes[i].base; - - *found = modesCount; - return modes; + *found = monitor->modeCount; + return monitor->modes; } void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) { - int i; - - for (i = 0; i < monitor->wl.modesCount; i++) - { - if (monitor->wl.modes[i].flags & WL_OUTPUT_MODE_CURRENT) - { - *mode = monitor->wl.modes[i].base; - return; - } - } + *mode = monitor->modes[monitor->wl.currentMode]; } -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { - // TODO _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Gamma ramp getting not supported yet"); + "Wayland: Gamma ramp access it not available"); + return GLFW_FALSE; } -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, + const GLFWgammaramp* ramp) { - // TODO _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Gamma ramp setting not supported yet"); + "Wayland: Gamma ramp access is not available"); } diff --git a/external/glfw/wl_platform.h b/external/glfw/wl_platform.h index e2d3d43..9fef848 100644 --- a/external/glfw/wl_platform.h +++ b/external/glfw/wl_platform.h @@ -24,11 +24,11 @@ // //======================================================================== -#ifndef _glfw3_wayland_platform_h_ -#define _glfw3_wayland_platform_h_ - #include #include +#ifdef HAVE_XKBCOMMON_COMPOSE_H +#include +#endif #include typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; @@ -45,14 +45,23 @@ typedef struct VkWaylandSurfaceCreateInfoKHR typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWaylandSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*); -#include "posix_tls.h" +#include "posix_thread.h" #include "posix_time.h" +#ifdef __linux__ #include "linux_joystick.h" +#else +#include "null_joystick.h" +#endif #include "xkb_unicode.h" #include "egl_context.h" +#include "osmesa_context.h" +#include "wayland-xdg-shell-client-protocol.h" +#include "wayland-xdg-decoration-client-protocol.h" +#include "wayland-viewporter-client-protocol.h" #include "wayland-relative-pointer-unstable-v1-client-protocol.h" #include "wayland-pointer-constraints-unstable-v1-client-protocol.h" +#include "wayland-idle-inhibit-unstable-v1-client-protocol.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) @@ -69,10 +78,96 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR #define _GLFW_PLATFORM_CONTEXT_STATE #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE +struct wl_cursor_image { + uint32_t width; + uint32_t height; + uint32_t hotspot_x; + uint32_t hotspot_y; + uint32_t delay; +}; +struct wl_cursor { + unsigned int image_count; + struct wl_cursor_image** images; + char* name; +}; +typedef struct wl_cursor_theme* (* PFN_wl_cursor_theme_load)(const char*, int, struct wl_shm*); +typedef void (* PFN_wl_cursor_theme_destroy)(struct wl_cursor_theme*); +typedef struct wl_cursor* (* PFN_wl_cursor_theme_get_cursor)(struct wl_cursor_theme*, const char*); +typedef struct wl_buffer* (* PFN_wl_cursor_image_get_buffer)(struct wl_cursor_image*); +#define wl_cursor_theme_load _glfw.wl.cursor.theme_load +#define wl_cursor_theme_destroy _glfw.wl.cursor.theme_destroy +#define wl_cursor_theme_get_cursor _glfw.wl.cursor.theme_get_cursor +#define wl_cursor_image_get_buffer _glfw.wl.cursor.image_get_buffer -// Wayland-specific video mode data -// -typedef struct _GLFWvidmodeWayland _GLFWvidmodeWayland; +typedef struct wl_egl_window* (* PFN_wl_egl_window_create)(struct wl_surface*, int, int); +typedef void (* PFN_wl_egl_window_destroy)(struct wl_egl_window*); +typedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int, int); +#define wl_egl_window_create _glfw.wl.egl.window_create +#define wl_egl_window_destroy _glfw.wl.egl.window_destroy +#define wl_egl_window_resize _glfw.wl.egl.window_resize + +typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags); +typedef void (* PFN_xkb_context_unref)(struct xkb_context*); +typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags); +typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*); +typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*); +typedef int (* PFN_xkb_keymap_key_repeats)(struct xkb_keymap*, xkb_keycode_t); +typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*); +typedef void (* PFN_xkb_state_unref)(struct xkb_state*); +typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**); +typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t); +typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum xkb_state_component); +#define xkb_context_new _glfw.wl.xkb.context_new +#define xkb_context_unref _glfw.wl.xkb.context_unref +#define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string +#define xkb_keymap_unref _glfw.wl.xkb.keymap_unref +#define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index +#define xkb_keymap_key_repeats _glfw.wl.xkb.keymap_key_repeats +#define xkb_state_new _glfw.wl.xkb.state_new +#define xkb_state_unref _glfw.wl.xkb.state_unref +#define xkb_state_key_get_syms _glfw.wl.xkb.state_key_get_syms +#define xkb_state_update_mask _glfw.wl.xkb.state_update_mask +#define xkb_state_serialize_mods _glfw.wl.xkb.state_serialize_mods + +#ifdef HAVE_XKBCOMMON_COMPOSE_H +typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags); +typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*); +typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags); +typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*); +typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t); +typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*); +typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_state*); +#define xkb_compose_table_new_from_locale _glfw.wl.xkb.compose_table_new_from_locale +#define xkb_compose_table_unref _glfw.wl.xkb.compose_table_unref +#define xkb_compose_state_new _glfw.wl.xkb.compose_state_new +#define xkb_compose_state_unref _glfw.wl.xkb.compose_state_unref +#define xkb_compose_state_feed _glfw.wl.xkb.compose_state_feed +#define xkb_compose_state_get_status _glfw.wl.xkb.compose_state_get_status +#define xkb_compose_state_get_one_sym _glfw.wl.xkb.compose_state_get_one_sym +#endif + +#define _GLFW_DECORATION_WIDTH 4 +#define _GLFW_DECORATION_TOP 24 +#define _GLFW_DECORATION_VERTICAL (_GLFW_DECORATION_TOP + _GLFW_DECORATION_WIDTH) +#define _GLFW_DECORATION_HORIZONTAL (2 * _GLFW_DECORATION_WIDTH) + +typedef enum _GLFWdecorationSideWayland +{ + mainWindow, + topDecoration, + leftDecoration, + rightDecoration, + bottomDecoration, + +} _GLFWdecorationSideWayland; + +typedef struct _GLFWdecorationWayland +{ + struct wl_surface* surface; + struct wl_subsurface* subsurface; + struct wp_viewport* viewport; + +} _GLFWdecorationWayland; // Wayland-specific per-window data // @@ -81,11 +176,19 @@ typedef struct _GLFWwindowWayland int width, height; GLFWbool visible; GLFWbool maximized; + GLFWbool hovered; + GLFWbool transparent; struct wl_surface* surface; struct wl_egl_window* native; - struct wl_shell_surface* shell_surface; + struct wl_shell_surface* shellSurface; struct wl_callback* callback; + struct { + struct xdg_surface* surface; + struct xdg_toplevel* toplevel; + struct zxdg_toplevel_decoration_v1* decoration; + } xdg; + _GLFWcursor* currentCursor; double cursorPosX, cursorPosY; @@ -102,6 +205,18 @@ typedef struct _GLFWwindowWayland struct zwp_relative_pointer_v1* relativePointer; struct zwp_locked_pointer_v1* lockedPointer; } pointerLock; + + struct zwp_idle_inhibitor_v1* idleInhibitor; + + GLFWbool wasFullscreen; + + struct { + GLFWbool serverSide; + struct wl_buffer* buffer; + _GLFWdecorationWayland top, left, right, bottom; + int focus; + } decorations; + } _GLFWwindowWayland; // Wayland-specific global data @@ -111,40 +226,105 @@ typedef struct _GLFWlibraryWayland struct wl_display* display; struct wl_registry* registry; struct wl_compositor* compositor; + struct wl_subcompositor* subcompositor; struct wl_shell* shell; struct wl_shm* shm; struct wl_seat* seat; struct wl_pointer* pointer; struct wl_keyboard* keyboard; + struct wl_data_device_manager* dataDeviceManager; + struct wl_data_device* dataDevice; + struct wl_data_offer* dataOffer; + struct wl_data_source* dataSource; + struct xdg_wm_base* wmBase; + struct zxdg_decoration_manager_v1* decorationManager; + struct wp_viewporter* viewporter; struct zwp_relative_pointer_manager_v1* relativePointerManager; struct zwp_pointer_constraints_v1* pointerConstraints; + struct zwp_idle_inhibit_manager_v1* idleInhibitManager; - int wl_compositor_version; + int compositorVersion; + int seatVersion; struct wl_cursor_theme* cursorTheme; + struct wl_cursor_theme* cursorThemeHiDPI; struct wl_surface* cursorSurface; - uint32_t pointerSerial; + int cursorTimerfd; + uint32_t serial; - _GLFWmonitor** monitors; - int monitorsCount; - int monitorsSize; - - short int publicKeys[256]; + int32_t keyboardRepeatRate; + int32_t keyboardRepeatDelay; + int keyboardLastKey; + int keyboardLastScancode; + char* clipboardString; + size_t clipboardSize; + char* clipboardSendString; + size_t clipboardSendSize; + int timerfd; + short int keycodes[256]; + short int scancodes[GLFW_KEY_LAST + 1]; struct { + void* handle; struct xkb_context* context; struct xkb_keymap* keymap; struct xkb_state* state; - xkb_mod_mask_t control_mask; - xkb_mod_mask_t alt_mask; - xkb_mod_mask_t shift_mask; - xkb_mod_mask_t super_mask; + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + struct xkb_compose_state* composeState; +#endif + + xkb_mod_mask_t controlMask; + xkb_mod_mask_t altMask; + xkb_mod_mask_t shiftMask; + xkb_mod_mask_t superMask; + xkb_mod_mask_t capsLockMask; + xkb_mod_mask_t numLockMask; unsigned int modifiers; + + PFN_xkb_context_new context_new; + PFN_xkb_context_unref context_unref; + PFN_xkb_keymap_new_from_string keymap_new_from_string; + PFN_xkb_keymap_unref keymap_unref; + PFN_xkb_keymap_mod_get_index keymap_mod_get_index; + PFN_xkb_keymap_key_repeats keymap_key_repeats; + PFN_xkb_state_new state_new; + PFN_xkb_state_unref state_unref; + PFN_xkb_state_key_get_syms state_key_get_syms; + PFN_xkb_state_update_mask state_update_mask; + PFN_xkb_state_serialize_mods state_serialize_mods; + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale; + PFN_xkb_compose_table_unref compose_table_unref; + PFN_xkb_compose_state_new compose_state_new; + PFN_xkb_compose_state_unref compose_state_unref; + PFN_xkb_compose_state_feed compose_state_feed; + PFN_xkb_compose_state_get_status compose_state_get_status; + PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym; +#endif } xkb; _GLFWwindow* pointerFocus; _GLFWwindow* keyboardFocus; + struct { + void* handle; + + PFN_wl_cursor_theme_load theme_load; + PFN_wl_cursor_theme_destroy theme_destroy; + PFN_wl_cursor_theme_get_cursor theme_get_cursor; + PFN_wl_cursor_image_get_buffer image_get_buffer; + } cursor; + + struct { + void* handle; + + PFN_wl_egl_window_create window_create; + PFN_wl_egl_window_destroy window_destroy; + PFN_wl_egl_window_resize window_resize; + } egl; + } _GLFWlibraryWayland; // Wayland-specific per-monitor data @@ -152,28 +332,27 @@ typedef struct _GLFWlibraryWayland typedef struct _GLFWmonitorWayland { struct wl_output* output; - - _GLFWvidmodeWayland* modes; - int modesCount; - int modesSize; - GLFWbool done; + int name; + int currentMode; int x; int y; int scale; + } _GLFWmonitorWayland; // Wayland-specific per-cursor data // typedef struct _GLFWcursorWayland { - struct wl_cursor_image* image; + struct wl_cursor* cursor; + struct wl_cursor* cursorHiDPI; struct wl_buffer* buffer; int width, height; int xhot, yhot; + int currentImage; } _GLFWcursorWayland; void _glfwAddOutputWayland(uint32_t name, uint32_t version); -#endif // _glfw3_wayland_platform_h_ diff --git a/external/glfw/wl_window.c b/external/glfw/wl_window.c index d633c66..0ae712d 100644 --- a/external/glfw/wl_window.c +++ b/external/glfw/wl_window.c @@ -35,24 +35,22 @@ #include #include #include +#include #include -#include -#include - -static void handlePing(void* data, - struct wl_shell_surface* shellSurface, - uint32_t serial) +static void shellSurfaceHandlePing(void* data, + struct wl_shell_surface* shellSurface, + uint32_t serial) { wl_shell_surface_pong(shellSurface, serial); } -static void handleConfigure(void* data, - struct wl_shell_surface* shellSurface, - uint32_t edges, - int32_t width, - int32_t height) +static void shellSurfaceHandleConfigure(void* data, + struct wl_shell_surface* shellSurface, + uint32_t edges, + int32_t width, + int32_t height) { _GLFWwindow* window = data; float aspectRatio; @@ -60,6 +58,16 @@ static void handleConfigure(void* data, if (!window->monitor) { + if (_glfw.wl.viewporter && window->decorated) + { + width -= _GLFW_DECORATION_HORIZONTAL; + height -= _GLFW_DECORATION_VERTICAL; + } + if (width < 1) + width = 1; + if (height < 1) + height = 1; + if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) { aspectRatio = (float)width / (float)height; @@ -86,26 +94,320 @@ static void handleConfigure(void* data, _glfwInputWindowDamage(window); } -static void handlePopupDone(void* data, - struct wl_shell_surface* shellSurface) +static void shellSurfaceHandlePopupDone(void* data, + struct wl_shell_surface* shellSurface) { } static const struct wl_shell_surface_listener shellSurfaceListener = { - handlePing, - handleConfigure, - handlePopupDone + shellSurfaceHandlePing, + shellSurfaceHandleConfigure, + shellSurfaceHandlePopupDone }; +static int createTmpfileCloexec(char* tmpname) +{ + int fd; + + fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) + unlink(tmpname); + + return fd; +} + +/* + * Create a new, unique, anonymous file of the given size, and + * return the file descriptor for it. The file descriptor is set + * CLOEXEC. The file is immediately suitable for mmap()'ing + * the given size at offset zero. + * + * The file should not have a permanent backing store like a disk, + * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. + * + * The file name is deleted from the file system. + * + * The file is suitable for buffer sharing between processes by + * transmitting the file descriptor over Unix sockets using the + * SCM_RIGHTS methods. + * + * posix_fallocate() is used to guarantee that disk space is available + * for the file at the given size. If disk space is insufficent, errno + * is set to ENOSPC. If posix_fallocate() is not supported, program may + * receive SIGBUS on accessing mmap()'ed file contents instead. + */ +static int createAnonymousFile(off_t size) +{ + static const char template[] = "/glfw-shared-XXXXXX"; + const char* path; + char* name; + int fd; + int ret; + +#ifdef HAVE_MEMFD_CREATE + fd = memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0) + { + // We can add this seal before calling posix_fallocate(), as the file + // is currently zero-sized anyway. + // + // There is also no need to check for the return value, we couldn’t do + // anything with it anyway. + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + } + else +#elif defined(SHM_ANON) + fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600); + if (fd < 0) +#endif + { + path = getenv("XDG_RUNTIME_DIR"); + if (!path) + { + errno = ENOENT; + return -1; + } + + name = calloc(strlen(path) + sizeof(template), 1); + strcpy(name, path); + strcat(name, template); + + fd = createTmpfileCloexec(name); + free(name); + if (fd < 0) + return -1; + } + +#if defined(SHM_ANON) + // posix_fallocate does not work on SHM descriptors + ret = ftruncate(fd, size); +#else + ret = posix_fallocate(fd, 0, size); +#endif + if (ret != 0) + { + close(fd); + errno = ret; + return -1; + } + return fd; +} + +static struct wl_buffer* createShmBuffer(const GLFWimage* image) +{ + struct wl_shm_pool* pool; + struct wl_buffer* buffer; + int stride = image->width * 4; + int length = image->width * image->height * 4; + void* data; + int fd, i; + + fd = createAnonymousFile(length); + if (fd < 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Creating a buffer file for %d B failed: %m", + length); + return NULL; + } + + data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: mmap failed: %m"); + close(fd); + return NULL; + } + + pool = wl_shm_create_pool(_glfw.wl.shm, fd, length); + + close(fd); + unsigned char* source = (unsigned char*) image->pixels; + unsigned char* target = data; + for (i = 0; i < image->width * image->height; i++, source += 4) + { + unsigned int alpha = source[3]; + + *target++ = (unsigned char) ((source[2] * alpha) / 255); + *target++ = (unsigned char) ((source[1] * alpha) / 255); + *target++ = (unsigned char) ((source[0] * alpha) / 255); + *target++ = (unsigned char) alpha; + } + + buffer = + wl_shm_pool_create_buffer(pool, 0, + image->width, + image->height, + stride, WL_SHM_FORMAT_ARGB8888); + munmap(data, length); + wl_shm_pool_destroy(pool); + + return buffer; +} + +static void createDecoration(_GLFWdecorationWayland* decoration, + struct wl_surface* parent, + struct wl_buffer* buffer, GLFWbool opaque, + int x, int y, + int width, int height) +{ + struct wl_region* region; + + decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor); + decoration->subsurface = + wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, + decoration->surface, parent); + wl_subsurface_set_position(decoration->subsurface, x, y); + decoration->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter, + decoration->surface); + wp_viewport_set_destination(decoration->viewport, width, height); + wl_surface_attach(decoration->surface, buffer, 0, 0); + + if (opaque) + { + region = wl_compositor_create_region(_glfw.wl.compositor); + wl_region_add(region, 0, 0, width, height); + wl_surface_set_opaque_region(decoration->surface, region); + wl_surface_commit(decoration->surface); + wl_region_destroy(region); + } + else + wl_surface_commit(decoration->surface); +} + +static void createDecorations(_GLFWwindow* window) +{ + unsigned char data[] = { 224, 224, 224, 255 }; + const GLFWimage image = { 1, 1, data }; + GLFWbool opaque = (data[3] == 255); + + if (!_glfw.wl.viewporter || !window->decorated || window->wl.decorations.serverSide) + return; + + if (!window->wl.decorations.buffer) + window->wl.decorations.buffer = createShmBuffer(&image); + if (!window->wl.decorations.buffer) + return; + + createDecoration(&window->wl.decorations.top, window->wl.surface, + window->wl.decorations.buffer, opaque, + 0, -_GLFW_DECORATION_TOP, + window->wl.width, _GLFW_DECORATION_TOP); + createDecoration(&window->wl.decorations.left, window->wl.surface, + window->wl.decorations.buffer, opaque, + -_GLFW_DECORATION_WIDTH, -_GLFW_DECORATION_TOP, + _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); + createDecoration(&window->wl.decorations.right, window->wl.surface, + window->wl.decorations.buffer, opaque, + window->wl.width, -_GLFW_DECORATION_TOP, + _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); + createDecoration(&window->wl.decorations.bottom, window->wl.surface, + window->wl.decorations.buffer, opaque, + -_GLFW_DECORATION_WIDTH, window->wl.height, + window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH); +} + +static void destroyDecoration(_GLFWdecorationWayland* decoration) +{ + if (decoration->surface) + wl_surface_destroy(decoration->surface); + if (decoration->subsurface) + wl_subsurface_destroy(decoration->subsurface); + if (decoration->viewport) + wp_viewport_destroy(decoration->viewport); + decoration->surface = NULL; + decoration->subsurface = NULL; + decoration->viewport = NULL; +} + +static void destroyDecorations(_GLFWwindow* window) +{ + destroyDecoration(&window->wl.decorations.top); + destroyDecoration(&window->wl.decorations.left); + destroyDecoration(&window->wl.decorations.right); + destroyDecoration(&window->wl.decorations.bottom); +} + +static void xdgDecorationHandleConfigure(void* data, + struct zxdg_toplevel_decoration_v1* decoration, + uint32_t mode) +{ + _GLFWwindow* window = data; + + window->wl.decorations.serverSide = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + + if (!window->wl.decorations.serverSide) + createDecorations(window); +} + +static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = { + xdgDecorationHandleConfigure, +}; + +// Makes the surface considered as XRGB instead of ARGB. +static void setOpaqueRegion(_GLFWwindow* window) +{ + struct wl_region* region; + + region = wl_compositor_create_region(_glfw.wl.compositor); + if (!region) + return; + + wl_region_add(region, 0, 0, window->wl.width, window->wl.height); + wl_surface_set_opaque_region(window->wl.surface, region); + wl_surface_commit(window->wl.surface); + wl_region_destroy(region); +} + + +static void resizeWindow(_GLFWwindow* window) +{ + int scale = window->wl.scale; + int scaledWidth = window->wl.width * scale; + int scaledHeight = window->wl.height * scale; + wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); + if (!window->wl.transparent) + setOpaqueRegion(window); + _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); + _glfwInputWindowContentScale(window, scale, scale); + + if (!window->wl.decorations.top.surface) + return; + + // Top decoration. + wp_viewport_set_destination(window->wl.decorations.top.viewport, + window->wl.width, _GLFW_DECORATION_TOP); + wl_surface_commit(window->wl.decorations.top.surface); + + // Left decoration. + wp_viewport_set_destination(window->wl.decorations.left.viewport, + _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); + wl_surface_commit(window->wl.decorations.left.surface); + + // Right decoration. + wl_subsurface_set_position(window->wl.decorations.right.subsurface, + window->wl.width, -_GLFW_DECORATION_TOP); + wp_viewport_set_destination(window->wl.decorations.right.viewport, + _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); + wl_surface_commit(window->wl.decorations.right.surface); + + // Bottom decoration. + wl_subsurface_set_position(window->wl.decorations.bottom.subsurface, + -_GLFW_DECORATION_WIDTH, window->wl.height); + wp_viewport_set_destination(window->wl.decorations.bottom.viewport, + window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH); + wl_surface_commit(window->wl.decorations.bottom.surface); +} + static void checkScaleChange(_GLFWwindow* window) { - int scaledWidth, scaledHeight; int scale = 1; int i; int monitorScale; // Check if we will be able to set the buffer scale or not. - if (_glfw.wl.wl_compositor_version < 3) + if (_glfw.wl.compositorVersion < 3) return; // Get the scale factor from the highest scale monitor. @@ -120,17 +422,14 @@ static void checkScaleChange(_GLFWwindow* window) if (scale != window->wl.scale) { window->wl.scale = scale; - scaledWidth = window->wl.width * scale; - scaledHeight = window->wl.height * scale; wl_surface_set_buffer_scale(window->wl.surface, scale); - wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); - _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); + resizeWindow(window); } } -static void handleEnter(void *data, - struct wl_surface *surface, - struct wl_output *output) +static void surfaceHandleEnter(void *data, + struct wl_surface *surface, + struct wl_output *output) { _GLFWwindow* window = data; _GLFWmonitor* monitor = wl_output_get_user_data(output); @@ -148,9 +447,9 @@ static void handleEnter(void *data, checkScaleChange(window); } -static void handleLeave(void *data, - struct wl_surface *surface, - struct wl_output *output) +static void surfaceHandleLeave(void *data, + struct wl_surface *surface, + struct wl_output *output) { _GLFWwindow* window = data; _GLFWmonitor* monitor = wl_output_get_user_data(output); @@ -170,23 +469,26 @@ static void handleLeave(void *data, } static const struct wl_surface_listener surfaceListener = { - handleEnter, - handleLeave + surfaceHandleEnter, + surfaceHandleLeave }; -// Makes the surface considered as XRGB instead of ARGB. -static void setOpaqueRegion(_GLFWwindow* window) +static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable) { - struct wl_region* region; - - region = wl_compositor_create_region(_glfw.wl.compositor); - if (!region) - return; - - wl_region_add(region, 0, 0, window->wl.width, window->wl.height); - wl_surface_set_opaque_region(window->wl.surface, region); - wl_surface_commit(window->wl.surface); - wl_region_destroy(region); + if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager) + { + window->wl.idleInhibitor = + zwp_idle_inhibit_manager_v1_create_inhibitor( + _glfw.wl.idleInhibitManager, window->wl.surface); + if (!window->wl.idleInhibitor) + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Idle inhibitor creation failed"); + } + else if (!enable && window->wl.idleInhibitor) + { + zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); + window->wl.idleInhibitor = NULL; + } } static GLFWbool createSurface(_GLFWwindow* window, @@ -212,65 +514,323 @@ static GLFWbool createSurface(_GLFWwindow* window, window->wl.height = wndconfig->height; window->wl.scale = 1; - // TODO: make this optional once issue #197 is fixed. - setOpaqueRegion(window); + if (!window->wl.transparent) + setOpaqueRegion(window); return GLFW_TRUE; } +static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, + int refreshRate) +{ + if (window->wl.xdg.toplevel) + { + xdg_toplevel_set_fullscreen( + window->wl.xdg.toplevel, + monitor->wl.output); + } + else if (window->wl.shellSurface) + { + wl_shell_surface_set_fullscreen( + window->wl.shellSurface, + WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, + refreshRate * 1000, // Convert Hz to mHz. + monitor->wl.output); + } + setIdleInhibitor(window, GLFW_TRUE); + if (!window->wl.decorations.serverSide) + destroyDecorations(window); +} + static GLFWbool createShellSurface(_GLFWwindow* window) { - window->wl.shell_surface = wl_shell_get_shell_surface(_glfw.wl.shell, - window->wl.surface); - if (!window->wl.shell_surface) + if (!_glfw.wl.shell) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: wl_shell protocol not available"); return GLFW_FALSE; + } - wl_shell_surface_add_listener(window->wl.shell_surface, + window->wl.shellSurface = wl_shell_get_shell_surface(_glfw.wl.shell, + window->wl.surface); + if (!window->wl.shellSurface) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Shell surface creation failed"); + return GLFW_FALSE; + } + + wl_shell_surface_add_listener(window->wl.shellSurface, &shellSurfaceListener, window); if (window->wl.title) - wl_shell_surface_set_title(window->wl.shell_surface, window->wl.title); + wl_shell_surface_set_title(window->wl.shellSurface, window->wl.title); if (window->monitor) { - wl_shell_surface_set_fullscreen( - window->wl.shell_surface, - WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, - 0, - window->monitor->wl.output); + setFullscreen(window, window->monitor, 0); } else if (window->wl.maximized) { - wl_shell_surface_set_maximized(window->wl.shell_surface, NULL); + wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); + setIdleInhibitor(window, GLFW_FALSE); + createDecorations(window); } else { - wl_shell_surface_set_toplevel(window->wl.shell_surface); + wl_shell_surface_set_toplevel(window->wl.shellSurface); + setIdleInhibitor(window, GLFW_FALSE); + createDecorations(window); } + wl_surface_commit(window->wl.surface); + return GLFW_TRUE; } -static int -createTmpfileCloexec(char* tmpname) +static void xdgToplevelHandleConfigure(void* data, + struct xdg_toplevel* toplevel, + int32_t width, + int32_t height, + struct wl_array* states) { - int fd; + _GLFWwindow* window = data; + float aspectRatio; + float targetRatio; + uint32_t* state; + GLFWbool maximized = GLFW_FALSE; + GLFWbool fullscreen = GLFW_FALSE; + GLFWbool activated = GLFW_FALSE; - fd = mkostemp(tmpname, O_CLOEXEC); - if (fd >= 0) - unlink(tmpname); + wl_array_for_each(state, states) + { + switch (*state) + { + case XDG_TOPLEVEL_STATE_MAXIMIZED: + maximized = GLFW_TRUE; + break; + case XDG_TOPLEVEL_STATE_FULLSCREEN: + fullscreen = GLFW_TRUE; + break; + case XDG_TOPLEVEL_STATE_RESIZING: + break; + case XDG_TOPLEVEL_STATE_ACTIVATED: + activated = GLFW_TRUE; + break; + } + } - return fd; + if (width != 0 && height != 0) + { + if (!maximized && !fullscreen) + { + if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) + { + aspectRatio = (float)width / (float)height; + targetRatio = (float)window->numer / (float)window->denom; + if (aspectRatio < targetRatio) + height = width / targetRatio; + else if (aspectRatio > targetRatio) + width = height * targetRatio; + } + } + + _glfwInputWindowSize(window, width, height); + _glfwPlatformSetWindowSize(window, width, height); + _glfwInputWindowDamage(window); + } + + if (window->wl.wasFullscreen && window->autoIconify) + { + if (!activated || !fullscreen) + { + _glfwPlatformIconifyWindow(window); + window->wl.wasFullscreen = GLFW_FALSE; + } + } + if (fullscreen && activated) + window->wl.wasFullscreen = GLFW_TRUE; + _glfwInputWindowFocus(window, activated); } -static void -handleEvents(int timeout) +static void xdgToplevelHandleClose(void* data, + struct xdg_toplevel* toplevel) +{ + _GLFWwindow* window = data; + _glfwInputWindowCloseRequest(window); +} + +static const struct xdg_toplevel_listener xdgToplevelListener = { + xdgToplevelHandleConfigure, + xdgToplevelHandleClose +}; + +static void xdgSurfaceHandleConfigure(void* data, + struct xdg_surface* surface, + uint32_t serial) +{ + xdg_surface_ack_configure(surface, serial); +} + +static const struct xdg_surface_listener xdgSurfaceListener = { + xdgSurfaceHandleConfigure +}; + +static void setXdgDecorations(_GLFWwindow* window) +{ + if (_glfw.wl.decorationManager) + { + window->wl.xdg.decoration = + zxdg_decoration_manager_v1_get_toplevel_decoration( + _glfw.wl.decorationManager, window->wl.xdg.toplevel); + zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration, + &xdgDecorationListener, + window); + zxdg_toplevel_decoration_v1_set_mode( + window->wl.xdg.decoration, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + else + { + window->wl.decorations.serverSide = GLFW_FALSE; + createDecorations(window); + } +} + +static GLFWbool createXdgSurface(_GLFWwindow* window) +{ + window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase, + window->wl.surface); + if (!window->wl.xdg.surface) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: xdg-surface creation failed"); + return GLFW_FALSE; + } + + xdg_surface_add_listener(window->wl.xdg.surface, + &xdgSurfaceListener, + window); + + window->wl.xdg.toplevel = xdg_surface_get_toplevel(window->wl.xdg.surface); + if (!window->wl.xdg.toplevel) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: xdg-toplevel creation failed"); + return GLFW_FALSE; + } + + xdg_toplevel_add_listener(window->wl.xdg.toplevel, + &xdgToplevelListener, + window); + + if (window->wl.title) + xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title); + + if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE) + xdg_toplevel_set_min_size(window->wl.xdg.toplevel, + window->minwidth, window->minheight); + if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) + xdg_toplevel_set_max_size(window->wl.xdg.toplevel, + window->maxwidth, window->maxheight); + + if (window->monitor) + { + xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, + window->monitor->wl.output); + setIdleInhibitor(window, GLFW_TRUE); + } + else if (window->wl.maximized) + { + xdg_toplevel_set_maximized(window->wl.xdg.toplevel); + setIdleInhibitor(window, GLFW_FALSE); + setXdgDecorations(window); + } + else + { + setIdleInhibitor(window, GLFW_FALSE); + setXdgDecorations(window); + } + + wl_surface_commit(window->wl.surface); + wl_display_roundtrip(_glfw.wl.display); + + return GLFW_TRUE; +} + +static void setCursorImage(_GLFWwindow* window, + _GLFWcursorWayland* cursorWayland) +{ + struct itimerspec timer = {}; + struct wl_cursor* wlCursor = cursorWayland->cursor; + struct wl_cursor_image* image; + struct wl_buffer* buffer; + struct wl_surface* surface = _glfw.wl.cursorSurface; + int scale = 1; + + if (!wlCursor) + buffer = cursorWayland->buffer; + else + { + if (window->wl.scale > 1 && cursorWayland->cursorHiDPI) + { + wlCursor = cursorWayland->cursorHiDPI; + scale = 2; + } + + image = wlCursor->images[cursorWayland->currentImage]; + buffer = wl_cursor_image_get_buffer(image); + if (!buffer) + return; + + timer.it_value.tv_sec = image->delay / 1000; + timer.it_value.tv_nsec = (image->delay % 1000) * 1000000; + timerfd_settime(_glfw.wl.cursorTimerfd, 0, &timer, NULL); + + cursorWayland->width = image->width; + cursorWayland->height = image->height; + cursorWayland->xhot = image->hotspot_x; + cursorWayland->yhot = image->hotspot_y; + } + + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, + surface, + cursorWayland->xhot / scale, + cursorWayland->yhot / scale); + wl_surface_set_buffer_scale(surface, scale); + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_damage(surface, 0, 0, + cursorWayland->width, cursorWayland->height); + wl_surface_commit(surface); +} + +static void incrementCursorImage(_GLFWwindow* window) +{ + _GLFWcursor* cursor; + + if (!window || window->wl.decorations.focus != mainWindow) + return; + + cursor = window->wl.currentCursor; + if (cursor && cursor->wl.cursor) + { + cursor->wl.currentImage += 1; + cursor->wl.currentImage %= cursor->wl.cursor->image_count; + setCursorImage(window, &cursor->wl); + } +} + +static void handleEvents(int timeout) { struct wl_display* display = _glfw.wl.display; struct pollfd fds[] = { { wl_display_get_fd(display), POLLIN }, + { _glfw.wl.timerfd, POLLIN }, + { _glfw.wl.cursorTimerfd, POLLIN }, }; + ssize_t read_ret; + uint64_t repeats, i; while (wl_display_prepare_read(display) != 0) wl_display_dispatch_pending(display); @@ -290,10 +850,38 @@ handleEvents(int timeout) return; } - if (poll(fds, 1, timeout) > 0) + if (poll(fds, 3, timeout) > 0) { - wl_display_read_events(display); - wl_display_dispatch_pending(display); + if (fds[0].revents & POLLIN) + { + wl_display_read_events(display); + wl_display_dispatch_pending(display); + } + else + { + wl_display_cancel_read(display); + } + + if (fds[1].revents & POLLIN) + { + read_ret = read(_glfw.wl.timerfd, &repeats, sizeof(repeats)); + if (read_ret != 8) + return; + + for (i = 0; i < repeats; ++i) + _glfwInputKey(_glfw.wl.keyboardFocus, _glfw.wl.keyboardLastKey, + _glfw.wl.keyboardLastScancode, GLFW_REPEAT, + _glfw.wl.xkb.modifiers); + } + + if (fds[2].revents & POLLIN) + { + read_ret = read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)); + if (read_ret != 8) + return; + + incrementCursorImage(_glfw.wl.pointerFocus); + } } else { @@ -301,62 +889,6 @@ handleEvents(int timeout) } } -/* - * Create a new, unique, anonymous file of the given size, and - * return the file descriptor for it. The file descriptor is set - * CLOEXEC. The file is immediately suitable for mmap()'ing - * the given size at offset zero. - * - * The file should not have a permanent backing store like a disk, - * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. - * - * The file name is deleted from the file system. - * - * The file is suitable for buffer sharing between processes by - * transmitting the file descriptor over Unix sockets using the - * SCM_RIGHTS methods. - * - * posix_fallocate() is used to guarantee that disk space is available - * for the file at the given size. If disk space is insufficent, errno - * is set to ENOSPC. If posix_fallocate() is not supported, program may - * receive SIGBUS on accessing mmap()'ed file contents instead. - */ -int -createAnonymousFile(off_t size) -{ - static const char template[] = "/glfw-shared-XXXXXX"; - const char* path; - char* name; - int fd; - int ret; - - path = getenv("XDG_RUNTIME_DIR"); - if (!path) - { - errno = ENOENT; - return -1; - } - - name = calloc(strlen(path) + sizeof(template), 1); - strcpy(name, path); - strcat(name, template); - - fd = createTmpfileCloexec(name); - - free(name); - - if (fd < 0) - return -1; - ret = posix_fallocate(fd, 0, size); - if (ret != 0) - { - close(fd); - errno = ret; - return -1; - } - return fd; -} - // Translates a GLFW standard cursor to a theme cursor name // static char *translateCursorShape(int shape) @@ -388,30 +920,53 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { + window->wl.transparent = fbconfig->transparent; + if (!createSurface(window, wndconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) { - if (!_glfwInitEGL()) - return GLFW_FALSE; - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; + if (ctxconfig->source == GLFW_EGL_CONTEXT_API || + ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } if (wndconfig->title) - window->wl.title = strdup(wndconfig->title); + window->wl.title = _glfw_strdup(wndconfig->title); if (wndconfig->visible) { - if (!createShellSurface(window)) - return GLFW_FALSE; + if (_glfw.wl.wmBase) + { + if (!createXdgSurface(window)) + return GLFW_FALSE; + } + else + { + if (!createShellSurface(window)) + return GLFW_FALSE; + } window->wl.visible = GLFW_TRUE; } else { - window->wl.shell_surface = NULL; + window->wl.xdg.surface = NULL; + window->wl.xdg.toplevel = NULL; + window->wl.shellSurface = NULL; window->wl.visible = GLFW_FALSE; } @@ -437,14 +992,30 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) _glfwInputWindowFocus(window, GLFW_FALSE); } + if (window->wl.idleInhibitor) + zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); + if (window->context.destroy) window->context.destroy(window); + destroyDecorations(window); + if (window->wl.xdg.decoration) + zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration); + + if (window->wl.decorations.buffer) + wl_buffer_destroy(window->wl.decorations.buffer); + if (window->wl.native) wl_egl_window_destroy(window->wl.native); - if (window->wl.shell_surface) - wl_shell_surface_destroy(window->wl.shell_surface); + if (window->wl.shellSurface) + wl_shell_surface_destroy(window->wl.shellSurface); + + if (window->wl.xdg.toplevel) + xdg_toplevel_destroy(window->wl.xdg.toplevel); + + if (window->wl.xdg.surface) + xdg_surface_destroy(window->wl.xdg.surface); if (window->wl.surface) wl_surface_destroy(window->wl.surface); @@ -457,9 +1028,11 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { if (window->wl.title) free(window->wl.title); - window->wl.title = strdup(title); - if (window->wl.shell_surface) - wl_shell_surface_set_title(window->wl.shell_surface, title); + window->wl.title = _glfw_strdup(title); + if (window->wl.xdg.toplevel) + xdg_toplevel_set_title(window->wl.xdg.toplevel, title); + else if (window->wl.shellSurface) + wl_shell_surface_set_title(window->wl.shellSurface, title); } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, @@ -496,30 +1069,44 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { - int scaledWidth = width * window->wl.scale; - int scaledHeight = height * window->wl.scale; window->wl.width = width; window->wl.height = height; - wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); - setOpaqueRegion(window); - _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); + resizeWindow(window); } void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) { - // TODO: find out how to trigger a resize. - // The actual limits are checked in the wl_shell_surface::configure handler. + if (_glfw.wl.wmBase) + { + if (window->wl.xdg.toplevel) + { + if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) + minwidth = minheight = 0; + if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) + maxwidth = maxheight = 0; + xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight); + xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight); + wl_surface_commit(window->wl.surface); + } + } + else + { + // TODO: find out how to trigger a resize. + // The actual limits are checked in the wl_shell_surface::configure handler. + } } -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, + int numer, int denom) { // TODO: find out how to trigger a resize. // The actual limits are checked in the wl_shell_surface::configure handler. } -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, + int* width, int* height) { _glfwPlatformGetWindowSize(window, width, height); *width *= window->wl.scale; @@ -530,47 +1117,83 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom) { - // TODO: will need a proper implementation once decorations are - // implemented, but for now just leave everything as 0. + if (window->decorated && !window->monitor && !window->wl.decorations.serverSide) + { + if (top) + *top = _GLFW_DECORATION_TOP; + if (left) + *left = _GLFW_DECORATION_WIDTH; + if (right) + *right = _GLFW_DECORATION_WIDTH; + if (bottom) + *bottom = _GLFW_DECORATION_WIDTH; + } +} + +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = (float) window->wl.scale; + if (yscale) + *yscale = (float) window->wl.scale; } void _glfwPlatformIconifyWindow(_GLFWwindow* window) { - // TODO: move to xdg_shell instead of wl_shell. - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Iconify window not supported"); + if (_glfw.wl.wmBase) + { + if (window->wl.xdg.toplevel) + xdg_toplevel_set_minimized(window->wl.xdg.toplevel); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Iconify window not supported on wl_shell"); + } } void _glfwPlatformRestoreWindow(_GLFWwindow* window) { - // TODO: also do the same for iconified. - if (window->monitor || window->wl.maximized) + if (window->wl.xdg.toplevel) { - if (window->wl.shell_surface) - wl_shell_surface_set_toplevel(window->wl.shell_surface); - - window->wl.maximized = GLFW_FALSE; + if (window->monitor) + xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); + if (window->wl.maximized) + xdg_toplevel_unset_maximized(window->wl.xdg.toplevel); + // There is no way to unset minimized, or even to know if we are + // minimized, so there is nothing to do here. } + else if (window->wl.shellSurface) + { + if (window->monitor || window->wl.maximized) + wl_shell_surface_set_toplevel(window->wl.shellSurface); + } + _glfwInputWindowMonitor(window, NULL); + window->wl.maximized = GLFW_FALSE; } void _glfwPlatformMaximizeWindow(_GLFWwindow* window) { - if (!window->monitor && !window->wl.maximized) + if (window->wl.xdg.toplevel) { - if (window->wl.shell_surface) - { - // Let the compositor select the best output. - wl_shell_surface_set_maximized(window->wl.shell_surface, NULL); - } - window->wl.maximized = GLFW_TRUE; + xdg_toplevel_set_maximized(window->wl.xdg.toplevel); } + else if (window->wl.shellSurface) + { + // Let the compositor select the best output. + wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); + } + window->wl.maximized = GLFW_TRUE; } void _glfwPlatformShowWindow(_GLFWwindow* window) { - if (!window->monitor) + if (!window->wl.visible) { - if (!window->wl.shell_surface) + if (_glfw.wl.wmBase) + createXdgSurface(window); + else if (!window->wl.shellSurface) createShellSurface(window); window->wl.visible = GLFW_TRUE; } @@ -578,12 +1201,26 @@ void _glfwPlatformShowWindow(_GLFWwindow* window) void _glfwPlatformHideWindow(_GLFWwindow* window) { - if (!window->monitor) + if (window->wl.xdg.toplevel) { - if (window->wl.shell_surface) - wl_shell_surface_destroy(window->wl.shell_surface); - window->wl.visible = GLFW_FALSE; + xdg_toplevel_destroy(window->wl.xdg.toplevel); + xdg_surface_destroy(window->wl.xdg.surface); + window->wl.xdg.toplevel = NULL; + window->wl.xdg.surface = NULL; } + else if (window->wl.shellSurface) + { + wl_shell_surface_destroy(window->wl.shellSurface); + window->wl.shellSurface = NULL; + } + window->wl.visible = GLFW_FALSE; +} + +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attention request not implemented yet"); } void _glfwPlatformFocusWindow(_GLFWwindow* window) @@ -600,17 +1237,19 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, { if (monitor) { - wl_shell_surface_set_fullscreen( - window->wl.shell_surface, - WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, - refreshRate * 1000, // Convert Hz to mHz. - monitor->wl.output); + setFullscreen(window, monitor, refreshRate); } else { - wl_shell_surface_set_toplevel(window->wl.shell_surface); + if (window->wl.xdg.toplevel) + xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); + else if (window->wl.shellSurface) + wl_shell_surface_set_toplevel(window->wl.shellSurface); + setIdleInhibitor(window, GLFW_FALSE); + if (!_glfw.wl.decorationManager) + createDecorations(window); } - _glfwInputWindowMonitorChange(window, monitor); + _glfwInputWindowMonitor(window, monitor); } int _glfwPlatformWindowFocused(_GLFWwindow* window) @@ -620,7 +1259,8 @@ int _glfwPlatformWindowFocused(_GLFWwindow* window) int _glfwPlatformWindowIconified(_GLFWwindow* window) { - // TODO: move to xdg_shell, wl_shell doesn't have any iconified concept. + // wl_shell doesn't have any iconified concept, and xdg-shell doesn’t give + // any way to request whether a surface is iconified. return GLFW_FALSE; } @@ -634,6 +1274,60 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return window->wl.maximized; } +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + return window->wl.hovered; +} + +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return window->wl.transparent; +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attribute setting not implemented yet"); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + if (!window->monitor) + { + if (enabled) + createDecorations(window); + else + destroyDecorations(window); + } +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attribute setting not implemented yet"); +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + return 1.f; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ +} + +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +{ + // This is handled in relativePointerHandleRelativeMotion +} + +GLFWbool _glfwPlatformRawMouseMotionSupported(void) +{ + return GLFW_TRUE; +} + void _glfwPlatformPollEvents(void) { handleEvents(0); @@ -680,62 +1374,24 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) _glfwPlatformSetCursor(window, window->wl.currentCursor); } -const char* _glfwPlatformGetKeyName(int key, int scancode) +const char* _glfwPlatformGetScancodeName(int scancode) { // TODO return NULL; } +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.wl.scancodes[key]; +} + int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) { - struct wl_shm_pool* pool; - int stride = image->width * 4; - int length = image->width * image->height * 4; - void* data; - int fd, i; - - fd = createAnonymousFile(length); - if (fd < 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Creating a buffer file for %d B failed: %m", - length); + cursor->wl.buffer = createShmBuffer(image); + if (!cursor->wl.buffer) return GLFW_FALSE; - } - - data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (data == MAP_FAILED) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Cursor mmap failed: %m"); - close(fd); - return GLFW_FALSE; - } - - pool = wl_shm_create_pool(_glfw.wl.shm, fd, length); - - close(fd); - unsigned char* source = (unsigned char*) image->pixels; - unsigned char* target = data; - for (i = 0; i < image->width * image->height; i++, source += 4) - { - unsigned int alpha = source[3]; - - *target++ = (unsigned char) ((source[2] * alpha) / 255); - *target++ = (unsigned char) ((source[1] * alpha) / 255); - *target++ = (unsigned char) ((source[0] * alpha) / 255); - *target++ = (unsigned char) alpha; - } - - cursor->wl.buffer = - wl_shm_pool_create_buffer(pool, 0, - image->width, - image->height, - stride, WL_SHM_FORMAT_ARGB8888); - munmap(data, length); - wl_shm_pool_destroy(pool); cursor->wl.width = image->width; cursor->wl.height = image->height; @@ -758,45 +1414,65 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) return GLFW_FALSE; } - cursor->wl.image = standardCursor->images[0]; + cursor->wl.cursor = standardCursor; + cursor->wl.currentImage = 0; + + if (_glfw.wl.cursorThemeHiDPI) + { + standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, + translateCursorShape(shape)); + cursor->wl.cursorHiDPI = standardCursor; + } + return GLFW_TRUE; } void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { // If it's a standard cursor we don't need to do anything here - if (cursor->wl.image) + if (cursor->wl.cursor) return; if (cursor->wl.buffer) wl_buffer_destroy(cursor->wl.buffer); } -static void handleRelativeMotion(void* data, - struct zwp_relative_pointer_v1* pointer, - uint32_t timeHi, - uint32_t timeLo, - wl_fixed_t dx, - wl_fixed_t dy, - wl_fixed_t dxUnaccel, - wl_fixed_t dyUnaccel) +static void relativePointerHandleRelativeMotion(void* data, + struct zwp_relative_pointer_v1* pointer, + uint32_t timeHi, + uint32_t timeLo, + wl_fixed_t dx, + wl_fixed_t dy, + wl_fixed_t dxUnaccel, + wl_fixed_t dyUnaccel) { _GLFWwindow* window = data; + double xpos = window->virtualCursorPosX; + double ypos = window->virtualCursorPosY; if (window->cursorMode != GLFW_CURSOR_DISABLED) return; - _glfwInputCursorPos(window, - window->virtualCursorPosX + wl_fixed_to_double(dxUnaccel), - window->virtualCursorPosY + wl_fixed_to_double(dyUnaccel)); + if (window->rawMouseMotion) + { + xpos += wl_fixed_to_double(dxUnaccel); + ypos += wl_fixed_to_double(dyUnaccel); + } + else + { + xpos += wl_fixed_to_double(dx); + ypos += wl_fixed_to_double(dy); + } + + _glfwInputCursorPos(window, xpos, ypos); } static const struct zwp_relative_pointer_v1_listener relativePointerListener = { - handleRelativeMotion + relativePointerHandleRelativeMotion }; -static void handleLocked(void* data, - struct zwp_locked_pointer_v1* lockedPointer) +static void lockedPointerHandleLocked(void* data, + struct zwp_locked_pointer_v1* lockedPointer) { } @@ -816,14 +1492,14 @@ static void unlockPointer(_GLFWwindow* window) static void lockPointer(_GLFWwindow* window); -static void handleUnlocked(void* data, - struct zwp_locked_pointer_v1* lockedPointer) +static void lockedPointerHandleUnlocked(void* data, + struct zwp_locked_pointer_v1* lockedPointer) { } static const struct zwp_locked_pointer_v1_listener lockedPointerListener = { - handleLocked, - handleUnlocked + lockedPointerHandleLocked, + lockedPointerHandleUnlocked }; static void lockPointer(_GLFWwindow* window) @@ -860,7 +1536,7 @@ static void lockPointer(_GLFWwindow* window) window->wl.pointerLock.relativePointer = relativePointer; window->wl.pointerLock.lockedPointer = lockedPointer; - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0); } @@ -871,10 +1547,8 @@ static GLFWbool isPointerLocked(_GLFWwindow* window) void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { - struct wl_buffer* buffer; struct wl_cursor* defaultCursor; - struct wl_cursor_image* image; - struct wl_surface* surface = _glfw.wl.cursorSurface; + struct wl_cursor* defaultCursorHiDPI = NULL; if (!_glfw.wl.pointer) return; @@ -883,7 +1557,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) // If we're not in the correct window just save the cursor // the next time the pointer enters the window the cursor will change - if (window != _glfw.wl.pointerFocus) + if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow) return; // Unlock possible pointer lock if no longer disabled. @@ -893,7 +1567,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) if (window->cursorMode == GLFW_CURSOR_NORMAL) { if (cursor) - image = cursor->wl.image; + setCursorImage(window, &cursor->wl); else { defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, @@ -904,33 +1578,19 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) "Wayland: Standard cursor not found"); return; } - image = defaultCursor->images[0]; - } - - if (image) - { - buffer = wl_cursor_image_get_buffer(image); - if (!buffer) - return; - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, - surface, - image->hotspot_x, - image->hotspot_y); - wl_surface_attach(surface, buffer, 0, 0); - wl_surface_damage(surface, 0, 0, - image->width, image->height); - wl_surface_commit(surface); - } - else - { - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, - surface, - cursor->wl.xhot, - cursor->wl.yhot); - wl_surface_attach(surface, cursor->wl.buffer, 0, 0); - wl_surface_damage(surface, 0, 0, - cursor->wl.width, cursor->wl.height); - wl_surface_commit(surface); + if (_glfw.wl.cursorThemeHiDPI) + defaultCursorHiDPI = + wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, + "left_ptr"); + _GLFWcursorWayland cursorWayland = { + defaultCursor, + defaultCursorHiDPI, + NULL, + 0, 0, + 0, 0, + 0 + }; + setCursorImage(window, &cursorWayland); } } else if (window->cursorMode == GLFW_CURSOR_DISABLED) @@ -940,48 +1600,230 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) } else if (window->cursorMode == GLFW_CURSOR_HIDDEN) { - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, - NULL, 0, 0); + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0); } } -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +static void dataSourceHandleTarget(void* data, + struct wl_data_source* dataSource, + const char* mimeType) { - // TODO - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Clipboard setting not implemented yet"); + if (_glfw.wl.dataSource != dataSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unknown clipboard data source"); + return; + } } -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +static void dataSourceHandleSend(void* data, + struct wl_data_source* dataSource, + const char* mimeType, + int fd) { - // TODO - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Clipboard getting not implemented yet"); - return NULL; + const char* string = _glfw.wl.clipboardSendString; + size_t len = _glfw.wl.clipboardSendSize; + int ret; + + if (_glfw.wl.dataSource != dataSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unknown clipboard data source"); + return; + } + + if (!string) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Copy requested from an invalid string"); + return; + } + + if (strcmp(mimeType, "text/plain;charset=utf-8") != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Wrong MIME type asked from clipboard"); + close(fd); + return; + } + + while (len > 0) + { + ret = write(fd, string, len); + if (ret == -1 && errno == EINTR) + continue; + if (ret == -1) + { + // TODO: also report errno maybe. + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Error while writing the clipboard"); + close(fd); + return; + } + len -= ret; + } + close(fd); } -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) +static void dataSourceHandleCancelled(void* data, + struct wl_data_source* dataSource) { - char** extensions; + wl_data_source_destroy(dataSource); - *count = 0; + if (_glfw.wl.dataSource != dataSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unknown clipboard data source"); + return; + } - if (!_glfw.vk.KHR_wayland_surface) + _glfw.wl.dataSource = NULL; +} + +static const struct wl_data_source_listener dataSourceListener = { + dataSourceHandleTarget, + dataSourceHandleSend, + dataSourceHandleCancelled, +}; + +void _glfwPlatformSetClipboardString(const char* string) +{ + if (_glfw.wl.dataSource) + { + wl_data_source_destroy(_glfw.wl.dataSource); + _glfw.wl.dataSource = NULL; + } + + if (_glfw.wl.clipboardSendString) + { + free(_glfw.wl.clipboardSendString); + _glfw.wl.clipboardSendString = NULL; + } + + _glfw.wl.clipboardSendString = strdup(string); + if (!_glfw.wl.clipboardSendString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to allocate clipboard string"); + return; + } + _glfw.wl.clipboardSendSize = strlen(string); + _glfw.wl.dataSource = + wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); + if (!_glfw.wl.dataSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to create clipboard source"); + free(_glfw.wl.clipboardSendString); + return; + } + wl_data_source_add_listener(_glfw.wl.dataSource, + &dataSourceListener, + NULL); + wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8"); + wl_data_device_set_selection(_glfw.wl.dataDevice, + _glfw.wl.dataSource, + _glfw.wl.serial); +} + +static GLFWbool growClipboardString(void) +{ + char* clipboard = _glfw.wl.clipboardString; + + clipboard = realloc(clipboard, _glfw.wl.clipboardSize * 2); + if (!clipboard) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to grow clipboard string"); + return GLFW_FALSE; + } + _glfw.wl.clipboardString = clipboard; + _glfw.wl.clipboardSize = _glfw.wl.clipboardSize * 2; + return GLFW_TRUE; +} + +const char* _glfwPlatformGetClipboardString(void) +{ + int fds[2]; + int ret; + size_t len = 0; + + if (!_glfw.wl.dataOffer) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "No clipboard data has been sent yet"); return NULL; + } - extensions = calloc(2, sizeof(char*)); - extensions[0] = strdup("VK_KHR_surface"); - extensions[1] = strdup("VK_KHR_wayland_surface"); + ret = pipe2(fds, O_CLOEXEC); + if (ret < 0) + { + // TODO: also report errno maybe? + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to create clipboard pipe fds"); + return NULL; + } - *count = 2; - return extensions; + wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]); + close(fds[1]); + + // XXX: this is a huge hack, this function shouldn’t be synchronous! + handleEvents(-1); + + while (1) + { + // Grow the clipboard if we need to paste something bigger, there is no + // shrink operation yet. + if (len + 4096 > _glfw.wl.clipboardSize) + { + if (!growClipboardString()) + { + close(fds[0]); + return NULL; + } + } + + // Then read from the fd to the clipboard, handling all known errors. + ret = read(fds[0], _glfw.wl.clipboardString + len, 4096); + if (ret == 0) + break; + if (ret == -1 && errno == EINTR) + continue; + if (ret == -1) + { + // TODO: also report errno maybe. + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to read from clipboard fd"); + close(fds[0]); + return NULL; + } + len += ret; + } + close(fds[0]); + if (len + 1 > _glfw.wl.clipboardSize) + { + if (!growClipboardString()) + return NULL; + } + _glfw.wl.clipboardString[len] = '\0'; + return _glfw.wl.clipboardString; +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ + if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface) + return; + + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_KHR_wayland_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { - PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = + PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR + vkGetPhysicalDeviceWaylandPresentationSupportKHR = (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) diff --git a/external/glfw/x11_init.c b/external/glfw/x11_init.c index 87bd877..dae5b98 100644 --- a/external/glfw/x11_init.c +++ b/external/glfw/x11_init.c @@ -2,7 +2,7 @@ // GLFW 3.3 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -232,13 +232,13 @@ static void createKeyTables(void) { int scancode, key; - memset(_glfw.x11.publicKeys, -1, sizeof(_glfw.x11.publicKeys)); - memset(_glfw.x11.nativeKeys, -1, sizeof(_glfw.x11.nativeKeys)); + memset(_glfw.x11.keycodes, -1, sizeof(_glfw.x11.keycodes)); + memset(_glfw.x11.scancodes, -1, sizeof(_glfw.x11.scancodes)); if (_glfw.x11.xkb.available) { - // Use XKB to determine physical key locations independently of the current - // keyboard layout + // Use XKB to determine physical key locations independently of the + // current keyboard layout char name[XkbKeyNameLength + 1]; XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd); @@ -305,7 +305,7 @@ static void createKeyTables(void) else key = GLFW_KEY_UNKNOWN; if ((scancode >= 0) && (scancode < 256)) - _glfw.x11.publicKeys[scancode] = key; + _glfw.x11.keycodes[scancode] = key; } XkbFreeNames(desc, XkbKeyNamesMask, True); @@ -316,12 +316,12 @@ static void createKeyTables(void) { // Translate the un-translated key codes using traditional X11 KeySym // lookups - if (_glfw.x11.publicKeys[scancode] < 0) - _glfw.x11.publicKeys[scancode] = translateKeyCode(scancode); + if (_glfw.x11.keycodes[scancode] < 0) + _glfw.x11.keycodes[scancode] = translateKeyCode(scancode); // Store the reverse translation for faster key name lookup - if (_glfw.x11.publicKeys[scancode] > 0) - _glfw.x11.nativeKeys[_glfw.x11.publicKeys[scancode]] = scancode; + if (_glfw.x11.keycodes[scancode] > 0) + _glfw.x11.scancodes[_glfw.x11.keycodes[scancode]] = scancode; } } @@ -381,13 +381,11 @@ static void detectEWMH(void) XInternAtom(_glfw.x11.display, "_NET_SUPPORTED", False); // Then we look for the _NET_SUPPORTING_WM_CHECK property of the root window - if (_glfwGetWindowPropertyX11(_glfw.x11.root, - supportingWmCheck, - XA_WINDOW, - (unsigned char**) &windowFromRoot) != 1) + if (!_glfwGetWindowPropertyX11(_glfw.x11.root, + supportingWmCheck, + XA_WINDOW, + (unsigned char**) &windowFromRoot)) { - if (windowFromRoot) - XFree(windowFromRoot); return; } @@ -395,14 +393,12 @@ static void detectEWMH(void) // It should be the ID of a child window (of the root) // Then we look for the same property on the child window - if (_glfwGetWindowPropertyX11(*windowFromRoot, - supportingWmCheck, - XA_WINDOW, - (unsigned char**) &windowFromChild) != 1) + if (!_glfwGetWindowPropertyX11(*windowFromRoot, + supportingWmCheck, + XA_WINDOW, + (unsigned char**) &windowFromChild)) { XFree(windowFromRoot); - if (windowFromChild) - XFree(windowFromChild); return; } @@ -442,12 +438,18 @@ static void detectEWMH(void) getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT"); _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ"); + _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION"); _glfw.x11.NET_WM_FULLSCREEN_MONITORS = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); _glfw.x11.NET_WM_WINDOW_TYPE = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE"); _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL"); + _glfw.x11.NET_WORKAREA = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WORKAREA"); + _glfw.x11.NET_CURRENT_DESKTOP = + getSupportedAtom(supportedAtoms, atomCount, "_NET_CURRENT_DESKTOP"); _glfw.x11.NET_ACTIVE_WINDOW = getSupportedAtom(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW"); _glfw.x11.NET_FRAME_EXTENTS = @@ -455,72 +457,192 @@ static void detectEWMH(void) _glfw.x11.NET_REQUEST_FRAME_EXTENTS = getSupportedAtom(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS"); - XFree(supportedAtoms); + if (supportedAtoms) + XFree(supportedAtoms); } -// Initialize X11 display and look for supported X11 extensions +// Look for and initialize supported X11 extensions // static GLFWbool initExtensions(void) { -#if defined(_GLFW_HAS_XF86VM) - // Check for XF86VidMode extension - _glfw.x11.vidmode.available = - XF86VidModeQueryExtension(_glfw.x11.display, - &_glfw.x11.vidmode.eventBase, - &_glfw.x11.vidmode.errorBase); -#endif /*_GLFW_HAS_XF86VM*/ - - // Check for RandR extension - if (XRRQueryExtension(_glfw.x11.display, - &_glfw.x11.randr.eventBase, - &_glfw.x11.randr.errorBase)) + _glfw.x11.vidmode.handle = _glfw_dlopen("libXxf86vm.so.1"); + if (_glfw.x11.vidmode.handle) { - if (XRRQueryVersion(_glfw.x11.display, - &_glfw.x11.randr.major, - &_glfw.x11.randr.minor)) + _glfw.x11.vidmode.QueryExtension = (PFN_XF86VidModeQueryExtension) + _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeQueryExtension"); + _glfw.x11.vidmode.GetGammaRamp = (PFN_XF86VidModeGetGammaRamp) + _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRamp"); + _glfw.x11.vidmode.SetGammaRamp = (PFN_XF86VidModeSetGammaRamp) + _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeSetGammaRamp"); + _glfw.x11.vidmode.GetGammaRampSize = (PFN_XF86VidModeGetGammaRampSize) + _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRampSize"); + + _glfw.x11.vidmode.available = + XF86VidModeQueryExtension(_glfw.x11.display, + &_glfw.x11.vidmode.eventBase, + &_glfw.x11.vidmode.errorBase); + } + +#if defined(__CYGWIN__) + _glfw.x11.xi.handle = _glfw_dlopen("libXi-6.so"); +#else + _glfw.x11.xi.handle = _glfw_dlopen("libXi.so.6"); +#endif + if (_glfw.x11.xi.handle) + { + _glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion) + _glfw_dlsym(_glfw.x11.xi.handle, "XIQueryVersion"); + _glfw.x11.xi.SelectEvents = (PFN_XISelectEvents) + _glfw_dlsym(_glfw.x11.xi.handle, "XISelectEvents"); + + if (XQueryExtension(_glfw.x11.display, + "XInputExtension", + &_glfw.x11.xi.majorOpcode, + &_glfw.x11.xi.eventBase, + &_glfw.x11.xi.errorBase)) { - // The GLFW RandR path requires at least version 1.3 - if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3) - _glfw.x11.randr.available = GLFW_TRUE; + _glfw.x11.xi.major = 2; + _glfw.x11.xi.minor = 0; + + if (XIQueryVersion(_glfw.x11.display, + &_glfw.x11.xi.major, + &_glfw.x11.xi.minor) == Success) + { + _glfw.x11.xi.available = GLFW_TRUE; + } } - else + } + +#if defined(__CYGWIN__) + _glfw.x11.randr.handle = _glfw_dlopen("libXrandr-2.so"); +#else + _glfw.x11.randr.handle = _glfw_dlopen("libXrandr.so.2"); +#endif + if (_glfw.x11.randr.handle) + { + _glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRAllocGamma"); + _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); + _glfw.x11.randr.FreeCrtcInfo = (PFN_XRRFreeCrtcInfo) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeCrtcInfo"); + _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); + _glfw.x11.randr.FreeOutputInfo = (PFN_XRRFreeOutputInfo) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeOutputInfo"); + _glfw.x11.randr.FreeScreenResources = (PFN_XRRFreeScreenResources) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeScreenResources"); + _glfw.x11.randr.GetCrtcGamma = (PFN_XRRGetCrtcGamma) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGamma"); + _glfw.x11.randr.GetCrtcGammaSize = (PFN_XRRGetCrtcGammaSize) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGammaSize"); + _glfw.x11.randr.GetCrtcInfo = (PFN_XRRGetCrtcInfo) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcInfo"); + _glfw.x11.randr.GetOutputInfo = (PFN_XRRGetOutputInfo) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputInfo"); + _glfw.x11.randr.GetOutputPrimary = (PFN_XRRGetOutputPrimary) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputPrimary"); + _glfw.x11.randr.GetScreenResourcesCurrent = (PFN_XRRGetScreenResourcesCurrent) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent"); + _glfw.x11.randr.QueryExtension = (PFN_XRRQueryExtension) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryExtension"); + _glfw.x11.randr.QueryVersion = (PFN_XRRQueryVersion) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryVersion"); + _glfw.x11.randr.SelectInput = (PFN_XRRSelectInput) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRSelectInput"); + _glfw.x11.randr.SetCrtcConfig = (PFN_XRRSetCrtcConfig) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcConfig"); + _glfw.x11.randr.SetCrtcGamma = (PFN_XRRSetCrtcGamma) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcGamma"); + _glfw.x11.randr.UpdateConfiguration = (PFN_XRRUpdateConfiguration) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRUpdateConfiguration"); + + if (XRRQueryExtension(_glfw.x11.display, + &_glfw.x11.randr.eventBase, + &_glfw.x11.randr.errorBase)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to query RandR version"); + if (XRRQueryVersion(_glfw.x11.display, + &_glfw.x11.randr.major, + &_glfw.x11.randr.minor)) + { + // The GLFW RandR path requires at least version 1.3 + if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3) + _glfw.x11.randr.available = GLFW_TRUE; + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to query RandR version"); + } } } if (_glfw.x11.randr.available) { - XRRScreenResources* sr = XRRGetScreenResources(_glfw.x11.display, - _glfw.x11.root); + XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, + _glfw.x11.root); if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0])) { - // This is either a headless system or an older Nvidia binary driver - // with broken gamma support - // Flag it as useless and fall back to Xf86VidMode gamma, if - // available - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: RandR gamma ramp support seems broken"); + // This is likely an older Nvidia driver with broken gamma support + // Flag it as useless and fall back to xf86vm gamma, if available _glfw.x11.randr.gammaBroken = GLFW_TRUE; } - XRRFreeScreenResources(sr); + if (!sr->ncrtc) + { + // A system without CRTCs is likely a system with broken RandR + // Disable the RandR monitor path and fall back to core functions + _glfw.x11.randr.monitorBroken = GLFW_TRUE; + } + XRRFreeScreenResources(sr); + } + + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { XRRSelectInput(_glfw.x11.display, _glfw.x11.root, RROutputChangeNotifyMask); } - if (XineramaQueryExtension(_glfw.x11.display, - &_glfw.x11.xinerama.major, - &_glfw.x11.xinerama.minor)) +#if defined(__CYGWIN__) + _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor-1.so"); +#else + _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so.1"); +#endif + if (_glfw.x11.xcursor.handle) { - if (XineramaIsActive(_glfw.x11.display)) - _glfw.x11.xinerama.available = GLFW_TRUE; + _glfw.x11.xcursor.ImageCreate = (PFN_XcursorImageCreate) + _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageCreate"); + _glfw.x11.xcursor.ImageDestroy = (PFN_XcursorImageDestroy) + _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy"); + _glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor) + _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor"); + } + +#if defined(__CYGWIN__) + _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama-1.so"); +#else + _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama.so.1"); +#endif + if (_glfw.x11.xinerama.handle) + { + _glfw.x11.xinerama.IsActive = (PFN_XineramaIsActive) + _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaIsActive"); + _glfw.x11.xinerama.QueryExtension = (PFN_XineramaQueryExtension) + _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryExtension"); + _glfw.x11.xinerama.QueryScreens = (PFN_XineramaQueryScreens) + _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryScreens"); + + if (XineramaQueryExtension(_glfw.x11.display, + &_glfw.x11.xinerama.major, + &_glfw.x11.xinerama.minor)) + { + if (XineramaIsActive(_glfw.x11.display)) + _glfw.x11.xinerama.available = GLFW_TRUE; + } } - // Check if Xkb is supported on this display _glfw.x11.xkb.major = 1; _glfw.x11.xkb.minor = 0; _glfw.x11.xkb.available = @@ -542,11 +664,42 @@ static GLFWbool initExtensions(void) } } - _glfw.x11.x11xcb.handle = dlopen("libX11-xcb.so", RTLD_LAZY | RTLD_GLOBAL); +#if defined(__CYGWIN__) + _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb-1.so"); +#else + _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb.so.1"); +#endif if (_glfw.x11.x11xcb.handle) { - _glfw.x11.x11xcb.XGetXCBConnection = (XGETXCBCONNECTION_T) - dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); + _glfw.x11.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection) + _glfw_dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); + } + +#if defined(__CYGWIN__) + _glfw.x11.xrender.handle = _glfw_dlopen("libXrender-1.so"); +#else + _glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1"); +#endif + if (_glfw.x11.xrender.handle) + { + _glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension) + _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryExtension"); + _glfw.x11.xrender.QueryVersion = (PFN_XRenderQueryVersion) + _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryVersion"); + _glfw.x11.xrender.FindVisualFormat = (PFN_XRenderFindVisualFormat) + _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderFindVisualFormat"); + + if (XRenderQueryExtension(_glfw.x11.display, + &_glfw.x11.xrender.errorBase, + &_glfw.x11.xrender.eventBase)) + { + if (XRenderQueryVersion(_glfw.x11.display, + &_glfw.x11.xrender.major, + &_glfw.x11.xrender.minor)) + { + _glfw.x11.xrender.available = GLFW_TRUE; + } + } } // Update the key code LUT @@ -559,10 +712,7 @@ static GLFWbool initExtensions(void) // String format atoms _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False); - _glfw.x11.UTF8_STRING = - XInternAtom(_glfw.x11.display, "UTF8_STRING", False); - _glfw.x11.COMPOUND_STRING = - XInternAtom(_glfw.x11.display, "COMPOUND_STRING", False); + _glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, "UTF8_STRING", False); _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False); // Custom selection property atom @@ -572,6 +722,8 @@ static GLFWbool initExtensions(void) // ICCCM standard clipboard atoms _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False); _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False); + _glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False); + _glfw.x11.INCR = XInternAtom(_glfw.x11.display, "INCR", False); _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False); // Clipboard manager atoms @@ -587,9 +739,10 @@ static GLFWbool initExtensions(void) _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False); _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False); _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False); - _glfw.x11.XdndLeave = XInternAtom(_glfw.x11.display, "XdndLeave", False); _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False); _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False); + _glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False); + _glfw.x11.text_uri_list = XInternAtom(_glfw.x11.display, "text/uri-list", False); // ICCCM, EWMH and Motif window property atoms // These can be set safely even without WM support @@ -612,24 +765,82 @@ static GLFWbool initExtensions(void) XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False); _glfw.x11.NET_WM_BYPASS_COMPOSITOR = XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False); + _glfw.x11.NET_WM_WINDOW_OPACITY = + XInternAtom(_glfw.x11.display, "_NET_WM_WINDOW_OPACITY", False); _glfw.x11.MOTIF_WM_HINTS = XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); + // The compositing manager selection name contains the screen number + { + char name[32]; + snprintf(name, sizeof(name), "_NET_WM_CM_S%u", _glfw.x11.screen); + _glfw.x11.NET_WM_CM_Sx = XInternAtom(_glfw.x11.display, name, False); + } + return GLFW_TRUE; } +// Retrieve system content scale via folklore heuristics +// +static void getSystemContentScale(float* xscale, float* yscale) +{ + // NOTE: Fall back to the display-wide DPI instead of RandR monitor DPI if + // Xft.dpi retrieval below fails as we don't currently have an exact + // policy for which monitor a window is considered to "be on" + float xdpi = DisplayWidth(_glfw.x11.display, _glfw.x11.screen) * + 25.4f / DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen); + float ydpi = DisplayHeight(_glfw.x11.display, _glfw.x11.screen) * + 25.4f / DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen); + + // NOTE: Basing the scale on Xft.dpi where available should provide the most + // consistent user experience (matches Qt, Gtk, etc), although not + // always the most accurate one + char* rms = XResourceManagerString(_glfw.x11.display); + if (rms) + { + XrmDatabase db = XrmGetStringDatabase(rms); + if (db) + { + XrmValue value; + char* type = NULL; + + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) + { + if (type && strcmp(type, "String") == 0) + xdpi = ydpi = atof(value.addr); + } + + XrmDestroyDatabase(db); + } + } + + *xscale = xdpi / 96.f; + *yscale = ydpi / 96.f; +} + // Create a blank cursor for hidden and disabled cursor modes // static Cursor createHiddenCursor(void) { - unsigned char pixels[16 * 16 * 4]; + unsigned char pixels[16 * 16 * 4] = { 0 }; GLFWimage image = { 16, 16, pixels }; - - memset(pixels, 0, sizeof(pixels)); - return _glfwCreateCursorX11(&image, 0, 0); } +// Create a helper window for IPC +// +static Window createHelperWindow(void) +{ + XSetWindowAttributes wa; + wa.event_mask = PropertyChangeMask; + + return XCreateWindow(_glfw.x11.display, _glfw.x11.root, + 0, 0, 1, 1, 0, 0, + InputOnly, + DefaultVisual(_glfw.x11.display, _glfw.x11.screen), + CWEventMask, &wa); +} + // X error handler // static int errorHandler(Display *display, XErrorEvent* event) @@ -664,7 +875,7 @@ void _glfwReleaseErrorHandlerX11(void) // void _glfwInputErrorX11(int error, const char* message) { - char buffer[8192]; + char buffer[_GLFW_MESSAGE_SIZE]; XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode, buffer, sizeof(buffer)); @@ -678,6 +889,9 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) int i; Cursor cursor; + if (!_glfw.x11.xcursor.handle) + return None; + XcursorImage* native = XcursorImageCreate(image->width, image->height); if (native == NULL) return None; @@ -712,13 +926,17 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) int _glfwPlatformInit(void) { #if !defined(X_HAVE_UTF8_STRING) - // HACK: If the current locale is C, apply the environment's locale - // This is done because the C locale breaks wide character input + // HACK: If the current locale is "C" and the Xlib UTF-8 functions are + // unavailable, apply the environment's locale in the hope that it's + // both available and not "C" + // This is done because the "C" locale breaks wide character input, + // which is what we fall back on when UTF-8 support is missing if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0) setlocale(LC_CTYPE, ""); #endif XInitThreads(); + XrmInitialize(); _glfw.x11.display = XOpenDisplay(NULL); if (!_glfw.x11.display) @@ -742,10 +960,13 @@ int _glfwPlatformInit(void) _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); _glfw.x11.context = XUniqueContext(); + getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY); + if (!initExtensions()) return GLFW_FALSE; - _glfw.x11.cursor = createHiddenCursor(); + _glfw.x11.helperWindowHandle = createHelperWindow(); + _glfw.x11.hiddenCursorHandle = createHiddenCursor(); if (XSupportsLocale()) { @@ -762,31 +983,38 @@ int _glfwPlatformInit(void) } } - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - +#if defined(__linux__) if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; +#endif _glfwInitTimerPOSIX(); + _glfwPollMonitorsX11(); return GLFW_TRUE; } void _glfwPlatformTerminate(void) { - if (_glfw.x11.x11xcb.handle) + if (_glfw.x11.helperWindowHandle) { - dlclose(_glfw.x11.x11xcb.handle); - _glfw.x11.x11xcb.handle = NULL; + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == + _glfw.x11.helperWindowHandle) + { + _glfwPushSelectionToManagerX11(); + } + + XDestroyWindow(_glfw.x11.display, _glfw.x11.helperWindowHandle); + _glfw.x11.helperWindowHandle = None; } - if (_glfw.x11.cursor) + if (_glfw.x11.hiddenCursorHandle) { - XFreeCursor(_glfw.x11.display, _glfw.x11.cursor); - _glfw.x11.cursor = (Cursor) 0; + XFreeCursor(_glfw.x11.display, _glfw.x11.hiddenCursorHandle); + _glfw.x11.hiddenCursorHandle = (Cursor) 0; } + free(_glfw.x11.primarySelectionString); free(_glfw.x11.clipboardString); if (_glfw.x11.im) @@ -795,35 +1023,74 @@ void _glfwPlatformTerminate(void) _glfw.x11.im = NULL; } - _glfwTerminateEGL(); - if (_glfw.x11.display) { XCloseDisplay(_glfw.x11.display); _glfw.x11.display = NULL; } - // NOTE: This needs to be done after XCloseDisplay, as libGL registers - // cleanup callbacks that get called by it + if (_glfw.x11.x11xcb.handle) + { + _glfw_dlclose(_glfw.x11.x11xcb.handle); + _glfw.x11.x11xcb.handle = NULL; + } + + if (_glfw.x11.xcursor.handle) + { + _glfw_dlclose(_glfw.x11.xcursor.handle); + _glfw.x11.xcursor.handle = NULL; + } + + if (_glfw.x11.randr.handle) + { + _glfw_dlclose(_glfw.x11.randr.handle); + _glfw.x11.randr.handle = NULL; + } + + if (_glfw.x11.xinerama.handle) + { + _glfw_dlclose(_glfw.x11.xinerama.handle); + _glfw.x11.xinerama.handle = NULL; + } + + if (_glfw.x11.xrender.handle) + { + _glfw_dlclose(_glfw.x11.xrender.handle); + _glfw.x11.xrender.handle = NULL; + } + + if (_glfw.x11.vidmode.handle) + { + _glfw_dlclose(_glfw.x11.vidmode.handle); + _glfw.x11.vidmode.handle = NULL; + } + + if (_glfw.x11.xi.handle) + { + _glfw_dlclose(_glfw.x11.xi.handle); + _glfw.x11.xi.handle = NULL; + } + + // NOTE: These need to be unloaded after XCloseDisplay, as they register + // cleanup callbacks that get called by that function + _glfwTerminateEGL(); _glfwTerminateGLX(); +#if defined(__linux__) _glfwTerminateJoysticksLinux(); - _glfwTerminateThreadLocalStoragePOSIX(); +#endif } const char* _glfwPlatformGetVersionString(void) { - return _GLFW_VERSION_NUMBER " X11 GLX EGL" + return _GLFW_VERSION_NUMBER " X11 GLX EGL OSMesa" #if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) " clock_gettime" #else " gettimeofday" #endif #if defined(__linux__) - " /dev/js" -#endif -#if defined(_GLFW_HAS_XF86VM) - " Xf86vm" + " evdev" #endif #if defined(_GLFW_BUILD_DLL) " shared" diff --git a/external/glfw/x11_monitor.c b/external/glfw/x11_monitor.c index 111ef1a..1cf0c42 100644 --- a/external/glfw/x11_monitor.c +++ b/external/glfw/x11_monitor.c @@ -2,7 +2,7 @@ // GLFW 3.3 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -30,6 +30,7 @@ #include #include #include +#include // Check whether the display mode should be included in enumeration @@ -44,7 +45,7 @@ static GLFWbool modeIsGood(const XRRModeInfo* mi) static int calculateRefreshRate(const XRRModeInfo* mi) { if (mi->hTotal && mi->vTotal) - return (int) ((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal)); + return (int) round((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal)); else return 0; } @@ -95,9 +96,128 @@ static GLFWvidmode vidmodeFromModeInfo(const XRRModeInfo* mi, ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// +// Poll for changes in the set of connected monitors +// +void _glfwPollMonitorsX11(void) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + int i, j, disconnectedCount, screenCount = 0; + _GLFWmonitor** disconnected = NULL; + XineramaScreenInfo* screens = NULL; + XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, + _glfw.x11.root); + RROutput primary = XRRGetOutputPrimary(_glfw.x11.display, + _glfw.x11.root); + + if (_glfw.x11.xinerama.available) + screens = XineramaQueryScreens(_glfw.x11.display, &screenCount); + + disconnectedCount = _glfw.monitorCount; + if (disconnectedCount) + { + disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + memcpy(disconnected, + _glfw.monitors, + _glfw.monitorCount * sizeof(_GLFWmonitor*)); + } + + for (i = 0; i < sr->noutput; i++) + { + int type, widthMM, heightMM; + XRROutputInfo* oi; + XRRCrtcInfo* ci; + _GLFWmonitor* monitor; + + oi = XRRGetOutputInfo(_glfw.x11.display, sr, sr->outputs[i]); + if (oi->connection != RR_Connected || oi->crtc == None) + { + XRRFreeOutputInfo(oi); + continue; + } + + for (j = 0; j < disconnectedCount; j++) + { + if (disconnected[j] && + disconnected[j]->x11.output == sr->outputs[i]) + { + disconnected[j] = NULL; + break; + } + } + + if (j < disconnectedCount) + { + XRRFreeOutputInfo(oi); + continue; + } + + ci = XRRGetCrtcInfo(_glfw.x11.display, sr, oi->crtc); + if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) + { + widthMM = oi->mm_height; + heightMM = oi->mm_width; + } + else + { + widthMM = oi->mm_width; + heightMM = oi->mm_height; + } + + monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM); + monitor->x11.output = sr->outputs[i]; + monitor->x11.crtc = oi->crtc; + + for (j = 0; j < screenCount; j++) + { + if (screens[j].x_org == ci->x && + screens[j].y_org == ci->y && + screens[j].width == ci->width && + screens[j].height == ci->height) + { + monitor->x11.index = j; + break; + } + } + + if (monitor->x11.output == primary) + type = _GLFW_INSERT_FIRST; + else + type = _GLFW_INSERT_LAST; + + _glfwInputMonitor(monitor, GLFW_CONNECTED, type); + + XRRFreeOutputInfo(oi); + XRRFreeCrtcInfo(ci); + } + + XRRFreeScreenResources(sr); + + if (screens) + XFree(screens); + + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i]) + _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); + } + + free(disconnected); + } + else + { + const int widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen); + const int heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen); + + _glfwInputMonitor(_glfwAllocMonitor("Display", widthMM, heightMM), + GLFW_CONNECTED, + _GLFW_INSERT_FIRST); + } +} + // Set the current video mode for the specified monitor // -GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired) +void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired) { if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { @@ -112,9 +232,9 @@ GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired) best = _glfwChooseVideoMode(monitor, desired); _glfwPlatformGetVideoMode(monitor, ¤t); if (_glfwCompareVideoModes(¤t, best) == 0) - return GLFW_TRUE; + return; - sr = XRRGetScreenResources(_glfw.x11.display, _glfw.x11.root); + sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); @@ -150,16 +270,7 @@ GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired) XRRFreeOutputInfo(oi); XRRFreeCrtcInfo(ci); XRRFreeScreenResources(sr); - - if (!native) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Monitor mode list changed"); - return GLFW_FALSE; - } } - - return GLFW_TRUE; } // Restore the saved (original) video mode for the specified monitor @@ -174,7 +285,7 @@ void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor) if (monitor->x11.oldMode == None) return; - sr = XRRGetScreenResources(_glfw.x11.display, _glfw.x11.root); + sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); XRRSetCrtcConfig(_glfw.x11.display, @@ -198,117 +309,8 @@ void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -_GLFWmonitor** _glfwPlatformGetMonitors(int* count) +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) { - int i, j, k, found = 0; - _GLFWmonitor** monitors = NULL; - - *count = 0; - - if (_glfw.x11.randr.available) - { - int screenCount = 0; - XineramaScreenInfo* screens = NULL; - XRRScreenResources* sr = XRRGetScreenResources(_glfw.x11.display, - _glfw.x11.root); - RROutput primary = XRRGetOutputPrimary(_glfw.x11.display, - _glfw.x11.root); - - monitors = calloc(sr->noutput, sizeof(_GLFWmonitor*)); - - if (_glfw.x11.xinerama.available) - screens = XineramaQueryScreens(_glfw.x11.display, &screenCount); - - for (i = 0; i < sr->ncrtc; i++) - { - XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, - sr, sr->crtcs[i]); - - for (j = 0; j < ci->noutput; j++) - { - int widthMM, heightMM; - _GLFWmonitor* monitor; - XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, - sr, ci->outputs[j]); - if (oi->connection != RR_Connected) - { - XRRFreeOutputInfo(oi); - continue; - } - - if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) - { - widthMM = oi->mm_height; - heightMM = oi->mm_width; - } - else - { - widthMM = oi->mm_width; - heightMM = oi->mm_height; - } - - monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM); - monitor->x11.output = ci->outputs[j]; - monitor->x11.crtc = oi->crtc; - - for (k = 0; k < screenCount; k++) - { - if (screens[k].x_org == ci->x && - screens[k].y_org == ci->y && - screens[k].width == ci->width && - screens[k].height == ci->height) - { - monitor->x11.index = k; - break; - } - } - - XRRFreeOutputInfo(oi); - - found++; - monitors[found - 1] = monitor; - - if (ci->outputs[j] == primary) - _GLFW_SWAP_POINTERS(monitors[0], monitors[found - 1]); - } - - XRRFreeCrtcInfo(ci); - } - - XRRFreeScreenResources(sr); - - if (screens) - XFree(screens); - - if (found == 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: RandR monitor support seems broken"); - - _glfw.x11.randr.monitorBroken = GLFW_TRUE; - free(monitors); - monitors = NULL; - } - } - - if (!monitors) - { - monitors = calloc(1, sizeof(_GLFWmonitor*)); - monitors[0] = _glfwAllocMonitor("Display", - DisplayWidthMM(_glfw.x11.display, - _glfw.x11.screen), - DisplayHeightMM(_glfw.x11.display, - _glfw.x11.screen)); - found = 1; - } - - *count = found; - return monitors; -} - -GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) -{ - return first->x11.crtc == second->x11.crtc; } void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) @@ -331,6 +333,109 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) } } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = _glfw.x11.contentScaleX; + if (yscale) + *yscale = _glfw.x11.contentScaleY; +} + +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) +{ + int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0; + + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + XRRScreenResources* sr; + XRRCrtcInfo* ci; + + sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); + ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); + + areaX = ci->x; + areaY = ci->y; + + const XRRModeInfo* mi = getModeInfo(sr, ci->mode); + + if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) + { + areaWidth = mi->height; + areaHeight = mi->width; + } + else + { + areaWidth = mi->width; + areaHeight = mi->height; + } + + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + } + else + { + areaWidth = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); + areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); + } + + if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP) + { + Atom* extents = NULL; + Atom* desktop = NULL; + const unsigned long extentCount = + _glfwGetWindowPropertyX11(_glfw.x11.root, + _glfw.x11.NET_WORKAREA, + XA_CARDINAL, + (unsigned char**) &extents); + + if (_glfwGetWindowPropertyX11(_glfw.x11.root, + _glfw.x11.NET_CURRENT_DESKTOP, + XA_CARDINAL, + (unsigned char**) &desktop) > 0) + { + if (extentCount >= 4 && *desktop < extentCount / 4) + { + const int globalX = extents[*desktop * 4 + 0]; + const int globalY = extents[*desktop * 4 + 1]; + const int globalWidth = extents[*desktop * 4 + 2]; + const int globalHeight = extents[*desktop * 4 + 3]; + + if (areaX < globalX) + { + areaWidth -= globalX - areaX; + areaX = globalX; + } + + if (areaY < globalY) + { + areaHeight -= globalY - areaY; + areaY = globalY; + } + + if (areaX + areaWidth > globalX + globalWidth) + areaWidth = globalX - areaX + globalWidth; + if (areaY + areaHeight > globalY + globalHeight) + areaHeight = globalY - areaY + globalHeight; + } + } + + if (extents) + XFree(extents); + if (desktop) + XFree(desktop); + } + + if (xpos) + *xpos = areaX; + if (ypos) + *ypos = areaY; + if (width) + *width = areaWidth; + if (height) + *height = areaHeight; +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { GLFWvidmode* result; @@ -412,7 +517,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) } } -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) { @@ -423,13 +528,13 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) _glfwAllocGammaArrays(ramp, size); - memcpy(ramp->red, gamma->red, size * sizeof(unsigned short)); + memcpy(ramp->red, gamma->red, size * sizeof(unsigned short)); memcpy(ramp->green, gamma->green, size * sizeof(unsigned short)); - memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); + memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); XRRFreeGamma(gamma); + return GLFW_TRUE; } -#if defined(_GLFW_HAS_XF86VM) else if (_glfw.x11.vidmode.available) { int size; @@ -440,24 +545,36 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) XF86VidModeGetGammaRamp(_glfw.x11.display, _glfw.x11.screen, ramp->size, ramp->red, ramp->green, ramp->blue); + return GLFW_TRUE; + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Gamma ramp access not supported by server"); + return GLFW_FALSE; } -#endif /*_GLFW_HAS_XF86VM*/ } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) { + if (XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc) != ramp->size) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Gamma ramp size must match current ramp size"); + return; + } + XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size); - memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); + memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short)); - memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short)); + memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short)); XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma); XRRFreeGamma(gamma); } -#if defined(_GLFW_HAS_XF86VM) else if (_glfw.x11.vidmode.available) { XF86VidModeSetGammaRamp(_glfw.x11.display, @@ -467,7 +584,11 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) (unsigned short*) ramp->green, (unsigned short*) ramp->blue); } -#endif /*_GLFW_HAS_XF86VM*/ + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Gamma ramp access not supported by server"); + } } diff --git a/external/glfw/x11_platform.h b/external/glfw/x11_platform.h index 074ab70..3b2b2b2 100644 --- a/external/glfw/x11_platform.h +++ b/external/glfw/x11_platform.h @@ -2,7 +2,7 @@ // GLFW 3.3 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,9 +25,6 @@ // //======================================================================== -#ifndef _glfw3_x11_platform_h_ -#define _glfw3_x11_platform_h_ - #include #include #include @@ -47,15 +44,84 @@ // The Xinerama extension provides legacy monitor indices #include -#if defined(_GLFW_HAS_XF86VM) - // The Xf86VidMode extension provides fallback gamma control - #include -#endif +// The XInput extension provides raw mouse motion input +#include + +typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int); +typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*); +typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*); +typedef void (* PFN_XRRFreeOutputInfo)(XRROutputInfo*); +typedef void (* PFN_XRRFreeScreenResources)(XRRScreenResources*); +typedef XRRCrtcGamma* (* PFN_XRRGetCrtcGamma)(Display*,RRCrtc); +typedef int (* PFN_XRRGetCrtcGammaSize)(Display*,RRCrtc); +typedef XRRCrtcInfo* (* PFN_XRRGetCrtcInfo) (Display*,XRRScreenResources*,RRCrtc); +typedef XRROutputInfo* (* PFN_XRRGetOutputInfo)(Display*,XRRScreenResources*,RROutput); +typedef RROutput (* PFN_XRRGetOutputPrimary)(Display*,Window); +typedef XRRScreenResources* (* PFN_XRRGetScreenResourcesCurrent)(Display*,Window); +typedef Bool (* PFN_XRRQueryExtension)(Display*,int*,int*); +typedef Status (* PFN_XRRQueryVersion)(Display*,int*,int*); +typedef void (* PFN_XRRSelectInput)(Display*,Window,int); +typedef Status (* PFN_XRRSetCrtcConfig)(Display*,XRRScreenResources*,RRCrtc,Time,int,int,RRMode,Rotation,RROutput*,int); +typedef void (* PFN_XRRSetCrtcGamma)(Display*,RRCrtc,XRRCrtcGamma*); +typedef int (* PFN_XRRUpdateConfiguration)(XEvent*); +#define XRRAllocGamma _glfw.x11.randr.AllocGamma +#define XRRFreeCrtcInfo _glfw.x11.randr.FreeCrtcInfo +#define XRRFreeGamma _glfw.x11.randr.FreeGamma +#define XRRFreeOutputInfo _glfw.x11.randr.FreeOutputInfo +#define XRRFreeScreenResources _glfw.x11.randr.FreeScreenResources +#define XRRGetCrtcGamma _glfw.x11.randr.GetCrtcGamma +#define XRRGetCrtcGammaSize _glfw.x11.randr.GetCrtcGammaSize +#define XRRGetCrtcInfo _glfw.x11.randr.GetCrtcInfo +#define XRRGetOutputInfo _glfw.x11.randr.GetOutputInfo +#define XRRGetOutputPrimary _glfw.x11.randr.GetOutputPrimary +#define XRRGetScreenResourcesCurrent _glfw.x11.randr.GetScreenResourcesCurrent +#define XRRQueryExtension _glfw.x11.randr.QueryExtension +#define XRRQueryVersion _glfw.x11.randr.QueryVersion +#define XRRSelectInput _glfw.x11.randr.SelectInput +#define XRRSetCrtcConfig _glfw.x11.randr.SetCrtcConfig +#define XRRSetCrtcGamma _glfw.x11.randr.SetCrtcGamma +#define XRRUpdateConfiguration _glfw.x11.randr.UpdateConfiguration + +typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int); +typedef void (* PFN_XcursorImageDestroy)(XcursorImage*); +typedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const XcursorImage*); +#define XcursorImageCreate _glfw.x11.xcursor.ImageCreate +#define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy +#define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor + +typedef Bool (* PFN_XineramaIsActive)(Display*); +typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*); +typedef XineramaScreenInfo* (* PFN_XineramaQueryScreens)(Display*,int*); +#define XineramaIsActive _glfw.x11.xinerama.IsActive +#define XineramaQueryExtension _glfw.x11.xinerama.QueryExtension +#define XineramaQueryScreens _glfw.x11.xinerama.QueryScreens typedef XID xcb_window_t; typedef XID xcb_visualid_t; typedef struct xcb_connection_t xcb_connection_t; -typedef xcb_connection_t* (* XGETXCBCONNECTION_T)(Display*); +typedef xcb_connection_t* (* PFN_XGetXCBConnection)(Display*); +#define XGetXCBConnection _glfw.x11.x11xcb.GetXCBConnection + +typedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*); +typedef Bool (* PFN_XF86VidModeGetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*); +typedef Bool (* PFN_XF86VidModeSetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*); +typedef Bool (* PFN_XF86VidModeGetGammaRampSize)(Display*,int,int*); +#define XF86VidModeQueryExtension _glfw.x11.vidmode.QueryExtension +#define XF86VidModeGetGammaRamp _glfw.x11.vidmode.GetGammaRamp +#define XF86VidModeSetGammaRamp _glfw.x11.vidmode.SetGammaRamp +#define XF86VidModeGetGammaRampSize _glfw.x11.vidmode.GetGammaRampSize + +typedef Status (* PFN_XIQueryVersion)(Display*,int*,int*); +typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); +#define XIQueryVersion _glfw.x11.xi.QueryVersion +#define XISelectEvents _glfw.x11.xi.SelectEvents + +typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*); +typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*); +typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const*); +#define XRenderQueryExtension _glfw.x11.xrender.QueryExtension +#define XRenderQueryVersion _glfw.x11.xrender.QueryVersion +#define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat typedef VkFlags VkXlibSurfaceCreateFlagsKHR; typedef VkFlags VkXcbSurfaceCreateFlagsKHR; @@ -83,12 +149,17 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(V typedef VkResult (APIENTRY *PFN_vkCreateXcbSurfaceKHR)(VkInstance,const VkXcbSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t); -#include "posix_tls.h" +#include "posix_thread.h" #include "posix_time.h" -#include "linux_joystick.h" #include "xkb_unicode.h" #include "glx_context.h" #include "egl_context.h" +#include "osmesa_context.h" +#if defined(__linux__) +#include "linux_joystick.h" +#else +#include "null_joystick.h" +#endif #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) @@ -112,6 +183,11 @@ typedef struct _GLFWwindowX11 XIC ic; GLFWbool overrideRedirect; + GLFWbool iconified; + GLFWbool maximized; + + // Whether the visual supports framebuffer transparency + GLFWbool transparent; // Cached position and size used to filter out duplicate events int width, height; @@ -122,8 +198,7 @@ typedef struct _GLFWwindowX11 // The last position the cursor was warped to by GLFW int warpCursorPosX, warpCursorPosY; - // The information from the last KeyPress event - unsigned int lastKeyCode; + // The time of the last KeyPress event Time lastKeyTime; } _GLFWwindowX11; @@ -136,22 +211,28 @@ typedef struct _GLFWlibraryX11 int screen; Window root; + // System content scale + float contentScaleX, contentScaleY; + // Helper window for IPC + Window helperWindowHandle; // Invisible cursor for hidden cursor mode - Cursor cursor; + Cursor hiddenCursorHandle; // Context for mapping window XIDs to _GLFWwindow pointers XContext context; // XIM input method XIM im; // Most recent error code received by X error handler int errorCode; + // Primary selection string (while the primary selection is owned) + char* primarySelectionString; // Clipboard string (while the selection is owned) char* clipboardString; // Key name string - char keyName[64]; + char keyName[5]; // X11 keycode to GLFW key LUT - short int publicKeys[256]; + short int keycodes[256]; // GLFW key to X11 keycode LUT - short int nativeKeys[GLFW_KEY_LAST + 1]; + short int scancodes[GLFW_KEY_LAST + 1]; // Where to place the cursor when re-enabled double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active @@ -173,8 +254,13 @@ typedef struct _GLFWlibraryX11 Atom NET_WM_STATE_FULLSCREEN; Atom NET_WM_STATE_MAXIMIZED_VERT; Atom NET_WM_STATE_MAXIMIZED_HORZ; + Atom NET_WM_STATE_DEMANDS_ATTENTION; Atom NET_WM_BYPASS_COMPOSITOR; Atom NET_WM_FULLSCREEN_MONITORS; + Atom NET_WM_WINDOW_OPACITY; + Atom NET_WM_CM_Sx; + Atom NET_WORKAREA; + Atom NET_CURRENT_DESKTOP; Atom NET_ACTIVE_WINDOW; Atom NET_FRAME_EXTENTS; Atom NET_REQUEST_FRAME_EXTENTS; @@ -187,14 +273,17 @@ typedef struct _GLFWlibraryX11 Atom XdndStatus; Atom XdndActionCopy; Atom XdndDrop; - Atom XdndLeave; Atom XdndFinished; Atom XdndSelection; + Atom XdndTypeList; + Atom text_uri_list; // Selection (clipboard) atoms Atom TARGETS; Atom MULTIPLE; + Atom INCR; Atom CLIPBOARD; + Atom PRIMARY; Atom CLIPBOARD_MANAGER; Atom SAVE_TARGETS; Atom NULL_; @@ -205,12 +294,30 @@ typedef struct _GLFWlibraryX11 struct { GLFWbool available; + void* handle; int eventBase; int errorBase; int major; int minor; GLFWbool gammaBroken; GLFWbool monitorBroken; + PFN_XRRAllocGamma AllocGamma; + PFN_XRRFreeCrtcInfo FreeCrtcInfo; + PFN_XRRFreeGamma FreeGamma; + PFN_XRRFreeOutputInfo FreeOutputInfo; + PFN_XRRFreeScreenResources FreeScreenResources; + PFN_XRRGetCrtcGamma GetCrtcGamma; + PFN_XRRGetCrtcGammaSize GetCrtcGammaSize; + PFN_XRRGetCrtcInfo GetCrtcInfo; + PFN_XRRGetOutputInfo GetOutputInfo; + PFN_XRRGetOutputPrimary GetOutputPrimary; + PFN_XRRGetScreenResourcesCurrent GetScreenResourcesCurrent; + PFN_XRRQueryExtension QueryExtension; + PFN_XRRQueryVersion QueryVersion; + PFN_XRRSelectInput SelectInput; + PFN_XRRSetCrtcConfig SetCrtcConfig; + PFN_XRRSetCrtcGamma SetCrtcGamma; + PFN_XRRUpdateConfiguration UpdateConfiguration; } randr; struct { @@ -232,27 +339,67 @@ typedef struct _GLFWlibraryX11 } saver; struct { + int version; Window source; + Atom format; } xdnd; + struct { + void* handle; + PFN_XcursorImageCreate ImageCreate; + PFN_XcursorImageDestroy ImageDestroy; + PFN_XcursorImageLoadCursor ImageLoadCursor; + } xcursor; + struct { GLFWbool available; + void* handle; int major; int minor; + PFN_XineramaIsActive IsActive; + PFN_XineramaQueryExtension QueryExtension; + PFN_XineramaQueryScreens QueryScreens; } xinerama; struct { void* handle; - XGETXCBCONNECTION_T XGetXCBConnection; + PFN_XGetXCBConnection GetXCBConnection; } x11xcb; -#if defined(_GLFW_HAS_XF86VM) struct { GLFWbool available; + void* handle; int eventBase; int errorBase; + PFN_XF86VidModeQueryExtension QueryExtension; + PFN_XF86VidModeGetGammaRamp GetGammaRamp; + PFN_XF86VidModeSetGammaRamp SetGammaRamp; + PFN_XF86VidModeGetGammaRampSize GetGammaRampSize; } vidmode; -#endif /*_GLFW_HAS_XF86VM*/ + + struct { + GLFWbool available; + void* handle; + int majorOpcode; + int eventBase; + int errorBase; + int major; + int minor; + PFN_XIQueryVersion QueryVersion; + PFN_XISelectEvents SelectEvents; + } xi; + + struct { + GLFWbool available; + void* handle; + int major; + int minor; + int eventBase; + int errorBase; + PFN_XRenderQueryExtension QueryExtension; + PFN_XRenderQueryVersion QueryVersion; + PFN_XRenderFindVisualFormat FindVisualFormat; + } xrender; } _GLFWlibraryX11; @@ -279,7 +426,8 @@ typedef struct _GLFWcursorX11 } _GLFWcursorX11; -GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired); +void _glfwPollMonitorsX11(void); +void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor); Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot); @@ -288,9 +436,11 @@ unsigned long _glfwGetWindowPropertyX11(Window window, Atom property, Atom type, unsigned char** value); +GLFWbool _glfwIsVisualTransparentX11(Visual* visual); void _glfwGrabErrorHandlerX11(void); void _glfwReleaseErrorHandlerX11(void); void _glfwInputErrorX11(int error, const char* message); -#endif // _glfw3_x11_platform_h_ +void _glfwPushSelectionToManagerX11(void); + diff --git a/external/glfw/x11_window.c b/external/glfw/x11_window.c index 7542321..f66c49b 100644 --- a/external/glfw/x11_window.c +++ b/external/glfw/x11_window.c @@ -2,7 +2,7 @@ // GLFW 3.3 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -48,6 +48,8 @@ #define Button6 6 #define Button7 7 +#define _GLFW_XDND_VERSION 5 + // Wait for data to arrive using select // This avoids blocking other threads via the per-display Xlib lock that also @@ -59,16 +61,19 @@ static GLFWbool waitForEvent(double* timeout) const int fd = ConnectionNumber(_glfw.x11.display); int count = fd + 1; - FD_ZERO(&fds); - FD_SET(fd, &fds); #if defined(__linux__) - FD_SET(_glfw.linux_js.inotify, &fds); - - if (fd < _glfw.linux_js.inotify) - count = _glfw.linux_js.inotify + 1; + if (_glfw.linjs.inotify > fd) + count = _glfw.linjs.inotify + 1; #endif for (;;) { + FD_ZERO(&fds); + FD_SET(fd, &fds); +#if defined(__linux__) + if (_glfw.linjs.inotify > 0) + FD_SET(_glfw.linjs.inotify, &fds); +#endif + if (timeout) { const long seconds = (long) *timeout; @@ -130,7 +135,9 @@ static int getWindowState(_GLFWwindow* window) result = state->state; } - XFree(state); + if (state) + XFree(state); + return result; } @@ -138,6 +145,9 @@ static int getWindowState(_GLFWwindow* window) // static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer) { + if (event->xany.window != _glfw.x11.helperWindowHandle) + return False; + return event->type == SelectionRequest || event->type == SelectionNotify || event->type == SelectionClear; @@ -154,27 +164,15 @@ static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointe event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS; } -// Translates a GLFW standard cursor to a font cursor shape +// Returns whether it is a property event for the specified selection transfer // -static int translateCursorShape(int shape) +static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer pointer) { - switch (shape) - { - case GLFW_ARROW_CURSOR: - return XC_left_ptr; - case GLFW_IBEAM_CURSOR: - return XC_xterm; - case GLFW_CROSSHAIR_CURSOR: - return XC_crosshair; - case GLFW_HAND_CURSOR: - return XC_hand1; - case GLFW_HRESIZE_CURSOR: - return XC_sb_h_double_arrow; - case GLFW_VRESIZE_CURSOR: - return XC_sb_v_double_arrow; - } - - return 0; + XEvent* notification = (XEvent*) pointer; + return event->type == PropertyNotify && + event->xproperty.state == PropertyNewValue && + event->xproperty.window == notification->xselection.requestor && + event->xproperty.atom == notification->xselection.property; } // Translates an X event modifier state mask @@ -191,6 +189,10 @@ static int translateState(int state) mods |= GLFW_MOD_ALT; if (state & Mod4Mask) mods |= GLFW_MOD_SUPER; + if (state & LockMask) + mods |= GLFW_MOD_CAPS_LOCK; + if (state & Mod2Mask) + mods |= GLFW_MOD_NUM_LOCK; return mods; } @@ -203,24 +205,7 @@ static int translateKey(int scancode) if (scancode < 0 || scancode > 255) return GLFW_KEY_UNKNOWN; - return _glfw.x11.publicKeys[scancode]; -} - -// Return the GLFW window corresponding to the specified X11 window -// -static _GLFWwindow* findWindowByHandle(Window handle) -{ - _GLFWwindow* window; - - if (XFindContext(_glfw.x11.display, - handle, - _glfw.x11.context, - (XPointer*) &window) != 0) - { - return NULL; - } - - return window; + return _glfw.x11.keycodes[scancode]; } // Sends an EWMH or ICCCM event to the window manager @@ -343,6 +328,7 @@ static void updateWindowMode(_GLFWwindow* window) } // Enable compositor bypass + if (!window->x11.transparent) { const unsigned long value = 1; @@ -381,6 +367,7 @@ static void updateWindowMode(_GLFWwindow* window) } // Disable compositor bypass + if (!window->x11.transparent) { XDeleteProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_BYPASS_COMPOSITOR); @@ -407,7 +394,12 @@ static char** parseUriList(char* text, int* count) continue; if (strncmp(line, prefix, strlen(prefix)) == 0) + { line += strlen(prefix); + // TODO: Validate hostname + while (*line != '/') + line++; + } (*count)++; @@ -434,13 +426,79 @@ static char** parseUriList(char* text, int* count) return paths; } -// Centers the cursor over the window client area +// Encode a Unicode code point to a UTF-8 stream +// Based on cutef8 by Jeff Bezanson (Public Domain) // -static void centerCursor(_GLFWwindow* window) +static size_t encodeUTF8(char* s, unsigned int ch) { - int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); - _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); + size_t count = 0; + + if (ch < 0x80) + s[count++] = (char) ch; + else if (ch < 0x800) + { + s[count++] = (ch >> 6) | 0xc0; + s[count++] = (ch & 0x3f) | 0x80; + } + else if (ch < 0x10000) + { + s[count++] = (ch >> 12) | 0xe0; + s[count++] = ((ch >> 6) & 0x3f) | 0x80; + s[count++] = (ch & 0x3f) | 0x80; + } + else if (ch < 0x110000) + { + s[count++] = (ch >> 18) | 0xf0; + s[count++] = ((ch >> 12) & 0x3f) | 0x80; + s[count++] = ((ch >> 6) & 0x3f) | 0x80; + s[count++] = (ch & 0x3f) | 0x80; + } + + return count; +} + +// Decode a Unicode code point from a UTF-8 stream +// Based on cutef8 by Jeff Bezanson (Public Domain) +// +#if defined(X_HAVE_UTF8_STRING) +static unsigned int decodeUTF8(const char** s) +{ + unsigned int ch = 0, count = 0; + static const unsigned int offsets[] = + { + 0x00000000u, 0x00003080u, 0x000e2080u, + 0x03c82080u, 0xfa082080u, 0x82082080u + }; + + do + { + ch = (ch << 6) + (unsigned char) **s; + (*s)++; + count++; + } while ((**s & 0xc0) == 0x80); + + assert(count <= 6); + return ch - offsets[count - 1]; +} +#endif /*X_HAVE_UTF8_STRING*/ + +// Convert the specified Latin-1 string to UTF-8 +// +static char* convertLatin1toUTF8(const char* source) +{ + size_t size = 1; + const char* sp; + + for (sp = source; *sp; sp++) + size += (*sp & 0x80) ? 2 : 1; + + char* target = calloc(size, 1); + char* tp = target; + + for (sp = source; *sp; sp++) + tp += encodeUTF8(tp, *sp); + + return target; } // Updates the cursor image according to its cursor mode @@ -458,7 +516,75 @@ static void updateCursorImage(_GLFWwindow* window) XUndefineCursor(_glfw.x11.display, window->x11.handle); } else - XDefineCursor(_glfw.x11.display, window->x11.handle, _glfw.x11.cursor); + { + XDefineCursor(_glfw.x11.display, window->x11.handle, + _glfw.x11.hiddenCursorHandle); + } +} + +// Enable XI2 raw mouse motion events +// +static void enableRawMouseMotion(_GLFWwindow* window) +{ + XIEventMask em; + unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; + + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISetMask(mask, XI_RawMotion); + + XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); +} + +// Disable XI2 raw mouse motion events +// +static void disableRawMouseMotion(_GLFWwindow* window) +{ + XIEventMask em; + unsigned char mask[] = { 0 }; + + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + + XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); +} + +// Apply disabled cursor mode to a focused window +// +static void disableCursor(_GLFWwindow* window) +{ + if (window->rawMouseMotion) + enableRawMouseMotion(window); + + _glfw.x11.disabledCursorWindow = window; + _glfwPlatformGetCursorPos(window, + &_glfw.x11.restoreCursorPosX, + &_glfw.x11.restoreCursorPosY); + updateCursorImage(window); + _glfwCenterCursorInContentArea(window); + XGrabPointer(_glfw.x11.display, window->x11.handle, True, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, + window->x11.handle, + _glfw.x11.hiddenCursorHandle, + CurrentTime); +} + +// Exit disabled cursor mode for the specified window +// +static void enableCursor(_GLFWwindow* window) +{ + if (window->rawMouseMotion) + disableRawMouseMotion(window); + + _glfw.x11.disabledCursorWindow = NULL; + XUngrabPointer(_glfw.x11.display, CurrentTime); + _glfwPlatformSetCursorPos(window, + _glfw.x11.restoreCursorPosX, + _glfw.x11.restoreCursorPosY); + updateCursorImage(window); } // Create the X11 window (and its colormap) @@ -467,12 +593,23 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, Visual* visual, int depth) { + int width = wndconfig->width; + int height = wndconfig->height; + + if (wndconfig->scaleToMonitor) + { + width *= _glfw.x11.contentScaleX; + height *= _glfw.x11.contentScaleY; + } + // Create a colormap based on the visual used by the current context window->x11.colormap = XCreateColormap(_glfw.x11.display, _glfw.x11.root, visual, AllocNone); + window->x11.transparent = _glfwIsVisualTransparentX11(visual); + // Create the actual window { XSetWindowAttributes wa; @@ -490,7 +627,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, window->x11.handle = XCreateWindow(_glfw.x11.display, _glfw.x11.root, 0, 0, - wndconfig->width, wndconfig->height, + width, height, 0, // Border width depth, // Color depth InputOutput, @@ -514,26 +651,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, } if (!wndconfig->decorated) - { - struct - { - unsigned long flags; - unsigned long functions; - unsigned long decorations; - long input_mode; - unsigned long status; - } hints; - - hints.flags = 2; // Set decorations - hints.decorations = 0; // No decorations - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.MOTIF_WM_HINTS, - _glfw.x11.MOTIF_WM_HINTS, 32, - PropModeReplace, - (unsigned char*) &hints, - sizeof(hints) / sizeof(long)); - } + _glfwPlatformSetWindowDecorated(window, GLFW_FALSE); if (_glfw.x11.NET_WM_STATE && !window->monitor) { @@ -553,6 +671,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, { states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT; states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ; + window->x11.maximized = GLFW_TRUE; } } @@ -578,7 +697,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, // Declare our PID { - const pid_t pid = getpid(); + const long pid = getpid(); XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_PID, XA_CARDINAL, 32, @@ -611,25 +730,41 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, XFree(hints); } - updateNormalHints(window, wndconfig->width, wndconfig->height); + updateNormalHints(window, width, height); // Set ICCCM WM_CLASS property - // HACK: Until a mechanism for specifying the application name is added, the - // initial window title is used as the window class name - if (strlen(wndconfig->title)) { XClassHint* hint = XAllocClassHint(); - hint->res_name = (char*) wndconfig->title; - hint->res_class = (char*) wndconfig->title; + + if (strlen(wndconfig->x11.instanceName) && + strlen(wndconfig->x11.className)) + { + hint->res_name = (char*) wndconfig->x11.instanceName; + hint->res_class = (char*) wndconfig->x11.className; + } + else + { + const char* resourceName = getenv("RESOURCE_NAME"); + if (resourceName && strlen(resourceName)) + hint->res_name = (char*) resourceName; + else if (strlen(wndconfig->title)) + hint->res_name = (char*) wndconfig->title; + else + hint->res_name = (char*) "glfw-application"; + + if (strlen(wndconfig->title)) + hint->res_class = (char*) wndconfig->title; + else + hint->res_class = (char*) "GLFW-Application"; + } XSetClassHint(_glfw.x11.display, window->x11.handle, hint); XFree(hint); } - if (_glfw.x11.XdndAware) + // Announce support for Xdnd (drag and drop) { - // Announce support for Xdnd (drag and drop) - const Atom version = 5; + const Atom version = _GLFW_XDND_VERSION; XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); @@ -660,11 +795,15 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, static Atom writeTargetToProperty(const XSelectionRequestEvent* request) { int i; - const Atom formats[] = { _glfw.x11.UTF8_STRING, - _glfw.x11.COMPOUND_STRING, - XA_STRING }; + char* selectionString = NULL; + const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING }; const int formatCount = sizeof(formats) / sizeof(formats[0]); + if (request->selection == _glfw.x11.PRIMARY) + selectionString = _glfw.x11.primarySelectionString; + else + selectionString = _glfw.x11.clipboardString; + if (request->property == None) { // The requester is a legacy client (ICCCM section 2.2) @@ -679,7 +818,6 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) const Atom targets[] = { _glfw.x11.TARGETS, _glfw.x11.MULTIPLE, _glfw.x11.UTF8_STRING, - _glfw.x11.COMPOUND_STRING, XA_STRING }; XChangeProperty(_glfw.x11.display, @@ -724,8 +862,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) targets[i], 8, PropModeReplace, - (unsigned char*) _glfw.x11.clipboardString, - strlen(_glfw.x11.clipboardString)); + (unsigned char *) selectionString, + strlen(selectionString)); } else targets[i + 1] = None; @@ -776,8 +914,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) request->target, 8, PropModeReplace, - (unsigned char*) _glfw.x11.clipboardString, - strlen(_glfw.x11.clipboardString)); + (unsigned char *) selectionString, + strlen(selectionString)); return request->property; } @@ -790,8 +928,16 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) static void handleSelectionClear(XEvent* event) { - free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = NULL; + if (event->xselectionclear.selection == _glfw.x11.PRIMARY) + { + free(_glfw.x11.primarySelectionString); + _glfw.x11.primarySelectionString = NULL; + } + else + { + free(_glfw.x11.clipboardString); + _glfw.x11.clipboardString = NULL; + } } static void handleSelectionRequest(XEvent* event) @@ -812,57 +958,151 @@ static void handleSelectionRequest(XEvent* event) XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply); } -static void pushSelectionToManager(_GLFWwindow* window) +static const char* getSelectionString(Atom selection) { - XConvertSelection(_glfw.x11.display, - _glfw.x11.CLIPBOARD_MANAGER, - _glfw.x11.SAVE_TARGETS, - None, - window->x11.handle, - CurrentTime); + size_t i; + char** selectionString = NULL; + const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING }; + const size_t targetCount = sizeof(targets) / sizeof(targets[0]); - for (;;) + if (selection == _glfw.x11.PRIMARY) + selectionString = &_glfw.x11.primarySelectionString; + else + selectionString = &_glfw.x11.clipboardString; + + if (XGetSelectionOwner(_glfw.x11.display, selection) == + _glfw.x11.helperWindowHandle) { - XEvent event; + // Instead of doing a large number of X round-trips just to put this + // string into a window property and then read it back, just return it + return *selectionString; + } - while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL)) + free(*selectionString); + *selectionString = NULL; + + for (i = 0; i < targetCount; i++) + { + char* data; + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + XEvent notification, dummy; + + XConvertSelection(_glfw.x11.display, + selection, + targets[i], + _glfw.x11.GLFW_SELECTION, + _glfw.x11.helperWindowHandle, + CurrentTime); + + while (!XCheckTypedWindowEvent(_glfw.x11.display, + _glfw.x11.helperWindowHandle, + SelectionNotify, + ¬ification)) { - switch (event.type) + waitForEvent(NULL); + } + + if (notification.xselection.property == None) + continue; + + XCheckIfEvent(_glfw.x11.display, + &dummy, + isSelPropNewValueNotify, + (XPointer) ¬ification); + + XGetWindowProperty(_glfw.x11.display, + notification.xselection.requestor, + notification.xselection.property, + 0, + LONG_MAX, + True, + AnyPropertyType, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + (unsigned char**) &data); + + if (actualType == _glfw.x11.INCR) + { + size_t size = 1; + char* string = NULL; + + for (;;) { - case SelectionRequest: - handleSelectionRequest(&event); - break; - - case SelectionClear: - handleSelectionClear(&event); - break; - - case SelectionNotify: + while (!XCheckIfEvent(_glfw.x11.display, + &dummy, + isSelPropNewValueNotify, + (XPointer) ¬ification)) { - if (event.xselection.target == _glfw.x11.SAVE_TARGETS) + waitForEvent(NULL); + } + + XFree(data); + XGetWindowProperty(_glfw.x11.display, + notification.xselection.requestor, + notification.xselection.property, + 0, + LONG_MAX, + True, + AnyPropertyType, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + (unsigned char**) &data); + + if (itemCount) + { + size += itemCount; + string = realloc(string, size); + string[size - itemCount - 1] = '\0'; + strcat(string, data); + } + + if (!itemCount) + { + if (targets[i] == XA_STRING) { - // This means one of two things; either the selection was - // not owned, which means there is no clipboard manager, or - // the transfer to the clipboard manager has completed - // In either case, it means we are done here - return; + *selectionString = convertLatin1toUTF8(string); + free(string); } + else + *selectionString = string; break; } } } + else if (actualType == targets[i]) + { + if (targets[i] == XA_STRING) + *selectionString = convertLatin1toUTF8(data); + else + *selectionString = _glfw_strdup(data); + } - waitForEvent(NULL); + XFree(data); + + if (*selectionString) + break; } + + if (!*selectionString) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "X11: Failed to convert selection to string"); + } + + return *selectionString; } // Make the specified window and its video mode active on its monitor // -static GLFWbool acquireMonitor(_GLFWwindow* window) +static void acquireMonitor(_GLFWwindow* window) { - GLFWbool status; - if (_glfw.x11.saver.count == 0) { // Remember old screen saver settings @@ -880,7 +1120,7 @@ static GLFWbool acquireMonitor(_GLFWwindow* window) if (!window->monitor->window) _glfw.x11.saver.count++; - status = _glfwSetVideoModeX11(window->monitor, &window->videoMode); + _glfwSetVideoModeX11(window->monitor, &window->videoMode); if (window->x11.overrideRedirect) { @@ -895,8 +1135,7 @@ static GLFWbool acquireMonitor(_GLFWwindow* window) xpos, ypos, mode.width, mode.height); } - _glfwInputMonitorWindowChange(window->monitor, window); - return status; + _glfwInputMonitorWindow(window->monitor, window); } // Remove the window and restore the original video mode @@ -906,7 +1145,7 @@ static void releaseMonitor(_GLFWwindow* window) if (window->monitor->window != window) return; - _glfwInputMonitorWindowChange(window->monitor, NULL); + _glfwInputMonitorWindow(window->monitor, NULL); _glfwRestoreVideoModeX11(window->monitor); _glfw.x11.saver.count--; @@ -922,31 +1161,6 @@ static void releaseMonitor(_GLFWwindow* window) } } -// Decode a Unicode code point from a UTF-8 stream -// Based on cutef8 by Jeff Bezanson (Public Domain) -// -#if defined(X_HAVE_UTF8_STRING) -static unsigned int decodeUTF8(const char** s) -{ - unsigned int ch = 0, count = 0; - static const unsigned int offsets[] = - { - 0x00000000u, 0x00003080u, 0x000e2080u, - 0x03c82080u, 0xfa082080u, 0x82082080u - }; - - do - { - ch = (ch << 6) + (unsigned char) **s; - (*s)++; - count++; - } while ((**s & 0xc0) == 0x80); - - assert(count <= 6); - return ch - offsets[count - 1]; -} -#endif /*X_HAVE_UTF8_STRING*/ - // Process the specified X event // static void processEvent(XEvent *event) @@ -967,19 +1181,67 @@ static void processEvent(XEvent *event) if (event->type == _glfw.x11.randr.eventBase + RRNotify) { XRRUpdateConfiguration(event); - _glfwInputMonitorChange(); + _glfwPollMonitorsX11(); return; } } - if (event->type != GenericEvent) + if (event->type == GenericEvent) { - window = findWindowByHandle(event->xany.window); - if (window == NULL) + if (_glfw.x11.xi.available) { - // This is an event for a window that has already been destroyed - return; + _GLFWwindow* window = _glfw.x11.disabledCursorWindow; + + if (window && + window->rawMouseMotion && + event->xcookie.extension == _glfw.x11.xi.majorOpcode && + XGetEventData(_glfw.x11.display, &event->xcookie) && + event->xcookie.evtype == XI_RawMotion) + { + XIRawEvent* re = event->xcookie.data; + if (re->valuators.mask_len) + { + const double* values = re->raw_values; + double xpos = window->virtualCursorPosX; + double ypos = window->virtualCursorPosY; + + if (XIMaskIsSet(re->valuators.mask, 0)) + { + xpos += *values; + values++; + } + + if (XIMaskIsSet(re->valuators.mask, 1)) + ypos += *values; + + _glfwInputCursorPos(window, xpos, ypos); + } + } + + XFreeEventData(_glfw.x11.display, &event->xcookie); } + + return; + } + + if (event->type == SelectionClear) + { + handleSelectionClear(event); + return; + } + else if (event->type == SelectionRequest) + { + handleSelectionRequest(event); + return; + } + + if (XFindContext(_glfw.x11.display, + event->xany.window, + _glfw.x11.context, + (XPointer*) &window) != 0) + { + // This is an event for a window that has already been destroyed + return; } switch (event->type) @@ -993,17 +1255,16 @@ static void processEvent(XEvent *event) if (window->x11.ic) { // HACK: Ignore duplicate key press events generated by ibus - // Corresponding release events are filtered out by the - // GLFW key repeat logic - if (window->x11.lastKeyCode != keycode || - window->x11.lastKeyTime != event->xkey.time) + // These have the same timestamp as the original event + // Corresponding release events are filtered out + // implicitly by the GLFW key repeat logic + if (window->x11.lastKeyTime < event->xkey.time) { if (keycode) _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); - } - window->x11.lastKeyCode = keycode; - window->x11.lastKeyTime = event->xkey.time; + window->x11.lastKeyTime = event->xkey.time; + } if (!filtered) { @@ -1040,8 +1301,10 @@ static void processEvent(XEvent *event) count = XwcLookupString(window->x11.ic, &event->xkey, - buffer, sizeof(buffer) / sizeof(wchar_t), - NULL, &status); + buffer, + sizeof(buffer) / sizeof(wchar_t), + NULL, + &status); if (status == XBufferOverflow) { @@ -1194,12 +1457,20 @@ static void processEvent(XEvent *event) case EnterNotify: { + // XEnterWindowEvent is XCrossingEvent + const int x = event->xcrossing.x; + const int y = event->xcrossing.y; + // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise // ignore the defined cursor for hidden cursor mode if (window->cursorMode == GLFW_CURSOR_HIDDEN) - _glfwPlatformSetCursorMode(window, GLFW_CURSOR_HIDDEN); + updateCursorImage(window); _glfwInputCursorEnter(window, GLFW_TRUE); + _glfwInputCursorPos(window, x, y); + + window->x11.lastCursorPosX = x; + window->x11.lastCursorPosY = y; return; } @@ -1214,7 +1485,8 @@ static void processEvent(XEvent *event) const int x = event->xmotion.x; const int y = event->xmotion.y; - if (x != window->x11.warpCursorPosX || y != window->x11.warpCursorPosY) + if (x != window->x11.warpCursorPosX || + y != window->x11.warpCursorPosY) { // The cursor was moved by something other than GLFW @@ -1222,6 +1494,8 @@ static void processEvent(XEvent *event) { if (_glfw.x11.disabledCursorWindow != window) return; + if (window->rawMouseMotion) + return; const int dx = x - window->x11.lastCursorPosX; const int dy = y - window->x11.lastCursorPosY; @@ -1291,14 +1565,15 @@ static void processEvent(XEvent *event) if (protocol == _glfw.x11.WM_DELETE_WINDOW) { - // The window manager was asked to close the window, for example by - // the user pressing a 'close' window decoration button + // The window manager was asked to close the window, for + // example by the user pressing a 'close' window decoration + // button _glfwInputWindowCloseRequest(window); } else if (protocol == _glfw.x11.NET_WM_PING) { - // The window manager is pinging the application to ensure it's - // still responding to events + // The window manager is pinging the application to ensure + // it's still responding to events XEvent reply = *event; reply.xclient.window = _glfw.x11.root; @@ -1312,44 +1587,121 @@ static void processEvent(XEvent *event) else if (event->xclient.message_type == _glfw.x11.XdndEnter) { // A drag operation has entered the window - // TODO: Check if UTF-8 string is supported by the source + unsigned long i, count; + Atom* formats = NULL; + const GLFWbool list = event->xclient.data.l[1] & 1; + + _glfw.x11.xdnd.source = event->xclient.data.l[0]; + _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24; + _glfw.x11.xdnd.format = None; + + if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) + return; + + if (list) + { + count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source, + _glfw.x11.XdndTypeList, + XA_ATOM, + (unsigned char**) &formats); + } + else + { + count = 3; + formats = (Atom*) event->xclient.data.l + 2; + } + + for (i = 0; i < count; i++) + { + if (formats[i] == _glfw.x11.text_uri_list) + { + _glfw.x11.xdnd.format = _glfw.x11.text_uri_list; + break; + } + } + + if (list && formats) + XFree(formats); } else if (event->xclient.message_type == _glfw.x11.XdndDrop) { - // The drag operation has finished dropping on - // the window, ask to convert it to a UTF-8 string - _glfw.x11.xdnd.source = event->xclient.data.l[0]; - XConvertSelection(_glfw.x11.display, - _glfw.x11.XdndSelection, - _glfw.x11.UTF8_STRING, - _glfw.x11.XdndSelection, - window->x11.handle, CurrentTime); + // The drag operation has finished by dropping on the window + Time time = CurrentTime; + + if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) + return; + + if (_glfw.x11.xdnd.format) + { + if (_glfw.x11.xdnd.version >= 1) + time = event->xclient.data.l[2]; + + // Request the chosen format from the source window + XConvertSelection(_glfw.x11.display, + _glfw.x11.XdndSelection, + _glfw.x11.xdnd.format, + _glfw.x11.XdndSelection, + window->x11.handle, + time); + } + else if (_glfw.x11.xdnd.version >= 2) + { + XEvent reply; + memset(&reply, 0, sizeof(reply)); + + reply.type = ClientMessage; + reply.xclient.window = _glfw.x11.xdnd.source; + reply.xclient.message_type = _glfw.x11.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = window->x11.handle; + reply.xclient.data.l[1] = 0; // The drag was rejected + reply.xclient.data.l[2] = None; + + XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, + False, NoEventMask, &reply); + XFlush(_glfw.x11.display); + } } else if (event->xclient.message_type == _glfw.x11.XdndPosition) { // The drag operation has moved over the window - const int absX = (event->xclient.data.l[2] >> 16) & 0xFFFF; - const int absY = (event->xclient.data.l[2]) & 0xFFFF; - int x, y; + const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff; + const int yabs = (event->xclient.data.l[2]) & 0xffff; + Window dummy; + int xpos, ypos; - _glfwPlatformGetWindowPos(window, &x, &y); - _glfwInputCursorPos(window, absX - x, absY - y); + if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) + return; + + XTranslateCoordinates(_glfw.x11.display, + _glfw.x11.root, + window->x11.handle, + xabs, yabs, + &xpos, &ypos, + &dummy); + + _glfwInputCursorPos(window, xpos, ypos); - // Reply that we are ready to copy the dragged data XEvent reply; memset(&reply, 0, sizeof(reply)); reply.type = ClientMessage; - reply.xclient.window = event->xclient.data.l[0]; + reply.xclient.window = _glfw.x11.xdnd.source; reply.xclient.message_type = _glfw.x11.XdndStatus; reply.xclient.format = 32; reply.xclient.data.l[0] = window->x11.handle; - reply.xclient.data.l[1] = 1; // Always accept the dnd with no rectangle reply.xclient.data.l[2] = 0; // Specify an empty rectangle reply.xclient.data.l[3] = 0; - reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy; - XSendEvent(_glfw.x11.display, event->xclient.data.l[0], + if (_glfw.x11.xdnd.format) + { + // Reply that we are ready to copy the dragged data + reply.xclient.data.l[1] = 1; // Accept with no rectangle + if (_glfw.x11.xdnd.version >= 2) + reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy; + } + + XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, False, NoEventMask, &reply); XFlush(_glfw.x11.display); } @@ -1359,11 +1711,11 @@ static void processEvent(XEvent *event) case SelectionNotify: { - if (event->xselection.property) + if (event->xselection.property == _glfw.x11.XdndSelection) { // The converted data from the drag operation has arrived char* data; - const int result = + const unsigned long result = _glfwGetWindowPropertyX11(event->xselection.requestor, event->xselection.property, event->xselection.target, @@ -1381,23 +1733,26 @@ static void processEvent(XEvent *event) free(paths); } - XFree(data); + if (data) + XFree(data); - XEvent reply; - memset(&reply, 0, sizeof(reply)); + if (_glfw.x11.xdnd.version >= 2) + { + XEvent reply; + memset(&reply, 0, sizeof(reply)); - reply.type = ClientMessage; - reply.xclient.window = _glfw.x11.xdnd.source; - reply.xclient.message_type = _glfw.x11.XdndFinished; - reply.xclient.format = 32; - reply.xclient.data.l[0] = window->x11.handle; - reply.xclient.data.l[1] = result; - reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; + reply.type = ClientMessage; + reply.xclient.window = _glfw.x11.xdnd.source; + reply.xclient.message_type = _glfw.x11.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = window->x11.handle; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; - // Reply that all is well - XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, - False, NoEventMask, &reply); - XFlush(_glfw.x11.display); + XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, + False, NoEventMask, &reply); + XFlush(_glfw.x11.display); + } } return; @@ -1406,7 +1761,7 @@ static void processEvent(XEvent *event) case FocusIn: { if (window->cursorMode == GLFW_CURSOR_DISABLED) - _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); + disableCursor(window); if (event->xfocus.mode == NotifyGrab || event->xfocus.mode == NotifyUngrab) @@ -1426,7 +1781,7 @@ static void processEvent(XEvent *event) case FocusOut: { if (window->cursorMode == GLFW_CURSOR_DISABLED) - _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL); + enableCursor(window); if (event->xfocus.mode == NotifyGrab || event->xfocus.mode == NotifyUngrab) @@ -1454,41 +1809,43 @@ static void processEvent(XEvent *event) case PropertyNotify: { - if (event->xproperty.atom == _glfw.x11.WM_STATE && - event->xproperty.state == PropertyNewValue) + if (event->xproperty.state != PropertyNewValue) + return; + + if (event->xproperty.atom == _glfw.x11.WM_STATE) { const int state = getWindowState(window); - if (state == IconicState) + if (state != IconicState && state != NormalState) + return; + + const GLFWbool iconified = (state == IconicState); + if (window->x11.iconified != iconified) { if (window->monitor) - releaseMonitor(window); + { + if (iconified) + releaseMonitor(window); + else + acquireMonitor(window); + } - _glfwInputWindowIconify(window, GLFW_TRUE); + window->x11.iconified = iconified; + _glfwInputWindowIconify(window, iconified); } - else if (state == NormalState) + } + else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE) + { + const GLFWbool maximized = _glfwPlatformWindowMaximized(window); + if (window->x11.maximized != maximized) { - if (window->monitor) - acquireMonitor(window); - - _glfwInputWindowIconify(window, GLFW_FALSE); + window->x11.maximized = maximized; + _glfwInputWindowMaximize(window, maximized); } } return; } - case SelectionClear: - { - handleSelectionClear(event); - return; - } - - case SelectionRequest: - { - handleSelectionRequest(event); - return; - } - case DestroyNotify: return; } @@ -1524,12 +1881,66 @@ unsigned long _glfwGetWindowPropertyX11(Window window, &bytesAfter, value); - if (type != AnyPropertyType && actualType != type) - return 0; - return itemCount; } +GLFWbool _glfwIsVisualTransparentX11(Visual* visual) +{ + if (!_glfw.x11.xrender.available) + return GLFW_FALSE; + + XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual); + return pf && pf->direct.alphaMask; +} + +// Push contents of our selection to clipboard manager +// +void _glfwPushSelectionToManagerX11(void) +{ + XConvertSelection(_glfw.x11.display, + _glfw.x11.CLIPBOARD_MANAGER, + _glfw.x11.SAVE_TARGETS, + None, + _glfw.x11.helperWindowHandle, + CurrentTime); + + for (;;) + { + XEvent event; + + while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL)) + { + switch (event.type) + { + case SelectionRequest: + handleSelectionRequest(&event); + break; + + case SelectionClear: + handleSelectionClear(&event); + break; + + case SelectionNotify: + { + if (event.xselection.target == _glfw.x11.SAVE_TARGETS) + { + // This means one of two things; either the selection + // was not owned, which means there is no clipboard + // manager, or the transfer to the clipboard manager has + // completed + // In either case, it means we are done here + return; + } + + break; + } + } + } + + waitForEvent(NULL); + } +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -1543,27 +1954,34 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, Visual* visual; int depth; - if (ctxconfig->client == GLFW_NO_API) - { - visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); - depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); - } - else + if (ctxconfig->client != GLFW_NO_API) { if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) { if (!_glfwInitGLX()) return GLFW_FALSE; - if (!_glfwChooseVisualGLX(ctxconfig, fbconfig, &visual, &depth)) + if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwInitEGL()) return GLFW_FALSE; - if (!_glfwChooseVisualEGL(ctxconfig, fbconfig, &visual, &depth)) + if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth)) return GLFW_FALSE; } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + } + } + + if (ctxconfig->client == GLFW_NO_API || + ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); + depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); } if (!createNativeWindow(window, wndconfig, visual, depth)) @@ -1576,21 +1994,23 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } if (window->monitor) { _glfwPlatformShowWindow(window); updateWindowMode(window); - if (!acquireMonitor(window)) - return GLFW_FALSE; - - centerCursor(window); + acquireMonitor(window); } XFlush(_glfw.x11.display); @@ -1616,12 +2036,6 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) if (window->x11.handle) { - if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == - window->x11.handle) - { - pushSelectionToManager(window); - } - XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context); XUnmapWindow(_glfw.x11.display, window->x11.handle); XDestroyWindow(_glfw.x11.display, window->x11.handle); @@ -1864,6 +2278,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, XFree(extents); } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = _glfw.x11.contentScaleX; + if (yscale) + *yscale = _glfw.x11.contentScaleY; +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { if (window->x11.overrideRedirect) @@ -1944,6 +2367,15 @@ void _glfwPlatformHideWindow(_GLFWwindow* window) XFlush(_glfw.x11.display); } +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION, + 0, 1, 0); +} + void _glfwPlatformFocusWindow(_GLFWwindow* window) { if (_glfw.x11.NET_ACTIVE_WINDOW) @@ -1973,28 +2405,37 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, } else { + if (!window->resizable) + updateNormalHints(window, width, height); + XMoveResizeWindow(_glfw.x11.display, window->x11.handle, xpos, ypos, width, height); } + XFlush(_glfw.x11.display); return; } if (window->monitor) releaseMonitor(window); - _glfwInputWindowMonitorChange(window, monitor); + _glfwInputWindowMonitor(window, monitor); updateNormalHints(window, width, height); - updateWindowMode(window); if (window->monitor) { - XMapRaised(_glfw.x11.display, window->x11.handle); - if (waitForVisibilityNotify(window)) - acquireMonitor(window); + if (!_glfwPlatformWindowVisible(window)) + { + XMapRaised(_glfw.x11.display, window->x11.handle); + waitForVisibilityNotify(window); + } + + updateWindowMode(window); + acquireMonitor(window); } else { + updateWindowMode(window); XMoveResizeWindow(_glfw.x11.display, window->x11.handle, xpos, ypos, width, height); } @@ -2028,6 +2469,14 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) Atom* states; unsigned long i; GLFWbool maximized = GLFW_FALSE; + + if (!_glfw.x11.NET_WM_STATE || + !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || + !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + return maximized; + } + const unsigned long count = _glfwGetWindowPropertyX11(window->x11.handle, _glfw.x11.NET_WM_STATE, @@ -2044,24 +2493,225 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) } } - XFree(states); + if (states) + XFree(states); + return maximized; } +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + Window w = _glfw.x11.root; + while (w) + { + Window root; + int rootX, rootY, childX, childY; + unsigned int mask; + + if (!XQueryPointer(_glfw.x11.display, w, + &root, &w, &rootX, &rootY, &childX, &childY, &mask)) + { + return GLFW_FALSE; + } + + if (w == window->x11.handle) + return GLFW_TRUE; + } + + return GLFW_FALSE; +} + +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + if (!window->x11.transparent) + return GLFW_FALSE; + + return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None; +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + updateNormalHints(window, width, height); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + if (enabled) + { + XDeleteProperty(_glfw.x11.display, + window->x11.handle, + _glfw.x11.MOTIF_WM_HINTS); + } + else + { + struct + { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; + } hints; + + hints.flags = 2; // Set decorations + hints.decorations = 0; // No decorations + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.MOTIF_WM_HINTS, + _glfw.x11.MOTIF_WM_HINTS, 32, + PropModeReplace, + (unsigned char*) &hints, + sizeof(hints) / sizeof(long)); + } +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE) + return; + + if (_glfwPlatformWindowVisible(window)) + { + const Atom action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + action, + _glfw.x11.NET_WM_STATE_ABOVE, + 0, 1, 0); + } + else + { + Atom* states; + unsigned long i, count; + + count = _glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_STATE, + XA_ATOM, + (unsigned char**) &states); + if (!states) + return; + + if (enabled) + { + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) + break; + } + + if (i == count) + { + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeAppend, + (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE, + 1); + } + } + else + { + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) + { + states[i] = states[count - 1]; + count--; + } + } + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (unsigned char*) &states, count); + } + + XFree(states); + } + + XFlush(_glfw.x11.display); +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + float opacity = 1.f; + + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx)) + { + CARD32* value = NULL; + + if (_glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_WINDOW_OPACITY, + XA_CARDINAL, + (unsigned char**) &value)) + { + opacity = (float) (*value / (double) 0xffffffffu); + } + + if (value) + XFree(value); + } + + return opacity; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ + const CARD32 value = (CARD32) (0xffffffffu * (double) opacity); + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*) &value, 1); +} + +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +{ + if (!_glfw.x11.xi.available) + return; + + if (_glfw.x11.disabledCursorWindow != window) + return; + + if (enabled) + enableRawMouseMotion(window); + else + disableRawMouseMotion(window); +} + +GLFWbool _glfwPlatformRawMouseMotionSupported(void) +{ + return _glfw.x11.xi.available; +} + void _glfwPlatformPollEvents(void) { - _glfwPollJoystickEvents(); + _GLFWwindow* window; - int count = XPending(_glfw.x11.display); - while (count--) +#if defined(__linux__) + _glfwDetectJoystickConnectionLinux(); +#endif + XPending(_glfw.x11.display); + + while (XQLength(_glfw.x11.display)) { XEvent event; XNextEvent(_glfw.x11.display, &event); processEvent(&event); } - if (_glfw.x11.disabledCursorWindow) - centerCursor(_glfw.x11.disabledCursorWindow); + window = _glfw.x11.disabledCursorWindow; + if (window) + { + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + + // NOTE: Re-center the cursor only if it has moved since the last call, + // to avoid breaking glfwWaitEvents with MotionNotify + if (window->x11.lastCursorPosX != width / 2 || + window->x11.lastCursorPosY != height / 2) + { + _glfwPlatformSetCursorPos(window, width / 2, height / 2); + } + } XFlush(_glfw.x11.display); } @@ -2088,15 +2738,14 @@ void _glfwPlatformWaitEventsTimeout(double timeout) void _glfwPlatformPostEmptyEvent(void) { XEvent event; - _GLFWwindow* window = _glfw.windowListHead; memset(&event, 0, sizeof(event)); event.type = ClientMessage; - event.xclient.window = window->x11.handle; + event.xclient.window = _glfw.x11.helperWindowHandle; event.xclient.format = 32; // Data is 32-bit longs event.xclient.message_type = _glfw.x11.NULL_; - XSendEvent(_glfw.x11.display, window->x11.handle, False, 0, &event); + XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event); XFlush(_glfw.x11.display); } @@ -2132,57 +2781,43 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { if (mode == GLFW_CURSOR_DISABLED) { - _glfw.x11.disabledCursorWindow = window; - _glfwPlatformGetCursorPos(window, - &_glfw.x11.restoreCursorPosX, - &_glfw.x11.restoreCursorPosY); - centerCursor(window); - XGrabPointer(_glfw.x11.display, window->x11.handle, True, - ButtonPressMask | ButtonReleaseMask | PointerMotionMask, - GrabModeAsync, GrabModeAsync, - window->x11.handle, _glfw.x11.cursor, CurrentTime); + if (_glfwPlatformWindowFocused(window)) + disableCursor(window); } else if (_glfw.x11.disabledCursorWindow == window) - { - _glfw.x11.disabledCursorWindow = NULL; - XUngrabPointer(_glfw.x11.display, CurrentTime); - _glfwPlatformSetCursorPos(window, - _glfw.x11.restoreCursorPosX, - _glfw.x11.restoreCursorPosY); - } + enableCursor(window); + else + updateCursorImage(window); - updateCursorImage(window); XFlush(_glfw.x11.display); } -const char* _glfwPlatformGetKeyName(int key, int scancode) +const char* _glfwPlatformGetScancodeName(int scancode) { - KeySym keysym; - int extra; - if (!_glfw.x11.xkb.available) return NULL; - if (key != GLFW_KEY_UNKNOWN) - scancode = _glfw.x11.nativeKeys[key]; - - if (!_glfwIsPrintable(_glfw.x11.publicKeys[scancode])) - return NULL; - - keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0); + const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0); if (keysym == NoSymbol) - return NULL; - - XkbTranslateKeySym(_glfw.x11.display, &keysym, 0, - _glfw.x11.keyName, sizeof(_glfw.x11.keyName), - &extra); - - if (!strlen(_glfw.x11.keyName)) return NULL; + const long ch = _glfwKeySym2Unicode(keysym); + if (ch == -1) + return NULL; + + const size_t count = encodeUTF8(_glfw.x11.keyName, (unsigned int) ch); + if (count == 0) + return NULL; + + _glfw.x11.keyName[count] = '\0'; return _glfw.x11.keyName; } +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.x11.scancodes[key]; +} + int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) @@ -2196,8 +2831,24 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { - cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, - translateCursorShape(shape)); + int native = 0; + + if (shape == GLFW_ARROW_CURSOR) + native = XC_left_ptr; + else if (shape == GLFW_IBEAM_CURSOR) + native = XC_xterm; + else if (shape == GLFW_CROSSHAIR_CURSOR) + native = XC_crosshair; + else if (shape == GLFW_HAND_CURSOR) + native = XC_hand2; + else if (shape == GLFW_HRESIZE_CURSOR) + native = XC_sb_h_double_arrow; + else if (shape == GLFW_VRESIZE_CURSOR) + native = XC_sb_v_double_arrow; + else + return GLFW_FALSE; + + cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native); if (!cursor->x11.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -2223,108 +2874,48 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) } } -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +void _glfwPlatformSetClipboardString(const char* string) { free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = strdup(string); + _glfw.x11.clipboardString = _glfw_strdup(string); XSetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD, - window->x11.handle, CurrentTime); + _glfw.x11.helperWindowHandle, + CurrentTime); if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) != - window->x11.handle) + _glfw.x11.helperWindowHandle) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to become owner of clipboard selection"); } } -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +const char* _glfwPlatformGetClipboardString(void) { - size_t i; - const Atom formats[] = { _glfw.x11.UTF8_STRING, - _glfw.x11.COMPOUND_STRING, - XA_STRING }; - const size_t formatCount = sizeof(formats) / sizeof(formats[0]); - - if (findWindowByHandle(XGetSelectionOwner(_glfw.x11.display, - _glfw.x11.CLIPBOARD))) - { - // Instead of doing a large number of X round-trips just to put this - // string into a window property and then read it back, just return it - return _glfw.x11.clipboardString; - } - - free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = NULL; - - for (i = 0; i < formatCount; i++) - { - char* data; - XEvent event; - - XConvertSelection(_glfw.x11.display, - _glfw.x11.CLIPBOARD, - formats[i], - _glfw.x11.GLFW_SELECTION, - window->x11.handle, CurrentTime); - - while (!XCheckTypedEvent(_glfw.x11.display, SelectionNotify, &event)) - waitForEvent(NULL); - - if (event.xselection.property == None) - continue; - - if (_glfwGetWindowPropertyX11(event.xselection.requestor, - event.xselection.property, - event.xselection.target, - (unsigned char**) &data)) - { - _glfw.x11.clipboardString = strdup(data); - } - - XFree(data); - - XDeleteProperty(_glfw.x11.display, - event.xselection.requestor, - event.xselection.property); - - if (_glfw.x11.clipboardString) - break; - } - - if (_glfw.x11.clipboardString == NULL) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "X11: Failed to convert clipboard to string"); - } - - return _glfw.x11.clipboardString; + return getSelectionString(_glfw.x11.CLIPBOARD); } -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { - char** extensions; - - *count = 0; + if (!_glfw.vk.KHR_surface) + return; if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle) { if (!_glfw.vk.KHR_xlib_surface) - return NULL; + return; } - extensions = calloc(2, sizeof(char*)); - extensions[0] = strdup("VK_KHR_surface"); + extensions[0] = "VK_KHR_surface"; + // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but + // not correctly implementing VK_KHR_xlib_surface if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) - extensions[1] = strdup("VK_KHR_xcb_surface"); + extensions[1] = "VK_KHR_xcb_surface"; else - extensions[1] = strdup("VK_KHR_xlib_surface"); - - *count = 2; - return extensions; + extensions[1] = "VK_KHR_xlib_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, @@ -2336,7 +2927,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) { - PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR = + PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR + vkGetPhysicalDeviceXcbPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) @@ -2346,8 +2938,7 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, return GLFW_FALSE; } - xcb_connection_t* connection = - _glfw.x11.x11xcb.XGetXCBConnection(_glfw.x11.display); + xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); if (!connection) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -2362,7 +2953,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, } else { - PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = + PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR + vkGetPhysicalDeviceXlibPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) @@ -2390,8 +2982,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, VkXcbSurfaceCreateInfoKHR sci; PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR; - xcb_connection_t* connection = - _glfw.x11.x11xcb.XGetXCBConnection(_glfw.x11.display); + xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); if (!connection) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -2473,3 +3064,29 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* handle) return window->x11.handle; } +GLFWAPI void glfwSetX11SelectionString(const char* string) +{ + _GLFW_REQUIRE_INIT(); + + free(_glfw.x11.primarySelectionString); + _glfw.x11.primarySelectionString = _glfw_strdup(string); + + XSetSelectionOwner(_glfw.x11.display, + _glfw.x11.PRIMARY, + _glfw.x11.helperWindowHandle, + CurrentTime); + + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) != + _glfw.x11.helperWindowHandle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to become owner of primary selection"); + } +} + +GLFWAPI const char* glfwGetX11SelectionString(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return getSelectionString(_glfw.x11.PRIMARY); +} + diff --git a/external/glfw/xkb_unicode.c b/external/glfw/xkb_unicode.c index e16a070..ad3cc23 100644 --- a/external/glfw/xkb_unicode.c +++ b/external/glfw/xkb_unicode.c @@ -2,7 +2,7 @@ // GLFW 3.3 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -826,9 +826,59 @@ static const struct codepair { { 0x13bd, 0x0153 }, { 0x13be, 0x0178 }, { 0x20ac, 0x20ac }, - // Numeric keypad with numlock on + { 0xfe50, '`' }, + { 0xfe51, 0x00b4 }, + { 0xfe52, '^' }, + { 0xfe53, '~' }, + { 0xfe54, 0x00af }, + { 0xfe55, 0x02d8 }, + { 0xfe56, 0x02d9 }, + { 0xfe57, 0x00a8 }, + { 0xfe58, 0x02da }, + { 0xfe59, 0x02dd }, + { 0xfe5a, 0x02c7 }, + { 0xfe5b, 0x00b8 }, + { 0xfe5c, 0x02db }, + { 0xfe5d, 0x037a }, + { 0xfe5e, 0x309b }, + { 0xfe5f, 0x309c }, + { 0xfe63, '/' }, + { 0xfe64, 0x02bc }, + { 0xfe65, 0x02bd }, + { 0xfe66, 0x02f5 }, + { 0xfe67, 0x02f3 }, + { 0xfe68, 0x02cd }, + { 0xfe69, 0xa788 }, + { 0xfe6a, 0x02f7 }, + { 0xfe6e, ',' }, + { 0xfe6f, 0x00a4 }, + { 0xfe80, 'a' }, // XK_dead_a + { 0xfe81, 'A' }, // XK_dead_A + { 0xfe82, 'e' }, // XK_dead_e + { 0xfe83, 'E' }, // XK_dead_E + { 0xfe84, 'i' }, // XK_dead_i + { 0xfe85, 'I' }, // XK_dead_I + { 0xfe86, 'o' }, // XK_dead_o + { 0xfe87, 'O' }, // XK_dead_O + { 0xfe88, 'u' }, // XK_dead_u + { 0xfe89, 'U' }, // XK_dead_U + { 0xfe8a, 0x0259 }, + { 0xfe8b, 0x018f }, + { 0xfe8c, 0x00b5 }, + { 0xfe90, '_' }, + { 0xfe91, 0x02c8 }, + { 0xfe92, 0x02cc }, { 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, - { 0xffbd /*XKB_KEY_KP_Equal*/, '=' }, + { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 }, + { 0xff96 /*XKB_KEY_KP_4*/, 0x0034 }, + { 0xff97 /*XKB_KEY_KP_8*/, 0x0038 }, + { 0xff98 /*XKB_KEY_KP_6*/, 0x0036 }, + { 0xff99 /*XKB_KEY_KP_2*/, 0x0032 }, + { 0xff9a /*XKB_KEY_KP_9*/, 0x0039 }, + { 0xff9b /*XKB_KEY_KP_3*/, 0x0033 }, + { 0xff9c /*XKB_KEY_KP_1*/, 0x0031 }, + { 0xff9d /*XKB_KEY_KP_5*/, 0x0035 }, + { 0xff9e /*XKB_KEY_KP_0*/, 0x0030 }, { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, { 0xffab /*XKB_KEY_KP_Add*/, '+' }, { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, @@ -844,7 +894,8 @@ static const struct codepair { { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, - { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 } + { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 }, + { 0xffbd /*XKB_KEY_KP_Equal*/, '=' } }; diff --git a/external/glfw/xkb_unicode.h b/external/glfw/xkb_unicode.h index 164a6fa..f95e14f 100644 --- a/external/glfw/xkb_unicode.h +++ b/external/glfw/xkb_unicode.h @@ -24,10 +24,5 @@ // //======================================================================== -#ifndef _glfw3_xkb_unicode_h_ -#define _glfw3_xkb_unicode_h_ - - long _glfwKeySym2Unicode(unsigned int keysym); -#endif // _glfw3_xkb_unicode_h_ diff --git a/external/imgui/LICENSE.txt b/external/imgui/LICENSE.txt new file mode 100644 index 0000000..3b439aa --- /dev/null +++ b/external/imgui/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2019 Omar Cornut + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/external/imgui/imconfig.h b/external/imgui/imconfig.h index afa5491..5d9caec 100644 --- a/external/imgui/imconfig.h +++ b/external/imgui/imconfig.h @@ -1,45 +1,58 @@ //----------------------------------------------------------------------------- // COMPILE-TIME OPTIONS FOR DEAR IMGUI -// Most options (memory allocation, clipboard callbacks, etc.) can be set at runtime via the ImGuiIO structure - ImGui::GetIO(). +// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. +// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. //----------------------------------------------------------------------------- -// A) You may edit imconfig.h (and not overwrite it when updating imgui, or maintain a patch/branch with your modifications to imconfig.h) -// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h" -// Note that options such as IMGUI_API, IM_VEC2_CLASS_EXTRA or ImDrawIdx needs to be defined consistently everywhere you include imgui.h, not only for the imgui*.cpp compilation units. +// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/branch with your modifications to imconfig.h) +// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h" +// If you do so you need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include +// the imgui*.cpp files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. +// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. +// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. //----------------------------------------------------------------------------- #pragma once //---- Define assertion handler. Defaults to calling assert(). //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) +//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts -//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows. +//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows +// Using dear imgui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. //#define IMGUI_API __declspec( dllexport ) //#define IMGUI_API __declspec( dllimport ) -//---- Don't define obsolete functions names. Consider enabling from time to time or when updating to reduce likelihood of using already obsolete function/names +//---- Don't define obsolete functions/enums names. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS -//---- Don't implement default handlers for Windows (so as not to link with certain functions) -//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // Don't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. -//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // Don't use and link with ImmGetContext/ImmSetCompositionWindow. - //---- Don't implement demo windows functionality (ShowDemoWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty) -//---- It is very strongly recommended to NOT disable the demo windows. Please read the comment at the top of imgui_demo.cpp. +// It is very strongly recommended to NOT disable the demo windows during development. Please read the comments in imgui_demo.cpp. //#define IMGUI_DISABLE_DEMO_WINDOWS +//#define IMGUI_DISABLE_METRICS_WINDOW -//---- Don't implement ImFormatString(), ImFormatStringV() so you can reimplement them yourself. -//#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS +//---- Don't implement some functions to reduce linkage requirements. +//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. +//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. +//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). +//#define IMGUI_DISABLE_OSX_FUNCTIONS // [OSX] Won't use and link with any OSX function (clipboard). +//#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf. +//#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h. +//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). //---- Include imgui_user.h at the end of imgui.h as a convenience //#define IMGUI_INCLUDE_IMGUI_USER_H -//---- Pack colors to BGRA8 instead of RGBA8 (if you needed to convert from one to another anyway) +//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) //#define IMGUI_USE_BGRA_PACKED_COLOR -//---- Implement STB libraries in a namespace to avoid linkage conflicts (defaults to global namespace) -//#define IMGUI_STB_NAMESPACE ImGuiStb +//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version +// By default the embedded implementations are declared static and not available outside of imgui cpp files. +//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" +//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" +//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION +//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION -//---- Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4. +//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. // This will be inlined as part of ImVec2 and ImVec4 class declarations. /* #define IM_VEC2_CLASS_EXTRA \ @@ -51,9 +64,18 @@ operator MyVec4() const { return MyVec4(x,y,z,w); } */ -//---- Use 32-bit vertex indices (instead of default 16-bit) to allow meshes with more than 64K vertices. Render function needs to support it. +//---- Using 32-bits vertex indices (default is 16-bits) is one way to allow large meshes with more than 64K vertices. +// Your renderer back-end will need to support it (most example renderer back-ends support both 16/32-bits indices). +// Another way to allow large meshes while keeping 16-bits indices is to handle ImDrawCmd::VtxOffset in your renderer. +// Read about ImGuiBackendFlags_RendererHasVtxOffset for details. //#define ImDrawIdx unsigned int +//---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly) +//struct ImDrawList; +//struct ImDrawCmd; +//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); +//#define ImDrawCallback MyImDrawCallback + //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. /* namespace ImGui diff --git a/external/imgui/imgui.cpp b/external/imgui/imgui.cpp index 775d61e..4d7ff31 100644 --- a/external/imgui/imgui.cpp +++ b/external/imgui/imgui.cpp @@ -1,59 +1,111 @@ -// dear imgui, v1.60 WIP +// dear imgui, v1.72 WIP // (main code and documentation) // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. // Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase. // Get latest version at https://github.com/ocornut/imgui // Releases change-log at https://github.com/ocornut/imgui/releases +// Technical Support for Getting Started https://discourse.dearimgui.org/c/getting-started // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269 + // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. +// See LICENSE.txt for copyright and licensing details (standard MIT License). // This library is free but I need your support to sustain development and maintenance. -// If you work for a company, please consider financial support, see Readme. For individuals: https://www.patreon.com/imgui +// Businesses: you can support continued maintenance and development via support contracts or sponsoring, see docs/README. +// Individuals: you can support continued maintenance and development via donations or Patreon https://www.patreon.com/imgui. + +// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. +// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without +// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't +// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you +// to a better solution or official support for them. /* - Index - - MISSION STATEMENT - - END-USER GUIDE - - PROGRAMMER GUIDE (read me!) - - Read first - - How to update to a newer version of Dear ImGui - - Getting started with integrating Dear ImGui in your code/engine - - Using gamepad/keyboard navigation [BETA] - - API BREAKING CHANGES (read me when you update!) - - ISSUES & TODO LIST - - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS - - How can I tell whether to dispatch mouse/keyboard to imgui or to my application? - - How can I display an image? What is ImTextureID, how does it works? - - How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on labels and the ID stack. - - How can I load a different font than the default? - - How can I easily use icons in my application? - - How can I load multiple fonts? - - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic? - - How can I use the drawing facilities without an ImGui window? (using ImDrawList API) - - I integrated Dear ImGui in my engine and the text or lines are blurry.. - - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. - - How can I help? - - ISSUES & TODO-LIST - - CODE +Index of this file: +DOCUMENTATION + +- MISSION STATEMENT +- END-USER GUIDE +- PROGRAMMER GUIDE (read me!) + - Read first. + - How to update to a newer version of Dear ImGui. + - Getting started with integrating Dear ImGui in your code/engine. + - This is how a simple application may look like (2 variations). + - This is how a simple rendering function may look like. + - Using gamepad/keyboard navigation controls. +- API BREAKING CHANGES (read me when you update!) +- FREQUENTLY ASKED QUESTIONS (FAQ), TIPS + - Where is the documentation? + - Which version should I get? + - Who uses Dear ImGui? + - Why the odd dual naming, "Dear ImGui" vs "ImGui"? + - How can I tell whether to dispatch mouse/keyboard to imgui or to my application? + - How can I display an image? What is ImTextureID, how does it works? + - Why are multiple widgets reacting when I interact with a single one? How can I have + multiple widgets with the same label or with an empty label? A primer on labels and the ID Stack... + - How can I use my own math types instead of ImVec2/ImVec4? + - How can I load a different font than the default? + - How can I easily use icons in my application? + - How can I load multiple fonts? + - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic? + - How can I interact with standard C++ types (such as std::string and std::vector)? + - How can I use the drawing facilities without a Dear ImGui window? (using ImDrawList API) + - How can I use Dear ImGui on a platform that doesn't have a mouse or a keyboard? (input share, remoting, gamepad) + - I integrated Dear ImGui in my engine and the text or lines are blurry.. + - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. + - How can I help? + +CODE +(search for "[SECTION]" in the code to find them) + +// [SECTION] FORWARD DECLARATIONS +// [SECTION] CONTEXT AND MEMORY ALLOCATORS +// [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) +// [SECTION] MISC HELPERS/UTILITIES (Maths, String, Format, Hash, File functions) +// [SECTION] MISC HELPERS/UTILITIES (ImText* functions) +// [SECTION] MISC HELPERS/UTILITIES (Color functions) +// [SECTION] ImGuiStorage +// [SECTION] ImGuiTextFilter +// [SECTION] ImGuiTextBuffer +// [SECTION] ImGuiListClipper +// [SECTION] RENDER HELPERS +// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) +// [SECTION] TOOLTIPS +// [SECTION] POPUPS +// [SECTION] KEYBOARD/GAMEPAD NAVIGATION +// [SECTION] COLUMNS +// [SECTION] DRAG AND DROP +// [SECTION] LOGGING/CAPTURING +// [SECTION] SETTINGS +// [SECTION] PLATFORM DEPENDENT HELPERS +// [SECTION] METRICS/DEBUG WINDOW + +*/ + +//----------------------------------------------------------------------------- +// DOCUMENTATION +//----------------------------------------------------------------------------- + +/* MISSION STATEMENT ================= - - Easy to use to create code-driven and data-driven tools - - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools - - Easy to hack and improve - - Minimize screen real-estate usage - - Minimize setup and maintenance - - Minimize state storage on user side - - Portable, minimize dependencies, run on target (consoles, phones, etc.) - - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window, opening a tree node - for the first time, etc. but a typical frame won't allocate anything) + - Easy to use to create code-driven and data-driven tools. + - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools. + - Easy to hack and improve. + - Minimize screen real-estate usage. + - Minimize setup and maintenance. + - Minimize state storage on user side. + - Portable, minimize dependencies, run on target (consoles, phones, etc.). + - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,. + opening a tree node for the first time, etc. but a typical frame should not allocate anything). Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes: - - Doesn't look fancy, doesn't animate - - Limited layout features, intricate layouts are typically crafted in code + - Doesn't look fancy, doesn't animate. + - Limited layout features, intricate layouts are typically crafted in code. END-USER GUIDE @@ -76,109 +128,162 @@ - ESCAPE to revert text to its original value. - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) - Controls are automatically adjusted for OSX to match standard OSX text editing operations. - - Gamepad navigation: see suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at goo.gl/9LgVZW. + - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard. + - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW PROGRAMMER GUIDE ================ - READ FIRST + READ FIRST: - Read the FAQ below this section! - - Your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention - on your side, no state duplication, less sync, less bugs. + - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction + or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs. - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. - - You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861 + - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build. + - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori). + You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md. + - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances. + For every application frame your UI code will be called only once. This is in contrast to e.g. Unity's own implementation of an IMGUI, + where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches. + - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. + - This codebase is also optimized to yield decent performances with typical "Debug" builds settings. + - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected). + If you get an assert, read the messages and comments around the assert. + - C++: this is a very C-ish codebase: we don't rely on C++11, we don't include any C++ headers, and ImGui:: is a namespace. + - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types. + See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that. + However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase. + - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!). - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI + HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI: - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h) - - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. - If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed from the public API. - If you have a problem with a missing function/symbols, search for its name in the code, there will likely be a comment about it. - Please report any issue to the GitHub page! + - Or maintain your own branch where you have imconfig.h modified. + - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. + If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed + from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will + likely be a comment about it. Please report any issue to the GitHub page! - Try to keep your copy of dear imgui reasonably up to date. - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE + GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE: - - Add the Dear ImGui source files to your projects, using your preferred build system. - It is recommended you build the .cpp files as part of your project and not as a library. - - You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types. - - See examples/ folder for standalone sample applications. - - You may be able to grab and copy a ready made imgui_impl_*** file from the examples/. + - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. + - Add the Dear ImGui source files to your projects or using your preferred build system. + It is recommended you build and statically link the .cpp files as part of your project and not as shared library (DLL). + - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types. - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them. + - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide. + Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" + phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render(). + - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code. + - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder. - - Init: retrieve the ImGuiIO structure with ImGui::GetIO() and fill the fields marked 'Settings': at minimum you need to set io.DisplaySize - (application resolution). Later on you will fill your keyboard mapping, clipboard handlers, and other advanced features but for a basic - integration you don't need to worry about it all. - - Init: call io.Fonts->GetTexDataAsRGBA32(...), it will build the font atlas texture, then load the texture pixels into graphics memory. - - Every frame: - - In your main loop as early a possible, fill the IO fields marked 'Input' (e.g. mouse position, buttons, keyboard info, etc.) - - Call ImGui::NewFrame() to begin the frame - - You can use any ImGui function you want between NewFrame() and Render() - - Call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your io.RenderDrawListFn handler. - (Even if you don't render, call Render() and ignore the callback, or call EndFrame() instead. Otherwhise some features will break) - - All rendering information are stored into command-lists until ImGui::Render() is called. - - Dear ImGui never touches or knows about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide. - - Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases - of your own application. - - Refer to the examples applications in the examples/ folder for instruction on how to setup your code. - - A minimal application skeleton may be: + HOW A SIMPLE APPLICATION MAY LOOK LIKE: + EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder). - // Application init + // Application init: create a dear imgui context, setup some options, load fonts ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); - io.DisplaySize.x = 1920.0f; - io.DisplaySize.y = 1280.0f; - // TODO: Fill others settings of the io structure later. + // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls. + // TODO: Fill optional fields of the io structure later. + // TODO: Load TTF/OTF fonts if you don't want to use the default font. - // Load texture atlas (there is a default font so you don't need to care about choosing a font yet) - unsigned char* pixels; + // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11) + ImGui_ImplWin32_Init(hwnd); + ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); + + // Application main loop + while (true) + { + // Feed inputs to dear imgui, start new frame + ImGui_ImplDX11_NewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); + + // Any application code here + ImGui::Text("Hello, world!"); + + // Render dear imgui into screen + ImGui::Render(); + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + g_pSwapChain->Present(1, 0); + } + + // Shutdown + ImGui_ImplDX11_Shutdown(); + ImGui_ImplWin32_Shutdown(); + ImGui::DestroyContext(); + + HOW A SIMPLE APPLICATION MAY LOOK LIKE: + EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE. + + // Application init: create a dear imgui context, setup some options, load fonts + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls. + // TODO: Fill optional fields of the io structure later. + // TODO: Load TTF/OTF fonts if you don't want to use the default font. + + // Build and load the texture atlas into a texture + // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer) int width, height; - io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height); - // TODO: At this points you've got the texture data and you need to upload that your your graphic system: - MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA) - // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'. This will be passed back to your via the renderer. + unsigned char* pixels = NULL; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + + // At this point you've got the texture data and you need to upload that your your graphic system: + // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'. + // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ below for details about ImTextureID. + MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32) io.Fonts->TexID = (void*)texture; // Application main loop while (true) { - // Setup low-level inputs (e.g. on Win32, GetKeyboardState(), or write to those fields from your Windows message loop handlers, etc.) - ImGuiIO& io = ImGui::GetIO(); - io.DeltaTime = 1.0f/60.0f; - io.MousePos = mouse_pos; - io.MouseDown[0] = mouse_button_0; - io.MouseDown[1] = mouse_button_1; + // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc. + // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings) + io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds) + io.DisplaySize.x = 1920.0f; // set the current display width + io.DisplaySize.y = 1280.0f; // set the current display height here + io.MousePos = my_mouse_pos; // set the mouse position + io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states + io.MouseDown[1] = my_mouse_buttons[1]; // Call NewFrame(), after this point you can use ImGui::* functions anytime + // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere) ImGui::NewFrame(); // Most of your application code here - MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); - MyGameRender(); // may use any ImGui functions as well! - - // Render & swap video buffers + ImGui::Text("Hello, world!"); + MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); + MyGameRender(); // may use any Dear ImGui functions as well! + + // Render dear imgui, swap buffers + // (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code) + ImGui::EndFrame(); ImGui::Render(); - MyImGuiRenderFunction(ImGui::GetDrawData()); + ImDrawData* draw_data = ImGui::GetDrawData(); + MyImGuiRenderFunction(draw_data); SwapBuffers(); } // Shutdown ImGui::DestroyContext(); + HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE: - - A minimal render function skeleton may be: - - void void MyRenderFunction(ImDrawData* draw_data) + void void MyImGuiRenderFunction(ImDrawData* draw_data) { // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled - // TODO: Setup viewport, orthographic projection matrix + // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize + // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color. for (int n = 0; n < draw_data->CmdListsCount; n++) { - const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by ImGui - const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by ImGui + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui + const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; @@ -188,16 +293,23 @@ } else { - // The texture for the draw call is specified by pcmd->TextureId. - // The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization. - MyEngineBindTexture(pcmd->TextureId); + // The texture for the draw call is specified by pcmd->TextureId. + // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. + MyEngineBindTexture((MyTexture*)pcmd->TextureId); - // We are using scissoring to clip some objects. All low-level graphics API supports it. - // If your engine doesn't support scissoring yet, you will get some small glitches (some elements outside their bounds) which you can fix later. - MyEngineScissor((int)pcmd->ClipRect.x, (int)pcmd->ClipRect.y, (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); + // We are using scissoring to clip some objects. All low-level graphics API should supports it. + // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches + // (some elements visible outside their bounds) but you can fix that once everything else works! + // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize) + // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize. + // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github), + // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space. + // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min) + ImVec2 pos = draw_data->DisplayPos; + MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y)); // Render 'pcmd->ElemCount/3' indexed triangles. - // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits if your engine doesn't support 16-bits indices. + // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits in imconfig.h if your engine doesn't support 16-bits indices. MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer); } idx_buffer += pcmd->ElemCount; @@ -205,40 +317,46 @@ } } - - The examples/ folders contains many functional implementation of the pseudo-code above. - - When calling NewFrame(), the 'io.WantCaptureMouse'/'io.WantCaptureKeyboard'/'io.WantTextInput' flags are updated. - They tell you if ImGui intends to use your inputs. So for example, if 'io.WantCaptureMouse' is set you would typically want to hide - mouse inputs from the rest of your application. Read the FAQ below for more information about those flags. + - The examples/ folders contains many actual implementation of the pseudo-codes above. + - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated. + They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the + rest of your application. In every cases you need to pass on the inputs to Dear ImGui. Refer to the FAQ for more information. + - Please read the FAQ below!. Amusingly, it is called a FAQ because people frequently run into the same issues! - USING GAMEPAD/KEYBOARD NAVIGATION [BETA] + USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS - - Ask questions and report issues at https://github.com/ocornut/imgui/issues/787 + - The gamepad/keyboard navigation is fairly functional and keeps being improved. + - Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse! + - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - - Keyboard: - - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. - - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag will be set. - For more advanced uses, you may want to read from: - - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). - - or query focus information with e.g. IsWindowFocused(), IsItemFocused() etc. functions. - Please reach out if you think the game vs navigation input sharing could be improved. - Gamepad: - - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). + - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. + - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame(). + Note that io.NavInputs[] is cleared by EndFrame(). - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values: 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. - Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, maybe a power curve, etc.). - - You can download PNG/PSD files depicting the gamepad controls for common controllers at: goo.gl/9LgVZW. - - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo to toggle the target. + Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.). + - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW. + - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo + to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. + - Keyboard: + - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. + NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. + - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag + will be set. For more advanced uses, you may want to read from: + - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. + - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). + - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions. Please reach out if you think the game vs navigation input sharing could be improved. - Mouse: - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. - - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (in your console/tablet/phone app) to share your PC mouse/keyboard. - - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavMoveMouse flag. - Enabling ImGuiConfigFlags_NavMoveMouse instructs dear imgui to move your mouse cursor along with navigation movements. - When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it wants the mouse cursor to be moved. + - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard. + - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag. + Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements. + When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved. When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that. - (If you set the NavMoveMouse flag but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will see your mouse as moving back and forth!) + (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!) (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want to set a boolean to ignore your other external mouse positions until the external source is moved again.) @@ -246,17 +364,66 @@ API BREAKING CHANGES ==================== - Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix. - Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. - Also read releases logs https://github.com/ocornut/imgui/releases for more details. + Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. + Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code. + When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. + You can read releases logs https://github.com/ocornut/imgui/releases for more details. - - 2018/03/03 (1.60) - Renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums. - - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment. + - 2019/06/14 (1.72) - removed redirecting functions/enums names that were marked obsolete in 1.51 (June 2017): ImGuiCol_Column*, ImGuiSetCond_*, IsItemHoveredRect(), IsPosHoveringAnyWindow(), IsMouseHoveringAnyWindow(), IsMouseHoveringWindow(), IMGUI_ONCE_UPON_A_FRAME. Grep this log for details and new names. + - 2019/06/07 (1.71) - rendering of child window outer decorations (bg color, border, scrollbars) is now performed as part of the parent window. If you have + overlapping child windows in a same parent, and relied on their relative z-order to be mapped to their submission order, this will affect your rendering. + This optimization is disabled if the parent window has no visual output, because it appears to be the most common situation leading to the creation of overlapping child windows. + Please reach out if you are affected. + - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete). + - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c). + - 2019/04/29 (1.70) - improved ImDrawList thick strokes (>1.0f) preserving correct thickness up to 90 degrees angles (e.g. rectangles). If you have custom rendering using thick lines, they will appear thicker now. + - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete). + - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete). + - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete). + - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with a dummy small value! + - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). + - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead! + - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Keep redirection typedef (will obsolete). + - 2018/12/20 (1.67) - made it illegal to call Begin("") with an empty string. This somehow half-worked before but had various undesirable side-effects. + - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags. + - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files. + - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete). + - 2018/09/06 (1.65) - renamed stb_truetype.h to imstb_truetype.h, stb_textedit.h to imstb_textedit.h, and stb_rect_pack.h to imstb_rectpack.h. + If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths. + - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427) + - 2018/08/31 (1.64) - added imgui_widgets.cpp file, extracted and moved widgets code out of imgui.cpp into imgui_widgets.cpp. Re-ordered some of the code remaining in imgui.cpp. + NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED. + Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions. + - 2018/08/22 (1.63) - renamed IsItemDeactivatedAfterChange() to IsItemDeactivatedAfterEdit() for consistency with new IsItemEdited() API. Kept redirection function (will obsolete soonish as IsItemDeactivatedAfterChange() is very recent). + - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete). + - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly). + - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges [update 1.67 renamed to ConfigWindowsResizeFromEdges] to enable the feature. + - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency. + - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time. + - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete). + - 2018/06/08 (1.62) - examples: the imgui_impl_xxx files have been split to separate platform (Win32, Glfw, SDL2, etc.) from renderer (DX11, OpenGL, Vulkan, etc.). + old bindings will still work as is, however prefer using the separated bindings as they will be updated to support multi-viewports. + when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call. + in particular, note that old bindings called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function. + - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set. + - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details. + - 2018/05/03 (1.61) - DragInt(): the default compile-time format string has been changed from "%.0f" to "%d", as we are not using integers internally any more. + If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format. + To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, giving time to users to upgrade their code. + If you have IMGUI_DISABLE_OBSOLETE_FUNCTIONS enabled, the code will instead assert! You may run a reg-exp search on your codebase for e.g. "DragInt.*%f" to help you find them. + - 2018/04/28 (1.61) - obsoleted InputFloat() functions taking an optional "int decimal_precision" in favor of an equivalent and more flexible "const char* format", + consistent with other functions. Kept redirection functions (will obsolete). + - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value. + - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch). + - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now. + - 2018/03/08 (1.60) - changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically. + - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums. + - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment. - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display. - 2018/02/07 (1.60) - reorganized context handling to be more explicit, - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END. - removed Shutdown() function, as DestroyContext() serve this purpose. - - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwhise CreateContext() will create its own font atlas instance. + - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance. - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts. - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts. - 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths. @@ -284,19 +451,23 @@ - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it. - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details. removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting. + IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly) + IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow) + IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior] - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead! - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete). - - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete). + - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Keep redirection typedef (will obsolete). + - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete). - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)". - - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)! + - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)! - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete). - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete). - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency. - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicily to fix. - - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame. + - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type. - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely. - - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete). - - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete). + - 2017/08/13 (1.51) - renamed ImGuiCol_Column to ImGuiCol_Separator, ImGuiCol_ColumnHovered to ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive to ImGuiCol_SeparatorActive. Kept redirection enums (will obsolete). + - 2017/08/11 (1.51) - renamed ImGuiSetCond_Always to ImGuiCond_Always, ImGuiSetCond_Once to ImGuiCond_Once, ImGuiSetCond_FirstUseEver to ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing to ImGuiCond_Appearing. Kept redirection enums (will obsolete). - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton(). - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu. - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options. @@ -308,9 +479,9 @@ - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc. - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal. - - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. - If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you. - However if your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. + - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. + If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you. + If your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color. ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { @@ -321,7 +492,7 @@ - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext(). - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen). - - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer. + - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDrawList::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer. - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337). - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337) - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). @@ -343,7 +514,7 @@ - the signature of the io.RenderDrawListsFn handler has changed! old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data). - argument: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount' + parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount' ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new. ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'. - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer. @@ -376,9 +547,9 @@ - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions. - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader. (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. - font init: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..> - became: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; - you now more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. + font init: { const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>; } + became: { unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; } + you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. it is now recommended that you sample the font texture with bilinear interpolation. (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID. (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) @@ -396,153 +567,289 @@ - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes - ISSUES & TODO-LIST - ================== - See TODO.txt - - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS ====================================== - Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application? - A: You can read the 'io.WantCaptureMouse'/'io.WantCaptureKeyboard'/'io.WantTextInput' flags from the ImGuiIO structure. + Q: Where is the documentation? + A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++. + - Run the examples/ and explore them. + - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function. + - The demo covers most features of Dear ImGui, so you can read the code and see its output. + - See documentation and comments at the top of imgui.cpp + effectively imgui.h. + - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the examples/ + folder to explain how to integrate Dear ImGui with your own engine/application. + - Your programming IDE is your friend, find the type or function declaration to find comments + associated to it. + + Q: Which version should I get? + A: I occasionally tag Releases (https://github.com/ocornut/imgui/releases) but it is generally safe + and recommended to sync to master/latest. The library is fairly stable and regressions tend to be + fixed fast when reported. You may also peak at the 'docking' branch which includes: + - Docking/Merging features (https://github.com/ocornut/imgui/issues/2109) + - Multi-viewport features (https://github.com/ocornut/imgui/issues/1542) + Many projects are using this branch and it is kept in sync with master regularly. + + Q: Who uses Dear ImGui? + A: See "Quotes" (https://github.com/ocornut/imgui/wiki/Quotes) and + "Software using Dear ImGui" (https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) Wiki pages + for a list of games/software which are publicly known to use dear imgui. Please add yours if you can! + + Q: Why the odd dual naming, "Dear ImGui" vs "ImGui"? + A: The library started its life as "ImGui" due to the fact that I didn't give it a proper name when + when I released 1.0, and had no particular expectation that it would take off. However, the term IMGUI + (immediate-mode graphical user interface) was coined before and is being used in variety of other + situations (e.g. Unity uses it own implementation of the IMGUI paradigm). + To reduce the ambiguity without affecting existing code bases, I have decided on an alternate, + longer name "Dear ImGui" that people can use to refer to this specific library. + Please try to refer to this library as "Dear ImGui". + + Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application? + A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } ) - When 'io.WantCaptureMouse' is set, imgui wants to use your mouse state, and you may want to discard/hide the inputs from the rest of your application. - When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application. - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS). - The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!). - It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs. - Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also - perfectly fine, as the bool toggle fairly rarely. - (Advanced note: text input releases focus on Return 'KeyDown', so the following Return 'KeyUp' event that your application receive will typically + Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false. + This is because imgui needs to detect that you clicked in the void to unfocus its own windows. + Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!). + It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs. + Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also + perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to UpdateHoveredWindowAndCaptureFlags(). + Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs - were for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.) + were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.) Q: How can I display an image? What is ImTextureID, how does it works? - A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function. - Dear ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry! - It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc. - At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render. - Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing. - (c++ tip: OpenGL uses integers to identify textures. You can safely store an integer into a void*, just cast it to void*, don't take it's address!) - To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions. - Dear ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use. - You may call ImGui::ShowMetricsWindow() to explore active draw lists and visualize/understand how the draw data is generated. - It is your responsibility to get textures uploaded to your GPU. + A: Short explanation: + - You may use functions such as ImGui::Image(), ImGui::ImageButton() or lower-level ImDrawList::AddImage() to emit draw calls that will use your own textures. + - Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as ImTextureID (void*) value. + - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason). + Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward. - Q: Can I have multiple widgets with the same label? Can I have widget without a label? - A: Yes. A primer on labels and the ID stack... + Long explanation: + - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices. + At the end of the frame those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code + to render them is generally fairly short (a few dozen lines). In the examples/ folder we provide functions for popular graphics API (OpenGL, DirectX, etc.). + - Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API. + We carry the information to identify a "texture" in the ImTextureID type. + ImTextureID is nothing more that a void*, aka 4/8 bytes worth of data: just enough to store 1 pointer or 1 integer of your choice. + Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function. + - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying + an image from the end-user perspective. This is what the _examples_ rendering functions are using: - - Elements that are typically not clickable, such as Text() items don't need an ID. + OpenGL: ImTextureID = GLuint (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp) + DirectX9: ImTextureID = LPDIRECT3DTEXTURE9 (see ImGui_ImplDX9_RenderDrawData() function in imgui_impl_dx9.cpp) + DirectX11: ImTextureID = ID3D11ShaderResourceView* (see ImGui_ImplDX11_RenderDrawData() function in imgui_impl_dx11.cpp) + DirectX12: ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE (see ImGui_ImplDX12_RenderDrawData() function in imgui_impl_dx12.cpp) - - Interactive widgets require state to be carried over multiple frames (most typically Dear ImGui often needs to remember what is - the "active" widget). to do so they need a unique ID. unique ID are typically derived from a string label, an integer index or a pointer. + For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID. + Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure + tying together both the texture and information about its format and how to read it. + - If you have a custom engine built over e.g. OpenGL, instead of passing GLuint around you may decide to use a high-level data type to carry information about + the texture as well as how to display it (shaders, etc.). The decision of what to use as ImTextureID can always be made better knowing how your codebase + is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them. + If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID + representation suggested by the example bindings is probably the best choice. + (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer) - Button("OK"); // Label = "OK", ID = hash of "OK" - Button("Cancel"); // Label = "Cancel", ID = hash of "Cancel" + User code may do: - - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK" - in two different windows or in two different locations of a tree. + // Cast our texture type to ImTextureID / void* + MyTexture* texture = g_CoffeeTableTexture; + ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height)); + + The renderer function called after ImGui::Render() will receive that same value that the user code passed: + + // Cast ImTextureID / void* stored in the draw command as our texture type + MyTexture* texture = (MyTexture*)pcmd->TextureId; + MyEngineBindTexture2D(texture); + + Once you understand this design you will understand that loading image files and turning them into displayable textures is not within the scope of Dear ImGui. + This is by design and is actually a good thing, because it means your code has full control over your data types and how you display them. + If you want to display an image file (e.g. PNG file) into the screen, please refer to documentation and tutorials for the graphics API you are using. + + Here's a simplified OpenGL example using stb_image.h: + + // Use stb_image.h to load a PNG from disk and turn it into raw RGBA pixel data: + #define STB_IMAGE_IMPLEMENTATION + #include + [...] + int my_image_width, my_image_height; + unsigned char* my_image_data = stbi_load("my_image.png", &my_image_width, &my_image_height, NULL, 4); + + // Turn the RGBA pixel data into an OpenGL texture: + GLuint my_opengl_texture; + glGenTextures(1, &my_opengl_texture); + glBindTexture(GL_TEXTURE_2D, my_opengl_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data); + + // Now that we have an OpenGL texture, assuming our imgui rendering function (imgui_impl_xxx.cpp file) takes GLuint as ImTextureID, we can display it: + ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height)); + + C/C++ tip: a void* is pointer-sized storage. You may safely store any pointer or integer into it by casting your value to ImTextureID / void*, and vice-versa. + Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTextureID / void*. + Examples: + + GLuint my_tex = XXX; + void* my_void_ptr; + my_void_ptr = (void*)(intptr_t)my_tex; // cast a GLuint into a void* (we don't take its address! we literally store the value inside the pointer) + my_tex = (GLuint)(intptr_t)my_void_ptr; // cast a void* into a GLuint + + ID3D11ShaderResourceView* my_dx11_srv = XXX; + void* my_void_ptr; + my_void_ptr = (void*)my_dx11_srv; // cast a ID3D11ShaderResourceView* into an opaque void* + my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr; // cast a void* into a ID3D11ShaderResourceView* + + Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated. + + Q: Why are multiple widgets reacting when I interact with a single one? + Q: How can I have multiple widgets with the same label or with an empty label? + A: A primer on labels and the ID Stack... + + Dear ImGui internally need to uniquely identify UI elements. + Elements that are typically not clickable (such as calls to the Text functions) don't need an ID. + Interactive widgets (such as calls to Button buttons) need a unique ID. + Unique ID are used internally to track active widgets and occasionally associate state to widgets. + Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element. + + - Unique ID are often derived from a string label: + + Button("OK"); // Label = "OK", ID = hash of (..., "OK") + Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel") + + - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having + two buttons labeled "OK" in different windows or different tree locations is fine. + We used "..." above to signify whatever was already pushed to the ID stack previously: + + Begin("MyWindow"); + Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK") + End(); + Begin("MyOtherWindow"); + Button("OK"); // Label = "OK", ID = hash of ("MyOtherWindow", "OK") + End(); - If you have a same ID twice in the same location, you'll have a conflict: Button("OK"); - Button("OK"); // ID collision! Both buttons will be treated as the same. + Button("OK"); // ID collision! Interacting with either button will trigger the first one. Fear not! this is easy to solve and there are many ways to solve it! - - When passing a label you can optionally specify extra unique ID information within string itself. + - Solving ID conflict in a simple/local context: + When passing a label you can optionally specify extra ID information within string itself. Use "##" to pass a complement to the ID that won't be visible to the end-user. - This helps solving the simple collision cases when you know which items are going to be created. + This helps solving the simple collision cases when you know e.g. at compilation time which items + are going to be created: - Button("Play"); // Label = "Play", ID = hash of "Play" - Button("Play##foo1"); // Label = "Play", ID = hash of "Play##foo1" (different from above) - Button("Play##foo2"); // Label = "Play", ID = hash of "Play##foo2" (different from above) + Begin("MyWindow"); + Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play") + Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above + Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above + End(); - If you want to completely hide the label, but still need an ID: - Checkbox("##On", &b); // Label = "", ID = hash of "##On" (no label!) + Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox! - - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels. - For example you may want to include varying information in a window title bar, but windows are uniquely identified by their ID.. - Use "###" to pass a label that isn't part of ID: + - Occasionally/rarely you might want change a label while preserving a constant ID. This allows + you to animate labels. For example you may want to include varying information in a window title bar, + but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID: - Button("Hello###ID"; // Label = "Hello", ID = hash of "ID" - Button("World###ID"; // Label = "World", ID = hash of "ID" (same as above) + Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "###ID") + Button("World###ID"); // Label = "World", ID = hash of (..., "###ID") // Same as above, even though the label looks different sprintf(buf, "My game (%f FPS)###MyGame", fps); - Begin(buf); // Variable label, ID = hash of "MyGame" + Begin(buf); // Variable title, ID = hash of "MyGame" - - Use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window. - This is the most convenient way of distinguishing ID if you are iterating and creating many UI elements. - You can push a pointer, a string or an integer value. Remember that ID are formed from the concatenation of _everything_ in the ID stack! + - Solving ID conflict in a more general manner: + Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts + within the same window. This is the most convenient way of distinguishing ID when iterating and + creating many UI elements programmatically. + You can push a pointer, a string or an integer value into the ID stack. + Remember that ID are formed from the concatenation of _everything_ pushed into the ID stack. + At each level of the stack we store the seed used for items at this level of the ID stack. + Begin("Window"); for (int i = 0; i < 100; i++) { - PushID(i); - Button("Click"); // Label = "Click", ID = hash of integer + "label" (unique) + PushID(i); // Push i to the id tack + Button("Click"); // Label = "Click", ID = hash of ("Window", i, "Click") PopID(); } - for (int i = 0; i < 100; i++) { MyObject* obj = Objects[i]; PushID(obj); - Button("Click"); // Label = "Click", ID = hash of pointer + "label" (unique) + Button("Click"); // Label = "Click", ID = hash of ("Window", obj pointer, "Click") PopID(); } - for (int i = 0; i < 100; i++) { MyObject* obj = Objects[i]; PushID(obj->Name); - Button("Click"); // Label = "Click", ID = hash of string + "label" (unique) + Button("Click"); // Label = "Click", ID = hash of ("Window", obj->Name, "Click") PopID(); } + End(); - - More example showing that you can stack multiple prefixes into the ID stack: + - You can stack multiple prefixes into the ID stack: - Button("Click"); // Label = "Click", ID = hash of "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "Click") PushID("node"); - Button("Click"); // Label = "Click", ID = hash of "node" + "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") PushID(my_ptr); - Button("Click"); // Label = "Click", ID = hash of "node" + ptr + "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click") PopID(); PopID(); - Tree nodes implicitly creates a scope for you by calling PushID(). - Button("Click"); // Label = "Click", ID = hash of "Click" - if (TreeNode("node")) + Button("Click"); // Label = "Click", ID = hash of (..., "Click") + if (TreeNode("node")) // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag) { - Button("Click"); // Label = "Click", ID = hash of "node" + "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") TreePop(); } - When working with trees, ID are used to preserve the open/close state of each tree node. Depending on your use cases you may want to use strings, indices or pointers as ID. - e.g. when displaying a single object that may change over time (dynamic 1-1 relationship), using a static string as ID will preserve your - node open/closed state when the targeted object change. - e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently. - experiment and see what makes more sense! + e.g. when following a single pointer that may change over time, using a static string as ID + will preserve your node open/closed state when the targeted object change. + e.g. when displaying a list of objects, using indices or pointers as ID will preserve the + node open/closed state differently. See what makes more sense in your situation! - Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13) + Q: How can I use my own math types instead of ImVec2/ImVec4? + A: You can edit imconfig.h and setup the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to add implicit type conversions. + This way you'll be able to use your own types everywhere, e.g. passing glm::vec2 to ImGui functions instead of ImVec2. + + Q: How can I load a different font than the default? A: Use the font atlas to load the TTF/OTF file you want: ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() + Default is ProggyClean.ttf, monospace, rendered at size 13, embedded in dear imgui's source code. + (Tip: monospace fonts are convenient because they allow to facilitate horizontal alignment directly at the string level.) + (Read the 'misc/fonts/README.txt' file for more details about font loading.) - New programmers: remember that in C/C++ and most programming languages if you want to use a backslash \ in a string literal you need to write a double backslash "\\": - io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels); // WRONG + New programmers: remember that in C/C++ and most programming languages if you want to use a + backslash \ within a string literal, you need to write it double backslash "\\": + io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels); // WRONG (you are escape the M here!) io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels); // CORRECT io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels); // ALSO CORRECT Q: How can I easily use icons in my application? - A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you main font. Then you can refer to icons within your - strings. Read 'How can I load multiple fonts?' and the file 'misc/fonts/README.txt' for instructions and useful header files. + A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you + main font. Then you can refer to icons within your strings. + You may want to see ImFontConfig::GlyphMinAdvanceX to make your icon look monospace to facilitate alignment. + (Read the 'misc/fonts/README.txt' file for more details about icons font loading.) + With some extra effort, you may use colorful icon by registering custom rectangle space inside the font atlas, + and copying your own graphics data into it. See misc/fonts/README.txt about using the AddCustomRectFontGlyph API. Q: How can I load multiple fonts? A: Use the font atlas to pack them into a single texture: - (Read misc/fonts/README.txt and the code in ImFontAtlas for more details.) + (Read the 'misc/fonts/README.txt' file and the code in ImFontAtlas for more details.) ImGuiIO& io = ImGui::GetIO(); ImFont* font0 = io.Fonts->AddFontDefault(); @@ -554,66 +861,112 @@ // Options ImFontConfig config; - config.OversampleH = 3; + config.OversampleH = 2; config.OversampleV = 1; - config.GlyphOffset.y -= 2.0f; // Move everything by 2 pixels up + config.GlyphOffset.y -= 1.0f; // Move everything by 1 pixels up config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters - io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config); + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, &config); // Combine multiple fonts into one (e.g. for icon fonts) - ImWchar ranges[] = { 0xf000, 0xf3ff, 0 }; + static ImWchar ranges[] = { 0xf000, 0xf3ff, 0 }; ImFontConfig config; config.MergeMode = true; io.Fonts->AddFontDefault(); - io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font - io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs + io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? - A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. + A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. // Add default Japanese ranges io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese()); - + // Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need) ImVector ranges; - ImFontAtlas::GlyphRangesBuilder builder; + ImFontGlyphRangesBuilder builder; builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters) builder.AddChar(0x7262); // Add a specific character builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted) io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data); - All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax. - Specifying literal in your source code using a local code page (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work! + All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8 + by using the u8"hello" syntax. Specifying literal in your source code using a local code page + (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work! Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8. - Text input: it is up to your application to pass the right character code to io.AddInputCharacter(). The applications in examples/ are doing that. - For languages using IME, on Windows you can copy the Hwnd of your application to io.ImeWindowHandle. - The default implementation of io.ImeSetInputScreenPosFn() on Windows will set your IME position correctly. + Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter(). + The applications in examples/ are doing that. + Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode). + You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state. + Windows: if your language is relying on an Input Method Editor (IME), you copy the HWND of your window to io.ImeWindowHandle in order for + the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly. + + Q: How can I interact with standard C++ types (such as std::string and std::vector)? + A: - Being highly portable (bindings for several languages, frameworks, programming style, obscure or older platforms/compilers), + and aiming for compatibility & performance suitable for every modern real-time game engines, dear imgui does not use + any of std C++ types. We use raw types (e.g. char* instead of std::string) because they adapt to more use cases. + - To use ImGui::InputText() with a std::string or any resizable string class, see misc/cpp/imgui_stdlib.h. + - To use combo boxes and list boxes with std::vector or any other data structure: the BeginCombo()/EndCombo() API + lets you iterate and submit items yourself, so does the ListBoxHeader()/ListBoxFooter() API. + Prefer using them over the old and awkward Combo()/ListBox() api. + - Generally for most high-level types you should be able to access the underlying data type. + You may write your own one-liner wrappers to facilitate user code (tip: add new functions in ImGui:: namespace from your code). + - Dear ImGui applications often need to make intensive use of strings. It is expected that many of the strings you will pass + to the API are raw literals (free in C/C++) or allocated in a manner that won't incur a large cost on your application. + Please bear in mind that using std::string on applications with large amount of UI may incur unsatisfactory performances. + Modern implementations of std::string often include small-string optimization (which is often a local buffer) but those + are not configurable and not the same across implementations. + - If you are finding your UI traversal cost to be too large, make sure your string usage is not leading to excessive amount + of heap allocations. Consider using literals, statically sized buffers and your own helper functions. A common pattern + is that you will need to build lots of strings on the fly, and their maximum length can be easily be scoped ahead. + One possible implementation of a helper to facilitate printf-style building of strings: https://github.com/ocornut/Str + This is a small helper where you can instance strings with configurable local buffers length. Many game engines will + provide similar or better string helpers. Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API) - A: - You can create a dummy window. Call SetNextWindowBgAlpha(0.0f), call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flags. + A: - You can create a dummy window. Call Begin() with the NoBackground | NoDecoration | NoSavedSettings | NoInputs flags. + (The ImGuiWindowFlags_NoDecoration flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse) Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like. - - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows. - - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData. + - You can call ImGui::GetBackgroundDrawList() or ImGui::GetForegroundDrawList() and use those draw list to display + contents behind or over every other imgui windows (one bg/fg drawlist per viewport). + - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create + your own ImDrawListSharedData, and then call your rendered code with your own ImDrawList or ImDrawData data. + + Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display) + A: - You can control Dear ImGui with a gamepad. Read about navigation in "Using gamepad/keyboard navigation controls". + (short version: map gamepad inputs into the io.NavInputs[] array + set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad) + - You can share your computer mouse seamlessly with your console/tablet/phone using Synergy (https://symless.com/synergy) + This is the preferred solution for developer productivity. + In particular, the "micro-synergy-client" repository (https://github.com/symless/micro-synergy-client) has simple + and portable source code (uSynergy.c/.h) for a small embeddable client that you can use on any platform to connect + to your host computer, based on the Synergy 1.x protocol. Make sure you download the Synergy 1 server on your computer. + Console SDK also sometimes provide equivalent tooling or wrapper for Synergy-like protocols. + - You may also use a third party solution such as Remote ImGui (https://github.com/JordiRos/remoteimgui) which sends + the vertices to render over the local network, allowing you to use Dear ImGui even on a screen-less machine. + - For touch inputs, you can increase the hit box of widgets (via the style.TouchPadding setting) to accommodate + for the lack of precision of touch inputs, but it is recommended you use a mouse or gamepad to allow optimizing + for screen real-estate and precision. Q: I integrated Dear ImGui in my engine and the text or lines are blurry.. A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f). Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension. Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. - A: You are probably mishandling the clipping rectangles in your render function. + A: You are probably mishandling the clipping rectangles in your render function. Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height). Q: How can I help? - A: - If you are experienced with Dear ImGui and C++, look at the github issues, or TODO.txt and see how you want/can help! - - Convince your company to fund development time! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README. - - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. - You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1269). Visuals are ideal as they inspire other programmers. + A: - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt + and see how you want to help and can help! + - Businesses: convince your company to fund development via support contracts/sponsoring! This is among the most useful thing you can do for dear imgui. + - Individuals: you can also become a Patron (http://www.patreon.com/imgui) or donate on PayPal! See README. + - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. + You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1902). Visuals are ideal as they inspire other programmers. But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions. - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on github or privately). - - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. + - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. this is also useful to set yourself in the context of another window (to get/set other settings) - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug". - tip: the ImGuiOnceUponAFrame helper will allow run the block of code only once a frame. You can use it to quickly add custom UI in the middle @@ -628,11 +981,12 @@ #endif #include "imgui.h" +#ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS +#endif #include "imgui_internal.h" -#include // toupper, isprint -#include // NULL, malloc, free, qsort, atoi +#include // toupper #include // vsnprintf, sscanf, printf #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier #include // intptr_t @@ -640,140 +994,145 @@ #include // intptr_t #endif -#define IMGUI_DEBUG_NAV_SCORING 0 -#define IMGUI_DEBUG_NAV_RECTS 0 +// Debug options +#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL +#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window // Visual Studio warnings #ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 4127) // condition expression is constant +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #endif -// Clang warnings with -Weverything -#ifdef __clang__ +// Clang/GCC warnings with -Weverything +#if defined(__clang__) #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning : unknown warning group '-Wformat-pedantic *' // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great! #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. #pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. -#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. +#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference is. #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // -#pragma clang diagnostic ignored "-Wformat-pedantic" // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic. -#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' // +#pragma clang diagnostic ignored "-Wformat-pedantic" // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic. +#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0 +#endif +#if __has_warning("-Wdouble-promotion") +#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. +#endif #elif defined(__GNUC__) +// We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association. +#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*' #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value -#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'xxxx' to type 'xxxx' casts away qualifiers #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false +#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #endif -// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall -#ifdef _MSC_VER -#define IMGUI_CDECL __cdecl -#else -#define IMGUI_CDECL -#endif +// When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. +static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in +static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear + +// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end) +static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow(). +static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. //------------------------------------------------------------------------- -// Forward Declarations +// [SECTION] FORWARD DECLARATIONS //------------------------------------------------------------------------- -static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true); - -static ImFont* GetDefaultFont(); static void SetCurrentWindow(ImGuiWindow* window); -static void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x); -static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y); -static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond); -static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond); -static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond); -static ImGuiWindow* FindHoveredWindow(); +static void FindHoveredWindow(); static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags); static void CheckStacksSize(ImGuiWindow* window, bool write); -static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); +static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges); static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); -static void AddWindowToDrawData(ImVector* out_list, ImGuiWindow* window); -static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); - -static ImGuiWindowSettings* AddWindowSettings(const char* name); - -static void LoadIniSettingsFromDisk(const char* ini_filename); -static void LoadIniSettingsFromMemory(const char* buf); -static void SaveIniSettingsToDisk(const char* ini_filename); -static void SaveIniSettingsToMemory(ImVector& out_buf); -static void MarkIniSettingsDirty(ImGuiWindow* window); +static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); static ImRect GetViewportRect(); -static void ClosePopupToLevel(int remaining); -static ImGuiWindow* GetFrontMostModalRootWindow(); - -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data); -static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); -static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); - -static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size); -static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size); -static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2); -static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format); - -namespace ImGui -{ -static void NavUpdate(); -static void NavUpdateWindowing(); -static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); - -static void UpdateMovingWindow(); -static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); -static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); -} - -//----------------------------------------------------------------------------- -// Platform dependent default implementations -//----------------------------------------------------------------------------- +// Settings +static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); +static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); +static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf); +// Platform Dependents default implementation for IO functions static const char* GetClipboardTextFn_DefaultImpl(void* user_data); static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); +namespace ImGui +{ +static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags); + +// Navigation +static void NavUpdate(); +static void NavUpdateWindowing(); +static void NavUpdateWindowingList(); +static void NavUpdateMoveResult(); +static float NavUpdatePageUpPageDown(int allowed_dir_flags); +static inline void NavUpdateAnyRequestFlag(); +static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id); +static ImVec2 NavCalcPreferredRefPos(); +static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); +static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); +static int FindWindowFocusIndex(ImGuiWindow* window); + +// Misc +static void UpdateMouseInputs(); +static void UpdateMouseWheel(); +static bool UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); +static void RenderWindowOuterBorders(ImGuiWindow* window); +static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); +static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); + +} + //----------------------------------------------------------------------------- -// Context +// [SECTION] CONTEXT AND MEMORY ALLOCATORS //----------------------------------------------------------------------------- -// Current context pointer. Implicitely used by all ImGui functions. Always assumed to be != NULL. -// CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext(). -// If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file. -// ImGui functions are not thread-safe because of this pointer. If you want thread-safety to allow N threads to access N different contexts, you can: -// - Change this variable to use thread local storage. You may #define GImGui in imconfig.h for that purpose. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 -// - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts) +// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL. +// ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext(). +// 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call +// SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading. +// In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into. +// 2) Important: Dear ImGui functions are not thread-safe because of this pointer. +// If you want thread-safety to allow N threads to access N different contexts, you can: +// - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h: +// struct ImGuiContext; +// extern thread_local ImGuiContext* MyImGuiTLS; +// #define GImGui MyImGuiTLS +// And then define MyImGuiTLS in one of your cpp file. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword. +// - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 +// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace. #ifndef GImGui ImGuiContext* GImGui = NULL; #endif // Memory Allocator functions. Use SetAllocatorFunctions() to change them. -// If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file. +// If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file. // Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction. #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS -static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; return malloc(size); } -static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; free(ptr); } +static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); } +static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); } #else -static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; (void)size; IM_ASSERT(0); return NULL; } -static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; (void)ptr; IM_ASSERT(0); } +static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; } +static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); } #endif static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper; static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper; static void* GImAllocatorUserData = NULL; -static size_t GImAllocatorActiveAllocationsCount = 0; //----------------------------------------------------------------------------- -// User facing structures +// [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) //----------------------------------------------------------------------------- ImGuiStyle::ImGuiStyle() @@ -784,6 +1143,7 @@ ImGuiStyle::ImGuiStyle() WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. WindowMinSize = ImVec2(32,32); // Minimum window size WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text + WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left. ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested. PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows @@ -795,20 +1155,24 @@ ImGuiStyle::ImGuiStyle() ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). - ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns - ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar + ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). + ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. + TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. + TabBorderSize = 0.0f; // Thickness of border around tabs. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. - DisplayWindowPadding = ImVec2(22,22); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. - DisplaySafeAreaPadding = ImVec2(4,4); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text when button is larger than text. + DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. + DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. - ImGui::StyleColorsClassic(this); + // Default theme + ImGui::StyleColorsDark(this); } // To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you. @@ -831,6 +1195,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor); GrabMinSize = ImFloor(GrabMinSize * scale_factor); GrabRounding = ImFloor(GrabRounding * scale_factor); + TabRounding = ImFloor(TabRounding * scale_factor); DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor); DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor); MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); @@ -842,9 +1207,10 @@ ImGuiIO::ImGuiIO() memset(this, 0, sizeof(*this)); // Settings + ConfigFlags = ImGuiConfigFlags_None; + BackendFlags = ImGuiBackendFlags_None; DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f/60.0f; - ConfigFlags = 0x00; IniSavingRate = 5.0f; IniFilename = "imgui.ini"; LogFilename = "imgui_log.txt"; @@ -861,17 +1227,21 @@ ImGuiIO::ImGuiIO() FontDefault = NULL; FontAllowUserScaling = false; DisplayFramebufferScale = ImVec2(1.0f, 1.0f); - DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f); - // Advanced/subtle behaviors + // Miscellaneous options + MouseDrawCursor = false; #ifdef __APPLE__ - OptMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag + ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag #else - OptMacOSXBehaviors = false; + ConfigMacOSXBehaviors = false; #endif - OptCursorBlink = true; + ConfigInputTextCursorBlink = true; + ConfigWindowsResizeFromEdges = true; + ConfigWindowsMoveFromTitleBarOnly = false; - // Settings (User Functions) + // Platform Functions + BackendPlatformName = BackendRendererName = NULL; + BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL; GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; ClipboardUserData = NULL; @@ -882,7 +1252,7 @@ ImGuiIO::ImGuiIO() RenderDrawListsFn = NULL; #endif - // Input (NB: we already have memset zero the entire structure) + // Input (NB: we already have memset zero the entire structure!) MousePos = ImVec2(-FLT_MAX, -FLT_MAX); MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); MouseDragThreshold = 6.0f; @@ -894,33 +1264,32 @@ ImGuiIO::ImGuiIO() // Pass in translated ASCII characters for text input. // - with glfw you can get those from the callback set in glfwSetCharCallback() // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message -void ImGuiIO::AddInputCharacter(ImWchar c) +void ImGuiIO::AddInputCharacter(unsigned int c) { - const int n = ImStrlenW(InputCharacters); - if (n + 1 < IM_ARRAYSIZE(InputCharacters)) - { - InputCharacters[n] = c; - InputCharacters[n+1] = '\0'; - } + if (c > 0 && c < 0x10000) + InputQueueCharacters.push_back((ImWchar)c); } void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) { - // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more - const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar); - ImWchar wchars[wchars_buf_len]; - ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL); - for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++) - AddInputCharacter(wchars[i]); + while (*utf8_chars != 0) + { + unsigned int c = 0; + utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL); + if (c > 0 && c < 0x10000) + InputQueueCharacters.push_back((ImWchar)c); + } +} + +void ImGuiIO::ClearInputCharacters() +{ + InputQueueCharacters.resize(0); } //----------------------------------------------------------------------------- -// HELPERS +// [SECTION] MISC HELPERS/UTILITIES (Maths, String, Format, Hash, File functions) //----------------------------------------------------------------------------- -#define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose -#define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 - ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p) { ImVec2 ap = p - a; @@ -969,6 +1338,7 @@ ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, return proj_ca; } +// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more. int ImStricmp(const char* str1, const char* str2) { int d; @@ -985,33 +1355,55 @@ int ImStrnicmp(const char* str1, const char* str2, size_t count) void ImStrncpy(char* dst, const char* src, size_t count) { - if (count < 1) return; - strncpy(dst, src, count); - dst[count-1] = 0; + if (count < 1) + return; + if (count > 1) + strncpy(dst, src, count - 1); + dst[count - 1] = 0; } -char* ImStrdup(const char *str) +char* ImStrdup(const char* str) { - size_t len = strlen(str) + 1; - void* buf = ImGui::MemAlloc(len); - return (char*)memcpy(buf, (const void*)str, len); + size_t len = strlen(str); + void* buf = IM_ALLOC(len + 1); + return (char*)memcpy(buf, (const void*)str, len + 1); } -char* ImStrchrRange(const char* str, const char* str_end, char c) +char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src) { - for ( ; str < str_end; str++) - if (*str == c) - return (char*)str; - return NULL; + size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1; + size_t src_size = strlen(src) + 1; + if (dst_buf_size < src_size) + { + IM_FREE(dst); + dst = (char*)IM_ALLOC(src_size); + if (p_dst_size) + *p_dst_size = src_size; + } + return (char*)memcpy(dst, (const void*)src, src_size); +} + +const char* ImStrchrRange(const char* str, const char* str_end, char c) +{ + const char* p = (const char*)memchr(str, (int)c, str_end - str); + return p; } int ImStrlenW(const ImWchar* str) { + //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bits int n = 0; while (*str++) n++; return n; } +// Find end-of-line. Return pointer will point to either first \n, either str_end. +const char* ImStreolRange(const char* str, const char* str_end) +{ + const char* p = (const char*)memchr(str, '\n', str_end - str); + return p ? p : str_end; +} + const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line { while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n') @@ -1041,27 +1433,46 @@ const char* ImStristr(const char* haystack, const char* haystack_end, const char return NULL; } -static const char* ImAtoi(const char* src, int* output) +// Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible. +void ImStrTrimBlanks(char* buf) { - int negative = 0; - if (*src == '-') { negative = 1; src++; } - if (*src == '+') { src++; } - int v = 0; - while (*src >= '0' && *src <= '9') - v = (v * 10) + (*src++ - '0'); - *output = negative ? -v : v; - return src; + char* p = buf; + while (p[0] == ' ' || p[0] == '\t') // Leading blanks + p++; + char* p_start = p; + while (*p != 0) // Find end of string + p++; + while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks + p--; + if (p_start != buf) // Copy memory if we had leading blanks + memmove(buf, p_start, p - p_start); + buf[p - p_start] = 0; // Zero terminate } -// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). +// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). // Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm. // B) When buf==NULL vsnprintf() will return the output size. #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS + +//#define IMGUI_USE_STB_SPRINTF +#ifdef IMGUI_USE_STB_SPRINTF +#define STB_SPRINTF_IMPLEMENTATION +#include "imstb_sprintf.h" +#endif + +#if defined(_MSC_VER) && !defined(vsnprintf) +#define vsnprintf _vsnprintf +#endif + int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) { va_list args; va_start(args, fmt); +#ifdef IMGUI_USE_STB_SPRINTF + int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args); +#else int w = vsnprintf(buf, buf_size, fmt, args); +#endif va_end(args); if (buf == NULL) return w; @@ -1073,7 +1484,11 @@ int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) { +#ifdef IMGUI_USE_STB_SPRINTF + int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args); +#else int w = vsnprintf(buf, buf_size, fmt, args); +#endif if (buf == NULL) return w; if (w == -1 || w >= (int)buf_size) @@ -1083,43 +1498,69 @@ int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) } #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS -// Pass data_size==0 for zero-terminated strings -// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. -ImU32 ImHash(const void* data, int data_size, ImU32 seed) +// CRC32 needs a 1KB lookup table (not cache friendly) +// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily: +// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe. +static const ImU32 GCrc32LookupTable[256] = { - static ImU32 crc32_lut[256] = { 0 }; - if (!crc32_lut[1]) - { - const ImU32 polynomial = 0xEDB88320; - for (ImU32 i = 0; i < 256; i++) - { - ImU32 crc = i; - for (ImU32 j = 0; j < 8; j++) - crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial); - crc32_lut[i] = crc; - } - } + 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, + 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, + 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, + 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, + 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, + 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, + 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, + 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, + 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, + 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, + 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, + 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, + 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, + 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, + 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, + 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D, +}; +// Known size hash +// It is ok to call ImHashData on a string with known length but the ### operator won't be supported. +// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. +ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed) +{ + ImU32 crc = ~seed; + const unsigned char* data = (const unsigned char*)data_p; + const ImU32* crc32_lut = GCrc32LookupTable; + while (data_size-- != 0) + crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++]; + return ~crc; +} + +// Zero-terminated string hash, with support for ### to reset back to seed value +// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. +// Because this syntax is rarely used we are optimizing for the common case. +// - If we reach ### in the string we discard the hash so far and reset to the seed. +// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build) +// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. +ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed) +{ seed = ~seed; ImU32 crc = seed; - const unsigned char* current = (const unsigned char*)data; - - if (data_size > 0) + const unsigned char* data = (const unsigned char*)data_p; + const ImU32* crc32_lut = GCrc32LookupTable; + if (data_size != 0) { - // Known size - while (data_size--) - crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++]; + while (data_size-- != 0) + { + unsigned char c = *data++; + if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#') + crc = seed; + crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; + } } else { - // Zero-terminated string - while (unsigned char c = *current++) + while (unsigned char c = *data++) { - // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. - // Because this syntax is rarely used we are optimizing for the common case. - // - If we reach ### in the string we discard the hash so far and reset to the seed. - // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller. - if (c == '#' && current[0] == '#' && current[1] == '#') + if (c == '#' && data[0] == '#' && data[1] == '#') crc = seed; crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; } @@ -1127,8 +1568,66 @@ ImU32 ImHash(const void* data, int data_size, ImU32 seed) return ~crc; } +FILE* ImFileOpen(const char* filename, const char* mode) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__) + // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can) + const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1; + const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1; + ImVector buf; + buf.resize(filename_wsize + mode_wsize); + ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL); + ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL); + return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]); +#else + return fopen(filename, mode); +#endif +} + +// Load file content into memory +// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree() +void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes) +{ + IM_ASSERT(filename && file_open_mode); + if (out_file_size) + *out_file_size = 0; + + FILE* f; + if ((f = ImFileOpen(filename, file_open_mode)) == NULL) + return NULL; + + long file_size_signed; + if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) + { + fclose(f); + return NULL; + } + + size_t file_size = (size_t)file_size_signed; + void* file_data = IM_ALLOC(file_size + padding_bytes); + if (file_data == NULL) + { + fclose(f); + return NULL; + } + if (fread(file_data, 1, file_size, f) != file_size) + { + fclose(f); + IM_FREE(file_data); + return NULL; + } + if (padding_bytes > 0) + memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes); + + fclose(f); + if (out_file_size) + *out_file_size = file_size; + + return file_data; +} + //----------------------------------------------------------------------------- -// ImText* helpers +// [SECTION] MISC HELPERS/UTILITIES (ImText* functions) //----------------------------------------------------------------------------- // Convert UTF-8 to 32-bits character, process single character input. @@ -1264,6 +1763,13 @@ static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) } } +// Not optimal but we very rarely use this function. +int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end) +{ + unsigned int dummy = 0; + return ImTextCharFromUtf8(&dummy, in_text, in_text_end); +} + static inline int ImTextCountUtf8BytesFromChar(unsigned int c) { if (c < 0x80) return 1; @@ -1303,6 +1809,11 @@ int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_e return bytes_count; } +//----------------------------------------------------------------------------- +// [SECTION] MISC HELPERS/UTILTIES (Color functions) +// Note: The Convert functions are early design which are not consistent with other API. +//----------------------------------------------------------------------------- + ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in) { float s = 1.0f/255.0f; @@ -1323,38 +1834,6 @@ ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) return out; } -ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) -{ - ImGuiStyle& style = GImGui->Style; - ImVec4 c = style.Colors[idx]; - c.w *= style.Alpha * alpha_mul; - return ColorConvertFloat4ToU32(c); -} - -ImU32 ImGui::GetColorU32(const ImVec4& col) -{ - ImGuiStyle& style = GImGui->Style; - ImVec4 c = col; - c.w *= style.Alpha; - return ColorConvertFloat4ToU32(c); -} - -const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx) -{ - ImGuiStyle& style = GImGui->Style; - return style.Colors[idx]; -} - -ImU32 ImGui::GetColorU32(ImU32 col) -{ - float style_alpha = GImGui->Style.Alpha; - if (style_alpha >= 1.0f) - return col; - int a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; - a = (int)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range. - return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); -} - // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) @@ -1372,7 +1851,7 @@ void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& } const float chroma = r - (g < b ? g : b); - out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f)); + out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f)); out_s = chroma / (r + 1e-20f); out_v = r; } @@ -1388,7 +1867,7 @@ void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& return; } - h = fmodf(h, 1.0f) / (60.0f/360.0f); + h = ImFmod(h, 1.0f) / (60.0f/360.0f); int i = (int)h; float f = h - (float)i; float p = v * (1.0f - s); @@ -1406,79 +1885,53 @@ void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& } } -FILE* ImFileOpen(const char* filename, const char* mode) +ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) { -#if defined(_WIN32) && !defined(__CYGWIN__) - // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can) - const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1; - const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1; - ImVector buf; - buf.resize(filename_wsize + mode_wsize); - ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL); - ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL); - return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]); -#else - return fopen(filename, mode); -#endif + ImGuiStyle& style = GImGui->Style; + ImVec4 c = style.Colors[idx]; + c.w *= style.Alpha * alpha_mul; + return ColorConvertFloat4ToU32(c); } -// Load file content into memory -// Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree() -void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes) +ImU32 ImGui::GetColorU32(const ImVec4& col) { - IM_ASSERT(filename && file_open_mode); - if (out_file_size) - *out_file_size = 0; + ImGuiStyle& style = GImGui->Style; + ImVec4 c = col; + c.w *= style.Alpha; + return ColorConvertFloat4ToU32(c); +} - FILE* f; - if ((f = ImFileOpen(filename, file_open_mode)) == NULL) - return NULL; +const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx) +{ + ImGuiStyle& style = GImGui->Style; + return style.Colors[idx]; +} - long file_size_signed; - if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) - { - fclose(f); - return NULL; - } - - int file_size = (int)file_size_signed; - void* file_data = ImGui::MemAlloc(file_size + padding_bytes); - if (file_data == NULL) - { - fclose(f); - return NULL; - } - if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size) - { - fclose(f); - ImGui::MemFree(file_data); - return NULL; - } - if (padding_bytes > 0) - memset((void *)(((char*)file_data) + file_size), 0, padding_bytes); - - fclose(f); - if (out_file_size) - *out_file_size = file_size; - - return file_data; +ImU32 ImGui::GetColorU32(ImU32 col) +{ + float style_alpha = GImGui->Style.Alpha; + if (style_alpha >= 1.0f) + return col; + ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; + a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range. + return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); } //----------------------------------------------------------------------------- -// ImGuiStorage +// [SECTION] ImGuiStorage // Helper: Key->value storage //----------------------------------------------------------------------------- // std::lower_bound but without the bullshit -static ImVector::iterator LowerBound(ImVector& data, ImGuiID key) +static ImGuiStorage::Pair* LowerBound(ImVector& data, ImGuiID key) { - ImVector::iterator first = data.begin(); - ImVector::iterator last = data.end(); + ImGuiStorage::Pair* first = data.Data; + ImGuiStorage::Pair* last = data.Data + data.Size; size_t count = (size_t)(last - first); while (count > 0) { size_t count2 = count >> 1; - ImVector::iterator mid = first + count2; + ImGuiStorage::Pair* mid = first + count2; if (mid->key < key) { first = ++mid; @@ -1495,9 +1948,9 @@ static ImVector::iterator LowerBound(ImVectorkey > ((const Pair*)rhs)->key) return +1; @@ -1506,12 +1959,12 @@ void ImGuiStorage::BuildSortByKey() } }; if (Data.Size > 1) - qsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID); + ImQsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID); } int ImGuiStorage::GetInt(ImGuiID key, int default_val) const { - ImVector::iterator it = LowerBound(const_cast&>(Data), key); + ImGuiStorage::Pair* it = LowerBound(const_cast&>(Data), key); if (it == Data.end() || it->key != key) return default_val; return it->val_i; @@ -1524,7 +1977,7 @@ bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const { - ImVector::iterator it = LowerBound(const_cast&>(Data), key); + ImGuiStorage::Pair* it = LowerBound(const_cast&>(Data), key); if (it == Data.end() || it->key != key) return default_val; return it->val_f; @@ -1532,7 +1985,7 @@ float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const void* ImGuiStorage::GetVoidPtr(ImGuiID key) const { - ImVector::iterator it = LowerBound(const_cast&>(Data), key); + ImGuiStorage::Pair* it = LowerBound(const_cast&>(Data), key); if (it == Data.end() || it->key != key) return NULL; return it->val_p; @@ -1541,7 +1994,7 @@ void* ImGuiStorage::GetVoidPtr(ImGuiID key) const // References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val) { - ImVector::iterator it = LowerBound(Data, key); + ImGuiStorage::Pair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) it = Data.insert(it, Pair(key, default_val)); return &it->val_i; @@ -1554,7 +2007,7 @@ bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val) float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) { - ImVector::iterator it = LowerBound(Data, key); + ImGuiStorage::Pair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) it = Data.insert(it, Pair(key, default_val)); return &it->val_f; @@ -1562,7 +2015,7 @@ float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) { - ImVector::iterator it = LowerBound(Data, key); + ImGuiStorage::Pair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) it = Data.insert(it, Pair(key, default_val)); return &it->val_p; @@ -1571,7 +2024,7 @@ void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) // FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame) void ImGuiStorage::SetInt(ImGuiID key, int val) { - ImVector::iterator it = LowerBound(Data, key); + ImGuiStorage::Pair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) { Data.insert(it, Pair(key, val)); @@ -1587,7 +2040,7 @@ void ImGuiStorage::SetBool(ImGuiID key, bool val) void ImGuiStorage::SetFloat(ImGuiID key, float val) { - ImVector::iterator it = LowerBound(Data, key); + ImGuiStorage::Pair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) { Data.insert(it, Pair(key, val)); @@ -1598,7 +2051,7 @@ void ImGuiStorage::SetFloat(ImGuiID key, float val) void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val) { - ImVector::iterator it = LowerBound(Data, key); + ImGuiStorage::Pair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) { Data.insert(it, Pair(key, val)); @@ -1614,7 +2067,7 @@ void ImGuiStorage::SetAllInt(int v) } //----------------------------------------------------------------------------- -// ImGuiTextFilter +// [SECTION] ImGuiTextFilter //----------------------------------------------------------------------------- // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" @@ -1635,46 +2088,48 @@ ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) bool ImGuiTextFilter::Draw(const char* label, float width) { if (width != 0.0f) - ImGui::PushItemWidth(width); + ImGui::SetNextItemWidth(width); bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); - if (width != 0.0f) - ImGui::PopItemWidth(); if (value_changed) Build(); return value_changed; } -void ImGuiTextFilter::TextRange::split(char separator, ImVector& out) +void ImGuiTextFilter::TextRange::split(char separator, ImVector* out) const { - out.resize(0); + out->resize(0); const char* wb = b; const char* we = wb; while (we < e) { if (*we == separator) { - out.push_back(TextRange(wb, we)); + out->push_back(TextRange(wb, we)); wb = we + 1; } we++; } if (wb != we) - out.push_back(TextRange(wb, we)); + out->push_back(TextRange(wb, we)); } void ImGuiTextFilter::Build() { Filters.resize(0); TextRange input_range(InputBuf, InputBuf+strlen(InputBuf)); - input_range.split(',', Filters); + input_range.split(',', &Filters); CountGrep = 0; for (int i = 0; i != Filters.Size; i++) { - Filters[i].trim_blanks(); - if (Filters[i].empty()) + TextRange& f = Filters[i]; + while (f.b < f.e && ImCharIsBlankA(f.b[0])) + f.b++; + while (f.e > f.b && ImCharIsBlankA(f.e[-1])) + f.e--; + if (f.empty()) continue; - if (Filters[i].front() != '-') + if (Filters[i].b[0] != '-') CountGrep += 1; } } @@ -1692,7 +2147,7 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const const TextRange& f = Filters[i]; if (f.empty()) continue; - if (f.front() == '-') + if (f.b[0] == '-') { // Subtract if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL) @@ -1714,35 +2169,37 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const } //----------------------------------------------------------------------------- -// ImGuiTextBuffer +// [SECTION] ImGuiTextBuffer //----------------------------------------------------------------------------- // On some platform vsnprintf() takes va_list by reference and modifies it. // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it. #ifndef va_copy +#if defined(__GNUC__) || defined(__clang__) +#define va_copy(dest, src) __builtin_va_copy(dest, src) +#else #define va_copy(dest, src) (dest = src) #endif +#endif -// Helper: Text buffer for logging/accumulating text -void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) +char ImGuiTextBuffer::EmptyString[1] = { 0 }; + +void ImGuiTextBuffer::append(const char* str, const char* str_end) { - va_list args_copy; - va_copy(args_copy, args); + int len = str_end ? (int)(str_end - str) : (int)strlen(str); - int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass. - if (len <= 0) - return; - - const int write_off = Buf.Size; + // Add zero-terminator the first time + const int write_off = (Buf.Size != 0) ? Buf.Size : 1; const int needed_sz = write_off + len; if (write_off + len >= Buf.Capacity) { - int double_capacity = Buf.Capacity * 2; - Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity); + int new_capacity = Buf.Capacity * 2; + Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity); } Buf.resize(needed_sz); - ImFormatStringV(&Buf[write_off - 1], len + 1, fmt, args_copy); + memcpy(&Buf[write_off - 1], str, (size_t)len); + Buf[write_off - 1 + len] = 0; } void ImGuiTextBuffer::appendf(const char* fmt, ...) @@ -1753,65 +2210,93 @@ void ImGuiTextBuffer::appendf(const char* fmt, ...) va_end(args); } -//----------------------------------------------------------------------------- -// ImGuiSimpleColumns (internal use only) -//----------------------------------------------------------------------------- - -ImGuiMenuColumns::ImGuiMenuColumns() +// Helper: Text buffer for logging/accumulating text +void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) { - Count = 0; - Spacing = Width = NextWidth = 0.0f; - memset(Pos, 0, sizeof(Pos)); - memset(NextWidths, 0, sizeof(NextWidths)); -} + va_list args_copy; + va_copy(args_copy, args); -void ImGuiMenuColumns::Update(int count, float spacing, bool clear) -{ - IM_ASSERT(Count <= IM_ARRAYSIZE(Pos)); - Count = count; - Width = NextWidth = 0.0f; - Spacing = spacing; - if (clear) memset(NextWidths, 0, sizeof(NextWidths)); - for (int i = 0; i < Count; i++) + int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass. + if (len <= 0) { - if (i > 0 && NextWidths[i] > 0.0f) - Width += Spacing; - Pos[i] = (float)(int)Width; - Width += NextWidths[i]; - NextWidths[i] = 0.0f; + va_end(args_copy); + return; } -} -float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double -{ - NextWidth = 0.0f; - NextWidths[0] = ImMax(NextWidths[0], w0); - NextWidths[1] = ImMax(NextWidths[1], w1); - NextWidths[2] = ImMax(NextWidths[2], w2); - for (int i = 0; i < 3; i++) - NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); - return ImMax(Width, NextWidth); -} + // Add zero-terminator the first time + const int write_off = (Buf.Size != 0) ? Buf.Size : 1; + const int needed_sz = write_off + len; + if (write_off + len >= Buf.Capacity) + { + int new_capacity = Buf.Capacity * 2; + Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity); + } -float ImGuiMenuColumns::CalcExtraSpace(float avail_w) -{ - return ImMax(0.0f, avail_w - Width); + Buf.resize(needed_sz); + ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy); + va_end(args_copy); } //----------------------------------------------------------------------------- -// ImGuiListClipper +// [SECTION] ImGuiListClipper +// This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed +// the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO) //----------------------------------------------------------------------------- +// Helper to calculate coarse clipping of large list of evenly sized items. +// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. +// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX +void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (g.LogEnabled) + { + // If logging is active, do not perform any clipping + *out_items_display_start = 0; + *out_items_display_end = items_count; + return; + } + if (window->SkipItems) + { + *out_items_display_start = *out_items_display_end = 0; + return; + } + + // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect + ImRect unclipped_rect = window->ClipRect; + if (g.NavMoveRequest) + unclipped_rect.Add(g.NavScoringRectScreen); + + const ImVec2 pos = window->DC.CursorPos; + int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); + int end = (int)((unclipped_rect.Max.y - pos.y) / items_height); + + // When performing a navigation request, ensure we have one item extra in the direction we are moving to + if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up) + start--; + if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down) + end++; + + start = ImClamp(start, 0, items_count); + end = ImClamp(end + 1, start, items_count); + *out_items_display_start = start; + *out_items_display_end = end; +} + static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height) { - // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor. - // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. Consider moving within SetCursorXXX functions? - ImGui::SetCursorPosY(pos_y); - ImGuiWindow* window = ImGui::GetCurrentWindow(); - window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage. - window->DC.PrevLineHeight = (line_height - GImGui->Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. - if (window->DC.ColumnsSet) - window->DC.ColumnsSet->CellMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly + // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. + // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. + // The clipper should probably have a 4th step to display the last item in a regular manner. + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + window->DC.CursorPos.y = pos_y; + window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y); + window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage. + window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. + if (ImGuiColumns* columns = window->DC.CurrentColumns) + columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly } // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1 @@ -1819,7 +2304,10 @@ static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height) // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style. void ImGuiListClipper::Begin(int count, float items_height) { - StartPosY = ImGui::GetCursorPosY(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + StartPosY = window->DC.CursorPos.y; ItemsHeight = items_height; ItemsCount = count; StepNo = 0; @@ -1846,25 +2334,28 @@ void ImGuiListClipper::End() bool ImGuiListClipper::Step() { - if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems) + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + if (ItemsCount == 0 || window->SkipItems) { - ItemsCount = -1; - return false; + ItemsCount = -1; + return false; } if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height. { DisplayStart = 0; DisplayEnd = 1; - StartPosY = ImGui::GetCursorPosY(); + StartPosY = window->DC.CursorPos.y; StepNo = 1; return true; } if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element. { if (ItemsCount == 1) { ItemsCount = -1; return false; } - float items_height = ImGui::GetCursorPosY() - StartPosY; + float items_height = window->DC.CursorPos.y - StartPosY; IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically - Begin(ItemsCount-1, items_height); + Begin(ItemsCount - 1, items_height); DisplayStart++; DisplayEnd++; StepNo = 3; @@ -1882,80 +2373,354 @@ bool ImGuiListClipper::Step() } //----------------------------------------------------------------------------- -// ImGuiWindow +// [SECTION] RENDER HELPERS +// Those (internal) functions are currently quite a legacy mess - their signature and behavior will change. +// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state. //----------------------------------------------------------------------------- +const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) +{ + const char* text_display_end = text; + if (!text_end) + text_end = (const char*)-1; + + while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) + text_display_end++; + return text_display_end; +} + +// Internal ImGui functions to render text +// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() +void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // Hide anything after a '##' string + const char* text_display_end; + if (hide_text_after_hash) + { + text_display_end = FindRenderedTextEnd(text, text_end); + } + else + { + if (!text_end) + text_end = text + strlen(text); // FIXME-OPT + text_display_end = text_end; + } + + if (text != text_display_end) + { + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); + if (g.LogEnabled) + LogRenderedText(&pos, text, text_display_end); + } +} + +void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + if (!text_end) + text_end = text + strlen(text); // FIXME-OPT + + if (text != text_end) + { + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); + if (g.LogEnabled) + LogRenderedText(&pos, text, text_end); + } +} + +// Default clip_rect uses (pos_min,pos_max) +// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) +void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) +{ + // Perform CPU side clipping for single clipped element to avoid using scissor state + ImVec2 pos = pos_min; + const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); + + const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; + const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; + bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); + if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min + need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); + + // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment. + if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); + if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); + + // Render + if (need_clipping) + { + ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); + draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); + } + else + { + draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); + } +} + +void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) +{ + // Hide anything after a '##' string + const char* text_display_end = FindRenderedTextEnd(text, text_end); + const int text_len = (int)(text_display_end - text); + if (text_len == 0) + return; + + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect); + if (g.LogEnabled) + LogRenderedText(&pos_min, text, text_display_end); +} + + +// Another overly complex function until we reorganize everything into a nice all-in-one helper. +// This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display. +// This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move. +void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known) +{ + ImGuiContext& g = *GImGui; + if (text_end_full == NULL) + text_end_full = FindRenderedTextEnd(text); + const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f); + + if (text_size.x > pos_max.x - pos_min.x) + { + // Hello wo... + // | | | + // min max ellipsis_max + // <-> this is generally some padding value + + // FIXME-STYLE: RenderPixelEllipsis() style should use actual font data. + const ImFont* font = draw_list->_Data->Font; + const float font_size = draw_list->_Data->FontSize; + const int ellipsis_dot_count = 3; + const float ellipsis_width = (1.0f + 1.0f) * ellipsis_dot_count - 1.0f; + const char* text_end_ellipsis = NULL; + + float text_width = ImMax((pos_max.x - ellipsis_width) - pos_min.x, 1.0f); + float text_size_clipped_x = font->CalcTextSizeA(font_size, text_width, 0.0f, text, text_end_full, &text_end_ellipsis).x; + if (text == text_end_ellipsis && text_end_ellipsis < text_end_full) + { + // Always display at least 1 character if there's no room for character + ellipsis + text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full); + text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x; + } + while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1])) + { + // Trim trailing space before ellipsis + text_end_ellipsis--; + text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte + } + RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f)); + + const float ellipsis_x = pos_min.x + text_size_clipped_x + 1.0f; + if (ellipsis_x + ellipsis_width - 1.0f <= ellipsis_max_x) + RenderPixelEllipsis(draw_list, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_dot_count); + } + else + { + RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f)); + } + + if (g.LogEnabled) + LogRenderedText(&pos_min, text, text_end_full); +} + +// Render a rectangle shaped with optional rounding and borders +void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); + const float border_size = g.Style.FrameBorderSize; + if (border && border_size > 0.0f) + { + window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); + window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); + } +} + +void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const float border_size = g.Style.FrameBorderSize; + if (border_size > 0.0f) + { + window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); + window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); + } +} + +// Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state +void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale) +{ + const float h = draw_list->_Data->FontSize * 1.00f; + float r = h * 0.40f * scale; + ImVec2 center = pos + ImVec2(h * 0.50f, h * 0.50f * scale); + + ImVec2 a, b, c; + switch (dir) + { + case ImGuiDir_Up: + case ImGuiDir_Down: + if (dir == ImGuiDir_Up) r = -r; + a = ImVec2(+0.000f,+0.750f) * r; + b = ImVec2(-0.866f,-0.750f) * r; + c = ImVec2(+0.866f,-0.750f) * r; + break; + case ImGuiDir_Left: + case ImGuiDir_Right: + if (dir == ImGuiDir_Left) r = -r; + a = ImVec2(+0.750f,+0.000f) * r; + b = ImVec2(-0.750f,+0.866f) * r; + c = ImVec2(-0.750f,-0.866f) * r; + break; + case ImGuiDir_None: + case ImGuiDir_COUNT: + IM_ASSERT(0); + break; + } + draw_list->AddTriangleFilled(center + a, center + b, center + c, col); +} + +void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col) +{ + draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8); +} + +void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + float thickness = ImMax(sz / 5.0f, 1.0f); + sz -= thickness*0.5f; + pos += ImVec2(thickness*0.25f, thickness*0.25f); + + float third = sz / 3.0f; + float bx = pos.x + third; + float by = pos.y + sz - third*0.5f; + window->DrawList->PathLineTo(ImVec2(bx - third, by - third)); + window->DrawList->PathLineTo(ImVec2(bx, by)); + window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2)); + window->DrawList->PathStroke(col, false, thickness); +} + +void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) +{ + ImGuiContext& g = *GImGui; + if (id != g.NavId) + return; + if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw)) + return; + ImGuiWindow* window = g.CurrentWindow; + if (window->DC.NavHideHighlightOneFrame) + return; + + float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; + ImRect display_rect = bb; + display_rect.ClipWith(window->ClipRect); + if (flags & ImGuiNavHighlightFlags_TypeDefault) + { + const float THICKNESS = 2.0f; + const float DISTANCE = 3.0f + THICKNESS * 0.5f; + display_rect.Expand(ImVec2(DISTANCE,DISTANCE)); + bool fully_visible = window->ClipRect.Contains(display_rect); + if (!fully_visible) + window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); + window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS); + if (!fully_visible) + window->DrawList->PopClipRect(); + } + if (flags & ImGuiNavHighlightFlags_TypeThin) + { + window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f); + } +} + +//----------------------------------------------------------------------------- +// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) +//----------------------------------------------------------------------------- + +// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) + : DrawListInst(&context->DrawListSharedData) { Name = ImStrdup(name); - ID = ImHash(name, 0); + ID = ImHashStr(name); IDStack.push_back(ID); - Flags = 0; - PosFloat = Pos = ImVec2(0.0f, 0.0f); + Flags = ImGuiWindowFlags_None; + Pos = ImVec2(0.0f, 0.0f); Size = SizeFull = ImVec2(0.0f, 0.0f); - SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); + ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f); WindowPadding = ImVec2(0.0f, 0.0f); WindowRounding = 0.0f; WindowBorderSize = 0.0f; + NameBufLen = (int)strlen(name) + 1; MoveId = GetID("#MOVE"); ChildId = 0; Scroll = ImVec2(0.0f, 0.0f); ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); - ScrollbarX = ScrollbarY = false; ScrollbarSizes = ImVec2(0.0f, 0.0f); + ScrollbarX = ScrollbarY = false; Active = WasActive = false; WriteAccessed = false; Collapsed = false; - CollapseToggleWanted = false; + WantCollapseToggle = false; SkipItems = false; Appearing = false; - CloseButton = false; + Hidden = false; + HasCloseButton = false; + ResizeBorderHeld = -1; + BeginCount = 0; BeginOrderWithinParent = -1; BeginOrderWithinContext = -1; - BeginCount = 0; PopupId = 0; AutoFitFramesX = AutoFitFramesY = -1; AutoFitOnlyGrows = false; AutoFitChildAxises = 0x00; AutoPosLastDirection = ImGuiDir_None; - HiddenFrames = 0; + HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0; SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); LastFrameActive = -1; ItemWidthDefault = 0.0f; FontWindowScale = 1.0f; + SettingsIdx = -1; - DrawList = IM_NEW(ImDrawList)(&context->DrawListSharedData); + DrawList = &DrawListInst; DrawList->_OwnerName = Name; ParentWindow = NULL; RootWindow = NULL; RootWindowForTitleBarHighlight = NULL; - RootWindowForTabbing = NULL; RootWindowForNav = NULL; NavLastIds[0] = NavLastIds[1] = 0; NavRectRel[0] = NavRectRel[1] = ImRect(); NavLastChildNavWindow = NULL; - - FocusIdxAllCounter = FocusIdxTabCounter = -1; - FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; - FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX; } ImGuiWindow::~ImGuiWindow() { - IM_DELETE(DrawList); + IM_ASSERT(DrawList == &DrawListInst); IM_DELETE(Name); for (int i = 0; i != ColumnsStorage.Size; i++) - ColumnsStorage[i].~ImGuiColumnsSet(); + ColumnsStorage[i].~ImGuiColumns(); } ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) { ImGuiID seed = IDStack.back(); - ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed); + ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); ImGui::KeepAliveID(id); return id; } @@ -1963,7 +2728,15 @@ ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) ImGuiID ImGuiWindow::GetID(const void* ptr) { ImGuiID seed = IDStack.back(); - ImGuiID id = ImHash(&ptr, sizeof(void*), seed); + ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); + ImGui::KeepAliveID(id); + return id; +} + +ImGuiID ImGuiWindow::GetID(int n) +{ + ImGuiID seed = IDStack.back(); + ImGuiID id = ImHashData(&n, sizeof(n), seed); ImGui::KeepAliveID(id); return id; } @@ -1971,7 +2744,19 @@ ImGuiID ImGuiWindow::GetID(const void* ptr) ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) { ImGuiID seed = IDStack.back(); - return ImHash(str, str_end ? (int)(str_end - str) : 0, seed); + return ImHashStr(str, str_end ? (str_end - str) : 0, seed); +} + +ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) +{ + ImGuiID seed = IDStack.back(); + return ImHashData(&ptr, sizeof(void*), seed); +} + +ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) +{ + ImGuiID seed = IDStack.back(); + return ImHashData(&n, sizeof(n), seed); } // This is only used in rare/specific situations to manufacture an ID out of nowhere. @@ -1979,15 +2764,11 @@ ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) { ImGuiID seed = IDStack.back(); const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) }; - ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed); + ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); ImGui::KeepAliveID(id); return id; } -//----------------------------------------------------------------------------- -// Internal API exposed in imgui_internal.h -//----------------------------------------------------------------------------- - static void SetCurrentWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; @@ -1996,7 +2777,7 @@ static void SetCurrentWindow(ImGuiWindow* window) g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); } -static void SetNavID(ImGuiID id, int nav_layer) +void ImGui::SetNavID(ImGuiID id, int nav_layer) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow); @@ -2005,7 +2786,7 @@ static void SetNavID(ImGuiID id, int nav_layer) g.NavWindow->NavLastIds[nav_layer] = id; } -static void SetNavIDAndMoveMouse(ImGuiID id, int nav_layer, const ImRect& rect_rel) +void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel) { ImGuiContext& g = *GImGui; SetNavID(id, nav_layer); @@ -2020,31 +2801,37 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) ImGuiContext& g = *GImGui; g.ActiveIdIsJustActivated = (g.ActiveId != id); if (g.ActiveIdIsJustActivated) + { g.ActiveIdTimer = 0.0f; + g.ActiveIdHasBeenPressedBefore = false; + g.ActiveIdHasBeenEditedBefore = false; + if (id != 0) + { + g.LastActiveId = id; + g.LastActiveIdTimer = 0.0f; + } + } g.ActiveId = id; g.ActiveIdAllowNavDirFlags = 0; + g.ActiveIdBlockNavInputFlags = 0; g.ActiveIdAllowOverlap = false; g.ActiveIdWindow = window; + g.ActiveIdHasBeenEditedThisFrame = false; if (id) { - g.ActiveIdIsAlive = true; + g.ActiveIdIsAlive = id; g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; } } -ImGuiID ImGui::GetActiveID() -{ - ImGuiContext& g = *GImGui; - return g.ActiveId; -} - +// FIXME-NAV: The existence of SetNavID/SetNavIDWithRectRel/SetFocusID is incredibly messy and confusing and needs some explanation or refactoring. void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it. - const int nav_layer = window->DC.NavLayerCurrent; + const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent; if (g.NavWindow != window) g.NavInitRequest = false; g.NavId = id; @@ -2070,7 +2857,8 @@ void ImGui::SetHoveredID(ImGuiID id) ImGuiContext& g = *GImGui; g.HoveredId = id; g.HoveredIdAllowOverlap = false; - g.HoveredIdTimer = (id != 0 && g.HoveredIdPreviousFrame == id) ? (g.HoveredIdTimer + g.IO.DeltaTime) : 0.0f; + if (id != 0 && g.HoveredIdPreviousFrame != id) + g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f; } ImGuiID ImGui::GetHoveredID() @@ -2083,7 +2871,22 @@ void ImGui::KeepAliveID(ImGuiID id) { ImGuiContext& g = *GImGui; if (g.ActiveId == id) - g.ActiveIdIsAlive = true; + g.ActiveIdIsAlive = id; + if (g.ActiveIdPreviousFrame == id) + g.ActiveIdPreviousFrameIsAlive = true; +} + +void ImGui::MarkItemEdited(ImGuiID id) +{ + // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit(). + // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need need to fill the data. + ImGuiContext& g = *GImGui; + IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive); + IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out. + //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); + g.ActiveIdHasBeenEditedThisFrame = true; + g.ActiveIdHasBeenEditedBefore = true; + g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; } static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) @@ -2115,18 +2918,20 @@ void ImGui::ItemSize(const ImVec2& size, float text_offset_y) return; // Always align ourselves on pixel boundaries - const float line_height = ImMax(window->DC.CurrentLineHeight, size.y); - const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y); + const float line_height = ImMax(window->DC.CurrLineSize.y, size.y); + const float text_base_offset = ImMax(window->DC.CurrLineTextBaseOffset, text_offset_y); //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG] - window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y); - window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y)); + window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x; + window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y; + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + window->DC.CursorPos.y = (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y); //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG] - window->DC.PrevLineHeight = line_height; + window->DC.PrevLineSize.y = line_height; window->DC.PrevLineTextBaseOffset = text_base_offset; - window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f; + window->DC.CurrLineSize.y = window->DC.CurrLineTextBaseOffset = 0.0f; // Horizontal layout mode if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) @@ -2138,253 +2943,6 @@ void ImGui::ItemSize(const ImRect& bb, float text_offset_y) ItemSize(bb.GetSize(), text_offset_y); } -static ImGuiDir NavScoreItemGetQuadrant(float dx, float dy) -{ - if (fabsf(dx) > fabsf(dy)) - return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; - return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; -} - -static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) -{ - if (a1 < b0) - return a1 - b0; - if (b1 < a0) - return a0 - b1; - return 0.0f; -} - -// Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057 -static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.NavLayer != window->DC.NavLayerCurrent) - return false; - - const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) - g.NavScoringCount++; - - // We perform scoring on items bounding box clipped by their parent window on the other axis (clipping on our movement axis would give us equal scores for all clipped items) - if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) - { - cand.Min.y = ImClamp(cand.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y); - cand.Max.y = ImClamp(cand.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y); - } - else - { - cand.Min.x = ImClamp(cand.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x); - cand.Max.x = ImClamp(cand.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x); - } - - // Compute distance between boxes - // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. - float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); - float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items - if (dby != 0.0f && dbx != 0.0f) - dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); - float dist_box = fabsf(dbx) + fabsf(dby); - - // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter) - float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x); - float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y); - float dist_center = fabsf(dcx) + fabsf(dcy); // L1 metric (need this for our connectedness guarantee) - - // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance - ImGuiDir quadrant; - float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; - if (dbx != 0.0f || dby != 0.0f) - { - // For non-overlapping boxes, use distance between boxes - dax = dbx; - day = dby; - dist_axial = dist_box; - quadrant = NavScoreItemGetQuadrant(dbx, dby); - } - else if (dcx != 0.0f || dcy != 0.0f) - { - // For overlapping boxes with different centers, use distance between centers - dax = dcx; - day = dcy; - dist_axial = dist_center; - quadrant = NavScoreItemGetQuadrant(dcx, dcy); - } - else - { - // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter) - quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; - } - -#if IMGUI_DEBUG_NAV_SCORING - char buf[128]; - if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) - { - ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); - g.OverlayDrawList.AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100)); - g.OverlayDrawList.AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); - g.OverlayDrawList.AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); - g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); - } - else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. - { - if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } - if (quadrant == g.NavMoveDir) - { - ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); - g.OverlayDrawList.AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); - g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); - } - } - #endif - - // Is it in the quadrant we're interesting in moving to? - bool new_best = false; - if (quadrant == g.NavMoveDir) - { - // Does it beat the current best candidate? - if (dist_box < result->DistBox) - { - result->DistBox = dist_box; - result->DistCenter = dist_center; - return true; - } - if (dist_box == result->DistBox) - { - // Try using distance between center points to break ties - if (dist_center < result->DistCenter) - { - result->DistCenter = dist_center; - new_best = true; - } - else if (dist_center == result->DistCenter) - { - // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items - // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), - // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. - if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance - new_best = true; - } - } - } - - // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches - // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) - // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. - // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward. - // Disabling it may however lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? - if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match - if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) - if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) - { - result->DistAxial = dist_axial; - new_best = true; - } - - return new_best; -} - -static void NavSaveLastChildNavWindow(ImGuiWindow* child_window) -{ - ImGuiWindow* parent_window = child_window; - while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) - parent_window = parent_window->ParentWindow; - if (parent_window && parent_window != child_window) - parent_window->NavLastChildNavWindow = child_window; -} - -// Call when we are expected to land on Layer 0 after FocusWindow() -static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window) -{ - return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window; -} - -static void NavRestoreLayer(int layer) -{ - ImGuiContext& g = *GImGui; - g.NavLayer = layer; - if (layer == 0) - g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); - if (layer == 0 && g.NavWindow->NavLastIds[0] != 0) - SetNavIDAndMoveMouse(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]); - else - ImGui::NavInitWindow(g.NavWindow, true); -} - -static inline void NavUpdateAnyRequestFlag() -{ - ImGuiContext& g = *GImGui; - g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || IMGUI_DEBUG_NAV_SCORING; -} - -static bool NavMoveRequestButNoResultYet() -{ - ImGuiContext& g = *GImGui; - return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; -} - -void ImGui::NavMoveRequestCancel() -{ - ImGuiContext& g = *GImGui; - g.NavMoveRequest = false; - NavUpdateAnyRequestFlag(); -} - -// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) -static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) -{ - ImGuiContext& g = *GImGui; - //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. - // return; - - const ImGuiItemFlags item_flags = window->DC.ItemFlags; - const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); - if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) - { - // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) - { - g.NavInitResultId = id; - g.NavInitResultRectRel = nav_bb_rel; - } - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) - { - g.NavInitRequest = false; // Found a match, clear request - NavUpdateAnyRequestFlag(); - } - } - - // Scoring for navigation - if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav)) - { - ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; -#if IMGUI_DEBUG_NAV_SCORING - // [DEBUG] Score all items in NavWindow at all times - if (!g.NavMoveRequest) - g.NavMoveDir = g.NavMoveDirLast; - bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; -#else - bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); -#endif - if (new_best) - { - result->ID = id; - result->ParentID = window->IDStack.back(); - result->Window = window; - result->RectRel = nav_bb_rel; - } - } - - // Update window-relative bounding box of navigated item - if (g.NavId == id) - { - g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. - g.NavLayer = window->DC.NavLayerCurrent; - g.NavIdIsAlive = true; - g.NavIdTabCounter = window->FocusIdxTabCounter; - window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) - } -} - // Declare item bounding box for clipping and interaction. // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). @@ -2397,9 +2955,13 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) { // Navigation processing runs prior to clipping early-out // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget - // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window. - // it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. - // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) + // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests + // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of + // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. + // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able + // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). + // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. + // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask; if (g.NavId == id || g.NavAnyRequest) if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) @@ -2409,7 +2971,13 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) window->DC.LastItemId = id; window->DC.LastItemRect = bb; - window->DC.LastItemStatusFlags = 0; + window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None; + g.NextItemData.Flags = ImGuiNextItemDataFlags_None; + +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (id != 0) + IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id); +#endif // Clipping test const bool is_clipped = IsClippedEx(bb, id, false); @@ -2451,15 +3019,17 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) return false; - // Test if interactions on this window are blocked by an active popup or modal + // Test if interactions on this window are blocked by an active popup or modal. + // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. if (!IsWindowContentHoverable(window, flags)) return false; // Test if the item is disabled - if (window->DC.ItemFlags & ImGuiItemFlags_Disabled) + if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) return false; - - // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case. + + // Special handling for the dummy item after Begin() which represent the title bar or tab. + // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. if (window->DC.LastItemId == window->MoveId && window->WriteAccessed) return false; return true; @@ -2479,7 +3049,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return false; if (!IsMouseHoveringRect(bb.Min, bb.Max)) return false; - if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_Default)) + if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) return false; if (window->DC.ItemFlags & ImGuiItemFlags_Disabled) return false; @@ -2499,26 +3069,39 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged return false; } -bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop) +// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. +bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { ImGuiContext& g = *GImGui; - const bool allow_keyboard_focus = (window->DC.ItemFlags & (ImGuiItemFlags_AllowKeyboardFocus | ImGuiItemFlags_Disabled)) == ImGuiItemFlags_AllowKeyboardFocus; - window->FocusIdxAllCounter++; - if (allow_keyboard_focus) - window->FocusIdxTabCounter++; + // Increment counters + const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; + window->DC.FocusCounterAll++; + if (is_tab_stop) + window->DC.FocusCounterTab++; - // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item. - // Note that we can always TAB out of a widget that doesn't allow tabbing in. - if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)) - window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. - - if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) - return true; - if (allow_keyboard_focus && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) + // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. + // (Note that we can always TAB out of a widget that doesn't allow tabbing in) + if (g.ActiveId == id && g.FocusTabPressed && !(g.ActiveIdBlockNavInputFlags & (1 << ImGuiNavInput_KeyTab_)) && g.FocusRequestNextWindow == NULL) { - g.NavJustTabbedId = id; - return true; + g.FocusRequestNextWindow = window; + g.FocusRequestNextCounterTab = window->DC.FocusCounterTab + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. + } + + // Handle focus requests + if (g.FocusRequestCurrWindow == window) + { + if (window->DC.FocusCounterAll == g.FocusRequestCurrCounterAll) + return true; + if (is_tab_stop && window->DC.FocusCounterTab == g.FocusRequestCurrCounterTab) + { + g.NavJustTabbedId = id; + return true; + } + + // If another item is about to be focused, we clear our own active id + if (g.ActiveId == id) + ClearActiveID(); } return false; @@ -2526,21 +3109,8 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop void ImGui::FocusableItemUnregister(ImGuiWindow* window) { - window->FocusIdxAllCounter--; - window->FocusIdxTabCounter--; -} - -ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y) -{ - ImGuiContext& g = *GImGui; - ImVec2 content_max; - if (size.x < 0.0f || size.y < 0.0f) - content_max = g.CurrentWindow->Pos + GetContentRegionMax(); - if (size.x <= 0.0f) - size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x; - if (size.y <= 0.0f) - size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y; - return size; + window->DC.FocusCounterAll--; + window->DC.FocusCounterTab--; } float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) @@ -2548,26 +3118,29 @@ float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) if (wrap_pos_x < 0.0f) return 0.0f; - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; if (wrap_pos_x == 0.0f) - wrap_pos_x = GetContentRegionMax().x + window->Pos.x; + wrap_pos_x = window->WorkRect.Max.x; else if (wrap_pos_x > 0.0f) wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space return ImMax(wrap_pos_x - pos.x, 1.0f); } -//----------------------------------------------------------------------------- - -void* ImGui::MemAlloc(size_t sz) +// IM_ALLOC() == ImGui::MemAlloc() +void* ImGui::MemAlloc(size_t size) { - GImAllocatorActiveAllocationsCount++; - return GImAllocatorAllocFunc(sz, GImAllocatorUserData); + if (ImGuiContext* ctx = GImGui) + ctx->IO.MetricsActiveAllocations++; + return GImAllocatorAllocFunc(size, GImAllocatorUserData); } +// IM_FREE() == ImGui::MemFree() void ImGui::MemFree(void* ptr) { - if (ptr) GImAllocatorActiveAllocationsCount--; + if (ptr) + if (ImGuiContext* ctx = GImGui) + ctx->IO.MetricsActiveAllocations--; return GImAllocatorFreeFunc(ptr, GImAllocatorUserData); } @@ -2587,7 +3160,7 @@ const char* ImGui::GetVersion() return IMGUI_VERSION; } -// Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself +// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself // Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module ImGuiContext* ImGui::GetCurrentContext() { @@ -2603,7 +3176,25 @@ void ImGui::SetCurrentContext(ImGuiContext* ctx) #endif } -void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data) +// Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui. +// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit +// If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code +// may see different structures than what imgui.cpp sees, which is problematic. +// We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear ImGui. +bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx) +{ + bool error = false; + if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!"); } + if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } + if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); } + if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } + if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); } + if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); } + if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); } + return !error; +} + +void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data) { GImAllocatorAllocFunc = alloc_func; GImAllocatorFreeFunc = free_func; @@ -2631,13 +3222,13 @@ void ImGui::DestroyContext(ImGuiContext* ctx) ImGuiIO& ImGui::GetIO() { - IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); + IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); return GImGui->IO; } ImGuiStyle& ImGui::GetStyle() { - IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); + IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); return GImGui->Style; } @@ -2648,7 +3239,7 @@ ImDrawData* ImGui::GetDrawData() return g.DrawData.Valid ? &g.DrawData : NULL; } -float ImGui::GetTime() +double ImGui::GetTime() { return GImGui->Time; } @@ -2658,9 +3249,20 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } -ImDrawList* ImGui::GetOverlayDrawList() +ImDrawList* ImGui::GetBackgroundDrawList() { - return &GImGui->OverlayDrawList; + return &GImGui->BackgroundDrawList; +} + +static ImDrawList* GetForegroundDrawList(ImGuiWindow*) +{ + // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. + return &GImGui->ForegroundDrawList; +} + +ImDrawList* ImGui::GetForegroundDrawList() +{ + return &GImGui->ForegroundDrawList; } ImDrawListSharedData* ImGui::GetDrawListSharedData() @@ -2668,569 +3270,43 @@ ImDrawListSharedData* ImGui::GetDrawListSharedData() return &GImGui->DrawListSharedData; } -// This needs to be called before we submit any widget (aka in or before Begin) -void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) +void ImGui::StartMouseMovingWindow(ImGuiWindow* window) +{ + // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows. + // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward. + // This is because we want ActiveId to be set even when the window is not permitted to move. + ImGuiContext& g = *GImGui; + FocusWindow(window); + SetActiveID(window->MoveId, window); + g.NavDisableHighlight = true; + g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos; + + bool can_move_window = true; + if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) + can_move_window = false; + if (can_move_window) + g.MovingWindow = window; +} + +// Handle mouse moving window +// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing() +void ImGui::UpdateMouseMovingWindowNewFrame() { ImGuiContext& g = *GImGui; - IM_ASSERT(window == g.NavWindow); - bool init_for_nav = false; - if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) - if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) - init_for_nav = true; - if (init_for_nav) + if (g.MovingWindow != NULL) { - SetNavID(0, g.NavLayer); - g.NavInitRequest = true; - g.NavInitRequestFromMove = false; - g.NavInitResultId = 0; - g.NavInitResultRectRel = ImRect(); - NavUpdateAnyRequestFlag(); - } - else - { - g.NavId = window->NavLastIds[0]; - } -} - -static ImVec2 NavCalcPreferredMousePos() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.NavWindow; - if (!window) - return g.IO.MousePos; - const ImRect& rect_rel = window->NavRectRel[g.NavLayer]; - ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); - ImRect visible_rect = GetViewportRect(); - return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. -} - -static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) -{ - ImGuiContext& g = *GImGui; - for (int i = g.Windows.Size-1; i >= 0; i--) - if (g.Windows[i] == window) - return i; - return -1; -} - -static ImGuiWindow* FindWindowNavigable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) -{ - ImGuiContext& g = *GImGui; - for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir) - if (ImGui::IsWindowNavFocusable(g.Windows[i])) - return g.Windows[i]; - return NULL; -} - -float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) -{ - ImGuiContext& g = *GImGui; - if (mode == ImGuiInputReadMode_Down) - return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) - - const float t = g.IO.NavInputsDownDuration[n]; - if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. - return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); - if (t < 0.0f) - return 0.0f; - if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. - return (t == 0.0f) ? 1.0f : 0.0f; - if (mode == ImGuiInputReadMode_Repeat) - return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f); - if (mode == ImGuiInputReadMode_RepeatSlow) - return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f); - if (mode == ImGuiInputReadMode_RepeatFast) - return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f); - return 0.0f; -} - -// Equivalent of IsKeyDown() for NavInputs[] -static bool IsNavInputDown(ImGuiNavInput n) -{ - return GImGui->IO.NavInputs[n] > 0.0f; -} - -// Equivalent of IsKeyPressed() for NavInputs[] -static bool IsNavInputPressed(ImGuiNavInput n, ImGuiInputReadMode mode) -{ - return ImGui::GetNavInputAmount(n, mode) > 0.0f; -} - -static bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiInputReadMode mode) -{ - return (ImGui::GetNavInputAmount(n1, mode) + ImGui::GetNavInputAmount(n2, mode)) > 0.0f; -} - -ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) -{ - ImVec2 delta(0.0f, 0.0f); - if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); - if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); - if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode)); - if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow)) - delta *= slow_factor; - if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast)) - delta *= fast_factor; - return delta; -} - -static void NavUpdateWindowingHighlightWindow(int focus_change_dir) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavWindowingTarget); - if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) - return; - - const int i_current = FindWindowIndex(g.NavWindowingTarget); - ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); - if (!window_target) - window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir); - g.NavWindowingTarget = window_target; - g.NavWindowingToggleLayer = false; -} - -// Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer) -static void ImGui::NavUpdateWindowing() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* apply_focus_window = NULL; - bool apply_toggle_layer = false; - - bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); - if (start_windowing_with_gamepad || start_windowing_with_keyboard) - if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavigable(g.Windows.Size - 1, -INT_MAX, -1)) - { - g.NavWindowingTarget = window->RootWindowForTabbing; - g.NavWindowingHighlightTimer = g.NavWindowingHighlightAlpha = 0.0f; - g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; - g.NavWindowingInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; - } - - // Gamepad update - g.NavWindowingHighlightTimer += g.IO.DeltaTime; - if (g.NavWindowingTarget && g.NavWindowingInputSource == ImGuiInputSource_NavGamepad) - { - // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.20f) / 0.05f)); - - // Select window to focus - const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow); - if (focus_change_dir != 0) - { - NavUpdateWindowingHighlightWindow(focus_change_dir); - g.NavWindowingHighlightAlpha = 1.0f; - } - - // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most) - if (!IsNavInputDown(ImGuiNavInput_Menu)) - { - g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. - if (g.NavWindowingToggleLayer && g.NavWindow) - apply_toggle_layer = true; - else if (!g.NavWindowingToggleLayer) - apply_focus_window = g.NavWindowingTarget; - g.NavWindowingTarget = NULL; - } - } - - // Keyboard: Focus - if (g.NavWindowingTarget && g.NavWindowingInputSource == ImGuiInputSource_NavKeyboard) - { - // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.15f) / 0.04f)); // 1.0f - if (IsKeyPressedMap(ImGuiKey_Tab, true)) - NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); - if (!g.IO.KeyCtrl) - apply_focus_window = g.NavWindowingTarget; - } - - // Keyboard: Press and Release ALT to toggle menu layer - // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB - if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) - if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) - apply_toggle_layer = true; - - // Move window - if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) - { - ImVec2 move_delta; - if (g.NavWindowingInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); - if (g.NavWindowingInputSource == ImGuiInputSource_NavGamepad) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); - if (move_delta.x != 0.0f || move_delta.y != 0.0f) - { - const float NAV_MOVE_SPEED = 800.0f; - const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); - g.NavWindowingTarget->PosFloat += move_delta * move_speed; - g.NavDisableMouseHover = true; - MarkIniSettingsDirty(g.NavWindowingTarget); - } - } - - // Apply final focus - if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowForTabbing)) - { - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); - ClosePopupsOverWindow(apply_focus_window); - FocusWindow(apply_focus_window); - if (apply_focus_window->NavLastIds[0] == 0) - NavInitWindow(apply_focus_window, false); - - // If the window only has a menu layer, select it directly - if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1)) - g.NavLayer = 1; - } - if (apply_focus_window) - g.NavWindowingTarget = NULL; - - // Apply menu/layer toggle - if (apply_toggle_layer && g.NavWindow) - { - ImGuiWindow* new_nav_window = g.NavWindow; - while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) - new_nav_window = new_nav_window->ParentWindow; - if (new_nav_window != g.NavWindow) - { - ImGuiWindow* old_nav_window = g.NavWindow; - FocusWindow(new_nav_window); - new_nav_window->NavLastChildNavWindow = old_nav_window; - } - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0); - } -} - -// NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated. -static void NavScrollToBringItemIntoView(ImGuiWindow* window, ImRect& item_rect_rel) -{ - // Scroll to keep newly navigated item fully into view - ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); - //g.OverlayDrawList.AddRect(window->Pos + window_rect_rel.Min, window->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] - if (window_rect_rel.Contains(item_rect_rel)) - return; - - ImGuiContext& g = *GImGui; - if (window->ScrollbarX && item_rect_rel.Min.x < window_rect_rel.Min.x) - { - window->ScrollTarget.x = item_rect_rel.Min.x + window->Scroll.x - g.Style.ItemSpacing.x; - window->ScrollTargetCenterRatio.x = 0.0f; - } - else if (window->ScrollbarX && item_rect_rel.Max.x >= window_rect_rel.Max.x) - { - window->ScrollTarget.x = item_rect_rel.Max.x + window->Scroll.x + g.Style.ItemSpacing.x; - window->ScrollTargetCenterRatio.x = 1.0f; - } - if (item_rect_rel.Min.y < window_rect_rel.Min.y) - { - window->ScrollTarget.y = item_rect_rel.Min.y + window->Scroll.y - g.Style.ItemSpacing.y; - window->ScrollTargetCenterRatio.y = 0.0f; - } - else if (item_rect_rel.Max.y >= window_rect_rel.Max.y) - { - window->ScrollTarget.y = item_rect_rel.Max.y + window->Scroll.y + g.Style.ItemSpacing.y; - window->ScrollTargetCenterRatio.y = 1.0f; - } - - // Estimate upcoming scroll so we can offset our relative mouse position so mouse position can be applied immediately (under this block) - ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); - item_rect_rel.Translate(window->Scroll - next_scroll); -} - -static void ImGui::NavUpdate() -{ - ImGuiContext& g = *GImGui; - g.IO.WantMoveMouse = false; - -#if 0 - if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); -#endif - - // Update Keyboard->Nav inputs mapping - memset(g.IO.NavInputs + ImGuiNavInput_InternalStart_, 0, (ImGuiNavInput_COUNT - ImGuiNavInput_InternalStart_) * sizeof(g.IO.NavInputs[0])); - if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) - { - #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (g.IO.KeyMap[_KEY] != -1 && IsKeyDown(g.IO.KeyMap[_KEY])) g.IO.NavInputs[_NAV_INPUT] = 1.0f; - NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); - NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); - NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); - NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); - NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); - NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); - NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); - if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; - if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; - if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; -#undef NAV_MAP_KEY - } - - memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); - for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) - g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; - - // Process navigation init request (select first/default focus) - if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) - { - // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) - IM_ASSERT(g.NavWindow); - if (g.NavInitRequestFromMove) - SetNavIDAndMoveMouse(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel); - else - SetNavID(g.NavInitResultId, g.NavLayer); - g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; - } - g.NavInitRequest = false; - g.NavInitRequestFromMove = false; - g.NavInitResultId = 0; - g.NavJustMovedToId = 0; - - // Process navigation move request - if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0)) - { - // Select which result to use - ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - if (g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) // Maybe entering a flattened child? In this case solve the tie using the regular scoring rules - if ((g.NavMoveResultOther.DistBox < g.NavMoveResultLocal.DistBox) || (g.NavMoveResultOther.DistBox == g.NavMoveResultLocal.DistBox && g.NavMoveResultOther.DistCenter < g.NavMoveResultLocal.DistCenter)) - result = &g.NavMoveResultOther; - - IM_ASSERT(g.NavWindow && result->Window); - - // Scroll to keep newly navigated item fully into view - if (g.NavLayer == 0) - NavScrollToBringItemIntoView(result->Window, result->RectRel); - - // Apply result from previous frame navigation directional move request - ClearActiveID(); - g.NavWindow = result->Window; - SetNavIDAndMoveMouse(result->ID, g.NavLayer, result->RectRel); - g.NavJustMovedToId = result->ID; - g.NavMoveFromClampedRefRect = false; - } - - // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame - if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) - { - IM_ASSERT(g.NavMoveRequest); - if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) - g.NavDisableHighlight = false; - g.NavMoveRequestForward = ImGuiNavForward_None; - } - - // Apply application mouse position movement, after we had a chance to process move request result. - if (g.NavMousePosDirty && g.NavIdIsAlive) - { - // Set mouse position given our knowledge of the nav widget position from last frame - if (g.IO.ConfigFlags & ImGuiConfigFlags_NavMoveMouse) - { - g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos(); - g.IO.WantMoveMouse = true; - } - g.NavMousePosDirty = false; - } - g.NavIdIsAlive = false; - g.NavJustTabbedId = 0; - IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); - - // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 - if (g.NavWindow) - NavSaveLastChildNavWindow(g.NavWindow); - if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) - g.NavWindow->NavLastChildNavWindow = NULL; - - NavUpdateWindowing(); - - // Set output flags for user application - g.IO.NavActive = (g.IO.ConfigFlags & (ImGuiConfigFlags_NavEnableGamepad | ImGuiConfigFlags_NavEnableKeyboard)) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); - g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; - - // Process NavCancel input (to close a popup, get back to parent, clear focus) - if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) - { - if (g.ActiveId != 0) - { - ClearActiveID(); - } - else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) - { - // Exit child window - ImGuiWindow* child_window = g.NavWindow; - ImGuiWindow* parent_window = g.NavWindow->ParentWindow; - IM_ASSERT(child_window->ChildId != 0); - FocusWindow(parent_window); - SetNavID(child_window->ChildId, 0); - g.NavIdIsAlive = false; - if (g.NavDisableMouseHover) - g.NavMousePosDirty = true; - } - else if (g.OpenPopupStack.Size > 0) - { - // Close open popup/menu - if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) - ClosePopupToLevel(g.OpenPopupStack.Size - 1); - } - else if (g.NavLayer != 0) - { - // Leave the "menu" layer - NavRestoreLayer(0); - } - else - { - // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were - if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) - g.NavWindow->NavLastIds[0] = 0; - g.NavId = 0; - } - } - - // Process manual activation request - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; - if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - { - bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); - bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); - if (g.ActiveId == 0 && activate_pressed) - g.NavActivateId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) - g.NavActivateDownId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) - g.NavActivatePressedId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) - g.NavInputId = g.NavId; - } - if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - g.NavDisableHighlight = true; - if (g.NavActivateId != 0) - IM_ASSERT(g.NavActivateDownId == g.NavActivateId); - g.NavMoveRequest = false; - - // Process programmatic activation request - if (g.NavNextActivateId != 0) - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; - g.NavNextActivateId = 0; - - // Initiate directional inputs request - const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; - if (g.NavMoveRequestForward == ImGuiNavForward_None) - { - g.NavMoveDir = ImGuiDir_None; - if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - { - if ((allowed_dir_flags & (1<Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) - { - // *Fallback* manual-scroll with NavUp/NavDown when window has no navigable item - ImGuiWindow* window = g.NavWindow; - const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) - { - if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) - SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); - if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) - SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); - } - - // *Normal* Manual scroll with NavScrollXXX keys - // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. - ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f); - if (scroll_dir.x != 0.0f && window->ScrollbarX) - { - SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); - g.NavMoveFromClampedRefRect = true; - } - if (scroll_dir.y != 0.0f) - { - SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); - g.NavMoveFromClampedRefRect = true; - } - } - - // Reset search results - g.NavMoveResultLocal.Clear(); - g.NavMoveResultOther.Clear(); - - // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items - if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0) - { - ImGuiWindow* window = g.NavWindow; - ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1)); - if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) - { - float pad = window->CalcFontSize() * 0.5f; - window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item - window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); - g.NavId = 0; - } - g.NavMoveFromClampedRefRect = false; - } - - // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) - ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); - g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect(); - g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); - g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; - IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous fabsf() calls in NavScoreItem(). - //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] - g.NavScoringCount = 0; -#if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] - if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames <= 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredMousePos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); g.OverlayDrawList.AddCircleFilled(p, 3.0f, col); g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } -#endif -} - -static void ImGui::UpdateMovingWindow() -{ - ImGuiContext& g = *GImGui; - if (g.MovingWindow && g.MovingWindow->MoveId == g.ActiveId && g.ActiveIdSource == ImGuiInputSource_Mouse) - { - // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window). - // We track it to preserve Focus and so that ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. + // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window). + // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. KeepAliveID(g.ActiveId); IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow); ImGuiWindow* moving_window = g.MovingWindow->RootWindow; - if (g.IO.MouseDown[0]) + if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) { ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; - if (moving_window->PosFloat.x != pos.x || moving_window->PosFloat.y != pos.y) + if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) { MarkIniSettingsDirty(moving_window); - moving_window->PosFloat = pos; + SetWindowPos(moving_window, pos, ImGuiCond_Always); } FocusWindow(g.MovingWindow); } @@ -3249,95 +3325,75 @@ static void ImGui::UpdateMovingWindow() if (!g.IO.MouseDown[0]) ClearActiveID(); } - g.MovingWindow = NULL; } } -void ImGui::NewFrame() +// Initiate moving window, handle left-click and right-click focus +void ImGui::UpdateMouseMovingWindowEndFrame() +{ + // Initiate moving window + ImGuiContext& g = *GImGui; + if (g.ActiveId != 0 || g.HoveredId != 0) + return; + + // Unless we just made a window/popup appear + if (g.NavWindow && g.NavWindow->Appearing) + return; + + // Click to focus window and start moving (after we're done with all our widgets) + if (g.IO.MouseClicked[0]) + { + if (g.HoveredRootWindow != NULL) + { + StartMouseMovingWindow(g.HoveredWindow); + if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar)) + if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) + g.MovingWindow = NULL; + } + else if (g.NavWindow != NULL && GetTopMostPopupModal() == NULL) + { + // Clicking on void disable focus + FocusWindow(NULL); + } + } + + // With right mouse button we close popups without changing focus based on where the mouse is aimed + // Instead, focus will be restored to the window under the bottom-most closed popup. + // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger) + if (g.IO.MouseClicked[1]) + { + // Find the top-most window between HoveredWindow and the top-most Modal Window. + // This is where we can trim the popup stack. + ImGuiWindow* modal = GetTopMostPopupModal(); + bool hovered_window_above_modal = false; + if (modal == NULL) + hovered_window_above_modal = true; + for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--) + { + ImGuiWindow* window = g.Windows[i]; + if (window == modal) + break; + if (window == g.HoveredWindow) + hovered_window_above_modal = true; + } + ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); + } +} + +static bool IsWindowActiveAndVisible(ImGuiWindow* window) +{ + return (window->Active) && (!window->Hidden); +} + +static void ImGui::UpdateMouseInputs() { - IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); ImGuiContext& g = *GImGui; - // Check user data - // (We pass an error message in the assert expression as a trick to get it visible to programmers who are not using a debugger, as most assert handlers display their argument) - IM_ASSERT(g.Initialized); - IM_ASSERT(g.IO.DeltaTime >= 0.0f && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)"); - IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value"); - IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); - IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); - IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting"); - IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)"); - IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); - for (int n = 0; n < ImGuiKey_COUNT; n++) - IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); + // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) + if (IsMousePosValid(&g.IO.MousePos)) + g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos); - // Do a simple check for required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was super recently added in 1.60 WIP) - if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) - IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); - - // Load settings on first frame - if (!g.SettingsLoaded) - { - IM_ASSERT(g.SettingsWindows.empty()); - LoadIniSettingsFromDisk(g.IO.IniFilename); - g.SettingsLoaded = true; - } - - g.Time += g.IO.DeltaTime; - g.FrameCount += 1; - g.TooltipOverrideCount = 0; - g.WindowsActiveCount = 0; - - SetCurrentFont(GetDefaultFont()); - IM_ASSERT(g.Font->IsLoaded()); - g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); - g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; - - g.OverlayDrawList.Clear(); - g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID); - g.OverlayDrawList.PushClipRectFullScreen(); - g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); - - // Mark rendering data as invalid to prevent user who may have a handle on it to use it - g.DrawData.Clear(); - - // Clear reference to active widget if the widget isn't alive anymore - if (!g.HoveredIdPreviousFrame) - g.HoveredIdTimer = 0.0f; - g.HoveredIdPreviousFrame = g.HoveredId; - g.HoveredId = 0; - g.HoveredIdAllowOverlap = false; - if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) - ClearActiveID(); - if (g.ActiveId) - g.ActiveIdTimer += g.IO.DeltaTime; - g.ActiveIdPreviousFrame = g.ActiveId; - g.ActiveIdIsAlive = false; - g.ActiveIdIsJustActivated = false; - if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId) - g.ScalarAsInputTextId = 0; - - // Elapse drag & drop payload - if (g.DragDropActive && g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) - { - ClearDragDrop(); - g.DragDropPayloadBufHeap.clear(); - memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); - } - g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr; - g.DragDropAcceptIdCurr = 0; - g.DragDropAcceptIdCurrRectSurface = FLT_MAX; - - // Update keyboard input state - memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); - for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) - g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; - - // Update gamepad/keyboard directional navigation - NavUpdate(); - - // Update mouse input state - // If mouse just appeared or disappeared (usually denoted by -FLT_MAX component, but in reality we test for -256000.0f) we cancel out movement in MouseDelta + // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; else @@ -3355,9 +3411,10 @@ void ImGui::NewFrame() g.IO.MouseDoubleClicked[i] = false; if (g.IO.MouseClicked[i]) { - if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime) + if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) { - if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) + ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); + if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) g.IO.MouseDoubleClicked[i] = true; g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click } @@ -3366,58 +3423,108 @@ void ImGui::NewFrame() g.IO.MouseClickedTime[i] = g.Time; } g.IO.MouseClickedPos[i] = g.IO.MousePos; + g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i]; g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; } else if (g.IO.MouseDown[i]) { - ImVec2 mouse_delta = g.IO.MousePos - g.IO.MouseClickedPos[i]; - g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, mouse_delta.x < 0.0f ? -mouse_delta.x : mouse_delta.x); - g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, mouse_delta.y < 0.0f ? -mouse_delta.y : mouse_delta.y); - g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(mouse_delta)); + // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold + ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); + g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos)); + g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); + g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); } + if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i]) + g.IO.MouseDownWasDoubleClick[i] = false; if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation g.NavDisableMouseHover = false; } +} - // Calculate frame-rate for the user, as a purely luxurious feature - g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; - g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); - g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame)); +void ImGui::UpdateMouseWheel() +{ + ImGuiContext& g = *GImGui; + if (!g.HoveredWindow || g.HoveredWindow->Collapsed) + return; + if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f) + return; - // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) - UpdateMovingWindow(); - - // Delay saving settings so we don't spam disk too much - if (g.SettingsDirtyTimer > 0.0f) + // Zoom / Scale window + // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned. + if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling && !g.HoveredWindow->Collapsed) { - g.SettingsDirtyTimer -= g.IO.DeltaTime; - if (g.SettingsDirtyTimer <= 0.0f) - SaveIniSettingsToDisk(g.IO.IniFilename); + ImGuiWindow* window = g.HoveredWindow; + const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); + const float scale = new_font_scale / window->FontWindowScale; + window->FontWindowScale = new_font_scale; + if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) + { + const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; + SetWindowPos(window, window->Pos + offset, 0); + window->Size = ImFloor(window->Size * scale); + window->SizeFull = ImFloor(window->SizeFull * scale); + } + return; } - // Find the window we are hovering - // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. - // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point. - // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. - g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow(); - g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; + // Mouse wheel scrolling + // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent + // FIXME: Lock scrolling window while not moving (see #2604) - ImGuiWindow* modal_window = GetFrontMostModalRootWindow(); - if (modal_window != NULL) + // Vertical Mouse Wheel scrolling + const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f; + if (wheel_y != 0.0f && !g.IO.KeyCtrl) { - g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f); + ImGuiWindow* window = g.HoveredWindow; + while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) + window = window->ParentWindow; + if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) + { + float max_step = window->InnerRect.GetHeight() * 0.67f; + float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step)); + SetWindowScrollY(window, window->Scroll.y - wheel_y * scroll_step); + } + } + + // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held + const float wheel_x = (g.IO.MouseWheelH != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheelH : (g.IO.MouseWheel != 0.0f && g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f; + if (wheel_x != 0.0f && !g.IO.KeyCtrl) + { + ImGuiWindow* window = g.HoveredWindow; + while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) + window = window->ParentWindow; + if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) + { + float max_step = window->InnerRect.GetWidth() * 0.67f; + float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step)); + SetWindowScrollX(window, window->Scroll.x - wheel_x * scroll_step); + } + } +} + +// The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) +void ImGui::UpdateHoveredWindowAndCaptureFlags() +{ + ImGuiContext& g = *GImGui; + + // Find the window hovered by mouse: + // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. + // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame. + // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. + FindHoveredWindow(); + + // Modal windows prevents cursor from hovering behind them. + ImGuiWindow* modal_window = GetTopMostPopupModal(); + if (modal_window) if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window)) g.HoveredRootWindow = g.HoveredWindow = NULL; - } - else - { - g.ModalWindowDarkeningRatio = 0.0f; - } - // Update the WantCaptureMouse/WantCaptureKeyboard flags, so user can capture/discard the inputs away from the rest of their application. - // When clicking outside of a window we assume the click is owned by the application and won't request capture. We need to track click ownership. + // Disabled mouse? + if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) + g.HoveredWindow = g.HoveredRootWindow = NULL; + + // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward. int mouse_earliest_button_down = -1; bool mouse_any_down = false; for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) @@ -3429,12 +3536,21 @@ void ImGui::NewFrame() if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down]) mouse_earliest_button_down = i; } - bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; + const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; + + // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. + // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) + const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; + if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) + g.HoveredWindow = g.HoveredRootWindow = NULL; + + // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app) if (g.WantCaptureMouseNextFrame != -1) g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0); else g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty()); + // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app) if (g.WantCaptureKeyboardNextFrame != -1) g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); else @@ -3442,162 +3558,248 @@ void ImGui::NewFrame() if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) g.IO.WantCaptureKeyboard = true; - g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : 0; + // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible + g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; +} + +void ImGui::NewFrame() +{ + IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); + ImGuiContext& g = *GImGui; + +#ifdef IMGUI_ENABLE_TEST_ENGINE + ImGuiTestEngineHook_PreNewFrame(&g); +#endif + + // Check user data + // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument) + IM_ASSERT(g.Initialized); + IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); + IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); + IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); + IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); + IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); + IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); + IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)!"); + IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); + IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); + + for (int n = 0; n < ImGuiKey_COUNT; n++) + IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); + + // Perform simple check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP) + if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) + IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); + + // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. + if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) + g.IO.ConfigWindowsResizeFromEdges = false; + + // Load settings on first frame (if not explicitly loaded manually before) + if (!g.SettingsLoaded) + { + IM_ASSERT(g.SettingsWindows.empty()); + if (g.IO.IniFilename) + LoadIniSettingsFromDisk(g.IO.IniFilename); + g.SettingsLoaded = true; + } + + // Save settings (with a delay after the last modification, so we don't spam disk too much) + if (g.SettingsDirtyTimer > 0.0f) + { + g.SettingsDirtyTimer -= g.IO.DeltaTime; + if (g.SettingsDirtyTimer <= 0.0f) + { + if (g.IO.IniFilename != NULL) + SaveIniSettingsToDisk(g.IO.IniFilename); + else + g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves. + g.SettingsDirtyTimer = 0.0f; + } + } + + g.Time += g.IO.DeltaTime; + g.FrameScopeActive = true; + g.FrameCount += 1; + g.TooltipOverrideCount = 0; + g.WindowsActiveCount = 0; + + // Setup current font and draw list shared data + g.IO.Fonts->Locked = true; + SetCurrentFont(GetDefaultFont()); + IM_ASSERT(g.Font->IsLoaded()); + g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); + g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; + g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; + if (g.Style.AntiAliasedLines) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; + if (g.Style.AntiAliasedFill) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; + if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; + + g.BackgroundDrawList.Clear(); + g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID); + g.BackgroundDrawList.PushClipRectFullScreen(); + + g.ForegroundDrawList.Clear(); + g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID); + g.ForegroundDrawList.PushClipRectFullScreen(); + + // Mark rendering data as invalid to prevent user who may have a handle on it to use it. + g.DrawData.Clear(); + + // Drag and drop keep the source ID alive so even if the source disappear our state is consistent + if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId) + KeepAliveID(g.DragDropPayload.SourceId); + + // Clear reference to active widget if the widget isn't alive anymore + if (!g.HoveredIdPreviousFrame) + g.HoveredIdTimer = 0.0f; + if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId)) + g.HoveredIdNotActiveTimer = 0.0f; + if (g.HoveredId) + g.HoveredIdTimer += g.IO.DeltaTime; + if (g.HoveredId && g.ActiveId != g.HoveredId) + g.HoveredIdNotActiveTimer += g.IO.DeltaTime; + g.HoveredIdPreviousFrame = g.HoveredId; + g.HoveredId = 0; + g.HoveredIdAllowOverlap = false; + if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) + ClearActiveID(); + if (g.ActiveId) + g.ActiveIdTimer += g.IO.DeltaTime; + g.LastActiveIdTimer += g.IO.DeltaTime; + g.ActiveIdPreviousFrame = g.ActiveId; + g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow; + g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore; + g.ActiveIdIsAlive = 0; + g.ActiveIdHasBeenEditedThisFrame = false; + g.ActiveIdPreviousFrameIsAlive = false; + g.ActiveIdIsJustActivated = false; + if (g.TempInputTextId != 0 && g.ActiveId != g.TempInputTextId) + g.TempInputTextId = 0; + + // Drag and drop + g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr; + g.DragDropAcceptIdCurr = 0; + g.DragDropAcceptIdCurrRectSurface = FLT_MAX; + g.DragDropWithinSourceOrTarget = false; + + // Update keyboard input state + memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); + for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) + g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; + + // Update gamepad/keyboard directional navigation + NavUpdate(); + + // Update mouse input state + UpdateMouseInputs(); + + // Calculate frame-rate for the user, as a purely luxurious feature + g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; + g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; + g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); + g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; + + // Find hovered window + // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame) + UpdateHoveredWindowAndCaptureFlags(); + + // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) + UpdateMouseMovingWindowNewFrame(); + + // Background darkening/whitening + if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f)) + g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f); + else + g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f); + g.MouseCursor = ImGuiMouseCursor_Arrow; g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; - g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default - - // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. - // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) - bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; - if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) - g.HoveredWindow = g.HoveredRootWindow = NULL; + g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default // Mouse wheel scrolling, scale - if (g.HoveredWindow && !g.HoveredWindow->Collapsed && (g.IO.MouseWheel != 0.0f || g.IO.MouseWheelH != 0.0f)) - { - // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set). - ImGuiWindow* window = g.HoveredWindow; - ImGuiWindow* scroll_window = window; - while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs) && scroll_window->ParentWindow) - scroll_window = scroll_window->ParentWindow; - const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs); - - if (g.IO.MouseWheel != 0.0f) - { - if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling) - { - // Zoom / Scale window - const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); - const float scale = new_font_scale / window->FontWindowScale; - window->FontWindowScale = new_font_scale; - - const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; - window->Pos += offset; - window->PosFloat += offset; - window->Size *= scale; - window->SizeFull *= scale; - } - else if (!g.IO.KeyCtrl && scroll_allowed) - { - // Mouse wheel vertical scrolling - float scroll_amount = 5 * scroll_window->CalcFontSize(); - scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f); - SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount); - } - } - if (g.IO.MouseWheelH != 0.0f && scroll_allowed) - { - // Mouse wheel horizontal scrolling (for hardware that supports it) - float scroll_amount = scroll_window->CalcFontSize(); - if (!g.IO.KeyCtrl && !(window->Flags & ImGuiWindowFlags_NoScrollWithMouse)) - SetWindowScrollX(window, window->Scroll.x - g.IO.MouseWheelH * scroll_amount); - } - } + UpdateMouseWheel(); // Pressing TAB activate widget focus - if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) + g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); + if (g.ActiveId == 0 && g.FocusTabPressed) { + // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also + // manipulate the Next fields even, even though they will be turned into Curr fields by the code below. + g.FocusRequestNextWindow = g.NavWindow; + g.FocusRequestNextCounterAll = INT_MAX; if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) - g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); + g.FocusRequestNextCounterTab = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); else - g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0; + g.FocusRequestNextCounterTab = g.IO.KeyShift ? -1 : 0; } + + // Turn queued focus request into current one + g.FocusRequestCurrWindow = NULL; + g.FocusRequestCurrCounterAll = g.FocusRequestCurrCounterTab = INT_MAX; + if (g.FocusRequestNextWindow != NULL) + { + ImGuiWindow* window = g.FocusRequestNextWindow; + g.FocusRequestCurrWindow = window; + if (g.FocusRequestNextCounterAll != INT_MAX && window->DC.FocusCounterAll != -1) + g.FocusRequestCurrCounterAll = ImModPositive(g.FocusRequestNextCounterAll, window->DC.FocusCounterAll + 1); + if (g.FocusRequestNextCounterTab != INT_MAX && window->DC.FocusCounterTab != -1) + g.FocusRequestCurrCounterTab = ImModPositive(g.FocusRequestNextCounterTab, window->DC.FocusCounterTab + 1); + g.FocusRequestNextWindow = NULL; + g.FocusRequestNextCounterAll = g.FocusRequestNextCounterTab = INT_MAX; + } + g.NavIdTabCounter = INT_MAX; // Mark all windows as not visible + IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size); for (int i = 0; i != g.Windows.Size; i++) { ImGuiWindow* window = g.Windows[i]; window->WasActive = window->Active; + window->BeginCount = 0; window->Active = false; window->WriteAccessed = false; } // Closing the focused window restore focus to the first active root window in descending z-order if (g.NavWindow && !g.NavWindow->WasActive) - FocusFrontMostActiveWindow(NULL); + FocusTopMostWindowUnderOne(NULL, NULL); // No window should be open at the beginning of the frame. // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. g.CurrentWindowStack.resize(0); - g.CurrentPopupStack.resize(0); - ClosePopupsOverWindow(g.NavWindow); + g.BeginPopupStack.resize(0); + ClosePopupsOverWindow(g.NavWindow, false); - // Create implicit window - we will only render it if the user has added something to it. + // Create implicit/fallback window - which we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. + // This fallback is particularly important as it avoid ImGui:: calls from crashing. SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver); Begin("Debug##Default"); -} + g.FrameScopePushedImplicitWindow = true; -static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) -{ - ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0)); - if (!settings) - settings = AddWindowSettings(name); - return (void*)settings; -} - -static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) -{ - ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; - float x, y; - int i; - if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y); - else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); - else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0); -} - -static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) -{ - // Gather data from windows that were active during this session - ImGuiContext& g = *imgui_ctx; - for (int i = 0; i != g.Windows.Size; i++) - { - ImGuiWindow* window = g.Windows[i]; - if (window->Flags & ImGuiWindowFlags_NoSavedSettings) - continue; - ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID); - if (!settings) - settings = AddWindowSettings(window->Name); - settings->Pos = window->Pos; - settings->Size = window->SizeFull; - settings->Collapsed = window->Collapsed; - } - - // Write a buffer - // If a window wasn't opened in this session we preserve its settings - buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve - for (int i = 0; i != g.SettingsWindows.Size; i++) - { - const ImGuiWindowSettings* settings = &g.SettingsWindows[i]; - if (settings->Pos.x == FLT_MAX) - continue; - const char* name = settings->Name; - if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() - name = p; - buf->appendf("[%s][%s]\n", handler->TypeName, name); - buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); - buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); - buf->appendf("Collapsed=%d\n", settings->Collapsed); - buf->appendf("\n"); - } +#ifdef IMGUI_ENABLE_TEST_ENGINE + ImGuiTestEngineHook_PostNewFrame(&g); +#endif } void ImGui::Initialize(ImGuiContext* context) { ImGuiContext& g = *context; IM_ASSERT(!g.Initialized && !g.SettingsLoaded); - g.LogClipboard = IM_NEW(ImGuiTextBuffer)(); // Add .ini handle for ImGuiWindow type ImGuiSettingsHandler ini_handler; ini_handler.TypeName = "Window"; - ini_handler.TypeHash = ImHash("Window", 0, 0); + ini_handler.TypeHash = ImHashStr("Window"); ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen; ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine; ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll; - g.SettingsHandlers.push_front(ini_handler); + g.SettingsHandlers.push_back(ini_handler); g.Initialized = true; } @@ -3605,45 +3807,54 @@ void ImGui::Initialize(ImGuiContext* context) // This function is merely here to free heap allocations. void ImGui::Shutdown(ImGuiContext* context) { - ImGuiContext& g = *context; - // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) + ImGuiContext& g = *context; if (g.IO.Fonts && g.FontAtlasOwnedByContext) + { + g.IO.Fonts->Locked = false; IM_DELETE(g.IO.Fonts); + } + g.IO.Fonts = NULL; - // Cleanup of other data are conditional on actually having initialize ImGui. + // Cleanup of other data are conditional on actually having initialized Dear ImGui. if (!g.Initialized) return; - SaveIniSettingsToDisk(g.IO.IniFilename); + // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) + if (g.SettingsLoaded && g.IO.IniFilename != NULL) + { + ImGuiContext* backup_context = GImGui; + SetCurrentContext(context); + SaveIniSettingsToDisk(g.IO.IniFilename); + SetCurrentContext(backup_context); + } // Clear everything else for (int i = 0; i < g.Windows.Size; i++) IM_DELETE(g.Windows[i]); g.Windows.clear(); + g.WindowsFocusOrder.clear(); g.WindowsSortBuffer.clear(); g.CurrentWindow = NULL; g.CurrentWindowStack.clear(); g.WindowsById.Clear(); g.NavWindow = NULL; - g.HoveredWindow = NULL; - g.HoveredRootWindow = NULL; - g.ActiveIdWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = NULL; + g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; g.MovingWindow = NULL; - for (int i = 0; i < g.SettingsWindows.Size; i++) - IM_DELETE(g.SettingsWindows[i].Name); g.ColorModifiers.clear(); g.StyleModifiers.clear(); g.FontStack.clear(); g.OpenPopupStack.clear(); - g.CurrentPopupStack.clear(); + g.BeginPopupStack.clear(); g.DrawDataBuilder.ClearFreeMemory(); - g.OverlayDrawList.ClearFreeMemory(); + g.BackgroundDrawList.ClearFreeMemory(); + g.ForegroundDrawList.ClearFreeMemory(); g.PrivateClipboard.clear(); - g.InputTextState.Text.clear(); - g.InputTextState.InitialText.clear(); - g.InputTextState.TempTextBuffer.clear(); + g.InputTextState.ClearFreeMemory(); + for (int i = 0; i < g.SettingsWindows.Size; i++) + IM_DELETE(g.SettingsWindows[i].Name); g.SettingsWindows.clear(); g.SettingsHandlers.clear(); @@ -3652,158 +3863,16 @@ void ImGui::Shutdown(ImGuiContext* context) fclose(g.LogFile); g.LogFile = NULL; } - if (g.LogClipboard) - IM_DELETE(g.LogClipboard); + g.LogBuffer.clear(); g.Initialized = false; } -ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - for (int i = 0; i != g.SettingsWindows.Size; i++) - if (g.SettingsWindows[i].Id == id) - return &g.SettingsWindows[i]; - return NULL; -} - -static ImGuiWindowSettings* AddWindowSettings(const char* name) -{ - ImGuiContext& g = *GImGui; - g.SettingsWindows.push_back(ImGuiWindowSettings()); - ImGuiWindowSettings* settings = &g.SettingsWindows.back(); - settings->Name = ImStrdup(name); - settings->Id = ImHash(name, 0); - return settings; -} - -static void LoadIniSettingsFromDisk(const char* ini_filename) -{ - if (!ini_filename) - return; - char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", NULL, +1); - if (!file_data) - return; - LoadIniSettingsFromMemory(file_data); - ImGui::MemFree(file_data); -} - -ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) -{ - ImGuiContext& g = *GImGui; - const ImGuiID type_hash = ImHash(type_name, 0, 0); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - if (g.SettingsHandlers[handler_n].TypeHash == type_hash) - return &g.SettingsHandlers[handler_n]; - return NULL; -} - -// Zero-tolerance, no error reporting, cheap .ini parsing -static void LoadIniSettingsFromMemory(const char* buf_readonly) -{ - // For convenience and to make the code simpler, we'll write zero terminators inside the buffer. So let's create a writable copy. - char* buf = ImStrdup(buf_readonly); - char* buf_end = buf + strlen(buf); - - ImGuiContext& g = *GImGui; - void* entry_data = NULL; - ImGuiSettingsHandler* entry_handler = NULL; - - char* line_end = NULL; - for (char* line = buf; line < buf_end; line = line_end + 1) - { - // Skip new lines markers, then find end of the line - while (*line == '\n' || *line == '\r') - line++; - line_end = line; - while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') - line_end++; - line_end[0] = 0; - - if (line[0] == '[' && line_end > line && line_end[-1] == ']') - { - // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. - line_end[-1] = 0; - const char* name_end = line_end - 1; - const char* type_start = line + 1; - char* type_end = ImStrchrRange(type_start, name_end, ']'); - const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; - if (!type_end || !name_start) - { - name_start = type_start; // Import legacy entries that have no type - type_start = "Window"; - } - else - { - *type_end = 0; // Overwrite first ']' - name_start++; // Skip second '[' - } - entry_handler = ImGui::FindSettingsHandler(type_start); - entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; - } - else if (entry_handler != NULL && entry_data != NULL) - { - // Let type handler parse the line - entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); - } - } - ImGui::MemFree(buf); - g.SettingsLoaded = true; -} - -static void SaveIniSettingsToDisk(const char* ini_filename) -{ - ImGuiContext& g = *GImGui; - g.SettingsDirtyTimer = 0.0f; - if (!ini_filename) - return; - - ImVector buf; - SaveIniSettingsToMemory(buf); - - FILE* f = ImFileOpen(ini_filename, "wt"); - if (!f) - return; - fwrite(buf.Data, sizeof(char), (size_t)buf.Size, f); - fclose(f); -} - -static void SaveIniSettingsToMemory(ImVector& out_buf) -{ - ImGuiContext& g = *GImGui; - g.SettingsDirtyTimer = 0.0f; - - ImGuiTextBuffer buf; - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - { - ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; - handler->WriteAllFn(&g, handler, &buf); - } - - buf.Buf.pop_back(); // Remove extra zero-terminator used by ImGuiTextBuffer - out_buf.swap(buf.Buf); -} - -void ImGui::MarkIniSettingsDirty() -{ - ImGuiContext& g = *GImGui; - if (g.SettingsDirtyTimer <= 0.0f) - g.SettingsDirtyTimer = g.IO.IniSavingRate; -} - -static void MarkIniSettingsDirty(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) - if (g.SettingsDirtyTimer <= 0.0f) - g.SettingsDirtyTimer = g.IO.IniSavingRate; -} - // FIXME: Add a more explicit sort order in the window structure. static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) { - const ImGuiWindow* a = *(const ImGuiWindow**)lhs; - const ImGuiWindow* b = *(const ImGuiWindow**)rhs; + const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs; + const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs; if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup)) return d; if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip)) @@ -3811,24 +3880,24 @@ static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) return (a->BeginOrderWithinParent - b->BeginOrderWithinParent); } -static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) +static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) { out_sorted_windows->push_back(window); if (window->Active) { int count = window->DC.ChildWindows.Size; if (count > 1) - qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); + ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); for (int i = 0; i < count; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; if (child->Active) - AddWindowToSortedBuffer(out_sorted_windows, child); + AddWindowToSortBuffer(out_sorted_windows, child); } } } -static void AddDrawListToDrawData(ImVector* out_render_list, ImDrawList* draw_list) +static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list) { if (draw_list->CmdBuffer.empty()) return; @@ -3842,40 +3911,51 @@ static void AddDrawListToDrawData(ImVector* out_render_list, ImDraw return; } - // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. May trigger for you if you are using PrimXXX functions incorrectly. + // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. + // May trigger for you if you are using PrimXXX functions incorrectly. IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); - IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); + if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset)) + IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) // If this assert triggers because you are drawing lots of stuff manually: - // A) Make sure you are coarse clipping, because ImDrawList let all your vertices pass. You can use the Metrics window to inspect draw list contents. - // B) If you need/want meshes with more than 64K vertices, uncomment the '#define ImDrawIdx unsigned int' line in imconfig.h to set the index size to 4 bytes. - // You'll need to handle the 4-bytes indices to your renderer. For example, the OpenGL example code detect index size at compile-time by doing: - // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); - // Your own engine or render API may use different parameters or function calls to specify index sizes. 2 and 4 bytes indices are generally supported by most API. - // C) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists. + // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. + // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics window to inspect draw list contents. + // - If you want large meshes with more than 64K vertices, you can either: + // (A) Handle the ImDrawCmd::VtxOffset value in your renderer back-end, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. + // Most example back-ends already support this from 1.71. Pre-1.71 back-ends won't. + // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them. + // (B) Or handle 32-bits indices in your renderer back-end, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h. + // Most example back-ends already support this. For example, the OpenGL example code detect index size at compile-time: + // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); + // Your own engine or render API may use different parameters or function calls to specify index sizes. + // 2 and 4 bytes indices are generally supported by most graphics API. + // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching + // the 64K limit to split your draw commands in multiple draw lists. if (sizeof(ImDrawIdx) == 2) IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); - out_render_list->push_back(draw_list); + out_list->push_back(draw_list); } static void AddWindowToDrawData(ImVector* out_render_list, ImGuiWindow* window) { + ImGuiContext& g = *GImGui; + g.IO.MetricsRenderWindows++; AddDrawListToDrawData(out_render_list, window->DrawList); for (int i = 0; i < window->DC.ChildWindows.Size; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; - if (child->Active && child->HiddenFrames <= 0) // clipped children may have been marked not active + if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active AddWindowToDrawData(out_render_list, child); } } -static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window) +// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) +static void AddRootWindowToDrawData(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - g.IO.MetricsActiveWindows++; if (window->Flags & ImGuiWindowFlags_Tooltip) AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window); else @@ -3900,16 +3980,20 @@ void ImDrawDataBuilder::FlattenIntoSingleLayer() } } -static void SetupDrawData(ImVector* draw_lists, ImDrawData* out_draw_data) +static void SetupDrawData(ImVector* draw_lists, ImDrawData* draw_data) { - out_draw_data->Valid = true; - out_draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; - out_draw_data->CmdListsCount = draw_lists->Size; - out_draw_data->TotalVtxCount = out_draw_data->TotalIdxCount = 0; + ImGuiIO& io = ImGui::GetIO(); + draw_data->Valid = true; + draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; + draw_data->CmdListsCount = draw_lists->Size; + draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; + draw_data->DisplayPos = ImVec2(0.0f, 0.0f); + draw_data->DisplaySize = io.DisplaySize; + draw_data->FramebufferScale = io.DisplayFramebufferScale; for (int n = 0; n < draw_lists->Size; n++) { - out_draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; - out_draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; + draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; + draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; } } @@ -3932,70 +4016,68 @@ void ImGui::PopClipRect() void ImGui::EndFrame() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() + IM_ASSERT(g.Initialized); if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times. return; + IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()?"); // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) - if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f) + if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)) { - g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y); - g.OsImePosSet = g.OsImePosRequest; + g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y); + g.PlatformImeLastPos = g.PlatformImePos; } - // Hide implicit "Debug" window if it hasn't been used - IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls + // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you + // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API). + if (g.CurrentWindowStack.Size != 1) + { + if (g.CurrentWindowStack.Size > 1) + { + IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?"); + while (g.CurrentWindowStack.Size > 1) // FIXME-ERRORHANDLING + End(); + } + else + { + IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?"); + } + } + + // Hide implicit/fallback "Debug" window if it hasn't been used + g.FrameScopePushedImplicitWindow = false; if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed) g.CurrentWindow->Active = false; End(); - if (g.ActiveId == 0 && g.HoveredId == 0) - { - if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear - { - // Click to focus window and start moving (after we're done with all our widgets) - if (g.IO.MouseClicked[0]) - { - if (g.HoveredRootWindow != NULL) - { - // Set ActiveId even if the _NoMove flag is set, without it dragging away from a window with _NoMove would activate hover on other windows. - FocusWindow(g.HoveredWindow); - SetActiveID(g.HoveredWindow->MoveId, g.HoveredWindow); - g.NavDisableHighlight = true; - g.ActiveIdClickOffset = g.IO.MousePos - g.HoveredRootWindow->Pos; - if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove) && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoMove)) - g.MovingWindow = g.HoveredWindow; - } - else if (g.NavWindow != NULL && GetFrontMostModalRootWindow() == NULL) - { - // Clicking on void disable focus - FocusWindow(NULL); - } - } + // Show CTRL+TAB list window + if (g.NavWindowingTarget) + NavUpdateWindowingList(); - // With right mouse button we close popups without changing focus - // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger) - if (g.IO.MouseClicked[1]) - { - // Find the top-most window between HoveredWindow and the front most Modal Window. - // This is where we can trim the popup stack. - ImGuiWindow* modal = GetFrontMostModalRootWindow(); - bool hovered_window_above_modal = false; - if (modal == NULL) - hovered_window_above_modal = true; - for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--) - { - ImGuiWindow* window = g.Windows[i]; - if (window == modal) - break; - if (window == g.HoveredWindow) - hovered_window_above_modal = true; - } - ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal); - } - } + // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) + if (g.DragDropActive) + { + bool is_delivered = g.DragDropPayload.Delivery; + bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton)); + if (is_delivered || is_elapsed) + ClearDragDrop(); } + // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing. + if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount) + { + g.DragDropWithinSourceOrTarget = true; + SetTooltip("..."); + g.DragDropWithinSourceOrTarget = false; + } + + // End frame + g.FrameScopeActive = false; + g.FrameCountEnded = g.FrameCount; + + // Initiate moving window + handle left-click and right-click focus + UpdateMouseMovingWindowEndFrame(); + // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because childs may not exist yet g.WindowsSortBuffer.resize(0); @@ -4005,363 +4087,71 @@ void ImGui::EndFrame() ImGuiWindow* window = g.Windows[i]; if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it continue; - AddWindowToSortedBuffer(&g.WindowsSortBuffer, window); + AddWindowToSortBuffer(&g.WindowsSortBuffer, window); } - IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong + // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong. + IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); g.Windows.swap(g.WindowsSortBuffer); + g.IO.MetricsActiveWindows = g.WindowsActiveCount; + + // Unlock font atlas + g.IO.Fonts->Locked = false; // Clear Input data for next frame g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; - memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); + g.IO.InputQueueCharacters.resize(0); memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); - - g.FrameCountEnded = g.FrameCount; } void ImGui::Render() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() + IM_ASSERT(g.Initialized); if (g.FrameCountEnded != g.FrameCount) - ImGui::EndFrame(); + EndFrame(); g.FrameCountRendered = g.FrameCount; - // Gather windows to render - g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0; + // Gather ImDrawList to render (for each active window) + g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0; g.DrawDataBuilder.Clear(); - ImGuiWindow* window_to_render_front_most = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget : NULL; + if (!g.BackgroundDrawList.VtxBuffer.empty()) + AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList); + + ImGuiWindow* windows_to_render_top_most[2]; + windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; + windows_to_render_top_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL; for (int n = 0; n != g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; - if (window->Active && window->HiddenFrames <= 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != window_to_render_front_most) - AddWindowToDrawDataSelectLayer(window); + if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) + AddRootWindowToDrawData(window); } - if (window_to_render_front_most && window_to_render_front_most->Active && window_to_render_front_most->HiddenFrames <= 0) // NavWindowingTarget is always temporarily displayed as the front-most window - AddWindowToDrawDataSelectLayer(window_to_render_front_most); + for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++) + if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window + AddRootWindowToDrawData(windows_to_render_top_most[n]); g.DrawDataBuilder.FlattenIntoSingleLayer(); // Draw software mouse cursor if requested - ImVec2 offset, size, uv[4]; - if (g.IO.MouseDrawCursor && g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &offset, &size, &uv[0], &uv[2])) - { - const ImVec2 pos = g.IO.MousePos - offset; - const ImTextureID tex_id = g.IO.Fonts->TexID; - const float sc = g.Style.MouseCursorScale; - g.OverlayDrawList.PushTextureID(tex_id); - g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(1,0)*sc, pos+ImVec2(1,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow - g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(2,0)*sc, pos+ImVec2(2,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow - g.OverlayDrawList.AddImage(tex_id, pos, pos + size*sc, uv[2], uv[3], IM_COL32(0,0,0,255)); // Black border - g.OverlayDrawList.AddImage(tex_id, pos, pos + size*sc, uv[0], uv[1], IM_COL32(255,255,255,255)); // White fill - g.OverlayDrawList.PopTextureID(); - } - if (!g.OverlayDrawList.VtxBuffer.empty()) - AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList); + if (g.IO.MouseDrawCursor) + RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor); + + if (!g.ForegroundDrawList.VtxBuffer.empty()) + AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList); // Setup ImDrawData structure for end-user SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData); g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount; g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount; - // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData() + // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) g.IO.RenderDrawListsFn(&g.DrawData); #endif } -const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) -{ - const char* text_display_end = text; - if (!text_end) - text_end = (const char*)-1; - - while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) - text_display_end++; - return text_display_end; -} - -// Pass text data straight to log (without being displayed) -void ImGui::LogText(const char* fmt, ...) -{ - ImGuiContext& g = *GImGui; - if (!g.LogEnabled) - return; - - va_list args; - va_start(args, fmt); - if (g.LogFile) - { - vfprintf(g.LogFile, fmt, args); - } - else - { - g.LogClipboard->appendfv(fmt, args); - } - va_end(args); -} - -// Internal version that takes a position to decide on newline placement and pad items according to their depth. -// We split text into individual lines to add current tree level padding -static void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (!text_end) - text_end = ImGui::FindRenderedTextEnd(text, text_end); - - const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1); - if (ref_pos) - window->DC.LogLinePosY = ref_pos->y; - - const char* text_remaining = text; - if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth - g.LogStartDepth = window->DC.TreeDepth; - const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth); - for (;;) - { - // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. - const char* line_end = text_remaining; - while (line_end < text_end) - if (*line_end == '\n') - break; - else - line_end++; - if (line_end >= text_end) - line_end = NULL; - - const bool is_first_line = (text == text_remaining); - bool is_last_line = false; - if (line_end == NULL) - { - is_last_line = true; - line_end = text_end; - } - if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0)) - { - const int char_count = (int)(line_end - text_remaining); - if (log_new_line || !is_first_line) - ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining); - else - ImGui::LogText(" %.*s", char_count, text_remaining); - } - - if (is_last_line) - break; - text_remaining = line_end + 1; - } -} - -// Internal ImGui functions to render text -// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() -void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // Hide anything after a '##' string - const char* text_display_end; - if (hide_text_after_hash) - { - text_display_end = FindRenderedTextEnd(text, text_end); - } - else - { - if (!text_end) - text_end = text + strlen(text); // FIXME-OPT - text_display_end = text_end; - } - - const int text_len = (int)(text_display_end - text); - if (text_len > 0) - { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); - if (g.LogEnabled) - LogRenderedText(&pos, text, text_display_end); - } -} - -void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (!text_end) - text_end = text + strlen(text); // FIXME-OPT - - const int text_len = (int)(text_end - text); - if (text_len > 0) - { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); - if (g.LogEnabled) - LogRenderedText(&pos, text, text_end); - } -} - -// Default clip_rect uses (pos_min,pos_max) -// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) -void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) -{ - // Hide anything after a '##' string - const char* text_display_end = FindRenderedTextEnd(text, text_end); - const int text_len = (int)(text_display_end - text); - if (text_len == 0) - return; - - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // Perform CPU side clipping for single clipped element to avoid using scissor state - ImVec2 pos = pos_min; - const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); - - const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; - const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; - bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); - if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min - need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); - - // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment. - if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); - if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); - - // Render - if (need_clipping) - { - ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); - } - else - { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); - } - if (g.LogEnabled) - LogRenderedText(&pos, text, text_display_end); -} - -// Render a rectangle shaped with optional rounding and borders -void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); - const float border_size = g.Style.FrameBorderSize; - if (border && border_size > 0.0f) - { - window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); - window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); - } -} - -void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - const float border_size = g.Style.FrameBorderSize; - if (border_size > 0.0f) - { - window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); - window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); - } -} - -// Render a triangle to denote expanded/collapsed state -void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - const float h = g.FontSize * 1.00f; - float r = h * 0.40f * scale; - ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale); - - ImVec2 a, b, c; - switch (dir) - { - case ImGuiDir_Up: - case ImGuiDir_Down: - if (dir == ImGuiDir_Up) r = -r; - center.y -= r * 0.25f; - a = ImVec2(0,1) * r; - b = ImVec2(-0.866f,-0.5f) * r; - c = ImVec2(+0.866f,-0.5f) * r; - break; - case ImGuiDir_Left: - case ImGuiDir_Right: - if (dir == ImGuiDir_Left) r = -r; - center.x -= r * 0.25f; - a = ImVec2(1,0) * r; - b = ImVec2(-0.500f,+0.866f) * r; - c = ImVec2(-0.500f,-0.866f) * r; - break; - case ImGuiDir_None: - case ImGuiDir_COUNT: - IM_ASSERT(0); - break; - } - - window->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text)); -} - -void ImGui::RenderBullet(ImVec2 pos) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8); -} - -void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - float thickness = ImMax(sz / 5.0f, 1.0f); - sz -= thickness*0.5f; - pos += ImVec2(thickness*0.25f, thickness*0.25f); - - float third = sz / 3.0f; - float bx = pos.x + third; - float by = pos.y + sz - third*0.5f; - window->DrawList->PathLineTo(ImVec2(bx - third, by - third)); - window->DrawList->PathLineTo(ImVec2(bx, by)); - window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2)); - window->DrawList->PathStroke(col, false, thickness); -} - -void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) -{ - ImGuiContext& g = *GImGui; - if (id != g.NavId) - return; - if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw)) - return; - ImGuiWindow* window = ImGui::GetCurrentWindow(); - if (window->DC.NavHideHighlightOneFrame) - return; - - float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; - ImRect display_rect = bb; - display_rect.ClipWith(window->ClipRect); - if (flags & ImGuiNavHighlightFlags_TypeDefault) - { - const float THICKNESS = 2.0f; - const float DISTANCE = 3.0f + THICKNESS * 0.5f; - display_rect.Expand(ImVec2(DISTANCE,DISTANCE)); - bool fully_visible = window->ClipRect.Contains(display_rect); - if (!fully_visible) - window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); - window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS); - if (!fully_visible) - window->DrawList->PopClipRect(); - } - if (flags & ImGuiNavHighlightFlags_TypeThin) - { - window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f); - } -} - // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize) ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) @@ -4380,69 +4170,53 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex return ImVec2(0.0f, font_size); ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); - // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field) - const float font_scale = font_size / font->FontSize; - const float character_spacing_x = 1.0f * font_scale; - if (text_size.x > 0.0f) - text_size.x -= character_spacing_x; + // Round text_size.x = (float)(int)(text_size.x + 0.95f); return text_size; } -// Helper to calculate coarse clipping of large list of evenly sized items. -// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. -// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX -void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.LogEnabled) - { - // If logging is active, do not perform any clipping - *out_items_display_start = 0; - *out_items_display_end = items_count; - return; - } - if (window->SkipItems) - { - *out_items_display_start = *out_items_display_end = 0; - return; - } - - const ImVec2 pos = window->DC.CursorPos; - int start = (int)((window->ClipRect.Min.y - pos.y) / items_height); - int end = (int)((window->ClipRect.Max.y - pos.y) / items_height); - if (g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Up) // When performing a navigation request, ensure we have one item extra in the direction we are moving to - start--; - if (g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) - end++; - - start = ImClamp(start, 0, items_count); - end = ImClamp(end + 1, start, items_count); - *out_items_display_start = start; - *out_items_display_end = end; -} - // Find window given position, search front-to-back -// FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected. -static ImGuiWindow* FindHoveredWindow() +// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically +// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is +// called, aka before the next Begin(). Moving window isn't affected. +static void FindHoveredWindow() { ImGuiContext& g = *GImGui; + + ImGuiWindow* hovered_window = NULL; + if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs)) + hovered_window = g.MovingWindow; + + ImVec2 padding_regular = g.Style.TouchExtraPadding; + ImVec2 padding_for_resize_from_edges = g.IO.ConfigWindowsResizeFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)) : padding_regular; for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* window = g.Windows[i]; - if (!window->Active) + if (!window->Active || window->Hidden) continue; - if (window->Flags & ImGuiWindowFlags_NoInputs) + if (window->Flags & ImGuiWindowFlags_NoMouseInputs) continue; // Using the clipped AABB, a child window will typically be clipped by its parent (not always) - ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding); - if (bb.Contains(g.IO.MousePos)) - return window; + ImRect bb(window->OuterRectClipped); + if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) + bb.Expand(padding_regular); + else + bb.Expand(padding_for_resize_from_edges); + if (!bb.Contains(g.IO.MousePos)) + continue; + + // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches. + if (hovered_window == NULL) + hovered_window = window; + if (hovered_window) + break; } - return NULL; + + g.HoveredWindow = hovered_window; + g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; + } // Test if mouse cursor is hovering given rectangle @@ -4451,22 +4225,17 @@ static ImGuiWindow* FindHoveredWindow() bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; // Clip ImRect rect_clipped(r_min, r_max); if (clip) - rect_clipped.ClipWith(window->ClipRect); + rect_clipped.ClipWith(g.CurrentWindow->ClipRect); // Expand for touch input const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); - return rect_for_touch.Contains(g.IO.MousePos); -} - -static bool IsKeyPressedMap(ImGuiKey key, bool repeat) -{ - const int key_index = GImGui->IO.KeyMap[key]; - return (key_index >= 0) ? ImGui::IsKeyPressed(key_index, repeat) : false; + if (!rect_for_touch.Contains(g.IO.MousePos)) + return false; + return true; } int ImGui::GetKeyIndex(ImGuiKey imgui_key) @@ -4475,7 +4244,7 @@ int ImGui::GetKeyIndex(ImGuiKey imgui_key) return GImGui->IO.KeyMap[imgui_key]; } -// Note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your back-end/engine stored them into KeyDown[]! +// Note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]! bool ImGui::IsKeyDown(int user_key_index) { if (user_key_index < 0) return false; @@ -4496,7 +4265,8 @@ int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_ int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate) { ImGuiContext& g = *GImGui; - if (key_index < 0) return false; + if (key_index < 0) + return 0; IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); const float t = g.IO.KeysDownDuration[key_index]; return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate); @@ -4505,7 +4275,8 @@ int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_r bool ImGui::IsKeyPressed(int user_key_index, bool repeat) { ImGuiContext& g = *GImGui; - if (user_key_index < 0) return false; + if (user_key_index < 0) + return false; IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); const float t = g.IO.KeysDownDuration[user_key_index]; if (t == 0.0f) @@ -4549,8 +4320,9 @@ bool ImGui::IsMouseClicked(int button, bool repeat) if (repeat && t > g.IO.KeyRepeatDelay) { - float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate; - if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f)) + // FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold. + int amount = CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.5f); + if (amount > 0) return true; } @@ -4591,20 +4363,24 @@ ImVec2 ImGui::GetMousePos() ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() { ImGuiContext& g = *GImGui; - if (g.CurrentPopupStack.Size > 0) - return g.OpenPopupStack[g.CurrentPopupStack.Size-1].OpenMousePos; + if (g.BeginPopupStack.Size > 0) + return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos; return g.IO.MousePos; } -// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position +// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position. bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) { - if (mouse_pos == NULL) - mouse_pos = &GImGui->IO.MousePos; + // The assert is only to silence a false-positive in XCode Static Analysis. + // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions). + IM_ASSERT(GImGui != NULL); const float MOUSE_INVALID = -256000.0f; - return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID; + ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos; + return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID; } +// Return the delta from the initial clicking position while the mouse button is clicked or was just released. +// This is locked and return 0.0f until the mouse moves past a distance threshold at least once. // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window. ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold) { @@ -4612,9 +4388,10 @@ ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold) IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); if (lock_threshold < 0.0f) lock_threshold = g.IO.MouseDragThreshold; - if (g.IO.MouseDown[button]) + if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) - return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment). + if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button])) + return g.IO.MousePos - g.IO.MouseClickedPos[button]; return ImVec2(0.0f, 0.0f); } @@ -4657,15 +4434,52 @@ bool ImGui::IsItemActive() return false; } +bool ImGui::IsItemActivated() +{ + ImGuiContext& g = *GImGui; + if (g.ActiveId) + { + ImGuiWindow* window = g.CurrentWindow; + if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId) + return true; + } + return false; +} + +bool ImGui::IsItemDeactivated() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated) + return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; + return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId); +} + +bool ImGui::IsItemDeactivatedAfterEdit() +{ + ImGuiContext& g = *GImGui; + return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore)); +} + bool ImGui::IsItemFocused() { ImGuiContext& g = *GImGui; - return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId; + ImGuiWindow* window = g.CurrentWindow; + + if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId) + return false; + return true; } bool ImGui::IsItemClicked(int mouse_button) { - return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_Default); + return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None); +} + +bool ImGui::IsItemToggledSelection() +{ + ImGuiContext& g = *GImGui; + return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; } bool ImGui::IsAnyItemHovered() @@ -4692,6 +4506,12 @@ bool ImGui::IsItemVisible() return window->ClipRect.Overlaps(window->DC.LastItemRect); } +bool ImGui::IsItemEdited() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0; +} + // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. void ImGui::SetItemAllowOverlap() { @@ -4723,363 +4543,57 @@ ImVec2 ImGui::GetItemRectSize() static ImRect GetViewportRect() { ImGuiContext& g = *GImGui; - if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y) - return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax); return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); } -// Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first. -void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip) -{ - ImGuiContext& g = *GImGui; - char window_name[16]; - ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); - if (override_previous_tooltip) - if (ImGuiWindow* window = FindWindowByName(window_name)) - if (window->Active) - { - // Hide previous tooltips. We can't easily "reset" the content of a window so we create a new one. - window->HiddenFrames = 1; - ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); - } - ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNav; - Begin(window_name, NULL, flags | extra_flags); -} - -void ImGui::SetTooltipV(const char* fmt, va_list args) -{ - BeginTooltipEx(0, true); - TextV(fmt, args); - EndTooltip(); -} - -void ImGui::SetTooltip(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - SetTooltipV(fmt, args); - va_end(args); -} - -void ImGui::BeginTooltip() -{ - BeginTooltipEx(0, false); -} - -void ImGui::EndTooltip() -{ - IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls - End(); -} - -// Mark popup as open (toggle toward open state). -// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. -// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). -// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) -void ImGui::OpenPopupEx(ImGuiID id) +static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* parent_window = g.CurrentWindow; - int current_stack_size = g.CurrentPopupStack.Size; - ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. - popup_ref.PopupId = id; - popup_ref.Window = NULL; - popup_ref.ParentWindow = parent_window; - popup_ref.OpenFrameCount = g.FrameCount; - popup_ref.OpenParentId = parent_window->IDStack.back(); - popup_ref.OpenMousePos = g.IO.MousePos; - popup_ref.OpenPopupPos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos; - if (g.OpenPopupStack.Size < current_stack_size + 1) - { - g.OpenPopupStack.push_back(popup_ref); - } - else - { - // Close child popups if any - g.OpenPopupStack.resize(current_stack_size + 1); - - // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui - // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing - // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand. - if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) - g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; - else - g.OpenPopupStack[current_stack_size] = popup_ref; - - // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). - // This is equivalent to what ClosePopupToLevel() does. - //if (g.OpenPopupStack[current_stack_size].PopupId == id) - // FocusWindow(parent_window); - } -} - -void ImGui::OpenPopup(const char* str_id) -{ - ImGuiContext& g = *GImGui; - OpenPopupEx(g.CurrentWindow->GetID(str_id)); -} - -void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) -{ - ImGuiContext& g = *GImGui; - if (g.OpenPopupStack.empty()) - return; - - // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. - // Don't close our own child popup windows. - int n = 0; - if (ref_window) - { - for (n = 0; n < g.OpenPopupStack.Size; n++) - { - ImGuiPopupRef& popup = g.OpenPopupStack[n]; - if (!popup.Window) - continue; - IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); - if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) - continue; - - // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow) - bool has_focus = false; - for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++) - has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow); - if (!has_focus) - break; - } - } - if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below - ClosePopupToLevel(n); -} - -static ImGuiWindow* GetFrontMostModalRootWindow() -{ - ImGuiContext& g = *GImGui; - for (int n = g.OpenPopupStack.Size-1; n >= 0; n--) - if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) - if (popup->Flags & ImGuiWindowFlags_Modal) - return popup; - return NULL; -} - -static void ClosePopupToLevel(int remaining) -{ - IM_ASSERT(remaining >= 0); - ImGuiContext& g = *GImGui; - ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow; - if (g.NavLayer == 0) - focus_window = NavRestoreLastChildNavWindow(focus_window); - ImGui::FocusWindow(focus_window); - focus_window->DC.NavHideHighlightOneFrame = true; - g.OpenPopupStack.resize(remaining); -} - -void ImGui::ClosePopup(ImGuiID id) -{ - if (!IsPopupOpen(id)) - return; - ImGuiContext& g = *GImGui; - ClosePopupToLevel(g.OpenPopupStack.Size - 1); -} - -// Close the popup we have begin-ed into. -void ImGui::CloseCurrentPopup() -{ - ImGuiContext& g = *GImGui; - int popup_idx = g.CurrentPopupStack.Size - 1; - if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) - return; - while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu)) - popup_idx--; - ClosePopupToLevel(popup_idx); -} - -bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) -{ - ImGuiContext& g = *GImGui; - if (!IsPopupOpen(id)) - { - g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values - return false; - } - - char name[20]; - if (extra_flags & ImGuiWindowFlags_ChildMenu) - ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth - else - ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame - - bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup); - if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) - EndPopup(); - - return is_open; -} - -bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance - { - g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values - return false; - } - return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); -} - -bool ImGui::IsPopupOpen(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id; -} - -bool ImGui::IsPopupOpen(const char* str_id) -{ - ImGuiContext& g = *GImGui; - return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id); -} - -bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - const ImGuiID id = window->GetID(name); - if (!IsPopupOpen(id)) - { - g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values - return false; - } - - // Center modal windows by default - // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. - if (g.NextWindowData.PosCond == 0) - SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - - bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings); - if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) - { - EndPopup(); - if (is_open) - ClosePopup(id); - return false; - } - - return is_open; -} - -static void NavProcessMoveRequestWrapAround(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (g.NavWindow == window && NavMoveRequestButNoResultYet()) - if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == 0) - { - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - ImGui::NavMoveRequestCancel(); - g.NavWindow->NavRectRel[0].Min.y = g.NavWindow->NavRectRel[0].Max.y = ((g.NavMoveDir == ImGuiDir_Up) ? ImMax(window->SizeFull.y, window->SizeContents.y) : 0.0f) - window->Scroll.y; - } -} - -void ImGui::EndPopup() -{ - ImGuiContext& g = *GImGui; (void)g; - IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls - IM_ASSERT(g.CurrentPopupStack.Size > 0); - - // Make all menus and popups wrap around for now, may need to expose that policy. - NavProcessMoveRequestWrapAround(g.CurrentWindow); - - End(); -} - -bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - { - ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - OpenPopupEx(id); - return true; - } - return false; -} - -// This is a helper to handle the simplest case of associating one named popup to one given widget. -// You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). -// You can pass a NULL str_id to use the identifier of the last item. -bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - OpenPopupEx(id); - return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); -} - -bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items) -{ - if (!str_id) - str_id = "window_context"; - ImGuiID id = GImGui->CurrentWindow->GetID(str_id); - if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - if (also_over_items || !IsAnyItemHovered()) - OpenPopupEx(id); - return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); -} - -bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) -{ - if (!str_id) - str_id = "void_context"; - ImGuiID id = GImGui->CurrentWindow->GetID(str_id); - if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) - OpenPopupEx(id); - return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); -} - -static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* parent_window = ImGui::GetCurrentWindow(); - ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow; + flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow; flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag - const ImVec2 content_avail = ImGui::GetContentRegionAvail(); + // Size + const ImVec2 content_avail = GetContentRegionAvail(); ImVec2 size = ImFloor(size_arg); const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00); if (size.x <= 0.0f) size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) if (size.y <= 0.0f) size.y = ImMax(content_avail.y + size.y, 4.0f); + SetNextWindowSize(size); - const float backup_border_size = g.Style.ChildBorderSize; - if (!border) - g.Style.ChildBorderSize = 0.0f; - flags |= extra_flags; - + // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value. char title[256]; if (name) ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id); else ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id); - ImGui::SetNextWindowSize(size); - bool ret = ImGui::Begin(title, NULL, flags); - ImGuiWindow* child_window = ImGui::GetCurrentWindow(); - child_window->ChildId = id; - child_window->AutoFitChildAxises = auto_fit_axises; + const float backup_border_size = g.Style.ChildBorderSize; + if (!border) + g.Style.ChildBorderSize = 0.0f; + bool ret = Begin(title, NULL, flags); g.Style.ChildBorderSize = backup_border_size; + ImGuiWindow* child_window = g.CurrentWindow; + child_window->ChildId = id; + child_window->AutoFitChildAxises = auto_fit_axises; + + // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually. + // While this is not really documented/defined, it seems that the expected thing to do. + if (child_window->BeginCount == 1) + parent_window->DC.CursorPos = child_window->Pos; + // Process navigation-in immediately so NavInit can run on first frame - if (!(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id) + if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll)) { - ImGui::FocusWindow(child_window); - ImGui::NavInitWindow(child_window, false); - ImGui::SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item + FocusWindow(child_window); + NavInitWindow(child_window, false); + SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item g.ActiveIdSource = ImGuiInputSource_Nav; } - return ret; } @@ -5107,8 +4621,7 @@ void ImGui::EndChild() } else { - // When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting. - ImVec2 sz = GetWindowSize(); + ImVec2 sz = window->Size; if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f sz.x = ImMax(4.0f, sz.x); if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y)) @@ -5144,14 +4657,15 @@ bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags ext PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); - return BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags); + bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags); + PopStyleVar(3); + PopStyleColor(); + return ret; } void ImGui::EndChildFrame() { EndChild(); - PopStyleVar(3); - PopStyleColor(); } // Save and compare stack sizes on Begin()/End() to detect usage errors @@ -5159,82 +4673,17 @@ static void CheckStacksSize(ImGuiWindow* window, bool write) { // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) ImGuiContext& g = *GImGui; - int* p_backup = &window->DC.StackSizesBackup[0]; - { int current = window->IDStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop() - { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup() - { int current = g.CurrentPopupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup() - { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor() - { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar() - { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont() + short* p_backup = &window->DC.StackSizesBackup[0]; + { int current = window->IDStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop() + { int current = window->DC.GroupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup() + { int current = g.BeginPopupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup() + // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them. + { int current = g.ColorModifiers.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor() + { int current = g.StyleModifiers.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar() + { int current = g.FontStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont() IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup)); } -enum ImGuiPopupPositionPolicy -{ - ImGuiPopupPositionPolicy_Default, - ImGuiPopupPositionPolicy_ComboBox -}; - -static ImVec2 FindBestWindowPosForPopup(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default) -{ - const ImGuiStyle& style = GImGui->Style; - - // r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) - // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. - ImVec2 safe_padding = style.DisplaySafeAreaPadding; - ImRect r_outer(GetViewportRect()); - r_outer.Expand(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? -safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? -safe_padding.y : 0.0f)); - ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); - //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); - //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); - - // Combo Box policy (we want a connecting edge) - if (policy == ImGuiPopupPositionPolicy_ComboBox) - { - const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; - for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) - { - const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; - if (n != -1 && dir == *last_dir) // Already tried this direction? - continue; - ImVec2 pos; - if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default) - if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right - if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left - if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left - if (!r_outer.Contains(ImRect(pos, pos + size))) - continue; - *last_dir = dir; - return pos; - } - } - - // Default popup policy - const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; - for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) - { - const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; - if (n != -1 && dir == *last_dir) // Already tried this direction? - continue; - float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x); - float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y); - if (avail_w < size.x || avail_h < size.y) - continue; - ImVec2 pos; - pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x; - pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y; - *last_dir = dir; - return pos; - } - - // Fallback, try to keep within display - *last_dir = ImGuiDir_None; - ImVec2 pos = ref_pos; - pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); - pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); - return pos; -} - static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled) { window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); @@ -5242,13 +4691,18 @@ static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, b window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags); } -ImGuiWindow* ImGui::FindWindowByName(const char* name) +ImGuiWindow* ImGui::FindWindowByID(ImGuiID id) { ImGuiContext& g = *GImGui; - ImGuiID id = ImHash(name, 0); return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id); } +ImGuiWindow* ImGui::FindWindowByName(const char* name) +{ + ImGuiID id = ImHashStr(name); + return FindWindowByID(id); +} + static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -5258,24 +4712,23 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl window->Flags = flags; g.WindowsById.SetVoidPtr(window->ID, window); + // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. + window->Pos = ImVec2(60, 60); + // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. if (!(flags & ImGuiWindowFlags_NoSavedSettings)) - { - // Retrieve settings from .ini file - // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. - window->Pos = window->PosFloat = ImVec2(60, 60); - if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) { + // Retrieve settings from .ini file + window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings); SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); - window->PosFloat = settings->Pos; - window->Pos = ImFloor(window->PosFloat); + window->Pos = ImFloor(settings->Pos); window->Collapsed = settings->Collapsed; if (ImLengthSqr(settings->Size) > 0.00001f) - size = settings->Size; + size = ImFloor(settings->Size); } - } - window->Size = window->SizeFull = window->SizeFullAtLastBegin = size; + window->Size = window->SizeFull = ImFloor(size); + window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) { @@ -5291,8 +4744,9 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } + g.WindowsFocusOrder.push_back(window); if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) - g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once + g.Windows.push_front(window); // Quite slow but rare and only once else g.Windows.push_back(window); return window; @@ -5301,7 +4755,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) { ImGuiContext& g = *GImGui; - if (g.NextWindowData.SizeConstraintCond != 0) + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) { // Using -1,-1 on either X/Y axis to preserve the current size. ImRect cr = g.NextWindowData.SizeConstraintRect; @@ -5317,6 +4771,8 @@ static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) g.NextWindowData.SizeCallback(&data); new_size = data.DesiredSize; } + new_size.x = ImFloor(new_size.x); + new_size.y = ImFloor(new_size.y); } // Minimum size @@ -5328,62 +4784,91 @@ static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) return new_size; } -static ImVec2 CalcSizeContents(ImGuiWindow* window) +static ImVec2 CalcContentSize(ImGuiWindow* window) { + if (window->Collapsed) + if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) + return window->ContentSize; + if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0) + return window->ContentSize; + ImVec2 sz; - sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x)); - sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y)); - return sz + window->WindowPadding; + sz.x = (float)(int)((window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x); + sz.y = (float)(int)((window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y); + return sz; } static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; - ImGuiWindowFlags flags = window->Flags; - ImVec2 size_auto_fit; - if ((flags & ImGuiWindowFlags_Tooltip) != 0) + ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight()); + ImVec2 size_pad = window->WindowPadding * 2.0f; + ImVec2 size_desired = size_contents + size_pad + size_decorations; + if (window->Flags & ImGuiWindowFlags_Tooltip) { - // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose. - size_auto_fit = size_contents; + // Tooltip always resize + return size_desired; } else { - // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding. - size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding)); + // Maximum window size is determined by the viewport size or monitor size + const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0; + const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0; + ImVec2 size_min = style.WindowMinSize; + if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups) + size_min = ImMin(size_min, ImVec2(4.0f, 4.0f)); + ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f)); + + // When the window cannot fit all contents (either because of constraints, either because screen is too small), + // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit); - if (size_auto_fit_after_constraint.x < size_contents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)) + bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - size_decorations.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); + bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - size_decorations.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); + if (will_have_scrollbar_x) size_auto_fit.y += style.ScrollbarSize; - if (size_auto_fit_after_constraint.y < size_contents.y && !(flags & ImGuiWindowFlags_NoScrollbar)) + if (will_have_scrollbar_y) size_auto_fit.x += style.ScrollbarSize; + return size_auto_fit; } - return size_auto_fit; } -static float GetScrollMaxX(ImGuiWindow* window) +ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window) { - return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x)); + ImVec2 size_contents = CalcContentSize(window); + return CalcSizeAfterConstraint(window, CalcSizeAutoFit(window, size_contents)); } -static float GetScrollMaxY(ImGuiWindow* window) -{ - return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y)); -} - -static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) +static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges) { + ImGuiContext& g = *GImGui; ImVec2 scroll = window->Scroll; - float cr_x = window->ScrollTargetCenterRatio.x; - float cr_y = window->ScrollTargetCenterRatio.y; if (window->ScrollTarget.x < FLT_MAX) - scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x); + { + float cr_x = window->ScrollTargetCenterRatio.x; + float target_x = window->ScrollTarget.x; + if (snap_on_edges && cr_x <= 0.0f && target_x <= window->WindowPadding.x) + target_x = 0.0f; + else if (snap_on_edges && cr_x >= 1.0f && target_x >= window->ContentSize.x + window->WindowPadding.x + GImGui->Style.ItemSpacing.x) + target_x = window->ContentSize.x + window->WindowPadding.x * 2.0f; + scroll.x = target_x - cr_x * window->InnerRect.GetWidth(); + } if (window->ScrollTarget.y < FLT_MAX) - scroll.y = window->ScrollTarget.y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y); + { + // 'snap_on_edges' allows for a discontinuity at the edge of scrolling limits to take account of WindowPadding so that scrolling to make the last item visible scroll far enough to see the padding. + float cr_y = window->ScrollTargetCenterRatio.y; + float target_y = window->ScrollTarget.y; + if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y) + target_y = 0.0f; + if (snap_on_edges && cr_y >= 1.0f && target_y >= window->ContentSize.y + window->WindowPadding.y + g.Style.ItemSpacing.y) + target_y = window->ContentSize.y + window->WindowPadding.y * 2.0f; + scroll.y = target_y - cr_y * window->InnerRect.GetHeight(); + } scroll = ImMax(scroll, ImVec2(0.0f, 0.0f)); if (!window->Collapsed && !window->SkipItems) { - scroll.x = ImMin(scroll.x, GetScrollMaxX(window)); - scroll.y = ImMin(scroll.y, GetScrollMaxY(window)); + scroll.x = ImMin(scroll.x, window->ScrollMax.x); + scroll.y = ImMin(scroll.y, window->ScrollMax.y); } return scroll; } @@ -5413,12 +4898,12 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co struct ImGuiResizeGripDef { - ImVec2 CornerPos; - ImVec2 InnerDir; - int AngleMin12, AngleMax12; + ImVec2 CornerPosN; + ImVec2 InnerDir; + int AngleMin12, AngleMax12; }; -const ImGuiResizeGripDef resize_grip_def[4] = +static const ImGuiResizeGripDef resize_grip_def[4] = { { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left @@ -5426,96 +4911,109 @@ const ImGuiResizeGripDef resize_grip_def[4] = { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right }; -static ImRect GetBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) +static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) { ImRect rect = window->Rect(); if (thickness == 0.0f) rect.Max -= ImVec2(1,1); - if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y, rect.Max.x - perp_padding, rect.Min.y + thickness); - if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x, rect.Max.y - perp_padding); - if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y); - if (border_n == 3) return ImRect(rect.Min.x, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); + if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); // Top + if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); // Right + if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); // Bottom + if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); // Left IM_ASSERT(0); return ImRect(); } // Handle resize for: Resize Grips, Borders, Gamepad -static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]) +// Return true when using auto-fit (double click on resize grip) +static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; - if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) - return; - const int resize_border_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 4 : 0; + if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) + return false; + if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window. + return false; + + bool ret_auto_fit = false; + const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0; const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f); - const float grip_hover_size = (float)(int)(grip_draw_size * 0.75f); + const float grip_hover_inner_size = (float)(int)(grip_draw_size * 0.75f); + const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f; ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); + // Resize grips and borders are on layer 1 + window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; + window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu); + // Manual resize grips PushID("#RESIZE"); for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); + const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window - ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size); - resize_rect.FixInverted(); + ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size); + if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); + if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); bool hovered, held; ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); + //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; - if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0) + if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0) { // Manual auto-fit when double-clicking size_target = CalcSizeAfterConstraint(window, size_auto_fit); + ret_auto_fit = true; ClearActiveID(); } else if (held) { // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip - CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target); + ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip + CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target); } if (resize_grip_n == 0 || held || hovered) resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); } for (int border_n = 0; border_n < resize_border_count; border_n++) { - const float BORDER_SIZE = 5.0f; // FIXME: Only works _inside_ window because of HoveredWindow check. - const float BORDER_APPEAR_TIMER = 0.05f; // Reduce visual noise bool hovered, held; - ImRect border_rect = GetBorderRect(window, border_n, grip_hover_size, BORDER_SIZE); + ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren); - if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held) + //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); + if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) { g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; - if (held) *border_held = border_n; + if (held) + *border_held = border_n; } if (held) { ImVec2 border_target = window->Pos; ImVec2 border_posn; - if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y); } - if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + BORDER_SIZE); } - if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + BORDER_SIZE); } - if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x); } + if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top + if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right + if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom + if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); } } PopID(); - // Navigation/gamepad resize - if (g.NavWindowingTarget == window) + // Navigation resize (keyboard/gamepad) + if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window) { ImVec2 nav_resize_delta; - if (g.NavWindowingInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift) + if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift) nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); - if (g.NavWindowingInputSource == ImGuiInputSource_NavGamepad) + if (g.NavInputSource == ImGuiInputSource_NavGamepad) nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down); if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) { @@ -5537,14 +5035,238 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } if (pos_target.x != FLT_MAX) { - window->Pos = window->PosFloat = ImFloor(pos_target); + window->Pos = ImFloor(pos_target); MarkIniSettingsDirty(window); } + // Resize nav layer + window->DC.NavLayerCurrent = ImGuiNavLayer_Main; + window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); + window->Size = window->SizeFull; + return ret_auto_fit; } -// Push a new ImGui window to add widgets to. +static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, const ImVec2& padding) +{ + ImGuiContext& g = *GImGui; + ImVec2 size_for_clamping = (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size; + window->Pos = ImMin(rect.Max - padding, ImMax(window->Pos + size_for_clamping, rect.Min + padding) - size_for_clamping); +} + +static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + float rounding = window->WindowRounding; + float border_size = window->WindowBorderSize; + if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground)) + window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); + + int border_held = window->ResizeBorderHeld; + if (border_held != -1) + { + struct ImGuiResizeBorderDef + { + ImVec2 InnerDir; + ImVec2 CornerPosN1, CornerPosN2; + float OuterAngle; + }; + static const ImGuiResizeBorderDef resize_border_def[4] = + { + { ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top + { ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right + { ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom + { ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f } // Left + }; + const ImGuiResizeBorderDef& def = resize_border_def[border_held]; + ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f); + window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI*0.25f, def.OuterAngle); + window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI*0.25f); + window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual + } + if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) + { + float y = window->Pos.y + window->TitleBarHeight() - 1; + window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize); + } +} + +void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size) +{ + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + ImGuiWindowFlags flags = window->Flags; + + // Draw window + handle manual resize + // As we highlight the title bar when want_focus is set, multiple reappearing windows will have have their title bar highlighted on their reappearing frame. + const float window_rounding = window->WindowRounding; + const float window_border_size = window->WindowBorderSize; + if (window->Collapsed) + { + // Title bar only + float backup_border_size = style.FrameBorderSize; + g.Style.FrameBorderSize = window->WindowBorderSize; + ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed); + RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding); + g.Style.FrameBorderSize = backup_border_size; + } + else + { + // Window background + if (!(flags & ImGuiWindowFlags_NoBackground)) + { + ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); + float alpha = 1.0f; + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) + alpha = g.NextWindowData.BgAlphaVal; + if (alpha != 1.0f) + bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); + window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); + } + + // Title bar + if (!(flags & ImGuiWindowFlags_NoTitleBar)) + { + ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); + window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); + } + + // Menu bar + if (flags & ImGuiWindowFlags_MenuBar) + { + ImRect menu_bar_rect = window->MenuBarRect(); + menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them. + window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top); + if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) + window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); + } + + // Scrollbars + if (window->ScrollbarX) + Scrollbar(ImGuiAxis_X); + if (window->ScrollbarY) + Scrollbar(ImGuiAxis_Y); + + // Render resize grips (after their input handling so we don't have a frame of latency) + if (!(flags & ImGuiWindowFlags_NoResize)) + { + for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) + { + const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; + const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); + window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); + window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); + } + } + + // Borders + RenderWindowOuterBorders(window); + } +} + +// Render title text, collapse button, close button +void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open) +{ + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + ImGuiWindowFlags flags = window->Flags; + + const bool has_close_button = (p_open != NULL); + const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse); + + // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) + const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; + window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; + window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; + window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu); + + // Layout buttons + // FIXME: Would be nice to generalize the subtleties expressed here into reusable code. + float pad_l = style.FramePadding.x; + float pad_r = style.FramePadding.x; + float button_sz = g.FontSize; + ImVec2 close_button_pos; + ImVec2 collapse_button_pos; + if (has_close_button) + { + pad_r += button_sz; + close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); + } + if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right) + { + pad_r += button_sz; + collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); + } + if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left) + { + collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y); + pad_l += button_sz; + } + + // Collapse button (submitting first so it gets priority when choosing a navigation init fallback) + if (has_collapse_button) + if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos)) + window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function + + // Close button + if (has_close_button) + if (CloseButton(window->GetID("#CLOSE"), close_button_pos)) + *p_open = false; + + window->DC.NavLayerCurrent = ImGuiNavLayer_Main; + window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); + window->DC.ItemFlags = item_flags_backup; + + // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) + // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code.. + const char* UNSAVED_DOCUMENT_MARKER = "*"; + const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f; + const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); + + // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button, + // while uncentered title text will still reach edges correct. + if (pad_l > style.FramePadding.x) + pad_l += g.Style.ItemInnerSpacing.x; + if (pad_r > style.FramePadding.x) + pad_r += g.Style.ItemInnerSpacing.x; + if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f) + { + float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center + float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x); + pad_l = ImMax(pad_l, pad_extend * centerness); + pad_r = ImMax(pad_r, pad_extend * centerness); + } + + ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y); + ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y); + //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] + RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); + if (flags & ImGuiWindowFlags_UnsavedDocument) + { + ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f); + ImVec2 off = ImVec2(0.0f, (float)(int)(-g.FontSize * 0.25f)); + RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r); + } +} + +void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) +{ + window->ParentWindow = parent_window; + window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; + if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) + window->RootWindow = parent_window->RootWindow; + if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) + window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; + while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) + { + IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL); + window->RootWindowForNav = window->RootWindowForNav->ParentWindow; + } +} + +// Push a new Dear ImGui window to add widgets to. // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. // - Begin/End can be called multiple times during the frame with the same window name to append content. // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file). @@ -5555,60 +5277,70 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; - IM_ASSERT(name != NULL); // Window name required - IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() + IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required + IM_ASSERT(g.FrameScopeActive); // Forgot to call ImGui::NewFrame() IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet // Find or create ImGuiWindow* window = FindWindowByName(name); - if (!window) + const bool window_just_created = (window == NULL); + if (window_just_created) { - ImVec2 size_on_first_use = (g.NextWindowData.SizeCond != 0) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here. + ImVec2 size_on_first_use = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here. window = CreateNewWindow(name, size_on_first_use, flags); } // Automatically disable manual moving/resizing when NoInputs is set - if (flags & ImGuiWindowFlags_NoInputs) + if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; - + if (flags & ImGuiWindowFlags_NavFlattened) IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); const int current_frame = g.FrameCount; const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); - if (first_begin_of_the_frame) - window->Flags = (ImGuiWindowFlags)flags; - else - flags = window->Flags; // Update the Appearing flag bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on - const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1); + const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); if (flags & ImGuiWindowFlags_Popup) { - ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size]; + ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed window_just_activated_by_user |= (window != popup_ref.Window); } window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize); - window->CloseButton = (p_open != NULL); if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); + // Update Flags, LastFrameActive, BeginOrderXXX fields + if (first_begin_of_the_frame) + { + window->Flags = (ImGuiWindowFlags)flags; + window->LastFrameActive = current_frame; + window->BeginOrderWithinParent = 0; + window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++); + } + else + { + flags = window->Flags; + } + // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); // Add to stack + // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() g.CurrentWindowStack.push_back(window); - SetCurrentWindow(window); + g.CurrentWindow = NULL; CheckStacksSize(window, true); if (flags & ImGuiWindowFlags_Popup) { - ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size]; + ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; popup_ref.Window = window; - g.CurrentPopupStack.push_back(popup_ref); + g.BeginPopupStack.push_back(popup_ref); window->PopupId = popup_ref.PopupId; } @@ -5618,7 +5350,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Process SetNextWindow***() calls bool window_pos_set_by_api = false; bool window_size_x_set_by_api = false, window_size_y_set_by_api = false; - if (g.NextWindowData.PosCond) + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) { window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0; if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f) @@ -5633,76 +5365,100 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond); } - g.NextWindowData.PosCond = 0; } - if (g.NextWindowData.SizeCond) + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) { window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f); window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f); SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond); - g.NextWindowData.SizeCond = 0; - } - if (g.NextWindowData.ContentSizeCond) - { - // Adjust passed "client size" to become a "window size" - window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal; - if (window->SizeContentsExplicit.y != 0.0f) - window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight(); - g.NextWindowData.ContentSizeCond = 0; } + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize) + window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal; else if (first_begin_of_the_frame) - { - window->SizeContentsExplicit = ImVec2(0.0f, 0.0f); - } - if (g.NextWindowData.CollapsedCond) - { + window->ContentSizeExplicit = ImVec2(0.0f, 0.0f); + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed) SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); - g.NextWindowData.CollapsedCond = 0; - } - if (g.NextWindowData.FocusCond) - { - SetWindowFocus(); - g.NextWindowData.FocusCond = 0; - } + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus) + FocusWindow(window); if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); // When reusing window again multiple times a frame, just append content (don't need to setup again) if (first_begin_of_the_frame) { - const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345) - // Initialize - window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForTabbing = window->RootWindowForNav = window; - if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !window_is_child_tooltip) - window->RootWindow = parent_window->RootWindow; - if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) - window->RootWindowForTitleBarHighlight = window->RootWindowForTabbing = parent_window->RootWindowForTitleBarHighlight; // Same value in master branch, will differ for docking - while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) - window->RootWindowForNav = window->RootWindowForNav->ParentWindow; + const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345) + UpdateWindowParentAndRootLinks(window, flags, parent_window); window->Active = true; - window->BeginOrderWithinParent = 0; - window->BeginOrderWithinContext = g.WindowsActiveCount++; - window->BeginCount = 0; + window->HasCloseButton = (p_open != NULL); window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); - window->LastFrameActive = current_frame; window->IDStack.resize(1); - // Lock window rounding, border size and rounding so that altering the border sizes for children doesn't have side-effects. - window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; - window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; + // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged). + // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere. + bool window_title_visible_elsewhere = false; + if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB + window_title_visible_elsewhere = true; + if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0) + { + size_t buf_len = (size_t)window->NameBufLen; + window->Name = ImStrdupcpy(window->Name, &buf_len, name); + window->NameBufLen = (int)buf_len; + } + + // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS + + // Update contents size from last frame for auto-fitting (or use explicit size) + window->ContentSize = CalcContentSize(window); + if (window->HiddenFramesCanSkipItems > 0) + window->HiddenFramesCanSkipItems--; + if (window->HiddenFramesCannotSkipItems > 0) + window->HiddenFramesCannotSkipItems--; + + // Hide new windows for one frame until they calculate their size + if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) + window->HiddenFramesCannotSkipItems = 1; + + // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) + // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size. + if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0) + { + window->HiddenFramesCannotSkipItems = 1; + if (flags & ImGuiWindowFlags_AlwaysAutoResize) + { + if (!window_size_x_set_by_api) + window->Size.x = window->SizeFull.x = 0.f; + if (!window_size_y_set_by_api) + window->Size.y = window->SizeFull.y = 0.f; + window->ContentSize = ImVec2(0.f, 0.f); + } + } + + // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style) + SetCurrentWindow(window); + + // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies) + + if (flags & ImGuiWindowFlags_ChildWindow) + window->WindowBorderSize = style.ChildBorderSize; + else + window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; window->WindowPadding = style.WindowPadding; if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f) window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f); + window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); + window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; // Collapse window by double-clicking on title bar // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) { + // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar. ImRect title_bar_rect = window->TitleBarRect(); - if (window->CollapseToggleWanted || (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])) + if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]) + window->WantCollapseToggle = true; + if (window->WantCollapseToggle) { window->Collapsed = !window->Collapsed; MarkIniSettingsDirty(window); @@ -5713,48 +5469,42 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { window->Collapsed = false; } - window->CollapseToggleWanted = false; + window->WantCollapseToggle = false; // SIZE - // Update contents size from last frame for auto-fitting (unless explicitly specified) - window->SizeContents = CalcSizeContents(window); - - // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) - if (window->HiddenFrames > 0) - window->HiddenFrames--; - if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && window_just_activated_by_user) - { - window->HiddenFrames = 1; - if (flags & ImGuiWindowFlags_AlwaysAutoResize) - { - if (!window_size_x_set_by_api) - window->Size.x = window->SizeFull.x = 0.f; - if (!window_size_y_set_by_api) - window->Size.y = window->SizeFull.y = 0.f; - window->SizeContents = ImVec2(0.f, 0.f); - } - } - // Calculate auto-fit size, handle automatic resize - const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents); - ImVec2 size_full_modified(FLT_MAX, FLT_MAX); - if (flags & ImGuiWindowFlags_AlwaysAutoResize && !window->Collapsed) + const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->ContentSize); + bool use_current_size_for_scrollbar_x = window_just_created; + bool use_current_size_for_scrollbar_y = window_just_created; + if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed) { // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc. if (!window_size_x_set_by_api) - window->SizeFull.x = size_full_modified.x = size_auto_fit.x; + { + window->SizeFull.x = size_auto_fit.x; + use_current_size_for_scrollbar_x = true; + } if (!window_size_y_set_by_api) - window->SizeFull.y = size_full_modified.y = size_auto_fit.y; + { + window->SizeFull.y = size_auto_fit.y; + use_current_size_for_scrollbar_y = true; + } } else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) { - // Auto-fit only grows during the first few frames + // Auto-fit may only grow window during the first few frames // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. if (!window_size_x_set_by_api && window->AutoFitFramesX > 0) - window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; + { + window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; + use_current_size_for_scrollbar_x = true; + } if (!window_size_y_set_by_api && window->AutoFitFramesY > 0) - window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; + { + window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; + use_current_size_for_scrollbar_y = true; + } if (!window->Collapsed) MarkIniSettingsDirty(window); } @@ -5763,20 +5513,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull); window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull; - // SCROLLBAR STATUS - - // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size). - if (!window->Collapsed) - { - // When reading the current size we need to read it after size constraints have been applied - float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x; - float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y; - window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar)); - window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); - if (window->ScrollbarX && !window->ScrollbarY) - window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar); - window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); - } + // Decoration size + const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); // POSITION @@ -5785,70 +5523,125 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { window->AutoPosLastDirection = ImGuiDir_None; if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api) - window->Pos = window->PosFloat = g.CurrentPopupStack.back().OpenPopupPos; + window->Pos = g.BeginPopupStack.back().OpenPopupPos; } // Position child window if (flags & ImGuiWindowFlags_ChildWindow) { - window->BeginOrderWithinParent = parent_window->DC.ChildWindows.Size; + IM_ASSERT(parent_window && parent_window->Active); + window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size; parent_window->DC.ChildWindows.push_back(window); if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip) - window->Pos = window->PosFloat = parent_window->DC.CursorPos; + window->Pos = parent_window->DC.CursorPos; } - const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFrames == 0); + const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0); if (window_pos_with_pivot) - { - // Position given a pivot (e.g. for centering) - SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); - } - else if (flags & ImGuiWindowFlags_ChildMenu) - { - // Child menus typically request _any_ position within the parent menu item, and then our FindBestPopupWindowPos() function will move the new menu outside the parent bounds. - // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. - IM_ASSERT(window_pos_set_by_api); - float horizontal_overlap = style.ItemSpacing.x; // We want some overlap to convey the relative depth of each popup (currently the amount of overlap it is hard-coded to style.ItemSpacing.x, may need to introduce another style value). - ImGuiWindow* parent_menu = parent_window_in_stack; - ImRect rect_to_avoid; - if (parent_menu->DC.MenuBarAppending) - rect_to_avoid = ImRect(-FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight(), FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight() + parent_menu->MenuBarHeight()); - else - rect_to_avoid = ImRect(parent_menu->Pos.x + horizontal_overlap, -FLT_MAX, parent_menu->Pos.x + parent_menu->Size.x - horizontal_overlap - parent_menu->ScrollbarSizes.x, FLT_MAX); - window->PosFloat = FindBestWindowPosForPopup(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid); - } + SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); // Position given a pivot (e.g. for centering) + else if ((flags & ImGuiWindowFlags_ChildMenu) != 0) + window->Pos = FindBestWindowPosForPopup(window); else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) - { - ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1); - window->PosFloat = FindBestWindowPosForPopup(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid); - } + window->Pos = FindBestWindowPosForPopup(window); + else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) + window->Pos = FindBestWindowPosForPopup(window); - // Position tooltip (always follows mouse) - if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) - { - float sc = g.Style.MouseCursorScale; - ImVec2 ref_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos; - ImRect rect_to_avoid; - if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavMoveMouse)) - rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); - else - rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. - window->PosFloat = FindBestWindowPosForPopup(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid); - if (window->AutoPosLastDirection == ImGuiDir_None) - window->PosFloat = ref_pos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. - } + // Clamp position/size so window stays visible within its viewport or monitor - // Clamp position so it stays visible - if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) + // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + ImRect viewport_rect(GetViewportRect()); + if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) { - if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + if (g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. { - ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); - window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size; - window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding); + ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); + ClampWindowRect(window, viewport_rect, clamp_padding); } } - window->Pos = ImFloor(window->PosFloat); + window->Pos = ImFloor(window->Pos); + + // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) + window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; + + // Apply window focus (new and reactivated windows are moved to front) + bool want_focus = false; + if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) + { + if (flags & ImGuiWindowFlags_Popup) + want_focus = true; + else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) + want_focus = true; + } + + // Handle manual resize: Resize Grips, Borders, Gamepad + int border_held = -1; + ImU32 resize_grip_col[4] = { 0 }; + const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // 4 + const float resize_grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f); + if (!window->Collapsed) + if (UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0])) + use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true; + window->ResizeBorderHeld = (signed char)border_held; + + // SCROLLBAR VISIBILITY + + // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size). + if (!window->Collapsed) + { + // When reading the current size we need to read it after size constraints have been applied. + // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again. + ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height); + ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes; + ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f; + float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x; + float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y; + //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons? + window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar)); + window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); + if (window->ScrollbarX && !window->ScrollbarY) + window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar); + window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); + } + + // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING) + // Update various regions. Variables they depends on should be set above in this function. + // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame. + + // Outer rectangle + // Not affected by window border size. Used by: + // - FindHoveredWindow() (w/ extra padding when border resize is enabled) + // - Begin() initial clipping rect for drawing window background and borders. + // - Begin() clipping whole child + const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect; + const ImRect outer_rect = window->Rect(); + const ImRect title_bar_rect = window->TitleBarRect(); + window->OuterRectClipped = outer_rect; + window->OuterRectClipped.ClipWith(host_rect); + + // Inner rectangle + // Not affected by window border size. Used by: + // - InnerClipRect + // - NavScrollToBringItemIntoView() + // - NavUpdatePageUpPageDown() + // - Scrollbar() + window->InnerRect.Min.x = window->Pos.x; + window->InnerRect.Min.y = window->Pos.y + decoration_up_height; + window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x; + window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y; + + // Inner clipping rectangle. + // Will extend a little bit outside the normal work region. + // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space. + // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. + // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior. + // Affected by window/frame border size. Used by: + // - Begin() initial clip rect + float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); + window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); + window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size); + window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); + window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize); + window->InnerClipRect.ClipWithFull(host_rect); // Default item width. Make it proportional to window size if window manually resizes if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) @@ -5856,48 +5649,36 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f); - // Prepare for focus requests - window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1); - window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1); - window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1; - window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX; + // SCROLLING + + // Lock down maximum scrolling + // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate + // for right/bottom aligned items without creating a scrollbar. + window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth()); + window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight()); // Apply scrolling - window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window); + window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true); window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); - // Apply focus, new windows appears in front - bool want_focus = false; - if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) - if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup)) - want_focus = true; - - // Handle manual resize: Resize Grips, Borders, Gamepad - int border_held = -1; - ImU32 resize_grip_col[4] = { 0 }; - const int resize_grip_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 2 : 1; // 4 - const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f); - if (!window->Collapsed) - UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]); - // DRAWING // Setup draw list and outer clipping rectangle window->DrawList->Clear(); - window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); - ImRect viewport_rect(GetViewportRect()); - if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) - PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true); - else - PushClipRect(viewport_rect.Min, viewport_rect.Max, true); + PushClipRect(host_rect.Min, host_rect.Max, false); - // Draw modal window background (darkens what is behind them) - if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow()) - window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); + // Draw modal window background (darkens what is behind them, all viewports) + const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0; + const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow); + if (dim_bg_for_modal || dim_bg_for_window_list) + { + const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); + window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); + } // Draw navigation selection/windowing rectangle background - if (g.NavWindowingTarget == window) + if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim) { ImRect bb = window->Rect(); bb.Expand(g.FontSize); @@ -5905,80 +5686,27 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); } - // Draw window + handle manual resize - const float window_rounding = window->WindowRounding; - const float window_border_size = window->WindowBorderSize; - const bool title_bar_is_highlight = want_focus || (g.NavWindow && window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); - const ImRect title_bar_rect = window->TitleBarRect(); - if (window->Collapsed) - { - // Title bar only - float backup_border_size = style.FrameBorderSize; - g.Style.FrameBorderSize = window->WindowBorderSize; - ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed); - RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding); - g.Style.FrameBorderSize = backup_border_size; - } - else - { - // Window background - ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); - if (g.NextWindowData.BgAlphaCond != 0) - { - bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); - g.NextWindowData.BgAlphaCond = 0; - } - window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); + // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call. + // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. + // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child. + // We also disabled this when we have dimming overlay behind this specific one child. + // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected. + bool render_decorations_in_parent = false; + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) + if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0) + render_decorations_in_parent = true; + if (render_decorations_in_parent) + window->DrawList = parent_window->DrawList; + + const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; + const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight); + RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size); - // Title bar - ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - if (!(flags & ImGuiWindowFlags_NoTitleBar)) - window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); - - // Menu bar - if (flags & ImGuiWindowFlags_MenuBar) - { - ImRect menu_bar_rect = window->MenuBarRect(); - menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them. - window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top); - if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) - window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); - } - - // Scrollbars - if (window->ScrollbarX) - Scrollbar(ImGuiLayoutType_Horizontal); - if (window->ScrollbarY) - Scrollbar(ImGuiLayoutType_Vertical); - - // Render resize grips (after their input handling so we don't have a frame of latency) - if (!(flags & ImGuiWindowFlags_NoResize)) - { - for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) - { - const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size))); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size))); - window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); - window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); - } - } - - // Borders - if (window_border_size > 0.0f) - window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size); - if (border_held != -1) - { - ImRect border = GetBorderRect(window, border_held, grip_draw_size, 0.0f); - window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size)); - } - if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar)) - window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize,-1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); - } + if (render_decorations_in_parent) + window->DrawList = &window->DrawListInst; // Draw navigation selection/windowing rectangle border - if (g.NavWindowingTarget == window) + if (g.NavWindowingTargetAnim == window) { float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); ImRect bb = window->Rect(); @@ -5991,45 +5719,60 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f); } - // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars. - window->SizeFullAtLastBegin = window->SizeFull; + // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING) - // Update ContentsRegionMax. All the variable it depends on are set above in this function. - window->ContentsRegionRect.Min.x = -window->Scroll.x + window->WindowPadding.x; - window->ContentsRegionRect.Min.y = -window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight(); - window->ContentsRegionRect.Max.x = -window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x)); - window->ContentsRegionRect.Max.y = -window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y)); + // Work rectangle. + // Affected by window padding and border size. Used by: + // - Columns() for right-most edge + // - TreeNode(), CollapsingHeader() for right-most edge + // - BeginTabBar() for right-most edge + const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar); + const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar); + const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x)); + const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y)); + window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize)); + window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize)); + window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x; + window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y; + + // [LEGACY] Contents Region + // FIXME-OBSOLETE: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. + // Used by: + // - Mouse wheel scrolling + many other things + window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x; + window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height; + window->ContentsRegionRect.Max.x = window->ContentsRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x)); + window->ContentsRegionRect.Max.y = window->ContentsRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y)); // Setup drawing context // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.) - window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x; - window->DC.GroupOffsetX = 0.0f; - window->DC.ColumnsOffsetX = 0.0f; - window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y); + window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x; + window->DC.GroupOffset.x = 0.0f; + window->DC.ColumnsOffset.x = 0.0f; + window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y); window->DC.CursorPos = window->DC.CursorStartPos; window->DC.CursorPosPrevLine = window->DC.CursorPos; window->DC.CursorMaxPos = window->DC.CursorStartPos; - window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f; - window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; + window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f); + window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; window->DC.NavHideHighlightOneFrame = false; - window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f); + window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; window->DC.NavLayerActiveMaskNext = 0x00; window->DC.MenuBarAppending = false; - window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x); - window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; window->DC.ChildWindows.resize(0); window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; - window->DC.ItemFlags = ImGuiItemFlags_Default_; + window->DC.FocusCounterAll = window->DC.FocusCounterTab = -1; + window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled window->DC.ItemFlagsStack.resize(0); window->DC.ItemWidthStack.resize(0); window->DC.TextWrapPosStack.resize(0); - window->DC.ColumnsSet = NULL; + window->DC.CurrentColumns = NULL; window->DC.TreeDepth = 0; - window->DC.TreeDepthMayJumpToParentOnPop = 0x00; + window->DC.TreeStoreMayJumpToParentOnPop = 0x00; window->DC.StateStorage = &window->StateStorage; window->DC.GroupStack.resize(0); window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); @@ -6054,54 +5797,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Title bar if (!(flags & ImGuiWindowFlags_NoTitleBar)) - { - // Close & collapse button are on layer 1 (same as menus) and don't default focus - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; - window->DC.NavLayerCurrent++; - window->DC.NavLayerCurrentMask <<= 1; - - // Collapse button - if (!(flags & ImGuiWindowFlags_NoCollapse)) - { - ImGuiID id = window->GetID("#COLLAPSE"); - ImRect bb(window->Pos + style.FramePadding + ImVec2(1,1), window->Pos + style.FramePadding + ImVec2(g.FontSize,g.FontSize) - ImVec2(1,1)); - ItemAdd(bb, id); // To allow navigation - if (ButtonBehavior(bb, id, NULL, NULL)) - window->CollapseToggleWanted = true; // Defer collapsing to next frame as we are too far in the Begin() function - RenderNavHighlight(bb, id); - RenderArrow(window->Pos + style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); - } - - // Close button - if (p_open != NULL) - { - const float PAD = 2.0f; - const float rad = (window->TitleBarHeight() - PAD*2.0f) * 0.5f; - if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-PAD - rad, PAD + rad), rad)) - *p_open = false; - } - - window->DC.NavLayerCurrent--; - window->DC.NavLayerCurrentMask >>= 1; - window->DC.ItemFlags = item_flags_backup; - - // Title text (FIXME: refactor text alignment facilities along with RenderText helpers) - ImVec2 text_size = CalcTextSize(name, NULL, true); - ImRect text_r = title_bar_rect; - float pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0 ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x; - float pad_right = (p_open != NULL) ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x; - if (style.WindowTitleAlign.x > 0.0f) pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x); - text_r.Min.x += pad_left; - text_r.Max.x -= pad_right; - ImRect clip_rect = text_r; - clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton() - RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect); - } - - // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() - window->WindowRectClipped = window->Rect(); - window->WindowRectClipped.ClipWith(window->ClipRect); + RenderWindowTitleBarContents(window, title_bar_rect, name, p_open); // Pressing CTRL+C while holding on a window copy its content to the clipboard // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope. @@ -6109,29 +5805,23 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) /* if (g.ActiveId == move_id) if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) - ImGui::LogToClipboard(); + LogToClipboard(); */ - // Inner rectangle - // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame - // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior. - window->InnerRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize; - window->InnerRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); - window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize; - window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize; - //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE); - - // Inner clipping rectangle - // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. - window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize))); - window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y); - window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize))); - window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y); - - // After Begin() we fill the last item / hovered data using the title bar data. Make that a standard behavior (to allow usage of context menus on title bar only, etc.). + // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). + // This is useful to allow creating context menus on title bar only, etc. window->DC.LastItemId = window->MoveId; window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; window->DC.LastItemRect = title_bar_rect; +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) + IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId); +#endif + } + else + { + // Append + SetCurrentWindow(window); } PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); @@ -6141,29 +5831,39 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->WriteAccessed = false; window->BeginCount++; - g.NextWindowData.SizeConstraintCond = 0; + g.NextWindowData.ClearFlags(); - // Child window can be out of sight and have "negative" clip windows. - // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar). if (flags & ImGuiWindowFlags_ChildWindow) { + // Child window can be out of sight and have "negative" clip windows. + // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); - window->Collapsed = parent_window && parent_window->Collapsed; - if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) - window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y); + if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) + window->HiddenFramesCanSkipItems = 1; - // We also hide the window from rendering because we've already added its border to the command list. - // (we could perform the check earlier in the function but it is simpler at this point) - if (window->Collapsed) - window->Active = false; + // Hide along with parent or if parent is collapsed + if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) + window->HiddenFramesCanSkipItems = 1; + if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0)) + window->HiddenFramesCannotSkipItems = 1; } - if (style.Alpha <= 0.0f) - window->Active = false; - // Return false if we don't intend to display anything to allow user to perform an early out optimization - window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0; - return !window->SkipItems; + // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point) + if (style.Alpha <= 0.0f) + window->HiddenFramesCanSkipItems = 1; + + // Update the Hidden flag + window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); + + // Update the SkipItems flag, used to early out of all items functions (no layout required) + bool skip_items = false; + if (window->Collapsed || !window->Active || window->Hidden) + if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) + skip_items = true; + window->SkipItems = skip_items; + + return !skip_items; } // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead. @@ -6172,22 +5872,30 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, { // Old API feature: we could pass the initial window size as a parameter. This was misleading because it only had an effect if the window didn't have data in the .ini file. if (size_first_use.x != 0.0f || size_first_use.y != 0.0f) - ImGui::SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver); + SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver); // Old API feature: override the window background alpha with a parameter. if (bg_alpha_override >= 0.0f) - ImGui::SetNextWindowBgAlpha(bg_alpha_override); + SetNextWindowBgAlpha(bg_alpha_override); - return ImGui::Begin(name, p_open, flags); + return Begin(name, p_open, flags); } #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS void ImGui::End() { ImGuiContext& g = *GImGui; + + if (g.CurrentWindowStack.Size <= 1 && g.FrameScopePushedImplicitWindow) + { + IM_ASSERT(g.CurrentWindowStack.Size > 1 && "Calling End() too many times!"); + return; // FIXME-ERRORHANDLING + } + IM_ASSERT(g.CurrentWindowStack.Size > 0); + ImGuiWindow* window = g.CurrentWindow; - if (window->DC.ColumnsSet != NULL) + if (window->DC.CurrentColumns) EndColumns(); PopClipRect(); // Inner window clip rectangle @@ -6198,137 +5906,41 @@ void ImGui::End() // Pop from window stack g.CurrentWindowStack.pop_back(); if (window->Flags & ImGuiWindowFlags_Popup) - g.CurrentPopupStack.pop_back(); + g.BeginPopupStack.pop_back(); CheckStacksSize(window, false); SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); } -// Vertical scrollbar -// The entire piece of code below is rather confusing because: -// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) -// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar -// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. -void ImGui::Scrollbar(ImGuiLayoutType direction) +void ImGui::BringWindowToFocusFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - const bool horizontal = (direction == ImGuiLayoutType_Horizontal); - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY"); - - // Render background - bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX); - float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f; - const ImRect window_rect = window->Rect(); - const float border_size = window->WindowBorderSize; - ImRect bb = horizontal - ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size) - : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size); - if (!horizontal) - bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f); - if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f) + if (g.WindowsFocusOrder.back() == window) return; - - int window_rounding_corners; - if (horizontal) - window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight); - else - window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight); - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners); - bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f))); - - // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) - float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight(); - float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y; - float win_size_avail_v = (horizontal ? window->SizeFull.x : window->SizeFull.y) - other_scrollbar_size_w; - float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y; - - // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) - // But we maintain a minimum size in pixel to allow for the user to still aim inside. - IM_ASSERT(ImMax(win_size_contents_v, win_size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. - const float win_size_v = ImMax(ImMax(win_size_contents_v, win_size_avail_v), 1.0f); - const float grab_h_pixels = ImClamp(scrollbar_size_v * (win_size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v); - const float grab_h_norm = grab_h_pixels / scrollbar_size_v; - - // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). - bool held = false; - bool hovered = false; - const bool previously_held = (g.ActiveId == id); - ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); - - float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v); - float scroll_ratio = ImSaturate(scroll_v / scroll_max); - float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; - if (held && grab_h_norm < 1.0f) - { - float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y; - float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; - float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y; - - // Click position in scrollbar normalized space (0.0f->1.0f) - const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); - SetHoveredID(id); - - bool seek_absolute = false; - if (!previously_held) + for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window + if (g.WindowsFocusOrder[i] == window) { - // On initial click calculate the distance between mouse and the center of the grab - if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm) - { - *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; - } - else - { - seek_absolute = true; - *click_delta_to_grab_center_v = 0.0f; - } + memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*)); + g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window; + break; } - - // Apply scroll - // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position - const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm)); - scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); - if (horizontal) - window->Scroll.x = scroll_v; - else - window->Scroll.y = scroll_v; - - // Update values for rendering - scroll_ratio = ImSaturate(scroll_v / scroll_max); - grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; - - // Update distance to grab now that we have seeked and saturated - if (seek_absolute) - *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; - } - - // Render - const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); - ImRect grab_rect; - if (horizontal) - grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y); - else - grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, window_rect.Max.y)); - window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding); } -void ImGui::BringWindowToFront(ImGuiWindow* window) +void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindow* current_front_window = g.Windows.back(); if (current_front_window == window || current_front_window->RootWindow == window) return; - for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window + for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window if (g.Windows[i] == window) { - g.Windows.erase(g.Windows.Data + i); - g.Windows.push_back(window); + memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*)); + g.Windows[g.Windows.Size - 1] = window; break; } } -void ImGui::BringWindowToBack(ImGuiWindow* window) +void ImGui::BringWindowToDisplayBack(ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (g.Windows[0] == window) @@ -6355,9 +5967,13 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavInitRequest = false; g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId g.NavIdIsAlive = false; - g.NavLayer = 0; + g.NavLayer = ImGuiNavLayer_Main; + //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); } + // Close popups if any + ClosePopupsOverWindow(window, false); + // Passing NULL allow to disable keyboard focus if (!window) return; @@ -6372,41 +5988,65 @@ void ImGui::FocusWindow(ImGuiWindow* window) ClearActiveID(); // Bring to front + BringWindowToFocusFront(window); if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) - BringWindowToFront(window); + BringWindowToDisplayFront(window); } -void ImGui::FocusFrontMostActiveWindow(ImGuiWindow* ignore_window) +void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) { ImGuiContext& g = *GImGui; - for (int i = g.Windows.Size - 1; i >= 0; i--) - if (g.Windows[i] != ignore_window && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) - { - ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(g.Windows[i]); - FocusWindow(focus_window); - return; - } + + int start_idx = g.WindowsFocusOrder.Size - 1; + if (under_this_window != NULL) + { + int under_this_window_idx = FindWindowFocusIndex(under_this_window); + if (under_this_window_idx != -1) + start_idx = under_this_window_idx - 1; + } + for (int i = start_idx; i >= 0; i--) + { + // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. + ImGuiWindow* window = g.WindowsFocusOrder[i]; + if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) + if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) + { + ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); + FocusWindow(focus_window); + return; + } + } + FocusWindow(NULL); +} + +void ImGui::SetNextItemWidth(float item_width) +{ + ImGuiContext& g = *GImGui; + g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth; + g.NextItemData.Width = item_width; } void ImGui::PushItemWidth(float item_width) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); + g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; } void ImGui::PushMultiItemsWidths(int components, float w_full) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; const ImGuiStyle& style = GImGui->Style; - if (w_full <= 0.0f) - w_full = CalcItemWidth(); const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); window->DC.ItemWidthStack.push_back(w_item_last); for (int i = 0; i < components-1; i++) window->DC.ItemWidthStack.push_back(w_item_one); window->DC.ItemWidth = window->DC.ItemWidthStack.back(); + g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; } void ImGui::PopItemWidth() @@ -6416,24 +6056,49 @@ void ImGui::PopItemWidth() window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back(); } +// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth(). +// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags() float ImGui::CalcItemWidth() { - ImGuiWindow* window = GetCurrentWindowRead(); - float w = window->DC.ItemWidth; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + float w; + if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth) + w = g.NextItemData.Width; + else + w = window->DC.ItemWidth; if (w < 0.0f) { - // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well. - float width_to_right_edge = GetContentRegionAvail().x; - w = ImMax(1.0f, width_to_right_edge + w); + float region_max_x = GetContentRegionMaxAbs().x; + w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w); } w = (float)(int)w; return w; } -static ImFont* GetDefaultFont() +// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth(). +// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical. +// Note that only CalcItemWidth() is publicly exposed. +// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable) +ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h) { - ImGuiContext& g = *GImGui; - return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; + ImGuiWindow* window = GImGui->CurrentWindow; + + ImVec2 region_max; + if (size.x < 0.0f || size.y < 0.0f) + region_max = GetContentRegionMaxAbs(); + + if (size.x == 0.0f) + size.x = default_w; + else if (size.x < 0.0f) + size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x); + + if (size.y == 0.0f) + size.y = default_h; + else if (size.y < 0.0f) + size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y); + + return size; } void ImGui::SetCurrentFont(ImFont* font) @@ -6442,7 +6107,7 @@ void ImGui::SetCurrentFont(ImFont* font) IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? IM_ASSERT(font->Scale > 0.0f); g.Font = font; - g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale; + g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale); g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; ImFontAtlas* atlas = g.Font->ContainerAtlas; @@ -6486,9 +6151,10 @@ void ImGui::PopItemFlag() window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back(); } +// FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) { - PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus, allow_keyboard_focus); + PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus); } void ImGui::PopAllowKeyboardFocus() @@ -6524,7 +6190,7 @@ void ImGui::PopTextWrapPos() void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col) { ImGuiContext& g = *GImGui; - ImGuiColMod backup; + ImGuiColorMod backup; backup.Col = idx; backup.BackupValue = g.Style.Colors[idx]; g.ColorModifiers.push_back(backup); @@ -6534,7 +6200,7 @@ void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col) void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) { ImGuiContext& g = *GImGui; - ImGuiColMod backup; + ImGuiColorMod backup; backup.Col = idx; backup.BackupValue = g.Style.Colors[idx]; g.ColorModifiers.push_back(backup); @@ -6546,7 +6212,7 @@ void ImGui::PopStyleColor(int count) ImGuiContext& g = *GImGui; while (count > 0) { - ImGuiColMod& backup = g.ColorModifiers.back(); + ImGuiColorMod& backup = g.ColorModifiers.back(); g.Style.Colors[backup.Col] = backup.BackupValue; g.ColorModifiers.pop_back(); count--; @@ -6556,33 +6222,36 @@ void ImGui::PopStyleColor(int count) struct ImGuiStyleVarInfo { ImGuiDataType Type; + ImU32 Count; ImU32 Offset; void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); } }; static const ImGuiStyleVarInfo GStyleVarInfo[] = { - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha - { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize - { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize - { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize - { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize - { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing - { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize - { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding - { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign }; static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx) @@ -6595,7 +6264,7 @@ static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx) void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) { const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); - if (var_info->Type == ImGuiDataType_Float) + if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { ImGuiContext& g = *GImGui; float* pvar = (float*)var_info->GetVarPtr(&g.Style); @@ -6603,13 +6272,13 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) *pvar = val; return; } - IM_ASSERT(0); // Called function with wrong-type? Variable is not a float. + IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!"); } void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) { const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); - if (var_info->Type == ImGuiDataType_Float2) + if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) { ImGuiContext& g = *GImGui; ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); @@ -6617,7 +6286,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) *pvar = val; return; } - IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2. + IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!"); } void ImGui::PopStyleVar(int count) @@ -6625,11 +6294,12 @@ void ImGui::PopStyleVar(int count) ImGuiContext& g = *GImGui; while (count > 0) { + // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it. ImGuiStyleMod& backup = g.StyleModifiers.back(); const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx); - if (info->Type == ImGuiDataType_Float) (*(float*)info->GetVarPtr(&g.Style)) = backup.BackupFloat[0]; - else if (info->Type == ImGuiDataType_Float2) (*(ImVec2*)info->GetVarPtr(&g.Style)) = ImVec2(backup.BackupFloat[0], backup.BackupFloat[1]); - else if (info->Type == ImGuiDataType_Int) (*(int*)info->GetVarPtr(&g.Style)) = backup.BackupInt[0]; + void* data = info->GetVarPtr(&g.Style); + if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; } + else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; } g.StyleModifiers.pop_back(); count--; } @@ -6673,18 +6343,21 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_ResizeGrip: return "ResizeGrip"; case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; - case ImGuiCol_CloseButton: return "CloseButton"; - case ImGuiCol_CloseButtonHovered: return "CloseButtonHovered"; - case ImGuiCol_CloseButtonActive: return "CloseButtonActive"; + case ImGuiCol_Tab: return "Tab"; + case ImGuiCol_TabHovered: return "TabHovered"; + case ImGuiCol_TabActive: return "TabActive"; + case ImGuiCol_TabUnfocused: return "TabUnfocused"; + case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive"; case ImGuiCol_PlotLines: return "PlotLines"; case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; case ImGuiCol_PlotHistogram: return "PlotHistogram"; case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; - case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening"; case ImGuiCol_DragDropTarget: return "DragDropTarget"; case ImGuiCol_NavHighlight: return "NavHighlight"; - case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; + case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; + case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg"; + case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg"; } IM_ASSERT(0); return "Unknown"; @@ -6736,7 +6409,7 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) } } - if (!IsWindowContentHoverable(g.HoveredRootWindow, flags)) + if (!IsWindowContentHoverable(g.HoveredWindow, flags)) return false; if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId) @@ -6747,11 +6420,11 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) { ImGuiContext& g = *GImGui; - IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() if (flags & ImGuiFocusedFlags_AnyWindow) return g.NavWindow != NULL; + IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) { case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: @@ -6766,10 +6439,11 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) } // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) +// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly. +// If you want a window to never be focused, you may use the e.g. NoInputs flag. bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) { - ImGuiContext& g = *GImGui; - return window->Active && window == window->RootWindowForTabbing && (!(window->Flags & ImGuiWindowFlags_NoNavFocus) || window == g.NavWindow); + return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus); } float ImGui::GetWindowWidth() @@ -6791,34 +6465,33 @@ ImVec2 ImGui::GetWindowPos() return window->Pos; } -static void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x) +void ImGui::SetWindowScrollX(ImGuiWindow* window, float new_scroll_x) { - window->DC.CursorMaxPos.x += window->Scroll.x; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it. window->Scroll.x = new_scroll_x; - window->DC.CursorMaxPos.x -= window->Scroll.x; } -static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y) +void ImGui::SetWindowScrollY(ImGuiWindow* window, float new_scroll_y) { - window->DC.CursorMaxPos.y += window->Scroll.y; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it. window->Scroll.y = new_scroll_y; - window->DC.CursorMaxPos.y -= window->Scroll.y; } -static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) +void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) { // Test condition (NB: bit 0 is always true) and clear flags for next time if (cond && (window->SetWindowPosAllowFlags & cond) == 0) return; + + IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX); // Set const ImVec2 old_pos = window->Pos; - window->PosFloat = pos; window->Pos = ImFloor(pos); - window->DC.CursorPos += (window->Pos - old_pos); // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor - window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected. + ImVec2 offset = window->Pos - old_pos; + window->DC.CursorPos += offset; // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor + window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected. + window->DC.CursorStartPos += offset; } void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond) @@ -6839,18 +6512,20 @@ ImVec2 ImGui::GetWindowSize() return window->Size; } -static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond) +void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond) { // Test condition (NB: bit 0 is always true) and clear flags for next time if (cond && (window->SetWindowSizeAllowFlags & cond) == 0) return; + + IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); // Set if (size.x > 0.0f) { window->AutoFitFramesX = 0; - window->SizeFull.x = size.x; + window->SizeFull.x = ImFloor(size.x); } else { @@ -6860,7 +6535,7 @@ static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond con if (size.y > 0.0f) { window->AutoFitFramesY = 0; - window->SizeFull.y = size.y; + window->SizeFull.y = ImFloor(size.y); } else { @@ -6880,7 +6555,7 @@ void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond) SetWindowSize(window, size, cond); } -static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond) +void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond) { // Test condition (NB: bit 0 is always true) and clear flags for next time if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0) @@ -6935,6 +6610,8 @@ void ImGui::SetWindowFocus(const char* name) void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot) { ImGuiContext& g = *GImGui; + IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos; g.NextWindowData.PosVal = pos; g.NextWindowData.PosPivotVal = pivot; g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always; @@ -6943,6 +6620,8 @@ void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pi void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond) { ImGuiContext& g = *GImGui; + IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize; g.NextWindowData.SizeVal = size; g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always; } @@ -6950,22 +6629,26 @@ void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond) void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data) { ImGuiContext& g = *GImGui; - g.NextWindowData.SizeConstraintCond = ImGuiCond_Always; + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint; g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max); g.NextWindowData.SizeCallback = custom_callback; g.NextWindowData.SizeCallbackUserData = custom_callback_user_data; } +// Content size = inner scrollable rectangle, padded with WindowPadding. +// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item. void ImGui::SetNextWindowContentSize(const ImVec2& size) { ImGuiContext& g = *GImGui; - g.NextWindowData.ContentSizeVal = size; // In Begin() we will add the size of window decorations (title bar, menu etc.) to that to form a SizeContents value. - g.NextWindowData.ContentSizeCond = ImGuiCond_Always; + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize; + g.NextWindowData.ContentSizeVal = size; } void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond) { ImGuiContext& g = *GImGui; + IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed; g.NextWindowData.CollapsedVal = collapsed; g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always; } @@ -6973,54 +6656,59 @@ void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond) void ImGui::SetNextWindowFocus() { ImGuiContext& g = *GImGui; - g.NextWindowData.FocusCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op) + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus; } void ImGui::SetNextWindowBgAlpha(float alpha) { ImGuiContext& g = *GImGui; + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha; g.NextWindowData.BgAlphaVal = alpha; - g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op) } -// In window space (not screen space!) +// FIXME: This is in window space (not screen space!). We should try to obsolete all those functions. ImVec2 ImGui::GetContentRegionMax() { - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; + ImVec2 mx = window->ContentsRegionRect.Max - window->Pos; + if (window->DC.CurrentColumns) + mx.x = window->WorkRect.Max.x - window->Pos.x; + return mx; +} + +// [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features. +ImVec2 ImGui::GetContentRegionMaxAbs() +{ + ImGuiWindow* window = GImGui->CurrentWindow; ImVec2 mx = window->ContentsRegionRect.Max; - if (window->DC.ColumnsSet) - mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x; + if (window->DC.CurrentColumns) + mx.x = window->WorkRect.Max.x; return mx; } ImVec2 ImGui::GetContentRegionAvail() { - ImGuiWindow* window = GetCurrentWindowRead(); - return GetContentRegionMax() - (window->DC.CursorPos - window->Pos); -} - -float ImGui::GetContentRegionAvailWidth() -{ - return GetContentRegionAvail().x; + ImGuiWindow* window = GImGui->CurrentWindow; + return GetContentRegionMaxAbs() - window->DC.CursorPos; } // In window space (not screen space!) ImVec2 ImGui::GetWindowContentRegionMin() { - ImGuiWindow* window = GetCurrentWindowRead(); - return window->ContentsRegionRect.Min; + ImGuiWindow* window = GImGui->CurrentWindow; + return window->ContentsRegionRect.Min - window->Pos; } ImVec2 ImGui::GetWindowContentRegionMax() { - ImGuiWindow* window = GetCurrentWindowRead(); - return window->ContentsRegionRect.Max; + ImGuiWindow* window = GImGui->CurrentWindow; + return window->ContentsRegionRect.Max - window->Pos; } float ImGui::GetWindowContentRegionWidth() { - ImGuiWindow* window = GetCurrentWindowRead(); - return window->ContentsRegionRect.Max.x - window->ContentsRegionRect.Min.x; + ImGuiWindow* window = GImGui->CurrentWindow; + return window->ContentsRegionRect.GetWidth(); } float ImGui::GetTextLineHeight() @@ -7129,31 +6817,35 @@ ImVec2 ImGui::GetCursorScreenPos() return window->DC.CursorPos; } -void ImGui::SetCursorScreenPos(const ImVec2& screen_pos) +void ImGui::SetCursorScreenPos(const ImVec2& pos) { ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos = screen_pos; + window->DC.CursorPos = pos; window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); } float ImGui::GetScrollX() { - return GImGui->CurrentWindow->Scroll.x; + ImGuiWindow* window = GImGui->CurrentWindow; + return window->Scroll.x; } float ImGui::GetScrollY() { - return GImGui->CurrentWindow->Scroll.y; + ImGuiWindow* window = GImGui->CurrentWindow; + return window->Scroll.y; } float ImGui::GetScrollMaxX() { - return GetScrollMaxX(GImGui->CurrentWindow); + ImGuiWindow* window = GImGui->CurrentWindow; + return window->ScrollMax.x; } float ImGui::GetScrollMaxY() { - return GetScrollMaxY(GImGui->CurrentWindow); + ImGuiWindow* window = GImGui->CurrentWindow; + return window->ScrollMax.y; } void ImGui::SetScrollX(float scroll_x) @@ -7166,31 +6858,44 @@ void ImGui::SetScrollX(float scroll_x) void ImGui::SetScrollY(float scroll_y) { ImGuiWindow* window = GetCurrentWindow(); - window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY + window->ScrollTarget.y = scroll_y; window->ScrollTargetCenterRatio.y = 0.0f; } -void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio) +void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio) +{ + // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size + ImGuiWindow* window = GetCurrentWindow(); + IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f); + window->ScrollTarget.x = (float)(int)(local_x + window->Scroll.x); + window->ScrollTargetCenterRatio.x = center_x_ratio; +} + +void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio) { // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size ImGuiWindow* window = GetCurrentWindow(); IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); - window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y); + window->ScrollTarget.y = (float)(int)(local_y + window->Scroll.y); window->ScrollTargetCenterRatio.y = center_y_ratio; +} - // Minor hack to to make scrolling to top/bottom of window take account of WindowPadding, it looks more right to the user this way - if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y) - window->ScrollTarget.y = 0.0f; - else if (center_y_ratio >= 1.0f && window->ScrollTarget.y >= window->SizeContents.y - window->WindowPadding.y + GImGui->Style.ItemSpacing.y) - window->ScrollTarget.y = window->SizeContents.y; +// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item. +void ImGui::SetScrollHereX(float center_x_ratio) +{ + ImGuiWindow* window = GetCurrentWindow(); + float target_x = window->DC.LastItemRect.Min.x - window->Pos.x; // Left of last item, in window space + float last_item_width = window->DC.LastItemRect.GetWidth(); + target_x += (last_item_width * center_x_ratio) + (GImGui->Style.ItemSpacing.x * (center_x_ratio - 0.5f) * 2.0f); // Precisely aim before, in the middle or after the last item. + SetScrollFromPosX(target_x, center_x_ratio); } // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. -void ImGui::SetScrollHere(float center_y_ratio) +void ImGui::SetScrollHereY(float center_y_ratio) { ImGuiWindow* window = GetCurrentWindow(); float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space - target_y += (window->DC.PrevLineHeight * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line. + target_y += (window->DC.PrevLineSize.y * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line. SetScrollFromPosY(target_y, center_y_ratio); } @@ -7203,9 +6908,11 @@ void ImGui::ActivateItem(ImGuiID id) void ImGui::SetKeyboardFocusHere(int offset) { IM_ASSERT(offset >= -1); // -1 is allowed but not below - ImGuiWindow* window = GetCurrentWindow(); - window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset; - window->FocusIdxTabRequestNext = INT_MAX; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + g.FocusRequestNextWindow = window; + g.FocusRequestNextCounterAll = window->DC.FocusCounterAll + 1 + offset; + g.FocusRequestNextCounterTab = INT_MAX; } void ImGui::SetItemDefaultFocus() @@ -7221,5066 +6928,185 @@ void ImGui::SetItemDefaultFocus() g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); NavUpdateAnyRequestFlag(); if (!IsItemVisible()) - SetScrollHere(); + SetScrollHereY(); } } void ImGui::SetStateStorage(ImGuiStorage* tree) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiWindow* window = GImGui->CurrentWindow; window->DC.StateStorage = tree ? tree : &window->StateStorage; } ImGuiStorage* ImGui::GetStateStorage() { - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; return window->DC.StateStorage; } -void ImGui::TextV(const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - TextUnformatted(g.TempBuffer, text_end); -} - -void ImGui::Text(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextV(fmt, args); - va_end(args); -} - -void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args) -{ - PushStyleColor(ImGuiCol_Text, col); - TextV(fmt, args); - PopStyleColor(); -} - -void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextColoredV(col, fmt, args); - va_end(args); -} - -void ImGui::TextDisabledV(const char* fmt, va_list args) -{ - PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]); - TextV(fmt, args); - PopStyleColor(); -} - -void ImGui::TextDisabled(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextDisabledV(fmt, args); - va_end(args); -} - -void ImGui::TextWrappedV(const char* fmt, va_list args) -{ - bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position is one ia already set - if (need_wrap) PushTextWrapPos(0.0f); - TextV(fmt, args); - if (need_wrap) PopTextWrapPos(); -} - -void ImGui::TextWrapped(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextWrappedV(fmt, args); - va_end(args); -} - -void ImGui::TextUnformatted(const char* text, const char* text_end) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - IM_ASSERT(text != NULL); - const char* text_begin = text; - if (text_end == NULL) - text_end = text + strlen(text); // FIXME-OPT - - const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset); - const float wrap_pos_x = window->DC.TextWrapPos; - const bool wrap_enabled = wrap_pos_x >= 0.0f; - if (text_end - text > 2000 && !wrap_enabled) - { - // Long text! - // Perform manual coarse clipping to optimize for long multi-line text - // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. - // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. - const char* line = text; - const float line_height = GetTextLineHeight(); - const ImRect clip_rect = window->ClipRect; - ImVec2 text_size(0,0); - - if (text_pos.y <= clip_rect.Max.y) - { - ImVec2 pos = text_pos; - - // Lines to skip (can't skip when logging text) - if (!g.LogEnabled) - { - int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height); - if (lines_skippable > 0) - { - int lines_skipped = 0; - while (line < text_end && lines_skipped < lines_skippable) - { - const char* line_end = strchr(line, '\n'); - if (!line_end) - line_end = text_end; - line = line_end + 1; - lines_skipped++; - } - pos.y += lines_skipped * line_height; - } - } - - // Lines to render - if (line < text_end) - { - ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); - while (line < text_end) - { - const char* line_end = strchr(line, '\n'); - if (IsClippedEx(line_rect, 0, false)) - break; - - const ImVec2 line_size = CalcTextSize(line, line_end, false); - text_size.x = ImMax(text_size.x, line_size.x); - RenderText(pos, line, line_end, false); - if (!line_end) - line_end = text_end; - line = line_end + 1; - line_rect.Min.y += line_height; - line_rect.Max.y += line_height; - pos.y += line_height; - } - - // Count remaining lines - int lines_skipped = 0; - while (line < text_end) - { - const char* line_end = strchr(line, '\n'); - if (!line_end) - line_end = text_end; - line = line_end + 1; - lines_skipped++; - } - pos.y += lines_skipped * line_height; - } - - text_size.y += (pos - text_pos).y; - } - - ImRect bb(text_pos, text_pos + text_size); - ItemSize(bb); - ItemAdd(bb, 0); - } - else - { - const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; - const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); - - // Account of baseline offset - ImRect bb(text_pos, text_pos + text_size); - ItemSize(text_size); - if (!ItemAdd(bb, 0)) - return; - - // Render (we don't hide text after ## in this end-user function) - RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); - } -} - -void ImGui::AlignTextToFramePadding() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - window->DC.CurrentLineHeight = ImMax(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y * 2); - window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); -} - -// Add a label+text combo aligned to other label+value widgets -void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float w = CalcItemWidth(); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2)); - const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, 0)) - return; - - // Render - const char* value_text_begin = &g.TempBuffer[0]; - const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f)); - if (label_size.x > 0.0f) - RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); -} - -void ImGui::LabelText(const char* label, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - LabelTextV(label, fmt, args); - va_end(args); -} - -bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - - if (flags & ImGuiButtonFlags_Disabled) - { - if (out_hovered) *out_hovered = false; - if (out_held) *out_held = false; - if (g.ActiveId == id) ClearActiveID(); - return false; - } - - // Default behavior requires click+release on same spot - if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0) - flags |= ImGuiButtonFlags_PressedOnClickRelease; - - ImGuiWindow* backup_hovered_window = g.HoveredWindow; - if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) - g.HoveredWindow = window; - - bool pressed = false; - bool hovered = ItemHoverable(bb, id); - - // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button - if ((flags & ImGuiButtonFlags_PressedOnDragDropHold) && g.DragDropActive && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) - if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - { - hovered = true; - SetHoveredID(id); - if (CalcTypematicPressedRepeatAmount(g.HoveredIdTimer + 0.0001f, g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, 0.01f, 0.70f)) // FIXME: Our formula for CalcTypematicPressedRepeatAmount() is fishy - { - pressed = true; - FocusWindow(window); - } - } - - if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) - g.HoveredWindow = backup_hovered_window; - - // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. - if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) - hovered = false; - - // Mouse - if (hovered) - { - if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) - { - // | CLICKING | HOLDING with ImGuiButtonFlags_Repeat - // PressedOnClickRelease | * | .. (NOT on release) <-- MOST COMMON! (*) only if both click/release were over bounds - // PressedOnClick | | .. - // PressedOnRelease | | .. (NOT on release) - // PressedOnDoubleClick | | .. - // FIXME-NAV: We don't honor those different behaviors. - if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) - { - SetActiveID(id, window); - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - SetFocusID(id, window); - FocusWindow(window); - } - if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0])) - { - pressed = true; - if (flags & ImGuiButtonFlags_NoHoldingActiveID) - ClearActiveID(); - else - SetActiveID(id, window); // Hold on ID - FocusWindow(window); - } - if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0]) - { - if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps - pressed = true; - ClearActiveID(); - } - - // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). - // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. - if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true)) - pressed = true; - } - - if (pressed) - g.NavDisableHighlight = true; - } - - // Gamepad/Keyboard navigation - // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse. - if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId)) - hovered = true; - - if (g.NavActivateDownId == id) - { - bool nav_activated_by_code = (g.NavActivateId == id); - bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed); - if (nav_activated_by_code || nav_activated_by_inputs) - pressed = true; - if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id) - { - // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. - g.NavActivateId = id; // This is so SetActiveId assign a Nav source - SetActiveID(id, window); - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - SetFocusID(id, window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - } - } - - bool held = false; - if (g.ActiveId == id) - { - if (g.ActiveIdSource == ImGuiInputSource_Mouse) - { - if (g.ActiveIdIsJustActivated) - g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; - if (g.IO.MouseDown[0]) - { - held = true; - } - else - { - if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease)) - if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps - if (!g.DragDropActive) - pressed = true; - ClearActiveID(); - } - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - g.NavDisableHighlight = true; - } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - if (g.NavActivateDownId != id) - ClearActiveID(); - } - } - - if (out_hovered) *out_hovered = hovered; - if (out_held) *out_held = held; - - return pressed; -} - -bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - ImVec2 pos = window->DC.CursorPos; - if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) - pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y; - ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); - - const ImRect bb(pos, pos + size); - ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(bb, id)) - return false; - - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) - flags |= ImGuiButtonFlags_Repeat; - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); - - // Render - const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); - RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); - - // Automatically close popups - //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) - // CloseCurrentPopup(); - - return pressed; -} - -bool ImGui::Button(const char* label, const ImVec2& size_arg) -{ - return ButtonEx(label, size_arg, 0); -} - -// Small buttons fits within text without additional vertical spacing. -bool ImGui::SmallButton(const char* label) -{ - ImGuiContext& g = *GImGui; - float backup_padding_y = g.Style.FramePadding.y; - g.Style.FramePadding.y = 0.0f; - bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine); - g.Style.FramePadding.y = backup_padding_y; - return pressed; -} - -bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiID id = window->GetID(str_id); - float sz = ImGui::GetFrameHeight(); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(sz, sz)); - ItemSize(bb); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - // Render - const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding); - RenderArrow(bb.Min + g.Style.FramePadding, dir); - - return pressed; -} - -// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. -// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) -bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - const ImGuiID id = window->GetID(str_id); - ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(bb); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - return pressed; -} - -// Button to close a window -bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window. - // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible). - const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius)); - bool is_clipped = !ItemAdd(bb, id); - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - if (is_clipped) - return pressed; - - // Render - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton); - ImVec2 center = bb.GetCenter(); - window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), col, 12); - - const float cross_extent = (radius * 0.7071f) - 1.0f; - if (hovered) - { - center -= ImVec2(0.5f, 0.5f); - window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), GetColorU32(ImGuiCol_Text)); - window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), GetColorU32(ImGuiCol_Text)); - } - return pressed; -} - -// [Internal] -bool ImGui::ArrowButton(ImGuiID id, ImGuiDir dir, ImVec2 padding, ImGuiButtonFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return false; - - const ImGuiStyle& style = g.Style; - - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + padding.x * 2.0f, g.FontSize + padding.y * 2.0f)); - ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); - - const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); - RenderArrow(bb.Min + padding, dir, 1.0f); - - return pressed; -} - -void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - if (border_col.w > 0.0f) - bb.Max += ImVec2(2,2); - ItemSize(bb); - if (!ItemAdd(bb, 0)) - return; - - if (border_col.w > 0.0f) - { - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f); - window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col)); - } - else - { - window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col)); - } -} - -// frame_padding < 0: uses FramePadding from style (default) -// frame_padding = 0: no framing -// frame_padding > 0: set framing size -// The color used are the button colors. -bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - // Default to using texture ID as ID. User can still push string/integer prefixes. - // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV. - PushID((void *)user_texture_id); - const ImGuiID id = window->GetID("#image"); - PopID(); - - const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding; - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2); - const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size); - ItemSize(bb); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - // Render - const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding)); - if (bg_col.w > 0.0f) - window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col)); - window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col)); - - return pressed; -} - -// Start logging ImGui output to TTY -void ImGui::LogToTTY(int max_depth) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - ImGuiWindow* window = g.CurrentWindow; - - IM_ASSERT(g.LogFile == NULL); - g.LogFile = stdout; - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; -} - -// Start logging ImGui output to given file -void ImGui::LogToFile(int max_depth, const char* filename) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - ImGuiWindow* window = g.CurrentWindow; - - if (!filename) - { - filename = g.IO.LogFilename; - if (!filename) - return; - } - - IM_ASSERT(g.LogFile == NULL); - g.LogFile = ImFileOpen(filename, "ab"); - if (!g.LogFile) - { - IM_ASSERT(g.LogFile != NULL); // Consider this an error - return; - } - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; -} - -// Start logging ImGui output to clipboard -void ImGui::LogToClipboard(int max_depth) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - ImGuiWindow* window = g.CurrentWindow; - - IM_ASSERT(g.LogFile == NULL); - g.LogFile = NULL; - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; -} - -void ImGui::LogFinish() -{ - ImGuiContext& g = *GImGui; - if (!g.LogEnabled) - return; - - LogText(IM_NEWLINE); - if (g.LogFile != NULL) - { - if (g.LogFile == stdout) - fflush(g.LogFile); - else - fclose(g.LogFile); - g.LogFile = NULL; - } - if (g.LogClipboard->size() > 1) - { - SetClipboardText(g.LogClipboard->begin()); - g.LogClipboard->clear(); - } - g.LogEnabled = false; -} - -// Helper to display logging buttons -void ImGui::LogButtons() -{ - ImGuiContext& g = *GImGui; - - PushID("LogButtons"); - const bool log_to_tty = Button("Log To TTY"); SameLine(); - const bool log_to_file = Button("Log To File"); SameLine(); - const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); - PushItemWidth(80.0f); - PushAllowKeyboardFocus(false); - SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL); - PopAllowKeyboardFocus(); - PopItemWidth(); - PopID(); - - // Start logging at the end of the function so that the buttons don't appear in the log - if (log_to_tty) - LogToTTY(g.LogAutoExpandMaxDepth); - if (log_to_file) - LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename); - if (log_to_clipboard) - LogToClipboard(g.LogAutoExpandMaxDepth); -} - -bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) -{ - if (flags & ImGuiTreeNodeFlags_Leaf) - return true; - - // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions) - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiStorage* storage = window->DC.StateStorage; - - bool is_open; - if (g.NextTreeNodeOpenCond != 0) - { - if (g.NextTreeNodeOpenCond & ImGuiCond_Always) - { - is_open = g.NextTreeNodeOpenVal; - storage->SetInt(id, is_open); - } - else - { - // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently. - const int stored_value = storage->GetInt(id, -1); - if (stored_value == -1) - { - is_open = g.NextTreeNodeOpenVal; - storage->SetInt(id, is_open); - } - else - { - is_open = stored_value != 0; - } - } - g.NextTreeNodeOpenCond = 0; - } - else - { - is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; - } - - // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). - // NB- If we are above max depth we still allow manually opened nodes to be logged. - if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) - is_open = true; - - return is_open; -} - -bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; - const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f); - - if (!label_end) - label_end = FindRenderedTextEnd(label); - const ImVec2 label_size = CalcTextSize(label, label_end, false); - - // We vertically grow up to current line height up the typical widget height. - const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it - const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2); - ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height)); - if (display_frame) - { - // Framed header expand a little outside the default padding - frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1; - frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1; - } - - const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing - const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser - ItemSize(ImVec2(text_width, frame_height), text_base_offset_y); - - // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing - // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) - const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y); - bool is_open = TreeNodeBehaviorIsOpen(id, flags); - - // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. - // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). - // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero. - if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - window->DC.TreeDepthMayJumpToParentOnPop |= (1 << window->DC.TreeDepth); - - bool item_add = ItemAdd(interact_bb, id); - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; - window->DC.LastItemDisplayRect = frame_bb; - - if (!item_add) - { - if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - TreePushRawID(id); - return is_open; - } - - // Flags that affects opening behavior: - // - 0(default) ..................... single-click anywhere to open - // - OpenOnDoubleClick .............. double-click anywhere to open - // - OpenOnArrow .................... single-click on arrow to open - // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open - ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0); - if (!(flags & ImGuiTreeNodeFlags_Leaf)) - button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; - if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) - button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0); - - bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); - if (!(flags & ImGuiTreeNodeFlags_Leaf)) - { - bool toggled = false; - if (pressed) - { - toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id); - if (flags & ImGuiTreeNodeFlags_OpenOnArrow) - toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover); - if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) - toggled |= g.IO.MouseDoubleClicked[0]; - if (g.DragDropActive && is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again. - toggled = false; - } - - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open) - { - toggled = true; - NavMoveRequestCancel(); - } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? - { - toggled = true; - NavMoveRequestCancel(); - } - - if (toggled) - { - is_open = !is_open; - window->DC.StateStorage->SetInt(id, is_open); - } - } - if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) - SetItemAllowOverlap(); - - // Render - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y); - if (display_frame) - { - // Framed type - RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding); - RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin); - RenderArrow(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); - if (g.LogEnabled) - { - // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here. - const char log_prefix[] = "\n##"; - const char log_suffix[] = "##"; - LogRenderedText(&text_pos, log_prefix, log_prefix+3); - RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - LogRenderedText(&text_pos, log_suffix+1, log_suffix+3); - } - else - { - RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - } - } - else - { - // Unframed typed for tree nodes - if (hovered || (flags & ImGuiTreeNodeFlags_Selected)) - { - RenderFrame(frame_bb.Min, frame_bb.Max, col, false); - RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin); - } - - if (flags & ImGuiTreeNodeFlags_Bullet) - RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); - else if (!(flags & ImGuiTreeNodeFlags_Leaf)) - RenderArrow(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); - if (g.LogEnabled) - LogRenderedText(&text_pos, ">"); - RenderText(text_pos, label, label_end, false); - } - - if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - TreePushRawID(id); - return is_open; -} - -// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). -// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). -bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label); -} - -bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - if (p_open && !*p_open) - return false; - - ImGuiID id = window->GetID(label); - bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 0), label); - if (p_open) - { - // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. - ImGuiContext& g = *GImGui; - float button_sz = g.FontSize * 0.5f; - ImGuiItemHoveredDataBackup last_item_backup; - if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz)) - *p_open = false; - last_item_backup.Restore(); - } - - return is_open; -} - -bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - return TreeNodeBehavior(window->GetID(label), flags, label, NULL); -} - -bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end); -} - -bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end); -} - -bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) -{ - return TreeNodeExV(str_id, 0, fmt, args); -} - -bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) -{ - return TreeNodeExV(ptr_id, 0, fmt, args); -} - -bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(str_id, flags, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(ptr_id, flags, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(str_id, 0, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(ptr_id, 0, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNode(const char* label) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - return TreeNodeBehavior(window->GetID(label), 0, label, NULL); -} - -void ImGui::TreeAdvanceToLabelPos() -{ - ImGuiContext& g = *GImGui; - g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing(); -} - -// Horizontal distance preceding label when using TreeNode() or Bullet() -float ImGui::GetTreeNodeToLabelSpacing() -{ - ImGuiContext& g = *GImGui; - return g.FontSize + (g.Style.FramePadding.x * 2.0f); -} - -void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond) -{ - ImGuiContext& g = *GImGui; - if (g.CurrentWindow->SkipItems) - return; - g.NextTreeNodeOpenVal = is_open; - g.NextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always; -} - void ImGui::PushID(const char* str_id) { - ImGuiWindow* window = GetCurrentWindowRead(); - window->IDStack.push_back(window->GetID(str_id)); + ImGuiWindow* window = GImGui->CurrentWindow; + window->IDStack.push_back(window->GetIDNoKeepAlive(str_id)); } void ImGui::PushID(const char* str_id_begin, const char* str_id_end) { - ImGuiWindow* window = GetCurrentWindowRead(); - window->IDStack.push_back(window->GetID(str_id_begin, str_id_end)); + ImGuiWindow* window = GImGui->CurrentWindow; + window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end)); } void ImGui::PushID(const void* ptr_id) { - ImGuiWindow* window = GetCurrentWindowRead(); - window->IDStack.push_back(window->GetID(ptr_id)); + ImGuiWindow* window = GImGui->CurrentWindow; + window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id)); } void ImGui::PushID(int int_id) { - const void* ptr_id = (void*)(intptr_t)int_id; - ImGuiWindow* window = GetCurrentWindowRead(); - window->IDStack.push_back(window->GetID(ptr_id)); + ImGuiWindow* window = GImGui->CurrentWindow; + window->IDStack.push_back(window->GetIDNoKeepAlive(int_id)); +} + +// Push a given id value ignoring the ID stack as a seed. +void ImGui::PushOverrideID(ImGuiID id) +{ + ImGuiWindow* window = GImGui->CurrentWindow; + window->IDStack.push_back(id); } void ImGui::PopID() { - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; window->IDStack.pop_back(); } ImGuiID ImGui::GetID(const char* str_id) { - return GImGui->CurrentWindow->GetID(str_id); + ImGuiWindow* window = GImGui->CurrentWindow; + return window->GetID(str_id); } ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end) { - return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end); + ImGuiWindow* window = GImGui->CurrentWindow; + return window->GetID(str_id_begin, str_id_end); } ImGuiID ImGui::GetID(const void* ptr_id) { - return GImGui->CurrentWindow->GetID(ptr_id); -} - -void ImGui::Bullet() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); - ItemSize(bb); - if (!ItemAdd(bb, 0)) - { - SameLine(0, style.FramePadding.x*2); - return; - } - - // Render and stay on same line - RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); - SameLine(0, style.FramePadding.x*2); -} - -// Text with a little bullet aligned to the typical tree node. -void ImGui::BulletTextV(const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - const char* text_begin = g.TempBuffer; - const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); - const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it - const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding - ItemSize(bb); - if (!ItemAdd(bb, 0)) - return; - - // Render - RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); - RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false); -} - -void ImGui::BulletText(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - BulletTextV(fmt, args); - va_end(args); -} - -static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size) -{ - if (data_type == ImGuiDataType_Int) - ImFormatString(buf, buf_size, display_format, *(int*)data_ptr); - else if (data_type == ImGuiDataType_Float) - ImFormatString(buf, buf_size, display_format, *(float*)data_ptr); -} - -static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size) -{ - if (data_type == ImGuiDataType_Int) - { - if (decimal_precision < 0) - ImFormatString(buf, buf_size, "%d", *(int*)data_ptr); - else - ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr); - } - else if (data_type == ImGuiDataType_Float) - { - if (decimal_precision < 0) - ImFormatString(buf, buf_size, "%f", *(float*)data_ptr); // Ideally we'd have a minimum decimal precision of 1 to visually denote that it is a float, while hiding non-significant digits? - else - ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr); - } -} - -static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)// Store into value1 -{ - if (data_type == ImGuiDataType_Int) - { - if (op == '+') - *(int*)value1 = *(int*)value1 + *(const int*)value2; - else if (op == '-') - *(int*)value1 = *(int*)value1 - *(const int*)value2; - } - else if (data_type == ImGuiDataType_Float) - { - if (op == '+') - *(float*)value1 = *(float*)value1 + *(const float*)value2; - else if (op == '-') - *(float*)value1 = *(float*)value1 - *(const float*)value2; - } -} - -// User can input math operators (e.g. +100) to edit a numerical values. -static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format) -{ - while (ImCharIsSpace(*buf)) - buf++; - - // We don't support '-' op because it would conflict with inputing negative value. - // Instead you can use +-100 to subtract from an existing value - char op = buf[0]; - if (op == '+' || op == '*' || op == '/') - { - buf++; - while (ImCharIsSpace(*buf)) - buf++; - } - else - { - op = 0; - } - if (!buf[0]) - return false; - - if (data_type == ImGuiDataType_Int) - { - if (!scalar_format) - scalar_format = "%d"; - int* v = (int*)data_ptr; - const int old_v = *v; - int arg0i = *v; - if (op && sscanf(initial_value_buf, scalar_format, &arg0i) < 1) - return false; - - // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision - float arg1f = 0.0f; - if (op == '+') { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i + arg1f); } // Add (use "+-" to subtract) - else if (op == '*') { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i * arg1f); } // Multiply - else if (op == '/') { if (sscanf(buf, "%f", &arg1f) == 1 && arg1f != 0.0f) *v = (int)(arg0i / arg1f); }// Divide - else { if (sscanf(buf, scalar_format, &arg0i) == 1) *v = arg0i; } // Assign constant (read as integer so big values are not lossy) - return (old_v != *v); - } - else if (data_type == ImGuiDataType_Float) - { - // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in - scalar_format = "%f"; - float* v = (float*)data_ptr; - const float old_v = *v; - float arg0f = *v; - if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1) - return false; - - float arg1f = 0.0f; - if (sscanf(buf, scalar_format, &arg1f) < 1) - return false; - if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) - else if (op == '*') { *v = arg0f * arg1f; } // Multiply - else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide - else { *v = arg1f; } // Assign constant - return (old_v != *v); - } - - return false; -} - -// Create text input in place of a slider (when CTRL+Clicking on slider) -// FIXME: Logic is messy and confusing. -bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - - // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) - // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id - SetActiveID(g.ScalarAsInputTextId, window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - SetHoveredID(0); - FocusableItemUnregister(window); - - char buf[32]; - DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf)); - bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll); - if (g.ScalarAsInputTextId == 0) // First frame we started displaying the InputText widget - { - IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID (else we'd need to store them both, which is also possible) - g.ScalarAsInputTextId = g.ActiveId; - SetHoveredID(id); - } - if (text_value_changed) - return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL); - return false; -} - -// Parse display precision back from the display format string -int ImGui::ParseFormatPrecision(const char* fmt, int default_precision) -{ - int precision = default_precision; - while ((fmt = strchr(fmt, '%')) != NULL) - { - fmt++; - if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%" - while (*fmt >= '0' && *fmt <= '9') - fmt++; - if (*fmt == '.') - { - fmt = ImAtoi(fmt + 1, &precision); - if (precision < 0 || precision > 10) - precision = default_precision; - } - if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation - precision = -1; - break; - } - return precision; -} - -static float GetMinimumStepAtDecimalPrecision(int decimal_precision) -{ - static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; - return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision); -} - -float ImGui::RoundScalar(float value, int decimal_precision) -{ - // Round past decimal precision - // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0 - // FIXME: Investigate better rounding methods - if (decimal_precision < 0) - return value; - const float min_step = GetMinimumStepAtDecimalPrecision(decimal_precision); - bool negative = value < 0.0f; - value = fabsf(value); - float remainder = fmodf(value, min_step); - if (remainder <= min_step*0.5f) - value -= remainder; - else - value += (min_step - remainder); - return negative ? -value : value; -} - -static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos) -{ - if (v_min == v_max) - return 0.0f; - - const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f); - const float v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min); - if (is_non_linear) - { - if (v_clamped < 0.0f) - { - const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min); - return (1.0f - powf(f, 1.0f/power)) * linear_zero_pos; - } - else - { - const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min)); - return linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos); - } - } - - // Linear slider - return (v_clamped - v_min) / (v_max - v_min); -} - -bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - const ImGuiStyle& style = g.Style; - - // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); - - const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f); - const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; - - const float grab_padding = 2.0f; - const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f); - float grab_sz; - if (decimal_precision != 0) - grab_sz = ImMin(style.GrabMinSize, slider_sz); - else - grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit - const float slider_usable_sz = slider_sz - grab_sz; - const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f; - const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f; - - // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f - float linear_zero_pos = 0.0f; // 0.0->1.0f - if (v_min * v_max < 0.0f) - { - // Different sign - const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power); - const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power); - linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0); - } - else - { - // Same sign - linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; - } - - // Process interacting with the slider - bool value_changed = false; - if (g.ActiveId == id) - { - bool set_new_value = false; - float clicked_t = 0.0f; - if (g.ActiveIdSource == ImGuiInputSource_Mouse) - { - if (!g.IO.MouseDown[0]) - { - ClearActiveID(); - } - else - { - const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; - clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f; - if (!is_horizontal) - clicked_t = 1.0f - clicked_t; - set_new_value = true; - } - } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); - float delta = is_horizontal ? delta2.x : -delta2.y; - if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) - { - ClearActiveID(); - } - else if (delta != 0.0f) - { - clicked_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos); - if (decimal_precision == 0 && !is_non_linear) - { - if (fabsf(v_max - v_min) <= 100.0f || IsNavInputDown(ImGuiNavInput_TweakSlow)) - delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (v_max - v_min); // Gamepad/keyboard tweak speeds in integer steps - else - delta /= 100.0f; - } - else - { - delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds - if (IsNavInputDown(ImGuiNavInput_TweakSlow)) - delta /= 10.0f; - } - if (IsNavInputDown(ImGuiNavInput_TweakFast)) - delta *= 10.0f; - set_new_value = true; - if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits - set_new_value = false; - else - clicked_t = ImSaturate(clicked_t + delta); - } - } - - if (set_new_value) - { - float new_value; - if (is_non_linear) - { - // Account for logarithmic scale on both sides of the zero - if (clicked_t < linear_zero_pos) - { - // Negative: rescale to the negative range before powering - float a = 1.0f - (clicked_t / linear_zero_pos); - a = powf(a, power); - new_value = ImLerp(ImMin(v_max,0.0f), v_min, a); - } - else - { - // Positive: rescale to the positive range before powering - float a; - if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f) - a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos); - else - a = clicked_t; - a = powf(a, power); - new_value = ImLerp(ImMax(v_min,0.0f), v_max, a); - } - } - else - { - // Linear slider - new_value = ImLerp(v_min, v_max, clicked_t); - } - - // Round past decimal precision - new_value = RoundScalar(new_value, decimal_precision); - if (*v != new_value) - { - *v = new_value; - value_changed = true; - } - } - } - - // Draw - float grab_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos); - if (!is_horizontal) - grab_t = 1.0f - grab_t; - const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); - ImRect grab_bb; - if (is_horizontal) - grab_bb = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + grab_padding), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - grab_padding)); - else - grab_bb = ImRect(ImVec2(frame_bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f)); - window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); - - return value_changed; -} - -// Use power!=1.0 for logarithmic sliders. -// Adjust display_format to decorate the value with a prefix or a suffix. -// "%.3f" 1.234 -// "%5.2f secs" 01.23 secs -// "Gold: %.0f" Gold: 1 -bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const float w = CalcItemWidth(); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - // NB- we don't call ItemSize() yet because we may turn into a text edit box below - if (!ItemAdd(total_bb, id, &frame_bb)) - { - ItemSize(total_bb, style.FramePadding.y); - return false; - } - const bool hovered = ItemHoverable(frame_bb, id); - - if (!display_format) - display_format = "%.3f"; - int decimal_precision = ParseFormatPrecision(display_format, 3); - - // Tabbing or CTRL-clicking on Slider turns it into an input box - bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, id); - if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) - { - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id) - { - start_text_input = true; - g.ScalarAsInputTextId = 0; - } - } - if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) - return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision); - - // Actual slider behavior + render grab - ItemSize(total_bb, style.FramePadding.y); - const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - char value_buf[64]; - const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); - RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - return value_changed; -} - -bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); - const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(frame_bb, id)) - return false; - const bool hovered = ItemHoverable(frame_bb, id); - - if (!display_format) - display_format = "%.3f"; - int decimal_precision = ParseFormatPrecision(display_format, 3); - - if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) - { - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - } - - // Actual slider behavior + render grab - bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - // For the vertical slider we allow centered text to overlap the frame padding - char value_buf[64]; - char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); - RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f)); - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - return value_changed; -} - -bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max) -{ - float v_deg = (*v_rad) * 360.0f / (2*IM_PI); - bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f); - *v_rad = v_deg * (2*IM_PI) / 360.0f; - return value_changed; -} - -bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format) -{ - if (!display_format) - display_format = "%.0f"; - float v_f = (float)*v; - bool value_changed = SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f); - *v = (int)v_f; - return value_changed; -} - -bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format) -{ - if (!display_format) - display_format = "%.0f"; - float v_f = (float)*v; - bool value_changed = VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f); - *v = (int)v_f; - return value_changed; -} - -// Add multiple sliders on 1 line for compact edition of multiple components -bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components); - for (int i = 0; i < components; i++) - { - PushID(i); - value_changed |= SliderFloat("##v", &v[i], v_min, v_max, display_format, power); - SameLine(0, g.Style.ItemInnerSpacing.x); - PopID(); - PopItemWidth(); - } - PopID(); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - - return value_changed; -} - -bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power) -{ - return SliderFloatN(label, v, 2, v_min, v_max, display_format, power); -} - -bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power) -{ - return SliderFloatN(label, v, 3, v_min, v_max, display_format, power); -} - -bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power) -{ - return SliderFloatN(label, v, 4, v_min, v_max, display_format, power); -} - -bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components); - for (int i = 0; i < components; i++) - { - PushID(i); - value_changed |= SliderInt("##v", &v[i], v_min, v_max, display_format); - SameLine(0, g.Style.ItemInnerSpacing.x); - PopID(); - PopItemWidth(); - } - PopID(); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - - return value_changed; -} - -bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format) -{ - return SliderIntN(label, v, 2, v_min, v_max, display_format); -} - -bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format) -{ - return SliderIntN(label, v, 3, v_min, v_max, display_format); -} - -bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format) -{ - return SliderIntN(label, v, 4, v_min, v_max, display_format); -} - -bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power) -{ - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); - - bool value_changed = false; - - // Process interacting with the drag - if (g.ActiveId == id) - { - if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) - ClearActiveID(); - else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) - ClearActiveID(); - } - if (g.ActiveId == id) - { - if (g.ActiveIdIsJustActivated) - { - // Lock current value on click - g.DragCurrentValue = *v; - g.DragLastMouseDelta = ImVec2(0.f, 0.f); - } - - if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX) - v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio; - - float v_cur = g.DragCurrentValue; - const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f); - float adjust_delta = 0.0f; - if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid()) - { - adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x; - if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) - adjust_delta *= g.DragSpeedScaleFast; - if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) - adjust_delta *= g.DragSpeedScaleSlow; - g.DragLastMouseDelta.x = mouse_drag_delta.x; - } - if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; - if (v_min < v_max && ((v_cur >= v_max && adjust_delta > 0.0f) || (v_cur <= v_min && adjust_delta < 0.0f))) // This is to avoid applying the saturation when already past the limits - adjust_delta = 0.0f; - v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); - } - adjust_delta *= v_speed; - - if (fabsf(adjust_delta) > 0.0f) - { - if (fabsf(power - 1.0f) > 0.001f) - { - // Logarithmic curve on both side of 0.0 - float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur; - float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f; - float v1 = powf(v0_abs, 1.0f / power) + (adjust_delta * v0_sign); - float v1_abs = v1 >= 0.0f ? v1 : -v1; - float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f; // Crossed sign line - v_cur = powf(v1_abs, power) * v0_sign * v1_sign; // Reapply sign - } - else - { - v_cur += adjust_delta; - } - - // Clamp - if (v_min < v_max) - v_cur = ImClamp(v_cur, v_min, v_max); - g.DragCurrentValue = v_cur; - } - - // Round to user desired precision, then apply - v_cur = RoundScalar(v_cur, decimal_precision); - if (*v != v_cur) - { - *v = v_cur; - value_changed = true; - } - } - - return value_changed; -} - -bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const float w = CalcItemWidth(); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); - const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - // NB- we don't call ItemSize() yet because we may turn into a text edit box below - if (!ItemAdd(total_bb, id, &frame_bb)) - { - ItemSize(total_bb, style.FramePadding.y); - return false; - } - const bool hovered = ItemHoverable(frame_bb, id); - - if (!display_format) - display_format = "%.3f"; - int decimal_precision = ParseFormatPrecision(display_format, 3); - - // Tabbing or CTRL-clicking on Drag turns it into an input box - bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, id); - if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) - { - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) - { - start_text_input = true; - g.ScalarAsInputTextId = 0; - } - } - if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) - return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision); - - // Actual drag behavior - ItemSize(total_bb, style.FramePadding.y); - const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - char value_buf[64]; - const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); - RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); - - return value_changed; -} - -bool ImGui::DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components); - for (int i = 0; i < components; i++) - { - PushID(i); - value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power); - SameLine(0, g.Style.ItemInnerSpacing.x); - PopID(); - PopItemWidth(); - } - PopID(); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - - return value_changed; -} - -bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power) -{ - return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power); -} - -bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power) -{ - return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power); -} - -bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power) -{ - return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power); -} - -bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* display_format, const char* display_format_max, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - PushID(label); - BeginGroup(); - PushMultiItemsWidths(2); - - bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format, power); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, display_format_max ? display_format_max : display_format, power); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - PopID(); - - return value_changed; -} - -// NB: v_speed is float to allow adjusting the drag speed with more precision -bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format) -{ - if (!display_format) - display_format = "%.0f"; - float v_f = (float)*v; - bool value_changed = DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format); - *v = (int)v_f; - return value_changed; -} - -bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components); - for (int i = 0; i < components; i++) - { - PushID(i); - value_changed |= DragInt("##v", &v[i], v_speed, v_min, v_max, display_format); - SameLine(0, g.Style.ItemInnerSpacing.x); - PopID(); - PopItemWidth(); - } - PopID(); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - - return value_changed; -} - -bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format) -{ - return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format); -} - -bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format) -{ - return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format); -} - -bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format) -{ - return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format); -} - -bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* display_format, const char* display_format_max) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - PushID(label); - BeginGroup(); - PushMultiItemsWidths(2); - - bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, display_format_max ? display_format_max : display_format); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - PopID(); - - return value_changed; -} - -void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - if (graph_size.x == 0.0f) - graph_size.x = CalcItemWidth(); - if (graph_size.y == 0.0f) - graph_size.y = label_size.y + (style.FramePadding.y * 2); - - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y)); - const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, 0, &frame_bb)) - return; - const bool hovered = ItemHoverable(inner_bb, 0); - - // Determine scale from values if not specified - if (scale_min == FLT_MAX || scale_max == FLT_MAX) - { - float v_min = FLT_MAX; - float v_max = -FLT_MAX; - for (int i = 0; i < values_count; i++) - { - const float v = values_getter(data, i); - v_min = ImMin(v_min, v); - v_max = ImMax(v_max, v); - } - if (scale_min == FLT_MAX) - scale_min = v_min; - if (scale_max == FLT_MAX) - scale_max = v_max; - } - - RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - - if (values_count > 0) - { - int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); - int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); - - // Tooltip on hover - int v_hovered = -1; - if (hovered) - { - const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); - const int v_idx = (int)(t * item_count); - IM_ASSERT(v_idx >= 0 && v_idx < values_count); - - const float v0 = values_getter(data, (v_idx + values_offset) % values_count); - const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count); - if (plot_type == ImGuiPlotType_Lines) - SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1); - else if (plot_type == ImGuiPlotType_Histogram) - SetTooltip("%d: %8.4g", v_idx, v0); - v_hovered = v_idx; - } - - const float t_step = 1.0f / (float)res_w; - const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); - - float v0 = values_getter(data, (0 + values_offset) % values_count); - float t0 = 0.0f; - ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle - float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands - - const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); - const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); - - for (int n = 0; n < res_w; n++) - { - const float t1 = t0 + t_step; - const int v1_idx = (int)(t0 * item_count + 0.5f); - IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); - const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count); - const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) ); - - // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU. - ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); - ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t)); - if (plot_type == ImGuiPlotType_Lines) - { - window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); - } - else if (plot_type == ImGuiPlotType_Histogram) - { - if (pos1.x >= pos0.x + 2.0f) - pos1.x -= 1.0f; - window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); - } - - t0 = t1; - tp0 = tp1; - } - } - - // Text overlay - if (overlay_text) - RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); -} - -struct ImGuiPlotArrayGetterData -{ - const float* Values; - int Stride; - - ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; } -}; - -static float Plot_ArrayGetter(void* data, int idx) -{ - ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data; - const float v = *(float*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride); - return v; -} - -void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) -{ - ImGuiPlotArrayGetterData data(values, stride); - PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) -{ - PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) -{ - ImGuiPlotArrayGetterData data(values, stride); - PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) -{ - PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size -void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - ImVec2 pos = window->DC.CursorPos; - ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f)); - ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(bb, 0)) - return; - - // Render - fraction = ImSaturate(fraction); - RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); - const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y); - RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding); - - // Default displaying the fraction as percentage string, but user can override it - char overlay_buf[32]; - if (!overlay) - { - ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f); - overlay = overlay_buf; - } - - ImVec2 overlay_size = CalcTextSize(overlay, NULL); - if (overlay_size.x > 0.0f) - RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb); -} - -bool ImGui::Checkbox(const char* label, bool* v) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); // We want a square shape to we use Y twice - ItemSize(check_bb, style.FramePadding.y); - - ImRect total_bb = check_bb; - if (label_size.x > 0) - SameLine(0, style.ItemInnerSpacing.x); - const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size); - if (label_size.x > 0) - { - ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); - total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max)); - } - - if (!ItemAdd(total_bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); - if (pressed) - *v = !(*v); - - RenderNavHighlight(total_bb, id); - RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); - if (*v) - { - const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); - const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); - RenderCheckMark(check_bb.Min + ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad*2.0f); - } - - if (g.LogEnabled) - LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]"); - if (label_size.x > 0.0f) - RenderText(text_bb.Min, label); - - return pressed; -} - -bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value) -{ - bool v = ((*flags & flags_value) == flags_value); - bool pressed = Checkbox(label, &v); - if (pressed) - { - if (v) - *flags |= flags_value; - else - *flags &= ~flags_value; - } - - return pressed; -} - -bool ImGui::RadioButton(const char* label, bool active) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1)); - ItemSize(check_bb, style.FramePadding.y); - - ImRect total_bb = check_bb; - if (label_size.x > 0) - SameLine(0, style.ItemInnerSpacing.x); - const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size); - if (label_size.x > 0) - { - ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); - total_bb.Add(text_bb); - } - - if (!ItemAdd(total_bb, id)) - return false; - - ImVec2 center = check_bb.GetCenter(); - center.x = (float)(int)center.x + 0.5f; - center.y = (float)(int)center.y + 0.5f; - const float radius = check_bb.GetHeight() * 0.5f; - - bool hovered, held; - bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); - - RenderNavHighlight(total_bb, id); - window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); - if (active) - { - const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); - const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); - window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16); - } - - if (style.FrameBorderSize > 0.0f) - { - window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize); - window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize); - } - - if (g.LogEnabled) - LogRenderedText(&text_bb.Min, active ? "(x)" : "( )"); - if (label_size.x > 0.0f) - RenderText(text_bb.Min, label); - - return pressed; -} - -bool ImGui::RadioButton(const char* label, int* v, int v_button) -{ - const bool pressed = RadioButton(label, *v == v_button); - if (pressed) - { - *v = v_button; - } - return pressed; -} - -static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) -{ - int line_count = 0; - const char* s = text_begin; - while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding - if (c == '\n') - line_count++; - s--; - if (s[0] != '\n' && s[0] != '\r') - line_count++; - *out_text_end = s; - return line_count; -} - -static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) -{ - ImFont* font = GImGui->Font; - const float line_height = GImGui->FontSize; - const float scale = line_height / font->FontSize; - - ImVec2 text_size = ImVec2(0,0); - float line_width = 0.0f; - - const ImWchar* s = text_begin; - while (s < text_end) - { - unsigned int c = (unsigned int)(*s++); - if (c == '\n') - { - text_size.x = ImMax(text_size.x, line_width); - text_size.y += line_height; - line_width = 0.0f; - if (stop_on_new_line) - break; - continue; - } - if (c == '\r') - continue; - - const float char_width = font->GetCharAdvance((unsigned short)c) * scale; - line_width += char_width; - } - - if (text_size.x < line_width) - text_size.x = line_width; - - if (out_offset) - *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n - - if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n - text_size.y += line_height; - - if (remaining) - *remaining = s; - - return text_size; -} - -// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) -namespace ImGuiStb -{ - -static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } -static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->Text[idx]; } -static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->Text[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); } -static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; } -static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; -static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) -{ - const ImWchar* text = obj->Text.Data; - const ImWchar* text_remaining = NULL; - const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); - r->x0 = 0.0f; - r->x1 = size.x; - r->baseline_y_delta = size.y; - r->ymin = 0.0f; - r->ymax = size.y; - r->num_chars = (int)(text_remaining - (text + line_start_idx)); -} - -static bool is_separator(unsigned int c) { return ImCharIsSpace(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } -static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->Text[idx-1] ) && !is_separator( obj->Text[idx] ) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } -#ifdef __APPLE__ // FIXME: Move setting to IO structure -static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->Text[idx-1] ) && is_separator( obj->Text[idx] ) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } -#else -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } -#endif -#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h -#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL - -static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) -{ - ImWchar* dst = obj->Text.Data + pos; - - // We maintain our buffer length in both UTF-8 and wchar formats - obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); - obj->CurLenW -= n; - - // Offset remaining text - const ImWchar* src = obj->Text.Data + pos + n; - while (ImWchar c = *src++) - *dst++ = c; - *dst = '\0'; -} - -static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) -{ - const int text_len = obj->CurLenW; - IM_ASSERT(pos <= text_len); - if (new_text_len + text_len + 1 > obj->Text.Size) - return false; - - const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); - if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA) - return false; - - ImWchar* text = obj->Text.Data; - if (pos != text_len) - memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); - memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); - - obj->CurLenW += new_text_len; - obj->CurLenA += new_text_len_utf8; - obj->Text[obj->CurLenW] = '\0'; - - return true; -} - -// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) -#define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left -#define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right -#define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up -#define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down -#define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line -#define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line -#define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text -#define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text -#define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor -#define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor -#define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo -#define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo -#define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word -#define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word -#define STB_TEXTEDIT_K_SHIFT 0x20000 - -#define STB_TEXTEDIT_IMPLEMENTATION -#include "stb_textedit.h" - -} - -void ImGuiTextEditState::OnKeyPressed(int key) -{ - stb_textedit_key(this, &StbState, key); - CursorFollow = true; - CursorAnimReset(); -} - -// Public API to manipulate UTF-8 text -// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) -// FIXME: The existence of this rarely exercised code path is a bit of a nuisance. -void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count) -{ - IM_ASSERT(pos + bytes_count <= BufTextLen); - char* dst = Buf + pos; - const char* src = Buf + pos + bytes_count; - while (char c = *src++) - *dst++ = c; - *dst = '\0'; - - if (CursorPos + bytes_count >= pos) - CursorPos -= bytes_count; - else if (CursorPos >= pos) - CursorPos = pos; - SelectionStart = SelectionEnd = CursorPos; - BufDirty = true; - BufTextLen -= bytes_count; -} - -void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) -{ - const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); - if (new_text_len + BufTextLen + 1 >= BufSize) - return; - - if (BufTextLen != pos) - memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); - memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); - Buf[BufTextLen + new_text_len] = '\0'; - - if (CursorPos >= pos) - CursorPos += new_text_len; - SelectionStart = SelectionEnd = CursorPos; - BufDirty = true; - BufTextLen += new_text_len; -} - -// Return false to discard a character. -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) -{ - unsigned int c = *p_char; - - if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF))) - { - bool pass = false; - pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); - pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); - if (!pass) - return false; - } - - if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys. - return false; - - if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank)) - { - if (flags & ImGuiInputTextFlags_CharsDecimal) - if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) - return false; - - if (flags & ImGuiInputTextFlags_CharsHexadecimal) - if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) - return false; - - if (flags & ImGuiInputTextFlags_CharsUppercase) - if (c >= 'a' && c <= 'z') - *p_char = (c += (unsigned int)('A'-'a')); - - if (flags & ImGuiInputTextFlags_CharsNoBlank) - if (ImCharIsSpace(c)) - return false; - } - - if (flags & ImGuiInputTextFlags_CallbackCharFilter) - { - ImGuiTextEditCallbackData callback_data; - memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); - callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; - callback_data.EventChar = (ImWchar)c; - callback_data.Flags = flags; - callback_data.UserData = user_data; - if (callback(&callback_data) != 0) - return false; - *p_char = callback_data.EventChar; - if (!callback_data.EventChar) - return false; - } - - return true; -} - -// Edit a string of text -// NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect. -// FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188 -bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) - IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) - - ImGuiContext& g = *GImGui; - const ImGuiIO& io = g.IO; - const ImGuiStyle& style = g.Style; - - const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; - const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0; - const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; - const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; - - if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn - BeginGroup(); - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f)); - - ImGuiWindow* draw_window = window; - if (is_multiline) - { - ItemAdd(total_bb, id, &frame_bb); - if (!BeginChildFrame(id, frame_bb.GetSize())) - { - EndChildFrame(); - EndGroup(); - return false; - } - draw_window = GetCurrentWindow(); - size.x -= draw_window->ScrollbarSizes.x; - } - else - { - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) - return false; - } - const bool hovered = ItemHoverable(frame_bb, id); - if (hovered) - g.MouseCursor = ImGuiMouseCursor_TextInput; - - // Password pushes a temporary font with only a fallback glyph - if (is_password) - { - const ImFontGlyph* glyph = g.Font->FindGlyph('*'); - ImFont* password_font = &g.InputTextPasswordFont; - password_font->FontSize = g.Font->FontSize; - password_font->Scale = g.Font->Scale; - password_font->DisplayOffset = g.Font->DisplayOffset; - password_font->Ascent = g.Font->Ascent; - password_font->Descent = g.Font->Descent; - password_font->ContainerAtlas = g.Font->ContainerAtlas; - password_font->FallbackGlyph = glyph; - password_font->FallbackAdvanceX = glyph->AdvanceX; - IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); - PushFont(password_font); - } - - // NB: we are only allowed to access 'edit_state' if we are the active widget. - ImGuiTextEditState& edit_state = g.InputTextState; - - const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing - const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); - const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; - - const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); - - bool clear_active_id = false; - - bool select_all = (g.ActiveId != id) && (((flags & ImGuiInputTextFlags_AutoSelectAll) != 0) || (g.NavInputId == id)) && (!is_multiline); - if (focus_requested || user_clicked || user_scrolled || g.NavInputId == id) - { - if (g.ActiveId != id) - { - // Start edition - // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) - // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) - const int prev_len_w = edit_state.CurLenW; - edit_state.Text.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size); - const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); - edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. - edit_state.CursorAnimReset(); - - // Preserve cursor position and undo/redo stack if we come back to same widget - // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar). - const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW); - if (recycle_state) - { - // Recycle existing cursor/selection/undo stack but clamp position - // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. - edit_state.CursorClamp(); - } - else - { - edit_state.Id = id; - edit_state.ScrollX = 0.0f; - stb_textedit_initialize_state(&edit_state.StbState, !is_multiline); - if (!is_multiline && focus_requested_by_code) - select_all = true; - } - if (flags & ImGuiInputTextFlags_AlwaysInsertMode) - edit_state.StbState.insert_mode = true; - if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) - select_all = true; - } - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory)) - g.ActiveIdAllowNavDirFlags |= ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); - } - else if (io.MouseClicked[0]) - { - // Release focus when we click outside - clear_active_id = true; - } - - bool value_changed = false; - bool enter_pressed = false; - - if (g.ActiveId == id) - { - if (!is_editable && !g.ActiveIdIsJustActivated) - { - // When read-only we always use the live data passed to the function - edit_state.Text.resize(buf_size+1); - const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); - edit_state.CurLenA = (int)(buf_end - buf); - edit_state.CursorClamp(); - } - - edit_state.BufSizeA = buf_size; - - // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. - // Down the line we should have a cleaner library-wide concept of Selected vs Active. - g.ActiveIdAllowOverlap = !io.MouseDown[0]; - g.WantTextInputNextFrame = 1; - - // Edit in progress - const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; - const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f)); - - const bool osx_double_click_selects_words = io.OptMacOSXBehaviors; // OS X style: Double click selects by word instead of selecting whole text - if (select_all || (hovered && !osx_double_click_selects_words && io.MouseDoubleClicked[0])) - { - edit_state.SelectAll(); - edit_state.SelectedAllMouseLock = true; - } - else if (hovered && osx_double_click_selects_words && io.MouseDoubleClicked[0]) - { - // Select a word only, OS X style (by simulating keystrokes) - edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); - edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); - } - else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock) - { - if (hovered) - { - stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y); - edit_state.CursorAnimReset(); - } - } - else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) - { - stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y); - edit_state.CursorAnimReset(); - edit_state.CursorFollow = true; - } - if (edit_state.SelectedAllMouseLock && !io.MouseDown[0]) - edit_state.SelectedAllMouseLock = false; - - if (io.InputCharacters[0]) - { - // Process text input (before we check for Return because using some IME will effectively send a Return?) - // We ignore CTRL inputs, but need to allow CTRL+ALT as some keyboards (e.g. German) use AltGR - which is Alt+Ctrl - to input certain characters. - if (!(io.KeyCtrl && !io.KeyAlt) && is_editable) - { - for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++) - if (unsigned int c = (unsigned int)io.InputCharacters[n]) - { - // Insert character if they pass filtering - if (!InputTextFilterCharacter(&c, flags, callback, user_data)) - continue; - edit_state.OnKeyPressed((int)c); - } - } - - // Consume characters - memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); - } - } - - bool cancel_edit = false; - if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) - { - // Handle key-presses - const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); - const bool is_shortcut_key_only = (io.OptMacOSXBehaviors ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl - const bool is_wordmove_key_down = io.OptMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl - const bool is_startend_key_down = io.OptMacOSXBehaviors && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End - const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper; - const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper; - - const bool is_cut = ((is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && is_editable && !is_password && (!is_multiline || edit_state.HasSelection()); - const bool is_copy = ((is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection()); - const bool is_paste = ((is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable; - - if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable) - { - if (!edit_state.HasSelection()) - { - if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT); - else if (io.OptMacOSXBehaviors && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT); - } - edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); - } - else if (IsKeyPressedMap(ImGuiKey_Enter)) - { - bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; - if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) - { - enter_pressed = clear_active_id = true; - } - else if (is_editable) - { - unsigned int c = '\n'; // Insert new line - if (InputTextFilterCharacter(&c, flags, callback, user_data)) - edit_state.OnKeyPressed((int)c); - } - } - else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable) - { - unsigned int c = '\t'; // Insert TAB - if (InputTextFilterCharacter(&c, flags, callback, user_data)) - edit_state.OnKeyPressed((int)c); - } - else if (IsKeyPressedMap(ImGuiKey_Escape)) { clear_active_id = cancel_edit = true; } - else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable && is_undoable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); } - else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable && is_undoable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); } - else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; } - else if (is_cut || is_copy) - { - // Cut, Copy - if (io.SetClipboardTextFn) - { - const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; - const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW; - edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1); - ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie); - SetClipboardText(edit_state.TempTextBuffer.Data); - } - - if (is_cut) - { - if (!edit_state.HasSelection()) - edit_state.SelectAll(); - edit_state.CursorFollow = true; - stb_textedit_cut(&edit_state, &edit_state.StbState); - } - } - else if (is_paste) - { - // Paste - if (const char* clipboard = GetClipboardText()) - { - // Filter pasted buffer - const int clipboard_len = (int)strlen(clipboard); - ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar)); - int clipboard_filtered_len = 0; - for (const char* s = clipboard; *s; ) - { - unsigned int c; - s += ImTextCharFromUtf8(&c, s, NULL); - if (c == 0) - break; - if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data)) - continue; - clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; - } - clipboard_filtered[clipboard_filtered_len] = 0; - if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation - { - stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len); - edit_state.CursorFollow = true; - } - ImGui::MemFree(clipboard_filtered); - } - } - } - - if (g.ActiveId == id) - { - if (cancel_edit) - { - // Restore initial value - if (is_editable) - { - ImStrncpy(buf, edit_state.InitialText.Data, buf_size); - value_changed = true; - } - } - - // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. - // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. Also this allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage. - bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); - if (apply_edit_back_to_user_buffer) - { - // Apply new value immediately - copy modified buffer back - // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer - // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. - // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. - if (is_editable) - { - edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4); - ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL); - } - - // User callback - if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) - { - IM_ASSERT(callback != NULL); - - // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. - ImGuiInputTextFlags event_flag = 0; - ImGuiKey event_key = ImGuiKey_COUNT; - if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) - { - event_flag = ImGuiInputTextFlags_CallbackCompletion; - event_key = ImGuiKey_Tab; - } - else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) - { - event_flag = ImGuiInputTextFlags_CallbackHistory; - event_key = ImGuiKey_UpArrow; - } - else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) - { - event_flag = ImGuiInputTextFlags_CallbackHistory; - event_key = ImGuiKey_DownArrow; - } - else if (flags & ImGuiInputTextFlags_CallbackAlways) - event_flag = ImGuiInputTextFlags_CallbackAlways; - - if (event_flag) - { - ImGuiTextEditCallbackData callback_data; - memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); - callback_data.EventFlag = event_flag; - callback_data.Flags = flags; - callback_data.UserData = user_data; - callback_data.ReadOnly = !is_editable; - - callback_data.EventKey = event_key; - callback_data.Buf = edit_state.TempTextBuffer.Data; - callback_data.BufTextLen = edit_state.CurLenA; - callback_data.BufSize = edit_state.BufSizeA; - callback_data.BufDirty = false; - - // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) - ImWchar* text = edit_state.Text.Data; - const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor); - const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start); - const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end); - - // Call user code - callback(&callback_data); - - // Read back what user may have modified - IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields - IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA); - IM_ASSERT(callback_data.Flags == flags); - if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); - if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); - if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); - if (callback_data.BufDirty) - { - IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL); - edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() - edit_state.CursorAnimReset(); - } - } - } - - // Copy back to user buffer - if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0) - { - ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size); - value_changed = true; - } - } - } - - // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) - if (clear_active_id && g.ActiveId == id) - ClearActiveID(); - - // Render - // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. - const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL; - - RenderNavHighlight(frame_bb, id); - if (!is_multiline) - RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - - const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size - ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; - ImVec2 text_size(0.f, 0.f); - const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY")); - if (g.ActiveId == id || is_currently_scrolling) - { - edit_state.CursorAnim += io.DeltaTime; - - // This is going to be messy. We need to: - // - Display the text (this alone can be more easily clipped) - // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) - // - Measure text height (for scrollbar) - // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) - // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. - const ImWchar* text_begin = edit_state.Text.Data; - ImVec2 cursor_offset, select_start_offset; - - { - // Count lines + find lines numbers straddling 'cursor' and 'select_start' position. - const ImWchar* searches_input_ptr[2]; - searches_input_ptr[0] = text_begin + edit_state.StbState.cursor; - searches_input_ptr[1] = NULL; - int searches_remaining = 1; - int searches_result_line_number[2] = { -1, -999 }; - if (edit_state.StbState.select_start != edit_state.StbState.select_end) - { - searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); - searches_result_line_number[1] = -1; - searches_remaining++; - } - - // Iterate all lines to find our line numbers - // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter. - searches_remaining += is_multiline ? 1 : 0; - int line_count = 0; - for (const ImWchar* s = text_begin; *s != 0; s++) - if (*s == '\n') - { - line_count++; - if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; } - if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; } - } - line_count++; - if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count; - if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count; - - // Calculate 2d position by finding the beginning of the line and measuring distance - cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; - cursor_offset.y = searches_result_line_number[0] * g.FontSize; - if (searches_result_line_number[1] >= 0) - { - select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; - select_start_offset.y = searches_result_line_number[1] * g.FontSize; - } - - // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) - if (is_multiline) - text_size = ImVec2(size.x, line_count * g.FontSize); - } - - // Scroll - if (edit_state.CursorFollow) - { - // Horizontal scroll in chunks of quarter width - if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) - { - const float scroll_increment_x = size.x * 0.25f; - if (cursor_offset.x < edit_state.ScrollX) - edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x); - else if (cursor_offset.x - size.x >= edit_state.ScrollX) - edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x); - } - else - { - edit_state.ScrollX = 0.0f; - } - - // Vertical scroll - if (is_multiline) - { - float scroll_y = draw_window->Scroll.y; - if (cursor_offset.y - g.FontSize < scroll_y) - scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); - else if (cursor_offset.y - size.y >= scroll_y) - scroll_y = cursor_offset.y - size.y; - draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag - draw_window->Scroll.y = scroll_y; - render_pos.y = draw_window->DC.CursorPos.y; - } - } - edit_state.CursorFollow = false; - const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f); - - // Draw selection - if (edit_state.StbState.select_start != edit_state.StbState.select_end) - { - const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); - const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end); - - float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. - float bg_offy_dn = is_multiline ? 0.0f : 2.0f; - ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg); - ImVec2 rect_pos = render_pos + select_start_offset - render_scroll; - for (const ImWchar* p = text_selected_begin; p < text_selected_end; ) - { - if (rect_pos.y > clip_rect.w + g.FontSize) - break; - if (rect_pos.y < clip_rect.y) - { - while (p < text_selected_end) - if (*p++ == '\n') - break; - } - else - { - ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); - if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines - ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn)); - rect.ClipWith(clip_rect); - if (rect.Overlaps(clip_rect)) - draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); - } - rect_pos.x = render_pos.x - render_scroll.x; - rect_pos.y += g.FontSize; - } - } - - draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect); - - // Draw blinking cursor - bool cursor_is_visible = (!g.IO.OptCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; - ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; - ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f); - if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) - draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); - - // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) - if (is_editable) - g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize); - } - else - { - // Render text only - const char* buf_end = NULL; - if (is_multiline) - text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width - draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect); - } - - if (is_multiline) - { - Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line - EndChildFrame(); - EndGroup(); - } - - if (is_password) - PopFont(); - - // Log as text - if (g.LogEnabled && !is_password) - LogRenderedText(&render_pos, buf_display, NULL); - - if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) - return enter_pressed; - else - return value_changed; -} - -bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) -{ - IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() - return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data); -} - -bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) -{ - return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); -} - -// NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "display_format" argument) -bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - BeginGroup(); - PushID(label); - const ImVec2 button_sz = ImVec2(GetFrameHeight(), GetFrameHeight()); - if (step_ptr) - PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2)); - - char buf[64]; - DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf)); - - bool value_changed = false; - if (!(extra_flags & ImGuiInputTextFlags_CharsHexadecimal)) - extra_flags |= ImGuiInputTextFlags_CharsDecimal; - extra_flags |= ImGuiInputTextFlags_AutoSelectAll; - if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view - value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format); - - // Step buttons - if (step_ptr) - { - PopItemWidth(); - SameLine(0, style.ItemInnerSpacing.x); - if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) - { - DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr); - value_changed = true; - } - SameLine(0, style.ItemInnerSpacing.x); - if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) - { - DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr); - value_changed = true; - } - } - PopID(); - - if (label_size.x > 0) - { - SameLine(0, style.ItemInnerSpacing.x); - RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label); - ItemSize(label_size, style.FramePadding.y); - } - EndGroup(); - - return value_changed; -} - -bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - char display_format[16]; - if (decimal_precision < 0) - strcpy(display_format, "%f"); // Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1 - else - ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision); - return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), display_format, extra_flags); -} - -bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags) -{ - // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes. - const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; - return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), scalar_format, extra_flags); -} - -bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components); - for (int i = 0; i < components; i++) - { - PushID(i); - value_changed |= InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags); - SameLine(0, g.Style.ItemInnerSpacing.x); - PopID(); - PopItemWidth(); - } - PopID(); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - - return value_changed; -} - -bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - return InputFloatN(label, v, 2, decimal_precision, extra_flags); -} - -bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - return InputFloatN(label, v, 3, decimal_precision, extra_flags); -} - -bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - return InputFloatN(label, v, 4, decimal_precision, extra_flags); -} - -bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components); - for (int i = 0; i < components; i++) - { - PushID(i); - value_changed |= InputInt("##v", &v[i], 0, 0, extra_flags); - SameLine(0, g.Style.ItemInnerSpacing.x); - PopID(); - PopItemWidth(); - } - PopID(); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - - return value_changed; -} - -bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags) -{ - return InputIntN(label, v, 2, extra_flags); -} - -bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags) -{ - return InputIntN(label, v, 3, extra_flags); -} - -bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags) -{ - return InputIntN(label, v, 4, extra_flags); -} - -static float CalcMaxPopupHeightFromItemCount(int items_count) -{ - ImGuiContext& g = *GImGui; - if (items_count <= 0) - return FLT_MAX; - return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2); -} - -bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) -{ - // Always consume the SetNextWindowSizeConstraint() call in our early return paths - ImGuiContext& g = *GImGui; - ImGuiCond backup_next_window_size_constraint = g.NextWindowData.SizeConstraintCond; - g.NextWindowData.SizeConstraintCond = 0; - - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together - - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - - const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth(); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); - bool popup_open = IsPopupOpen(id); - - const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); - const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(frame_bb, id); - if (!(flags & ImGuiComboFlags_NoPreview)) - window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Max.y), frame_col, style.FrameRounding, ImDrawCornerFlags_Left); - if (!(flags & ImGuiComboFlags_NoArrowButton)) - { - window->DrawList->AddRectFilled(ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button), style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right); - RenderArrow(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down); - } - RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding); - if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) - RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f)); - if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - if ((pressed || g.NavActivateId == id) && !popup_open) - { - if (window->DC.NavLayerCurrent == 0) - window->NavLastIds[0] = id; - OpenPopupEx(id); - popup_open = true; - } - - if (!popup_open) - return false; - - if (backup_next_window_size_constraint) - { - g.NextWindowData.SizeConstraintCond = backup_next_window_size_constraint; - g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); - } - else - { - if ((flags & ImGuiComboFlags_HeightMask_) == 0) - flags |= ImGuiComboFlags_HeightRegular; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one - int popup_max_height_in_items = -1; - if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; - else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; - else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20; - SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); - } - - char name[16]; - ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth - - // Peak into expected window size so we can position it - if (ImGuiWindow* popup_window = FindWindowByName(name)) - if (popup_window->WasActive) - { - ImVec2 size_contents = CalcSizeContents(popup_window); - ImVec2 size_expected = CalcSizeAfterConstraint(popup_window, CalcSizeAutoFit(popup_window, size_contents)); - if (flags & ImGuiComboFlags_PopupAlignLeft) - popup_window->AutoPosLastDirection = ImGuiDir_Left; - ImVec2 pos = FindBestWindowPosForPopup(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, frame_bb, ImGuiPopupPositionPolicy_ComboBox); - SetNextWindowPos(pos); - } - - ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings; - if (!Begin(name, NULL, window_flags)) - { - EndPopup(); - IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above - return false; - } - - // Horizontally align ourselves with the framed text - if (style.FramePadding.x != style.WindowPadding.x) - Indent(style.FramePadding.x - style.WindowPadding.x); - - return true; -} - -void ImGui::EndCombo() -{ - const ImGuiStyle& style = GImGui->Style; - if (style.FramePadding.x != style.WindowPadding.x) - Unindent(style.FramePadding.x - style.WindowPadding.x); - EndPopup(); -} - -// Old API, prefer using BeginCombo() nowadays if you can. -bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items) -{ - ImGuiContext& g = *GImGui; - - const char* preview_text = NULL; - if (*current_item >= 0 && *current_item < items_count) - items_getter(data, *current_item, &preview_text); - - // The old Combo() API exposed "popup_max_height_in_items", however the new more general BeginCombo() API doesn't, so we emulate it here. - if (popup_max_height_in_items != -1 && !g.NextWindowData.SizeConstraintCond) - { - float popup_max_height = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items); - SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, popup_max_height)); - } - - if (!BeginCombo(label, preview_text, 0)) - return false; - - // Display items - // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed) - bool value_changed = false; - for (int i = 0; i < items_count; i++) - { - PushID((void*)(intptr_t)i); - const bool item_selected = (i == *current_item); - const char* item_text; - if (!items_getter(data, i, &item_text)) - item_text = "*Unknown item*"; - if (Selectable(item_text, item_selected)) - { - value_changed = true; - *current_item = i; - } - if (item_selected) - SetItemDefaultFocus(); - PopID(); - } - - EndCombo(); - return value_changed; -} - -static bool Items_ArrayGetter(void* data, int idx, const char** out_text) -{ - const char* const* items = (const char* const*)data; - if (out_text) - *out_text = items[idx]; - return true; -} - -static bool Items_SingleStringGetter(void* data, int idx, const char** out_text) -{ - // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited. - const char* items_separated_by_zeros = (const char*)data; - int items_count = 0; - const char* p = items_separated_by_zeros; - while (*p) - { - if (idx == items_count) - break; - p += strlen(p) + 1; - items_count++; - } - if (!*p) - return false; - if (out_text) - *out_text = p; - return true; -} - -// Combo box helper allowing to pass an array of strings. -bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items) -{ - const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items); - return value_changed; -} - -// Combo box helper allowing to pass all items in a single string. -bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) -{ - int items_count = 0; - const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open - while (*p) - { - p += strlen(p) + 1; - items_count++; - } - bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); - return value_changed; -} - -// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image. -// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID. -bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) // FIXME-OPT: Avoid if vertically clipped. - PopClipRect(); - - ImGuiID id = window->GetID(label); - ImVec2 label_size = CalcTextSize(label, NULL, true); - ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); - ImVec2 pos = window->DC.CursorPos; - pos.y += window->DC.CurrentLineTextBaseOffset; - ImRect bb(pos, pos + size); - ItemSize(bb); - - // Fill horizontal space. - ImVec2 window_padding = window->WindowPadding; - float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x; - float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x); - ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y); - ImRect bb_with_spacing(pos, pos + size_draw); - if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth)) - bb_with_spacing.Max.x += window_padding.x; - - // Selectables are tightly packed together, we extend the box to cover spacing between selectable. - float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f); - float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f); - float spacing_R = style.ItemSpacing.x - spacing_L; - float spacing_D = style.ItemSpacing.y - spacing_U; - bb_with_spacing.Min.x -= spacing_L; - bb_with_spacing.Min.y -= spacing_U; - bb_with_spacing.Max.x += spacing_R; - bb_with_spacing.Max.y += spacing_D; - if (!ItemAdd(bb_with_spacing, (flags & ImGuiSelectableFlags_Disabled) ? 0 : id)) - { - if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) - PushColumnClipRect(); - return false; - } - - ImGuiButtonFlags button_flags = 0; - if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_NoHoldingActiveID; - if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnRelease; - if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled; - if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; - bool hovered, held; - bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags); - if (flags & ImGuiSelectableFlags_Disabled) - selected = false; - - // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets) - if (pressed || hovered)// && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)) - if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerActiveMask) - { - g.NavDisableHighlight = true; - SetNavID(id, window->DC.NavLayerCurrent); - } - - // Render - if (hovered || selected) - { - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f); - RenderNavHighlight(bb_with_spacing, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); - } - - if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) - { - PushColumnClipRect(); - bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x); - } - - if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f)); - if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); - - // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) - CloseCurrentPopup(); - return pressed; -} - -bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) -{ - if (Selectable(label, *p_selected, flags, size_arg)) - { - *p_selected = !*p_selected; - return true; - } - return false; -} - -// Helper to calculate the size of a listbox and display a label on the right. -// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty" -bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - const ImGuiStyle& style = GetStyle(); - const ImGuiID id = GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. - ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y); - ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); - ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); - ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy. - - BeginGroup(); - if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - BeginChildFrame(id, frame_bb.GetSize()); - return true; -} - -bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) -{ - // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. - // However we don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. - // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution. - if (height_in_items < 0) - height_in_items = ImMin(items_count, 7); - float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f); - - // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild(). - ImVec2 size; - size.x = 0.0f; - size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y; - return ListBoxHeader(label, size); -} - -void ImGui::ListBoxFooter() -{ - ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow; - const ImRect bb = parent_window->DC.LastItemRect; - const ImGuiStyle& style = GetStyle(); - - EndChildFrame(); - - // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect) - // We call SameLine() to restore DC.CurrentLine* data - SameLine(); - parent_window->DC.CursorPos = bb.Min; - ItemSize(bb, style.FramePadding.y); - EndGroup(); -} - -bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items) -{ - const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); - return value_changed; -} - -bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) -{ - if (!ListBoxHeader(label, items_count, height_in_items)) - return false; - - // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper. - bool value_changed = false; - ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. - while (clipper.Step()) - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - { - const bool item_selected = (i == *current_item); - const char* item_text; - if (!items_getter(data, i, &item_text)) - item_text = "*Unknown item*"; - - PushID(i); - if (Selectable(item_text, item_selected)) - { - *current_item = i; - value_changed = true; - } - if (item_selected) - SetItemDefaultFocus(); - PopID(); - } - ListBoxFooter(); - return value_changed; -} - -bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - ImGuiStyle& style = g.Style; - ImVec2 pos = window->DC.CursorPos; - ImVec2 label_size = CalcTextSize(label, NULL, true); - - ImGuiSelectableFlags flags = ImGuiSelectableFlags_MenuItem | (enabled ? 0 : ImGuiSelectableFlags_Disabled); - bool pressed; - if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) - { - // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful - // Note that in this situation we render neither the shortcut neither the selected tick mark - float w = label_size.x; - window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); - PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); - pressed = Selectable(label, false, flags, ImVec2(w, 0.0f)); - PopStyleVar(); - window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). - } - else - { - ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f); - float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame - float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); - pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f)); - if (shortcut_size.x > 0.0f) - { - PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); - PopStyleColor(); - } - if (selected) - RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); - } - return pressed; -} - -bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) -{ - if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled)) - { - if (p_selected) - *p_selected = !*p_selected; - return true; - } - return false; -} - -bool ImGui::BeginMainMenuBar() -{ - ImGuiContext& g = *GImGui; - SetNextWindowPos(ImVec2(0.0f, 0.0f)); - SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f)); - PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); - if (!Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_MenuBar) - || !BeginMenuBar()) - { - End(); - PopStyleVar(2); - return false; - } - g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x; - return true; -} - -void ImGui::EndMainMenuBar() -{ - EndMenuBar(); - - // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window - ImGuiContext& g = *GImGui; - if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) - FocusFrontMostActiveWindow(g.NavWindow); - - End(); - PopStyleVar(2); -} - -bool ImGui::BeginMenuBar() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - if (!(window->Flags & ImGuiWindowFlags_MenuBar)) - return false; - - IM_ASSERT(!window->DC.MenuBarAppending); - BeginGroup(); // Save position - PushID("##menubar"); - - // We don't clip with regular window clipping rectangle as it is already set to the area below. However we clip with window full rect. - // We remove 1 worth of rounding to Max.x to that text in long menus don't tend to display over the lower-right rounded area, which looks particularly glitchy. - ImRect bar_rect = window->MenuBarRect(); - ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f)); - clip_rect.ClipWith(window->WindowRectClipped); - PushClipRect(clip_rect.Min, clip_rect.Max, false); - - window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffsetX, bar_rect.Min.y);// + g.Style.FramePadding.y); - window->DC.LayoutType = ImGuiLayoutType_Horizontal; - window->DC.NavLayerCurrent++; - window->DC.NavLayerCurrentMask <<= 1; - window->DC.MenuBarAppending = true; - AlignTextToFramePadding(); - return true; -} - -void ImGui::EndMenuBar() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ImGuiContext& g = *GImGui; - - // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. - if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) - { - ImGuiWindow* nav_earliest_child = g.NavWindow; - while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) - nav_earliest_child = nav_earliest_child->ParentWindow; - if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None) - { - // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. - // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) - IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check - FocusWindow(window); - SetNavIDAndMoveMouse(window->NavLastIds[1], 1, window->NavRectRel[1]); - g.NavLayer = 1; - g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - NavMoveRequestCancel(); - } - } - - IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); - IM_ASSERT(window->DC.MenuBarAppending); - PopClipRect(); - PopID(); - window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x; - window->DC.GroupStack.back().AdvanceCursor = false; - EndGroup(); - window->DC.LayoutType = ImGuiLayoutType_Vertical; - window->DC.NavLayerCurrent--; - window->DC.NavLayerCurrentMask >>= 1; - window->DC.MenuBarAppending = false; -} - -bool ImGui::BeginMenu(const char* label, bool enabled) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - - ImVec2 label_size = CalcTextSize(label, NULL, true); - - bool pressed; - bool menu_is_open = IsPopupOpen(id); - bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].OpenParentId == window->IDStack.back()); - ImGuiWindow* backed_nav_window = g.NavWindow; - if (menuset_is_open) - g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) - - // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestPopupWindowPos). - ImVec2 popup_pos, pos = window->DC.CursorPos; - if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) - { - // Menu inside an horizontal menu bar - // Selectable extend their highlight by half ItemSpacing in each direction. - // For ChildMenu, the popup position will be overwritten by the call to FindBestPopupWindowPos() in Begin() - popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight()); - window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); - PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); - float w = label_size.x; - pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); - PopStyleVar(); - window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). - } - else - { - // Menu inside a menu - popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); - float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame - float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); - pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); - if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderArrow(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right); - if (!enabled) PopStyleColor(); - } - - const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id); - if (menuset_is_open) - g.NavWindow = backed_nav_window; - - bool want_open = false, want_close = false; - if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) - { - // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. - bool moving_within_opened_triangle = false; - if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar)) - { - if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window) - { - ImRect next_window_rect = next_window->Rect(); - ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; - ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); - ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); - float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. - ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues - tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? - tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); - moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); - //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug - } - } - - want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle); - want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed); - - if (g.NavActivateId == id) - { - want_close = menu_is_open; - want_open = !menu_is_open; - } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open - { - want_open = true; - NavMoveRequestCancel(); - } - } - else - { - // Menu bar - if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it - { - want_close = true; - want_open = menu_is_open = false; - } - else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others - { - want_open = true; - } - else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open - { - want_open = true; - NavMoveRequestCancel(); - } - } - - if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' - want_close = true; - if (want_close && IsPopupOpen(id)) - ClosePopupToLevel(g.CurrentPopupStack.Size); - - if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size) - { - // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. - OpenPopup(label); - return false; - } - - menu_is_open |= want_open; - if (want_open) - OpenPopup(label); - - if (menu_is_open) - { - SetNextWindowPos(popup_pos, ImGuiCond_Always); - ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu); - menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) - } - - return menu_is_open; -} - -void ImGui::EndMenu() -{ - // Nav: When a left move request _within our child menu_ failed, close the menu. - // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs. - // However it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) - { - ClosePopupToLevel(g.OpenPopupStack.Size - 1); - NavMoveRequestCancel(); - } - - EndPopup(); -} - -// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) -{ - ImGuiContext& g = *GImGui; - - int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - BeginTooltipEx(0, true); - - const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; - if (text_end > text) - { - TextUnformatted(text, text_end); - Separator(); - } - - ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); - ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); - SameLine(); - if (flags & ImGuiColorEditFlags_NoAlpha) - Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); - else - Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); - EndTooltip(); -} - -static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b) -{ - float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; - int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); - int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); - int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); - return IM_COL32(r, g, b, 0xFF); -} - -// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. -// I spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding alltogether. -void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) - { - ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col)); - ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col)); - window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags); - - int yi = 0; - for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++) - { - float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y); - if (y2 <= y1) - continue; - for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) - { - float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); - if (x2 <= x1) - continue; - int rounding_corners_flags_cell = 0; - if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; } - if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; } - rounding_corners_flags_cell &= rounding_corners_flags; - window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell); - } - } - } - else - { - window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags); - } -} - -void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) -{ - ImGuiContext& g = *GImGui; - if ((flags & ImGuiColorEditFlags__InputsMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask; - if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask; - if ((flags & ImGuiColorEditFlags__PickerMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask))); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check only 1 option is selected - g.ColorEditOptions = flags; -} - -// A little colored square. Return true when clicked. -// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. -// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. -bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiID id = window->GetID(desc_id); - float default_size = GetFrameHeight(); - if (size.x == 0.0f) - size.x = default_size; - if (size.y == 0.0f) - size.y = default_size; - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - if (flags & ImGuiColorEditFlags_NoAlpha) - flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); - - ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f); - float grid_step = ImMin(size.x, size.y) / 2.99f; - float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f); - ImRect bb_inner = bb; - float off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts. - bb_inner.Expand(off); - if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f) - { - float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f); - RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight); - window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft); - } - else - { - // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha - ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha; - if (col_source.w < 1.0f) - RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); - else - window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All); - } - RenderNavHighlight(bb, id); - if (g.Style.FrameBorderSize > 0.0f) - RenderFrameBorder(bb.Min, bb.Max, rounding); - else - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border - - // Drag and Drop Source - if (g.ActiveId == id && BeginDragDropSource()) // NB: The ActiveId test is merely an optional micro-optimization - { - if (flags & ImGuiColorEditFlags_NoAlpha) - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col, sizeof(float) * 3, ImGuiCond_Once); - else - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once); - ColorButton(desc_id, col, flags); - SameLine(); - TextUnformatted("Color"); - EndDragDropSource(); - hovered = false; - } - - // Tooltip - if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) - ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); - - return pressed; -} - -bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags) -{ - return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); -} - -void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) -{ - bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask); - bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); - if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) - return; - ImGuiContext& g = *GImGui; - ImGuiColorEditFlags opts = g.ColorEditOptions; - if (allow_opt_inputs) - { - if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB; - if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV; - if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX; - } - if (allow_opt_datatype) - { - if (allow_opt_inputs) Separator(); - if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8; - if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float; - } - - if (allow_opt_inputs || allow_opt_datatype) - Separator(); - if (Button("Copy as..", ImVec2(-1,0))) - OpenPopup("Copy"); - if (BeginPopup("Copy")) - { - int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - char buf[64]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); - if (Selectable(buf)) - SetClipboardText(buf); - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); - if (Selectable(buf)) - SetClipboardText(buf); - if (flags & ImGuiColorEditFlags_NoAlpha) - ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb); - else - ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca); - if (Selectable(buf)) - SetClipboardText(buf); - EndPopup(); - } - - g.ColorEditOptions = opts; - EndPopup(); -} - -static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, const float* ref_col) -{ - bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask); - bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); - if ((!allow_opt_picker && !allow_opt_alpha_bar) || !ImGui::BeginPopup("context")) - return; - ImGuiContext& g = *GImGui; - if (allow_opt_picker) - { - ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function - ImGui::PushItemWidth(picker_size.x); - for (int picker_type = 0; picker_type < 2; picker_type++) - { - // Draw small/thumbnail version of each picker type (over an invisible button for selection) - if (picker_type > 0) ImGui::Separator(); - ImGui::PushID(picker_type); - ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha); - if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar; - if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; - ImVec2 backup_pos = ImGui::GetCursorScreenPos(); - if (ImGui::Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup - g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); - ImGui::SetCursorScreenPos(backup_pos); - ImVec4 dummy_ref_col; - memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4)); - ImGui::ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags); - ImGui::PopID(); - } - ImGui::PopItemWidth(); - } - if (allow_opt_alpha_bar) - { - if (allow_opt_picker) ImGui::Separator(); - ImGui::CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); - } - ImGui::EndPopup(); -} - -// Edit colors components (each component in 0.0f..1.0f range). -// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -// With typical options: Left-click on colored square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. -bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float square_sz = GetFrameHeight(); - const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); - const float w_items_all = CalcItemWidth() - w_extra; - const char* label_display_end = FindRenderedTextEnd(label); - - const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; - const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; - const int components = alpha ? 4 : 3; - const ImGuiColorEditFlags flags_untouched = flags; - - BeginGroup(); - PushID(label); - - // If we're not showing any slider there's no point in doing any HSV conversions - if (flags & ImGuiColorEditFlags_NoInputs) - flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions; - - // Context menu: display and modify options (before defaults are applied) - if (!(flags & ImGuiColorEditFlags_NoOptions)) - ColorEditOptionsPopup(col, flags); - - // Read stored options - if (!(flags & ImGuiColorEditFlags__InputsMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask); - if (!(flags & ImGuiColorEditFlags__DataTypeMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask); - if (!(flags & ImGuiColorEditFlags__PickerMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask); - flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask)); - - // Convert to the formats we need - float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; - if (flags & ImGuiColorEditFlags_HSV) - ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); - int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; - - bool value_changed = false; - bool value_changed_as_float = false; - - if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) - { - // RGB/HSV 0..255 Sliders - const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); - const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); - - const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); - const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; - const char* fmt_table_int[3][4] = - { - { "%3.0f", "%3.0f", "%3.0f", "%3.0f" }, // Short display - { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" }, // Long display for RGBA - { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" } // Long display for HSVA - }; - const char* fmt_table_float[3][4] = - { - { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display - { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA - { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA - }; - const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1; - - PushItemWidth(w_item_one); - for (int n = 0; n < components; n++) - { - if (n > 0) - SameLine(0, style.ItemInnerSpacing.x); - if (n + 1 == components) - PushItemWidth(w_item_last); - if (flags & ImGuiColorEditFlags_Float) - value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); - else - value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - } - PopItemWidth(); - PopItemWidth(); - } - else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) - { - // RGB Hexadecimal Input - char buf[64]; - if (alpha) - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255), ImClamp(i[3],0,255)); - else - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255)); - PushItemWidth(w_items_all); - if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) - { - value_changed = true; - char* p = buf; - while (*p == '#' || ImCharIsSpace(*p)) - p++; - i[0] = i[1] = i[2] = i[3] = 0; - if (alpha) - sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) - else - sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - PopItemWidth(); - } - - ImGuiWindow* picker_active_window = NULL; - if (!(flags & ImGuiColorEditFlags_NoSmallPreview)) - { - if (!(flags & ImGuiColorEditFlags_NoInputs)) - SameLine(0, style.ItemInnerSpacing.x); - - const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f); - if (ColorButton("##ColorButton", col_v4, flags)) - { - if (!(flags & ImGuiColorEditFlags_NoPicker)) - { - // Store current color and open a picker - g.ColorPickerRef = col_v4; - OpenPopup("picker"); - SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y)); - } - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - - if (BeginPopup("picker")) - { - picker_active_window = g.CurrentWindow; - if (label != label_display_end) - { - TextUnformatted(label, label_display_end); - Separator(); - } - ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; - ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; - PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? - value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); - PopItemWidth(); - EndPopup(); - } - } - - if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) - { - SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); - } - - // Convert back - if (picker_active_window == NULL) - { - if (!value_changed_as_float) - for (int n = 0; n < 4; n++) - f[n] = i[n] / 255.0f; - if (flags & ImGuiColorEditFlags_HSV) - ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); - if (value_changed) - { - col[0] = f[0]; - col[1] = f[1]; - col[2] = f[2]; - if (alpha) - col[3] = f[3]; - } - } - - PopID(); - EndGroup(); - - // Drag and Drop Target - if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && BeginDragDropTarget()) // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. - { - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) - { - memcpy((float*)col, payload->Data, sizeof(float) * 3); - value_changed = true; - } - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) - { - memcpy((float*)col, payload->Data, sizeof(float) * components); - value_changed = true; - } - EndDragDropTarget(); - } - - // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). - if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) - window->DC.LastItemId = g.ActiveId; - - return value_changed; -} - -bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags) -{ - float col4[4] = { col[0], col[1], col[2], 1.0f }; - if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha)) - return false; - col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2]; - return true; -} - -// 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side. -static void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col) -{ - switch (direction) - { - case ImGuiDir_Left: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return; - case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return; - case ImGuiDir_Up: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return; - case ImGuiDir_Down: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return; - case ImGuiDir_None: case ImGuiDir_COUNT: break; // Fix warnings - } -} - -static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w) -{ - RenderArrow(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32_BLACK); - RenderArrow(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32_WHITE); - RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32_BLACK); - RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32_WHITE); -} - -// ColorPicker -// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) -bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - ImDrawList* draw_list = window->DrawList; - - ImGuiStyle& style = g.Style; - ImGuiIO& io = g.IO; - - PushID(label); - BeginGroup(); - - if (!(flags & ImGuiColorEditFlags_NoSidePreview)) - flags |= ImGuiColorEditFlags_NoSmallPreview; - - // Context menu: display and store options. - if (!(flags & ImGuiColorEditFlags_NoOptions)) - ColorPickerOptionsPopup(flags, col); - - // Read stored options - if (!(flags & ImGuiColorEditFlags__PickerMask)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected - if (!(flags & ImGuiColorEditFlags_NoOptions)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); - - // Setup - int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4; - bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha); - ImVec2 picker_pos = window->DC.CursorPos; - float square_sz = GetFrameHeight(); - float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars - float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box - float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x; - float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x; - float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f); - - float backup_initial_col[4]; - memcpy(backup_initial_col, col, components * sizeof(float)); - - float wheel_thickness = sv_picker_size * 0.08f; - float wheel_r_outer = sv_picker_size * 0.50f; - float wheel_r_inner = wheel_r_outer - wheel_thickness; - ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f); - - // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic. - float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f); - ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point. - ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. - ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. - - float H,S,V; - ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V); - - bool value_changed = false, value_changed_h = false, value_changed_sv = false; - - PushItemFlag(ImGuiItemFlags_NoNav, true); - if (flags & ImGuiColorEditFlags_PickerHueWheel) - { - // Hue wheel + SV triangle logic - InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size)); - if (IsItemActive()) - { - ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center; - ImVec2 current_off = g.IO.MousePos - wheel_center; - float initial_dist2 = ImLengthSqr(initial_off); - if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1)) - { - // Interactive with Hue wheel - H = atan2f(current_off.y, current_off.x) / IM_PI*0.5f; - if (H < 0.0f) - H += 1.0f; - value_changed = value_changed_h = true; - } - float cos_hue_angle = cosf(-H * 2.0f * IM_PI); - float sin_hue_angle = sinf(-H * 2.0f * IM_PI); - if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle))) - { - // Interacting with SV triangle - ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle); - if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)) - current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated); - float uu, vv, ww; - ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww); - V = ImClamp(1.0f - vv, 0.0001f, 1.0f); - S = ImClamp(uu / V, 0.0001f, 1.0f); - value_changed = value_changed_sv = true; - } - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - } - else if (flags & ImGuiColorEditFlags_PickerHueBar) - { - // SV rectangle logic - InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size)); - if (IsItemActive()) - { - S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1)); - V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); - value_changed = value_changed_sv = true; - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - - // Hue bar logic - SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); - InvisibleButton("hue", ImVec2(bars_width, sv_picker_size)); - if (IsItemActive()) - { - H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); - value_changed = value_changed_h = true; - } - } - - // Alpha bar logic - if (alpha_bar) - { - SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); - InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); - if (IsItemActive()) - { - col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); - value_changed = true; - } - } - PopItemFlag(); // ImGuiItemFlags_NoNav - - if (!(flags & ImGuiColorEditFlags_NoSidePreview)) - { - SameLine(0, style.ItemInnerSpacing.x); - BeginGroup(); - } - - if (!(flags & ImGuiColorEditFlags_NoLabel)) - { - const char* label_display_end = FindRenderedTextEnd(label); - if (label != label_display_end) - { - if ((flags & ImGuiColorEditFlags_NoSidePreview)) - SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); - } - } - - if (!(flags & ImGuiColorEditFlags_NoSidePreview)) - { - PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); - ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); - if ((flags & ImGuiColorEditFlags_NoLabel)) - Text("Current"); - ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)); - if (ref_col != NULL) - { - Text("Original"); - ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]); - if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2))) - { - memcpy(col, ref_col, components * sizeof(float)); - value_changed = true; - } - } - PopItemFlag(); - EndGroup(); - } - - // Convert back color to RGB - if (value_changed_h || value_changed_sv) - ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); - - // R,G,B and H,S,V slider color editor - if ((flags & ImGuiColorEditFlags_NoInputs) == 0) - { - PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; - ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; - if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0) - value_changed |= ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB); - if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0) - value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV); - if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0) - value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX); - PopItemWidth(); - } - - // Try to cancel hue wrap (after ColorEdit), if any - if (value_changed) - { - float new_H, new_S, new_V; - ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); - if (new_H <= 0 && H > 0) - { - if (new_V <= 0 && V != new_V) - ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]); - else if (new_S <= 0) - ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]); - } - } - - ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); - ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); - ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f)); - - const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) }; - ImVec2 sv_cursor_pos; - - if (flags & ImGuiColorEditFlags_PickerHueWheel) - { - // Render Hue Wheel - const float aeps = 1.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out). - const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12); - for (int n = 0; n < 6; n++) - { - const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps; - const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps; - const int vert_start_idx = draw_list->VtxBuffer.Size; - draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); - draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness); - const int vert_end_idx = draw_list->VtxBuffer.Size; - - // Paint colors over existing vertices - ImVec2 gradient_p0(wheel_center.x + cosf(a0) * wheel_r_inner, wheel_center.y + sinf(a0) * wheel_r_inner); - ImVec2 gradient_p1(wheel_center.x + cosf(a1) * wheel_r_inner, wheel_center.y + sinf(a1) * wheel_r_inner); - ShadeVertsLinearColorGradientKeepAlpha(draw_list->VtxBuffer.Data + vert_start_idx, draw_list->VtxBuffer.Data + vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]); - } - - // Render Cursor + preview on Hue Wheel - float cos_hue_angle = cosf(H * 2.0f * IM_PI); - float sin_hue_angle = sinf(H * 2.0f * IM_PI); - ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f); - float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; - int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32); - draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); - draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, IM_COL32(128,128,128,255), hue_cursor_segments); - draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments); - - // Render SV triangle (rotated according to hue) - ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle); - ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); - ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); - ImVec2 uv_white = GetFontTexUvWhitePixel(); - draw_list->PrimReserve(6, 6); - draw_list->PrimVtx(tra, uv_white, hue_color32); - draw_list->PrimVtx(trb, uv_white, hue_color32); - draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE); - draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS); - draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK); - draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS); - draw_list->AddTriangle(tra, trb, trc, IM_COL32(128,128,128,255), 1.5f); - sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); - } - else if (flags & ImGuiColorEditFlags_PickerHueBar) - { - // Render SV Square - draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE); - draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32_BLACK, IM_COL32_BLACK); - RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), 0.0f); - sv_cursor_pos.x = ImClamp((float)(int)(picker_pos.x + ImSaturate(S) * sv_picker_size + 0.5f), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much - sv_cursor_pos.y = ImClamp((float)(int)(picker_pos.y + ImSaturate(1 - V) * sv_picker_size + 0.5f), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2); - - // Render Hue Bar - for (int i = 0; i < 6; ++i) - draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), hue_colors[i], hue_colors[i], hue_colors[i + 1], hue_colors[i + 1]); - float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f); - RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f); - RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); - } - - // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) - float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f; - draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12); - draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, IM_COL32(128,128,128,255), 12); - draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12); - - // Render alpha bar - if (alpha_bar) - { - float alpha = ImSaturate(col[3]); - ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size); - RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0,0,0,0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f)); - draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, col32_no_alpha, col32_no_alpha, col32_no_alpha & ~IM_COL32_A_MASK, col32_no_alpha & ~IM_COL32_A_MASK); - float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f); - RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f); - RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); - } - - EndGroup(); - PopID(); - - return value_changed && memcmp(backup_initial_col, col, components * sizeof(float)); -} - -// Horizontal separating line. -void ImGui::Separator() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ImGuiContext& g = *GImGui; - - ImGuiWindowFlags flags = 0; - if ((flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)) == 0) - flags |= (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)))); // Check that only 1 option is selected - if (flags & ImGuiSeparatorFlags_Vertical) - { - VerticalSeparator(); - return; - } - - // Horizontal Separator - if (window->DC.ColumnsSet) - PopClipRect(); - - float x1 = window->Pos.x; - float x2 = window->Pos.x + window->Size.x; - if (!window->DC.GroupStack.empty()) - x1 += window->DC.IndentX; - - const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f)); - ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout. - if (!ItemAdd(bb, 0)) - { - if (window->DC.ColumnsSet) - PushColumnClipRect(); - return; - } - - window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator)); - - if (g.LogEnabled) - LogRenderedText(NULL, IM_NEWLINE "--------------------------------"); - - if (window->DC.ColumnsSet) - { - PushColumnClipRect(); - window->DC.ColumnsSet->CellMinY = window->DC.CursorPos.y; - } -} - -void ImGui::VerticalSeparator() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ImGuiContext& g = *GImGui; - - float y1 = window->DC.CursorPos.y; - float y2 = window->DC.CursorPos.y + window->DC.CurrentLineHeight; - const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2)); - ItemSize(ImVec2(bb.GetWidth(), 0.0f)); - if (!ItemAdd(bb, 0)) - return; - - window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator)); - if (g.LogEnabled) - LogText(" |"); -} - -bool ImGui::SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; - bool item_add = ItemAdd(bb, id); - window->DC.ItemFlags = item_flags_backup; - if (!item_add) - return false; - - bool hovered, held; - ImRect bb_interact = bb; - bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); - ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); - if (g.ActiveId != id) - SetItemAllowOverlap(); - - if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id)) - SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); - - ImRect bb_render = bb; - if (held) - { - ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min; - float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x; - - // Minimum pane size - if (mouse_delta < min_size1 - *size1) - mouse_delta = min_size1 - *size1; - if (mouse_delta > *size2 - min_size2) - mouse_delta = *size2 - min_size2; - - // Apply resize - *size1 += mouse_delta; - *size2 -= mouse_delta; - bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta)); - } - - // Render - const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); - window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, g.Style.FrameRounding); - - return held; -} - -void ImGui::Spacing() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ItemSize(ImVec2(0,0)); -} - -void ImGui::Dummy(const ImVec2& size) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(bb); - ItemAdd(bb, 0); + ImGuiWindow* window = GImGui->CurrentWindow; + return window->GetID(ptr_id); } bool ImGui::IsRectVisible(const ImVec2& size) { - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); } bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) { - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); } // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) void ImGui::BeginGroup() { + ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1); ImGuiGroupData& group_data = window->DC.GroupStack.back(); group_data.BackupCursorPos = window->DC.CursorPos; group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; - group_data.BackupIndentX = window->DC.IndentX; - group_data.BackupGroupOffsetX = window->DC.GroupOffsetX; - group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight; - group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset; - group_data.BackupLogLinePosY = window->DC.LogLinePosY; - group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive; - group_data.AdvanceCursor = true; + group_data.BackupIndent = window->DC.Indent; + group_data.BackupGroupOffset = window->DC.GroupOffset; + group_data.BackupCurrLineSize = window->DC.CurrLineSize; + group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset; + group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; + group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive; + group_data.EmitItem = true; - window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX; - window->DC.IndentX = window->DC.GroupOffsetX; + window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x; + window->DC.Indent = window->DC.GroupOffset; window->DC.CursorMaxPos = window->DC.CursorPos; - window->DC.CurrentLineHeight = 0.0f; - window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; + window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); + if (g.LogEnabled) + g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return } void ImGui::EndGroup() { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - - IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls + IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls ImGuiGroupData& group_data = window->DC.GroupStack.back(); - ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos); - group_bb.Max = ImMax(group_bb.Min, group_bb.Max); + ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos)); window->DC.CursorPos = group_data.BackupCursorPos; window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos); - window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight; - window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset; - window->DC.IndentX = group_data.BackupIndentX; - window->DC.GroupOffsetX = group_data.BackupGroupOffsetX; - window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; + window->DC.Indent = group_data.BackupIndent; + window->DC.GroupOffset = group_data.BackupGroupOffset; + window->DC.CurrLineSize = group_data.BackupCurrLineSize; + window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset; + if (g.LogEnabled) + g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return - if (group_data.AdvanceCursor) + if (!group_data.EmitItem) { - window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. - ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset); - ItemAdd(group_bb, 0); + window->DC.GroupStack.pop_back(); + return; } - // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will be functional on the entire group. - // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but if you search for LastItemId you'll notice it is only used in that context. - const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow); - if (active_id_within_group) + window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. + ItemSize(group_bb.GetSize(), 0.0f); + ItemAdd(group_bb, 0); + + // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group. + // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets. + // Also if you grep for LastItemId you'll notice it is only used in that context. + // (The tests not symmetrical because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.) + const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId; + const bool group_contains_prev_active_id = !group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive; + if (group_contains_curr_active_id) window->DC.LastItemId = g.ActiveId; + else if (group_contains_prev_active_id) + window->DC.LastItemId = g.ActiveIdPreviousFrame; window->DC.LastItemRect = group_bb; - window->DC.GroupStack.pop_back(); + // Forward Edited flag + if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame) + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; + // Forward Deactivated flag + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated; + if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame) + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated; + + window->DC.GroupStack.pop_back(); //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] } // Gets back to previous line and continue with horizontal layout -// pos_x == 0 : follow right after previous item -// pos_x != 0 : align to specified x position (relative to window/group left) -// spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0 -// spacing_w >= 0 : enforce spacing amount -void ImGui::SameLine(float pos_x, float spacing_w) +// offset_from_start_x == 0 : follow right after previous item +// offset_from_start_x != 0 : align to specified x position (relative to window/group left) +// spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0 +// spacing_w >= 0 : enforce spacing amount +void ImGui::SameLine(float offset_from_start_x, float spacing_w) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; ImGuiContext& g = *GImGui; - if (pos_x != 0.0f) + if (offset_from_start_x != 0.0f) { if (spacing_w < 0.0f) spacing_w = 0.0f; - window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffsetX + window->DC.ColumnsOffsetX; + window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x; window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; } else @@ -12289,85 +7115,1622 @@ void ImGui::SameLine(float pos_x, float spacing_w) window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w; window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; } - window->DC.CurrentLineHeight = window->DC.PrevLineHeight; - window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; + window->DC.CurrLineSize = window->DC.PrevLineSize; + window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; } -void ImGui::NewLine() +void ImGui::Indent(float indent_w) { + ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); + window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; + window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; +} + +void ImGui::Unindent(float indent_w) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; + window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; +} + +//----------------------------------------------------------------------------- +// [SECTION] TOOLTIPS +//----------------------------------------------------------------------------- + +void ImGui::BeginTooltip() +{ + ImGuiContext& g = *GImGui; + if (g.DragDropWithinSourceOrTarget) + { + // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor) + // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. + // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do. + //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; + ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); + SetNextWindowPos(tooltip_pos); + SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); + //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( + BeginTooltipEx(0, true); + } + else + { + BeginTooltipEx(0, false); + } +} + +// Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first. +void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip) +{ + ImGuiContext& g = *GImGui; + char window_name[16]; + ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); + if (override_previous_tooltip) + if (ImGuiWindow* window = FindWindowByName(window_name)) + if (window->Active) + { + // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. + window->Hidden = true; + window->HiddenFramesCanSkipItems = 1; + ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); + } + ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize; + Begin(window_name, NULL, flags | extra_flags); +} + +void ImGui::EndTooltip() +{ + IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls + End(); +} + +void ImGui::SetTooltipV(const char* fmt, va_list args) +{ + ImGuiContext& g = *GImGui; + if (g.DragDropWithinSourceOrTarget) + BeginTooltip(); + else + BeginTooltipEx(0, true); + TextV(fmt, args); + EndTooltip(); +} + +void ImGui::SetTooltip(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + SetTooltipV(fmt, args); + va_end(args); +} + +//----------------------------------------------------------------------------- +// [SECTION] POPUPS +//----------------------------------------------------------------------------- + +bool ImGui::IsPopupOpen(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id; +} + +bool ImGui::IsPopupOpen(const char* str_id) +{ + ImGuiContext& g = *GImGui; + return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id); +} + +ImGuiWindow* ImGui::GetTopMostPopupModal() +{ + ImGuiContext& g = *GImGui; + for (int n = g.OpenPopupStack.Size-1; n >= 0; n--) + if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) + if (popup->Flags & ImGuiWindowFlags_Modal) + return popup; + return NULL; +} + +void ImGui::OpenPopup(const char* str_id) +{ + ImGuiContext& g = *GImGui; + OpenPopupEx(g.CurrentWindow->GetID(str_id)); +} + +// Mark popup as open (toggle toward open state). +// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. +// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). +// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) +void ImGui::OpenPopupEx(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* parent_window = g.CurrentWindow; + int current_stack_size = g.BeginPopupStack.Size; + ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. + popup_ref.PopupId = id; + popup_ref.Window = NULL; + popup_ref.SourceWindow = g.NavWindow; + popup_ref.OpenFrameCount = g.FrameCount; + popup_ref.OpenParentId = parent_window->IDStack.back(); + popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); + popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; + + //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id); + if (g.OpenPopupStack.Size < current_stack_size + 1) + { + g.OpenPopupStack.push_back(popup_ref); + } + else + { + // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui + // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing + // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand. + if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) + { + g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; + } + else + { + // Close child popups if any, then flag popup for open/reopen + g.OpenPopupStack.resize(current_stack_size + 1); + g.OpenPopupStack[current_stack_size] = popup_ref; + } + + // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). + // This is equivalent to what ClosePopupToLevel() does. + //if (g.OpenPopupStack[current_stack_size].PopupId == id) + // FocusWindow(parent_window); + } +} + +bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button) +{ + ImGuiWindow* window = GImGui->CurrentWindow; + if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + { + ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! + IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) + OpenPopupEx(id); + return true; + } + return false; +} + +void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup) +{ + ImGuiContext& g = *GImGui; + if (g.OpenPopupStack.empty()) + return; + + // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. + // Don't close our own child popup windows. + int popup_count_to_keep = 0; + if (ref_window) + { + // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow) + for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++) + { + ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep]; + if (!popup.Window) + continue; + IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); + if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) + continue; + + // Trim the stack when popups are not direct descendant of the reference window (the reference window is often the NavWindow) + bool popup_or_descendent_is_ref_window = false; + for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_is_ref_window; m++) + if (ImGuiWindow* popup_window = g.OpenPopupStack[m].Window) + if (popup_window->RootWindow == ref_window->RootWindow) + popup_or_descendent_is_ref_window = true; + if (!popup_or_descendent_is_ref_window) + break; + } + } + if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below + { + //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); + ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup); + } +} + +void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); + ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow; + ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window; + g.OpenPopupStack.resize(remaining); + + if (restore_focus_to_window_under_popup) + { + if (focus_window && !focus_window->WasActive && popup_window) + { + // Fallback + FocusTopMostWindowUnderOne(popup_window, NULL); + } + else + { + if (g.NavLayer == 0 && focus_window) + focus_window = NavRestoreLastChildNavWindow(focus_window); + FocusWindow(focus_window); + } + } +} + +// Close the popup we have begin-ed into. +void ImGui::CloseCurrentPopup() +{ + ImGuiContext& g = *GImGui; + int popup_idx = g.BeginPopupStack.Size - 1; + if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) + return; + + // Closing a menu closes its top-most parent popup (unless a modal) + while (popup_idx > 0) + { + ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window; + ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window; + bool close_parent = false; + if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu)) + if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal)) + close_parent = true; + if (!close_parent) + break; + popup_idx--; + } + //IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx); + ClosePopupToLevel(popup_idx, true); + + // A common pattern is to close a popup when selecting a menu item/selectable that will open another window. + // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window. + // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic. + if (ImGuiWindow* window = g.NavWindow) + window->DC.NavHideHighlightOneFrame = true; +} + +bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) +{ + ImGuiContext& g = *GImGui; + if (!IsPopupOpen(id)) + { + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values + return false; + } + + char name[20]; + if (extra_flags & ImGuiWindowFlags_ChildMenu) + ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth + else + ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame + + bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup); + if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) + EndPopup(); + + return is_open; +} + +bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) +{ + ImGuiContext& g = *GImGui; + if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance + { + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values + return false; + } + flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings; + return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags); +} + +// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup. +// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here. +bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const ImGuiID id = window->GetID(name); + if (!IsPopupOpen(id)) + { + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values + return false; + } + + // Center modal windows by default + // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. + if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) + SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + + flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings; + const bool is_open = Begin(name, p_open, flags); + if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + { + EndPopup(); + if (is_open) + ClosePopupToLevel(g.BeginPopupStack.Size, true); + return false; + } + return is_open; +} + +void ImGui::EndPopup() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls + IM_ASSERT(g.BeginPopupStack.Size > 0); + + // Make all menus and popups wrap around for now, may need to expose that policy. + NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY); + + End(); +} + +// This is a helper to handle the simplest case of associating one named popup to one given widget. +// You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). +// You can pass a NULL str_id to use the identifier of the last item. +bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) +{ + ImGuiWindow* window = GImGui->CurrentWindow; if (window->SkipItems) + return false; + ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! + IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) + if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + OpenPopupEx(id); + return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); +} + +bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items) +{ + if (!str_id) + str_id = "window_context"; + ImGuiID id = GImGui->CurrentWindow->GetID(str_id); + if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + if (also_over_items || !IsAnyItemHovered()) + OpenPopupEx(id); + return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); +} + +bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) +{ + if (!str_id) + str_id = "void_context"; + ImGuiID id = GImGui->CurrentWindow->GetID(str_id); + if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) + OpenPopupEx(id); + return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); +} + +// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) +// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. +ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy) +{ + ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); + //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); + //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); + + // Combo Box policy (we want a connecting edge) + if (policy == ImGuiPopupPositionPolicy_ComboBox) + { + const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; + for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) + { + const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; + if (n != -1 && dir == *last_dir) // Already tried this direction? + continue; + ImVec2 pos; + if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default) + if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right + if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left + if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left + if (!r_outer.Contains(ImRect(pos, pos + size))) + continue; + *last_dir = dir; + return pos; + } + } + + // Default popup policy + const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; + for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) + { + const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; + if (n != -1 && dir == *last_dir) // Already tried this direction? + continue; + float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x); + float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y); + if (avail_w < size.x || avail_h < size.y) + continue; + ImVec2 pos; + pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x; + pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y; + *last_dir = dir; + return pos; + } + + // Fallback, try to keep within display + *last_dir = ImGuiDir_None; + ImVec2 pos = ref_pos; + pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); + pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); + return pos; +} + +ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) +{ + IM_UNUSED(window); + ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding; + ImRect r_screen = GetViewportRect(); + r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); + return r_screen; +} + +ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + + ImRect r_outer = GetWindowAllowedExtentRect(window); + if (window->Flags & ImGuiWindowFlags_ChildMenu) + { + // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds. + // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. + IM_ASSERT(g.CurrentWindow == window); + ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; + float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). + ImRect r_avoid; + if (parent_window->DC.MenuBarAppending) + r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); + else + r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX); + return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); + } + if (window->Flags & ImGuiWindowFlags_Popup) + { + ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1); + return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); + } + if (window->Flags & ImGuiWindowFlags_Tooltip) + { + // Position tooltip (always follows mouse) + float sc = g.Style.MouseCursorScale; + ImVec2 ref_pos = NavCalcPreferredRefPos(); + ImRect r_avoid; + if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) + r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); + else + r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. + ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); + if (window->AutoPosLastDirection == ImGuiDir_None) + pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. + return pos; + } + IM_ASSERT(0); + return window->Pos; +} + + +//----------------------------------------------------------------------------- +// [SECTION] KEYBOARD/GAMEPAD NAVIGATION +//----------------------------------------------------------------------------- + +ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy) +{ + if (ImFabs(dx) > ImFabs(dy)) + return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; + return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; +} + +static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1) +{ + if (a1 < b0) + return a1 - b0; + if (b1 < a0) + return a0 - b1; + return 0.0f; +} + +static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect) +{ + if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) + { + r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); + r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); + } + else + { + r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); + r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); + } +} + +// Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057 +static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (g.NavLayer != window->DC.NavLayerCurrent) + return false; + + const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) + g.NavScoringCount++; + + // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring + if (window->ParentWindow == g.NavWindow) + { + IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened); + if (!window->ClipRect.Contains(cand)) + return false; + cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window + } + + // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items) + // For example, this ensure that items in one column are not reached when moving vertically from items in another column. + NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect); + + // Compute distance between boxes + // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. + float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); + float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items + if (dby != 0.0f && dbx != 0.0f) + dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); + float dist_box = ImFabs(dbx) + ImFabs(dby); + + // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter) + float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x); + float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y); + float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee) + + // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance + ImGuiDir quadrant; + float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; + if (dbx != 0.0f || dby != 0.0f) + { + // For non-overlapping boxes, use distance between boxes + dax = dbx; + day = dby; + dist_axial = dist_box; + quadrant = ImGetDirQuadrantFromDelta(dbx, dby); + } + else if (dcx != 0.0f || dcy != 0.0f) + { + // For overlapping boxes with different centers, use distance between centers + dax = dcx; + day = dcy; + dist_axial = dist_center; + quadrant = ImGetDirQuadrantFromDelta(dcx, dcy); + } + else + { + // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter) + quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; + } + +#if IMGUI_DEBUG_NAV_SCORING + char buf[128]; + if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) + { + ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); + ImDrawList* draw_list = ImGui::GetForegroundDrawList(window); + draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); + draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); + draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); + draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); + } + else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. + { + if (ImGui::IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } + if (quadrant == g.NavMoveDir) + { + ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); + ImDrawList* draw_list = ImGui::GetForegroundDrawList(window); + draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); + draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); + } + } + #endif + + // Is it in the quadrant we're interesting in moving to? + bool new_best = false; + if (quadrant == g.NavMoveDir) + { + // Does it beat the current best candidate? + if (dist_box < result->DistBox) + { + result->DistBox = dist_box; + result->DistCenter = dist_center; + return true; + } + if (dist_box == result->DistBox) + { + // Try using distance between center points to break ties + if (dist_center < result->DistCenter) + { + result->DistCenter = dist_center; + new_best = true; + } + else if (dist_center == result->DistCenter) + { + // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items + // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), + // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. + if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance + new_best = true; + } + } + } + + // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches + // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) + // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. + // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward. + // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? + if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match + if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) + if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) + { + result->DistAxial = dist_axial; + new_best = true; + } + + return new_best; +} + +// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) +static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) +{ + ImGuiContext& g = *GImGui; + //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. + // return; + + const ImGuiItemFlags item_flags = window->DC.ItemFlags; + const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + + // Process Init Request + if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) + { + // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback + if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) + { + g.NavInitResultId = id; + g.NavInitResultRectRel = nav_bb_rel; + } + if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) + { + g.NavInitRequest = false; // Found a match, clear request + NavUpdateAnyRequestFlag(); + } + } + + // Process Move Request (scoring for navigation) + // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) + if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled|ImGuiItemFlags_NoNav))) + { + ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; +#if IMGUI_DEBUG_NAV_SCORING + // [DEBUG] Score all items in NavWindow at all times + if (!g.NavMoveRequest) + g.NavMoveDir = g.NavMoveDirLast; + bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; +#else + bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); +#endif + if (new_best) + { + result->ID = id; + result->SelectScopeId = g.MultiSelectScopeId; + result->Window = window; + result->RectRel = nav_bb_rel; + } + + const float VISIBLE_RATIO = 0.70f; + if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) + if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) + if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb)) + { + result = &g.NavMoveResultLocalVisibleSet; + result->ID = id; + result->SelectScopeId = g.MultiSelectScopeId; + result->Window = window; + result->RectRel = nav_bb_rel; + } + } + + // Update window-relative bounding box of navigated item + if (g.NavId == id) + { + g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. + g.NavLayer = window->DC.NavLayerCurrent; + g.NavIdIsAlive = true; + g.NavIdTabCounter = window->DC.FocusCounterTab; + window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) + } +} + +bool ImGui::NavMoveRequestButNoResultYet() +{ + ImGuiContext& g = *GImGui; + return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; +} + +void ImGui::NavMoveRequestCancel() +{ + ImGuiContext& g = *GImGui; + g.NavMoveRequest = false; + NavUpdateAnyRequestFlag(); +} + +void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None); + ImGui::NavMoveRequestCancel(); + g.NavMoveDir = move_dir; + g.NavMoveClipDir = clip_dir; + g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; + g.NavMoveRequestFlags = move_flags; + g.NavWindow->NavRectRel[g.NavLayer] = bb_rel; +} + +void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags) +{ + ImGuiContext& g = *GImGui; + if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0) + return; + IM_ASSERT(move_flags != 0); // No points calling this with no wrapping + ImRect bb_rel = window->NavRectRel[0]; + + ImGuiDir clip_dir = g.NavMoveDir; + if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + { + bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; } + NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + } + if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + { + bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; } + NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + } + if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; } + NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + } + if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; } + NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + } +} + +// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). +// This way we could find the last focused window among our children. It would be much less confusing this way? +static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window) +{ + ImGuiWindow* parent_window = nav_window; + while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) + parent_window = parent_window->ParentWindow; + if (parent_window && parent_window != nav_window) + parent_window->NavLastChildNavWindow = nav_window; +} + +// Restore the last focused child. +// Call when we are expected to land on the Main Layer (0) after FocusWindow() +static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window) +{ + return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window; +} + +static void NavRestoreLayer(ImGuiNavLayer layer) +{ + ImGuiContext& g = *GImGui; + g.NavLayer = layer; + if (layer == 0) + g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow); + if (layer == 0 && g.NavWindow->NavLastIds[0] != 0) + ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]); + else + ImGui::NavInitWindow(g.NavWindow, true); +} + +static inline void ImGui::NavUpdateAnyRequestFlag() +{ + ImGuiContext& g = *GImGui; + g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); + if (g.NavAnyRequest) + IM_ASSERT(g.NavWindow != NULL); +} + +// This needs to be called before we submit any widget (aka in or before Begin) +void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(window == g.NavWindow); + bool init_for_nav = false; + if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) + if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) + init_for_nav = true; + if (init_for_nav) + { + SetNavID(0, g.NavLayer); + g.NavInitRequest = true; + g.NavInitRequestFromMove = false; + g.NavInitResultId = 0; + g.NavInitResultRectRel = ImRect(); + NavUpdateAnyRequestFlag(); + } + else + { + g.NavId = window->NavLastIds[0]; + } +} + +static ImVec2 ImGui::NavCalcPreferredRefPos() +{ + ImGuiContext& g = *GImGui; + if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) + { + // Mouse (we need a fallback in case the mouse becomes invalid after being used) + if (IsMousePosValid(&g.IO.MousePos)) + return g.IO.MousePos; + return g.LastValidMousePos; + } + else + { + // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. + const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; + ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); + ImRect visible_rect = GetViewportRect(); + return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. + } +} + +float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) +{ + ImGuiContext& g = *GImGui; + if (mode == ImGuiInputReadMode_Down) + return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) + + const float t = g.IO.NavInputsDownDuration[n]; + if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. + return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); + if (t < 0.0f) + return 0.0f; + if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. + return (t == 0.0f) ? 1.0f : 0.0f; + if (mode == ImGuiInputReadMode_Repeat) + return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f); + if (mode == ImGuiInputReadMode_RepeatSlow) + return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f); + if (mode == ImGuiInputReadMode_RepeatFast) + return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f); + return 0.0f; +} + +ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) +{ + ImVec2 delta(0.0f, 0.0f); + if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); + if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); + if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode)); + if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow)) + delta *= slow_factor; + if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast)) + delta *= fast_factor; + return delta; +} + +// Scroll to keep newly navigated item fully into view +// NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated. +static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect) +{ + ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); + //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] + if (window_rect.Contains(item_rect)) return; ImGuiContext& g = *GImGui; - const ImGuiLayoutType backup_layout_type = window->DC.LayoutType; - window->DC.LayoutType = ImGuiLayoutType_Vertical; - if (window->DC.CurrentLineHeight > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height. - ItemSize(ImVec2(0,0)); - else - ItemSize(ImVec2(0.0f, g.FontSize)); - window->DC.LayoutType = backup_layout_type; + if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x) + { + window->ScrollTarget.x = item_rect.Min.x - window->Pos.x + window->Scroll.x - g.Style.ItemSpacing.x; + window->ScrollTargetCenterRatio.x = 0.0f; + } + else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x) + { + window->ScrollTarget.x = item_rect.Max.x - window->Pos.x + window->Scroll.x + g.Style.ItemSpacing.x; + window->ScrollTargetCenterRatio.x = 1.0f; + } + if (item_rect.Min.y < window_rect.Min.y) + { + window->ScrollTarget.y = item_rect.Min.y - window->Pos.y + window->Scroll.y - g.Style.ItemSpacing.y; + window->ScrollTargetCenterRatio.y = 0.0f; + } + else if (item_rect.Max.y >= window_rect.Max.y) + { + window->ScrollTarget.y = item_rect.Max.y - window->Pos.y + window->Scroll.y + g.Style.ItemSpacing.y; + window->ScrollTargetCenterRatio.y = 1.0f; + } } +static void ImGui::NavUpdate() +{ + ImGuiContext& g = *GImGui; + g.IO.WantSetMousePos = false; +#if 0 + if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); +#endif + + // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard) + bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + if (nav_gamepad_active) + if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f) + g.NavInputSource = ImGuiInputSource_NavGamepad; + + // Update Keyboard->Nav inputs mapping + if (nav_keyboard_active) + { + #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } + NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); + NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); + NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); + NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); + NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); + NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); + NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); + NAV_MAP_KEY(ImGuiKey_Tab, ImGuiNavInput_KeyTab_ ); + if (g.IO.KeyCtrl) + g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; + if (g.IO.KeyShift) + g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; + if (g.IO.KeyAlt && !g.IO.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu. + g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; + #undef NAV_MAP_KEY + } + memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); + for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) + g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; + + // Process navigation init request (select first/default focus) + // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) + if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove) && g.NavWindow) + { + // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) + if (g.NavInitRequestFromMove) + SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel); + else + SetNavID(g.NavInitResultId, g.NavLayer); + g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; + } + g.NavInitRequest = false; + g.NavInitRequestFromMove = false; + g.NavInitResultId = 0; + g.NavJustMovedToId = 0; + + // Process navigation move request + if (g.NavMoveRequest) + NavUpdateMoveResult(); + + // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame + if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) + { + IM_ASSERT(g.NavMoveRequest); + if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) + g.NavDisableHighlight = false; + g.NavMoveRequestForward = ImGuiNavForward_None; + } + + // Apply application mouse position movement, after we had a chance to process move request result. + if (g.NavMousePosDirty && g.NavIdIsAlive) + { + // Set mouse position given our knowledge of the navigated item position from last frame + if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) + { + if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) + { + g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos(); + g.IO.WantSetMousePos = true; + } + } + g.NavMousePosDirty = false; + } + g.NavIdIsAlive = false; + g.NavJustTabbedId = 0; + IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); + + // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 + if (g.NavWindow) + NavSaveLastChildNavWindowIntoParent(g.NavWindow); + if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) + g.NavWindow->NavLastChildNavWindow = NULL; + + // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) + NavUpdateWindowing(); + + // Set output flags for user application + g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); + g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); + + // Process NavCancel input (to close a popup, get back to parent, clear focus) + if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) + { + if (g.ActiveId != 0) + { + if (!(g.ActiveIdBlockNavInputFlags & (1 << ImGuiNavInput_Cancel))) + ClearActiveID(); + } + else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) + { + // Exit child window + ImGuiWindow* child_window = g.NavWindow; + ImGuiWindow* parent_window = g.NavWindow->ParentWindow; + IM_ASSERT(child_window->ChildId != 0); + FocusWindow(parent_window); + SetNavID(child_window->ChildId, 0); + g.NavIdIsAlive = false; + if (g.NavDisableMouseHover) + g.NavMousePosDirty = true; + } + else if (g.OpenPopupStack.Size > 0) + { + // Close open popup/menu + if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) + ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); + } + else if (g.NavLayer != 0) + { + // Leave the "menu" layer + NavRestoreLayer(ImGuiNavLayer_Main); + } + else + { + // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were + if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) + g.NavWindow->NavLastIds[0] = 0; + g.NavId = 0; + } + } + + // Process manual activation request + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; + if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + { + bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); + bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); + if (g.ActiveId == 0 && activate_pressed) + g.NavActivateId = g.NavId; + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) + g.NavActivateDownId = g.NavId; + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) + g.NavActivatePressedId = g.NavId; + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) + g.NavInputId = g.NavId; + } + if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + g.NavDisableHighlight = true; + if (g.NavActivateId != 0) + IM_ASSERT(g.NavActivateDownId == g.NavActivateId); + g.NavMoveRequest = false; + + // Process programmatic activation request + if (g.NavNextActivateId != 0) + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; + g.NavNextActivateId = 0; + + // Initiate directional inputs request + const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; + if (g.NavMoveRequestForward == ImGuiNavForward_None) + { + g.NavMoveDir = ImGuiDir_None; + g.NavMoveRequestFlags = ImGuiNavMoveFlags_None; + if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + { + if ((allowed_dir_flags & (1<Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) + { + // *Fallback* manual-scroll with Nav directional keys when window has no navigable item + ImGuiWindow* window = g.NavWindow; + const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. + if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) + { + if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) + SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); + if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) + SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); + } + + // *Normal* Manual scroll with NavScrollXXX keys + // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. + ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f); + if (scroll_dir.x != 0.0f && window->ScrollbarX) + { + SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); + g.NavMoveFromClampedRefRect = true; + } + if (scroll_dir.y != 0.0f) + { + SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); + g.NavMoveFromClampedRefRect = true; + } + } + + // Reset search results + g.NavMoveResultLocal.Clear(); + g.NavMoveResultLocalVisibleSet.Clear(); + g.NavMoveResultOther.Clear(); + + // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items + if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0) + { + ImGuiWindow* window = g.NavWindow; + ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1)); + if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) + { + float pad = window->CalcFontSize() * 0.5f; + window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item + window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); + g.NavId = 0; + } + g.NavMoveFromClampedRefRect = false; + } + + // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) + ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); + g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect(); + g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y); + g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); + g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; + IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). + //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] + g.NavScoringCount = 0; +#if IMGUI_DEBUG_NAV_RECTS + if (g.NavWindow) + { + ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); + if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] + if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + } +#endif +} + +// Apply result from previous frame navigation directional move request +static void ImGui::NavUpdateMoveResult() +{ + ImGuiContext& g = *GImGui; + if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) + { + // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) + if (g.NavId != 0) + { + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + } + return; + } + + // Select which result to use + ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + + // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. + if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) + if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId) + result = &g.NavMoveResultLocalVisibleSet; + + // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. + if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) + if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter)) + result = &g.NavMoveResultOther; + IM_ASSERT(g.NavWindow && result->Window); + + // Scroll to keep newly navigated item fully into view. + if (g.NavLayer == 0) + { + ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); + NavScrollToBringItemIntoView(result->Window, rect_abs); + + // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate() + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(result->Window, false); + ImVec2 delta_scroll = result->Window->Scroll - next_scroll; + result->RectRel.Translate(delta_scroll); + + // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy). + if (result->Window->Flags & ImGuiWindowFlags_ChildWindow) + NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll)); + } + + ClearActiveID(); + g.NavWindow = result->Window; + if (g.NavId != result->ID) + { + // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) + g.NavJustMovedToId = result->ID; + g.NavJustMovedToMultiSelectScopeId = result->SelectScopeId; + } + SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel); + g.NavMoveFromClampedRefRect = false; +} + +static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags) +{ + ImGuiContext& g = *GImGui; + if (g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0) + { + ImGuiWindow* window = g.NavWindow; + bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up)); + bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down)); + if (page_up_held != page_down_held) // If either (not both) are pressed + { + if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) + { + // Fallback manual-scroll when window has no navigable item + if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) + SetWindowScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); + else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) + SetWindowScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); + } + else + { + const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; + const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); + float nav_scoring_rect_offset_y = 0.0f; + if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) + { + nav_scoring_rect_offset_y = -page_offset_y; + g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item) + g.NavMoveClipDir = ImGuiDir_Up; + g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + } + else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) + { + nav_scoring_rect_offset_y = +page_offset_y; + g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item) + g.NavMoveClipDir = ImGuiDir_Down; + g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + } + return nav_scoring_rect_offset_y; + } + } + } + return 0.0f; +} + +static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N) +{ + ImGuiContext& g = *GImGui; + for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--) + if (g.WindowsFocusOrder[i] == window) + return i; + return -1; +} + +static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) +{ + ImGuiContext& g = *GImGui; + for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir) + if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i])) + return g.WindowsFocusOrder[i]; + return NULL; +} + +static void NavUpdateWindowingHighlightWindow(int focus_change_dir) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavWindowingTarget); + if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) + return; + + const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget); + ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); + if (!window_target) + window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir); + if (window_target) // Don't reset windowing target if there's a single window in the list + g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; + g.NavWindowingToggleLayer = false; +} + +// Windowing management mode +// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer) +// Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer) +static void ImGui::NavUpdateWindowing() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* apply_focus_window = NULL; + bool apply_toggle_layer = false; + + ImGuiWindow* modal_window = GetTopMostPopupModal(); + if (modal_window != NULL) + { + g.NavWindowingTarget = NULL; + return; + } + + // Fade out + if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) + { + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f); + if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) + g.NavWindowingTargetAnim = NULL; + } + + // Start CTRL-TAB or Square+L/R window selection + bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); + bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); + if (start_windowing_with_gamepad || start_windowing_with_keyboard) + if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) + { + g.NavWindowingTarget = g.NavWindowingTargetAnim = window; + g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; + g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; + g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; + } + + // Gamepad update + g.NavWindowingTimer += g.IO.DeltaTime; + if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad) + { + // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); + + // Select window to focus + const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow); + if (focus_change_dir != 0) + { + NavUpdateWindowingHighlightWindow(focus_change_dir); + g.NavWindowingHighlightAlpha = 1.0f; + } + + // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most) + if (!IsNavInputDown(ImGuiNavInput_Menu)) + { + g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. + if (g.NavWindowingToggleLayer && g.NavWindow) + apply_toggle_layer = true; + else if (!g.NavWindowingToggleLayer) + apply_focus_window = g.NavWindowingTarget; + g.NavWindowingTarget = NULL; + } + } + + // Keyboard: Focus + if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard) + { + // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f + if (IsKeyPressedMap(ImGuiKey_Tab, true)) + NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); + if (!g.IO.KeyCtrl) + apply_focus_window = g.NavWindowingTarget; + } + + // Keyboard: Press and Release ALT to toggle menu layer + // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB + if (IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed)) + g.NavWindowingToggleLayer = true; + if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) + if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) + apply_toggle_layer = true; + + // Move window + if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) + { + ImVec2 move_delta; + if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift) + move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); + if (g.NavInputSource == ImGuiInputSource_NavGamepad) + move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); + if (move_delta.x != 0.0f || move_delta.y != 0.0f) + { + const float NAV_MOVE_SPEED = 800.0f; + const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well + SetWindowPos(g.NavWindowingTarget->RootWindow, g.NavWindowingTarget->RootWindow->Pos + move_delta * move_speed, ImGuiCond_Always); + g.NavDisableMouseHover = true; + MarkIniSettingsDirty(g.NavWindowingTarget); + } + } + + // Apply final focus + if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) + { + ClearActiveID(); + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); + ClosePopupsOverWindow(apply_focus_window, false); + FocusWindow(apply_focus_window); + if (apply_focus_window->NavLastIds[0] == 0) + NavInitWindow(apply_focus_window, false); + + // If the window only has a menu layer, select it directly + if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu)) + g.NavLayer = ImGuiNavLayer_Menu; + } + if (apply_focus_window) + g.NavWindowingTarget = NULL; + + // Apply menu/layer toggle + if (apply_toggle_layer && g.NavWindow) + { + // Move to parent menu if necessary + ImGuiWindow* new_nav_window = g.NavWindow; + while (new_nav_window->ParentWindow + && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 + && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 + && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) + new_nav_window = new_nav_window->ParentWindow; + if (new_nav_window != g.NavWindow) + { + ImGuiWindow* old_nav_window = g.NavWindow; + FocusWindow(new_nav_window); + new_nav_window->NavLastChildNavWindow = old_nav_window; + } + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + + // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID. + const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; + NavRestoreLayer(new_nav_layer); + } +} + +// Window has already passed the IsWindowNavFocusable() +static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) +{ + if (window->Flags & ImGuiWindowFlags_Popup) + return "(Popup)"; + if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0) + return "(Main menu bar)"; + return "(Untitled)"; +} + +// Overlay displayed when using CTRL+TAB. Called by EndFrame(). +void ImGui::NavUpdateWindowingList() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavWindowingTarget != NULL); + + if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY) + return; + + if (g.NavWindowingList == NULL) + g.NavWindowingList = FindWindowByName("###NavWindowingList"); + SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); + SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); + PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); + Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); + for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) + { + ImGuiWindow* window = g.WindowsFocusOrder[n]; + if (!IsWindowNavFocusable(window)) + continue; + const char* label = window->Name; + if (label == FindRenderedTextEnd(label)) + label = GetFallbackWindowNameForWindowingList(window); + Selectable(label, g.NavWindowingTarget == window); + } + End(); + PopStyleVar(); +} + +//----------------------------------------------------------------------------- +// [SECTION] COLUMNS +// In the current version, Columns are very weak. Needs to be replaced with a more full-featured system. +//----------------------------------------------------------------------------- + void ImGui::NextColumn() { ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems || window->DC.ColumnsSet == NULL) + if (window->SkipItems || window->DC.CurrentColumns == NULL) return; ImGuiContext& g = *GImGui; + ImGuiColumns* columns = window->DC.CurrentColumns; + + if (columns->Count == 1) + { + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + IM_ASSERT(columns->Current == 0); + return; + } + PopItemWidth(); PopClipRect(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; - columns->CellMaxY = ImMax(columns->CellMaxY, window->DC.CursorPos.y); + columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); if (++columns->Current < columns->Count) { // Columns 1+ cancel out IndentX - window->DC.ColumnsOffsetX = GetColumnOffset(columns->Current) - window->DC.IndentX + g.Style.ItemSpacing.x; - window->DrawList->ChannelsSetCurrent(columns->Current); + // FIXME-COLUMNS: Unnecessary, could be locked? + window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + g.Style.ItemSpacing.x; + window->DrawList->ChannelsSetCurrent(columns->Current + 1); } else { - window->DC.ColumnsOffsetX = 0.0f; - window->DrawList->ChannelsSetCurrent(0); + // New row/line + window->DC.ColumnsOffset.x = 0.0f; + window->DrawList->ChannelsSetCurrent(1); columns->Current = 0; - columns->CellMinY = columns->CellMaxY; + columns->LineMinY = columns->LineMaxY; } - window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); - window->DC.CursorPos.y = columns->CellMinY; - window->DC.CurrentLineHeight = 0.0f; - window->DC.CurrentLineTextBaseOffset = 0.0f; + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + window->DC.CursorPos.y = columns->LineMinY; + window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); + window->DC.CurrLineTextBaseOffset = 0.0f; - PushColumnClipRect(); - PushItemWidth(GetColumnWidth() * 0.65f); // FIXME: Move on columns setup + PushColumnClipRect(columns->Current); // FIXME-COLUMNS: Could it be an overwrite? + + // FIXME-COLUMNS: Share code with BeginColumns() - move code on columns setup. + float offset_0 = GetColumnOffset(columns->Current); + float offset_1 = GetColumnOffset(columns->Current + 1); + float width = offset_1 - offset_0; + PushItemWidth(width * 0.65f); + window->WorkRect.Max.x = window->Pos.x + offset_1 - window->WindowPadding.x; } int ImGui::GetColumnIndex() { ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0; + return window->DC.CurrentColumns ? window->DC.CurrentColumns->Current : 0; } int ImGui::GetColumnsCount() { ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1; + return window->DC.CurrentColumns ? window->DC.CurrentColumns->Count : 1; } -static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm) +static float OffsetNormToPixels(const ImGuiColumns* columns, float offset_norm) { - return offset_norm * (columns->MaxX - columns->MinX); + return offset_norm * (columns->OffMaxX - columns->OffMinX); } -static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset) +static float PixelsToOffsetNorm(const ImGuiColumns* columns, float offset) { - return offset / (columns->MaxX - columns->MinX); + return offset / (columns->OffMaxX - columns->OffMinX); } -static inline float GetColumnsRectHalfWidth() { return 4.0f; } +static const float COLUMNS_HIT_RECT_HALF_WIDTH = 4.0f; -static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index) +static float GetDraggedColumnOffset(ImGuiColumns* columns, int column_index) { // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning. @@ -12376,7 +8739,7 @@ static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index) IM_ASSERT(column_index > 0); // We are not supposed to drag column 0. IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index)); - float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x; + float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + COLUMNS_HIT_RECT_HALF_WIDTH - window->Pos.x; x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing); if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths)) x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing); @@ -12387,7 +8750,7 @@ static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index) float ImGui::GetColumnOffset(int column_index) { ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; + ImGuiColumns* columns = window->DC.CurrentColumns; IM_ASSERT(columns != NULL); if (column_index < 0) @@ -12395,11 +8758,11 @@ float ImGui::GetColumnOffset(int column_index) IM_ASSERT(column_index < columns->Columns.Size); const float t = columns->Columns[column_index].OffsetNorm; - const float x_offset = ImLerp(columns->MinX, columns->MaxX, t); + const float x_offset = ImLerp(columns->OffMinX, columns->OffMaxX, t); return x_offset; } -static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false) +static float GetColumnWidthEx(ImGuiColumns* columns, int column_index, bool before_resize = false) { if (column_index < 0) column_index = columns->Current; @@ -12415,7 +8778,7 @@ static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool b float ImGui::GetColumnWidth(int column_index) { ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; + ImGuiColumns* columns = window->DC.CurrentColumns; IM_ASSERT(columns != NULL); if (column_index < 0) @@ -12427,7 +8790,7 @@ void ImGui::SetColumnOffset(int column_index, float offset) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImGuiColumnsSet* columns = window->DC.ColumnsSet; + ImGuiColumns* columns = window->DC.CurrentColumns; IM_ASSERT(columns != NULL); if (column_index < 0) @@ -12438,8 +8801,8 @@ void ImGui::SetColumnOffset(int column_index, float offset) const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f; if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow)) - offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index)); - columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX); + offset = ImMin(offset, columns->OffMaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index)); + columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->OffMinX); if (preserve_width) SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width)); @@ -12448,7 +8811,7 @@ void ImGui::SetColumnOffset(int column_index, float offset) void ImGui::SetColumnWidth(int column_index, float width) { ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; + ImGuiColumns* columns = window->DC.CurrentColumns; IM_ASSERT(columns != NULL); if (column_index < 0) @@ -12459,62 +8822,93 @@ void ImGui::SetColumnWidth(int column_index, float width) void ImGui::PushColumnClipRect(int column_index) { ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; + ImGuiColumns* columns = window->DC.CurrentColumns; if (column_index < 0) column_index = columns->Current; - PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false); + ImGuiColumnData* column = &columns->Columns[column_index]; + PushClipRect(column->ClipRect.Min, column->ClipRect.Max, false); } -static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id) +// Get into the columns background draw command (which is generally the same draw command as before we called BeginColumns) +void ImGui::PushColumnsBackground() { + ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiColumns* columns = window->DC.CurrentColumns; + window->DrawList->ChannelsSetCurrent(0); + int cmd_size = window->DrawList->CmdBuffer.Size; + PushClipRect(columns->HostClipRect.Min, columns->HostClipRect.Max, false); + IM_UNUSED(cmd_size); + IM_ASSERT(cmd_size == window->DrawList->CmdBuffer.Size); // Being in channel 0 this should not have created an ImDrawCmd +} + +void ImGui::PopColumnsBackground() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiColumns* columns = window->DC.CurrentColumns; + window->DrawList->ChannelsSetCurrent(columns->Current + 1); + PopClipRect(); +} + +ImGuiColumns* ImGui::FindOrCreateColumns(ImGuiWindow* window, ImGuiID id) +{ + // We have few columns per window so for now we don't need bother much with turning this into a faster lookup. for (int n = 0; n < window->ColumnsStorage.Size; n++) if (window->ColumnsStorage[n].ID == id) return &window->ColumnsStorage[n]; - window->ColumnsStorage.push_back(ImGuiColumnsSet()); - ImGuiColumnsSet* columns = &window->ColumnsStorage.back(); + window->ColumnsStorage.push_back(ImGuiColumns()); + ImGuiColumns* columns = &window->ColumnsStorage.back(); columns->ID = id; return columns; } -void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags) +ImGuiID ImGui::GetColumnsID(const char* str_id, int columns_count) { - ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - IM_ASSERT(columns_count > 1); - IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported - // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget. // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer. PushID(0x11223347 + (str_id ? 0 : columns_count)); ImGuiID id = window->GetID(str_id ? str_id : "columns"); PopID(); + return id; +} + +void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + IM_ASSERT(columns_count >= 1); + IM_ASSERT(window->DC.CurrentColumns == NULL); // Nested columns are currently not supported + // Acquire storage for the columns set - ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id); + ImGuiID id = GetColumnsID(str_id, columns_count); + ImGuiColumns* columns = FindOrCreateColumns(window, id); IM_ASSERT(columns->ID == id); columns->Current = 0; columns->Count = columns_count; columns->Flags = flags; - window->DC.ColumnsSet = columns; + window->DC.CurrentColumns = columns; // Set state for first column - const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->InnerClipRect.Max.x - window->Pos.x); - columns->MinX = window->DC.IndentX - g.Style.ItemSpacing.x; // Lock our horizontal range - columns->MaxX = ImMax(content_region_width - window->Scroll.x, columns->MinX + 1.0f); - columns->StartPosY = window->DC.CursorPos.y; - columns->StartMaxPosX = window->DC.CursorMaxPos.x; - columns->CellMinY = columns->CellMaxY = window->DC.CursorPos.y; - window->DC.ColumnsOffsetX = 0.0f; - window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); + columns->OffMinX = window->DC.Indent.x - g.Style.ItemSpacing.x; + columns->OffMaxX = ImMax(window->WorkRect.Max.x - window->Pos.x, columns->OffMinX + 1.0f); + columns->HostCursorPosY = window->DC.CursorPos.y; + columns->HostCursorMaxPosX = window->DC.CursorMaxPos.x; + columns->HostClipRect = window->ClipRect; + columns->HostWorkRect = window->WorkRect; + columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y; + window->DC.ColumnsOffset.x = 0.0f; + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Clear data if columns count changed if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1) columns->Columns.resize(0); - // Initialize defaults + // Initialize default widths columns->IsFirstFrame = (columns->Columns.Size == 0); if (columns->Columns.Size == 0) { @@ -12531,64 +8925,80 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlag { // Compute clipping rectangle ImGuiColumnData* column = &columns->Columns[n]; - float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f); + float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n)); float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f); column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX); column->ClipRect.ClipWith(window->ClipRect); } - window->DrawList->ChannelsSplit(columns->Count); - PushColumnClipRect(); - PushItemWidth(GetColumnWidth() * 0.65f); + if (columns->Count > 1) + { + window->DrawList->ChannelsSplit(1 + columns->Count); + window->DrawList->ChannelsSetCurrent(1); + PushColumnClipRect(0); + } + + float offset_0 = GetColumnOffset(columns->Current); + float offset_1 = GetColumnOffset(columns->Current + 1); + float width = offset_1 - offset_0; + PushItemWidth(width * 0.65f); + window->WorkRect.Max.x = window->Pos.x + offset_1 - window->WindowPadding.x; } void ImGui::EndColumns() { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; + ImGuiColumns* columns = window->DC.CurrentColumns; IM_ASSERT(columns != NULL); PopItemWidth(); - PopClipRect(); - window->DrawList->ChannelsMerge(); + if (columns->Count > 1) + { + PopClipRect(); + window->DrawList->ChannelsMerge(); + } - columns->CellMaxY = ImMax(columns->CellMaxY, window->DC.CursorPos.y); - window->DC.CursorPos.y = columns->CellMaxY; - if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize)) - window->DC.CursorMaxPos.x = ImMax(columns->StartMaxPosX, columns->MaxX); // Restore cursor max pos, as columns don't grow parent + const ImGuiColumnsFlags flags = columns->Flags; + columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); + window->DC.CursorPos.y = columns->LineMaxY; + if (!(flags & ImGuiColumnsFlags_GrowParentContentsSize)) + window->DC.CursorMaxPos.x = columns->HostCursorMaxPosX; // Restore cursor max pos, as columns don't grow parent // Draw columns borders and handle resize + // The IsBeingResized flag ensure we preserve pre-resize columns width so back-and-forth are not lossy bool is_being_resized = false; - if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems) + if (!(flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems) { - const float y1 = columns->StartPosY; - const float y2 = window->DC.CursorPos.y; + // We clip Y boundaries CPU side because very long triangles are mishandled by some GPU drivers. + const float y1 = ImMax(columns->HostCursorPosY, window->ClipRect.Min.y); + const float y2 = ImMin(window->DC.CursorPos.y, window->ClipRect.Max.y); int dragging_column = -1; for (int n = 1; n < columns->Count; n++) { + ImGuiColumnData* column = &columns->Columns[n]; float x = window->Pos.x + GetColumnOffset(n); const ImGuiID column_id = columns->ID + ImGuiID(n); - const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction - const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2)); + const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH; + const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2)); KeepAliveID(column_id); - if (IsClippedEx(column_rect, column_id, false)) + if (IsClippedEx(column_hit_rect, column_id, false)) continue; - + bool hovered = false, held = false; - if (!(columns->Flags & ImGuiColumnsFlags_NoResize)) + if (!(flags & ImGuiColumnsFlags_NoResize)) { - ButtonBehavior(column_rect, column_id, &hovered, &held); + ButtonBehavior(column_hit_rect, column_id, &hovered, &held); if (hovered || held) g.MouseCursor = ImGuiMouseCursor_ResizeEW; - if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize)) + if (held && !(column->Flags & ImGuiColumnsFlags_NoResize)) dragging_column = n; } - // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.) + // Draw column const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); const float xi = (float)(int)x; - window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col); + window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col); } // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame. @@ -12604,9 +9014,10 @@ void ImGui::EndColumns() } columns->IsBeingResized = is_being_resized; - window->DC.ColumnsSet = NULL; - window->DC.ColumnsOffsetX = 0.0f; - window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); + window->WorkRect = columns->HostWorkRect; + window->DC.CurrentColumns = NULL; + window->DC.ColumnsOffset.x = 0.0f; + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); } // [2018-03: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing] @@ -12614,104 +9025,23 @@ void ImGui::Columns(int columns_count, const char* id, bool border) { ImGuiWindow* window = GetCurrentWindow(); IM_ASSERT(columns_count >= 1); - if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count != columns_count) - EndColumns(); - + ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder); //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior + ImGuiColumns* columns = window->DC.CurrentColumns; + if (columns != NULL && columns->Count == columns_count && columns->Flags == flags) + return; + + if (columns != NULL) + EndColumns(); + if (columns_count != 1) BeginColumns(id, columns_count, flags); } -void ImGui::Indent(float indent_w) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - window->DC.IndentX += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX; -} - -void ImGui::Unindent(float indent_w) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - window->DC.IndentX -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX; -} - -void ImGui::TreePush(const char* str_id) -{ - ImGuiWindow* window = GetCurrentWindow(); - Indent(); - window->DC.TreeDepth++; - PushID(str_id ? str_id : "#TreePush"); -} - -void ImGui::TreePush(const void* ptr_id) -{ - ImGuiWindow* window = GetCurrentWindow(); - Indent(); - window->DC.TreeDepth++; - PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); -} - -void ImGui::TreePushRawID(ImGuiID id) -{ - ImGuiWindow* window = GetCurrentWindow(); - Indent(); - window->DC.TreeDepth++; - window->IDStack.push_back(id); -} - -void ImGui::TreePop() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - Unindent(); - - window->DC.TreeDepth--; - if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) - if (g.NavIdIsAlive && (window->DC.TreeDepthMayJumpToParentOnPop & (1 << window->DC.TreeDepth))) - { - SetNavID(window->IDStack.back(), g.NavLayer); - NavMoveRequestCancel(); - } - window->DC.TreeDepthMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1; - - PopID(); -} - -void ImGui::Value(const char* prefix, bool b) -{ - Text("%s: %s", prefix, (b ? "true" : "false")); -} - -void ImGui::Value(const char* prefix, int v) -{ - Text("%s: %d", prefix, v); -} - -void ImGui::Value(const char* prefix, unsigned int v) -{ - Text("%s: %d", prefix, v); -} - -void ImGui::Value(const char* prefix, float v, const char* float_format) -{ - if (float_format) - { - char fmt[64]; - ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); - Text(fmt, prefix, v); - } - else - { - Text("%s: %.3f", prefix, v); - } -} //----------------------------------------------------------------------------- -// DRAG AND DROP +// [SECTION] DRAG AND DROP //----------------------------------------------------------------------------- void ImGui::ClearDragDrop() @@ -12719,12 +9049,16 @@ void ImGui::ClearDragDrop() ImGuiContext& g = *GImGui; g.DragDropActive = false; g.DragDropPayload.Clear(); + g.DragDropAcceptFlags = ImGuiDragDropFlags_None; g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0; g.DragDropAcceptIdCurrRectSurface = FLT_MAX; g.DragDropAcceptFrameCount = -1; + + g.DragDropPayloadBufHeap.clear(); + memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); } -// Call when current ID is active. +// Call when current ID is active. // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource() bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) { @@ -12753,16 +9087,16 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) return false; } - // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image() - // We build a throwaway ID based on current ID stack + relative AABB of items in window. - // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. - // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. - bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0; - if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window)) + // Early out + if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) return false; + + // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image() + // We build a throwaway ID based on current ID stack + relative AABB of items in window. + // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. + // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect); - if (is_hovered) - SetHoveredID(source_id); + bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id); if (is_hovered && g.IO.MouseClicked[mouse_button]) { SetActiveID(source_id, window); @@ -12771,6 +9105,10 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker. g.ActiveIdAllowOverlap = is_hovered; } + else + { + g.ActiveIdAllowOverlap = false; + } if (g.ActiveId != source_id) return false; source_parent_id = window->IDStack.back(); @@ -12779,7 +9117,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) else { window = NULL; - source_id = ImHash("#SourceExtern", 0); + source_id = ImHashStr("#SourceExtern"); source_drag_active = true; } @@ -12796,15 +9134,20 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) g.DragDropSourceFlags = flags; g.DragDropMouseButton = mouse_button; } + g.DragDropSourceFrameCount = g.FrameCount; + g.DragDropWithinSourceOrTarget = true; if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) { - // FIXME-DRAG - //SetNextWindowPos(g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding); - //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This is better but e.g ColorButton with checkboard has issue with transparent colors :( - SetNextWindowPos(g.IO.MousePos); - PushStyleColor(ImGuiCol_PopupBg, GetStyleColorVec4(ImGuiCol_PopupBg) * ImVec4(1.0f, 1.0f, 1.0f, 0.6f)); + // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) + // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. BeginTooltip(); + if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) + { + ImGuiWindow* tooltip_window = g.CurrentWindow; + tooltip_window->SkipItems = true; + tooltip_window->HiddenFramesCanSkipItems = 1; + } } if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) @@ -12819,17 +9162,15 @@ void ImGui::EndDragDropSource() { ImGuiContext& g = *GImGui; IM_ASSERT(g.DragDropActive); + IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?"); if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) - { EndTooltip(); - PopStyleColor(); - //PopStyleVar(); - } // Discard the drag if have not called SetDragDropPayload() if (g.DragDropPayload.DataFrameCount == -1) ClearDragDrop(); + g.DragDropWithinSourceOrTarget = false; } // Use 'cond' to choose to submit payload on drag start or every frame @@ -12841,7 +9182,7 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s cond = ImGuiCond_Always; IM_ASSERT(type != NULL); - IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 12 characters long"); + IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long"); IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0)); IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once); IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource() @@ -12856,14 +9197,14 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s // Store in heap g.DragDropPayloadBufHeap.resize((int)data_size); payload.Data = g.DragDropPayloadBufHeap.Data; - memcpy((void*)payload.Data, data, data_size); + memcpy(payload.Data, data, data_size); } else if (data_size > 0) { // Store locally memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); payload.Data = g.DragDropPayloadBufLocal; - memcpy((void*)payload.Data, data, data_size); + memcpy(payload.Data, data, data_size); } else { @@ -12888,9 +9229,13 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) IM_ASSERT(id != 0); if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) return false; + if (window->SkipItems) + return false; + IM_ASSERT(g.DragDropWithinSourceOrTarget == false); g.DragDropTargetRect = bb; g.DragDropTargetId = id; + g.DragDropWithinSourceOrTarget = true; return true; } @@ -12917,8 +9262,10 @@ bool ImGui::BeginDragDropTarget() if (g.DragDropPayload.SourceId == id) return false; + IM_ASSERT(g.DragDropWithinSourceOrTarget == false); g.DragDropTargetRect = display_rect; g.DragDropTargetId = id; + g.DragDropWithinSourceOrTarget = true; return true; } @@ -12934,7 +9281,7 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop ImGuiWindow* window = g.CurrentWindow; ImGuiPayload& payload = g.DragDropPayload; IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ? - IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ? + IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ? if (type != NULL && !payload.IsDataType(type)) return NULL; @@ -12945,6 +9292,7 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop float r_surface = r.GetWidth() * r.GetHeight(); if (r_surface < g.DragDropAcceptIdCurrRectSurface) { + g.DragDropAcceptFlags = flags; g.DragDropAcceptIdCurr = g.DragDropTargetId; g.DragDropAcceptIdCurrRectSurface = r_surface; } @@ -12957,7 +9305,7 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop // FIXME-DRAG: Settle on a proper default visuals for drop target. r.Expand(3.5f); bool push_clip_rect = !window->ClipRect.Contains(r); - if (push_clip_rect) window->DrawList->PushClipRectFullScreen(); + if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1)); window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f); if (push_clip_rect) window->DrawList->PopClipRect(); } @@ -12970,86 +9318,583 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop return &payload; } +const ImGuiPayload* ImGui::GetDragDropPayload() +{ + ImGuiContext& g = *GImGui; + return g.DragDropActive ? &g.DragDropPayload : NULL; +} + // We don't really use/need this now, but added it for the sake of consistency and because we might need it later. void ImGui::EndDragDropTarget() { - ImGuiContext& g = *GImGui; (void)g; + ImGuiContext& g = *GImGui; IM_ASSERT(g.DragDropActive); + IM_ASSERT(g.DragDropWithinSourceOrTarget); + g.DragDropWithinSourceOrTarget = false; +} + + +//----------------------------------------------------------------------------- +// [SECTION] LOGGING/CAPTURING +//----------------------------------------------------------------------------- +// All text output from the interface can be captured into tty/file/clipboard. +// By default, tree nodes are automatically opened during logging. +//----------------------------------------------------------------------------- + +// Pass text data straight to log (without being displayed) +void ImGui::LogText(const char* fmt, ...) +{ + ImGuiContext& g = *GImGui; + if (!g.LogEnabled) + return; + + va_list args; + va_start(args, fmt); + if (g.LogFile) + vfprintf(g.LogFile, fmt, args); + else + g.LogBuffer.appendfv(fmt, args); + va_end(args); +} + +// Internal version that takes a position to decide on newline placement and pad items according to their depth. +// We split text into individual lines to add current tree level padding +void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + if (!text_end) + text_end = FindRenderedTextEnd(text, text_end); + + const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1); + if (ref_pos) + g.LogLinePosY = ref_pos->y; + if (log_new_line) + g.LogLineFirstItem = true; + + const char* text_remaining = text; + if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth + g.LogDepthRef = window->DC.TreeDepth; + const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef); + for (;;) + { + // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. + // We don't add a trailing \n to allow a subsequent item on the same line to be captured. + const char* line_start = text_remaining; + const char* line_end = ImStreolRange(line_start, text_end); + const bool is_first_line = (line_start == text); + const bool is_last_line = (line_end == text_end); + if (!is_last_line || (line_start != line_end)) + { + const int char_count = (int)(line_end - line_start); + if (log_new_line || !is_first_line) + LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start); + else if (g.LogLineFirstItem) + LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start); + else + LogText(" %.*s", char_count, line_start); + g.LogLineFirstItem = false; + } + else if (log_new_line) + { + // An empty "" string at a different Y position should output a carriage return. + LogText(IM_NEWLINE); + break; + } + + if (is_last_line) + break; + text_remaining = line_end + 1; + } +} + +// Start logging/capturing text output +void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT(g.LogEnabled == false); + IM_ASSERT(g.LogFile == NULL); + IM_ASSERT(g.LogBuffer.empty()); + g.LogEnabled = true; + g.LogType = type; + g.LogDepthRef = window->DC.TreeDepth; + g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault); + g.LogLinePosY = FLT_MAX; + g.LogLineFirstItem = true; +} + +void ImGui::LogToTTY(int auto_open_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + LogBegin(ImGuiLogType_TTY, auto_open_depth); + g.LogFile = stdout; +} + +// Start logging/capturing text output to given file +void ImGui::LogToFile(int auto_open_depth, const char* filename) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + + // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still + // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE. + // By opening the file in binary mode "ab" we have consistent output everywhere. + if (!filename) + filename = g.IO.LogFilename; + if (!filename || !filename[0]) + return; + FILE* f = ImFileOpen(filename, "ab"); + if (f == NULL) + { + IM_ASSERT(0); + return; + } + + LogBegin(ImGuiLogType_File, auto_open_depth); + g.LogFile = f; +} + +// Start logging/capturing text output to clipboard +void ImGui::LogToClipboard(int auto_open_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + LogBegin(ImGuiLogType_Clipboard, auto_open_depth); +} + +void ImGui::LogToBuffer(int auto_open_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + LogBegin(ImGuiLogType_Buffer, auto_open_depth); +} + +void ImGui::LogFinish() +{ + ImGuiContext& g = *GImGui; + if (!g.LogEnabled) + return; + + LogText(IM_NEWLINE); + switch (g.LogType) + { + case ImGuiLogType_TTY: + fflush(g.LogFile); + break; + case ImGuiLogType_File: + fclose(g.LogFile); + break; + case ImGuiLogType_Buffer: + break; + case ImGuiLogType_Clipboard: + if (!g.LogBuffer.empty()) + SetClipboardText(g.LogBuffer.begin()); + break; + case ImGuiLogType_None: + IM_ASSERT(0); + break; + } + + g.LogEnabled = false; + g.LogType = ImGuiLogType_None; + g.LogFile = NULL; + g.LogBuffer.clear(); +} + +// Helper to display logging buttons +// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!) +void ImGui::LogButtons() +{ + ImGuiContext& g = *GImGui; + + PushID("LogButtons"); + const bool log_to_tty = Button("Log To TTY"); SameLine(); + const bool log_to_file = Button("Log To File"); SameLine(); + const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); + PushAllowKeyboardFocus(false); + SetNextItemWidth(80.0f); + SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); + PopAllowKeyboardFocus(); + PopID(); + + // Start logging at the end of the function so that the buttons don't appear in the log + if (log_to_tty) + LogToTTY(); + if (log_to_file) + LogToFile(); + if (log_to_clipboard) + LogToClipboard(); } //----------------------------------------------------------------------------- -// PLATFORM DEPENDENT HELPERS +// [SECTION] SETTINGS //----------------------------------------------------------------------------- -#if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)) -#undef WIN32_LEAN_AND_MEAN +void ImGui::MarkIniSettingsDirty() +{ + ImGuiContext& g = *GImGui; + if (g.SettingsDirtyTimer <= 0.0f) + g.SettingsDirtyTimer = g.IO.IniSavingRate; +} + +void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) + if (g.SettingsDirtyTimer <= 0.0f) + g.SettingsDirtyTimer = g.IO.IniSavingRate; +} + +ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) +{ + ImGuiContext& g = *GImGui; + g.SettingsWindows.push_back(ImGuiWindowSettings()); + ImGuiWindowSettings* settings = &g.SettingsWindows.back(); + settings->Name = ImStrdup(name); + settings->ID = ImHashStr(name); + return settings; +} + +ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + for (int i = 0; i != g.SettingsWindows.Size; i++) + if (g.SettingsWindows[i].ID == id) + return &g.SettingsWindows[i]; + return NULL; +} + +ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name) +{ + if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name))) + return settings; + return CreateNewWindowSettings(name); +} + +void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) +{ + size_t file_data_size = 0; + char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size); + if (!file_data) + return; + LoadIniSettingsFromMemory(file_data, (size_t)file_data_size); + IM_FREE(file_data); +} + +ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) +{ + ImGuiContext& g = *GImGui; + const ImGuiID type_hash = ImHashStr(type_name); + for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) + if (g.SettingsHandlers[handler_n].TypeHash == type_hash) + return &g.SettingsHandlers[handler_n]; + return NULL; +} + +// Zero-tolerance, no error reporting, cheap .ini parsing +void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.Initialized); + IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0); + + // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter). + // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy.. + if (ini_size == 0) + ini_size = strlen(ini_data); + char* buf = (char*)IM_ALLOC(ini_size + 1); + char* buf_end = buf + ini_size; + memcpy(buf, ini_data, ini_size); + buf[ini_size] = 0; + + void* entry_data = NULL; + ImGuiSettingsHandler* entry_handler = NULL; + + char* line_end = NULL; + for (char* line = buf; line < buf_end; line = line_end + 1) + { + // Skip new lines markers, then find end of the line + while (*line == '\n' || *line == '\r') + line++; + line_end = line; + while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') + line_end++; + line_end[0] = 0; + if (line[0] == ';') + continue; + if (line[0] == '[' && line_end > line && line_end[-1] == ']') + { + // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. + line_end[-1] = 0; + const char* name_end = line_end - 1; + const char* type_start = line + 1; + char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']'); + const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; + if (!type_end || !name_start) + { + name_start = type_start; // Import legacy entries that have no type + type_start = "Window"; + } + else + { + *type_end = 0; // Overwrite first ']' + name_start++; // Skip second '[' + } + entry_handler = FindSettingsHandler(type_start); + entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; + } + else if (entry_handler != NULL && entry_data != NULL) + { + // Let type handler parse the line + entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); + } + } + IM_FREE(buf); + g.SettingsLoaded = true; +} + +void ImGui::SaveIniSettingsToDisk(const char* ini_filename) +{ + ImGuiContext& g = *GImGui; + g.SettingsDirtyTimer = 0.0f; + if (!ini_filename) + return; + + size_t ini_data_size = 0; + const char* ini_data = SaveIniSettingsToMemory(&ini_data_size); + FILE* f = ImFileOpen(ini_filename, "wt"); + if (!f) + return; + fwrite(ini_data, sizeof(char), ini_data_size, f); + fclose(f); +} + +// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer +const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) +{ + ImGuiContext& g = *GImGui; + g.SettingsDirtyTimer = 0.0f; + g.SettingsIniData.Buf.resize(0); + g.SettingsIniData.Buf.push_back(0); + for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) + { + ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; + handler->WriteAllFn(&g, handler, &g.SettingsIniData); + } + if (out_size) + *out_size = (size_t)g.SettingsIniData.size(); + return g.SettingsIniData.c_str(); +} + +static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) +{ + ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name)); + if (!settings) + settings = ImGui::CreateNewWindowSettings(name); + return (void*)settings; +} + +static void SettingsHandlerWindow_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler*, void* entry, const char* line) +{ + ImGuiContext& g = *ctx; + ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; + float x, y; + int i; + if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y); + else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize); + else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0); +} + +static void SettingsHandlerWindow_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) +{ + // Gather data from windows that were active during this session + // (if a window wasn't opened in this session we preserve its settings) + ImGuiContext& g = *ctx; + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Flags & ImGuiWindowFlags_NoSavedSettings) + continue; + + ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID); + if (!settings) + { + settings = ImGui::CreateNewWindowSettings(window->Name); + window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings); + } + IM_ASSERT(settings->ID == window->ID); + settings->Pos = window->Pos; + settings->Size = window->SizeFull; + settings->Collapsed = window->Collapsed; + } + + // Write to text buffer + buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve + for (int i = 0; i != g.SettingsWindows.Size; i++) + { + const ImGuiWindowSettings* settings = &g.SettingsWindows[i]; + if (settings->Pos.x == FLT_MAX) + continue; + const char* name = settings->Name; + if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() + name = p; + buf->appendf("[%s][%s]\n", handler->TypeName, name); + buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); + buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); + buf->appendf("Collapsed=%d\n", settings->Collapsed); + buf->appendf("\n"); + } +} + + +//----------------------------------------------------------------------------- +// [SECTION] VIEWPORTS, PLATFORM WINDOWS +//----------------------------------------------------------------------------- + +// (this section is filled in the 'docking' branch) + + +//----------------------------------------------------------------------------- +// [SECTION] DOCKING +//----------------------------------------------------------------------------- + +// (this section is filled in the 'docking' branch) + + +//----------------------------------------------------------------------------- +// [SECTION] PLATFORM DEPENDENT HELPERS +//----------------------------------------------------------------------------- + +#if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)) +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif #ifndef __MINGW32__ #include #else #include #endif +#elif defined(__APPLE__) +#include #endif -// Win32 API clipboard implementation -#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) +#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) #ifdef _MSC_VER #pragma comment(lib, "user32") #endif +// Win32 clipboard implementation static const char* GetClipboardTextFn_DefaultImpl(void*) { static ImVector buf_local; buf_local.clear(); - if (!OpenClipboard(NULL)) + if (!::OpenClipboard(NULL)) return NULL; - HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT); + HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT); if (wbuf_handle == NULL) { - CloseClipboard(); + ::CloseClipboard(); return NULL; } - if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle)) + if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle)) { int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1; buf_local.resize(buf_len); ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL); } - GlobalUnlock(wbuf_handle); - CloseClipboard(); + ::GlobalUnlock(wbuf_handle); + ::CloseClipboard(); return buf_local.Data; } static void SetClipboardTextFn_DefaultImpl(void*, const char* text) { - if (!OpenClipboard(NULL)) + if (!::OpenClipboard(NULL)) return; const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1; - HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar)); + HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar)); if (wbuf_handle == NULL) { - CloseClipboard(); + ::CloseClipboard(); return; } - ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle); + ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle); ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL); - GlobalUnlock(wbuf_handle); - EmptyClipboard(); - SetClipboardData(CF_UNICODETEXT, wbuf_handle); - CloseClipboard(); + ::GlobalUnlock(wbuf_handle); + ::EmptyClipboard(); + if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL) + ::GlobalFree(wbuf_handle); + ::CloseClipboard(); +} + +#elif defined(__APPLE__) && TARGET_OS_OSX && !defined(IMGUI_DISABLE_OSX_FUNCTIONS) + +#include // Use old API to avoid need for separate .mm file +static PasteboardRef main_clipboard = 0; + +// OSX clipboard implementation +static void SetClipboardTextFn_DefaultImpl(void*, const char* text) +{ + if (!main_clipboard) + PasteboardCreate(kPasteboardClipboard, &main_clipboard); + PasteboardClear(main_clipboard); + CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text)); + if (cf_data) + { + PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0); + CFRelease(cf_data); + } +} + +static const char* GetClipboardTextFn_DefaultImpl(void*) +{ + if (!main_clipboard) + PasteboardCreate(kPasteboardClipboard, &main_clipboard); + PasteboardSynchronize(main_clipboard); + + ItemCount item_count = 0; + PasteboardGetItemCount(main_clipboard, &item_count); + for (int i = 0; i < item_count; i++) + { + PasteboardItemID item_id = 0; + PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id); + CFArrayRef flavor_type_array = 0; + PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array); + for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++) + { + CFDataRef cf_data; + if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr) + { + static ImVector clipboard_text; + int length = (int)CFDataGetLength(cf_data); + clipboard_text.resize(length + 1); + CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)clipboard_text.Data); + clipboard_text[length] = 0; + CFRelease(cf_data); + return clipboard_text.Data; + } + } + } + return NULL; } #else -// Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers +// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers. static const char* GetClipboardTextFn_DefaultImpl(void*) { ImGuiContext& g = *GImGui; return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin(); } -// Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers static void SetClipboardTextFn_DefaultImpl(void*, const char* text) { ImGuiContext& g = *GImGui; @@ -13063,7 +9908,7 @@ static void SetClipboardTextFn_DefaultImpl(void*, const char* text) #endif // Win32 API IME support (for Asian languages, etc.) -#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) +#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) #include #ifdef _MSC_VER @@ -13074,13 +9919,14 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) { // Notify OS Input Method Editor of text input position if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle) - if (HIMC himc = ImmGetContext(hwnd)) + if (HIMC himc = ::ImmGetContext(hwnd)) { COMPOSITIONFORM cf; cf.ptCurrentPos.x = x; cf.ptCurrentPos.y = y; cf.dwStyle = CFS_FORCE_POSITION; - ImmSetCompositionWindow(himc, &cf); + ::ImmSetCompositionWindow(himc, &cf); + ::ImmReleaseContext(hwnd, himc); } } @@ -13091,178 +9937,297 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} #endif //----------------------------------------------------------------------------- -// HELP +// [SECTION] METRICS/DEBUG WINDOW //----------------------------------------------------------------------------- +#ifndef IMGUI_DISABLE_METRICS_WINDOW void ImGui::ShowMetricsWindow(bool* p_open) { - if (ImGui::Begin("ImGui Metrics", p_open)) + if (!ImGui::Begin("Dear ImGui Metrics", p_open)) { - ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); - ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3); - ImGui::Text("%d allocations", (int)GImAllocatorActiveAllocationsCount); - static bool show_clip_rects = true; - ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_clip_rects); - ImGui::Separator(); + ImGui::End(); + return; + } - struct Funcs + enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Contents, WRT_ContentsRegionRect, WRT_Count }; // Windows Rect Type + const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Contents", "ContentsRegionRect" }; + + static bool show_windows_begin_order = false; + static bool show_windows_rects = false; + static int show_windows_rect_type = WRT_WorkRect; + static bool show_drawcmd_clip_rects = true; + + ImGuiIO& io = ImGui::GetIO(); + ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); + ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); + ImGui::Text("%d active allocations", io.MetricsActiveAllocations); + ImGui::Separator(); + + struct Funcs + { + static ImRect GetWindowRect(ImGuiWindow* window, int rect_type) { - static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label) - { - bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size); - if (draw_list == ImGui::GetWindowDrawList()) - { - ImGui::SameLine(); - ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered) - if (node_open) ImGui::TreePop(); - return; - } - - ImDrawList* overlay_draw_list = ImGui::GetOverlayDrawList(); // Render additional visuals into the top-most draw list - if (window && ImGui::IsItemHovered()) - overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); - if (!node_open) - return; - - int elem_offset = 0; - for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++) - { - if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0) - continue; - if (pcmd->UserCallback) - { - ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); - continue; - } - ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; - bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %4d %s vtx, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); - if (show_clip_rects && ImGui::IsItemHovered()) - { - ImRect clip_rect = pcmd->ClipRect; - ImRect vtxs_rect; - for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++) - vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos); - clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255)); - vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255)); - } - if (!pcmd_node_open) - continue; - - // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. - ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. - while (clipper.Step()) - for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++) - { - char buf[300]; - char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf); - ImVec2 triangles_pos[3]; - for (int n = 0; n < 3; n++, vtx_i++) - { - ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i]; - triangles_pos[n] = v.pos; - buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); - } - ImGui::Selectable(buf, false); - if (ImGui::IsItemHovered()) - { - ImDrawListFlags backup_flags = overlay_draw_list->Flags; - overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles. - overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f); - overlay_draw_list->Flags = backup_flags; - } - } - ImGui::TreePop(); - } - ImGui::TreePop(); - } - - static void NodeWindows(ImVector& windows, const char* label) - { - if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size)) - return; - for (int i = 0; i < windows.Size; i++) - Funcs::NodeWindow(windows[i], "Window"); - ImGui::TreePop(); - } - - static void NodeWindow(ImGuiWindow* window, const char* label) - { - if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) - return; - ImGuiWindowFlags flags = window->Flags; - NodeDrawList(window, window->DrawList, "DrawList"); - ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); - ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s..)", flags, - (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", - (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : ""); - ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window)); - ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed); - ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); - ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); - if (window->NavRectRel[0].IsInverted()) - ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); - else - ImGui::BulletText("NavRectRel[0]: "); - if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); - if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); - if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) - { - for (int n = 0; n < window->ColumnsStorage.Size; n++) - { - const ImGuiColumnsSet* columns = &window->ColumnsStorage[n]; - if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags)) - { - ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX); - for (int column_n = 0; column_n < columns->Columns.Size; column_n++) - ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm)); - ImGui::TreePop(); - } - } - ImGui::TreePop(); - } - ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); - ImGui::TreePop(); - } - }; - - // Access private state, we are going to display the draw lists from last frame - ImGuiContext& g = *GImGui; - Funcs::NodeWindows(g.Windows, "Windows"); - if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size)) - { - for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++) - Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList"); - ImGui::TreePop(); + if (rect_type == WRT_OuterRect) { return window->Rect(); } + else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; } + else if (rect_type == WRT_InnerRect) { return window->InnerRect; } + else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; } + else if (rect_type == WRT_WorkRect) { return window->WorkRect; } + else if (rect_type == WRT_Contents) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); } + else if (rect_type == WRT_ContentsRegionRect) { return window->ContentsRegionRect; } + IM_ASSERT(0); + return ImRect(); } - if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size)) + + static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label) { - for (int i = 0; i < g.OpenPopupStack.Size; i++) + bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size); + if (draw_list == ImGui::GetWindowDrawList()) { - ImGuiWindow* window = g.OpenPopupStack[i].Window; - ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : ""); + ImGui::SameLine(); + ImGui::TextColored(ImVec4(1.0f,0.4f,0.4f,1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered) + if (node_open) ImGui::TreePop(); + return; + } + + ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list + if (window && IsItemHovered()) + fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); + if (!node_open) + return; + + int elem_offset = 0; + for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++) + { + if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0) + continue; + if (pcmd->UserCallback) + { + ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); + continue; + } + ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; + char buf[300]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "Draw %4d triangles, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)", + pcmd->ElemCount/3, (void*)(intptr_t)pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); + bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf); + if (show_drawcmd_clip_rects && fg_draw_list && ImGui::IsItemHovered()) + { + ImRect clip_rect = pcmd->ClipRect; + ImRect vtxs_rect; + for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++) + vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos); + clip_rect.Floor(); fg_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,0,255,255)); + vtxs_rect.Floor(); fg_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,255,0,255)); + } + if (!pcmd_node_open) + continue; + + // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. + ImGui::Text("ElemCount: %d, ElemCount/3: %d, VtxOffset: +%d, IdxOffset: +%d", pcmd->ElemCount, pcmd->ElemCount/3, pcmd->VtxOffset, pcmd->IdxOffset); + ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. + while (clipper.Step()) + for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++) + { + char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf); + ImVec2 triangles_pos[3]; + for (int n = 0; n < 3; n++, idx_i++) + { + int vtx_i = idx_buffer ? idx_buffer[idx_i] : idx_i; + ImDrawVert& v = draw_list->VtxBuffer[vtx_i]; + triangles_pos[n] = v.pos; + buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", + (n == 0) ? "elem" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); + } + ImGui::Selectable(buf, false); + if (fg_draw_list && ImGui::IsItemHovered()) + { + ImDrawListFlags backup_flags = fg_draw_list->Flags; + fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles. + fg_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f); + fg_draw_list->Flags = backup_flags; + } + } + ImGui::TreePop(); } ImGui::TreePop(); } - if (ImGui::TreeNode("Internal state")) + + static void NodeColumns(const ImGuiColumns* columns) { - const char* input_source_names[] = { "None", "Mouse", "Nav", "NavGamepad", "NavKeyboard" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); - ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); - ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); - ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec)", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not - ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), ActiveIdSource: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, input_source_names[g.ActiveIdSource]); - ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); - ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); - ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); - ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); - ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); - ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); + if (!ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags)) + return; + ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX); + for (int column_n = 0; column_n < columns->Columns.Size; column_n++) + ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm)); ImGui::TreePop(); } + + static void NodeWindows(ImVector& windows, const char* label) + { + if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size)) + return; + for (int i = 0; i < windows.Size; i++) + Funcs::NodeWindow(windows[i], "Window"); + ImGui::TreePop(); + } + + static void NodeWindow(ImGuiWindow* window, const char* label) + { + if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) + return; + ImGuiWindowFlags flags = window->Flags; + NodeDrawList(window, window->DrawList, "DrawList"); + ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y); + ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags, + (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", + (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "", + (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); + ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y); + ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); + ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); + ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); + ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); + if (!window->NavRectRel[0].IsInverted()) + ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); + else + ImGui::BulletText("NavRectRel[0]: "); + if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); + if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow"); + if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); + if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) + { + for (int n = 0; n < window->ColumnsStorage.Size; n++) + NodeColumns(&window->ColumnsStorage[n]); + ImGui::TreePop(); + } + ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); + ImGui::TreePop(); + } + + static void NodeTabBar(ImGuiTabBar* tab_bar) + { + // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings. + char buf[256]; + char* p = buf; + const char* buf_end = buf + IM_ARRAYSIZE(buf); + ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : ""); + if (ImGui::TreeNode(tab_bar, "%s", buf)) + { + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + ImGui::PushID(tab); + if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); + if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine(); + ImGui::Text("%02d%c Tab 0x%08X", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID); + ImGui::PopID(); + } + ImGui::TreePop(); + } + } + }; + + // Access private state, we are going to display the draw lists from last frame + ImGuiContext& g = *GImGui; + Funcs::NodeWindows(g.Windows, "Windows"); + if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size)) + { + for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++) + Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList"); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) + { + for (int i = 0; i < g.OpenPopupStack.Size; i++) + { + ImGuiWindow* window = g.OpenPopupStack[i].Window; + ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : ""); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size)) + { + for (int n = 0; n < g.TabBars.Data.Size; n++) + Funcs::NodeTabBar(g.TabBars.GetByIndex(n)); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Internal state")) + { + const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); + ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); + ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); + ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not + ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); + ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); + ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); + ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); + ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); + ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]); + ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); + ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); + ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); + ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); + ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Tools")) + { + ImGui::Checkbox("Show windows begin order", &show_windows_begin_order); + ImGui::Checkbox("Show windows rectangles", &show_windows_rects); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12); + show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count); + if (show_windows_rects && g.NavWindow) + { + ImGui::BulletText("'%s':", g.NavWindow->Name); + ImGui::Indent(); + for (int rect_n = 0; rect_n < WRT_Count; rect_n++) + { + ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n); + ImGui::Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]); + } + ImGui::Unindent(); + } + ImGui::Checkbox("Show clipping rectangle when hovering ImDrawCmd node", &show_drawcmd_clip_rects); + ImGui::TreePop(); + } + + if (show_windows_rects || show_windows_begin_order) + { + for (int n = 0; n < g.Windows.Size; n++) + { + ImGuiWindow* window = g.Windows[n]; + if (!window->WasActive) + continue; + ImDrawList* draw_list = GetForegroundDrawList(window); + if (show_windows_rects) + { + ImRect r = Funcs::GetWindowRect(window, show_windows_rect_type); + draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)); + } + if (show_windows_begin_order && !(window->Flags & ImGuiWindowFlags_ChildWindow)) + { + char buf[32]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); + float font_size = ImGui::GetFontSize(); + draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); + draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf); + } + } } ImGui::End(); } +#else +void ImGui::ShowMetricsWindow(bool*) +{ +} +#endif //----------------------------------------------------------------------------- diff --git a/external/imgui/imgui.h b/external/imgui/imgui.h index 1111014..ec4e135 100644 --- a/external/imgui/imgui.h +++ b/external/imgui/imgui.h @@ -1,14 +1,33 @@ -// dear imgui, v1.60 WIP +// dear imgui, v1.72 WIP // (headers) // See imgui.cpp file for documentation. // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. -// Read 'Programmer guide' in imgui.cpp for notes on how to setup ImGui in your codebase. +// Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. // Get latest version at https://github.com/ocornut/imgui +/* + +Index of this file: +// Header mess +// Forward declarations and basic types +// ImGui API (Dear ImGui end-user API) +// Flags & Enumerations +// Memory allocations macros +// ImVector<> +// ImGuiStyle +// ImGuiIO +// Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) +// Obsolete functions +// Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) +// Draw List API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) +// Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) + +*/ + #pragma once -// User-editable configuration files (edit stock imconfig.h or define IMGUI_USER_CONFIG to your own filename) +// Configuration file with compile-time options (edit imconfig.h or define IMGUI_USER_CONFIG to your own filename) #ifdef IMGUI_USER_CONFIG #include IMGUI_USER_CONFIG #endif @@ -16,210 +35,280 @@ #include "imconfig.h" #endif -#include // FLT_MAX -#include // va_list -#include // ptrdiff_t, NULL -#include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp +//----------------------------------------------------------------------------- +// Header mess +//----------------------------------------------------------------------------- -#define IMGUI_VERSION "1.60 WIP" +#include // FLT_MAX +#include // va_list +#include // ptrdiff_t, NULL +#include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp -// Define attributes of all API symbols declarations, e.g. for DLL under Windows. +// Version +// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) +#define IMGUI_VERSION "1.72 WIP" +#define IMGUI_VERSION_NUM 17101 +#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) + +// Define attributes of all API symbols declarations (e.g. for DLL under Windows) +// IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default bindings files (imgui_impl_xxx.h) +// Using dear imgui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. #ifndef IMGUI_API #define IMGUI_API #endif - -// Define assertion handler. -#ifndef IM_ASSERT -#include -#define IM_ASSERT(_EXPR) assert(_EXPR) +#ifndef IMGUI_IMPL_API +#define IMGUI_IMPL_API IMGUI_API #endif -// Helpers -// Some compilers support applying printf-style warnings to user functions. +// Helper Macros +#ifndef IM_ASSERT +#include +#define IM_ASSERT(_EXPR) assert(_EXPR) // You can override the default assert handler by editing imconfig.h +#endif #if defined(__clang__) || defined(__GNUC__) -#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) +#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) // Apply printf-style warnings to user functions. #define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) #else #define IM_FMTARGS(FMT) #define IM_FMTLIST(FMT) #endif -#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR))) -#define IM_OFFSETOF(_TYPE,_MEMBER) ((size_t)&(((_TYPE*)0)->_MEMBER)) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in modern C++. +#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR))) // Size of a static C-style array. Don't use on pointers! +#define IM_OFFSETOF(_TYPE,_MEMBER) ((size_t)&(((_TYPE*)0)->_MEMBER)) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in modern C++. +#define IM_UNUSED(_VAR) ((void)_VAR) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds. +// Warnings #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind +#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #endif -// Forward declarations -struct ImDrawChannel; // Temporary storage for outputting drawing commands out of order, used by ImDrawList::ChannelsSplit() -struct ImDrawCmd; // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call) -struct ImDrawData; // All draw command lists required to render the frame -struct ImDrawList; // A single draw command list (generally one per window) +//----------------------------------------------------------------------------- +// Forward declarations and basic types +//----------------------------------------------------------------------------- + +struct ImDrawChannel; // Temporary storage to output draw commands out of order, used by ImDrawListSplitter and ImDrawList::ChannelsSplit() +struct ImDrawCmd; // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call, unless it is a callback) +struct ImDrawData; // All draw command lists required to render the frame + pos/size coordinates to use for the projection matrix. +struct ImDrawList; // A single draw command list (generally one per window, conceptually you may see this as a dynamic "mesh" builder) struct ImDrawListSharedData; // Data shared among multiple draw lists (typically owned by parent ImGui context, but you may create one yourself) -struct ImDrawVert; // A single vertex (20 bytes by default, override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT) +struct ImDrawListSplitter; // Helper to split a draw list into different layers which can be drawn into out of order, then flattened back. +struct ImDrawVert; // A single vertex (pos + uv + col = 20 bytes by default. Override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT) struct ImFont; // Runtime data for a single font within a parent ImFontAtlas struct ImFontAtlas; // Runtime data for multiple fonts, bake multiple fonts into a single texture, TTF/OTF font loader struct ImFontConfig; // Configuration data when adding a font or merging fonts -struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 +struct ImFontGlyph; // A single font glyph (code point + coordinates within in ImFontAtlas + offset) +struct ImFontGlyphRangesBuilder; // Helper to build glyph ranges from text/string data +struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*OBSOLETE* please avoid using) +struct ImGuiContext; // Dear ImGui context (opaque structure, unless including imgui_internal.h) struct ImGuiIO; // Main configuration and I/O between your application and ImGui -struct ImGuiOnceUponAFrame; // Simple helper for running a block of code not more than once a frame, used by IMGUI_ONCE_UPON_A_FRAME macro -struct ImGuiStorage; // Simple custom key value storage -struct ImGuiStyle; // Runtime data for styling/colors -struct ImGuiTextFilter; // Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" -struct ImGuiTextBuffer; // Text buffer for logging/accumulating text -struct ImGuiTextEditCallbackData; // Shared state of ImGui::InputText() when using custom ImGuiTextEditCallback (rare/advanced use) -struct ImGuiSizeCallbackData; // Structure used to constraint window size in custom ways when using custom ImGuiSizeCallback (rare/advanced use) +struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiListClipper; // Helper to manually clip large list of items +struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame, used by IMGUI_ONCE_UPON_A_FRAME macro struct ImGuiPayload; // User data payload for drag and drop operations -struct ImGuiContext; // ImGui context (opaque) +struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) +struct ImGuiStorage; // Helper for key->value storage +struct ImGuiStyle; // Runtime data for styling/colors +struct ImGuiTextBuffer; // Helper to hold and append into a text buffer (~string builder) +struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbb][,ccccc]") +// Typedefs and Enums/Flags (declared as int for compatibility with old C++, to allow using as flags and to not pollute the top of this file) +// Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. #ifndef ImTextureID -typedef void* ImTextureID; // user data to identify a texture (this is whatever to you want it to be! read the FAQ about ImTextureID in imgui.cpp) +typedef void* ImTextureID; // User data to identify a texture (this is whatever to you want it to be! read the FAQ about ImTextureID in imgui.cpp) +#endif +typedef unsigned int ImGuiID; // Unique ID used by widgets (typically hashed from a stack of string) +typedef unsigned short ImWchar; // A single U16 character for keyboard input/display. We encode them as multi bytes UTF-8 when used in strings. +typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A color identifier for styling +typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for many Set*() functions +typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type +typedef int ImGuiDir; // -> enum ImGuiDir_ // Enum: A cardinal direction +typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A key identifier (ImGui-side enum) +typedef int ImGuiNavInput; // -> enum ImGuiNavInput_ // Enum: An input identifier for navigation +typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor identifier +typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling +typedef int ImDrawCornerFlags; // -> enum ImDrawCornerFlags_ // Flags: for ImDrawList::AddRect(), AddRectFilled() etc. +typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList +typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas +typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags +typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: for ColorEdit4(), ColorPicker4() etc. +typedef int ImGuiColumnsFlags; // -> enum ImGuiColumnsFlags_ // Flags: for Columns(), BeginColumns() +typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags +typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: for BeginCombo() +typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: for BeginDragDropSource(), AcceptDragDropPayload() +typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused() +typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. +typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline() +typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() +typedef int ImGuiTabBarFlags; // -> enum ImGuiTabBarFlags_ // Flags: for BeginTabBar() +typedef int ImGuiTabItemFlags; // -> enum ImGuiTabItemFlags_ // Flags: for BeginTabItem() +typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: for TreeNode(), TreeNodeEx(), CollapsingHeader() +typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin(), BeginChild() +typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData *data); +typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); + +// Scalar data types +typedef signed char ImS8; // 8-bit signed integer == char +typedef unsigned char ImU8; // 8-bit unsigned integer +typedef signed short ImS16; // 16-bit signed integer +typedef unsigned short ImU16; // 16-bit unsigned integer +typedef signed int ImS32; // 32-bit signed integer == int +typedef unsigned int ImU32; // 32-bit unsigned integer (often used to store packed colors) +#if defined(_MSC_VER) && !defined(__clang__) +typedef signed __int64 ImS64; // 64-bit signed integer (pre and post C++11 with Visual Studio) +typedef unsigned __int64 ImU64; // 64-bit unsigned integer (pre and post C++11 with Visual Studio) +#elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100) +#include +typedef int64_t ImS64; // 64-bit signed integer (pre C++11) +typedef uint64_t ImU64; // 64-bit unsigned integer (pre C++11) +#else +typedef signed long long ImS64; // 64-bit signed integer (post C++11) +typedef unsigned long long ImU64; // 64-bit unsigned integer (post C++11) #endif -// Typedefs and Enumerations (declared as int for compatibility with old C++ and to not pollute the top of this file) -typedef unsigned int ImU32; // 32-bit unsigned integer (typically used to store packed colors) -typedef unsigned int ImGuiID; // unique ID used by widgets (typically hashed from a stack of string) -typedef unsigned short ImWchar; // character for keyboard input/display -typedef int ImGuiCol; // enum: a color identifier for styling // enum ImGuiCol_ -typedef int ImGuiDir; // enum: a cardinal direction // enum ImGuiDir_ -typedef int ImGuiCond; // enum: a condition for Set*() // enum ImGuiCond_ -typedef int ImGuiKey; // enum: a key identifier (ImGui-side enum) // enum ImGuiKey_ -typedef int ImGuiNavInput; // enum: an input identifier for navigation // enum ImGuiNavInput_ -typedef int ImGuiMouseCursor; // enum: a mouse cursor identifier // enum ImGuiMouseCursor_ -typedef int ImGuiStyleVar; // enum: a variable identifier for styling // enum ImGuiStyleVar_ -typedef int ImDrawCornerFlags; // flags: for ImDrawList::AddRect*() etc. // enum ImDrawCornerFlags_ -typedef int ImDrawListFlags; // flags: for ImDrawList // enum ImDrawListFlags_ -typedef int ImFontAtlasFlags; // flags: for ImFontAtlas // enum ImFontAtlasFlags_ -typedef int ImGuiColorEditFlags; // flags: for ColorEdit*(), ColorPicker*() // enum ImGuiColorEditFlags_ -typedef int ImGuiColumnsFlags; // flags: for *Columns*() // enum ImGuiColumnsFlags_ -typedef int ImGuiConfigFlags; // flags: for io.ConfigFlags // enum ImGuiConfigFlags_ -typedef int ImGuiDragDropFlags; // flags: for *DragDrop*() // enum ImGuiDragDropFlags_ -typedef int ImGuiComboFlags; // flags: for BeginCombo() // enum ImGuiComboFlags_ -typedef int ImGuiFocusedFlags; // flags: for IsWindowFocused() // enum ImGuiFocusedFlags_ -typedef int ImGuiHoveredFlags; // flags: for IsItemHovered() etc. // enum ImGuiHoveredFlags_ -typedef int ImGuiInputTextFlags; // flags: for InputText*() // enum ImGuiInputTextFlags_ -typedef int ImGuiSelectableFlags; // flags: for Selectable() // enum ImGuiSelectableFlags_ -typedef int ImGuiTreeNodeFlags; // flags: for TreeNode*(),CollapsingHeader()// enum ImGuiTreeNodeFlags_ -typedef int ImGuiWindowFlags; // flags: for Begin*() // enum ImGuiWindowFlags_ -typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data); -typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); -#if defined(_MSC_VER) && !defined(__clang__) -typedef unsigned __int64 ImU64; // 64-bit unsigned integer -#else -typedef unsigned long long ImU64; // 64-bit unsigned integer -#endif - -// Others helpers at bottom of the file: -// class ImVector<> // Lightweight std::vector like class. -// IMGUI_ONCE_UPON_A_FRAME // Execute a block of code once per frame only (convenient for creating UI within deep-nested code that runs multiple times) - +// 2D vector (often used to store positions, sizes, etc.) struct ImVec2 { - float x, y; - ImVec2() { x = y = 0.0f; } + float x, y; + ImVec2() { x = y = 0.0f; } ImVec2(float _x, float _y) { x = _x; y = _y; } - float operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return (&x)[idx]; } // We very rarely use this [] operator, thus an assert is fine. -#ifdef IM_VEC2_CLASS_EXTRA // Define constructor and implicit cast operators in imconfig.h to convert back<>forth from your math types and ImVec2. - IM_VEC2_CLASS_EXTRA + float operator[] (size_t idx) const { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. + float& operator[] (size_t idx) { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. +#ifdef IM_VEC2_CLASS_EXTRA + IM_VEC2_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2. #endif }; +// 4D vector (often used to store floating-point colors) struct ImVec4 { - float x, y, z, w; - ImVec4() { x = y = z = w = 0.0f; } + float x, y, z, w; + ImVec4() { x = y = z = w = 0.0f; } ImVec4(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; } -#ifdef IM_VEC4_CLASS_EXTRA // Define constructor and implicit cast operators in imconfig.h to convert back<>forth from your math types and ImVec4. - IM_VEC4_CLASS_EXTRA +#ifdef IM_VEC4_CLASS_EXTRA + IM_VEC4_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4. #endif }; -// ImGui end-user API -// In a namespace so that user can add extra functions in a separate file (e.g. Value() helpers for your vector or common types) +//----------------------------------------------------------------------------- +// ImGui: Dear ImGui end-user API +// (Inside a namespace so you can add extra functions in your own separate file. Please don't modify imgui.cpp/.h!) +//----------------------------------------------------------------------------- + namespace ImGui { - // Context creation and access, if you want to use multiple context, share context between modules (e.g. DLL). - // All contexts share a same ImFontAtlas by default. If you want different font atlas, you can new() them and overwrite the GetIO().Fonts variable of an ImGui context. + // Context creation and access + // Each context create its own ImFontAtlas by default. You may instance one yourself and pass it to CreateContext() to share a font atlas between imgui contexts. // All those functions are not reliant on the current context. IMGUI_API ImGuiContext* CreateContext(ImFontAtlas* shared_font_atlas = NULL); - IMGUI_API void DestroyContext(ImGuiContext* ctx = NULL); // NULL = Destroy current context + IMGUI_API void DestroyContext(ImGuiContext* ctx = NULL); // NULL = destroy current context IMGUI_API ImGuiContext* GetCurrentContext(); IMGUI_API void SetCurrentContext(ImGuiContext* ctx); + IMGUI_API bool DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // Main - IMGUI_API ImGuiIO& GetIO(); - IMGUI_API ImGuiStyle& GetStyle(); - IMGUI_API void NewFrame(); // start a new ImGui frame, you can submit any command from this point until Render()/EndFrame(). - IMGUI_API void Render(); // ends the ImGui frame, finalize the draw data. (Obsolete: optionally call io.RenderDrawListsFn if set. Nowadays, prefer calling your render function yourself.) - IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). this is what you have to render. (Obsolete: this used to be passed to your io.RenderDrawListsFn() function.) - IMGUI_API void EndFrame(); // ends the ImGui frame. automatically called by Render(), so most likely don't need to ever call that yourself directly. If you don't need to render you may call EndFrame() but you'll have wasted CPU already. If you don't need to render, better to not create any imgui windows instead! + IMGUI_API ImGuiIO& GetIO(); // access the IO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags) + IMGUI_API ImGuiStyle& GetStyle(); // access the Style structure (colors, sizes). Always use PushStyleCol(), PushStyleVar() to modify style mid-frame. + IMGUI_API void NewFrame(); // start a new Dear ImGui frame, you can submit any command from this point until Render()/EndFrame(). + IMGUI_API void EndFrame(); // ends the Dear ImGui frame. automatically called by Render(), you likely don't need to call that yourself directly. If you don't need to render data (skipping rendering) you may call EndFrame() but you'll have wasted CPU already! If you don't need to render, better to not create any imgui windows and not call NewFrame() at all! + IMGUI_API void Render(); // ends the Dear ImGui frame, finalize the draw data. You can get call GetDrawData() to obtain it and run your rendering function. (Obsolete: this used to call io.RenderDrawListsFn(). Nowadays, we allow and prefer calling your render function yourself.) + IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). this is what you have to render. - // Demo, Debug, Informations - IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create demo/test window (previously called ShowTestWindow). demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! - IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create metrics window. display ImGui internals: draw commands (with individual draw calls and vertices), window list, basic internal state, etc. + // Demo, Debug, Information + IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window (previously called ShowTestWindow). demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! + IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information. + IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debug window. display Dear ImGui internals: draw commands (with individual draw calls and vertices), window list, basic internal state, etc. IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) - IMGUI_API bool ShowStyleSelector(const char* label); - IMGUI_API void ShowFontSelector(const char* label); + IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. + IMGUI_API void ShowFontSelector(const char* label); // add font selector block (not a window), essentially a combo listing the loaded fonts. IMGUI_API void ShowUserGuide(); // add basic help/info block (not a window): how to manipulate ImGui as a end-user (mouse/keyboard controls). - IMGUI_API const char* GetVersion(); + IMGUI_API const char* GetVersion(); // get the compiled version string e.g. "1.23" (essentially the compiled value for IMGUI_VERSION) // Styles - IMGUI_API void StyleColorsDark(ImGuiStyle* dst = NULL); // New, recommended style - IMGUI_API void StyleColorsClassic(ImGuiStyle* dst = NULL); // Classic imgui style (default) - IMGUI_API void StyleColorsLight(ImGuiStyle* dst = NULL); // Best used with borders and a custom, thicker font + IMGUI_API void StyleColorsDark(ImGuiStyle* dst = NULL); // new, recommended style (default) + IMGUI_API void StyleColorsClassic(ImGuiStyle* dst = NULL); // classic imgui style + IMGUI_API void StyleColorsLight(ImGuiStyle* dst = NULL); // best used with borders and a custom, thicker font - // Window - IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // push window to the stack and start appending to it. see .cpp for details. return false when window is collapsed (so you can early out in your code) but you always need to call End() regardless. 'bool* p_open' creates a widget on the upper-right to close the window (which sets your bool to false). - IMGUI_API void End(); // always call even if Begin() return false (which indicates a collapsed window)! finish appending to current window, pop it off the window stack. - IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0); // begin a scrolling region. size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). size>0.0f: fixed size. each axis can use a different mode, e.g. ImVec2(0,400). - IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0); // " - IMGUI_API void EndChild(); // always call even if BeginChild() return false (which indicates a collapsed or clipping child window) - IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates - IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() - IMGUI_API float GetContentRegionAvailWidth(); // - IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates - IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates - IMGUI_API float GetWindowContentRegionWidth(); // - IMGUI_API ImDrawList* GetWindowDrawList(); // get rendering command-list if you want to append your own draw primitives - IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList api) - IMGUI_API ImVec2 GetWindowSize(); // get current window size - IMGUI_API float GetWindowWidth(); - IMGUI_API float GetWindowHeight(); - IMGUI_API bool IsWindowCollapsed(); + // Windows + // - Begin() = push window to the stack and start appending to it. End() = pop window from the stack. + // - You may append multiple times to the same window during the same frame. + // - Passing 'bool* p_open != NULL' shows a window-closing widget in the upper-right corner of the window, + // which clicking will set the boolean to false when clicked. + // - Begin() return false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting + // anything to the window. Always call a matching End() for each Begin() call, regardless of its return value! + // [this is due to legacy reason and is inconsistent with most other functions such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. + // where the EndXXX call should only be called if the corresponding BeginXXX function returned true.] + // - Note that the bottom of window stack always contains a window called "Debug". + IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); + IMGUI_API void End(); + + // Child Windows + // - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child. + // - For each independent axis of 'size': ==0.0f: use remaining host window size / >0.0f: fixed size / <0.0f: use remaining window size minus abs(size) / Each axis can use a different mode, e.g. ImVec2(0,400). + // - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting anything to the window. + // Always call a matching EndChild() for each BeginChild() call, regardless of its return value [this is due to legacy reason and is inconsistent with most other functions such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function returned true.] + IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0); + IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0); + IMGUI_API void EndChild(); + + // Windows Utilities + // - "current window" = the window we are appending into while inside a Begin()/End() block. "next window" = next window we will Begin() into. IMGUI_API bool IsWindowAppearing(); - IMGUI_API void SetWindowFontScale(float scale); // per-window font scale. Adjust IO.FontGlobalScale if you want to scale all windows + IMGUI_API bool IsWindowCollapsed(); + IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. + IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! + IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives + IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList API) + IMGUI_API ImVec2 GetWindowSize(); // get current window size + IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) + IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) + // Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0,0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc. - IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() - IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Use callback to apply non-trivial programmatic constraints. - IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (~ enforce the range of scrollbars). not including window decorations (title bar, menu bar, etc.). set an axis to 0.0f to leave it automatic. call before Begin() - IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // set next window collapsed state. call before Begin() - IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most. call before Begin() - IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. - IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. - IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0,0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. - IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). - IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / front-most. prefer using SetNextWindowFocus(). + IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() + IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Sizes will be rounded down. Use callback to apply non-trivial programmatic constraints. + IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (~ scrollable client area, which enforce the range of scrollbars). Not including window decorations (title bar, menu bar, etc.) nor WindowPadding. set an axis to 0.0f to leave it automatic. call before Begin() + IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // set next window collapsed state. call before Begin() + IMGUI_API void SetNextWindowFocus(); // set next window to be focused / top-most. call before Begin() + IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground. + IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. + IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0,0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. + IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). + IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / top-most. prefer using SetNextWindowFocus(). + IMGUI_API void SetWindowFontScale(float scale); // set font scale. Adjust IO.FontGlobalScale if you want to scale all windows IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0); // set named window position. IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond = 0); // set named window size. set axis to 0.0f to force an auto-fit on this axis. IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0); // set named window collapsed state - IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / front-most. use NULL to remove focus. + IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / top-most. use NULL to remove focus. - IMGUI_API float GetScrollX(); // get scrolling amount [0..GetScrollMaxX()] - IMGUI_API float GetScrollY(); // get scrolling amount [0..GetScrollMaxY()] - IMGUI_API float GetScrollMaxX(); // get maximum scrolling amount ~~ ContentSize.X - WindowSize.X - IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.Y - WindowSize.Y - IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0..GetScrollMaxX()] - IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] - IMGUI_API void SetScrollHere(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. - IMGUI_API void SetScrollFromPosY(float pos_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions. - IMGUI_API void SetStateStorage(ImGuiStorage* tree); // replace tree state storage with our own (if you want to manipulate it yourself, typically clear subsection of it) - IMGUI_API ImGuiStorage* GetStateStorage(); + // Content region + // - Those functions are bound to be redesigned soon (they are confusing, incomplete and return values in local window coordinates which increases confusion) + IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates + IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() + IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates + IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates + IMGUI_API float GetWindowContentRegionWidth(); // + + // Windows Scrolling + IMGUI_API float GetScrollX(); // get scrolling amount [0..GetScrollMaxX()] + IMGUI_API float GetScrollY(); // get scrolling amount [0..GetScrollMaxY()] + IMGUI_API float GetScrollMaxX(); // get maximum scrolling amount ~~ ContentSize.X - WindowSize.X + IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.Y - WindowSize.Y + IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0..GetScrollMaxX()] + IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] + IMGUI_API void SetScrollHereX(float center_x_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_x_ratio=0.0: left, 0.5: center, 1.0: right. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. + IMGUI_API void SetScrollHereY(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. + IMGUI_API void SetScrollFromPosX(float local_x, float center_x_ratio = 0.5f); // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position. + IMGUI_API void SetScrollFromPosY(float local_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position. // Parameters stacks (shared) - IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font + IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font IMGUI_API void PopFont(); IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col); IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); @@ -227,52 +316,258 @@ namespace ImGui IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); IMGUI_API void PopStyleVar(int count = 1); - IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwhise use GetColorU32() to get style color + style alpha. - IMGUI_API ImFont* GetFont(); // get current font - IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied - IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API - IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier - IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied - IMGUI_API ImU32 GetColorU32(ImU32 col); // retrieve given color with style alpha applied + IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in. + IMGUI_API ImFont* GetFont(); // get current font + IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied + IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API + IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier + IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied + IMGUI_API ImU32 GetColorU32(ImU32 col); // retrieve given color with style alpha applied // Parameters stacks (current window) - IMGUI_API void PushItemWidth(float item_width); // width of items for the common item+label case, pixels. 0.0f = default to ~2/3 of windows width, >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side) + IMGUI_API void PushItemWidth(float item_width); // set width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side). 0.0f = default to ~2/3 of windows width, IMGUI_API void PopItemWidth(); - IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position - IMGUI_API void PushTextWrapPos(float wrap_pos_x = 0.0f); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space + IMGUI_API void SetNextItemWidth(float item_width); // set width of the _next_ common large "item+label" widget. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side) + IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position + IMGUI_API void PushTextWrapPos(float wrap_local_pos_x = 0.0f); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space IMGUI_API void PopTextWrapPos(); - IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets + IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets IMGUI_API void PopAllowKeyboardFocus(); - IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. + IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. IMGUI_API void PopButtonRepeat(); // Cursor / Layout - IMGUI_API void Separator(); // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator. - IMGUI_API void SameLine(float pos_x = 0.0f, float spacing_w = -1.0f); // call between widgets or groups to layout them horizontally - IMGUI_API void NewLine(); // undo a SameLine() - IMGUI_API void Spacing(); // add vertical spacing - IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size - IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by style.IndentSpacing or indent_w if != 0 - IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by style.IndentSpacing or indent_w if != 0 - IMGUI_API void BeginGroup(); // lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) - IMGUI_API void EndGroup(); - IMGUI_API ImVec2 GetCursorPos(); // cursor position is relative to window position - IMGUI_API float GetCursorPosX(); // " - IMGUI_API float GetCursorPosY(); // " - IMGUI_API void SetCursorPos(const ImVec2& local_pos); // " - IMGUI_API void SetCursorPosX(float x); // " - IMGUI_API void SetCursorPosY(float y); // " - IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position - IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute screen coordinates [0..io.DisplaySize] (useful to work with ImDrawList API) - IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] - IMGUI_API void AlignTextToFramePadding(); // vertically align/lower upcoming text to FramePadding.y so that it will aligns to upcoming widgets (call if you have text on a line before regular widgets) - IMGUI_API float GetTextLineHeight(); // ~ FontSize - IMGUI_API float GetTextLineHeightWithSpacing(); // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text) - IMGUI_API float GetFrameHeight(); // ~ FontSize + style.FramePadding.y * 2 - IMGUI_API float GetFrameHeightWithSpacing(); // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets) + // - By "cursor" we mean the current output position. + // - The typical widget behavior is to output themselves at the current cursor position, then move the cursor one line down. + IMGUI_API void Separator(); // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator. + IMGUI_API void SameLine(float offset_from_start_x=0.0f, float spacing=-1.0f); // call between widgets or groups to layout them horizontally. X position given in window coordinates. + IMGUI_API void NewLine(); // undo a SameLine() or force a new line when in an horizontal-layout context. + IMGUI_API void Spacing(); // add vertical spacing. + IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size. unlike InvisibleButton(), Dummy() won't take the mouse click or be navigable into. + IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by style.IndentSpacing or indent_w if != 0 + IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by style.IndentSpacing or indent_w if != 0 + IMGUI_API void BeginGroup(); // lock horizontal starting position + IMGUI_API void EndGroup(); // unlock horizontal starting position + capture the whole group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) + IMGUI_API ImVec2 GetCursorPos(); // cursor position in window coordinates (relative to window position) + IMGUI_API float GetCursorPosX(); // (some functions are using window-relative coordinates, such as: GetCursorPos, GetCursorStartPos, GetContentRegionMax, GetWindowContentRegion* etc. + IMGUI_API float GetCursorPosY(); // other functions such as GetCursorScreenPos or everything in ImDrawList:: + IMGUI_API void SetCursorPos(const ImVec2& local_pos); // are using the main, absolute coordinate system. + IMGUI_API void SetCursorPosX(float local_x); // GetWindowPos() + GetCursorPos() == GetCursorScreenPos() etc.) + IMGUI_API void SetCursorPosY(float local_y); // + IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position in window coordinates + IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute screen coordinates [0..io.DisplaySize] (useful to work with ImDrawList API) + IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] + IMGUI_API void AlignTextToFramePadding(); // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item) + IMGUI_API float GetTextLineHeight(); // ~ FontSize + IMGUI_API float GetTextLineHeightWithSpacing(); // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text) + IMGUI_API float GetFrameHeight(); // ~ FontSize + style.FramePadding.y * 2 + IMGUI_API float GetFrameHeightWithSpacing(); // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets) + + // ID stack/scopes + // - Read the FAQ for more details about how ID are handled in dear imgui. If you are creating widgets in a loop you most + // likely want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. + // - The resulting ID are hashes of the entire stack. + // - You can also use the "Label##foobar" syntax within widget label to distinguish them from each others. + // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed and used as an ID, + // whereas "str_id" denote a string that is only used as an ID and not normally displayed. + IMGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string). + IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string). + IMGUI_API void PushID(const void* ptr_id); // push pointer into the ID stack (will hash pointer). + IMGUI_API void PushID(int int_id); // push integer into the ID stack (will hash integer). + IMGUI_API void PopID(); // pop from the ID stack. + IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself + IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end); + IMGUI_API ImGuiID GetID(const void* ptr_id); + + // Widgets: Text + IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text. + IMGUI_API void Text(const char* fmt, ...) IM_FMTARGS(1); // simple formatted text + IMGUI_API void TextV(const char* fmt, va_list args) IM_FMTLIST(1); + IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...) IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor(); + IMGUI_API void TextColoredV(const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(2); + IMGUI_API void TextDisabled(const char* fmt, ...) IM_FMTARGS(1); // shortcut for PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); Text(fmt, ...); PopStyleColor(); + IMGUI_API void TextDisabledV(const char* fmt, va_list args) IM_FMTLIST(1); + IMGUI_API void TextWrapped(const char* fmt, ...) IM_FMTARGS(1); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();. Note that this won't work on an auto-resizing window if there's no other widgets to extend the window width, yoy may need to set a size using SetNextWindowSize(). + IMGUI_API void TextWrappedV(const char* fmt, va_list args) IM_FMTLIST(1); + IMGUI_API void LabelText(const char* label, const char* fmt, ...) IM_FMTARGS(2); // display text+label aligned the same way as value+label widgets + IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args) IM_FMTLIST(2); + IMGUI_API void BulletText(const char* fmt, ...) IM_FMTARGS(1); // shortcut for Bullet()+Text() + IMGUI_API void BulletTextV(const char* fmt, va_list args) IM_FMTLIST(1); + + // Widgets: Main + // - Most widgets return true when the value has been changed or when pressed/selected + IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0)); // button + IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text + IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); // button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) + IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape + IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); + IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding + IMGUI_API bool Checkbox(const char* label, bool* v); + IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); + IMGUI_API bool RadioButton(const char* label, bool active); // use with e.g. if (RadioButton("one", my_value==1)) { my_value = 1; } + IMGUI_API bool RadioButton(const char* label, int* v, int v_button); // shortcut to handle the above pattern when value is an integer + IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-1,0), const char* overlay = NULL); + IMGUI_API void Bullet(); // draw a small circle and keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses + + // Widgets: Combo Box + // - The new BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items. + // - The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. + IMGUI_API bool BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0); + IMGUI_API void EndCombo(); // only call EndCombo() if BeginCombo() returns true! + IMGUI_API bool Combo(const char* label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1); + IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1); // Separate items with \0 within a string, end item-list with \0\0. e.g. "One\0Two\0Three\0" + IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1); + + // Widgets: Drags + // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x + // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. + // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). + IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound + IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); + IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); + IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); + IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", const char* format_max = NULL, float power = 1.0f); + IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); // If v_min >= v_max we have no bound + IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); + IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); + IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); + IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", const char* format_max = NULL); + IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* v, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f); + IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f); + + // Widgets: Sliders + // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. + IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for power curve sliders + IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); + IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); + IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); + IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f, const char* format = "%.0f deg"); + IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = "%d"); + IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format = "%d"); + IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format = "%d"); + IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format = "%d"); + IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f); + IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f); + IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); + IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format = "%d"); + IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f); + + // Widgets: Input with Keyboard + // - If you want to use InputText() with a dynamic string type such as std::string or your own, see misc/cpp/imgui_stdlib.h + // - Most of the ImGuiInputTextFlags flags are only useful for InputText() and not for InputFloatX, InputIntX, InputDouble etc. + IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0,0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputFloat2(const char* label, float v[2], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputFloat3(const char* label, float v[3], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputFloat4(const char* label, float v[4], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0, double step_fast = 0.0, const char* format = "%.6f", ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputScalar(const char* label, ImGuiDataType data_type, void* v, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); + + // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little colored preview square that can be left-clicked to open a picker, and right-clicked to open an option menu.) + // - Note that in C++ a 'float v[X]' function argument is the _same_ as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. + // - You can pass the address of a first float element out of a contiguous structure, e.g. &myvector.x + IMGUI_API bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); + IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0); + IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); + IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL); + IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0)); // display a colored square/button, hover for details, return true when pressed. + IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls. + + // Widgets: Trees + // - TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents. + IMGUI_API bool TreeNode(const char* label); + IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to easily decorelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). + IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2); // " + IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args) IM_FMTLIST(2); + IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args) IM_FMTLIST(2); + IMGUI_API bool TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags = 0); + IMGUI_API bool TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); + IMGUI_API bool TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); + IMGUI_API bool TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); + IMGUI_API bool TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); + IMGUI_API void TreePush(const char* str_id); // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired. + IMGUI_API void TreePush(const void* ptr_id = NULL); // " + IMGUI_API void TreePop(); // ~ Unindent()+PopId() + IMGUI_API void TreeAdvanceToLabelPos(); // advance cursor x position by GetTreeNodeToLabelSpacing() + IMGUI_API float GetTreeNodeToLabelSpacing(); // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode + IMGUI_API bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0); // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop(). + IMGUI_API bool CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags = 0); // when 'p_open' isn't NULL, display an additional small close button on upper right of the header + IMGUI_API void SetNextItemOpen(bool is_open, ImGuiCond cond = 0); // set next TreeNode/CollapsingHeader open state. + + // Widgets: Selectables + // - A selectable highlights when hovered, and can display another color when selected. + // - Neighbors selectable extend their highlight bounds in order to leave no gap between them. + IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height + IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. + + // Widgets: List Boxes + // - FIXME: To be consistent with all the newer API, ListBoxHeader/ListBoxFooter should in reality be called BeginListBox/EndListBox. Will rename them. + IMGUI_API bool ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1); + IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); + IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0,0)); // use if you want to reimplement ListBox() will custom data or interactions. if the function return true, you can output elements then call ListBoxFooter() afterwards. + IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // " + IMGUI_API void ListBoxFooter(); // terminate the scrolling region. only call ListBoxFooter() if ListBoxHeader() returned true! + + // Widgets: Data Plotting + IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); + IMGUI_API void PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); + IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); + IMGUI_API void PlotHistogram(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); + + // Widgets: Value() Helpers. + // - Those are merely shortcut to calling Text() with a format string. Output single value in "name: value" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace) + IMGUI_API void Value(const char* prefix, bool b); + IMGUI_API void Value(const char* prefix, int v); + IMGUI_API void Value(const char* prefix, unsigned int v); + IMGUI_API void Value(const char* prefix, float v, const char* float_format = NULL); + + // Widgets: Menus + IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. + IMGUI_API void EndMainMenuBar(); // only call EndMainMenuBar() if BeginMainMenuBar() returns true! + IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window). + IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true! + IMGUI_API bool BeginMenu(const char* label, bool enabled = true); // create a sub-menu entry. only call EndMenu() if this returns true! + IMGUI_API void EndMenu(); // only call EndMenu() if BeginMenu() returns true! + IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. shortcuts are displayed for convenience but not processed by ImGui at the moment + IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL + + // Tooltips + IMGUI_API void BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items). + IMGUI_API void EndTooltip(); + IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). override any previous call to SetTooltip(). + IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); + + // Popups, Modals + // The properties of popups windows are: + // - They block normal mouse hovering detection outside them. (*) + // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE. + // - Their visibility state (~bool) is held internally by imgui instead of being held by the programmer as we are used to with regular Begin() calls. + // User can manipulate the visibility state by calling OpenPopup(). + // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even when normally blocked by a popup. + // Those three properties are connected. The library needs to hold their visibility state because it can close popups at any time. + IMGUI_API void OpenPopup(const char* str_id); // call to mark popup as open (don't call every frame!). popups are closed when user click outside, or if CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. By default, Selectable()/MenuItem() are calling CloseCurrentPopup(). Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). + IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returns true! + IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked on last item. if you can pass a NULL str_id only if the previous item had an id. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! + IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, int mouse_button = 1, bool also_over_items = true); // helper to open and begin popup when clicked on current window. + IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked in void (where there are no imgui windows). + IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // modal dialog (regular window with title bar, block interactions behind the modal window, can't close the modal window by clicking outside) + IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! + IMGUI_API bool OpenPopupOnItemClick(const char* str_id = NULL, int mouse_button = 1); // helper to open popup when clicked on last item (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors). return true when just opened. + IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open at the current begin-ed level of the popup stack. + IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup. // Columns - // You can also use SameLine(pos_x) for simplified columns. The columns API is still work-in-progress and rather lacking. + // - You can also use SameLine(pos_x) to mimic simplified columns. + // - The columns API is work-in-progress and rather lacking (columns are arguably the worst part of dear imgui at the moment!) IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished IMGUI_API int GetColumnIndex(); // get current column index @@ -282,313 +577,187 @@ namespace ImGui IMGUI_API void SetColumnOffset(int column_index, float offset_x); // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column IMGUI_API int GetColumnsCount(); - // ID scopes - // If you are creating widgets in a loop you most likely want to push a unique identifier (e.g. object pointer, loop index) so ImGui can differentiate them. - // You can also use the "##foobar" syntax within widget label to distinguish them from each others. Read "A primer on the use of labels/IDs" in the FAQ for more details. - IMGUI_API void PushID(const char* str_id); // push identifier into the ID stack. IDs are hash of the entire stack! - IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); - IMGUI_API void PushID(const void* ptr_id); - IMGUI_API void PushID(int int_id); - IMGUI_API void PopID(); - IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself - IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end); - IMGUI_API ImGuiID GetID(const void* ptr_id); + // Tab Bars, Tabs + // [BETA API] API may evolve! + IMGUI_API bool BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0); // create and append into a TabBar + IMGUI_API void EndTabBar(); // only call EndTabBar() if BeginTabBar() returns true! + IMGUI_API bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0);// create a Tab. Returns true if the Tab is selected. + IMGUI_API void EndTabItem(); // only call EndTabItem() if BeginTabItem() returns true! + IMGUI_API void SetTabItemClosed(const char* tab_or_docked_window_label); // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name. - // Widgets: Text - IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text. - IMGUI_API void Text(const char* fmt, ...) IM_FMTARGS(1); // simple formatted text - IMGUI_API void TextV(const char* fmt, va_list args) IM_FMTLIST(1); - IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...) IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor(); - IMGUI_API void TextColoredV(const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(2); - IMGUI_API void TextDisabled(const char* fmt, ...) IM_FMTARGS(1); // shortcut for PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); Text(fmt, ...); PopStyleColor(); - IMGUI_API void TextDisabledV(const char* fmt, va_list args) IM_FMTLIST(1); - IMGUI_API void TextWrapped(const char* fmt, ...) IM_FMTARGS(1); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();. Note that this won't work on an auto-resizing window if there's no other widgets to extend the window width, yoy may need to set a size using SetNextWindowSize(). - IMGUI_API void TextWrappedV(const char* fmt, va_list args) IM_FMTLIST(1); - IMGUI_API void LabelText(const char* label, const char* fmt, ...) IM_FMTARGS(2); // display text+label aligned the same way as value+label widgets - IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args) IM_FMTLIST(2); - IMGUI_API void BulletText(const char* fmt, ...) IM_FMTARGS(1); // shortcut for Bullet()+Text() - IMGUI_API void BulletTextV(const char* fmt, va_list args) IM_FMTLIST(1); - IMGUI_API void Bullet(); // draw a small circle and keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses - - // Widgets: Main - IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0)); // button - IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text - IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); - IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); // button behavior without the visuals, useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) - IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); - IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding - IMGUI_API bool Checkbox(const char* label, bool* v); - IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); - IMGUI_API bool RadioButton(const char* label, bool active); - IMGUI_API bool RadioButton(const char* label, int* v, int v_button); - IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), int stride = sizeof(float)); - IMGUI_API void PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); - IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), int stride = sizeof(float)); - IMGUI_API void PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); - IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-1,0), const char* overlay = NULL); - - // Widgets: Combo Box - // The new BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it. - // The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. - IMGUI_API bool BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0); - IMGUI_API void EndCombo(); // only call EndCombo() if BeginCombo() returns true! - IMGUI_API bool Combo(const char* label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1); - IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1); // Separate items with \0 within a string, end item-list with \0\0. e.g. "One\0Two\0Three\0" - IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1); - - // Widgets: Drags (tip: ctrl+click on a drag box to input with keyboard. manually input values aren't clamped, can go off-bounds) - // For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x - // Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). - IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound - IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); - IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); - IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); - IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", const char* display_format_max = NULL, float power = 1.0f); - IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); // If v_min >= v_max we have no bound - IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); - IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); - IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); - IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f", const char* display_format_max = NULL); - - // Widgets: Input with Keyboard - IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0,0), ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags = 0); - - // Widgets: Sliders (tip: ctrl+click on a slider to input with keyboard. manually input values aren't clamped, can go off-bounds) - IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for logarithmic sliders - IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); - IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); - IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); - IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f); - IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format = "%.0f"); - IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format = "%.0f"); - IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format = "%.0f"); - IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format = "%.0f"); - IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); - IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format = "%.0f"); - - // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little colored preview square that can be left-clicked to open a picker, and right-clicked to open an option menu.) - // Note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can the pass the address of a first float element out of a contiguous structure, e.g. &myvector.x - IMGUI_API bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); - IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0); - IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); - IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL); - IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0)); // display a colored square/button, hover for details, return true when pressed. - IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls. - - // Widgets: Trees - IMGUI_API bool TreeNode(const char* label); // if returning 'true' the node is open and the tree id is pushed into the id stack. user is responsible for calling TreePop(). - IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). - IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2); // " - IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args) IM_FMTLIST(2); - IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args) IM_FMTLIST(2); - IMGUI_API bool TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags = 0); - IMGUI_API bool TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); - IMGUI_API bool TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); - IMGUI_API bool TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); - IMGUI_API bool TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); - IMGUI_API void TreePush(const char* str_id); // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call Push/Pop yourself for layout purpose - IMGUI_API void TreePush(const void* ptr_id = NULL); // " - IMGUI_API void TreePop(); // ~ Unindent()+PopId() - IMGUI_API void TreeAdvanceToLabelPos(); // advance cursor x position by GetTreeNodeToLabelSpacing() - IMGUI_API float GetTreeNodeToLabelSpacing(); // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode - IMGUI_API void SetNextTreeNodeOpen(bool is_open, ImGuiCond cond = 0); // set next TreeNode/CollapsingHeader open state. - IMGUI_API bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0); // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop(). - IMGUI_API bool CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags = 0); // when 'p_open' isn't NULL, display an additional small close button on upper right of the header - - // Widgets: Selectable / Lists - IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height - IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. - IMGUI_API bool ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1); - IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); - IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0,0)); // use if you want to reimplement ListBox() will custom data or interactions. make sure to call ListBoxFooter() afterwards. - IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // " - IMGUI_API void ListBoxFooter(); // terminate the scrolling region - - // Widgets: Value() Helpers. Output single value in "name: value" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace) - IMGUI_API void Value(const char* prefix, bool b); - IMGUI_API void Value(const char* prefix, int v); - IMGUI_API void Value(const char* prefix, unsigned int v); - IMGUI_API void Value(const char* prefix, float v, const char* float_format = NULL); - - // Tooltips - IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set text tooltip under mouse-cursor, typically use with ImGui::IsItemHovered(). overidde any previous call to SetTooltip(). - IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); - IMGUI_API void BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of contents). - IMGUI_API void EndTooltip(); - - // Menus - IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. - IMGUI_API void EndMainMenuBar(); // only call EndMainMenuBar() if BeginMainMenuBar() returns true! - IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window). - IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true! - IMGUI_API bool BeginMenu(const char* label, bool enabled = true); // create a sub-menu entry. only call EndMenu() if this returns true! - IMGUI_API void EndMenu(); // only call EndBegin() if BeginMenu() returns true! - IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. shortcuts are displayed for convenience but not processed by ImGui at the moment - IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL - - // Popups - IMGUI_API void OpenPopup(const char* str_id); // call to mark popup as open (don't call every frame!). popups are closed when user click outside, or if CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. By default, Selectable()/MenuItem() are calling CloseCurrentPopup(). Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). - IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returns true! - IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked on last item. if you can pass a NULL str_id only if the previous item had an id. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! - IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, int mouse_button = 1, bool also_over_items = true); // helper to open and begin popup when clicked on current window. - IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked in void (where there are no imgui windows). - IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // modal dialog (regular window with title bar, block interactions behind the modal window, can't close the modal window by clicking outside) - IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! - IMGUI_API bool OpenPopupOnItemClick(const char* str_id = NULL, int mouse_button = 1); // helper to open popup when clicked on last item. return true when just opened. - IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open - IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup. - - // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. - IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty - IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); // start logging to file - IMGUI_API void LogToClipboard(int max_depth = -1); // start logging to OS clipboard + // Logging/Capture + // - All text output from the interface can be captured into tty/file/clipboard. By default, tree nodes are automatically opened during logging. + IMGUI_API void LogToTTY(int auto_open_depth = -1); // start logging to tty (stdout) + IMGUI_API void LogToFile(int auto_open_depth = -1, const char* filename = NULL); // start logging to file + IMGUI_API void LogToClipboard(int auto_open_depth = -1); // start logging to OS clipboard IMGUI_API void LogFinish(); // stop logging (close file, etc.) IMGUI_API void LogButtons(); // helper to display buttons for logging to tty/file/clipboard IMGUI_API void LogText(const char* fmt, ...) IM_FMTARGS(1); // pass text data straight to log (without being displayed) // Drag and Drop - // [BETA API] Missing Demo code. API may evolve. + // [BETA API] API may evolve! IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call when the current item is active. If this return true, you can call SetDragDropPayload() + EndDragDropSource() - IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t size, ImGuiCond cond = 0);// type is a user defined string of maximum 12 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. + IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true! - IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive an item. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() - IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. - IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! + IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() + IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. + IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! + IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. may return NULL. use ImGuiPayload::IsDataType() to test for the payload type. // Clipping IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); IMGUI_API void PopClipRect(); // Focus, Activation - // (Prefer using "SetItemDefaultFocus()" over "if (IsWindowAppearing()) SetScrollHere()" when applicable, to make your code more forward compatible when navigation branch is merged) - IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. Please use instead of "if (IsWindowAppearing()) SetScrollHere()" to signify "default item". + // - Prefer using "SetItemDefaultFocus()" over "if (IsWindowAppearing()) SetScrollHereY()" when applicable to signify "this is the default item" + IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. - // Utilities + // Item/Widgets Utilities + // - Most of the functions are referring to the last/previous item we submitted. + // - See Demo Window under "Widgets->Querying Status" for an interactive visualization of most of those functions. IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags = 0); // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options. - IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) + IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited. This will continuously return true while holding mouse button on an item. Items that don't interact will always return false) IMGUI_API bool IsItemFocused(); // is the last item focused for keyboard/gamepad navigation? - IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) - IMGUI_API bool IsItemVisible(); // is the last item visible? (aka not out of sight due to clipping/scrolling.) - IMGUI_API bool IsAnyItemHovered(); - IMGUI_API bool IsAnyItemActive(); - IMGUI_API bool IsAnyItemFocused(); - IMGUI_API ImVec2 GetItemRectMin(); // get bounding rectangle of last item, in screen space - IMGUI_API ImVec2 GetItemRectMax(); // " - IMGUI_API ImVec2 GetItemRectSize(); // get size of last item, in screen space + IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) == IsMouseClicked(mouse_button) && IsItemHovered() + IMGUI_API bool IsItemVisible(); // is the last item visible? (items may be out of sight because of clipping/scrolling) + IMGUI_API bool IsItemEdited(); // did the last item modify its underlying value this frame? or was pressed? This is generally the same as the "bool" return value of many widgets. + IMGUI_API bool IsItemActivated(); // was the last item just made active (item was previously inactive). + IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that requires continuous editing. + IMGUI_API bool IsItemDeactivatedAfterEdit(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that requires continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item). + IMGUI_API bool IsAnyItemHovered(); // is any item hovered? + IMGUI_API bool IsAnyItemActive(); // is any item active? + IMGUI_API bool IsAnyItemFocused(); // is any item focused? + IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space) + IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space) + IMGUI_API ImVec2 GetItemRectSize(); // get size of last item IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. - IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags = 0); // is current window focused? or its root/child, depending on flags. see flags for options. - IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags = 0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! + + // Miscellaneous Utilities IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. - IMGUI_API float GetTime(); - IMGUI_API int GetFrameCount(); - IMGUI_API ImDrawList* GetOverlayDrawList(); // this draw list will be the last rendered one, useful to quickly draw overlays shapes/text - IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); - IMGUI_API const char* GetStyleColorName(ImGuiCol idx); + IMGUI_API double GetTime(); // get global imgui time. incremented by io.DeltaTime every frame. + IMGUI_API int GetFrameCount(); // get global imgui frame count. incremented by 1 every frame. + IMGUI_API ImDrawList* GetBackgroundDrawList(); // this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. + IMGUI_API ImDrawList* GetForegroundDrawList(); // this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. + IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances. + IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.). + IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) + IMGUI_API ImGuiStorage* GetStateStorage(); IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // calculate coarse clipping for large list of evenly sized items. Prefer using the ImGuiListClipper higher-level helper if you can. - IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window) + // Color Utilities IMGUI_API ImVec4 ColorConvertU32ToFloat4(ImU32 in); IMGUI_API ImU32 ColorConvertFloat4ToU32(const ImVec4& in); IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v); IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); - // Inputs + // Inputs Utilities IMGUI_API int GetKeyIndex(ImGuiKey imgui_key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] - IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index]. note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your backend/engine stored them into KeyDown[]! + IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index]. note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]! IMGUI_API bool IsKeyPressed(int user_key_index, bool repeat = true); // was key pressed (went from !Down to Down). if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate IMGUI_API bool IsKeyReleased(int user_key_index); // was key released (went from Down to !Down).. IMGUI_API int GetKeyPressedAmount(int key_index, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate - IMGUI_API bool IsMouseDown(int button); // is mouse button held + IMGUI_API bool IsMouseDown(int button); // is mouse button held (0=left, 1=right, 2=middle) IMGUI_API bool IsAnyMouseDown(); // is any mouse button held - IMGUI_API bool IsMouseClicked(int button, bool repeat = false); // did mouse button clicked (went from !Down to Down) + IMGUI_API bool IsMouseClicked(int button, bool repeat = false); // did mouse button clicked (went from !Down to Down) (0=left, 1=right, 2=middle) IMGUI_API bool IsMouseDoubleClicked(int button); // did mouse button double-clicked. a double-click returns false in IsMouseClicked(). uses io.MouseDoubleClickTime. IMGUI_API bool IsMouseReleased(int button); // did mouse button released (went from Down to !Down) IMGUI_API bool IsMouseDragging(int button = 0, float lock_threshold = -1.0f); // is mouse dragging. if lock_threshold < -1.0f uses io.MouseDraggingThreshold - IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings. disregarding of consideration of focus/window ordering/blocked by a popup. - IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // + IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. + IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls - IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve backup of mouse positioning at the time of opening popup we have BeginPopup() into - IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // dragging amount since clicking. if lock_threshold < -1.0f uses io.MouseDraggingThreshold + IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve backup of mouse position at the time of opening popup we have BeginPopup() into + IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // return the delta from the initial clicking position while the mouse button is pressed or was just released. This is locked and return 0.0f until the mouse moves past a distance threshold at least once. If lock_threshold < -1.0f uses io.MouseDraggingThreshold. IMGUI_API void ResetMouseDragDelta(int button = 0); // IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you IMGUI_API void SetMouseCursor(ImGuiMouseCursor type); // set desired cursor type - IMGUI_API void CaptureKeyboardFromApp(bool capture = true); // manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application handle). e.g. force capture keyboard when your widget is being hovered. - IMGUI_API void CaptureMouseFromApp(bool capture = true); // manually override io.WantCaptureMouse flag next frame (said flag is entirely left for your application handle). + IMGUI_API void CaptureKeyboardFromApp(bool want_capture_keyboard_value = true); // attention: misleading name! manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application to handle). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard_value"; after the next NewFrame() call. + IMGUI_API void CaptureMouseFromApp(bool want_capture_mouse_value = true); // attention: misleading name! manually override io.WantCaptureMouse flag next frame (said flag is entirely left for your application to handle). This is equivalent to setting "io.WantCaptureMouse = want_capture_mouse_value;" after the next NewFrame() call. // Clipboard Utilities (also see the LogToClipboard() function to capture or output text data to the clipboard) IMGUI_API const char* GetClipboardText(); IMGUI_API void SetClipboardText(const char* text); - // Memory Utilities - // All those functions are not reliant on the current context. - // If you reload the contents of imgui.cpp at runtime, you may need to call SetCurrentContext() + SetAllocatorFunctions() again. - IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data = NULL); + // Settings/.Ini Utilities + // - The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini"). + // - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually. + IMGUI_API void LoadIniSettingsFromDisk(const char* ini_filename); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename). + IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source. + IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext). + IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings. + + // Memory Allocators + // - All those functions are not reliant on the current context. + // - If you reload the contents of imgui.cpp at runtime, you may need to call SetCurrentContext() + SetAllocatorFunctions() again because we use global storage for those. + IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = NULL); IMGUI_API void* MemAlloc(size_t size); IMGUI_API void MemFree(void* ptr); } // namespace ImGui +//----------------------------------------------------------------------------- +// Flags & Enumerations +//----------------------------------------------------------------------------- + // Flags for ImGui::Begin() enum ImGuiWindowFlags_ { + ImGuiWindowFlags_None = 0, ImGuiWindowFlags_NoTitleBar = 1 << 0, // Disable title-bar ImGuiWindowFlags_NoResize = 1 << 1, // Disable user resizing with the lower-right grip ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window - ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programatically) + ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programmatically) ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame - //ImGuiWindowFlags_ShowBorders = 1 << 7, // Show borders around windows and items (OBSOLETE! Use e.g. style.FrameBorderSize=1.0f to enable borders). + ImGuiWindowFlags_NoBackground = 1 << 7, // Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f). ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file - ImGuiWindowFlags_NoInputs = 1 << 9, // Disable catching mouse or keyboard inputs, hovering test with pass through. + ImGuiWindowFlags_NoMouseInputs = 1 << 9, // Disable catching mouse, hovering test with pass through. ImGuiWindowFlags_MenuBar = 1 << 10, // Has a menu-bar ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, // Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section. ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12, // Disable taking focus when transitioning from hidden to visible state - ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, // Disable bringing window to front when taking focus (e.g. clicking on it or programatically giving it focus) + ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, // Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus) ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) - ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // (WIP) Enable resize from any corners and borders. Your back-end needs to honor the different values of io.MouseCursor set by imgui. ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) + ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. When used in a tab/docking context, tab is selected on closure and closure is deferred by one frame to allow code to cancel the closure (with a confirmation popup, etc.) without flicker. ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, + ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, + ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, // [Internal] - ImGuiWindowFlags_NavFlattened = 1 << 23, // (WIP) Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) + ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal() ImGuiWindowFlags_ChildMenu = 1 << 28 // Don't use! For internal use by BeginMenu() + + // [Obsolete] + //ImGuiWindowFlags_ShowBorders = 1 << 7, // --> Set style.FrameBorderSize=1.0f / style.WindowBorderSize=1.0f to enable borders around windows and items + //ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // --> Set io.ConfigWindowsResizeFromEdges and make sure mouse cursors are supported by back-end (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) }; // Flags for ImGui::InputText() enum ImGuiInputTextFlags_ { + ImGuiInputTextFlags_None = 0, ImGuiInputTextFlags_CharsDecimal = 1 << 0, // Allow 0123456789.+-*/ ImGuiInputTextFlags_CharsHexadecimal = 1 << 1, // Allow 0123456789ABCDEFabcdef ImGuiInputTextFlags_CharsUppercase = 1 << 2, // Turn a..z into A..Z ImGuiInputTextFlags_CharsNoBlank = 1 << 3, // Filter out spaces, tabs ImGuiInputTextFlags_AutoSelectAll = 1 << 4, // Select entire text when first taking mouse focus - ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to when the value was modified) - ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Call user function on pressing TAB (for completion handling) - ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Call user function on pressing Up/Down arrows (for history handling) - ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Call user function every time. User code may query cursor position, modify text buffer. - ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Call user function to filter character. Modify data->EventChar to replace/filter input, or return 1 to discard character. + ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider looking at the IsItemDeactivatedAfterEdit() function. + ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Callback on pressing TAB (for completion handling) + ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Callback on pressing Up/Down arrows (for history handling) + ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Callback on each iteration. User code may query cursor position, modify text buffer. + ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. ImGuiInputTextFlags_AllowTabInput = 1 << 10, // Pressing TAB input a '\t' character into the text field ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11, // In multi-line mode, unfocus with Enter, add new line with Ctrl+Enter (default is opposite: unfocus with Ctrl+Enter, add line with Enter). ImGuiInputTextFlags_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally @@ -596,13 +765,17 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_ReadOnly = 1 << 14, // Read-only mode ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*' ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). + ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) + ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) // [Internal] - ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline() + ImGuiInputTextFlags_Multiline = 1 << 20, // For internal use by InputTextMultiline() + ImGuiInputTextFlags_NoMarkEdited = 1 << 21 // For internal use by functions using InputText() before reformatting data }; // Flags for ImGui::TreeNodeEx(), ImGui::CollapsingHeader*() enum ImGuiTreeNodeFlags_ { + ImGuiTreeNodeFlags_None = 0, ImGuiTreeNodeFlags_Selected = 1 << 0, // Draw as selected ImGuiTreeNodeFlags_Framed = 1 << 1, // Full colored frame (e.g. for CollapsingHeader) ImGuiTreeNodeFlags_AllowItemOverlap = 1 << 2, // Hit testing to allow subsequent widgets to overlap this one @@ -611,31 +784,34 @@ enum ImGuiTreeNodeFlags_ ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, // Default node to be open ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, // Need double-click to open node ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Only open when clicking on the arrow part. If ImGuiTreeNodeFlags_OpenOnDoubleClick is also set, single-click arrow or double-click all box to open. - ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes). + ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes). ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding(). - //ImGuITreeNodeFlags_SpanAllAvailWidth = 1 << 11, // FIXME: TODO: Extend hit box horizontally even if not framed + //ImGuiTreeNodeFlags_SpanAllAvailWidth = 1 << 11, // FIXME: TODO: Extend hit box horizontally even if not framed //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 12, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 13, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop) - ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoAutoOpenOnLog + ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiTreeNodeFlags_AllowOverlapMode = ImGuiTreeNodeFlags_AllowItemOverlap + , ImGuiTreeNodeFlags_AllowOverlapMode = ImGuiTreeNodeFlags_AllowItemOverlap // [renamed in 1.53] #endif }; // Flags for ImGui::Selectable() enum ImGuiSelectableFlags_ { + ImGuiSelectableFlags_None = 0, ImGuiSelectableFlags_DontClosePopups = 1 << 0, // Clicking this don't close parent popup window ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Selectable frame can span all columns (text will still fit in current column) - ImGuiSelectableFlags_AllowDoubleClick = 1 << 2 // Generate press events on double clicks too + ImGuiSelectableFlags_AllowDoubleClick = 1 << 2, // Generate press events on double clicks too + ImGuiSelectableFlags_Disabled = 1 << 3 // Cannot be selected, display grayed out text }; // Flags for ImGui::BeginCombo() enum ImGuiComboFlags_ { + ImGuiComboFlags_None = 0, ImGuiComboFlags_PopupAlignLeft = 1 << 0, // Align the popup toward the left by default ImGuiComboFlags_HeightSmall = 1 << 1, // Max ~4 items visible. Tip: If you want your combo popup to be a specific size you can use SetNextWindowSizeConstraints() prior to calling BeginCombo() ImGuiComboFlags_HeightRegular = 1 << 2, // Max ~8 items visible (default) @@ -646,27 +822,56 @@ enum ImGuiComboFlags_ ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest }; +// Flags for ImGui::BeginTabBar() +enum ImGuiTabBarFlags_ +{ + ImGuiTabBarFlags_None = 0, + ImGuiTabBarFlags_Reorderable = 1 << 0, // Allow manually dragging tabs to re-order them + New tabs are appended at the end of list + ImGuiTabBarFlags_AutoSelectNewTabs = 1 << 1, // Automatically select new tabs when they appear + ImGuiTabBarFlags_TabListPopupButton = 1 << 2, // Disable buttons to open the tab list popup + ImGuiTabBarFlags_NoCloseWithMiddleMouseButton = 1 << 3, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. + ImGuiTabBarFlags_NoTabListScrollingButtons = 1 << 4, // Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll) + ImGuiTabBarFlags_NoTooltip = 1 << 5, // Disable tooltips when hovering a tab + ImGuiTabBarFlags_FittingPolicyResizeDown = 1 << 6, // Resize tabs when they don't fit + ImGuiTabBarFlags_FittingPolicyScroll = 1 << 7, // Add scroll buttons when tabs don't fit + ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll, + ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyResizeDown +}; + +// Flags for ImGui::BeginTabItem() +enum ImGuiTabItemFlags_ +{ + ImGuiTabItemFlags_None = 0, + ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. Also: tab is selected on closure and closure is deferred by one frame to allow code to undo it without flicker. + ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programmatically make the tab selected when calling BeginTabItem() + ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. + ImGuiTabItemFlags_NoPushId = 1 << 3 // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() +}; + // Flags for ImGui::IsWindowFocused() enum ImGuiFocusedFlags_ { + ImGuiFocusedFlags_None = 0, ImGuiFocusedFlags_ChildWindows = 1 << 0, // IsWindowFocused(): Return true if any children of the window is focused ImGuiFocusedFlags_RootWindow = 1 << 1, // IsWindowFocused(): Test from root window (top most parent of the current hierarchy) - ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused + ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use ImGui::GetIO().WantCaptureMouse instead. ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; // Flags for ImGui::IsItemHovered(), ImGui::IsWindowHovered() -// Note: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that. Please read the FAQ! +// Note: if you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that. Please read the FAQ! +// Note: windows with the ImGuiWindowFlags_NoInputs flag are ignored by IsWindowHovered() calls. enum ImGuiHoveredFlags_ { - ImGuiHoveredFlags_Default = 0, // Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them. + ImGuiHoveredFlags_None = 0, // Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them. ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 3, // Return true even if a popup window is normally blocking access to this item/window //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is overlapped by another window + ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window + ImGuiHoveredFlags_AllowWhenDisabled = 1 << 7, // Return true even if the item is disabled ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows }; @@ -674,23 +879,42 @@ enum ImGuiHoveredFlags_ // Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload() enum ImGuiDragDropFlags_ { + ImGuiDragDropFlags_None = 0, // BeginDragDropSource() flags - ImGuiDragDropFlags_SourceNoPreviewTooltip = 1 << 0, // By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disable this behavior. - ImGuiDragDropFlags_SourceNoDisableHover = 1 << 1, // By default, when dragging we clear data so that IsItemHovered() will return true, to avoid subsequent user code submitting tooltips. This flag disable this behavior so you can still call IsItemHovered() on the source item. - ImGuiDragDropFlags_SourceNoHoldToOpenOthers = 1 << 2, // Disable the behavior that allows to open tree nodes and collapsing header by holding over them while dragging a source item. - ImGuiDragDropFlags_SourceAllowNullID = 1 << 3, // Allow items such as Text(), Image() that have no unique identifier to be used as drag source, by manufacturing a temporary identifier based on their window-relative position. This is extremely unusual within the dear imgui ecosystem and so we made it explicit. - ImGuiDragDropFlags_SourceExtern = 1 << 4, // External source (from outside of imgui), won't attempt to read current item/window info. Will always return true. Only one Extern source can be active simultaneously. + ImGuiDragDropFlags_SourceNoPreviewTooltip = 1 << 0, // By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disable this behavior. + ImGuiDragDropFlags_SourceNoDisableHover = 1 << 1, // By default, when dragging we clear data so that IsItemHovered() will return false, to avoid subsequent user code submitting tooltips. This flag disable this behavior so you can still call IsItemHovered() on the source item. + ImGuiDragDropFlags_SourceNoHoldToOpenOthers = 1 << 2, // Disable the behavior that allows to open tree nodes and collapsing header by holding over them while dragging a source item. + ImGuiDragDropFlags_SourceAllowNullID = 1 << 3, // Allow items such as Text(), Image() that have no unique identifier to be used as drag source, by manufacturing a temporary identifier based on their window-relative position. This is extremely unusual within the dear imgui ecosystem and so we made it explicit. + ImGuiDragDropFlags_SourceExtern = 1 << 4, // External source (from outside of dear imgui), won't attempt to read current item/window info. Will always return true. Only one Extern source can be active simultaneously. + ImGuiDragDropFlags_SourceAutoExpirePayload = 1 << 5, // Automatically expire the payload if the source cease to be submitted (otherwise payloads are persisting while being dragged) // AcceptDragDropPayload() flags - ImGuiDragDropFlags_AcceptBeforeDelivery = 1 << 10, // AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered. - ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 1 << 11, // Do not draw the default highlight rectangle when hovering over target. + ImGuiDragDropFlags_AcceptBeforeDelivery = 1 << 10, // AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered. + ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 1 << 11, // Do not draw the default highlight rectangle when hovering over target. + ImGuiDragDropFlags_AcceptNoPreviewTooltip = 1 << 12, // Request hiding the BeginDragDropSource tooltip from the BeginDragDropTarget site. ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect // For peeking ahead and inspecting the payload before delivery. }; -// Standard Drag and Drop payload types. You can define you own payload types using 12-characters long strings. Types starting with '_' are defined by Dear ImGui. -#define IMGUI_PAYLOAD_TYPE_COLOR_3F "_COL3F" // float[3] // Standard type for colors, without alpha. User code may use this type. -#define IMGUI_PAYLOAD_TYPE_COLOR_4F "_COL4F" // float[4] // Standard type for colors. User code may use this type. +// Standard Drag and Drop payload types. You can define you own payload types using short strings. Types starting with '_' are defined by Dear ImGui. +#define IMGUI_PAYLOAD_TYPE_COLOR_3F "_COL3F" // float[3]: Standard type for colors, without alpha. User code may use this type. +#define IMGUI_PAYLOAD_TYPE_COLOR_4F "_COL4F" // float[4]: Standard type for colors. User code may use this type. -// A direction +// A primary data type +enum ImGuiDataType_ +{ + ImGuiDataType_S8, // signed char / char (with sensible compilers) + ImGuiDataType_U8, // unsigned char + ImGuiDataType_S16, // short + ImGuiDataType_U16, // unsigned short + ImGuiDataType_S32, // int + ImGuiDataType_U32, // unsigned int + ImGuiDataType_S64, // long long / __int64 + ImGuiDataType_U64, // unsigned long long / unsigned __int64 + ImGuiDataType_Float, // float + ImGuiDataType_Double, // double + ImGuiDataType_COUNT +}; + +// A cardinal direction enum ImGuiDir_ { ImGuiDir_None = -1, @@ -728,33 +952,34 @@ enum ImGuiKey_ ImGuiKey_COUNT }; -// [BETA] Gamepad/Keyboard directional navigation -// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. -// Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). -// Read instructions in imgui.cpp for more details. Download PNG/PSD at goo.gl/9LgVZW. +// Gamepad/Keyboard directional navigation +// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. +// Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Back-end: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). +// Read instructions in imgui.cpp for more details. Download PNG/PSD at http://goo.gl/9LgVZW. enum ImGuiNavInput_ { // Gamepad Mapping - ImGuiNavInput_Activate, // activate / open / toggle / tweak value // e.g. Circle (PS4), A (Xbox), A (Switch), Space (Keyboard) - ImGuiNavInput_Cancel, // cancel / close / exit // e.g. Cross (PS4), B (Xbox), B (Switch), Escape (Keyboard) + ImGuiNavInput_Activate, // activate / open / toggle / tweak value // e.g. Cross (PS4), A (Xbox), A (Switch), Space (Keyboard) + ImGuiNavInput_Cancel, // cancel / close / exit // e.g. Circle (PS4), B (Xbox), B (Switch), Escape (Keyboard) ImGuiNavInput_Input, // text input / on-screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch), Return (Keyboard) ImGuiNavInput_Menu, // tap: toggle menu / hold: focus, move, resize // e.g. Square (PS4), X (Xbox), Y (Switch), Alt (Keyboard) ImGuiNavInput_DpadLeft, // move / tweak / resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down (Gamepads), Arrow keys (Keyboard) - ImGuiNavInput_DpadRight, // - ImGuiNavInput_DpadUp, // - ImGuiNavInput_DpadDown, // + ImGuiNavInput_DpadRight, // + ImGuiNavInput_DpadUp, // + ImGuiNavInput_DpadDown, // ImGuiNavInput_LStickLeft, // scroll / move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down - ImGuiNavInput_LStickRight, // - ImGuiNavInput_LStickUp, // - ImGuiNavInput_LStickDown, // + ImGuiNavInput_LStickRight, // + ImGuiNavInput_LStickUp, // + ImGuiNavInput_LStickDown, // ImGuiNavInput_FocusPrev, // next window (w/ PadMenu) // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) - ImGuiNavInput_FocusNext, // prev window (w/ PadMenu) // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) + ImGuiNavInput_FocusNext, // prev window (w/ PadMenu) // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) ImGuiNavInput_TweakSlow, // slower tweaks // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) ImGuiNavInput_TweakFast, // faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. - // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) may be directly reading from io.KeyDown[] instead of io.NavInputs[]. + // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[]. ImGuiNavInput_KeyMenu_, // toggle menu // = io.KeyAlt + ImGuiNavInput_KeyTab_, // tab // = Tab key ImGuiNavInput_KeyLeft_, // move left // = Arrow keys ImGuiNavInput_KeyRight_, // move right ImGuiNavInput_KeyUp_, // move up @@ -763,17 +988,30 @@ enum ImGuiNavInput_ ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyMenu_ }; -// Configuration flags stored in io.ConfigFlags +// Configuration flags stored in io.ConfigFlags. Set by user/application. enum ImGuiConfigFlags_ { - ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeyDown[]. - ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. - ImGuiConfigFlags_NavMoveMouse = 1 << 2, // Request navigation to allow moving the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. - ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Do not set the io.WantCaptureKeyboard flag with io.NavActive is set. + ImGuiConfigFlags_None = 0, + ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeysDown[]. + ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. Back-end also needs to set ImGuiBackendFlags_HasGamepad. + ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. + ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set. + ImGuiConfigFlags_NoMouse = 1 << 4, // Instruct imgui to clear mouse position/buttons in NewFrame(). This allows ignoring the mouse information set by the back-end. + ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct back-end to not alter mouse cursor shape and visibility. Use if the back-end cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead. - // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core ImGui) - ImGuiConfigFlags_IsSRGB = 1 << 20, // Back-end is SRGB-aware. - ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Back-end is using a touch screen instead of a mouse. + // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core Dear ImGui) + ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. + ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Application is using a touch screen instead of a mouse. +}; + +// Back-end capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom back-end. +enum ImGuiBackendFlags_ +{ + ImGuiBackendFlags_None = 0, + ImGuiBackendFlags_HasGamepad = 1 << 0, // Back-end Platform supports gamepad and currently has one connected. + ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Back-end Platform supports honoring GetMouseCursor() value to change the OS cursor shape. + ImGuiBackendFlags_HasSetMousePos = 1 << 2, // Back-end Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if ImGuiConfigFlags_NavEnableSetMousePos is set). + ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3 // Back-end Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bits indices. }; // Enumeration for PushStyleColor() / PopStyleColor() @@ -812,24 +1050,29 @@ enum ImGuiCol_ ImGuiCol_ResizeGrip, ImGuiCol_ResizeGripHovered, ImGuiCol_ResizeGripActive, - ImGuiCol_CloseButton, - ImGuiCol_CloseButtonHovered, - ImGuiCol_CloseButtonActive, + ImGuiCol_Tab, + ImGuiCol_TabHovered, + ImGuiCol_TabActive, + ImGuiCol_TabUnfocused, + ImGuiCol_TabUnfocusedActive, ImGuiCol_PlotLines, ImGuiCol_PlotLinesHovered, ImGuiCol_PlotHistogram, ImGuiCol_PlotHistogramHovered, ImGuiCol_TextSelectedBg, - ImGuiCol_ModalWindowDarkening, // darken entire screen when a modal window is active ImGuiCol_DragDropTarget, - ImGuiCol_NavHighlight, // gamepad/keyboard: current highlighted item - ImGuiCol_NavWindowingHighlight, // gamepad/keyboard: when holding NavMenu to focus/move/resize windows + ImGuiCol_NavHighlight, // Gamepad/keyboard: current highlighted item + ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB + ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the CTRL+TAB window list, when active + ImGuiCol_ModalWindowDimBg, // Darken/colorize entire screen behind a modal window, when one is active ImGuiCol_COUNT // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - //, ImGuiCol_ComboBg = ImGuiCol_PopupBg // ComboBg has been merged with PopupBg, so a redirect isn't accurate. - , ImGuiCol_ChildWindowBg = ImGuiCol_ChildBg, ImGuiCol_Column = ImGuiCol_Separator, ImGuiCol_ColumnHovered = ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive = ImGuiCol_SeparatorActive + , ImGuiCol_ModalWindowDarkening = ImGuiCol_ModalWindowDimBg // [renamed in 1.63] + , ImGuiCol_ChildWindowBg = ImGuiCol_ChildBg // [renamed in 1.53] + //ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered, // [unused since 1.60+] the close button now uses regular button colors. + //ImGuiCol_ComboBg, // [unused since 1.53+] ComboBg has been merged with PopupBg, so a redirect isn't accurate. #endif }; @@ -838,7 +1081,7 @@ enum ImGuiCol_ // NB: if changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type. enum ImGuiStyleVar_ { - // Enum name ......................// Member in ImGuiStyle structure (see ImGuiStyle for descriptions) + // Enum name --------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions) ImGuiStyleVar_Alpha, // float Alpha ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding ImGuiStyleVar_WindowRounding, // float WindowRounding @@ -859,19 +1102,23 @@ enum ImGuiStyleVar_ ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding ImGuiStyleVar_GrabMinSize, // float GrabMinSize ImGuiStyleVar_GrabRounding, // float GrabRounding + ImGuiStyleVar_TabRounding, // float TabRounding ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign + ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign ImGuiStyleVar_COUNT // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiStyleVar_Count_ = ImGuiStyleVar_COUNT, ImGuiStyleVar_ChildWindowRounding = ImGuiStyleVar_ChildRounding + , ImGuiStyleVar_Count_ = ImGuiStyleVar_COUNT // [renamed in 1.60] + , ImGuiStyleVar_ChildWindowRounding = ImGuiStyleVar_ChildRounding // [renamed in 1.53] #endif }; -// Enumeration for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton() +// Flags for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton() enum ImGuiColorEditFlags_ { - ImGuiColorEditFlags_NoAlpha = 1 << 1, // // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (read 3 components from the input pointer). + ImGuiColorEditFlags_None = 0, + ImGuiColorEditFlags_NoAlpha = 1 << 1, // // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (will only read 3 components from the input pointer). ImGuiColorEditFlags_NoPicker = 1 << 2, // // ColorEdit: disable picker when clicking on colored square. ImGuiColorEditFlags_NoOptions = 1 << 3, // // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview. ImGuiColorEditFlags_NoSmallPreview = 1 << 4, // // ColorEdit, ColorPicker: disable colored square preview next to the inputs. (e.g. to show only the inputs) @@ -879,73 +1126,169 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_NoTooltip = 1 << 6, // // ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview. ImGuiColorEditFlags_NoLabel = 1 << 7, // // ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker). ImGuiColorEditFlags_NoSidePreview = 1 << 8, // // ColorPicker: disable bigger color preview on right side of the picker, use small colored square preview instead. - // User Options (right-click on widget to change some of them). You can set application defaults using SetColorEditOptions(). The idea is that you probably don't want to override them in most of your calls, let the user choose and/or call SetColorEditOptions() during startup. - ImGuiColorEditFlags_AlphaBar = 1 << 9, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. - ImGuiColorEditFlags_AlphaPreview = 1 << 10, // // ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a checkerboard, instead of opaque. - ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 11, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard, instead of opaque. - ImGuiColorEditFlags_HDR = 1 << 12, // // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well). - ImGuiColorEditFlags_RGB = 1 << 13, // [Inputs] // ColorEdit: choose one among RGB/HSV/HEX. ColorPicker: choose any combination using RGB/HSV/HEX. - ImGuiColorEditFlags_HSV = 1 << 14, // [Inputs] // " - ImGuiColorEditFlags_HEX = 1 << 15, // [Inputs] // " - ImGuiColorEditFlags_Uint8 = 1 << 16, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255. - ImGuiColorEditFlags_Float = 1 << 17, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers. - ImGuiColorEditFlags_PickerHueBar = 1 << 18, // [PickerMode] // ColorPicker: bar for Hue, rectangle for Sat/Value. - ImGuiColorEditFlags_PickerHueWheel = 1 << 19, // [PickerMode] // ColorPicker: wheel for Hue, triangle for Sat/Value. - // Internals/Masks - ImGuiColorEditFlags__InputsMask = ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_HSV|ImGuiColorEditFlags_HEX, + ImGuiColorEditFlags_NoDragDrop = 1 << 9, // // ColorEdit: disable drag and drop target. ColorButton: disable drag and drop source. + + // User Options (right-click on widget to change some of them). + ImGuiColorEditFlags_AlphaBar = 1 << 16, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. + ImGuiColorEditFlags_AlphaPreview = 1 << 17, // // ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a checkerboard, instead of opaque. + ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 18, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard, instead of opaque. + ImGuiColorEditFlags_HDR = 1 << 19, // // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well). + ImGuiColorEditFlags_DisplayRGB = 1 << 20, // [Display] // ColorEdit: override _display_ type among RGB/HSV/Hex. ColorPicker: select any combination using one or more of RGB/HSV/Hex. + ImGuiColorEditFlags_DisplayHSV = 1 << 21, // [Display] // " + ImGuiColorEditFlags_DisplayHex = 1 << 22, // [Display] // " + ImGuiColorEditFlags_Uint8 = 1 << 23, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255. + ImGuiColorEditFlags_Float = 1 << 24, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers. + ImGuiColorEditFlags_PickerHueBar = 1 << 25, // [Picker] // ColorPicker: bar for Hue, rectangle for Sat/Value. + ImGuiColorEditFlags_PickerHueWheel = 1 << 26, // [Picker] // ColorPicker: wheel for Hue, triangle for Sat/Value. + ImGuiColorEditFlags_InputRGB = 1 << 27, // [Input] // ColorEdit, ColorPicker: input and output data in RGB format. + ImGuiColorEditFlags_InputHSV = 1 << 28, // [Input] // ColorEdit, ColorPicker: input and output data in HSV format. + + // Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to + // override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup. + ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_DisplayRGB|ImGuiColorEditFlags_InputRGB|ImGuiColorEditFlags_PickerHueBar, + + // [Internal] Masks + ImGuiColorEditFlags__DisplayMask = ImGuiColorEditFlags_DisplayRGB|ImGuiColorEditFlags_DisplayHSV|ImGuiColorEditFlags_DisplayHex, ImGuiColorEditFlags__DataTypeMask = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_Float, ImGuiColorEditFlags__PickerMask = ImGuiColorEditFlags_PickerHueWheel|ImGuiColorEditFlags_PickerHueBar, - ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_PickerHueBar // Change application default using SetColorEditOptions() + ImGuiColorEditFlags__InputMask = ImGuiColorEditFlags_InputRGB|ImGuiColorEditFlags_InputHSV + + // Obsolete names (will be removed) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + , ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69] +#endif }; // Enumeration for GetMouseCursor() +// User code may request binding to display given cursor by calling SetMouseCursor(), which is why we have some cursors that are marked unused here enum ImGuiMouseCursor_ { ImGuiMouseCursor_None = -1, ImGuiMouseCursor_Arrow = 0, ImGuiMouseCursor_TextInput, // When hovering over InputText, etc. - ImGuiMouseCursor_ResizeAll, // Unused + ImGuiMouseCursor_ResizeAll, // (Unused by Dear ImGui functions) ImGuiMouseCursor_ResizeNS, // When hovering over an horizontal border ImGuiMouseCursor_ResizeEW, // When hovering over a vertical border or a column ImGuiMouseCursor_ResizeNESW, // When hovering over the bottom-left corner of a window ImGuiMouseCursor_ResizeNWSE, // When hovering over the bottom-right corner of a window + ImGuiMouseCursor_Hand, // (Unused by Dear ImGui functions. Use for e.g. hyperlinks) ImGuiMouseCursor_COUNT // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiMouseCursor_Count_ = ImGuiMouseCursor_COUNT + , ImGuiMouseCursor_Count_ = ImGuiMouseCursor_COUNT // [renamed in 1.60] #endif }; -// Condition for ImGui::SetWindow***(), SetNextWindow***(), SetNextTreeNode***() functions -// All those functions treat 0 as a shortcut to ImGuiCond_Always. From the point of view of the user use this as an enum (don't combine multiple values into flags). +// Enumateration for ImGui::SetWindow***(), SetNextWindow***(), SetNextItem***() functions +// Represent a condition. +// Important: Treat as a regular enum! Do NOT combine multiple values using binary operators! All the functions above treat 0 as a shortcut to ImGuiCond_Always. enum ImGuiCond_ { ImGuiCond_Always = 1 << 0, // Set the variable ImGuiCond_Once = 1 << 1, // Set the variable once per runtime session (only the first call with succeed) - ImGuiCond_FirstUseEver = 1 << 2, // Set the variable if the window has no saved data (if doesn't exist in the .ini file) - ImGuiCond_Appearing = 1 << 3 // Set the variable if the window is appearing after being hidden/inactive (or the first time) - - // Obsolete names (will be removed) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiSetCond_Always = ImGuiCond_Always, ImGuiSetCond_Once = ImGuiCond_Once, ImGuiSetCond_FirstUseEver = ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing = ImGuiCond_Appearing -#endif + ImGuiCond_FirstUseEver = 1 << 2, // Set the variable if the object/window has no persistently saved data (no entry in .ini file) + ImGuiCond_Appearing = 1 << 3 // Set the variable if the object/window is appearing after being hidden/inactive (or the first time) }; +//----------------------------------------------------------------------------- +// Helpers: Memory allocations macros +// IM_MALLOC(), IM_FREE(), IM_NEW(), IM_PLACEMENT_NEW(), IM_DELETE() +// We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. +// Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. +//----------------------------------------------------------------------------- + +struct ImNewDummy {}; +inline void* operator new(size_t, ImNewDummy, void* ptr) { return ptr; } +inline void operator delete(void*, ImNewDummy, void*) {} // This is only required so we can use the symmetrical new() +#define IM_ALLOC(_SIZE) ImGui::MemAlloc(_SIZE) +#define IM_FREE(_PTR) ImGui::MemFree(_PTR) +#define IM_PLACEMENT_NEW(_PTR) new(ImNewDummy(), _PTR) +#define IM_NEW(_TYPE) new(ImNewDummy(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE +template void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p); } } + +//----------------------------------------------------------------------------- +// Helper: ImVector<> +// Lightweight std::vector<>-like class to avoid dragging dependencies (also, some implementations of STL with debug enabled are absurdly slow, we bypass it so our code runs fast in debug). +// You generally do NOT need to care or use this ever. But we need to make it available in imgui.h because some of our data structures are relying on it. +// Important: clear() frees memory, resize(0) keep the allocated buffer. We use resize(0) a lot to intentionally recycle allocated buffers across frames and amortize our costs. +// Important: our implementation does NOT call C++ constructors/destructors, we treat everything as raw data! This is intentional but be extra mindful of that, +// do NOT use this class as a std::vector replacement in your own code! Many of the structures used by dear imgui can be safely initialized by a zero-memset. +//----------------------------------------------------------------------------- + +template +struct ImVector +{ + int Size; + int Capacity; + T* Data; + + // Provide standard typedefs but we don't use them ourselves. + typedef T value_type; + typedef value_type* iterator; + typedef const value_type* const_iterator; + + // Constructors, destructor + inline ImVector() { Size = Capacity = 0; Data = NULL; } + inline ImVector(const ImVector& src) { Size = Capacity = 0; Data = NULL; operator=(src); } + inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; } + inline ~ImVector() { if (Data) IM_FREE(Data); } + + inline bool empty() const { return Size == 0; } + inline int size() const { return Size; } + inline int size_in_bytes() const { return Size * (int)sizeof(T); } + inline int capacity() const { return Capacity; } + inline T& operator[](int i) { IM_ASSERT(i < Size); return Data[i]; } + inline const T& operator[](int i) const { IM_ASSERT(i < Size); return Data[i]; } + + inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } + inline T* begin() { return Data; } + inline const T* begin() const { return Data; } + inline T* end() { return Data + Size; } + inline const T* end() const { return Data + Size; } + inline T& front() { IM_ASSERT(Size > 0); return Data[0]; } + inline const T& front() const { IM_ASSERT(Size > 0); return Data[0]; } + inline T& back() { IM_ASSERT(Size > 0); return Data[Size - 1]; } + inline const T& back() const { IM_ASSERT(Size > 0); return Data[Size - 1]; } + inline void swap(ImVector& rhs) { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; T* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } + + inline int _grow_capacity(int sz) const { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > sz ? new_capacity : sz; } + inline void resize(int new_size) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; } + inline void resize(int new_size, const T& v) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; } + inline void reserve(int new_capacity) { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; } + + // NB: It is illegal to call push_back/push_front/insert with a reference pointing inside the ImVector data itself! e.g. v.push_back(v[10]) is forbidden. + inline void push_back(const T& v) { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; } + inline void pop_back() { IM_ASSERT(Size > 0); Size--; } + inline void push_front(const T& v) { if (Size == 0) push_back(v); else insert(Data, v); } + inline T* erase(const T* it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; } + inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data+Size && it_last > it && it_last <= Data+Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - count) * sizeof(T)); Size -= (int)count; return Data + off; } + inline T* erase_unsorted(const T* it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; if (it < Data+Size-1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; } + inline T* insert(const T* it, const T& v) { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; } + inline bool contains(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; } + inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; return (int)off; } +}; + +//----------------------------------------------------------------------------- +// ImGuiStyle // You may modify the ImGui::GetStyle() main instance during initialization and before NewFrame(). -// During the frame, prefer using ImGui::PushStyleVar(ImGuiStyleVar_XXXX)/PopStyleVar() to alter the main style values, and ImGui::PushStyleColor(ImGuiCol_XXX)/PopStyleColor() for colors. +// During the frame, use ImGui::PushStyleVar(ImGuiStyleVar_XXXX)/PopStyleVar() to alter the main style values, +// and ImGui::PushStyleColor(ImGuiCol_XXX)/PopStyleColor() for colors. +//----------------------------------------------------------------------------- + struct ImGuiStyle { - float Alpha; // Global alpha applies to everything in ImGui. + float Alpha; // Global alpha applies to everything in Dear ImGui. ImVec2 WindowPadding; // Padding within a window. float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. float WindowBorderSize; // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). ImVec2 WindowMinSize; // Minimum window size. This is a global setting. If you want to constraint individual windows, use SetNextWindowSizeConstraints(). ImVec2 WindowTitleAlign; // Alignment for title bar text. Defaults to (0.0f,0.5f) for left-aligned,vertically centered. + ImGuiDir WindowMenuButtonPosition; // Side of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left. float ChildRounding; // Radius of child window corners rounding. Set to 0.0f to have rectangular windows. float ChildBorderSize; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). - float PopupRounding; // Radius of popup window corners rounding. - float PopupBorderSize; // Thickness of border around popup windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). + float PopupRounding; // Radius of popup window corners rounding. (Note that tooltip windows use WindowRounding) + float PopupBorderSize; // Thickness of border around popup/tooltip windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). ImVec2 FramePadding; // Padding within a framed rectangle (used by most widgets). float FrameRounding; // Radius of frame corners rounding. Set to 0.0f to have rectangular frame (used by most widgets). float FrameBorderSize; // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). @@ -953,14 +1296,17 @@ struct ImGuiStyle ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label). ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! float IndentSpacing; // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). - float ColumnsMinSpacing; // Minimum horizontal spacing between two columns. + float ColumnsMinSpacing; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). float ScrollbarSize; // Width of the vertical scrollbar, Height of the horizontal scrollbar. float ScrollbarRounding; // Radius of grab corners for scrollbar. float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. - ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered. - ImVec2 DisplayWindowPadding; // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. - ImVec2 DisplaySafeAreaPadding; // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. + float TabBorderSize; // Thickness of border around tabs. + ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). + ImVec2 SelectableTextAlign; // Alignment of selectable text when selectable is larger than text. Defaults to (0.0f, 0.0f) (top-left aligned). + ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. + ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU. bool AntiAliasedFill; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) @@ -971,59 +1317,76 @@ struct ImGuiStyle IMGUI_API void ScaleAllSizes(float scale_factor); }; -// This is where your app communicate with ImGui. Access via ImGui::GetIO(). -// Read 'Programmer guide' section in .cpp file for general usage. +//----------------------------------------------------------------------------- +// ImGuiIO +// Communicate most settings and inputs/outputs to Dear ImGui using this structure. +// Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage. +//----------------------------------------------------------------------------- + struct ImGuiIO { //------------------------------------------------------------------ - // Settings (fill once) // Default value: + // Configuration (fill once) // Default value //------------------------------------------------------------------ - ImVec2 DisplaySize; // // Display size, in pixels. For clamping windows positions. - float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. - ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Gamepad/keyboard navigation options, etc. - float IniSavingRate; // = 5.0f // Maximum time between saving positions/sizes to .ini file, in seconds. - const char* IniFilename; // = "imgui.ini" // Path to .ini file. NULL to disable .ini saving. - const char* LogFilename; // = "imgui_log.txt" // Path to .log file (default parameter to ImGui::LogToFile when no file is specified). - float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. - float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. - float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. - int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. - float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). - float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. - void* UserData; // = NULL // Store your own data for retrieval by callbacks. + ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc. + ImGuiBackendFlags BackendFlags; // = 0 // See ImGuiBackendFlags_ enum. Set by back-end (imgui_impl_xxx files or custom back-end) to communicate features supported by the back-end. + ImVec2 DisplaySize; // // Main display size, in pixels. + float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. + float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. + const char* IniFilename; // = "imgui.ini" // Path to .ini file. Set NULL to disable automatic .ini loading/saving, if e.g. you want to manually load/save from memory. + const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified). + float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. + float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. + float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. + int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. + float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). + float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. + void* UserData; // = NULL // Store your own data for retrieval by callbacks. - ImFontAtlas* Fonts; // // Load and assemble one or more fonts into a single tightly packed texture. Output to Fonts array. - float FontGlobalScale; // = 1.0f // Global scale all fonts - bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. - ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0]. - ImVec2 DisplayFramebufferScale; // = (1.0f,1.0f) // For retina display or other situations where window coordinates are different from framebuffer coordinates. User storage only, presently not used by ImGui. - ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. - ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize + ImFontAtlas*Fonts; // // Font atlas: load, rasterize and pack one or more fonts into a single texture. + float FontGlobalScale; // = 1.0f // Global scale all fonts + bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. + ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0]. + ImVec2 DisplayFramebufferScale; // = (1, 1) // For retina display or other situations where window coordinates are different from framebuffer coordinates. This generally ends up in ImDrawData::FramebufferScale. - // Advanced/subtle behaviors - bool OptMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl - bool OptCursorBlink; // = true // Enable blinking cursor, for users who consider it annoying. + // Miscellaneous options + bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. + bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) + bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) + bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) + bool ConfigWindowsMoveFromTitleBarOnly; // = false // [BETA] Set to true to only allow moving windows when clicked+dragged from the title bar. Windows without a title bar are not affected. //------------------------------------------------------------------ - // Settings (User Functions) + // Platform Functions + // (the imgui_impl_xxxx back-end files are setting those up for you) //------------------------------------------------------------------ - // Optional: access OS clipboard + // Optional: Platform/Renderer back-end name (informational only! will be displayed in About Window) + User data for back-end/wrappers to store their own stuff. + const char* BackendPlatformName; // = NULL + const char* BackendRendererName; // = NULL + void* BackendPlatformUserData; // = NULL + void* BackendRendererUserData; // = NULL + void* BackendLanguageUserData; // = NULL + + // Optional: Access OS clipboard // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) const char* (*GetClipboardTextFn)(void* user_data); void (*SetClipboardTextFn)(void* user_data, const char* text); void* ClipboardUserData; - // Optional: notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME in Windows) + // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows) // (default to use native imm32 api on Windows) void (*ImeSetInputScreenPosFn)(int x, int y); - void* ImeWindowHandle; // (Windows) Set this to your HWND to get automatic IME cursor positioning. + void* ImeWindowHandle; // = NULL // (Windows) Set this to your HWND to get automatic IME cursor positioning. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - // [OBSOLETE] Rendering function, will be automatically called in Render(). Please call your rendering function yourself now! You can obtain the ImDrawData* by calling ImGui::GetDrawData() after Render(). - // See example applications if you are unsure of how to implement this. + // [OBSOLETE since 1.60+] Rendering function, will be automatically called in Render(). Please call your rendering function yourself now! + // You can obtain the ImDrawData* by calling ImGui::GetDrawData() after Render(). See example applications if you are unsure of how to implement this. void (*RenderDrawListsFn)(ImDrawData* data); +#else + // This is only here to keep ImGuiIO the same size/layout, so that IMGUI_DISABLE_OBSOLETE_FUNCTIONS can exceptionally be used outside of imconfig.h. + void* RenderDrawListsFnUnused; #endif //------------------------------------------------------------------ @@ -1031,73 +1394,163 @@ struct ImGuiIO //------------------------------------------------------------------ ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX,-FLT_MAX) if mouse is unavailable (on another screen, etc.) - bool MouseDown[5]; // Mouse buttons: left, right, middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. - float MouseWheel; // Mouse wheel: 1 unit scrolls about 5 lines text. - float MouseWheelH; // Mouse wheel (Horizontal). Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends. - bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). + bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. + float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. + float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends. bool KeyCtrl; // Keyboard modifier pressed: Control bool KeyShift; // Keyboard modifier pressed: Shift bool KeyAlt; // Keyboard modifier pressed: Alt bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows bool KeysDown[512]; // Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). - ImWchar InputCharacters[16+1]; // List of characters input (translated by user from keypress+keyboard state). Fill using AddInputCharacter() helper. - float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs (keyboard keys will be auto-mapped and be written here by ImGui::NewFrame) + float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). // Functions - IMGUI_API void AddInputCharacter(ImWchar c); // Add new character into InputCharacters[] - IMGUI_API void AddInputCharactersUTF8(const char* utf8_chars); // Add new characters into InputCharacters[] from an UTF-8 string - inline void ClearInputCharacters() { InputCharacters[0] = 0; } // Clear the text input buffer manually + IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input + IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string + IMGUI_API void ClearInputCharacters(); // Clear the text input buffer manually //------------------------------------------------------------------ // Output - Retrieve after calling NewFrame() //------------------------------------------------------------------ - bool WantCaptureMouse; // When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. This is set by ImGui when it wants to use your mouse (e.g. unclicked mouse is hovering a window, or a widget is active). - bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. This is set by ImGui when it wants to use your keyboard inputs. - bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). - bool WantMoveMouse; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when ImGuiConfigFlags_NavMoveMouse flag is enabled. - bool NavActive; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. - bool NavVisible; // Directional navigation is visible and allowed (will handle ImGuiKey_NavXXX events). - float Framerate; // Application framerate estimation, in frame per second. Solely for convenience. Rolling average estimation based on IO.DeltaTime over 120 frames - int MetricsRenderVertices; // Vertices output during last call to Render() - int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 - int MetricsActiveWindows; // Number of visible root windows (exclude child windows) - ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. + bool WantCaptureMouse; // When io.WantCaptureMouse is true, imgui will use the mouse inputs, do not dispatch them to your main game/application (in both cases, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.). + bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, imgui will use the keyboard inputs, do not dispatch them to your main game/application (in both cases, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.). + bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). + bool WantSetMousePos; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled. + bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. IMPORTANT: You need to clear io.WantSaveIniSettings yourself. + bool NavActive; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. + bool NavVisible; // Directional navigation is visible and allowed (will handle ImGuiKey_NavXXX events). + float Framerate; // Application framerate estimation, in frame per second. Solely for convenience. Rolling average estimation based on IO.DeltaTime over 120 frames + int MetricsRenderVertices; // Vertices output during last call to Render() + int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 + int MetricsRenderWindows; // Number of visible windows + int MetricsActiveWindows; // Number of active windows + int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts. + ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. //------------------------------------------------------------------ // [Internal] ImGui will maintain those fields. Forward compatibility not guaranteed! //------------------------------------------------------------------ - ImVec2 MousePosPrev; // Previous mouse position temporary storage (nb: not for public use, set to MousePos in NewFrame()) - ImVec2 MouseClickedPos[5]; // Position at time of clicking - float MouseClickedTime[5]; // Time of last click (used to figure out double-click) - bool MouseClicked[5]; // Mouse button went from !Down to Down - bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? - bool MouseReleased[5]; // Mouse button went from Down to !Down - bool MouseDownOwned[5]; // Track if button was clicked inside a window. We don't request mouse capture from the application if click started outside ImGui bounds. - float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) - float MouseDownDurationPrev[5]; // Previous time the mouse button has been down - ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point - float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point - float KeysDownDuration[512]; // Duration the keyboard key has been down (0.0f == just pressed) - float KeysDownDurationPrev[512]; // Previous duration the key has been down + ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) + ImVec2 MouseClickedPos[5]; // Position at time of clicking + double MouseClickedTime[5]; // Time of last click (used to figure out double-click) + bool MouseClicked[5]; // Mouse button went from !Down to Down + bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? + bool MouseReleased[5]; // Mouse button went from Down to !Down + bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window. We don't request mouse capture from the application if click started outside ImGui bounds. + bool MouseDownWasDoubleClick[5]; // Track if button down was a double-click + float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) + float MouseDownDurationPrev[5]; // Previous time the mouse button has been down + ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point + float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point + float KeysDownDuration[512]; // Duration the keyboard key has been down (0.0f == just pressed) + float KeysDownDurationPrev[512]; // Previous duration the key has been down float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; + ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform back-end). Fill using AddInputCharacter() helper. IMGUI_API ImGuiIO(); }; +//----------------------------------------------------------------------------- +// Misc data structures +//----------------------------------------------------------------------------- + +// Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used. +// The callback function should return 0 by default. +// Callbacks (follow a flag name and see comments in ImGuiInputTextFlags_ declarations for more details) +// - ImGuiInputTextFlags_CallbackCompletion: Callback on pressing TAB +// - ImGuiInputTextFlags_CallbackHistory: Callback on pressing Up/Down arrows +// - ImGuiInputTextFlags_CallbackAlways: Callback on each iteration +// - ImGuiInputTextFlags_CallbackCharFilter: Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. +// - ImGuiInputTextFlags_CallbackResize: Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. +struct ImGuiInputTextCallbackData +{ + ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only + ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only + void* UserData; // What user passed to InputText() // Read-only + + // Arguments for the different callback events + // - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary. + // - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state. + ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0; + ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] + char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! + int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length() + int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1 + bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always] + int CursorPos; // // Read-write // [Completion,History,Always] + int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection) + int SelectionEnd; // // Read-write // [Completion,History,Always] + + // Helper functions for text manipulation. + // Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection. + IMGUI_API ImGuiInputTextCallbackData(); + IMGUI_API void DeleteChars(int pos, int bytes_count); + IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); + bool HasSelection() const { return SelectionStart != SelectionEnd; } +}; + +// Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin(). +// NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough. +struct ImGuiSizeCallbackData +{ + void* UserData; // Read-only. What user passed to SetNextWindowSizeConstraints() + ImVec2 Pos; // Read-only. Window position, for reference. + ImVec2 CurrentSize; // Read-only. Current window size. + ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. +}; + +// Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload() +struct ImGuiPayload +{ + // Members + void* Data; // Data (copied and owned by dear imgui) + int DataSize; // Data size + + // [Internal] + ImGuiID SourceId; // Source item id + ImGuiID SourceParentId; // Source parent id (if available) + int DataFrameCount; // Data timestamp + char DataType[32+1]; // Data type tag (short user-supplied string, 32 characters max) + bool Preview; // Set when AcceptDragDropPayload() was called and mouse has been hovering the target item (nb: handle overlapping drag targets) + bool Delivery; // Set when AcceptDragDropPayload() was called and mouse button is released over the target item. + + ImGuiPayload() { Clear(); } + void Clear() { SourceId = SourceParentId = 0; Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); DataFrameCount = -1; Preview = Delivery = false; } + bool IsDataType(const char* type) const { return DataFrameCount != -1 && strcmp(type, DataType) == 0; } + bool IsPreview() const { return Preview; } + bool IsDelivery() const { return Delivery; } +}; + //----------------------------------------------------------------------------- // Obsolete functions (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details) +// Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead. //----------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { - // OBSOLETED in 1.60 (from Dec 2017) + // OBSOLETED in 1.71 (from June 2019) + static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } + // OBSOLETED in 1.70 (from May 2019) + static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; } + // OBSOLETED in 1.69 (from Mar 2019) + static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } + // OBSOLETED in 1.66 (from Sep 2018) + static inline void SetScrollHere(float center_ratio=0.5f){ SetScrollHereY(center_ratio); } + // OBSOLETED in 1.63 (between Aug 2018 and Sept 2018) + static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } + // OBSOLETED in 1.61 (between Apr 2018 and Aug 2018) + IMGUI_API bool InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags flags = 0); // Use the 'const char* format' version instead of 'decimal_precision'! + IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags flags = 0); + // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018) static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } - static inline ImVec2 CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge = false, float outward = 0.f) { (void)on_edge; (void)outward; IM_ASSERT(0); return pos; } + static inline ImVec2 CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge = false, float outward = 0.f) { IM_UNUSED(on_edge); IM_UNUSED(outward); IM_ASSERT(0); return pos; } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) static inline void ShowTestWindow() { return ShowDemoWindow(); } static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } @@ -1105,90 +1558,21 @@ namespace ImGui static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017) - bool Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override = -1.0f, ImGuiWindowFlags flags = 0); // Use SetNextWindowSize(size, ImGuiCond_FirstUseEver) + SetNextWindowBgAlpha() instead. + IMGUI_API bool Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override = -1.0f, ImGuiWindowFlags flags = 0); // Use SetNextWindowSize(size, ImGuiCond_FirstUseEver) + SetNextWindowBgAlpha() instead. static inline bool IsRootWindowOrAnyChildHovered() { return IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); } static inline void AlignFirstTextHeightToWidgets() { AlignTextToFramePadding(); } static inline void SetNextWindowPosCenter(ImGuiCond c=0) { ImGuiIO& io = GetIO(); SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), c, ImVec2(0.5f, 0.5f)); } - // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) - static inline bool IsItemHoveredRect() { return IsItemHovered(ImGuiHoveredFlags_RectOnly); } - static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; } // This was misleading and partly broken. You probably want to use the ImGui::GetIO().WantCaptureMouse flag instead. - static inline bool IsMouseHoveringAnyWindow() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } - static inline bool IsMouseHoveringWindow() { return IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); } - // OBSOLETED IN 1.49 (between Apr 2016 and May 2016) - static inline bool CollapsingHeader(const char* label, const char* str_id, bool framed = true, bool default_open = false) { (void)str_id; (void)framed; ImGuiTreeNodeFlags default_open_flags = 1 << 5; return CollapsingHeader(label, (default_open ? default_open_flags : 0)); } } +typedef ImGuiInputTextCallback ImGuiTextEditCallback; // OBSOLETED in 1.63 (from Aug 2018): made the names consistent +typedef ImGuiInputTextCallbackData ImGuiTextEditCallbackData; #endif //----------------------------------------------------------------------------- // Helpers //----------------------------------------------------------------------------- -// Lightweight std::vector<> like class to avoid dragging dependencies (also: windows implementation of STL with debug enabled is absurdly slow, so let's bypass it so our code runs fast in debug). -// Our implementation does NOT call C++ constructors/destructors. This is intentional and we do not require it. Do not use this class as a straight std::vector replacement in your code! -template -class ImVector -{ -public: - int Size; - int Capacity; - T* Data; - - typedef T value_type; - typedef value_type* iterator; - typedef const value_type* const_iterator; - - inline ImVector() { Size = Capacity = 0; Data = NULL; } - inline ~ImVector() { if (Data) ImGui::MemFree(Data); } - - inline bool empty() const { return Size == 0; } - inline int size() const { return Size; } - inline int capacity() const { return Capacity; } - - inline value_type& operator[](int i) { IM_ASSERT(i < Size); return Data[i]; } - inline const value_type& operator[](int i) const { IM_ASSERT(i < Size); return Data[i]; } - - inline void clear() { if (Data) { Size = Capacity = 0; ImGui::MemFree(Data); Data = NULL; } } - inline iterator begin() { return Data; } - inline const_iterator begin() const { return Data; } - inline iterator end() { return Data + Size; } - inline const_iterator end() const { return Data + Size; } - inline value_type& front() { IM_ASSERT(Size > 0); return Data[0]; } - inline const value_type& front() const { IM_ASSERT(Size > 0); return Data[0]; } - inline value_type& back() { IM_ASSERT(Size > 0); return Data[Size - 1]; } - inline const value_type& back() const { IM_ASSERT(Size > 0); return Data[Size - 1]; } - inline void swap(ImVector& rhs) { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; value_type* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } - - inline int _grow_capacity(int sz) const { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > sz ? new_capacity : sz; } - - inline void resize(int new_size) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; } - inline void resize(int new_size, const T& v){ if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) Data[n] = v; Size = new_size; } - inline void reserve(int new_capacity) - { - if (new_capacity <= Capacity) - return; - T* new_data = (value_type*)ImGui::MemAlloc((size_t)new_capacity * sizeof(T)); - if (Data) - memcpy(new_data, Data, (size_t)Size * sizeof(T)); - ImGui::MemFree(Data); - Data = new_data; - Capacity = new_capacity; - } - - // NB: &v cannot be pointing inside the ImVector Data itself! e.g. v.push_back(v[10]) is forbidden. - inline void push_back(const value_type& v) { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); Data[Size++] = v; } - inline void pop_back() { IM_ASSERT(Size > 0); Size--; } - inline void push_front(const value_type& v) { if (Size == 0) push_back(v); else insert(Data, v); } - - inline iterator erase(const_iterator it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(value_type)); Size--; return Data + off; } - inline iterator insert(const_iterator it, const value_type& v) { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(value_type)); Data[off] = v; Size++; return Data + off; } - inline bool contains(const value_type& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; } -}; - -// Helper: execute a block of code at maximum once a frame. Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame. -// Usage: -// static ImGuiOnceUponAFrame oaf; -// if (oaf) -// ImGui::Text("This will be called only once per frame"); +// Helper: Execute a block of code at maximum once a frame. Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame. +// Usage: static ImGuiOnceUponAFrame oaf; if (oaf) ImGui::Text("This will be called only once per frame"); struct ImGuiOnceUponAFrame { ImGuiOnceUponAFrame() { RefFrame = -1; } @@ -1196,14 +1580,17 @@ struct ImGuiOnceUponAFrame operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; } }; -// Helper macro for ImGuiOnceUponAFrame. Attention: The macro expands into 2 statement so make sure you don't use it within e.g. an if() statement without curly braces. -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Will obsolete -#define IMGUI_ONCE_UPON_A_FRAME static ImGuiOnceUponAFrame imgui_oaf; if (imgui_oaf) -#endif - // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" struct ImGuiTextFilter { + IMGUI_API ImGuiTextFilter(const char* default_filter = ""); + IMGUI_API bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build + IMGUI_API bool PassFilter(const char* text, const char* text_end = NULL) const; + IMGUI_API void Build(); + void Clear() { InputBuf[0] = 0; Build(); } + bool IsActive() const { return !Filters.empty(); } + + // [Internal] struct TextRange { const char* b; @@ -1211,49 +1598,41 @@ struct ImGuiTextFilter TextRange() { b = e = NULL; } TextRange(const char* _b, const char* _e) { b = _b; e = _e; } - const char* begin() const { return b; } - const char* end() const { return e; } - bool empty() const { return b == e; } - char front() const { return *b; } - static bool is_blank(char c) { return c == ' ' || c == '\t'; } - void trim_blanks() { while (b < e && is_blank(*b)) b++; while (e > b && is_blank(*(e-1))) e--; } - IMGUI_API void split(char separator, ImVector& out); + const char* begin() const { return b; } + const char* end () const { return e; } + bool empty() const { return b == e; } + IMGUI_API void split(char separator, ImVector* out) const; }; - char InputBuf[256]; ImVector Filters; int CountGrep; - - IMGUI_API ImGuiTextFilter(const char* default_filter = ""); - IMGUI_API bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build - IMGUI_API bool PassFilter(const char* text, const char* text_end = NULL) const; - IMGUI_API void Build(); - void Clear() { InputBuf[0] = 0; Build(); } - bool IsActive() const { return !Filters.empty(); } }; -// Helper: Text buffer for logging/accumulating text +// Helper: Growable text buffer for logging/accumulating text +// (this could be called 'ImGuiTextBuilder' / 'ImGuiStringBuilder') struct ImGuiTextBuffer { ImVector Buf; + static char EmptyString[1]; - ImGuiTextBuffer() { Buf.push_back(0); } - inline char operator[](int i) { return Buf.Data[i]; } - const char* begin() const { return &Buf.front(); } - const char* end() const { return &Buf.back(); } // Buf is zero-terminated, so end() will point on the zero-terminator - int size() const { return Buf.Size - 1; } - bool empty() { return Buf.Size <= 1; } - void clear() { Buf.clear(); Buf.push_back(0); } - void reserve(int capacity) { Buf.reserve(capacity); } - const char* c_str() const { return Buf.Data; } + ImGuiTextBuffer() { } + inline char operator[](int i) { IM_ASSERT(Buf.Data != NULL); return Buf.Data[i]; } + const char* begin() const { return Buf.Data ? &Buf.front() : EmptyString; } + const char* end() const { return Buf.Data ? &Buf.back() : EmptyString; } // Buf is zero-terminated, so end() will point on the zero-terminator + int size() const { return Buf.Size ? Buf.Size - 1 : 0; } + bool empty() { return Buf.Size <= 1; } + void clear() { Buf.clear(); } + void reserve(int capacity) { Buf.reserve(capacity); } + const char* c_str() const { return Buf.Data ? Buf.Data : EmptyString; } + IMGUI_API void append(const char* str, const char* str_end = NULL); IMGUI_API void appendf(const char* fmt, ...) IM_FMTARGS(2); IMGUI_API void appendfv(const char* fmt, va_list args) IM_FMTLIST(2); }; -// Helper: Simple Key->value storage +// Helper: Key->Value storage // Typically you don't have to worry about this since a storage is held within each Window. -// We use it to e.g. store collapse state for a tree (Int 0/1), store color edit options. -// This is optimized for efficient reading (dichotomy into a contiguous buffer), rare writing (typically tied to user interactions) +// We use it to e.g. store collapse state for a tree (Int 0/1) +// This is optimized for efficient lookup (dichotomy into a contiguous buffer) and rare insertion (typically tied to user interactions aka max once a frame) // You can use it as custom user storage for temporary values. Declare your own storage if, for example: // - You want to manipulate the open/close state of a particular sub-tree in your interface (tree node uses Int 0/1 to store their state). // - You want to store custom debug data easily without adding or editing structures in your code (probably not efficient, but convenient) @@ -1299,109 +1678,9 @@ struct ImGuiStorage IMGUI_API void BuildSortByKey(); }; -// Shared state of InputText(), passed to callback when a ImGuiInputTextFlags_Callback* flag is used and the corresponding callback is triggered. -struct ImGuiTextEditCallbackData -{ - ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only - ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only - void* UserData; // What user passed to InputText() // Read-only - bool ReadOnly; // Read-only mode // Read-only - - // CharFilter event: - ImWchar EventChar; // Character input // Read-write (replace character or set to zero) - - // Completion,History,Always events: - // If you modify the buffer contents make sure you update 'BufTextLen' and set 'BufDirty' to true. - ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only - char* Buf; // Current text buffer // Read-write (pointed data only, can't replace the actual pointer) - int BufTextLen; // Current text length in bytes // Read-write - int BufSize; // Maximum text length in bytes // Read-only - bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write - int CursorPos; // // Read-write - int SelectionStart; // // Read-write (== to SelectionEnd when no selection) - int SelectionEnd; // // Read-write - - // NB: Helper functions for text manipulation. Calling those function loses selection. - IMGUI_API void DeleteChars(int pos, int bytes_count); - IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); - bool HasSelection() const { return SelectionStart != SelectionEnd; } -}; - -// Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin(). -// NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough. -struct ImGuiSizeCallbackData -{ - void* UserData; // Read-only. What user passed to SetNextWindowSizeConstraints() - ImVec2 Pos; // Read-only. Window position, for reference. - ImVec2 CurrentSize; // Read-only. Current window size. - ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. -}; - -// Data payload for Drag and Drop operations -struct ImGuiPayload -{ - // Members - const void* Data; // Data (copied and owned by dear imgui) - int DataSize; // Data size - - // [Internal] - ImGuiID SourceId; // Source item id - ImGuiID SourceParentId; // Source parent id (if available) - int DataFrameCount; // Data timestamp - char DataType[12 + 1]; // Data type tag (short user-supplied string, 12 characters max) - bool Preview; // Set when AcceptDragDropPayload() was called and mouse has been hovering the target item (nb: handle overlapping drag targets) - bool Delivery; // Set when AcceptDragDropPayload() was called and mouse button is released over the target item. - - ImGuiPayload() { Clear(); } - void Clear() { SourceId = SourceParentId = 0; Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); DataFrameCount = -1; Preview = Delivery = false; } - bool IsDataType(const char* type) const { return DataFrameCount != -1 && strcmp(type, DataType) == 0; } - bool IsPreview() const { return Preview; } - bool IsDelivery() const { return Delivery; } -}; - -// Helpers macros to generate 32-bits encoded colors -#ifdef IMGUI_USE_BGRA_PACKED_COLOR -#define IM_COL32_R_SHIFT 16 -#define IM_COL32_G_SHIFT 8 -#define IM_COL32_B_SHIFT 0 -#define IM_COL32_A_SHIFT 24 -#define IM_COL32_A_MASK 0xFF000000 -#else -#define IM_COL32_R_SHIFT 0 -#define IM_COL32_G_SHIFT 8 -#define IM_COL32_B_SHIFT 16 -#define IM_COL32_A_SHIFT 24 -#define IM_COL32_A_MASK 0xFF000000 -#endif -#define IM_COL32(R,G,B,A) (((ImU32)(A)<>IM_COL32_R_SHIFT)&0xFF) * sc; Value.y = (float)((rgba>>IM_COL32_G_SHIFT)&0xFF) * sc; Value.z = (float)((rgba>>IM_COL32_B_SHIFT)&0xFF) * sc; Value.w = (float)((rgba>>IM_COL32_A_SHIFT)&0xFF) * sc; } - ImColor(float r, float g, float b, float a = 1.0f) { Value.x = r; Value.y = g; Value.z = b; Value.w = a; } - ImColor(const ImVec4& col) { Value = col; } - inline operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); } - inline operator ImVec4() const { return Value; } - - // FIXME-OBSOLETE: May need to obsolete/cleanup those helpers. - inline void SetHSV(float h, float s, float v, float a = 1.0f){ ImGui::ColorConvertHSVtoRGB(h, s, v, Value.x, Value.y, Value.z); Value.w = a; } - static ImColor HSV(float h, float s, float v, float a = 1.0f) { float r,g,b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r,g,b,a); } -}; - // Helper: Manually clip large list of items. // If you are submitting lots of evenly spaced items and you have a random access to the list, you can perform coarse clipping based on visibility to save yourself from processing those items at all. -// The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. +// The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. // ImGui already clip items based on their bounds but it needs to measure text size to do so. Coarse clipping before submission makes this cost and your own data fetching/submission cost null. // Usage: // ImGuiListClipper clipper(1000); // we have 1000 elements, evenly spaced. @@ -1429,30 +1708,87 @@ struct ImGuiListClipper IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. }; +// Helpers macros to generate 32-bits encoded colors +#ifdef IMGUI_USE_BGRA_PACKED_COLOR +#define IM_COL32_R_SHIFT 16 +#define IM_COL32_G_SHIFT 8 +#define IM_COL32_B_SHIFT 0 +#define IM_COL32_A_SHIFT 24 +#define IM_COL32_A_MASK 0xFF000000 +#else +#define IM_COL32_R_SHIFT 0 +#define IM_COL32_G_SHIFT 8 +#define IM_COL32_B_SHIFT 16 +#define IM_COL32_A_SHIFT 24 +#define IM_COL32_A_MASK 0xFF000000 +#endif +#define IM_COL32(R,G,B,A) (((ImU32)(A)<>IM_COL32_R_SHIFT)&0xFF) * sc; Value.y = (float)((rgba>>IM_COL32_G_SHIFT)&0xFF) * sc; Value.z = (float)((rgba>>IM_COL32_B_SHIFT)&0xFF) * sc; Value.w = (float)((rgba>>IM_COL32_A_SHIFT)&0xFF) * sc; } + ImColor(float r, float g, float b, float a = 1.0f) { Value.x = r; Value.y = g; Value.z = b; Value.w = a; } + ImColor(const ImVec4& col) { Value = col; } + inline operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); } + inline operator ImVec4() const { return Value; } + + // FIXME-OBSOLETE: May need to obsolete/cleanup those helpers. + inline void SetHSV(float h, float s, float v, float a = 1.0f){ ImGui::ColorConvertHSVtoRGB(h, s, v, Value.x, Value.y, Value.z); Value.w = a; } + static ImColor HSV(float h, float s, float v, float a = 1.0f) { float r,g,b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r,g,b,a); } +}; + //----------------------------------------------------------------------------- -// Draw List +// Draw List API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) // Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. //----------------------------------------------------------------------------- // Draw callbacks for advanced uses. -// NB- You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering (you can poke into the draw list for that) -// Draw callback may be useful for example, A) Change your GPU render state, B) render a complex 3D scene inside a UI element (without an intermediate texture/render target), etc. -// The expected behavior from your rendering function is 'if (cmd.UserCallback != NULL) cmd.UserCallback(parent_list, cmd); else RenderTriangles()' +// NB: You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering, +// you can poke into the draw list for that! Draw callback may be useful for example to: +// A) Change your GPU render state, +// B) render a complex 3D scene inside a UI element without an intermediate texture/render target, etc. +// The expected behavior from your rendering function is 'if (cmd.UserCallback != NULL) { cmd.UserCallback(parent_list, cmd); } else { RenderTriangles() }' +// If you want to override the signature of ImDrawCallback, you can simply use e.g. '#define ImDrawCallback MyDrawCallback' (in imconfig.h) + update rendering back-end accordingly. +#ifndef ImDrawCallback typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd); +#endif + +// Special Draw callback value to request renderer back-end to reset the graphics/render state. +// The renderer back-end needs to handle this special value, otherwise it will crash trying to call a function at this address. +// This is useful for example if you submitted callbacks which you know have altered the render state and you want it to be restored. +// It is not done by default because they are many perfectly useful way of altering render state for imgui contents (e.g. changing shader/blending settings before an Image call). +#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-1) // Typically, 1 command = 1 GPU draw call (unless command is a callback) +// Pre 1.71 back-ends will typically ignore the VtxOffset/IdxOffset fields. When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' +// is enabled, those fields allow us to render meshes larger than 64K vertices while keeping 16-bits indices. struct ImDrawCmd { unsigned int ElemCount; // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[]. - ImVec4 ClipRect; // Clipping rectangle (x1, y1, x2, y2) + ImVec4 ClipRect; // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in "viewport" coordinates ImTextureID TextureId; // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. + unsigned int VtxOffset; // Start offset in vertex buffer. Pre-1.71 or without ImGuiBackendFlags_RendererHasVtxOffset: always 0. With ImGuiBackendFlags_RendererHasVtxOffset: may be >0 to support meshes larger than 64K vertices with 16-bits indices. + unsigned int IdxOffset; // Start offset in index buffer. Always equal to sum of ElemCount drawn so far. ImDrawCallback UserCallback; // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally. void* UserCallbackData; // The draw callback code can access this. - ImDrawCmd() { ElemCount = 0; ClipRect.x = ClipRect.y = ClipRect.z = ClipRect.w = 0.0f; TextureId = NULL; UserCallback = NULL; UserCallbackData = NULL; } + ImDrawCmd() { ElemCount = 0; ClipRect.x = ClipRect.y = ClipRect.z = ClipRect.w = 0.0f; TextureId = (ImTextureID)NULL; VtxOffset = IdxOffset = 0; UserCallback = NULL; UserCallbackData = NULL; } }; -// Vertex index (override with '#define ImDrawIdx unsigned int' inside in imconfig.h) +// Vertex index +// (to allow large meshes with 16-bits indices: set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset in the renderer back-end) +// (to use 32-bits indices: override with '#define ImDrawIdx unsigned int' in imconfig.h) #ifndef ImDrawIdx typedef unsigned short ImDrawIdx; #endif @@ -1468,17 +1804,33 @@ struct ImDrawVert #else // You can override the vertex format layout by defining IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT in imconfig.h // The code expect ImVec2 pos (8 bytes), ImVec2 uv (8 bytes), ImU32 col (4 bytes), but you can re-order them or add other fields as needed to simplify integration in your engine. -// The type has to be described within the macro (you can either declare the struct or use a typedef) -// NOTE: IMGUI DOESN'T CLEAR THE STRUCTURE AND DOESN'T CALL A CONSTRUCTOR SO ANY CUSTOM FIELD WILL BE UNINITIALIZED. IF YOU ADD EXTRA FIELDS (SUCH AS A 'Z' COORDINATES) YOU WILL NEED TO CLEAR THEM DURING RENDER OR TO IGNORE THEM. +// The type has to be described within the macro (you can either declare the struct or use a typedef). This is because ImVec2/ImU32 are likely not declared a the time you'd want to set your type up. +// NOTE: IMGUI DOESN'T CLEAR THE STRUCTURE AND DOESN'T CALL A CONSTRUCTOR SO ANY CUSTOM FIELD WILL BE UNINITIALIZED. IF YOU ADD EXTRA FIELDS (SUCH AS A 'Z' COORDINATES) YOU WILL NEED TO CLEAR THEM DURING RENDER OR TO IGNORE THEM. IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT; #endif -// Draw channels are used by the Columns API to "split" the render list into different channels while building, so items of each column can be batched together. -// You can also use them to simulate drawing layers and submit primitives in a different order than how they will be rendered. +// For use by ImDrawListSplitter. struct ImDrawChannel { - ImVector CmdBuffer; - ImVector IdxBuffer; + ImVector _CmdBuffer; + ImVector _IdxBuffer; +}; + +// Split/Merge functions are used to split the draw list into different layers which can be drawn into out of order. +// This is used by the Columns api, so items of each column can be batched together in a same draw call. +struct ImDrawListSplitter +{ + int _Current; // Current channel number (0) + int _Count; // Number of active channels (1+) + ImVector _Channels; // Draw channels (not resized down so _Count might be < Channels.Size) + + inline ImDrawListSplitter() { Clear(); } + inline ~ImDrawListSplitter() { ClearFreeMemory(); } + inline void Clear() { _Current = 0; _Count = 1; } // Do not clear Channels[] so our allocations are reused next frame + IMGUI_API void ClearFreeMemory(); + IMGUI_API void Split(ImDrawList* draw_list, int count); + IMGUI_API void Merge(ImDrawList* draw_list); + IMGUI_API void SetCurrentChannel(ImDrawList* draw_list, int channel_idx); }; enum ImDrawCornerFlags_ @@ -1496,15 +1848,19 @@ enum ImDrawCornerFlags_ enum ImDrawListFlags_ { - ImDrawListFlags_AntiAliasedLines = 1 << 0, - ImDrawListFlags_AntiAliasedFill = 1 << 1 + ImDrawListFlags_None = 0, + ImDrawListFlags_AntiAliasedLines = 1 << 0, // Lines are anti-aliased (*2 the number of triangles for 1.0f wide line, otherwise *3 the number of triangles) + ImDrawListFlags_AntiAliasedFill = 1 << 1, // Filled shapes have anti-aliased edges (*2 the number of vertices) + ImDrawListFlags_AllowVtxOffset = 1 << 2 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. }; // Draw command list -// This is the low-level list of polygons that ImGui functions are filling. At the end of the frame, all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering. -// Each ImGui window contains its own ImDrawList. You can use ImGui::GetWindowDrawList() to access the current window draw list and draw custom primitives. +// This is the low-level list of polygons that ImGui:: functions are filling. At the end of the frame, +// all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering. +// Each dear imgui window contains its own ImDrawList. You can use ImGui::GetWindowDrawList() to +// access the current window draw list and draw custom primitives. // You can interleave normal ImGui:: calls and adding primitives to the current draw list. -// All positions are generally in pixel coordinates (top-left at (0,0), bottom-right at io.DisplaySize), however you are totally free to apply whatever transformation matrix to want to the data (if you apply such transformation you'll want to apply it to ClipRect as well) +// All positions are generally in pixel coordinates (top-left at (0,0), bottom-right at io.DisplaySize), but you are totally free to apply whatever transformation matrix to want to the data (if you apply such transformation you'll want to apply it to ClipRect as well) // Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui:: functions), if you use this API a lot consider coarse culling your drawn objects. struct ImDrawList { @@ -1512,20 +1868,19 @@ struct ImDrawList ImVector CmdBuffer; // Draw commands. Typically 1 command = 1 GPU draw call, unless the command is a callback. ImVector IdxBuffer; // Index buffer. Each command consume ImDrawCmd::ElemCount of those ImVector VtxBuffer; // Vertex buffer. + ImDrawListFlags Flags; // Flags, you may poke into these to adjust anti-aliasing settings per-primitive. // [Internal, used while building lists] - ImDrawListFlags Flags; // Flags, you may poke into these to adjust anti-aliasing settings per-primitive. const ImDrawListSharedData* _Data; // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context) const char* _OwnerName; // Pointer to owner window's name for debugging - unsigned int _VtxCurrentIdx; // [Internal] == VtxBuffer.Size + unsigned int _VtxCurrentOffset; // [Internal] Always 0 unless 'Flags & ImDrawListFlags_AllowVtxOffset'. + unsigned int _VtxCurrentIdx; // [Internal] Generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0. ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) ImVector _ClipRectStack; // [Internal] ImVector _TextureIdStack; // [Internal] ImVector _Path; // [Internal] current path building - int _ChannelsCurrent; // [Internal] current channel number (0) - int _ChannelsCount; // [Internal] number of active channels (1+) - ImVector _Channels; // [Internal] draw channels for columns API (not resized down so _ChannelsCount may be smaller than _Channels.Size) + ImDrawListSplitter _Splitter; // [Internal] for channels api // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData() or create and use your own ImDrawListSharedData (so you can use ImDrawList without ImGui) ImDrawList(const ImDrawListSharedData* shared_data) { _Data = shared_data; _OwnerName = NULL; Clear(); } @@ -1540,8 +1895,8 @@ struct ImDrawList // Primitives IMGUI_API void AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness = 1.0f); - IMGUI_API void AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ImDrawCornerFlags_All, float thickness = 1.0f); // a: upper-left, b: lower-right, rounding_corners_flags: 4-bits corresponding to which corner to round - IMGUI_API void AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ImDrawCornerFlags_All); // a: upper-left, b: lower-right + IMGUI_API void AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ImDrawCornerFlags_All, float thickness = 1.0f); // a: upper-left, b: lower-right (== upper-left + size), rounding_corners_flags: 4-bits corresponding to which corner to round + IMGUI_API void AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ImDrawCornerFlags_All); // a: upper-left, b: lower-right (== upper-left + size) IMGUI_API void AddRectFilledMultiColor(const ImVec2& a, const ImVec2& b, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left); IMGUI_API void AddQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col, float thickness = 1.0f); IMGUI_API void AddQuadFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col); @@ -1551,34 +1906,35 @@ struct ImDrawList IMGUI_API void AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12); IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); - IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,1), ImU32 col = 0xFFFFFFFF); - IMGUI_API void AddImageQuad(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,0), const ImVec2& uv_c = ImVec2(1,1), const ImVec2& uv_d = ImVec2(0,1), ImU32 col = 0xFFFFFFFF); + IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,1), ImU32 col = IM_COL32_WHITE); + IMGUI_API void AddImageQuad(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,0), const ImVec2& uv_c = ImVec2(1,1), const ImVec2& uv_d = ImVec2(0,1), ImU32 col = IM_COL32_WHITE); IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col, float rounding, int rounding_corners = ImDrawCornerFlags_All); - IMGUI_API void AddPolyline(const ImVec2* points, const int num_points, ImU32 col, bool closed, float thickness); - IMGUI_API void AddConvexPolyFilled(const ImVec2* points, const int num_points, ImU32 col); + IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, bool closed, float thickness); + IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order. IMGUI_API void AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments = 0); - // Stateful path API, add points then finish with PathFill() or PathStroke() - inline void PathClear() { _Path.resize(0); } + // Stateful path API, add points then finish with PathFillConvex() or PathStroke() + inline void PathClear() { _Path.Size = 0; } inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); } - inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path[_Path.Size-1], &pos, 8) != 0) _Path.push_back(pos); } - inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); PathClear(); } - inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness); PathClear(); } + inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size-1], &pos, 8) != 0) _Path.push_back(pos); } + inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } // Note: Anti-aliased filling requires points to be in clockwise order. + inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness); _Path.Size = 0; } IMGUI_API void PathArcTo(const ImVec2& centre, float radius, float a_min, float a_max, int num_segments = 10); - IMGUI_API void PathArcToFast(const ImVec2& centre, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle + IMGUI_API void PathArcToFast(const ImVec2& centre, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle IMGUI_API void PathBezierCurveTo(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, int num_segments = 0); IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, int rounding_corners_flags = ImDrawCornerFlags_All); - // Channels - // - Use to simulate layers. By switching channels to can render out-of-order (e.g. submit foreground primitives before background primitives) - // - Use to minimize draw calls (e.g. if going back-and-forth between multiple non-overlapping clipping rectangles, prefer to append into separate channels then merge at the end) - IMGUI_API void ChannelsSplit(int channels_count); - IMGUI_API void ChannelsMerge(); - IMGUI_API void ChannelsSetCurrent(int channel_index); - // Advanced IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible + IMGUI_API ImDrawList* CloneOutput() const; // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer. + + // Advanced: Channels + // - Use to split render into layers. By switching channels to can render out-of-order (e.g. submit foreground primitives before background primitives) + // - Use to minimize draw calls (e.g. if going back-and-forth between multiple non-overlapping clipping rectangles, prefer to append into separate channels then merge at the end) + inline void ChannelsSplit(int count) { _Splitter.Split(this, count); } + inline void ChannelsMerge() { _Splitter.Merge(this); } + inline void ChannelsSetCurrent(int n) { _Splitter.SetCurrentChannel(this, n); } // Internal helpers // NB: all primitives needs to be reserved via PrimReserve() beforehand! @@ -1595,40 +1951,53 @@ struct ImDrawList IMGUI_API void UpdateTextureID(); }; -// All draw data to render an ImGui frame +// All draw data to render a Dear ImGui frame +// (NB: the style and the naming convention here is a little inconsistent, we currently preserve them for backward compatibility purpose, +// as this is one of the oldest structure exposed by the library! Basically, ImDrawList == CmdList) struct ImDrawData { bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. - ImDrawList** CmdLists; - int CmdListsCount; - int TotalVtxCount; // For convenience, sum of all cmd_lists vtx_buffer.Size - int TotalIdxCount; // For convenience, sum of all cmd_lists idx_buffer.Size + ImDrawList** CmdLists; // Array of ImDrawList* to render. The ImDrawList are owned by ImGuiContext and only pointed to from here. + int CmdListsCount; // Number of ImDrawList* to render + int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size + int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size + ImVec2 DisplayPos; // Upper-left position of the viewport to render (== upper-left of the orthogonal projection matrix to use) + ImVec2 DisplaySize; // Size of the viewport to render (== io.DisplaySize for the main viewport) (DisplayPos + DisplaySize == lower-right of the orthogonal projection matrix to use) + ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display. // Functions - ImDrawData() { Clear(); } - void Clear() { Valid = false; CmdLists = NULL; CmdListsCount = TotalVtxCount = TotalIdxCount = 0; } // Draw lists are owned by the ImGuiContext and only pointed to here. - IMGUI_API void DeIndexAllBuffers(); // For backward compatibility or convenience: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! - IMGUI_API void ScaleClipRects(const ImVec2& sc); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. + ImDrawData() { Valid = false; Clear(); } + ~ImDrawData() { Clear(); } + void Clear() { Valid = false; CmdLists = NULL; CmdListsCount = TotalVtxCount = TotalIdxCount = 0; DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.f, 0.f); } // The ImDrawList are owned by ImGuiContext! + IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! + IMGUI_API void ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than Dear ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. }; +//----------------------------------------------------------------------------- +// Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont) +//----------------------------------------------------------------------------- + struct ImFontConfig { - void* FontData; // // TTF/OTF data - int FontDataSize; // // TTF/OTF data size - bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). - int FontNo; // 0 // Index of font within TTF/OTF file - float SizePixels; // // Size in pixels for rasterizer. - int OversampleH, OversampleV; // 3, 1 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis. - bool PixelSnapH; // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. - ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now. - ImVec2 GlyphOffset; // 0, 0 // Offset all glyphs from this font input. - const ImWchar* GlyphRanges; // NULL // Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. - bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. - unsigned int RasterizerFlags; // 0x00 // Settings for custom font rasterizer (e.g. ImGuiFreeType). Leave as zero if you aren't using one. - float RasterizerMultiply; // 1.0f // Brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. + void* FontData; // // TTF/OTF data + int FontDataSize; // // TTF/OTF data size + bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). + int FontNo; // 0 // Index of font within TTF/OTF file + float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). + int OversampleH; // 3 // Rasterize at higher quality for sub-pixel positioning. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. + int OversampleV; // 1 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis. + bool PixelSnapH; // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. + ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now. + ImVec2 GlyphOffset; // 0, 0 // Offset all glyphs from this font input. + const ImWchar* GlyphRanges; // NULL // Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. + float GlyphMinAdvanceX; // 0 // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font + float GlyphMaxAdvanceX; // FLT_MAX // Maximum AdvanceX for glyphs + bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. + unsigned int RasterizerFlags; // 0x00 // Settings for custom font rasterizer (e.g. ImGuiFreeType). Leave as zero if you aren't using one. + float RasterizerMultiply; // 1.0f // Brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. // [Internal] - char Name[32]; // Name (strictly to ease debugging) + char Name[40]; // Name (strictly to ease debugging) ImFont* DstFont; IMGUI_API ImFontConfig(); @@ -1642,20 +2011,46 @@ struct ImFontGlyph float U0, V0, U1, V1; // Texture coordinates }; +// Helper to build glyph ranges from text/string data. Feed your application strings/characters to it then call BuildRanges(). +// This is essentially a tightly packed of vector of 64k booleans = 8KB storage. +struct ImFontGlyphRangesBuilder +{ + ImVector UsedChars; // Store 1-bit per Unicode code point (0=unused, 1=used) + + ImFontGlyphRangesBuilder() { Clear(); } + inline void Clear() { int size_in_bytes = 0x10000 / 8; UsedChars.resize(size_in_bytes / (int)sizeof(ImU32)); memset(UsedChars.Data, 0, (size_t)size_in_bytes); } + inline bool GetBit(int n) const { int off = (n >> 5); ImU32 mask = 1u << (n & 31); return (UsedChars[off] & mask) != 0; } // Get bit n in the array + inline void SetBit(int n) { int off = (n >> 5); ImU32 mask = 1u << (n & 31); UsedChars[off] |= mask; } // Set bit n in the array + inline void AddChar(ImWchar c) { SetBit(c); } // Add character + IMGUI_API void AddText(const char* text, const char* text_end = NULL); // Add string (each character of the UTF-8 string are added) + IMGUI_API void AddRanges(const ImWchar* ranges); // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault()) to force add all of ASCII/Latin+Ext + IMGUI_API void BuildRanges(ImVector* out_ranges); // Output new ranges +}; + enum ImFontAtlasFlags_ { + ImFontAtlasFlags_None = 0, ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two ImFontAtlasFlags_NoMouseCursors = 1 << 1 // Don't build software mouse cursors into the atlas }; -// Load and rasterize multiple TTF/OTF fonts into a same texture. -// Sharing a texture for multiple fonts allows us to reduce the number of draw calls during rendering. -// We also add custom graphic data into the texture that serves for ImGui. -// 1. (Optional) Call AddFont*** functions. If you don't call any, the default font will be loaded for you. -// 2. Call GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data. -// 3. Upload the pixels data into a texture within your graphics system. -// 4. Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture. This value will be passed back to you during rendering to identify the texture. -// IMPORTANT: If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the ImFont is build (when calling GetTextData*** or Build()). We only copy the pointer, not the data. +// Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: +// - One or more fonts. +// - Custom graphics data needed to render the shapes needed by Dear ImGui. +// - Mouse cursor shapes for software cursor rendering (unless setting 'Flags |= ImFontAtlasFlags_NoMouseCursors' in the font atlas). +// It is the user-code responsibility to setup/build the atlas, then upload the pixel data into a texture accessible by your graphics api. +// - Optionally, call any of the AddFont*** functions. If you don't call any, the default font embedded in the code will be loaded for you. +// - Call GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data. +// - Upload the pixels data into a texture within your graphics system (see imgui_impl_xxxx.cpp examples) +// - Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture in a format natural to your graphics API. +// This value will be passed back to you during rendering to identify the texture. Read FAQ entry about ImTextureID for more details. +// Common pitfalls: +// - If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the +// atlas is build (when calling GetTexData*** or Build()). We only copy the pointer, not the data. +// - Important: By default, AddFontFromMemoryTTF() takes ownership of the data. Even though we are not writing to it, we will free the pointer on destruction. +// You can set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed, +// - Even though many functions are suffixed with "TTF", OTF data is supported just as well. +// - This is an old API and it is currently awkward for those and and various other reasons! We will address them in the future! struct ImFontAtlas { IMGUI_API ImFontAtlas(); @@ -1663,7 +2058,7 @@ struct ImFontAtlas IMGUI_API ImFont* AddFont(const ImFontConfig* font_cfg); IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL); IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); - IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after Build(). Set font_cfg->FontDataOwnedByAtlas to false to keep ownership. + IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed. IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp. IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter. IMGUI_API void ClearInputData(); // Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts. @@ -1673,11 +2068,13 @@ struct ImFontAtlas // Build atlas, retrieve pixel data. // User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID(). - // RGBA32 format is provided for convenience and compatibility, but note that unless you use CustomRect to draw color data, the RGB pixels emitted from Fonts will all be white (~75% of waste). - // Pitch = Width * BytesPerPixels + // The pitch is always = Width * BytesPerPixels (1 or 4) + // Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into + // the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste. IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel + bool IsBuilt() { return Fonts.Size > 0 && (TexPixelsAlpha8 != NULL || TexPixelsRGBA32 != NULL); } void SetTexID(ImTextureID id) { TexID = id; } //------------------------------------------- @@ -1686,32 +2083,25 @@ struct ImFontAtlas // Helpers to retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list) // NB: Make sure that your string are UTF-8 and NOT in your local code page. In C++11, you can create UTF-8 string literal using the u8"Hello world" syntax. See FAQ for details. - IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin - IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters - IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs - IMGUI_API const ImWchar* GetGlyphRangesChinese(); // Default + Japanese + full set of about 21000 CJK Unified Ideographs - IMGUI_API const ImWchar* GetGlyphRangesCyrillic(); // Default + about 400 Cyrillic characters - IMGUI_API const ImWchar* GetGlyphRangesThai(); // Default + Thai characters - - // Helpers to build glyph ranges from text data. Feed your application strings/characters to it then call BuildRanges(). - struct GlyphRangesBuilder - { - ImVector UsedChars; // Store 1-bit per Unicode code point (0=unused, 1=used) - GlyphRangesBuilder() { UsedChars.resize(0x10000 / 8); memset(UsedChars.Data, 0, 0x10000 / 8); } - bool GetBit(int n) { return (UsedChars[n >> 3] & (1 << (n & 7))) != 0; } - void SetBit(int n) { UsedChars[n >> 3] |= 1 << (n & 7); } // Set bit 'c' in the array - void AddChar(ImWchar c) { SetBit(c); } // Add character - IMGUI_API void AddText(const char* text, const char* text_end = NULL); // Add string (each character of the UTF-8 string are added) - IMGUI_API void AddRanges(const ImWchar* ranges); // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault) to force add all of ASCII/Latin+Ext - IMGUI_API void BuildRanges(ImVector* out_ranges); // Output new ranges - }; + // NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data. + IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin + IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters + IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs + IMGUI_API const ImWchar* GetGlyphRangesChineseFull(); // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs + IMGUI_API const ImWchar* GetGlyphRangesChineseSimplifiedCommon();// Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese + IMGUI_API const ImWchar* GetGlyphRangesCyrillic(); // Default + about 400 Cyrillic characters + IMGUI_API const ImWchar* GetGlyphRangesThai(); // Default + Thai characters + IMGUI_API const ImWchar* GetGlyphRangesVietnamese(); // Default + Vietname characters //------------------------------------------- - // Custom Rectangles/Glyphs API + // [BETA] Custom Rectangles/Glyphs API //------------------------------------------- - // You can request arbitrary rectangles to be packed into the atlas, for your own purposes. After calling Build(), you can query the rectangle position and render your pixels. - // You can also request your rectangles to be mapped as font glyph (given a font + Unicode point), so you can render e.g. custom colorful icons and use them as regular glyphs. + // You can request arbitrary rectangles to be packed into the atlas, for your own purposes. + // After calling Build(), you can query the rectangle position and render your pixels. + // You can also request your rectangles to be mapped as font glyph (given a font + Unicode point), + // so you can render e.g. custom colorful icons and use them as regular glyphs. + // Read misc/fonts/README.txt for more details about using colorful icons. struct CustomRect { unsigned int ID; // Input // User ID. Use <0x10000 to map into a font glyph, >=0x10000 for other/internal/custom texture data. @@ -1723,12 +2113,11 @@ struct ImFontAtlas CustomRect() { ID = 0xFFFFFFFF; Width = Height = 0; X = Y = 0xFFFF; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0,0); Font = NULL; } bool IsPacked() const { return X != 0xFFFF; } }; - IMGUI_API int AddCustomRectRegular(unsigned int id, int width, int height); // Id needs to be >= 0x10000. Id >= 0x80000000 are reserved for ImGui and ImDrawList IMGUI_API int AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0,0)); // Id needs to be < 0x10000 to register a rectangle to map into a specific font. const CustomRect* GetCustomRectByIndex(int index) const { if (index < 0) return NULL; return &CustomRects[index]; } - // Internals + // [Internal] IMGUI_API void CalcCustomRectUV(const CustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max); IMGUI_API bool GetMouseCursorTexData(ImGuiMouseCursor cursor, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]); @@ -1736,10 +2125,11 @@ struct ImFontAtlas // Members //------------------------------------------- + bool Locked; // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert. ImFontAtlasFlags Flags; // Build flags (see ImFontAtlasFlags_) ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure. int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. - int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. + int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0. // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. @@ -1753,37 +2143,42 @@ struct ImFontAtlas ImVector CustomRects; // Rectangles for packing custom texture data into the atlas. ImVector ConfigData; // Internal data int CustomRectIds[1]; // Identifiers of custom texture rectangle used by ImFontAtlas/ImDrawList + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ +#endif }; // Font runtime data and rendering // ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32(). struct ImFont { - // Members: Hot ~62/78 bytes - float FontSize; // // Height of characters, set during loading (don't change after loading) - float Scale; // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() - ImVec2 DisplayOffset; // = (0.f,1.f) // Offset font rendering by xx pixels - ImVector Glyphs; // // All glyphs. - ImVector IndexAdvanceX; // // Sparse. Glyphs->AdvanceX in a directly indexable way (more cache-friendly, for CalcTextSize functions which are often bottleneck in large UI). - ImVector IndexLookup; // // Sparse. Index glyphs by Unicode code-point. - const ImFontGlyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) - float FallbackAdvanceX; // == FallbackGlyph->AdvanceX - ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() + // Members: Hot ~20/24 bytes (for CalcTextSize) + ImVector IndexAdvanceX; // 12-16 // out // // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this this info, and are often bottleneck in large UI). + float FallbackAdvanceX; // 4 // out // = FallbackGlyph->AdvanceX + float FontSize; // 4 // in // // Height of characters/line, set during loading (don't change after loading) - // Members: Cold ~18/26 bytes - short ConfigDataCount; // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. - ImFontConfig* ConfigData; // // Pointer within ContainerAtlas->ConfigData - ImFontAtlas* ContainerAtlas; // // What we has been loaded into - float Ascent, Descent; // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] - int MetricsTotalSurface;// // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) + // Members: Hot ~36/48 bytes (for CalcTextSize + render loop) + ImVector IndexLookup; // 12-16 // out // // Sparse. Index glyphs by Unicode code-point. + ImVector Glyphs; // 12-16 // out // // All glyphs. + const ImFontGlyph* FallbackGlyph; // 4-8 // out // = FindGlyph(FontFallbackChar) + ImVec2 DisplayOffset; // 8 // in // = (0,0) // Offset font rendering by xx pixels + + // Members: Cold ~32/40 bytes + ImFontAtlas* ContainerAtlas; // 4-8 // out // // What we has been loaded into + const ImFontConfig* ConfigData; // 4-8 // in // // Pointer within ContainerAtlas->ConfigData + short ConfigDataCount; // 2 // in // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. + ImWchar FallbackChar; // 2 // in // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() + float Scale; // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale() + float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] + int MetricsTotalSurface;// 4 // out // // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) + bool DirtyLookupTables; // 1 // out // // Methods IMGUI_API ImFont(); IMGUI_API ~ImFont(); - IMGUI_API void ClearOutputData(); - IMGUI_API void BuildLookupTable(); IMGUI_API const ImFontGlyph*FindGlyph(ImWchar c) const; - IMGUI_API void SetFallbackChar(ImWchar c); + IMGUI_API const ImFontGlyph*FindGlyphNoFallback(ImWchar c) const; float GetCharAdvance(ImWchar c) const { return ((int)c < IndexAdvanceX.Size) ? IndexAdvanceX[(int)c] : FallbackAdvanceX; } bool IsLoaded() const { return ContainerAtlas != NULL; } const char* GetDebugName() const { return ConfigData ? ConfigData->Name : ""; } @@ -1792,21 +2187,26 @@ struct ImFont // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL) const; // utf8 IMGUI_API const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const; - IMGUI_API void RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const; + IMGUI_API void RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const; IMGUI_API void RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const; - // [Internal] + // [Internal] Don't use! + IMGUI_API void BuildLookupTable(); + IMGUI_API void ClearOutputData(); IMGUI_API void GrowIndex(int new_size); IMGUI_API void AddGlyph(ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x); IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. + IMGUI_API void SetFallbackChar(ImWchar c); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - typedef ImFontGlyph Glyph; // OBSOLETE 1.52+ + typedef ImFontGlyph Glyph; // OBSOLETED in 1.52+ #endif }; #if defined(__clang__) #pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop #endif // Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h) diff --git a/external/imgui/imgui_demo.cpp b/external/imgui/imgui_demo.cpp index 852b251..a8d72f2 100644 --- a/external/imgui/imgui_demo.cpp +++ b/external/imgui/imgui_demo.cpp @@ -1,29 +1,65 @@ -// dear imgui, v1.60 WIP +// dear imgui, v1.72 WIP // (demo code) -// Message to the person tempted to delete this file when integrating ImGui into their code base: -// Don't do it! Do NOT remove this file from your project! It is useful reference code that you and other users will want to refer to. +// Message to the person tempted to delete this file when integrating Dear ImGui into their code base: +// Do NOT remove this file from your project! Think again! It is the most useful reference code that you and other coders +// will want to refer to and call. Have the ImGui::ShowDemoWindow() function wired in an always-available debug menu of +// your game/app! Removing this file from your project is hindering access to documentation for everyone in your team, +// likely leading you to poorer usage of the library. // Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow(). -// During development, you can call ImGui::ShowDemoWindow() in your code to learn about various features of ImGui. Have it wired in a debug menu! -// Removing this file from your project is hindering access to documentation for everyone in your team, likely leading you to poorer usage of the library. -// Note that you can #define IMGUI_DISABLE_DEMO_WINDOWS in imconfig.h for the same effect. -// If you want to link core ImGui in your final builds but not those demo windows, #define IMGUI_DISABLE_DEMO_WINDOWS in imconfig.h and those functions will be empty. -// In other situation, when you have ImGui available you probably want this to be available for reference and execution. +// If you want to link core Dear ImGui in your shipped builds but want an easy guarantee that the demo will not be linked, +// you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty. +// In other situation, whenever you have Dear ImGui available you probably want this to be available for reference. // Thank you, // -Your beloved friend, imgui_demo.cpp (that you won't delete) -// Message to beginner C/C++ programmers. About the meaning of 'static': in this demo code, we frequently we use 'static' variables inside functions. -// We do this as a way to gather code and data in the same place, just to make the demo code faster to read, faster to write, and use less code. -// A static variable persist across calls, so it is essentially like a global variable but declared inside the scope of the function. -// It also happens to be a convenient way of storing simple UI related information as long as your function doesn't need to be reentrant or used in threads. -// This might be a pattern you occasionally want to use in your code, but most of the real data you would be editing is likely to be stored outside your function. +// Message to beginner C/C++ programmers about the meaning of the 'static' keyword: +// In this demo code, we frequently we use 'static' variables inside functions. A static variable persist across calls, so it is +// essentially like a global variable but declared inside the scope of the function. We do this as a way to gather code and data +// in the same place, to make the demo source code faster to read, faster to write, and smaller in size. +// It also happens to be a convenient way of storing simple UI related information as long as your function doesn't need to be +// reentrant or used in multiple threads. This might be a pattern you will want to use in your code, but most of the real data +// you would be editing is likely going to be stored outside your functions. + +// The Demo code is this file is designed to be easy to copy-and-paste in into your application! +// Because of this: +// - We never omit the ImGui:: namespace when calling functions, even though most of our code is already in the same namespace. +// - We try to declare static variables in the local scope, as close as possible to the code using them. +// - We never use any of the helpers/facilities used internally by dear imgui, unless it has been exposed in the public API (imgui.h). +// - We never use maths operators on ImVec2/ImVec4. For other imgui sources files, they are provided by imgui_internal.h w/ IMGUI_DEFINE_MATH_OPERATORS, +// for your own sources file they are optional and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h. +// Because we don't want to assume anything about your support of maths operators, we don't use them in imgui_demo.cpp. + +/* + +Index of this file: + +// [SECTION] Forward Declarations, Helpers +// [SECTION] Demo Window / ShowDemoWindow() +// [SECTION] About Window / ShowAboutWindow() +// [SECTION] Style Editor / ShowStyleEditor() +// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() +// [SECTION] Example App: Debug Console / ShowExampleAppConsole() +// [SECTION] Example App: Debug Log / ShowExampleAppLog() +// [SECTION] Example App: Simple Layout / ShowExampleAppLayout() +// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() +// [SECTION] Example App: Long Text / ShowExampleAppLongText() +// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize() +// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize() +// [SECTION] Example App: Simple Overlay / ShowExampleAppSimpleOverlay() +// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles() +// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() +// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() + +*/ #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #include "imgui.h" -#include // toupper, isprint +#include // toupper +#include // INT_MIN, INT_MAX #include // sqrtf, powf, cosf, sinf, floorf, ceilf #include // vsnprintf, sscanf, printf #include // NULL, malloc, free, atoi @@ -35,38 +71,45 @@ #ifdef _MSC_VER #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#define snprintf _snprintf #endif -#ifdef __clang__ +#if defined(__clang__) #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. #pragma clang diagnostic ignored "-Wdeprecated-declarations" // warning : 'xx' is deprecated: The POSIX name for this item.. // for strdup used in demo code (so user can copy & paste the code) #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' #pragma clang diagnostic ignored "-Wformat-security" // warning : warning: format string is not a string literal #pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. +#pragma clang diagnostic ignored "-Wunused-macros" // warning : warning: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used. +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0 +#endif +#if __has_warning("-Wdouble-promotion") +#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. +#endif #if __has_warning("-Wreserved-id-macro") #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier // #endif #elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size #pragma GCC diagnostic ignored "-Wformat-security" // warning : format string is not a string literal (potentially insecure) #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value -#if (__GNUC__ >= 6) -#pragma GCC diagnostic ignored "-Wmisleading-indentation" // warning: this 'if' clause does not guard this statement // GCC 6.0+ only. See #883 on GitHub. -#endif +#pragma GCC diagnostic ignored "-Wmisleading-indentation" // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement // GCC 6.0+ only. See #883 on GitHub. #endif // Play it nice with Windows users. Notepad in 2017 still doesn't display text data with Unix-style \n. #ifdef _WIN32 -#define IM_NEWLINE "\r\n" +#define IM_NEWLINE "\r\n" +#define snprintf _snprintf +#define vsnprintf _vsnprintf #else -#define IM_NEWLINE "\n" +#define IM_NEWLINE "\n" #endif #define IM_MAX(_A,_B) (((_A) >= (_B)) ? (_A) : (_B)) //----------------------------------------------------------------------------- -// DEMO CODE +// [SECTION] Forward Declarations, Helpers //----------------------------------------------------------------------------- #if !defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && defined(IMGUI_DISABLE_TEST_WINDOWS) && !defined(IMGUI_DISABLE_DEMO_WINDOWS) // Obsolete name since 1.53, TEST->DEMO @@ -75,6 +118,9 @@ #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) +// Forward Declarations +static void ShowExampleAppDocuments(bool* p_open); +static void ShowExampleAppMainMenuBar(); static void ShowExampleAppConsole(bool* p_open); static void ShowExampleAppLog(bool* p_open); static void ShowExampleAppLayout(bool* p_open); @@ -82,13 +128,14 @@ static void ShowExampleAppPropertyEditor(bool* p_open); static void ShowExampleAppLongText(bool* p_open); static void ShowExampleAppAutoResize(bool* p_open); static void ShowExampleAppConstrainedResize(bool* p_open); -static void ShowExampleAppFixedOverlay(bool* p_open); +static void ShowExampleAppSimpleOverlay(bool* p_open); static void ShowExampleAppWindowTitles(bool* p_open); static void ShowExampleAppCustomRendering(bool* p_open); -static void ShowExampleAppMainMenuBar(); static void ShowExampleMenuFile(); -static void ShowHelpMarker(const char* desc) +// Helper to display a little (?) mark which shows a tooltip when hovered. +// In your own code you may want to display an actual icon if you are using a merged icon fonts (see misc/fonts/README.txt) +static void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); if (ImGui::IsItemHovered()) @@ -101,14 +148,16 @@ static void ShowHelpMarker(const char* desc) } } +// Helper to display basic user controls. void ImGui::ShowUserGuide() { + ImGuiIO& io = ImGui::GetIO(); ImGui::BulletText("Double-click on title bar to collapse window."); ImGui::BulletText("Click and drag on lower right corner to resize window\n(double-click to auto fit window to its contents)."); ImGui::BulletText("Click and drag on any empty space to move window."); ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text."); - if (ImGui::GetIO().FontAllowUserScaling) + if (io.FontAllowUserScaling) ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents."); ImGui::BulletText("Mouse Wheel to scroll."); ImGui::BulletText("While editing text:\n"); @@ -123,10 +172,25 @@ void ImGui::ShowUserGuide() ImGui::Unindent(); } -// Demonstrate most ImGui features (big function!) +//----------------------------------------------------------------------------- +// [SECTION] Demo Window / ShowDemoWindow() +//----------------------------------------------------------------------------- + +// We split the contents of the big ShowDemoWindow() function into smaller functions (because the link time of very large functions grow non-linearly) +static void ShowDemoWindowWidgets(); +static void ShowDemoWindowLayout(); +static void ShowDemoWindowPopups(); +static void ShowDemoWindowColumns(); +static void ShowDemoWindowMisc(); + +// Demonstrate most Dear ImGui features (this is big function!) +// You may execute this function to experiment with the UI and understand what it does. You may then search for keywords in the code when you are interested by a specific feature. void ImGui::ShowDemoWindow(bool* p_open) { - // Examples apps + IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing dear imgui context. Refer to examples app!"); // Exceptionally add an extra assert here for people confused with initial dear imgui setup + + // Examples Apps (accessible from the "Examples" menu) + static bool show_app_documents = false; static bool show_app_main_menu_bar = false; static bool show_app_console = false; static bool show_app_log = false; @@ -135,14 +199,11 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool show_app_long_text = false; static bool show_app_auto_resize = false; static bool show_app_constrained_resize = false; - static bool show_app_fixed_overlay = false; + static bool show_app_simple_overlay = false; static bool show_app_window_titles = false; static bool show_app_custom_rendering = false; - static bool show_app_style_editor = false; - - static bool show_app_metrics = false; - static bool show_app_about = false; + if (show_app_documents) ShowExampleAppDocuments(&show_app_documents); if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); if (show_app_console) ShowExampleAppConsole(&show_app_console); if (show_app_log) ShowExampleAppLog(&show_app_log); @@ -151,22 +212,20 @@ void ImGui::ShowDemoWindow(bool* p_open) if (show_app_long_text) ShowExampleAppLongText(&show_app_long_text); if (show_app_auto_resize) ShowExampleAppAutoResize(&show_app_auto_resize); if (show_app_constrained_resize) ShowExampleAppConstrainedResize(&show_app_constrained_resize); - if (show_app_fixed_overlay) ShowExampleAppFixedOverlay(&show_app_fixed_overlay); + if (show_app_simple_overlay) ShowExampleAppSimpleOverlay(&show_app_simple_overlay); if (show_app_window_titles) ShowExampleAppWindowTitles(&show_app_window_titles); if (show_app_custom_rendering) ShowExampleAppCustomRendering(&show_app_custom_rendering); + // Dear ImGui Apps (accessible from the "Help" menu) + static bool show_app_metrics = false; + static bool show_app_style_editor = false; + static bool show_app_about = false; + if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); } if (show_app_style_editor) { ImGui::Begin("Style Editor", &show_app_style_editor); ImGui::ShowStyleEditor(); ImGui::End(); } - if (show_app_about) - { - ImGui::Begin("About Dear ImGui", &show_app_about, ImGuiWindowFlags_AlwaysAutoResize); - ImGui::Text("Dear ImGui, %s", ImGui::GetVersion()); - ImGui::Separator(); - ImGui::Text("By Omar Cornut and all dear imgui contributors."); - ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); - ImGui::End(); - } + if (show_app_about) { ImGui::ShowAboutWindow(&show_app_about); } + // Demonstrate the various window flags. Typically you would just use the default! static bool no_titlebar = false; static bool no_scrollbar = false; static bool no_menu = false; @@ -175,32 +234,38 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool no_collapse = false; static bool no_close = false; static bool no_nav = false; + static bool no_background = false; + static bool no_bring_to_front = false; - // Demonstrate the various window flags. Typically you would just use the default. ImGuiWindowFlags window_flags = 0; - if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; - if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; - if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar; - if (no_move) window_flags |= ImGuiWindowFlags_NoMove; - if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; - if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; - if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; - if (no_close) p_open = NULL; // Don't pass our bool* to Begin + if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; + if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; + if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar; + if (no_move) window_flags |= ImGuiWindowFlags_NoMove; + if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; + if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; + if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; + if (no_background) window_flags |= ImGuiWindowFlags_NoBackground; + if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus; + if (no_close) p_open = NULL; // Don't pass our bool* to Begin - ImGui::SetNextWindowSize(ImVec2(550,680), ImGuiCond_FirstUseEver); - if (!ImGui::Begin("ImGui Demo", p_open, window_flags)) + // We specify a default position/size in case there's no data in the .ini file. Typically this isn't required! We only do it to make the Demo applications a little more welcoming. + ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver); + + // Main body of the Demo window starts here. + if (!ImGui::Begin("Dear ImGui Demo", p_open, window_flags)) { // Early out if the window is collapsed, as an optimization. ImGui::End(); return; } - //ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f); // 2/3 of the space for widget and 1/3 for labels - ImGui::PushItemWidth(-140); // Right align, keep 140 pixels for labels + // Most "big" widgets share a common width settings by default. + //ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f); // Use 2/3 of the space for widgets and 1/3 for labels (default) + ImGui::PushItemWidth(ImGui::GetFontSize() * -12); // Use fixed width for labels (by passing a negative value), the rest goes to widgets. We choose a width proportional to our font size. - ImGui::Text("dear imgui says hello. (%s)", IMGUI_VERSION); - - // Menu + // Menu Bar if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Menu")) @@ -218,9 +283,10 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::MenuItem("Long text display", NULL, &show_app_long_text); ImGui::MenuItem("Auto-resizing window", NULL, &show_app_auto_resize); ImGui::MenuItem("Constrained-resizing window", NULL, &show_app_constrained_resize); - ImGui::MenuItem("Simple overlay", NULL, &show_app_fixed_overlay); + ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay); ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles); ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); + ImGui::MenuItem("Documents", NULL, &show_app_documents); ImGui::EndMenu(); } if (ImGui::BeginMenu("Help")) @@ -233,14 +299,93 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::EndMenuBar(); } + ImGui::Text("dear imgui says hello. (%s)", IMGUI_VERSION); ImGui::Spacing(); + if (ImGui::CollapsingHeader("Help")) { - ImGui::TextWrapped("This window is being created by the ShowDemoWindow() function. Please refer to the code in imgui_demo.cpp for reference.\n\n"); + ImGui::Text("PROGRAMMER GUIDE:"); + ImGui::BulletText("Please see the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!"); + ImGui::BulletText("Please see the comments in imgui.cpp."); + ImGui::BulletText("Please see the examples/ in application."); + ImGui::BulletText("Enable 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls."); + ImGui::BulletText("Enable 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls."); + ImGui::Separator(); + ImGui::Text("USER GUIDE:"); ImGui::ShowUserGuide(); } + if (ImGui::CollapsingHeader("Configuration")) + { + ImGuiIO& io = ImGui::GetIO(); + + if (ImGui::TreeNode("Configuration##2")) + { + ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); + ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); + ImGui::SameLine(); HelpMarker("Required back-end to feed in gamepad inputs in io.NavInputs[] and set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details."); + ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos); + ImGui::SameLine(); HelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos."); + ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouse); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) // Create a way to restore this flag otherwise we could be stuck completely! + { + if (fmodf((float)ImGui::GetTime(), 0.40f) < 0.20f) + { + ImGui::SameLine(); + ImGui::Text("<>"); + } + if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Space))) + io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse; + } + ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); + ImGui::SameLine(); HelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); + ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); + ImGui::SameLine(); HelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); + ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges); + ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback."); + ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly); + ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); + ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor for you. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); + ImGui::TreePop(); + ImGui::Separator(); + } + + if (ImGui::TreeNode("Backend Flags")) + { + HelpMarker("Those flags are set by the back-ends (imgui_impl_xxx files) to specify their capabilities."); + ImGuiBackendFlags backend_flags = io.BackendFlags; // Make a local copy to avoid modifying actual back-end flags. + ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasGamepad); + ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasMouseCursors); + ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasSetMousePos); + ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", (unsigned int *)&backend_flags, ImGuiBackendFlags_RendererHasVtxOffset); + ImGui::TreePop(); + ImGui::Separator(); + } + + if (ImGui::TreeNode("Style")) + { + ImGui::ShowStyleEditor(); + ImGui::TreePop(); + ImGui::Separator(); + } + + if (ImGui::TreeNode("Capture/Logging")) + { + ImGui::TextWrapped("The logging API redirects all text output so you can easily capture the content of a window or a block. Tree nodes can be automatically expanded."); + HelpMarker("Try opening any of the contents below in this window and then click one of the \"Log To\" button."); + ImGui::LogButtons(); + ImGui::TextWrapped("You can also call ImGui::LogText() to output directly to the log without a visual output."); + if (ImGui::Button("Copy \"Hello, world!\" to clipboard")) + { + ImGui::LogToClipboard(); + ImGui::LogText("Hello, world!"); + ImGui::LogFinish(); + } + ImGui::TreePop(); + } + } + if (ImGui::CollapsingHeader("Window options")) { ImGui::Checkbox("No titlebar", &no_titlebar); ImGui::SameLine(150); @@ -250,486 +395,563 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("No resize", &no_resize); ImGui::SameLine(300); ImGui::Checkbox("No collapse", &no_collapse); ImGui::Checkbox("No close", &no_close); ImGui::SameLine(150); - ImGui::Checkbox("No nav", &no_nav); - - if (ImGui::TreeNode("Style")) - { - ImGui::ShowStyleEditor(); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Capture/Logging")) - { - ImGui::TextWrapped("The logging API redirects all text output so you can easily capture the content of a window or a block. Tree nodes can be automatically expanded. You can also call ImGui::LogText() to output directly to the log without a visual output."); - ImGui::LogButtons(); - ImGui::TreePop(); - } + ImGui::Checkbox("No nav", &no_nav); ImGui::SameLine(300); + ImGui::Checkbox("No background", &no_background); + ImGui::Checkbox("No bring to front", &no_bring_to_front); } - if (ImGui::CollapsingHeader("Widgets")) + // All demo contents + ShowDemoWindowWidgets(); + ShowDemoWindowLayout(); + ShowDemoWindowPopups(); + ShowDemoWindowColumns(); + ShowDemoWindowMisc(); + + // End of ShowDemoWindow() + ImGui::End(); +} + +static void ShowDemoWindowWidgets() +{ + if (!ImGui::CollapsingHeader("Widgets")) + return; + + if (ImGui::TreeNode("Basic")) { - if (ImGui::TreeNode("Basic")) + static int clicked = 0; + if (ImGui::Button("Button")) + clicked++; + if (clicked & 1) { - static int clicked = 0; - if (ImGui::Button("Button")) - clicked++; - if (clicked & 1) - { - ImGui::SameLine(); - ImGui::Text("Thanks for clicking me!"); - } - - static bool check = true; - ImGui::Checkbox("checkbox", &check); - - static int e = 0; - ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine(); - ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine(); - ImGui::RadioButton("radio c", &e, 2); - - // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. - for (int i = 0; i < 7; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.6f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i/7.0f, 0.7f, 0.7f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i/7.0f, 0.8f, 0.8f)); - ImGui::Button("Click"); - ImGui::PopStyleColor(3); - ImGui::PopID(); - } - - // Arrow buttons - float spacing = ImGui::GetStyle().ItemInnerSpacing.x; - if (ImGui::ArrowButton("##left", ImGuiDir_Left)) {} - ImGui::SameLine(0.0f, spacing); - if (ImGui::ArrowButton("##left", ImGuiDir_Right)) {} - - ImGui::Text("Hover over me"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip"); - ImGui::SameLine(); - ImGui::Text("- or me"); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::Text("I am a fancy tooltip"); - static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); - ImGui::EndTooltip(); - } + ImGui::Text("Thanks for clicking me!"); + } - // Testing ImGuiOnceUponAFrame helper. - //static ImGuiOnceUponAFrame once; - //for (int i = 0; i < 5; i++) - // if (once) - // ImGui::Text("This will be displayed only once."); + static bool check = true; + ImGui::Checkbox("checkbox", &check); - ImGui::Separator(); + static int e = 0; + ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine(); + ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine(); + ImGui::RadioButton("radio c", &e, 2); - ImGui::LabelText("label", "Value"); + // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. + for (int i = 0; i < 7; i++) + { + if (i > 0) + ImGui::SameLine(); + ImGui::PushID(i); + ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i/7.0f, 0.7f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i/7.0f, 0.8f, 0.8f)); + ImGui::Button("Click"); + ImGui::PopStyleColor(3); + ImGui::PopID(); + } - { - // Simplified one-liner Combo() API, using values packed in a single constant string - static int current_item_1 = 1; - ImGui::Combo("combo", ¤t_item_1, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); - //ImGui::Combo("combo w/ array of char*", ¤t_item_2_idx, items, IM_ARRAYSIZE(items)); // Combo using proper array. You can also pass a callback to retrieve array value, no need to create/copy an array just for that. + // Use AlignTextToFramePadding() to align text baseline to the baseline of framed elements (otherwise a Text+SameLine+Button sequence will have the text a little too high by default) + ImGui::AlignTextToFramePadding(); + ImGui::Text("Hold to repeat:"); + ImGui::SameLine(); - // General BeginCombo() API, you have full control over your selection data and display type - const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO", "PPPP", "QQQQQQQQQQ", "RRR", "SSSS" }; - static const char* current_item_2 = NULL; - if (ImGui::BeginCombo("combo 2", current_item_2)) // The second parameter is the label previewed before opening the combo. - { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) - { - bool is_selected = (current_item_2 == items[n]); // You can store your selection however you want, outside or inside your objects - if (ImGui::Selectable(items[n], is_selected)) - current_item_2 = items[n]; - if (is_selected) - ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) - } - ImGui::EndCombo(); - } - } + // Arrow buttons with Repeater + static int counter = 0; + float spacing = ImGui::GetStyle().ItemInnerSpacing.x; + ImGui::PushButtonRepeat(true); + if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { counter--; } + ImGui::SameLine(0.0f, spacing); + if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { counter++; } + ImGui::PopButtonRepeat(); + ImGui::SameLine(); + ImGui::Text("%d", counter); - { - static char str0[128] = "Hello, world!"; - static int i0=123; - static float f0=0.001f; - ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); - ImGui::SameLine(); ShowHelpMarker("Hold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n"); + ImGui::Text("Hover over me"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("I am a tooltip"); - ImGui::InputInt("input int", &i0); - ImGui::SameLine(); ShowHelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n"); + ImGui::SameLine(); + ImGui::Text("- or me"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("I am a fancy tooltip"); + static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; + ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); + ImGui::EndTooltip(); + } - ImGui::InputFloat("input float", &f0, 0.01f, 1.0f); + ImGui::Separator(); - static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; - ImGui::InputFloat3("input float3", vec4a); - } + ImGui::LabelText("label", "Value"); - { - static int i1=50, i2=42; - ImGui::DragInt("drag int", &i1, 1); - ImGui::SameLine(); ShowHelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value."); + { + // Using the _simplified_ one-liner Combo() api here + // See "Combo" section for examples of how to use the more complete BeginCombo()/EndCombo() api. + const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; + static int item_current = 0; + ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); + ImGui::SameLine(); HelpMarker("Refer to the \"Combo\" section below for an explanation of the full BeginCombo/EndCombo API, and demonstration of various flags.\n"); + } - ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%.0f%%"); + { + static char str0[128] = "Hello, world!"; + ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); + ImGui::SameLine(); HelpMarker("USER:\nHold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n\nPROGRAMMER:\nYou can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated in imgui_demo.cpp)."); - static float f1=1.00f, f2=0.0067f; - ImGui::DragFloat("drag float", &f1, 0.005f); - ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns"); - } + static char str1[128] = ""; + ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1)); - { - static int i1=0; - ImGui::SliderInt("slider int", &i1, -1, 3); - ImGui::SameLine(); ShowHelpMarker("CTRL+click to input value."); + static int i0 = 123; + ImGui::InputInt("input int", &i0); + ImGui::SameLine(); HelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n"); - static float f1=0.123f, f2=0.0f; - ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); - ImGui::SliderFloat("slider log float", &f2, -10.0f, 10.0f, "%.4f", 3.0f); - static float angle = 0.0f; - ImGui::SliderAngle("slider angle", &angle); - } + static float f0 = 0.001f; + ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f"); + static double d0 = 999999.00000001; + ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.8f"); + + static float f1 = 1.e10f; + ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e"); + ImGui::SameLine(); HelpMarker("You can input value using the scientific notation,\n e.g. \"1e+8\" becomes \"100000000\".\n"); + + static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; + ImGui::InputFloat3("input float3", vec4a); + } + + { + static int i1 = 50, i2 = 42; + ImGui::DragInt("drag int", &i1, 1); + ImGui::SameLine(); HelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value."); + + ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%"); + + static float f1=1.00f, f2=0.0067f; + ImGui::DragFloat("drag float", &f1, 0.005f); + ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns"); + } + + { + static int i1=0; + ImGui::SliderInt("slider int", &i1, -1, 3); + ImGui::SameLine(); HelpMarker("CTRL+click to input value."); + + static float f1=0.123f, f2=0.0f; + ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); + ImGui::SliderFloat("slider float (curve)", &f2, -10.0f, 10.0f, "%.4f", 2.0f); + static float angle = 0.0f; + ImGui::SliderAngle("slider angle", &angle); + } + + { static float col1[3] = { 1.0f,0.0f,0.2f }; static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; ImGui::ColorEdit3("color 1", col1); - ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nRight-click on the colored square to show options.\nCTRL+click on individual component to input value.\n"); + ImGui::SameLine(); HelpMarker("Click on the colored square to open a color picker.\nClick and hold to use drag and drop.\nRight-click on the colored square to show options.\nCTRL+click on individual component to input value.\n"); ImGui::ColorEdit4("color 2", col2); + } + { + // List box const char* listbox_items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; static int listbox_item_current = 1; ImGui::ListBox("listbox\n(single select)", &listbox_item_current, listbox_items, IM_ARRAYSIZE(listbox_items), 4); //static int listbox_item_current2 = 2; - //ImGui::PushItemWidth(-1); + //ImGui::SetNextItemWidth(-1); //ImGui::ListBox("##listbox2", &listbox_item_current2, listbox_items, IM_ARRAYSIZE(listbox_items), 4); - //ImGui::PopItemWidth(); + } + ImGui::TreePop(); + } + + // Testing ImGuiOnceUponAFrame helper. + //static ImGuiOnceUponAFrame once; + //for (int i = 0; i < 5; i++) + // if (once) + // ImGui::Text("This will be displayed only once."); + + if (ImGui::TreeNode("Trees")) + { + if (ImGui::TreeNode("Basic trees")) + { + for (int i = 0; i < 5; i++) + { + // Use SetNextItemOpen() so set the default state of a node to be open. + // We could also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing! + if (i == 0) + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + + if (ImGui::TreeNode((void*)(intptr_t)i, "Child %d", i)) + { + ImGui::Text("blah blah"); + ImGui::SameLine(); + if (ImGui::SmallButton("button")) {}; + ImGui::TreePop(); + } + } ImGui::TreePop(); } - if (ImGui::TreeNode("Trees")) + if (ImGui::TreeNode("Advanced, with Selectable nodes")) { - if (ImGui::TreeNode("Basic trees")) + HelpMarker("This is a more typical looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); + static bool align_label_with_current_x_position = false; + ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position); + ImGui::Text("Hello!"); + if (align_label_with_current_x_position) + ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); + + static int selection_mask = (1 << 2); // Dumb representation of what may be user-side selection state. You may carry selection state inside or outside your objects in whatever format you see fit. + int node_clicked = -1; // Temporary storage of what node we have clicked to process selection at the end of the loop. May be a pointer to your own node type, etc. + ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui::GetFontSize()*3); // Increase spacing to differentiate leaves from expanded contents. + for (int i = 0; i < 6; i++) { - for (int i = 0; i < 5; i++) - if (ImGui::TreeNode((void*)(intptr_t)i, "Child %d", i)) + // Disable the default open on single-click behavior and pass in Selected flag according to our selection state. + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; + if (selection_mask & (1 << i)) + node_flags |= ImGuiTreeNodeFlags_Selected; + if (i < 3) + { + // Items 0..2 are Tree Node + bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); + if (ImGui::IsItemClicked()) + node_clicked = i; + if (node_open) { - ImGui::Text("blah blah"); - ImGui::SameLine(); - if (ImGui::SmallButton("button")) { }; + ImGui::Text("Blah blah\nBlah Blah"); ImGui::TreePop(); } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Advanced, with Selectable nodes")) - { - ShowHelpMarker("This is a more standard looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); - static bool align_label_with_current_x_position = false; - ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position); - ImGui::Text("Hello!"); - if (align_label_with_current_x_position) - ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); - - static int selection_mask = (1 << 2); // Dumb representation of what may be user-side selection state. You may carry selection state inside or outside your objects in whatever format you see fit. - int node_clicked = -1; // Temporary storage of what node we have clicked to process selection at the end of the loop. May be a pointer to your own node type, etc. - ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui::GetFontSize()*3); // Increase spacing to differentiate leaves from expanded contents. - for (int i = 0; i < 6; i++) - { - // Disable the default open on single-click behavior and pass in Selected flag according to our selection state. - ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ((selection_mask & (1 << i)) ? ImGuiTreeNodeFlags_Selected : 0); - if (i < 3) - { - // Node - bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); - if (ImGui::IsItemClicked()) - node_clicked = i; - if (node_open) - { - ImGui::Text("Blah blah\nBlah Blah"); - ImGui::TreePop(); - } - } - else - { - // Leaf: The only reason we have a TreeNode at all is to allow selection of the leaf. Otherwise we can use BulletText() or TreeAdvanceToLabelPos()+Text(). - node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet - ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); - if (ImGui::IsItemClicked()) - node_clicked = i; - } } - if (node_clicked != -1) + else { - // Update selection state. Process outside of tree loop to avoid visual inconsistencies during the clicking-frame. - if (ImGui::GetIO().KeyCtrl) - selection_mask ^= (1 << node_clicked); // CTRL+click to toggle - else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection - selection_mask = (1 << node_clicked); // Click to single-select + // Items 3..5 are Tree Leaves + // The only reason we use TreeNode at all is to allow selection of the leaf. + // Otherwise we can use BulletText() or TreeAdvanceToLabelPos()+Text(). + node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet + ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); + if (ImGui::IsItemClicked()) + node_clicked = i; } - ImGui::PopStyleVar(); - if (align_label_with_current_x_position) - ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); - ImGui::TreePop(); } + if (node_clicked != -1) + { + // Update selection state. Process outside of tree loop to avoid visual inconsistencies during the clicking-frame. + if (ImGui::GetIO().KeyCtrl) + selection_mask ^= (1 << node_clicked); // CTRL+click to toggle + else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection + selection_mask = (1 << node_clicked); // Click to single-select + } + ImGui::PopStyleVar(); + if (align_label_with_current_x_position) + ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); + ImGui::TreePop(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Collapsing Headers")) + { + static bool closable_group = true; + ImGui::Checkbox("Show 2nd header", &closable_group); + if (ImGui::CollapsingHeader("Header")) + { + ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); + for (int i = 0; i < 5; i++) + ImGui::Text("Some content %d", i); + } + if (ImGui::CollapsingHeader("Header with a close button", &closable_group)) + { + ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); + for (int i = 0; i < 5; i++) + ImGui::Text("More content %d", i); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Bullets")) + { + ImGui::BulletText("Bullet point 1"); + ImGui::BulletText("Bullet point 2\nOn multiple lines"); + ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)"); + ImGui::Bullet(); ImGui::SmallButton("Button"); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Text")) + { + if (ImGui::TreeNode("Colored Text")) + { + // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility. + ImGui::TextColored(ImVec4(1.0f,0.0f,1.0f,1.0f), "Pink"); + ImGui::TextColored(ImVec4(1.0f,1.0f,0.0f,1.0f), "Yellow"); + ImGui::TextDisabled("Disabled"); + ImGui::SameLine(); HelpMarker("The TextDisabled color is stored in ImGuiStyle."); ImGui::TreePop(); } - if (ImGui::TreeNode("Collapsing Headers")) + if (ImGui::TreeNode("Word Wrapping")) { - static bool closable_group = true; - ImGui::Checkbox("Enable extra group", &closable_group); - if (ImGui::CollapsingHeader("Header")) - { - ImGui::Text("IsItemHovered: %d", IsItemHovered()); - for (int i = 0; i < 5; i++) - ImGui::Text("Some content %d", i); - } - if (ImGui::CollapsingHeader("Header with a close button", &closable_group)) - { - ImGui::Text("IsItemHovered: %d", IsItemHovered()); - for (int i = 0; i < 5; i++) - ImGui::Text("More content %d", i); - } - ImGui::TreePop(); - } + // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. + ImGui::TextWrapped("This text should automatically wrap on the edge of the window. The current implementation for text wrapping follows simple rules suitable for English and possibly other languages."); + ImGui::Spacing(); - if (ImGui::TreeNode("Bullets")) - { - ImGui::BulletText("Bullet point 1"); - ImGui::BulletText("Bullet point 2\nOn multiple lines"); - ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)"); - ImGui::Bullet(); ImGui::SmallButton("Button"); - ImGui::TreePop(); - } + static float wrap_width = 200.0f; + ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f"); - if (ImGui::TreeNode("Text")) - { - if (ImGui::TreeNode("Colored Text")) - { - // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility. - ImGui::TextColored(ImVec4(1.0f,0.0f,1.0f,1.0f), "Pink"); - ImGui::TextColored(ImVec4(1.0f,1.0f,0.0f,1.0f), "Yellow"); - ImGui::TextDisabled("Disabled"); - ImGui::SameLine(); ShowHelpMarker("The TextDisabled color is stored in ImGuiStyle."); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Word Wrapping")) - { - // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. - ImGui::TextWrapped("This text should automatically wrap on the edge of the window. The current implementation for text wrapping follows simple rules suitable for English and possibly other languages."); - ImGui::Spacing(); - - static float wrap_width = 200.0f; - ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f"); - - ImGui::Text("Test paragraph 1:"); - ImVec2 pos = ImGui::GetCursorScreenPos(); - ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255)); - ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); - ImGui::Text("The lazy dog is a good dog. This paragraph is made to fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", wrap_width); - ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255)); - ImGui::PopTextWrapPos(); - - ImGui::Text("Test paragraph 2:"); - pos = ImGui::GetCursorScreenPos(); - ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255)); - ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); - ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh"); - ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255)); - ImGui::PopTextWrapPos(); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("UTF-8 Text")) - { - // UTF-8 test with Japanese characters - // (needs a suitable font, try Arial Unicode or M+ fonts http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/index-en.html) - // - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8 - // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. Visual Studio save your file as 'UTF-8 without signature') - // - HOWEVER, FOR THIS DEMO FILE, BECAUSE WE WANT TO SUPPORT COMPILER, WE ARE *NOT* INCLUDING RAW UTF-8 CHARACTERS IN THIS SOURCE FILE. - // Instead we are encoding a few string with hexadecimal constants. Don't do this in your application! - // Note that characters values are preserved even by InputText() if the font cannot be displayed, so you can safely copy & paste garbled characters into another application. - ImGui::TextWrapped("CJK text will only appears if the font was loaded with the appropriate CJK character ranges. Call io.Font->LoadFromFileTTF() manually to load extra character ranges."); - ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); - ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)"); - static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; // "nihongo" - ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf)); - ImGui::TreePop(); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Images")) - { - ImGui::TextWrapped("Below we are displaying the font texture (which is the only texture we have access to in this demo). Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. Hover the texture for a zoomed view!"); - ImGuiIO& io = ImGui::GetIO(); - - // Here we are grabbing the font texture because that's the only one we have access to inside the demo code. - // Remember that ImTextureID is just storage for whatever you want it to be, it is essentially a value that will be passed to the render function inside the ImDrawCmd structure. - // If you use one of the default imgui_impl_XXXX.cpp renderer, they all have comments at the top of their file to specify what they expect to be stored in ImTextureID. - // (for example, the imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer. The imgui_impl_glfw_gl3.cpp renderer expect a GLuint OpenGL texture identifier etc.) - // If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers to ImGui::Image(), and gather width/height through your own functions, etc. - // Using ShowMetricsWindow() as a "debugger" to inspect the draw data that are being passed to your render will help you debug issues if you are confused about this. - // Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage(). - ImTextureID my_tex_id = io.Fonts->TexID; - float my_tex_w = (float)io.Fonts->TexWidth; - float my_tex_h = (float)io.Fonts->TexHeight; - - ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); + ImGui::Text("Test paragraph 1:"); ImVec2 pos = ImGui::GetCursorScreenPos(); - ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); - if (ImGui::IsItemHovered()) + ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255)); + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); + ImGui::Text("The lazy dog is a good dog. This paragraph is made to fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", wrap_width); + ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255)); + ImGui::PopTextWrapPos(); + + ImGui::Text("Test paragraph 2:"); + pos = ImGui::GetCursorScreenPos(); + ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255)); + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); + ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh"); + ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255)); + ImGui::PopTextWrapPos(); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("UTF-8 Text")) + { + // UTF-8 test with Japanese characters + // (Needs a suitable font, try Noto, or Arial Unicode, or M+ fonts. Read misc/fonts/README.txt for details.) + // - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8 + // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. Visual Studio save your file as 'UTF-8 without signature') + // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8 CHARACTERS IN THIS SOURCE FILE. + // Instead we are encoding a few strings with hexadecimal constants. Don't do this in your application! + // Please use u8"text in any language" in your application! + // Note that characters values are preserved even by InputText() if the font cannot be displayed, so you can safely copy & paste garbled characters into another application. + ImGui::TextWrapped("CJK text will only appears if the font was loaded with the appropriate CJK character ranges. Call io.Font->AddFontFromFileTTF() manually to load extra character ranges. Read misc/fonts/README.txt for details."); + ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); // Normally we would use u8"blah blah" with the proper characters directly in the string. + ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)"); + static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; + //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis + ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf)); + ImGui::TreePop(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Images")) + { + ImGuiIO& io = ImGui::GetIO(); + ImGui::TextWrapped("Below we are displaying the font texture (which is the only texture we have access to in this demo). Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. Hover the texture for a zoomed view!"); + + // Here we are grabbing the font texture because that's the only one we have access to inside the demo code. + // Remember that ImTextureID is just storage for whatever you want it to be, it is essentially a value that will be passed to the render function inside the ImDrawCmd structure. + // If you use one of the default imgui_impl_XXXX.cpp renderer, they all have comments at the top of their file to specify what they expect to be stored in ImTextureID. + // (for example, the imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer. The imgui_impl_glfw_gl3.cpp renderer expect a GLuint OpenGL texture identifier etc.) + // If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers to ImGui::Image(), and gather width/height through your own functions, etc. + // Using ShowMetricsWindow() as a "debugger" to inspect the draw data that are being passed to your render will help you debug issues if you are confused about this. + // Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage(). + ImTextureID my_tex_id = io.Fonts->TexID; + float my_tex_w = (float)io.Fonts->TexWidth; + float my_tex_h = (float)io.Fonts->TexHeight; + + ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); + ImVec2 pos = ImGui::GetCursorScreenPos(); + ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), ImVec2(0,0), ImVec2(1,1), ImVec4(1.0f,1.0f,1.0f,1.0f), ImVec4(1.0f,1.0f,1.0f,0.5f)); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + float region_sz = 32.0f; + float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; if (region_x < 0.0f) region_x = 0.0f; else if (region_x > my_tex_w - region_sz) region_x = my_tex_w - region_sz; + float region_y = io.MousePos.y - pos.y - region_sz * 0.5f; if (region_y < 0.0f) region_y = 0.0f; else if (region_y > my_tex_h - region_sz) region_y = my_tex_h - region_sz; + float zoom = 4.0f; + ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y); + ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz); + ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h); + ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h); + ImGui::Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), ImVec4(1.0f, 1.0f, 1.0f, 0.5f)); + ImGui::EndTooltip(); + } + ImGui::TextWrapped("And now some textured buttons.."); + static int pressed_count = 0; + for (int i = 0; i < 8; i++) + { + ImGui::PushID(i); + int frame_padding = -1 + i; // -1 = uses default padding + if (ImGui::ImageButton(my_tex_id, ImVec2(32,32), ImVec2(0,0), ImVec2(32.0f/my_tex_w,32/my_tex_h), frame_padding, ImVec4(0.0f,0.0f,0.0f,1.0f))) + pressed_count += 1; + ImGui::PopID(); + ImGui::SameLine(); + } + ImGui::NewLine(); + ImGui::Text("Pressed %d times.", pressed_count); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Combo")) + { + // Expose flags as checkbox for the demo + static ImGuiComboFlags flags = 0; + ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", (unsigned int*)&flags, ImGuiComboFlags_PopupAlignLeft); + ImGui::SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo"); + if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", (unsigned int*)&flags, ImGuiComboFlags_NoArrowButton)) + flags &= ~ImGuiComboFlags_NoPreview; // Clear the other flag, as we cannot combine both + if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", (unsigned int*)&flags, ImGuiComboFlags_NoPreview)) + flags &= ~ImGuiComboFlags_NoArrowButton; // Clear the other flag, as we cannot combine both + + // General BeginCombo() API, you have full control over your selection data and display type. + // (your selection data could be an index, a pointer to the object, an id for the object, a flag stored in the object itself, etc.) + const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; + static const char* item_current = items[0]; // Here our selection is a single pointer stored outside the object. + if (ImGui::BeginCombo("combo 1", item_current, flags)) // The second parameter is the label previewed before opening the combo. + { + for (int n = 0; n < IM_ARRAYSIZE(items); n++) { - ImGui::BeginTooltip(); - float focus_sz = 32.0f; - float focus_x = io.MousePos.x - pos.x - focus_sz * 0.5f; if (focus_x < 0.0f) focus_x = 0.0f; else if (focus_x > my_tex_w - focus_sz) focus_x = my_tex_w - focus_sz; - float focus_y = io.MousePos.y - pos.y - focus_sz * 0.5f; if (focus_y < 0.0f) focus_y = 0.0f; else if (focus_y > my_tex_h - focus_sz) focus_y = my_tex_h - focus_sz; - ImGui::Text("Min: (%.2f, %.2f)", focus_x, focus_y); - ImGui::Text("Max: (%.2f, %.2f)", focus_x + focus_sz, focus_y + focus_sz); - ImVec2 uv0 = ImVec2((focus_x) / my_tex_w, (focus_y) / my_tex_h); - ImVec2 uv1 = ImVec2((focus_x + focus_sz) / my_tex_w, (focus_y + focus_sz) / my_tex_h); - ImGui::Image(my_tex_id, ImVec2(128,128), uv0, uv1, ImColor(255,255,255,255), ImColor(255,255,255,128)); - ImGui::EndTooltip(); + bool is_selected = (item_current == items[n]); + if (ImGui::Selectable(items[n], is_selected)) + item_current = items[n]; + if (is_selected) + ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) } - ImGui::TextWrapped("And now some textured buttons.."); - static int pressed_count = 0; - for (int i = 0; i < 8; i++) + ImGui::EndCombo(); + } + + // Simplified one-liner Combo() API, using values packed in a single constant string + static int item_current_2 = 0; + ImGui::Combo("combo 2 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); + + // Simplified one-liner Combo() using an array of const char* + static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview + ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); + + // Simplified one-liner Combo() using an accessor function + struct FuncHolder { static bool ItemGetter(void* data, int idx, const char** out_str) { *out_str = ((const char**)data)[idx]; return true; } }; + static int item_current_4 = 0; + ImGui::Combo("combo 4 (function)", &item_current_4, &FuncHolder::ItemGetter, items, IM_ARRAYSIZE(items)); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Selectables")) + { + // Selectable() has 2 overloads: + // - The one taking "bool selected" as a read-only selection information. When Selectable() has been clicked is returns true and you can alter selection state accordingly. + // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases) + // The earlier is more flexible, as in real application your selection may be stored in a different manner (in flags within objects, as an external list, etc). + if (ImGui::TreeNode("Basic")) + { + static bool selection[5] = { false, true, false, false, false }; + ImGui::Selectable("1. I am selectable", &selection[0]); + ImGui::Selectable("2. I am selectable", &selection[1]); + ImGui::Text("3. I am not selectable"); + ImGui::Selectable("4. I am selectable", &selection[3]); + if (ImGui::Selectable("5. I am double clickable", selection[4], ImGuiSelectableFlags_AllowDoubleClick)) + if (ImGui::IsMouseDoubleClicked(0)) + selection[4] = !selection[4]; + ImGui::TreePop(); + } + if (ImGui::TreeNode("Selection State: Single Selection")) + { + static int selected = -1; + for (int n = 0; n < 5; n++) + { + char buf[32]; + sprintf(buf, "Object %d", n); + if (ImGui::Selectable(buf, selected == n)) + selected = n; + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Selection State: Multiple Selection")) + { + HelpMarker("Hold CTRL and click to select multiple items."); + static bool selection[5] = { false, false, false, false, false }; + for (int n = 0; n < 5; n++) + { + char buf[32]; + sprintf(buf, "Object %d", n); + if (ImGui::Selectable(buf, selection[n])) + { + if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held + memset(selection, 0, sizeof(selection)); + selection[n] ^= 1; + } + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Rendering more text into the same line")) + { + // Using the Selectable() override that takes "bool* p_selected" parameter and toggle your booleans automatically. + static bool selected[3] = { false, false, false }; + ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); + ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(300); ImGui::Text("12,345 bytes"); + ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("In columns")) + { + ImGui::Columns(3, NULL, false); + static bool selected[16] = { 0 }; + for (int i = 0; i < 16; i++) + { + char label[32]; sprintf(label, "Item %d", i); + if (ImGui::Selectable(label, &selected[i])) {} + ImGui::NextColumn(); + } + ImGui::Columns(1); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Grid")) + { + static bool selected[4*4] = { true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true }; + for (int i = 0; i < 4*4; i++) { ImGui::PushID(i); - int frame_padding = -1 + i; // -1 = uses default padding - if (ImGui::ImageButton(my_tex_id, ImVec2(32,32), ImVec2(0,0), ImVec2(32.0f/my_tex_w,32/my_tex_h), frame_padding, ImColor(0,0,0,255))) - pressed_count += 1; + if (ImGui::Selectable("Sailor", &selected[i], 0, ImVec2(50,50))) + { + // Note: We _unnecessarily_ test for both x/y and i here only to silence some static analyzer. The second part of each test is unnecessary. + int x = i % 4; + int y = i / 4; + if (x > 0) { selected[i - 1] ^= 1; } + if (x < 3 && i < 15) { selected[i + 1] ^= 1; } + if (y > 0 && i > 3) { selected[i - 4] ^= 1; } + if (y < 3 && i < 12) { selected[i + 4] ^= 1; } + } + if ((i % 4) < 3) ImGui::SameLine(); ImGui::PopID(); - ImGui::SameLine(); } - ImGui::NewLine(); - ImGui::Text("Pressed %d times.", pressed_count); ImGui::TreePop(); } - - if (ImGui::TreeNode("Selectables")) + if (ImGui::TreeNode("Alignment")) { - // Selectable() has 2 overloads: - // - The one taking "bool selected" as a read-only selection information. When Selectable() has been clicked is returns true and you can alter selection state accordingly. - // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases) - // The earlier is more flexible, as in real application your selection may be stored in a different manner (in flags within objects, as an external list, etc). - if (ImGui::TreeNode("Basic")) + HelpMarker("Alignment applies when a selectable is larger than its text content.\nBy default, Selectables uses style.SelectableTextAlign but it can be overriden on a per-item basis using PushStyleVar()."); + static bool selected[3*3] = { true, false, true, false, true, false, true, false, true }; + for (int y = 0; y < 3; y++) { - static bool selection[5] = { false, true, false, false, false }; - ImGui::Selectable("1. I am selectable", &selection[0]); - ImGui::Selectable("2. I am selectable", &selection[1]); - ImGui::Text("3. I am not selectable"); - ImGui::Selectable("4. I am selectable", &selection[3]); - if (ImGui::Selectable("5. I am double clickable", selection[4], ImGuiSelectableFlags_AllowDoubleClick)) - if (ImGui::IsMouseDoubleClicked(0)) - selection[4] = !selection[4]; - ImGui::TreePop(); - } - if (ImGui::TreeNode("Selection State: Single Selection")) - { - static int selected = -1; - for (int n = 0; n < 5; n++) + for (int x = 0; x < 3; x++) { - char buf[32]; - sprintf(buf, "Object %d", n); - if (ImGui::Selectable(buf, selected == n)) - selected = n; + ImVec2 alignment = ImVec2((float)x / 2.0f, (float)y / 2.0f); + char name[32]; + sprintf(name, "(%.1f,%.1f)", alignment.x, alignment.y); + if (x > 0) ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment); + ImGui::Selectable(name, &selected[3*y+x], ImGuiSelectableFlags_None, ImVec2(80,80)); + ImGui::PopStyleVar(); } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Selection State: Multiple Selection")) - { - ShowHelpMarker("Hold CTRL and click to select multiple items."); - static bool selection[5] = { false, false, false, false, false }; - for (int n = 0; n < 5; n++) - { - char buf[32]; - sprintf(buf, "Object %d", n); - if (ImGui::Selectable(buf, selection[n])) - { - if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held - memset(selection, 0, sizeof(selection)); - selection[n] ^= 1; - } - } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Rendering more text into the same line")) - { - // Using the Selectable() override that takes "bool* p_selected" parameter and toggle your booleans automatically. - static bool selected[3] = { false, false, false }; - ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); - ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(300); ImGui::Text("12,345 bytes"); - ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("In columns")) - { - ImGui::Columns(3, NULL, false); - static bool selected[16] = { 0 }; - for (int i = 0; i < 16; i++) - { - char label[32]; sprintf(label, "Item %d", i); - if (ImGui::Selectable(label, &selected[i])) {} - ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Grid")) - { - static bool selected[16] = { true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true }; - for (int i = 0; i < 16; i++) - { - ImGui::PushID(i); - if (ImGui::Selectable("Sailor", &selected[i], 0, ImVec2(50,50))) - { - int x = i % 4, y = i / 4; - if (x > 0) selected[i - 1] ^= 1; - if (x < 3) selected[i + 1] ^= 1; - if (y > 0) selected[i - 4] ^= 1; - if (y < 3) selected[i + 4] ^= 1; - } - if ((i % 4) < 3) ImGui::SameLine(); - ImGui::PopID(); - } - ImGui::TreePop(); } ImGui::TreePop(); } + ImGui::TreePop(); + } - if (ImGui::TreeNode("Filtered Text Input")) - { - static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); - static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); - static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); - static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); - static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); - struct TextFilters { static int FilterImGuiLetters(ImGuiTextEditCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; - static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); - - ImGui::Text("Password input"); - static char bufpass[64] = "password123"; - ImGui::InputText("password", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); - ImGui::SameLine(); ShowHelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); - ImGui::InputText("password (clear)", bufpass, 64, ImGuiInputTextFlags_CharsNoBlank); - - ImGui::TreePop(); - } - + if (ImGui::TreeNode("Text Input")) + { if (ImGui::TreeNode("Multi-line Text Input")) { - static bool read_only = false; - static char text[1024*16] = + // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize + // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings. + static char text[1024 * 16] = "/*\n" " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" " the hexadecimal encoding of one offending instruction,\n" @@ -741,1050 +963,1796 @@ void ImGui::ShowDemoWindow(bool* p_open) "label:\n" "\tlock cmpxchg8b eax\n"; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); - ImGui::Checkbox("Read-only", &read_only); - ImGui::PopStyleVar(); - ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), ImGuiInputTextFlags_AllowTabInput | (read_only ? ImGuiInputTextFlags_ReadOnly : 0)); + static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput; + HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp)"); + ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", (unsigned int*)&flags, ImGuiInputTextFlags_ReadOnly); + ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", (unsigned int*)&flags, ImGuiInputTextFlags_AllowTabInput); + ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", (unsigned int*)&flags, ImGuiInputTextFlags_CtrlEnterForNewLine); + ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags); ImGui::TreePop(); } - if (ImGui::TreeNode("Plots widgets")) + if (ImGui::TreeNode("Filtered Text Input")) { - static bool animate = true; - ImGui::Checkbox("Animate", &animate); + static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); + static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); + static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); + static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); + static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); + struct TextFilters { static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; + static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); - static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); + ImGui::Text("Password input"); + static char bufpass[64] = "password123"; + ImGui::InputText("password", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); + ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); + ImGui::InputTextWithHint("password (w/ hint)", "", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); + ImGui::InputText("password (clear)", bufpass, 64, ImGuiInputTextFlags_CharsNoBlank); + ImGui::TreePop(); + } - // Create a dummy array of contiguous float values to plot - // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float and the sizeof() of your structure in the Stride parameter. - static float values[90] = { 0 }; - static int values_offset = 0; - static float refresh_time = 0.0f; - if (!animate || refresh_time == 0.0f) - refresh_time = ImGui::GetTime(); - while (refresh_time < ImGui::GetTime()) // Create dummy data at fixed 60 hz rate for the demo - { - static float phase = 0.0f; - values[values_offset] = cosf(phase); - values_offset = (values_offset+1) % IM_ARRAYSIZE(values); - phase += 0.10f*values_offset; - refresh_time += 1.0f/60.0f; - } - ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, "avg 0.0", -1.0f, 1.0f, ImVec2(0,80)); - ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0,80)); - - // Use functions to generate output - // FIXME: This is rather awkward because current plot API only pass in indices. We probably want an API passing floats and user provide sample rate/count. + if (ImGui::TreeNode("Resize Callback")) + { + // If you have a custom string type you would typically create a ImGui::InputText() wrapper than takes your type as input. + // See misc/cpp/imgui_stdlib.h and .cpp for an implementation of this using std::string. + HelpMarker("Demonstrate using ImGuiInputTextFlags_CallbackResize to wire your resizable string type to InputText().\n\nSee misc/cpp/imgui_stdlib.h for an implementation of this for std::string."); struct Funcs { - static float Sin(void*, int i) { return sinf(i * 0.1f); } - static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; } - }; - static int func_type = 0, display_count = 70; - ImGui::Separator(); - ImGui::PushItemWidth(100); ImGui::Combo("func", &func_type, "Sin\0Saw\0"); ImGui::PopItemWidth(); - ImGui::SameLine(); - ImGui::SliderInt("Sample count", &display_count, 1, 400); - float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw; - ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80)); - ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80)); - ImGui::Separator(); - - // Animate a simple progress bar - static float progress = 0.0f, progress_dir = 1.0f; - if (animate) - { - progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; - if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; } - if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; } - } - - // Typically we would use ImVec2(-1.0f,0.0f) to use all available width, or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth. - ImGui::ProgressBar(progress, ImVec2(0.0f,0.0f)); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::Text("Progress Bar"); - - float progress_saturated = (progress < 0.0f) ? 0.0f : (progress > 1.0f) ? 1.0f : progress; - char buf[32]; - sprintf(buf, "%d/%d", (int)(progress_saturated*1753), 1753); - ImGui::ProgressBar(progress, ImVec2(0.f,0.f), buf); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Color/Picker Widgets")) - { - static ImVec4 color = ImColor(114, 144, 154, 200); - - static bool alpha_preview = true; - static bool alpha_half_preview = false; - static bool options_menu = true; - static bool hdr = false; - ImGui::Checkbox("With Alpha Preview", &alpha_preview); - ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview); - ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); ShowHelpMarker("Right-click on the individual color widget to show options."); - ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); ShowHelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); - int misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); - - ImGui::Text("Color widget:"); - ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nCTRL+click on individual component to input value.\n"); - ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); - - ImGui::Text("Color widget HSV with Alpha:"); - ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_HSV | misc_flags); - - ImGui::Text("Color widget with Float Display:"); - ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); - - ImGui::Text("Color button with Picker:"); - ImGui::SameLine(); ShowHelpMarker("With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\nWith the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only be used for the tooltip and picker popup."); - ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); - - ImGui::Text("Color button with Custom Picker Popup:"); - - // Generate a dummy palette - static bool saved_palette_inited = false; - static ImVec4 saved_palette[32]; - if (!saved_palette_inited) - for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) + static int MyResizeCallback(ImGuiInputTextCallbackData* data) { - ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, saved_palette[n].x, saved_palette[n].y, saved_palette[n].z); - saved_palette[n].w = 1.0f; // Alpha - } - saved_palette_inited = true; - - static ImVec4 backup_color; - bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags); - ImGui::SameLine(); - open_popup |= ImGui::Button("Palette"); - if (open_popup) - { - ImGui::OpenPopup("mypicker"); - backup_color = color; - } - if (ImGui::BeginPopup("mypicker")) - { - // FIXME: Adding a drag and drop example here would be perfect! - ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!"); - ImGui::Separator(); - ImGui::ColorPicker4("##picker", (float*)&color, misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); - ImGui::SameLine(); - ImGui::BeginGroup(); - ImGui::Text("Current"); - ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40)); - ImGui::Text("Previous"); - if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40))) - color = backup_color; - ImGui::Separator(); - ImGui::Text("Palette"); - for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) - { - ImGui::PushID(n); - if ((n % 8) != 0) - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y); - if (ImGui::ColorButton("##palette", saved_palette[n], ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip, ImVec2(20,20))) - color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha! - - if (ImGui::BeginDragDropTarget()) + if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) { - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) - memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3); - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) - memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4); - EndDragDropTarget(); + ImVector* my_str = (ImVector*)data->UserData; + IM_ASSERT(my_str->begin() == data->Buf); + my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1 + data->Buf = my_str->begin(); } - - ImGui::PopID(); + return 0; } - ImGui::EndGroup(); - ImGui::EndPopup(); - } - ImGui::Text("Color button only:"); - ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags, ImVec2(80,80)); - - ImGui::Text("Color picker:"); - static bool alpha = true; - static bool alpha_bar = true; - static bool side_preview = true; - static bool ref_color = false; - static ImVec4 ref_color_v(1.0f,0.0f,1.0f,0.5f); - static int inputs_mode = 2; - static int picker_mode = 0; - ImGui::Checkbox("With Alpha", &alpha); - ImGui::Checkbox("With Alpha Bar", &alpha_bar); - ImGui::Checkbox("With Side Preview", &side_preview); - if (side_preview) - { - ImGui::SameLine(); - ImGui::Checkbox("With Ref Color", &ref_color); - if (ref_color) + // Tip: Because ImGui:: is a namespace you would typicall add your own function into the namespace in your own source files. + // For example, you may add a function called ImGui::InputText(const char* label, MyString* my_str). + static bool MyInputTextMultiline(const char* label, ImVector* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0) { - ImGui::SameLine(); - ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags); + IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); + return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size, flags | ImGuiInputTextFlags_CallbackResize, Funcs::MyResizeCallback, (void*)my_str); } - } - ImGui::Combo("Inputs Mode", &inputs_mode, "All Inputs\0No Inputs\0RGB Input\0HSV Input\0HEX Input\0"); - ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0Hue bar + SV rect\0Hue wheel + SV triangle\0"); - ImGui::SameLine(); ShowHelpMarker("User can right-click the picker to change mode."); - ImGuiColorEditFlags flags = misc_flags; - if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() - if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; - if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; - if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; - if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; - if (inputs_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; - if (inputs_mode == 2) flags |= ImGuiColorEditFlags_RGB; - if (inputs_mode == 3) flags |= ImGuiColorEditFlags_HSV; - if (inputs_mode == 4) flags |= ImGuiColorEditFlags_HEX; - ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL); - - ImGui::Text("Programmatically set defaults/options:"); - ImGui::SameLine(); ShowHelpMarker("SetColorEditOptions() is designed to allow you to set boot-time default.\nWe don't have Push/Pop functions because you can force options on a per-widget basis if needed, and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid encouraging you to persistently save values that aren't forward-compatible."); - if (ImGui::Button("Uint8 + HSV")) - ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_HSV); - ImGui::SameLine(); - if (ImGui::Button("Float + HDR")) - ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_RGB); + }; + // For this demo we are using ImVector as a string container. + // Note that because we need to store a terminating zero character, our size/capacity are 1 more than usually reported by a typical string class. + static ImVector my_str; + if (my_str.empty()) + my_str.push_back(0); + Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16)); + ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity()); ImGui::TreePop(); } - if (ImGui::TreeNode("Range Widgets")) - { - static float begin = 10, end = 90; - static int begin_i = 100, end_i = 1000; - ImGui::DragFloatRange2("range", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%"); - ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %.0f units", "Max: %.0f units"); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Multi-component Widgets")) - { - static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; - static int vec4i[4] = { 1, 5, 100, 255 }; - - ImGui::InputFloat2("input float2", vec4f); - ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f); - ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f); - ImGui::DragInt2("drag int2", vec4i, 1, 0, 255); - ImGui::InputInt2("input int2", vec4i); - ImGui::SliderInt2("slider int2", vec4i, 0, 255); - ImGui::Spacing(); - - ImGui::InputFloat3("input float3", vec4f); - ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f); - ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f); - ImGui::DragInt3("drag int3", vec4i, 1, 0, 255); - ImGui::InputInt3("input int3", vec4i); - ImGui::SliderInt3("slider int3", vec4i, 0, 255); - ImGui::Spacing(); - - ImGui::InputFloat4("input float4", vec4f); - ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f); - ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f); - ImGui::InputInt4("input int4", vec4i); - ImGui::DragInt4("drag int4", vec4i, 1, 0, 255); - ImGui::SliderInt4("slider int4", vec4i, 0, 255); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Vertical Sliders")) - { - const float spacing = 4; - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing)); - - static int int_value = 0; - ImGui::VSliderInt("##int", ImVec2(18,160), &int_value, 0, 5); - ImGui::SameLine(); - - static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f }; - ImGui::PushID("set1"); - for (int i = 0; i < 7; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i/7.0f, 0.5f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i/7.0f, 0.7f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i/7.0f, 0.9f, 0.9f)); - ImGui::VSliderFloat("##v", ImVec2(18,160), &values[i], 0.0f, 1.0f, ""); - if (ImGui::IsItemActive() || ImGui::IsItemHovered()) - ImGui::SetTooltip("%.3f", values[i]); - ImGui::PopStyleColor(4); - ImGui::PopID(); - } - ImGui::PopID(); - - ImGui::SameLine(); - ImGui::PushID("set2"); - static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f }; - const int rows = 3; - const ImVec2 small_slider_size(18, (160.0f-(rows-1)*spacing)/rows); - for (int nx = 0; nx < 4; nx++) - { - if (nx > 0) ImGui::SameLine(); - ImGui::BeginGroup(); - for (int ny = 0; ny < rows; ny++) - { - ImGui::PushID(nx*rows+ny); - ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, ""); - if (ImGui::IsItemActive() || ImGui::IsItemHovered()) - ImGui::SetTooltip("%.3f", values2[nx]); - ImGui::PopID(); - } - ImGui::EndGroup(); - } - ImGui::PopID(); - - ImGui::SameLine(); - ImGui::PushID("set3"); - for (int i = 0; i < 4; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40); - ImGui::VSliderFloat("##v", ImVec2(40,160), &values[i], 0.0f, 1.0f, "%.2f\nsec"); - ImGui::PopStyleVar(); - ImGui::PopID(); - } - ImGui::PopID(); - ImGui::PopStyleVar(); - ImGui::TreePop(); - } + ImGui::TreePop(); } - if (ImGui::CollapsingHeader("Layout")) + if (ImGui::TreeNode("Plots Widgets")) { - if (ImGui::TreeNode("Child regions")) + static bool animate = true; + ImGui::Checkbox("Animate", &animate); + + static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; + ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); + + // Create a dummy array of contiguous float values to plot + // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float and the sizeof() of your structure in the Stride parameter. + static float values[90] = { 0 }; + static int values_offset = 0; + static double refresh_time = 0.0; + if (!animate || refresh_time == 0.0) + refresh_time = ImGui::GetTime(); + while (refresh_time < ImGui::GetTime()) // Create dummy data at fixed 60 hz rate for the demo { - static bool disable_mouse_wheel = false; - static bool disable_menu = false; - ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel); - ImGui::Checkbox("Disable Menu", &disable_menu); + static float phase = 0.0f; + values[values_offset] = cosf(phase); + values_offset = (values_offset+1) % IM_ARRAYSIZE(values); + phase += 0.10f*values_offset; + refresh_time += 1.0f/60.0f; + } + ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, "avg 0.0", -1.0f, 1.0f, ImVec2(0,80)); + ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0,80)); - static int line = 50; - bool goto_line = ImGui::Button("Goto"); - ImGui::SameLine(); - ImGui::PushItemWidth(100); - goto_line |= ImGui::InputInt("##Line", &line, 0, 0, ImGuiInputTextFlags_EnterReturnsTrue); - ImGui::PopItemWidth(); + // Use functions to generate output + // FIXME: This is rather awkward because current plot API only pass in indices. We probably want an API passing floats and user provide sample rate/count. + struct Funcs + { + static float Sin(void*, int i) { return sinf(i * 0.1f); } + static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; } + }; + static int func_type = 0, display_count = 70; + ImGui::Separator(); + ImGui::SetNextItemWidth(100); + ImGui::Combo("func", &func_type, "Sin\0Saw\0"); + ImGui::SameLine(); + ImGui::SliderInt("Sample count", &display_count, 1, 400); + float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw; + ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80)); + ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80)); + ImGui::Separator(); - // Child 1: no border, enable horizontal scrollbar - { - ImGui::BeginChild("Child1", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 300), false, ImGuiWindowFlags_HorizontalScrollbar | (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0)); - for (int i = 0; i < 100; i++) - { - ImGui::Text("%04d: scrollable region", i); - if (goto_line && line == i) - ImGui::SetScrollHere(); - } - if (goto_line && line >= 100) - ImGui::SetScrollHere(); - ImGui::EndChild(); - } - - ImGui::SameLine(); - - // Child 2: rounded border - { - ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); - ImGui::BeginChild("Child2", ImVec2(0,300), true, (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0) | (disable_menu ? 0 : ImGuiWindowFlags_MenuBar)); - if (!disable_menu && ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("Menu")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - ImGui::Columns(2); - for (int i = 0; i < 100; i++) - { - if (i == 50) - ImGui::NextColumn(); - char buf[32]; - sprintf(buf, "%08x", i*5731); - ImGui::Button(buf, ImVec2(-1.0f, 0.0f)); - } - ImGui::EndChild(); - ImGui::PopStyleVar(); - } - - ImGui::TreePop(); + // Animate a simple progress bar + static float progress = 0.0f, progress_dir = 1.0f; + if (animate) + { + progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; + if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; } + if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; } } - if (ImGui::TreeNode("Widgets Width")) + // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width, + // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth. + ImGui::ProgressBar(progress, ImVec2(0.0f,0.0f)); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::Text("Progress Bar"); + + float progress_saturated = (progress < 0.0f) ? 0.0f : (progress > 1.0f) ? 1.0f : progress; + char buf[32]; + sprintf(buf, "%d/%d", (int)(progress_saturated*1753), 1753); + ImGui::ProgressBar(progress, ImVec2(0.f,0.f), buf); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Color/Picker Widgets")) + { + static ImVec4 color = ImVec4(114.0f/255.0f, 144.0f/255.0f, 154.0f/255.0f, 200.0f/255.0f); + + static bool alpha_preview = true; + static bool alpha_half_preview = false; + static bool drag_and_drop = true; + static bool options_menu = true; + static bool hdr = false; + ImGui::Checkbox("With Alpha Preview", &alpha_preview); + ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview); + ImGui::Checkbox("With Drag and Drop", &drag_and_drop); + ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options."); + ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); + int misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); + + ImGui::Text("Color widget:"); + ImGui::SameLine(); HelpMarker("Click on the colored square to open a color picker.\nCTRL+click on individual component to input value.\n"); + ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); + + ImGui::Text("Color widget HSV with Alpha:"); + ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | misc_flags); + + ImGui::Text("Color widget with Float Display:"); + ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); + + ImGui::Text("Color button with Picker:"); + ImGui::SameLine(); HelpMarker("With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\nWith the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only be used for the tooltip and picker popup."); + ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); + + ImGui::Text("Color button with Custom Picker Popup:"); + + // Generate a dummy default palette. The palette will persist and can be edited. + static bool saved_palette_init = true; + static ImVec4 saved_palette[32] = { }; + if (saved_palette_init) { - static float f = 0.0f; - ImGui::Text("PushItemWidth(100)"); - ImGui::SameLine(); ShowHelpMarker("Fixed width."); - ImGui::PushItemWidth(100); - ImGui::DragFloat("float##1", &f); - ImGui::PopItemWidth(); - - ImGui::Text("PushItemWidth(GetWindowWidth() * 0.5f)"); - ImGui::SameLine(); ShowHelpMarker("Half of window width."); - ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); - ImGui::DragFloat("float##2", &f); - ImGui::PopItemWidth(); - - ImGui::Text("PushItemWidth(GetContentRegionAvailWidth() * 0.5f)"); - ImGui::SameLine(); ShowHelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); - ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() * 0.5f); - ImGui::DragFloat("float##3", &f); - ImGui::PopItemWidth(); - - ImGui::Text("PushItemWidth(-100)"); - ImGui::SameLine(); ShowHelpMarker("Align to right edge minus 100"); - ImGui::PushItemWidth(-100); - ImGui::DragFloat("float##4", &f); - ImGui::PopItemWidth(); - - ImGui::Text("PushItemWidth(-1)"); - ImGui::SameLine(); ShowHelpMarker("Align to right edge"); - ImGui::PushItemWidth(-1); - ImGui::DragFloat("float##5", &f); - ImGui::PopItemWidth(); - - ImGui::TreePop(); + for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) + { + ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, saved_palette[n].x, saved_palette[n].y, saved_palette[n].z); + saved_palette[n].w = 1.0f; // Alpha + } + saved_palette_init = false; } - if (ImGui::TreeNode("Basic Horizontal Layout")) + static ImVec4 backup_color; + bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags); + ImGui::SameLine(); + open_popup |= ImGui::Button("Palette"); + if (open_popup) { - ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)"); + ImGui::OpenPopup("mypicker"); + backup_color = color; + } + if (ImGui::BeginPopup("mypicker")) + { + ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!"); + ImGui::Separator(); + ImGui::ColorPicker4("##picker", (float*)&color, misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); + ImGui::SameLine(); - // Text - ImGui::Text("Two items: Hello"); ImGui::SameLine(); - ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); - - // Adjust spacing - ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20); - ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); - - // Button - ImGui::AlignTextToFramePadding(); - ImGui::Text("Normal buttons"); ImGui::SameLine(); - ImGui::Button("Banana"); ImGui::SameLine(); - ImGui::Button("Apple"); ImGui::SameLine(); - ImGui::Button("Corniflower"); - - // Button - ImGui::Text("Small buttons"); ImGui::SameLine(); - ImGui::SmallButton("Like this one"); ImGui::SameLine(); - ImGui::Text("can fit within a text block."); - - // Aligned to arbitrary position. Easy/cheap column. - ImGui::Text("Aligned"); - ImGui::SameLine(150); ImGui::Text("x=150"); - ImGui::SameLine(300); ImGui::Text("x=300"); - ImGui::Text("Aligned"); - ImGui::SameLine(150); ImGui::SmallButton("x=150"); - ImGui::SameLine(300); ImGui::SmallButton("x=300"); - - // Checkbox - static bool c1=false,c2=false,c3=false,c4=false; - ImGui::Checkbox("My", &c1); ImGui::SameLine(); - ImGui::Checkbox("Tailor", &c2); ImGui::SameLine(); - ImGui::Checkbox("Is", &c3); ImGui::SameLine(); - ImGui::Checkbox("Rich", &c4); - - // Various - static float f0=1.0f, f1=2.0f, f2=3.0f; - ImGui::PushItemWidth(80); - const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; - static int item = -1; - ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); - ImGui::SliderFloat("X", &f0, 0.0f,5.0f); ImGui::SameLine(); - ImGui::SliderFloat("Y", &f1, 0.0f,5.0f); ImGui::SameLine(); - ImGui::SliderFloat("Z", &f2, 0.0f,5.0f); - ImGui::PopItemWidth(); - - ImGui::PushItemWidth(80); - ImGui::Text("Lists:"); - static int selection[4] = { 0, 1, 2, 3 }; - for (int i = 0; i < 4; i++) + ImGui::BeginGroup(); // Lock X position + ImGui::Text("Current"); + ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40)); + ImGui::Text("Previous"); + if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40))) + color = backup_color; + ImGui::Separator(); + ImGui::Text("Palette"); + for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); + ImGui::PushID(n); + if ((n % 8) != 0) + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y); + if (ImGui::ColorButton("##palette", saved_palette[n], ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip, ImVec2(20,20))) + color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha! + + // Allow user to drop colors into each palette entry + // (Note that ColorButton is already a drag source by default, unless using ImGuiColorEditFlags_NoDragDrop) + if (ImGui::BeginDragDropTarget()) + { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) + memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3); + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) + memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4); + ImGui::EndDragDropTarget(); + } + ImGui::PopID(); - //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); } - ImGui::PopItemWidth(); + ImGui::EndGroup(); + ImGui::EndPopup(); + } - // Dummy - ImVec2 sz(30,30); - ImGui::Button("A", sz); ImGui::SameLine(); - ImGui::Dummy(sz); ImGui::SameLine(); - ImGui::Button("B", sz); + ImGui::Text("Color button only:"); + ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags, ImVec2(80,80)); + ImGui::Text("Color picker:"); + static bool alpha = true; + static bool alpha_bar = true; + static bool side_preview = true; + static bool ref_color = false; + static ImVec4 ref_color_v(1.0f,0.0f,1.0f,0.5f); + static int display_mode = 0; + static int picker_mode = 0; + ImGui::Checkbox("With Alpha", &alpha); + ImGui::Checkbox("With Alpha Bar", &alpha_bar); + ImGui::Checkbox("With Side Preview", &side_preview); + if (side_preview) + { + ImGui::SameLine(); + ImGui::Checkbox("With Ref Color", &ref_color); + if (ref_color) + { + ImGui::SameLine(); + ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags); + } + } + ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0None\0RGB Only\0HSV Only\0Hex Only\0"); + ImGui::SameLine(); HelpMarker("ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, but the user can change it with a right-click.\n\nColorPicker defaults to displaying RGB+HSV+Hex if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions()."); + ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0Hue bar + SV rect\0Hue wheel + SV triangle\0"); + ImGui::SameLine(); HelpMarker("User can right-click the picker to change mode."); + ImGuiColorEditFlags flags = misc_flags; + if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() + if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; + if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; + if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; + if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; + if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; // Disable all RGB/HSV/Hex displays + if (display_mode == 2) flags |= ImGuiColorEditFlags_DisplayRGB; // Override display mode + if (display_mode == 3) flags |= ImGuiColorEditFlags_DisplayHSV; + if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex; + ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL); + + ImGui::Text("Programmatically set defaults:"); + ImGui::SameLine(); HelpMarker("SetColorEditOptions() is designed to allow you to set boot-time default.\nWe don't have Push/Pop functions because you can force options on a per-widget basis if needed, and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid encouraging you to persistently save values that aren't forward-compatible."); + if (ImGui::Button("Default: Uint8 + HSV + Hue Bar")) + ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar); + if (ImGui::Button("Default: Float + HDR + Hue Wheel")) + ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel); + + // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0) + static ImVec4 color_stored_as_hsv(0.23f, 1.0f, 1.0f, 1.0f); + ImGui::Spacing(); + ImGui::Text("HSV encoded colors"); + ImGui::SameLine(); HelpMarker("By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the added benefit that you can manipulate hue values with the picker even when saturation or value are zero."); + ImGui::Text("Color widget with InputHSV:"); + ImGui::ColorEdit4("HSV shown as RGB##1", (float*)&color_stored_as_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float); + ImGui::ColorEdit4("HSV shown as HSV##1", (float*)&color_stored_as_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float); + ImGui::DragFloat4("Raw HSV values", (float*)&color_stored_as_hsv, 0.01f, 0.0f, 1.0f); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Range Widgets")) + { + static float begin = 10, end = 90; + static int begin_i = 100, end_i = 1000; + ImGui::DragFloatRange2("range", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%"); + ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units"); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Data Types")) + { + // The DragScalar/InputScalar/SliderScalar functions allow various data types: signed/unsigned int/long long and float/double + // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum to pass the type, + // and passing all arguments by address. + // This is the reason the test code below creates local variables to hold "zero" "one" etc. for each types. + // In practice, if you frequently use a given type that is not covered by the normal API entry points, you can wrap it + // yourself inside a 1 line function which can take typed argument as value instead of void*, and then pass their address + // to the generic function. For example: + // bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld") + // { + // return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format); + // } + + // Limits (as helper variables that we can take the address of) + // Note that the SliderScalar function has a maximum usable range of half the natural type maximum, hence the /2 below. + #ifndef LLONG_MIN + ImS64 LLONG_MIN = -9223372036854775807LL - 1; + ImS64 LLONG_MAX = 9223372036854775807LL; + ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1); + #endif + const char s8_zero = 0, s8_one = 1, s8_fifty = 50, s8_min = -128, s8_max = 127; + const ImU8 u8_zero = 0, u8_one = 1, u8_fifty = 50, u8_min = 0, u8_max = 255; + const short s16_zero = 0, s16_one = 1, s16_fifty = 50, s16_min = -32768, s16_max = 32767; + const ImU16 u16_zero = 0, u16_one = 1, u16_fifty = 50, u16_min = 0, u16_max = 65535; + const ImS32 s32_zero = 0, s32_one = 1, s32_fifty = 50, s32_min = INT_MIN/2, s32_max = INT_MAX/2, s32_hi_a = INT_MAX/2 - 100, s32_hi_b = INT_MAX/2; + const ImU32 u32_zero = 0, u32_one = 1, u32_fifty = 50, u32_min = 0, u32_max = UINT_MAX/2, u32_hi_a = UINT_MAX/2 - 100, u32_hi_b = UINT_MAX/2; + const ImS64 s64_zero = 0, s64_one = 1, s64_fifty = 50, s64_min = LLONG_MIN/2, s64_max = LLONG_MAX/2, s64_hi_a = LLONG_MAX/2 - 100, s64_hi_b = LLONG_MAX/2; + const ImU64 u64_zero = 0, u64_one = 1, u64_fifty = 50, u64_min = 0, u64_max = ULLONG_MAX/2, u64_hi_a = ULLONG_MAX/2 - 100, u64_hi_b = ULLONG_MAX/2; + const float f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f; + const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0; + + // State + static char s8_v = 127; + static ImU8 u8_v = 255; + static short s16_v = 32767; + static ImU16 u16_v = 65535; + static ImS32 s32_v = -1; + static ImU32 u32_v = (ImU32)-1; + static ImS64 s64_v = -1; + static ImU64 u64_v = (ImU64)-1; + static float f32_v = 0.123f; + static double f64_v = 90000.01234567890123456789; + + const float drag_speed = 0.2f; + static bool drag_clamp = false; + ImGui::Text("Drags:"); + ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); HelpMarker("As with every widgets in dear imgui, we never modify values unless there is a user interaction.\nYou can override the clamping limits by using CTRL+Click to input a value."); + ImGui::DragScalar("drag s8", ImGuiDataType_S8, &s8_v, drag_speed, drag_clamp ? &s8_zero : NULL, drag_clamp ? &s8_fifty : NULL); + ImGui::DragScalar("drag u8", ImGuiDataType_U8, &u8_v, drag_speed, drag_clamp ? &u8_zero : NULL, drag_clamp ? &u8_fifty : NULL, "%u ms"); + ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL); + ImGui::DragScalar("drag u16", ImGuiDataType_U16, &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL, drag_clamp ? &u16_fifty : NULL, "%u ms"); + ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL); + ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms"); + ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL); + ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL); + ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 1.0f); + ImGui::DragScalar("drag float ^2", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 2.0f); ImGui::SameLine(); HelpMarker("You can use the 'power' parameter to increase tweaking precision on one side of the range."); + ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams", 1.0f); + ImGui::DragScalar("drag double ^2", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", 2.0f); + + ImGui::Text("Sliders"); + ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d"); + ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u"); + ImGui::SliderScalar("slider s16 full", ImGuiDataType_S16, &s16_v, &s16_min, &s16_max, "%d"); + ImGui::SliderScalar("slider u16 full", ImGuiDataType_U16, &u16_v, &u16_min, &u16_max, "%u"); + ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d"); + ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d"); + ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d"); + ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u"); + ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u"); + ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u"); + ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%I64d"); + ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%I64d"); + ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%I64d"); + ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%I64u ms"); + ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%I64u ms"); + ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%I64u ms"); + ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one); + ImGui::SliderScalar("slider float low^2", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", 2.0f); + ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e"); + ImGui::SliderScalar("slider double low", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f grams", 1.0f); + ImGui::SliderScalar("slider double low^2",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", 2.0f); + ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams", 1.0f); + + static bool inputs_step = true; + ImGui::Text("Inputs"); + ImGui::Checkbox("Show step buttons", &inputs_step); + ImGui::InputScalar("input s8", ImGuiDataType_S8, &s8_v, inputs_step ? &s8_one : NULL, NULL, "%d"); + ImGui::InputScalar("input u8", ImGuiDataType_U8, &u8_v, inputs_step ? &u8_one : NULL, NULL, "%u"); + ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d"); + ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u"); + ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d"); + ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal); + ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u"); + ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal); + ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL); + ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL); + ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL); + ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Multi-component Widgets")) + { + static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; + static int vec4i[4] = { 1, 5, 100, 255 }; + + ImGui::InputFloat2("input float2", vec4f); + ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f); + ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f); + ImGui::InputInt2("input int2", vec4i); + ImGui::DragInt2("drag int2", vec4i, 1, 0, 255); + ImGui::SliderInt2("slider int2", vec4i, 0, 255); + ImGui::Spacing(); + + ImGui::InputFloat3("input float3", vec4f); + ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f); + ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f); + ImGui::InputInt3("input int3", vec4i); + ImGui::DragInt3("drag int3", vec4i, 1, 0, 255); + ImGui::SliderInt3("slider int3", vec4i, 0, 255); + ImGui::Spacing(); + + ImGui::InputFloat4("input float4", vec4f); + ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f); + ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f); + ImGui::InputInt4("input int4", vec4i); + ImGui::DragInt4("drag int4", vec4i, 1, 0, 255); + ImGui::SliderInt4("slider int4", vec4i, 0, 255); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Vertical Sliders")) + { + const float spacing = 4; + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing)); + + static int int_value = 0; + ImGui::VSliderInt("##int", ImVec2(18,160), &int_value, 0, 5); + ImGui::SameLine(); + + static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f }; + ImGui::PushID("set1"); + for (int i = 0; i < 7; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i/7.0f, 0.5f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i/7.0f, 0.7f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i/7.0f, 0.9f, 0.9f)); + ImGui::VSliderFloat("##v", ImVec2(18,160), &values[i], 0.0f, 1.0f, ""); + if (ImGui::IsItemActive() || ImGui::IsItemHovered()) + ImGui::SetTooltip("%.3f", values[i]); + ImGui::PopStyleColor(4); + ImGui::PopID(); + } + ImGui::PopID(); + + ImGui::SameLine(); + ImGui::PushID("set2"); + static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f }; + const int rows = 3; + const ImVec2 small_slider_size(18, (160.0f-(rows-1)*spacing)/rows); + for (int nx = 0; nx < 4; nx++) + { + if (nx > 0) ImGui::SameLine(); + ImGui::BeginGroup(); + for (int ny = 0; ny < rows; ny++) + { + ImGui::PushID(nx*rows+ny); + ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, ""); + if (ImGui::IsItemActive() || ImGui::IsItemHovered()) + ImGui::SetTooltip("%.3f", values2[nx]); + ImGui::PopID(); + } + ImGui::EndGroup(); + } + ImGui::PopID(); + + ImGui::SameLine(); + ImGui::PushID("set3"); + for (int i = 0; i < 4; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40); + ImGui::VSliderFloat("##v", ImVec2(40,160), &values[i], 0.0f, 1.0f, "%.2f\nsec"); + ImGui::PopStyleVar(); + ImGui::PopID(); + } + ImGui::PopID(); + ImGui::PopStyleVar(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Drag and Drop")) + { + { + // ColorEdit widgets automatically act as drag source and drag target. + // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F to allow your own widgets + // to use colors in their drag and drop interaction. Also see the demo in Color Picker -> Palette demo. + ImGui::BulletText("Drag and drop in standard widgets"); + ImGui::Indent(); + static float col1[3] = { 1.0f,0.0f,0.2f }; + static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; + ImGui::ColorEdit3("color 1", col1); + ImGui::ColorEdit4("color 2", col2); + ImGui::Unindent(); + } + + { + ImGui::BulletText("Drag and drop to copy/swap items"); + ImGui::Indent(); + enum Mode + { + Mode_Copy, + Mode_Move, + Mode_Swap + }; + static int mode = 0; + if (ImGui::RadioButton("Copy", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine(); + if (ImGui::RadioButton("Move", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine(); + if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; } + static const char* names[9] = { "Bobby", "Beatrice", "Betty", "Brianna", "Barry", "Bernard", "Bibi", "Blaine", "Bryn" }; + for (int n = 0; n < IM_ARRAYSIZE(names); n++) + { + ImGui::PushID(n); + if ((n % 3) != 0) + ImGui::SameLine(); + ImGui::Button(names[n], ImVec2(60,60)); + + // Our buttons are both drag sources and drag targets here! + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) + { + ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int)); // Set payload to carry the index of our item (could be anything) + if (mode == Mode_Copy) { ImGui::Text("Copy %s", names[n]); } // Display preview (could be anything, e.g. when dragging an image we could decide to display the filename and a small preview of the image, etc.) + if (mode == Mode_Move) { ImGui::Text("Move %s", names[n]); } + if (mode == Mode_Swap) { ImGui::Text("Swap %s", names[n]); } + ImGui::EndDragDropSource(); + } + if (ImGui::BeginDragDropTarget()) + { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) + { + IM_ASSERT(payload->DataSize == sizeof(int)); + int payload_n = *(const int*)payload->Data; + if (mode == Mode_Copy) + { + names[n] = names[payload_n]; + } + if (mode == Mode_Move) + { + names[n] = names[payload_n]; + names[payload_n] = ""; + } + if (mode == Mode_Swap) + { + const char* tmp = names[n]; + names[n] = names[payload_n]; + names[payload_n] = tmp; + } + } + ImGui::EndDragDropTarget(); + } + ImGui::PopID(); + } + ImGui::Unindent(); + } + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Querying Status (Active/Focused/Hovered etc.)")) + { + // Display the value of IsItemHovered() and other common item state functions. Note that the flags can be combined. + // (because BulletText is an item itself and that would affect the output of IsItemHovered() we pass all state in a single call to simplify the code). + static int item_type = 1; + static bool b = false; + static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f }; + static char str[16] = {}; + ImGui::RadioButton("Text", &item_type, 0); + ImGui::RadioButton("Button", &item_type, 1); + ImGui::RadioButton("Checkbox", &item_type, 2); + ImGui::RadioButton("SliderFloat", &item_type, 3); + ImGui::RadioButton("InputText", &item_type, 4); + ImGui::RadioButton("InputFloat3", &item_type, 5); + ImGui::RadioButton("ColorEdit4", &item_type, 6); + ImGui::RadioButton("MenuItem", &item_type, 7); + ImGui::RadioButton("TreeNode (w/ double-click)", &item_type, 8); + ImGui::RadioButton("ListBox", &item_type, 9); + ImGui::Separator(); + bool ret = false; + if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction + if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button + if (item_type == 2) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox + if (item_type == 3) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item + if (item_type == 4) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing) + if (item_type == 5) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) + if (item_type == 6) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) + if (item_type == 7) { ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy) + if (item_type == 8) { ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy. + if (item_type == 9) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } + ImGui::BulletText( + "Return value = %d\n" + "IsItemFocused() = %d\n" + "IsItemHovered() = %d\n" + "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" + "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" + "IsItemHovered(_AllowWhenOverlapped) = %d\n" + "IsItemHovered(_RectOnly) = %d\n" + "IsItemActive() = %d\n" + "IsItemEdited() = %d\n" + "IsItemActivated() = %d\n" + "IsItemDeactivated() = %d\n" + "IsItemDeactivatedAfterEdit() = %d\n" + "IsItemVisible() = %d\n" + "IsItemClicked() = %d\n" + "GetItemRectMin() = (%.1f, %.1f)\n" + "GetItemRectMax() = (%.1f, %.1f)\n" + "GetItemRectSize() = (%.1f, %.1f)", + ret, + ImGui::IsItemFocused(), + ImGui::IsItemHovered(), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped), + ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), + ImGui::IsItemActive(), + ImGui::IsItemEdited(), + ImGui::IsItemActivated(), + ImGui::IsItemDeactivated(), + ImGui::IsItemDeactivatedAfterEdit(), + ImGui::IsItemVisible(), + ImGui::IsItemClicked(), + ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y, + ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y, + ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y + ); + + static bool embed_all_inside_a_child_window = false; + ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window); + if (embed_all_inside_a_child_window) + ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20), true); + + // Testing IsWindowFocused() function with its various flags. Note that the flags can be combined. + ImGui::BulletText( + "IsWindowFocused() = %d\n" + "IsWindowFocused(_ChildWindows) = %d\n" + "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" + "IsWindowFocused(_RootWindow) = %d\n" + "IsWindowFocused(_AnyWindow) = %d\n", + ImGui::IsWindowFocused(), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); + + // Testing IsWindowHovered() function with its various flags. Note that the flags can be combined. + ImGui::BulletText( + "IsWindowHovered() = %d\n" + "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" + "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" + "IsWindowHovered(_ChildWindows) = %d\n" + "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" + "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" + "IsWindowHovered(_RootWindow) = %d\n" + "IsWindowHovered(_AnyWindow) = %d\n", + ImGui::IsWindowHovered(), + ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), + ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); + + ImGui::BeginChild("child", ImVec2(0, 50), true); + ImGui::Text("This is another child window for testing the _ChildWindows flag."); + ImGui::EndChild(); + if (embed_all_inside_a_child_window) + ImGui::EndChild(); + + static char dummy_str[] = "This is a dummy field to be able to tab-out of the widgets above."; + ImGui::InputText("dummy", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly); + + // Calling IsItemHovered() after begin returns the hovered status of the title bar. + // This is useful in particular if you want to create a context menu (with BeginPopupContextItem) associated to the title bar of a window. + static bool test_window = false; + ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window); + if (test_window) + { + ImGui::Begin("Title bar Hovered/Active tests", &test_window); + if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered() + { + if (ImGui::MenuItem("Close")) { test_window = false; } + ImGui::EndPopup(); + } + ImGui::Text( + "IsItemHovered() after begin = %d (== is title bar hovered)\n" + "IsItemActive() after begin = %d (== is window being clicked/moved)\n", + ImGui::IsItemHovered(), ImGui::IsItemActive()); + ImGui::End(); + } + + ImGui::TreePop(); + } +} + +static void ShowDemoWindowLayout() +{ + if (!ImGui::CollapsingHeader("Layout")) + return; + + if (ImGui::TreeNode("Child windows")) + { + HelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window."); + static bool disable_mouse_wheel = false; + static bool disable_menu = false; + ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel); + ImGui::Checkbox("Disable Menu", &disable_menu); + + static int line = 50; + bool goto_line = ImGui::Button("Goto"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(100); + goto_line |= ImGui::InputInt("##Line", &line, 0, 0, ImGuiInputTextFlags_EnterReturnsTrue); + + // Child 1: no border, enable horizontal scrollbar + { + ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar | (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0); + ImGui::BeginChild("Child1", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 260), false, window_flags); + for (int i = 0; i < 100; i++) + { + ImGui::Text("%04d: scrollable region", i); + if (goto_line && line == i) + ImGui::SetScrollHereY(); + } + if (goto_line && line >= 100) + ImGui::SetScrollHereY(); + ImGui::EndChild(); + } + + ImGui::SameLine(); + + // Child 2: rounded border + { + ImGuiWindowFlags window_flags = (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0) | (disable_menu ? 0 : ImGuiWindowFlags_MenuBar); + ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); + ImGui::BeginChild("Child2", ImVec2(0, 260), true, window_flags); + if (!disable_menu && ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("Menu")) + { + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + ImGui::Columns(2); + for (int i = 0; i < 100; i++) + { + char buf[32]; + sprintf(buf, "%03d", i); + ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f)); + ImGui::NextColumn(); + } + ImGui::EndChild(); + ImGui::PopStyleVar(); + } + + ImGui::Separator(); + + // Demonstrate a few extra things + // - Changing ImGuiCol_ChildBg (which is transparent black in default styles) + // - Using SetCursorPos() to position the child window (because the child window is an item from the POV of the parent window) + // You can also call SetNextWindowPos() to position the child window. The parent window will effectively layout from this position. + // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from the POV of the parent window) + // See "Widgets" -> "Querying Status (Active/Focused/Hovered etc.)" section for more details about this. + { + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10); + ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100)); + ImGui::BeginChild("blah", ImVec2(200, 100), true, ImGuiWindowFlags_None); + for (int n = 0; n < 50; n++) + ImGui::Text("Some test %d", n); + ImGui::EndChild(); + ImVec2 child_rect_min = ImGui::GetItemRectMin(); + ImVec2 child_rect_max = ImGui::GetItemRectMax(); + ImGui::PopStyleColor(); + ImGui::Text("Rect of child window is: (%.0f,%.0f) (%.0f,%.0f)", child_rect_min.x, child_rect_min.y, child_rect_max.x, child_rect_max.y); + } + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Widgets Width")) + { + // Use SetNextItemWidth() to set the width of a single upcoming item. + // Use PushItemWidth()/PopItemWidth() to set the width of a group of items. + static float f = 0.0f; + ImGui::Text("SetNextItemWidth/PushItemWidth(100)"); + ImGui::SameLine(); HelpMarker("Fixed width."); + ImGui::SetNextItemWidth(100); + ImGui::DragFloat("float##1", &f); + + ImGui::Text("SetNextItemWidth/PushItemWidth(GetWindowWidth() * 0.5f)"); + ImGui::SameLine(); HelpMarker("Half of window width."); + ImGui::SetNextItemWidth(ImGui::GetWindowWidth() * 0.5f); + ImGui::DragFloat("float##2", &f); + + ImGui::Text("SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)"); + ImGui::SameLine(); HelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); + ImGui::DragFloat("float##3", &f); + + ImGui::Text("SetNextItemWidth/PushItemWidth(-100)"); + ImGui::SameLine(); HelpMarker("Align to right edge minus 100"); + ImGui::SetNextItemWidth(-100); + ImGui::DragFloat("float##4", &f); + + // Demonstrate using PushItemWidth to surround three items. Calling SetNextItemWidth() before each of them would have the same effect. + ImGui::Text("SetNextItemWidth/PushItemWidth(-1)"); + ImGui::SameLine(); HelpMarker("Align to right edge"); + ImGui::PushItemWidth(-1); + ImGui::DragFloat("float##5a", &f); + ImGui::DragFloat("float##5b", &f); + ImGui::DragFloat("float##5c", &f); + ImGui::PopItemWidth(); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Basic Horizontal Layout")) + { + ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)"); + + // Text + ImGui::Text("Two items: Hello"); ImGui::SameLine(); + ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + + // Adjust spacing + ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20); + ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + + // Button + ImGui::AlignTextToFramePadding(); + ImGui::Text("Normal buttons"); ImGui::SameLine(); + ImGui::Button("Banana"); ImGui::SameLine(); + ImGui::Button("Apple"); ImGui::SameLine(); + ImGui::Button("Corniflower"); + + // Button + ImGui::Text("Small buttons"); ImGui::SameLine(); + ImGui::SmallButton("Like this one"); ImGui::SameLine(); + ImGui::Text("can fit within a text block."); + + // Aligned to arbitrary position. Easy/cheap column. + ImGui::Text("Aligned"); + ImGui::SameLine(150); ImGui::Text("x=150"); + ImGui::SameLine(300); ImGui::Text("x=300"); + ImGui::Text("Aligned"); + ImGui::SameLine(150); ImGui::SmallButton("x=150"); + ImGui::SameLine(300); ImGui::SmallButton("x=300"); + + // Checkbox + static bool c1 = false, c2 = false, c3 = false, c4 = false; + ImGui::Checkbox("My", &c1); ImGui::SameLine(); + ImGui::Checkbox("Tailor", &c2); ImGui::SameLine(); + ImGui::Checkbox("Is", &c3); ImGui::SameLine(); + ImGui::Checkbox("Rich", &c4); + + // Various + static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f; + ImGui::PushItemWidth(80); + const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; + static int item = -1; + ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); + ImGui::SliderFloat("X", &f0, 0.0f, 5.0f); ImGui::SameLine(); + ImGui::SliderFloat("Y", &f1, 0.0f, 5.0f); ImGui::SameLine(); + ImGui::SliderFloat("Z", &f2, 0.0f, 5.0f); + ImGui::PopItemWidth(); + + ImGui::PushItemWidth(80); + ImGui::Text("Lists:"); + static int selection[4] = { 0, 1, 2, 3 }; + for (int i = 0; i < 4; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); + ImGui::PopID(); + //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); + } + ImGui::PopItemWidth(); + + // Dummy + ImVec2 button_sz(40, 40); + ImGui::Button("A", button_sz); ImGui::SameLine(); + ImGui::Dummy(button_sz); ImGui::SameLine(); + ImGui::Button("B", button_sz); + + // Manually wrapping (we should eventually provide this as an automatic layout feature, but for now you can do it manually) + ImGui::Text("Manually wrapping:"); + ImGuiStyle& style = ImGui::GetStyle(); + int buttons_count = 20; + float window_visible_x2 = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; + for (int n = 0; n < buttons_count; n++) + { + ImGui::PushID(n); + ImGui::Button("Box", button_sz); + float last_button_x2 = ImGui::GetItemRectMax().x; + float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line + if (n + 1 < buttons_count && next_button_x2 < window_visible_x2) + ImGui::SameLine(); + ImGui::PopID(); + } + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Tabs")) + { + if (ImGui::TreeNode("Basic")) + { + ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; + if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) + { + if (ImGui::BeginTabItem("Avocado")) + { + ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Broccoli")) + { + ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Cucumber")) + { + ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + ImGui::Separator(); ImGui::TreePop(); } - if (ImGui::TreeNode("Groups")) + if (ImGui::TreeNode("Advanced & Close Button")) { - ImGui::TextWrapped("(Using ImGui::BeginGroup()/EndGroup() to layout items. BeginGroup() basically locks the horizontal position. EndGroup() bundles the whole group so that you can use functions such as IsItemHovered() on it.)"); - ImGui::BeginGroup(); - { - ImGui::BeginGroup(); - ImGui::Button("AAA"); - ImGui::SameLine(); - ImGui::Button("BBB"); - ImGui::SameLine(); - ImGui::BeginGroup(); - ImGui::Button("CCC"); - ImGui::Button("DDD"); - ImGui::EndGroup(); - ImGui::SameLine(); - ImGui::Button("EEE"); - ImGui::EndGroup(); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("First group hovered"); - } - // Capture the group size and create widgets using the same size - ImVec2 size = ImGui::GetItemRectSize(); - const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f }; - ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size); + // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0). + static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable; + ImGui::CheckboxFlags("ImGuiTabBarFlags_Reorderable", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_Reorderable); + ImGui::CheckboxFlags("ImGuiTabBarFlags_AutoSelectNewTabs", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_AutoSelectNewTabs); + ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton); + ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton); + if ((tab_bar_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) + tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyDefault_; + if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown)) + tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown); + if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll)) + tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll); - ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f,size.y)); + // Tab Bar + const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" }; + static bool opened[4] = { true, true, true, true }; // Persistent user state + for (int n = 0; n < IM_ARRAYSIZE(opened); n++) + { + if (n > 0) { ImGui::SameLine(); } + ImGui::Checkbox(names[n], &opened[n]); + } + + // Passing a bool* to BeginTabItem() is similar to passing one to Begin(): the underlying bool will be set to false when the tab is closed. + if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) + { + for (int n = 0; n < IM_ARRAYSIZE(opened); n++) + if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n])) + { + ImGui::Text("This is the %s tab!", names[n]); + if (n & 1) + ImGui::Text("I am an odd tab."); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + ImGui::Separator(); + ImGui::TreePop(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Groups")) + { + HelpMarker("BeginGroup() basically locks the horizontal position for new line. EndGroup() bundles the whole group so that you can use \"item\" functions such as IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group."); + ImGui::BeginGroup(); + { + ImGui::BeginGroup(); + ImGui::Button("AAA"); ImGui::SameLine(); - ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f,size.y)); + ImGui::Button("BBB"); + ImGui::SameLine(); + ImGui::BeginGroup(); + ImGui::Button("CCC"); + ImGui::Button("DDD"); ImGui::EndGroup(); ImGui::SameLine(); + ImGui::Button("EEE"); + ImGui::EndGroup(); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("First group hovered"); + } + // Capture the group size and create widgets using the same size + ImVec2 size = ImGui::GetItemRectSize(); + const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f }; + ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size); - ImGui::Button("LEVERAGE\nBUZZWORD", size); - ImGui::SameLine(); + ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f, size.y)); + ImGui::SameLine(); + ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f, size.y)); + ImGui::EndGroup(); + ImGui::SameLine(); - ImGui::ListBoxHeader("List", size); + ImGui::Button("LEVERAGE\nBUZZWORD", size); + ImGui::SameLine(); + + if (ImGui::ListBoxHeader("List", size)) + { ImGui::Selectable("Selected", true); ImGui::Selectable("Not Selected", false); ImGui::ListBoxFooter(); - - ImGui::TreePop(); } - if (ImGui::TreeNode("Text Baseline Alignment")) - { - ImGui::TextWrapped("(This is testing the vertical alignment that occurs on text to keep it at the same baseline as widgets. Lines only composed of text or \"small\" widgets fit in less vertical spaces than lines with normal widgets)"); - - ImGui::Text("One\nTwo\nThree"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("Banana"); - - ImGui::Text("Banana"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("One\nTwo\nThree"); - - ImGui::Button("HOP##1"); ImGui::SameLine(); - ImGui::Text("Banana"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("Banana"); - - ImGui::Button("HOP##2"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("Banana"); - - ImGui::Button("TEST##1"); ImGui::SameLine(); - ImGui::Text("TEST"); ImGui::SameLine(); - ImGui::SmallButton("TEST##2"); - - ImGui::AlignTextToFramePadding(); // If your line starts with text, call this to align it to upcoming widgets. - ImGui::Text("Text aligned to Widget"); ImGui::SameLine(); - ImGui::Button("Widget##1"); ImGui::SameLine(); - ImGui::Text("Widget"); ImGui::SameLine(); - ImGui::SmallButton("Widget##2"); ImGui::SameLine(); - ImGui::Button("Widget##3"); - - // Tree - const float spacing = ImGui::GetStyle().ItemInnerSpacing.x; - ImGui::Button("Button##1"); - ImGui::SameLine(0.0f, spacing); - if (ImGui::TreeNode("Node##1")) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data - - ImGui::AlignTextToFramePadding(); // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget. Otherwise you can use SmallButton (smaller fit). - bool node_open = ImGui::TreeNode("Node##2"); // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add child content. - ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2"); - if (node_open) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data - - // Bullet - ImGui::Button("Button##3"); - ImGui::SameLine(0.0f, spacing); - ImGui::BulletText("Bullet text"); - - ImGui::AlignTextToFramePadding(); - ImGui::BulletText("Node"); - ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##4"); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Scrolling")) - { - ImGui::TextWrapped("(Use SetScrollHere() or SetScrollFromPosY() to scroll to a given position.)"); - static bool track = true; - static int track_line = 50, scroll_to_px = 200; - ImGui::Checkbox("Track", &track); - ImGui::PushItemWidth(100); - ImGui::SameLine(130); track |= ImGui::DragInt("##line", &track_line, 0.25f, 0, 99, "Line = %.0f"); - bool scroll_to = ImGui::Button("Scroll To Pos"); - ImGui::SameLine(130); scroll_to |= ImGui::DragInt("##pos_y", &scroll_to_px, 1.00f, 0, 9999, "Y = %.0f px"); - ImGui::PopItemWidth(); - if (scroll_to) track = false; - - for (int i = 0; i < 5; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::BeginGroup(); - ImGui::Text("%s", i == 0 ? "Top" : i == 1 ? "25%" : i == 2 ? "Center" : i == 3 ? "75%" : "Bottom"); - ImGui::BeginChild(ImGui::GetID((void*)(intptr_t)i), ImVec2(ImGui::GetWindowWidth() * 0.17f, 200.0f), true); - if (scroll_to) - ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_px, i * 0.25f); - for (int line = 0; line < 100; line++) - { - if (track && line == track_line) - { - ImGui::TextColored(ImColor(255,255,0), "Line %d", line); - ImGui::SetScrollHere(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom - } - else - { - ImGui::Text("Line %d", line); - } - } - float scroll_y = ImGui::GetScrollY(), scroll_max_y = ImGui::GetScrollMaxY(); - ImGui::EndChild(); - ImGui::Text("%.0f/%0.f", scroll_y, scroll_max_y); - ImGui::EndGroup(); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Horizontal Scrolling")) - { - ImGui::Bullet(); ImGui::TextWrapped("Horizontal scrolling for a window has to be enabled explicitly via the ImGuiWindowFlags_HorizontalScrollbar flag."); - ImGui::Bullet(); ImGui::TextWrapped("You may want to explicitly specify content width by calling SetNextWindowContentWidth() before Begin()."); - static int lines = 7; - ImGui::SliderInt("Lines", &lines, 1, 15); - ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f)); - ImGui::BeginChild("scrolling", ImVec2(0, ImGui::GetFrameHeightWithSpacing()*7 + 30), true, ImGuiWindowFlags_HorizontalScrollbar); - for (int line = 0; line < lines; line++) - { - // Display random stuff (for the sake of this trivial demo we are using basic Button+SameLine. If you want to create your own time line for a real application you may be better off - // manipulating the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets yourself. You may also want to use the lower-level ImDrawList API) - int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3); - for (int n = 0; n < num_buttons; n++) - { - if (n > 0) ImGui::SameLine(); - ImGui::PushID(n + line * 1000); - char num_buf[16]; - sprintf(num_buf, "%d", n); - const char* label = (!(n%15)) ? "FizzBuzz" : (!(n%3)) ? "Fizz" : (!(n%5)) ? "Buzz" : num_buf; - float hue = n*0.05f; - ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f)); - ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f)); - ImGui::PopStyleColor(3); - ImGui::PopID(); - } - } - float scroll_x = ImGui::GetScrollX(), scroll_max_x = ImGui::GetScrollMaxX(); - ImGui::EndChild(); - ImGui::PopStyleVar(2); - float scroll_x_delta = 0.0f; - ImGui::SmallButton("<<"); if (ImGui::IsItemActive()) scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; ImGui::SameLine(); - ImGui::Text("Scroll from code"); ImGui::SameLine(); - ImGui::SmallButton(">>"); if (ImGui::IsItemActive()) scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; ImGui::SameLine(); - ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x); - if (scroll_x_delta != 0.0f) - { - ImGui::BeginChild("scrolling"); // Demonstrate a trick: you can use Begin to set yourself in the context of another window (here we are already out of your child window) - ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta); - ImGui::End(); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Clipping")) - { - static ImVec2 size(100, 100), offset(50, 20); - ImGui::TextWrapped("On a per-widget basis we are occasionally clipping text CPU-side if it won't fit in its frame. Otherwise we are doing coarser clipping + passing a scissor rectangle to the renderer. The system is designed to try minimizing both execution and CPU/GPU rendering cost."); - ImGui::DragFloat2("size", (float*)&size, 0.5f, 0.0f, 200.0f, "%.0f"); - ImGui::TextWrapped("(Click and drag)"); - ImVec2 pos = ImGui::GetCursorScreenPos(); - ImVec4 clip_rect(pos.x, pos.y, pos.x+size.x, pos.y+size.y); - ImGui::InvisibleButton("##dummy", size); - if (ImGui::IsItemActive() && ImGui::IsMouseDragging()) { offset.x += ImGui::GetIO().MouseDelta.x; offset.y += ImGui::GetIO().MouseDelta.y; } - ImGui::GetWindowDrawList()->AddRectFilled(pos, ImVec2(pos.x+size.x,pos.y+size.y), IM_COL32(90,90,120,255)); - ImGui::GetWindowDrawList()->AddText(ImGui::GetFont(), ImGui::GetFontSize()*2.0f, ImVec2(pos.x+offset.x,pos.y+offset.y), IM_COL32(255,255,255,255), "Line 1 hello\nLine 2 clip me!", NULL, 0.0f, &clip_rect); - ImGui::TreePop(); - } + ImGui::TreePop(); } - if (ImGui::CollapsingHeader("Popups & Modal windows")) + if (ImGui::TreeNode("Text Baseline Alignment")) { - if (ImGui::TreeNode("Popups")) + HelpMarker("This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. Lines only composed of text or \"small\" widgets fit in less vertical spaces than lines with normal widgets."); + + ImGui::Text("One\nTwo\nThree"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("Banana"); + + ImGui::Text("Banana"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("One\nTwo\nThree"); + + ImGui::Button("HOP##1"); ImGui::SameLine(); + ImGui::Text("Banana"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("Banana"); + + ImGui::Button("HOP##2"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("Banana"); + + ImGui::Button("TEST##1"); ImGui::SameLine(); + ImGui::Text("TEST"); ImGui::SameLine(); + ImGui::SmallButton("TEST##2"); + + ImGui::AlignTextToFramePadding(); // If your line starts with text, call this to align it to upcoming widgets. + ImGui::Text("Text aligned to Widget"); ImGui::SameLine(); + ImGui::Button("Widget##1"); ImGui::SameLine(); + ImGui::Text("Widget"); ImGui::SameLine(); + ImGui::SmallButton("Widget##2"); ImGui::SameLine(); + ImGui::Button("Widget##3"); + + // Tree + const float spacing = ImGui::GetStyle().ItemInnerSpacing.x; + ImGui::Button("Button##1"); + ImGui::SameLine(0.0f, spacing); + if (ImGui::TreeNode("Node##1")) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data + + ImGui::AlignTextToFramePadding(); // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget. Otherwise you can use SmallButton (smaller fit). + bool node_open = ImGui::TreeNode("Node##2"); // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add child content. + ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2"); + if (node_open) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data + + // Bullet + ImGui::Button("Button##3"); + ImGui::SameLine(0.0f, spacing); + ImGui::BulletText("Bullet text"); + + ImGui::AlignTextToFramePadding(); + ImGui::BulletText("Node"); + ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##4"); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Scrolling")) + { + // Vertical scroll functions + HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position."); + + static bool track = true; + static int track_item = 50; + static float scroll_to_off_px = 0.0f; + static float scroll_to_pos_px = 200.0f; + ImGui::Checkbox("Track", &track); + ImGui::PushItemWidth(100); + ImGui::SameLine(140); track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d"); + + bool scroll_to_off = ImGui::Button("Scroll Offset"); + ImGui::SameLine(140); scroll_to_off |= ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, 9999, "+%.0f px"); + + bool scroll_to_pos = ImGui::Button("Scroll To Pos"); + ImGui::SameLine(140); scroll_to_pos |= ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, 0, 9999, "X/Y = %.0f px"); + + ImGui::PopItemWidth(); + if (scroll_to_off || scroll_to_pos) + track = false; + + ImGuiStyle& style = ImGui::GetStyle(); + float child_w = (ImGui::GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5; + if (child_w < 1.0f) + child_w = 1.0f; + ImGui::PushID("##VerticalScrolling"); + for (int i = 0; i < 5; i++) { - ImGui::TextWrapped("When a popup is active, it inhibits interacting with windows that are behind the popup. Clicking outside the popup closes it."); + if (i > 0) ImGui::SameLine(); + ImGui::BeginGroup(); + const char* names[] = { "Top", "25%", "Center", "75%", "Bottom" }; + ImGui::TextUnformatted(names[i]); - static int selected_fish = -1; - const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" }; - static bool toggles[] = { true, false, false, false, false }; - - // Simple selection popup - // (If you want to show the current selection inside the Button itself, you may want to build a string using the "###" operator to preserve a constant ID with a variable label) - if (ImGui::Button("Select..")) - ImGui::OpenPopup("select"); - ImGui::SameLine(); - ImGui::TextUnformatted(selected_fish == -1 ? "" : names[selected_fish]); - if (ImGui::BeginPopup("select")) + ImGui::BeginChild(ImGui::GetID((void*)(intptr_t)i), ImVec2(child_w, 200.0f), true, ImGuiWindowFlags_None); + if (scroll_to_off) + ImGui::SetScrollY(scroll_to_off_px); + if (scroll_to_pos) + ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_pos_px, i * 0.25f); + for (int item = 0; item < 100; item++) { - ImGui::Text("Aquarium"); - ImGui::Separator(); - for (int i = 0; i < IM_ARRAYSIZE(names); i++) - if (ImGui::Selectable(names[i])) - selected_fish = i; - ImGui::EndPopup(); + if (track && item == track_item) + { + ImGui::TextColored(ImVec4(1,1,0,1), "Item %d", item); + ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom + } + else + { + ImGui::Text("Item %d", item); + } + } + float scroll_y = ImGui::GetScrollY(); + float scroll_max_y = ImGui::GetScrollMaxY(); + ImGui::EndChild(); + ImGui::Text("%.0f/%.0f", scroll_y, scroll_max_y); + ImGui::EndGroup(); + } + ImGui::PopID(); + + // Horizontal scroll functions + ImGui::Spacing(); + HelpMarker("Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\nUsing the \"Scroll To Pos\" button above will make the discontinuity at edges visible: scrolling to the top/bottom/left/right-most item will add an additional WindowPadding to reflect on reaching the edge of the list.\n\nBecause the clipping rectangle of most window hides half worth of WindowPadding on the left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the equivalent SetScrollFromPosY(+1) wouldn't."); + ImGui::PushID("##HorizontalScrolling"); + for (int i = 0; i < 5; i++) + { + float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f; + ImGui::BeginChild(ImGui::GetID((void*)(intptr_t)i), ImVec2(-100, child_height), true, ImGuiWindowFlags_HorizontalScrollbar); + if (scroll_to_off) + ImGui::SetScrollX(scroll_to_off_px); + if (scroll_to_pos) + ImGui::SetScrollFromPosX(ImGui::GetCursorStartPos().x + scroll_to_pos_px, i * 0.25f); + for (int item = 0; item < 100; item++) + { + if (track && item == track_item) + { + ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item); + ImGui::SetScrollHereX(i * 0.25f); // 0.0f:left, 0.5f:center, 1.0f:right + } + else + { + ImGui::Text("Item %d", item); + } + ImGui::SameLine(); + } + float scroll_x = ImGui::GetScrollX(); + float scroll_max_x = ImGui::GetScrollMaxX(); + ImGui::EndChild(); + ImGui::SameLine(); + const char* names[] = { "Left", "25%", "Center", "75%", "Right" }; + ImGui::Text("%s\n%.0f/%.0f", names[i], scroll_x, scroll_max_x); + ImGui::Spacing(); + } + ImGui::PopID(); + + // Miscellaneous Horizontal Scrolling Demo + HelpMarker("Horizontal scrolling for a window has to be enabled explicitly via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\nYou may want to explicitly specify content width by calling SetNextWindowContentWidth() before Begin()."); + static int lines = 7; + ImGui::SliderInt("Lines", &lines, 1, 15); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f)); + ImGui::BeginChild("scrolling", ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30), true, ImGuiWindowFlags_HorizontalScrollbar); + for (int line = 0; line < lines; line++) + { + // Display random stuff (for the sake of this trivial demo we are using basic Button+SameLine. If you want to create your own time line for a real application you may be better off + // manipulating the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets yourself. You may also want to use the lower-level ImDrawList API) + int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3); + for (int n = 0; n < num_buttons; n++) + { + if (n > 0) ImGui::SameLine(); + ImGui::PushID(n + line * 1000); + char num_buf[16]; + sprintf(num_buf, "%d", n); + const char* label = (!(n%15)) ? "FizzBuzz" : (!(n%3)) ? "Fizz" : (!(n%5)) ? "Buzz" : num_buf; + float hue = n*0.05f; + ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f)); + ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f)); + ImGui::PopStyleColor(3); + ImGui::PopID(); + } + } + float scroll_x = ImGui::GetScrollX(); + float scroll_max_x = ImGui::GetScrollMaxX(); + ImGui::EndChild(); + ImGui::PopStyleVar(2); + float scroll_x_delta = 0.0f; + ImGui::SmallButton("<<"); if (ImGui::IsItemActive()) { scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; } ImGui::SameLine(); + ImGui::Text("Scroll from code"); ImGui::SameLine(); + ImGui::SmallButton(">>"); if (ImGui::IsItemActive()) { scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; } ImGui::SameLine(); + ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x); + if (scroll_x_delta != 0.0f) + { + ImGui::BeginChild("scrolling"); // Demonstrate a trick: you can use Begin to set yourself in the context of another window (here we are already out of your child window) + ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta); + ImGui::EndChild(); + } + ImGui::Spacing(); + + static bool show_horizontal_contents_size_demo_window = false; + ImGui::Checkbox("Show Horizontal contents size demo window", &show_horizontal_contents_size_demo_window); + + if (show_horizontal_contents_size_demo_window) + { + static bool show_h_scrollbar = true; + static bool show_button = true; + static bool show_tree_nodes = true; + static bool show_text_wrapped = false; + static bool show_columns = true; + static bool show_tab_bar = true; + static bool show_child = false; + static bool explicit_content_size = false; + static float contents_size_x = 300.0f; + if (explicit_content_size) + ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f)); + ImGui::Begin("Horizontal contents size demo window", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0)); + HelpMarker("Test of different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\nUse 'Metrics->Tools->Show windows rectangles' to visualize rectangles."); + ImGui::Checkbox("H-scrollbar", &show_h_scrollbar); + ImGui::Checkbox("Button", &show_button); // Will grow contents size (unless explicitly overwritten) + ImGui::Checkbox("Tree nodes", &show_tree_nodes); // Will grow contents size and display highlight over full width + ImGui::Checkbox("Text wrapped", &show_text_wrapped);// Will grow and use contents size + ImGui::Checkbox("Columns", &show_columns); // Will use contents size + ImGui::Checkbox("Tab bar", &show_tab_bar); // Will use contents size + ImGui::Checkbox("Child", &show_child); // Will grow and use contents size + ImGui::Checkbox("Explicit content size", &explicit_content_size); + ImGui::Text("Scroll %.1f/%.1f %.1f/%.1f", ImGui::GetScrollX(), ImGui::GetScrollMaxX(), ImGui::GetScrollY(), ImGui::GetScrollMaxY()); + if (explicit_content_size) + { + ImGui::SameLine(); + ImGui::SetNextItemWidth(100); + ImGui::DragFloat("##csx", &contents_size_x); + ImVec2 p = ImGui::GetCursorScreenPos(); + ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE); + ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(p.x + contents_size_x - 10, p.y), ImVec2(p.x + contents_size_x, p.y + 10), IM_COL32_WHITE); + ImGui::Dummy(ImVec2(0, 10)); + } + ImGui::PopStyleVar(2); + ImGui::Separator(); + if (show_button) + { + ImGui::Button("this is a 300-wide button", ImVec2(300, 0)); + } + if (show_tree_nodes) + { + bool open = true; + if (ImGui::TreeNode("this is a tree node")) + { + if (ImGui::TreeNode("another one of those tree node...")) + { + ImGui::Text("Some tree contents"); + ImGui::TreePop(); + } + ImGui::TreePop(); + } + ImGui::CollapsingHeader("CollapsingHeader", &open); + } + if (show_text_wrapped) + { + ImGui::TextWrapped("This text should automatically wrap on the edge of the work rectangle."); + } + if (show_columns) + { + ImGui::Columns(4); + for (int n = 0; n < 4; n++) + { + ImGui::Text("Width %.2f", ImGui::GetColumnWidth()); + ImGui::NextColumn(); + } + ImGui::Columns(1); + } + if (show_tab_bar && ImGui::BeginTabBar("Hello")) + { + if (ImGui::BeginTabItem("OneOneOne")) { ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("TwoTwoTwo")) { ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("ThreeThreeThree")) { ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("FourFourFour")) { ImGui::EndTabItem(); } + ImGui::EndTabBar(); + } + if (show_child) + { + ImGui::BeginChild("child", ImVec2(0,0), true); + ImGui::EndChild(); + } + ImGui::End(); + } + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Clipping")) + { + static ImVec2 size(100, 100), offset(50, 20); + ImGui::TextWrapped("On a per-widget basis we are occasionally clipping text CPU-side if it won't fit in its frame. Otherwise we are doing coarser clipping + passing a scissor rectangle to the renderer. The system is designed to try minimizing both execution and CPU/GPU rendering cost."); + ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f"); + ImGui::TextWrapped("(Click and drag)"); + ImVec2 pos = ImGui::GetCursorScreenPos(); + ImVec4 clip_rect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); + ImGui::InvisibleButton("##dummy", size); + if (ImGui::IsItemActive() && ImGui::IsMouseDragging()) { offset.x += ImGui::GetIO().MouseDelta.x; offset.y += ImGui::GetIO().MouseDelta.y; } + ImGui::GetWindowDrawList()->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(90, 90, 120, 255)); + ImGui::GetWindowDrawList()->AddText(ImGui::GetFont(), ImGui::GetFontSize()*2.0f, ImVec2(pos.x + offset.x, pos.y + offset.y), IM_COL32(255, 255, 255, 255), "Line 1 hello\nLine 2 clip me!", NULL, 0.0f, &clip_rect); + ImGui::TreePop(); + } +} + +static void ShowDemoWindowPopups() +{ + if (!ImGui::CollapsingHeader("Popups & Modal windows")) + return; + + // The properties of popups windows are: + // - They block normal mouse hovering detection outside them. (*) + // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE. + // - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as we are used to with regular Begin() calls. + // User can manipulate the visibility state by calling OpenPopup(). + // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even when normally blocked by a popup. + // Those three properties are connected. The library needs to hold their visibility state because it can close popups at any time. + + // Typical use for regular windows: + // bool my_tool_is_active = false; if (ImGui::Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", &my_tool_is_active) { [...] } End(); + // Typical use for popups: + // if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); } + + // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state. + // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below. + + if (ImGui::TreeNode("Popups")) + { + ImGui::TextWrapped("When a popup is active, it inhibits interacting with windows that are behind the popup. Clicking outside the popup closes it."); + + static int selected_fish = -1; + const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" }; + static bool toggles[] = { true, false, false, false, false }; + + // Simple selection popup + // (If you want to show the current selection inside the Button itself, you may want to build a string using the "###" operator to preserve a constant ID with a variable label) + if (ImGui::Button("Select..")) + ImGui::OpenPopup("my_select_popup"); + ImGui::SameLine(); + ImGui::TextUnformatted(selected_fish == -1 ? "" : names[selected_fish]); + if (ImGui::BeginPopup("my_select_popup")) + { + ImGui::Text("Aquarium"); + ImGui::Separator(); + for (int i = 0; i < IM_ARRAYSIZE(names); i++) + if (ImGui::Selectable(names[i])) + selected_fish = i; + ImGui::EndPopup(); + } + + // Showing a menu with toggles + if (ImGui::Button("Toggle..")) + ImGui::OpenPopup("my_toggle_popup"); + if (ImGui::BeginPopup("my_toggle_popup")) + { + for (int i = 0; i < IM_ARRAYSIZE(names); i++) + ImGui::MenuItem(names[i], "", &toggles[i]); + if (ImGui::BeginMenu("Sub-menu")) + { + ImGui::MenuItem("Click me"); + ImGui::EndMenu(); } - // Showing a menu with toggles - if (ImGui::Button("Toggle..")) - ImGui::OpenPopup("toggle"); - if (ImGui::BeginPopup("toggle")) + ImGui::Separator(); + ImGui::Text("Tooltip here"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("I am a tooltip over a popup"); + + if (ImGui::Button("Stacked Popup")) + ImGui::OpenPopup("another popup"); + if (ImGui::BeginPopup("another popup")) { for (int i = 0; i < IM_ARRAYSIZE(names); i++) ImGui::MenuItem(names[i], "", &toggles[i]); if (ImGui::BeginMenu("Sub-menu")) { ImGui::MenuItem("Click me"); + if (ImGui::Button("Stacked Popup")) + ImGui::OpenPopup("another popup"); + if (ImGui::BeginPopup("another popup")) + { + ImGui::Text("I am the last one here."); + ImGui::EndPopup(); + } ImGui::EndMenu(); } - - ImGui::Separator(); - ImGui::Text("Tooltip here"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip over a popup"); - - if (ImGui::Button("Stacked Popup")) - ImGui::OpenPopup("another popup"); - if (ImGui::BeginPopup("another popup")) - { - for (int i = 0; i < IM_ARRAYSIZE(names); i++) - ImGui::MenuItem(names[i], "", &toggles[i]); - if (ImGui::BeginMenu("Sub-menu")) - { - ImGui::MenuItem("Click me"); - ImGui::EndMenu(); - } - ImGui::EndPopup(); - } ImGui::EndPopup(); } - - if (ImGui::Button("Popup Menu..")) - ImGui::OpenPopup("FilePopup"); - if (ImGui::BeginPopup("FilePopup")) - { - ShowExampleMenuFile(); - ImGui::EndPopup(); - } - - ImGui::TreePop(); + ImGui::EndPopup(); } - if (ImGui::TreeNode("Context menus")) + // Call the more complete ShowExampleMenuFile which we use in various places of this demo + if (ImGui::Button("File Menu..")) + ImGui::OpenPopup("my_file_popup"); + if (ImGui::BeginPopup("my_file_popup")) { - // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing: - // if (IsItemHovered() && IsMouseClicked(0)) - // OpenPopup(id); - // return BeginPopup(id); - // For more advanced uses you may want to replicate and cuztomize this code. This the comments inside BeginPopupContextItem() implementation. - static float value = 0.5f; - ImGui::Text("Value = %.3f (<-- right-click here)", value); - if (ImGui::BeginPopupContextItem("item context menu")) - { - if (ImGui::Selectable("Set to zero")) value = 0.0f; - if (ImGui::Selectable("Set to PI")) value = 3.1415f; - ImGui::PushItemWidth(-1); - ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f); - ImGui::PopItemWidth(); - ImGui::EndPopup(); - } - - static char name[32] = "Label1"; - char buf[64]; sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label - ImGui::Button(buf); - if (ImGui::BeginPopupContextItem()) // When used after an item that has an ID (here the Button), we can skip providing an ID to BeginPopupContextItem(). - { - ImGui::Text("Edit name:"); - ImGui::InputText("##edit", name, IM_ARRAYSIZE(name)); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - ImGui::SameLine(); ImGui::Text("(<-- right-click here)"); - - ImGui::TreePop(); + ShowExampleMenuFile(); + ImGui::EndPopup(); } - if (ImGui::TreeNode("Modals")) - { - ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside the window."); - - if (ImGui::Button("Delete..")) - ImGui::OpenPopup("Delete?"); - if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) - { - ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); - ImGui::Separator(); - - //static int dummy_i = 0; - //ImGui::Combo("Combo", &dummy_i, "Delete\0Delete harder\0"); - - static bool dont_ask_me_next_time = false; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); - ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time); - ImGui::PopStyleVar(); - - if (ImGui::Button("OK", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } - ImGui::SetItemDefaultFocus(); - ImGui::SameLine(); - if (ImGui::Button("Cancel", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } - ImGui::EndPopup(); - } - - if (ImGui::Button("Stacked modals..")) - ImGui::OpenPopup("Stacked 1"); - if (ImGui::BeginPopupModal("Stacked 1")) - { - ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDarkening] for darkening."); - static int item = 1; - ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); - static float color[4] = { 0.4f,0.7f,0.0f,0.5f }; - ImGui::ColorEdit4("color", color); // This is to test behavior of stacked regular popups over a modal - - if (ImGui::Button("Add another modal..")) - ImGui::OpenPopup("Stacked 2"); - if (ImGui::BeginPopupModal("Stacked 2")) - { - ImGui::Text("Hello from Stacked The Second!"); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Menus inside a regular window")) - { - ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); - ImGui::Separator(); - // NB: As a quirk in this very specific example, we want to differentiate the parent of this menu from the parent of the various popup menus above. - // To do so we are encloding the items in a PushID()/PopID() block to make them two different menusets. If we don't, opening any popup above and hovering our menu here - // would open it. This is because once a menu is active, we allow to switch to a sibling menu by just hovering on it, which is the desired behavior for regular menus. - ImGui::PushID("foo"); - ImGui::MenuItem("Menu item", "CTRL+M"); - if (ImGui::BeginMenu("Menu inside a regular window")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - ImGui::PopID(); - ImGui::Separator(); - ImGui::TreePop(); - } + ImGui::TreePop(); } - if (ImGui::CollapsingHeader("Columns")) + if (ImGui::TreeNode("Context menus")) { - ImGui::PushID("Columns"); - - // Basic columns - if (ImGui::TreeNode("Basic")) + // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing: + // if (IsItemHovered() && IsMouseReleased(0)) + // OpenPopup(id); + // return BeginPopup(id); + // For more advanced uses you may want to replicate and cuztomize this code. This the comments inside BeginPopupContextItem() implementation. + static float value = 0.5f; + ImGui::Text("Value = %.3f (<-- right-click here)", value); + if (ImGui::BeginPopupContextItem("item context menu")) { - ImGui::Text("Without border:"); - ImGui::Columns(3, "mycolumns3", false); // 3-ways, no border - ImGui::Separator(); - for (int n = 0; n < 14; n++) - { - char label[32]; - sprintf(label, "Item %d", n); - if (ImGui::Selectable(label)) {} - //if (ImGui::Button(label, ImVec2(-1,0))) {} - ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::Separator(); - - ImGui::Text("With border:"); - ImGui::Columns(4, "mycolumns"); // 4-ways, with border - ImGui::Separator(); - ImGui::Text("ID"); ImGui::NextColumn(); - ImGui::Text("Name"); ImGui::NextColumn(); - ImGui::Text("Path"); ImGui::NextColumn(); - ImGui::Text("Hovered"); ImGui::NextColumn(); - ImGui::Separator(); - const char* names[3] = { "One", "Two", "Three" }; - const char* paths[3] = { "/path/one", "/path/two", "/path/three" }; - static int selected = -1; - for (int i = 0; i < 3; i++) - { - char label[32]; - sprintf(label, "%04d", i); - if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns)) - selected = i; - bool hovered = ImGui::IsItemHovered(); - ImGui::NextColumn(); - ImGui::Text(names[i]); ImGui::NextColumn(); - ImGui::Text(paths[i]); ImGui::NextColumn(); - ImGui::Text("%d", hovered); ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); + if (ImGui::Selectable("Set to zero")) value = 0.0f; + if (ImGui::Selectable("Set to PI")) value = 3.1415f; + ImGui::SetNextItemWidth(-1); + ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f); + ImGui::EndPopup(); } - // Create multiple items in a same cell before switching to next column - if (ImGui::TreeNode("Mixed items")) + // We can also use OpenPopupOnItemClick() which is the same as BeginPopupContextItem() but without the Begin call. + // So here we will make it that clicking on the text field with the right mouse button (1) will toggle the visibility of the popup above. + ImGui::Text("(You can also right-click me to open the same popup as above.)"); + ImGui::OpenPopupOnItemClick("item context menu", 1); + + // When used after an item that has an ID (here the Button), we can skip providing an ID to BeginPopupContextItem(). + // BeginPopupContextItem() will use the last item ID as the popup ID. + // In addition here, we want to include your editable label inside the button label. We use the ### operator to override the ID (read FAQ about ID for details) + static char name[32] = "Label1"; + char buf[64]; sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label + ImGui::Button(buf); + if (ImGui::BeginPopupContextItem()) { - ImGui::Columns(3, "mixed"); - ImGui::Separator(); - - ImGui::Text("Hello"); - ImGui::Button("Banana"); - ImGui::NextColumn(); - - ImGui::Text("ImGui"); - ImGui::Button("Apple"); - static float foo = 1.0f; - ImGui::InputFloat("red", &foo, 0.05f, 0, 3); - ImGui::Text("An extra line here."); - ImGui::NextColumn(); - - ImGui::Text("Sailor"); - ImGui::Button("Corniflower"); - static float bar = 1.0f; - ImGui::InputFloat("blue", &bar, 0.05f, 0, 3); - ImGui::NextColumn(); - - if (ImGui::CollapsingHeader("Category A")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); - if (ImGui::CollapsingHeader("Category B")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); - if (ImGui::CollapsingHeader("Category C")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); + ImGui::Text("Edit name:"); + ImGui::InputText("##edit", name, IM_ARRAYSIZE(name)); + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); } + ImGui::SameLine(); ImGui::Text("(<-- right-click here)"); - // Word wrapping - if (ImGui::TreeNode("Word-wrapping")) - { - ImGui::Columns(2, "word-wrapping"); - ImGui::Separator(); - ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); - ImGui::TextWrapped("Hello Left"); - ImGui::NextColumn(); - ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); - ImGui::TextWrapped("Hello Right"); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); - } + ImGui::TreePop(); + } - if (ImGui::TreeNode("Borders")) + if (ImGui::TreeNode("Modals")) + { + ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside the window."); + + if (ImGui::Button("Delete..")) + ImGui::OpenPopup("Delete?"); + + if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { - // NB: Future columns API should allow automatic horizontal borders. - static bool h_borders = true; - static bool v_borders = true; - ImGui::Checkbox("horizontal", &h_borders); + ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); + ImGui::Separator(); + + //static int dummy_i = 0; + //ImGui::Combo("Combo", &dummy_i, "Delete\0Delete harder\0"); + + static bool dont_ask_me_next_time = false; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time); + ImGui::PopStyleVar(); + + if (ImGui::Button("OK", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } + ImGui::SetItemDefaultFocus(); ImGui::SameLine(); - ImGui::Checkbox("vertical", &v_borders); - ImGui::Columns(4, NULL, v_borders); - for (int i = 0; i < 4*3; i++) - { - if (h_borders && ImGui::GetColumnIndex() == 0) - ImGui::Separator(); - ImGui::Text("%c%c%c", 'a'+i, 'a'+i, 'a'+i); - ImGui::Text("Width %.2f\nOffset %.2f", ImGui::GetColumnWidth(), ImGui::GetColumnOffset()); - ImGui::NextColumn(); - } - ImGui::Columns(1); - if (h_borders) - ImGui::Separator(); - ImGui::TreePop(); + if (ImGui::Button("Cancel", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } + ImGui::EndPopup(); } - // Scrolling columns - /* - if (ImGui::TreeNode("Vertical Scrolling")) + if (ImGui::Button("Stacked modals..")) + ImGui::OpenPopup("Stacked 1"); + if (ImGui::BeginPopupModal("Stacked 1", NULL, ImGuiWindowFlags_MenuBar)) { - ImGui::BeginChild("##header", ImVec2(0, ImGui::GetTextLineHeightWithSpacing()+ImGui::GetStyle().ItemSpacing.y)); - ImGui::Columns(3); - ImGui::Text("ID"); ImGui::NextColumn(); - ImGui::Text("Name"); ImGui::NextColumn(); - ImGui::Text("Path"); ImGui::NextColumn(); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::EndChild(); - ImGui::BeginChild("##scrollingregion", ImVec2(0, 60)); - ImGui::Columns(3); - for (int i = 0; i < 10; i++) + if (ImGui::BeginMenuBar()) { - ImGui::Text("%04d", i); ImGui::NextColumn(); - ImGui::Text("Foobar"); ImGui::NextColumn(); - ImGui::Text("/path/foobar/%04d/", i); ImGui::NextColumn(); + if (ImGui::BeginMenu("File")) + { + if (ImGui::MenuItem("Dummy menu item")) {} + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); } - ImGui::Columns(1); - ImGui::EndChild(); - ImGui::TreePop(); - } - */ + ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it."); - if (ImGui::TreeNode("Horizontal Scrolling")) - { - ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f)); - ImGui::BeginChild("##ScrollingRegion", ImVec2(0, ImGui::GetFontSize() * 20), false, ImGuiWindowFlags_HorizontalScrollbar); - ImGui::Columns(10); - int ITEMS_COUNT = 2000; - ImGuiListClipper clipper(ITEMS_COUNT); // Also demonstrate using the clipper for large list - while (clipper.Step()) + // Testing behavior of widgets stacking their own regular popups over the modal. + static int item = 1; + static float color[4] = { 0.4f,0.7f,0.0f,0.5f }; + ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); + ImGui::ColorEdit4("color", color); + + if (ImGui::Button("Add another modal..")) + ImGui::OpenPopup("Stacked 2"); + + // Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which will close the popup. + // Note that the visibility state of popups is owned by imgui, so the input value of the bool actually doesn't matter here. + bool dummy_open = true; + if (ImGui::BeginPopupModal("Stacked 2", &dummy_open)) { - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - for (int j = 0; j < 10; j++) - { - ImGui::Text("Line %d Column %d...", i, j); - ImGui::NextColumn(); - } + ImGui::Text("Hello from Stacked The Second!"); + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); } - ImGui::Columns(1); - ImGui::EndChild(); - ImGui::TreePop(); + + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); } - bool node_open = ImGui::TreeNode("Tree within single cell"); - ImGui::SameLine(); ShowHelpMarker("NB: Tree node must be poped before ending the cell. There's no storage of state per-cell."); - if (node_open) + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Menus inside a regular window")) + { + ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); + ImGui::Separator(); + // NB: As a quirk in this very specific example, we want to differentiate the parent of this menu from the parent of the various popup menus above. + // To do so we are encloding the items in a PushID()/PopID() block to make them two different menusets. If we don't, opening any popup above and hovering our menu here + // would open it. This is because once a menu is active, we allow to switch to a sibling menu by just hovering on it, which is the desired behavior for regular menus. + ImGui::PushID("foo"); + ImGui::MenuItem("Menu item", "CTRL+M"); + if (ImGui::BeginMenu("Menu inside a regular window")) { - ImGui::Columns(2, "tree items"); - ImGui::Separator(); - if (ImGui::TreeNode("Hello")) { ImGui::BulletText("Sailor"); ImGui::TreePop(); } ImGui::NextColumn(); - if (ImGui::TreeNode("Bonjour")) { ImGui::BulletText("Marin"); ImGui::TreePop(); } ImGui::NextColumn(); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); + ShowExampleMenuFile(); + ImGui::EndMenu(); } ImGui::PopID(); + ImGui::Separator(); + ImGui::TreePop(); + } +} + +static void ShowDemoWindowColumns() +{ + if (!ImGui::CollapsingHeader("Columns")) + return; + + ImGui::PushID("Columns"); + + static bool disable_indent = false; + ImGui::Checkbox("Disable tree indentation", &disable_indent); + ImGui::SameLine(); + HelpMarker("Disable the indenting of tree nodes so demo columns can use the full window width."); + if (disable_indent) + ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0.0f); + + // Basic columns + if (ImGui::TreeNode("Basic")) + { + ImGui::Text("Without border:"); + ImGui::Columns(3, "mycolumns3", false); // 3-ways, no border + ImGui::Separator(); + for (int n = 0; n < 14; n++) + { + char label[32]; + sprintf(label, "Item %d", n); + if (ImGui::Selectable(label)) {} + //if (ImGui::Button(label, ImVec2(-FLT_MIN,0.0f))) {} + ImGui::NextColumn(); + } + ImGui::Columns(1); + ImGui::Separator(); + + ImGui::Text("With border:"); + ImGui::Columns(4, "mycolumns"); // 4-ways, with border + ImGui::Separator(); + ImGui::Text("ID"); ImGui::NextColumn(); + ImGui::Text("Name"); ImGui::NextColumn(); + ImGui::Text("Path"); ImGui::NextColumn(); + ImGui::Text("Hovered"); ImGui::NextColumn(); + ImGui::Separator(); + const char* names[3] = { "One", "Two", "Three" }; + const char* paths[3] = { "/path/one", "/path/two", "/path/three" }; + static int selected = -1; + for (int i = 0; i < 3; i++) + { + char label[32]; + sprintf(label, "%04d", i); + if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns)) + selected = i; + bool hovered = ImGui::IsItemHovered(); + ImGui::NextColumn(); + ImGui::Text(names[i]); ImGui::NextColumn(); + ImGui::Text(paths[i]); ImGui::NextColumn(); + ImGui::Text("%d", hovered); ImGui::NextColumn(); + } + ImGui::Columns(1); + ImGui::Separator(); + ImGui::TreePop(); } + if (ImGui::TreeNode("Borders")) + { + // NB: Future columns API should allow automatic horizontal borders. + static bool h_borders = true; + static bool v_borders = true; + ImGui::Checkbox("horizontal", &h_borders); + ImGui::SameLine(); + ImGui::Checkbox("vertical", &v_borders); + ImGui::Columns(4, NULL, v_borders); + for (int i = 0; i < 4 * 3; i++) + { + if (h_borders && ImGui::GetColumnIndex() == 0) + ImGui::Separator(); + ImGui::Text("%c%c%c", 'a' + i, 'a' + i, 'a' + i); + ImGui::Text("Width %.2f", ImGui::GetColumnWidth()); + ImGui::Text("Offset %.2f", ImGui::GetColumnOffset()); + ImGui::Text("Long text that is likely to clip"); + ImGui::Button("Button", ImVec2(-FLT_MIN, 0.0f)); + ImGui::NextColumn(); + } + ImGui::Columns(1); + if (h_borders) + ImGui::Separator(); + ImGui::TreePop(); + } + + // Create multiple items in a same cell before switching to next column + if (ImGui::TreeNode("Mixed items")) + { + ImGui::Columns(3, "mixed"); + ImGui::Separator(); + + ImGui::Text("Hello"); + ImGui::Button("Banana"); + ImGui::NextColumn(); + + ImGui::Text("ImGui"); + ImGui::Button("Apple"); + static float foo = 1.0f; + ImGui::InputFloat("red", &foo, 0.05f, 0, "%.3f"); + ImGui::Text("An extra line here."); + ImGui::NextColumn(); + + ImGui::Text("Sailor"); + ImGui::Button("Corniflower"); + static float bar = 1.0f; + ImGui::InputFloat("blue", &bar, 0.05f, 0, "%.3f"); + ImGui::NextColumn(); + + if (ImGui::CollapsingHeader("Category A")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); + if (ImGui::CollapsingHeader("Category B")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); + if (ImGui::CollapsingHeader("Category C")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); + ImGui::Columns(1); + ImGui::Separator(); + ImGui::TreePop(); + } + + // Word wrapping + if (ImGui::TreeNode("Word-wrapping")) + { + ImGui::Columns(2, "word-wrapping"); + ImGui::Separator(); + ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); + ImGui::TextWrapped("Hello Left"); + ImGui::NextColumn(); + ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); + ImGui::TextWrapped("Hello Right"); + ImGui::Columns(1); + ImGui::Separator(); + ImGui::TreePop(); + } + + // Scrolling columns + /* + if (ImGui::TreeNode("Vertical Scrolling")) + { + ImGui::BeginChild("##header", ImVec2(0, ImGui::GetTextLineHeightWithSpacing()+ImGui::GetStyle().ItemSpacing.y)); + ImGui::Columns(3); + ImGui::Text("ID"); ImGui::NextColumn(); + ImGui::Text("Name"); ImGui::NextColumn(); + ImGui::Text("Path"); ImGui::NextColumn(); + ImGui::Columns(1); + ImGui::Separator(); + ImGui::EndChild(); + ImGui::BeginChild("##scrollingregion", ImVec2(0, 60)); + ImGui::Columns(3); + for (int i = 0; i < 10; i++) + { + ImGui::Text("%04d", i); ImGui::NextColumn(); + ImGui::Text("Foobar"); ImGui::NextColumn(); + ImGui::Text("/path/foobar/%04d/", i); ImGui::NextColumn(); + } + ImGui::Columns(1); + ImGui::EndChild(); + ImGui::TreePop(); + } + */ + + if (ImGui::TreeNode("Horizontal Scrolling")) + { + ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f)); + ImGui::BeginChild("##ScrollingRegion", ImVec2(0, ImGui::GetFontSize() * 20), false, ImGuiWindowFlags_HorizontalScrollbar); + ImGui::Columns(10); + int ITEMS_COUNT = 2000; + ImGuiListClipper clipper(ITEMS_COUNT); // Also demonstrate using the clipper for large list + while (clipper.Step()) + { + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + for (int j = 0; j < 10; j++) + { + ImGui::Text("Line %d Column %d...", i, j); + ImGui::NextColumn(); + } + } + ImGui::Columns(1); + ImGui::EndChild(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Tree")) + { + ImGui::Columns(2, "tree", true); + for (int x = 0; x < 3; x++) + { + bool open1 = ImGui::TreeNode((void*)(intptr_t)x, "Node%d", x); + ImGui::NextColumn(); + ImGui::Text("Node contents"); + ImGui::NextColumn(); + if (open1) + { + for (int y = 0; y < 3; y++) + { + bool open2 = ImGui::TreeNode((void*)(intptr_t)y, "Node%d.%d", x, y); + ImGui::NextColumn(); + ImGui::Text("Node contents"); + if (open2) + { + ImGui::Text("Even more contents"); + if (ImGui::TreeNode("Tree in column")) + { + ImGui::Text("The quick brown fox jumps over the lazy dog"); + ImGui::TreePop(); + } + } + ImGui::NextColumn(); + if (open2) + ImGui::TreePop(); + } + ImGui::TreePop(); + } + } + ImGui::Columns(1); + ImGui::TreePop(); + } + + if (disable_indent) + ImGui::PopStyleVar(); + ImGui::PopID(); +} + +static void ShowDemoWindowMisc() +{ if (ImGui::CollapsingHeader("Filtering")) { static ImGuiTextFilter filter; @@ -1807,32 +2775,27 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse); ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard); ImGui::Text("WantTextInput: %d", io.WantTextInput); - ImGui::Text("WantMoveMouse: %d", io.WantMoveMouse); + ImGui::Text("WantSetMousePos: %d", io.WantSetMousePos); ImGui::Text("NavActive: %d, NavVisible: %d", io.NavActive, io.NavVisible); - ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); - ImGui::SameLine(); ShowHelpMarker("Request ImGui to render a mouse cursor for you in software. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); - ImGui::CheckboxFlags("io.ConfigFlags: NavMoveMouse", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavMoveMouse); - ImGui::SameLine(); ShowHelpMarker("Request ImGui to move your move cursor when using gamepad/keyboard navigation. NewFrame() will change io.MousePos and set the io.WantMoveMouse flag, your backend will need to apply the new mouse position."); - if (ImGui::TreeNode("Keyboard, Mouse & Navigation State")) { if (ImGui::IsMousePosValid()) ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); else ImGui::Text("Mouse pos: "); + ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse dbl-clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); - ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (io.KeysDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("%d (%.02f secs)", i, io.KeysDownDuration[i]); } - ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } - ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } + ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (io.KeysDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("%d (0x%X) (%.02f secs)", i, i, io.KeysDownDuration[i]); } + ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X)", i, i); } + ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X)", i, i); } ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); + ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputs[i]); } ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); } @@ -1858,7 +2821,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); ImGui::PushAllowKeyboardFocus(false); ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); - //ImGui::SameLine(); ShowHelperMarker("Use ImGui::PushAllowKeyboardFocus(bool)\nto disable tabbing through certain widgets."); + //ImGui::SameLine(); HelpMarker("Use ImGui::PushAllowKeyboardFocus(bool)\nto disable tabbing through certain widgets."); ImGui::PopAllowKeyboardFocus(); ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); ImGui::TreePop(); @@ -1894,9 +2857,9 @@ void ImGui::ShowDemoWindow(bool* p_open) // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item static float f3[3] = { 0.0f, 0.0f, 0.0f }; int focus_ahead = -1; - if (ImGui::Button("Focus on X")) focus_ahead = 0; ImGui::SameLine(); - if (ImGui::Button("Focus on Y")) focus_ahead = 1; ImGui::SameLine(); - if (ImGui::Button("Focus on Z")) focus_ahead = 2; + if (ImGui::Button("Focus on X")) { focus_ahead = 0; } ImGui::SameLine(); + if (ImGui::Button("Focus on Y")) { focus_ahead = 1; } ImGui::SameLine(); + if (ImGui::Button("Focus on Z")) { focus_ahead = 2; } if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead); ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f); @@ -1904,100 +2867,34 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::TreePop(); } - if (ImGui::TreeNode("Focused & Hovered Test")) - { - static bool embed_all_inside_a_child_window = false; - ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window); - if (embed_all_inside_a_child_window) - ImGui::BeginChild("embeddingchild", ImVec2(0, ImGui::GetFontSize() * 25), true); - - // Testing IsWindowFocused() function with its various flags (note that the flags can be combined) - ImGui::BulletText( - "IsWindowFocused() = %d\n" - "IsWindowFocused(_ChildWindows) = %d\n" - "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" - "IsWindowFocused(_RootWindow) = %d\n" - "IsWindowFocused(_AnyWindow) = %d\n", - ImGui::IsWindowFocused(), - ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), - ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), - ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), - ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); - - // Testing IsWindowHovered() function with its various flags (note that the flags can be combined) - ImGui::BulletText( - "IsWindowHovered() = %d\n" - "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" - "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" - "IsWindowHovered(_ChildWindows) = %d\n" - "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" - "IsWindowHovered(_RootWindow) = %d\n" - "IsWindowHovered(_AnyWindow) = %d\n", - ImGui::IsWindowHovered(), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), - ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); - - // Testing IsItemHovered() function (because BulletText is an item itself and that would affect the output of IsItemHovered, we pass all lines in a single items to shorten the code) - ImGui::Button("ITEM"); - ImGui::BulletText( - "IsItemHovered() = %d\n" - "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" - "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" - "IsItemHovered(_AllowWhenOverlapped) = %d\n" - "IsItemhovered(_RectOnly) = %d\n", - ImGui::IsItemHovered(), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped), - ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly)); - - ImGui::BeginChild("child", ImVec2(0,50), true); - ImGui::Text("This is another child window for testing IsWindowHovered() flags."); - ImGui::EndChild(); - - if (embed_all_inside_a_child_window) - EndChild(); - - ImGui::TreePop(); - } - if (ImGui::TreeNode("Dragging")) { ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget."); for (int button = 0; button < 3; button++) - ImGui::Text("IsMouseDragging(%d):\n w/ default threshold: %d,\n w/ zero threshold: %d\n w/ large threshold: %d", + ImGui::Text("IsMouseDragging(%d):\n w/ default threshold: %d,\n w/ zero threshold: %d\n w/ large threshold: %d", button, ImGui::IsMouseDragging(button), ImGui::IsMouseDragging(button, 0.0f), ImGui::IsMouseDragging(button, 20.0f)); + ImGui::Button("Drag Me"); if (ImGui::IsItemActive()) - { - // Draw a line between the button and the mouse cursor - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - draw_list->PushClipRectFullScreen(); - draw_list->AddLine(io.MouseClickedPos[0], io.MousePos, ImGui::GetColorU32(ImGuiCol_Button), 4.0f); - draw_list->PopClipRect(); + ImGui::GetForegroundDrawList()->AddLine(io.MouseClickedPos[0], io.MousePos, ImGui::GetColorU32(ImGuiCol_Button), 4.0f); // Draw a line between the button and the mouse cursor - // Drag operations gets "unlocked" when the mouse has moved past a certain threshold (the default threshold is stored in io.MouseDragThreshold) - // You can request a lower or higher threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta() - ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f); - ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0); - ImVec2 mouse_delta = io.MouseDelta; - ImGui::SameLine(); ImGui::Text("Raw (%.1f, %.1f), WithLockThresold (%.1f, %.1f), MouseDelta (%.1f, %.1f)", value_raw.x, value_raw.y, value_with_lock_threshold.x, value_with_lock_threshold.y, mouse_delta.x, mouse_delta.y); - } + // Drag operations gets "unlocked" when the mouse has moved past a certain threshold (the default threshold is stored in io.MouseDragThreshold) + // You can request a lower or higher threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta() + ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f); + ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0); + ImVec2 mouse_delta = io.MouseDelta; + ImGui::Text("GetMouseDragDelta(0):\n w/ default threshold: (%.1f, %.1f),\n w/ zero threshold: (%.1f, %.1f)\nMouseDelta: (%.1f, %.1f)", value_with_lock_threshold.x, value_with_lock_threshold.y, value_raw.x, value_raw.y, mouse_delta.x, mouse_delta.y); ImGui::TreePop(); } if (ImGui::TreeNode("Mouse cursors")) { - const char* mouse_cursors_names[] = { "Arrow", "TextInput", "Move", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE" }; + const char* mouse_cursors_names[] = { "Arrow", "TextInput", "Move", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand" }; IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT); ImGui::Text("Current mouse cursor = %d: %s", ImGui::GetMouseCursor(), mouse_cursors_names[ImGui::GetMouseCursor()]); ImGui::Text("Hover to see mouse cursors:"); - ImGui::SameLine(); ShowHelpMarker("Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, otherwise your backend needs to handle it."); + ImGui::SameLine(); HelpMarker("Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, otherwise your backend needs to handle it."); for (int i = 0; i < ImGuiMouseCursor_COUNT; i++) { char label[32]; @@ -2009,10 +2906,136 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::TreePop(); } } +} +//----------------------------------------------------------------------------- +// [SECTION] About Window / ShowAboutWindow() +// Access from Dear ImGui Demo -> Help -> About +//----------------------------------------------------------------------------- + +void ImGui::ShowAboutWindow(bool* p_open) +{ + if (!ImGui::Begin("About Dear ImGui", p_open, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::End(); + return; + } + ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); + ImGui::Separator(); + ImGui::Text("By Omar Cornut and all dear imgui contributors."); + ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); + + static bool show_config_info = false; + ImGui::Checkbox("Config/Build Information", &show_config_info); + if (show_config_info) + { + ImGuiIO& io = ImGui::GetIO(); + ImGuiStyle& style = ImGui::GetStyle(); + + bool copy_to_clipboard = ImGui::Button("Copy to clipboard"); + ImGui::BeginChildFrame(ImGui::GetID("cfginfos"), ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18), ImGuiWindowFlags_NoMove); + if (copy_to_clipboard) + ImGui::LogToClipboard(); + + ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); + ImGui::Separator(); + ImGui::Text("sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert)); + ImGui::Text("define: __cplusplus=%d", (int)__cplusplus); +#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_WIN32_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_WIN32_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_MATH_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_MATH_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS + ImGui::Text("define: IMGUI_DISABLE_DEFAULT_ALLOCATORS"); +#endif +#ifdef IMGUI_USE_BGRA_PACKED_COLOR + ImGui::Text("define: IMGUI_USE_BGRA_PACKED_COLOR"); +#endif +#ifdef _WIN32 + ImGui::Text("define: _WIN32"); +#endif +#ifdef _WIN64 + ImGui::Text("define: _WIN64"); +#endif +#ifdef __linux__ + ImGui::Text("define: __linux__"); +#endif +#ifdef __APPLE__ + ImGui::Text("define: __APPLE__"); +#endif +#ifdef _MSC_VER + ImGui::Text("define: _MSC_VER=%d", _MSC_VER); +#endif +#ifdef __MINGW32__ + ImGui::Text("define: __MINGW32__"); +#endif +#ifdef __MINGW64__ + ImGui::Text("define: __MINGW64__"); +#endif +#ifdef __GNUC__ + ImGui::Text("define: __GNUC__=%d", (int)__GNUC__); +#endif +#ifdef __clang_version__ + ImGui::Text("define: __clang_version__=%s", __clang_version__); +#endif + ImGui::Separator(); + ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL"); + ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL"); + ImGui::Text("io.ConfigFlags: 0x%08X", io.ConfigFlags); + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) ImGui::Text(" NavEnableKeyboard"); + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) ImGui::Text(" NavEnableGamepad"); + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) ImGui::Text(" NavEnableSetMousePos"); + if (io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard) ImGui::Text(" NavNoCaptureKeyboard"); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse"); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange"); + if (io.MouseDrawCursor) ImGui::Text("io.MouseDrawCursor"); + if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); + if (io.ConfigInputTextCursorBlink) ImGui::Text("io.ConfigInputTextCursorBlink"); + if (io.ConfigWindowsResizeFromEdges) ImGui::Text("io.ConfigWindowsResizeFromEdges"); + if (io.ConfigWindowsMoveFromTitleBarOnly) ImGui::Text("io.ConfigWindowsMoveFromTitleBarOnly"); + ImGui::Text("io.BackendFlags: 0x%08X", io.BackendFlags); + if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(" HasGamepad"); + if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(" HasMouseCursors"); + if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos"); + if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(" RendererHasVtxOffset"); + ImGui::Separator(); + ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight); + ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y); + ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); + ImGui::Separator(); + ImGui::Text("style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y); + ImGui::Text("style.WindowBorderSize: %.2f", style.WindowBorderSize); + ImGui::Text("style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y); + ImGui::Text("style.FrameRounding: %.2f", style.FrameRounding); + ImGui::Text("style.FrameBorderSize: %.2f", style.FrameBorderSize); + ImGui::Text("style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y); + ImGui::Text("style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y); + + if (copy_to_clipboard) + ImGui::LogFinish(); + ImGui::EndChildFrame(); + } ImGui::End(); } +//----------------------------------------------------------------------------- +// [SECTION] Style Editor / ShowStyleEditor() +//----------------------------------------------------------------------------- + // Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. // Here we use the simplified Combo() api that packs items into a single literal string. Useful for quick combo boxes where the choices are known locally. bool ImGui::ShowStyleSelector(const char* label) @@ -2040,12 +3063,17 @@ void ImGui::ShowFontSelector(const char* label) if (ImGui::BeginCombo(label, font_current->GetDebugName())) { for (int n = 0; n < io.Fonts->Fonts.Size; n++) - if (ImGui::Selectable(io.Fonts->Fonts[n]->GetDebugName(), io.Fonts->Fonts[n] == font_current)) - io.FontDefault = io.Fonts->Fonts[n]; + { + ImFont* font = io.Fonts->Fonts[n]; + ImGui::PushID((void*)font); + if (ImGui::Selectable(font->GetDebugName(), font == font_current)) + io.FontDefault = font; + ImGui::PopID(); + } ImGui::EndCombo(); } - ImGui::SameLine(); - ShowHelpMarker( + ImGui::SameLine(); + HelpMarker( "- Load additional fonts with io.Fonts->AddFontFromFileTTF().\n" "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n" "- Read FAQ and documentation in misc/fonts/ for more details.\n" @@ -2073,7 +3101,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::ShowFontSelector("Fonts##Selector"); // Simplified Settings - if (ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f")) + if (ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f")) style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding { bool window_border = (style.WindowBorderSize > 0.0f); if (ImGui::Checkbox("WindowBorder", &window_border)) style.WindowBorderSize = window_border ? 1.0f : 0.0f; } ImGui::SameLine(); @@ -2088,199 +3116,224 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) if (ImGui::Button("Revert Ref")) style = *ref; ImGui::SameLine(); - ShowHelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. Use \"Export Colors\" below to save them somewhere."); + HelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. Use \"Export Colors\" below to save them somewhere."); - if (ImGui::TreeNode("Rendering")) - { - ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); ShowHelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); - ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); - ImGui::PushItemWidth(100); - ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, FLT_MAX, NULL, 2.0f); - if (style.CurveTessellationTol < 0.0f) style.CurveTessellationTol = 0.10f; - ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero. - ImGui::PopItemWidth(); - ImGui::TreePop(); - } + ImGui::Separator(); - if (ImGui::TreeNode("Settings")) + if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None)) { - ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 16.0f, "%.0f"); - ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); - ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); - ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); - ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); - ImGui::Text("BorderSize"); - ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::Text("Rounding"); - ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 14.0f, "%.0f"); - ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 16.0f, "%.0f"); - ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); - ImGui::Text("Alignment"); - ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); - ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); ShowHelpMarker("Alignment applies when a button is larger than its text content."); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Colors")) - { - static int output_dest = 0; - static bool output_only_modified = true; - if (ImGui::Button("Export Unsaved")) + if (ImGui::BeginTabItem("Sizes")) { - if (output_dest == 0) - ImGui::LogToClipboard(); - else - ImGui::LogToTTY(); - ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;" IM_NEWLINE); + ImGui::Text("Main"); + ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); + ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); + ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); + ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); + ImGui::Text("Borders"); + ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::Text("Rounding"); + ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); + ImGui::Text("Alignment"); + ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); + ImGui::Combo("WindowMenuButtonPosition", (int*)&style.WindowMenuButtonPosition, "Left\0Right\0"); + ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Alignment applies when a button is larger than its text content."); + ImGui::SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content."); + ImGui::Text("Safe Area Padding"); ImGui::SameLine(); HelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); + ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Colors")) + { + static int output_dest = 0; + static bool output_only_modified = true; + if (ImGui::Button("Export Unsaved")) + { + if (output_dest == 0) + ImGui::LogToClipboard(); + else + ImGui::LogToTTY(); + ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;" IM_NEWLINE); + for (int i = 0; i < ImGuiCol_COUNT; i++) + { + const ImVec4& col = style.Colors[i]; + const char* name = ImGui::GetStyleColorName(i); + if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) + ImGui::LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE, name, 23 - (int)strlen(name), "", col.x, col.y, col.z, col.w); + } + ImGui::LogFinish(); + } + ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); + ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified); + + static ImGuiTextFilter filter; + filter.Draw("Filter colors", ImGui::GetFontSize() * 16); + + static ImGuiColorEditFlags alpha_flags = 0; + ImGui::RadioButton("Opaque", &alpha_flags, 0); ImGui::SameLine(); + ImGui::RadioButton("Alpha", &alpha_flags, ImGuiColorEditFlags_AlphaPreview); ImGui::SameLine(); + ImGui::RadioButton("Both", &alpha_flags, ImGuiColorEditFlags_AlphaPreviewHalf); ImGui::SameLine(); + HelpMarker("In the color list:\nLeft-click on colored square to open color picker,\nRight-click to open edit options menu."); + + ImGui::BeginChild("##colors", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened); + ImGui::PushItemWidth(-160); for (int i = 0; i < ImGuiCol_COUNT; i++) { - const ImVec4& col = style.Colors[i]; const char* name = ImGui::GetStyleColorName(i); - if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) - ImGui::LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE, name, 23-(int)strlen(name), "", col.x, col.y, col.z, col.w); - } - ImGui::LogFinish(); - } - ImGui::SameLine(); ImGui::PushItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); ImGui::PopItemWidth(); - ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified); - - ImGui::Text("Tip: Left-click on colored square to open color picker,\nRight-click to open edit options menu."); - - static ImGuiTextFilter filter; - filter.Draw("Filter colors", 200); - - static ImGuiColorEditFlags alpha_flags = 0; - ImGui::RadioButton("Opaque", &alpha_flags, 0); ImGui::SameLine(); - ImGui::RadioButton("Alpha", &alpha_flags, ImGuiColorEditFlags_AlphaPreview); ImGui::SameLine(); - ImGui::RadioButton("Both", &alpha_flags, ImGuiColorEditFlags_AlphaPreviewHalf); - - ImGui::BeginChild("#colors", ImVec2(0, 300), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened); - ImGui::PushItemWidth(-160); - for (int i = 0; i < ImGuiCol_COUNT; i++) - { - const char* name = ImGui::GetStyleColorName(i); - if (!filter.PassFilter(name)) - continue; - ImGui::PushID(i); - ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags); - if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) - { - // Tips: in a real user application, you may want to merge and use an icon font into the main font, so instead of "Save"/"Revert" you'd use icons. - // Read the FAQ and misc/fonts/README.txt about using icon fonts. It's really easy and super convenient! - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Save")) ref->Colors[i] = style.Colors[i]; - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Revert")) style.Colors[i] = ref->Colors[i]; - } - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); - ImGui::TextUnformatted(name); - ImGui::PopID(); - } - ImGui::PopItemWidth(); - ImGui::EndChild(); - - ImGui::TreePop(); - } - - bool fonts_opened = ImGui::TreeNode("Fonts", "Fonts (%d)", ImGui::GetIO().Fonts->Fonts.Size); - if (fonts_opened) - { - ImFontAtlas* atlas = ImGui::GetIO().Fonts; - if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) - { - ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); - ImGui::TreePop(); - } - ImGui::PushItemWidth(100); - for (int i = 0; i < atlas->Fonts.Size; i++) - { - ImFont* font = atlas->Fonts[i]; - ImGui::PushID(font); - bool font_details_opened = ImGui::TreeNode(font, "Font %d: \'%s\', %.2f px, %d glyphs", i, font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size); - ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) ImGui::GetIO().FontDefault = font; - if (font_details_opened) - { - ImGui::PushFont(font); - ImGui::Text("The quick brown fox jumps over the lazy dog"); - ImGui::PopFont(); - ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font - ImGui::InputFloat("Font offset", &font->DisplayOffset.y, 1, 1, 0); - ImGui::SameLine(); ShowHelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)"); - ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); - ImGui::Text("Fallback character: '%c' (%d)", font->FallbackChar, font->FallbackChar); - ImGui::Text("Texture surface: %d pixels (approx) ~ %dx%d", font->MetricsTotalSurface, (int)sqrtf((float)font->MetricsTotalSurface), (int)sqrtf((float)font->MetricsTotalSurface)); - for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) - if (ImFontConfig* cfg = &font->ConfigData[config_i]) - ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d", config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH); - if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) + if (!filter.PassFilter(name)) + continue; + ImGui::PushID(i); + ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags); + if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) { - // Display all glyphs of the fonts in separate pages of 256 characters - const ImFontGlyph* glyph_fallback = font->FallbackGlyph; // Forcefully/dodgily make FindGlyph() return NULL on fallback, which isn't the default behavior. - font->FallbackGlyph = NULL; - for (int base = 0; base < 0x10000; base += 256) + // Tips: in a real user application, you may want to merge and use an icon font into the main font, so instead of "Save"/"Revert" you'd use icons. + // Read the FAQ and misc/fonts/README.txt about using icon fonts. It's really easy and super convenient! + ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Save")) ref->Colors[i] = style.Colors[i]; + ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Revert")) style.Colors[i] = ref->Colors[i]; + } + ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); + ImGui::TextUnformatted(name); + ImGui::PopID(); + } + ImGui::PopItemWidth(); + ImGui::EndChild(); + + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Fonts")) + { + ImGuiIO& io = ImGui::GetIO(); + ImFontAtlas* atlas = io.Fonts; + HelpMarker("Read FAQ and misc/fonts/README.txt for details on font loading."); + ImGui::PushItemWidth(120); + for (int i = 0; i < atlas->Fonts.Size; i++) + { + ImFont* font = atlas->Fonts[i]; + ImGui::PushID(font); + bool font_details_opened = ImGui::TreeNode(font, "Font %d: \"%s\"\n%.2f px, %d glyphs, %d file(s)", i, font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount); + ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) { io.FontDefault = font; } + if (font_details_opened) + { + ImGui::PushFont(font); + ImGui::Text("The quick brown fox jumps over the lazy dog"); + ImGui::PopFont(); + ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font + ImGui::SameLine(); HelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)"); + ImGui::InputFloat("Font offset", &font->DisplayOffset.y, 1, 1, "%.0f"); + ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); + ImGui::Text("Fallback character: '%c' (%d)", font->FallbackChar, font->FallbackChar); + const float surface_sqrt = sqrtf((float)font->MetricsTotalSurface); + ImGui::Text("Texture surface: %d pixels (approx) ~ %dx%d", font->MetricsTotalSurface, (int)surface_sqrt, (int)surface_sqrt); + for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) + if (const ImFontConfig* cfg = &font->ConfigData[config_i]) + ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d", config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH); + if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) { - int count = 0; - for (int n = 0; n < 256; n++) - count += font->FindGlyph((ImWchar)(base + n)) ? 1 : 0; - if (count > 0 && ImGui::TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base+255, count, count > 1 ? "glyphs" : "glyph")) + // Display all glyphs of the fonts in separate pages of 256 characters + for (int base = 0; base < 0x10000; base += 256) { - float cell_spacing = style.ItemSpacing.y; - ImVec2 cell_size(font->FontSize * 1, font->FontSize * 1); - ImVec2 base_pos = ImGui::GetCursorScreenPos(); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); + int count = 0; for (int n = 0; n < 256; n++) + count += font->FindGlyphNoFallback((ImWchar)(base + n)) ? 1 : 0; + if (count > 0 && ImGui::TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) { - ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size.x + cell_spacing), base_pos.y + (n / 16) * (cell_size.y + cell_spacing)); - ImVec2 cell_p2(cell_p1.x + cell_size.x, cell_p1.y + cell_size.y); - const ImFontGlyph* glyph = font->FindGlyph((ImWchar)(base+n)); - draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255,255,255,100) : IM_COL32(255,255,255,50)); - font->RenderChar(draw_list, cell_size.x, cell_p1, ImGui::GetColorU32(ImGuiCol_Text), (ImWchar)(base+n)); // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions available to generate a string. - if (glyph && ImGui::IsMouseHoveringRect(cell_p1, cell_p2)) + float cell_size = font->FontSize * 1; + float cell_spacing = style.ItemSpacing.y; + ImVec2 base_pos = ImGui::GetCursorScreenPos(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + for (int n = 0; n < 256; n++) { - ImGui::BeginTooltip(); - ImGui::Text("Codepoint: U+%04X", base+n); - ImGui::Separator(); - ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX); - ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); - ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); - ImGui::EndTooltip(); + ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); + ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); + const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); + draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); + if (glyph) + font->RenderChar(draw_list, cell_size, cell_p1, ImGui::GetColorU32(ImGuiCol_Text), (ImWchar)(base + n)); // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions available to generate a string. + if (glyph && ImGui::IsMouseHoveringRect(cell_p1, cell_p2)) + { + ImGui::BeginTooltip(); + ImGui::Text("Codepoint: U+%04X", base + n); + ImGui::Separator(); + ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX); + ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); + ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); + ImGui::EndTooltip(); + } } + ImGui::Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); + ImGui::TreePop(); } - ImGui::Dummy(ImVec2((cell_size.x + cell_spacing) * 16, (cell_size.y + cell_spacing) * 16)); - ImGui::TreePop(); } + ImGui::TreePop(); } - font->FallbackGlyph = glyph_fallback; ImGui::TreePop(); } + ImGui::PopID(); + } + if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) + { + ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); + ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col); ImGui::TreePop(); } - ImGui::PopID(); + + static float window_scale = 1.0f; + if (ImGui::DragFloat("this window scale", &window_scale, 0.005f, 0.3f, 2.0f, "%.2f")) // scale only this window + ImGui::SetWindowFontScale(window_scale); + ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, 0.3f, 2.0f, "%.2f"); // scale everything + ImGui::PopItemWidth(); + + ImGui::EndTabItem(); } - static float window_scale = 1.0f; - ImGui::DragFloat("this window scale", &window_scale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale only this window - ImGui::DragFloat("global scale", &ImGui::GetIO().FontGlobalScale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale everything - ImGui::PopItemWidth(); - ImGui::SetWindowFontScale(window_scale); - ImGui::TreePop(); + + if (ImGui::BeginTabItem("Rendering")) + { + ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); + ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); + ImGui::PushItemWidth(100); + ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, FLT_MAX, "%.2f", 2.0f); + if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f; + ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero. + ImGui::PopItemWidth(); + + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); } ImGui::PopItemWidth(); } -// Demonstrate creating a fullscreen menu bar and populating it. +//----------------------------------------------------------------------------- +// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() +//----------------------------------------------------------------------------- + +// Demonstrate creating a "main" fullscreen menu bar and populating it. +// Note the difference between BeginMainMenuBar() and BeginMenuBar(): +// - BeginMenuBar() = menu-bar inside current window we Begin()-ed into (the window needs the ImGuiWindowFlags_MenuBar flag) +// - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it. static void ShowExampleAppMainMenuBar() { if (ImGui::BeginMainMenuBar()) { - if (ImGui::BeginMenu("File")) + if (ImGui::BeginMenu("File")) { ShowExampleMenuFile(); ImGui::EndMenu(); @@ -2299,6 +3352,7 @@ static void ShowExampleAppMainMenuBar() } } +// Note that shortcuts are currently provided for display only (future version will add flags to BeginMenu to process shortcuts) static void ShowExampleMenuFile() { ImGui::MenuItem("(dummy menu)", NULL, false, false); @@ -2364,236 +3418,22 @@ static void ShowExampleMenuFile() if (ImGui::MenuItem("Quit", "Alt+F4")) {} } -// Demonstrate creating a window which gets auto-resized according to its content. -static void ShowExampleAppAutoResize(bool* p_open) -{ - if (!ImGui::Begin("Example: Auto-resizing window", p_open, ImGuiWindowFlags_AlwaysAutoResize)) - { - ImGui::End(); - return; - } +//----------------------------------------------------------------------------- +// [SECTION] Example App: Debug Console / ShowExampleAppConsole() +//----------------------------------------------------------------------------- - static int lines = 10; - ImGui::Text("Window will resize every-frame to the size of its content.\nNote that you probably don't want to query the window size to\noutput your content because that would create a feedback loop."); - ImGui::SliderInt("Number of lines", &lines, 1, 20); - for (int i = 0; i < lines; i++) - ImGui::Text("%*sThis is line %d", i*4, "", i); // Pad with space to extend size horizontally - ImGui::End(); -} - -// Demonstrate creating a window with custom resize constraints. -static void ShowExampleAppConstrainedResize(bool* p_open) -{ - struct CustomConstraints // Helper functions to demonstrate programmatic constraints - { - static void Square(ImGuiSizeCallbackData* data) { data->DesiredSize = ImVec2(IM_MAX(data->DesiredSize.x, data->DesiredSize.y), IM_MAX(data->DesiredSize.x, data->DesiredSize.y)); } - static void Step(ImGuiSizeCallbackData* data) { float step = (float)(int)(intptr_t)data->UserData; data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step); } - }; - - static bool auto_resize = false; - static int type = 0; - static int display_lines = 10; - if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Vertical only - if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Horizontal only - if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100 - if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width 400-500 - if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, 500)); // Height 400-500 - if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square - if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)100);// Fixed Step - - ImGuiWindowFlags flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0; - if (ImGui::Begin("Example: Constrained Resize", p_open, flags)) - { - const char* desc[] = - { - "Resize vertical only", - "Resize horizontal only", - "Width > 100, Height > 100", - "Width 400-500", - "Height 400-500", - "Custom: Always Square", - "Custom: Fixed Steps (100)", - }; - if (ImGui::Button("200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); - if (ImGui::Button("500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); - if (ImGui::Button("800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } - ImGui::PushItemWidth(200); - ImGui::Combo("Constraint", &type, desc, IM_ARRAYSIZE(desc)); - ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100); - ImGui::PopItemWidth(); - ImGui::Checkbox("Auto-resize", &auto_resize); - for (int i = 0; i < display_lines; i++) - ImGui::Text("%*sHello, sailor! Making this line long enough for the example.", i * 4, ""); - } - ImGui::End(); -} - -// Demonstrate creating a simple static window with no decoration + a context-menu to choose which corner of the screen to use. -static void ShowExampleAppFixedOverlay(bool* p_open) -{ - const float DISTANCE = 10.0f; - static int corner = 0; - ImVec2 window_pos = ImVec2((corner & 1) ? ImGui::GetIO().DisplaySize.x - DISTANCE : DISTANCE, (corner & 2) ? ImGui::GetIO().DisplaySize.y - DISTANCE : DISTANCE); - ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); - ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); - ImGui::SetNextWindowBgAlpha(0.3f); // Transparent background - if (ImGui::Begin("Example: Fixed Overlay", p_open, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NoFocusOnAppearing|ImGuiWindowFlags_NoNav)) - { - ImGui::Text("Simple overlay\nin the corner of the screen.\n(right-click to change position)"); - ImGui::Separator(); - ImGui::Text("Mouse Position: (%.1f,%.1f)", ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y); - if (ImGui::BeginPopupContextWindow()) - { - if (ImGui::MenuItem("Top-left", NULL, corner == 0)) corner = 0; - if (ImGui::MenuItem("Top-right", NULL, corner == 1)) corner = 1; - if (ImGui::MenuItem("Bottom-left", NULL, corner == 2)) corner = 2; - if (ImGui::MenuItem("Bottom-right", NULL, corner == 3)) corner = 3; - if (p_open && ImGui::MenuItem("Close")) *p_open = false; - ImGui::EndPopup(); - } - ImGui::End(); - } -} - -// Demonstrate using "##" and "###" in identifiers to manipulate ID generation. -// This apply to regular items as well. Read FAQ section "How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on the purpose of labels/IDs." for details. -static void ShowExampleAppWindowTitles(bool*) -{ - // By default, Windows are uniquely identified by their title. - // You can use the "##" and "###" markers to manipulate the display/ID. - - // Using "##" to display same title but have unique identifier. - ImGui::SetNextWindowPos(ImVec2(100,100), ImGuiCond_FirstUseEver); - ImGui::Begin("Same title as another window##1"); - ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); - ImGui::End(); - - ImGui::SetNextWindowPos(ImVec2(100,200), ImGuiCond_FirstUseEver); - ImGui::Begin("Same title as another window##2"); - ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique."); - ImGui::End(); - - // Using "###" to display a changing title but keep a static identifier "AnimatedTitle" - char buf[128]; - sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime()/0.25f)&3], ImGui::GetFrameCount()); - ImGui::SetNextWindowPos(ImVec2(100,300), ImGuiCond_FirstUseEver); - ImGui::Begin(buf); - ImGui::Text("This window has a changing title."); - ImGui::End(); -} - -// Demonstrate using the low-level ImDrawList to draw custom shapes. -static void ShowExampleAppCustomRendering(bool* p_open) -{ - ImGui::SetNextWindowSize(ImVec2(350,560), ImGuiCond_FirstUseEver); - if (!ImGui::Begin("Example: Custom rendering", p_open)) - { - ImGui::End(); - return; - } - - // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of overloaded operators, etc. - // Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your types and ImVec2/ImVec4. - // ImGui defines overloaded operators but they are internal to imgui.cpp and not exposed outside (to avoid messing with your types) - // In this example we are not using the maths operators! - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - - // Primitives - ImGui::Text("Primitives"); - static float sz = 36.0f; - static ImVec4 col = ImVec4(1.0f,1.0f,0.4f,1.0f); - ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f"); - ImGui::ColorEdit3("Color", &col.x); - { - const ImVec2 p = ImGui::GetCursorScreenPos(); - const ImU32 col32 = ImColor(col); - float x = p.x + 4.0f, y = p.y + 4.0f, spacing = 8.0f; - for (int n = 0; n < 2; n++) - { - float thickness = (n == 0) ? 1.0f : 4.0f; - draw_list->AddCircle(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 20, thickness); x += sz+spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 0.0f, ImDrawCornerFlags_All, thickness); x += sz+spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_All, thickness); x += sz+spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotRight, thickness); x += sz+spacing; - draw_list->AddTriangle(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32, thickness); x += sz+spacing; - draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y ), col32, thickness); x += sz+spacing; - draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, thickness); x += sz+spacing; - draw_list->AddLine(ImVec2(x, y), ImVec2(x, y+sz), col32, thickness); x += spacing; - draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x+sz*1.3f,y+sz*0.3f), ImVec2(x+sz-sz*1.3f,y+sz-sz*0.3f), ImVec2(x+sz, y+sz), col32, thickness); - x = p.x + 4; - y += sz+spacing; - } - draw_list->AddCircleFilled(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 32); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotRight); x += sz+spacing; - draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32); x += sz+spacing; - draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x+sz, y+sz), IM_COL32(0,0,0,255), IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255)); - ImGui::Dummy(ImVec2((sz+spacing)*8, (sz+spacing)*3)); - } - ImGui::Separator(); - { - static ImVector points; - static bool adding_line = false; - ImGui::Text("Canvas example"); - if (ImGui::Button("Clear")) points.clear(); - if (points.Size >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } } - ImGui::Text("Left-click and drag to add lines,\nRight-click to undo"); - - // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use IsItemHovered() - // However you can draw directly and poll mouse/keyboard by yourself. You can manipulate the cursor using GetCursorPos() and SetCursorPos(). - // If you only use the ImDrawList API, you can notify the owner window of its extends by using SetCursorPos(max). - ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! - ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available - if (canvas_size.x < 50.0f) canvas_size.x = 50.0f; - if (canvas_size.y < 50.0f) canvas_size.y = 50.0f; - draw_list->AddRectFilledMultiColor(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(50,50,50,255), IM_COL32(50,50,60,255), IM_COL32(60,60,70,255), IM_COL32(50,50,60,255)); - draw_list->AddRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(255,255,255,255)); - - bool adding_preview = false; - ImGui::InvisibleButton("canvas", canvas_size); - ImVec2 mouse_pos_in_canvas = ImVec2(ImGui::GetIO().MousePos.x - canvas_pos.x, ImGui::GetIO().MousePos.y - canvas_pos.y); - if (adding_line) - { - adding_preview = true; - points.push_back(mouse_pos_in_canvas); - if (!ImGui::IsMouseDown(0)) - adding_line = adding_preview = false; - } - if (ImGui::IsItemHovered()) - { - if (!adding_line && ImGui::IsMouseClicked(0)) - { - points.push_back(mouse_pos_in_canvas); - adding_line = true; - } - if (ImGui::IsMouseClicked(1) && !points.empty()) - { - adding_line = adding_preview = false; - points.pop_back(); - points.pop_back(); - } - } - draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x+canvas_size.x, canvas_pos.y+canvas_size.y), true); // clip lines within the canvas (if we resize it, etc.) - for (int i = 0; i < points.Size - 1; i += 2) - draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i+1].x, canvas_pos.y + points[i+1].y), IM_COL32(255,255,0,255), 2.0f); - draw_list->PopClipRect(); - if (adding_preview) - points.pop_back(); - } - ImGui::End(); -} - -// Demonstrating creating a simple console window, with scrolling, filtering, completion and history. +// Demonstrate creating a simple console window, with scrolling, filtering, completion and history. // For the console example, here we are using a more C++ like approach of declaring a class to hold the data and the functions. struct ExampleAppConsole { char InputBuf[256]; ImVector Items; - bool ScrollToBottom; + ImVector Commands; ImVector History; int HistoryPos; // -1: new line, 0..History.Size-1 browsing history. - ImVector Commands; + ImGuiTextFilter Filter; + bool AutoScroll; + bool ScrollToBottom; ExampleAppConsole() { @@ -2603,8 +3443,10 @@ struct ExampleAppConsole Commands.push_back("HELP"); Commands.push_back("HISTORY"); Commands.push_back("CLEAR"); - Commands.push_back("CLASSIFY"); // "classify" is here to provide an example of "C"+[tab] completing to "CL" and displaying matches. - AddLog("Welcome to ImGui!"); + Commands.push_back("CLASSIFY"); // "classify" is only here to provide an example of "C"+[tab] completing to "CL" and displaying matches. + AutoScroll = true; + ScrollToBottom = true; + AddLog("Welcome to Dear ImGui!"); } ~ExampleAppConsole() { @@ -2616,7 +3458,8 @@ struct ExampleAppConsole // Portable helpers static int Stricmp(const char* str1, const char* str2) { int d; while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } return d; } static int Strnicmp(const char* str1, const char* str2, int n) { int d = 0; while (n > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; n--; } return d; } - static char* Strdup(const char *str) { size_t len = strlen(str) + 1; void* buff = malloc(len); return (char*)memcpy(buff, (const void*)str, len); } + static char* Strdup(const char *str) { size_t len = strlen(str) + 1; void* buf = malloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)str, len); } + static void Strtrim(char* str) { char* str_end = str + strlen(str); while (str_end > str && str_end[-1] == ' ') str_end--; *str_end = 0; } void ClearLog() { @@ -2636,7 +3479,8 @@ struct ExampleAppConsole buf[IM_ARRAYSIZE(buf)-1] = 0; va_end(args); Items.push_back(Strdup(buf)); - ScrollToBottom = true; + if (AutoScroll) + ScrollToBottom = true; } void Draw(const char* title, bool* p_open) @@ -2652,7 +3496,7 @@ struct ExampleAppConsole // Here we create a context menu only available from the title bar. if (ImGui::BeginPopupContextItem()) { - if (ImGui::MenuItem("Close")) + if (ImGui::MenuItem("Close Console")) *p_open = false; ImGui::EndPopup(); } @@ -2662,7 +3506,7 @@ struct ExampleAppConsole // TODO: display items starting from the bottom - if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine(); + if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine(); if (ImGui::SmallButton("Add Dummy Error")) { AddLog("[error] something went wrong"); } ImGui::SameLine(); if (ImGui::SmallButton("Clear")) { ClearLog(); } ImGui::SameLine(); bool copy_to_clipboard = ImGui::SmallButton("Copy"); ImGui::SameLine(); @@ -2671,10 +3515,20 @@ struct ExampleAppConsole ImGui::Separator(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); - static ImGuiTextFilter filter; - filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180); - ImGui::PopStyleVar(); + // Options menu + if (ImGui::BeginPopup("Options")) + { + if (ImGui::Checkbox("Auto-scroll", &AutoScroll)) + if (AutoScroll) + ScrollToBottom = true; + ImGui::EndPopup(); + } + + // Options, Filter + if (ImGui::Button("Options")) + ImGui::OpenPopup("Options"); + ImGui::SameLine(); + Filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180); ImGui::Separator(); const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); // 1 separator, 1 input text @@ -2692,30 +3546,31 @@ struct ExampleAppConsole // ImGuiListClipper clipper(Items.Size); // while (clipper.Step()) // for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - // However take note that you can not use this code as is if a filter is active because it breaks the 'cheap random-access' property. We would need random-access on the post-filtered list. + // However, note that you can not use this code as is if a filter is active because it breaks the 'cheap random-access' property. We would need random-access on the post-filtered list. // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices that passed the filtering test, recomputing this array when user changes the filter, // and appending newly elements as they are inserted. This is left as a task to the user until we can manage to improve this example code! // If your items are of variable size you may want to implement code similar to what ImGuiListClipper does. Or split your data into fixed height items to allow random-seeking into your list. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4,1)); // Tighten spacing if (copy_to_clipboard) ImGui::LogToClipboard(); - ImVec4 col_default_text = ImGui::GetStyleColorVec4(ImGuiCol_Text); for (int i = 0; i < Items.Size; i++) { const char* item = Items[i]; - if (!filter.PassFilter(item)) + if (!Filter.PassFilter(item)) continue; - ImVec4 col = col_default_text; - if (strstr(item, "[error]")) col = ImColor(1.0f,0.4f,0.4f,1.0f); - else if (strncmp(item, "# ", 2) == 0) col = ImColor(1.0f,0.78f,0.58f,1.0f); - ImGui::PushStyleColor(ImGuiCol_Text, col); + + // Normally you would store more information in your item (e.g. make Items[] an array of structure, store color/type etc.) + bool pop_color = false; + if (strstr(item, "[error]")) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f)); pop_color = true; } + else if (strncmp(item, "# ", 2) == 0) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.8f, 0.6f, 1.0f)); pop_color = true; } ImGui::TextUnformatted(item); - ImGui::PopStyleColor(); + if (pop_color) + ImGui::PopStyleColor(); } if (copy_to_clipboard) ImGui::LogFinish(); if (ScrollToBottom) - ImGui::SetScrollHere(); + ImGui::SetScrollHereY(1.0f); ScrollToBottom = false; ImGui::PopStyleVar(); ImGui::EndChild(); @@ -2725,15 +3580,15 @@ struct ExampleAppConsole bool reclaim_focus = false; if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), ImGuiInputTextFlags_EnterReturnsTrue|ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_CallbackHistory, &TextEditCallbackStub, (void*)this)) { - char* input_end = InputBuf+strlen(InputBuf); - while (input_end > InputBuf && input_end[-1] == ' ') { input_end--; } *input_end = 0; - if (InputBuf[0]) - ExecCommand(InputBuf); - strcpy(InputBuf, ""); + char* s = InputBuf; + Strtrim(s); + if (s[0]) + ExecCommand(s); + strcpy(s, ""); reclaim_focus = true; } - // Demonstrate keeping focus on the input box + // Auto-focus on window apparition ImGui::SetItemDefaultFocus(); if (reclaim_focus) ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget @@ -2777,15 +3632,18 @@ struct ExampleAppConsole { AddLog("Unknown command: '%s'\n", command_line); } + + // On commad input, we scroll to bottom even if AutoScroll==false + ScrollToBottom = true; } - static int TextEditCallbackStub(ImGuiTextEditCallbackData* data) // In C++11 you are better off using lambdas for this sort of forwarding callbacks + static int TextEditCallbackStub(ImGuiInputTextCallbackData* data) // In C++11 you are better off using lambdas for this sort of forwarding callbacks { ExampleAppConsole* console = (ExampleAppConsole*)data->UserData; return console->TextEditCallback(data); } - int TextEditCallback(ImGuiTextEditCallbackData* data) + int TextEditCallback(ImGuiInputTextCallbackData* data) { //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd); switch (data->EventFlag) @@ -2876,8 +3734,9 @@ struct ExampleAppConsole // A better implementation would preserve the data on the current input line along with cursor position. if (prev_history_pos != HistoryPos) { - data->CursorPos = data->SelectionStart = data->SelectionEnd = data->BufTextLen = (int)snprintf(data->Buf, (size_t)data->BufSize, "%s", (HistoryPos >= 0) ? History[HistoryPos] : ""); - data->BufDirty = true; + const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : ""; + data->DeleteChars(0, data->BufTextLen); + data->InsertChars(0, history_str); } } } @@ -2891,6 +3750,10 @@ static void ShowExampleAppConsole(bool* p_open) console.Draw("Example: Console", p_open); } +//----------------------------------------------------------------------------- +// [SECTION] Example App: Debug Log / ShowExampleAppLog() +//----------------------------------------------------------------------------- + // Usage: // static ExampleAppLog my_log; // my_log.AddLog("Hello %d world\n", 123); @@ -2899,10 +3762,23 @@ struct ExampleAppLog { ImGuiTextBuffer Buf; ImGuiTextFilter Filter; - ImVector LineOffsets; // Index to lines offset + ImVector LineOffsets; // Index to lines offset. We maintain this with AddLog() calls, allowing us to have a random access on lines + bool AutoScroll; bool ScrollToBottom; - void Clear() { Buf.clear(); LineOffsets.clear(); } + ExampleAppLog() + { + AutoScroll = true; + ScrollToBottom = false; + Clear(); + } + + void Clear() + { + Buf.clear(); + LineOffsets.clear(); + LineOffsets.push_back(0); + } void AddLog(const char* fmt, ...) IM_FMTARGS(2) { @@ -2913,42 +3789,91 @@ struct ExampleAppLog va_end(args); for (int new_size = Buf.size(); old_size < new_size; old_size++) if (Buf[old_size] == '\n') - LineOffsets.push_back(old_size); - ScrollToBottom = true; + LineOffsets.push_back(old_size + 1); + if (AutoScroll) + ScrollToBottom = true; } void Draw(const char* title, bool* p_open = NULL) { - ImGui::SetNextWindowSize(ImVec2(500,400), ImGuiCond_FirstUseEver); - ImGui::Begin(title, p_open); - if (ImGui::Button("Clear")) Clear(); + if (!ImGui::Begin(title, p_open)) + { + ImGui::End(); + return; + } + + // Options menu + if (ImGui::BeginPopup("Options")) + { + if (ImGui::Checkbox("Auto-scroll", &AutoScroll)) + if (AutoScroll) + ScrollToBottom = true; + ImGui::EndPopup(); + } + + // Main window + if (ImGui::Button("Options")) + ImGui::OpenPopup("Options"); + ImGui::SameLine(); + bool clear = ImGui::Button("Clear"); ImGui::SameLine(); bool copy = ImGui::Button("Copy"); ImGui::SameLine(); Filter.Draw("Filter", -100.0f); + ImGui::Separator(); ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_HorizontalScrollbar); - if (copy) ImGui::LogToClipboard(); + if (clear) + Clear(); + if (copy) + ImGui::LogToClipboard(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + const char* buf = Buf.begin(); + const char* buf_end = Buf.end(); if (Filter.IsActive()) { - const char* buf_begin = Buf.begin(); - const char* line = buf_begin; - for (int line_no = 0; line != NULL; line_no++) + // In this example we don't use the clipper when Filter is enabled. + // This is because we don't have a random access on the result on our filter. + // A real application processing logs with ten of thousands of entries may want to store the result of search/filter. + // especially if the filtering function is not trivial (e.g. reg-exp). + for (int line_no = 0; line_no < LineOffsets.Size; line_no++) { - const char* line_end = (line_no < LineOffsets.Size) ? buf_begin + LineOffsets[line_no] : NULL; - if (Filter.PassFilter(line, line_end)) - ImGui::TextUnformatted(line, line_end); - line = line_end && line_end[1] ? line_end + 1 : NULL; + const char* line_start = buf + LineOffsets[line_no]; + const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end; + if (Filter.PassFilter(line_start, line_end)) + ImGui::TextUnformatted(line_start, line_end); } } else { - ImGui::TextUnformatted(Buf.begin()); + // The simplest and easy way to display the entire buffer: + // ImGui::TextUnformatted(buf_begin, buf_end); + // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines. + // Here we instead demonstrate using the clipper to only process lines that are within the visible area. + // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended. + // Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height, + // both of which we can handle since we an array pointing to the beginning of each line of text. + // When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper. + // Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries) + ImGuiListClipper clipper; + clipper.Begin(LineOffsets.Size); + while (clipper.Step()) + { + for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) + { + const char* line_start = buf + LineOffsets[line_no]; + const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end; + ImGui::TextUnformatted(line_start, line_end); + } + } + clipper.End(); } + ImGui::PopStyleVar(); if (ScrollToBottom) - ImGui::SetScrollHere(1.0f); + ImGui::SetScrollHereY(1.0f); ScrollToBottom = false; ImGui::EndChild(); ImGui::End(); @@ -2960,24 +3885,38 @@ static void ShowExampleAppLog(bool* p_open) { static ExampleAppLog log; - // Demo: add random items (unless Ctrl is held) - static float last_time = -1.0f; - float time = ImGui::GetTime(); - if (time - last_time >= 0.20f && !ImGui::GetIO().KeyCtrl) + // For the demo: add a debug button _BEFORE_ the normal log window contents + // We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window. + // Most of the contents of the window will be added by the log.Draw() call. + ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); + ImGui::Begin("Example: Log", p_open); + if (ImGui::SmallButton("[Debug] Add 5 entries")) { - const char* random_words[] = { "system", "info", "warning", "error", "fatal", "notice", "log" }; - log.AddLog("[%s] Hello, time is %.1f, frame count is %d\n", random_words[rand() % IM_ARRAYSIZE(random_words)], time, ImGui::GetFrameCount()); - last_time = time; + static int counter = 0; + for (int n = 0; n < 5; n++) + { + const char* categories[3] = { "info", "warn", "error" }; + const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" }; + log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n", + ImGui::GetFrameCount(), categories[counter % IM_ARRAYSIZE(categories)], ImGui::GetTime(), words[counter % IM_ARRAYSIZE(words)]); + counter++; + } } + ImGui::End(); + // Actually call in the regular Log helper (which will Begin() into the same window as we just did) log.Draw("Example: Log", p_open); } +//----------------------------------------------------------------------------- +// [SECTION] Example App: Simple Layout / ShowExampleAppLayout() +//----------------------------------------------------------------------------- + // Demonstrate create a window with multiple child windows. static void ShowExampleAppLayout(bool* p_open) { ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver); - if (ImGui::Begin("Example: Layout", p_open, ImGuiWindowFlags_MenuBar)) + if (ImGui::Begin("Example: Simple layout", p_open, ImGuiWindowFlags_MenuBar)) { if (ImGui::BeginMenuBar()) { @@ -3007,7 +3946,20 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us ImGui::Text("MyObject: %d", selected); ImGui::Separator(); - ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "); + if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None)) + { + if (ImGui::BeginTabItem("Description")) + { + ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Details")) + { + ImGui::Text("ID: 0123456789"); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } ImGui::EndChild(); if (ImGui::Button("Revert")) {} ImGui::SameLine(); @@ -3017,6 +3969,10 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::End(); } +//----------------------------------------------------------------------------- +// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() +//----------------------------------------------------------------------------- + // Demonstrate create a simple property editor. static void ShowExampleAppPropertyEditor(bool* p_open) { @@ -3027,7 +3983,7 @@ static void ShowExampleAppPropertyEditor(bool* p_open) return; } - ShowHelpMarker("This example shows how you may implement a property editor using two columns.\nAll objects/fields data are dummies here.\nRemember that in many simple cases, you can use ImGui::SameLine(xxx) to position\nyour cursor horizontally instead of using the Columns() API."); + HelpMarker("This example shows how you may implement a property editor using two columns.\nAll objects/fields data are dummies here.\nRemember that in many simple cases, you can use ImGui::SameLine(xxx) to position\nyour cursor horizontally instead of using the Columns() API."); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2,2)); ImGui::Columns(2); @@ -3056,20 +4012,15 @@ static void ShowExampleAppPropertyEditor(bool* p_open) } else { + // Here we use a TreeNode to highlight on hover (we could use e.g. Selectable as well) ImGui::AlignTextToFramePadding(); - // Here we use a Selectable (instead of Text) to highlight on hover - //ImGui::Text("Field_%d", i); - char label[32]; - sprintf(label, "Field_%d", i); - ImGui::Bullet(); - ImGui::Selectable(label); + ImGui::TreeNodeEx("Field", ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Bullet, "Field_%d", i); ImGui::NextColumn(); - ImGui::PushItemWidth(-1); + ImGui::SetNextItemWidth(-1); if (i >= 5) ImGui::InputFloat("##value", &dummy_members[i], 1.0f); else ImGui::DragFloat("##value", &dummy_members[i], 0.01f); - ImGui::PopItemWidth(); ImGui::NextColumn(); } ImGui::PopID(); @@ -3090,6 +4041,10 @@ static void ShowExampleAppPropertyEditor(bool* p_open) ImGui::End(); } +//----------------------------------------------------------------------------- +// [SECTION] Example App: Long Text / ShowExampleAppLongText() +//----------------------------------------------------------------------------- + // Demonstrate/test rendering huge amount of text, and the incidence of clipping. static void ShowExampleAppLongText(bool* p_open) { @@ -3104,7 +4059,7 @@ static void ShowExampleAppLongText(bool* p_open) static ImGuiTextBuffer log; static int lines = 0; ImGui::Text("Printing unusually long amount of text."); - ImGui::Combo("Test type", &test_type, "Single call to TextUnformatted()\0Multiple calls to Text(), clipped manually\0Multiple calls to Text(), not clipped (slow)\0"); + ImGui::Combo("Test type", &test_type, "Single call to TextUnformatted()\0Multiple calls to Text(), clipped\0Multiple calls to Text(), not clipped (slow)\0"); ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size()); if (ImGui::Button("Clear")) { log.clear(); lines = 0; } ImGui::SameLine(); @@ -3144,9 +4099,568 @@ static void ShowExampleAppLongText(bool* p_open) ImGui::End(); } +//----------------------------------------------------------------------------- +// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize() +//----------------------------------------------------------------------------- + +// Demonstrate creating a window which gets auto-resized according to its content. +static void ShowExampleAppAutoResize(bool* p_open) +{ + if (!ImGui::Begin("Example: Auto-resizing window", p_open, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::End(); + return; + } + + static int lines = 10; + ImGui::Text("Window will resize every-frame to the size of its content.\nNote that you probably don't want to query the window size to\noutput your content because that would create a feedback loop."); + ImGui::SliderInt("Number of lines", &lines, 1, 20); + for (int i = 0; i < lines; i++) + ImGui::Text("%*sThis is line %d", i * 4, "", i); // Pad with space to extend size horizontally + ImGui::End(); +} + +//----------------------------------------------------------------------------- +// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize() +//----------------------------------------------------------------------------- + +// Demonstrate creating a window with custom resize constraints. +static void ShowExampleAppConstrainedResize(bool* p_open) +{ + struct CustomConstraints // Helper functions to demonstrate programmatic constraints + { + static void Square(ImGuiSizeCallbackData* data) { data->DesiredSize = ImVec2(IM_MAX(data->DesiredSize.x, data->DesiredSize.y), IM_MAX(data->DesiredSize.x, data->DesiredSize.y)); } + static void Step(ImGuiSizeCallbackData* data) { float step = (float)(int)(intptr_t)data->UserData; data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step); } + }; + + static bool auto_resize = false; + static int type = 0; + static int display_lines = 10; + if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Vertical only + if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Horizontal only + if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100 + if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width 400-500 + if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, 500)); // Height 400-500 + if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square + if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)(intptr_t)100); // Fixed Step + + ImGuiWindowFlags flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0; + if (ImGui::Begin("Example: Constrained Resize", p_open, flags)) + { + const char* desc[] = + { + "Resize vertical only", + "Resize horizontal only", + "Width > 100, Height > 100", + "Width 400-500", + "Height 400-500", + "Custom: Always Square", + "Custom: Fixed Steps (100)", + }; + if (ImGui::Button("200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); + if (ImGui::Button("500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); + if (ImGui::Button("800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } + ImGui::SetNextItemWidth(200); + ImGui::Combo("Constraint", &type, desc, IM_ARRAYSIZE(desc)); + ImGui::SetNextItemWidth(200); + ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100); + ImGui::Checkbox("Auto-resize", &auto_resize); + for (int i = 0; i < display_lines; i++) + ImGui::Text("%*sHello, sailor! Making this line long enough for the example.", i * 4, ""); + } + ImGui::End(); +} + +//----------------------------------------------------------------------------- +// [SECTION] Example App: Simple Overlay / ShowExampleAppSimpleOverlay() +//----------------------------------------------------------------------------- + +// Demonstrate creating a simple static window with no decoration + a context-menu to choose which corner of the screen to use. +static void ShowExampleAppSimpleOverlay(bool* p_open) +{ + const float DISTANCE = 10.0f; + static int corner = 0; + ImGuiIO& io = ImGui::GetIO(); + if (corner != -1) + { + ImVec2 window_pos = ImVec2((corner & 1) ? io.DisplaySize.x - DISTANCE : DISTANCE, (corner & 2) ? io.DisplaySize.y - DISTANCE : DISTANCE); + ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); + ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); + } + ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background + if (ImGui::Begin("Example: Simple overlay", p_open, (corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) + { + ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)"); + ImGui::Separator(); + if (ImGui::IsMousePosValid()) + ImGui::Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y); + else + ImGui::Text("Mouse Position: "); + if (ImGui::BeginPopupContextWindow()) + { + if (ImGui::MenuItem("Custom", NULL, corner == -1)) corner = -1; + if (ImGui::MenuItem("Top-left", NULL, corner == 0)) corner = 0; + if (ImGui::MenuItem("Top-right", NULL, corner == 1)) corner = 1; + if (ImGui::MenuItem("Bottom-left", NULL, corner == 2)) corner = 2; + if (ImGui::MenuItem("Bottom-right", NULL, corner == 3)) corner = 3; + if (p_open && ImGui::MenuItem("Close")) *p_open = false; + ImGui::EndPopup(); + } + } + ImGui::End(); +} + +//----------------------------------------------------------------------------- +// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles() +//----------------------------------------------------------------------------- + +// Demonstrate using "##" and "###" in identifiers to manipulate ID generation. +// This apply to all regular items as well. Read FAQ section "How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on the purpose of labels/IDs." for details. +static void ShowExampleAppWindowTitles(bool*) +{ + // By default, Windows are uniquely identified by their title. + // You can use the "##" and "###" markers to manipulate the display/ID. + + // Using "##" to display same title but have unique identifier. + ImGui::SetNextWindowPos(ImVec2(100, 100), ImGuiCond_FirstUseEver); + ImGui::Begin("Same title as another window##1"); + ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); + ImGui::End(); + + ImGui::SetNextWindowPos(ImVec2(100, 200), ImGuiCond_FirstUseEver); + ImGui::Begin("Same title as another window##2"); + ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique."); + ImGui::End(); + + // Using "###" to display a changing title but keep a static identifier "AnimatedTitle" + char buf[128]; + sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount()); + ImGui::SetNextWindowPos(ImVec2(100, 300), ImGuiCond_FirstUseEver); + ImGui::Begin(buf); + ImGui::Text("This window has a changing title."); + ImGui::End(); +} + +//----------------------------------------------------------------------------- +// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() +//----------------------------------------------------------------------------- + +// Demonstrate using the low-level ImDrawList to draw custom shapes. +static void ShowExampleAppCustomRendering(bool* p_open) +{ + ImGui::SetNextWindowSize(ImVec2(350, 560), ImGuiCond_FirstUseEver); + if (!ImGui::Begin("Example: Custom rendering", p_open)) + { + ImGui::End(); + return; + } + + // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of overloaded operators, etc. + // Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your types and ImVec2/ImVec4. + // ImGui defines overloaded operators but they are internal to imgui.cpp and not exposed outside (to avoid messing with your types) + // In this example we are not using the maths operators! + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + + if (ImGui::BeginTabBar("##TabBar")) + { + // Primitives + if (ImGui::BeginTabItem("Primitives")) + { + static float sz = 36.0f; + static float thickness = 3.0f; + static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); + ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f"); + ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); + ImGui::ColorEdit4("Color", &colf.x); + const ImVec2 p = ImGui::GetCursorScreenPos(); + const ImU32 col = ImColor(colf); + float x = p.x + 4.0f, y = p.y + 4.0f; + float spacing = 10.0f; + ImDrawCornerFlags corners_none = 0; + ImDrawCornerFlags corners_all = ImDrawCornerFlags_All; + ImDrawCornerFlags corners_tl_br = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight; + for (int n = 0; n < 2; n++) + { + // First line uses a thickness of 1.0f, second line uses the configurable thickness + float th = (n == 0) ? 1.0f : thickness; + draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, 6, th); x += sz + spacing; // Hexagon + draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, 20, th); x += sz + spacing; // Circle + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, corners_none, th); x += sz + spacing; // Square + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_all, th); x += sz + spacing; // Square with all rounded corners + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br, th); x += sz + spacing; // Square with two rounded corners + draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th); x += sz + spacing; // Triangle + draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th); x += sz*0.4f + spacing; // Thin triangle + draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) + draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) + draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line + draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x + sz*1.3f, y + sz*0.3f), ImVec2(x + sz - sz*1.3f, y + sz - sz*0.3f), ImVec2(x + sz, y + sz), col, th); + x = p.x + 4; + y += sz + spacing; + } + draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, 6); x += sz + spacing; // Hexagon + draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, 32); x += sz + spacing; // Circle + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f); x += sz + spacing; // Square with all rounded corners + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br); x += sz + spacing; // Square with two rounded corners + draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col); x += sz + spacing; // Triangle + draw_list->AddTriangleFilled(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col); x += sz*0.4f + spacing; // Thin triangle + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col); x += spacing*2.0f; // Vertical line (faster than AddLine, but only handle integer thickness) + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine) + draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255)); + ImGui::Dummy(ImVec2((sz + spacing) * 9.8f, (sz + spacing) * 3)); + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Canvas")) + { + static ImVector points; + static bool adding_line = false; + if (ImGui::Button("Clear")) points.clear(); + if (points.Size >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } } + ImGui::Text("Left-click and drag to add lines,\nRight-click to undo"); + + // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use IsItemHovered() + // But you can also draw directly and poll mouse/keyboard by yourself. You can manipulate the cursor using GetCursorPos() and SetCursorPos(). + // If you only use the ImDrawList API, you can notify the owner window of its extends by using SetCursorPos(max). + ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! + ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available + if (canvas_size.x < 50.0f) canvas_size.x = 50.0f; + if (canvas_size.y < 50.0f) canvas_size.y = 50.0f; + draw_list->AddRectFilledMultiColor(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(50, 50, 50, 255), IM_COL32(50, 50, 60, 255), IM_COL32(60, 60, 70, 255), IM_COL32(50, 50, 60, 255)); + draw_list->AddRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(255, 255, 255, 255)); + + bool adding_preview = false; + ImGui::InvisibleButton("canvas", canvas_size); + ImVec2 mouse_pos_in_canvas = ImVec2(ImGui::GetIO().MousePos.x - canvas_pos.x, ImGui::GetIO().MousePos.y - canvas_pos.y); + if (adding_line) + { + adding_preview = true; + points.push_back(mouse_pos_in_canvas); + if (!ImGui::IsMouseDown(0)) + adding_line = adding_preview = false; + } + if (ImGui::IsItemHovered()) + { + if (!adding_line && ImGui::IsMouseClicked(0)) + { + points.push_back(mouse_pos_in_canvas); + adding_line = true; + } + if (ImGui::IsMouseClicked(1) && !points.empty()) + { + adding_line = adding_preview = false; + points.pop_back(); + points.pop_back(); + } + } + draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), true); // clip lines within the canvas (if we resize it, etc.) + for (int i = 0; i < points.Size - 1; i += 2) + draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i + 1].x, canvas_pos.y + points[i + 1].y), IM_COL32(255, 255, 0, 255), 2.0f); + draw_list->PopClipRect(); + if (adding_preview) + points.pop_back(); + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("BG/FG draw lists")) + { + static bool draw_bg = true; + static bool draw_fg = true; + ImGui::Checkbox("Draw in Background draw list", &draw_bg); + ImGui::Checkbox("Draw in Foreground draw list", &draw_fg); + ImVec2 window_pos = ImGui::GetWindowPos(); + ImVec2 window_size = ImGui::GetWindowSize(); + ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f); + if (draw_bg) + ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 32, 10+4); + if (draw_fg) + ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 32, 10); + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } + + ImGui::End(); +} + +//----------------------------------------------------------------------------- +// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() +//----------------------------------------------------------------------------- + +// Simplified structure to mimic a Document model +struct MyDocument +{ + const char* Name; // Document title + bool Open; // Set when the document is open (in this demo, we keep an array of all available documents to simplify the demo) + bool OpenPrev; // Copy of Open from last update. + bool Dirty; // Set when the document has been modified + bool WantClose; // Set when the document + ImVec4 Color; // An arbitrary variable associated to the document + + MyDocument(const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f,1.0f,1.0f,1.0f)) + { + Name = name; + Open = OpenPrev = open; + Dirty = false; + WantClose = false; + Color = color; + } + void DoOpen() { Open = true; } + void DoQueueClose() { WantClose = true; } + void DoForceClose() { Open = false; Dirty = false; } + void DoSave() { Dirty = false; } + + // Display dummy contents for the Document + static void DisplayContents(MyDocument* doc) + { + ImGui::PushID(doc); + ImGui::Text("Document \"%s\"", doc->Name); + ImGui::PushStyleColor(ImGuiCol_Text, doc->Color); + ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); + ImGui::PopStyleColor(); + if (ImGui::Button("Modify", ImVec2(100, 0))) + doc->Dirty = true; + ImGui::SameLine(); + if (ImGui::Button("Save", ImVec2(100, 0))) + doc->DoSave(); + ImGui::ColorEdit3("color", &doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior. + ImGui::PopID(); + } + + // Display context menu for the Document + static void DisplayContextMenu(MyDocument* doc) + { + if (!ImGui::BeginPopupContextItem()) + return; + + char buf[256]; + sprintf(buf, "Save %s", doc->Name); + if (ImGui::MenuItem(buf, "CTRL+S", false, doc->Open)) + doc->DoSave(); + if (ImGui::MenuItem("Close", "CTRL+W", false, doc->Open)) + doc->DoQueueClose(); + ImGui::EndPopup(); + } +}; + +struct ExampleAppDocuments +{ + ImVector Documents; + + ExampleAppDocuments() + { + Documents.push_back(MyDocument("Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f))); + Documents.push_back(MyDocument("Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f))); + Documents.push_back(MyDocument("Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f))); + Documents.push_back(MyDocument("Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f))); + Documents.push_back(MyDocument("A Rather Long Title", false)); + Documents.push_back(MyDocument("Some Document", false)); + } +}; + +// [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface. +// If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo, as opposed +// to clicking on the regular tab closing button) and stops being submitted, it will take a frame for the tab bar to notice its absence. +// During this frame there will be a gap in the tab bar, and if the tab that has disappeared was the selected one, the tab bar +// will report no selected tab during the frame. This will effectively give the impression of a flicker for one frame. +// We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch. +// Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag. +static void NotifyOfDocumentsClosedElsewhere(ExampleAppDocuments& app) +{ + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open && doc->OpenPrev) + ImGui::SetTabItemClosed(doc->Name); + doc->OpenPrev = doc->Open; + } +} + +void ShowExampleAppDocuments(bool* p_open) +{ + static ExampleAppDocuments app; + + // Options + static bool opt_reorderable = true; + static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_; + + if (!ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar)) + { + ImGui::End(); + return; + } + + // Menu + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + int open_count = 0; + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + open_count += app.Documents[doc_n].Open ? 1 : 0; + + if (ImGui::BeginMenu("Open", open_count < app.Documents.Size)) + { + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open) + if (ImGui::MenuItem(doc->Name)) + doc->DoOpen(); + } + ImGui::EndMenu(); + } + if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0)) + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + app.Documents[doc_n].DoQueueClose(); + if (ImGui::MenuItem("Exit", "Alt+F4")) {} + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + + // [Debug] List documents with one checkbox for each + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (doc_n > 0) + ImGui::SameLine(); + ImGui::PushID(doc); + if (ImGui::Checkbox(doc->Name, &doc->Open)) + if (!doc->Open) + doc->DoForceClose(); + ImGui::PopID(); + } + + ImGui::Separator(); + + // Submit Tab Bar and Tabs + { + ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0); + if (ImGui::BeginTabBar("##tabs", tab_bar_flags)) + { + if (opt_reorderable) + NotifyOfDocumentsClosedElsewhere(app); + + // [DEBUG] Stress tests + //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on. + //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway.. + + // Submit Tabs + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open) + continue; + + ImGuiTabItemFlags tab_flags = (doc->Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); + bool visible = ImGui::BeginTabItem(doc->Name, &doc->Open, tab_flags); + + // Cancel attempt to close when unsaved add to save queue so we can display a popup. + if (!doc->Open && doc->Dirty) + { + doc->Open = true; + doc->DoQueueClose(); + } + + MyDocument::DisplayContextMenu(doc); + if (visible) + { + MyDocument::DisplayContents(doc); + ImGui::EndTabItem(); + } + } + + ImGui::EndTabBar(); + } + } + + // Update closing queue + static ImVector close_queue; + if (close_queue.empty()) + { + // Close queue is locked once we started a popup + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (doc->WantClose) + { + doc->WantClose = false; + close_queue.push_back(doc); + } + } + } + + // Display closing confirmation UI + if (!close_queue.empty()) + { + int close_queue_unsaved_documents = 0; + for (int n = 0; n < close_queue.Size; n++) + if (close_queue[n]->Dirty) + close_queue_unsaved_documents++; + + if (close_queue_unsaved_documents == 0) + { + // Close documents when all are unsaved + for (int n = 0; n < close_queue.Size; n++) + close_queue[n]->DoForceClose(); + close_queue.clear(); + } + else + { + if (!ImGui::IsPopupOpen("Save?")) + ImGui::OpenPopup("Save?"); + if (ImGui::BeginPopupModal("Save?")) + { + ImGui::Text("Save change to the following items?"); + ImGui::SetNextItemWidth(-1.0f); + if (ImGui::ListBoxHeader("##", close_queue_unsaved_documents, 6)) + { + for (int n = 0; n < close_queue.Size; n++) + if (close_queue[n]->Dirty) + ImGui::Text("%s", close_queue[n]->Name); + ImGui::ListBoxFooter(); + } + + if (ImGui::Button("Yes", ImVec2(80, 0))) + { + for (int n = 0; n < close_queue.Size; n++) + { + if (close_queue[n]->Dirty) + close_queue[n]->DoSave(); + close_queue[n]->DoForceClose(); + } + close_queue.clear(); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("No", ImVec2(80, 0))) + { + for (int n = 0; n < close_queue.Size; n++) + close_queue[n]->DoForceClose(); + close_queue.clear(); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(80, 0))) + { + close_queue.clear(); + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + } + } + + ImGui::End(); +} + // End of Demo code #else +void ImGui::ShowAboutWindow(bool*) {} void ImGui::ShowDemoWindow(bool*) {} void ImGui::ShowUserGuide() {} void ImGui::ShowStyleEditor(ImGuiStyle*) {} diff --git a/external/imgui/imgui_draw.cpp b/external/imgui/imgui_draw.cpp index 53f8160..51ff2cd 100644 --- a/external/imgui/imgui_draw.cpp +++ b/external/imgui/imgui_draw.cpp @@ -1,47 +1,67 @@ -// dear imgui, v1.60 WIP +// dear imgui, v1.72 WIP // (drawing and font code) -// Contains implementation for -// - Default styles -// - ImDrawList -// - ImDrawData -// - ImFontAtlas -// - ImFont -// - Default font data +/* + +Index of this file: + +// [SECTION] STB libraries implementation +// [SECTION] Style functions +// [SECTION] ImDrawList +// [SECTION] ImDrawListSplitter +// [SECTION] ImDrawData +// [SECTION] Helpers ShadeVertsXXX functions +// [SECTION] ImFontConfig +// [SECTION] ImFontAtlas +// [SECTION] ImFontAtlas glyph ranges helpers +// [SECTION] ImFontGlyphRangesBuilder +// [SECTION] ImFont +// [SECTION] Internal Render Helpers +// [SECTION] Decompression code +// [SECTION] Default font data (ProggyClean.ttf) + +*/ #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #include "imgui.h" +#ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS +#endif #include "imgui_internal.h" #include // vsnprintf, sscanf, printf #if !defined(alloca) -#ifdef _WIN32 +#if defined(__GLIBC__) || defined(__sun) || defined(__CYGWIN__) || defined(__APPLE__) || defined(__SWITCH__) +#include // alloca (glibc uses . Note that Cygwin may have _WIN32 defined, so the order matters here) +#elif defined(_WIN32) #include // alloca #if !defined(alloca) #define alloca _alloca // for clang with MS Codegen #endif -#elif defined(__GLIBC__) || defined(__sun) -#include // alloca #else #include // alloca #endif #endif +// Visual Studio warnings #ifdef _MSC_VER +#pragma warning (disable: 4127) // condition expression is constant #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#define snprintf _snprintf #endif -#ifdef __clang__ +// Clang/GCC warnings with -Weverything +#if defined(__clang__) #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok. -#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. +#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference is. #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0 +#endif #if __has_warning("-Wcomma") #pragma clang diagnostic ignored "-Wcomma" // warning : possible misuse of comma operator here // #endif @@ -49,22 +69,27 @@ #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier // #endif #if __has_warning("-Wdouble-promotion") -#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function +#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. #endif #elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value -#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'xxxx' to type 'xxxx' casts away qualifiers +#pragma GCC diagnostic ignored "-Wstack-protector" // warning: stack protector not protecting local variables: variable length buffer +#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #endif //------------------------------------------------------------------------- -// STB libraries implementation +// [SECTION] STB libraries implementation //------------------------------------------------------------------------- -//#define IMGUI_STB_NAMESPACE ImGuiStb -//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION +// Compile time options: +//#define IMGUI_STB_NAMESPACE ImStb +//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" +//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION +//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION #ifdef IMGUI_STB_NAMESPACE namespace IMGUI_STB_NAMESPACE @@ -76,55 +101,76 @@ namespace IMGUI_STB_NAMESPACE #pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration #endif -#ifdef __clang__ +#if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" #pragma clang diagnostic ignored "-Wmissing-prototypes" #pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#pragma clang diagnostic ignored "-Wcast-qual" // warning : cast from 'const xxxx *' to 'xxx *' drops const qualifier // #endif -#ifdef __GNUC__ +#if defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtype-limits" // warning: comparison is always true due to limited range of data type [-Wtype-limits] +#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers #endif -#define STBRP_ASSERT(x) IM_ASSERT(x) +#ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) #ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION #define STBRP_STATIC +#define STBRP_ASSERT(x) IM_ASSERT(x) +#define STBRP_SORT ImQsort #define STB_RECT_PACK_IMPLEMENTATION #endif -#include "stb_rect_pack.h" +#ifdef IMGUI_STB_RECT_PACK_FILENAME +#include IMGUI_STB_RECT_PACK_FILENAME +#else +#include "imstb_rectpack.h" +#endif +#endif -#define STBTT_malloc(x,u) ((void)(u), ImGui::MemAlloc(x)) -#define STBTT_free(x,u) ((void)(u), ImGui::MemFree(x)) -#define STBTT_assert(x) IM_ASSERT(x) +#ifndef STB_TRUETYPE_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) #ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION +#define STBTT_malloc(x,u) ((void)(u), IM_ALLOC(x)) +#define STBTT_free(x,u) ((void)(u), IM_FREE(x)) +#define STBTT_assert(x) IM_ASSERT(x) +#define STBTT_fmod(x,y) ImFmod(x,y) +#define STBTT_sqrt(x) ImSqrt(x) +#define STBTT_pow(x,y) ImPow(x,y) +#define STBTT_fabs(x) ImFabs(x) +#define STBTT_ifloor(x) ((int)ImFloorStd(x)) +#define STBTT_iceil(x) ((int)ImCeil(x)) #define STBTT_STATIC #define STB_TRUETYPE_IMPLEMENTATION #else #define STBTT_DEF extern #endif -#include "stb_truetype.h" +#ifdef IMGUI_STB_TRUETYPE_FILENAME +#include IMGUI_STB_TRUETYPE_FILENAME +#else +#include "imstb_truetype.h" +#endif +#endif -#ifdef __GNUC__ +#if defined(__GNUC__) #pragma GCC diagnostic pop #endif -#ifdef __clang__ +#if defined(__clang__) #pragma clang diagnostic pop #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) #pragma warning (pop) #endif #ifdef IMGUI_STB_NAMESPACE -} // namespace ImGuiStb +} // namespace ImStb using namespace IMGUI_STB_NAMESPACE; #endif //----------------------------------------------------------------------------- -// Style functions +// [SECTION] Style functions //----------------------------------------------------------------------------- void ImGui::StyleColorsDark(ImGuiStyle* dst) @@ -135,7 +181,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.94f); - colors[ImGuiCol_ChildBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.00f); + colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f); colors[ImGuiCol_Border] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); @@ -165,18 +211,21 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); - colors[ImGuiCol_CloseButton] = ImVec4(0.41f, 0.41f, 0.41f, 0.50f); - colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f); - colors[ImGuiCol_CloseButtonActive] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f); + colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f); + colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; + colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); + colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); + colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); - colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); + colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); } void ImGui::StyleColorsClassic(ImGuiStyle* dst) @@ -211,24 +260,27 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f); colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f); colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f); - colors[ImGuiCol_Separator] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); + colors[ImGuiCol_Separator] = ImVec4(0.50f, 0.50f, 0.50f, 0.60f); colors[ImGuiCol_SeparatorHovered] = ImVec4(0.60f, 0.60f, 0.70f, 1.00f); colors[ImGuiCol_SeparatorActive] = ImVec4(0.70f, 0.70f, 0.90f, 1.00f); colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.16f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f); - colors[ImGuiCol_CloseButton] = ImVec4(0.50f, 0.50f, 0.90f, 0.50f); - colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.70f, 0.70f, 0.90f, 0.60f); - colors[ImGuiCol_CloseButtonActive] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f); + colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f); + colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; + colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); + colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); + colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); - colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); + colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); } // Those light colors are better suited with a thicker font than the default one + FrameBorder @@ -239,8 +291,6 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_Text] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); - //colors[ImGuiCol_TextHovered] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - //colors[ImGuiCol_TextActive] = ImVec4(1.00f, 1.00f, 0.00f, 1.00f); colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 1.00f); colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_PopupBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.98f); @@ -266,28 +316,31 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f); colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_Separator] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); + colors[ImGuiCol_Separator] = ImVec4(0.39f, 0.39f, 0.39f, 0.62f); colors[ImGuiCol_SeparatorHovered] = ImVec4(0.14f, 0.44f, 0.80f, 0.78f); colors[ImGuiCol_SeparatorActive] = ImVec4(0.14f, 0.44f, 0.80f, 1.00f); colors[ImGuiCol_ResizeGrip] = ImVec4(0.80f, 0.80f, 0.80f, 0.56f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); - colors[ImGuiCol_CloseButton] = ImVec4(0.59f, 0.59f, 0.59f, 0.50f); - colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f); - colors[ImGuiCol_CloseButtonActive] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f); + colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f); + colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; + colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); + colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); + colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.45f, 0.00f, 1.00f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); - colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f); + colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f); + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); } //----------------------------------------------------------------------------- -// ImDrawListData +// ImDrawList //----------------------------------------------------------------------------- ImDrawListSharedData::ImDrawListSharedData() @@ -296,34 +349,30 @@ ImDrawListSharedData::ImDrawListSharedData() FontSize = 0.0f; CurveTessellationTol = 0.0f; ClipRectFullscreen = ImVec4(-8192.0f, -8192.0f, +8192.0f, +8192.0f); - + InitialFlags = ImDrawListFlags_None; + // Const data for (int i = 0; i < IM_ARRAYSIZE(CircleVtx12); i++) { const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(CircleVtx12); - CircleVtx12[i] = ImVec2(cosf(a), sinf(a)); + CircleVtx12[i] = ImVec2(ImCos(a), ImSin(a)); } } -//----------------------------------------------------------------------------- -// ImDrawList -//----------------------------------------------------------------------------- - void ImDrawList::Clear() { CmdBuffer.resize(0); IdxBuffer.resize(0); VtxBuffer.resize(0); - Flags = ImDrawListFlags_AntiAliasedLines | ImDrawListFlags_AntiAliasedFill; + Flags = _Data ? _Data->InitialFlags : ImDrawListFlags_None; + _VtxCurrentOffset = 0; _VtxCurrentIdx = 0; _VtxWritePtr = NULL; _IdxWritePtr = NULL; _ClipRectStack.resize(0); _TextureIdStack.resize(0); _Path.resize(0); - _ChannelsCurrent = 0; - _ChannelsCount = 1; - // NB: Do not clear channels so our allocations are re-used after the first frame. + _Splitter.Clear(); } void ImDrawList::ClearFreeMemory() @@ -337,26 +386,30 @@ void ImDrawList::ClearFreeMemory() _ClipRectStack.clear(); _TextureIdStack.clear(); _Path.clear(); - _ChannelsCurrent = 0; - _ChannelsCount = 1; - for (int i = 0; i < _Channels.Size; i++) - { - if (i == 0) memset(&_Channels[0], 0, sizeof(_Channels[0])); // channel 0 is a copy of CmdBuffer/IdxBuffer, don't destruct again - _Channels[i].CmdBuffer.clear(); - _Channels[i].IdxBuffer.clear(); - } - _Channels.clear(); + _Splitter.ClearFreeMemory(); +} + +ImDrawList* ImDrawList::CloneOutput() const +{ + ImDrawList* dst = IM_NEW(ImDrawList(_Data)); + dst->CmdBuffer = CmdBuffer; + dst->IdxBuffer = IdxBuffer; + dst->VtxBuffer = VtxBuffer; + dst->Flags = Flags; + return dst; } // Using macros because C++ is a terrible language, we want guaranteed inline, no code in header, and no overhead in Debug builds #define GetCurrentClipRect() (_ClipRectStack.Size ? _ClipRectStack.Data[_ClipRectStack.Size-1] : _Data->ClipRectFullscreen) -#define GetCurrentTextureId() (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : NULL) +#define GetCurrentTextureId() (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : (ImTextureID)NULL) void ImDrawList::AddDrawCmd() { ImDrawCmd draw_cmd; draw_cmd.ClipRect = GetCurrentClipRect(); draw_cmd.TextureId = GetCurrentTextureId(); + draw_cmd.VtxOffset = _VtxCurrentOffset; + draw_cmd.IdxOffset = IdxBuffer.Size; IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w); CmdBuffer.push_back(draw_cmd); @@ -463,88 +516,17 @@ void ImDrawList::PopTextureID() UpdateTextureID(); } -void ImDrawList::ChannelsSplit(int channels_count) -{ - IM_ASSERT(_ChannelsCurrent == 0 && _ChannelsCount == 1); - int old_channels_count = _Channels.Size; - if (old_channels_count < channels_count) - _Channels.resize(channels_count); - _ChannelsCount = channels_count; - - // _Channels[] (24/32 bytes each) hold storage that we'll swap with this->_CmdBuffer/_IdxBuffer - // The content of _Channels[0] at this point doesn't matter. We clear it to make state tidy in a debugger but we don't strictly need to. - // When we switch to the next channel, we'll copy _CmdBuffer/_IdxBuffer into _Channels[0] and then _Channels[1] into _CmdBuffer/_IdxBuffer - memset(&_Channels[0], 0, sizeof(ImDrawChannel)); - for (int i = 1; i < channels_count; i++) - { - if (i >= old_channels_count) - { - IM_PLACEMENT_NEW(&_Channels[i]) ImDrawChannel(); - } - else - { - _Channels[i].CmdBuffer.resize(0); - _Channels[i].IdxBuffer.resize(0); - } - if (_Channels[i].CmdBuffer.Size == 0) - { - ImDrawCmd draw_cmd; - draw_cmd.ClipRect = _ClipRectStack.back(); - draw_cmd.TextureId = _TextureIdStack.back(); - _Channels[i].CmdBuffer.push_back(draw_cmd); - } - } -} - -void ImDrawList::ChannelsMerge() -{ - // Note that we never use or rely on channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use. - if (_ChannelsCount <= 1) - return; - - ChannelsSetCurrent(0); - if (CmdBuffer.Size && CmdBuffer.back().ElemCount == 0) - CmdBuffer.pop_back(); - - int new_cmd_buffer_count = 0, new_idx_buffer_count = 0; - for (int i = 1; i < _ChannelsCount; i++) - { - ImDrawChannel& ch = _Channels[i]; - if (ch.CmdBuffer.Size && ch.CmdBuffer.back().ElemCount == 0) - ch.CmdBuffer.pop_back(); - new_cmd_buffer_count += ch.CmdBuffer.Size; - new_idx_buffer_count += ch.IdxBuffer.Size; - } - CmdBuffer.resize(CmdBuffer.Size + new_cmd_buffer_count); - IdxBuffer.resize(IdxBuffer.Size + new_idx_buffer_count); - - ImDrawCmd* cmd_write = CmdBuffer.Data + CmdBuffer.Size - new_cmd_buffer_count; - _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size - new_idx_buffer_count; - for (int i = 1; i < _ChannelsCount; i++) - { - ImDrawChannel& ch = _Channels[i]; - if (int sz = ch.CmdBuffer.Size) { memcpy(cmd_write, ch.CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; } - if (int sz = ch.IdxBuffer.Size) { memcpy(_IdxWritePtr, ch.IdxBuffer.Data, sz * sizeof(ImDrawIdx)); _IdxWritePtr += sz; } - } - UpdateClipRect(); // We call this instead of AddDrawCmd(), so that empty channels won't produce an extra draw call. - _ChannelsCount = 1; -} - -void ImDrawList::ChannelsSetCurrent(int idx) -{ - IM_ASSERT(idx < _ChannelsCount); - if (_ChannelsCurrent == idx) return; - memcpy(&_Channels.Data[_ChannelsCurrent].CmdBuffer, &CmdBuffer, sizeof(CmdBuffer)); // copy 12 bytes, four times - memcpy(&_Channels.Data[_ChannelsCurrent].IdxBuffer, &IdxBuffer, sizeof(IdxBuffer)); - _ChannelsCurrent = idx; - memcpy(&CmdBuffer, &_Channels.Data[_ChannelsCurrent].CmdBuffer, sizeof(CmdBuffer)); - memcpy(&IdxBuffer, &_Channels.Data[_ChannelsCurrent].IdxBuffer, sizeof(IdxBuffer)); - _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size; -} - // NB: this can be called with negative count for removing primitives (as long as the result does not underflow) void ImDrawList::PrimReserve(int idx_count, int vtx_count) { + // Large mesh support (when enabled) + if (sizeof(ImDrawIdx) == 2 && (_VtxCurrentIdx + vtx_count >= (1 << 16)) && (Flags & ImDrawListFlags_AllowVtxOffset)) + { + _VtxCurrentOffset = VtxBuffer.Size; + _VtxCurrentIdx = 0; + AddDrawCmd(); + } + ImDrawCmd& draw_cmd = CmdBuffer.Data[CmdBuffer.Size-1]; draw_cmd.ElemCount += idx_count; @@ -602,7 +584,13 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c _IdxWritePtr += 6; } +// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superflous function calls to optimize debug/non-inlined builds. +// Those macros expects l-values. +#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } +#define IM_FIXNORMAL2F(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 < 0.5f) d2 = 0.5f; float inv_lensq = 1.0f / d2; VX *= inv_lensq; VY *= inv_lensq; } + // TODO: Thickness anti-aliased lines cap are missing their AA fringe. +// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds. void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, bool closed, float thickness) { if (points_count < 2) @@ -626,16 +614,17 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 PrimReserve(idx_count, vtx_count); // Temporary buffer - ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2)); + ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2)); //-V630 ImVec2* temp_points = temp_normals + points_count; for (int i1 = 0; i1 < count; i1++) { const int i2 = (i1+1) == points_count ? 0 : i1+1; - ImVec2 diff = points[i2] - points[i1]; - diff *= ImInvLength(diff, 1.0f); - temp_normals[i1].x = diff.y; - temp_normals[i1].y = -diff.x; + float dx = points[i2].x - points[i1].x; + float dy = points[i2].y - points[i1].y; + IM_NORMALIZE2F_OVER_ZERO(dx, dy); + temp_normals[i1].x = dy; + temp_normals[i1].y = -dx; } if (!closed) temp_normals[points_count-1] = temp_normals[points_count-2]; @@ -658,17 +647,18 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+3; // Average normals - ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f; - float dmr2 = dm.x*dm.x + dm.y*dm.y; - if (dmr2 > 0.000001f) - { - float scale = 1.0f / dmr2; - if (scale > 100.0f) scale = 100.0f; - dm *= scale; - } - dm *= AA_SIZE; - temp_points[i2*2+0] = points[i2] + dm; - temp_points[i2*2+1] = points[i2] - dm; + float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; + float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f; + IM_FIXNORMAL2F(dm_x, dm_y) + dm_x *= AA_SIZE; + dm_y *= AA_SIZE; + + // Add temporary vertexes + ImVec2* out_vtx = &temp_points[i2*2]; + out_vtx[0].x = points[i2].x + dm_x; + out_vtx[0].y = points[i2].y + dm_y; + out_vtx[1].x = points[i2].x - dm_x; + out_vtx[1].y = points[i2].y - dm_y; // Add indexes _IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); @@ -712,20 +702,24 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+4; // Average normals - ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f; - float dmr2 = dm.x*dm.x + dm.y*dm.y; - if (dmr2 > 0.000001f) - { - float scale = 1.0f / dmr2; - if (scale > 100.0f) scale = 100.0f; - dm *= scale; - } - ImVec2 dm_out = dm * (half_inner_thickness + AA_SIZE); - ImVec2 dm_in = dm * half_inner_thickness; - temp_points[i2*4+0] = points[i2] + dm_out; - temp_points[i2*4+1] = points[i2] + dm_in; - temp_points[i2*4+2] = points[i2] - dm_in; - temp_points[i2*4+3] = points[i2] - dm_out; + float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; + float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f; + IM_FIXNORMAL2F(dm_x, dm_y); + float dm_out_x = dm_x * (half_inner_thickness + AA_SIZE); + float dm_out_y = dm_y * (half_inner_thickness + AA_SIZE); + float dm_in_x = dm_x * half_inner_thickness; + float dm_in_y = dm_y * half_inner_thickness; + + // Add temporary vertexes + ImVec2* out_vtx = &temp_points[i2*4]; + out_vtx[0].x = points[i2].x + dm_out_x; + out_vtx[0].y = points[i2].y + dm_out_y; + out_vtx[1].x = points[i2].x + dm_in_x; + out_vtx[1].y = points[i2].y + dm_in_y; + out_vtx[2].x = points[i2].x - dm_in_x; + out_vtx[2].y = points[i2].y - dm_in_y; + out_vtx[3].x = points[i2].x - dm_out_x; + out_vtx[3].y = points[i2].y - dm_out_y; // Add indexes _IdxWritePtr[0] = (ImDrawIdx)(idx2+1); _IdxWritePtr[1] = (ImDrawIdx)(idx1+1); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); @@ -763,11 +757,13 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const int i2 = (i1+1) == points_count ? 0 : i1+1; const ImVec2& p1 = points[i1]; const ImVec2& p2 = points[i2]; - ImVec2 diff = p2 - p1; - diff *= ImInvLength(diff, 1.0f); - const float dx = diff.x * (thickness * 0.5f); - const float dy = diff.y * (thickness * 0.5f); + float dx = p2.x - p1.x; + float dy = p2.y - p1.y; + IM_NORMALIZE2F_OVER_ZERO(dx, dy); + dx *= (thickness * 0.5f); + dy *= (thickness * 0.5f); + _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; @@ -782,8 +778,12 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 } } +// We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds. void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col) { + if (points_count < 3) + return; + const ImVec2 uv = _Data->TexUvWhitePixel; if (Flags & ImDrawListFlags_AntiAliasedFill) @@ -805,15 +805,16 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun } // Compute normals - ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); + ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); //-V630 for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { const ImVec2& p0 = points[i0]; const ImVec2& p1 = points[i1]; - ImVec2 diff = p1 - p0; - diff *= ImInvLength(diff, 1.0f); - temp_normals[i0].x = diff.y; - temp_normals[i0].y = -diff.x; + float dx = p1.x - p0.x; + float dy = p1.y - p0.y; + IM_NORMALIZE2F_OVER_ZERO(dx, dy); + temp_normals[i0].x = dy; + temp_normals[i0].y = -dx; } for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) @@ -821,19 +822,15 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun // Average normals const ImVec2& n0 = temp_normals[i0]; const ImVec2& n1 = temp_normals[i1]; - ImVec2 dm = (n0 + n1) * 0.5f; - float dmr2 = dm.x*dm.x + dm.y*dm.y; - if (dmr2 > 0.000001f) - { - float scale = 1.0f / dmr2; - if (scale > 100.0f) scale = 100.0f; - dm *= scale; - } - dm *= AA_SIZE * 0.5f; + float dm_x = (n0.x + n1.x) * 0.5f; + float dm_y = (n0.y + n1.y) * 0.5f; + IM_FIXNORMAL2F(dm_x, dm_y); + dm_x *= AA_SIZE * 0.5f; + dm_y *= AA_SIZE * 0.5f; // Add vertices - _VtxWritePtr[0].pos = (points[i1] - dm); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner - _VtxWritePtr[1].pos = (points[i1] + dm); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer + _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner + _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer _VtxWritePtr += 2; // Add indexes for fringes @@ -885,11 +882,14 @@ void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float a_min, floa _Path.push_back(centre); return; } + + // Note that we are adding a point at both a_min and a_max. + // If you are trying to draw a full closed circle you don't want the overlapping points! _Path.reserve(_Path.Size + (num_segments + 1)); for (int i = 0; i <= num_segments; i++) { const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min); - _Path.push_back(ImVec2(centre.x + cosf(a) * radius, centre.y + sinf(a) * radius)); + _Path.push_back(ImVec2(centre.x + ImCos(a) * radius, centre.y + ImSin(a) * radius)); } } @@ -945,8 +945,8 @@ void ImDrawList::PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImV void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, int rounding_corners) { - rounding = ImMin(rounding, fabsf(b.x - a.x) * ( ((rounding_corners & ImDrawCornerFlags_Top) == ImDrawCornerFlags_Top) || ((rounding_corners & ImDrawCornerFlags_Bot) == ImDrawCornerFlags_Bot) ? 0.5f : 1.0f ) - 1.0f); - rounding = ImMin(rounding, fabsf(b.y - a.y) * ( ((rounding_corners & ImDrawCornerFlags_Left) == ImDrawCornerFlags_Left) || ((rounding_corners & ImDrawCornerFlags_Right) == ImDrawCornerFlags_Right) ? 0.5f : 1.0f ) - 1.0f); + rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((rounding_corners & ImDrawCornerFlags_Top) == ImDrawCornerFlags_Top) || ((rounding_corners & ImDrawCornerFlags_Bot) == ImDrawCornerFlags_Bot) ? 0.5f : 1.0f ) - 1.0f); + rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((rounding_corners & ImDrawCornerFlags_Left) == ImDrawCornerFlags_Left) || ((rounding_corners & ImDrawCornerFlags_Right) == ImDrawCornerFlags_Right) ? 0.5f : 1.0f ) - 1.0f); if (rounding <= 0.0f || rounding_corners == 0) { @@ -1068,21 +1068,23 @@ void ImDrawList::AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments, float thickness) { - if ((col & IM_COL32_A_MASK) == 0) + if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2) return; + // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments; - PathArcTo(centre, radius-0.5f, 0.0f, a_max, num_segments); + PathArcTo(centre, radius-0.5f, 0.0f, a_max, num_segments - 1); PathStroke(col, true, thickness); } void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments) { - if ((col & IM_COL32_A_MASK) == 0) + if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2) return; + // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments; - PathArcTo(centre, radius, 0.0f, a_max, num_segments); + PathArcTo(centre, radius, 0.0f, a_max, num_segments - 1); PathFillConvex(col); } @@ -1181,14 +1183,141 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& a, c PathRect(a, b, rounding, rounding_corners); PathFillConvex(col); int vert_end_idx = VtxBuffer.Size; - ImGui::ShadeVertsLinearUV(VtxBuffer.Data + vert_start_idx, VtxBuffer.Data + vert_end_idx, a, b, uv_a, uv_b, true); + ImGui::ShadeVertsLinearUV(this, vert_start_idx, vert_end_idx, a, b, uv_a, uv_b, true); if (push_texture_id) PopTextureID(); } + //----------------------------------------------------------------------------- -// ImDrawData +// ImDrawListSplitter +//----------------------------------------------------------------------------- +// FIXME: This may be a little confusing, trying to be a little too low-level/optimal instead of just doing vector swap.. +//----------------------------------------------------------------------------- + +void ImDrawListSplitter::ClearFreeMemory() +{ + for (int i = 0; i < _Channels.Size; i++) + { + if (i == _Current) + memset(&_Channels[i], 0, sizeof(_Channels[i])); // Current channel is a copy of CmdBuffer/IdxBuffer, don't destruct again + _Channels[i]._CmdBuffer.clear(); + _Channels[i]._IdxBuffer.clear(); + } + _Current = 0; + _Count = 1; + _Channels.clear(); +} + +void ImDrawListSplitter::Split(ImDrawList* draw_list, int channels_count) +{ + IM_ASSERT(_Current == 0 && _Count <= 1); + int old_channels_count = _Channels.Size; + if (old_channels_count < channels_count) + _Channels.resize(channels_count); + _Count = channels_count; + + // Channels[] (24/32 bytes each) hold storage that we'll swap with draw_list->_CmdBuffer/_IdxBuffer + // The content of Channels[0] at this point doesn't matter. We clear it to make state tidy in a debugger but we don't strictly need to. + // When we switch to the next channel, we'll copy draw_list->_CmdBuffer/_IdxBuffer into Channels[0] and then Channels[1] into draw_list->CmdBuffer/_IdxBuffer + memset(&_Channels[0], 0, sizeof(ImDrawChannel)); + for (int i = 1; i < channels_count; i++) + { + if (i >= old_channels_count) + { + IM_PLACEMENT_NEW(&_Channels[i]) ImDrawChannel(); + } + else + { + _Channels[i]._CmdBuffer.resize(0); + _Channels[i]._IdxBuffer.resize(0); + } + if (_Channels[i]._CmdBuffer.Size == 0) + { + ImDrawCmd draw_cmd; + draw_cmd.ClipRect = draw_list->_ClipRectStack.back(); + draw_cmd.TextureId = draw_list->_TextureIdStack.back(); + _Channels[i]._CmdBuffer.push_back(draw_cmd); + } + } +} + +static inline bool CanMergeDrawCommands(ImDrawCmd* a, ImDrawCmd* b) +{ + return memcmp(&a->ClipRect, &b->ClipRect, sizeof(a->ClipRect)) == 0 && a->TextureId == b->TextureId && a->VtxOffset == b->VtxOffset && !a->UserCallback && !b->UserCallback; +} + +void ImDrawListSplitter::Merge(ImDrawList* draw_list) +{ + // Note that we never use or rely on channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use. + if (_Count <= 1) + return; + + SetCurrentChannel(draw_list, 0); + if (draw_list->CmdBuffer.Size != 0 && draw_list->CmdBuffer.back().ElemCount == 0) + draw_list->CmdBuffer.pop_back(); + + // Calculate our final buffer sizes. Also fix the incorrect IdxOffset values in each command. + int new_cmd_buffer_count = 0; + int new_idx_buffer_count = 0; + ImDrawCmd* last_cmd = (_Count > 0 && draw_list->CmdBuffer.Size > 0) ? &draw_list->CmdBuffer.back() : NULL; + int idx_offset = last_cmd ? last_cmd->IdxOffset + last_cmd->ElemCount : 0; + for (int i = 1; i < _Count; i++) + { + ImDrawChannel& ch = _Channels[i]; + if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0) + ch._CmdBuffer.pop_back(); + if (ch._CmdBuffer.Size > 0 && last_cmd != NULL && CanMergeDrawCommands(last_cmd, &ch._CmdBuffer[0])) + { + // Merge previous channel last draw command with current channel first draw command if matching. + last_cmd->ElemCount += ch._CmdBuffer[0].ElemCount; + idx_offset += ch._CmdBuffer[0].ElemCount; + ch._CmdBuffer.erase(ch._CmdBuffer.Data); + } + if (ch._CmdBuffer.Size > 0) + last_cmd = &ch._CmdBuffer.back(); + new_cmd_buffer_count += ch._CmdBuffer.Size; + new_idx_buffer_count += ch._IdxBuffer.Size; + for (int cmd_n = 0; cmd_n < ch._CmdBuffer.Size; cmd_n++) + { + ch._CmdBuffer.Data[cmd_n].IdxOffset = idx_offset; + idx_offset += ch._CmdBuffer.Data[cmd_n].ElemCount; + } + } + draw_list->CmdBuffer.resize(draw_list->CmdBuffer.Size + new_cmd_buffer_count); + draw_list->IdxBuffer.resize(draw_list->IdxBuffer.Size + new_idx_buffer_count); + + // Write commands and indices in order (they are fairly small structures, we don't copy vertices only indices) + ImDrawCmd* cmd_write = draw_list->CmdBuffer.Data + draw_list->CmdBuffer.Size - new_cmd_buffer_count; + ImDrawIdx* idx_write = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size - new_idx_buffer_count; + for (int i = 1; i < _Count; i++) + { + ImDrawChannel& ch = _Channels[i]; + if (int sz = ch._CmdBuffer.Size) { memcpy(cmd_write, ch._CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; } + if (int sz = ch._IdxBuffer.Size) { memcpy(idx_write, ch._IdxBuffer.Data, sz * sizeof(ImDrawIdx)); idx_write += sz; } + } + draw_list->_IdxWritePtr = idx_write; + draw_list->UpdateClipRect(); // We call this instead of AddDrawCmd(), so that empty channels won't produce an extra draw call. + _Count = 1; +} + +void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx) +{ + IM_ASSERT(idx >= 0 && idx < _Count); + if (_Current == idx) + return; + // Overwrite ImVector (12/16 bytes), four times. This is merely a silly optimization instead of doing .swap() + memcpy(&_Channels.Data[_Current]._CmdBuffer, &draw_list->CmdBuffer, sizeof(draw_list->CmdBuffer)); + memcpy(&_Channels.Data[_Current]._IdxBuffer, &draw_list->IdxBuffer, sizeof(draw_list->IdxBuffer)); + _Current = idx; + memcpy(&draw_list->CmdBuffer, &_Channels.Data[idx]._CmdBuffer, sizeof(draw_list->CmdBuffer)); + memcpy(&draw_list->IdxBuffer, &_Channels.Data[idx]._IdxBuffer, sizeof(draw_list->IdxBuffer)); + draw_list->_IdxWritePtr = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size; +} + +//----------------------------------------------------------------------------- +// [SECTION] ImDrawData //----------------------------------------------------------------------------- // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! @@ -1210,8 +1339,10 @@ void ImDrawData::DeIndexAllBuffers() } } -// Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. -void ImDrawData::ScaleClipRects(const ImVec2& scale) +// Helper to scale the ClipRect field of each ImDrawCmd. +// Use if your final output buffer is at a different scale than draw_data->DisplaySize, +// or if there is a difference between your window resolution and framebuffer resolution. +void ImDrawData::ScaleClipRects(const ImVec2& fb_scale) { for (int i = 0; i < CmdListsCount; i++) { @@ -1219,20 +1350,22 @@ void ImDrawData::ScaleClipRects(const ImVec2& scale) for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i]; - cmd->ClipRect = ImVec4(cmd->ClipRect.x * scale.x, cmd->ClipRect.y * scale.y, cmd->ClipRect.z * scale.x, cmd->ClipRect.w * scale.y); + cmd->ClipRect = ImVec4(cmd->ClipRect.x * fb_scale.x, cmd->ClipRect.y * fb_scale.y, cmd->ClipRect.z * fb_scale.x, cmd->ClipRect.w * fb_scale.y); } } } //----------------------------------------------------------------------------- -// Shade functions +// [SECTION] Helpers ShadeVertsXXX functions //----------------------------------------------------------------------------- // Generic linear color gradient, write to RGB fields, leave A untouched. -void ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawVert* vert_start, ImDrawVert* vert_end, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1) +void ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1) { ImVec2 gradient_extent = gradient_p1 - gradient_p0; float gradient_inv_length2 = 1.0f / ImLengthSqr(gradient_extent); + ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx; + ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx; for (ImDrawVert* vert = vert_start; vert < vert_end; vert++) { float d = ImDot(vert->pos - gradient_p0, gradient_extent); @@ -1244,25 +1377,8 @@ void ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawVert* vert_start, ImDra } } -// Scan and shade backward from the end of given vertices. Assume vertices are text only (= vert_start..vert_end going left to right) so we can break as soon as we are out the gradient bounds. -void ImGui::ShadeVertsLinearAlphaGradientForLeftToRightText(ImDrawVert* vert_start, ImDrawVert* vert_end, float gradient_p0_x, float gradient_p1_x) -{ - float gradient_extent_x = gradient_p1_x - gradient_p0_x; - float gradient_inv_length2 = 1.0f / (gradient_extent_x * gradient_extent_x); - int full_alpha_count = 0; - for (ImDrawVert* vert = vert_end - 1; vert >= vert_start; vert--) - { - float d = (vert->pos.x - gradient_p0_x) * (gradient_extent_x); - float alpha_mul = 1.0f - ImClamp(d * gradient_inv_length2, 0.0f, 1.0f); - if (alpha_mul >= 1.0f && ++full_alpha_count > 2) - return; // Early out - int a = (int)(((vert->col >> IM_COL32_A_SHIFT) & 0xFF) * alpha_mul); - vert->col = (vert->col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); - } -} - // Distribute UV over (a, b) rectangle -void ImGui::ShadeVertsLinearUV(ImDrawVert* vert_start, ImDrawVert* vert_end, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp) +void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp) { const ImVec2 size = b - a; const ImVec2 uv_size = uv_b - uv_a; @@ -1270,11 +1386,12 @@ void ImGui::ShadeVertsLinearUV(ImDrawVert* vert_start, ImDrawVert* vert_end, con size.x != 0.0f ? (uv_size.x / size.x) : 0.0f, size.y != 0.0f ? (uv_size.y / size.y) : 0.0f); + ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx; + ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx; if (clamp) { const ImVec2 min = ImMin(uv_a, uv_b); const ImVec2 max = ImMax(uv_a, uv_b); - for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex) vertex->uv = ImClamp(uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale), min, max); } @@ -1286,7 +1403,7 @@ void ImGui::ShadeVertsLinearUV(ImDrawVert* vert_start, ImDrawVert* vert_end, con } //----------------------------------------------------------------------------- -// ImFontConfig +// [SECTION] ImFontConfig //----------------------------------------------------------------------------- ImFontConfig::ImFontConfig() @@ -1296,12 +1413,14 @@ ImFontConfig::ImFontConfig() FontDataOwnedByAtlas = true; FontNo = 0; SizePixels = 0.0f; - OversampleH = 3; + OversampleH = 3; // FIXME: 2 may be a better default? OversampleV = 1; PixelSnapH = false; GlyphExtraSpacing = ImVec2(0.0f, 0.0f); GlyphOffset = ImVec2(0.0f, 0.0f); GlyphRanges = NULL; + GlyphMinAdvanceX = 0.0f; + GlyphMaxAdvanceX = FLT_MAX; MergeMode = false; RasterizerFlags = 0x00; RasterizerMultiply = 1.0f; @@ -1310,61 +1429,63 @@ ImFontConfig::ImFontConfig() } //----------------------------------------------------------------------------- -// ImFontAtlas +// [SECTION] ImFontAtlas //----------------------------------------------------------------------------- // A work of art lies ahead! (. = white layer, X = black layer, others are blank) -// The white texels on the top left are the ones we'll use everywhere in ImGui to render filled shapes. -const int FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF = 90; +// The white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes. +const int FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF = 108; const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27; const unsigned int FONT_ATLAS_DEFAULT_TEX_DATA_ID = 0x80000000; static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] = { - "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX" - "..- -X.....X- X.X - X.X -X.....X - X.....X" - "--- -XXX.XXX- X...X - X...X -X....X - X....X" - "X - X.X - X.....X - X.....X -X...X - X...X" - "XX - X.X -X.......X- X.......X -X..X.X - X.X..X" - "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X" - "X..X - X.X - X.X - X.X -XX X.X - X.X XX" - "X...X - X.X - X.X - XX X.X XX - X.X - X.X " - "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X " - "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X " - "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X " - "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X " - "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X " - "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X " - "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X " - "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X " - "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX " - "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------" - "X.X X..X - -X.......X- X.......X - XX XX - " - "XX X..X - - X.....X - X.....X - X.X X.X - " - " X..X - X...X - X...X - X..X X..X - " - " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - " - "------------ - X - X -X.....................X- " - " ----------------------------------- X...XXXXXXXXXXXXX...X - " - " - X..X X..X - " - " - X.X X.X - " - " - XX XX - " + "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX " + "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X " + "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X " + "X - X.X - X.....X - X.....X -X...X - X...X- X..X " + "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X " + "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX " + "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX " + "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX " + "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X " + "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X" + "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X" + "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X" + "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X" + "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X" + "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X" + "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X" + "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X " + "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X " + "X.X X..X - -X.......X- X.......X - XX XX - - X..........X " + "XX X..X - - X.....X - X.....X - X.X X.X - - X........X " + " X..X - X...X - X...X - X..X X..X - - X........X " + " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX " + "------------ - X - X -X.....................X- ------------------" + " ----------------------------------- X...XXXXXXXXXXXXX...X - " + " - X..X X..X - " + " - X.X X.X - " + " - XX XX - " }; static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3] = { // Pos ........ Size ......... Offset ...... - { ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow - { ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput + { ImVec2( 0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow + { ImVec2(13,0), ImVec2( 7,16), ImVec2( 1, 8) }, // ImGuiMouseCursor_TextInput { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_ResizeAll - { ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS - { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW - { ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW - { ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE + { ImVec2(21,0), ImVec2( 9,23), ImVec2( 4,11) }, // ImGuiMouseCursor_ResizeNS + { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 4) }, // ImGuiMouseCursor_ResizeEW + { ImVec2(73,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNESW + { ImVec2(55,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNWSE + { ImVec2(91,0), ImVec2(17,22), ImVec2( 5, 0) }, // ImGuiMouseCursor_Hand }; ImFontAtlas::ImFontAtlas() { - Flags = 0x00; - TexID = NULL; + Locked = false; + Flags = ImFontAtlasFlags_None; + TexID = (ImTextureID)NULL; TexDesiredWidth = 0; TexGlyphPadding = 1; @@ -1379,15 +1500,17 @@ ImFontAtlas::ImFontAtlas() ImFontAtlas::~ImFontAtlas() { + IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); Clear(); } void ImFontAtlas::ClearInputData() { + IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); for (int i = 0; i < ConfigData.Size; i++) if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas) { - ImGui::MemFree(ConfigData[i].FontData); + IM_FREE(ConfigData[i].FontData); ConfigData[i].FontData = NULL; } @@ -1406,16 +1529,18 @@ void ImFontAtlas::ClearInputData() void ImFontAtlas::ClearTexData() { + IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); if (TexPixelsAlpha8) - ImGui::MemFree(TexPixelsAlpha8); + IM_FREE(TexPixelsAlpha8); if (TexPixelsRGBA32) - ImGui::MemFree(TexPixelsRGBA32); + IM_FREE(TexPixelsRGBA32); TexPixelsAlpha8 = NULL; TexPixelsRGBA32 = NULL; } void ImFontAtlas::ClearFonts() { + IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); for (int i = 0; i < Fonts.Size; i++) IM_DELETE(Fonts[i]); Fonts.clear(); @@ -1454,7 +1579,7 @@ void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_wid GetTexDataAsAlpha8(&pixels, NULL, NULL); if (pixels) { - TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc((size_t)(TexWidth * TexHeight * 4)); + TexPixelsRGBA32 = (unsigned int*)IM_ALLOC((size_t)TexWidth * (size_t)TexHeight * 4); const unsigned char* src = pixels; unsigned int* dst = TexPixelsRGBA32; for (int n = TexWidth * TexHeight; n > 0; n--) @@ -1470,6 +1595,7 @@ void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_wid ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) { + IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0); IM_ASSERT(font_cfg->SizePixels > 0.0f); @@ -1477,15 +1603,15 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) if (!font_cfg->MergeMode) Fonts.push_back(IM_NEW(ImFont)); else - IM_ASSERT(!Fonts.empty()); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font. + IM_ASSERT(!Fonts.empty() && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font. ConfigData.push_back(*font_cfg); ImFontConfig& new_font_cfg = ConfigData.back(); - if (!new_font_cfg.DstFont) + if (new_font_cfg.DstFont == NULL) new_font_cfg.DstFont = Fonts.back(); if (!new_font_cfg.FontDataOwnedByAtlas) { - new_font_cfg.FontData = ImGui::MemAlloc(new_font_cfg.FontDataSize); + new_font_cfg.FontData = IM_ALLOC(new_font_cfg.FontDataSize); new_font_cfg.FontDataOwnedByAtlas = true; memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize); } @@ -1496,8 +1622,8 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) } // Default font TTF is compressed with stb_compress then base85 encoded (see misc/fonts/binary_to_compressed_c.cpp for encoder) -static unsigned int stb_decompress_length(unsigned char *input); -static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsigned int length); +static unsigned int stb_decompress_length(const unsigned char *input); +static unsigned int stb_decompress(unsigned char *output, const unsigned char *input, unsigned int length); static const char* GetDefaultCompressedFontDataTTFBase85(); static unsigned int Decode85Byte(char c) { return c >= '\\' ? c-36 : c-35; } static void Decode85(const unsigned char* src, unsigned char* dst) @@ -1520,17 +1646,22 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template) font_cfg.OversampleH = font_cfg.OversampleV = 1; font_cfg.PixelSnapH = true; } - if (font_cfg.Name[0] == '\0') strcpy(font_cfg.Name, "ProggyClean.ttf, 13px"); - if (font_cfg.SizePixels <= 0.0f) font_cfg.SizePixels = 13.0f; + if (font_cfg.SizePixels <= 0.0f) + font_cfg.SizePixels = 13.0f * 1.0f; + if (font_cfg.Name[0] == '\0') + ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels); const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85(); - ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, GetGlyphRangesDefault()); + const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault(); + ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, glyph_ranges); + font->DisplayOffset.y = 1.0f; return font; } ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) { - int data_size = 0; + IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); + size_t data_size = 0; void* data = ImFileLoadToMemory(filename, "rb", &data_size, 0); if (!data) { @@ -1543,14 +1674,15 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, // Store a short copy of filename into into the font name for convenience const char* p; for (p = filename + strlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {} - snprintf(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s, %.0fpx", p, size_pixels); + ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s, %.0fpx", p, size_pixels); } - return AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg, glyph_ranges); + return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges); } // NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build(). ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) { + IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); IM_ASSERT(font_cfg.FontData == NULL); font_cfg.FontData = ttf_data; @@ -1563,9 +1695,9 @@ ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float si ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) { - const unsigned int buf_decompressed_size = stb_decompress_length((unsigned char*)compressed_ttf_data); - unsigned char* buf_decompressed_data = (unsigned char *)ImGui::MemAlloc(buf_decompressed_size); - stb_decompress(buf_decompressed_data, (unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size); + const unsigned int buf_decompressed_size = stb_decompress_length((const unsigned char*)compressed_ttf_data); + unsigned char* buf_decompressed_data = (unsigned char *)IM_ALLOC(buf_decompressed_size); + stb_decompress(buf_decompressed_data, (const unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size); ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); IM_ASSERT(font_cfg.FontData == NULL); @@ -1576,10 +1708,10 @@ ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_d ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges) { int compressed_ttf_size = (((int)strlen(compressed_ttf_data_base85) + 4) / 5) * 4; - void* compressed_ttf = ImGui::MemAlloc((size_t)compressed_ttf_size); + void* compressed_ttf = IM_ALLOC((size_t)compressed_ttf_size); Decode85((const unsigned char*)compressed_ttf_data_base85, (unsigned char*)compressed_ttf); ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges); - ImGui::MemFree(compressed_ttf); + IM_FREE(compressed_ttf); return font; } @@ -1627,6 +1759,7 @@ bool ImFontAtlas::GetMouseCursorTexData(ImGuiMouseCursor cursor_type, ImVec2* ou if (Flags & ImFontAtlasFlags_NoMouseCursors) return false; + IM_ASSERT(CustomRectIds[0] != -1); ImFontAtlas::CustomRect& r = CustomRects[CustomRectIds[0]]; IM_ASSERT(r.ID == FONT_ATLAS_DEFAULT_TEX_DATA_ID); ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][0] + ImVec2((float)r.X, (float)r.Y); @@ -1643,6 +1776,7 @@ bool ImFontAtlas::GetMouseCursorTexData(ImGuiMouseCursor cursor_type, ImVec2* ou bool ImFontAtlas::Build() { + IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); return ImFontAtlasBuildWithStbTruetype(this); } @@ -1663,199 +1797,296 @@ void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsig data[i] = table[data[i]]; } +// Temporary data for one source font (multiple source fonts can be merged into one destination ImFont) +// (C++03 doesn't allow instancing ImVector<> with function-local types so we declare the type here.) +struct ImFontBuildSrcData +{ + stbtt_fontinfo FontInfo; + stbtt_pack_range PackRange; // Hold the list of codepoints to pack (essentially points to Codepoints.Data) + stbrp_rect* Rects; // Rectangle to pack. We first fill in their size and the packer will give us their position. + stbtt_packedchar* PackedChars; // Output glyphs + const ImWchar* SrcRanges; // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF) + int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[] + int GlyphsHighest; // Highest requested codepoint + int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font) + ImBoolVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB) + ImVector GlyphsList; // Glyph codepoints list (flattened version of GlyphsMap) +}; + +// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont) +struct ImFontBuildDstData +{ + int SrcCount; // Number of source fonts targeting this destination font. + int GlyphsHighest; + int GlyphsCount; + ImBoolVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font. +}; + +static void UnpackBoolVectorToFlatIndexList(const ImBoolVector* in, ImVector* out) +{ + IM_ASSERT(sizeof(in->Storage.Data[0]) == sizeof(int)); + const int* it_begin = in->Storage.begin(); + const int* it_end = in->Storage.end(); + for (const int* it = it_begin; it < it_end; it++) + if (int entries_32 = *it) + for (int bit_n = 0; bit_n < 32; bit_n++) + if (entries_32 & (1u << bit_n)) + out->push_back((int)((it - it_begin) << 5) + bit_n); +} + bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) { IM_ASSERT(atlas->ConfigData.Size > 0); ImFontAtlasBuildRegisterDefaultCustomRects(atlas); - atlas->TexID = NULL; + // Clear atlas + atlas->TexID = (ImTextureID)NULL; atlas->TexWidth = atlas->TexHeight = 0; atlas->TexUvScale = ImVec2(0.0f, 0.0f); atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f); atlas->ClearTexData(); - // Count glyphs/ranges - int total_glyphs_count = 0; - int total_ranges_count = 0; - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) + // Temporary storage for building + ImVector src_tmp_array; + ImVector dst_tmp_array; + src_tmp_array.resize(atlas->ConfigData.Size); + dst_tmp_array.resize(atlas->Fonts.Size); + memset(src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes()); + memset(dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes()); + + // 1. Initialize font loading structure, check font data validity + for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++) { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - if (!cfg.GlyphRanges) - cfg.GlyphRanges = atlas->GetGlyphRangesDefault(); - for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, total_ranges_count++) - total_glyphs_count += (in_range[1] - in_range[0]) + 1; - } - - // We need a width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish. - // Width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. - atlas->TexWidth = (atlas->TexDesiredWidth > 0) ? atlas->TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512; - atlas->TexHeight = 0; - - // Start packing - const int max_tex_height = 1024*32; - stbtt_pack_context spc = {}; - if (!stbtt_PackBegin(&spc, NULL, atlas->TexWidth, max_tex_height, 0, atlas->TexGlyphPadding, NULL)) - return false; - stbtt_PackSetOversampling(&spc, 1, 1); - - // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). - ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info); - - // Initialize font information (so we can error without any cleanup) - struct ImFontTempBuildData - { - stbtt_fontinfo FontInfo; - stbrp_rect* Rects; - int RectsCount; - stbtt_pack_range* Ranges; - int RangesCount; - }; - ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)atlas->ConfigData.Size * sizeof(ImFontTempBuildData)); - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) - { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + ImFontConfig& cfg = atlas->ConfigData[src_i]; IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas)); - const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); - IM_ASSERT(font_offset >= 0); - if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) - { - atlas->TexWidth = atlas->TexHeight = 0; // Reset output on failure - ImGui::MemFree(tmp_array); + // Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices) + src_tmp.DstIndex = -1; + for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++) + if (cfg.DstFont == atlas->Fonts[output_i]) + src_tmp.DstIndex = output_i; + IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array? + if (src_tmp.DstIndex == -1) return false; - } + + // Initialize helper structure for font loading and verify that the TTF/OTF data is correct + const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); + IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found."); + if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) + return false; + + // Measure highest codepoints + ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex]; + src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault(); + for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) + src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]); + dst_tmp.SrcCount++; + dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest); } + // 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs. + int total_glyphs_count = 0; + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) + { + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex]; + src_tmp.GlyphsSet.Resize(src_tmp.GlyphsHighest + 1); + if (dst_tmp.GlyphsSet.Storage.empty()) + dst_tmp.GlyphsSet.Resize(dst_tmp.GlyphsHighest + 1); + + for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) + for (int codepoint = src_range[0]; codepoint <= src_range[1]; codepoint++) + { + if (dst_tmp.GlyphsSet.GetBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option for MergeMode (e.g. MergeOverwrite==true) + continue; + if (!stbtt_FindGlyphIndex(&src_tmp.FontInfo, codepoint)) // It is actually in the font? + continue; + + // Add to avail set/counters + src_tmp.GlyphsCount++; + dst_tmp.GlyphsCount++; + src_tmp.GlyphsSet.SetBit(codepoint, true); + dst_tmp.GlyphsSet.SetBit(codepoint, true); + total_glyphs_count++; + } + } + + // 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another) + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) + { + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount); + UnpackBoolVectorToFlatIndexList(&src_tmp.GlyphsSet, &src_tmp.GlyphsList); + src_tmp.GlyphsSet.Clear(); + IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount); + } + for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++) + dst_tmp_array[dst_i].GlyphsSet.Clear(); + dst_tmp_array.clear(); + // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) - int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0; - stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbtt_packedchar)); - stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbrp_rect)); - stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_ranges_count * sizeof(stbtt_pack_range)); - memset(buf_packedchars, 0, total_glyphs_count * sizeof(stbtt_packedchar)); - memset(buf_rects, 0, total_glyphs_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity. - memset(buf_ranges, 0, total_ranges_count * sizeof(stbtt_pack_range)); + // (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity) + ImVector buf_rects; + ImVector buf_packedchars; + buf_rects.resize(total_glyphs_count); + buf_packedchars.resize(total_glyphs_count); + memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes()); + memset(buf_packedchars.Data, 0, (size_t)buf_packedchars.size_in_bytes()); - // First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point) - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) + // 4. Gather glyphs sizes so we can pack them in our virtual canvas. + int total_surface = 0; + int buf_rects_out_n = 0; + int buf_packedchars_out_n = 0; + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + if (src_tmp.GlyphsCount == 0) + continue; - // Setup ranges - int font_glyphs_count = 0; - int font_ranges_count = 0; - for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, font_ranges_count++) - font_glyphs_count += (in_range[1] - in_range[0]) + 1; - tmp.Ranges = buf_ranges + buf_ranges_n; - tmp.RangesCount = font_ranges_count; - buf_ranges_n += font_ranges_count; - for (int i = 0; i < font_ranges_count; i++) + src_tmp.Rects = &buf_rects[buf_rects_out_n]; + src_tmp.PackedChars = &buf_packedchars[buf_packedchars_out_n]; + buf_rects_out_n += src_tmp.GlyphsCount; + buf_packedchars_out_n += src_tmp.GlyphsCount; + + // Convert our ranges in the format stb_truetype wants + ImFontConfig& cfg = atlas->ConfigData[src_i]; + src_tmp.PackRange.font_size = cfg.SizePixels; + src_tmp.PackRange.first_unicode_codepoint_in_range = 0; + src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data; + src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size; + src_tmp.PackRange.chardata_for_range = src_tmp.PackedChars; + src_tmp.PackRange.h_oversample = (unsigned char)cfg.OversampleH; + src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV; + + // Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects) + const float scale = (cfg.SizePixels > 0) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels); + const int padding = atlas->TexGlyphPadding; + for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++) { - const ImWchar* in_range = &cfg.GlyphRanges[i * 2]; - stbtt_pack_range& range = tmp.Ranges[i]; - range.font_size = cfg.SizePixels; - range.first_unicode_codepoint_in_range = in_range[0]; - range.num_chars = (in_range[1] - in_range[0]) + 1; - range.chardata_for_range = buf_packedchars + buf_packedchars_n; - buf_packedchars_n += range.num_chars; + int x0, y0, x1, y1; + const int glyph_index_in_font = stbtt_FindGlyphIndex(&src_tmp.FontInfo, src_tmp.GlyphsList[glyph_i]); + IM_ASSERT(glyph_index_in_font != 0); + stbtt_GetGlyphBitmapBoxSubpixel(&src_tmp.FontInfo, glyph_index_in_font, scale * cfg.OversampleH, scale * cfg.OversampleV, 0, 0, &x0, &y0, &x1, &y1); + src_tmp.Rects[glyph_i].w = (stbrp_coord)(x1 - x0 + padding + cfg.OversampleH - 1); + src_tmp.Rects[glyph_i].h = (stbrp_coord)(y1 - y0 + padding + cfg.OversampleV - 1); + total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h; } - - // Pack - tmp.Rects = buf_rects + buf_rects_n; - tmp.RectsCount = font_glyphs_count; - buf_rects_n += font_glyphs_count; - stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); - int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); - IM_ASSERT(n == font_glyphs_count); - stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n); - - // Extend texture height - for (int i = 0; i < n; i++) - if (tmp.Rects[i].was_packed) - atlas->TexHeight = ImMax(atlas->TexHeight, tmp.Rects[i].y + tmp.Rects[i].h); } - IM_ASSERT(buf_rects_n == total_glyphs_count); - IM_ASSERT(buf_packedchars_n == total_glyphs_count); - IM_ASSERT(buf_ranges_n == total_ranges_count); - // Create texture + // We need a width for the skyline algorithm, any width! + // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. + // User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface. + const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1; + atlas->TexHeight = 0; + if (atlas->TexDesiredWidth > 0) + atlas->TexWidth = atlas->TexDesiredWidth; + else + atlas->TexWidth = (surface_sqrt >= 4096*0.7f) ? 4096 : (surface_sqrt >= 2048*0.7f) ? 2048 : (surface_sqrt >= 1024*0.7f) ? 1024 : 512; + + // 5. Start packing + // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). + const int TEX_HEIGHT_MAX = 1024 * 32; + stbtt_pack_context spc = {}; + stbtt_PackBegin(&spc, NULL, atlas->TexWidth, TEX_HEIGHT_MAX, 0, atlas->TexGlyphPadding, NULL); + ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info); + + // 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point. + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) + { + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + if (src_tmp.GlyphsCount == 0) + continue; + + stbrp_pack_rects((stbrp_context*)spc.pack_info, src_tmp.Rects, src_tmp.GlyphsCount); + + // Extend texture height and mark missing glyphs as non-packed so we won't render them. + // FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?) + for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++) + if (src_tmp.Rects[glyph_i].was_packed) + atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h); + } + + // 7. Allocate texture atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight); atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); - atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight); + atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight); memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight); spc.pixels = atlas->TexPixelsAlpha8; spc.height = atlas->TexHeight; - // Second pass: render font characters - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) + // 8. Render/rasterize font characters into the texture + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; - stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); - stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); + ImFontConfig& cfg = atlas->ConfigData[src_i]; + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + if (src_tmp.GlyphsCount == 0) + continue; + + stbtt_PackFontRangesRenderIntoRects(&spc, &src_tmp.FontInfo, &src_tmp.PackRange, 1, src_tmp.Rects); + + // Apply multiply operator if (cfg.RasterizerMultiply != 1.0f) { unsigned char multiply_table[256]; ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply); - for (const stbrp_rect* r = tmp.Rects; r != tmp.Rects + tmp.RectsCount; r++) + stbrp_rect* r = &src_tmp.Rects[0]; + for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++, r++) if (r->was_packed) - ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, spc.pixels, r->x, r->y, r->w, r->h, spc.stride_in_bytes); + ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, atlas->TexPixelsAlpha8, r->x, r->y, r->w, r->h, atlas->TexWidth * 1); } - tmp.Rects = NULL; + src_tmp.Rects = NULL; } // End packing stbtt_PackEnd(&spc); - ImGui::MemFree(buf_rects); - buf_rects = NULL; + buf_rects.clear(); - // Third pass: setup ImFont and glyphs for runtime - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) + // 9. Setup ImFont and glyphs for runtime + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + if (src_tmp.GlyphsCount == 0) + continue; + + ImFontConfig& cfg = atlas->ConfigData[src_i]; ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true) - const float font_scale = stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels); + const float font_scale = stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels); int unscaled_ascent, unscaled_descent, unscaled_line_gap; - stbtt_GetFontVMetrics(&tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); + stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); - const float ascent = unscaled_ascent * font_scale; - const float descent = unscaled_descent * font_scale; + const float ascent = ImFloor(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1)); + const float descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1)); ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent); - const float off_x = cfg.GlyphOffset.x; - const float off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f); + const float font_off_x = cfg.GlyphOffset.x; + const float font_off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f); - for (int i = 0; i < tmp.RangesCount; i++) + for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++) { - stbtt_pack_range& range = tmp.Ranges[i]; - for (int char_idx = 0; char_idx < range.num_chars; char_idx += 1) - { - const stbtt_packedchar& pc = range.chardata_for_range[char_idx]; - if (!pc.x0 && !pc.x1 && !pc.y0 && !pc.y1) - continue; + const int codepoint = src_tmp.GlyphsList[glyph_i]; + const stbtt_packedchar& pc = src_tmp.PackedChars[glyph_i]; - const int codepoint = range.first_unicode_codepoint_in_range + char_idx; - if (cfg.MergeMode && dst_font->FindGlyph((unsigned short)codepoint)) - continue; + const float char_advance_x_org = pc.xadvance; + const float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX); + float char_off_x = font_off_x; + if (char_advance_x_org != char_advance_x_mod) + char_off_x += cfg.PixelSnapH ? (float)(int)((char_advance_x_mod - char_advance_x_org) * 0.5f) : (char_advance_x_mod - char_advance_x_org) * 0.5f; - stbtt_aligned_quad q; - float dummy_x = 0.0f, dummy_y = 0.0f; - stbtt_GetPackedQuad(range.chardata_for_range, atlas->TexWidth, atlas->TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0); - dst_font->AddGlyph((ImWchar)codepoint, q.x0 + off_x, q.y0 + off_y, q.x1 + off_x, q.y1 + off_y, q.s0, q.t0, q.s1, q.t1, pc.xadvance); - } + // Register glyph + stbtt_aligned_quad q; + float dummy_x = 0.0f, dummy_y = 0.0f; + stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &dummy_x, &dummy_y, &q, 0); + dst_font->AddGlyph((ImWchar)codepoint, q.x0 + char_off_x, q.y0 + font_off_y, q.x1 + char_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, char_advance_x_mod); } } - // Cleanup temporaries - ImGui::MemFree(buf_packedchars); - ImGui::MemFree(buf_ranges); - ImGui::MemFree(tmp_array); + // Cleanup temporary (ImVector doesn't honor destructor) + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) + src_tmp_array[src_i].~ImFontBuildSrcData(); ImFontAtlasBuildFinish(atlas); - return true; } @@ -1883,16 +2114,17 @@ void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* f font->ConfigDataCount++; } -void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* pack_context_opaque) +void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque) { - stbrp_context* pack_context = (stbrp_context*)pack_context_opaque; + stbrp_context* pack_context = (stbrp_context*)stbrp_context_opaque; + IM_ASSERT(pack_context != NULL); ImVector& user_rects = atlas->CustomRects; IM_ASSERT(user_rects.Size >= 1); // We expect at least the default custom rects to be registered, else something went wrong. ImVector pack_rects; pack_rects.resize(user_rects.Size); - memset(pack_rects.Data, 0, sizeof(stbrp_rect) * user_rects.Size); + memset(pack_rects.Data, 0, (size_t)pack_rects.size_in_bytes()); for (int i = 0; i < user_rects.Size; i++) { pack_rects[i].w = user_rects[i].Width; @@ -1960,7 +2192,8 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas) // Build all fonts lookup tables for (int i = 0; i < atlas->Fonts.Size; i++) - atlas->Fonts[i]->BuildLookupTable(); + if (atlas->Fonts[i]->DirtyLookupTables) + atlas->Fonts[i]->BuildLookupTable(); } // Retrieve list of range (2 int per range, values are inclusive) @@ -1986,12 +2219,13 @@ const ImWchar* ImFontAtlas::GetGlyphRangesKorean() return &ranges[0]; } -const ImWchar* ImFontAtlas::GetGlyphRangesChinese() +const ImWchar* ImFontAtlas::GetGlyphRangesChineseFull() { static const ImWchar ranges[] = { 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x2000, 0x206F, // General Punctuation + 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions 0xFF00, 0xFFEF, // Half-width characters 0x4e00, 0x9FAF, // CJK Ideograms @@ -2000,67 +2234,141 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChinese() return &ranges[0]; } -const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() +static void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short* accumulative_offsets, int accumulative_offsets_count, ImWchar* out_ranges) { - // Store the 1946 ideograms code points as successive offsets from the initial unicode codepoint 0x4E00. Each offset has an implicit +1. - // This encoding is designed to helps us reduce the source code size. - // FIXME: Source a list of the revised 2136 joyo kanji list from 2010 and rebuild this. - // The current list was sourced from http://theinstructionlimit.com/author/renaudbedardrenaudbedard/page/3 - // Note that you may use ImFontAtlas::GlyphRangesBuilder to create your own ranges, by merging existing ranges or adding new characters. - static const short offsets_from_0x4E00[] = + for (int n = 0; n < accumulative_offsets_count; n++, out_ranges += 2) { - -1,0,1,3,0,0,0,0,1,0,5,1,1,0,7,4,6,10,0,1,9,9,7,1,3,19,1,10,7,1,0,1,0,5,1,0,6,4,2,6,0,0,12,6,8,0,3,5,0,1,0,9,0,0,8,1,1,3,4,5,13,0,0,8,2,17, - 4,3,1,1,9,6,0,0,0,2,1,3,2,22,1,9,11,1,13,1,3,12,0,5,9,2,0,6,12,5,3,12,4,1,2,16,1,1,4,6,5,3,0,6,13,15,5,12,8,14,0,0,6,15,3,6,0,18,8,1,6,14,1, - 5,4,12,24,3,13,12,10,24,0,0,0,1,0,1,1,2,9,10,2,2,0,0,3,3,1,0,3,8,0,3,2,4,4,1,6,11,10,14,6,15,3,4,15,1,0,0,5,2,2,0,0,1,6,5,5,6,0,3,6,5,0,0,1,0, - 11,2,2,8,4,7,0,10,0,1,2,17,19,3,0,2,5,0,6,2,4,4,6,1,1,11,2,0,3,1,2,1,2,10,7,6,3,16,0,8,24,0,0,3,1,1,3,0,1,6,0,0,0,2,0,1,5,15,0,1,0,0,2,11,19, - 1,4,19,7,6,5,1,0,0,0,0,5,1,0,1,9,0,0,5,0,2,0,1,0,3,0,11,3,0,2,0,0,0,0,0,9,3,6,4,12,0,14,0,0,29,10,8,0,14,37,13,0,31,16,19,0,8,30,1,20,8,3,48, - 21,1,0,12,0,10,44,34,42,54,11,18,82,0,2,1,2,12,1,0,6,2,17,2,12,7,0,7,17,4,2,6,24,23,8,23,39,2,16,23,1,0,5,1,2,15,14,5,6,2,11,0,8,6,2,2,2,14, - 20,4,15,3,4,11,10,10,2,5,2,1,30,2,1,0,0,22,5,5,0,3,1,5,4,1,0,0,2,2,21,1,5,1,2,16,2,1,3,4,0,8,4,0,0,5,14,11,2,16,1,13,1,7,0,22,15,3,1,22,7,14, - 22,19,11,24,18,46,10,20,64,45,3,2,0,4,5,0,1,4,25,1,0,0,2,10,0,0,0,1,0,1,2,0,0,9,1,2,0,0,0,2,5,2,1,1,5,5,8,1,1,1,5,1,4,9,1,3,0,1,0,1,1,2,0,0, - 2,0,1,8,22,8,1,0,0,0,0,4,2,1,0,9,8,5,0,9,1,30,24,2,6,4,39,0,14,5,16,6,26,179,0,2,1,1,0,0,0,5,2,9,6,0,2,5,16,7,5,1,1,0,2,4,4,7,15,13,14,0,0, - 3,0,1,0,0,0,2,1,6,4,5,1,4,9,0,3,1,8,0,0,10,5,0,43,0,2,6,8,4,0,2,0,0,9,6,0,9,3,1,6,20,14,6,1,4,0,7,2,3,0,2,0,5,0,3,1,0,3,9,7,0,3,4,0,4,9,1,6,0, - 9,0,0,2,3,10,9,28,3,6,2,4,1,2,32,4,1,18,2,0,3,1,5,30,10,0,2,2,2,0,7,9,8,11,10,11,7,2,13,7,5,10,0,3,40,2,0,1,6,12,0,4,5,1,5,11,11,21,4,8,3,7, - 8,8,33,5,23,0,0,19,8,8,2,3,0,6,1,1,1,5,1,27,4,2,5,0,3,5,6,3,1,0,3,1,12,5,3,3,2,0,7,7,2,1,0,4,0,1,1,2,0,10,10,6,2,5,9,7,5,15,15,21,6,11,5,20, - 4,3,5,5,2,5,0,2,1,0,1,7,28,0,9,0,5,12,5,5,18,30,0,12,3,3,21,16,25,32,9,3,14,11,24,5,66,9,1,2,0,5,9,1,5,1,8,0,8,3,3,0,1,15,1,4,8,1,2,7,0,7,2, - 8,3,7,5,3,7,10,2,1,0,0,2,25,0,6,4,0,10,0,4,2,4,1,12,5,38,4,0,4,1,10,5,9,4,0,14,4,2,5,18,20,21,1,3,0,5,0,7,0,3,7,1,3,1,1,8,1,0,0,0,3,2,5,2,11, - 6,0,13,1,3,9,1,12,0,16,6,2,1,0,2,1,12,6,13,11,2,0,28,1,7,8,14,13,8,13,0,2,0,5,4,8,10,2,37,42,19,6,6,7,4,14,11,18,14,80,7,6,0,4,72,12,36,27, - 7,7,0,14,17,19,164,27,0,5,10,7,3,13,6,14,0,2,2,5,3,0,6,13,0,0,10,29,0,4,0,3,13,0,3,1,6,51,1,5,28,2,0,8,0,20,2,4,0,25,2,10,13,10,0,16,4,0,1,0, - 2,1,7,0,1,8,11,0,0,1,2,7,2,23,11,6,6,4,16,2,2,2,0,22,9,3,3,5,2,0,15,16,21,2,9,20,15,15,5,3,9,1,0,0,1,7,7,5,4,2,2,2,38,24,14,0,0,15,5,6,24,14, - 5,5,11,0,21,12,0,3,8,4,11,1,8,0,11,27,7,2,4,9,21,59,0,1,39,3,60,62,3,0,12,11,0,3,30,11,0,13,88,4,15,5,28,13,1,4,48,17,17,4,28,32,46,0,16,0, - 18,11,1,8,6,38,11,2,6,11,38,2,0,45,3,11,2,7,8,4,30,14,17,2,1,1,65,18,12,16,4,2,45,123,12,56,33,1,4,3,4,7,0,0,0,3,2,0,16,4,2,4,2,0,7,4,5,2,26, - 2,25,6,11,6,1,16,2,6,17,77,15,3,35,0,1,0,5,1,0,38,16,6,3,12,3,3,3,0,9,3,1,3,5,2,9,0,18,0,25,1,3,32,1,72,46,6,2,7,1,3,14,17,0,28,1,40,13,0,20, - 15,40,6,38,24,12,43,1,1,9,0,12,6,0,6,2,4,19,3,7,1,48,0,9,5,0,5,6,9,6,10,15,2,11,19,3,9,2,0,1,10,1,27,8,1,3,6,1,14,0,26,0,27,16,3,4,9,6,2,23, - 9,10,5,25,2,1,6,1,1,48,15,9,15,14,3,4,26,60,29,13,37,21,1,6,4,0,2,11,22,23,16,16,2,2,1,3,0,5,1,6,4,0,0,4,0,0,8,3,0,2,5,0,7,1,7,3,13,2,4,10, - 3,0,2,31,0,18,3,0,12,10,4,1,0,7,5,7,0,5,4,12,2,22,10,4,2,15,2,8,9,0,23,2,197,51,3,1,1,4,13,4,3,21,4,19,3,10,5,40,0,4,1,1,10,4,1,27,34,7,21, - 2,17,2,9,6,4,2,3,0,4,2,7,8,2,5,1,15,21,3,4,4,2,2,17,22,1,5,22,4,26,7,0,32,1,11,42,15,4,1,2,5,0,19,3,1,8,6,0,10,1,9,2,13,30,8,2,24,17,19,1,4, - 4,25,13,0,10,16,11,39,18,8,5,30,82,1,6,8,18,77,11,13,20,75,11,112,78,33,3,0,0,60,17,84,9,1,1,12,30,10,49,5,32,158,178,5,5,6,3,3,1,3,1,4,7,6, - 19,31,21,0,2,9,5,6,27,4,9,8,1,76,18,12,1,4,0,3,3,6,3,12,2,8,30,16,2,25,1,5,5,4,3,0,6,10,2,3,1,0,5,1,19,3,0,8,1,5,2,6,0,0,0,19,1,2,0,5,1,2,5, - 1,3,7,0,4,12,7,3,10,22,0,9,5,1,0,2,20,1,1,3,23,30,3,9,9,1,4,191,14,3,15,6,8,50,0,1,0,0,4,0,0,1,0,2,4,2,0,2,3,0,2,0,2,2,8,7,0,1,1,1,3,3,17,11, - 91,1,9,3,2,13,4,24,15,41,3,13,3,1,20,4,125,29,30,1,0,4,12,2,21,4,5,5,19,11,0,13,11,86,2,18,0,7,1,8,8,2,2,22,1,2,6,5,2,0,1,2,8,0,2,0,5,2,1,0, - 2,10,2,0,5,9,2,1,2,0,1,0,4,0,0,10,2,5,3,0,6,1,0,1,4,4,33,3,13,17,3,18,6,4,7,1,5,78,0,4,1,13,7,1,8,1,0,35,27,15,3,0,0,0,1,11,5,41,38,15,22,6, - 14,14,2,1,11,6,20,63,5,8,27,7,11,2,2,40,58,23,50,54,56,293,8,8,1,5,1,14,0,1,12,37,89,8,8,8,2,10,6,0,0,0,4,5,2,1,0,1,1,2,7,0,3,3,0,4,6,0,3,2, - 19,3,8,0,0,0,4,4,16,0,4,1,5,1,3,0,3,4,6,2,17,10,10,31,6,4,3,6,10,126,7,3,2,2,0,9,0,0,5,20,13,0,15,0,6,0,2,5,8,64,50,3,2,12,2,9,0,0,11,8,20, - 109,2,18,23,0,0,9,61,3,0,28,41,77,27,19,17,81,5,2,14,5,83,57,252,14,154,263,14,20,8,13,6,57,39,38, + out_ranges[0] = out_ranges[1] = (ImWchar)(base_codepoint + accumulative_offsets[n]); + base_codepoint += accumulative_offsets[n]; + } + out_ranges[0] = 0; +} + +//------------------------------------------------------------------------- +// [SECTION] ImFontAtlas glyph ranges helpers +//------------------------------------------------------------------------- + +const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() +{ + // Store 2500 regularly used characters for Simplified Chinese. + // Sourced from https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8 + // This table covers 97.97% of all characters used during the month in July, 1987. + // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters. + // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.) + static const short accumulative_offsets_from_0x4E00[] = + { + 0,1,2,4,1,1,1,1,2,1,3,2,1,2,2,1,1,1,1,1,5,2,1,2,3,3,3,2,2,4,1,1,1,2,1,5,2,3,1,2,1,2,1,1,2,1,1,2,2,1,4,1,1,1,1,5,10,1,2,19,2,1,2,1,2,1,2,1,2, + 1,5,1,6,3,2,1,2,2,1,1,1,4,8,5,1,1,4,1,1,3,1,2,1,5,1,2,1,1,1,10,1,1,5,2,4,6,1,4,2,2,2,12,2,1,1,6,1,1,1,4,1,1,4,6,5,1,4,2,2,4,10,7,1,1,4,2,4, + 2,1,4,3,6,10,12,5,7,2,14,2,9,1,1,6,7,10,4,7,13,1,5,4,8,4,1,1,2,28,5,6,1,1,5,2,5,20,2,2,9,8,11,2,9,17,1,8,6,8,27,4,6,9,20,11,27,6,68,2,2,1,1, + 1,2,1,2,2,7,6,11,3,3,1,1,3,1,2,1,1,1,1,1,3,1,1,8,3,4,1,5,7,2,1,4,4,8,4,2,1,2,1,1,4,5,6,3,6,2,12,3,1,3,9,2,4,3,4,1,5,3,3,1,3,7,1,5,1,1,1,1,2, + 3,4,5,2,3,2,6,1,1,2,1,7,1,7,3,4,5,15,2,2,1,5,3,22,19,2,1,1,1,1,2,5,1,1,1,6,1,1,12,8,2,9,18,22,4,1,1,5,1,16,1,2,7,10,15,1,1,6,2,4,1,2,4,1,6, + 1,1,3,2,4,1,6,4,5,1,2,1,1,2,1,10,3,1,3,2,1,9,3,2,5,7,2,19,4,3,6,1,1,1,1,1,4,3,2,1,1,1,2,5,3,1,1,1,2,2,1,1,2,1,1,2,1,3,1,1,1,3,7,1,4,1,1,2,1, + 1,2,1,2,4,4,3,8,1,1,1,2,1,3,5,1,3,1,3,4,6,2,2,14,4,6,6,11,9,1,15,3,1,28,5,2,5,5,3,1,3,4,5,4,6,14,3,2,3,5,21,2,7,20,10,1,2,19,2,4,28,28,2,3, + 2,1,14,4,1,26,28,42,12,40,3,52,79,5,14,17,3,2,2,11,3,4,6,3,1,8,2,23,4,5,8,10,4,2,7,3,5,1,1,6,3,1,2,2,2,5,28,1,1,7,7,20,5,3,29,3,17,26,1,8,4, + 27,3,6,11,23,5,3,4,6,13,24,16,6,5,10,25,35,7,3,2,3,3,14,3,6,2,6,1,4,2,3,8,2,1,1,3,3,3,4,1,1,13,2,2,4,5,2,1,14,14,1,2,2,1,4,5,2,3,1,14,3,12, + 3,17,2,16,5,1,2,1,8,9,3,19,4,2,2,4,17,25,21,20,28,75,1,10,29,103,4,1,2,1,1,4,2,4,1,2,3,24,2,2,2,1,1,2,1,3,8,1,1,1,2,1,1,3,1,1,1,6,1,5,3,1,1, + 1,3,4,1,1,5,2,1,5,6,13,9,16,1,1,1,1,3,2,3,2,4,5,2,5,2,2,3,7,13,7,2,2,1,1,1,1,2,3,3,2,1,6,4,9,2,1,14,2,14,2,1,18,3,4,14,4,11,41,15,23,15,23, + 176,1,3,4,1,1,1,1,5,3,1,2,3,7,3,1,1,2,1,2,4,4,6,2,4,1,9,7,1,10,5,8,16,29,1,1,2,2,3,1,3,5,2,4,5,4,1,1,2,2,3,3,7,1,6,10,1,17,1,44,4,6,2,1,1,6, + 5,4,2,10,1,6,9,2,8,1,24,1,2,13,7,8,8,2,1,4,1,3,1,3,3,5,2,5,10,9,4,9,12,2,1,6,1,10,1,1,7,7,4,10,8,3,1,13,4,3,1,6,1,3,5,2,1,2,17,16,5,2,16,6, + 1,4,2,1,3,3,6,8,5,11,11,1,3,3,2,4,6,10,9,5,7,4,7,4,7,1,1,4,2,1,3,6,8,7,1,6,11,5,5,3,24,9,4,2,7,13,5,1,8,82,16,61,1,1,1,4,2,2,16,10,3,8,1,1, + 6,4,2,1,3,1,1,1,4,3,8,4,2,2,1,1,1,1,1,6,3,5,1,1,4,6,9,2,1,1,1,2,1,7,2,1,6,1,5,4,4,3,1,8,1,3,3,1,3,2,2,2,2,3,1,6,1,2,1,2,1,3,7,1,8,2,1,2,1,5, + 2,5,3,5,10,1,2,1,1,3,2,5,11,3,9,3,5,1,1,5,9,1,2,1,5,7,9,9,8,1,3,3,3,6,8,2,3,2,1,1,32,6,1,2,15,9,3,7,13,1,3,10,13,2,14,1,13,10,2,1,3,10,4,15, + 2,15,15,10,1,3,9,6,9,32,25,26,47,7,3,2,3,1,6,3,4,3,2,8,5,4,1,9,4,2,2,19,10,6,2,3,8,1,2,2,4,2,1,9,4,4,4,6,4,8,9,2,3,1,1,1,1,3,5,5,1,3,8,4,6, + 2,1,4,12,1,5,3,7,13,2,5,8,1,6,1,2,5,14,6,1,5,2,4,8,15,5,1,23,6,62,2,10,1,1,8,1,2,2,10,4,2,2,9,2,1,1,3,2,3,1,5,3,3,2,1,3,8,1,1,1,11,3,1,1,4, + 3,7,1,14,1,2,3,12,5,2,5,1,6,7,5,7,14,11,1,3,1,8,9,12,2,1,11,8,4,4,2,6,10,9,13,1,1,3,1,5,1,3,2,4,4,1,18,2,3,14,11,4,29,4,2,7,1,3,13,9,2,2,5, + 3,5,20,7,16,8,5,72,34,6,4,22,12,12,28,45,36,9,7,39,9,191,1,1,1,4,11,8,4,9,2,3,22,1,1,1,1,4,17,1,7,7,1,11,31,10,2,4,8,2,3,2,1,4,2,16,4,32,2, + 3,19,13,4,9,1,5,2,14,8,1,1,3,6,19,6,5,1,16,6,2,10,8,5,1,2,3,1,5,5,1,11,6,6,1,3,3,2,6,3,8,1,1,4,10,7,5,7,7,5,8,9,2,1,3,4,1,1,3,1,3,3,2,6,16, + 1,4,6,3,1,10,6,1,3,15,2,9,2,10,25,13,9,16,6,2,2,10,11,4,3,9,1,2,6,6,5,4,30,40,1,10,7,12,14,33,6,3,6,7,3,1,3,1,11,14,4,9,5,12,11,49,18,51,31, + 140,31,2,2,1,5,1,8,1,10,1,4,4,3,24,1,10,1,3,6,6,16,3,4,5,2,1,4,2,57,10,6,22,2,22,3,7,22,6,10,11,36,18,16,33,36,2,5,5,1,1,1,4,10,1,4,13,2,7, + 5,2,9,3,4,1,7,43,3,7,3,9,14,7,9,1,11,1,1,3,7,4,18,13,1,14,1,3,6,10,73,2,2,30,6,1,11,18,19,13,22,3,46,42,37,89,7,3,16,34,2,2,3,9,1,7,1,1,1,2, + 2,4,10,7,3,10,3,9,5,28,9,2,6,13,7,3,1,3,10,2,7,2,11,3,6,21,54,85,2,1,4,2,2,1,39,3,21,2,2,5,1,1,1,4,1,1,3,4,15,1,3,2,4,4,2,3,8,2,20,1,8,7,13, + 4,1,26,6,2,9,34,4,21,52,10,4,4,1,5,12,2,11,1,7,2,30,12,44,2,30,1,1,3,6,16,9,17,39,82,2,2,24,7,1,7,3,16,9,14,44,2,1,2,1,2,3,5,2,4,1,6,7,5,3, + 2,6,1,11,5,11,2,1,18,19,8,1,3,24,29,2,1,3,5,2,2,1,13,6,5,1,46,11,3,5,1,1,5,8,2,10,6,12,6,3,7,11,2,4,16,13,2,5,1,1,2,2,5,2,28,5,2,23,10,8,4, + 4,22,39,95,38,8,14,9,5,1,13,5,4,3,13,12,11,1,9,1,27,37,2,5,4,4,63,211,95,2,2,2,1,3,5,2,1,1,2,2,1,1,1,3,2,4,1,2,1,1,5,2,2,1,1,2,3,1,3,1,1,1, + 3,1,4,2,1,3,6,1,1,3,7,15,5,3,2,5,3,9,11,4,2,22,1,6,3,8,7,1,4,28,4,16,3,3,25,4,4,27,27,1,4,1,2,2,7,1,3,5,2,28,8,2,14,1,8,6,16,25,3,3,3,14,3, + 3,1,1,2,1,4,6,3,8,4,1,1,1,2,3,6,10,6,2,3,18,3,2,5,5,4,3,1,5,2,5,4,23,7,6,12,6,4,17,11,9,5,1,1,10,5,12,1,1,11,26,33,7,3,6,1,17,7,1,5,12,1,11, + 2,4,1,8,14,17,23,1,2,1,7,8,16,11,9,6,5,2,6,4,16,2,8,14,1,11,8,9,1,1,1,9,25,4,11,19,7,2,15,2,12,8,52,7,5,19,2,16,4,36,8,1,16,8,24,26,4,6,2,9, + 5,4,36,3,28,12,25,15,37,27,17,12,59,38,5,32,127,1,2,9,17,14,4,1,2,1,1,8,11,50,4,14,2,19,16,4,17,5,4,5,26,12,45,2,23,45,104,30,12,8,3,10,2,2, + 3,3,1,4,20,7,2,9,6,15,2,20,1,3,16,4,11,15,6,134,2,5,59,1,2,2,2,1,9,17,3,26,137,10,211,59,1,2,4,1,4,1,1,1,2,6,2,3,1,1,2,3,2,3,1,3,4,4,2,3,3, + 1,4,3,1,7,2,2,3,1,2,1,3,3,3,2,2,3,2,1,3,14,6,1,3,2,9,6,15,27,9,34,145,1,1,2,1,1,1,1,2,1,1,1,1,2,2,2,3,1,2,1,1,1,2,3,5,8,3,5,2,4,1,3,2,2,2,12, + 4,1,1,1,10,4,5,1,20,4,16,1,15,9,5,12,2,9,2,5,4,2,26,19,7,1,26,4,30,12,15,42,1,6,8,172,1,1,4,2,1,1,11,2,2,4,2,1,2,1,10,8,1,2,1,4,5,1,2,5,1,8, + 4,1,3,4,2,1,6,2,1,3,4,1,2,1,1,1,1,12,5,7,2,4,3,1,1,1,3,3,6,1,2,2,3,3,3,2,1,2,12,14,11,6,6,4,12,2,8,1,7,10,1,35,7,4,13,15,4,3,23,21,28,52,5, + 26,5,6,1,7,10,2,7,53,3,2,1,1,1,2,163,532,1,10,11,1,3,3,4,8,2,8,6,2,2,23,22,4,2,2,4,2,1,3,1,3,3,5,9,8,2,1,2,8,1,10,2,12,21,20,15,105,2,3,1,1, + 3,2,3,1,1,2,5,1,4,15,11,19,1,1,1,1,5,4,5,1,1,2,5,3,5,12,1,2,5,1,11,1,1,15,9,1,4,5,3,26,8,2,1,3,1,1,15,19,2,12,1,2,5,2,7,2,19,2,20,6,26,7,5, + 2,2,7,34,21,13,70,2,128,1,1,2,1,1,2,1,1,3,2,2,2,15,1,4,1,3,4,42,10,6,1,49,85,8,1,2,1,1,4,4,2,3,6,1,5,7,4,3,211,4,1,2,1,2,5,1,2,4,2,2,6,5,6, + 10,3,4,48,100,6,2,16,296,5,27,387,2,2,3,7,16,8,5,38,15,39,21,9,10,3,7,59,13,27,21,47,5,21,6 }; - static ImWchar base_ranges[] = + static ImWchar base_ranges[] = // not zero-terminated { 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x2000, 0x206F, // General Punctuation + 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF, // Half-width characters + 0xFF00, 0xFFEF // Half-width characters }; - static bool full_ranges_unpacked = false; - static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(offsets_from_0x4E00)*2 + 1]; - if (!full_ranges_unpacked) + static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; + if (!full_ranges[0]) { - // Unpack - int codepoint = 0x4e00; memcpy(full_ranges, base_ranges, sizeof(base_ranges)); - ImWchar* dst = full_ranges + IM_ARRAYSIZE(base_ranges); - for (int n = 0; n < IM_ARRAYSIZE(offsets_from_0x4E00); n++, dst += 2) - dst[0] = dst[1] = (ImWchar)(codepoint += (offsets_from_0x4E00[n] + 1)); - dst[0] = 0; - full_ranges_unpacked = true; + UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges)); + } + return &full_ranges[0]; +} + +const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() +{ + // 1946 common ideograms code points for Japanese + // Sourced from http://theinstructionlimit.com/common-kanji-character-ranges-for-xna-spritefont-rendering + // FIXME: Source a list of the revised 2136 Joyo Kanji list from 2010 and rebuild this. + // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters. + // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.) + static const short accumulative_offsets_from_0x4E00[] = + { + 0,1,2,4,1,1,1,1,2,1,6,2,2,1,8,5,7,11,1,2,10,10,8,2,4,20,2,11,8,2,1,2,1,6,2,1,7,5,3,7,1,1,13,7,9,1,4,6,1,2,1,10,1,1,9,2,2,4,5,6,14,1,1,9,3,18, + 5,4,2,2,10,7,1,1,1,3,2,4,3,23,2,10,12,2,14,2,4,13,1,6,10,3,1,7,13,6,4,13,5,2,3,17,2,2,5,7,6,4,1,7,14,16,6,13,9,15,1,1,7,16,4,7,1,19,9,2,7,15, + 2,6,5,13,25,4,14,13,11,25,1,1,1,2,1,2,2,3,10,11,3,3,1,1,4,4,2,1,4,9,1,4,3,5,5,2,7,12,11,15,7,16,4,5,16,2,1,1,6,3,3,1,1,2,7,6,6,7,1,4,7,6,1,1, + 2,1,12,3,3,9,5,8,1,11,1,2,3,18,20,4,1,3,6,1,7,3,5,5,7,2,2,12,3,1,4,2,3,2,3,11,8,7,4,17,1,9,25,1,1,4,2,2,4,1,2,7,1,1,1,3,1,2,6,16,1,2,1,1,3,12, + 20,2,5,20,8,7,6,2,1,1,1,1,6,2,1,2,10,1,1,6,1,3,1,2,1,4,1,12,4,1,3,1,1,1,1,1,10,4,7,5,13,1,15,1,1,30,11,9,1,15,38,14,1,32,17,20,1,9,31,2,21,9, + 4,49,22,2,1,13,1,11,45,35,43,55,12,19,83,1,3,2,3,13,2,1,7,3,18,3,13,8,1,8,18,5,3,7,25,24,9,24,40,3,17,24,2,1,6,2,3,16,15,6,7,3,12,1,9,7,3,3, + 3,15,21,5,16,4,5,12,11,11,3,6,3,2,31,3,2,1,1,23,6,6,1,4,2,6,5,2,1,1,3,3,22,2,6,2,3,17,3,2,4,5,1,9,5,1,1,6,15,12,3,17,2,14,2,8,1,23,16,4,2,23, + 8,15,23,20,12,25,19,47,11,21,65,46,4,3,1,5,6,1,2,5,26,2,1,1,3,11,1,1,1,2,1,2,3,1,1,10,2,3,1,1,1,3,6,3,2,2,6,6,9,2,2,2,6,2,5,10,2,4,1,2,1,2,2, + 3,1,1,3,1,2,9,23,9,2,1,1,1,1,5,3,2,1,10,9,6,1,10,2,31,25,3,7,5,40,1,15,6,17,7,27,180,1,3,2,2,1,1,1,6,3,10,7,1,3,6,17,8,6,2,2,1,3,5,5,8,16,14, + 15,1,1,4,1,2,1,1,1,3,2,7,5,6,2,5,10,1,4,2,9,1,1,11,6,1,44,1,3,7,9,5,1,3,1,1,10,7,1,10,4,2,7,21,15,7,2,5,1,8,3,4,1,3,1,6,1,4,2,1,4,10,8,1,4,5, + 1,5,10,2,7,1,10,1,1,3,4,11,10,29,4,7,3,5,2,3,33,5,2,19,3,1,4,2,6,31,11,1,3,3,3,1,8,10,9,12,11,12,8,3,14,8,6,11,1,4,41,3,1,2,7,13,1,5,6,2,6,12, + 12,22,5,9,4,8,9,9,34,6,24,1,1,20,9,9,3,4,1,7,2,2,2,6,2,28,5,3,6,1,4,6,7,4,2,1,4,2,13,6,4,4,3,1,8,8,3,2,1,5,1,2,2,3,1,11,11,7,3,6,10,8,6,16,16, + 22,7,12,6,21,5,4,6,6,3,6,1,3,2,1,2,8,29,1,10,1,6,13,6,6,19,31,1,13,4,4,22,17,26,33,10,4,15,12,25,6,67,10,2,3,1,6,10,2,6,2,9,1,9,4,4,1,2,16,2, + 5,9,2,3,8,1,8,3,9,4,8,6,4,8,11,3,2,1,1,3,26,1,7,5,1,11,1,5,3,5,2,13,6,39,5,1,5,2,11,6,10,5,1,15,5,3,6,19,21,22,2,4,1,6,1,8,1,4,8,2,4,2,2,9,2, + 1,1,1,4,3,6,3,12,7,1,14,2,4,10,2,13,1,17,7,3,2,1,3,2,13,7,14,12,3,1,29,2,8,9,15,14,9,14,1,3,1,6,5,9,11,3,38,43,20,7,7,8,5,15,12,19,15,81,8,7, + 1,5,73,13,37,28,8,8,1,15,18,20,165,28,1,6,11,8,4,14,7,15,1,3,3,6,4,1,7,14,1,1,11,30,1,5,1,4,14,1,4,2,7,52,2,6,29,3,1,9,1,21,3,5,1,26,3,11,14, + 11,1,17,5,1,2,1,3,2,8,1,2,9,12,1,1,2,3,8,3,24,12,7,7,5,17,3,3,3,1,23,10,4,4,6,3,1,16,17,22,3,10,21,16,16,6,4,10,2,1,1,2,8,8,6,5,3,3,3,39,25, + 15,1,1,16,6,7,25,15,6,6,12,1,22,13,1,4,9,5,12,2,9,1,12,28,8,3,5,10,22,60,1,2,40,4,61,63,4,1,13,12,1,4,31,12,1,14,89,5,16,6,29,14,2,5,49,18,18, + 5,29,33,47,1,17,1,19,12,2,9,7,39,12,3,7,12,39,3,1,46,4,12,3,8,9,5,31,15,18,3,2,2,66,19,13,17,5,3,46,124,13,57,34,2,5,4,5,8,1,1,1,4,3,1,17,5, + 3,5,3,1,8,5,6,3,27,3,26,7,12,7,2,17,3,7,18,78,16,4,36,1,2,1,6,2,1,39,17,7,4,13,4,4,4,1,10,4,2,4,6,3,10,1,19,1,26,2,4,33,2,73,47,7,3,8,2,4,15, + 18,1,29,2,41,14,1,21,16,41,7,39,25,13,44,2,2,10,1,13,7,1,7,3,5,20,4,8,2,49,1,10,6,1,6,7,10,7,11,16,3,12,20,4,10,3,1,2,11,2,28,9,2,4,7,2,15,1, + 27,1,28,17,4,5,10,7,3,24,10,11,6,26,3,2,7,2,2,49,16,10,16,15,4,5,27,61,30,14,38,22,2,7,5,1,3,12,23,24,17,17,3,3,2,4,1,6,2,7,5,1,1,5,1,1,9,4, + 1,3,6,1,8,2,8,4,14,3,5,11,4,1,3,32,1,19,4,1,13,11,5,2,1,8,6,8,1,6,5,13,3,23,11,5,3,16,3,9,10,1,24,3,198,52,4,2,2,5,14,5,4,22,5,20,4,11,6,41, + 1,5,2,2,11,5,2,28,35,8,22,3,18,3,10,7,5,3,4,1,5,3,8,9,3,6,2,16,22,4,5,5,3,3,18,23,2,6,23,5,27,8,1,33,2,12,43,16,5,2,3,6,1,20,4,2,9,7,1,11,2, + 10,3,14,31,9,3,25,18,20,2,5,5,26,14,1,11,17,12,40,19,9,6,31,83,2,7,9,19,78,12,14,21,76,12,113,79,34,4,1,1,61,18,85,10,2,2,13,31,11,50,6,33,159, + 179,6,6,7,4,4,2,4,2,5,8,7,20,32,22,1,3,10,6,7,28,5,10,9,2,77,19,13,2,5,1,4,4,7,4,13,3,9,31,17,3,26,2,6,6,5,4,1,7,11,3,4,2,1,6,2,20,4,1,9,2,6, + 3,7,1,1,1,20,2,3,1,6,2,3,6,2,4,8,1,5,13,8,4,11,23,1,10,6,2,1,3,21,2,2,4,24,31,4,10,10,2,5,192,15,4,16,7,9,51,1,2,1,1,5,1,1,2,1,3,5,3,1,3,4,1, + 3,1,3,3,9,8,1,2,2,2,4,4,18,12,92,2,10,4,3,14,5,25,16,42,4,14,4,2,21,5,126,30,31,2,1,5,13,3,22,5,6,6,20,12,1,14,12,87,3,19,1,8,2,9,9,3,3,23,2, + 3,7,6,3,1,2,3,9,1,3,1,6,3,2,1,3,11,3,1,6,10,3,2,3,1,2,1,5,1,1,11,3,6,4,1,7,2,1,2,5,5,34,4,14,18,4,19,7,5,8,2,6,79,1,5,2,14,8,2,9,2,1,36,28,16, + 4,1,1,1,2,12,6,42,39,16,23,7,15,15,3,2,12,7,21,64,6,9,28,8,12,3,3,41,59,24,51,55,57,294,9,9,2,6,2,15,1,2,13,38,90,9,9,9,3,11,7,1,1,1,5,6,3,2, + 1,2,2,3,8,1,4,4,1,5,7,1,4,3,20,4,9,1,1,1,5,5,17,1,5,2,6,2,4,1,4,5,7,3,18,11,11,32,7,5,4,7,11,127,8,4,3,3,1,10,1,1,6,21,14,1,16,1,7,1,3,6,9,65, + 51,4,3,13,3,10,1,1,12,9,21,110,3,19,24,1,1,10,62,4,1,29,42,78,28,20,18,82,6,3,15,6,84,58,253,15,155,264,15,21,9,14,7,58,40,39, + }; + static ImWchar base_ranges[] = // not zero-terminated + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana + 0x31F0, 0x31FF, // Katakana Phonetic Extensions + 0xFF00, 0xFFEF // Half-width characters + }; + static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; + if (!full_ranges[0]) + { + memcpy(full_ranges, base_ranges, sizeof(base_ranges)); + UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges)); } return &full_ranges[0]; } @@ -2090,11 +2398,28 @@ const ImWchar* ImFontAtlas::GetGlyphRangesThai() return &ranges[0]; } +const ImWchar* ImFontAtlas::GetGlyphRangesVietnamese() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + 0x0102, 0x0103, + 0x0110, 0x0111, + 0x0128, 0x0129, + 0x0168, 0x0169, + 0x01A0, 0x01A1, + 0x01AF, 0x01B0, + 0x1EA0, 0x1EF9, + 0, + }; + return &ranges[0]; +} + //----------------------------------------------------------------------------- -// ImFontAtlas::GlyphRangesBuilder +// [SECTION] ImFontGlyphRangesBuilder //----------------------------------------------------------------------------- -void ImFontAtlas::GlyphRangesBuilder::AddText(const char* text, const char* text_end) +void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end) { while (text_end ? (text < text_end) : *text) { @@ -2108,20 +2433,21 @@ void ImFontAtlas::GlyphRangesBuilder::AddText(const char* text, const char* text } } -void ImFontAtlas::GlyphRangesBuilder::AddRanges(const ImWchar* ranges) +void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) { for (; ranges[0]; ranges += 2) for (ImWchar c = ranges[0]; c <= ranges[1]; c++) AddChar(c); } -void ImFontAtlas::GlyphRangesBuilder::BuildRanges(ImVector* out_ranges) +void ImFontGlyphRangesBuilder::BuildRanges(ImVector* out_ranges) { - for (int n = 0; n < 0x10000; n++) + int max_codepoint = 0x10000; + for (int n = 0; n < max_codepoint; n++) if (GetBit(n)) { out_ranges->push_back((ImWchar)n); - while (n < 0x10000 && GetBit(n + 1)) + while (n < max_codepoint - 1 && GetBit(n + 1)) n++; out_ranges->push_back((ImWchar)n); } @@ -2129,41 +2455,40 @@ void ImFontAtlas::GlyphRangesBuilder::BuildRanges(ImVector* out_ranges) } //----------------------------------------------------------------------------- -// ImFont +// [SECTION] ImFont //----------------------------------------------------------------------------- ImFont::ImFont() { - Scale = 1.0f; + FontSize = 0.0f; + FallbackAdvanceX = 0.0f; FallbackChar = (ImWchar)'?'; - DisplayOffset = ImVec2(0.0f, 1.0f); - ClearOutputData(); + DisplayOffset = ImVec2(0.0f, 0.0f); + FallbackGlyph = NULL; + ContainerAtlas = NULL; + ConfigData = NULL; + ConfigDataCount = 0; + DirtyLookupTables = false; + Scale = 1.0f; + Ascent = Descent = 0.0f; + MetricsTotalSurface = 0; } ImFont::~ImFont() { - // Invalidate active font so that the user gets a clear crash instead of a dangling pointer. - // If you want to delete fonts you need to do it between Render() and NewFrame(). - // FIXME-CLEANUP - /* - ImGuiContext& g = *GImGui; - if (g.Font == this) - g.Font = NULL; - */ ClearOutputData(); } void ImFont::ClearOutputData() { FontSize = 0.0f; + FallbackAdvanceX = 0.0f; Glyphs.clear(); IndexAdvanceX.clear(); IndexLookup.clear(); FallbackGlyph = NULL; - FallbackAdvanceX = 0.0f; - ConfigDataCount = 0; - ConfigData = NULL; ContainerAtlas = NULL; + DirtyLookupTables = true; Ascent = Descent = 0.0f; MetricsTotalSurface = 0; } @@ -2177,30 +2502,30 @@ void ImFont::BuildLookupTable() IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved IndexAdvanceX.clear(); IndexLookup.clear(); + DirtyLookupTables = false; GrowIndex(max_codepoint + 1); for (int i = 0; i < Glyphs.Size; i++) { int codepoint = (int)Glyphs[i].Codepoint; IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX; - IndexLookup[codepoint] = (unsigned short)i; + IndexLookup[codepoint] = (ImWchar)i; } // Create a glyph to handle TAB // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?) - if (FindGlyph((unsigned short)' ')) + if (FindGlyph((ImWchar)' ')) { if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times Glyphs.resize(Glyphs.Size + 1); ImFontGlyph& tab_glyph = Glyphs.back(); - tab_glyph = *FindGlyph((unsigned short)' '); + tab_glyph = *FindGlyph((ImWchar)' '); tab_glyph.Codepoint = '\t'; - tab_glyph.AdvanceX *= 4; + tab_glyph.AdvanceX *= IM_TABSIZE; IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX; - IndexLookup[(int)tab_glyph.Codepoint] = (unsigned short)(Glyphs.Size-1); + IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size-1); } - FallbackGlyph = NULL; - FallbackGlyph = FindGlyph(FallbackChar); + FallbackGlyph = FindGlyphNoFallback(FallbackChar); FallbackAdvanceX = FallbackGlyph ? FallbackGlyph->AdvanceX : 0.0f; for (int i = 0; i < max_codepoint + 1; i++) if (IndexAdvanceX[i] < 0.0f) @@ -2219,28 +2544,31 @@ void ImFont::GrowIndex(int new_size) if (new_size <= IndexLookup.Size) return; IndexAdvanceX.resize(new_size, -1.0f); - IndexLookup.resize(new_size, (unsigned short)-1); + IndexLookup.resize(new_size, (ImWchar)-1); } +// x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero. +// Not to be mistaken with texture coordinates, which are held by u0/v0/u1/v1 in normalized format (0.0..1.0 on each texture axis). void ImFont::AddGlyph(ImWchar codepoint, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x) { Glyphs.resize(Glyphs.Size + 1); ImFontGlyph& glyph = Glyphs.back(); glyph.Codepoint = (ImWchar)codepoint; - glyph.X0 = x0; - glyph.Y0 = y0; - glyph.X1 = x1; + glyph.X0 = x0; + glyph.Y0 = y0; + glyph.X1 = x1; glyph.Y1 = y1; - glyph.U0 = u0; - glyph.V0 = v0; - glyph.U1 = u1; + glyph.U0 = u0; + glyph.V0 = v0; + glyph.U1 = u1; glyph.V1 = v1; glyph.AdvanceX = advance_x + ConfigData->GlyphExtraSpacing.x; // Bake spacing into AdvanceX if (ConfigData->PixelSnapH) glyph.AdvanceX = (float)(int)(glyph.AdvanceX + 0.5f); - + // Compute rough surface usage metrics (+1 to account for average padding, +0.99 to round) + DirtyLookupTables = true; MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * ContainerAtlas->TexWidth + 1.99f) * (int)((glyph.V1 - glyph.V0) * ContainerAtlas->TexHeight + 1.99f); } @@ -2249,25 +2577,34 @@ void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function. int index_size = IndexLookup.Size; - if (dst < index_size && IndexLookup.Data[dst] == (unsigned short)-1 && !overwrite_dst) // 'dst' already exists + if (dst < index_size && IndexLookup.Data[dst] == (ImWchar)-1 && !overwrite_dst) // 'dst' already exists return; if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op return; GrowIndex(dst + 1); - IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (unsigned short)-1; + IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (ImWchar)-1; IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f; } const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const { - if (c < IndexLookup.Size) - { - const unsigned short i = IndexLookup[c]; - if (i != (unsigned short)-1) - return &Glyphs.Data[i]; - } - return FallbackGlyph; + if (c >= IndexLookup.Size) + return FallbackGlyph; + const ImWchar i = IndexLookup.Data[c]; + if (i == (ImWchar)-1) + return FallbackGlyph; + return &Glyphs.Data[i]; +} + +const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const +{ + if (c >= IndexLookup.Size) + return NULL; + const ImWchar i = IndexLookup.Data[c]; + if (i == (ImWchar)-1) + return NULL; + return &Glyphs.Data[i]; } const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const @@ -2324,8 +2661,8 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c } } - const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX); - if (ImCharIsSpace(c)) + const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX); + if (ImCharIsBlankW(c)) { if (inside_word) { @@ -2355,7 +2692,7 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c } // We ignore blank width at the end of the line (they can be skipped) - if (line_width + word_width >= wrap_width) + if (line_width + word_width > wrap_width) { // Words that cannot possibly fit within an entire line will be cut anywhere. if (word_width < wrap_width) @@ -2408,7 +2745,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons while (s < text_end) { const char c = *s; - if (ImCharIsSpace(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } + if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } } continue; } @@ -2441,7 +2778,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons continue; } - const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX) * scale; + const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX) * scale; if (line_width + char_width >= max_width) { s = prev_s; @@ -2463,7 +2800,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons return text_size; } -void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const +void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const { if (c == ' ' || c == '\t' || c == '\n' || c == '\r') // Match behavior of RenderText(), those 4 codepoints are hard-coded. return; @@ -2480,7 +2817,7 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const { if (!text_end) - text_end = text_begin + strlen(text_begin); // ImGui functions generally already provides a valid text_end, so this is merely to handle direct calls. + text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. // Align to be pixel perfect pos.x = (float)(int)pos.x + DisplayOffset.x; @@ -2495,11 +2832,32 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col const bool word_wrap_enabled = (wrap_width > 0.0f); const char* word_wrap_eol = NULL; - // Skip non-visible lines + // Fast-forward to first visible line const char* s = text_begin; - if (!word_wrap_enabled && y + line_height < clip_rect.y) - while (s < text_end && *s != '\n') // Fast-forward to next line - s++; + if (y + line_height < clip_rect.y && !word_wrap_enabled) + while (y + line_height < clip_rect.y && s < text_end) + { + s = (const char*)memchr(s, '\n', text_end - s); + s = s ? s + 1 : text_end; + y += line_height; + } + + // For large text, scan for the last visible line in order to avoid over-reserving in the call to PrimReserve() + // Note that very large horizontal line will still be affected by the issue (e.g. a one megabyte string buffer without a newline will likely crash atm) + if (text_end - s > 10000 && !word_wrap_enabled) + { + const char* s_end = s; + float y_end = y; + while (y_end < clip_rect.w && s_end < text_end) + { + s_end = (const char*)memchr(s_end, '\n', text_end - s_end); + s_end = s_end ? s_end + 1 : text_end; + y_end += line_height; + } + text_end = s_end; + } + if (s == text_end) + return; // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized) const int vtx_count_max = (int)(text_end - s) * 4; @@ -2533,7 +2891,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col while (s < text_end) { const char c = *s; - if (ImCharIsSpace(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } + if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } } continue; } @@ -2558,12 +2916,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col { x = pos.x; y += line_height; - if (y > clip_rect.w) - break; - if (!word_wrap_enabled && y + line_height < clip_rect.y) - while (s < text_end && *s != '\n') // Fast-forward to next line - s++; + break; // break out of main loop continue; } if (c == '\r') @@ -2571,7 +2925,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col } float char_width = 0.0f; - if (const ImFontGlyph* glyph = FindGlyph((unsigned short)c)) + if (const ImFontGlyph* glyph = FindGlyph((ImWchar)c)) { char_width = glyph->AdvanceX * scale; @@ -2646,18 +3000,62 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col draw_list->CmdBuffer[draw_list->CmdBuffer.Size-1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size); draw_list->_VtxWritePtr = vtx_write; draw_list->_IdxWritePtr = idx_write; - draw_list->_VtxCurrentIdx = (unsigned int)draw_list->VtxBuffer.Size; + draw_list->_VtxCurrentIdx = vtx_current_idx; } //----------------------------------------------------------------------------- -// Internals Drawing Helpers +// [SECTION] Internal Render Helpers +// (progressively moved from imgui.cpp to here when they are redesigned to stop accessing ImGui global state) //----------------------------------------------------------------------------- +// - RenderMouseCursor() +// - RenderArrowPointingAt() +// - RenderRectFilledRangeH() +// - RenderPixelEllipsis() +//----------------------------------------------------------------------------- + +void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor) +{ + if (mouse_cursor == ImGuiMouseCursor_None) + return; + IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT); + + const ImU32 col_shadow = IM_COL32(0, 0, 0, 48); + const ImU32 col_border = IM_COL32(0, 0, 0, 255); // Black + const ImU32 col_fill = IM_COL32(255, 255, 255, 255); // White + + ImFontAtlas* font_atlas = draw_list->_Data->Font->ContainerAtlas; + ImVec2 offset, size, uv[4]; + if (font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2])) + { + pos -= offset; + const ImTextureID tex_id = font_atlas->TexID; + draw_list->PushTextureID(tex_id); + draw_list->AddImage(tex_id, pos + ImVec2(1,0)*scale, pos + ImVec2(1,0)*scale + size*scale, uv[2], uv[3], col_shadow); + draw_list->AddImage(tex_id, pos + ImVec2(2,0)*scale, pos + ImVec2(2,0)*scale + size*scale, uv[2], uv[3], col_shadow); + draw_list->AddImage(tex_id, pos, pos + size*scale, uv[2], uv[3], col_border); + draw_list->AddImage(tex_id, pos, pos + size*scale, uv[0], uv[1], col_fill); + draw_list->PopTextureID(); + } +} + +// Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side. +void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col) +{ + switch (direction) + { + case ImGuiDir_Left: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return; + case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return; + case ImGuiDir_Up: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return; + case ImGuiDir_Down: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return; + case ImGuiDir_None: case ImGuiDir_COUNT: break; // Fix warnings + } +} static inline float ImAcos01(float x) { if (x <= 0.0f) return IM_PI * 0.5f; if (x >= 1.0f) return 0.0f; - return acosf(x); + return ImAcos(x); //return (-0.69813170079773212f * x * x - 0.87266462599716477f) * x + 1.5707963267948966f; // Cheap approximation, may be enough for what we do. } @@ -2681,13 +3079,14 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im const float inv_rounding = 1.0f / rounding; const float arc0_b = ImAcos01(1.0f - (p0.x - rect.Min.x) * inv_rounding); const float arc0_e = ImAcos01(1.0f - (p1.x - rect.Min.x) * inv_rounding); + const float half_pi = IM_PI * 0.5f; // We will == compare to this because we know this is the exact value ImAcos01 can return. const float x0 = ImMax(p0.x, rect.Min.x + rounding); if (arc0_b == arc0_e) { draw_list->PathLineTo(ImVec2(x0, p1.y)); draw_list->PathLineTo(ImVec2(x0, p0.y)); } - else if (arc0_b == 0.0f && arc0_e == IM_PI*0.5f) + else if (arc0_b == 0.0f && arc0_e == half_pi) { draw_list->PathArcToFast(ImVec2(x0, p1.y - rounding), rounding, 3, 6); // BL draw_list->PathArcToFast(ImVec2(x0, p0.y + rounding), rounding, 6, 9); // TR @@ -2707,7 +3106,7 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im draw_list->PathLineTo(ImVec2(x1, p0.y)); draw_list->PathLineTo(ImVec2(x1, p1.y)); } - else if (arc1_b == 0.0f && arc1_e == IM_PI*0.5f) + else if (arc1_b == 0.0f && arc1_e == half_pi) { draw_list->PathArcToFast(ImVec2(x1, p0.y + rounding), rounding, 9, 12); // TR draw_list->PathArcToFast(ImVec2(x1, p1.y - rounding), rounding, 0, 3); // BR @@ -2721,35 +3120,49 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im draw_list->PathFillConvex(col); } +// FIXME: Rendering an ellipsis "..." is a surprisingly tricky problem for us... we cannot rely on font glyph having it, +// and regular dot are typically too wide. If we render a dot/shape ourselves it comes with the risk that it wouldn't match +// the boldness or positioning of what the font uses... +void ImGui::RenderPixelEllipsis(ImDrawList* draw_list, ImVec2 pos, ImU32 col, int count) +{ + ImFont* font = draw_list->_Data->Font; + const float font_scale = draw_list->_Data->FontSize / font->FontSize; + pos.y += (float)(int)(font->DisplayOffset.y + font->Ascent * font_scale + 0.5f - 1.0f); + for (int dot_n = 0; dot_n < count; dot_n++) + draw_list->AddRectFilled(ImVec2(pos.x + dot_n * 2.0f, pos.y), ImVec2(pos.x + dot_n * 2.0f + 1.0f, pos.y + 1.0f), col); +} + //----------------------------------------------------------------------------- -// DEFAULT FONT DATA +// [SECTION] Decompression code //----------------------------------------------------------------------------- -// Compressed with stb_compress() then converted to a C array. +// Compressed with stb_compress() then converted to a C array and encoded as base85. // Use the program in misc/fonts/binary_to_compressed_c.cpp to create the array from a TTF file. +// The purpose of encoding as base85 instead of "0x00,0x01,..." style is only save on _source code_ size. // Decompression from stb.h (public domain) by Sean Barrett https://github.com/nothings/stb/blob/master/stb.h //----------------------------------------------------------------------------- -static unsigned int stb_decompress_length(unsigned char *input) +static unsigned int stb_decompress_length(const unsigned char *input) { return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]; } -static unsigned char *stb__barrier, *stb__barrier2, *stb__barrier3, *stb__barrier4; +static unsigned char *stb__barrier_out_e, *stb__barrier_out_b; +static const unsigned char *stb__barrier_in_b; static unsigned char *stb__dout; -static void stb__match(unsigned char *data, unsigned int length) +static void stb__match(const unsigned char *data, unsigned int length) { // INVERSE of memmove... write each byte before copying the next... - IM_ASSERT (stb__dout + length <= stb__barrier); - if (stb__dout + length > stb__barrier) { stb__dout += length; return; } - if (data < stb__barrier4) { stb__dout = stb__barrier+1; return; } + IM_ASSERT(stb__dout + length <= stb__barrier_out_e); + if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; } + if (data < stb__barrier_out_b) { stb__dout = stb__barrier_out_e+1; return; } while (length--) *stb__dout++ = *data++; } -static void stb__lit(unsigned char *data, unsigned int length) +static void stb__lit(const unsigned char *data, unsigned int length) { - IM_ASSERT (stb__dout + length <= stb__barrier); - if (stb__dout + length > stb__barrier) { stb__dout += length; return; } - if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } + IM_ASSERT(stb__dout + length <= stb__barrier_out_e); + if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; } + if (data < stb__barrier_in_b) { stb__dout = stb__barrier_out_e+1; return; } memcpy(stb__dout, data, length); stb__dout += length; } @@ -2758,7 +3171,7 @@ static void stb__lit(unsigned char *data, unsigned int length) #define stb__in3(x) ((i[x] << 16) + stb__in2((x)+1)) #define stb__in4(x) ((i[x] << 24) + stb__in3((x)+1)) -static unsigned char *stb_decompress_token(unsigned char *i) +static const unsigned char *stb_decompress_token(const unsigned char *i) { if (*i >= 0x20) { // use fewer if's for cases that expand small if (*i >= 0x80) stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; @@ -2806,21 +3219,20 @@ static unsigned int stb_adler32(unsigned int adler32, unsigned char *buffer, uns return (unsigned int)(s2 << 16) + (unsigned int)s1; } -static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsigned int length) +static unsigned int stb_decompress(unsigned char *output, const unsigned char *i, unsigned int /*length*/) { unsigned int olen; if (stb__in4(0) != 0x57bC0000) return 0; if (stb__in4(4) != 0) return 0; // error! stream is > 4GB olen = stb_decompress_length(i); - stb__barrier2 = i; - stb__barrier3 = i+length; - stb__barrier = output + olen; - stb__barrier4 = output; + stb__barrier_in_b = i; + stb__barrier_out_e = output + olen; + stb__barrier_out_b = output; i += 16; stb__dout = output; for (;;) { - unsigned char *old_i = i; + const unsigned char *old_i = i; i = stb_decompress_token(i); if (i == old_i) { if (*i == 0x05 && i[1] == 0xfa) { @@ -2840,6 +3252,8 @@ static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsi } } +//----------------------------------------------------------------------------- +// [SECTION] Default font data (ProggyClean.ttf) //----------------------------------------------------------------------------- // ProggyClean.ttf // Copyright (c) 2004, 2005 Tristan Grimmer @@ -2847,7 +3261,8 @@ static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsi // Download and more information at http://upperbounds.net //----------------------------------------------------------------------------- // File: 'ProggyClean.ttf' (41208 bytes) -// Exported using binary_to_compressed_c.cpp +// Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding). +// The purpose of encoding as base85 instead of "0x00,0x01,..." style is only save on _source code_ size. //----------------------------------------------------------------------------- static const char proggy_clean_ttf_compressed_data_base85[11980+1] = "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/" diff --git a/external/imgui/imgui_impl_glfw.cpp b/external/imgui/imgui_impl_glfw.cpp new file mode 100644 index 0000000..a728aa5 --- /dev/null +++ b/external/imgui/imgui_impl_glfw.cpp @@ -0,0 +1,330 @@ +// dear imgui: Platform Binding for GLFW +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) +// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// (Requires: GLFW 3.1+) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. +// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter(). +// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized. +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. +// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. +// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. +// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. +// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. +// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. +// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()). +// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. +// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. +// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. +// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). +// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. +// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. +// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). +// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. + +#include "imgui.h" +#include "imgui_impl_glfw.h" + +// GLFW +#include +#ifdef _WIN32 +#undef APIENTRY +#define GLFW_EXPOSE_NATIVE_WIN32 +#include // for glfwGetWin32Window +#endif +#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING +#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED +#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity +#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale +#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface + +// Data +enum GlfwClientApi +{ + GlfwClientApi_Unknown, + GlfwClientApi_OpenGL, + GlfwClientApi_Vulkan +}; +static GLFWwindow* g_Window = NULL; // Main window +static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; +static double g_Time = 0.0; +static bool g_MouseJustPressed[5] = { false, false, false, false, false }; +static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 }; + +// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. +static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL; +static GLFWscrollfun g_PrevUserCallbackScroll = NULL; +static GLFWkeyfun g_PrevUserCallbackKey = NULL; +static GLFWcharfun g_PrevUserCallbackChar = NULL; + +static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) +{ + return glfwGetClipboardString((GLFWwindow*)user_data); +} + +static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) +{ + glfwSetClipboardString((GLFWwindow*)user_data, text); +} + +void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) +{ + if (g_PrevUserCallbackMousebutton != NULL) + g_PrevUserCallbackMousebutton(window, button, action, mods); + + if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed)) + g_MouseJustPressed[button] = true; +} + +void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) +{ + if (g_PrevUserCallbackScroll != NULL) + g_PrevUserCallbackScroll(window, xoffset, yoffset); + + ImGuiIO& io = ImGui::GetIO(); + io.MouseWheelH += (float)xoffset; + io.MouseWheel += (float)yoffset; +} + +void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (g_PrevUserCallbackKey != NULL) + g_PrevUserCallbackKey(window, key, scancode, action, mods); + + ImGuiIO& io = ImGui::GetIO(); + if (action == GLFW_PRESS) + io.KeysDown[key] = true; + if (action == GLFW_RELEASE) + io.KeysDown[key] = false; + + // Modifiers are not reliable across systems + io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; + io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; + io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; + io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; +} + +void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) +{ + if (g_PrevUserCallbackChar != NULL) + g_PrevUserCallbackChar(window, c); + + ImGuiIO& io = ImGui::GetIO(); + io.AddInputCharacter(c); +} + +static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) +{ + g_Window = window; + g_Time = 0.0; + + // Setup back-end capabilities flags + ImGuiIO& io = ImGui::GetIO(); + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendPlatformName = "imgui_impl_glfw"; + + // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. + io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; + io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; + io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; + io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; + io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; + io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; + io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; + io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; + io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; + io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; + io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; + io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; + io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; + io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; + io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; + io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; + io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; + io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; + io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; + io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; + io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; + + io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; + io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; + io.ClipboardUserData = g_Window; +#if defined(_WIN32) + io.ImeWindowHandle = (void*)glfwGetWin32Window(g_Window); +#endif + + g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); + g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. + g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); + g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); + g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. + g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. + g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); + + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + g_PrevUserCallbackMousebutton = NULL; + g_PrevUserCallbackScroll = NULL; + g_PrevUserCallbackKey = NULL; + g_PrevUserCallbackChar = NULL; + if (install_callbacks) + { + g_PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); + g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); + g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); + g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); + } + + g_ClientApi = client_api; + return true; +} + +bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL); +} + +bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); +} + +void ImGui_ImplGlfw_Shutdown() +{ + for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) + { + glfwDestroyCursor(g_MouseCursors[cursor_n]); + g_MouseCursors[cursor_n] = NULL; + } + g_ClientApi = GlfwClientApi_Unknown; +} + +static void ImGui_ImplGlfw_UpdateMousePosAndButtons() +{ + // Update buttons + ImGuiIO& io = ImGui::GetIO(); + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) + { + // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; + g_MouseJustPressed[i] = false; + } + + // Update mouse position + const ImVec2 mouse_pos_backup = io.MousePos; + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); +#ifdef __EMSCRIPTEN__ + const bool focused = true; // Emscripten +#else + const bool focused = glfwGetWindowAttrib(g_Window, GLFW_FOCUSED) != 0; +#endif + if (focused) + { + if (io.WantSetMousePos) + { + glfwSetCursorPos(g_Window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y); + } + else + { + double mouse_x, mouse_y; + glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); + io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); + } + } +} + +static void ImGui_ImplGlfw_UpdateMouseCursor() +{ + ImGuiIO& io = ImGui::GetIO(); + if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(g_Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + return; + + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + } + else + { + // Show OS mouse cursor + // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. + glfwSetCursor(g_Window, g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); + glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } +} + +static void ImGui_ImplGlfw_UpdateGamepads() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(io.NavInputs, 0, sizeof(io.NavInputs)); + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) + return; + + // Update gamepad inputs + #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } + #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } + int axes_count = 0, buttons_count = 0; + const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); + const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); + MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A + MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B + MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X + MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y + MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left + MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right + MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up + MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down + MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB + MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB + MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB + MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB + MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f); + MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f); + MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f); + MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f); + #undef MAP_BUTTON + #undef MAP_ANALOG + if (axes_count > 0 && buttons_count > 0) + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + else + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; +} + +void ImGui_ImplGlfw_NewFrame() +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); + + // Setup display size (every frame to accommodate for window resizing) + int w, h; + int display_w, display_h; + glfwGetWindowSize(g_Window, &w, &h); + glfwGetFramebufferSize(g_Window, &display_w, &display_h); + io.DisplaySize = ImVec2((float)w, (float)h); + if (w > 0 && h > 0) + io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); + + // Setup time step + double current_time = glfwGetTime(); + io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); + g_Time = current_time; + + ImGui_ImplGlfw_UpdateMousePosAndButtons(); + ImGui_ImplGlfw_UpdateMouseCursor(); + + // Update game controllers (if enabled and available) + ImGui_ImplGlfw_UpdateGamepads(); +} diff --git a/external/imgui/imgui_impl_glfw.h b/external/imgui/imgui_impl_glfw.h new file mode 100644 index 0000000..ccbe840 --- /dev/null +++ b/external/imgui/imgui_impl_glfw.h @@ -0,0 +1,33 @@ +// dear imgui: Platform Binding for GLFW +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) +// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. +// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +// About GLSL version: +// The 'glsl_version' initialization parameter defaults to "#version 150" if NULL. +// Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure! + +#pragma once + +struct GLFWwindow; + +IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); +IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); +IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); + +// InitXXX function with 'install_callbacks=true': install GLFW callbacks. They will call user's previously installed callbacks, if any. +// InitXXX function with 'install_callbacks=false': do not install GLFW callbacks. You will need to call them yourself from your own GLFW callbacks. +IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); +IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); +IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); +IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); diff --git a/external/imgui/imgui_impl_glfw_gl2.cpp b/external/imgui/imgui_impl_glfw_gl2.cpp deleted file mode 100644 index 89d2509..0000000 --- a/external/imgui/imgui_impl_glfw_gl2.cpp +++ /dev/null @@ -1,354 +0,0 @@ -// ImGui GLFW binding with OpenGL (legacy, fixed pipeline) -// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) - -// Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - -// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** -// **Prefer using the code in the opengl3_example/ folder** -// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. -// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more -// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might -// confuse your GPU driver. -// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). -// 2018-02-20: Inputs: Renamed GLFW callbacks exposed in .h to not include GL2 in their name. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL2_RenderDrawData() in the .h file so you can call it yourself. -// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-02-06: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). -// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. -// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. -// 2018-01-09: Misc: Renamed imgui_impl_glfw.* to imgui_impl_glfw_gl2.*. -// 2017-09-01: OpenGL: Save and restore current polygon mode. -// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). -// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. -// 2016-09-10: OpenGL: Uploading font texture as RGBA32 to increase compatibility with users shaders (not ideal). -// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. - -#include "imgui.h" -#include "imgui_impl_glfw_gl2.h" - -// GLFW -#include -#ifdef _WIN32 -#undef APIENTRY -#define GLFW_EXPOSE_NATIVE_WIN32 -#define GLFW_EXPOSE_NATIVE_WGL -#include -#endif - -// GLFW data -static GLFWwindow* g_Window = NULL; -static double g_Time = 0.0f; -static bool g_MouseJustPressed[3] = { false, false, false }; -static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 }; - -// OpenGL data -static GLuint g_FontTexture = 0; - -// OpenGL2 Render function. -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -void ImGui_ImplGlfwGL2_RenderDrawData(ImDrawData* draw_data) -{ - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width == 0 || fb_height == 0) - return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); - - // We are using the OpenGL fixed pipeline to make the example code simpler to read! - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glEnable(GL_SCISSOR_TEST); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnable(GL_TEXTURE_2D); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound - - // Setup viewport, orthographic projection matrix - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0.0f, io.DisplaySize.x, io.DisplaySize.y, 0.0f, -1.0f, +1.0f); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - // Render command lists - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; - const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; - glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos))); - glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv))); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col))); - - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); - } - idx_buffer += pcmd->ElemCount; - } - } - - // Restore modified state - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPopAttrib(); - glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]); - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); -} - -static const char* ImGui_ImplGlfwGL2_GetClipboardText(void* user_data) -{ - return glfwGetClipboardString((GLFWwindow*)user_data); -} - -static void ImGui_ImplGlfwGL2_SetClipboardText(void* user_data, const char* text) -{ - glfwSetClipboardString((GLFWwindow*)user_data, text); -} - -void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/) -{ - if (action == GLFW_PRESS && button >= 0 && button < 3) - g_MouseJustPressed[button] = true; -} - -void ImGui_ImplGlfw_ScrollCallback(GLFWwindow*, double xoffset, double yoffset) -{ - ImGuiIO& io = ImGui::GetIO(); - io.MouseWheelH += (float)xoffset; - io.MouseWheel += (float)yoffset; -} - -void ImGui_ImplGlfw_KeyCallback(GLFWwindow*, int key, int, int action, int mods) -{ - ImGuiIO& io = ImGui::GetIO(); - if (action == GLFW_PRESS) - io.KeysDown[key] = true; - if (action == GLFW_RELEASE) - io.KeysDown[key] = false; - - (void)mods; // Modifiers are not reliable across systems - io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; - io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; - io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; - io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; -} - -void ImGui_ImplGlfw_CharCallback(GLFWwindow*, unsigned int c) -{ - ImGuiIO& io = ImGui::GetIO(); - if (c > 0 && c < 0x10000) - io.AddInputCharacter((unsigned short)c); -} - -bool ImGui_ImplGlfwGL2_CreateDeviceObjects() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. - - // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; - - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); - - return true; -} - -void ImGui_ImplGlfwGL2_InvalidateDeviceObjects() -{ - if (g_FontTexture) - { - glDeleteTextures(1, &g_FontTexture); - ImGui::GetIO().Fonts->TexID = 0; - g_FontTexture = 0; - } -} - -static void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) -{ - glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); - glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); - glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); - glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); -} - -bool ImGui_ImplGlfwGL2_Init(GLFWwindow* window, bool install_callbacks) -{ - g_Window = window; - - ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. - io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; - io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; - io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; - io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; - io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; - io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; - io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; - io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; - io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; - io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; - io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; - io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; - io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; - io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; - io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; - io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; - io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; - io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; - - io.SetClipboardTextFn = ImGui_ImplGlfwGL2_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplGlfwGL2_GetClipboardText; - io.ClipboardUserData = g_Window; -#ifdef _WIN32 - io.ImeWindowHandle = glfwGetWin32Window(g_Window); -#endif - - // Load cursors - // FIXME: GLFW doesn't expose suitable cursors for ResizeAll, ResizeNESW, ResizeNWSE. We revert to arrow cursor for those. - g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - - if (install_callbacks) - ImGui_ImplGlfw_InstallCallbacks(window); - - return true; -} - -void ImGui_ImplGlfwGL2_Shutdown() -{ - // Destroy GLFW mouse cursors - for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) - glfwDestroyCursor(g_MouseCursors[cursor_n]); - memset(g_MouseCursors, 0, sizeof(g_MouseCursors)); - - // Destroy OpenGL objects - ImGui_ImplGlfwGL2_InvalidateDeviceObjects(); -} - -void ImGui_ImplGlfwGL2_NewFrame() -{ - if (!g_FontTexture) - ImGui_ImplGlfwGL2_CreateDeviceObjects(); - - ImGuiIO& io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - int w, h; - int display_w, display_h; - glfwGetWindowSize(g_Window, &w, &h); - glfwGetFramebufferSize(g_Window, &display_w, &display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); - - // Setup time step - double current_time = glfwGetTime(); - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); - g_Time = current_time; - - // Setup inputs - // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents()) - if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) - { - if (io.WantMoveMouse) - { - glfwSetCursorPos(g_Window, (double)io.MousePos.x, (double)io.MousePos.y); // Set mouse position if requested by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) - } - else - { - double mouse_x, mouse_y; - glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); - } - } - else - { - io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX); - } - - for (int i = 0; i < 3; i++) - { - // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; - g_MouseJustPressed[i] = false; - } - - // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor - ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); - if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) - { - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); - } - else - { - glfwSetCursor(g_Window, g_MouseCursors[cursor] ? g_MouseCursors[cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - } - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); -} diff --git a/external/imgui/imgui_impl_glfw_gl2.h b/external/imgui/imgui_impl_glfw_gl2.h deleted file mode 100644 index 4e0f393..0000000 --- a/external/imgui/imgui_impl_glfw_gl2.h +++ /dev/null @@ -1,32 +0,0 @@ -// ImGui GLFW binding with OpenGL (legacy, fixed pipeline) -// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) - -// Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - -// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** -// **Prefer using the code in the opengl3_example/ folder** -// See imgui_impl_glfw.cpp for details. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -struct GLFWwindow; - -IMGUI_API bool ImGui_ImplGlfwGL2_Init(GLFWwindow* window, bool install_callbacks); -IMGUI_API void ImGui_ImplGlfwGL2_Shutdown(); -IMGUI_API void ImGui_ImplGlfwGL2_NewFrame(); -IMGUI_API void ImGui_ImplGlfwGL2_RenderDrawData(ImDrawData* draw_data); - -// Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplGlfwGL2_InvalidateDeviceObjects(); -IMGUI_API bool ImGui_ImplGlfwGL2_CreateDeviceObjects(); - -// GLFW callbacks (registered by default to GLFW if you enable 'install_callbacks' during initialization) -// Provided here if you want to chain callbacks yourself. You may also handle inputs yourself and use those as a reference. -IMGUI_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); -IMGUI_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); -IMGUI_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); -IMGUI_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); diff --git a/external/imgui/imgui_impl_glfw_gl3.cpp b/external/imgui/imgui_impl_glfw_gl3.cpp deleted file mode 100644 index 011d1eb..0000000 --- a/external/imgui/imgui_impl_glfw_gl3.cpp +++ /dev/null @@ -1,518 +0,0 @@ -// ImGui GLFW binding with OpenGL3 + shaders -// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) -// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) - -// Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplGlfwGL3_Init() so user can override the GLSL version e.g. "#version 150". -// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. -// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). -// 2018-02-20: Inputs: Renamed GLFW callbacks exposed in .h to not include GL3 in their name. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL3_RenderDrawData() in the .h file so you can call it yourself. -// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. -// 2018-01-25: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). -// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. -// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. -// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. (Also changed GL context from 3.3 to 3.2 in example's main.cpp) -// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. -// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). -// 2017-05-01: OpenGL: Fixed save and restore of current blend function state. -// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. -// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. -// 2016-04-30: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "imgui.h" -#include "imgui_impl_glfw_gl3.h" - -// GLAD -#include - -#include -#ifdef _WIN32 -#undef APIENTRY -#define GLFW_EXPOSE_NATIVE_WIN32 -#define GLFW_EXPOSE_NATIVE_WGL -#include -#endif - -// GLFW data -static GLFWwindow* g_Window = NULL; -static double g_Time = 0.0f; -static bool g_MouseJustPressed[3] = { false, false, false }; -static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 }; - -// OpenGL3 data -static char g_GlslVersion[32] = "#version 150"; -static GLuint g_FontTexture = 0; -static int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; -static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; -static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; -static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; - -// OpenGL3 Render function. -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -void ImGui_ImplGlfwGL3_RenderDrawData(ImDrawData* draw_data) -{ - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width == 0 || fb_height == 0) - return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); - - // Backup GL state - GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); - glActiveTexture(GL_TEXTURE0); - GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); - GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer); - GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); - GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); - GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); - GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); - GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); - GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); - GLboolean last_enable_blend = glIsEnabled(GL_BLEND); - GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); - GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); - GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); - - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill - glEnable(GL_BLEND); - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glEnable(GL_SCISSOR_TEST); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - - // Setup viewport, orthographic projection matrix - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); - const float ortho_projection[4][4] = - { - { 2.0f/io.DisplaySize.x, 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f/-io.DisplaySize.y, 0.0f, 0.0f }, - { 0.0f, 0.0f, -1.0f, 0.0f }, - {-1.0f, 1.0f, 0.0f, 1.0f }, - }; - glUseProgram(g_ShaderHandle); - glUniform1i(g_AttribLocationTex, 0); - glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); - glBindSampler(0, 0); // Rely on combined texture/sampler state. - - // Recreate the VAO every time - // (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.) - GLuint vao_handle = 0; - glGenVertexArrays(1, &vao_handle); - glBindVertexArray(vao_handle); - glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); - glEnableVertexAttribArray(g_AttribLocationPosition); - glEnableVertexAttribArray(g_AttribLocationUV); - glEnableVertexAttribArray(g_AttribLocationColor); - glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); - glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); - glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); - - // Draw - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawIdx* idx_buffer_offset = 0; - - glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); - - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); - } - idx_buffer_offset += pcmd->ElemCount; - } - } - glDeleteVertexArrays(1, &vao_handle); - - // Restore modified GL state - glUseProgram(last_program); - glBindTexture(GL_TEXTURE_2D, last_texture); - glBindSampler(0, last_sampler); - glActiveTexture(last_active_texture); - glBindVertexArray(last_vertex_array); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer); - glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); - glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); - if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); - if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); - if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); - if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); - glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); -} - -static const char* ImGui_ImplGlfwGL3_GetClipboardText(void* user_data) -{ - return glfwGetClipboardString((GLFWwindow*)user_data); -} - -static void ImGui_ImplGlfwGL3_SetClipboardText(void* user_data, const char* text) -{ - glfwSetClipboardString((GLFWwindow*)user_data, text); -} - -void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/) -{ - if (action == GLFW_PRESS && button >= 0 && button < 3) - g_MouseJustPressed[button] = true; -} - -void ImGui_ImplGlfw_ScrollCallback(GLFWwindow*, double xoffset, double yoffset) -{ - ImGuiIO& io = ImGui::GetIO(); - io.MouseWheelH += (float)xoffset; - io.MouseWheel += (float)yoffset; -} - -void ImGui_ImplGlfw_KeyCallback(GLFWwindow*, int key, int, int action, int mods) -{ - ImGuiIO& io = ImGui::GetIO(); - if (action == GLFW_PRESS) - io.KeysDown[key] = true; - if (action == GLFW_RELEASE) - io.KeysDown[key] = false; - - (void)mods; // Modifiers are not reliable across systems - io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; - io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; - io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; - io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; -} - -void ImGui_ImplGlfw_CharCallback(GLFWwindow*, unsigned int c) -{ - ImGuiIO& io = ImGui::GetIO(); - if (c > 0 && c < 0x10000) - io.AddInputCharacter((unsigned short)c); -} - -bool ImGui_ImplGlfwGL3_CreateFontsTexture() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. - - // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; - - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); - - return true; -} - -bool ImGui_ImplGlfwGL3_CreateDeviceObjects() -{ - // Backup GL state - GLint last_texture, last_array_buffer, last_vertex_array; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); - - const GLchar* vertex_shader = - "uniform mat4 ProjMtx;\n" - "in vec2 Position;\n" - "in vec2 UV;\n" - "in vec4 Color;\n" - "out vec2 Frag_UV;\n" - "out vec4 Frag_Color;\n" - "void main()\n" - "{\n" - " Frag_UV = UV;\n" - " Frag_Color = Color;\n" - " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" - "}\n"; - - const GLchar* fragment_shader = - "uniform sampler2D Texture;\n" - "in vec2 Frag_UV;\n" - "in vec4 Frag_Color;\n" - "out vec4 Out_Color;\n" - "void main()\n" - "{\n" - " Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n" - "}\n"; - - const GLchar* vertex_shader_with_version[2] = { g_GlslVersion, vertex_shader }; - const GLchar* fragment_shader_with_version[2] = { g_GlslVersion, fragment_shader }; - - g_ShaderHandle = glCreateProgram(); - g_VertHandle = glCreateShader(GL_VERTEX_SHADER); - g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL); - glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL); - glCompileShader(g_VertHandle); - glCompileShader(g_FragHandle); - glAttachShader(g_ShaderHandle, g_VertHandle); - glAttachShader(g_ShaderHandle, g_FragHandle); - glLinkProgram(g_ShaderHandle); - - g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); - g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); - g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position"); - g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV"); - g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color"); - - glGenBuffers(1, &g_VboHandle); - glGenBuffers(1, &g_ElementsHandle); - - ImGui_ImplGlfwGL3_CreateFontsTexture(); - - // Restore modified GL state - glBindTexture(GL_TEXTURE_2D, last_texture); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - glBindVertexArray(last_vertex_array); - - return true; -} - -void ImGui_ImplGlfwGL3_InvalidateDeviceObjects() -{ - if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle); - if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle); - g_VboHandle = g_ElementsHandle = 0; - - if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle); - if (g_VertHandle) glDeleteShader(g_VertHandle); - g_VertHandle = 0; - - if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle); - if (g_FragHandle) glDeleteShader(g_FragHandle); - g_FragHandle = 0; - - if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle); - g_ShaderHandle = 0; - - if (g_FontTexture) - { - glDeleteTextures(1, &g_FontTexture); - ImGui::GetIO().Fonts->TexID = 0; - g_FontTexture = 0; - } -} - -static void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) -{ - glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); - glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); - glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); - glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); -} - -bool ImGui_ImplGlfwGL3_Init(GLFWwindow* window, bool install_callbacks, const char* glsl_version) -{ - g_Window = window; - - // Store GL version string so we can refer to it later in case we recreate shaders. - if (glsl_version == NULL) - glsl_version = "#version 150"; - IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersion)); - strcpy(g_GlslVersion, glsl_version); - strcat(g_GlslVersion, "\n"); - - // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. - ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; - io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; - io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; - io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; - io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; - io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; - io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; - io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; - io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; - io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; - io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; - io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; - io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; - io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; - io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; - io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; - io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; - io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; - io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; - - io.SetClipboardTextFn = ImGui_ImplGlfwGL3_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplGlfwGL3_GetClipboardText; - io.ClipboardUserData = g_Window; -#ifdef _WIN32 - io.ImeWindowHandle = glfwGetWin32Window(g_Window); -#endif - - // Load cursors - // FIXME: GLFW doesn't expose suitable cursors for ResizeAll, ResizeNESW, ResizeNWSE. We revert to arrow cursor for those. - g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - - if (install_callbacks) - ImGui_ImplGlfw_InstallCallbacks(window); - - return true; -} - -void ImGui_ImplGlfwGL3_Shutdown() -{ - // Destroy GLFW mouse cursors - for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) - glfwDestroyCursor(g_MouseCursors[cursor_n]); - memset(g_MouseCursors, 0, sizeof(g_MouseCursors)); - - // Destroy OpenGL objects - ImGui_ImplGlfwGL3_InvalidateDeviceObjects(); -} - -void ImGui_ImplGlfwGL3_NewFrame() -{ - if (!g_FontTexture) - ImGui_ImplGlfwGL3_CreateDeviceObjects(); - - ImGuiIO& io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - int w, h; - int display_w, display_h; - glfwGetWindowSize(g_Window, &w, &h); - glfwGetFramebufferSize(g_Window, &display_w, &display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); - - // Setup time step - double current_time = glfwGetTime(); - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); - g_Time = current_time; - - // Setup inputs - // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents()) - if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) - { - if (io.WantMoveMouse) - { - glfwSetCursorPos(g_Window, (double)io.MousePos.x, (double)io.MousePos.y); // Set mouse position if requested by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) - } - else - { - double mouse_x, mouse_y; - glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); - } - } - else - { - io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX); - } - - for (int i = 0; i < 3; i++) - { - // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; - g_MouseJustPressed[i] = false; - } - - // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor - ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); - if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) - { - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); - } - else - { - glfwSetCursor(g_Window, g_MouseCursors[cursor] ? g_MouseCursors[cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - } - - // Gamepad navigation mapping [BETA] - memset(io.NavInputs, 0, sizeof(io.NavInputs)); - if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) - { - // Update gamepad inputs - #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } - #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } - int axes_count = 0, buttons_count = 0; - const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); - const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); - MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A - MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B - MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X - MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y - MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left - MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right - MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up - MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down - MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB - MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB - MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB - MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB - MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f); - MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f); - MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f); - MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f); - #undef MAP_BUTTON - #undef MAP_ANALOG - } - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); -} diff --git a/external/imgui/imgui_impl_glfw_gl3.h b/external/imgui/imgui_impl_glfw_gl3.h deleted file mode 100644 index 33b5329..0000000 --- a/external/imgui/imgui_impl_glfw_gl3.h +++ /dev/null @@ -1,31 +0,0 @@ -// ImGui GLFW binding with OpenGL3 + shaders -// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) -// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) - -// Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -struct GLFWwindow; - -IMGUI_API bool ImGui_ImplGlfwGL3_Init(GLFWwindow* window, bool install_callbacks, const char* glsl_version = NULL); -IMGUI_API void ImGui_ImplGlfwGL3_Shutdown(); -IMGUI_API void ImGui_ImplGlfwGL3_NewFrame(); -IMGUI_API void ImGui_ImplGlfwGL3_RenderDrawData(ImDrawData* draw_data); - -// Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplGlfwGL3_InvalidateDeviceObjects(); -IMGUI_API bool ImGui_ImplGlfwGL3_CreateDeviceObjects(); - -// GLFW callbacks (installed by default if you enable 'install_callbacks' during initialization) -// Provided here if you want to chain callbacks. -// You can also handle inputs yourself and use those as a reference. -IMGUI_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); -IMGUI_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); -IMGUI_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); -IMGUI_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); diff --git a/external/imgui/imgui_impl_opengl2.cpp b/external/imgui/imgui_impl_opengl2.cpp new file mode 100644 index 0000000..95720ff --- /dev/null +++ b/external/imgui/imgui_impl_opengl2.cpp @@ -0,0 +1,244 @@ +// dear imgui: Renderer for OpenGL2 (legacy OpenGL, fixed pipeline) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** +// **Prefer using the code in imgui_impl_opengl3.cpp** +// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. +// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more +// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might +// confuse your GPU driver. +// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. +// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display. +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. +// 2018-08-03: OpenGL: Disabling/restoring GL_LIGHTING and GL_COLOR_MATERIAL to increase compatibility with legacy OpenGL applications. +// 2018-06-08: Misc: Extracted imgui_impl_opengl2.cpp/.h away from the old combined GLFW/SDL+OpenGL2 examples. +// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL2_RenderDrawData() in the .h file so you can call it yourself. +// 2017-09-01: OpenGL: Save and restore current polygon mode. +// 2016-09-10: OpenGL: Uploading font texture as RGBA32 to increase compatibility with users shaders (not ideal). +// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. + +#include "imgui.h" +#include "imgui_impl_opengl2.h" +#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier +#include // intptr_t +#else +#include // intptr_t +#endif + +// Include OpenGL header (without an OpenGL loader) requires a bit of fiddling +#if defined(_WIN32) && !defined(APIENTRY) +#define APIENTRY __stdcall // It is customary to use APIENTRY for OpenGL function pointer declarations on all platforms. Additionally, the Windows OpenGL header needs APIENTRY. +#endif +#if defined(_WIN32) && !defined(WINGDIAPI) +#define WINGDIAPI __declspec(dllimport) // Some Windows OpenGL headers need this +#endif +#if defined(__APPLE__) +#define GL_SILENCE_DEPRECATION +#include +#else +#include +#endif + +// OpenGL Data +static GLuint g_FontTexture = 0; + +// Functions +bool ImGui_ImplOpenGL2_Init() +{ + // Setup back-end capabilities flags + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_opengl2"; + return true; +} + +void ImGui_ImplOpenGL2_Shutdown() +{ + ImGui_ImplOpenGL2_DestroyDeviceObjects(); +} + +void ImGui_ImplOpenGL2_NewFrame() +{ + if (!g_FontTexture) + ImGui_ImplOpenGL2_CreateDeviceObjects(); +} + +static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height) +{ + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glDisable(GL_COLOR_MATERIAL); + glEnable(GL_SCISSOR_TEST); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnable(GL_TEXTURE_2D); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + // If you are using this code with non-legacy OpenGL header/contexts (which you should not, prefer using imgui_impl_opengl3.cpp!!), + // you may need to backup/reset/restore current shader using the lines below. DO NOT MODIFY THIS FILE! Add the code in your calling function: + // GLint last_program; + // glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); + // glUseProgram(0); + // ImGui_ImplOpenGL2_RenderDrawData(...); + // glUseProgram(last_program) + + // Setup viewport, orthographic projection matrix + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(draw_data->DisplayPos.x, draw_data->DisplayPos.x + draw_data->DisplaySize.x, draw_data->DisplayPos.y + draw_data->DisplaySize.y, draw_data->DisplayPos.y, -1.0f, +1.0f); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); +} + +// OpenGL2 Render function. +// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) +// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. +void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) +{ + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); + if (fb_width == 0 || fb_height == 0) + return; + + // Backup GL state + GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); + GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); + GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); + glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); + + // Setup desired GL state + ImGui_ImplOpenGL2_SetupRenderState(draw_data, fb_width, fb_height); + + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) + + // Render command lists + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; + const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; + glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos))); + glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv))); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col))); + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplOpenGL2_SetupRenderState(draw_data, fb_width, fb_height); + else + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec4 clip_rect; + clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; + clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; + clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; + clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; + + if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) + { + // Apply scissor/clipping rectangle + glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); + + // Bind texture, Draw + glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); + } + } + idx_buffer += pcmd->ElemCount; + } + } + + // Restore modified GL state + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glPopAttrib(); + glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]); + glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); + glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); +} + +bool ImGui_ImplOpenGL2_CreateFontsTexture() +{ + // Build texture atlas + ImGuiIO& io = ImGui::GetIO(); + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system + GLint last_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGenTextures(1, &g_FontTexture); + glBindTexture(GL_TEXTURE_2D, g_FontTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Store our identifier + io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture; + + // Restore state + glBindTexture(GL_TEXTURE_2D, last_texture); + + return true; +} + +void ImGui_ImplOpenGL2_DestroyFontsTexture() +{ + if (g_FontTexture) + { + ImGuiIO& io = ImGui::GetIO(); + glDeleteTextures(1, &g_FontTexture); + io.Fonts->TexID = 0; + g_FontTexture = 0; + } +} + +bool ImGui_ImplOpenGL2_CreateDeviceObjects() +{ + return ImGui_ImplOpenGL2_CreateFontsTexture(); +} + +void ImGui_ImplOpenGL2_DestroyDeviceObjects() +{ + ImGui_ImplOpenGL2_DestroyFontsTexture(); +} diff --git a/external/imgui/imgui_impl_opengl2.h b/external/imgui/imgui_impl_opengl2.h new file mode 100644 index 0000000..2e3271d --- /dev/null +++ b/external/imgui/imgui_impl_opengl2.h @@ -0,0 +1,30 @@ +// dear imgui: Renderer for OpenGL2 (legacy OpenGL, fixed pipeline) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** +// **Prefer using the code in imgui_impl_opengl3.cpp** +// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. +// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more +// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might +// confuse your GPU driver. +// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. + +#pragma once + +IMGUI_IMPL_API bool ImGui_ImplOpenGL2_Init(); +IMGUI_IMPL_API void ImGui_ImplOpenGL2_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplOpenGL2_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data); + +// Called by Init/NewFrame/Shutdown +IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateFontsTexture(); +IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyFontsTexture(); +IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyDeviceObjects(); diff --git a/external/imgui/imgui_impl_opengl3.cpp b/external/imgui/imgui_impl_opengl3.cpp new file mode 100644 index 0000000..80c4f56 --- /dev/null +++ b/external/imgui/imgui_impl_opengl3.cpp @@ -0,0 +1,632 @@ +// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline +// - Desktop GL: 3.x 4.x +// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bits indices. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. +// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. +// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop. +// 2019-03-15: OpenGL: Added a dummy GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early. +// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0). +// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader. +// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display. +// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450). +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. +// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN. +// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used. +// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES". +// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation. +// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link. +// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples. +// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. +// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state. +// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer. +// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150". +// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. +// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. +// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. +// 2017-05-01: OpenGL: Fixed save and restore of current blend func state. +// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. +// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. +// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752) + +//---------------------------------------- +// OpenGL GLSL GLSL +// version version string +//---------------------------------------- +// 2.0 110 "#version 110" +// 2.1 120 "#version 120" +// 3.0 130 "#version 130" +// 3.1 140 "#version 140" +// 3.2 150 "#version 150" +// 3.3 330 "#version 330 core" +// 4.0 400 "#version 400 core" +// 4.1 410 "#version 410 core" +// 4.2 420 "#version 410 core" +// 4.3 430 "#version 430 core" +// ES 2.0 100 "#version 100" = WebGL 1.0 +// ES 3.0 300 "#version 300 es" = WebGL 2.0 +//---------------------------------------- + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "imgui.h" +#include "imgui_impl_opengl3.h" +#include +#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier +#include // intptr_t +#else +#include // intptr_t +#endif +#if defined(__APPLE__) +#include "TargetConditionals.h" +#endif + +// Auto-detect GL version +#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) +#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) +#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" +#elif defined(__EMSCRIPTEN__) +#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" +#endif +#endif + +#if defined(IMGUI_IMPL_OPENGL_ES2) +#include +#elif defined(IMGUI_IMPL_OPENGL_ES3) +#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) +#include // Use GL ES 3 +#else +#include // Use GL ES 3 +#endif +#else +// About Desktop OpenGL function loaders: +// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers. +// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad). +// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. +#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) +#include // Needs to be initialized with gl3wInit() in user's code +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) +#include // Needs to be initialized with glewInit() in user's code +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) +#include // Needs to be initialized with gladLoadGL() in user's code +#else +#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM +#endif +#endif + +// Desktop GL has glDrawElementsBaseVertex() which GL ES and WebGL don't have. +#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3) +#define IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX 0 +#else +#define IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX 1 +#endif + +// OpenGL Data +static char g_GlslVersionString[32] = ""; +static GLuint g_FontTexture = 0; +static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; +static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location +static int g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location +static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; + +// Functions +bool ImGui_ImplOpenGL3_Init(const char* glsl_version) +{ + // Setup back-end capabilities flags + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_opengl3"; +#if IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. +#endif + + // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. +#if defined(IMGUI_IMPL_OPENGL_ES2) + if (glsl_version == NULL) + glsl_version = "#version 100"; +#elif defined(IMGUI_IMPL_OPENGL_ES3) + if (glsl_version == NULL) + glsl_version = "#version 300 es"; +#else + if (glsl_version == NULL) + glsl_version = "#version 130"; +#endif + IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); + strcpy(g_GlslVersionString, glsl_version); + strcat(g_GlslVersionString, "\n"); + + // Make a dummy GL call (we don't actually need the result) + // IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code. + // Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above. + GLint current_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture); + + return true; +} + +void ImGui_ImplOpenGL3_Shutdown() +{ + ImGui_ImplOpenGL3_DestroyDeviceObjects(); +} + +void ImGui_ImplOpenGL3_NewFrame() +{ + if (!g_FontTexture) + ImGui_ImplOpenGL3_CreateDeviceObjects(); +} + +static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object) +{ + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); +#ifdef GL_POLYGON_MODE + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +#endif + + // Setup viewport, orthographic projection matrix + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; + const float ortho_projection[4][4] = + { + { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, + { 0.0f, 0.0f, -1.0f, 0.0f }, + { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, + }; + glUseProgram(g_ShaderHandle); + glUniform1i(g_AttribLocationTex, 0); + glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); +#ifdef GL_SAMPLER_BINDING + glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. +#endif + + (void)vertex_array_object; +#ifndef IMGUI_IMPL_OPENGL_ES2 + glBindVertexArray(vertex_array_object); +#endif + + // Bind vertex/index buffers and setup attributes for ImDrawVert + glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); + glEnableVertexAttribArray(g_AttribLocationVtxPos); + glEnableVertexAttribArray(g_AttribLocationVtxUV); + glEnableVertexAttribArray(g_AttribLocationVtxColor); + glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); + glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); + glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); +} + +// OpenGL3 Render function. +// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) +// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. +void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) +{ + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); + if (fb_width <= 0 || fb_height <= 0) + return; + + // Backup GL state + GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); + glActiveTexture(GL_TEXTURE0); + GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); + GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); +#ifdef GL_SAMPLER_BINDING + GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); +#endif + GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_ES2 + GLint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object); +#endif +#ifdef GL_POLYGON_MODE + GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); +#endif + GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); + GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); + GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); + GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); + GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); + GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); + GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); + GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); + GLboolean last_enable_blend = glIsEnabled(GL_BLEND); + GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); + GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); + GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + bool clip_origin_lower_left = true; +#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__) + GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) + if (last_clip_origin == GL_UPPER_LEFT) + clip_origin_lower_left = false; +#endif + + // Setup desired GL state + // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts) + // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. + GLuint vertex_array_object = 0; +#ifndef IMGUI_IMPL_OPENGL_ES2 + glGenVertexArrays(1, &vertex_array_object); +#endif + ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); + + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) + + // Render command lists + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + + // Upload vertex/index buffers + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback != NULL) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); + else + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec4 clip_rect; + clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; + clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; + clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; + clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; + + if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) + { + // Apply scissor/clipping rectangle + if (clip_origin_lower_left) + glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); + else + glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT) + + // Bind texture, Draw + glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); +#if IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX + glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset); +#else + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx))); +#endif + } + } + } + } + + // Destroy the temporary VAO +#ifndef IMGUI_IMPL_OPENGL_ES2 + glDeleteVertexArrays(1, &vertex_array_object); +#endif + + // Restore modified GL state + glUseProgram(last_program); + glBindTexture(GL_TEXTURE_2D, last_texture); +#ifdef GL_SAMPLER_BINDING + glBindSampler(0, last_sampler); +#endif + glActiveTexture(last_active_texture); +#ifndef IMGUI_IMPL_OPENGL_ES2 + glBindVertexArray(last_vertex_array_object); +#endif + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); + glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); + glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); + if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); + if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); + if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); + if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); +#ifdef GL_POLYGON_MODE + glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); +#endif + glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); + glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); +} + +bool ImGui_ImplOpenGL3_CreateFontsTexture() +{ + // Build texture atlas + ImGuiIO& io = ImGui::GetIO(); + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system + GLint last_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGenTextures(1, &g_FontTexture); + glBindTexture(GL_TEXTURE_2D, g_FontTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +#ifdef GL_UNPACK_ROW_LENGTH + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +#endif + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Store our identifier + io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture; + + // Restore state + glBindTexture(GL_TEXTURE_2D, last_texture); + + return true; +} + +void ImGui_ImplOpenGL3_DestroyFontsTexture() +{ + if (g_FontTexture) + { + ImGuiIO& io = ImGui::GetIO(); + glDeleteTextures(1, &g_FontTexture); + io.Fonts->TexID = 0; + g_FontTexture = 0; + } +} + +// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file. +static bool CheckShader(GLuint handle, const char* desc) +{ + GLint status = 0, log_length = 0; + glGetShaderiv(handle, GL_COMPILE_STATUS, &status); + glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); + if ((GLboolean)status == GL_FALSE) + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc); + if (log_length > 1) + { + ImVector buf; + buf.resize((int)(log_length + 1)); + glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); + fprintf(stderr, "%s\n", buf.begin()); + } + return (GLboolean)status == GL_TRUE; +} + +// If you get an error please report on GitHub. You may try different GL context version or GLSL version. +static bool CheckProgram(GLuint handle, const char* desc) +{ + GLint status = 0, log_length = 0; + glGetProgramiv(handle, GL_LINK_STATUS, &status); + glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); + if ((GLboolean)status == GL_FALSE) + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString); + if (log_length > 1) + { + ImVector buf; + buf.resize((int)(log_length + 1)); + glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); + fprintf(stderr, "%s\n", buf.begin()); + } + return (GLboolean)status == GL_TRUE; +} + +bool ImGui_ImplOpenGL3_CreateDeviceObjects() +{ + // Backup GL state + GLint last_texture, last_array_buffer; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_ES2 + GLint last_vertex_array; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); +#endif + + // Parse GLSL version string + int glsl_version = 130; + sscanf(g_GlslVersionString, "#version %d", &glsl_version); + + const GLchar* vertex_shader_glsl_120 = + "uniform mat4 ProjMtx;\n" + "attribute vec2 Position;\n" + "attribute vec2 UV;\n" + "attribute vec4 Color;\n" + "varying vec2 Frag_UV;\n" + "varying vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_130 = + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 UV;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_300_es = + "precision mediump float;\n" + "layout (location = 0) in vec2 Position;\n" + "layout (location = 1) in vec2 UV;\n" + "layout (location = 2) in vec4 Color;\n" + "uniform mat4 ProjMtx;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_410_core = + "layout (location = 0) in vec2 Position;\n" + "layout (location = 1) in vec2 UV;\n" + "layout (location = 2) in vec4 Color;\n" + "uniform mat4 ProjMtx;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_120 = + "#ifdef GL_ES\n" + " precision mediump float;\n" + "#endif\n" + "uniform sampler2D Texture;\n" + "varying vec2 Frag_UV;\n" + "varying vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_130 = + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main()\n" + "{\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_300_es = + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "layout (location = 0) out vec4 Out_Color;\n" + "void main()\n" + "{\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_410_core = + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "uniform sampler2D Texture;\n" + "layout (location = 0) out vec4 Out_Color;\n" + "void main()\n" + "{\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + // Select shaders matching our GLSL versions + const GLchar* vertex_shader = NULL; + const GLchar* fragment_shader = NULL; + if (glsl_version < 130) + { + vertex_shader = vertex_shader_glsl_120; + fragment_shader = fragment_shader_glsl_120; + } + else if (glsl_version >= 410) + { + vertex_shader = vertex_shader_glsl_410_core; + fragment_shader = fragment_shader_glsl_410_core; + } + else if (glsl_version == 300) + { + vertex_shader = vertex_shader_glsl_300_es; + fragment_shader = fragment_shader_glsl_300_es; + } + else + { + vertex_shader = vertex_shader_glsl_130; + fragment_shader = fragment_shader_glsl_130; + } + + // Create shaders + const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader }; + g_VertHandle = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL); + glCompileShader(g_VertHandle); + CheckShader(g_VertHandle, "vertex shader"); + + const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader }; + g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL); + glCompileShader(g_FragHandle); + CheckShader(g_FragHandle, "fragment shader"); + + g_ShaderHandle = glCreateProgram(); + glAttachShader(g_ShaderHandle, g_VertHandle); + glAttachShader(g_ShaderHandle, g_FragHandle); + glLinkProgram(g_ShaderHandle); + CheckProgram(g_ShaderHandle, "shader program"); + + g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); + g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); + g_AttribLocationVtxPos = glGetAttribLocation(g_ShaderHandle, "Position"); + g_AttribLocationVtxUV = glGetAttribLocation(g_ShaderHandle, "UV"); + g_AttribLocationVtxColor = glGetAttribLocation(g_ShaderHandle, "Color"); + + // Create buffers + glGenBuffers(1, &g_VboHandle); + glGenBuffers(1, &g_ElementsHandle); + + ImGui_ImplOpenGL3_CreateFontsTexture(); + + // Restore modified GL state + glBindTexture(GL_TEXTURE_2D, last_texture); + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_ES2 + glBindVertexArray(last_vertex_array); +#endif + + return true; +} + +void ImGui_ImplOpenGL3_DestroyDeviceObjects() +{ + if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle); + if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle); + g_VboHandle = g_ElementsHandle = 0; + + if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle); + if (g_VertHandle) glDeleteShader(g_VertHandle); + g_VertHandle = 0; + + if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle); + if (g_FragHandle) glDeleteShader(g_FragHandle); + g_FragHandle = 0; + + if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle); + g_ShaderHandle = 0; + + ImGui_ImplOpenGL3_DestroyFontsTexture(); +} diff --git a/external/imgui/imgui_impl_opengl3.h b/external/imgui/imgui_impl_opengl3.h new file mode 100644 index 0000000..1e202b7 --- /dev/null +++ b/external/imgui/imgui_impl_opengl3.h @@ -0,0 +1,47 @@ +// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline +// - Desktop GL: 3.x 4.x +// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bits indices. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +// About Desktop OpenGL function loaders: +// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers. +// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad). +// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. + +// About GLSL version: +// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. +// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" +// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. + +#pragma once + +// Specific OpenGL versions +//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten +//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android + +// Set default OpenGL3 loader to be glad +#if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) +#define IMGUI_IMPL_OPENGL_LOADER_GLAD +#endif + +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); + +// Called by Init/NewFrame/Shutdown +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); diff --git a/external/imgui/imgui_internal.h b/external/imgui/imgui_internal.h index 79fc4d9..3b96116 100644 --- a/external/imgui/imgui_internal.h +++ b/external/imgui/imgui_internal.h @@ -1,127 +1,209 @@ -// dear imgui, v1.60 WIP -// (internals) +// dear imgui, v1.72 WIP +// (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! // Set: // #define IMGUI_DEFINE_MATH_OPERATORS // To implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) +/* + +Index of this file: +// Header mess +// Forward declarations +// STB libraries includes +// Context pointer +// Generic helpers +// Misc data structures +// Main imgui context +// Tab bar, tab item +// Internal API + +*/ + #pragma once +//----------------------------------------------------------------------------- +// Header mess +//----------------------------------------------------------------------------- + #ifndef IMGUI_VERSION #error Must include imgui.h before imgui_internal.h #endif #include // FILE* +#include // NULL, malloc, free, qsort, atoi, atof #include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf #include // INT_MIN, INT_MAX +// Visual Studio warnings #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) #endif -#ifdef __clang__ +// Clang/GCC warnings with -Weverything +#if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h #pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h #pragma clang diagnostic ignored "-Wold-style-cast" +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif +#if __has_warning("-Wdouble-promotion") +#pragma clang diagnostic ignored "-Wdouble-promotion" +#endif +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind +#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #endif //----------------------------------------------------------------------------- -// Forward Declarations +// Forward declarations //----------------------------------------------------------------------------- -struct ImRect; -struct ImGuiColMod; -struct ImGuiStyleMod; -struct ImGuiGroupData; -struct ImGuiMenuColumns; -struct ImGuiDrawContext; -struct ImGuiTextEditState; -struct ImGuiPopupRef; -struct ImGuiWindow; -struct ImGuiWindowSettings; +struct ImRect; // An axis-aligned rectangle (2 points) +struct ImDrawDataBuilder; // Helper to build a ImDrawData instance +struct ImDrawListSharedData; // Data shared between all ImDrawList instances +struct ImGuiColorMod; // Stacked color modifier, backup of modified data so we can restore it +struct ImGuiColumnData; // Storage data for a single column +struct ImGuiColumns; // Storage data for a columns set +struct ImGuiContext; // Main Dear ImGui context +struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum +struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() +struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box +struct ImGuiItemHoveredDataBackup; // Backup and restore IsItemHovered() internal data +struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only +struct ImGuiNavMoveResult; // Result of a directional navigation move query result +struct ImGuiNextWindowData; // Storage for SetNextWindow** functions +struct ImGuiNextItemData; // Storage for SetNextItem** functions +struct ImGuiPopupData; // Storage for current popup stack +struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file +struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it +struct ImGuiTabBar; // Storage for a tab bar +struct ImGuiTabItem; // Storage for a tab item (within a tab bar) +struct ImGuiWindow; // Storage for one window +struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame) +struct ImGuiWindowSettings; // Storage for window settings stored in .ini file (we keep one of those even if the actual window wasn't instanced during this session) -typedef int ImGuiLayoutType; // enum: horizontal or vertical // enum ImGuiLayoutType_ -typedef int ImGuiButtonFlags; // flags: for ButtonEx(), ButtonBehavior() // enum ImGuiButtonFlags_ -typedef int ImGuiItemFlags; // flags: for PushItemFlag() // enum ImGuiItemFlags_ -typedef int ImGuiItemStatusFlags; // flags: storage for DC.LastItemXXX // enum ImGuiItemStatusFlags_ -typedef int ImGuiNavHighlightFlags; // flags: for RenderNavHighlight() // enum ImGuiNavHighlightFlags_ -typedef int ImGuiNavDirSourceFlags; // flags: for GetNavInputAmount2d() // enum ImGuiNavDirSourceFlags_ -typedef int ImGuiSeparatorFlags; // flags: for Separator() - internal // enum ImGuiSeparatorFlags_ -typedef int ImGuiSliderFlags; // flags: for SliderBehavior() // enum ImGuiSliderFlags_ +// Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. +typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical +typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for ButtonEx(), ButtonBehavior() +typedef int ImGuiDragFlags; // -> enum ImGuiDragFlags_ // Flags: for DragBehavior() +typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() +typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags +typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() +typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // Flags: for GetNavInputAmount2d() +typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests +typedef int ImGuiNextItemDataFlags; // -> enum ImGuiNextItemDataFlags_ // Flags: for SetNextItemXXX() functions +typedef int ImGuiNextWindowDataFlags; // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions +typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for SeparatorEx() +typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for SliderBehavior() +typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() //------------------------------------------------------------------------- -// STB libraries +// STB libraries includes //------------------------------------------------------------------------- -namespace ImGuiStb +namespace ImStb { #undef STB_TEXTEDIT_STRING #undef STB_TEXTEDIT_CHARTYPE -#define STB_TEXTEDIT_STRING ImGuiTextEditState +#define STB_TEXTEDIT_STRING ImGuiInputTextState #define STB_TEXTEDIT_CHARTYPE ImWchar #define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f -#include "stb_textedit.h" +#define STB_TEXTEDIT_UNDOSTATECOUNT 99 +#define STB_TEXTEDIT_UNDOCHARCOUNT 999 +#include "imstb_textedit.h" -} // namespace ImGuiStb +} // namespace ImStb //----------------------------------------------------------------------------- -// Context +// Context pointer //----------------------------------------------------------------------------- #ifndef GImGui -extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointer +extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #endif //----------------------------------------------------------------------------- -// Helpers +// Generic helpers //----------------------------------------------------------------------------- #define IM_PI 3.14159265358979323846f #ifdef _WIN32 -#define IM_NEWLINE "\r\n" // Play it nice with Windows users (2018: Notepad _still_ doesn't display files properly when they use Unix-style carriage returns) +#define IM_NEWLINE "\r\n" // Play it nice with Windows users (2018/05 news: Microsoft announced that Notepad will finally display Unix-style carriage returns!) #else #define IM_NEWLINE "\n" #endif +#define IM_TABSIZE (4) + +#define IMGUI_DEBUG_LOG(_FMT,...) printf("[%05d] " _FMT, GImGui->FrameCount, __VA_ARGS__) +#define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1] +#define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose +#define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 + +// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall +#ifdef _MSC_VER +#define IMGUI_CDECL __cdecl +#else +#define IMGUI_CDECL +#endif // Helpers: UTF-8 <> wchar IMGUI_API int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count -IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count +IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // read one character. return input UTF-8 bytes count IMGUI_API int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) -IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string as UTF-8 code-points +IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 +IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 // Helpers: Misc -IMGUI_API ImU32 ImHash(const void* data, int data_size, ImU32 seed = 0); // Pass data_size==0 for zero-terminated strings -IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size = NULL, int padding_bytes = 0); +IMGUI_API ImU32 ImHashData(const void* data, size_t data_size, ImU32 seed = 0); +IMGUI_API ImU32 ImHashStr(const char* data, size_t data_size = 0, ImU32 seed = 0); +IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size = NULL, int padding_bytes = 0); IMGUI_API FILE* ImFileOpen(const char* filename, const char* file_open_mode); -static inline bool ImCharIsSpace(int c) { return c == ' ' || c == '\t' || c == 0x3000; } +static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } +static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; } static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } +#define ImQsort qsort +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +static inline ImU32 ImHash(const void* data, int size, ImU32 seed = 0) { return size ? ImHashData(data, (size_t)size, seed) : ImHashStr((const char*)data, 0, seed); } // [moved to ImHashStr/ImHashData in 1.68] +#endif // Helpers: Geometry IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p); IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); IMGUI_API ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); IMGUI_API void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w); +IMGUI_API ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy); // Helpers: String IMGUI_API int ImStricmp(const char* str1, const char* str2); IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); IMGUI_API char* ImStrdup(const char* str); -IMGUI_API char* ImStrchrRange(const char* str_begin, const char* str_end, char c); +IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str); +IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); IMGUI_API int ImStrlenW(const ImWchar* str); -IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line +IMGUI_API const char* ImStreolRange(const char* str, const char* str_end); // End end-of-line +IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); +IMGUI_API void ImStrTrimBlanks(char* str); IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3); IMGUI_API int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3); +IMGUI_API const char* ImParseFormatFindStart(const char* format); +IMGUI_API const char* ImParseFormatFindEnd(const char* format); +IMGUI_API const char* ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size); +IMGUI_API int ImParseFormatPrecision(const char* format, int default_value); -// Helpers: Math -// We are keeping those not leaking to the user by default, in the case the user has implicit cast operators between ImVec2 and its own types (when IM_VEC2_CLASS_EXTRA is defined) +// Helpers: ImVec2/ImVec4 operators +// We are keeping those disabled by default so they don't leak in user space, to allow user enabling implicit cast operators between ImVec2 and their own types (using IM_VEC2_CLASS_EXTRA etc.) +// We unfortunately don't have a unary- operator for ImVec2 because this would needs to be defined inside the class itself. #ifdef IMGUI_DEFINE_MATH_OPERATORS static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x*rhs, lhs.y*rhs); } static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x/rhs, lhs.y/rhs); } @@ -138,50 +220,97 @@ static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x*rhs.x, lhs.y*rhs.y, lhs.z*rhs.z, lhs.w*rhs.w); } #endif -static inline int ImMin(int lhs, int rhs) { return lhs < rhs ? lhs : rhs; } -static inline int ImMax(int lhs, int rhs) { return lhs >= rhs ? lhs : rhs; } -static inline float ImMin(float lhs, float rhs) { return lhs < rhs ? lhs : rhs; } -static inline float ImMax(float lhs, float rhs) { return lhs >= rhs ? lhs : rhs; } +// Helpers: Maths +// - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy) +#ifndef IMGUI_DISABLE_MATH_FUNCTIONS +static inline float ImFabs(float x) { return fabsf(x); } +static inline float ImSqrt(float x) { return sqrtf(x); } +static inline float ImPow(float x, float y) { return powf(x, y); } +static inline double ImPow(double x, double y) { return pow(x, y); } +static inline float ImFmod(float x, float y) { return fmodf(x, y); } +static inline double ImFmod(double x, double y) { return fmod(x, y); } +static inline float ImCos(float x) { return cosf(x); } +static inline float ImSin(float x) { return sinf(x); } +static inline float ImAcos(float x) { return acosf(x); } +static inline float ImAtan2(float y, float x) { return atan2f(y, x); } +static inline double ImAtof(const char* s) { return atof(s); } +static inline float ImFloorStd(float x) { return floorf(x); } // we already uses our own ImFloor() { return (float)(int)v } internally so the standard one wrapper is named differently (it's used by stb_truetype) +static inline float ImCeil(float x) { return ceilf(x); } +#endif +// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support for variety of types: signed/unsigned int/long long float/double +// (Exceptionally using templates here but we could also redefine them for variety of types) +template static inline T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } +template static inline T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; } +template static inline T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } +template static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); } +template static inline void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; } +template static inline T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; } +template static inline T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; } +// - Misc maths helpers static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); } static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); } -static inline int ImClamp(int v, int mn, int mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } -static inline float ImClamp(float v, float mn, float mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } -static inline ImVec2 ImClamp(const ImVec2& f, const ImVec2& mn, ImVec2 mx) { return ImVec2(ImClamp(f.x,mn.x,mx.x), ImClamp(f.y,mn.y,mx.y)); } -static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } -static inline void ImSwap(int& a, int& b) { int tmp = a; a = b; b = tmp; } -static inline void ImSwap(float& a, float& b) { float tmp = a; a = b; b = tmp; } -static inline int ImLerp(int a, int b, float t) { return (int)(a + (b - a) * t); } -static inline float ImLerp(float a, float b, float t) { return a + (b - a) * t; } +static inline ImVec2 ImClamp(const ImVec2& v, const ImVec2& mn, ImVec2 mx) { return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); } static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); } static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); } +static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; } static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; } -static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / sqrtf(d); return fail_value; } +static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / ImSqrt(d); return fail_value; } static inline float ImFloor(float f) { return (float)(int)f; } static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } +static inline int ImModPositive(int a, int b) { return (a + b) % b; } static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } -// We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. -// Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. -struct ImNewPlacementDummy {}; -inline void* operator new(size_t, ImNewPlacementDummy, void* ptr) { return ptr; } -inline void operator delete(void*, ImNewPlacementDummy, void*) {} // This is only required so we can use the symetrical new() -#define IM_PLACEMENT_NEW(_PTR) new(ImNewPlacementDummy(), _PTR) -#define IM_NEW(_TYPE) new(ImNewPlacementDummy(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE -template void IM_DELETE(T*& p) { if (p) { p->~T(); ImGui::MemFree(p); p = NULL; } } +// Helper: ImBoolVector. Store 1-bit per value. +// Note that Resize() currently clears the whole vector. +struct ImBoolVector +{ + ImVector Storage; + ImBoolVector() { } + void Resize(int sz) { Storage.resize((sz + 31) >> 5); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); } + void Clear() { Storage.clear(); } + bool GetBit(int n) const { int off = (n >> 5); int mask = 1 << (n & 31); return (Storage[off] & mask) != 0; } + void SetBit(int n, bool v) { int off = (n >> 5); int mask = 1 << (n & 31); if (v) Storage[off] |= mask; else Storage[off] &= ~mask; } +}; + +// Helper: ImPool<>. Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer, +// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object. +typedef int ImPoolIdx; +template +struct IMGUI_API ImPool +{ + ImVector Data; // Contiguous data + ImGuiStorage Map; // ID->Index + ImPoolIdx FreeIdx; // Next free idx to use + + ImPool() { FreeIdx = 0; } + ~ImPool() { Clear(); } + T* GetByKey(ImGuiID key) { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Data[idx] : NULL; } + T* GetByIndex(ImPoolIdx n) { return &Data[n]; } + ImPoolIdx GetIndex(const T* p) const { IM_ASSERT(p >= Data.Data && p < Data.Data + Data.Size); return (ImPoolIdx)(p - Data.Data); } + T* GetOrAddByKey(ImGuiID key) { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Data[*p_idx]; *p_idx = FreeIdx; return Add(); } + bool Contains(const T* p) const { return (p >= Data.Data && p < Data.Data + Data.Size); } + void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Data[idx].~T(); } Map.Clear(); Data.clear(); FreeIdx = 0; } + T* Add() { int idx = FreeIdx; if (idx == Data.Size) { Data.resize(Data.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Data[idx]; } IM_PLACEMENT_NEW(&Data[idx]) T(); return &Data[idx]; } + void Remove(ImGuiID key, const T* p) { Remove(key, GetIndex(p)); } + void Remove(ImGuiID key, ImPoolIdx idx) { Data[idx].~T(); *(int*)&Data[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); } + void Reserve(int capacity) { Data.reserve(capacity); Map.Data.reserve(capacity); } + int GetSize() const { return Data.Size; } +}; //----------------------------------------------------------------------------- -// Types +// Misc data structures //----------------------------------------------------------------------------- enum ImGuiButtonFlags_ { + ImGuiButtonFlags_None = 0, ImGuiButtonFlags_Repeat = 1 << 0, // hold to repeat - ImGuiButtonFlags_PressedOnClickRelease = 1 << 1, // return true on click + release on same item [DEFAULT if no PressedOn* flag is set] + ImGuiButtonFlags_PressedOnClickRelease = 1 << 1, // [Default] return true on click + release on same item ImGuiButtonFlags_PressedOnClick = 1 << 2, // return true on click (default requires click+release) ImGuiButtonFlags_PressedOnRelease = 1 << 3, // return true on release (default requires click+release) ImGuiButtonFlags_PressedOnDoubleClick = 1 << 4, // return true on double-click (default requires click+release) @@ -193,17 +322,26 @@ enum ImGuiButtonFlags_ ImGuiButtonFlags_NoKeyModifiers = 1 << 10, // disable interaction if a key modifier is held ImGuiButtonFlags_NoHoldingActiveID = 1 << 11, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only) ImGuiButtonFlags_PressedOnDragDropHold = 1 << 12, // press when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers) - ImGuiButtonFlags_NoNavFocus = 1 << 13 // don't override navigation focus when activated + ImGuiButtonFlags_NoNavFocus = 1 << 13, // don't override navigation focus when activated + ImGuiButtonFlags_NoHoveredOnNav = 1 << 14 // don't report as hovered when navigated on }; enum ImGuiSliderFlags_ { + ImGuiSliderFlags_None = 0, ImGuiSliderFlags_Vertical = 1 << 0 }; +enum ImGuiDragFlags_ +{ + ImGuiDragFlags_None = 0, + ImGuiDragFlags_Vertical = 1 << 0 +}; + enum ImGuiColumnsFlags_ { // Default: 0 + ImGuiColumnsFlags_None = 0, ImGuiColumnsFlags_NoBorder = 1 << 0, // Disable column dividers ImGuiColumnsFlags_NoResize = 1 << 1, // Disable resizing columns when clicking on the dividers ImGuiColumnsFlags_NoPreserveWidths = 1 << 2, // Disable column width preservation when adjusting columns @@ -211,35 +349,89 @@ enum ImGuiColumnsFlags_ ImGuiColumnsFlags_GrowParentContentsSize= 1 << 4 // (WIP) Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove. }; +// Extend ImGuiSelectableFlags_ enum ImGuiSelectableFlagsPrivate_ { // NB: need to be in sync with last value of ImGuiSelectableFlags_ - ImGuiSelectableFlags_Menu = 1 << 3, // -> PressedOnClick - ImGuiSelectableFlags_MenuItem = 1 << 4, // -> PressedOnRelease - ImGuiSelectableFlags_Disabled = 1 << 5, - ImGuiSelectableFlags_DrawFillAvailWidth = 1 << 6 + ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20, + ImGuiSelectableFlags_PressedOnClick = 1 << 21, + ImGuiSelectableFlags_PressedOnRelease = 1 << 22, + ImGuiSelectableFlags_DrawFillAvailWidth = 1 << 23, // FIXME: We may be able to remove this (added in 6251d379 for menus) + ImGuiSelectableFlags_AllowItemOverlap = 1 << 24 +}; + +// Extend ImGuiTreeNodeFlags_ +enum ImGuiTreeNodeFlagsPrivate_ +{ + ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 20 }; enum ImGuiSeparatorFlags_ { + ImGuiSeparatorFlags_None = 0, ImGuiSeparatorFlags_Horizontal = 1 << 0, // Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar - ImGuiSeparatorFlags_Vertical = 1 << 1 + ImGuiSeparatorFlags_Vertical = 1 << 1, + ImGuiSeparatorFlags_SpanAllColumns = 1 << 2 +}; + +// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). +// This is going to be exposed in imgui.h when stabilized enough. +enum ImGuiItemFlags_ +{ + ImGuiItemFlags_NoTabStop = 1 << 0, // false + ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. + ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211 + ImGuiItemFlags_NoNav = 1 << 3, // false + ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false + ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window + ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) + ImGuiItemFlags_Default_ = 0 }; // Storage for LastItem data enum ImGuiItemStatusFlags_ { + ImGuiItemStatusFlags_None = 0, ImGuiItemStatusFlags_HoveredRect = 1 << 0, - ImGuiItemStatusFlags_HasDisplayRect = 1 << 1 + ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, + ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) + ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected" because reporting the change allows us to handle clipping with less issues. + ImGuiItemStatusFlags_HasDeactivated = 1 << 4, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. + ImGuiItemStatusFlags_Deactivated = 1 << 5 // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. + +#ifdef IMGUI_ENABLE_TEST_ENGINE + , // [imgui-test only] + ImGuiItemStatusFlags_Openable = 1 << 10, // + ImGuiItemStatusFlags_Opened = 1 << 11, // + ImGuiItemStatusFlags_Checkable = 1 << 12, // + ImGuiItemStatusFlags_Checked = 1 << 13 // +#endif +}; + +enum ImGuiTextFlags_ +{ + ImGuiTextFlags_None = 0, + ImGuiTextFlags_NoWidthForLargeClippedText = 1 << 0 }; // FIXME: this is in development, not exposed/functional as a generic feature yet. +// Horizontal/Vertical enums are fixed to 0/1 so they may be used to index ImVec2 enum ImGuiLayoutType_ { - ImGuiLayoutType_Vertical, - ImGuiLayoutType_Horizontal + ImGuiLayoutType_Horizontal = 0, + ImGuiLayoutType_Vertical = 1 }; +enum ImGuiLogType +{ + ImGuiLogType_None = 0, + ImGuiLogType_TTY, + ImGuiLogType_File, + ImGuiLogType_Buffer, + ImGuiLogType_Clipboard +}; + +// X/Y enums are fixed to 0/1 so they may be used to index ImVec2 enum ImGuiAxis { ImGuiAxis_None = -1, @@ -253,13 +445,6 @@ enum ImGuiPlotType ImGuiPlotType_Histogram }; -enum ImGuiDataType -{ - ImGuiDataType_Int, - ImGuiDataType_Float, - ImGuiDataType_Float2 -}; - enum ImGuiInputSource { ImGuiInputSource_None = 0, @@ -267,7 +452,7 @@ enum ImGuiInputSource ImGuiInputSource_Nav, ImGuiInputSource_NavKeyboard, // Only used occasionally for storage, not tested/handled by most code ImGuiInputSource_NavGamepad, // " - ImGuiInputSource_COUNT, + ImGuiInputSource_COUNT }; // FIXME-NAV: Clarify/expose various repeat delay/rate @@ -283,19 +468,32 @@ enum ImGuiInputReadMode enum ImGuiNavHighlightFlags_ { + ImGuiNavHighlightFlags_None = 0, ImGuiNavHighlightFlags_TypeDefault = 1 << 0, ImGuiNavHighlightFlags_TypeThin = 1 << 1, - ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, + ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. ImGuiNavHighlightFlags_NoRounding = 1 << 3 }; enum ImGuiNavDirSourceFlags_ { + ImGuiNavDirSourceFlags_None = 0, ImGuiNavDirSourceFlags_Keyboard = 1 << 0, ImGuiNavDirSourceFlags_PadDPad = 1 << 1, ImGuiNavDirSourceFlags_PadLStick = 1 << 2 }; +enum ImGuiNavMoveFlags_ +{ + ImGuiNavMoveFlags_None = 0, + ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side + ImGuiNavMoveFlags_LoopY = 1 << 1, + ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) + ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful for provided for completeness + ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) + ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5 // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible. +}; + enum ImGuiNavForward { ImGuiNavForward_None, @@ -303,6 +501,27 @@ enum ImGuiNavForward ImGuiNavForward_ForwardActive }; +enum ImGuiNavLayer +{ + ImGuiNavLayer_Main = 0, // Main scrolling layer + ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu) + ImGuiNavLayer_COUNT +}; + +enum ImGuiPopupPositionPolicy +{ + ImGuiPopupPositionPolicy_Default, + ImGuiPopupPositionPolicy_ComboBox +}; + +// 1D vector (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches) +struct ImVec1 +{ + float x; + ImVec1() { x = 0.0f; } + ImVec1(float _x) { x = _x; } +}; + // 2D axis aligned bounding-box // NB: we can't rely on ImVec2 math operators being available here struct IMGUI_API ImRect @@ -330,16 +549,25 @@ struct IMGUI_API ImRect void Add(const ImRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; } void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } - void Translate(const ImVec2& v) { Min.x += v.x; Min.y += v.y; Max.x += v.x; Max.y += v.y; } + void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; } + void TranslateX(float dx) { Min.x += dx; Max.x += dx; } + void TranslateY(float dy) { Min.y += dy; Max.y += dy; } void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display. void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } - void FixInverted() { if (Min.x > Max.x) ImSwap(Min.x, Max.x); if (Min.y > Max.y) ImSwap(Min.y, Max.y); } bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } }; +// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo(). +struct ImGuiDataTypeInfo +{ + size_t Size; // Size in byte + const char* PrintFmt; // Default printf format for the type + const char* ScanFmt; // Default scanf format for the type +}; + // Stacked color modifier, backup of modified data so we can restore it -struct ImGuiColMod +struct ImGuiColorMod { ImGuiCol Col; ImVec4 BackupValue; @@ -355,27 +583,26 @@ struct ImGuiStyleMod ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; } }; -// Stacked data for BeginGroup()/EndGroup() +// Stacked storage data for BeginGroup()/EndGroup() struct ImGuiGroupData { ImVec2 BackupCursorPos; ImVec2 BackupCursorMaxPos; - float BackupIndentX; - float BackupGroupOffsetX; - float BackupCurrentLineHeight; - float BackupCurrentLineTextBaseOffset; - float BackupLogLinePosY; - bool BackupActiveIdIsAlive; - bool AdvanceCursor; + ImVec1 BackupIndent; + ImVec1 BackupGroupOffset; + ImVec2 BackupCurrLineSize; + float BackupCurrLineTextBaseOffset; + ImGuiID BackupActiveIdIsAlive; + bool BackupActiveIdPreviousFrameIsAlive; + bool EmitItem; }; -// Simple column measurement currently used for MenuItem() only. This is very short-sighted/throw-away code and NOT a generic helper. +// Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper. struct IMGUI_API ImGuiMenuColumns { - int Count; float Spacing; float Width, NextWidth; - float Pos[4], NextWidths[4]; + float Pos[3], NextWidths[3]; ImGuiMenuColumns(); void Update(int count, float spacing, bool clear); @@ -384,63 +611,74 @@ struct IMGUI_API ImGuiMenuColumns }; // Internal state of the currently focused/edited text input box -struct IMGUI_API ImGuiTextEditState +struct IMGUI_API ImGuiInputTextState { - ImGuiID Id; // widget id owning the text state - ImVector Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. - ImVector InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) - ImVector TempTextBuffer; - int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. - int BufSizeA; // end-user buffer size - float ScrollX; - ImGuiStb::STB_TexteditState StbState; - float CursorAnim; - bool CursorFollow; - bool SelectedAllMouseLock; + ImGuiID ID; // widget id owning the text state + int CurLenW, CurLenA; // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 len is valid even if TextA is not. + ImVector TextW; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. + ImVector TextA; // temporary UTF8 buffer for callbacks and other operations. this is not updated in every code-path! size=capacity. + ImVector InitialTextA; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) + bool TextAIsValid; // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument) + int BufCapacityA; // end-user buffer capacity + float ScrollX; // horizontal scrolling/offset + ImStb::STB_TexteditState Stb; // state for stb_textedit.h + float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately + bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) + bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection - ImGuiTextEditState() { memset(this, 0, sizeof(*this)); } + // Temporarily set when active + ImGuiInputTextFlags UserFlags; + ImGuiInputTextCallback UserCallback; + void* UserCallbackData; + + ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } + void ClearFreeMemory() { TextW.clear(); TextA.clear(); InitialTextA.clear(); } void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking - void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); } - bool HasSelection() const { return StbState.select_start != StbState.select_end; } - void ClearSelection() { StbState.select_start = StbState.select_end = StbState.cursor; } - void SelectAll() { StbState.select_start = 0; StbState.cursor = StbState.select_end = CurLenW; StbState.has_preferred_x = false; } - void OnKeyPressed(int key); + void CursorClamp() { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); } + bool HasSelection() const { return Stb.select_start != Stb.select_end; } + void ClearSelection() { Stb.select_start = Stb.select_end = Stb.cursor; } + void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; } + int GetUndoAvailCount() const { return Stb.undostate.undo_point; } + int GetRedoAvailCount() const { return STB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; } + void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation }; -// Data saved in imgui.ini file +// Windows data saved in imgui.ini file struct ImGuiWindowSettings { char* Name; - ImGuiID Id; + ImGuiID ID; ImVec2 Pos; ImVec2 Size; bool Collapsed; - ImGuiWindowSettings() { Name = NULL; Id = 0; Pos = Size = ImVec2(0,0); Collapsed = false; } + ImGuiWindowSettings() { Name = NULL; ID = 0; Pos = Size = ImVec2(0,0); Collapsed = false; } }; struct ImGuiSettingsHandler { - const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' - ImGuiID TypeHash; // == ImHash(TypeName, 0, 0) - void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); - void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); - void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); + const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' + ImGuiID TypeHash; // == ImHashStr(TypeName) + void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); // Read: Called when entering into a new ini entry e.g. "[Window][Name]" + void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry + void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' void* UserData; ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); } }; // Storage for current popup stack -struct ImGuiPopupRef +struct ImGuiPopupData { ImGuiID PopupId; // Set on OpenPopup() ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() - ImGuiWindow* ParentWindow; // Set on OpenPopup() + ImGuiWindow* SourceWindow; // Set on OpenPopup() copy of NavWindow at the time of opening the popup int OpenFrameCount; // Set on OpenPopup() - ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differenciate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) + ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup + + ImGuiPopupData() { PopupId = 0; Window = SourceWindow = NULL; OpenFrameCount = -1; OpenParentId = 0; } }; struct ImGuiColumnData @@ -450,10 +688,10 @@ struct ImGuiColumnData ImGuiColumnsFlags Flags; // Not exposed ImRect ClipRect; - ImGuiColumnData() { OffsetNorm = OffsetNormBeforeResize = 0.0f; Flags = 0; } + ImGuiColumnData() { OffsetNorm = OffsetNormBeforeResize = 0.0f; Flags = ImGuiColumnsFlags_None; } }; -struct ImGuiColumnsSet +struct ImGuiColumns { ImGuiID ID; ImGuiColumnsFlags Flags; @@ -461,29 +699,32 @@ struct ImGuiColumnsSet bool IsBeingResized; int Current; int Count; - float MinX, MaxX; - float StartPosY; - float StartMaxPosX; // Backup of CursorMaxPos - float CellMinY, CellMaxY; + float OffMinX, OffMaxX; // Offsets from HostWorkRect.Min.x + float LineMinY, LineMaxY; + float HostCursorPosY; // Backup of CursorPos at the time of BeginColumns() + float HostCursorMaxPosX; // Backup of CursorMaxPos at the time of BeginColumns() + ImRect HostClipRect; // Backup of ClipRect at the time of BeginColumns() + ImRect HostWorkRect; // Backup of WorkRect at the time of BeginColumns() ImVector Columns; - ImGuiColumnsSet() { Clear(); } + ImGuiColumns() { Clear(); } void Clear() { ID = 0; - Flags = 0; + Flags = ImGuiColumnsFlags_None; IsFirstFrame = false; IsBeingResized = false; Current = 0; Count = 1; - MinX = MaxX = 0.0f; - StartPosY = 0.0f; - StartMaxPosX = 0.0f; - CellMinY = CellMaxY = 0.0f; + OffMinX = OffMaxX = 0.0f; + LineMinY = LineMaxY = 0.0f; + HostCursorPosY = 0.0f; + HostCursorMaxPosX = 0.0f; Columns.clear(); } }; +// Data shared between all ImDrawList instances struct IMGUI_API ImDrawListSharedData { ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas @@ -491,6 +732,7 @@ struct IMGUI_API ImDrawListSharedData float FontSize; // Current/default font size (optional, for simplified AddText overload) float CurveTessellationTol; ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen() + ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) // Const data // FIXME: Bake rounded corners fill/borders in atlas @@ -511,7 +753,7 @@ struct ImDrawDataBuilder struct ImGuiNavMoveResult { ImGuiID ID; // Best candidate - ImGuiID ParentID; // Best candidate window->IDStack.back() - to compare context + ImGuiID SelectScopeId;// Best candidate window current selectable group ID ImGuiWindow* Window; // Best candidate window float DistBox; // Best candidate box distance to current NavId float DistCenter; // Best candidate center distance to current NavId @@ -519,51 +761,89 @@ struct ImGuiNavMoveResult ImRect RectRel; // Best candidate bounding box in window relative space ImGuiNavMoveResult() { Clear(); } - void Clear() { ID = ParentID = 0; Window = NULL; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); } + void Clear() { ID = SelectScopeId = 0; Window = NULL; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); } +}; + +enum ImGuiNextWindowDataFlags_ +{ + ImGuiNextWindowDataFlags_None = 0, + ImGuiNextWindowDataFlags_HasPos = 1 << 0, + ImGuiNextWindowDataFlags_HasSize = 1 << 1, + ImGuiNextWindowDataFlags_HasContentSize = 1 << 2, + ImGuiNextWindowDataFlags_HasCollapsed = 1 << 3, + ImGuiNextWindowDataFlags_HasSizeConstraint = 1 << 4, + ImGuiNextWindowDataFlags_HasFocus = 1 << 5, + ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6 }; // Storage for SetNexWindow** functions struct ImGuiNextWindowData { - ImGuiCond PosCond; - ImGuiCond SizeCond; - ImGuiCond ContentSizeCond; - ImGuiCond CollapsedCond; - ImGuiCond SizeConstraintCond; - ImGuiCond FocusCond; - ImGuiCond BgAlphaCond; - ImVec2 PosVal; - ImVec2 PosPivotVal; - ImVec2 SizeVal; - ImVec2 ContentSizeVal; - bool CollapsedVal; - ImRect SizeConstraintRect; // Valid if 'SetNextWindowSizeConstraint' is true - ImGuiSizeCallback SizeCallback; - void* SizeCallbackUserData; - float BgAlphaVal; - - ImGuiNextWindowData() - { - PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = 0; - PosVal = PosPivotVal = SizeVal = ImVec2(0.0f, 0.0f); - ContentSizeVal = ImVec2(0.0f, 0.0f); - CollapsedVal = false; - SizeConstraintRect = ImRect(); - SizeCallback = NULL; - SizeCallbackUserData = NULL; - BgAlphaVal = FLT_MAX; - } - - void Clear() - { - PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = 0; - } + ImGuiNextWindowDataFlags Flags; + ImGuiCond PosCond; + ImGuiCond SizeCond; + ImGuiCond CollapsedCond; + ImVec2 PosVal; + ImVec2 PosPivotVal; + ImVec2 SizeVal; + ImVec2 ContentSizeVal; + bool CollapsedVal; + ImRect SizeConstraintRect; + ImGuiSizeCallback SizeCallback; + void* SizeCallbackUserData; + float BgAlphaVal; + ImVec2 MenuBarOffsetMinVal; // *Always on* This is not exposed publicly, so we don't clear it. + + ImGuiNextWindowData() { memset(this, 0, sizeof(*this)); } + inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; } }; -// Main state for ImGui +enum ImGuiNextItemDataFlags_ +{ + ImGuiNextItemDataFlags_None = 0, + ImGuiNextItemDataFlags_HasWidth = 1 << 0, + ImGuiNextItemDataFlags_HasOpen = 1 << 1 +}; + +struct ImGuiNextItemData +{ + ImGuiNextItemDataFlags Flags; + float Width; // Set by SetNextItemWidth(). + bool OpenVal; // Set by SetNextItemOpen() function. + ImGuiCond OpenCond; + + ImGuiNextItemData() { memset(this, 0, sizeof(*this)); } + inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; } +}; + +//----------------------------------------------------------------------------- +// Tabs +//----------------------------------------------------------------------------- + +struct ImGuiShrinkWidthItem +{ + int Index; + float Width; +}; + +struct ImGuiTabBarRef +{ + ImGuiTabBar* Ptr; // Either field can be set, not both. Dock node tab bars are loose while BeginTabBar() ones are in a pool. + int IndexInMainPool; + + ImGuiTabBarRef(ImGuiTabBar* ptr) { Ptr = ptr; IndexInMainPool = -1; } + ImGuiTabBarRef(int index_in_main_pool) { Ptr = NULL; IndexInMainPool = index_in_main_pool; } +}; + +//----------------------------------------------------------------------------- +// Main imgui context +//----------------------------------------------------------------------------- + struct ImGuiContext { bool Initialized; + bool FrameScopeActive; // Set by NewFrame(), cleared by EndFrame() + bool FrameScopePushedImplicitWindow; // Set by NewFrame(), cleared by EndFrame() bool FontAtlasOwnedByContext; // Io.Fonts-> is owned by the ImGuiContext and will be destructed along with it. ImGuiIO IO; ImGuiStyle Style; @@ -571,12 +851,14 @@ struct ImGuiContext float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height. ImDrawListSharedData DrawListSharedData; - - float Time; + double Time; int FrameCount; int FrameCountEnded; int FrameCountRendered; - ImVector Windows; + + // Windows state + ImVector Windows; // Windows, sorted in display order, back to front + ImVector WindowsFocusOrder; // Windows, sorted in focus order, back to front ImVector WindowsSortBuffer; ImVector CurrentWindowStack; ImGuiStorage WindowsById; @@ -584,29 +866,45 @@ struct ImGuiContext ImGuiWindow* CurrentWindow; // Being drawn into ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) + ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actually window that is moved is generally MovingWindow->RootWindow. + + // Item/widgets state and tracking information ImGuiID HoveredId; // Hovered widget bool HoveredIdAllowOverlap; ImGuiID HoveredIdPreviousFrame; - float HoveredIdTimer; + float HoveredIdTimer; // Measure contiguous hovering time + float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active ImGuiID ActiveId; // Active widget - ImGuiID ActiveIdPreviousFrame; + ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame) float ActiveIdTimer; - bool ActiveIdIsAlive; // Active widget has been seen this frame bool ActiveIdIsJustActivated; // Set at the time of activation for one frame bool ActiveIdAllowOverlap; // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always) + bool ActiveIdHasBeenPressedBefore; // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch. + bool ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state. + bool ActiveIdHasBeenEditedThisFrame; int ActiveIdAllowNavDirFlags; // Active widget allows using directional navigation (e.g. can activate a button and move away from it) + int ActiveIdBlockNavInputFlags; ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) ImGuiWindow* ActiveIdWindow; ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard) - ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actually window that is moved is generally MovingWindow->RootWindow. - ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() + ImGuiID ActiveIdPreviousFrame; + bool ActiveIdPreviousFrameIsAlive; + bool ActiveIdPreviousFrameHasBeenEditedBefore; + ImGuiWindow* ActiveIdPreviousFrameWindow; + + ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation. + float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. + + // Next window/item data + ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions + ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions + + // Shared stacks + ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() ImVector FontStack; // Stack for PushFont()/PopFont() - ImVector OpenPopupStack; // Which popups are open (persistent) - ImVector CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame) - ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions - bool NextTreeNodeOpenVal; // Storage for SetNextTreeNode** functions - ImGuiCond NextTreeNodeOpenCond; + ImVectorOpenPopupStack; // Which popups are open (persistent) + ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) // Navigation data (for gamepad/keyboard) ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' @@ -616,19 +914,22 @@ struct ImGuiContext ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0 ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0 ImGuiID NavJustTabbedId; // Just tabbed to this id. - ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame - ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest) + ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). + ImGuiID NavJustMovedToMultiSelectScopeId; // Just navigated to this select scope id (result of a successfully MoveRequest). + ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. + ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. int NavScoringCount; // Metrics for debugging - ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most. - float NavWindowingHighlightTimer; + ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed top-most. + ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f + ImGuiWindow* NavWindowingList; + float NavWindowingTimer; float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; - ImGuiInputSource NavWindowingInputSource; // Gamepad or keyboard mode - int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. + ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid - bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavMoveMouse) if set (NB: this not enabled by default) + bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default) bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest @@ -638,113 +939,157 @@ struct ImGuiContext ImRect NavInitResultRectRel; bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items bool NavMoveRequest; // Move request for this frame + ImGuiNavMoveFlags NavMoveRequestFlags; ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu) ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request + ImGuiDir NavMoveClipDir; ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow - ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using the NavFlattened flag) + ImGuiNavMoveResult NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) + ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) + + // Tabbing system (older than Nav, active even if Nav is disabled. FIXME-NAV: This needs a redesign!) + ImGuiWindow* FocusRequestCurrWindow; // + ImGuiWindow* FocusRequestNextWindow; // + int FocusRequestCurrCounterAll; // Any item being requested for focus, stored as an index (we on layout to be stable between the frame pressing TAB and the next frame, semi-ouch) + int FocusRequestCurrCounterTab; // Tab item being requested for focus, stored as an index + int FocusRequestNextCounterAll; // Stored for next frame + int FocusRequestNextCounterTab; // " + bool FocusTabPressed; // // Render ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user ImDrawDataBuilder DrawDataBuilder; - float ModalWindowDarkeningRatio; - ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays + float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) + ImDrawList BackgroundDrawList; // First draw list to be rendered. + ImDrawList ForegroundDrawList; // Last draw list to be rendered. This is where we the render software mouse cursor (if io.MouseDrawCursor is set) and most debug overlays. ImGuiMouseCursor MouseCursor; // Drag and Drop bool DragDropActive; + bool DragDropWithinSourceOrTarget; ImGuiDragDropFlags DragDropSourceFlags; + int DragDropSourceFrameCount; int DragDropMouseButton; ImGuiPayload DragDropPayload; ImRect DragDropTargetRect; ImGuiID DragDropTargetId; - float DragDropAcceptIdCurrRectSurface; + ImGuiDragDropFlags DragDropAcceptFlags; + float DragDropAcceptIdCurrRectSurface; // Target item surface (we resolve overlapping targets by prioritizing the smaller surface) ImGuiID DragDropAcceptIdCurr; // Target item id (set at the time of accepting the payload) ImGuiID DragDropAcceptIdPrev; // Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets) int DragDropAcceptFrameCount; // Last time a target expressed a desire to accept the source ImVector DragDropPayloadBufHeap; // We don't expose the ImVector<> directly - unsigned char DragDropPayloadBufLocal[8]; + unsigned char DragDropPayloadBufLocal[8]; // Local buffer for small payloads + + // Tab bars + ImPool TabBars; + ImGuiTabBar* CurrentTabBar; + ImVector CurrentTabBarStack; + ImVector ShrinkWidthBuffer; // Widget state - ImGuiTextEditState InputTextState; + ImVec2 LastValidMousePos; + ImGuiInputTextState InputTextState; ImFont InputTextPasswordFont; - ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. + ImGuiID TempInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets ImVec4 ColorPickerRef; - float DragCurrentValue; // Currently dragged value, always float, not rounded by end-user precision settings - ImVec2 DragLastMouseDelta; + bool DragCurrentAccumDirty; + float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio - float DragSpeedScaleSlow; - float DragSpeedScaleFast; - ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? + float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? int TooltipOverrideCount; ImVector PrivateClipboard; // If no custom clipboard handler is defined - ImVec2 OsImePosRequest, OsImePosSet; // Cursor position request & last passed to the OS Input Method Editor + + // Range-Select/Multi-Select + // [This is unused in this branch, but left here to facilitate merging/syncing multiple branches] + ImGuiID MultiSelectScopeId; + + // Platform support + ImVec2 PlatformImePos; // Cursor position request & last passed to the OS Input Method Editor + ImVec2 PlatformImeLastPos; // Settings bool SettingsLoaded; - float SettingsDirtyTimer; // Save .ini Settings on disk when time reaches zero - ImVector SettingsWindows; // .ini settings for ImGuiWindow + float SettingsDirtyTimer; // Save .ini Settings to memory when time reaches zero + ImGuiTextBuffer SettingsIniData; // In memory .ini settings ImVector SettingsHandlers; // List of .ini settings handlers + ImVector SettingsWindows; // ImGuiWindow .ini settings entries (parsed from the last loaded .ini file and maintained on saving) // Logging bool LogEnabled; + ImGuiLogType LogType; FILE* LogFile; // If != NULL log to stdout/ file - ImGuiTextBuffer* LogClipboard; // Else log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. - int LogStartDepth; - int LogAutoExpandMaxDepth; + ImGuiTextBuffer LogBuffer; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. + float LogLinePosY; + bool LogLineFirstItem; + int LogDepthRef; + int LogDepthToExpand; + int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. // Misc - float FramerateSecPerFrame[120]; // calculate estimate of framerate for user + float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. int FramerateSecPerFrameIdx; float FramerateSecPerFrameAccum; - int WantCaptureMouseNextFrame; // explicit capture via CaptureInputs() sets those flags + int WantCaptureMouseNextFrame; // Explicit capture via CaptureKeyboardFromApp()/CaptureMouseFromApp() sets those flags int WantCaptureKeyboardNextFrame; int WantTextInputNextFrame; - char TempBuffer[1024*3+1]; // temporary text buffer + char TempBuffer[1024*3+1]; // Temporary text buffer - ImGuiContext(ImFontAtlas* shared_font_atlas) : OverlayDrawList(NULL) + ImGuiContext(ImFontAtlas* shared_font_atlas) : BackgroundDrawList(&DrawListSharedData), ForegroundDrawList(&DrawListSharedData) { Initialized = false; + FrameScopeActive = FrameScopePushedImplicitWindow = false; Font = NULL; FontSize = FontBaseSize = 0.0f; FontAtlasOwnedByContext = shared_font_atlas ? false : true; IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); - Time = 0.0f; FrameCount = 0; FrameCountEnded = FrameCountRendered = -1; + WindowsActiveCount = 0; CurrentWindow = NULL; HoveredWindow = NULL; HoveredRootWindow = NULL; + MovingWindow = NULL; + HoveredId = 0; HoveredIdAllowOverlap = false; HoveredIdPreviousFrame = 0; - HoveredIdTimer = 0.0f; + HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f; ActiveId = 0; - ActiveIdPreviousFrame = 0; + ActiveIdIsAlive = 0; ActiveIdTimer = 0.0f; - ActiveIdIsAlive = false; ActiveIdIsJustActivated = false; ActiveIdAllowOverlap = false; - ActiveIdAllowNavDirFlags = 0; + ActiveIdHasBeenPressedBefore = false; + ActiveIdHasBeenEditedBefore = false; + ActiveIdHasBeenEditedThisFrame = false; + ActiveIdAllowNavDirFlags = 0x00; + ActiveIdBlockNavInputFlags = 0x00; ActiveIdClickOffset = ImVec2(-1,-1); ActiveIdWindow = NULL; ActiveIdSource = ImGuiInputSource_None; - MovingWindow = NULL; - NextTreeNodeOpenVal = false; - NextTreeNodeOpenCond = 0; + + ActiveIdPreviousFrame = 0; + ActiveIdPreviousFrameIsAlive = false; + ActiveIdPreviousFrameHasBeenEditedBefore = false; + ActiveIdPreviousFrameWindow = NULL; + + LastActiveId = 0; + LastActiveIdTimer = 0.0f; NavWindow = NULL; NavId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; - NavJustTabbedId = NavJustMovedToId = NavNextActivateId = 0; + NavJustTabbedId = NavJustMovedToId = NavJustMovedToMultiSelectScopeId = NavNextActivateId = 0; + NavInputSource = ImGuiInputSource_None; NavScoringRectScreen = ImRect(); NavScoringCount = 0; - NavWindowingTarget = NULL; - NavWindowingHighlightTimer = NavWindowingHighlightAlpha = 0.0f; + NavWindowingTarget = NavWindowingTargetAnim = NavWindowingList = NULL; + NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; - NavWindowingInputSource = ImGuiInputSource_None; - NavLayer = 0; + NavLayer = ImGuiNavLayer_Main; NavIdTabCounter = INT_MAX; NavIdIsAlive = false; NavMousePosDirty = false; @@ -756,42 +1101,56 @@ struct ImGuiContext NavInitResultId = 0; NavMoveFromClampedRefRect = false; NavMoveRequest = false; + NavMoveRequestFlags = 0; NavMoveRequestForward = ImGuiNavForward_None; - NavMoveDir = NavMoveDirLast = ImGuiDir_None; + NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; - ModalWindowDarkeningRatio = 0.0f; - OverlayDrawList._Data = &DrawListSharedData; - OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging + FocusRequestCurrWindow = FocusRequestNextWindow = NULL; + FocusRequestCurrCounterAll = FocusRequestCurrCounterTab = INT_MAX; + FocusRequestNextCounterAll = FocusRequestNextCounterTab = INT_MAX; + FocusTabPressed = false; + + DimBgRatio = 0.0f; + BackgroundDrawList._OwnerName = "##Background"; // Give it a name for debugging + ForegroundDrawList._OwnerName = "##Foreground"; // Give it a name for debugging MouseCursor = ImGuiMouseCursor_Arrow; - DragDropActive = false; + DragDropActive = DragDropWithinSourceOrTarget = false; DragDropSourceFlags = 0; + DragDropSourceFrameCount = -1; DragDropMouseButton = -1; DragDropTargetId = 0; + DragDropAcceptFlags = 0; DragDropAcceptIdCurrRectSurface = 0.0f; DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0; DragDropAcceptFrameCount = -1; memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); - ScalarAsInputTextId = 0; + CurrentTabBar = NULL; + + LastValidMousePos = ImVec2(0.0f, 0.0f); + TempInputTextId = 0; ColorEditOptions = ImGuiColorEditFlags__OptionsDefault; - DragCurrentValue = 0.0f; - DragLastMouseDelta = ImVec2(0.0f, 0.0f); + DragCurrentAccumDirty = false; + DragCurrentAccum = 0.0f; DragSpeedDefaultRatio = 1.0f / 100.0f; - DragSpeedScaleSlow = 1.0f / 100.0f; - DragSpeedScaleFast = 10.0f; - ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f); + ScrollbarClickDeltaToGrabCenter = 0.0f; TooltipOverrideCount = 0; - OsImePosRequest = OsImePosSet = ImVec2(-1.0f, -1.0f); + + MultiSelectScopeId = 0; + + PlatformImePos = PlatformImeLastPos = ImVec2(FLT_MAX, FLT_MAX); SettingsLoaded = false; SettingsDirtyTimer = 0.0f; LogEnabled = false; + LogType = ImGuiLogType_None; LogFile = NULL; - LogClipboard = NULL; - LogStartDepth = 0; - LogAutoExpandMaxDepth = 2; + LogLinePosY = FLT_MAX; + LogLineFirstItem = false; + LogDepthRef = 0; + LogDepthToExpand = LogDepthToExpandDefault = 2; memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); FramerateSecPerFrameIdx = 0; @@ -801,50 +1160,42 @@ struct ImGuiContext } }; -// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). -// This is going to be exposed in imgui.h when stabilized enough. -enum ImGuiItemFlags_ -{ - ImGuiItemFlags_AllowKeyboardFocus = 1 << 0, // true - ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. - ImGuiItemFlags_Disabled = 1 << 2, // false // FIXME-WIP: Disable interactions but doesn't affect visuals. Should be: grey out and disable interactions with widgets that affect data + view widgets (WIP) - ImGuiItemFlags_NoNav = 1 << 3, // false - ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false - ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window - ImGuiItemFlags_Default_ = ImGuiItemFlags_AllowKeyboardFocus -}; +//----------------------------------------------------------------------------- +// ImGuiWindow +//----------------------------------------------------------------------------- -// Transient per-window data, reset at the beginning of the frame -// FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiDrawContext is quite tenuous and could be reconsidered. -struct IMGUI_API ImGuiDrawContext +// Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. +// FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered. +struct IMGUI_API ImGuiWindowTempData { ImVec2 CursorPos; ImVec2 CursorPosPrevLine; - ImVec2 CursorStartPos; - ImVec2 CursorMaxPos; // Used to implicitly calculate the size of our contents, always growing during the frame. Turned into window->SizeContents at the beginning of next frame - float CurrentLineHeight; - float CurrentLineTextBaseOffset; - float PrevLineHeight; + ImVec2 CursorStartPos; // Initial position in client area with padding + ImVec2 CursorMaxPos; // Used to implicitly calculate the size of our contents, always growing during the frame. Used to calculate window->ContentSize at the beginning of next frame + ImVec2 CurrLineSize; + ImVec2 PrevLineSize; + float CurrLineTextBaseOffset; float PrevLineTextBaseOffset; - float LogLinePosY; int TreeDepth; - ImU32 TreeDepthMayJumpToParentOnPop; // Store a copy of !g.NavIdIsAlive for TreeDepth 0..31 + ImU32 TreeStoreMayJumpToParentOnPop; // Store a copy of !g.NavIdIsAlive for TreeDepth 0..31.. Could be turned into a ImU64 if necessary. ImGuiID LastItemId; ImGuiItemStatusFlags LastItemStatusFlags; ImRect LastItemRect; // Interaction rect ImRect LastItemDisplayRect; // End-user display rect (only valid if LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) - bool NavHideHighlightOneFrame; - bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) - int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) + ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) int NavLayerCurrentMask; // = (1 << NavLayerCurrent) used by ItemAdd prior to clipping. int NavLayerActiveMask; // Which layer have been written to (result from previous frame) int NavLayerActiveMaskNext; // Which layer have been written to (buffer for current frame) + bool NavHideHighlightOneFrame; + bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) bool MenuBarAppending; // FIXME: Remove this - float MenuBarOffsetX; + ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs. ImVector ChildWindows; ImGuiStorage* StateStorage; ImGuiLayoutType LayoutType; ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() + int FocusCounterAll; // Counter for focus/tabbing system. Start at -1 and increase as assigned via FocusableItemRegister() (FIXME-NAV: Needs redesign) + int FocusCounterTab; // (same, but only count widgets which you can Tab through) // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. ImGuiItemFlags ItemFlags; // == ItemFlagsStack.back() [empty == ImGuiItemFlags_Default] @@ -854,122 +1205,125 @@ struct IMGUI_API ImGuiDrawContext ImVector ItemWidthStack; ImVector TextWrapPosStack; ImVectorGroupStack; - int StackSizesBackup[6]; // Store size of various stacks for asserting + short StackSizesBackup[6]; // Store size of various stacks for asserting - float IndentX; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) - float GroupOffsetX; - float ColumnsOffsetX; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. - ImGuiColumnsSet* ColumnsSet; // Current columns set + ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) + ImVec1 GroupOffset; + ImVec1 ColumnsOffset; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. + ImGuiColumns* CurrentColumns; // Current columns set - ImGuiDrawContext() + ImGuiWindowTempData() { CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f); - CurrentLineHeight = PrevLineHeight = 0.0f; - CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; - LogLinePosY = -1.0f; + CurrLineSize = PrevLineSize = ImVec2(0.0f, 0.0f); + CurrLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; TreeDepth = 0; - TreeDepthMayJumpToParentOnPop = 0x00; + TreeStoreMayJumpToParentOnPop = 0x00; LastItemId = 0; LastItemStatusFlags = 0; LastItemRect = LastItemDisplayRect = ImRect(); + NavLayerActiveMask = NavLayerActiveMaskNext = 0x00; + NavLayerCurrent = ImGuiNavLayer_Main; + NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); NavHideHighlightOneFrame = false; NavHasScroll = false; - NavLayerActiveMask = NavLayerActiveMaskNext = 0x00; - NavLayerCurrent = 0; - NavLayerCurrentMask = 1 << 0; MenuBarAppending = false; - MenuBarOffsetX = 0.0f; + MenuBarOffset = ImVec2(0.0f, 0.0f); StateStorage = NULL; LayoutType = ParentLayoutType = ImGuiLayoutType_Vertical; - ItemWidth = 0.0f; + FocusCounterAll = FocusCounterTab = -1; + ItemFlags = ImGuiItemFlags_Default_; + ItemWidth = 0.0f; TextWrapPos = -1.0f; memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); - IndentX = 0.0f; - GroupOffsetX = 0.0f; - ColumnsOffsetX = 0.0f; - ColumnsSet = NULL; + Indent = ImVec1(0.0f); + GroupOffset = ImVec1(0.0f); + ColumnsOffset = ImVec1(0.0f); + CurrentColumns = NULL; } }; -// Windows data +// Storage for one window struct IMGUI_API ImGuiWindow { char* Name; ImGuiID ID; // == ImHash(Name) ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ - ImVec2 PosFloat; - ImVec2 Pos; // Position rounded-up to nearest pixel + ImVec2 Pos; // Position (always rounded-up to nearest pixel) ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) ImVec2 SizeFull; // Size when non collapsed - ImVec2 SizeFullAtLastBegin; // Copy of SizeFull at the end of Begin. This is the reference value we'll use on the next frame to decide if we need scrollbars. - ImVec2 SizeContents; // Size of contents (== extents reach of the drawing cursor) from previous frame. Include decoration, window title, border, menu, etc. - ImVec2 SizeContentsExplicit; // Size of contents explicitly set by the user via SetNextWindowContentSize() - ImRect ContentsRegionRect; // Maximum visible content position in window coordinates. ~~ (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis + ImVec2 ContentSize; // Size of contents/scrollable client area (calculated from the extents reach of the cursor) from previous frame. Does not include window decoration or window padding. + ImVec2 ContentSizeExplicit; // Size of contents/scrollable client area explicitly request by the user via SetNextWindowContentSize(). ImVec2 WindowPadding; // Window padding at the time of begin. float WindowRounding; // Window rounding at the time of begin. float WindowBorderSize; // Window border size at the time of begin. + int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)! ImGuiID MoveId; // == window->GetID("#MOVE") - ImGuiID ChildId; // Id of corresponding item in parent window (for child windows) + ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) ImVec2 Scroll; + ImVec2 ScrollMax; ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered + ImVec2 ScrollbarSizes; // Size taken by scrollbars on each axis bool ScrollbarX, ScrollbarY; - ImVec2 ScrollbarSizes; bool Active; // Set to true on Begin(), unless Collapsed bool WasActive; bool WriteAccessed; // Set to true when any widget access the current window bool Collapsed; // Set when collapsing window to become only title-bar - bool CollapseToggleWanted; + bool WantCollapseToggle; bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed) bool Appearing; // Set during the frame where the window is appearing (or re-appearing) - bool CloseButton; // Set when the window has a close button (p_open != NULL) - int BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. - int BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues. - int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) + bool Hidden; // Do not display (== (HiddenFrames*** > 0)) + bool HasCloseButton; // Set when the window has a close button (p_open != NULL) + signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) + short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) + short BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. + short BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues. ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) int AutoFitFramesX, AutoFitFramesY; bool AutoFitOnlyGrows; int AutoFitChildAxises; ImGuiDir AutoPosLastDirection; - int HiddenFrames; - ImGuiCond SetWindowPosAllowFlags; // store condition flags for next SetWindowPos() call. - ImGuiCond SetWindowSizeAllowFlags; // store condition flags for next SetWindowSize() call. - ImGuiCond SetWindowCollapsedAllowFlags; // store condition flags for next SetWindowCollapsed() call. + int HiddenFramesCanSkipItems; // Hide the window for N frames + int HiddenFramesCannotSkipItems; // Hide the window for N frames while allowing items to be submitted so we can measure their size + ImGuiCond SetWindowPosAllowFlags; // store acceptable condition flags for SetNextWindowPos() use. + ImGuiCond SetWindowSizeAllowFlags; // store acceptable condition flags for SetNextWindowSize() use. + ImGuiCond SetWindowCollapsedAllowFlags; // store acceptable condition flags for SetNextWindowCollapsed() use. ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right. - ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame + ImGuiWindowTempData DC; // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name. ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack - ImRect ClipRect; // = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2. - ImRect WindowRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window. - ImRect InnerRect, InnerClipRect; - int LastFrameActive; + + // The best way to understand what those rectangles are is to use the 'Metrics -> Tools -> Show windows rectangles' viewer. + // The main 'OuterRect', omitted as a field, is window->Rect(). + ImRect OuterRectClipped; // == Window->Rect() just after setup in Begin(). == window->Rect() for root window. + ImRect InnerRect; // Inner rectangle (omit title bar, menu bar, scroll bar) + ImRect InnerClipRect; // == InnerRect shrunk by WindowPadding*0.5f on each side, clipped within viewport or parent clip rect. + ImRect WorkRect; // Cover the whole scrolling region, shrunk by WindowPadding*1.0f on each side. This is meant to replace ContentsRegionRect over time (from 1.71+ onward). + ImRect ClipRect; // Current clipping/scissoring rectangle, evolve as we are using PushClipRect(), etc. == DrawList->clip_rect_stack.back(). + ImRect ContentsRegionRect; // FIXME: This is currently confusing/misleading. It is essentially WorkRect but not handling of scrolling. We currently rely on it as right/bottom aligned sizing operation need some size to rely on. + + int LastFrameActive; // Last frame number the window was Active. float ItemWidthDefault; ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items ImGuiStorage StateStorage; - ImVector ColumnsStorage; - float FontWindowScale; // Scale multiplier per-window - ImDrawList* DrawList; + ImVector ColumnsStorage; + float FontWindowScale; // User scale multiplier per-window + int SettingsIdx; // Index into SettingsWindow[] (indices are always valid as we only grow the array from the back) + + ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) + ImDrawList DrawListInst; ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. - ImGuiWindow* RootWindowForTabbing; // Point to ourself or first ancestor which can be CTRL-Tabbed into. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.) - ImGuiID NavLastIds[2]; // Last known NavId for this window, per layer (0/1) - ImRect NavRectRel[2]; // Reference rectangle, in window relative space - - // Navigation / Focus - // FIXME-NAV: Merge all this with the new Nav system, at least the request variables should be moved to ImGuiContext - int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() - int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through) - int FocusIdxAllRequestCurrent; // Item being requested for focus - int FocusIdxTabRequestCurrent; // Tab-able item being requested for focus - int FocusIdxAllRequestNext; // Item being requested for focus, for next update (relies on layout to be stable between the frame pressing TAB and the next frame) - int FocusIdxTabRequestNext; // " + ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1) + ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space public: ImGuiWindow(ImGuiContext* context, const char* name); @@ -977,7 +1331,10 @@ public: ImGuiID GetID(const char* str, const char* str_end = NULL); ImGuiID GetID(const void* ptr); + ImGuiID GetID(int n); ImGuiID GetIDNoKeepAlive(const char* str, const char* str_end = NULL); + ImGuiID GetIDNoKeepAlive(const void* ptr); + ImGuiID GetIDNoKeepAlive(int n); ImGuiID GetIDFromRectangle(const ImRect& r_abs); // We don't use g.FontSize because the window may be != g.CurrentWidow. @@ -985,11 +1342,11 @@ public: float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; } float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; } ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } - float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } + float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } }; -// Backup and restore just enough data to be able to use IsItemHovered() on item A after another B in the same window has overwritten the data. +// Backup and restore just enough data to be able to use IsItemHovered() on item A after another B in the same window has overwritten the data. struct ImGuiItemHoveredDataBackup { ImGuiID LastItemId; @@ -998,8 +1355,77 @@ struct ImGuiItemHoveredDataBackup ImRect LastItemDisplayRect; ImGuiItemHoveredDataBackup() { Backup(); } - void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemStatusFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; LastItemDisplayRect = window->DC.LastItemDisplayRect; } - void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } + void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemStatusFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; LastItemDisplayRect = window->DC.LastItemDisplayRect; } + void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } +}; + +//----------------------------------------------------------------------------- +// Tab bar, tab item +//----------------------------------------------------------------------------- + +// Extend ImGuiTabBarFlags_ +enum ImGuiTabBarFlagsPrivate_ +{ + ImGuiTabBarFlags_DockNode = 1 << 20, // Part of a dock node [we don't use this in the master branch but it facilitate branch syncing to keep this around] + ImGuiTabBarFlags_IsFocused = 1 << 21, + ImGuiTabBarFlags_SaveSettings = 1 << 22 // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs +}; + +// Extend ImGuiTabItemFlags_ +enum ImGuiTabItemFlagsPrivate_ +{ + ImGuiTabItemFlags_NoCloseButton = 1 << 20 // Store whether p_open is set or not, which we need to recompute WidthContents during layout. +}; + +// Storage for one active tab item (sizeof() 26~32 bytes) +struct ImGuiTabItem +{ + ImGuiID ID; + ImGuiTabItemFlags Flags; + int LastFrameVisible; + int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance + int NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames + float Offset; // Position relative to beginning of tab + float Width; // Width currently displayed + float WidthContents; // Width of actual contents, stored during BeginTabItem() call + + ImGuiTabItem() { ID = Flags = 0; LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; Offset = Width = WidthContents = 0.0f; } +}; + +// Storage for a tab bar (sizeof() 92~96 bytes) +struct ImGuiTabBar +{ + ImVector Tabs; + ImGuiID ID; // Zero for tab-bars used by docking + ImGuiID SelectedTabId; // Selected tab + ImGuiID NextSelectedTabId; + ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview) + int CurrFrameVisible; + int PrevFrameVisible; + ImRect BarRect; + float ContentsHeight; + float OffsetMax; // Distance from BarRect.Min.x, locked during layout + float OffsetNextTab; // Distance from BarRect.Min.x, incremented with each BeginTabItem() call, not used if ImGuiTabBarFlags_Reorderable if set. + float ScrollingAnim; + float ScrollingTarget; + float ScrollingTargetDistToVisibility; + float ScrollingSpeed; + ImGuiTabBarFlags Flags; + ImGuiID ReorderRequestTabId; + int ReorderRequestDir; + bool WantLayout; + bool VisibleTabWasSubmitted; + short LastTabItemIdx; // For BeginTabItem()/EndTabItem() + ImVec2 FramePadding; // style.FramePadding locked at the time of BeginTabBar() + ImGuiTextBuffer TabsNames; // For non-docking tab bar we re-append names in a contiguous buffer. + + ImGuiTabBar(); + int GetTabOrder(const ImGuiTabItem* tab) const { return Tabs.index_from_ptr(tab); } + const char* GetTabName(const ImGuiTabItem* tab) const + { + IM_ASSERT(tab->NameOffset != -1 && tab->NameOffset < TabsNames.Buf.Size); + return TabsNames.Buf.Data + tab->NameOffset; + } }; //----------------------------------------------------------------------------- @@ -1012,138 +1438,250 @@ namespace ImGui // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window) // If this ever crash because g.CurrentWindow is NULL it means that either // - ImGui::NewFrame() has never been called, which is illegal. - // - You are calling ImGui functions after ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. + // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; } + IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void FocusWindow(ImGuiWindow* window); - IMGUI_API void BringWindowToFront(ImGuiWindow* window); - IMGUI_API void BringWindowToBack(ImGuiWindow* window); + IMGUI_API void FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window); + IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); + IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); + IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); + IMGUI_API void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x); + IMGUI_API void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y); + IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window); + IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); + IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0); + IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); + IMGUI_API void SetCurrentFont(ImFont* font); + inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } + + // Init IMGUI_API void Initialize(ImGuiContext* context); IMGUI_API void Shutdown(ImGuiContext* context); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). - IMGUI_API void MarkIniSettingsDirty(); - IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); - IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id); + // NewFrame + IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); + IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); + IMGUI_API void UpdateMouseMovingWindowNewFrame(); + IMGUI_API void UpdateMouseMovingWindowEndFrame(); + // Settings + IMGUI_API void MarkIniSettingsDirty(); + IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window); + IMGUI_API ImGuiWindowSettings* CreateNewWindowSettings(const char* name); + IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id); + IMGUI_API ImGuiWindowSettings* FindOrCreateWindowSettings(const char* name); + IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); + + // Basic Accessors + inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemId; } + inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } + inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; } IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); - IMGUI_API ImGuiID GetActiveID(); IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window); IMGUI_API void ClearActiveID(); - IMGUI_API void SetHoveredID(ImGuiID id); IMGUI_API ImGuiID GetHoveredID(); + IMGUI_API void SetHoveredID(ImGuiID id); IMGUI_API void KeepAliveID(ImGuiID id); + IMGUI_API void MarkItemEdited(ImGuiID id); + IMGUI_API void PushOverrideID(ImGuiID id); + // Basic Helpers for widget code IMGUI_API void ItemSize(const ImVec2& size, float text_offset_y = 0.0f); IMGUI_API void ItemSize(const ImRect& bb, float text_offset_y = 0.0f); IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged); - IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop = true); // Return true if focus is requested + IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id); // Return true if focus is requested IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); - IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); + IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); - IMGUI_API void PushMultiItemsWidths(int components, float width_full = 0.0f); + IMGUI_API void PushMultiItemsWidths(int components, float width_full); IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PopItemFlag(); + IMGUI_API bool IsItemToggledSelection(); // Was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly) + IMGUI_API ImVec2 GetContentRegionMaxAbs(); + IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); - IMGUI_API void SetCurrentFont(ImFont* font); + // Logging/Capture + IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name. + IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer + // Popups, Modals, Tooltips IMGUI_API void OpenPopupEx(ImGuiID id); - IMGUI_API void ClosePopup(ImGuiID id); - IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window); - IMGUI_API bool IsPopupOpen(ImGuiID id); + IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); + IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); + IMGUI_API bool IsPopupOpen(ImGuiID id); // Test for id within current popup stack level (currently begin-ed into); this doesn't scan the whole popup stack! IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip = true); + IMGUI_API ImGuiWindow* GetTopMostPopupModal(); + IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); + IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default); + // Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); + IMGUI_API bool NavMoveRequestButNoResultYet(); IMGUI_API void NavMoveRequestCancel(); - IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. - + IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags); + IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode); IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f); IMGUI_API int CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate); + IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. + IMGUI_API void SetNavID(ImGuiID id, int nav_layer); + IMGUI_API void SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel); - IMGUI_API void Scrollbar(ImGuiLayoutType direction); - IMGUI_API void VerticalSeparator(); // Vertical separator, for menu bars (use current line height). not exposed because it is misleading what it doesn't have an effect on regular layout. - IMGUI_API bool SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f); + // Inputs + inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { const int key_index = GImGui->IO.KeyMap[key]; return (key_index >= 0) ? IsKeyPressed(key_index, repeat) : false; } + inline bool IsNavInputDown(ImGuiNavInput n) { return GImGui->IO.NavInputs[n] > 0.0f; } + inline bool IsNavInputPressed(ImGuiNavInput n, ImGuiInputReadMode mode) { return GetNavInputAmount(n, mode) > 0.0f; } + inline bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiInputReadMode mode) { return (GetNavInputAmount(n1, mode) + GetNavInputAmount(n2, mode)) > 0.0f; } + // Drag and Drop IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); IMGUI_API void ClearDragDrop(); IMGUI_API bool IsDragDropPayloadBeingAccepted(); - // FIXME-WIP: New Columns API + // New Columns API (FIXME-WIP) IMGUI_API void BeginColumns(const char* str_id, int count, ImGuiColumnsFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns(). IMGUI_API void EndColumns(); // close columns - IMGUI_API void PushColumnClipRect(int column_index = -1); + IMGUI_API void PushColumnClipRect(int column_index); + IMGUI_API void PushColumnsBackground(); + IMGUI_API void PopColumnsBackground(); + IMGUI_API ImGuiID GetColumnsID(const char* str_id, int count); + IMGUI_API ImGuiColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id); - // NB: All position are in absolute pixels coordinates (never using window coordinates internally) + // Tab Bars + IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags); + IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); + IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); + IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); + IMGUI_API void TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); + IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags); + IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button); + IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); + IMGUI_API bool TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id); + + // Render helpers // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT. + // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally) IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL); + IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); + IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, int rounding_corners_flags = ~0); - IMGUI_API void RenderArrow(ImVec2 pos, ImGuiDir dir, float scale = 1.0f); - IMGUI_API void RenderBullet(ImVec2 pos); IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col, float sz); IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_TypeDefault); // Navigation highlight - IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. + IMGUI_API void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL); - IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); + // Render helpers (those functions don't access any ImGui state!) + IMGUI_API void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f); + IMGUI_API void RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col); + IMGUI_API void RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor = ImGuiMouseCursor_Arrow); + IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); + IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); + IMGUI_API void RenderPixelEllipsis(ImDrawList* draw_list, ImVec2 pos, ImU32 col, int count); + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + // 2019/06/07: Updating prototypes of some of the internal functions. Leaving those for reference for a short while. + inline void RenderArrow(ImVec2 pos, ImGuiDir dir, float scale=1.0f) { ImGuiWindow* window = GetCurrentWindow(); RenderArrow(window->DrawList, pos, GetColorU32(ImGuiCol_Text), dir, scale); } + inline void RenderBullet(ImVec2 pos) { ImGuiWindow* window = GetCurrentWindow(); RenderBullet(window->DrawList, pos, GetColorU32(ImGuiCol_Text)); } +#endif + + // Widgets + IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); - IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); - IMGUI_API bool ArrowButton(ImGuiID id, ImGuiDir dir, ImVec2 padding, ImGuiButtonFlags flags = 0); + IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos); + IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); + IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags); + IMGUI_API void Scrollbar(ImGuiAxis axis); + IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float avail_v, float contents_v, ImDrawCornerFlags rounding_corners); + IMGUI_API ImGuiID GetScrollbarID(ImGuiWindow* window, ImGuiAxis axis); + IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags); - IMGUI_API bool SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags = 0); - IMGUI_API bool SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power); - IMGUI_API bool SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format); + // Widgets low-level behaviors + IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); + IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power, ImGuiDragFlags flags); + IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); + IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f); + IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); + IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextItemOpen() data, if any. May return true when logging + IMGUI_API void TreePushOverrideID(ImGuiID id); - IMGUI_API bool DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power); - IMGUI_API bool DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power); - IMGUI_API bool DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format); + // Template functions are instantiated in imgui_widgets.cpp for a finite number of types. + // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036). + // e.g. " extern template IMGUI_API float RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, float v); " + template IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, float power, ImGuiDragFlags flags); + template IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); + template IMGUI_API float SliderCalcRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float power, float linear_zero_pos); + template IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); - IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags); - IMGUI_API bool InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags); - IMGUI_API bool InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags); - IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision); + // Data type helpers + IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type); + IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format); + IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2); + IMGUI_API bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format); + // InputText + IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool TempInputTextScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format); + inline bool TempInputTextIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputTextId == id); } + + // Color IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags); IMGUI_API void ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags); + IMGUI_API void ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags); - IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); - IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging - IMGUI_API void TreePushRawID(ImGuiID id); + // Plot + IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size); - IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size); - - IMGUI_API int ParseFormatPrecision(const char* fmt, int default_value); - IMGUI_API float RoundScalar(float value, int decimal_precision); - - // Shade functions - IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawVert* vert_start, ImDrawVert* vert_end, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1); - IMGUI_API void ShadeVertsLinearAlphaGradientForLeftToRightText(ImDrawVert* vert_start, ImDrawVert* vert_end, float gradient_p0_x, float gradient_p1_x); - IMGUI_API void ShadeVertsLinearUV(ImDrawVert* vert_start, ImDrawVert* vert_end, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp); + // Shade functions (write over already created vertices) + IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1); + IMGUI_API void ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp); } // namespace ImGui // ImFontAtlas internals IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); -IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* spc); +IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); +IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque); IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); -#ifdef __clang__ +// Test engine hooks (imgui-test) +//#define IMGUI_ENABLE_TEST_ENGINE +#ifdef IMGUI_ENABLE_TEST_ENGINE +extern void ImGuiTestEngineHook_PreNewFrame(ImGuiContext* ctx); +extern void ImGuiTestEngineHook_PostNewFrame(ImGuiContext* ctx); +extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id); +extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); +extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...); +#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB, _ID) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) +#define IMGUI_TEST_ENGINE_LOG(_FMT, ...) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log +#else +#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB, _ID) do { } while (0) +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) do { } while (0) +#define IMGUI_TEST_ENGINE_LOG(_FMT, ...) do { } while (0) +#endif + +#if defined(__clang__) #pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop #endif #ifdef _MSC_VER diff --git a/external/imgui/imgui_widgets.cpp b/external/imgui/imgui_widgets.cpp new file mode 100644 index 0000000..e59836c --- /dev/null +++ b/external/imgui/imgui_widgets.cpp @@ -0,0 +1,7091 @@ +// dear imgui, v1.72 WIP +// (widgets code) + +/* + +Index of this file: + +// [SECTION] Forward Declarations +// [SECTION] Widgets: Text, etc. +// [SECTION] Widgets: Main (Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc.) +// [SECTION] Widgets: Low-level Layout helpers (Spacing, Dummy, NewLine, Separator, etc.) +// [SECTION] Widgets: ComboBox +// [SECTION] Data Type and Data Formatting Helpers +// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc. +// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. +// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc. +// [SECTION] Widgets: InputText, InputTextMultiline +// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. +// [SECTION] Widgets: TreeNode, CollapsingHeader, etc. +// [SECTION] Widgets: Selectable +// [SECTION] Widgets: ListBox +// [SECTION] Widgets: PlotLines, PlotHistogram +// [SECTION] Widgets: Value helpers +// [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc. +// [SECTION] Widgets: BeginTabBar, EndTabBar, etc. +// [SECTION] Widgets: BeginTabItem, EndTabItem, etc. + +*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "imgui.h" +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif +#include "imgui_internal.h" + +#include // toupper +#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier +#include // intptr_t +#else +#include // intptr_t +#endif + +// Visual Studio warnings +#ifdef _MSC_VER +#pragma warning (disable: 4127) // condition expression is constant +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#endif + +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. +#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0 +#endif +#if __has_warning("-Wdouble-promotion") +#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. +#endif +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind +#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked +#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead +#endif + +//------------------------------------------------------------------------- +// Data +//------------------------------------------------------------------------- + +// Those MIN/MAX values are not define because we need to point to them +static const signed char IM_S8_MIN = -128; +static const signed char IM_S8_MAX = 127; +static const unsigned char IM_U8_MIN = 0; +static const unsigned char IM_U8_MAX = 0xFF; +static const signed short IM_S16_MIN = -32768; +static const signed short IM_S16_MAX = 32767; +static const unsigned short IM_U16_MIN = 0; +static const unsigned short IM_U16_MAX = 0xFFFF; +static const ImS32 IM_S32_MIN = INT_MIN; // (-2147483647 - 1), (0x80000000); +static const ImS32 IM_S32_MAX = INT_MAX; // (2147483647), (0x7FFFFFFF) +static const ImU32 IM_U32_MIN = 0; +static const ImU32 IM_U32_MAX = UINT_MAX; // (0xFFFFFFFF) +#ifdef LLONG_MIN +static const ImS64 IM_S64_MIN = LLONG_MIN; // (-9223372036854775807ll - 1ll); +static const ImS64 IM_S64_MAX = LLONG_MAX; // (9223372036854775807ll); +#else +static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1; +static const ImS64 IM_S64_MAX = 9223372036854775807LL; +#endif +static const ImU64 IM_U64_MIN = 0; +#ifdef ULLONG_MAX +static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull); +#else +static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); +#endif + +//------------------------------------------------------------------------- +// [SECTION] Forward Declarations +//------------------------------------------------------------------------- + +// For InputTextEx() +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data); +static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); +static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Text, etc. +//------------------------------------------------------------------------- +// - TextUnformatted() +// - Text() +// - TextV() +// - TextColored() +// - TextColoredV() +// - TextDisabled() +// - TextDisabledV() +// - TextWrapped() +// - TextWrappedV() +// - LabelText() +// - LabelTextV() +// - BulletText() +// - BulletTextV() +//------------------------------------------------------------------------- + +void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + IM_ASSERT(text != NULL); + const char* text_begin = text; + if (text_end == NULL) + text_end = text + strlen(text); // FIXME-OPT + + const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + const float wrap_pos_x = window->DC.TextWrapPos; + const bool wrap_enabled = (wrap_pos_x >= 0.0f); + if (text_end - text > 2000 && !wrap_enabled) + { + // Long text! + // Perform manual coarse clipping to optimize for long multi-line text + // - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. + // - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. + // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop. + const char* line = text; + const float line_height = GetTextLineHeight(); + ImVec2 text_size(0,0); + + // Lines to skip (can't skip when logging text) + ImVec2 pos = text_pos; + if (!g.LogEnabled) + { + int lines_skippable = (int)((window->ClipRect.Min.y - text_pos.y) / line_height); + if (lines_skippable > 0) + { + int lines_skipped = 0; + while (line < text_end && lines_skipped < lines_skippable) + { + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + } + + // Lines to render + if (line < text_end) + { + ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); + while (line < text_end) + { + if (IsClippedEx(line_rect, 0, false)) + break; + + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); + RenderText(pos, line, line_end, false); + line = line_end + 1; + line_rect.Min.y += line_height; + line_rect.Max.y += line_height; + pos.y += line_height; + } + + // Count remaining lines + int lines_skipped = 0; + while (line < text_end) + { + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + text_size.y = (pos - text_pos).y; + + ImRect bb(text_pos, text_pos + text_size); + ItemSize(text_size); + ItemAdd(bb, 0); + } + else + { + const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; + const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); + + ImRect bb(text_pos, text_pos + text_size); + ItemSize(text_size); + if (!ItemAdd(bb, 0)) + return; + + // Render (we don't hide text after ## in this end-user function) + RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); + } +} + +void ImGui::TextUnformatted(const char* text, const char* text_end) +{ + TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); +} + +void ImGui::Text(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextV(fmt, args); + va_end(args); +} + +void ImGui::TextV(const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + TextEx(g.TempBuffer, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); +} + +void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextColoredV(col, fmt, args); + va_end(args); +} + +void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args) +{ + PushStyleColor(ImGuiCol_Text, col); + TextV(fmt, args); + PopStyleColor(); +} + +void ImGui::TextDisabled(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextDisabledV(fmt, args); + va_end(args); +} + +void ImGui::TextDisabledV(const char* fmt, va_list args) +{ + PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]); + TextV(fmt, args); + PopStyleColor(); +} + +void ImGui::TextWrapped(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextWrappedV(fmt, args); + va_end(args); +} + +void ImGui::TextWrappedV(const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + bool need_backup = (window->DC.TextWrapPos < 0.0f); // Keep existing wrap position if one is already set + if (need_backup) + PushTextWrapPos(0.0f); + TextV(fmt, args); + if (need_backup) + PopTextWrapPos(); +} + +void ImGui::LabelText(const char* label, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + LabelTextV(label, fmt, args); + va_end(args); +} + +// Add a label+text combo aligned to other label+value widgets +void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2)); + const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, 0)) + return; + + // Render + const char* value_text_begin = &g.TempBuffer[0]; + const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f)); + if (label_size.x > 0.0f) + RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); +} + +void ImGui::BulletText(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + BulletTextV(fmt, args); + va_end(args); +} + +// Text with a little bullet aligned to the typical tree node. +void ImGui::BulletTextV(const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + const char* text_begin = g.TempBuffer; + const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); + const float text_base_offset_y = ImMax(0.0f, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it + const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding + ItemSize(bb); + if (!ItemAdd(bb, 0)) + return; + + // Render + ImU32 text_col = GetColorU32(ImGuiCol_Text); + RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), text_col); + RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false); +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Main +//------------------------------------------------------------------------- +// - ButtonBehavior() [Internal] +// - Button() +// - SmallButton() +// - InvisibleButton() +// - ArrowButton() +// - CloseButton() [Internal] +// - CollapseButton() [Internal] +// - ScrollbarEx() [Internal] +// - Scrollbar() [Internal] +// - Image() +// - ImageButton() +// - Checkbox() +// - CheckboxFlags() +// - RadioButton() +// - ProgressBar() +// - Bullet() +//------------------------------------------------------------------------- + +// The ButtonBehavior() function is key to many interactions and used by many/most widgets. +// Because we handle so many cases (keyboard/gamepad navigation, drag and drop) and many specific behavior (via ImGuiButtonFlags_), +// this code is a little complex. +// By far the most common path is interacting with the Mouse using the default ImGuiButtonFlags_PressedOnClickRelease button behavior. +// See the series of events below and the corresponding state reported by dear imgui: +//------------------------------------------------------------------------------------------------------------------------------------------------ +// with PressedOnClickRelease: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() +// Frame N+0 (mouse is outside bb) - - - - - - +// Frame N+1 (mouse moves inside bb) - true - - - - +// Frame N+2 (mouse button is down) - true true true - true +// Frame N+3 (mouse button is down) - true true - - - +// Frame N+4 (mouse moves outside bb) - - true - - - +// Frame N+5 (mouse moves inside bb) - true true - - - +// Frame N+6 (mouse button is released) true true - - true - +// Frame N+7 (mouse button is released) - true - - - - +// Frame N+8 (mouse moves outside bb) - - - - - - +//------------------------------------------------------------------------------------------------------------------------------------------------ +// with PressedOnClick: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() +// Frame N+2 (mouse button is down) true true true true - true +// Frame N+3 (mouse button is down) - true true - - - +// Frame N+6 (mouse button is released) - true - - true - +// Frame N+7 (mouse button is released) - true - - - - +//------------------------------------------------------------------------------------------------------------------------------------------------ +// with PressedOnRelease: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() +// Frame N+2 (mouse button is down) - true - - - true +// Frame N+3 (mouse button is down) - true - - - - +// Frame N+6 (mouse button is released) true true - - - - +// Frame N+7 (mouse button is released) - true - - - - +//------------------------------------------------------------------------------------------------------------------------------------------------ +// with PressedOnDoubleClick: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() +// Frame N+0 (mouse button is down) - true - - - true +// Frame N+1 (mouse button is down) - true - - - - +// Frame N+2 (mouse button is released) - true - - - - +// Frame N+3 (mouse button is released) - true - - - - +// Frame N+4 (mouse button is down) true true true true - true +// Frame N+5 (mouse button is down) - true true - - - +// Frame N+6 (mouse button is released) - true - - true - +// Frame N+7 (mouse button is released) - true - - - - +//------------------------------------------------------------------------------------------------------------------------------------------------ +// Note that some combinations are supported, +// - PressedOnDragDropHold can generally be associated with any flag. +// - PressedOnDoubleClick can be associated by PressedOnClickRelease/PressedOnRelease, in which case the second release event won't be reported. +//------------------------------------------------------------------------------------------------------------------------------------------------ +// The behavior of the return-value changes when ImGuiButtonFlags_Repeat is set: +// Repeat+ Repeat+ Repeat+ Repeat+ +// PressedOnClickRelease PressedOnClick PressedOnRelease PressedOnDoubleClick +//------------------------------------------------------------------------------------------------------------------------------------------------- +// Frame N+0 (mouse button is down) - true - true +// ... - - - - +// Frame N + RepeatDelay true true - true +// ... - - - - +// Frame N + RepeatDelay + RepeatRate*N true true - true +//------------------------------------------------------------------------------------------------------------------------------------------------- + +bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + if (flags & ImGuiButtonFlags_Disabled) + { + if (out_hovered) *out_hovered = false; + if (out_held) *out_held = false; + if (g.ActiveId == id) ClearActiveID(); + return false; + } + + // Default behavior requires click+release on same spot + if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0) + flags |= ImGuiButtonFlags_PressedOnClickRelease; + + ImGuiWindow* backup_hovered_window = g.HoveredWindow; + const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window; + if (flatten_hovered_children) + g.HoveredWindow = window; + +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (id != 0 && window->DC.LastItemId != id) + ImGuiTestEngineHook_ItemAdd(&g, bb, id); +#endif + + bool pressed = false; + bool hovered = ItemHoverable(bb, id); + + // Drag source doesn't report as hovered + if (hovered && g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover)) + hovered = false; + + // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button + if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) + if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) + { + hovered = true; + SetHoveredID(id); + if (CalcTypematicPressedRepeatAmount(g.HoveredIdTimer + 0.0001f, g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, 0.01f, 0.70f)) // FIXME: Our formula for CalcTypematicPressedRepeatAmount() is fishy + { + pressed = true; + FocusWindow(window); + } + } + + if (flatten_hovered_children) + g.HoveredWindow = backup_hovered_window; + + // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. + if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) + hovered = false; + + // Mouse + if (hovered) + { + if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) + { + if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) + { + SetActiveID(id, window); + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); + FocusWindow(window); + } + if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0])) + { + pressed = true; + if (flags & ImGuiButtonFlags_NoHoldingActiveID) + ClearActiveID(); + else + SetActiveID(id, window); // Hold on ID + FocusWindow(window); + } + if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0]) + { + if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps + pressed = true; + ClearActiveID(); + } + + // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). + // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. + if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true)) + pressed = true; + } + + if (pressed) + g.NavDisableHighlight = true; + } + + // Gamepad/Keyboard navigation + // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse. + if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId)) + if (!(flags & ImGuiButtonFlags_NoHoveredOnNav)) + hovered = true; + + if (g.NavActivateDownId == id) + { + bool nav_activated_by_code = (g.NavActivateId == id); + bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed); + if (nav_activated_by_code || nav_activated_by_inputs) + pressed = true; + if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id) + { + // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. + g.NavActivateId = id; // This is so SetActiveId assign a Nav source + SetActiveID(id, window); + if ((nav_activated_by_code || nav_activated_by_inputs) && !(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); + } + } + + bool held = false; + if (g.ActiveId == id) + { + if (pressed) + g.ActiveIdHasBeenPressedBefore = true; + if (g.ActiveIdSource == ImGuiInputSource_Mouse) + { + if (g.ActiveIdIsJustActivated) + g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; + if (g.IO.MouseDown[0]) + { + held = true; + } + else + { + if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease) && !g.DragDropActive) + { + bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDownWasDoubleClick[0]; + bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay; // Repeat mode trumps + if (!is_double_click_release && !is_repeating_already) + pressed = true; + } + ClearActiveID(); + } + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + g.NavDisableHighlight = true; + } + else if (g.ActiveIdSource == ImGuiInputSource_Nav) + { + if (g.NavActivateDownId != id) + ClearActiveID(); + } + } + + if (out_hovered) *out_hovered = hovered; + if (out_held) *out_held = held; + + return pressed; +} + +bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + ImVec2 pos = window->DC.CursorPos; + if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) + pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y; + ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); + + const ImRect bb(pos, pos + size); + ItemSize(size, style.FramePadding.y); + if (!ItemAdd(bb, id)) + return false; + + if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + flags |= ImGuiButtonFlags_Repeat; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); + if (pressed) + MarkItemEdited(id); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderNavHighlight(bb, id); + RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); + RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); + + // Automatically close popups + //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) + // CloseCurrentPopup(); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); + return pressed; +} + +bool ImGui::Button(const char* label, const ImVec2& size_arg) +{ + return ButtonEx(label, size_arg, 0); +} + +// Small buttons fits within text without additional vertical spacing. +bool ImGui::SmallButton(const char* label) +{ + ImGuiContext& g = *GImGui; + float backup_padding_y = g.Style.FramePadding.y; + g.Style.FramePadding.y = 0.0f; + bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine); + g.Style.FramePadding.y = backup_padding_y; + return pressed; +} + +// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. +// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) +bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + // Cannot use zero-size for InvisibleButton(). Unlike Button() there is not way to fallback using the label size. + IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f); + + const ImGuiID id = window->GetID(str_id); + ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(size); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + return pressed; +} + +bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiID id = window->GetID(str_id); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + const float default_size = GetFrameHeight(); + ItemSize(size, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); + if (!ItemAdd(bb, id)) + return false; + + if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + flags |= ImGuiButtonFlags_Repeat; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); + + // Render + const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + const ImU32 text_col = GetColorU32(ImGuiCol_Text); + RenderNavHighlight(bb, id); + RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding); + RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), text_col, dir); + + return pressed; +} + +bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir) +{ + float sz = GetFrameHeight(); + return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), 0); +} + +// Button to close a window +bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)//, float size) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window. + // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible). + const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f); + bool is_clipped = !ItemAdd(bb, id); + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + if (is_clipped) + return pressed; + + // Render + ImU32 col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); + ImVec2 center = bb.GetCenter(); + if (hovered) + window->DrawList->AddCircleFilled(center, ImMax(2.0f, g.FontSize * 0.5f + 1.0f), col, 12); + + float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; + ImU32 cross_col = GetColorU32(ImGuiCol_Text); + center -= ImVec2(0.5f, 0.5f); + window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), cross_col, 1.0f); + window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), cross_col, 1.0f); + + return pressed; +} + +bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f); + ItemAdd(bb, id); + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); + + // Render + ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + ImU32 text_col = GetColorU32(ImGuiCol_Text); + ImVec2 center = bb.GetCenter(); + if (hovered || held) + window->DrawList->AddCircleFilled(center/*+ ImVec2(0.0f, -0.5f)*/, g.FontSize * 0.5f + 1.0f, bg_col, 12); + RenderArrow(window->DrawList, bb.Min + g.Style.FramePadding, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); + + // Switch to moving the window after mouse is moved beyond the initial drag threshold + if (IsItemActive() && IsMouseDragging()) + StartMouseMovingWindow(window); + + return pressed; +} + +ImGuiID ImGui::GetScrollbarID(ImGuiWindow* window, ImGuiAxis axis) +{ + return window->GetIDNoKeepAlive(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY"); +} + +// Vertical/Horizontal scrollbar +// The entire piece of code below is rather confusing because: +// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) +// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar +// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. +// Still, the code should probably be made simpler.. +bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float size_avail_v, float size_contents_v, ImDrawCornerFlags rounding_corners) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + const float bb_frame_width = bb_frame.GetWidth(); + const float bb_frame_height = bb_frame.GetHeight(); + if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f) + return false; + + // When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the resize grab) + float alpha = 1.0f; + if ((axis == ImGuiAxis_Y) && bb_frame_height < g.FontSize + g.Style.FramePadding.y * 2.0f) + alpha = ImSaturate((bb_frame_height - g.FontSize) / (g.Style.FramePadding.y * 2.0f)); + if (alpha <= 0.0f) + return false; + + const ImGuiStyle& style = g.Style; + const bool allow_interaction = (alpha >= 1.0f); + const bool horizontal = (axis == ImGuiAxis_X); + + ImRect bb = bb_frame; + bb.Expand(ImVec2(-ImClamp((float)(int)((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f))); + + // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) + const float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight(); + + // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) + // But we maintain a minimum size in pixel to allow for the user to still aim inside. + IM_ASSERT(ImMax(size_contents_v, size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. + const float win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), 1.0f); + const float grab_h_pixels = ImClamp(scrollbar_size_v * (size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v); + const float grab_h_norm = grab_h_pixels / scrollbar_size_v; + + // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). + bool held = false; + bool hovered = false; + ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); + + float scroll_max = ImMax(1.0f, size_contents_v - size_avail_v); + float scroll_ratio = ImSaturate(*p_scroll_v / scroll_max); + float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; + if (held && allow_interaction && grab_h_norm < 1.0f) + { + float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y; + float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; + + // Click position in scrollbar normalized space (0.0f->1.0f) + const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); + SetHoveredID(id); + + bool seek_absolute = false; + if (g.ActiveIdIsJustActivated) + { + // On initial click calculate the distance between mouse and the center of the grab + seek_absolute = (clicked_v_norm < grab_v_norm || clicked_v_norm > grab_v_norm + grab_h_norm); + if (seek_absolute) + g.ScrollbarClickDeltaToGrabCenter = 0.0f; + else + g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f; + } + + // Apply scroll + // It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position + const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm)); + *p_scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); + + // Update values for rendering + scroll_ratio = ImSaturate(*p_scroll_v / scroll_max); + grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; + + // Update distance to grab now that we have seeked and saturated + if (seek_absolute) + g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f; + } + + // Render + window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, rounding_corners); + const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha); + ImRect grab_rect; + if (horizontal) + grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y); + else + grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels); + window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding); + + return held; +} + +void ImGui::Scrollbar(ImGuiAxis axis) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + const ImGuiID id = GetScrollbarID(window, axis); + KeepAliveID(id); + + // Calculate scrollbar bounding box + const ImRect outer_rect = window->Rect(); + const ImRect inner_rect = window->InnerRect; + const float border_size = window->WindowBorderSize; + const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; + IM_ASSERT(scrollbar_size > 0.0f); + const float other_scrollbar_size = window->ScrollbarSizes[axis]; + ImDrawCornerFlags rounding_corners = (other_scrollbar_size <= 0.0f) ? ImDrawCornerFlags_BotRight : 0; + ImRect bb; + if (axis == ImGuiAxis_X) + { + bb.Min = ImVec2(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size)); + bb.Max = ImVec2(inner_rect.Max.x, outer_rect.Max.y); + rounding_corners |= ImDrawCornerFlags_BotLeft; + } + else + { + bb.Min = ImVec2(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y); + bb.Max = ImVec2(outer_rect.Max.x, window->InnerRect.Max.y); + rounding_corners |= ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0; + } + ScrollbarEx(bb, id, axis, &window->Scroll[axis], inner_rect.Max[axis] - inner_rect.Min[axis], window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f, rounding_corners); +} + +void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + if (border_col.w > 0.0f) + bb.Max += ImVec2(2, 2); + ItemSize(bb); + if (!ItemAdd(bb, 0)) + return; + + if (border_col.w > 0.0f) + { + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f); + window->DrawList->AddImage(user_texture_id, bb.Min + ImVec2(1, 1), bb.Max - ImVec2(1, 1), uv0, uv1, GetColorU32(tint_col)); + } + else + { + window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col)); + } +} + +// frame_padding < 0: uses FramePadding from style (default) +// frame_padding = 0: no framing +// frame_padding > 0: set framing size +// The color used are the button colors. +bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + // Default to using texture ID as ID. User can still push string/integer prefixes. + // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV. + PushID((void*)(intptr_t)user_texture_id); + const ImGuiID id = window->GetID("#image"); + PopID(); + + const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2); + const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size); + ItemSize(bb); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderNavHighlight(bb, id); + RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding)); + if (bg_col.w > 0.0f) + window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col)); + window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col)); + + return pressed; +} + +bool ImGui::Checkbox(const char* label, bool* v) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + const float square_sz = GetFrameHeight(); + const ImVec2 pos = window->DC.CursorPos; + const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); + if (pressed) + { + *v = !(*v); + MarkItemEdited(id); + } + + const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); + RenderNavHighlight(total_bb, id); + RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); + ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); + if (window->DC.ItemFlags & ImGuiItemFlags_MixedValue) + { + // Undocumented tristate/mixed/indeterminate checkbox (#2644) + ImVec2 pad(ImMax(1.0f, (float)(int)(square_sz / 3.6f)), ImMax(1.0f, (float)(int)(square_sz / 3.6f))); + window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding); + } + else if (*v) + { + const float pad = ImMax(1.0f, (float)(int)(square_sz / 6.0f)); + RenderCheckMark(check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad*2.0f); + } + + if (g.LogEnabled) + LogRenderedText(&total_bb.Min, *v ? "[x]" : "[ ]"); + if (label_size.x > 0.0f) + RenderText(ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y), label); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); + return pressed; +} + +bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value) +{ + bool v = ((*flags & flags_value) == flags_value); + bool pressed = Checkbox(label, &v); + if (pressed) + { + if (v) + *flags |= flags_value; + else + *flags &= ~flags_value; + } + + return pressed; +} + +bool ImGui::RadioButton(const char* label, bool active) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + const float square_sz = GetFrameHeight(); + const ImVec2 pos = window->DC.CursorPos; + const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); + const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id)) + return false; + + ImVec2 center = check_bb.GetCenter(); + center.x = (float)(int)center.x + 0.5f; + center.y = (float)(int)center.y + 0.5f; + const float radius = (square_sz - 1.0f) * 0.5f; + + bool hovered, held; + bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); + if (pressed) + MarkItemEdited(id); + + RenderNavHighlight(total_bb, id); + window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); + if (active) + { + const float pad = ImMax(1.0f, (float)(int)(square_sz / 6.0f)); + window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark), 16); + } + + if (style.FrameBorderSize > 0.0f) + { + window->DrawList->AddCircle(center + ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize); + window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize); + } + + if (g.LogEnabled) + LogRenderedText(&total_bb.Min, active ? "(x)" : "( )"); + if (label_size.x > 0.0f) + RenderText(ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y), label); + + return pressed; +} + +bool ImGui::RadioButton(const char* label, int* v, int v_button) +{ + const bool pressed = RadioButton(label, *v == v_button); + if (pressed) + *v = v_button; + return pressed; +} + +// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size +void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + ImVec2 pos = window->DC.CursorPos; + ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f); + ImRect bb(pos, pos + size); + ItemSize(size, style.FramePadding.y); + if (!ItemAdd(bb, 0)) + return; + + // Render + fraction = ImSaturate(fraction); + RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); + const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y); + RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding); + + // Default displaying the fraction as percentage string, but user can override it + char overlay_buf[32]; + if (!overlay) + { + ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f); + overlay = overlay_buf; + } + + ImVec2 overlay_size = CalcTextSize(overlay, NULL); + if (overlay_size.x > 0.0f) + RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb); +} + +void ImGui::Bullet() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); + ItemSize(bb); + if (!ItemAdd(bb, 0)) + { + SameLine(0, style.FramePadding.x*2); + return; + } + + // Render and stay on same line + ImU32 text_col = GetColorU32(ImGuiCol_Text); + RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), text_col); + SameLine(0, style.FramePadding.x * 2.0f); +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Low-level Layout helpers +//------------------------------------------------------------------------- +// - Spacing() +// - Dummy() +// - NewLine() +// - AlignTextToFramePadding() +// - SeparatorEx() [Internal] +// - Separator() +// - SplitterBehavior() [Internal] +// - ShrinkWidths() [Internal] +//------------------------------------------------------------------------- + +void ImGui::Spacing() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + ItemSize(ImVec2(0,0)); +} + +void ImGui::Dummy(const ImVec2& size) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(size); + ItemAdd(bb, 0); +} + +void ImGui::NewLine() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiLayoutType backup_layout_type = window->DC.LayoutType; + window->DC.LayoutType = ImGuiLayoutType_Vertical; + if (window->DC.CurrLineSize.y > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height. + ItemSize(ImVec2(0,0)); + else + ItemSize(ImVec2(0.0f, g.FontSize)); + window->DC.LayoutType = backup_layout_type; +} + +void ImGui::AlignTextToFramePadding() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + window->DC.CurrLineSize.y = ImMax(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y * 2); + window->DC.CurrLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, g.Style.FramePadding.y); +} + +// Horizontal/vertical separating line +void ImGui::SeparatorEx(ImGuiSeparatorFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))); // Check that only 1 option is selected + + float thickness_draw = 1.0f; + float thickness_layout = 0.0f; + if (flags & ImGuiSeparatorFlags_Vertical) + { + // Vertical separator, for menu bars (use current line height). Not exposed because it is misleading and it doesn't have an effect on regular layout. + float y1 = window->DC.CursorPos.y; + float y2 = window->DC.CursorPos.y + window->DC.CurrLineSize.y; + const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + thickness_draw, y2)); + ItemSize(ImVec2(thickness_layout, 0.0f)); + if (!ItemAdd(bb, 0)) + return; + + // Draw + window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator)); + if (g.LogEnabled) + LogText(" |"); + } + else if (flags & ImGuiSeparatorFlags_Horizontal) + { + // Horizontal Separator + float x1 = window->Pos.x; + float x2 = window->Pos.x + window->Size.x; + if (!window->DC.GroupStack.empty()) + x1 += window->DC.Indent.x; + + ImGuiColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL; + if (columns) + PushColumnsBackground(); + + // We don't provide our width to the layout so that it doesn't get feed back into AutoFit + const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness_draw)); + ItemSize(ImVec2(0.0f, thickness_layout)); + if (!ItemAdd(bb, 0)) + { + if (columns) + PopColumnsBackground(); + return; + } + + // Draw + window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x, bb.Min.y), GetColorU32(ImGuiCol_Separator)); + if (g.LogEnabled) + LogRenderedText(&bb.Min, "--------------------------------"); + + if (columns) + { + PopColumnsBackground(); + columns->LineMinY = window->DC.CursorPos.y; + } + } +} + +void ImGui::Separator() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + // Those flags should eventually be overridable by the user + ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; + flags |= ImGuiSeparatorFlags_SpanAllColumns; + SeparatorEx(flags); +} + +// Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise. +bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; + window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; + bool item_add = ItemAdd(bb, id); + window->DC.ItemFlags = item_flags_backup; + if (!item_add) + return false; + + bool hovered, held; + ImRect bb_interact = bb; + bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); + ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); + if (g.ActiveId != id) + SetItemAllowOverlap(); + + if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) + SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); + + ImRect bb_render = bb; + if (held) + { + ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min; + float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x; + + // Minimum pane size + float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1); + float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2); + if (mouse_delta < -size_1_maximum_delta) + mouse_delta = -size_1_maximum_delta; + if (mouse_delta > size_2_maximum_delta) + mouse_delta = size_2_maximum_delta; + + // Apply resize + if (mouse_delta != 0.0f) + { + if (mouse_delta < 0.0f) + IM_ASSERT(*size1 + mouse_delta >= min_size1); + if (mouse_delta > 0.0f) + IM_ASSERT(*size2 - mouse_delta >= min_size2); + *size1 += mouse_delta; + *size2 -= mouse_delta; + bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta)); + MarkItemEdited(id); + } + } + + // Render + const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); + window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, g.Style.FrameRounding); + + return held; +} + +static int IMGUI_CDECL ShrinkWidthItemComparer(const void* lhs, const void* rhs) +{ + const ImGuiShrinkWidthItem* a = (const ImGuiShrinkWidthItem*)lhs; + const ImGuiShrinkWidthItem* b = (const ImGuiShrinkWidthItem*)rhs; + if (int d = (int)(b->Width - a->Width)) + return d; + return (b->Index - a->Index); +} + +// Shrink excess width from a set of item, by removing width from the larger items first. +void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess) +{ + if (count > 1) + ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer); + int count_same_width = 1; + while (width_excess > 0.0f && count_same_width < count) + { + while (count_same_width < count && items[0].Width == items[count_same_width].Width) + count_same_width++; + float width_to_remove_per_item_max = (count_same_width < count) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f); + float width_to_remove_per_item = ImMin(width_excess / count_same_width, width_to_remove_per_item_max); + for (int item_n = 0; item_n < count_same_width; item_n++) + items[item_n].Width -= width_to_remove_per_item; + width_excess -= width_to_remove_per_item * count_same_width; + } +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: ComboBox +//------------------------------------------------------------------------- +// - BeginCombo() +// - EndCombo() +// - Combo() +//------------------------------------------------------------------------- + +static float CalcMaxPopupHeightFromItemCount(int items_count) +{ + ImGuiContext& g = *GImGui; + if (items_count <= 0) + return FLT_MAX; + return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2); +} + +bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) +{ + // Always consume the SetNextWindowSizeConstraint() call in our early return paths + ImGuiContext& g = *GImGui; + bool has_window_size_constraint = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) != 0; + g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; + + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together + + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const float expected_w = CalcItemWidth(); + const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : expected_w; + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); + bool popup_open = IsPopupOpen(id); + + const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + const float value_x2 = ImMax(frame_bb.Min.x, frame_bb.Max.x - arrow_size); + RenderNavHighlight(frame_bb, id); + if (!(flags & ImGuiComboFlags_NoPreview)) + window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(value_x2, frame_bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Left); + if (!(flags & ImGuiComboFlags_NoArrowButton)) + { + ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + ImU32 text_col = GetColorU32(ImGuiCol_Text); + window->DrawList->AddRectFilled(ImVec2(value_x2, frame_bb.Min.y), frame_bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right); + if (value_x2 + arrow_size - style.FramePadding.x <= frame_bb.Max.x) + RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); + } + RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding); + if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) + RenderTextClipped(frame_bb.Min + style.FramePadding, ImVec2(value_x2, frame_bb.Max.y), preview_value, NULL, NULL, ImVec2(0.0f,0.0f)); + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + if ((pressed || g.NavActivateId == id) && !popup_open) + { + if (window->DC.NavLayerCurrent == 0) + window->NavLastIds[0] = id; + OpenPopupEx(id); + popup_open = true; + } + + if (!popup_open) + return false; + + if (has_window_size_constraint) + { + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint; + g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); + } + else + { + if ((flags & ImGuiComboFlags_HeightMask_) == 0) + flags |= ImGuiComboFlags_HeightRegular; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one + int popup_max_height_in_items = -1; + if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; + else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; + else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20; + SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); + } + + char name[16]; + ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth + + // Peak into expected window size so we can position it + if (ImGuiWindow* popup_window = FindWindowByName(name)) + if (popup_window->WasActive) + { + ImVec2 size_expected = CalcWindowExpectedSize(popup_window); + if (flags & ImGuiComboFlags_PopupAlignLeft) + popup_window->AutoPosLastDirection = ImGuiDir_Left; + ImRect r_outer = GetWindowAllowedExtentRect(popup_window); + ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox); + SetNextWindowPos(pos); + } + + // Horizontally align ourselves with the framed text + ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings; + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y)); + bool ret = Begin(name, NULL, window_flags); + PopStyleVar(); + if (!ret) + { + EndPopup(); + IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above + return false; + } + return true; +} + +void ImGui::EndCombo() +{ + EndPopup(); +} + +// Getter for the old Combo() API: const char*[] +static bool Items_ArrayGetter(void* data, int idx, const char** out_text) +{ + const char* const* items = (const char* const*)data; + if (out_text) + *out_text = items[idx]; + return true; +} + +// Getter for the old Combo() API: "item1\0item2\0item3\0" +static bool Items_SingleStringGetter(void* data, int idx, const char** out_text) +{ + // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited. + const char* items_separated_by_zeros = (const char*)data; + int items_count = 0; + const char* p = items_separated_by_zeros; + while (*p) + { + if (idx == items_count) + break; + p += strlen(p) + 1; + items_count++; + } + if (!*p) + return false; + if (out_text) + *out_text = p; + return true; +} + +// Old API, prefer using BeginCombo() nowadays if you can. +bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items) +{ + ImGuiContext& g = *GImGui; + + // Call the getter to obtain the preview string which is a parameter to BeginCombo() + const char* preview_value = NULL; + if (*current_item >= 0 && *current_item < items_count) + items_getter(data, *current_item, &preview_value); + + // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here. + if (popup_max_height_in_items != -1 && !(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)) + SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); + + if (!BeginCombo(label, preview_value, ImGuiComboFlags_None)) + return false; + + // Display items + // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed) + bool value_changed = false; + for (int i = 0; i < items_count; i++) + { + PushID((void*)(intptr_t)i); + const bool item_selected = (i == *current_item); + const char* item_text; + if (!items_getter(data, i, &item_text)) + item_text = "*Unknown item*"; + if (Selectable(item_text, item_selected)) + { + value_changed = true; + *current_item = i; + } + if (item_selected) + SetItemDefaultFocus(); + PopID(); + } + + EndCombo(); + return value_changed; +} + +// Combo box helper allowing to pass an array of strings. +bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items) +{ + const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items); + return value_changed; +} + +// Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0" +bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) +{ + int items_count = 0; + const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open + while (*p) + { + p += strlen(p) + 1; + items_count++; + } + bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); + return value_changed; +} + +//------------------------------------------------------------------------- +// [SECTION] Data Type and Data Formatting Helpers [Internal] +//------------------------------------------------------------------------- +// - PatchFormatStringFloatToInt() +// - DataTypeGetInfo() +// - DataTypeFormatString() +// - DataTypeApplyOp() +// - DataTypeApplyOpFromText() +// - GetMinimumStepAtDecimalPrecision +// - RoundScalarWithFormat<>() +//------------------------------------------------------------------------- + +static const ImGuiDataTypeInfo GDataTypeInfo[] = +{ + { sizeof(char), "%d", "%d" }, // ImGuiDataType_S8 + { sizeof(unsigned char), "%u", "%u" }, + { sizeof(short), "%d", "%d" }, // ImGuiDataType_S16 + { sizeof(unsigned short), "%u", "%u" }, + { sizeof(int), "%d", "%d" }, // ImGuiDataType_S32 + { sizeof(unsigned int), "%u", "%u" }, +#ifdef _MSC_VER + { sizeof(ImS64), "%I64d","%I64d" }, // ImGuiDataType_S64 + { sizeof(ImU64), "%I64u","%I64u" }, +#else + { sizeof(ImS64), "%lld", "%lld" }, // ImGuiDataType_S64 + { sizeof(ImU64), "%llu", "%llu" }, +#endif + { sizeof(float), "%f", "%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) + { sizeof(double), "%f", "%lf" }, // ImGuiDataType_Double +}; +IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); + +// FIXME-LEGACY: Prior to 1.61 our DragInt() function internally used floats and because of this the compile-time default value for format was "%.0f". +// Even though we changed the compile-time default, we expect users to have carried %f around, which would break the display of DragInt() calls. +// To honor backward compatibility we are rewriting the format string, unless IMGUI_DISABLE_OBSOLETE_FUNCTIONS is enabled. What could possibly go wrong?! +static const char* PatchFormatStringFloatToInt(const char* fmt) +{ + if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '0' && fmt[3] == 'f' && fmt[4] == 0) // Fast legacy path for "%.0f" which is expected to be the most common case. + return "%d"; + const char* fmt_start = ImParseFormatFindStart(fmt); // Find % (if any, and ignore %%) + const char* fmt_end = ImParseFormatFindEnd(fmt_start); // Find end of format specifier, which itself is an exercise of confidence/recklessness (because snprintf is dependent on libc or user). + if (fmt_end > fmt_start && fmt_end[-1] == 'f') + { +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if (fmt_start == fmt && fmt_end[0] == 0) + return "%d"; + ImGuiContext& g = *GImGui; + ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%.*s%%d%s", (int)(fmt_start - fmt), fmt, fmt_end); // Honor leading and trailing decorations, but lose alignment/precision. + return g.TempBuffer; +#else + IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d" +#endif + } + return fmt; +} + +const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type) +{ + IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); + return &GDataTypeInfo[data_type]; +} + +int ImGui::DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format) +{ + // Signedness doesn't matter when pushing integer arguments + if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) + return ImFormatString(buf, buf_size, format, *(const ImU32*)data_ptr); + if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) + return ImFormatString(buf, buf_size, format, *(const ImU64*)data_ptr); + if (data_type == ImGuiDataType_Float) + return ImFormatString(buf, buf_size, format, *(const float*)data_ptr); + if (data_type == ImGuiDataType_Double) + return ImFormatString(buf, buf_size, format, *(const double*)data_ptr); + if (data_type == ImGuiDataType_S8) + return ImFormatString(buf, buf_size, format, *(const ImS8*)data_ptr); + if (data_type == ImGuiDataType_U8) + return ImFormatString(buf, buf_size, format, *(const ImU8*)data_ptr); + if (data_type == ImGuiDataType_S16) + return ImFormatString(buf, buf_size, format, *(const ImS16*)data_ptr); + if (data_type == ImGuiDataType_U16) + return ImFormatString(buf, buf_size, format, *(const ImU16*)data_ptr); + IM_ASSERT(0); + return 0; +} + +void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2) +{ + IM_ASSERT(op == '+' || op == '-'); + switch (data_type) + { + case ImGuiDataType_S8: + if (op == '+') { *(ImS8*)output = ImAddClampOverflow(*(const ImS8*)arg1, *(const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); } + if (op == '-') { *(ImS8*)output = ImSubClampOverflow(*(const ImS8*)arg1, *(const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); } + return; + case ImGuiDataType_U8: + if (op == '+') { *(ImU8*)output = ImAddClampOverflow(*(const ImU8*)arg1, *(const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); } + if (op == '-') { *(ImU8*)output = ImSubClampOverflow(*(const ImU8*)arg1, *(const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); } + return; + case ImGuiDataType_S16: + if (op == '+') { *(ImS16*)output = ImAddClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); } + if (op == '-') { *(ImS16*)output = ImSubClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); } + return; + case ImGuiDataType_U16: + if (op == '+') { *(ImU16*)output = ImAddClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); } + if (op == '-') { *(ImU16*)output = ImSubClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); } + return; + case ImGuiDataType_S32: + if (op == '+') { *(ImS32*)output = ImAddClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); } + if (op == '-') { *(ImS32*)output = ImSubClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); } + return; + case ImGuiDataType_U32: + if (op == '+') { *(ImU32*)output = ImAddClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); } + if (op == '-') { *(ImU32*)output = ImSubClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); } + return; + case ImGuiDataType_S64: + if (op == '+') { *(ImS64*)output = ImAddClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); } + if (op == '-') { *(ImS64*)output = ImSubClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); } + return; + case ImGuiDataType_U64: + if (op == '+') { *(ImU64*)output = ImAddClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); } + if (op == '-') { *(ImU64*)output = ImSubClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); } + return; + case ImGuiDataType_Float: + if (op == '+') { *(float*)output = *(const float*)arg1 + *(const float*)arg2; } + if (op == '-') { *(float*)output = *(const float*)arg1 - *(const float*)arg2; } + return; + case ImGuiDataType_Double: + if (op == '+') { *(double*)output = *(const double*)arg1 + *(const double*)arg2; } + if (op == '-') { *(double*)output = *(const double*)arg1 - *(const double*)arg2; } + return; + case ImGuiDataType_COUNT: break; + } + IM_ASSERT(0); +} + +// User can input math operators (e.g. +100) to edit a numerical values. +// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. +bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format) +{ + while (ImCharIsBlankA(*buf)) + buf++; + + // We don't support '-' op because it would conflict with inputing negative value. + // Instead you can use +-100 to subtract from an existing value + char op = buf[0]; + if (op == '+' || op == '*' || op == '/') + { + buf++; + while (ImCharIsBlankA(*buf)) + buf++; + } + else + { + op = 0; + } + if (!buf[0]) + return false; + + // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. + IM_ASSERT(data_type < ImGuiDataType_COUNT); + int data_backup[2]; + const ImGuiDataTypeInfo* type_info = ImGui::DataTypeGetInfo(data_type); + IM_ASSERT(type_info->Size <= sizeof(data_backup)); + memcpy(data_backup, data_ptr, type_info->Size); + + if (format == NULL) + format = type_info->ScanFmt; + + // FIXME-LEGACY: The aim is to remove those operators and write a proper expression evaluator at some point.. + int arg1i = 0; + if (data_type == ImGuiDataType_S32) + { + int* v = (int*)data_ptr; + int arg0i = *v; + float arg1f = 0.0f; + if (op && sscanf(initial_value_buf, format, &arg0i) < 1) + return false; + // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision + if (op == '+') { if (sscanf(buf, "%d", &arg1i)) *v = (int)(arg0i + arg1i); } // Add (use "+-" to subtract) + else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (int)(arg0i * arg1f); } // Multiply + else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } // Divide + else { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; } // Assign constant + } + else if (data_type == ImGuiDataType_Float) + { + // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in + format = "%f"; + float* v = (float*)data_ptr; + float arg0f = *v, arg1f = 0.0f; + if (op && sscanf(initial_value_buf, format, &arg0f) < 1) + return false; + if (sscanf(buf, format, &arg1f) < 1) + return false; + if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) + else if (op == '*') { *v = arg0f * arg1f; } // Multiply + else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide + else { *v = arg1f; } // Assign constant + } + else if (data_type == ImGuiDataType_Double) + { + format = "%lf"; // scanf differentiate float/double unlike printf which forces everything to double because of ellipsis + double* v = (double*)data_ptr; + double arg0f = *v, arg1f = 0.0; + if (op && sscanf(initial_value_buf, format, &arg0f) < 1) + return false; + if (sscanf(buf, format, &arg1f) < 1) + return false; + if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) + else if (op == '*') { *v = arg0f * arg1f; } // Multiply + else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide + else { *v = arg1f; } // Assign constant + } + else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) + { + // All other types assign constant + // We don't bother handling support for legacy operators since they are a little too crappy. Instead we will later implement a proper expression evaluator in the future. + sscanf(buf, format, data_ptr); + } + else + { + // Small types need a 32-bit buffer to receive the result from scanf() + int v32; + sscanf(buf, format, &v32); + if (data_type == ImGuiDataType_S8) + *(ImS8*)data_ptr = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX); + else if (data_type == ImGuiDataType_U8) + *(ImU8*)data_ptr = (ImU8)ImClamp(v32, (int)IM_U8_MIN, (int)IM_U8_MAX); + else if (data_type == ImGuiDataType_S16) + *(ImS16*)data_ptr = (ImS16)ImClamp(v32, (int)IM_S16_MIN, (int)IM_S16_MAX); + else if (data_type == ImGuiDataType_U16) + *(ImU16*)data_ptr = (ImU16)ImClamp(v32, (int)IM_U16_MIN, (int)IM_U16_MAX); + else + IM_ASSERT(0); + } + + return memcmp(data_backup, data_ptr, type_info->Size) != 0; +} + +static float GetMinimumStepAtDecimalPrecision(int decimal_precision) +{ + static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; + if (decimal_precision < 0) + return FLT_MIN; + return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); +} + +template +static const char* ImAtoi(const char* src, TYPE* output) +{ + int negative = 0; + if (*src == '-') { negative = 1; src++; } + if (*src == '+') { src++; } + TYPE v = 0; + while (*src >= '0' && *src <= '9') + v = (v * 10) + (*src++ - '0'); + *output = negative ? -v : v; + return src; +} + +template +TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v) +{ + const char* fmt_start = ImParseFormatFindStart(format); + if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string + return v; + char v_str[64]; + ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v); + const char* p = v_str; + while (*p == ' ') + p++; + if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) + v = (TYPE)ImAtof(p); + else + ImAtoi(p, (SIGNEDTYPE*)&v); + return v; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc. +//------------------------------------------------------------------------- +// - DragBehaviorT<>() [Internal] +// - DragBehavior() [Internal] +// - DragScalar() +// - DragScalarN() +// - DragFloat() +// - DragFloat2() +// - DragFloat3() +// - DragFloat4() +// - DragFloatRange2() +// - DragInt() +// - DragInt2() +// - DragInt3() +// - DragInt4() +// - DragIntRange2() +//------------------------------------------------------------------------- + +// This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls) +template +bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiDragFlags flags) +{ + ImGuiContext& g = *GImGui; + const ImGuiAxis axis = (flags & ImGuiDragFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; + const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); + const bool has_min_max = (v_min != v_max); + const bool is_power = (power != 1.0f && is_decimal && has_min_max && (v_max - v_min < FLT_MAX)); + + // Default tweak speed + if (v_speed == 0.0f && has_min_max && (v_max - v_min < FLT_MAX)) + v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio); + + // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings + float adjust_delta = 0.0f; + if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && g.IO.MouseDragMaxDistanceSqr[0] > 1.0f*1.0f) + { + adjust_delta = g.IO.MouseDelta[axis]; + if (g.IO.KeyAlt) + adjust_delta *= 1.0f / 100.0f; + if (g.IO.KeyShift) + adjust_delta *= 10.0f; + } + else if (g.ActiveIdSource == ImGuiInputSource_Nav) + { + int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0; + adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f / 10.0f, 10.0f)[axis]; + v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); + } + adjust_delta *= v_speed; + + // For vertical drag we currently assume that Up=higher value (like we do with vertical sliders). This may become a parameter. + if (axis == ImGuiAxis_Y) + adjust_delta = -adjust_delta; + + // Clear current value on activation + // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300. + bool is_just_activated = g.ActiveIdIsJustActivated; + bool is_already_past_limits_and_pushing_outward = has_min_max && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f)); + bool is_drag_direction_change_with_power = is_power && ((adjust_delta < 0 && g.DragCurrentAccum > 0) || (adjust_delta > 0 && g.DragCurrentAccum < 0)); + if (is_just_activated || is_already_past_limits_and_pushing_outward || is_drag_direction_change_with_power) + { + g.DragCurrentAccum = 0.0f; + g.DragCurrentAccumDirty = false; + } + else if (adjust_delta != 0.0f) + { + g.DragCurrentAccum += adjust_delta; + g.DragCurrentAccumDirty = true; + } + + if (!g.DragCurrentAccumDirty) + return false; + + TYPE v_cur = *v; + FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f; + + if (is_power) + { + // Offset + round to user desired precision, with a curve on the v_min..v_max range to get more precision on one side of the range + FLOATTYPE v_old_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power); + FLOATTYPE v_new_norm_curved = v_old_norm_curved + (g.DragCurrentAccum / (v_max - v_min)); + v_cur = v_min + (TYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min); + v_old_ref_for_accum_remainder = v_old_norm_curved; + } + else + { + v_cur += (TYPE)g.DragCurrentAccum; + } + + // Round to user desired precision based on format string + v_cur = RoundScalarWithFormatT(format, data_type, v_cur); + + // Preserve remainder after rounding has been applied. This also allow slow tweaking of values. + g.DragCurrentAccumDirty = false; + if (is_power) + { + FLOATTYPE v_cur_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power); + g.DragCurrentAccum -= (float)(v_cur_norm_curved - v_old_ref_for_accum_remainder); + } + else + { + g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v); + } + + // Lose zero sign for float/double + if (v_cur == (TYPE)-0) + v_cur = (TYPE)0; + + // Clamp values (+ handle overflow/wrap-around for integer types) + if (*v != v_cur && has_min_max) + { + if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_decimal)) + v_cur = v_min; + if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_decimal)) + v_cur = v_max; + } + + // Apply result + if (*v == v_cur) + return false; + *v = v_cur; + return true; +} + +bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power, ImGuiDragFlags flags) +{ + ImGuiContext& g = *GImGui; + if (g.ActiveId == id) + { + if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) + ClearActiveID(); + else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) + ClearActiveID(); + } + if (g.ActiveId != id) + return false; + + switch (data_type) + { + case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)v; bool r = DragBehaviorT(ImGuiDataType_S32, &v32, v_speed, v_min ? *(const ImS8*) v_min : IM_S8_MIN, v_max ? *(const ImS8*)v_max : IM_S8_MAX, format, power, flags); if (r) *(ImS8*)v = (ImS8)v32; return r; } + case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)v; bool r = DragBehaviorT(ImGuiDataType_U32, &v32, v_speed, v_min ? *(const ImU8*) v_min : IM_U8_MIN, v_max ? *(const ImU8*)v_max : IM_U8_MAX, format, power, flags); if (r) *(ImU8*)v = (ImU8)v32; return r; } + case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)v; bool r = DragBehaviorT(ImGuiDataType_S32, &v32, v_speed, v_min ? *(const ImS16*)v_min : IM_S16_MIN, v_max ? *(const ImS16*)v_max : IM_S16_MAX, format, power, flags); if (r) *(ImS16*)v = (ImS16)v32; return r; } + case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)v; bool r = DragBehaviorT(ImGuiDataType_U32, &v32, v_speed, v_min ? *(const ImU16*)v_min : IM_U16_MIN, v_max ? *(const ImU16*)v_max : IM_U16_MAX, format, power, flags); if (r) *(ImU16*)v = (ImU16)v32; return r; } + case ImGuiDataType_S32: return DragBehaviorT(data_type, (ImS32*)v, v_speed, v_min ? *(const ImS32* )v_min : IM_S32_MIN, v_max ? *(const ImS32* )v_max : IM_S32_MAX, format, power, flags); + case ImGuiDataType_U32: return DragBehaviorT(data_type, (ImU32*)v, v_speed, v_min ? *(const ImU32* )v_min : IM_U32_MIN, v_max ? *(const ImU32* )v_max : IM_U32_MAX, format, power, flags); + case ImGuiDataType_S64: return DragBehaviorT(data_type, (ImS64*)v, v_speed, v_min ? *(const ImS64* )v_min : IM_S64_MIN, v_max ? *(const ImS64* )v_max : IM_S64_MAX, format, power, flags); + case ImGuiDataType_U64: return DragBehaviorT(data_type, (ImU64*)v, v_speed, v_min ? *(const ImU64* )v_min : IM_U64_MIN, v_max ? *(const ImU64* )v_max : IM_U64_MAX, format, power, flags); + case ImGuiDataType_Float: return DragBehaviorT(data_type, (float*)v, v_speed, v_min ? *(const float* )v_min : -FLT_MAX, v_max ? *(const float* )v_max : FLT_MAX, format, power, flags); + case ImGuiDataType_Double: return DragBehaviorT(data_type, (double*)v, v_speed, v_min ? *(const double*)v_min : -DBL_MAX, v_max ? *(const double*)v_max : DBL_MAX, format, power, flags); + case ImGuiDataType_COUNT: break; + } + IM_ASSERT(0); + return false; +} + +bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + if (power != 1.0f) + IM_ASSERT(v_min != NULL && v_max != NULL); // When using a power curve the drag needs to have known bounds + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = CalcItemWidth(); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb)) + return false; + + // Default format string when passing NULL + if (format == NULL) + format = DataTypeGetInfo(data_type)->PrintFmt; + else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) + format = PatchFormatStringFloatToInt(format); + + // Tabbing or CTRL-clicking on Drag turns it into an input box + const bool hovered = ItemHoverable(frame_bb, id); + bool temp_input_is_active = TempInputTextIsActive(id); + bool temp_input_start = false; + if (!temp_input_is_active) + { + const bool focus_requested = FocusableItemRegister(window, id); + const bool clicked = (hovered && g.IO.MouseClicked[0]); + const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]); + if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavInputId == id) + { + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); + if (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavInputId == id) + { + temp_input_start = true; + FocusableItemUnregister(window); + } + } + } + if (temp_input_is_active || temp_input_start) + return TempInputTextScalar(frame_bb, id, label, data_type, v, format); + + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavHighlight(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); + + // Drag behavior + const bool value_changed = DragBehavior(id, data_type, v, v_speed, v_min, v_max, format, power, ImGuiDragFlags_None); + if (value_changed) + MarkItemEdited(id); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); + RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + return value_changed; +} + +bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min, const void* v_max, const char* format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components, CalcItemWidth()); + size_t type_size = GDataTypeInfo[data_type].Size; + for (int i = 0; i < components; i++) + { + PushID(i); + if (i > 0) + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= DragScalar("", data_type, v, v_speed, v_min, v_max, format, power); + PopID(); + PopItemWidth(); + v = (void*)((char*)v + type_size); + } + PopID(); + + const char* label_end = FindRenderedTextEnd(label); + if (label != label_end) + { + SameLine(0, g.Style.ItemInnerSpacing.x); + TextEx(label, label_end); + } + + EndGroup(); + return value_changed; +} + +bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power) +{ + return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); +} + +bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power) +{ + return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); +} + +bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power) +{ + return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); +} + +bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power) +{ + return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); +} + +bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + PushID(label); + BeginGroup(); + PushMultiItemsWidths(2, CalcItemWidth()); + + bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format, power); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, format_max ? format_max : format, power); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + + TextEx(label, FindRenderedTextEnd(label)); + EndGroup(); + PopID(); + return value_changed; +} + +// NB: v_speed is float to allow adjusting the drag speed with more precision +bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format) +{ + return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format); +} + +bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format) +{ + return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format); +} + +bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format) +{ + return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format); +} + +bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format) +{ + return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format); +} + +bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + PushID(label); + BeginGroup(); + PushMultiItemsWidths(2, CalcItemWidth()); + + bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, format_max ? format_max : format); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + + TextEx(label, FindRenderedTextEnd(label)); + EndGroup(); + PopID(); + + return value_changed; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. +//------------------------------------------------------------------------- +// - SliderBehaviorT<>() [Internal] +// - SliderBehavior() [Internal] +// - SliderScalar() +// - SliderScalarN() +// - SliderFloat() +// - SliderFloat2() +// - SliderFloat3() +// - SliderFloat4() +// - SliderAngle() +// - SliderInt() +// - SliderInt2() +// - SliderInt3() +// - SliderInt4() +// - VSliderScalar() +// - VSliderFloat() +// - VSliderInt() +//------------------------------------------------------------------------- + +template +float ImGui::SliderCalcRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float power, float linear_zero_pos) +{ + if (v_min == v_max) + return 0.0f; + + const bool is_power = (power != 1.0f) && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double); + const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min); + if (is_power) + { + if (v_clamped < 0.0f) + { + const float f = 1.0f - (float)((v_clamped - v_min) / (ImMin((TYPE)0, v_max) - v_min)); + return (1.0f - ImPow(f, 1.0f/power)) * linear_zero_pos; + } + else + { + const float f = (float)((v_clamped - ImMax((TYPE)0, v_min)) / (v_max - ImMax((TYPE)0, v_min))); + return linear_zero_pos + ImPow(f, 1.0f/power) * (1.0f - linear_zero_pos); + } + } + + // Linear slider + return (float)((FLOATTYPE)(v_clamped - v_min) / (FLOATTYPE)(v_max - v_min)); +} + +// FIXME: Move some of the code into SliderBehavior(). Current responsability is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc. +template +bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb) +{ + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; + const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); + const bool is_power = (power != 1.0f) && is_decimal; + + const float grab_padding = 2.0f; + const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f; + float grab_sz = style.GrabMinSize; + SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max); + if (!is_decimal && v_range >= 0) // v_range < 0 may happen on integer overflows + grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit + grab_sz = ImMin(grab_sz, slider_sz); + const float slider_usable_sz = slider_sz - grab_sz; + const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f; + const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5f; + + // For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f + float linear_zero_pos; // 0.0->1.0f + if (is_power && v_min * v_max < 0.0f) + { + // Different sign + const FLOATTYPE linear_dist_min_to_0 = ImPow(v_min >= 0 ? (FLOATTYPE)v_min : -(FLOATTYPE)v_min, (FLOATTYPE)1.0f / power); + const FLOATTYPE linear_dist_max_to_0 = ImPow(v_max >= 0 ? (FLOATTYPE)v_max : -(FLOATTYPE)v_max, (FLOATTYPE)1.0f / power); + linear_zero_pos = (float)(linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0)); + } + else + { + // Same sign + linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; + } + + // Process interacting with the slider + bool value_changed = false; + if (g.ActiveId == id) + { + bool set_new_value = false; + float clicked_t = 0.0f; + if (g.ActiveIdSource == ImGuiInputSource_Mouse) + { + if (!g.IO.MouseDown[0]) + { + ClearActiveID(); + } + else + { + const float mouse_abs_pos = g.IO.MousePos[axis]; + clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f; + if (axis == ImGuiAxis_Y) + clicked_t = 1.0f - clicked_t; + set_new_value = true; + } + } + else if (g.ActiveIdSource == ImGuiInputSource_Nav) + { + const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); + float delta = (axis == ImGuiAxis_X) ? delta2.x : -delta2.y; + if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) + { + ClearActiveID(); + } + else if (delta != 0.0f) + { + clicked_t = SliderCalcRatioFromValueT(data_type, *v, v_min, v_max, power, linear_zero_pos); + const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0; + if ((decimal_precision > 0) || is_power) + { + delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds + if (IsNavInputDown(ImGuiNavInput_TweakSlow)) + delta /= 10.0f; + } + else + { + if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow)) + delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps + else + delta /= 100.0f; + } + if (IsNavInputDown(ImGuiNavInput_TweakFast)) + delta *= 10.0f; + set_new_value = true; + if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits + set_new_value = false; + else + clicked_t = ImSaturate(clicked_t + delta); + } + } + + if (set_new_value) + { + TYPE v_new; + if (is_power) + { + // Account for power curve scale on both sides of the zero + if (clicked_t < linear_zero_pos) + { + // Negative: rescale to the negative range before powering + float a = 1.0f - (clicked_t / linear_zero_pos); + a = ImPow(a, power); + v_new = ImLerp(ImMin(v_max, (TYPE)0), v_min, a); + } + else + { + // Positive: rescale to the positive range before powering + float a; + if (ImFabs(linear_zero_pos - 1.0f) > 1.e-6f) + a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos); + else + a = clicked_t; + a = ImPow(a, power); + v_new = ImLerp(ImMax(v_min, (TYPE)0), v_max, a); + } + } + else + { + // Linear slider + if (is_decimal) + { + v_new = ImLerp(v_min, v_max, clicked_t); + } + else + { + // For integer values we want the clicking position to match the grab box so we round above + // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. + FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t; + TYPE v_new_off_floor = (TYPE)(v_new_off_f); + TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5); + if (!is_decimal && v_new_off_floor < v_new_off_round) + v_new = v_min + v_new_off_round; + else + v_new = v_min + v_new_off_floor; + } + } + + // Round to user desired precision based on format string + v_new = RoundScalarWithFormatT(format, data_type, v_new); + + // Apply result + if (*v != v_new) + { + *v = v_new; + value_changed = true; + } + } + } + + if (slider_sz < 1.0f) + { + *out_grab_bb = ImRect(bb.Min, bb.Min); + } + else + { + // Output grab position so it can be displayed by the caller + float grab_t = SliderCalcRatioFromValueT(data_type, *v, v_min, v_max, power, linear_zero_pos); + if (axis == ImGuiAxis_Y) + grab_t = 1.0f - grab_t; + const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); + if (axis == ImGuiAxis_X) + *out_grab_bb = ImRect(grab_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz * 0.5f, bb.Max.y - grab_padding); + else + *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5f); + } + + return value_changed; +} + +// For 32-bits and larger types, slider bounds are limited to half the natural type range. +// So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok. +// It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders. +bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb) +{ + switch (data_type) + { + case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)v_min, *(const ImS8*)v_max, format, power, flags, out_grab_bb); if (r) *(ImS8*)v = (ImS8)v32; return r; } + case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_U32, &v32, *(const ImU8*)v_min, *(const ImU8*)v_max, format, power, flags, out_grab_bb); if (r) *(ImU8*)v = (ImU8)v32; return r; } + case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)v_min, *(const ImS16*)v_max, format, power, flags, out_grab_bb); if (r) *(ImS16*)v = (ImS16)v32; return r; } + case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)v_min, *(const ImU16*)v_max, format, power, flags, out_grab_bb); if (r) *(ImU16*)v = (ImU16)v32; return r; } + case ImGuiDataType_S32: + IM_ASSERT(*(const ImS32*)v_min >= IM_S32_MIN/2 && *(const ImS32*)v_max <= IM_S32_MAX/2); + return SliderBehaviorT(bb, id, data_type, (ImS32*)v, *(const ImS32*)v_min, *(const ImS32*)v_max, format, power, flags, out_grab_bb); + case ImGuiDataType_U32: + IM_ASSERT(*(const ImU32*)v_min <= IM_U32_MAX/2); + return SliderBehaviorT(bb, id, data_type, (ImU32*)v, *(const ImU32*)v_min, *(const ImU32*)v_max, format, power, flags, out_grab_bb); + case ImGuiDataType_S64: + IM_ASSERT(*(const ImS64*)v_min >= IM_S64_MIN/2 && *(const ImS64*)v_max <= IM_S64_MAX/2); + return SliderBehaviorT(bb, id, data_type, (ImS64*)v, *(const ImS64*)v_min, *(const ImS64*)v_max, format, power, flags, out_grab_bb); + case ImGuiDataType_U64: + IM_ASSERT(*(const ImU64*)v_min <= IM_U64_MAX/2); + return SliderBehaviorT(bb, id, data_type, (ImU64*)v, *(const ImU64*)v_min, *(const ImU64*)v_max, format, power, flags, out_grab_bb); + case ImGuiDataType_Float: + IM_ASSERT(*(const float*)v_min >= -FLT_MAX/2.0f && *(const float*)v_max <= FLT_MAX/2.0f); + return SliderBehaviorT(bb, id, data_type, (float*)v, *(const float*)v_min, *(const float*)v_max, format, power, flags, out_grab_bb); + case ImGuiDataType_Double: + IM_ASSERT(*(const double*)v_min >= -DBL_MAX/2.0f && *(const double*)v_max <= DBL_MAX/2.0f); + return SliderBehaviorT(bb, id, data_type, (double*)v, *(const double*)v_min, *(const double*)v_max, format, power, flags, out_grab_bb); + case ImGuiDataType_COUNT: break; + } + IM_ASSERT(0); + return false; +} + +bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb)) + return false; + + // Default format string when passing NULL + if (format == NULL) + format = DataTypeGetInfo(data_type)->PrintFmt; + else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) + format = PatchFormatStringFloatToInt(format); + + // Tabbing or CTRL-clicking on Slider turns it into an input box + const bool hovered = ItemHoverable(frame_bb, id); + bool temp_input_is_active = TempInputTextIsActive(id); + bool temp_input_start = false; + if (!temp_input_is_active) + { + const bool focus_requested = FocusableItemRegister(window, id); + const bool clicked = (hovered && g.IO.MouseClicked[0]); + if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id) + { + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); + if (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id) + { + temp_input_start = true; + FocusableItemUnregister(window); + } + } + } + if (temp_input_is_active || temp_input_start) + return TempInputTextScalar(frame_bb, id, label, data_type, v, format); + + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavHighlight(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); + + // Slider behavior + ImRect grab_bb; + const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_None, &grab_bb); + if (value_changed) + MarkItemEdited(id); + + // Render grab + if (grab_bb.Max.x > grab_bb.Min.x) + window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); + RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + return value_changed; +} + +// Add multiple sliders on 1 line for compact edition of multiple components +bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components, CalcItemWidth()); + size_t type_size = GDataTypeInfo[data_type].Size; + for (int i = 0; i < components; i++) + { + PushID(i); + if (i > 0) + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, power); + PopID(); + PopItemWidth(); + v = (void*)((char*)v + type_size); + } + PopID(); + + const char* label_end = FindRenderedTextEnd(label); + if (label != label_end) + { + SameLine(0, g.Style.ItemInnerSpacing.x); + TextEx(label, label_end); + } + + EndGroup(); + return value_changed; +} + +bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power) +{ + return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); +} + +bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power) +{ + return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); +} + +bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power) +{ + return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); +} + +bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power) +{ + return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); +} + +bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format) +{ + if (format == NULL) + format = "%.0f deg"; + float v_deg = (*v_rad) * 360.0f / (2*IM_PI); + bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, 1.0f); + *v_rad = v_deg * (2*IM_PI) / 360.0f; + return value_changed; +} + +bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format) +{ + return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format); +} + +bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format) +{ + return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format); +} + +bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format) +{ + return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format); +} + +bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format) +{ + return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format); +} + +bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); + const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(frame_bb, id)) + return false; + + // Default format string when passing NULL + if (format == NULL) + format = DataTypeGetInfo(data_type)->PrintFmt; + else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) + format = PatchFormatStringFloatToInt(format); + + const bool hovered = ItemHoverable(frame_bb, id); + if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) + { + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); + } + + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavHighlight(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); + + // Slider behavior + ImRect grab_bb; + const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical, &grab_bb); + if (value_changed) + MarkItemEdited(id); + + // Render grab + if (grab_bb.Max.y > grab_bb.Min.y) + window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + // For the vertical slider we allow centered text to overlap the frame padding + char value_buf[64]; + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); + RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f)); + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + return value_changed; +} + +bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, float power) +{ + return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, power); +} + +bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format) +{ + return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format); +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc. +//------------------------------------------------------------------------- +// - ImParseFormatFindStart() [Internal] +// - ImParseFormatFindEnd() [Internal] +// - ImParseFormatTrimDecorations() [Internal] +// - ImParseFormatPrecision() [Internal] +// - TempInputTextScalar() [Internal] +// - InputScalar() +// - InputScalarN() +// - InputFloat() +// - InputFloat2() +// - InputFloat3() +// - InputFloat4() +// - InputInt() +// - InputInt2() +// - InputInt3() +// - InputInt4() +// - InputDouble() +//------------------------------------------------------------------------- + +// We don't use strchr() because our strings are usually very short and often start with '%' +const char* ImParseFormatFindStart(const char* fmt) +{ + while (char c = fmt[0]) + { + if (c == '%' && fmt[1] != '%') + return fmt; + else if (c == '%') + fmt++; + fmt++; + } + return fmt; +} + +const char* ImParseFormatFindEnd(const char* fmt) +{ + // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format. + if (fmt[0] != '%') + return fmt; + const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A')); + const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a')); + for (char c; (c = *fmt) != 0; fmt++) + { + if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0) + return fmt + 1; + if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0) + return fmt + 1; + } + return fmt; +} + +// Extract the format out of a format string with leading or trailing decorations +// fmt = "blah blah" -> return fmt +// fmt = "%.3f" -> return fmt +// fmt = "hello %.3f" -> return fmt + 6 +// fmt = "%.3f hello" -> return buf written with "%.3f" +const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_size) +{ + const char* fmt_start = ImParseFormatFindStart(fmt); + if (fmt_start[0] != '%') + return fmt; + const char* fmt_end = ImParseFormatFindEnd(fmt_start); + if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data. + return fmt_start; + ImStrncpy(buf, fmt_start, ImMin((size_t)(fmt_end - fmt_start) + 1, buf_size)); + return buf; +} + +// Parse display precision back from the display format string +// FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed. +int ImParseFormatPrecision(const char* fmt, int default_precision) +{ + fmt = ImParseFormatFindStart(fmt); + if (fmt[0] != '%') + return default_precision; + fmt++; + while (*fmt >= '0' && *fmt <= '9') + fmt++; + int precision = INT_MAX; + if (*fmt == '.') + { + fmt = ImAtoi(fmt + 1, &precision); + if (precision < 0 || precision > 99) + precision = default_precision; + } + if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation + precision = -1; + if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX) + precision = -1; + return (precision == INT_MAX) ? default_precision : precision; +} + +// Create text input in place of another active widget (e.g. used when doing a CTRL+Click on drag/slider widgets) +// FIXME: Facilitate using this in variety of other situations. +bool ImGui::TempInputTextScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format) +{ + ImGuiContext& g = *GImGui; + + // On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id. + // We clear ActiveID on the first frame to allow the InputText() taking it back. + const bool init = (g.TempInputTextId != id); + if (init) + ClearActiveID(); + + char fmt_buf[32]; + char data_buf[32]; + format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); + DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, data_ptr, format); + ImStrTrimBlanks(data_buf); + + g.CurrentWindow->DC.CursorPos = bb.Min; + ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; + flags |= ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal); + bool value_changed = InputTextEx(label, NULL, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags); + if (init) + { + // First frame we started displaying the InputText widget, we expect it to take the active id. + IM_ASSERT(g.ActiveId == id); + g.TempInputTextId = g.ActiveId; + } + if (value_changed) + { + value_changed = DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialTextA.Data, data_type, data_ptr, NULL); + if (value_changed) + MarkItemEdited(id); + } + return value_changed; +} + +bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_ptr, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + + if (format == NULL) + format = DataTypeGetInfo(data_type)->PrintFmt; + + char buf[64]; + DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, format); + + bool value_changed = false; + if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) + flags |= ImGuiInputTextFlags_CharsDecimal; + flags |= ImGuiInputTextFlags_AutoSelectAll; + flags |= ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselve by comparing the actual data rather than the string. + + if (step != NULL) + { + const float button_size = GetFrameHeight(); + + BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive() + PushID(label); + SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); + if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view + value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, data_ptr, format); + + // Step buttons + const ImVec2 backup_frame_padding = style.FramePadding; + style.FramePadding.x = style.FramePadding.y; + ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups; + if (flags & ImGuiInputTextFlags_ReadOnly) + button_flags |= ImGuiButtonFlags_Disabled; + SameLine(0, style.ItemInnerSpacing.x); + if (ButtonEx("-", ImVec2(button_size, button_size), button_flags)) + { + DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step); + value_changed = true; + } + SameLine(0, style.ItemInnerSpacing.x); + if (ButtonEx("+", ImVec2(button_size, button_size), button_flags)) + { + DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step); + value_changed = true; + } + + const char* label_end = FindRenderedTextEnd(label); + if (label != label_end) + { + SameLine(0, style.ItemInnerSpacing.x); + TextEx(label, label_end); + } + style.FramePadding = backup_frame_padding; + + PopID(); + EndGroup(); + } + else + { + if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) + value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, data_ptr, format); + } + if (value_changed) + MarkItemEdited(window->DC.LastItemId); + + return value_changed; +} + +bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components, CalcItemWidth()); + size_t type_size = GDataTypeInfo[data_type].Size; + for (int i = 0; i < components; i++) + { + PushID(i); + if (i > 0) + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= InputScalar("", data_type, v, step, step_fast, format, flags); + PopID(); + PopItemWidth(); + v = (void*)((char*)v + type_size); + } + PopID(); + + const char* label_end = FindRenderedTextEnd(label); + if (label != label_end) + { + SameLine(0.0f, g.Style.ItemInnerSpacing.x); + TextEx(label, label_end); + } + + EndGroup(); + return value_changed; +} + +bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags) +{ + flags |= ImGuiInputTextFlags_CharsScientific; + return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), format, flags); +} + +bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags) +{ + return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags); +} + +bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags flags) +{ + return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags); +} + +bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags flags) +{ + return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags); +} + +// Prefer using "const char* format" directly, which is more flexible and consistent with other API. +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags flags) +{ + char format[16] = "%f"; + if (decimal_precision >= 0) + ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); + return InputFloat(label, v, step, step_fast, format, flags); +} + +bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags flags) +{ + char format[16] = "%f"; + if (decimal_precision >= 0) + ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); + return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags); +} + +bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags flags) +{ + char format[16] = "%f"; + if (decimal_precision >= 0) + ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); + return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags); +} + +bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags flags) +{ + char format[16] = "%f"; + if (decimal_precision >= 0) + ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); + return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags); +} +#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS + +bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags flags) +{ + // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes. + const char* format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; + return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), format, flags); +} + +bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags) +{ + return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", flags); +} + +bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags) +{ + return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", flags); +} + +bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags) +{ + return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", flags); +} + +bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags) +{ + flags |= ImGuiInputTextFlags_CharsScientific; + return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), format, flags); +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: InputText, InputTextMultiline, InputTextWithHint +//------------------------------------------------------------------------- +// - InputText() +// - InputTextWithHint() +// - InputTextMultiline() +// - InputTextEx() [Internal] +//------------------------------------------------------------------------- + +bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() + return InputTextEx(label, NULL, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data); +} + +bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + return InputTextEx(label, NULL, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); +} + +bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() + return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); +} + +static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) +{ + int line_count = 0; + const char* s = text_begin; + while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding + if (c == '\n') + line_count++; + s--; + if (s[0] != '\n' && s[0] != '\r') + line_count++; + *out_text_end = s; + return line_count; +} + +static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) +{ + ImGuiContext& g = *GImGui; + ImFont* font = g.Font; + const float line_height = g.FontSize; + const float scale = line_height / font->FontSize; + + ImVec2 text_size = ImVec2(0,0); + float line_width = 0.0f; + + const ImWchar* s = text_begin; + while (s < text_end) + { + unsigned int c = (unsigned int)(*s++); + if (c == '\n') + { + text_size.x = ImMax(text_size.x, line_width); + text_size.y += line_height; + line_width = 0.0f; + if (stop_on_new_line) + break; + continue; + } + if (c == '\r') + continue; + + const float char_width = font->GetCharAdvance((ImWchar)c) * scale; + line_width += char_width; + } + + if (text_size.x < line_width) + text_size.x = line_width; + + if (out_offset) + *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n + + if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n + text_size.y += line_height; + + if (remaining) + *remaining = s; + + return text_size; +} + +// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) +namespace ImStb +{ + +static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } +static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; } +static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); } +static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; } +static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; +static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) +{ + const ImWchar* text = obj->TextW.Data; + const ImWchar* text_remaining = NULL; + const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); + r->x0 = 0.0f; + r->x1 = size.x; + r->baseline_y_delta = size.y; + r->ymin = 0.0f; + r->ymax = size.y; + r->num_chars = (int)(text_remaining - (text + line_start_idx)); +} + +static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } +static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->TextW[idx-1] ) && !is_separator( obj->TextW[idx] ) ) : 1; } +static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } +#ifdef __APPLE__ // FIXME: Move setting to IO structure +static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->TextW[idx-1] ) && is_separator( obj->TextW[idx] ) ) : 1; } +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } +#else +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } +#endif +#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL + +static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) +{ + ImWchar* dst = obj->TextW.Data + pos; + + // We maintain our buffer length in both UTF-8 and wchar formats + obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); + obj->CurLenW -= n; + + // Offset remaining text (FIXME-OPT: Use memmove) + const ImWchar* src = obj->TextW.Data + pos + n; + while (ImWchar c = *src++) + *dst++ = c; + *dst = '\0'; +} + +static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) +{ + const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0; + const int text_len = obj->CurLenW; + IM_ASSERT(pos <= text_len); + + const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); + if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA)) + return false; + + // Grow internal buffer if needed + if (new_text_len + text_len + 1 > obj->TextW.Size) + { + if (!is_resizable) + return false; + IM_ASSERT(text_len < obj->TextW.Size); + obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1); + } + + ImWchar* text = obj->TextW.Data; + if (pos != text_len) + memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); + memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); + + obj->CurLenW += new_text_len; + obj->CurLenA += new_text_len_utf8; + obj->TextW[obj->CurLenW] = '\0'; + + return true; +} + +// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) +#define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left +#define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right +#define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up +#define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down +#define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line +#define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line +#define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text +#define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text +#define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor +#define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor +#define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo +#define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo +#define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word +#define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word +#define STB_TEXTEDIT_K_SHIFT 0x20000 + +#define STB_TEXTEDIT_IMPLEMENTATION +#include "imstb_textedit.h" + +} + +void ImGuiInputTextState::OnKeyPressed(int key) +{ + stb_textedit_key(this, &Stb, key); + CursorFollow = true; + CursorAnimReset(); +} + +ImGuiInputTextCallbackData::ImGuiInputTextCallbackData() +{ + memset(this, 0, sizeof(*this)); +} + +// Public API to manipulate UTF-8 text +// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) +// FIXME: The existence of this rarely exercised code path is a bit of a nuisance. +void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count) +{ + IM_ASSERT(pos + bytes_count <= BufTextLen); + char* dst = Buf + pos; + const char* src = Buf + pos + bytes_count; + while (char c = *src++) + *dst++ = c; + *dst = '\0'; + + if (CursorPos + bytes_count >= pos) + CursorPos -= bytes_count; + else if (CursorPos >= pos) + CursorPos = pos; + SelectionStart = SelectionEnd = CursorPos; + BufDirty = true; + BufTextLen -= bytes_count; +} + +void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) +{ + const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; + const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); + if (new_text_len + BufTextLen >= BufSize) + { + if (!is_resizable) + return; + + // Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the midly similar code (until we remove the U16 buffer alltogether!) + ImGuiContext& g = *GImGui; + ImGuiInputTextState* edit_state = &g.InputTextState; + IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID); + IM_ASSERT(Buf == edit_state->TextA.Data); + int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; + edit_state->TextA.reserve(new_buf_size + 1); + Buf = edit_state->TextA.Data; + BufSize = edit_state->BufCapacityA = new_buf_size; + } + + if (BufTextLen != pos) + memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); + memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); + Buf[BufTextLen + new_text_len] = '\0'; + + if (CursorPos >= pos) + CursorPos += new_text_len; + SelectionStart = SelectionEnd = CursorPos; + BufDirty = true; + BufTextLen += new_text_len; +} + +// Return false to discard a character. +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + unsigned int c = *p_char; + + // Filter non-printable (NB: isprint is unreliable! see #2467) + if (c < 0x20) + { + bool pass = false; + pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); + pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); + if (!pass) + return false; + } + + // Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME) + if (c >= 0xE000 && c <= 0xF8FF) + return false; + + // Generic named filters + if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific)) + { + if (flags & ImGuiInputTextFlags_CharsDecimal) + if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) + return false; + + if (flags & ImGuiInputTextFlags_CharsScientific) + if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E')) + return false; + + if (flags & ImGuiInputTextFlags_CharsHexadecimal) + if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) + return false; + + if (flags & ImGuiInputTextFlags_CharsUppercase) + if (c >= 'a' && c <= 'z') + *p_char = (c += (unsigned int)('A'-'a')); + + if (flags & ImGuiInputTextFlags_CharsNoBlank) + if (ImCharIsBlankW(c)) + return false; + } + + // Custom callback filter + if (flags & ImGuiInputTextFlags_CallbackCharFilter) + { + ImGuiInputTextCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData)); + callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; + callback_data.EventChar = (ImWchar)c; + callback_data.Flags = flags; + callback_data.UserData = user_data; + if (callback(&callback_data) != 0) + return false; + *p_char = callback_data.EventChar; + if (!callback_data.EventChar) + return false; + } + + return true; +} + +// Edit a string of text +// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!". +// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match +// Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator. +// - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect. +// - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h +// (FIXME: Rather confusing and messy function, among the worse part of our codebase, expecting to rewrite a V2 at some point.. Partly because we are +// doing UTF8 > U16 > UTF8 conversions on the go to easily interface with stb_textedit. Ideally should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) +bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) + IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) + + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + const ImGuiStyle& style = g.Style; + + const bool RENDER_SELECTION_WHEN_INACTIVE = false; + const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; + const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0; + const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; + const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; + const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; + if (is_resizable) + IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! + + if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope, + BeginGroup(); + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f)); + + ImGuiWindow* draw_window = window; + if (is_multiline) + { + if (!ItemAdd(total_bb, id, &frame_bb)) + { + ItemSize(total_bb, style.FramePadding.y); + EndGroup(); + return false; + } + if (!BeginChildFrame(id, frame_bb.GetSize())) + { + EndChildFrame(); + EndGroup(); + return false; + } + draw_window = GetCurrentWindow(); + draw_window->DC.NavLayerActiveMaskNext |= draw_window->DC.NavLayerCurrentMask; // This is to ensure that EndChild() will display a navigation highlight + size.x -= draw_window->ScrollbarSizes.x; + } + else + { + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb)) + return false; + } + const bool hovered = ItemHoverable(frame_bb, id); + if (hovered) + g.MouseCursor = ImGuiMouseCursor_TextInput; + + // NB: we are only allowed to access 'edit_state' if we are the active widget. + ImGuiInputTextState* state = NULL; + if (g.InputTextState.ID == id) + state = &g.InputTextState; + + const bool focus_requested = FocusableItemRegister(window, id); + const bool focus_requested_by_code = focus_requested && (g.FocusRequestCurrWindow == window && g.FocusRequestCurrCounterAll == window->DC.FocusCounterAll); + const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; + + const bool user_clicked = hovered && io.MouseClicked[0]; + const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard)); + const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetScrollbarID(draw_window, ImGuiAxis_Y); + const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetScrollbarID(draw_window, ImGuiAxis_Y); + + bool clear_active_id = false; + bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); + + const bool init_make_active = (focus_requested || user_clicked || user_scroll_finish || user_nav_input_start); + const bool init_state = (init_make_active || user_scroll_active); + if (init_state && g.ActiveId != id) + { + // Access state even if we don't own it yet. + state = &g.InputTextState; + state->CursorAnimReset(); + + // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) + // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) + const int buf_len = (int)strlen(buf); + state->InitialTextA.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. + memcpy(state->InitialTextA.Data, buf, buf_len + 1); + + // Start edition + const char* buf_end = NULL; + state->TextW.resize(buf_size + 1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data is always pointing to at least an empty string. + state->TextA.resize(0); + state->TextAIsValid = false; // TextA is not valid yet (we will display buf until then) + state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, buf_size, buf, NULL, &buf_end); + state->CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. + + // Preserve cursor position and undo/redo stack if we come back to same widget + // FIXME: For non-readonly widgets we might be able to require that TextAIsValid && TextA == buf ? (untested) and discard undo stack if user buffer has changed. + const bool recycle_state = (state->ID == id); + if (recycle_state) + { + // Recycle existing cursor/selection/undo stack but clamp position + // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. + state->CursorClamp(); + } + else + { + state->ID = id; + state->ScrollX = 0.0f; + stb_textedit_initialize_state(&state->Stb, !is_multiline); + if (!is_multiline && focus_requested_by_code) + select_all = true; + } + if (flags & ImGuiInputTextFlags_AlwaysInsertMode) + state->Stb.insert_mode = 1; + if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) + select_all = true; + } + + if (g.ActiveId != id && init_make_active) + { + IM_ASSERT(state && state->ID == id); + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + IM_ASSERT(ImGuiNavInput_COUNT < 32); + g.ActiveIdBlockNavInputFlags = (1 << ImGuiNavInput_Cancel); + if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character. + g.ActiveIdBlockNavInputFlags |= (1 << ImGuiNavInput_KeyTab_); + if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory)) + g.ActiveIdAllowNavDirFlags = ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); + } + + // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function) + if (g.ActiveId == id && state == NULL) + ClearActiveID(); + + // Release focus when we click outside + if (g.ActiveId == id && io.MouseClicked[0] && !init_state && !init_make_active) //-V560 + clear_active_id = true; + + // Lock the decision of whether we are going to take the path displaying the cursor or selection + const bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); + bool render_selection = state && state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); + bool value_changed = false; + bool enter_pressed = false; + + // When read-only we always use the live data passed to the function + // FIXME-OPT: Because our selection/cursor code currently needs the wide text we need to convert it when active, which is not ideal :( + if (is_readonly && state != NULL && (render_cursor || render_selection)) + { + const char* buf_end = NULL; + state->TextW.resize(buf_size + 1); + state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, buf, NULL, &buf_end); + state->CurLenA = (int)(buf_end - buf); + state->CursorClamp(); + render_selection &= state->HasSelection(); + } + + // Select the buffer to render. + const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state && state->TextAIsValid; + const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0); + + // Password pushes a temporary font with only a fallback glyph + if (is_password && !is_displaying_hint) + { + const ImFontGlyph* glyph = g.Font->FindGlyph('*'); + ImFont* password_font = &g.InputTextPasswordFont; + password_font->FontSize = g.Font->FontSize; + password_font->Scale = g.Font->Scale; + password_font->DisplayOffset = g.Font->DisplayOffset; + password_font->Ascent = g.Font->Ascent; + password_font->Descent = g.Font->Descent; + password_font->ContainerAtlas = g.Font->ContainerAtlas; + password_font->FallbackGlyph = glyph; + password_font->FallbackAdvanceX = glyph->AdvanceX; + IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); + PushFont(password_font); + } + + // Process mouse inputs and character inputs + int backup_current_text_length = 0; + if (g.ActiveId == id) + { + IM_ASSERT(state != NULL); + backup_current_text_length = state->CurLenA; + state->BufCapacityA = buf_size; + state->UserFlags = flags; + state->UserCallback = callback; + state->UserCallbackData = callback_user_data; + + // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. + // Down the line we should have a cleaner library-wide concept of Selected vs Active. + g.ActiveIdAllowOverlap = !io.MouseDown[0]; + g.WantTextInputNextFrame = 1; + + // Edit in progress + const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->ScrollX; + const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f)); + + const bool is_osx = io.ConfigMacOSXBehaviors; + if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0])) + { + state->SelectAll(); + state->SelectedAllMouseLock = true; + } + else if (hovered && is_osx && io.MouseDoubleClicked[0]) + { + // Double-click select a word only, OS X style (by simulating keystrokes) + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + } + else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) + { + if (hovered) + { + stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); + state->CursorAnimReset(); + } + } + else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) + { + stb_textedit_drag(state, &state->Stb, mouse_x, mouse_y); + state->CursorAnimReset(); + state->CursorFollow = true; + } + if (state->SelectedAllMouseLock && !io.MouseDown[0]) + state->SelectedAllMouseLock = false; + + // It is ill-defined whether the back-end needs to send a \t character when pressing the TAB keys. + // Win32 and GLFW naturally do it but not SDL. + const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); + if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly) + if (!io.InputQueueCharacters.contains('\t')) + { + unsigned int c = '\t'; // Insert TAB + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + state->OnKeyPressed((int)c); + } + + // Process regular text input (before we check for Return because using some IME will effectively send a Return?) + // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. + if (io.InputQueueCharacters.Size > 0) + { + if (!ignore_char_inputs && !is_readonly && !user_nav_input_start) + for (int n = 0; n < io.InputQueueCharacters.Size; n++) + { + // Insert character if they pass filtering + unsigned int c = (unsigned int)io.InputQueueCharacters[n]; + if (c == '\t' && io.KeyShift) + continue; + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + state->OnKeyPressed((int)c); + } + + // Consume characters + io.InputQueueCharacters.resize(0); + } + } + + // Process other shortcuts/key-presses + bool cancel_edit = false; + if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) + { + IM_ASSERT(state != NULL); + const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); + const bool is_osx = io.ConfigMacOSXBehaviors; + const bool is_shortcut_key = (is_osx ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl + const bool is_osx_shift_shortcut = is_osx && io.KeySuper && io.KeyShift && !io.KeyCtrl && !io.KeyAlt; + const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl + const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End + const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper; + const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper; + + const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); + const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection()); + const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_readonly; + const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && !is_readonly && is_undoable); + const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable; + + if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Backspace) && !is_readonly) + { + if (!state->HasSelection()) + { + if (is_wordmove_key_down) + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT); + else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) + state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT); + } + state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); + } + else if (IsKeyPressedMap(ImGuiKey_Enter)) + { + bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; + if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) + { + enter_pressed = clear_active_id = true; + } + else if (!is_readonly) + { + unsigned int c = '\n'; // Insert new line + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + state->OnKeyPressed((int)c); + } + } + else if (IsKeyPressedMap(ImGuiKey_Escape)) + { + clear_active_id = cancel_edit = true; + } + else if (is_undo || is_redo) + { + state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); + state->ClearSelection(); + } + else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A)) + { + state->SelectAll(); + state->CursorFollow = true; + } + else if (is_cut || is_copy) + { + // Cut, Copy + if (io.SetClipboardTextFn) + { + const int ib = state->HasSelection() ? ImMin(state->Stb.select_start, state->Stb.select_end) : 0; + const int ie = state->HasSelection() ? ImMax(state->Stb.select_start, state->Stb.select_end) : state->CurLenW; + const int clipboard_data_len = ImTextCountUtf8BytesFromStr(state->TextW.Data + ib, state->TextW.Data + ie) + 1; + char* clipboard_data = (char*)IM_ALLOC(clipboard_data_len * sizeof(char)); + ImTextStrToUtf8(clipboard_data, clipboard_data_len, state->TextW.Data + ib, state->TextW.Data + ie); + SetClipboardText(clipboard_data); + MemFree(clipboard_data); + } + if (is_cut) + { + if (!state->HasSelection()) + state->SelectAll(); + state->CursorFollow = true; + stb_textedit_cut(state, &state->Stb); + } + } + else if (is_paste) + { + if (const char* clipboard = GetClipboardText()) + { + // Filter pasted buffer + const int clipboard_len = (int)strlen(clipboard); + ImWchar* clipboard_filtered = (ImWchar*)IM_ALLOC((clipboard_len+1) * sizeof(ImWchar)); + int clipboard_filtered_len = 0; + for (const char* s = clipboard; *s; ) + { + unsigned int c; + s += ImTextCharFromUtf8(&c, s, NULL); + if (c == 0) + break; + if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + continue; + clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; + } + clipboard_filtered[clipboard_filtered_len] = 0; + if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation + { + stb_textedit_paste(state, &state->Stb, clipboard_filtered, clipboard_filtered_len); + state->CursorFollow = true; + } + MemFree(clipboard_filtered); + } + } + + // Update render selection flag after events have been handled, so selection highlight can be displayed during the same frame. + render_selection |= state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); + } + + // Process callbacks and apply result back to user's buffer. + if (g.ActiveId == id) + { + IM_ASSERT(state != NULL); + const char* apply_new_text = NULL; + int apply_new_text_length = 0; + if (cancel_edit) + { + // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. + if (!is_readonly && strcmp(buf, state->InitialTextA.Data) != 0) + { + apply_new_text = state->InitialTextA.Data; + apply_new_text_length = state->InitialTextA.Size - 1; + } + } + + // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. + // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. Also this allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage. + bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); + if (apply_edit_back_to_user_buffer) + { + // Apply new value immediately - copy modified buffer back + // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer + // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. + // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. + if (!is_readonly) + { + state->TextAIsValid = true; + state->TextA.resize(state->TextW.Size * 4 + 1); + ImTextStrToUtf8(state->TextA.Data, state->TextA.Size, state->TextW.Data, NULL); + } + + // User callback + if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) + { + IM_ASSERT(callback != NULL); + + // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. + ImGuiInputTextFlags event_flag = 0; + ImGuiKey event_key = ImGuiKey_COUNT; + if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) + { + event_flag = ImGuiInputTextFlags_CallbackCompletion; + event_key = ImGuiKey_Tab; + } + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; + event_key = ImGuiKey_UpArrow; + } + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; + event_key = ImGuiKey_DownArrow; + } + else if (flags & ImGuiInputTextFlags_CallbackAlways) + event_flag = ImGuiInputTextFlags_CallbackAlways; + + if (event_flag) + { + ImGuiInputTextCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData)); + callback_data.EventFlag = event_flag; + callback_data.Flags = flags; + callback_data.UserData = callback_user_data; + + callback_data.EventKey = event_key; + callback_data.Buf = state->TextA.Data; + callback_data.BufTextLen = state->CurLenA; + callback_data.BufSize = state->BufCapacityA; + callback_data.BufDirty = false; + + // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) + ImWchar* text = state->TextW.Data; + const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + state->Stb.cursor); + const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + state->Stb.select_start); + const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + state->Stb.select_end); + + // Call user code + callback(&callback_data); + + // Read back what user may have modified + IM_ASSERT(callback_data.Buf == state->TextA.Data); // Invalid to modify those fields + IM_ASSERT(callback_data.BufSize == state->BufCapacityA); + IM_ASSERT(callback_data.Flags == flags); + if (callback_data.CursorPos != utf8_cursor_pos) { state->Stb.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); state->CursorFollow = true; } + if (callback_data.SelectionStart != utf8_selection_start) { state->Stb.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } + if (callback_data.SelectionEnd != utf8_selection_end) { state->Stb.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } + if (callback_data.BufDirty) + { + IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! + if (callback_data.BufTextLen > backup_current_text_length && is_resizable) + state->TextW.resize(state->TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); + state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, callback_data.Buf, NULL); + state->CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() + state->CursorAnimReset(); + } + } + } + + // Will copy result string if modified + if (!is_readonly && strcmp(state->TextA.Data, buf) != 0) + { + apply_new_text = state->TextA.Data; + apply_new_text_length = state->CurLenA; + } + } + + // Copy result to user buffer + if (apply_new_text) + { + IM_ASSERT(apply_new_text_length >= 0); + if (backup_current_text_length != apply_new_text_length && is_resizable) + { + ImGuiInputTextCallbackData callback_data; + callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; + callback_data.Flags = flags; + callback_data.Buf = buf; + callback_data.BufTextLen = apply_new_text_length; + callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); + callback_data.UserData = callback_user_data; + callback(&callback_data); + buf = callback_data.Buf; + buf_size = callback_data.BufSize; + apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); + IM_ASSERT(apply_new_text_length <= buf_size); + } + + // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. + ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); + value_changed = true; + } + + // Clear temporary user storage + state->UserFlags = 0; + state->UserCallback = NULL; + state->UserCallbackData = NULL; + } + + // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) + if (clear_active_id && g.ActiveId == id) + ClearActiveID(); + + // Render frame + if (!is_multiline) + { + RenderNavHighlight(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + } + + const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size + ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; + ImVec2 text_size(0.0f, 0.0f); + + // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line + // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether. + // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash. + const int buf_display_max_length = 2 * 1024 * 1024; + const char* buf_display = buf_display_from_state ? state->TextA.Data : buf; //-V595 + const char* buf_display_end = NULL; // We have specialized paths below for setting the length + if (is_displaying_hint) + { + buf_display = hint; + buf_display_end = hint + strlen(hint); + } + + // Render text. We currently only render selection when the widget is active or while scrolling. + // FIXME: We could remove the '&& render_cursor' to keep rendering selection when inactive. + if (render_cursor || render_selection) + { + IM_ASSERT(state != NULL); + if (!is_displaying_hint) + buf_display_end = buf_display + state->CurLenA; + + // Render text (with cursor and selection) + // This is going to be messy. We need to: + // - Display the text (this alone can be more easily clipped) + // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) + // - Measure text height (for scrollbar) + // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) + // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. + const ImWchar* text_begin = state->TextW.Data; + ImVec2 cursor_offset, select_start_offset; + + { + // Find lines numbers straddling 'cursor' (slot 0) and 'select_start' (slot 1) positions. + const ImWchar* searches_input_ptr[2] = { NULL, NULL }; + int searches_result_line_no[2] = { -1000, -1000 }; + int searches_remaining = 0; + if (render_cursor) + { + searches_input_ptr[0] = text_begin + state->Stb.cursor; + searches_result_line_no[0] = -1; + searches_remaining++; + } + if (render_selection) + { + searches_input_ptr[1] = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); + searches_result_line_no[1] = -1; + searches_remaining++; + } + + // Iterate all lines to find our line numbers + // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter. + searches_remaining += is_multiline ? 1 : 0; + int line_count = 0; + //for (const ImWchar* s = text_begin; (s = (const ImWchar*)wcschr((const wchar_t*)s, (wchar_t)'\n')) != NULL; s++) // FIXME-OPT: Could use this when wchar_t are 16-bits + for (const ImWchar* s = text_begin; *s != 0; s++) + if (*s == '\n') + { + line_count++; + if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; if (--searches_remaining <= 0) break; } + if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; if (--searches_remaining <= 0) break; } + } + line_count++; + if (searches_result_line_no[0] == -1) + searches_result_line_no[0] = line_count; + if (searches_result_line_no[1] == -1) + searches_result_line_no[1] = line_count; + + // Calculate 2d position by finding the beginning of the line and measuring distance + cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; + cursor_offset.y = searches_result_line_no[0] * g.FontSize; + if (searches_result_line_no[1] >= 0) + { + select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; + select_start_offset.y = searches_result_line_no[1] * g.FontSize; + } + + // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) + if (is_multiline) + text_size = ImVec2(size.x, line_count * g.FontSize); + } + + // Scroll + if (render_cursor && state->CursorFollow) + { + // Horizontal scroll in chunks of quarter width + if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) + { + const float scroll_increment_x = size.x * 0.25f; + if (cursor_offset.x < state->ScrollX) + state->ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x); + else if (cursor_offset.x - size.x >= state->ScrollX) + state->ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x); + } + else + { + state->ScrollX = 0.0f; + } + + // Vertical scroll + if (is_multiline) + { + float scroll_y = draw_window->Scroll.y; + if (cursor_offset.y - g.FontSize < scroll_y) + scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); + else if (cursor_offset.y - size.y >= scroll_y) + scroll_y = cursor_offset.y - size.y; + draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag + draw_window->Scroll.y = scroll_y; + draw_pos.y = draw_window->DC.CursorPos.y; + } + + state->CursorFollow = false; + } + + // Draw selection + const ImVec2 draw_scroll = ImVec2(state->ScrollX, 0.0f); + if (render_selection) + { + const ImWchar* text_selected_begin = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); + const ImWchar* text_selected_end = text_begin + ImMax(state->Stb.select_start, state->Stb.select_end); + + ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests. + float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. + float bg_offy_dn = is_multiline ? 0.0f : 2.0f; + ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll; + for (const ImWchar* p = text_selected_begin; p < text_selected_end; ) + { + if (rect_pos.y > clip_rect.w + g.FontSize) + break; + if (rect_pos.y < clip_rect.y) + { + //p = (const ImWchar*)wmemchr((const wchar_t*)p, '\n', text_selected_end - p); // FIXME-OPT: Could use this when wchar_t are 16-bits + //p = p ? p + 1 : text_selected_end; + while (p < text_selected_end) + if (*p++ == '\n') + break; + } + else + { + ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); + if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines + ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn)); + rect.ClipWith(clip_rect); + if (rect.Overlaps(clip_rect)) + draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); + } + rect_pos.x = draw_pos.x - draw_scroll.x; + rect_pos.y += g.FontSize; + } + } + + // We test for 'buf_display_max_length' as a way to avoid some pathological cases (e.g. single-line 1 MB string) which would make ImDrawList crash. + if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) + { + ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); + draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); + } + + // Draw blinking cursor + if (render_cursor) + { + state->CursorAnim += io.DeltaTime; + bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; + ImVec2 cursor_screen_pos = draw_pos + cursor_offset - draw_scroll; + ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); + if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) + draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); + + // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) + if (!is_readonly) + g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); + } + } + else + { + // Render text only (no selection, no cursor) + if (is_multiline) + text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_display_end) * g.FontSize); // We don't need width + else if (!is_displaying_hint && g.ActiveId == id) + buf_display_end = buf_display + state->CurLenA; + else if (!is_displaying_hint) + buf_display_end = buf_display + strlen(buf_display); + + if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) + { + ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); + draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); + } + } + + if (is_multiline) + { + Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line + EndChildFrame(); + EndGroup(); + } + + if (is_password && !is_displaying_hint) + PopFont(); + + // Log as text + if (g.LogEnabled && !(is_password && !is_displaying_hint)) + LogRenderedText(&draw_pos, buf_display, buf_display_end); + + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + if (value_changed && !(flags & ImGuiInputTextFlags_NoMarkEdited)) + MarkItemEdited(id); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) + return enter_pressed; + else + return value_changed; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. +//------------------------------------------------------------------------- +// - ColorEdit3() +// - ColorEdit4() +// - ColorPicker3() +// - RenderColorRectWithAlphaCheckerboard() [Internal] +// - ColorPicker4() +// - ColorButton() +// - SetColorEditOptions() +// - ColorTooltip() [Internal] +// - ColorEditOptionsPopup() [Internal] +// - ColorPickerOptionsPopup() [Internal] +//------------------------------------------------------------------------- + +bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags) +{ + return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); +} + +// Edit colors components (each component in 0.0f..1.0f range). +// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +// With typical options: Left-click on colored square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. +bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float square_sz = GetFrameHeight(); + const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); + const float w_items_all = CalcItemWidth() - w_extra; + const char* label_display_end = FindRenderedTextEnd(label); + g.NextItemData.ClearFlags(); + + BeginGroup(); + PushID(label); + + // If we're not showing any slider there's no point in doing any HSV conversions + const ImGuiColorEditFlags flags_untouched = flags; + if (flags & ImGuiColorEditFlags_NoInputs) + flags = (flags & (~ImGuiColorEditFlags__DisplayMask)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions; + + // Context menu: display and modify options (before defaults are applied) + if (!(flags & ImGuiColorEditFlags_NoOptions)) + ColorEditOptionsPopup(col, flags); + + // Read stored options + if (!(flags & ImGuiColorEditFlags__DisplayMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DisplayMask); + if (!(flags & ImGuiColorEditFlags__DataTypeMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask); + if (!(flags & ImGuiColorEditFlags__PickerMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask); + if (!(flags & ImGuiColorEditFlags__InputMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputMask); + flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__DisplayMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask)); + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DisplayMask)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check that only 1 is selected + + const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; + const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; + const int components = alpha ? 4 : 3; + + // Convert to the formats we need + float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; + if ((flags & ImGuiColorEditFlags_InputHSV) && (flags & ImGuiColorEditFlags_DisplayRGB)) + ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); + else if ((flags & ImGuiColorEditFlags_InputRGB) && (flags & ImGuiColorEditFlags_DisplayHSV)) + ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); + int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; + + bool value_changed = false; + bool value_changed_as_float = false; + + if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) + { + // RGB/HSV 0..255 Sliders + const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); + const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); + + const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); + static const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; + static const char* fmt_table_int[3][4] = + { + { "%3d", "%3d", "%3d", "%3d" }, // Short display + { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA + { "H:%3d", "S:%3d", "V:%3d", "A:%3d" } // Long display for HSVA + }; + static const char* fmt_table_float[3][4] = + { + { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display + { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA + { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA + }; + const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1; + + for (int n = 0; n < components; n++) + { + if (n > 0) + SameLine(0, style.ItemInnerSpacing.x); + SetNextItemWidth((n + 1 < components) ? w_item_one : w_item_last); + if (flags & ImGuiColorEditFlags_Float) + { + value_changed |= DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); + value_changed_as_float |= value_changed; + } + else + { + value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context"); + } + } + else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) + { + // RGB Hexadecimal Input + char buf[64]; + if (alpha) + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255), ImClamp(i[3],0,255)); + else + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255)); + SetNextItemWidth(w_items_all); + if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) + { + value_changed = true; + char* p = buf; + while (*p == '#' || ImCharIsBlankA(*p)) + p++; + i[0] = i[1] = i[2] = i[3] = 0; + if (alpha) + sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) + else + sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context"); + } + + ImGuiWindow* picker_active_window = NULL; + if (!(flags & ImGuiColorEditFlags_NoSmallPreview)) + { + if (!(flags & ImGuiColorEditFlags_NoInputs)) + SameLine(0, style.ItemInnerSpacing.x); + + const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f); + if (ColorButton("##ColorButton", col_v4, flags)) + { + if (!(flags & ImGuiColorEditFlags_NoPicker)) + { + // Store current color and open a picker + g.ColorPickerRef = col_v4; + OpenPopup("picker"); + SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y)); + } + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context"); + + if (BeginPopup("picker")) + { + picker_active_window = g.CurrentWindow; + if (label != label_display_end) + { + TextEx(label, label_display_end); + Spacing(); + } + ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; + ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__DisplayMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; + SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? + value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); + EndPopup(); + } + } + + if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) + { + SameLine(0, style.ItemInnerSpacing.x); + TextEx(label, label_display_end); + } + + // Convert back + if (value_changed && picker_active_window == NULL) + { + if (!value_changed_as_float) + for (int n = 0; n < 4; n++) + f[n] = i[n] / 255.0f; + if ((flags & ImGuiColorEditFlags_DisplayHSV) && (flags & ImGuiColorEditFlags_InputRGB)) + ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); + if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV)) + ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); + + col[0] = f[0]; + col[1] = f[1]; + col[2] = f[2]; + if (alpha) + col[3] = f[3]; + } + + PopID(); + EndGroup(); + + // Drag and Drop Target + // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. + if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) + { + bool accepted_drag_drop = false; + if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) + { + memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any //-V512 + value_changed = accepted_drag_drop = true; + } + if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) + { + memcpy((float*)col, payload->Data, sizeof(float) * components); + value_changed = accepted_drag_drop = true; + } + + // Drag-drop payloads are always RGB + if (accepted_drag_drop && (flags & ImGuiColorEditFlags_InputHSV)) + ColorConvertRGBtoHSV(col[0], col[1], col[2], col[0], col[1], col[2]); + EndDragDropTarget(); + } + + // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). + if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) + window->DC.LastItemId = g.ActiveId; + + if (value_changed) + MarkItemEdited(window->DC.LastItemId); + + return value_changed; +} + +bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags) +{ + float col4[4] = { col[0], col[1], col[2], 1.0f }; + if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha)) + return false; + col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2]; + return true; +} + +static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b) +{ + float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; + int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); + int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); + int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); + return IM_COL32(r, g, b, 0xFF); +} + +// Helper for ColorPicker4() +// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. +// I spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding alltogether. +void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) + { + ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col)); + ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col)); + window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags); + + int yi = 0; + for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++) + { + float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y); + if (y2 <= y1) + continue; + for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) + { + float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); + if (x2 <= x1) + continue; + int rounding_corners_flags_cell = 0; + if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; } + if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; } + rounding_corners_flags_cell &= rounding_corners_flags; + window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell); + } + } + } + else + { + window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags); + } +} + +// Helper for ColorPicker4() +static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w) +{ + ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32_BLACK); + ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32_WHITE); + ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32_BLACK); + ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32_WHITE); +} + +// Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +// (In C++ the 'float col[4]' notation for a function argument is equivalent to 'float* col', we only specify a size to facilitate understanding of the code.) +// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) +bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImDrawList* draw_list = window->DrawList; + ImGuiStyle& style = g.Style; + ImGuiIO& io = g.IO; + + const float width = CalcItemWidth(); + g.NextItemData.ClearFlags(); + + PushID(label); + BeginGroup(); + + if (!(flags & ImGuiColorEditFlags_NoSidePreview)) + flags |= ImGuiColorEditFlags_NoSmallPreview; + + // Context menu: display and store options. + if (!(flags & ImGuiColorEditFlags_NoOptions)) + ColorPickerOptionsPopup(col, flags); + + // Read stored options + if (!(flags & ImGuiColorEditFlags__PickerMask)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; + if (!(flags & ImGuiColorEditFlags__InputMask)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__InputMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__InputMask; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__PickerMask)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check that only 1 is selected + if (!(flags & ImGuiColorEditFlags_NoOptions)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); + + // Setup + int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4; + bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha); + ImVec2 picker_pos = window->DC.CursorPos; + float square_sz = GetFrameHeight(); + float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars + float sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box + float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x; + float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x; + float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f); + + float backup_initial_col[4]; + memcpy(backup_initial_col, col, components * sizeof(float)); + + float wheel_thickness = sv_picker_size * 0.08f; + float wheel_r_outer = sv_picker_size * 0.50f; + float wheel_r_inner = wheel_r_outer - wheel_thickness; + ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f); + + // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic. + float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f); + ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point. + ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. + ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. + + float H = col[0], S = col[1], V = col[2]; + float R = col[0], G = col[1], B = col[2]; + if (flags & ImGuiColorEditFlags_InputRGB) + ColorConvertRGBtoHSV(R, G, B, H, S, V); + else if (flags & ImGuiColorEditFlags_InputHSV) + ColorConvertHSVtoRGB(H, S, V, R, G, B); + + bool value_changed = false, value_changed_h = false, value_changed_sv = false; + + PushItemFlag(ImGuiItemFlags_NoNav, true); + if (flags & ImGuiColorEditFlags_PickerHueWheel) + { + // Hue wheel + SV triangle logic + InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size)); + if (IsItemActive()) + { + ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center; + ImVec2 current_off = g.IO.MousePos - wheel_center; + float initial_dist2 = ImLengthSqr(initial_off); + if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1)) + { + // Interactive with Hue wheel + H = ImAtan2(current_off.y, current_off.x) / IM_PI*0.5f; + if (H < 0.0f) + H += 1.0f; + value_changed = value_changed_h = true; + } + float cos_hue_angle = ImCos(-H * 2.0f * IM_PI); + float sin_hue_angle = ImSin(-H * 2.0f * IM_PI); + if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle))) + { + // Interacting with SV triangle + ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle); + if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)) + current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated); + float uu, vv, ww; + ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww); + V = ImClamp(1.0f - vv, 0.0001f, 1.0f); + S = ImClamp(uu / V, 0.0001f, 1.0f); + value_changed = value_changed_sv = true; + } + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context"); + } + else if (flags & ImGuiColorEditFlags_PickerHueBar) + { + // SV rectangle logic + InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size)); + if (IsItemActive()) + { + S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1)); + V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); + value_changed = value_changed_sv = true; + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context"); + + // Hue bar logic + SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); + InvisibleButton("hue", ImVec2(bars_width, sv_picker_size)); + if (IsItemActive()) + { + H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); + value_changed = value_changed_h = true; + } + } + + // Alpha bar logic + if (alpha_bar) + { + SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); + InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); + if (IsItemActive()) + { + col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); + value_changed = true; + } + } + PopItemFlag(); // ImGuiItemFlags_NoNav + + if (!(flags & ImGuiColorEditFlags_NoSidePreview)) + { + SameLine(0, style.ItemInnerSpacing.x); + BeginGroup(); + } + + if (!(flags & ImGuiColorEditFlags_NoLabel)) + { + const char* label_display_end = FindRenderedTextEnd(label); + if (label != label_display_end) + { + if ((flags & ImGuiColorEditFlags_NoSidePreview)) + SameLine(0, style.ItemInnerSpacing.x); + TextEx(label, label_display_end); + } + } + + if (!(flags & ImGuiColorEditFlags_NoSidePreview)) + { + PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); + ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + if ((flags & ImGuiColorEditFlags_NoLabel)) + Text("Current"); + + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; + ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)); + if (ref_col != NULL) + { + Text("Original"); + ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]); + if (ColorButton("##original", ref_col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2))) + { + memcpy(col, ref_col, components * sizeof(float)); + value_changed = true; + } + } + PopItemFlag(); + EndGroup(); + } + + // Convert back color to RGB + if (value_changed_h || value_changed_sv) + { + if (flags & ImGuiColorEditFlags_InputRGB) + { + ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + col[0] = H; + col[1] = S; + col[2] = V; + } + } + + // R,G,B and H,S,V slider color editor + bool value_changed_fix_hue_wrap = false; + if ((flags & ImGuiColorEditFlags_NoInputs) == 0) + { + PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; + if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) + { + // FIXME: Hackily differenciating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget. + // For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050) + value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap); + value_changed = true; + } + if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_DisplayHSV); + if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_DisplayHex); + PopItemWidth(); + } + + // Try to cancel hue wrap (after ColorEdit4 call), if any + if (value_changed_fix_hue_wrap && (flags & ImGuiColorEditFlags_InputRGB)) + { + float new_H, new_S, new_V; + ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); + if (new_H <= 0 && H > 0) + { + if (new_V <= 0 && V != new_V) + ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]); + else if (new_S <= 0) + ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]); + } + } + + if (value_changed) + { + if (flags & ImGuiColorEditFlags_InputRGB) + { + R = col[0]; + G = col[1]; + B = col[2]; + ColorConvertRGBtoHSV(R, G, B, H, S, V); + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + H = col[0]; + S = col[1]; + V = col[2]; + ColorConvertHSVtoRGB(H, S, V, R, G, B); + } + } + + ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); + ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); + ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(R, G, B, 1.0f)); + + const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) }; + ImVec2 sv_cursor_pos; + + if (flags & ImGuiColorEditFlags_PickerHueWheel) + { + // Render Hue Wheel + const float aeps = 1.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out). + const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12); + for (int n = 0; n < 6; n++) + { + const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps; + const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps; + const int vert_start_idx = draw_list->VtxBuffer.Size; + draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); + draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness); + const int vert_end_idx = draw_list->VtxBuffer.Size; + + // Paint colors over existing vertices + ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner); + ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner); + ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]); + } + + // Render Cursor + preview on Hue Wheel + float cos_hue_angle = ImCos(H * 2.0f * IM_PI); + float sin_hue_angle = ImSin(H * 2.0f * IM_PI); + ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f); + float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; + int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32); + draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); + draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, IM_COL32(128,128,128,255), hue_cursor_segments); + draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments); + + // Render SV triangle (rotated according to hue) + ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle); + ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); + ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); + ImVec2 uv_white = GetFontTexUvWhitePixel(); + draw_list->PrimReserve(6, 6); + draw_list->PrimVtx(tra, uv_white, hue_color32); + draw_list->PrimVtx(trb, uv_white, hue_color32); + draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE); + draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS); + draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK); + draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS); + draw_list->AddTriangle(tra, trb, trc, IM_COL32(128,128,128,255), 1.5f); + sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); + } + else if (flags & ImGuiColorEditFlags_PickerHueBar) + { + // Render SV Square + draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE); + draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32_BLACK, IM_COL32_BLACK); + RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), 0.0f); + sv_cursor_pos.x = ImClamp((float)(int)(picker_pos.x + ImSaturate(S) * sv_picker_size + 0.5f), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much + sv_cursor_pos.y = ImClamp((float)(int)(picker_pos.y + ImSaturate(1 - V) * sv_picker_size + 0.5f), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2); + + // Render Hue Bar + for (int i = 0; i < 6; ++i) + draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), hue_colors[i], hue_colors[i], hue_colors[i + 1], hue_colors[i + 1]); + float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f); + RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f); + RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); + } + + // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) + float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f; + draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12); + draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, IM_COL32(128,128,128,255), 12); + draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12); + + // Render alpha bar + if (alpha_bar) + { + float alpha = ImSaturate(col[3]); + ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size); + RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0,0,0,0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f)); + draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, col32_no_alpha, col32_no_alpha, col32_no_alpha & ~IM_COL32_A_MASK, col32_no_alpha & ~IM_COL32_A_MASK); + float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f); + RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f); + RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); + } + + EndGroup(); + + if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) + value_changed = false; + if (value_changed) + MarkItemEdited(window->DC.LastItemId); + + PopID(); + + return value_changed; +} + +// A little colored square. Return true when clicked. +// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. +// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. +// Note that 'col' may be encoded in HSV if ImGuiColorEditFlags_InputHSV is set. +bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiID id = window->GetID(desc_id); + float default_size = GetFrameHeight(); + if (size.x == 0.0f) + size.x = default_size; + if (size.y == 0.0f) + size.y = default_size; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + if (flags & ImGuiColorEditFlags_NoAlpha) + flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); + + ImVec4 col_rgb = col; + if (flags & ImGuiColorEditFlags_InputHSV) + ColorConvertHSVtoRGB(col_rgb.x, col_rgb.y, col_rgb.z, col_rgb.x, col_rgb.y, col_rgb.z); + + ImVec4 col_rgb_without_alpha(col_rgb.x, col_rgb.y, col_rgb.z, 1.0f); + float grid_step = ImMin(size.x, size.y) / 2.99f; + float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f); + ImRect bb_inner = bb; + float off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts. + bb_inner.Expand(off); + if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f) + { + float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f); + RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight); + window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft); + } + else + { + // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha + ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col_rgb : col_rgb_without_alpha; + if (col_source.w < 1.0f) + RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); + else + window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All); + } + RenderNavHighlight(bb, id); + if (g.Style.FrameBorderSize > 0.0f) + RenderFrameBorder(bb.Min, bb.Max, rounding); + else + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border + + // Drag and Drop Source + // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test. + if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource()) + { + if (flags & ImGuiColorEditFlags_NoAlpha) + SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col_rgb, sizeof(float) * 3, ImGuiCond_Once); + else + SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col_rgb, sizeof(float) * 4, ImGuiCond_Once); + ColorButton(desc_id, col, flags); + SameLine(); + TextEx("Color"); + EndDragDropSource(); + } + + // Tooltip + if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) + ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); + + if (pressed) + MarkItemEdited(id); + + return pressed; +} + +// Initialize/override default color options +void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) +{ + ImGuiContext& g = *GImGui; + if ((flags & ImGuiColorEditFlags__DisplayMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DisplayMask; + if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask; + if ((flags & ImGuiColorEditFlags__PickerMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask; + if ((flags & ImGuiColorEditFlags__InputMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputMask; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DisplayMask)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DataTypeMask)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__PickerMask)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check only 1 option is selected + g.ColorEditOptions = flags; +} + +// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) +{ + ImGuiContext& g = *GImGui; + + BeginTooltipEx(0, true); + const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; + if (text_end > text) + { + TextEx(text, text_end); + Separator(); + } + + ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); + ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); + ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); + SameLine(); + if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags__InputMask)) + { + if (flags & ImGuiColorEditFlags_NoAlpha) + Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); + else + Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + if (flags & ImGuiColorEditFlags_NoAlpha) + Text("H: %.3f, S: %.3f, V: %.3f", col[0], col[1], col[2]); + else + Text("H: %.3f, S: %.3f, V: %.3f, A: %.3f", col[0], col[1], col[2], col[3]); + } + EndTooltip(); +} + +void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) +{ + bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__DisplayMask); + bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); + if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) + return; + ImGuiContext& g = *GImGui; + ImGuiColorEditFlags opts = g.ColorEditOptions; + if (allow_opt_inputs) + { + if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayRGB; + if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayHSV; + if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayHex; + } + if (allow_opt_datatype) + { + if (allow_opt_inputs) Separator(); + if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8; + if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float; + } + + if (allow_opt_inputs || allow_opt_datatype) + Separator(); + if (Button("Copy as..", ImVec2(-1,0))) + OpenPopup("Copy"); + if (BeginPopup("Copy")) + { + int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); + char buf[64]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + if (Selectable(buf)) + SetClipboardText(buf); + ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); + if (Selectable(buf)) + SetClipboardText(buf); + if (flags & ImGuiColorEditFlags_NoAlpha) + ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb); + else + ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca); + if (Selectable(buf)) + SetClipboardText(buf); + EndPopup(); + } + + g.ColorEditOptions = opts; + EndPopup(); +} + +void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags) +{ + bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask); + bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); + if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context")) + return; + ImGuiContext& g = *GImGui; + if (allow_opt_picker) + { + ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function + PushItemWidth(picker_size.x); + for (int picker_type = 0; picker_type < 2; picker_type++) + { + // Draw small/thumbnail version of each picker type (over an invisible button for selection) + if (picker_type > 0) Separator(); + PushID(picker_type); + ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha); + if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar; + if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; + ImVec2 backup_pos = GetCursorScreenPos(); + if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup + g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); + SetCursorScreenPos(backup_pos); + ImVec4 dummy_ref_col; + memcpy(&dummy_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4)); + ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags); + PopID(); + } + PopItemWidth(); + } + if (allow_opt_alpha_bar) + { + if (allow_opt_picker) Separator(); + CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); + } + EndPopup(); +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: TreeNode, CollapsingHeader, etc. +//------------------------------------------------------------------------- +// - TreeNode() +// - TreeNodeV() +// - TreeNodeEx() +// - TreeNodeExV() +// - TreeNodeBehavior() [Internal] +// - TreePush() +// - TreePop() +// - TreeAdvanceToLabelPos() +// - GetTreeNodeToLabelSpacing() +// - SetNextItemOpen() +// - CollapsingHeader() +//------------------------------------------------------------------------- + +bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(str_id, 0, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(ptr_id, 0, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNode(const char* label) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + return TreeNodeBehavior(window->GetID(label), 0, label, NULL); +} + +bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) +{ + return TreeNodeExV(str_id, 0, fmt, args); +} + +bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) +{ + return TreeNodeExV(ptr_id, 0, fmt, args); +} + +bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + return TreeNodeBehavior(window->GetID(label), flags, label, NULL); +} + +bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(str_id, flags, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(ptr_id, flags, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end); +} + +bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end); +} + +bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) +{ + if (flags & ImGuiTreeNodeFlags_Leaf) + return true; + + // We only write to the tree storage if the user clicks (or explicitly use the SetNextItemOpen function) + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStorage* storage = window->DC.StateStorage; + + bool is_open; + if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasOpen) + { + if (g.NextItemData.OpenCond & ImGuiCond_Always) + { + is_open = g.NextItemData.OpenVal; + storage->SetInt(id, is_open); + } + else + { + // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently. + const int stored_value = storage->GetInt(id, -1); + if (stored_value == -1) + { + is_open = g.NextItemData.OpenVal; + storage->SetInt(id, is_open); + } + else + { + is_open = stored_value != 0; + } + } + } + else + { + is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; + } + + // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). + // NB- If we are above max depth we still allow manually opened nodes to be logged. + if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && (window->DC.TreeDepth - g.LogDepthRef) < g.LogDepthToExpand) + is_open = true; + + return is_open; +} + +bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; + const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f); + + if (!label_end) + label_end = FindRenderedTextEnd(label); + const ImVec2 label_size = CalcTextSize(label, label_end, false); + + // We vertically grow up to current line height up the typical widget height. + const float text_base_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it + const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2); + ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->WorkRect.Max.x, window->DC.CursorPos.y + frame_height)); + if (display_frame) + { + // Framed header expand a little outside the default padding + frame_bb.Min.x -= (float)(int)(window->WindowPadding.x * 0.5f - 1.0f); + frame_bb.Max.x += (float)(int)(window->WindowPadding.x * 0.5f); + } + + const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing + const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser + ItemSize(ImVec2(text_width, frame_height), text_base_offset_y); + + // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing + // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) + const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y); + bool is_open = TreeNodeBehaviorIsOpen(id, flags); + bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; + + // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. + // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). + // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero. + if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + window->DC.TreeStoreMayJumpToParentOnPop |= (1 << window->DC.TreeDepth); + + bool item_add = ItemAdd(interact_bb, id); + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; + window->DC.LastItemDisplayRect = frame_bb; + + if (!item_add) + { + if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + TreePushOverrideID(id); + IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); + return is_open; + } + + // Flags that affects opening behavior: + // - 0 (default) .................... single-click anywhere to open + // - OpenOnDoubleClick .............. double-click anywhere to open + // - OpenOnArrow .................... single-click on arrow to open + // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open + ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers; + if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) + button_flags |= ImGuiButtonFlags_AllowItemOverlap; + if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) + button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0); + if (!is_leaf) + button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; + + bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0; + const bool was_selected = selected; + + bool hovered, held; + bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); + bool toggled = false; + if (!is_leaf) + { + if (pressed) + { + toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id); + if (flags & ImGuiTreeNodeFlags_OpenOnArrow) + toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover); + if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) + toggled |= g.IO.MouseDoubleClicked[0]; + if (g.DragDropActive && is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again. + toggled = false; + } + + if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open) + { + toggled = true; + NavMoveRequestCancel(); + } + if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? + { + toggled = true; + NavMoveRequestCancel(); + } + + if (toggled) + { + is_open = !is_open; + window->DC.StateStorage->SetInt(id, is_open); + } + } + if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) + SetItemAllowOverlap(); + + // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger. + if (selected != was_selected) //-V547 + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + + // Render + const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + const ImU32 text_col = GetColorU32(ImGuiCol_Text); + const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y); + ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_TypeThin; + if (display_frame) + { + // Framed type + RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding); + RenderNavHighlight(frame_bb, id, nav_highlight_flags); + RenderArrow(window->DrawList, frame_bb.Min + ImVec2(padding.x, text_base_offset_y), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); + if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) + frame_bb.Max.x -= g.FontSize + style.FramePadding.x; + if (g.LogEnabled) + { + // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here. + const char log_prefix[] = "\n##"; + const char log_suffix[] = "##"; + LogRenderedText(&text_pos, log_prefix, log_prefix+3); + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); + LogRenderedText(&text_pos, log_suffix, log_suffix+2); + } + else + { + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); + } + } + else + { + // Unframed typed for tree nodes + if (hovered || selected) + { + RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); + RenderNavHighlight(frame_bb, id, nav_highlight_flags); + } + + if (flags & ImGuiTreeNodeFlags_Bullet) + RenderBullet(window->DrawList, frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y), text_col); + else if (!is_leaf) + RenderArrow(window->DrawList, frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); + if (g.LogEnabled) + LogRenderedText(&text_pos, ">"); + RenderText(text_pos, label, label_end, false); + } + + if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + TreePushOverrideID(id); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); + return is_open; +} + +void ImGui::TreePush(const char* str_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + PushID(str_id ? str_id : "#TreePush"); +} + +void ImGui::TreePush(const void* ptr_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); +} + +void ImGui::TreePushOverrideID(ImGuiID id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + window->IDStack.push_back(id); +} + +void ImGui::TreePop() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + Unindent(); + + window->DC.TreeDepth--; + if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) + if (g.NavIdIsAlive && (window->DC.TreeStoreMayJumpToParentOnPop & (1 << window->DC.TreeDepth))) + { + SetNavID(window->IDStack.back(), g.NavLayer); + NavMoveRequestCancel(); + } + window->DC.TreeStoreMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1; + + IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much. + PopID(); +} + +void ImGui::TreeAdvanceToLabelPos() +{ + ImGuiContext& g = *GImGui; + g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing(); +} + +// Horizontal distance preceding label when using TreeNode() or Bullet() +float ImGui::GetTreeNodeToLabelSpacing() +{ + ImGuiContext& g = *GImGui; + return g.FontSize + (g.Style.FramePadding.x * 2.0f); +} + +// Set next TreeNode/CollapsingHeader open state. +void ImGui::SetNextItemOpen(bool is_open, ImGuiCond cond) +{ + ImGuiContext& g = *GImGui; + if (g.CurrentWindow->SkipItems) + return; + g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasOpen; + g.NextItemData.OpenVal = is_open; + g.NextItemData.OpenCond = cond ? cond : ImGuiCond_Always; +} + +// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). +// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). +bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label); +} + +bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + if (p_open && !*p_open) + return false; + + ImGuiID id = window->GetID(label); + flags |= ImGuiTreeNodeFlags_CollapsingHeader | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_ClipLabelForTrailingButton : 0); + bool is_open = TreeNodeBehavior(id, flags, label); + if (p_open) + { + // Create a small overlapping close button + // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. + // FIXME: CloseButton can overlap into text, need find a way to clip the text somehow. + ImGuiContext& g = *GImGui; + ImGuiItemHoveredDataBackup last_item_backup; + float button_size = g.FontSize; + float button_x = ImMax(window->DC.LastItemRect.Min.x, window->DC.LastItemRect.Max.x - g.Style.FramePadding.x * 2.0f - button_size); + float button_y = window->DC.LastItemRect.Min.y; + if (CloseButton(window->GetID((void*)((intptr_t)id + 1)), ImVec2(button_x, button_y))) + *p_open = false; + last_item_backup.Restore(); + } + + return is_open; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Selectable +//------------------------------------------------------------------------- +// - Selectable() +//------------------------------------------------------------------------- + +// Tip: pass a non-visible label (e.g. "##dummy") then you can use the space to draw other text or image. +// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id. +bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns) // FIXME-OPT: Avoid if vertically clipped. + PushColumnsBackground(); + + ImGuiID id = window->GetID(label); + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); + ImVec2 pos = window->DC.CursorPos; + pos.y += window->DC.CurrLineTextBaseOffset; + ImRect bb_inner(pos, pos + size); + ItemSize(size); + + // Fill horizontal space. + ImVec2 window_padding = window->WindowPadding; + float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x; + float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - pos.x); + ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y); + ImRect bb(pos, pos + size_draw); + if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth)) + bb.Max.x += window_padding.x; + + // Selectables are tightly packed together so we extend the box to cover spacing between selectable. + const float spacing_x = style.ItemSpacing.x; + const float spacing_y = style.ItemSpacing.y; + const float spacing_L = (float)(int)(spacing_x * 0.50f); + const float spacing_U = (float)(int)(spacing_y * 0.50f); + bb.Min.x -= spacing_L; + bb.Min.y -= spacing_U; + bb.Max.x += (spacing_x - spacing_L); + bb.Max.y += (spacing_y - spacing_U); + + bool item_add; + if (flags & ImGuiSelectableFlags_Disabled) + { + ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; + window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus; + item_add = ItemAdd(bb, id); + window->DC.ItemFlags = backup_item_flags; + } + else + { + item_add = ItemAdd(bb, id); + } + if (!item_add) + { + if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns) + PopColumnsBackground(); + return false; + } + + // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries + ImGuiButtonFlags button_flags = 0; + if (flags & ImGuiSelectableFlags_NoHoldingActiveID) button_flags |= ImGuiButtonFlags_NoHoldingActiveID; + if (flags & ImGuiSelectableFlags_PressedOnClick) button_flags |= ImGuiButtonFlags_PressedOnClick; + if (flags & ImGuiSelectableFlags_PressedOnRelease) button_flags |= ImGuiButtonFlags_PressedOnRelease; + if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled; + if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; + if (flags & ImGuiSelectableFlags_AllowItemOverlap) button_flags |= ImGuiButtonFlags_AllowItemOverlap; + + if (flags & ImGuiSelectableFlags_Disabled) + selected = false; + + const bool was_selected = selected; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets) + if (pressed || hovered) + if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) + { + g.NavDisableHighlight = true; + SetNavID(id, window->DC.NavLayerCurrent); + } + if (pressed) + MarkItemEdited(id); + + if (flags & ImGuiSelectableFlags_AllowItemOverlap) + SetItemAllowOverlap(); + + // In this branch, Selectable() cannot toggle the selection so this will never trigger. + if (selected != was_selected) //-V547 + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + + // Render + if (hovered || selected) + { + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + RenderFrame(bb.Min, bb.Max, col, false, 0.0f); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); + } + + if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns) + { + PopColumnsBackground(); + bb.Max.x -= (GetContentRegionMax().x - max_x); + } + + if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); + RenderTextClipped(bb_inner.Min, bb_inner.Max, label, NULL, &label_size, style.SelectableTextAlign, &bb); + if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); + + // Automatically close popups + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) + CloseCurrentPopup(); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + return pressed; +} + +bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) +{ + if (Selectable(label, *p_selected, flags, size_arg)) + { + *p_selected = !*p_selected; + return true; + } + return false; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: ListBox +//------------------------------------------------------------------------- +// - ListBox() +// - ListBoxHeader() +// - ListBoxFooter() +//------------------------------------------------------------------------- +// FIXME: This is an old API. We should redesign some of it, rename ListBoxHeader->BeginListBox, ListBoxFooter->EndListBox +// and promote using them over existing ListBox() functions, similarly to change with combo boxes. +//------------------------------------------------------------------------- + +// FIXME: In principle this function should be called BeginListBox(). We should rename it after re-evaluating if we want to keep the same signature. +// Helper to calculate the size of a listbox and display a label on the right. +// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an non-visible label e.g. "##empty" +bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. + ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y); + ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); + ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); + ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy. + g.NextItemData.ClearFlags(); + + if (!IsRectVisible(bb.Min, bb.Max)) + { + ItemSize(bb.GetSize(), style.FramePadding.y); + ItemAdd(bb, 0, &frame_bb); + return false; + } + + BeginGroup(); + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + BeginChildFrame(id, frame_bb.GetSize()); + return true; +} + +// FIXME: In principle this function should be called EndListBox(). We should rename it after re-evaluating if we want to keep the same signature. +bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) +{ + // Size default to hold ~7.25 items. + // We add +25% worth of item height to allow the user to see at a glance if there are more items up/down, without looking at the scrollbar. + // We don't add this extra bit if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. + // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution. + if (height_in_items < 0) + height_in_items = ImMin(items_count, 7); + const ImGuiStyle& style = GetStyle(); + float height_in_items_f = (height_in_items < items_count) ? (height_in_items + 0.25f) : (height_in_items + 0.00f); + + // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild(). + ImVec2 size; + size.x = 0.0f; + size.y = GetTextLineHeightWithSpacing() * height_in_items_f + style.FramePadding.y * 2.0f; + return ListBoxHeader(label, size); +} + +// FIXME: In principle this function should be called EndListBox(). We should rename it after re-evaluating if we want to keep the same signature. +void ImGui::ListBoxFooter() +{ + ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow; + const ImRect bb = parent_window->DC.LastItemRect; + const ImGuiStyle& style = GetStyle(); + + EndChildFrame(); + + // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect) + // We call SameLine() to restore DC.CurrentLine* data + SameLine(); + parent_window->DC.CursorPos = bb.Min; + ItemSize(bb, style.FramePadding.y); + EndGroup(); +} + +bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items) +{ + const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); + return value_changed; +} + +bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) +{ + if (!ListBoxHeader(label, items_count, height_in_items)) + return false; + + // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper. + ImGuiContext& g = *GImGui; + bool value_changed = false; + ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. + while (clipper.Step()) + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + { + const bool item_selected = (i == *current_item); + const char* item_text; + if (!items_getter(data, i, &item_text)) + item_text = "*Unknown item*"; + + PushID(i); + if (Selectable(item_text, item_selected)) + { + *current_item = i; + value_changed = true; + } + if (item_selected) + SetItemDefaultFocus(); + PopID(); + } + ListBoxFooter(); + if (value_changed) + MarkItemEdited(g.CurrentWindow->DC.LastItemId); + + return value_changed; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: PlotLines, PlotHistogram +//------------------------------------------------------------------------- +// - PlotEx() [Internal] +// - PlotLines() +// - PlotHistogram() +//------------------------------------------------------------------------- + +void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + if (frame_size.x == 0.0f) + frame_size.x = CalcItemWidth(); + if (frame_size.y == 0.0f) + frame_size.y = label_size.y + (style.FramePadding.y * 2); + + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); + const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, 0, &frame_bb)) + return; + const bool hovered = ItemHoverable(frame_bb, id); + + // Determine scale from values if not specified + if (scale_min == FLT_MAX || scale_max == FLT_MAX) + { + float v_min = FLT_MAX; + float v_max = -FLT_MAX; + for (int i = 0; i < values_count; i++) + { + const float v = values_getter(data, i); + if (v != v) // Ignore NaN values + continue; + v_min = ImMin(v_min, v); + v_max = ImMax(v_max, v); + } + if (scale_min == FLT_MAX) + scale_min = v_min; + if (scale_max == FLT_MAX) + scale_max = v_max; + } + + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + + const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1; + if (values_count >= values_count_min) + { + int res_w = ImMin((int)frame_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); + int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); + + // Tooltip on hover + int v_hovered = -1; + if (hovered && inner_bb.Contains(g.IO.MousePos)) + { + const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); + const int v_idx = (int)(t * item_count); + IM_ASSERT(v_idx >= 0 && v_idx < values_count); + + const float v0 = values_getter(data, (v_idx + values_offset) % values_count); + const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count); + if (plot_type == ImGuiPlotType_Lines) + SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1); + else if (plot_type == ImGuiPlotType_Histogram) + SetTooltip("%d: %8.4g", v_idx, v0); + v_hovered = v_idx; + } + + const float t_step = 1.0f / (float)res_w; + const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); + + float v0 = values_getter(data, (0 + values_offset) % values_count); + float t0 = 0.0f; + ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle + float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands + + const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); + const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); + + for (int n = 0; n < res_w; n++) + { + const float t1 = t0 + t_step; + const int v1_idx = (int)(t0 * item_count + 0.5f); + IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); + const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count); + const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) ); + + // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU. + ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); + ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t)); + if (plot_type == ImGuiPlotType_Lines) + { + window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); + } + else if (plot_type == ImGuiPlotType_Histogram) + { + if (pos1.x >= pos0.x + 2.0f) + pos1.x -= 1.0f; + window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); + } + + t0 = t1; + tp0 = tp1; + } + } + + // Text overlay + if (overlay_text) + RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); +} + +struct ImGuiPlotArrayGetterData +{ + const float* Values; + int Stride; + + ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; } +}; + +static float Plot_ArrayGetter(void* data, int idx) +{ + ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data; + const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride); + return v; +} + +void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) +{ + ImGuiPlotArrayGetterData data(values, stride); + PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) +{ + ImGuiPlotArrayGetterData data(values, stride); + PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Value helpers +// Those is not very useful, legacy API. +//------------------------------------------------------------------------- +// - Value() +//------------------------------------------------------------------------- + +void ImGui::Value(const char* prefix, bool b) +{ + Text("%s: %s", prefix, (b ? "true" : "false")); +} + +void ImGui::Value(const char* prefix, int v) +{ + Text("%s: %d", prefix, v); +} + +void ImGui::Value(const char* prefix, unsigned int v) +{ + Text("%s: %d", prefix, v); +} + +void ImGui::Value(const char* prefix, float v, const char* float_format) +{ + if (float_format) + { + char fmt[64]; + ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); + Text(fmt, prefix, v); + } + else + { + Text("%s: %.3f", prefix, v); + } +} + +//------------------------------------------------------------------------- +// [SECTION] MenuItem, BeginMenu, EndMenu, etc. +//------------------------------------------------------------------------- +// - ImGuiMenuColumns [Internal] +// - BeginMainMenuBar() +// - EndMainMenuBar() +// - BeginMenuBar() +// - EndMenuBar() +// - BeginMenu() +// - EndMenu() +// - MenuItem() +//------------------------------------------------------------------------- + +// Helpers for internal use +ImGuiMenuColumns::ImGuiMenuColumns() +{ + Spacing = Width = NextWidth = 0.0f; + memset(Pos, 0, sizeof(Pos)); + memset(NextWidths, 0, sizeof(NextWidths)); +} + +void ImGuiMenuColumns::Update(int count, float spacing, bool clear) +{ + IM_ASSERT(count == IM_ARRAYSIZE(Pos)); + IM_UNUSED(count); + Width = NextWidth = 0.0f; + Spacing = spacing; + if (clear) + memset(NextWidths, 0, sizeof(NextWidths)); + for (int i = 0; i < IM_ARRAYSIZE(Pos); i++) + { + if (i > 0 && NextWidths[i] > 0.0f) + Width += Spacing; + Pos[i] = (float)(int)Width; + Width += NextWidths[i]; + NextWidths[i] = 0.0f; + } +} + +float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double +{ + NextWidth = 0.0f; + NextWidths[0] = ImMax(NextWidths[0], w0); + NextWidths[1] = ImMax(NextWidths[1], w1); + NextWidths[2] = ImMax(NextWidths[2], w2); + for (int i = 0; i < IM_ARRAYSIZE(Pos); i++) + NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); + return ImMax(Width, NextWidth); +} + +float ImGuiMenuColumns::CalcExtraSpace(float avail_w) +{ + return ImMax(0.0f, avail_w - Width); +} + +// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. +bool ImGui::BeginMainMenuBar() +{ + ImGuiContext& g = *GImGui; + g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); + SetNextWindowPos(ImVec2(0.0f, 0.0f)); + SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); + PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; + bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); + PopStyleVar(2); + g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); + if (!is_open) + { + End(); + return false; + } + return true; //-V1020 +} + +void ImGui::EndMainMenuBar() +{ + EndMenuBar(); + + // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window + // FIXME: With this strategy we won't be able to restore a NULL focus. + ImGuiContext& g = *GImGui; + if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0 && !g.NavAnyRequest) + FocusTopMostWindowUnderOne(g.NavWindow, NULL); + + End(); +} + +bool ImGui::BeginMenuBar() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + if (!(window->Flags & ImGuiWindowFlags_MenuBar)) + return false; + + IM_ASSERT(!window->DC.MenuBarAppending); + BeginGroup(); // Backup position on layer 0 + PushID("##menubar"); + + // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. + // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy. + ImRect bar_rect = window->MenuBarRect(); + ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f)); + clip_rect.ClipWith(window->OuterRectClipped); + PushClipRect(clip_rect.Min, clip_rect.Max, false); + + window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y); + window->DC.LayoutType = ImGuiLayoutType_Horizontal; + window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; + window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu); + window->DC.MenuBarAppending = true; + AlignTextToFramePadding(); + return true; +} + +void ImGui::EndMenuBar() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + ImGuiContext& g = *GImGui; + + // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. + if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) + { + ImGuiWindow* nav_earliest_child = g.NavWindow; + while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) + nav_earliest_child = nav_earliest_child->ParentWindow; + if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None) + { + // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. + // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) + const ImGuiNavLayer layer = ImGuiNavLayer_Menu; + IM_ASSERT(window->DC.NavLayerActiveMaskNext & (1 << layer)); // Sanity check + FocusWindow(window); + SetNavIDWithRectRel(window->NavLastIds[layer], layer, window->NavRectRel[layer]); + g.NavLayer = layer; + g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. + g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; + NavMoveRequestCancel(); + } + } + + IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); + IM_ASSERT(window->DC.MenuBarAppending); + PopClipRect(); + PopID(); + window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. + window->DC.GroupStack.back().EmitItem = false; + EndGroup(); // Restore position on layer 0 + window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.NavLayerCurrent = ImGuiNavLayer_Main; + window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); + window->DC.MenuBarAppending = false; +} + +bool ImGui::BeginMenu(const char* label, bool enabled) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + ImVec2 label_size = CalcTextSize(label, NULL, true); + + bool pressed; + bool menu_is_open = IsPopupOpen(id); + bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); + ImGuiWindow* backed_nav_window = g.NavWindow; + if (menuset_is_open) + g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) + + // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, + // However the final position is going to be different! It is choosen by FindBestWindowPosForPopup(). + // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. + ImVec2 popup_pos, pos = window->DC.CursorPos; + if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) + { + // Menu inside an horizontal menu bar + // Selectable extend their highlight by half ItemSpacing in each direction. + // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() + popup_pos = ImVec2(pos.x - 1.0f - (float)(int)(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight()); + window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); + PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); + float w = label_size.x; + pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); + PopStyleVar(); + window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + } + else + { + // Menu inside a menu + popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); + float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame + float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); + pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); + ImU32 text_col = GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled); + RenderArrow(window->DrawList, pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), text_col, ImGuiDir_Right); + } + + const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id); + if (menuset_is_open) + g.NavWindow = backed_nav_window; + + bool want_open = false; + bool want_close = false; + if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + { + // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu + // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. + bool moving_toward_other_child_menu = false; + + ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? g.OpenPopupStack[g.BeginPopupStack.Size].Window : NULL; + if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) + { + // FIXME-DPI: Values should be derived from a master "scale" factor. + ImRect next_window_rect = child_menu_window->Rect(); + ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; + ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); + ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); + float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. + ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues + tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? + tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); + moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); + //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] + } + if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu) + want_close = true; + + if (!menu_is_open && hovered && pressed) // Click to open + want_open = true; + else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open + want_open = true; + + if (g.NavActivateId == id) + { + want_close = menu_is_open; + want_open = !menu_is_open; + } + if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open + { + want_open = true; + NavMoveRequestCancel(); + } + } + else + { + // Menu bar + if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it + { + want_close = true; + want_open = menu_is_open = false; + } + else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others + { + want_open = true; + } + else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open + { + want_open = true; + NavMoveRequestCancel(); + } + } + + if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' + want_close = true; + if (want_close && IsPopupOpen(id)) + ClosePopupToLevel(g.BeginPopupStack.Size, true); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); + + if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) + { + // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. + OpenPopup(label); + return false; + } + + menu_is_open |= want_open; + if (want_open) + OpenPopup(label); + + if (menu_is_open) + { + // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) + SetNextWindowPos(popup_pos, ImGuiCond_Always); + ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; + if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + flags |= ImGuiWindowFlags_ChildWindow; + menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + } + + return menu_is_open; +} + +void ImGui::EndMenu() +{ + // Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu). + // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs. + // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) + { + ClosePopupToLevel(g.BeginPopupStack.Size, true); + NavMoveRequestCancel(); + } + + EndPopup(); +} + +bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + ImVec2 pos = window->DC.CursorPos; + ImVec2 label_size = CalcTextSize(label, NULL, true); + + ImGuiSelectableFlags flags = ImGuiSelectableFlags_PressedOnRelease | (enabled ? 0 : ImGuiSelectableFlags_Disabled); + bool pressed; + if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) + { + // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful + // Note that in this situation we render neither the shortcut neither the selected tick mark + float w = label_size.x; + window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); + PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); + pressed = Selectable(label, false, flags, ImVec2(w, 0.0f)); + PopStyleVar(); + window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + } + else + { + ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f); + float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame + float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); + pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f)); + if (shortcut_size.x > 0.0f) + { + PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); + PopStyleColor(); + } + if (selected) + RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); + } + + IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); + return pressed; +} + +bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) +{ + if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled)) + { + if (p_selected) + *p_selected = !*p_selected; + return true; + } + return false; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: BeginTabBar, EndTabBar, etc. +//------------------------------------------------------------------------- +// [BETA API] API may evolve! This code has been extracted out of the Docking branch, +// and some of the construct which are not used in Master may be left here to facilitate merging. +//------------------------------------------------------------------------- +// - BeginTabBar() +// - BeginTabBarEx() [Internal] +// - EndTabBar() +// - TabBarLayout() [Internal] +// - TabBarCalcTabID() [Internal] +// - TabBarCalcMaxTabWidth() [Internal] +// - TabBarFindTabById() [Internal] +// - TabBarRemoveTab() [Internal] +// - TabBarCloseTab() [Internal] +// - TabBarScrollClamp()v +// - TabBarScrollToTab() [Internal] +// - TabBarQueueChangeTabOrder() [Internal] +// - TabBarScrollingButtons() [Internal] +// - TabBarTabListPopupButton() [Internal] +//------------------------------------------------------------------------- + +namespace ImGui +{ + static void TabBarLayout(ImGuiTabBar* tab_bar); + static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label); + static float TabBarCalcMaxTabWidth(); + static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling); + static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); + static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar); + static ImGuiTabItem* TabBarTabListPopupButton(ImGuiTabBar* tab_bar); +} + +ImGuiTabBar::ImGuiTabBar() +{ + ID = 0; + SelectedTabId = NextSelectedTabId = VisibleTabId = 0; + CurrFrameVisible = PrevFrameVisible = -1; + ContentsHeight = 0.0f; + OffsetMax = OffsetNextTab = 0.0f; + ScrollingAnim = ScrollingTarget = ScrollingTargetDistToVisibility = ScrollingSpeed = 0.0f; + Flags = ImGuiTabBarFlags_None; + ReorderRequestTabId = 0; + ReorderRequestDir = 0; + WantLayout = VisibleTabWasSubmitted = false; + LastTabItemIdx = -1; +} + +static int IMGUI_CDECL TabItemComparerByVisibleOffset(const void* lhs, const void* rhs) +{ + const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; + const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; + return (int)(a->Offset - b->Offset); +} + +static ImGuiTabBar* GetTabBarFromTabBarRef(const ImGuiTabBarRef& ref) +{ + ImGuiContext& g = *GImGui; + return ref.Ptr ? ref.Ptr : g.TabBars.GetByIndex(ref.IndexInMainPool); +} + +static ImGuiTabBarRef GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + if (g.TabBars.Contains(tab_bar)) + return ImGuiTabBarRef(g.TabBars.GetIndex(tab_bar)); + return ImGuiTabBarRef(tab_bar); +} + +bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + ImGuiID id = window->GetID(str_id); + ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id); + ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->WorkRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2); + tab_bar->ID = id; + return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused); +} + +bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + if ((flags & ImGuiTabBarFlags_DockNode) == 0) + PushOverrideID(tab_bar->ID); + + // Add to stack + g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar)); + g.CurrentTabBar = tab_bar; + + if (tab_bar->CurrFrameVisible == g.FrameCount) + { + //IMGUI_DEBUG_LOG("BeginTabBarEx already called this frame\n", g.FrameCount); + IM_ASSERT(0); + return true; + } + + // When toggling back from ordered to manually-reorderable, shuffle tabs to enforce the last visible order. + // Otherwise, the most recently inserted tabs would move at the end of visible list which can be a little too confusing or magic for the user. + if ((flags & ImGuiTabBarFlags_Reorderable) && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable) && tab_bar->Tabs.Size > 1 && tab_bar->PrevFrameVisible != -1) + ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByVisibleOffset); + + // Flags + if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) + flags |= ImGuiTabBarFlags_FittingPolicyDefault_; + + tab_bar->Flags = flags; + tab_bar->BarRect = tab_bar_bb; + tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab() + tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible; + tab_bar->CurrFrameVisible = g.FrameCount; + tab_bar->FramePadding = g.Style.FramePadding; + + // Layout + ItemSize(ImVec2(0.0f /*tab_bar->OffsetMax*/, tab_bar->BarRect.GetHeight())); // Don't feed width back + window->DC.CursorPos.x = tab_bar->BarRect.Min.x; + + // Draw separator + const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_Tab); + const float y = tab_bar->BarRect.Max.y - 1.0f; + { + const float separator_min_x = tab_bar->BarRect.Min.x - ImFloor(window->WindowPadding.x * 0.5f); + const float separator_max_x = tab_bar->BarRect.Max.x + ImFloor(window->WindowPadding.x * 0.5f); + window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f); + } + return true; +} + +void ImGui::EndTabBar() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT(tab_bar != NULL && "Mismatched BeginTabBar()/EndTabBar()!"); + return; // FIXME-ERRORHANDLING + } + if (tab_bar->WantLayout) + TabBarLayout(tab_bar); + + // Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed(). + const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); + if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing) + tab_bar->ContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, 0.0f); + else + window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->ContentsHeight; + + if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) + PopID(); + + g.CurrentTabBarStack.pop_back(); + g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back()); +} + +// This is called only once a frame before by the first call to ItemTab() +// The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions. +static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + tab_bar->WantLayout = false; + + // Garbage collect + int tab_dst_n = 0; + for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n]; + if (tab->LastFrameVisible < tab_bar->PrevFrameVisible) + { + if (tab->ID == tab_bar->SelectedTabId) + tab_bar->SelectedTabId = 0; + continue; + } + if (tab_dst_n != tab_src_n) + tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n]; + tab_dst_n++; + } + if (tab_bar->Tabs.Size != tab_dst_n) + tab_bar->Tabs.resize(tab_dst_n); + + // Setup next selected tab + ImGuiID scroll_track_selected_tab_id = 0; + if (tab_bar->NextSelectedTabId) + { + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId; + tab_bar->NextSelectedTabId = 0; + scroll_track_selected_tab_id = tab_bar->SelectedTabId; + } + + // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot). + if (tab_bar->ReorderRequestTabId != 0) + { + if (ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId)) + { + //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools + int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir; + if (tab2_order >= 0 && tab2_order < tab_bar->Tabs.Size) + { + ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; + ImGuiTabItem item_tmp = *tab1; + *tab1 = *tab2; + *tab2 = item_tmp; + if (tab2->ID == tab_bar->SelectedTabId) + scroll_track_selected_tab_id = tab2->ID; + tab1 = tab2 = NULL; + } + if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) + MarkIniSettingsDirty(); + } + tab_bar->ReorderRequestTabId = 0; + } + + // Tab List Popup (will alter tab_bar->BarRect and therefore the available width!) + const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0; + if (tab_list_popup_button) + if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Max.x! + scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; + + // Compute ideal widths + g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size); + float width_total_contents = 0.0f; + ImGuiTabItem* most_recently_selected_tab = NULL; + bool found_selected_tab_id = false; + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible); + + if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) + most_recently_selected_tab = tab; + if (tab->ID == tab_bar->SelectedTabId) + found_selected_tab_id = true; + + // Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar. + // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet, + // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window. + const char* tab_name = tab_bar->GetTabName(tab); + const bool has_close_button = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) ? false : true; + tab->WidthContents = TabItemCalcSize(tab_name, has_close_button).x; + + width_total_contents += (tab_n > 0 ? g.Style.ItemInnerSpacing.x : 0.0f) + tab->WidthContents; + + // Store data so we can build an array sorted by width if we need to shrink tabs down + g.ShrinkWidthBuffer[tab_n].Index = tab_n; + g.ShrinkWidthBuffer[tab_n].Width = tab->WidthContents; + } + + // Compute width + const float initial_offset_x = 0.0f; // g.Style.ItemInnerSpacing.x; + const float width_avail = ImMax(tab_bar->BarRect.GetWidth() - initial_offset_x, 0.0f); + float width_excess = (width_avail < width_total_contents) ? (width_total_contents - width_avail) : 0.0f; + if (width_excess > 0.0f && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown)) + { + // If we don't have enough room, resize down the largest tabs first + ShrinkWidths(g.ShrinkWidthBuffer.Data, g.ShrinkWidthBuffer.Size, width_excess); + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index].Width = (float)(int)g.ShrinkWidthBuffer[tab_n].Width; + } + else + { + const float tab_max_width = TabBarCalcMaxTabWidth(); + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + tab->Width = ImMin(tab->WidthContents, tab_max_width); + } + } + + // Layout all active tabs + float offset_x = initial_offset_x; + tab_bar->OffsetNextTab = offset_x; // This is used by non-reorderable tab bar where the submission order is always honored. + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + tab->Offset = offset_x; + if (scroll_track_selected_tab_id == 0 && g.NavJustMovedToId == tab->ID) + scroll_track_selected_tab_id = tab->ID; + offset_x += tab->Width + g.Style.ItemInnerSpacing.x; + } + tab_bar->OffsetMax = ImMax(offset_x - g.Style.ItemInnerSpacing.x, 0.0f); + + // Horizontal scrolling buttons + const bool scrolling_buttons = (tab_bar->OffsetMax > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll); + if (scrolling_buttons) + if (ImGuiTabItem* tab_to_select = TabBarScrollingButtons(tab_bar)) // NB: Will alter BarRect.Max.x! + scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; + + // If we have lost the selected tab, select the next most recently active one + if (found_selected_tab_id == false) + tab_bar->SelectedTabId = 0; + if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL) + scroll_track_selected_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID; + + // Lock in visible tab + tab_bar->VisibleTabId = tab_bar->SelectedTabId; + tab_bar->VisibleTabWasSubmitted = false; + + // Update scrolling + if (scroll_track_selected_tab_id) + if (ImGuiTabItem* scroll_track_selected_tab = TabBarFindTabByID(tab_bar, scroll_track_selected_tab_id)) + TabBarScrollToTab(tab_bar, scroll_track_selected_tab); + tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim); + tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget); + if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget) + { + // Scrolling speed adjust itself so we can always reach our target in 1/3 seconds. + // Teleport if we are aiming far off the visible line + tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, 70.0f * g.FontSize); + tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, ImFabs(tab_bar->ScrollingTarget - tab_bar->ScrollingAnim) / 0.3f); + const bool teleport = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) || (tab_bar->ScrollingTargetDistToVisibility > 10.0f * g.FontSize); + tab_bar->ScrollingAnim = teleport ? tab_bar->ScrollingTarget : ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, g.IO.DeltaTime * tab_bar->ScrollingSpeed); + } + else + { + tab_bar->ScrollingSpeed = 0.0f; + } + + // Clear name buffers + if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) + tab_bar->TabsNames.Buf.resize(0); +} + +// Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack. +static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label) +{ + if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) + { + ImGuiID id = ImHashStr(label); + KeepAliveID(id); + return id; + } + else + { + ImGuiWindow* window = GImGui->CurrentWindow; + return window->GetID(label); + } +} + +static float ImGui::TabBarCalcMaxTabWidth() +{ + ImGuiContext& g = *GImGui; + return g.FontSize * 20.0f; +} + +ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id) +{ + if (tab_id != 0) + for (int n = 0; n < tab_bar->Tabs.Size; n++) + if (tab_bar->Tabs[n].ID == tab_id) + return &tab_bar->Tabs[n]; + return NULL; +} + +// The *TabId fields be already set by the docking system _before_ the actual TabItem was created, so we clear them regardless. +void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) +{ + if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) + tab_bar->Tabs.erase(tab); + if (tab_bar->VisibleTabId == tab_id) { tab_bar->VisibleTabId = 0; } + if (tab_bar->SelectedTabId == tab_id) { tab_bar->SelectedTabId = 0; } + if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; } +} + +// Called on manual closure attempt +void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) +{ + if ((tab_bar->VisibleTabId == tab->ID) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument)) + { + // This will remove a frame of lag for selecting another tab on closure. + // However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure + tab->LastFrameVisible = -1; + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0; + } + else if ((tab_bar->VisibleTabId != tab->ID) && (tab->Flags & ImGuiTabItemFlags_UnsavedDocument)) + { + // Actually select before expecting closure + tab_bar->NextSelectedTabId = tab->ID; + } +} + +static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling) +{ + scrolling = ImMin(scrolling, tab_bar->OffsetMax - tab_bar->BarRect.GetWidth()); + return ImMax(scrolling, 0.0f); +} + +static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) +{ + ImGuiContext& g = *GImGui; + float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar) + int order = tab_bar->GetTabOrder(tab); + float tab_x1 = tab->Offset + (order > 0 ? -margin : 0.0f); + float tab_x2 = tab->Offset + tab->Width + (order + 1 < tab_bar->Tabs.Size ? margin : 1.0f); + tab_bar->ScrollingTargetDistToVisibility = 0.0f; + if (tab_bar->ScrollingTarget > tab_x1) + { + tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f); + tab_bar->ScrollingTarget = tab_x1; + } + else if (tab_bar->ScrollingTarget < tab_x2 - tab_bar->BarRect.GetWidth()) + { + tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - tab_bar->BarRect.GetWidth()) - tab_bar->ScrollingAnim, 0.0f); + tab_bar->ScrollingTarget = tab_x2 - tab_bar->BarRect.GetWidth(); + } +} + +void ImGui::TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir) +{ + IM_ASSERT(dir == -1 || dir == +1); + IM_ASSERT(tab_bar->ReorderRequestTabId == 0); + tab_bar->ReorderRequestTabId = tab->ID; + tab_bar->ReorderRequestDir = dir; +} + +static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f); + const float scrolling_buttons_width = arrow_button_size.x * 2.0f; + + const ImVec2 backup_cursor_pos = window->DC.CursorPos; + //window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255)); + + const ImRect avail_bar_rect = tab_bar->BarRect; + bool want_clip_rect = !avail_bar_rect.Contains(ImRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(scrolling_buttons_width, 0.0f))); + if (want_clip_rect) + PushClipRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max + ImVec2(g.Style.ItemInnerSpacing.x, 0.0f), true); + + ImGuiTabItem* tab_to_select = NULL; + + int select_dir = 0; + ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; + arrow_col.w *= 0.5f; + + PushStyleColor(ImGuiCol_Text, arrow_col); + PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + const float backup_repeat_delay = g.IO.KeyRepeatDelay; + const float backup_repeat_rate = g.IO.KeyRepeatRate; + g.IO.KeyRepeatDelay = 0.250f; + g.IO.KeyRepeatRate = 0.200f; + window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y); + if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat)) + select_dir = -1; + window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width + arrow_button_size.x, tab_bar->BarRect.Min.y); + if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat)) + select_dir = +1; + PopStyleColor(2); + g.IO.KeyRepeatRate = backup_repeat_rate; + g.IO.KeyRepeatDelay = backup_repeat_delay; + + if (want_clip_rect) + PopClipRect(); + + if (select_dir != 0) + if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) + { + int selected_order = tab_bar->GetTabOrder(tab_item); + int target_order = selected_order + select_dir; + tab_to_select = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order]; // If we are at the end of the list, still scroll to make our tab visible + } + window->DC.CursorPos = backup_cursor_pos; + tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f; + + return tab_to_select; +} + +static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // We use g.Style.FramePadding.y to match the square ArrowButton size + const float tab_list_popup_button_width = g.FontSize + g.Style.FramePadding.y; + const ImVec2 backup_cursor_pos = window->DC.CursorPos; + window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x - g.Style.FramePadding.y, tab_bar->BarRect.Min.y); + tab_bar->BarRect.Min.x += tab_list_popup_button_width; + + ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; + arrow_col.w *= 0.5f; + PushStyleColor(ImGuiCol_Text, arrow_col); + PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + bool open = BeginCombo("##v", NULL, ImGuiComboFlags_NoPreview); + PopStyleColor(2); + + ImGuiTabItem* tab_to_select = NULL; + if (open) + { + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + const char* tab_name = tab_bar->GetTabName(tab); + if (Selectable(tab_name, tab_bar->SelectedTabId == tab->ID)) + tab_to_select = tab; + } + EndCombo(); + } + + window->DC.CursorPos = backup_cursor_pos; + return tab_to_select; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: BeginTabItem, EndTabItem, etc. +//------------------------------------------------------------------------- +// [BETA API] API may evolve! This code has been extracted out of the Docking branch, +// and some of the construct which are not used in Master may be left here to facilitate merging. +//------------------------------------------------------------------------- +// - BeginTabItem() +// - EndTabItem() +// - TabItemEx() [Internal] +// - SetTabItemClosed() +// - TabItemCalcSize() [Internal] +// - TabItemBackground() [Internal] +// - TabItemLabelAndCloseButton() [Internal] +//------------------------------------------------------------------------- + +bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT(tab_bar && "Needs to be called between BeginTabBar() and EndTabBar()!"); + return false; // FIXME-ERRORHANDLING + } + bool ret = TabItemEx(tab_bar, label, p_open, flags); + if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; + PushOverrideID(tab->ID); // We already hashed 'label' so push into the ID stack directly instead of doing another hash through PushID(label) + } + return ret; +} + +void ImGui::EndTabItem() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT(tab_bar != NULL && "Needs to be called between BeginTabBar() and EndTabBar()!"); + return; + } + IM_ASSERT(tab_bar->LastTabItemIdx >= 0); + ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; + if (!(tab->Flags & ImGuiTabItemFlags_NoPushId)) + window->IDStack.pop_back(); +} + +bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags) +{ + // Layout whole tab bar if not already done + if (tab_bar->WantLayout) + TabBarLayout(tab_bar); + + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = TabBarCalcTabID(tab_bar, label); + + // If the user called us with *p_open == false, we early out and don't render. We make a dummy call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. + if (p_open && !*p_open) + { + PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); + ItemAdd(ImRect(), id); + PopItemFlag(); + return false; + } + + // Calculate tab contents size + ImVec2 size = TabItemCalcSize(label, p_open != NULL); + + // Acquire tab data + ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id); + bool tab_is_new = false; + if (tab == NULL) + { + tab_bar->Tabs.push_back(ImGuiTabItem()); + tab = &tab_bar->Tabs.back(); + tab->ID = id; + tab->Width = size.x; + tab_is_new = true; + } + tab_bar->LastTabItemIdx = (short)tab_bar->Tabs.index_from_ptr(tab); + tab->WidthContents = size.x; + + if (p_open == NULL) + flags |= ImGuiTabItemFlags_NoCloseButton; + + const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); + const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0; + const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount); + tab->LastFrameVisible = g.FrameCount; + tab->Flags = flags; + + // Append name with zero-terminator + tab->NameOffset = tab_bar->TabsNames.size(); + tab_bar->TabsNames.append(label, label + strlen(label) + 1); + + // If we are not reorderable, always reset offset based on submission order. + // (We already handled layout and sizing using the previous known order, but sizing is not affected by order!) + if (!tab_appearing && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) + { + tab->Offset = tab_bar->OffsetNextTab; + tab_bar->OffsetNextTab += tab->Width + g.Style.ItemInnerSpacing.x; + } + + // Update selected tab + if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0) + if (!tab_bar_appearing || tab_bar->SelectedTabId == 0) + tab_bar->NextSelectedTabId = id; // New tabs gets activated + if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId != id)) // SetSelected can only be passed on explicit tab bar + tab_bar->NextSelectedTabId = id; + + // Lock visibility + bool tab_contents_visible = (tab_bar->VisibleTabId == id); + if (tab_contents_visible) + tab_bar->VisibleTabWasSubmitted = true; + + // On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches + if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing) + if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs)) + tab_contents_visible = true; + + if (tab_appearing && !(tab_bar_appearing && !tab_is_new)) + { + PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); + ItemAdd(ImRect(), id); + PopItemFlag(); + return tab_contents_visible; + } + + if (tab_bar->SelectedTabId == id) + tab->LastFrameSelected = g.FrameCount; + + // Backup current layout position + const ImVec2 backup_main_cursor_pos = window->DC.CursorPos; + + // Layout + size.x = tab->Width; + window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2((float)(int)tab->Offset - tab_bar->ScrollingAnim, 0.0f); + ImVec2 pos = window->DC.CursorPos; + ImRect bb(pos, pos + size); + + // We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation) + bool want_clip_rect = (bb.Min.x < tab_bar->BarRect.Min.x) || (bb.Max.x >= tab_bar->BarRect.Max.x); + if (want_clip_rect) + PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->BarRect.Min.x), bb.Min.y - 1), ImVec2(tab_bar->BarRect.Max.x, bb.Max.y), true); + + ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos; + ItemSize(bb.GetSize(), style.FramePadding.y); + window->DC.CursorMaxPos = backup_cursor_max_pos; + + if (!ItemAdd(bb, id)) + { + if (want_clip_rect) + PopClipRect(); + window->DC.CursorPos = backup_main_cursor_pos; + return tab_contents_visible; + } + + // Click to Select a tab + ImGuiButtonFlags button_flags = (ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_AllowItemOverlap); + if (g.DragDropActive) + button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + if (pressed) + tab_bar->NextSelectedTabId = id; + hovered |= (g.HoveredId == id); + + // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) + if (!held) + SetItemAllowOverlap(); + + // Drag and drop: re-order tabs + if (held && !tab_appearing && IsMouseDragging(0)) + { + if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) + { + // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x + if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) + { + if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) + TabBarQueueChangeTabOrder(tab_bar, tab, -1); + } + else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) + { + if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) + TabBarQueueChangeTabOrder(tab_bar, tab, +1); + } + } + } + +#if 0 + if (hovered && g.HoveredIdNotActiveTimer > 0.50f && bb.GetWidth() < tab->WidthContents) + { + // Enlarge tab display when hovering + bb.Max.x = bb.Min.x + (float)(int)ImLerp(bb.GetWidth(), tab->WidthContents, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)); + display_draw_list = GetForegroundDrawList(window); + TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive)); + } +#endif + + // Render tab shape + ImDrawList* display_draw_list = window->DrawList; + const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabUnfocused)); + TabItemBackground(display_draw_list, bb, flags, tab_col); + RenderNavHighlight(bb, id); + + // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. + const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1))) + tab_bar->NextSelectedTabId = id; + + if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) + flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; + + // Render tab label, process close button + const ImGuiID close_button_id = p_open ? window->GetID((void*)((intptr_t)id + 1)) : 0; + bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id); + if (just_closed && p_open != NULL) + { + *p_open = false; + TabBarCloseTab(tab_bar, tab); + } + + // Restore main window position so user can draw there + if (want_clip_rect) + PopClipRect(); + window->DC.CursorPos = backup_main_cursor_pos; + + // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer) + // We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar (which g.HoveredId ignores) + if (g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > 0.50f && IsItemHovered()) + if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip)) + SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); + + return tab_contents_visible; +} + +// [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed. +// To use it to need to call the function SetTabItemClosed() after BeginTabBar() and before any call to BeginTabItem() +void ImGui::SetTabItemClosed(const char* label) +{ + ImGuiContext& g = *GImGui; + bool is_within_manual_tab_bar = g.CurrentTabBar && !(g.CurrentTabBar->Flags & ImGuiTabBarFlags_DockNode); + if (is_within_manual_tab_bar) + { + ImGuiTabBar* tab_bar = g.CurrentTabBar; + IM_ASSERT(tab_bar->WantLayout); // Needs to be called AFTER BeginTabBar() and BEFORE the first call to BeginTabItem() + ImGuiID tab_id = TabBarCalcTabID(tab_bar, label); + TabBarRemoveTab(tab_bar, tab_id); + } +} + +ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button) +{ + ImGuiContext& g = *GImGui; + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f); + if (has_close_button) + size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle. + else + size.x += g.Style.FramePadding.x + 1.0f; + return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y); +} + +void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col) +{ + // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it. + ImGuiContext& g = *GImGui; + const float width = bb.GetWidth(); + IM_UNUSED(flags); + IM_ASSERT(width > 0.0f); + const float rounding = ImMax(0.0f, ImMin(g.Style.TabRounding, width * 0.5f - 1.0f)); + const float y1 = bb.Min.y + 1.0f; + const float y2 = bb.Max.y - 1.0f; + draw_list->PathLineTo(ImVec2(bb.Min.x, y2)); + draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9); + draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12); + draw_list->PathLineTo(ImVec2(bb.Max.x, y2)); + draw_list->PathFillConvex(col); + if (g.Style.TabBorderSize > 0.0f) + { + draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2)); + draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9); + draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12); + draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2)); + draw_list->PathStroke(GetColorU32(ImGuiCol_Border), false, g.Style.TabBorderSize); + } +} + +// Render text label (with custom clipping) + Unsaved Document marker + Close Button logic +// We tend to lock style.FramePadding for a given tab-bar, hence the 'frame_padding' parameter. +bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id) +{ + ImGuiContext& g = *GImGui; + ImVec2 label_size = CalcTextSize(label, NULL, true); + if (bb.GetWidth() <= 1.0f) + return false; + + // Render text label (with clipping + alpha gradient) + unsaved marker + const char* TAB_UNSAVED_MARKER = "*"; + ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y); + if (flags & ImGuiTabItemFlags_UnsavedDocument) + { + text_pixel_clip_bb.Max.x -= CalcTextSize(TAB_UNSAVED_MARKER, NULL, false).x; + ImVec2 unsaved_marker_pos(ImMin(bb.Min.x + frame_padding.x + label_size.x + 2, text_pixel_clip_bb.Max.x), bb.Min.y + frame_padding.y + (float)(int)(-g.FontSize * 0.25f)); + RenderTextClippedEx(draw_list, unsaved_marker_pos, bb.Max - frame_padding, TAB_UNSAVED_MARKER, NULL, NULL); + } + ImRect text_ellipsis_clip_bb = text_pixel_clip_bb; + + // Close Button + // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap() + // 'hovered' will be true when hovering the Tab but NOT when hovering the close button + // 'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button + // 'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false + bool close_button_pressed = false; + bool close_button_visible = false; + if (close_button_id != 0) + if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == close_button_id) + close_button_visible = true; + if (close_button_visible) + { + ImGuiItemHoveredDataBackup last_item_backup; + const float close_button_sz = g.FontSize; + PushStyleVar(ImGuiStyleVar_FramePadding, frame_padding); + if (CloseButton(close_button_id, ImVec2(bb.Max.x - frame_padding.x * 2.0f - close_button_sz, bb.Min.y))) + close_button_pressed = true; + PopStyleVar(); + last_item_backup.Restore(); + + // Close with middle mouse button + if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) + close_button_pressed = true; + + text_pixel_clip_bb.Max.x -= close_button_sz; + } + + float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f; + RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size); + + return close_button_pressed; +} diff --git a/external/imgui/stb_rect_pack.h b/external/imgui/imstb_rectpack.h similarity index 97% rename from external/imgui/stb_rect_pack.h rename to external/imgui/imstb_rectpack.h index 2b07dcc..23f922a 100644 --- a/external/imgui/stb_rect_pack.h +++ b/external/imgui/imstb_rectpack.h @@ -1,4 +1,10 @@ -// stb_rect_pack.h - v0.11 - public domain - rectangle packing +// [DEAR IMGUI] +// This is a slightly modified version of stb_rect_pack.h 0.99. +// Those changes would need to be pushed into nothings/stb: +// - Added STBRP__CDECL +// Grep for [DEAR IMGUI] to find the changes. + +// stb_rect_pack.h - v0.99 - public domain - rectangle packing // Sean Barrett 2014 // // Useful for e.g. packing rectangular textures into an atlas. @@ -34,6 +40,7 @@ // // Version history: // +// 0.99 (2019-02-07) warning fixes // 0.11 (2017-03-03) return packing success/fail result // 0.10 (2016-10-25) remove cast-away-const to avoid warnings // 0.09 (2016-08-27) fix compiler warnings @@ -204,6 +211,7 @@ struct stbrp_context #define STBRP_ASSERT assert #endif +// [DEAR IMGUI] Added STBRP__CDECL #ifdef _MSC_VER #define STBRP__NOTUSED(v) (void)(v) #define STBRP__CDECL __cdecl @@ -512,6 +520,7 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i return res; } +// [DEAR IMGUI] Added STBRP__CDECL static int STBRP__CDECL rect_height_compare(const void *a, const void *b) { const stbrp_rect *p = (const stbrp_rect *) a; @@ -523,6 +532,7 @@ static int STBRP__CDECL rect_height_compare(const void *a, const void *b) return (p->w > q->w) ? -1 : (p->w < q->w); } +// [DEAR IMGUI] Added STBRP__CDECL static int STBRP__CDECL rect_original_order(const void *a, const void *b) { const stbrp_rect *p = (const stbrp_rect *) a; @@ -543,9 +553,6 @@ STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int nu // we use the 'was_packed' field internally to allow sorting/unsorting for (i=0; i < num_rects; ++i) { rects[i].was_packed = i; - #ifndef STBRP_LARGE_RECTS - STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); - #endif } // sort according to heuristic diff --git a/external/imgui/stb_textedit.h b/external/imgui/imstb_textedit.h similarity index 87% rename from external/imgui/stb_textedit.h rename to external/imgui/imstb_textedit.h index 4b731a0..d7fcbd6 100644 --- a/external/imgui/stb_textedit.h +++ b/external/imgui/imstb_textedit.h @@ -1,10 +1,10 @@ -// [ImGui] this is a slightly modified version of stb_truetype.h 1.9. Those changes would need to be pushed into nothings/sb -// [ImGui] - fixed linestart handler when over last character of multi-line buffer + simplified existing code (#588, #815) -// [ImGui] - fixed a state corruption/crash bug in stb_text_redo and stb_textedit_discard_redo (#715) -// [ImGui] - fixed a crash bug in stb_textedit_discard_redo (#681) -// [ImGui] - fixed some minor warnings +// [DEAR IMGUI] +// This is a slightly modified version of stb_textedit.h 1.13. +// Those changes would need to be pushed into nothings/stb: +// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) +// Grep for [DEAR IMGUI] to find the changes. -// stb_textedit.h - v1.9 - public domain - Sean Barrett +// stb_textedit.h - v1.13 - public domain - Sean Barrett // Development of this library was sponsored by RAD Game Tools // // This C header file implements the guts of a multi-line text-editing @@ -23,9 +23,7 @@ // // LICENSE // -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. +// See end of file for license information. // // // DEPENDENCIES @@ -37,6 +35,10 @@ // // VERSION HISTORY // +// 1.13 (2019-02-07) fix bug in undo size management +// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash +// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield +// 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual // 1.9 (2016-08-27) customizable move-by-word // 1.8 (2016-04-02) better keyboard handling when mouse button is down // 1.7 (2015-09-13) change y range handling in case baseline is non-0 @@ -55,12 +57,13 @@ // // Ulf Winklemann: move-by-word in 1.1 // Fabian Giesen: secondary key inputs in 1.5 -// Martins Mozeiko: STB_TEXTEDIT_memmove +// Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6 // // Bugfixes: // Scott Graham // Daniel Keller // Omar Cornut +// Dan Thompson // // USAGE // @@ -90,8 +93,8 @@ // moderate sizes. The undo system does no memory allocations, so // it grows STB_TexteditState by the worst-case storage which is (in bytes): // -// [4 + sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT -// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT +// [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT +// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT // // // Implementation mode: @@ -114,7 +117,7 @@ // Symbols that must be the same in header-file and implementation mode: // // STB_TEXTEDIT_CHARTYPE the character type -// STB_TEXTEDIT_POSITIONTYPE small type that a valid cursor position +// STB_TEXTEDIT_POSITIONTYPE small type that is a valid cursor position // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer // @@ -203,7 +206,7 @@ // void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) // int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) // int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) -// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) +// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key) // // Each of these functions potentially updates the string and updates the // state. @@ -237,7 +240,9 @@ // inputs, set a high bit to distinguish the two; then you can define the // various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit // set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is -// clear. +// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to +// anything other type you wante before including. +// // // When rendering, you can read the cursor position and selection state from // the STB_TexteditState. @@ -297,9 +302,9 @@ typedef struct { // private data STB_TEXTEDIT_POSITIONTYPE where; - short insert_length; - short delete_length; - short char_storage; + STB_TEXTEDIT_POSITIONTYPE insert_length; + STB_TEXTEDIT_POSITIONTYPE delete_length; + int char_storage; } StbUndoRecord; typedef struct @@ -308,7 +313,7 @@ typedef struct StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT]; STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; short undo_point, redo_point; - short undo_char_point, redo_char_point; + int undo_char_point, redo_char_point; } StbUndoState; typedef struct @@ -450,6 +455,15 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) // API click: on mouse down, move the cursor to the clicked location, and reset the selection static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) { + // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse + // goes off the top or bottom of the text + if( state->single_line ) + { + StbTexteditRow r; + STB_TEXTEDIT_LAYOUTROW(&r, str, 0); + y = r.ymin; + } + state->cursor = stb_text_locate_coord(str, x, y); state->select_start = state->cursor; state->select_end = state->cursor; @@ -459,9 +473,21 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) { - int p = stb_text_locate_coord(str, x, y); + int p = 0; + + // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse + // goes off the top or bottom of the text + if( state->single_line ) + { + StbTexteditRow r; + STB_TEXTEDIT_LAYOUTROW(&r, str, 0); + y = r.ymin; + } + if (state->select_start == state->select_end) state->select_start = state->cursor; + + p = stb_text_locate_coord(str, x, y); state->cursor = state->select_end = p; } @@ -539,7 +565,6 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s // now scan to find xpos find->x = r.x0; - i = 0; for (i=0; first+i < n; ++i) find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); } @@ -669,7 +694,7 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) { if (STB_TEXT_HAS_SELECTION(state)) { - stb_textedit_delete_selection(str,state); // implicity clamps + stb_textedit_delete_selection(str,state); // implicitly clamps state->has_preferred_x = 0; return 1; } @@ -677,9 +702,8 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) } // API paste: replace existing selection with passed-in text -static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) +static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) { - STB_TEXTEDIT_CHARTYPE *text = (STB_TEXTEDIT_CHARTYPE *) ctext; // if there's a selection, the paste should delete it stb_textedit_clamp(str, state); stb_textedit_delete_selection(str,state); @@ -696,8 +720,12 @@ static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state return 0; } +#ifndef STB_TEXTEDIT_KEYTYPE +#define STB_TEXTEDIT_KEYTYPE int +#endif + // API key: process a keyboard input -static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) +static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) { retry: switch (key) { @@ -718,7 +746,7 @@ retry: state->has_preferred_x = 0; } } else { - stb_textedit_delete_selection(str,state); // implicity clamps + stb_textedit_delete_selection(str,state); // implicitly clamps if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { stb_text_makeundo_insert(state, state->cursor, 1); ++state->cursor; @@ -1074,14 +1102,14 @@ static void stb_textedit_discard_undo(StbUndoState *state) if (state->undo_rec[0].char_storage >= 0) { int n = state->undo_rec[0].insert_length, i; // delete n characters from all other records - state->undo_char_point = state->undo_char_point - (short) n; // vsnet05 - STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) ((size_t)state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); + state->undo_char_point -= n; + STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); for (i=0; i < state->undo_point; ++i) if (state->undo_rec[i].char_storage >= 0) - state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short) n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it + state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it } --state->undo_point; - STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) ((size_t)state->undo_point*sizeof(state->undo_rec[0]))); + STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); } } @@ -1097,14 +1125,24 @@ static void stb_textedit_discard_redo(StbUndoState *state) // if the k'th undo state has characters, clean those up if (state->undo_rec[k].char_storage >= 0) { int n = state->undo_rec[k].insert_length, i; - // delete n characters from all other records - state->redo_char_point = state->redo_char_point + (short) n; // vsnet05 - STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((size_t)(STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); + // move the remaining redo character data to the end of the buffer + state->redo_char_point += n; + STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); + // adjust the position of all the other records to account for above memmove for (i=state->redo_point; i < k; ++i) if (state->undo_rec[i].char_storage >= 0) - state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short) n; // vsnet05 + state->undo_rec[i].char_storage += n; } - STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point, state->undo_rec + state->redo_point-1, (size_t) ((size_t)(STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); + // now move all the redo records towards the end of the buffer; the first one is at 'redo_point' + // {DEAR IMGUI] + size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0])); + const char* buf_begin = (char*)state->undo_rec; (void)buf_begin; + const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end; + IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin); + IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end); + STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size); + + // now move redo_point to point to the new one ++state->redo_point; } } @@ -1140,15 +1178,15 @@ static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, return NULL; r->where = pos; - r->insert_length = (short) insert_len; - r->delete_length = (short) delete_len; + r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len; + r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len; if (insert_len == 0) { r->char_storage = -1; return NULL; } else { r->char_storage = state->undo_char_point; - state->undo_char_point = state->undo_char_point + (short) insert_len; + state->undo_char_point += insert_len; return &state->undo_char[r->char_storage]; } } @@ -1188,16 +1226,16 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) // there's definitely room to store the characters eventually while (s->undo_char_point + u.delete_length > s->redo_char_point) { - // there's currently not enough room, so discard a redo record - stb_textedit_discard_redo(s); // should never happen: if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) return; + // there's currently not enough room, so discard a redo record + stb_textedit_discard_redo(s); } r = &s->undo_rec[s->redo_point-1]; r->char_storage = s->redo_char_point - u.delete_length; - s->redo_char_point = s->redo_char_point - (short) u.delete_length; + s->redo_char_point = s->redo_char_point - u.delete_length; // now save the characters for (i=0; i < u.delete_length; ++i) @@ -1319,4 +1357,61 @@ static void stb_textedit_initialize_state(STB_TexteditState *state, int is_singl { stb_textedit_clear_state(state, is_single_line); } + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) +{ + return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + #endif//STB_TEXTEDIT_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/external/imgui/stb_truetype.h b/external/imgui/imstb_truetype.h similarity index 97% rename from external/imgui/stb_truetype.h rename to external/imgui/imstb_truetype.h index f65deb5..c1cdb18 100644 --- a/external/imgui/stb_truetype.h +++ b/external/imgui/imstb_truetype.h @@ -1,4 +1,9 @@ -// stb_truetype.h - v1.19 - public domain +// [DEAR IMGUI] +// This is a slightly modified version of stb_truetype.h 1.20. +// Mostly fixing for compiler and static analyzer warnings. +// Grep for [DEAR IMGUI] to find the changes. + +// stb_truetype.h - v1.20 - public domain // authored from 2009-2016 by Sean Barrett / RAD Game Tools // // This library processes TrueType files: @@ -49,6 +54,7 @@ // // VERSION HISTORY // +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() // 1.19 (2018-02-11) GPOS kerning, STBTT_fmod // 1.18 (2018-01-29) add missing function // 1.17 (2017-07-23) make more arguments const; doc fix @@ -75,7 +81,7 @@ // // USAGE // -// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// Include this file in whatever places need to refer to it. In ONE C/C++ // file, write: // #define STB_TRUETYPE_IMPLEMENTATION // before the #include of this file. This expands out the actual @@ -247,8 +253,8 @@ // Documentation & header file 520 LOC \___ 660 LOC documentation // Sample code 140 LOC / // Truetype parsing 620 LOC ---- 620 LOC TrueType -// Software rasterization 240 LOC \ . -// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation +// Software rasterization 240 LOC \. +// Curve tessellation 120 LOC \__ 550 LOC Bitmap creation // Bitmap management 100 LOC / // Baked bitmap interface 70 LOC / // Font name matching & access 150 LOC ---- 150 @@ -556,6 +562,8 @@ STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int p // // It's inefficient; you might want to c&p it and optimize it. +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); +// Query the font vertical metrics without having to create a font first. ////////////////////////////////////////////////////////////////////////////// @@ -641,6 +649,12 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h // To use with PackFontRangesGather etc., you must set it before calls // call to PackFontRangesGatherRects. +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); +// If skip != 0, this tells stb_truetype to skip any codepoints for which +// there is no corresponding glyph. If skip=0, which is the default, then +// codepoints without a glyph recived the font's "missing character" glyph, +// typically an empty box by convention. + STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above int char_index, // character to display float *xpos, float *ypos, // pointers to current position in screen pixel space @@ -669,6 +683,7 @@ struct stbtt_pack_context { int height; int stride_in_bytes; int padding; + int skip_missing; unsigned int h_oversample, v_oversample; unsigned char *pixels; void *nodes; @@ -694,7 +709,7 @@ STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); // file will only define one font and it always be at offset 0, so it will // return '0' for index 0, and -1 for all other indices. -// The following structure is defined publically so you can declare one on +// The following structure is defined publicly so you can declare one on // the stack or as a global or etc, but you should treat it as opaque. struct stbtt_fontinfo { @@ -733,6 +748,7 @@ STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codep // and you want a speed-up, call this function with the character you're // going to process, then use glyph-based functions instead of the // codepoint-based functions. +// Returns 0 if the character codepoint is not defined in the font. ////////////////////////////////////////////////////////////////////////////// @@ -820,7 +836,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s // returns # of vertices and fills *vertices with the pointer to them // these are expressed in "unscaled" coordinates // -// The shape is a series of countours. Each one starts with +// The shape is a series of contours. Each one starts with // a STBTT_moveto, then consists of a series of mixed // STBTT_lineto and STBTT_curveto segments. A lineto // draws a line from previous endpoint to its x,y; a curveto @@ -916,7 +932,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); // These functions compute a discretized SDF field for a single character, suitable for storing // in a single-channel texture, sampling with bilinear filtering, and testing against -// larger than some threshhold to produce scalable fonts. +// larger than some threshold to produce scalable fonts. // info -- the font // scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap // glyph/codepoint -- the character to generate the SDF for @@ -1825,7 +1841,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s if (comp_verts) STBTT_free(comp_verts, info->userdata); return 0; } - if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); //-V595 STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); if (vertices) STBTT_free(vertices, info->userdata); vertices = tmp; @@ -2196,7 +2212,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st } break; default: - if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) + if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) //-V560 return STBTT__CSERR("reserved operator"); // push immediate @@ -2368,7 +2384,8 @@ static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); - classDefTable = classDef1ValueArray + 2 * glyphCount; + // [DEAR IMGUI] Commented to fix static analyzer warning + //classDefTable = classDef1ValueArray + 2 * glyphCount; } break; case 2: { @@ -2392,7 +2409,8 @@ static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) return (stbtt_int32)ttUSHORT(classRangeRecord + 4); } - classDefTable = classRangeRecords + 6 * classRangeCount; + // [DEAR IMGUI] Commented to fix static analyzer warning + //classDefTable = classRangeRecords + 6 * classRangeCount; } break; default: { @@ -3024,6 +3042,8 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, dx = -dx; dy = -dy; t = x0, x0 = xb, xb = t; + // [DEAR IMGUI] Fix static analyzer warning + (void)dx; // [ImGui: fix static analyzer warning] } x1 = (int) x_top; @@ -3161,7 +3181,13 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, if (e->y0 != e->y1) { stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); if (z != NULL) { - STBTT_assert(z->ey >= scan_y_top); + if (j == 0 && off_y != 0) { + if (z->ey < scan_y_top) { + // this can happen due to subpixel positioning and some kind of fp rounding error i think + z->ey = scan_y_top; + } + } + STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds // insert at front z->next = active; active = z; @@ -3230,7 +3256,7 @@ static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) { - /* threshhold for transitioning to insertion sort */ + /* threshold for transitioning to insertion sort */ while (n > 12) { stbtt__edge t; int c01,c12,c,m,i,j; @@ -3365,7 +3391,7 @@ static void stbtt__add_point(stbtt__point *points, int n, float x, float y) points[n].y = y; } -// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) { // midpoint @@ -3790,6 +3816,7 @@ STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, in spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; spc->h_oversample = 1; spc->v_oversample = 1; + spc->skip_missing = 0; stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); @@ -3815,6 +3842,11 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h spc->v_oversample = v_oversample; } +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) +{ + spc->skip_missing = skip; +} + #define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) @@ -3968,13 +4000,17 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb int x0,y0,x1,y1; int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; int glyph = stbtt_FindGlyphIndex(info, codepoint); - stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, - scale * spc->h_oversample, - scale * spc->v_oversample, - 0,0, - &x0,&y0,&x1,&y1); - rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); - rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0 && spc->skip_missing) { + rects[k].w = rects[k].h = 0; + } else { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + } ++k; } } @@ -4027,7 +4063,7 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const sub_y = stbtt__oversample_shift(spc->v_oversample); for (j=0; j < ranges[i].num_chars; ++j) { stbrp_rect *r = &rects[k]; - if (r->was_packed) { + if (r->was_packed && r->w != 0 && r->h != 0) { stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; int advance, lsb, x0,y0,x1,y1; int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; @@ -4141,6 +4177,19 @@ STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char * return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); } +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) +{ + int i_ascent, i_descent, i_lineGap; + float scale; + stbtt_fontinfo info; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); + *ascent = (float) i_ascent * scale; + *descent = (float) i_descent * scale; + *lineGap = (float) i_lineGap * scale; +} + STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) { float ipw = 1.0f / pw, iph = 1.0f / ph; @@ -4253,7 +4302,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex int winding = 0; orig[0] = x; - orig[1] = y; + //orig[1] = y; // [DEAR IMGUI] commmented double assignment // make sure y never passes through a vertex of the shape y_frac = (float) STBTT_fmod(y, 1.0f); diff --git a/external/rapidjson/allocators.h b/external/rapidjson/allocators.h index 655f4a3..cc67c89 100644 --- a/external/rapidjson/allocators.h +++ b/external/rapidjson/allocators.h @@ -52,6 +52,19 @@ concept Allocator { \endcode */ + +/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User-defined kDefaultChunkCapacity definition. + + User can define this as any \c size that is a power of 2. +*/ + +#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY +#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) +#endif + + /////////////////////////////////////////////////////////////////////////////// // CrtAllocator @@ -248,7 +261,7 @@ private: return false; } - static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. + static const int kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity. //! Chunk header for perpending to each chunk. /*! Chunks are stored as a singly linked list. diff --git a/external/rapidjson/cursorstreamwrapper.h b/external/rapidjson/cursorstreamwrapper.h new file mode 100644 index 0000000..52c11a7 --- /dev/null +++ b/external/rapidjson/cursorstreamwrapper.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_ +#define RAPIDJSON_CURSORSTREAMWRAPPER_H_ + +#include "stream.h" + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + + +//! Cursor stream wrapper for counting line and column number if error exists. +/*! + \tparam InputStream Any stream that implements Stream Concept +*/ +template > +class CursorStreamWrapper : public GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + + CursorStreamWrapper(InputStream& is): + GenericStreamWrapper(is), line_(1), col_(0) {} + + // counting line and column number + Ch Take() { + Ch ch = this->is_.Take(); + if(ch == '\n') { + line_ ++; + col_ = 0; + } else { + col_ ++; + } + return ch; + } + + //! Get the error line number, if error exists. + size_t GetLine() const { return line_; } + //! Get the error column number, if error exists. + size_t GetColumn() const { return col_; } + +private: + size_t line_; //!< Current Line + size_t col_; //!< Current Column +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ diff --git a/external/rapidjson/document.h b/external/rapidjson/document.h index 3873b99..9783fe4 100644 --- a/external/rapidjson/document.h +++ b/external/rapidjson/document.h @@ -26,34 +26,21 @@ #include RAPIDJSON_DIAG_PUSH -#ifdef _MSC_VER -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data -#ifdef _MINWINDEF_ // see: http://stackoverflow.com/questions/22744262/cant-call-stdmax-because-minwindef-h-defines-max -#ifndef NOMINMAX -#pragma push_macro("min") -#pragma push_macro("max") -#undef min -#undef max -#endif -#endif -#endif - #ifdef __clang__ RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data #endif #ifdef __GNUC__ RAPIDJSON_DIAG_OFF(effc++) -#if __GNUC__ >= 6 -RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions -#endif #endif // __GNUC__ #ifndef RAPIDJSON_NOMEMBERITERATORCLASS -#include // std::iterator, std::random_access_iterator_tag +#include // std::random_access_iterator_tag #endif #if RAPIDJSON_HAS_CXX11_RVALUE_REFS @@ -79,6 +66,12 @@ template struct GenericMember { GenericValue name; //!< name of member (must be a string) GenericValue value; //!< value of member. + + // swap() for std::sort() and other potential use in STL. + friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { + a.name.Swap(b.name); + a.value.Swap(b.value); + } }; /////////////////////////////////////////////////////////////////////////////// @@ -106,16 +99,13 @@ struct GenericMember { \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator */ template -class GenericMemberIterator - : public std::iterator >::Type> { +class GenericMemberIterator { friend class GenericValue; template friend class GenericMemberIterator; typedef GenericMember PlainType; typedef typename internal::MaybeAddConst::Type ValueType; - typedef std::iterator BaseType; public: //! Iterator type itself @@ -125,12 +115,21 @@ public: //! Non-constant iterator type typedef GenericMemberIterator NonConstIterator; + /** \name std::iterator_traits support */ + //@{ + typedef ValueType value_type; + typedef ValueType * pointer; + typedef ValueType & reference; + typedef std::ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + //@} + //! Pointer to (const) GenericMember - typedef typename BaseType::pointer Pointer; + typedef pointer Pointer; //! Reference to (const) GenericMember - typedef typename BaseType::reference Reference; + typedef reference Reference; //! Signed integer type (e.g. \c ptrdiff_t) - typedef typename BaseType::difference_type DifferenceType; + typedef difference_type DifferenceType; //! Default constructor (singular value) /*! Creates an iterator pointing to no element. @@ -206,17 +205,17 @@ private: // class-based member iterator implementation disabled, use plain pointers template -struct GenericMemberIterator; +class GenericMemberIterator; //! non-const GenericMemberIterator template -struct GenericMemberIterator { +class GenericMemberIterator { //! use plain pointer as iterator type typedef GenericMember* Iterator; }; //! const GenericMemberIterator template -struct GenericMemberIterator { +class GenericMemberIterator { //! use plain const pointer as iterator type typedef const GenericMember* Iterator; }; @@ -308,7 +307,7 @@ struct GenericStringRef { */ #endif explicit GenericStringRef(const CharType* str) - : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); } + : s(str), length(NotNullStrLen(str)) {} //! Create constant string reference from pointer and length #ifndef __clang__ // -Wdocumentation @@ -320,7 +319,7 @@ struct GenericStringRef { */ #endif GenericStringRef(const CharType* str, SizeType len) - : s(str), length(len) { RAPIDJSON_ASSERT(s != 0); } + : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} @@ -331,6 +330,14 @@ struct GenericStringRef { const SizeType length; //!< length of the string (excluding the trailing NULL terminator) private: + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + //! Disallow construction from non-const array template GenericStringRef(CharType (&str)[N]) /* = delete */; @@ -338,6 +345,9 @@ private: GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; }; +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + //! Mark a character pointer as constant string /*! Mark a plain character pointer as a "string literal". This function can be used to avoid copying a character string to be referenced as a @@ -352,7 +362,7 @@ private: */ template inline GenericStringRef StringRef(const CharType* str) { - return GenericStringRef(str, internal::StrLen(str)); + return GenericStringRef(str); } //! Mark a character pointer as constant string @@ -442,6 +452,26 @@ struct TypeHelper { static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } }; +#ifdef _MSC_VER +RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static long Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned long Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; +#endif + template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsInt64(); } @@ -515,7 +545,7 @@ struct TypeHelper { static bool Is(const ValueType& v) { return v.IsObject(); } static ObjectType Get(ValueType& v) { return v.GetObject(); } static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } - static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } }; template @@ -598,11 +628,11 @@ public: \note Default content for number is zero. */ explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { - static const uint16_t defaultFlags[7] = { + static const uint16_t defaultFlags[] = { kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, kNumberAnyFlag }; - RAPIDJSON_ASSERT(type <= kNumberType); + RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType); data_.f.flags = defaultFlags[type]; // Use ShortString to store empty string. @@ -615,18 +645,19 @@ public: \tparam SourceAllocator allocator of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) \see CopyFrom() */ template - GenericValue(const GenericValue& rhs, Allocator& allocator) { + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { switch (rhs.GetType()) { case kObjectType: { SizeType count = rhs.data_.o.size; Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); const typename GenericValue::Member* rm = rhs.GetMembersPointer(); for (SizeType i = 0; i < count; i++) { - new (&lm[i].name) GenericValue(rm[i].name, allocator); - new (&lm[i].value) GenericValue(rm[i].value, allocator); + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); } data_.f.flags = kObjectFlag; data_.o.size = data_.o.capacity = count; @@ -638,14 +669,14 @@ public: GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); const GenericValue* re = rhs.GetElementsPointer(); for (SizeType i = 0; i < count; i++) - new (&le[i]) GenericValue(re[i], allocator); + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); data_.f.flags = kArrayFlag; data_.a.size = data_.a.capacity = count; SetElementsPointer(le); } break; case kStringType: - if (rhs.data_.f.flags == kConstStringFlag) { + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { data_.f.flags = rhs.data_.f.flags; data_ = *reinterpret_cast(&rhs.data_); } @@ -803,9 +834,10 @@ public: /*! \param rhs Source of the assignment. It will become a null value after assignment. */ GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { - RAPIDJSON_ASSERT(this != &rhs); - this->~GenericValue(); - RawAssign(rhs); + if (RAPIDJSON_LIKELY(this != &rhs)) { + this->~GenericValue(); + RawAssign(rhs); + } return *this; } @@ -850,12 +882,13 @@ public: \tparam SourceAllocator Allocator type of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator to use for copying + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) */ template - GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); this->~GenericValue(); - new (this) GenericValue(rhs, allocator); + new (this) GenericValue(rhs, allocator, copyConstStrings); return *this; } @@ -896,7 +929,7 @@ public: //! Equal-to operator /*! \note If an object contains duplicated named member, comparing equality with any object is always \c false. - \note Linear time complexity (number of all values in the subtree and total lengths of all strings). + \note Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings). */ template bool operator==(const GenericValue& rhs) const { @@ -1005,14 +1038,14 @@ public: uint64_t u = GetUint64(); volatile double d = static_cast(u); return (d >= 0.0) - && (d < static_cast(std::numeric_limits::max())) + && (d < static_cast((std::numeric_limits::max)())) && (u == static_cast(d)); } if (IsInt64()) { int64_t i = GetInt64(); volatile double d = static_cast(i); - return (d >= static_cast(std::numeric_limits::min())) - && (d < static_cast(std::numeric_limits::max())) + return (d >= static_cast((std::numeric_limits::min)())) + && (d < static_cast((std::numeric_limits::max)())) && (i == static_cast(d)); } return true; // double, int, uint are always lossless @@ -1029,8 +1062,8 @@ public: bool IsLosslessFloat() const { if (!IsNumber()) return false; double a = GetDouble(); - if (a < static_cast(-std::numeric_limits::max()) - || a > static_cast(std::numeric_limits::max())) + if (a < static_cast(-(std::numeric_limits::max)()) + || a > static_cast((std::numeric_limits::max)())) return false; double b = static_cast(static_cast(a)); return a >= b && a <= b; // Prevent -Wfloat-equal @@ -1065,6 +1098,9 @@ public: //! Get the number of members in the object. SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + //! Get the capacity of object. + SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; } + //! Check whether the object is empty. bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } @@ -1133,6 +1169,21 @@ public: /*! \pre IsObject() == true */ MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + //! Request the object to have enough capacity to store members. + /*! \param newCapacity The capacity that the object at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsObject()); + if (newCapacity > data_.o.capacity) { + SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), data_.o.capacity * sizeof(Member), newCapacity * sizeof(Member)))); + data_.o.capacity = newCapacity; + } + return *this; + } + //! Check whether a member exists in the object. /*! \param name Member name to be searched. @@ -1238,17 +1289,8 @@ public: RAPIDJSON_ASSERT(name.IsString()); ObjectData& o = data_.o; - if (o.size >= o.capacity) { - if (o.capacity == 0) { - o.capacity = kDefaultObjectCapacity; - SetMembersPointer(reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member)))); - } - else { - SizeType oldCapacity = o.capacity; - o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 - SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member)))); - } - } + if (o.size >= o.capacity) + MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator); Member* members = GetMembersPointer(); members[o.size].name.RawAssign(name); members[o.size].value.RawAssign(value); @@ -1475,7 +1517,7 @@ public: MemberIterator pos = MemberBegin() + (first - MemberBegin()); for (MemberIterator itr = pos; itr != last; ++itr) itr->~Member(); - std::memmove(&*pos, &*last, static_cast(MemberEnd() - last) * sizeof(Member)); + std::memmove(static_cast(&*pos), &*last, static_cast(MemberEnd() - last) * sizeof(Member)); data_.o.size -= static_cast(last - first); return pos; } @@ -1678,8 +1720,8 @@ public: RAPIDJSON_ASSERT(last <= End()); ValueIterator pos = Begin() + (first - Begin()); for (ValueIterator itr = pos; itr != last; ++itr) - itr->~GenericValue(); - std::memmove(pos, last, static_cast(End() - last) * sizeof(GenericValue)); + itr->~GenericValue(); + std::memmove(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); data_.a.size -= static_cast(last - first); return pos; } @@ -1760,7 +1802,7 @@ public: \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } //! Set this value as a string by copying from source string. /*! \param s source string. @@ -1768,7 +1810,15 @@ public: \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } #if RAPIDJSON_HAS_STDSTRING //! Set this value as a string by copying from source string. @@ -1778,7 +1828,7 @@ public: \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ - GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); } + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } #endif //@} @@ -1986,7 +2036,7 @@ private: if (count) { GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); SetElementsPointer(e); - std::memcpy(e, values, count * sizeof(GenericValue)); + std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); } else SetElementsPointer(0); @@ -1999,7 +2049,7 @@ private: if (count) { Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); SetMembersPointer(m); - std::memcpy(m, members, count * sizeof(Member)); + std::memcpy(static_cast(m), members, count * sizeof(Member)); } else SetMembersPointer(0); @@ -2162,6 +2212,10 @@ public: return *this; } + // Allow Swap with ValueType. + // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. + using ValueType::Swap; + //! free-standing swap function helper /*! Helper function to enable support for common swap implementation pattern based on \c std::swap: @@ -2293,7 +2347,7 @@ public: template GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); - MemoryStream ms(static_cast(str), length * sizeof(typename SourceEncoding::Ch)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); EncodedInputStream is(ms); ParseStream(is); return *this; @@ -2330,7 +2384,7 @@ public: //!@name Handling parse errors //!@{ - //! Whether a parse error has occured in the last parsing. + //! Whether a parse error has occurred in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } //! Get the \ref ParseErrorCode of last parsing. @@ -2531,6 +2585,7 @@ public: ~GenericObject() {} SizeType MemberCount() const { return value_.MemberCount(); } + SizeType MemberCapacity() const { return value_.MemberCapacity(); } bool ObjectEmpty() const { return value_.ObjectEmpty(); } template ValueType& operator[](T* name) const { return value_[name]; } template ValueType& operator[](const GenericValue& name) const { return value_[name]; } @@ -2539,6 +2594,7 @@ public: #endif MemberIterator MemberBegin() const { return value_.MemberBegin(); } MemberIterator MemberEnd() const { return value_.MemberEnd(); } + GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; } bool HasMember(const Ch* name) const { return value_.HasMember(name); } #if RAPIDJSON_HAS_STDSTRING bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } @@ -2564,7 +2620,7 @@ public: GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - void RemoveAllMembers() { return value_.RemoveAllMembers(); } + void RemoveAllMembers() { value_.RemoveAllMembers(); } bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } #if RAPIDJSON_HAS_STDSTRING bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } @@ -2591,12 +2647,6 @@ private: }; RAPIDJSON_NAMESPACE_END -#ifdef _MINWINDEF_ // see: http://stackoverflow.com/questions/22744262/cant-call-stdmax-because-minwindef-h-defines-max -#ifndef NOMINMAX -#pragma pop_macro("min") -#pragma pop_macro("max") -#endif -#endif RAPIDJSON_DIAG_POP #endif // RAPIDJSON_DOCUMENT_H_ diff --git a/external/rapidjson/encodings.h b/external/rapidjson/encodings.h index ed7d44d..0b24467 100644 --- a/external/rapidjson/encodings.h +++ b/external/rapidjson/encodings.h @@ -17,7 +17,7 @@ #include "rapidjson.h" -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data RAPIDJSON_DIAG_OFF(4702) // unreachable code @@ -144,9 +144,9 @@ struct UTF8 { template static bool Decode(InputStream& is, unsigned* codepoint) { -#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) -#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) -#define TAIL() COPY(); TRANS(0x70) +#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) typename InputStream::Ch c = is.Take(); if (!(c & 0x80)) { *codepoint = static_cast(c); @@ -161,44 +161,44 @@ struct UTF8 { } bool result = true; switch (type) { - case 2: TAIL(); return result; - case 3: TAIL(); TAIL(); return result; - case 4: COPY(); TRANS(0x50); TAIL(); return result; - case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; - case 6: TAIL(); TAIL(); TAIL(); return result; - case 10: COPY(); TRANS(0x20); TAIL(); return result; - case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; default: return false; } -#undef COPY -#undef TRANS -#undef TAIL +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL } template static bool Validate(InputStream& is, OutputStream& os) { -#define COPY() os.Put(c = is.Take()) -#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) -#define TAIL() COPY(); TRANS(0x70) +#define RAPIDJSON_COPY() os.Put(c = is.Take()) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) Ch c; - COPY(); + RAPIDJSON_COPY(); if (!(c & 0x80)) return true; bool result = true; switch (GetRange(static_cast(c))) { - case 2: TAIL(); return result; - case 3: TAIL(); TAIL(); return result; - case 4: COPY(); TRANS(0x50); TAIL(); return result; - case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; - case 6: TAIL(); TAIL(); TAIL(); return result; - case 10: COPY(); TRANS(0x20); TAIL(); return result; - case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; default: return false; } -#undef COPY -#undef TRANS -#undef TAIL +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL } static unsigned char GetRange(unsigned char c) { @@ -384,7 +384,7 @@ struct UTF16BE : UTF16 { static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); unsigned c = static_cast(static_cast(is.Take())) << 8; - c |= static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())); return static_cast(c); } @@ -620,28 +620,28 @@ struct AutoUTF { #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x template - RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; (*f[os.GetType()])(os, codepoint); } template - RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; (*f[os.GetType()])(os, codepoint); } template - RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { typedef bool (*DecodeFunc)(InputStream&, unsigned*); static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; return (*f[is.GetType()])(is, codepoint); } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { typedef bool (*ValidateFunc)(InputStream&, OutputStream&); static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; return (*f[is.GetType()])(is, os); @@ -658,7 +658,7 @@ template struct Transcoder { //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; @@ -667,7 +667,7 @@ struct Transcoder { } template - RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; @@ -677,7 +677,7 @@ struct Transcoder { //! Validate one Unicode codepoint from an encoded stream. template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Transcode(is, os); // Since source/target encoding is different, must transcode. } }; @@ -690,26 +690,26 @@ inline void PutUnsafe(Stream& stream, typename Stream::Ch c); template struct Transcoder { template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template - RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Encoding::Validate(is, os); // source/target encoding are the same } }; RAPIDJSON_NAMESPACE_END -#if defined(__GNUC__) || defined(_MSC_VER) +#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__)) RAPIDJSON_DIAG_POP #endif diff --git a/external/rapidjson/error/error.h b/external/rapidjson/error/error.h index 95cb31a..9311d2f 100644 --- a/external/rapidjson/error/error.h +++ b/external/rapidjson/error/error.h @@ -104,6 +104,8 @@ enum ParseErrorCode { \see GenericReader::Parse, GenericDocument::Parse */ struct ParseResult { + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; public: //! Default constructor, no error. ParseResult() : code_(kParseErrorNone), offset_(0) {} @@ -115,8 +117,8 @@ public: //! Get the error offset, if \ref IsError(), 0 otherwise. size_t Offset() const { return offset_; } - //! Conversion to \c bool, returns \c true, iff !\ref IsError(). - operator bool() const { return !IsError(); } + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } //! Whether the result is an error. bool IsError() const { return code_ != kParseErrorNone; } @@ -124,6 +126,10 @@ public: bool operator==(ParseErrorCode code) const { return code_ == code; } friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } + //! Reset error code. void Clear() { Set(kParseErrorNone); } //! Update error code and offset. diff --git a/external/rapidjson/filereadstream.h b/external/rapidjson/filereadstream.h index b56ea13..6b34370 100644 --- a/external/rapidjson/filereadstream.h +++ b/external/rapidjson/filereadstream.h @@ -59,7 +59,7 @@ public: // For encoding detection only. const Ch* Peek4() const { - return (current_ + 4 <= bufferLast_) ? current_ : 0; + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; } private: @@ -68,7 +68,7 @@ private: ++current_; else if (!eof_) { count_ += readCount_; - readCount_ = fread(buffer_, 1, bufferSize_, fp_); + readCount_ = std::fread(buffer_, 1, bufferSize_, fp_); bufferLast_ = buffer_ + readCount_ - 1; current_ = buffer_; diff --git a/external/rapidjson/filewritestream.h b/external/rapidjson/filewritestream.h index 6378dd6..8b48fee 100644 --- a/external/rapidjson/filewritestream.h +++ b/external/rapidjson/filewritestream.h @@ -25,7 +25,7 @@ RAPIDJSON_DIAG_OFF(unreachable-code) RAPIDJSON_NAMESPACE_BEGIN -//! Wrapper of C file stream for input using fread(). +//! Wrapper of C file stream for output using fwrite(). /*! \note implements Stream concept */ @@ -62,7 +62,7 @@ public: void Flush() { if (current_ != buffer_) { - size_t result = fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + size_t result = std::fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); if (result < static_cast(current_ - buffer_)) { // failure deliberately ignored at this time // added to avoid warn_unused_result build errors diff --git a/external/rapidjson/internal/biginteger.h b/external/rapidjson/internal/biginteger.h index 9d3e88c..a31c8a8 100644 --- a/external/rapidjson/internal/biginteger.h +++ b/external/rapidjson/internal/biginteger.h @@ -17,7 +17,7 @@ #include "../rapidjson.h" -#if defined(_MSC_VER) && defined(_M_AMD64) +#if defined(_MSC_VER) && !__INTEL_COMPILER && defined(_M_AMD64) #include // for _umul128 #pragma intrinsic(_umul128) #endif @@ -133,7 +133,7 @@ public: RAPIDJSON_ASSERT(count_ + offset <= kCapacity); if (interShift == 0) { - std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); + std::memmove(digits_ + offset, digits_, count_ * sizeof(Type)); count_ += offset; } else { diff --git a/external/rapidjson/internal/diyfp.h b/external/rapidjson/internal/diyfp.h index c9fefdc..b6c2cf5 100644 --- a/external/rapidjson/internal/diyfp.h +++ b/external/rapidjson/internal/diyfp.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // This is a C++ header-only implementation of Grisu2 algorithm from the publication: @@ -20,8 +20,9 @@ #define RAPIDJSON_DIYFP_H_ #include "../rapidjson.h" +#include -#if defined(_MSC_VER) && defined(_M_AMD64) +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) #include #pragma intrinsic(_BitScanReverse64) #pragma intrinsic(_umul128) @@ -56,7 +57,7 @@ struct DiyFp { if (biased_e != 0) { f = significand + kDpHiddenBit; e = biased_e - kDpExponentBias; - } + } else { f = significand; e = kDpMinExponent + 1; @@ -99,6 +100,7 @@ struct DiyFp { } DiyFp Normalize() const { + RAPIDJSON_ASSERT(f != 0); // https://stackoverflow.com/a/26809183/291737 #if defined(_MSC_VER) && defined(_M_AMD64) unsigned long index; _BitScanReverse64(&index, f); @@ -141,7 +143,16 @@ struct DiyFp { double d; uint64_t u64; }u; - const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); + if (e < kDpDenormalExponent) { + // Underflow. + return 0.0; + } + if (e >= kDpMaxExponent) { + // Overflow. + return std::numeric_limits::infinity(); + } + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : static_cast(e + kDpExponentBias); u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); return u.d; @@ -220,9 +231,10 @@ inline DiyFp GetCachedPowerByIndex(size_t index) { 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066 }; + RAPIDJSON_ASSERT(index < 87); return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); } - + inline DiyFp GetCachedPower(int e, int* K) { //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; @@ -238,10 +250,11 @@ inline DiyFp GetCachedPower(int e, int* K) { } inline DiyFp GetCachedPower10(int exp, int *outExp) { - unsigned index = (static_cast(exp) + 348u) / 8u; - *outExp = -348 + static_cast(index) * 8; - return GetCachedPowerByIndex(index); - } + RAPIDJSON_ASSERT(exp >= -348); + unsigned index = static_cast(exp + 348) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); +} #ifdef __GNUC__ RAPIDJSON_DIAG_POP diff --git a/external/rapidjson/internal/itoa.h b/external/rapidjson/internal/itoa.h index 01a4e7e..9b1c45c 100644 --- a/external/rapidjson/internal/itoa.h +++ b/external/rapidjson/internal/itoa.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_ITOA_ @@ -37,12 +37,14 @@ inline const char* GetDigitsLut() { } inline char* u32toa(uint32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + const char* cDigitsLut = GetDigitsLut(); if (value < 10000) { const uint32_t d1 = (value / 100) << 1; const uint32_t d2 = (value % 100) << 1; - + if (value >= 1000) *buffer++ = cDigitsLut[d1]; if (value >= 100) @@ -55,13 +57,13 @@ inline char* u32toa(uint32_t value, char* buffer) { // value = bbbbcccc const uint32_t b = value / 10000; const uint32_t c = value % 10000; - + const uint32_t d1 = (b / 100) << 1; const uint32_t d2 = (b % 100) << 1; - + const uint32_t d3 = (c / 100) << 1; const uint32_t d4 = (c % 100) << 1; - + if (value >= 10000000) *buffer++ = cDigitsLut[d1]; if (value >= 1000000) @@ -69,7 +71,7 @@ inline char* u32toa(uint32_t value, char* buffer) { if (value >= 100000) *buffer++ = cDigitsLut[d2]; *buffer++ = cDigitsLut[d2 + 1]; - + *buffer++ = cDigitsLut[d3]; *buffer++ = cDigitsLut[d3 + 1]; *buffer++ = cDigitsLut[d4]; @@ -77,10 +79,10 @@ inline char* u32toa(uint32_t value, char* buffer) { } else { // value = aabbbbcccc in decimal - + const uint32_t a = value / 100000000; // 1 to 42 value %= 100000000; - + if (a >= 10) { const unsigned i = a << 1; *buffer++ = cDigitsLut[i]; @@ -91,13 +93,13 @@ inline char* u32toa(uint32_t value, char* buffer) { const uint32_t b = value / 10000; // 0 to 9999 const uint32_t c = value % 10000; // 0 to 9999 - + const uint32_t d1 = (b / 100) << 1; const uint32_t d2 = (b % 100) << 1; - + const uint32_t d3 = (c / 100) << 1; const uint32_t d4 = (c % 100) << 1; - + *buffer++ = cDigitsLut[d1]; *buffer++ = cDigitsLut[d1 + 1]; *buffer++ = cDigitsLut[d2]; @@ -111,6 +113,7 @@ inline char* u32toa(uint32_t value, char* buffer) { } inline char* i32toa(int32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); uint32_t u = static_cast(value); if (value < 0) { *buffer++ = '-'; @@ -121,6 +124,7 @@ inline char* i32toa(int32_t value, char* buffer) { } inline char* u64toa(uint64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); const char* cDigitsLut = GetDigitsLut(); const uint64_t kTen8 = 100000000; const uint64_t kTen9 = kTen8 * 10; @@ -131,13 +135,13 @@ inline char* u64toa(uint64_t value, char* buffer) { const uint64_t kTen14 = kTen8 * 1000000; const uint64_t kTen15 = kTen8 * 10000000; const uint64_t kTen16 = kTen8 * kTen8; - + if (value < kTen8) { uint32_t v = static_cast(value); if (v < 10000) { const uint32_t d1 = (v / 100) << 1; const uint32_t d2 = (v % 100) << 1; - + if (v >= 1000) *buffer++ = cDigitsLut[d1]; if (v >= 100) @@ -150,13 +154,13 @@ inline char* u64toa(uint64_t value, char* buffer) { // value = bbbbcccc const uint32_t b = v / 10000; const uint32_t c = v % 10000; - + const uint32_t d1 = (b / 100) << 1; const uint32_t d2 = (b % 100) << 1; - + const uint32_t d3 = (c / 100) << 1; const uint32_t d4 = (c % 100) << 1; - + if (value >= 10000000) *buffer++ = cDigitsLut[d1]; if (value >= 1000000) @@ -164,7 +168,7 @@ inline char* u64toa(uint64_t value, char* buffer) { if (value >= 100000) *buffer++ = cDigitsLut[d2]; *buffer++ = cDigitsLut[d2 + 1]; - + *buffer++ = cDigitsLut[d3]; *buffer++ = cDigitsLut[d3 + 1]; *buffer++ = cDigitsLut[d4]; @@ -174,22 +178,22 @@ inline char* u64toa(uint64_t value, char* buffer) { else if (value < kTen16) { const uint32_t v0 = static_cast(value / kTen8); const uint32_t v1 = static_cast(value % kTen8); - + const uint32_t b0 = v0 / 10000; const uint32_t c0 = v0 % 10000; - + const uint32_t d1 = (b0 / 100) << 1; const uint32_t d2 = (b0 % 100) << 1; - + const uint32_t d3 = (c0 / 100) << 1; const uint32_t d4 = (c0 % 100) << 1; const uint32_t b1 = v1 / 10000; const uint32_t c1 = v1 % 10000; - + const uint32_t d5 = (b1 / 100) << 1; const uint32_t d6 = (b1 % 100) << 1; - + const uint32_t d7 = (c1 / 100) << 1; const uint32_t d8 = (c1 % 100) << 1; @@ -207,9 +211,8 @@ inline char* u64toa(uint64_t value, char* buffer) { *buffer++ = cDigitsLut[d3 + 1]; if (value >= kTen9) *buffer++ = cDigitsLut[d4]; - if (value >= kTen8) - *buffer++ = cDigitsLut[d4 + 1]; - + + *buffer++ = cDigitsLut[d4 + 1]; *buffer++ = cDigitsLut[d5]; *buffer++ = cDigitsLut[d5 + 1]; *buffer++ = cDigitsLut[d6]; @@ -222,7 +225,7 @@ inline char* u64toa(uint64_t value, char* buffer) { else { const uint32_t a = static_cast(value / kTen16); // 1 to 1844 value %= kTen16; - + if (a < 10) *buffer++ = static_cast('0' + static_cast(a)); else if (a < 100) { @@ -232,7 +235,7 @@ inline char* u64toa(uint64_t value, char* buffer) { } else if (a < 1000) { *buffer++ = static_cast('0' + static_cast(a / 100)); - + const uint32_t i = (a % 100) << 1; *buffer++ = cDigitsLut[i]; *buffer++ = cDigitsLut[i + 1]; @@ -245,28 +248,28 @@ inline char* u64toa(uint64_t value, char* buffer) { *buffer++ = cDigitsLut[j]; *buffer++ = cDigitsLut[j + 1]; } - + const uint32_t v0 = static_cast(value / kTen8); const uint32_t v1 = static_cast(value % kTen8); - + const uint32_t b0 = v0 / 10000; const uint32_t c0 = v0 % 10000; - + const uint32_t d1 = (b0 / 100) << 1; const uint32_t d2 = (b0 % 100) << 1; - + const uint32_t d3 = (c0 / 100) << 1; const uint32_t d4 = (c0 % 100) << 1; - + const uint32_t b1 = v1 / 10000; const uint32_t c1 = v1 % 10000; - + const uint32_t d5 = (b1 / 100) << 1; const uint32_t d6 = (b1 % 100) << 1; - + const uint32_t d7 = (c1 / 100) << 1; const uint32_t d8 = (c1 % 100) << 1; - + *buffer++ = cDigitsLut[d1]; *buffer++ = cDigitsLut[d1 + 1]; *buffer++ = cDigitsLut[d2]; @@ -284,11 +287,12 @@ inline char* u64toa(uint64_t value, char* buffer) { *buffer++ = cDigitsLut[d8]; *buffer++ = cDigitsLut[d8 + 1]; } - + return buffer; } inline char* i64toa(int64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); uint64_t u = static_cast(value); if (value < 0) { *buffer++ = '-'; diff --git a/external/rapidjson/internal/meta.h b/external/rapidjson/internal/meta.h index 5a9aaa4..d401edf 100644 --- a/external/rapidjson/internal/meta.h +++ b/external/rapidjson/internal/meta.h @@ -21,7 +21,8 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif -#if defined(_MSC_VER) + +#if defined(_MSC_VER) && !defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(6334) #endif @@ -174,7 +175,11 @@ template struct RemoveSfinaeTag { typedef T Type; RAPIDJSON_NAMESPACE_END //@endcond -#if defined(__GNUC__) || defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/external/rapidjson/internal/regex.h b/external/rapidjson/internal/regex.h index 1369ea2..16e3559 100644 --- a/external/rapidjson/internal/regex.h +++ b/external/rapidjson/internal/regex.h @@ -24,16 +24,17 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) RAPIDJSON_DIAG_OFF(implicit-fallthrough) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) +#if __GNUC__ >= 7 +RAPIDJSON_DIAG_OFF(implicit-fallthrough) #endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif #ifndef RAPIDJSON_REGEX_VERBOSE @@ -117,7 +118,8 @@ public: template friend class GenericRegexSearch; GenericRegex(const Ch* source, Allocator* allocator = 0) : - states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), + states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), anchorBegin_(), anchorEnd_() { GenericStringStream ss(source); @@ -125,7 +127,10 @@ public: Parse(ds); } - ~GenericRegex() {} + ~GenericRegex() + { + RAPIDJSON_DELETE(ownAllocator_); + } bool IsValid() const { return root_ != kRegexInvalidState; @@ -187,10 +192,9 @@ private: template void Parse(DecodedStream& ds) { - Allocator allocator; - Stack operandStack(&allocator, 256); // Frag - Stack operatorStack(&allocator, 256); // Operator - Stack atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis) + Stack operandStack(allocator_, 256); // Frag + Stack operatorStack(allocator_, 256); // Operator + Stack atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis) *atomCountStack.template Push() = 0; @@ -391,8 +395,7 @@ private: } return false; - default: - RAPIDJSON_ASSERT(op == kOneOrMore); + case kOneOrMore: if (operandStack.GetSize() >= sizeof(Frag)) { Frag e = *operandStack.template Pop(1); SizeType s = NewState(kRegexInvalidState, e.start, 0); @@ -401,6 +404,10 @@ private: return true; } return false; + + default: + // syntax error (e.g. unclosed kLeftParenthesis) + return false; } } @@ -581,6 +588,8 @@ private: } } + Allocator* ownAllocator_; + Allocator* allocator_; Stack states_; Stack ranges_; SizeType root_; @@ -720,11 +729,11 @@ typedef GenericRegexSearch RegexSearch; } // namespace internal RAPIDJSON_NAMESPACE_END -#ifdef __clang__ +#ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif -#ifdef _MSC_VER +#if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif diff --git a/external/rapidjson/internal/stack.h b/external/rapidjson/internal/stack.h index 5c5398c..45dca6a 100644 --- a/external/rapidjson/internal/stack.h +++ b/external/rapidjson/internal/stack.h @@ -17,6 +17,7 @@ #include "../allocators.h" #include "swap.h" +#include #if defined(__clang__) RAPIDJSON_DIAG_PUSH @@ -100,7 +101,7 @@ public: void ShrinkToFit() { if (Empty()) { // If the stack is empty, completely deallocate the memory. - Allocator::Free(stack_); + Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc) stack_ = 0; stackTop_ = 0; stackEnd_ = 0; @@ -114,7 +115,7 @@ public: template RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { // Expand the stack if needed - if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) + if (RAPIDJSON_UNLIKELY(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) Expand(count); } @@ -127,7 +128,7 @@ public: template RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { RAPIDJSON_ASSERT(stackTop_); - RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); + RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); T* ret = reinterpret_cast(stackTop_); stackTop_ += sizeof(T) * count; return ret; diff --git a/external/rapidjson/internal/strfunc.h b/external/rapidjson/internal/strfunc.h index de41d8f..226439a 100644 --- a/external/rapidjson/internal/strfunc.h +++ b/external/rapidjson/internal/strfunc.h @@ -16,6 +16,7 @@ #define RAPIDJSON_INTERNAL_STRFUNC_H_ #include "../stream.h" +#include RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -34,6 +35,16 @@ inline SizeType StrLen(const Ch* s) { return SizeType(p - s); } +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + //! Returns number of code points in a encoded string. template bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { diff --git a/external/rapidjson/internal/strtod.h b/external/rapidjson/internal/strtod.h index adf49e3..dfca22b 100644 --- a/external/rapidjson/internal/strtod.h +++ b/external/rapidjson/internal/strtod.h @@ -19,6 +19,8 @@ #include "biginteger.h" #include "diyfp.h" #include "pow10.h" +#include +#include RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -126,20 +128,20 @@ inline bool StrtodFast(double d, int p, double* result) { } // Compute an approximation and see if it is within 1/2 ULP -inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { +inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result) { uint64_t significand = 0; - size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 - for (; i < length; i++) { + int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < dLen; i++) { if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) break; significand = significand * 10u + static_cast(decimals[i] - '0'); } - if (i < length && decimals[i] >= '5') // Rounding + if (i < dLen && decimals[i] >= '5') // Rounding significand++; - size_t remaining = length - i; + int remaining = dLen - i; const int kUlpShift = 3; const int kUlp = 1 << kUlpShift; int64_t error = (remaining == 0) ? 0 : kUlp / 2; @@ -148,24 +150,24 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit v = v.Normalize(); error <<= -v.e; - const int dExp = static_cast(decimalPosition) - static_cast(i) + exp; + dExp += remaining; int actualExp; DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); if (actualExp != dExp) { static const DiyFp kPow10[] = { - DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 - DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 - DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 - DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 - DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 - DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 - DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 }; - int adjustment = dExp - actualExp - 1; - RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); - v = v * kPow10[adjustment]; - if (length + static_cast(adjustment)> 19u) // has more digits than decimal digits in 64-bit + int adjustment = dExp - actualExp; + RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); + v = v * kPow10[adjustment - 1]; + if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit error += kUlp / 2; } @@ -203,9 +205,9 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); } -inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { - const BigInteger dInt(decimals, length); - const int dExp = static_cast(decimalPosition) - static_cast(length) + exp; +inline double StrtodBigInteger(double approx, const char* decimals, int dLen, int dExp) { + RAPIDJSON_ASSERT(dLen >= 0); + const BigInteger dInt(decimals, static_cast(dLen)); Double a(approx); int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); if (cmp < 0) @@ -225,42 +227,61 @@ inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t RAPIDJSON_ASSERT(d >= 0.0); RAPIDJSON_ASSERT(length >= 1); - double result; + double result = 0.0; if (StrtodFast(d, p, &result)) return result; + RAPIDJSON_ASSERT(length <= INT_MAX); + int dLen = static_cast(length); + + RAPIDJSON_ASSERT(length >= decimalPosition); + RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); + int dExpAdjust = static_cast(length - decimalPosition); + + RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); + int dExp = exp - dExpAdjust; + + // Make sure length+dExp does not overflow + RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); + // Trim leading zeros - while (*decimals == '0' && length > 1) { - length--; + while (dLen > 0 && *decimals == '0') { + dLen--; decimals++; - decimalPosition--; } // Trim trailing zeros - while (decimals[length - 1] == '0' && length > 1) { - length--; - decimalPosition--; - exp++; + while (dLen > 0 && decimals[dLen - 1] == '0') { + dLen--; + dExp++; + } + + if (dLen == 0) { // Buffer only contains zeros. + return 0.0; } // Trim right-most digits - const int kMaxDecimalDigit = 780; - if (static_cast(length) > kMaxDecimalDigit) { - int delta = (static_cast(length) - kMaxDecimalDigit); - exp += delta; - decimalPosition -= static_cast(delta); - length = kMaxDecimalDigit; + const int kMaxDecimalDigit = 767 + 1; + if (dLen > kMaxDecimalDigit) { + dExp += dLen - kMaxDecimalDigit; + dLen = kMaxDecimalDigit; } - // If too small, underflow to zero - if (int(length) + exp < -324) + // If too small, underflow to zero. + // Any x <= 10^-324 is interpreted as zero. + if (dLen + dExp <= -324) return 0.0; - if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) + // If too large, overflow to infinity. + // Any x >= 10^309 is interpreted as +infinity. + if (dLen + dExp > 309) + return std::numeric_limits::infinity(); + + if (StrtodDiyFp(decimals, dLen, dExp, &result)) return result; // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison - return StrtodBigInteger(result, decimals, length, decimalPosition, exp); + return StrtodBigInteger(result, decimals, dLen, dExp); } } // namespace internal diff --git a/external/rapidjson/istreamwrapper.h b/external/rapidjson/istreamwrapper.h index 8639c8c..c4950b9 100644 --- a/external/rapidjson/istreamwrapper.h +++ b/external/rapidjson/istreamwrapper.h @@ -17,13 +17,12 @@ #include "stream.h" #include +#include #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) -#endif - -#ifdef _MSC_VER +#elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized #endif @@ -50,57 +49,71 @@ template class BasicIStreamWrapper { public: typedef typename StreamType::char_type Ch; - BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {} - Ch Peek() const { - typename StreamType::int_type c = stream_.peek(); - return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast(c) : static_cast('\0'); + //! Constructor. + /*! + \param stream stream opened for read. + */ + BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + Read(); } - Ch Take() { - typename StreamType::int_type c = stream_.get(); - if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) { - count_++; - return static_cast(c); - } - else - return '\0'; + //! Constructor. + /*! + \param stream stream opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); } - // tellg() may return -1 when failed. So we count by ourself. - size_t Tell() const { return count_; } + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + // Not implemented void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } // For encoding detection only. const Ch* Peek4() const { - RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream. - int i; - bool hasError = false; - for (i = 0; i < 4; ++i) { - typename StreamType::int_type c = stream_.get(); - if (c == StreamType::traits_type::eof()) { - hasError = true; - stream_.clear(); - break; - } - peekBuffer_[i] = static_cast(c); - } - for (--i; i >= 0; --i) - stream_.putback(peekBuffer_[i]); - return !hasError ? peekBuffer_ : 0; + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; } private: + BasicIStreamWrapper(); BasicIStreamWrapper(const BasicIStreamWrapper&); BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); - StreamType& stream_; - size_t count_; //!< Number of characters read. Note: - mutable Ch peekBuffer_[4]; + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = bufferSize_; + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (!stream_.read(buffer_, static_cast(bufferSize_))) { + readCount_ = static_cast(stream_.gcount()); + *(bufferLast_ = buffer_ + readCount_) = '\0'; + eof_ = true; + } + } + } + + StreamType &stream_; + Ch peekBuffer_[4], *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; }; typedef BasicIStreamWrapper IStreamWrapper; diff --git a/external/rapidjson/license.txt b/external/rapidjson/license.txt new file mode 100644 index 0000000..7ccc161 --- /dev/null +++ b/external/rapidjson/license.txt @@ -0,0 +1,57 @@ +Tencent is pleased to support the open source community by making RapidJSON available. + +Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. + +If you have downloaded a copy of the RapidJSON binary from Tencent, please note that the RapidJSON binary is licensed under the MIT License. +If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON. To avoid the problematic JSON license in your own projects, it's sufficient to exclude the bin/jsonchecker/ directory, as it's the only code under the JSON license. +A copy of the MIT License is included in this file. + +Other dependencies and licenses: + +Open Source Software Licensed Under the BSD License: +-------------------------------------------------------------------- + +The msinttypes r29 +Copyright (c) 2006-2013 Alexander Chemeris +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Open Source Software Licensed Under the JSON License: +-------------------------------------------------------------------- + +json.org +Copyright (c) 2002 JSON.org +All Rights Reserved. + +JSON_checker +Copyright (c) 2002 JSON.org +All Rights Reserved. + + +Terms of the JSON License: +--------------------------------------------------- + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Terms of the MIT License: +-------------------------------------------------------------------- + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/external/rapidjson/pointer.h b/external/rapidjson/pointer.h index bc7acfd..063abab 100644 --- a/external/rapidjson/pointer.h +++ b/external/rapidjson/pointer.h @@ -21,9 +21,7 @@ #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(switch-enum) -#endif - -#ifdef _MSC_VER +#elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif @@ -165,7 +163,12 @@ public: GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Copy constructor. - GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + GenericPointer(const GenericPointer& rhs) : allocator_(rhs.allocator_), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { *this = rhs; } @@ -197,6 +200,36 @@ public: return *this; } + //! Swap the content of this pointer with an other. + /*! + \param other The pointer to swap with. + \note Constant complexity. + */ + GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, other.allocator_); + internal::Swap(ownAllocator_, other.ownAllocator_); + internal::Swap(nameBuffer_, other.nameBuffer_); + internal::Swap(tokens_, other.tokens_); + internal::Swap(tokenCount_, other.tokenCount_); + internal::Swap(parseErrorOffset_, other.parseErrorOffset_); + internal::Swap(parseErrorCode_, other.parseErrorCode_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.pointer, b.pointer); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + //@} //!@name Append token @@ -240,7 +273,7 @@ public: template RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) Append(T* name, Allocator* allocator = 0) const { - return Append(name, StrLen(name), allocator); + return Append(name, internal::StrLen(name), allocator); } #if RAPIDJSON_HAS_STDSTRING @@ -353,6 +386,33 @@ public: */ bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + //! Less than operator. + /*! + \note Invalid pointers are always greater than valid ones. + */ + bool operator<(const GenericPointer& rhs) const { + if (!IsValid()) + return false; + if (!rhs.IsValid()) + return true; + + if (tokenCount_ != rhs.tokenCount_) + return tokenCount_ < rhs.tokenCount_; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index) + return tokens_[i].index < rhs.tokens_[i].index; + + if (tokens_[i].length != rhs.tokens_[i].length) + return tokens_[i].length < rhs.tokens_[i].length; + + if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) + return cmp < 0; + } + + return false; + } + //@} //!@name Stringify @@ -532,14 +592,14 @@ public: */ ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; - Value& v = Create(root, allocator, &alreadyExist); + ValueType& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); } //! Query a value in a subtree with default null-terminated string. ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; - Value& v = Create(root, allocator, &alreadyExist); + ValueType& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.SetString(defaultValue, allocator); } @@ -547,7 +607,7 @@ public: //! Query a value in a subtree with default std::basic_string. ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; - Value& v = Create(root, allocator, &alreadyExist); + ValueType& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.SetString(defaultValue, allocator); } #endif @@ -1347,11 +1407,7 @@ bool EraseValueByPointer(T& root, const CharType(&source)[N]) { RAPIDJSON_NAMESPACE_END -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#ifdef _MSC_VER +#if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif diff --git a/external/rapidjson/prettywriter.h b/external/rapidjson/prettywriter.h index d663208..45afb69 100644 --- a/external/rapidjson/prettywriter.h +++ b/external/rapidjson/prettywriter.h @@ -39,7 +39,7 @@ enum PrettyFormatOptions { //! Writer with indentation and spacing. /*! - \tparam OutputStream Type of ouptut os. + \tparam OutputStream Type of output os. \tparam SourceEncoding Encoding of source string. \tparam TargetEncoding Encoding of output stream. \tparam StackAllocator Type of allocator for allocating memory of stack. @@ -47,7 +47,7 @@ enum PrettyFormatOptions { template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> class PrettyWriter : public Writer { public: - typedef Writer Base; + typedef Writer Base; typedef typename Base::Ch Ch; //! Constructor @@ -92,26 +92,26 @@ public: */ //@{ - bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } - bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } - bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } - bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } - bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } - bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } - bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } bool RawNumber(const Ch* str, SizeType length, bool copy = false) { RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kNumberType); - return Base::WriteString(str, length); + return Base::EndValue(Base::WriteString(str, length)); } bool String(const Ch* str, SizeType length, bool copy = false) { RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kStringType); - return Base::WriteString(str, length); + return Base::EndValue(Base::WriteString(str, length)); } #if RAPIDJSON_HAS_STDSTRING @@ -136,19 +136,21 @@ public: bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); - RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty) { Base::os_->Put('\n'); WriteIndent(); } - bool ret = Base::WriteEndObject(); + bool ret = Base::EndValue(Base::WriteEndObject()); (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text - Base::os_->Flush(); + Base::Flush(); return true; } @@ -168,11 +170,11 @@ public: Base::os_->Put('\n'); WriteIndent(); } - bool ret = Base::WriteEndArray(); + bool ret = Base::EndValue(Base::WriteEndArray()); (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text - Base::os_->Flush(); + Base::Flush(); return true; } @@ -199,7 +201,7 @@ public: bool RawValue(const Ch* json, size_t length, Type type) { RAPIDJSON_ASSERT(json != 0); PrettyPrefix(type); - return Base::WriteRawValue(json, length); + return Base::EndValue(Base::WriteRawValue(json, length)); } protected: diff --git a/external/rapidjson/rapidjson.h b/external/rapidjson/rapidjson.h index e667d8b..549936f 100644 --- a/external/rapidjson/rapidjson.h +++ b/external/rapidjson/rapidjson.h @@ -26,7 +26,7 @@ Some RapidJSON features are configurable to adapt the library to a wide variety of platforms, environments and usage scenarios. Most of the - features can be configured in terms of overriden or predefined + features can be configured in terms of overridden or predefined preprocessor macros at compile-time. Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. @@ -49,6 +49,11 @@ // token stringification #define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) #define RAPIDJSON_DO_STRINGIFY(x) #x + +// token concatenation +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y //!@endcond /*! \def RAPIDJSON_MAJOR_VERSION @@ -214,7 +219,7 @@ # elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # else -# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif // __BYTE_ORDER__ // Detect with GLIBC's endian.h # elif defined(__GLIBC__) @@ -224,7 +229,7 @@ # elif (__BYTE_ORDER == __BIG_ENDIAN) # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # else -# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif // __GLIBC__ // Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro # elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) @@ -236,12 +241,12 @@ # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif defined(_MSC_VER) && defined(_M_ARM) +# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # elif defined(RAPIDJSON_DOXYGEN_RUNNING) # define RAPIDJSON_ENDIAN # else -# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif #endif // RAPIDJSON_ENDIAN @@ -264,16 +269,11 @@ /*! \ingroup RAPIDJSON_CONFIG \param x pointer to align - Some machines require strict data alignment. Currently the default uses 4 bytes - alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms. + Some machines require strict data alignment. The default is 8 bytes. User can customize by defining the RAPIDJSON_ALIGN function macro. */ #ifndef RAPIDJSON_ALIGN -#if RAPIDJSON_64BIT == 1 -#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) -#else -#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u) -#endif +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) #endif /////////////////////////////////////////////////////////////////////////////// @@ -320,17 +320,17 @@ #endif /////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD /*! \def RAPIDJSON_SIMD \ingroup RAPIDJSON_CONFIG - \brief Enable SSE2/SSE4.2 optimization. + \brief Enable SSE2/SSE4.2/Neon optimization. RapidJSON supports optimized implementations for some parsing operations - based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible - processors. + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. - To enable these optimizations, two different symbols can be defined; + To enable these optimizations, three different symbols can be defined; \code // Enable SSE2 optimization. #define RAPIDJSON_SSE2 @@ -339,13 +339,17 @@ #define RAPIDJSON_SSE42 \endcode - \c RAPIDJSON_SSE42 takes precedence, if both are defined. + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. If any of these symbols is defined, RapidJSON defines the macro \c RAPIDJSON_SIMD to indicate the availability of the optimized code. */ #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ - || defined(RAPIDJSON_DOXYGEN_RUNNING) + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) #define RAPIDJSON_SIMD #endif @@ -405,7 +409,15 @@ RAPIDJSON_NAMESPACE_END /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_STATIC_ASSERT -// Adopt from boost +// Prefer C++11 static_assert, if available +#ifndef RAPIDJSON_STATIC_ASSERT +#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation from boost #ifndef RAPIDJSON_STATIC_ASSERT #ifndef __clang__ //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN @@ -413,14 +425,10 @@ RAPIDJSON_NAMESPACE_END RAPIDJSON_NAMESPACE_BEGIN template struct STATIC_ASSERTION_FAILURE; template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; -template struct StaticAssertTest {}; +template struct StaticAssertTest {}; RAPIDJSON_NAMESPACE_END -#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) -#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) -#define RAPIDJSON_DO_JOIN2(X, Y) X##Y - -#if defined(__GNUC__) +#if defined(__GNUC__) || defined(__clang__) #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) #else #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE @@ -438,7 +446,7 @@ RAPIDJSON_NAMESPACE_END typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE -#endif +#endif // RAPIDJSON_STATIC_ASSERT /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY @@ -530,13 +538,14 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS #if defined(__clang__) #if __has_feature(cxx_rvalue_references) && \ - (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) + (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 #else #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 #endif #elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 #else @@ -547,8 +556,9 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT #if defined(__clang__) #define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) -#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) -// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 #else #define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 @@ -562,14 +572,19 @@ RAPIDJSON_NAMESPACE_END // no automatic detection, yet #ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#if (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 +#else #define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 #endif +#endif #ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR #if defined(__clang__) #define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) #elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1700) + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 #else #define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 @@ -578,6 +593,32 @@ RAPIDJSON_NAMESPACE_END //!@endcond +//! Assertion (in non-throwing contexts). + /*! \ingroup RAPIDJSON_CONFIG + Some functions provide a \c noexcept guarantee, if the compiler supports it. + In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to + throw an exception. This macro adds a separate customization point for + such cases. + + Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is + supported, and to \ref RAPIDJSON_ASSERT otherwise. + */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NOEXCEPT_ASSERT + +#ifndef RAPIDJSON_NOEXCEPT_ASSERT +#ifdef RAPIDJSON_ASSERT_THROWS +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT_ASSERT(x) +#else +#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT +#else +#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_ASSERT_THROWS +#endif // RAPIDJSON_NOEXCEPT_ASSERT + /////////////////////////////////////////////////////////////////////////////// // new/delete diff --git a/external/rapidjson/reader.h b/external/rapidjson/reader.h index dbb5e16..44a6bcd 100644 --- a/external/rapidjson/reader.h +++ b/external/rapidjson/reader.h @@ -33,12 +33,8 @@ #include #elif defined(RAPIDJSON_SSE2) #include -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(RAPIDJSON_NEON) +#include #endif #ifdef __clang__ @@ -46,6 +42,10 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(old-style-cast) RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code #endif #ifdef __GNUC__ @@ -411,7 +411,92 @@ inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { return SkipWhitespace(p, end); } -#endif // RAPIDJSON_SSE2 +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz =__builtin_clzll(high);; + return p + 8 + (lz >> 3); + } + } else { + int lz = __builtin_clzll(low);; + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz = __builtin_clzll(high); + return p + 8 + (lz >> 3); + } + } else { + int lz = __builtin_clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON #ifdef RAPIDJSON_SIMD //! Template function specialization for InsituStringStream @@ -457,7 +542,8 @@ public: /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) */ - GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : + stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {} //! Parse JSON text. /*! \tparam parseFlags Combination of \ref ParseFlag. @@ -513,7 +599,84 @@ public: return Parse(is, handler); } - //! Whether a parse error has occured in the last parsing. + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + bool IterativeParseNext(InputStream& is, Handler& handler) { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + // If we've finished or hit an error... + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; + + // If StopWhenDone is not set... + if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') { + // ... this is considered an error. + HandleError(state_, is); + return false; + } + } + + // Success! We are done! + return true; + } + + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. + if (!IsIterativeParsingDelimiterState(n)) + return true; + } + + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { + HandleError(state_, is); + return false; + } + + return true; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const { + return IsIterativeParsingCompleteState(state_); + } + + //! Whether a parse error has occurred in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } //! Get the \ref ParseErrorCode of last parsing. @@ -736,7 +899,7 @@ private: return false; } - // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + // Helper function to parse four hexadecimal digits in \uXXXX in ParseString(). template unsigned ParseHex4(InputStream& is, size_t escapeOffset) { unsigned codepoint = 0; @@ -843,7 +1006,7 @@ private: Ch c = is.Peek(); if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape - size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset + size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset is.Take(); Ch e = is.Peek(); if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { @@ -878,7 +1041,7 @@ private: if (c == '\0') RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); else - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); } else { size_t offset = is.Tell(); @@ -913,7 +1076,7 @@ private: // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -922,7 +1085,7 @@ private: const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -976,7 +1139,7 @@ private: // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -985,7 +1148,7 @@ private: const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -1024,7 +1187,7 @@ private: // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -1033,7 +1196,7 @@ private: const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -1052,7 +1215,180 @@ private: is.src_ = is.dst_ = p; } -#endif +#elif defined(RAPIDJSON_NEON) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high);; + length = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low);; + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz = __builtin_clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + int lz = __builtin_clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON template class NumberStream; @@ -1170,18 +1506,27 @@ private: } // Parse NaN or Infinity here else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { - useNanOrInf = true; - if (RAPIDJSON_LIKELY(Consume(s, 'N') && Consume(s, 'a') && Consume(s, 'N'))) { - d = std::numeric_limits::quiet_NaN(); + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } } - else if (RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) { - d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); - if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') - && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } } - else + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); @@ -1216,8 +1561,6 @@ private: // Force double for big integer if (useDouble) { while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); d = d * 10 + (s.TakePush() - '0'); } } @@ -1287,9 +1630,18 @@ private: if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { exp = static_cast(s.Take() - '0'); if (expMinus) { + // (exp + expFrac) must not underflow int => we're detecting when -exp gets + // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into + // underflow territory): + // + // -(exp * 10 + 9) + expFrac >= INT_MIN + // <=> exp <= (expFrac - INT_MIN - 9) / 10 + RAPIDJSON_ASSERT(expFrac <= 0); + int maxExp = (expFrac + 2147483639) / 10; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { exp = exp * 10 + static_cast(s.Take() - '0'); - if (exp >= 214748364) { // Issue #313: prevent overflow exponent + if (RAPIDJSON_UNLIKELY(exp > maxExp)) { while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent s.Take(); } @@ -1348,6 +1700,13 @@ private: else d = internal::StrtodNormalPrecision(d, p); + // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal + if (d > (std::numeric_limits::max)()) { + // Overflow + // TODO: internal::StrtodX should report overflow (or underflow) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + cont = handler.Double(minus ? -d : d); } else if (useNanOrInf) { @@ -1393,29 +1752,31 @@ private: // States enum IterativeParsingState { - IterativeParsingStartState = 0, - IterativeParsingFinishState, - IterativeParsingErrorState, + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, // Object states IterativeParsingObjectInitialState, IterativeParsingMemberKeyState, - IterativeParsingKeyValueDelimiterState, IterativeParsingMemberValueState, - IterativeParsingMemberDelimiterState, IterativeParsingObjectFinishState, // Array states IterativeParsingArrayInitialState, IterativeParsingElementState, - IterativeParsingElementDelimiterState, IterativeParsingArrayFinishState, // Single value state - IterativeParsingValueState - }; + IterativeParsingValueState, - enum { cIterativeParsingStateCount = IterativeParsingValueState + 1 }; + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + + cIterativeParsingStateCount + }; // Tokens enum Token { @@ -1437,7 +1798,7 @@ private: kTokenCount }; - RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const { //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #define N NumberToken @@ -1464,9 +1825,21 @@ private: return NumberToken; } - RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const { // current state x one lookahead token -> new state static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, // Start { IterativeParsingArrayInitialState, // Left bracket @@ -1481,18 +1854,6 @@ private: IterativeParsingValueState, // Null IterativeParsingValueState // Number }, - // Finish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // Error(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, // ObjectInitial { IterativeParsingErrorState, // Left bracket @@ -1521,20 +1882,6 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // KeyValueDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberValueState, // String - IterativeParsingMemberValueState, // False - IterativeParsingMemberValueState, // True - IterativeParsingMemberValueState, // Null - IterativeParsingMemberValueState // Number - }, // MemberValue { IterativeParsingErrorState, // Left bracket @@ -1549,20 +1896,6 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // MemberDelimiter - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingObjectFinishState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberKeyState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, // ObjectFinish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, @@ -1597,6 +1930,18 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, // ElementDelimiter { IterativeParsingArrayInitialState, // Left bracket(push Element state) @@ -1611,18 +1956,34 @@ private: IterativeParsingElementState, // Null IterativeParsingElementState // Number }, - // ArrayFinish(sink state) + // MemberDelimiter { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number }, - // Single Value (sink state) + // KeyValueDelimiter { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - } + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, }; // End of G return static_cast(G[state][token]); @@ -1803,6 +2164,14 @@ private: } } + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const { + return s >= IterativeParsingElementDelimiterState; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { + return s <= IterativeParsingErrorState; + } + template ParseResult IterativeParse(InputStream& is, Handler& handler) { parseResult_.Clear(); @@ -1841,6 +2210,7 @@ private: static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. ParseResult parseResult_; + IterativeParsingState state_; }; // class GenericReader //! Reader with UTF8 encoding and default allocator. @@ -1848,7 +2218,7 @@ typedef GenericReader, UTF8<> > Reader; RAPIDJSON_NAMESPACE_END -#ifdef __clang__ +#if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif @@ -1857,8 +2227,4 @@ RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_POP #endif -#ifdef _MSC_VER -RAPIDJSON_DIAG_POP -#endif - #endif // RAPIDJSON_READER_H_ diff --git a/external/rapidjson/schema.h b/external/rapidjson/schema.h index 3f81d9b..26ae947 100644 --- a/external/rapidjson/schema.h +++ b/external/rapidjson/schema.h @@ -17,6 +17,7 @@ #include "document.h" #include "pointer.h" +#include "stringbuffer.h" #include // abs, floor #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) @@ -25,7 +26,7 @@ #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 #endif -#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) #define RAPIDJSON_SCHEMA_USE_STDREGEX 1 #else #define RAPIDJSON_SCHEMA_USE_STDREGEX 0 @@ -62,9 +63,7 @@ RAPIDJSON_DIAG_OFF(weak-vtables) RAPIDJSON_DIAG_OFF(exit-time-destructors) RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) RAPIDJSON_DIAG_OFF(variadic-macros) -#endif - -#ifdef _MSC_VER +#elif defined(_MSC_VER) RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif @@ -157,6 +156,62 @@ public: virtual void FreeState(void* p) = 0; }; +/////////////////////////////////////////////////////////////////////////////// +// IValidationErrorHandler + +template +class IValidationErrorHandler { +public: + typedef typename SchemaType::Ch Ch; + typedef typename SchemaType::SValue SValue; + + virtual ~IValidationErrorHandler() {} + + virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(double actual, const SValue& expected) = 0; + virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; + + virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; + + virtual void DisallowedItem(SizeType index) = 0; + virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; + + virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void StartMissingProperties() = 0; + virtual void AddMissingProperty(const SValue& name) = 0; + virtual bool EndMissingProperties() = 0; + virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; + + virtual void StartDependencyErrors() = 0; + virtual void StartMissingDependentProperties() = 0; + virtual void AddMissingDependentProperty(const SValue& targetName) = 0; + virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; + virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0; + virtual bool EndDependencyErrors() = 0; + + virtual void DisallowedValue() = 0; + virtual void StartDisallowedType() = 0; + virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; + virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; + virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void Disallowed() = 0; +}; + + /////////////////////////////////////////////////////////////////////////////// // Hasher @@ -261,6 +316,7 @@ template struct SchemaValidationContext { typedef Schema SchemaType; typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef IValidationErrorHandler ErrorHandlerType; typedef typename SchemaType::ValueType ValueType; typedef typename ValueType::Ch Ch; @@ -270,8 +326,9 @@ struct SchemaValidationContext { kPatternValidatorWithAdditionalProperty }; - SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : + SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s) : factory(f), + error_handler(eh), schema(s), valueSchema(), invalidKeyword(), @@ -311,6 +368,7 @@ struct SchemaValidationContext { } SchemaValidatorFactoryType& factory; + ErrorHandlerType& error_handler; const SchemaType* schema; const SchemaType* valueSchema; const Ch* invalidKeyword; @@ -345,16 +403,20 @@ public: typedef SchemaValidationContext Context; typedef Schema SchemaType; typedef GenericValue SValue; + typedef IValidationErrorHandler ErrorHandler; friend class GenericSchemaDocument; Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : allocator_(allocator), + uri_(schemaDocument->GetURI(), *allocator), + pointer_(p, allocator), typeless_(schemaDocument->GetTypeless()), enum_(), enumCount_(), not_(), type_((1 << kTotalSchemaType) - 1), // typeless validatorCount_(), + notValidatorIndex_(), properties_(), additionalPropertiesSchema_(), patternProperties_(), @@ -378,7 +440,8 @@ public: minLength_(0), maxLength_(~SizeType(0)), exclusiveMinimum_(false), - exclusiveMaximum_(false) + exclusiveMaximum_(false), + defaultValueLength_(0) { typedef typename SchemaDocumentType::ValueType ValueType; typedef typename ValueType::ConstValueIterator ConstValueIterator; @@ -401,7 +464,7 @@ public: enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { typedef Hasher > EnumHasherType; - char buffer[256 + 24]; + char buffer[256u + 24]; MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); EnumHasherType h(&hasherAllocator, 256); itr->Accept(h); @@ -573,6 +636,12 @@ public: if (const ValueType* v = GetMember(value, GetMultipleOfString())) if (v->IsNumber() && v->GetDouble() > 0.0) multipleOf_.CopyFrom(*v, *allocator_); + + // Default + if (const ValueType* v = GetMember(value, GetDefaultValueString())) + if (v->IsString()) + defaultValueLength_ = v->GetStringLength(); + } ~Schema() { @@ -596,6 +665,14 @@ public: #endif } + const SValue& GetURI() const { + return uri_; + } + + const PointerType& GetPointer() const { + return pointer_; + } + bool BeginValue(Context& context) const { if (context.inArray) { if (uniqueItems_) @@ -610,8 +687,10 @@ public: context.valueSchema = additionalItemsSchema_; else if (additionalItems_) context.valueSchema = typeless_; - else + else { + context.error_handler.DisallowedItem(context.arrayElementIndex); RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); + } } else context.valueSchema = typeless_; @@ -636,15 +715,21 @@ public: } if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { - if (!patternValid) + if (!patternValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } } else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { - if (!patternValid || !otherValid) + if (!patternValid || !otherValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } } - else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) + else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } } if (enum_) { @@ -652,19 +737,23 @@ public: for (SizeType i = 0; i < enumCount_; i++) if (enum_[i] == h) goto foundEnum; + context.error_handler.DisallowedValue(); RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); foundEnum:; } if (allOf_.schemas) for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) - if (!context.validators[i]->IsValid()) + if (!context.validators[i]->IsValid()) { + context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); + } if (anyOf_.schemas) { for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) if (context.validators[i]->IsValid()) goto foundAny; + context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); foundAny:; } @@ -673,30 +762,39 @@ public: bool oneValid = false; for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) if (context.validators[i]->IsValid()) { - if (oneValid) + if (oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); - else + } else oneValid = true; } - if (!oneValid) + if (!oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + } } - if (not_ && context.validators[notValidatorIndex_]->IsValid()) + if (not_ && context.validators[notValidatorIndex_]->IsValid()) { + context.error_handler.Disallowed(); RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); + } return true; } - bool Null(Context& context) const { - if (!(type_ & (1 << kNullSchemaType))) + bool Null(Context& context) const { + if (!(type_ & (1 << kNullSchemaType))) { + DisallowedType(context, GetNullString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } return CreateParallelValidator(context); } - bool Bool(Context& context, bool) const { - if (!(type_ & (1 << kBooleanSchemaType))) + bool Bool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) { + DisallowedType(context, GetBooleanString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } return CreateParallelValidator(context); } @@ -725,8 +823,10 @@ public: } bool Double(Context& context, double d) const { - if (!(type_ & (1 << kNumberSchemaType))) + if (!(type_ & (1 << kNumberSchemaType))) { + DisallowedType(context, GetNumberString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) return false; @@ -741,28 +841,38 @@ public: } bool String(Context& context, const Ch* str, SizeType length, bool) const { - if (!(type_ & (1 << kStringSchemaType))) + if (!(type_ & (1 << kStringSchemaType))) { + DisallowedType(context, GetStringString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } if (minLength_ != 0 || maxLength_ != SizeType(~0)) { SizeType count; if (internal::CountStringCodePoint(str, length, &count)) { - if (count < minLength_) + if (count < minLength_) { + context.error_handler.TooShort(str, length, minLength_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); - if (count > maxLength_) + } + if (count > maxLength_) { + context.error_handler.TooLong(str, length, maxLength_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); + } } } - if (pattern_ && !IsPatternMatch(pattern_, str, length)) + if (pattern_ && !IsPatternMatch(pattern_, str, length)) { + context.error_handler.DoesNotMatch(str, length); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); + } return CreateParallelValidator(context); } - bool StartObject(Context& context) const { - if (!(type_ & (1 << kObjectSchemaType))) + bool StartObject(Context& context) const { + if (!(type_ & (1 << kObjectSchemaType))) { + DisallowedType(context, GetObjectString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } if (hasDependencies_ || hasRequired_) { context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); @@ -783,8 +893,10 @@ public: if (patternProperties_) { context.patternPropertiesSchemaCount = 0; for (SizeType i = 0; i < patternPropertyCount_; i++) - if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + context.valueSchema = typeless_; + } } SizeType index; @@ -818,45 +930,66 @@ public: return true; } - if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties + if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties + context.error_handler.DisallowedProperty(str, len); RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); - - return true; - } - - bool EndObject(Context& context, SizeType memberCount) const { - if (hasRequired_) - for (SizeType index = 0; index < propertyCount_; index++) - if (properties_[index].required) - if (!context.propertyExist[index]) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); - - if (memberCount < minProperties_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); - - if (memberCount > maxProperties_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); - - if (hasDependencies_) { - for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) - if (context.propertyExist[sourceIndex]) { - if (properties_[sourceIndex].dependencies) { - for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) - if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); - } - else if (properties_[sourceIndex].dependenciesSchema) - if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); - } } return true; } - bool StartArray(Context& context) const { - if (!(type_ & (1 << kArraySchemaType))) + bool EndObject(Context& context, SizeType memberCount) const { + if (hasRequired_) { + context.error_handler.StartMissingProperties(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required && !context.propertyExist[index]) + if (properties_[index].schema->defaultValueLength_ == 0 ) + context.error_handler.AddMissingProperty(properties_[index].name); + if (context.error_handler.EndMissingProperties()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + } + + if (memberCount < minProperties_) { + context.error_handler.TooFewProperties(memberCount, minProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); + } + + if (memberCount > maxProperties_) { + context.error_handler.TooManyProperties(memberCount, maxProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); + } + + if (hasDependencies_) { + context.error_handler.StartDependencyErrors(); + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { + const Property& source = properties_[sourceIndex]; + if (context.propertyExist[sourceIndex]) { + if (source.dependencies) { + context.error_handler.StartMissingDependentProperties(); + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) + context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); + context.error_handler.EndMissingDependentProperties(source.name); + } + else if (source.dependenciesSchema) { + ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; + if (!dependenciesValidator->IsValid()) + context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); + } + } + } + if (context.error_handler.EndDependencyErrors()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + + return true; + } + + bool StartArray(Context& context) const { + if (!(type_ & (1 << kArraySchemaType))) { + DisallowedType(context, GetArrayString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } context.arrayElementIndex = 0; context.inArray = true; @@ -864,14 +997,18 @@ public: return CreateParallelValidator(context); } - bool EndArray(Context& context, SizeType elementCount) const { + bool EndArray(Context& context, SizeType elementCount) const { context.inArray = false; - if (elementCount < minItems_) + if (elementCount < minItems_) { + context.error_handler.TooFewItems(elementCount, minItems_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); + } - if (elementCount > maxItems_) + if (elementCount > maxItems_) { + context.error_handler.TooManyItems(elementCount, maxItems_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); + } return true; } @@ -880,7 +1017,7 @@ public: #define RAPIDJSON_STRING_(name, ...) \ static const ValueType& Get##name##String() {\ static const Ch s[] = { __VA_ARGS__, '\0' };\ - static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ return v;\ } @@ -917,6 +1054,7 @@ public: RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') #undef RAPIDJSON_STRING_ @@ -933,7 +1071,7 @@ private: }; #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX - typedef internal::GenericRegex RegexType; + typedef internal::GenericRegex RegexType; #elif RAPIDJSON_SCHEMA_USE_STDREGEX typedef std::basic_regex RegexType; #else @@ -993,7 +1131,7 @@ private: template RegexType* CreatePattern(const ValueType& value) { if (value.IsString()) { - RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString()); + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); if (!r->IsValid()) { r->~RegexType(); AllocatorType::Free(r); @@ -1011,12 +1149,15 @@ private: #elif RAPIDJSON_SCHEMA_USE_STDREGEX template RegexType* CreatePattern(const ValueType& value) { - if (value.IsString()) + if (value.IsString()) { + RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); try { - return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); } catch (const std::regex_error&) { + AllocatorType::Free(r); } + } return 0; } @@ -1092,15 +1233,20 @@ private: } bool CheckInt(Context& context, int64_t i) const { - if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } if (!minimum_.IsNull()) { if (minimum_.IsInt64()) { - if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } } else if (minimum_.IsUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() } else if (!CheckDoubleMinimum(context, static_cast(i))) @@ -1109,19 +1255,23 @@ private: if (!maximum_.IsNull()) { if (maximum_.IsInt64()) { - if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } } - else if (maximum_.IsUint64()) - /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64() + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() else if (!CheckDoubleMaximum(context, static_cast(i))) return false; } if (!multipleOf_.IsNull()) { if (multipleOf_.IsUint64()) { - if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } } else if (!CheckDoubleMultipleOf(context, static_cast(i))) return false; @@ -1131,13 +1281,17 @@ private: } bool CheckUint(Context& context, uint64_t i) const { - if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } if (!minimum_.IsNull()) { if (minimum_.IsUint64()) { - if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } } else if (minimum_.IsInt64()) /* do nothing */; // i >= 0 > minimum.Getint64() @@ -1147,19 +1301,25 @@ private: if (!maximum_.IsNull()) { if (maximum_.IsUint64()) { - if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } } - else if (maximum_.IsInt64()) + else if (maximum_.IsInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ + } else if (!CheckDoubleMaximum(context, static_cast(i))) return false; } if (!multipleOf_.IsNull()) { if (multipleOf_.IsUint64()) { - if (i % multipleOf_.GetUint64() != 0) + if (i % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } } else if (!CheckDoubleMultipleOf(context, static_cast(i))) return false; @@ -1169,14 +1329,18 @@ private: } bool CheckDoubleMinimum(Context& context, double d) const { - if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) { + context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } return true; } bool CheckDoubleMaximum(Context& context, double d) const { - if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { + context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } return true; } @@ -1184,11 +1348,29 @@ private: double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); double q = std::floor(a / b); double r = a - q * b; - if (r > 0.0) + if (r > 0.0) { + context.error_handler.NotMultipleOf(d, multipleOf_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } return true; } + void DisallowedType(Context& context, const ValueType& actualType) const { + ErrorHandler& eh = context.error_handler; + eh.StartDisallowedType(); + + if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); + if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); + if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); + if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); + if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); + + if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); + else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); + + eh.EndDisallowedType(actualType); + } + struct Property { Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} ~Property() { AllocatorType::Free(dependencies); } @@ -1213,6 +1395,8 @@ private: }; AllocatorType* allocator_; + SValue uri_; + PointerType pointer_; const SchemaType* typeless_; uint64_t* enum_; SizeType enumCount_; @@ -1254,6 +1438,8 @@ private: SValue multipleOf_; bool exclusiveMinimum_; bool exclusiveMaximum_; + + SizeType defaultValueLength_; }; template @@ -1322,6 +1508,7 @@ public: typedef typename EncodingType::Ch Ch; typedef internal::Schema SchemaType; typedef GenericPointer PointerType; + typedef GenericValue URIType; friend class internal::Schema; template friend class GenericSchemaValidator; @@ -1331,10 +1518,13 @@ public: Compile a JSON document into schema document. \param document A JSON document as source. + \param uri The base URI of this schema document for purposes of violation reporting. + \param uriLength Length of \c name, in code points. \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. \param allocator An optional allocator instance for allocating memory. Can be null. */ - explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, + IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : remoteProvider_(remoteProvider), allocator_(allocator), ownAllocator_(), @@ -1346,8 +1536,11 @@ public: if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + Ch noUri[1] = {0}; + uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); - new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_); // Generate root schema, it will call CreateSchema() to create sub-schemas, // And call AddRefSchema() if there are $ref. @@ -1385,7 +1578,8 @@ public: root_(rhs.root_), typeless_(rhs.typeless_), schemaMap_(std::move(rhs.schemaMap_)), - schemaRef_(std::move(rhs.schemaRef_)) + schemaRef_(std::move(rhs.schemaRef_)), + uri_(std::move(rhs.uri_)) { rhs.remoteProvider_ = 0; rhs.allocator_ = 0; @@ -1407,6 +1601,8 @@ public: RAPIDJSON_DELETE(ownAllocator_); } + const URIType& GetURI() const { return uri_; } + //! Get the root schema. const SchemaType& GetRoot() const { return *root_; } @@ -1489,6 +1685,7 @@ private: if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { if (schema) *schema = sc; + new (schemaMap_.template Push()) SchemaEntry(source, const_cast(sc), false, allocator_); return true; } } @@ -1537,6 +1734,7 @@ private: SchemaType* typeless_; internal::Stack schemaMap_; // Stores created Pointer -> Schemas internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref + URIType uri_; }; //! GenericSchemaDocument using Value type. @@ -1565,13 +1763,17 @@ template < typename StateAllocator = CrtAllocator> class GenericSchemaValidator : public internal::ISchemaStateFactory, - public internal::ISchemaValidator + public internal::ISchemaValidator, + public internal::IValidationErrorHandler { public: typedef typename SchemaDocumentType::SchemaType SchemaType; typedef typename SchemaDocumentType::PointerType PointerType; typedef typename SchemaType::EncodingType EncodingType; + typedef typename SchemaType::SValue SValue; typedef typename EncodingType::Ch Ch; + typedef GenericStringRef StringRefType; + typedef GenericValue ValueType; //! Constructor without output handler. /*! @@ -1592,7 +1794,10 @@ public: ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), - outputHandler_(CreateNullHandler()), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(0) @@ -1620,8 +1825,10 @@ public: ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), - outputHandler_(outputHandler), - nullHandler_(0), + outputHandler_(&outputHandler), + error_(kObjectType), + currentError_(), + missingDependents_(), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(0) @@ -1632,10 +1839,6 @@ public: //! Destructor. ~GenericSchemaValidator() { Reset(); - if (nullHandler_) { - nullHandler_->~OutputHandler(); - StateAllocator::Free(nullHandler_); - } RAPIDJSON_DELETE(ownStateAllocator_); } @@ -1644,6 +1847,9 @@ public: while (!schemaStack_.Empty()) PopSchema(); documentStack_.Clear(); + error_.SetObject(); + currentError_.SetNull(); + missingDependents_.SetNull(); valid_ = true; } @@ -1651,9 +1857,13 @@ public: // Implementation of ISchemaValidator virtual bool IsValid() const { return valid_; } + //! Gets the error object. + ValueType& GetError() { return error_; } + const ValueType& GetError() const { return error_; } + //! Gets the JSON pointer pointed to the invalid schema. PointerType GetInvalidSchemaPointer() const { - return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); + return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); } //! Gets the keyword of invalid schema. @@ -1663,9 +1873,196 @@ public: //! Gets the JSON pointer pointed to the invalid value. PointerType GetInvalidDocumentPointer() const { - return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + if (documentStack_.Empty()) { + return PointerType(); + } + else { + return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } } + void NotMultipleOf(int64_t actual, const SValue& expected) { + AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); + } + void NotMultipleOf(uint64_t actual, const SValue& expected) { + AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); + } + void NotMultipleOf(double actual, const SValue& expected) { + AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); + } + void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + + void TooLong(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(SchemaType::GetMaxLengthString(), + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void TooShort(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(SchemaType::GetMinLengthString(), + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void DoesNotMatch(const Ch* str, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(SchemaType::GetPatternString()); + } + + void DisallowedItem(SizeType index) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); + AddCurrentError(SchemaType::GetAdditionalItemsString(), true); + } + void TooFewItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(SchemaType::GetMinItemsString(), + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooManyItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(SchemaType::GetMaxItemsString(), + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void DuplicateItems(SizeType index1, SizeType index2) { + ValueType duplicates(kArrayType); + duplicates.PushBack(index1, GetStateAllocator()); + duplicates.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); + AddCurrentError(SchemaType::GetUniqueItemsString(), true); + } + + void TooManyProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(SchemaType::GetMaxPropertiesString(), + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooFewProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(SchemaType::GetMinPropertiesString(), + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void StartMissingProperties() { + currentError_.SetArray(); + } + void AddMissingProperty(const SValue& name) { + currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); + } + bool EndMissingProperties() { + if (currentError_.Empty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(SchemaType::GetRequiredString()); + return true; + } + void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) + MergeError(static_cast(subvalidators[i])->GetError()); + } + void DisallowedProperty(const Ch* name, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(SchemaType::GetAdditionalPropertiesString(), true); + } + + void StartDependencyErrors() { + currentError_.SetObject(); + } + void StartMissingDependentProperties() { + missingDependents_.SetArray(); + } + void AddMissingDependentProperty(const SValue& targetName) { + missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndMissingDependentProperties(const SValue& sourceName) { + if (!missingDependents_.Empty()) + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + missingDependents_, GetStateAllocator()); + } + void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + static_cast(subvalidator)->GetError(), GetStateAllocator()); + } + bool EndDependencyErrors() { + if (currentError_.ObjectEmpty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(SchemaType::GetDependenciesString()); + return true; + } + + void DisallowedValue() { + currentError_.SetObject(); + AddCurrentError(SchemaType::GetEnumString()); + } + void StartDisallowedType() { + currentError_.SetArray(); + } + void AddExpectedType(const typename SchemaType::ValueType& expectedType) { + currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndDisallowedType(const typename SchemaType::ValueType& actualType) { + ValueType error(kObjectType); + error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); + error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); + currentError_ = error; + AddCurrentError(SchemaType::GetTypeString()); + } + void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) { + MergeError(static_cast(subvalidators[i])->GetError()); + } + } + void NoneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(SchemaType::GetAnyOfString(), subvalidators, count); + } + void NotOneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(SchemaType::GetOneOfString(), subvalidators, count); + } + void Disallowed() { + currentError_.SetObject(); + AddCurrentError(SchemaType::GetNotString()); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') + RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') + RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') + RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') + RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') + RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') + RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') + +#undef RAPIDJSON_STRING_ + #if RAPIDJSON_SCHEMA_VERBOSE #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ RAPIDJSON_MULTILINEMACRO_BEGIN\ @@ -1697,14 +2094,14 @@ RAPIDJSON_MULTILINEMACRO_END } #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ - return valid_ = EndValue() && outputHandler_.method arg2 + return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2) #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) - bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); } bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } @@ -1719,7 +2116,7 @@ RAPIDJSON_MULTILINEMACRO_END bool StartObject() { RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); - return valid_ = outputHandler_.StartObject(); + return valid_ = !outputHandler_ || outputHandler_->StartObject(); } bool Key(const Ch* str, SizeType len, bool copy) { @@ -1727,7 +2124,7 @@ RAPIDJSON_MULTILINEMACRO_END AppendToken(str, len); if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); - return valid_ = outputHandler_.Key(str, len, copy); + return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); } bool EndObject(SizeType memberCount) { @@ -1740,7 +2137,7 @@ RAPIDJSON_MULTILINEMACRO_END bool StartArray() { RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); - return valid_ = outputHandler_.StartArray(); + return valid_ = !outputHandler_ || outputHandler_->StartArray(); } bool EndArray(SizeType elementCount) { @@ -1757,7 +2154,7 @@ RAPIDJSON_MULTILINEMACRO_END // Implementation of ISchemaStateFactory virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { - return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, + return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), #if RAPIDJSON_SCHEMA_VERBOSE depth_ + 1, #endif @@ -1789,7 +2186,7 @@ RAPIDJSON_MULTILINEMACRO_END } virtual void FreeState(void* p) { - return StateAllocator::Free(p); + StateAllocator::Free(p); } private: @@ -1800,6 +2197,7 @@ private: GenericSchemaValidator( const SchemaDocumentType& schemaDocument, const SchemaType& root, + const char* basePath, size_t basePathSize, #if RAPIDJSON_SCHEMA_VERBOSE unsigned depth, #endif @@ -1813,12 +2211,17 @@ private: ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), - outputHandler_(CreateNullHandler()), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(depth) #endif { + if (basePath && basePathSize) + memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); } StateAllocator& GetStateAllocator() { @@ -1882,8 +2285,10 @@ private: if (!a) CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) - if (itr->GetUint64() == h) + if (itr->GetUint64() == h) { + DuplicateItems(static_cast(itr - a->Begin()), a->Size()); RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); + } a->PushBack(h, GetStateAllocator()); } } @@ -1912,7 +2317,7 @@ private: } } - RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, &schema); } + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema); } RAPIDJSON_FORCEINLINE void PopSchema() { Context* c = schemaStack_.template Pop(1); @@ -1923,14 +2328,74 @@ private: c->~Context(); } + void AddErrorLocation(ValueType& result, bool parent) { + GenericStringBuffer sb; + PointerType instancePointer = GetInvalidDocumentPointer(); + ((parent && instancePointer.GetTokenCount() > 0) + ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) + : instancePointer).StringifyUriFragment(sb); + ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); + sb.Clear(); + memcpy(sb.Push(CurrentSchema().GetURI().GetStringLength()), + CurrentSchema().GetURI().GetString(), + CurrentSchema().GetURI().GetStringLength() * sizeof(Ch)); + GetInvalidSchemaPointer().StringifyUriFragment(sb); + ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); + } + + void AddError(ValueType& keyword, ValueType& error) { + typename ValueType::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, GetStateAllocator()); + else { + if (member->value.IsObject()) { + ValueType errors(kArrayType); + errors.PushBack(member->value, GetStateAllocator()); + member->value = errors; + } + member->value.PushBack(error, GetStateAllocator()); + } + } + + void AddCurrentError(const typename SchemaType::ValueType& keyword, bool parent = false) { + AddErrorLocation(currentError_, parent); + AddError(ValueType(keyword, GetStateAllocator(), false).Move(), currentError_); + } + + void MergeError(ValueType& other) { + for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { + AddError(it->name, it->value); + } + } + + void AddNumberError(const typename SchemaType::ValueType& keyword, ValueType& actual, const SValue& expected, + const typename SchemaType::ValueType& (*exclusive)() = 0) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); + currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); + if (exclusive) + currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); + AddCurrentError(keyword); + } + + void AddErrorArray(const typename SchemaType::ValueType& keyword, + ISchemaValidator** subvalidators, SizeType count) { + ValueType errors(kArrayType); + for (SizeType i = 0; i < count; ++i) + errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); + AddCurrentError(keyword); + } + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } Context& CurrentContext() { return *schemaStack_.template Top(); } const Context& CurrentContext() const { return *schemaStack_.template Top(); } - OutputHandler& CreateNullHandler() { - return *(nullHandler_ = static_cast(GetStateAllocator().Malloc(sizeof(OutputHandler)))); - } - static const size_t kDefaultSchemaStackCapacity = 1024; static const size_t kDefaultDocumentStackCapacity = 256; const SchemaDocumentType* schemaDocument_; @@ -1939,8 +2404,10 @@ private: StateAllocator* ownStateAllocator_; internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) - OutputHandler& outputHandler_; - OutputHandler* nullHandler_; + OutputHandler* outputHandler_; + ValueType error_; + ValueType currentError_; + ValueType missingDependents_; bool valid_; #if RAPIDJSON_SCHEMA_VERBOSE unsigned depth_; @@ -1972,13 +2439,14 @@ class SchemaValidatingReader { public: typedef typename SchemaDocumentType::PointerType PointerType; typedef typename InputStream::Ch Ch; + typedef GenericValue ValueType; //! Constructor /*! \param is Input stream. \param sd Schema document. */ - SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), error_(kObjectType), isValid_(true) {} template bool operator()(Handler& handler) { @@ -1991,11 +2459,13 @@ public: invalidSchemaPointer_ = PointerType(); invalidSchemaKeyword_ = 0; invalidDocumentPointer_ = PointerType(); + error_.SetObject(); } else { invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + error_.CopyFrom(validator.GetError(), allocator_); } return parseResult_; @@ -2006,6 +2476,7 @@ public: const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + const ValueType& GetError() const { return error_; } private: InputStream& is_; @@ -2015,6 +2486,8 @@ private: PointerType invalidSchemaPointer_; const Ch* invalidSchemaKeyword_; PointerType invalidDocumentPointer_; + StackAllocator allocator_; + ValueType error_; bool isValid_; }; diff --git a/external/rapidjson/stream.h b/external/rapidjson/stream.h index fef82c2..7f2643e 100644 --- a/external/rapidjson/stream.h +++ b/external/rapidjson/stream.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #include "rapidjson.h" @@ -100,6 +100,50 @@ inline void PutN(Stream& stream, Ch c, size_t n) { PutUnsafe(stream, c); } +/////////////////////////////////////////////////////////////////////////////// +// GenericStreamWrapper + +//! A Stream Wrapper +/*! \tThis string stream is a wrapper for any stream by just forwarding any + \treceived message to the origin stream. + \note implements Stream concept +*/ + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +template > +class GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + GenericStreamWrapper(InputStream& is): is_(is) {} + + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() { return is_.Tell(); } + Ch* PutBegin() { return is_.PutBegin(); } + void Put(Ch ch) { is_.Put(ch); } + void Flush() { is_.Flush(); } + size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } + + // wrapper for MemoryStream + const Ch* Peek4() const { return is_.Peek4(); } + + // wrapper for AutoUTFInputStream + UTFType GetType() const { return is_.GetType(); } + bool HasBOM() const { return is_.HasBOM(); } + +protected: + InputStream& is_; +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + /////////////////////////////////////////////////////////////////////////////// // StringStream diff --git a/external/rapidjson/writer.h b/external/rapidjson/writer.h index 5b3004b..6f5b690 100644 --- a/external/rapidjson/writer.h +++ b/external/rapidjson/writer.h @@ -16,6 +16,7 @@ #define RAPIDJSON_WRITER_H_ #include "stream.h" +#include "internal/meta.h" #include "internal/stack.h" #include "internal/strfunc.h" #include "internal/dtoa.h" @@ -31,11 +32,8 @@ #include #elif defined(RAPIDJSON_SSE2) #include -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#elif defined(RAPIDJSON_NEON) +#include #endif #ifdef __clang__ @@ -43,6 +41,9 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(unreachable-code) RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #endif RAPIDJSON_NAMESPACE_BEGIN @@ -219,10 +220,18 @@ public: bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif + bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); - RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value level_stack_.template Pop(1); return EndValue(WriteEndObject()); } @@ -246,9 +255,9 @@ public: //@{ //! Simpler but slower overload. - bool String(const Ch* str) { return String(str, internal::StrLen(str)); } - bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } - + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } + //@} //! Write a raw JSON value. @@ -265,6 +274,14 @@ public: return EndValue(WriteRawValue(json, length)); } + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } + protected: //! Information for each nested level struct Level { @@ -441,9 +458,13 @@ protected: bool WriteRawValue(const Ch* json, size_t length) { PutReserve(*os_, length); - for (size_t i = 0; i < length; i++) { - RAPIDJSON_ASSERT(json[i] != '\0'); - PutUnsafe(*os_, json[i]); + GenericStringStream is(json); + while (RAPIDJSON_LIKELY(is.Tell() < length)) { + RAPIDJSON_ASSERT(is.Peek() != '\0'); + if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; } return true; } @@ -471,7 +492,7 @@ protected: // Flush the value if it is the top level one. bool EndValue(bool ret) { if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text - os_->Flush(); + Flush(); return ret; } @@ -575,7 +596,7 @@ inline bool Writer::ScanWriteUnescapedString(StringStream& is, siz // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -584,7 +605,7 @@ inline bool Writer::ScanWriteUnescapedString(StringStream& is, siz const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -609,15 +630,79 @@ inline bool Writer::ScanWriteUnescapedString(StringStream& is, siz is.src_ = p; return RAPIDJSON_LIKELY(is.Tell() < length); } -#endif // defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON RAPIDJSON_NAMESPACE_END -#ifdef _MSC_VER -RAPIDJSON_DIAG_POP -#endif - -#ifdef __clang__ +#if defined(_MSC_VER) || defined(__clang__) RAPIDJSON_DIAG_POP #endif diff --git a/external/tinyobjloader/LICENSE b/external/tinyobjloader/LICENSE new file mode 100644 index 0000000..3af18ab --- /dev/null +++ b/external/tinyobjloader/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2012-2019 Syoyo Fujita and many contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/external/tinyobjloader/tiny_obj_loader.cc b/external/tinyobjloader/tiny_obj_loader.cc new file mode 100644 index 0000000..e57d044 --- /dev/null +++ b/external/tinyobjloader/tiny_obj_loader.cc @@ -0,0 +1,2 @@ +#define TINYOBJLOADER_IMPLEMENTATION +#include "tiny_obj_loader.h" diff --git a/external/tinyobjloader/tiny_obj_loader.h b/external/tinyobjloader/tiny_obj_loader.h new file mode 100644 index 0000000..62815c8 --- /dev/null +++ b/external/tinyobjloader/tiny_obj_loader.h @@ -0,0 +1,2927 @@ +/* +The MIT License (MIT) + +Copyright (c) 2012-2018 Syoyo Fujita and many contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// +// version 2.0.0 : Add new object oriented API. 1.x API is still provided. +// * Support line primitive. +// * Support points primitive. +// version 1.4.0 : Modifed ParseTextureNameAndOption API +// version 1.3.1 : Make ParseTextureNameAndOption API public +// version 1.3.0 : Separate warning and error message(breaking API of LoadObj) +// version 1.2.3 : Added color space extension('-colorspace') to tex opts. +// version 1.2.2 : Parse multiple group names. +// version 1.2.1 : Added initial support for line('l') primitive(PR #178) +// version 1.2.0 : Hardened implementation(#175) +// version 1.1.1 : Support smoothing groups(#162) +// version 1.1.0 : Support parsing vertex color(#144) +// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138) +// version 1.0.7 : Support multiple tex options(#126) +// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124) +// version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43) +// version 1.0.4 : Support multiple filenames for 'mtllib'(#112) +// version 1.0.3 : Support parsing texture options(#85) +// version 1.0.2 : Improve parsing speed by about a factor of 2 for large +// files(#105) +// version 1.0.1 : Fixes a shape is lost if obj ends with a 'usemtl'(#104) +// version 1.0.0 : Change data structure. Change license from BSD to MIT. +// + +// +// Use this in *one* .cc +// #define TINYOBJLOADER_IMPLEMENTATION +// #include "tiny_obj_loader.h" +// + +#ifndef TINY_OBJ_LOADER_H_ +#define TINY_OBJ_LOADER_H_ + +#include +#include +#include + +namespace tinyobj { + +#ifdef __clang__ +#pragma clang diagnostic push +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif + +#pragma clang diagnostic ignored "-Wpadded" + +#endif + +// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ... +// +// -blendu on | off # set horizontal texture blending +// (default on) +// -blendv on | off # set vertical texture blending +// (default on) +// -boost real_value # boost mip-map sharpness +// -mm base_value gain_value # modify texture map values (default +// 0 1) +// # base_value = brightness, +// gain_value = contrast +// -o u [v [w]] # Origin offset (default +// 0 0 0) +// -s u [v [w]] # Scale (default +// 1 1 1) +// -t u [v [w]] # Turbulence (default +// 0 0 0) +// -texres resolution # texture resolution to create +// -clamp on | off # only render texels in the clamped +// 0-1 range (default off) +// # When unclamped, textures are +// repeated across a surface, +// # when clamped, only texels which +// fall within the 0-1 +// # range are rendered. +// -bm mult_value # bump multiplier (for bump maps +// only) +// +// -imfchan r | g | b | m | l | z # specifies which channel of the file +// is used to +// # create a scalar or bump texture. +// r:red, g:green, +// # b:blue, m:matte, l:luminance, +// z:z-depth.. +// # (the default for bump is 'l' and +// for decal is 'm') +// bump -imfchan r bumpmap.tga # says to use the red channel of +// bumpmap.tga as the bumpmap +// +// For reflection maps... +// +// -type sphere # specifies a sphere for a "refl" +// reflection map +// -type cube_top | cube_bottom | # when using a cube map, the texture +// file for each +// cube_front | cube_back | # side of the cube is specified +// separately +// cube_left | cube_right +// +// TinyObjLoader extension. +// +// -colorspace SPACE # Color space of the texture. e.g. +// 'sRGB` or 'linear' +// + +#ifdef TINYOBJLOADER_USE_DOUBLE +//#pragma message "using double" +typedef double real_t; +#else +//#pragma message "using float" +typedef float real_t; +#endif + +typedef enum { + TEXTURE_TYPE_NONE, // default + TEXTURE_TYPE_SPHERE, + TEXTURE_TYPE_CUBE_TOP, + TEXTURE_TYPE_CUBE_BOTTOM, + TEXTURE_TYPE_CUBE_FRONT, + TEXTURE_TYPE_CUBE_BACK, + TEXTURE_TYPE_CUBE_LEFT, + TEXTURE_TYPE_CUBE_RIGHT +} texture_type_t; + +typedef struct { + texture_type_t type; // -type (default TEXTURE_TYPE_NONE) + real_t sharpness; // -boost (default 1.0?) + real_t brightness; // base_value in -mm option (default 0) + real_t contrast; // gain_value in -mm option (default 1) + real_t origin_offset[3]; // -o u [v [w]] (default 0 0 0) + real_t scale[3]; // -s u [v [w]] (default 1 1 1) + real_t turbulence[3]; // -t u [v [w]] (default 0 0 0) + // int texture_resolution; // -texres resolution (default = ?) TODO + bool clamp; // -clamp (default false) + char imfchan; // -imfchan (the default for bump is 'l' and for decal is 'm') + bool blendu; // -blendu (default on) + bool blendv; // -blendv (default on) + real_t bump_multiplier; // -bm (for bump maps only, default 1.0) + + // extension + std::string colorspace; // Explicitly specify color space of stored texel + // value. Usually `sRGB` or `linear` (default empty). +} texture_option_t; + +typedef struct { + std::string name; + + real_t ambient[3]; + real_t diffuse[3]; + real_t specular[3]; + real_t transmittance[3]; + real_t emission[3]; + real_t shininess; + real_t ior; // index of refraction + real_t dissolve; // 1 == opaque; 0 == fully transparent + // illumination model (see http://www.fileformat.info/format/material/) + int illum; + + int dummy; // Suppress padding warning. + + std::string ambient_texname; // map_Ka + std::string diffuse_texname; // map_Kd + std::string specular_texname; // map_Ks + std::string specular_highlight_texname; // map_Ns + std::string bump_texname; // map_bump, map_Bump, bump + std::string displacement_texname; // disp + std::string alpha_texname; // map_d + std::string reflection_texname; // refl + + texture_option_t ambient_texopt; + texture_option_t diffuse_texopt; + texture_option_t specular_texopt; + texture_option_t specular_highlight_texopt; + texture_option_t bump_texopt; + texture_option_t displacement_texopt; + texture_option_t alpha_texopt; + texture_option_t reflection_texopt; + + // PBR extension + // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr + real_t roughness; // [0, 1] default 0 + real_t metallic; // [0, 1] default 0 + real_t sheen; // [0, 1] default 0 + real_t clearcoat_thickness; // [0, 1] default 0 + real_t clearcoat_roughness; // [0, 1] default 0 + real_t anisotropy; // aniso. [0, 1] default 0 + real_t anisotropy_rotation; // anisor. [0, 1] default 0 + real_t pad0; + std::string roughness_texname; // map_Pr + std::string metallic_texname; // map_Pm + std::string sheen_texname; // map_Ps + std::string emissive_texname; // map_Ke + std::string normal_texname; // norm. For normal mapping. + + texture_option_t roughness_texopt; + texture_option_t metallic_texopt; + texture_option_t sheen_texopt; + texture_option_t emissive_texopt; + texture_option_t normal_texopt; + + int pad2; + + std::map unknown_parameter; + +#ifdef TINY_OBJ_LOADER_PYTHON_BINDING + // For pybind11 + std::array GetDiffuse() { + std::array values; + values[0] = double(diffuse[0]); + values[1] = double(diffuse[1]); + values[2] = double(diffuse[2]); + + return values; + } + + std::array GetSpecular() { + std::array values; + values[0] = double(specular[0]); + values[1] = double(specular[1]); + values[2] = double(specular[2]); + + return values; + } + + std::array GetTransmittance() { + std::array values; + values[0] = double(transmittance[0]); + values[1] = double(transmittance[1]); + values[2] = double(transmittance[2]); + + return values; + } + + std::array GetEmission() { + std::array values; + values[0] = double(emission[0]); + values[1] = double(emission[1]); + values[2] = double(emission[2]); + + return values; + } + + std::array GetAmbient() { + std::array values; + values[0] = double(ambient[0]); + values[1] = double(ambient[1]); + values[2] = double(ambient[2]); + + return values; + } + + void SetDiffuse(std::array &a) { + diffuse[0] = real_t(a[0]); + diffuse[1] = real_t(a[1]); + diffuse[2] = real_t(a[2]); + } + + void SetAmbient(std::array &a) { + ambient[0] = real_t(a[0]); + ambient[1] = real_t(a[1]); + ambient[2] = real_t(a[2]); + } + + void SetSpecular(std::array &a) { + specular[0] = real_t(a[0]); + specular[1] = real_t(a[1]); + specular[2] = real_t(a[2]); + } + + void SetTransmittance(std::array &a) { + transmittance[0] = real_t(a[0]); + transmittance[1] = real_t(a[1]); + transmittance[2] = real_t(a[2]); + } + + std::string GetCustomParameter(const std::string &key) { + std::map::const_iterator it = + unknown_parameter.find(key); + + if (it != unknown_parameter.end()) { + return it->second; + } + return std::string(); + } + +#endif + +} material_t; + +typedef struct { + std::string name; + + std::vector intValues; + std::vector floatValues; + std::vector stringValues; +} tag_t; + +// Index struct to support different indices for vtx/normal/texcoord. +// -1 means not used. +typedef struct { + int vertex_index; + int normal_index; + int texcoord_index; +} index_t; + +typedef struct { + std::vector indices; + std::vector + num_face_vertices; // The number of vertices per + // face. 3 = triangle, 4 = quad, + // ... Up to 255 vertices per face. + std::vector material_ids; // per-face material ID + std::vector smoothing_group_ids; // per-face smoothing group + // ID(0 = off. positive value + // = group id) + std::vector tags; // SubD tag +} mesh_t; + +// typedef struct { +// std::vector indices; // pairs of indices for lines +//} path_t; + +typedef struct { + // Linear flattened indices. + std::vector indices; // indices for vertices(poly lines) + std::vector num_line_vertices; // The number of vertices per line. +} lines_t; + +typedef struct { + std::vector indices; // indices for points +} points_t; + +typedef struct { + std::string name; + mesh_t mesh; + lines_t lines; + points_t points; +} shape_t; + +// Vertex attributes +struct attrib_t { + std::vector vertices; // 'v'(xyz) + + // For backward compatibility, we store vertex weight in separate array. + std::vector vertex_weights; // 'v'(w) + std::vector normals; // 'vn' + std::vector texcoords; // 'vt'(uv) + + // For backward compatibility, we store texture coordinate 'w' in separate + // array. + std::vector texcoord_ws; // 'vt'(w) + std::vector colors; // extension: vertex colors + + attrib_t() {} + + // + // For pybind11 + // + const std::vector &GetVertices() const { return vertices; } + + const std::vector &GetVertexWeights() const { return vertex_weights; } +}; + +typedef struct callback_t_ { + // W is optional and set to 1 if there is no `w` item in `v` line + void (*vertex_cb)(void *user_data, real_t x, real_t y, real_t z, real_t w); + void (*normal_cb)(void *user_data, real_t x, real_t y, real_t z); + + // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in + // `vt` line. + void (*texcoord_cb)(void *user_data, real_t x, real_t y, real_t z); + + // called per 'f' line. num_indices is the number of face indices(e.g. 3 for + // triangle, 4 for quad) + // 0 will be passed for undefined index in index_t members. + void (*index_cb)(void *user_data, index_t *indices, int num_indices); + // `name` material name, `material_id` = the array index of material_t[]. -1 + // if + // a material not found in .mtl + void (*usemtl_cb)(void *user_data, const char *name, int material_id); + // `materials` = parsed material data. + void (*mtllib_cb)(void *user_data, const material_t *materials, + int num_materials); + // There may be multiple group names + void (*group_cb)(void *user_data, const char **names, int num_names); + void (*object_cb)(void *user_data, const char *name); + + callback_t_() + : vertex_cb(NULL), + normal_cb(NULL), + texcoord_cb(NULL), + index_cb(NULL), + usemtl_cb(NULL), + mtllib_cb(NULL), + group_cb(NULL), + object_cb(NULL) {} +} callback_t; + +class MaterialReader { + public: + MaterialReader() {} + virtual ~MaterialReader(); + + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *warn, + std::string *err) = 0; +}; + +/// +/// Read .mtl from a file. +/// +class MaterialFileReader : public MaterialReader { + public: + explicit MaterialFileReader(const std::string &mtl_basedir) + : m_mtlBaseDir(mtl_basedir) {} + virtual ~MaterialFileReader() {} + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *warn, + std::string *err); + + private: + std::string m_mtlBaseDir; +}; + +/// +/// Read .mtl from a stream. +/// +class MaterialStreamReader : public MaterialReader { + public: + explicit MaterialStreamReader(std::istream &inStream) + : m_inStream(inStream) {} + virtual ~MaterialStreamReader() {} + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *warn, + std::string *err); + + private: + std::istream &m_inStream; +}; + +// v2 API +struct ObjReaderConfig { + bool triangulate; // triangulate polygon? + + /// Parse vertex color. + /// If vertex color is not present, its filled with default value. + /// false = no vertex color + /// This will increase memory of parsed .obj + bool vertex_color; + + /// + /// Search path to .mtl file. + /// Default = "" = search from the same directory of .obj file. + /// Valid only when loading .obj from a file. + /// + std::string mtl_search_path; + + ObjReaderConfig() : triangulate(true), vertex_color(true) {} +}; + +/// +/// Wavefront .obj reader class(v2 API) +/// +class ObjReader { + public: + ObjReader() : valid_(false) {} + ~ObjReader() {} + + /// + /// Load .obj and .mtl from a file. + /// + /// @param[in] filename wavefront .obj filename + /// @param[in] config Reader configuration + /// + bool ParseFromFile(const std::string &filename, + const ObjReaderConfig &config = ObjReaderConfig()); + + /// + /// Parse .obj from a text string. + /// Need to supply .mtl text string by `mtl_text`. + /// This function ignores `mtllib` line in .obj text. + /// + /// @param[in] obj_text wavefront .obj filename + /// @param[in] mtl_text wavefront .mtl filename + /// @param[in] config Reader configuration + /// + bool ParseFromString(const std::string &obj_text, const std::string &mtl_text, + const ObjReaderConfig &config = ObjReaderConfig()); + + /// + /// .obj was loaded or parsed correctly. + /// + bool Valid() const { return valid_; } + + const attrib_t &GetAttrib() const { return attrib_; } + + const std::vector &GetShapes() const { return shapes_; } + + const std::vector &GetMaterials() const { return materials_; } + + /// + /// Warning message(may be filled after `Load` or `Parse`) + /// + const std::string &Warning() const { return warning_; } + + /// + /// Error message(filled when `Load` or `Parse` failed) + /// + const std::string &Error() const { return error_; } + + private: + bool valid_; + + attrib_t attrib_; + std::vector shapes_; + std::vector materials_; + + std::string warning_; + std::string error_; +}; + +/// ==>>========= Legacy v1 API ============================================= + +/// Loads .obj from a file. +/// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data +/// 'shapes' will be filled with parsed shape data +/// Returns true when loading .obj become success. +/// Returns warning message into `warn`, and error message into `err` +/// 'mtl_basedir' is optional, and used for base directory for .mtl file. +/// In default(`NULL'), .mtl file is searched from an application's working +/// directory. +/// 'triangulate' is optional, and used whether triangulate polygon face in .obj +/// or not. +/// Option 'default_vcols_fallback' specifies whether vertex colors should +/// always be defined, even if no colors are given (fallback to white). +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *warn, + std::string *err, const char *filename, + const char *mtl_basedir = NULL, bool triangulate = true, + bool default_vcols_fallback = true); + +/// Loads .obj from a file with custom user callback. +/// .mtl is loaded as usual and parsed material_t data will be passed to +/// `callback.mtllib_cb`. +/// Returns true when loading .obj/.mtl become success. +/// Returns warning message into `warn`, and error message into `err` +/// See `examples/callback_api/` for how to use this function. +bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, + void *user_data = NULL, + MaterialReader *readMatFn = NULL, + std::string *warn = NULL, std::string *err = NULL); + +/// Loads object from a std::istream, uses `readMatFn` to retrieve +/// std::istream for materials. +/// Returns true when loading .obj become success. +/// Returns warning and error message into `err` +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *warn, + std::string *err, std::istream *inStream, + MaterialReader *readMatFn = NULL, bool triangulate = true, + bool default_vcols_fallback = true); + +/// Loads materials into std::map +void LoadMtl(std::map *material_map, + std::vector *materials, std::istream *inStream, + std::string *warning, std::string *err); + +/// +/// Parse texture name and texture option for custom texture parameter through +/// material::unknown_parameter +/// +/// @param[out] texname Parsed texture name +/// @param[out] texopt Parsed texopt +/// @param[in] linebuf Input string +/// +bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, + const char *linebuf); + +/// =<<========== Legacy v1 API ============================================= + +} // namespace tinyobj + +#endif // TINY_OBJ_LOADER_H_ + +#ifdef TINYOBJLOADER_IMPLEMENTATION +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace tinyobj { + +MaterialReader::~MaterialReader() {} + +struct vertex_index_t { + int v_idx, vt_idx, vn_idx; + vertex_index_t() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} + explicit vertex_index_t(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} + vertex_index_t(int vidx, int vtidx, int vnidx) + : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} +}; + +// Internal data structure for face representation +// index + smoothing group. +struct face_t { + unsigned int + smoothing_group_id; // smoothing group id. 0 = smoothing groupd is off. + int pad_; + std::vector vertex_indices; // face vertex indices. + + face_t() : smoothing_group_id(0), pad_(0) {} +}; + +// Internal data structure for line representation +struct __line_t { + // l v1/vt1 v2/vt2 ... + // In the specification, line primitrive does not have normal index, but + // TinyObjLoader allow it + std::vector vertex_indices; +}; + +// Internal data structure for points representation +struct __points_t { + // p v1 v2 ... + // In the specification, point primitrive does not have normal index and + // texture coord index, but TinyObjLoader allow it. + std::vector vertex_indices; +}; + +struct tag_sizes { + tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {} + int num_ints; + int num_reals; + int num_strings; +}; + +struct obj_shape { + std::vector v; + std::vector vn; + std::vector vt; +}; + +// +// Manages group of primitives(face, line, points, ...) +struct PrimGroup { + std::vector faceGroup; + std::vector<__line_t> lineGroup; + std::vector<__points_t> pointsGroup; + + void clear() { + faceGroup.clear(); + lineGroup.clear(); + pointsGroup.clear(); + } + + bool IsEmpty() const { + return faceGroup.empty() && lineGroup.empty() && pointsGroup.empty(); + } + + // TODO(syoyo): bspline, surface, ... +}; + +// See +// http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf +static std::istream &safeGetline(std::istream &is, std::string &t) { + t.clear(); + + // The characters in the stream are read one-by-one using a std::streambuf. + // That is faster than reading them one-by-one using the std::istream. + // Code that uses streambuf this way must be guarded by a sentry object. + // The sentry object performs various tasks, + // such as thread synchronization and updating the stream state. + + std::istream::sentry se(is, true); + std::streambuf *sb = is.rdbuf(); + + if (se) { + for (;;) { + int c = sb->sbumpc(); + switch (c) { + case '\n': + return is; + case '\r': + if (sb->sgetc() == '\n') sb->sbumpc(); + return is; + case EOF: + // Also handle the case when the last line has no line ending + if (t.empty()) is.setstate(std::ios::eofbit); + return is; + default: + t += static_cast(c); + } + } + } + + return is; +} + +#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) +#define IS_DIGIT(x) \ + (static_cast((x) - '0') < static_cast(10)) +#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) + +// Make index zero-base, and also support relative index. +static inline bool fixIndex(int idx, int n, int *ret) { + if (!ret) { + return false; + } + + if (idx > 0) { + (*ret) = idx - 1; + return true; + } + + if (idx == 0) { + // zero is not allowed according to the spec. + return false; + } + + if (idx < 0) { + (*ret) = n + idx; // negative value = relative + return true; + } + + return false; // never reach here. +} + +static inline std::string parseString(const char **token) { + std::string s; + (*token) += strspn((*token), " \t"); + size_t e = strcspn((*token), " \t\r"); + s = std::string((*token), &(*token)[e]); + (*token) += e; + return s; +} + +static inline int parseInt(const char **token) { + (*token) += strspn((*token), " \t"); + int i = atoi((*token)); + (*token) += strcspn((*token), " \t\r"); + return i; +} + +// Tries to parse a floating point number located at s. +// +// s_end should be a location in the string where reading should absolutely +// stop. For example at the end of the string, to prevent buffer overflows. +// +// Parses the following EBNF grammar: +// sign = "+" | "-" ; +// END = ? anything not in digit ? +// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; +// integer = [sign] , digit , {digit} ; +// decimal = integer , ["." , integer] ; +// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; +// +// Valid strings are for example: +// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 +// +// If the parsing is a success, result is set to the parsed value and true +// is returned. +// +// The function is greedy and will parse until any of the following happens: +// - a non-conforming character is encountered. +// - s_end is reached. +// +// The following situations triggers a failure: +// - s >= s_end. +// - parse failure. +// +static bool tryParseDouble(const char *s, const char *s_end, double *result) { + if (s >= s_end) { + return false; + } + + double mantissa = 0.0; + // This exponent is base 2 rather than 10. + // However the exponent we parse is supposed to be one of ten, + // thus we must take care to convert the exponent/and or the + // mantissa to a * 2^E, where a is the mantissa and E is the + // exponent. + // To get the final double we will use ldexp, it requires the + // exponent to be in base 2. + int exponent = 0; + + // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED + // TO JUMP OVER DEFINITIONS. + char sign = '+'; + char exp_sign = '+'; + char const *curr = s; + + // How many characters were read in a loop. + int read = 0; + // Tells whether a loop terminated due to reaching s_end. + bool end_not_reached = false; + bool leading_decimal_dots = false; + + /* + BEGIN PARSING. + */ + + // Find out what sign we've got. + if (*curr == '+' || *curr == '-') { + sign = *curr; + curr++; + if ((curr != s_end) && (*curr == '.')) { + // accept. Somethig like `.7e+2`, `-.5234` + leading_decimal_dots = true; + } + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else if (*curr == '.') { + // accept. Somethig like `.7e+2`, `-.5234` + leading_decimal_dots = true; + } else { + goto fail; + } + + // Read the integer part. + end_not_reached = (curr != s_end); + if (!leading_decimal_dots) { + while (end_not_reached && IS_DIGIT(*curr)) { + mantissa *= 10; + mantissa += static_cast(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + } + + // We must make sure we actually got something. + if (!leading_decimal_dots) { + if (read == 0) goto fail; + } + + // We allow numbers of form "#", "###" etc. + if (!end_not_reached) goto assemble; + + // Read the decimal part. + if (*curr == '.') { + curr++; + read = 1; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + static const double pow_lut[] = { + 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, + }; + const int lut_entries = sizeof pow_lut / sizeof pow_lut[0]; + + // NOTE: Don't use powf here, it will absolutely murder precision. + mantissa += static_cast(*curr - 0x30) * + (read < lut_entries ? pow_lut[read] : std::pow(10.0, -read)); + read++; + curr++; + end_not_reached = (curr != s_end); + } + } else if (*curr == 'e' || *curr == 'E') { + } else { + goto assemble; + } + + if (!end_not_reached) goto assemble; + + // Read the exponent part. + if (*curr == 'e' || *curr == 'E') { + curr++; + // Figure out if a sign is present and if it is. + end_not_reached = (curr != s_end); + if (end_not_reached && (*curr == '+' || *curr == '-')) { + exp_sign = *curr; + curr++; + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else { + // Empty E is not allowed. + goto fail; + } + + read = 0; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + exponent *= 10; + exponent += static_cast(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + exponent *= (exp_sign == '+' ? 1 : -1); + if (read == 0) goto fail; + } + +assemble: + *result = (sign == '+' ? 1 : -1) * + (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) + : mantissa); + return true; +fail: + return false; +} + +static inline real_t parseReal(const char **token, double default_value = 0.0) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + double val = default_value; + tryParseDouble((*token), end, &val); + real_t f = static_cast(val); + (*token) = end; + return f; +} + +static inline bool parseReal(const char **token, real_t *out) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + double val; + bool ret = tryParseDouble((*token), end, &val); + if (ret) { + real_t f = static_cast(val); + (*out) = f; + } + (*token) = end; + return ret; +} + +static inline void parseReal2(real_t *x, real_t *y, const char **token, + const double default_x = 0.0, + const double default_y = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); +} + +static inline void parseReal3(real_t *x, real_t *y, real_t *z, + const char **token, const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); +} + +static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w, + const char **token, const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0, + const double default_w = 1.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); + (*w) = parseReal(token, default_w); +} + +// Extension: parse vertex with colors(6 items) +static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z, + real_t *r, real_t *g, real_t *b, + const char **token, + const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); + + const bool found_color = + parseReal(token, r) && parseReal(token, g) && parseReal(token, b); + + if (!found_color) { + (*r) = (*g) = (*b) = 1.0; + } + + return found_color; +} + +static inline bool parseOnOff(const char **token, bool default_value = true) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + + bool ret = default_value; + if ((0 == strncmp((*token), "on", 2))) { + ret = true; + } else if ((0 == strncmp((*token), "off", 3))) { + ret = false; + } + + (*token) = end; + return ret; +} + +static inline texture_type_t parseTextureType( + const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + texture_type_t ty = default_value; + + if ((0 == strncmp((*token), "cube_top", strlen("cube_top")))) { + ty = TEXTURE_TYPE_CUBE_TOP; + } else if ((0 == strncmp((*token), "cube_bottom", strlen("cube_bottom")))) { + ty = TEXTURE_TYPE_CUBE_BOTTOM; + } else if ((0 == strncmp((*token), "cube_left", strlen("cube_left")))) { + ty = TEXTURE_TYPE_CUBE_LEFT; + } else if ((0 == strncmp((*token), "cube_right", strlen("cube_right")))) { + ty = TEXTURE_TYPE_CUBE_RIGHT; + } else if ((0 == strncmp((*token), "cube_front", strlen("cube_front")))) { + ty = TEXTURE_TYPE_CUBE_FRONT; + } else if ((0 == strncmp((*token), "cube_back", strlen("cube_back")))) { + ty = TEXTURE_TYPE_CUBE_BACK; + } else if ((0 == strncmp((*token), "sphere", strlen("sphere")))) { + ty = TEXTURE_TYPE_SPHERE; + } + + (*token) = end; + return ty; +} + +static tag_sizes parseTagTriple(const char **token) { + tag_sizes ts; + + (*token) += strspn((*token), " \t"); + ts.num_ints = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return ts; + } + + (*token)++; // Skip '/' + + (*token) += strspn((*token), " \t"); + ts.num_reals = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return ts; + } + (*token)++; // Skip '/' + + ts.num_strings = parseInt(token); + + return ts; +} + +// Parse triples with index offsets: i, i/j/k, i//k, i/j +static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize, + vertex_index_t *ret) { + if (!ret) { + return false; + } + + vertex_index_t vi(-1); + + if (!fixIndex(atoi((*token)), vsize, &(vi.v_idx))) { + return false; + } + + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + (*ret) = vi; + return true; + } + (*token)++; + + // i//k + if ((*token)[0] == '/') { + (*token)++; + if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) { + return false; + } + (*token) += strcspn((*token), "/ \t\r"); + (*ret) = vi; + return true; + } + + // i/j/k or i/j + if (!fixIndex(atoi((*token)), vtsize, &(vi.vt_idx))) { + return false; + } + + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + (*ret) = vi; + return true; + } + + // i/j/k + (*token)++; // skip '/' + if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) { + return false; + } + (*token) += strcspn((*token), "/ \t\r"); + + (*ret) = vi; + + return true; +} + +// Parse raw triples: i, i/j/k, i//k, i/j +static vertex_index_t parseRawTriple(const char **token) { + vertex_index_t vi(static_cast(0)); // 0 is an invalid index in OBJ + + vi.v_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + (*token)++; + + // i//k + if ((*token)[0] == '/') { + (*token)++; + vi.vn_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + return vi; + } + + // i/j/k or i/j + vi.vt_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + + // i/j/k + (*token)++; // skip '/' + vi.vn_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + return vi; +} + +bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, + const char *linebuf) { + // @todo { write more robust lexer and parser. } + bool found_texname = false; + std::string texture_name; + + const char *token = linebuf; // Assume line ends with NULL + + while (!IS_NEW_LINE((*token))) { + token += strspn(token, " \t"); // skip space + if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) { + token += 8; + texopt->blendu = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-blendv", 7)) && IS_SPACE((token[7]))) { + token += 8; + texopt->blendv = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-clamp", 6)) && IS_SPACE((token[6]))) { + token += 7; + texopt->clamp = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-boost", 6)) && IS_SPACE((token[6]))) { + token += 7; + texopt->sharpness = parseReal(&token, 1.0); + } else if ((0 == strncmp(token, "-bm", 3)) && IS_SPACE((token[3]))) { + token += 4; + texopt->bump_multiplier = parseReal(&token, 1.0); + } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]), + &(texopt->origin_offset[2]), &token); + } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]), + &token, 1.0, 1.0, 1.0); + } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]), + &(texopt->turbulence[2]), &token); + } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) { + token += 5; + texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE); + } else if ((0 == strncmp(token, "-imfchan", 8)) && IS_SPACE((token[8]))) { + token += 9; + token += strspn(token, " \t"); + const char *end = token + strcspn(token, " \t\r"); + if ((end - token) == 1) { // Assume one char for -imfchan + texopt->imfchan = (*token); + } + token = end; + } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) { + token += 4; + parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); + } else if ((0 == strncmp(token, "-colorspace", 11)) && + IS_SPACE((token[11]))) { + token += 12; + texopt->colorspace = parseString(&token); + } else { +// Assume texture filename +#if 0 + size_t len = strcspn(token, " \t\r"); // untile next space + texture_name = std::string(token, token + len); + token += len; + + token += strspn(token, " \t"); // skip space +#else + // Read filename until line end to parse filename containing whitespace + // TODO(syoyo): Support parsing texture option flag after the filename. + texture_name = std::string(token); + token += texture_name.length(); +#endif + + found_texname = true; + } + } + + if (found_texname) { + (*texname) = texture_name; + return true; + } else { + return false; + } +} + +static void InitTexOpt(texture_option_t *texopt, const bool is_bump) { + if (is_bump) { + texopt->imfchan = 'l'; + } else { + texopt->imfchan = 'm'; + } + texopt->bump_multiplier = static_cast(1.0); + texopt->clamp = false; + texopt->blendu = true; + texopt->blendv = true; + texopt->sharpness = static_cast(1.0); + texopt->brightness = static_cast(0.0); + texopt->contrast = static_cast(1.0); + texopt->origin_offset[0] = static_cast(0.0); + texopt->origin_offset[1] = static_cast(0.0); + texopt->origin_offset[2] = static_cast(0.0); + texopt->scale[0] = static_cast(1.0); + texopt->scale[1] = static_cast(1.0); + texopt->scale[2] = static_cast(1.0); + texopt->turbulence[0] = static_cast(0.0); + texopt->turbulence[1] = static_cast(0.0); + texopt->turbulence[2] = static_cast(0.0); + texopt->type = TEXTURE_TYPE_NONE; +} + +static void InitMaterial(material_t *material) { + InitTexOpt(&material->ambient_texopt, /* is_bump */ false); + InitTexOpt(&material->diffuse_texopt, /* is_bump */ false); + InitTexOpt(&material->specular_texopt, /* is_bump */ false); + InitTexOpt(&material->specular_highlight_texopt, /* is_bump */ false); + InitTexOpt(&material->bump_texopt, /* is_bump */ true); + InitTexOpt(&material->displacement_texopt, /* is_bump */ false); + InitTexOpt(&material->alpha_texopt, /* is_bump */ false); + InitTexOpt(&material->reflection_texopt, /* is_bump */ false); + InitTexOpt(&material->roughness_texopt, /* is_bump */ false); + InitTexOpt(&material->metallic_texopt, /* is_bump */ false); + InitTexOpt(&material->sheen_texopt, /* is_bump */ false); + InitTexOpt(&material->emissive_texopt, /* is_bump */ false); + InitTexOpt(&material->normal_texopt, + /* is_bump */ false); // @fixme { is_bump will be true? } + material->name = ""; + material->ambient_texname = ""; + material->diffuse_texname = ""; + material->specular_texname = ""; + material->specular_highlight_texname = ""; + material->bump_texname = ""; + material->displacement_texname = ""; + material->reflection_texname = ""; + material->alpha_texname = ""; + for (int i = 0; i < 3; i++) { + material->ambient[i] = static_cast(0.0); + material->diffuse[i] = static_cast(0.0); + material->specular[i] = static_cast(0.0); + material->transmittance[i] = static_cast(0.0); + material->emission[i] = static_cast(0.0); + } + material->illum = 0; + material->dissolve = static_cast(1.0); + material->shininess = static_cast(1.0); + material->ior = static_cast(1.0); + + material->roughness = static_cast(0.0); + material->metallic = static_cast(0.0); + material->sheen = static_cast(0.0); + material->clearcoat_thickness = static_cast(0.0); + material->clearcoat_roughness = static_cast(0.0); + material->anisotropy_rotation = static_cast(0.0); + material->anisotropy = static_cast(0.0); + material->roughness_texname = ""; + material->metallic_texname = ""; + material->sheen_texname = ""; + material->emissive_texname = ""; + material->normal_texname = ""; + + material->unknown_parameter.clear(); +} + +// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html +template +static int pnpoly(int nvert, T *vertx, T *verty, T testx, T testy) { + int i, j, c = 0; + for (i = 0, j = nvert - 1; i < nvert; j = i++) { + if (((verty[i] > testy) != (verty[j] > testy)) && + (testx < + (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + + vertx[i])) + c = !c; + } + return c; +} + +// TODO(syoyo): refactor function. +static bool exportGroupsToShape(shape_t *shape, const PrimGroup &prim_group, + const std::vector &tags, + const int material_id, const std::string &name, + bool triangulate, + const std::vector &v) { + if (prim_group.IsEmpty()) { + return false; + } + + shape->name = name; + + // polygon + if (!prim_group.faceGroup.empty()) { + // Flatten vertices and indices + for (size_t i = 0; i < prim_group.faceGroup.size(); i++) { + const face_t &face = prim_group.faceGroup[i]; + + size_t npolys = face.vertex_indices.size(); + + if (npolys < 3) { + // Face must have 3+ vertices. + continue; + } + + vertex_index_t i0 = face.vertex_indices[0]; + vertex_index_t i1(-1); + vertex_index_t i2 = face.vertex_indices[1]; + + if (triangulate) { + // find the two axes to work in + size_t axes[2] = {1, 2}; + for (size_t k = 0; k < npolys; ++k) { + i0 = face.vertex_indices[(k + 0) % npolys]; + i1 = face.vertex_indices[(k + 1) % npolys]; + i2 = face.vertex_indices[(k + 2) % npolys]; + size_t vi0 = size_t(i0.v_idx); + size_t vi1 = size_t(i1.v_idx); + size_t vi2 = size_t(i2.v_idx); + + if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) || + ((3 * vi2 + 2) >= v.size())) { + // Invalid triangle. + // FIXME(syoyo): Is it ok to simply skip this invalid triangle? + continue; + } + real_t v0x = v[vi0 * 3 + 0]; + real_t v0y = v[vi0 * 3 + 1]; + real_t v0z = v[vi0 * 3 + 2]; + real_t v1x = v[vi1 * 3 + 0]; + real_t v1y = v[vi1 * 3 + 1]; + real_t v1z = v[vi1 * 3 + 2]; + real_t v2x = v[vi2 * 3 + 0]; + real_t v2y = v[vi2 * 3 + 1]; + real_t v2z = v[vi2 * 3 + 2]; + real_t e0x = v1x - v0x; + real_t e0y = v1y - v0y; + real_t e0z = v1z - v0z; + real_t e1x = v2x - v1x; + real_t e1y = v2y - v1y; + real_t e1z = v2z - v1z; + real_t cx = std::fabs(e0y * e1z - e0z * e1y); + real_t cy = std::fabs(e0z * e1x - e0x * e1z); + real_t cz = std::fabs(e0x * e1y - e0y * e1x); + const real_t epsilon = std::numeric_limits::epsilon(); + if (cx > epsilon || cy > epsilon || cz > epsilon) { + // found a corner + if (cx > cy && cx > cz) { + } else { + axes[0] = 0; + if (cz > cx && cz > cy) axes[1] = 1; + } + break; + } + } + + real_t area = 0; + for (size_t k = 0; k < npolys; ++k) { + i0 = face.vertex_indices[(k + 0) % npolys]; + i1 = face.vertex_indices[(k + 1) % npolys]; + size_t vi0 = size_t(i0.v_idx); + size_t vi1 = size_t(i1.v_idx); + if (((vi0 * 3 + axes[0]) >= v.size()) || + ((vi0 * 3 + axes[1]) >= v.size()) || + ((vi1 * 3 + axes[0]) >= v.size()) || + ((vi1 * 3 + axes[1]) >= v.size())) { + // Invalid index. + continue; + } + real_t v0x = v[vi0 * 3 + axes[0]]; + real_t v0y = v[vi0 * 3 + axes[1]]; + real_t v1x = v[vi1 * 3 + axes[0]]; + real_t v1y = v[vi1 * 3 + axes[1]]; + area += (v0x * v1y - v0y * v1x) * static_cast(0.5); + } + + face_t remainingFace = face; // copy + size_t guess_vert = 0; + vertex_index_t ind[3]; + real_t vx[3]; + real_t vy[3]; + + // How many iterations can we do without decreasing the remaining + // vertices. + size_t remainingIterations = face.vertex_indices.size(); + size_t previousRemainingVertices = remainingFace.vertex_indices.size(); + + while (remainingFace.vertex_indices.size() > 3 && + remainingIterations > 0) { + npolys = remainingFace.vertex_indices.size(); + if (guess_vert >= npolys) { + guess_vert -= npolys; + } + + if (previousRemainingVertices != npolys) { + // The number of remaining vertices decreased. Reset counters. + previousRemainingVertices = npolys; + remainingIterations = npolys; + } else { + // We didn't consume a vertex on previous iteration, reduce the + // available iterations. + remainingIterations--; + } + + for (size_t k = 0; k < 3; k++) { + ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys]; + size_t vi = size_t(ind[k].v_idx); + if (((vi * 3 + axes[0]) >= v.size()) || + ((vi * 3 + axes[1]) >= v.size())) { + // ??? + vx[k] = static_cast(0.0); + vy[k] = static_cast(0.0); + } else { + vx[k] = v[vi * 3 + axes[0]]; + vy[k] = v[vi * 3 + axes[1]]; + } + } + real_t e0x = vx[1] - vx[0]; + real_t e0y = vy[1] - vy[0]; + real_t e1x = vx[2] - vx[1]; + real_t e1y = vy[2] - vy[1]; + real_t cross = e0x * e1y - e0y * e1x; + // if an internal angle + if (cross * area < static_cast(0.0)) { + guess_vert += 1; + continue; + } + + // check all other verts in case they are inside this triangle + bool overlap = false; + for (size_t otherVert = 3; otherVert < npolys; ++otherVert) { + size_t idx = (guess_vert + otherVert) % npolys; + + if (idx >= remainingFace.vertex_indices.size()) { + // ??? + continue; + } + + size_t ovi = size_t(remainingFace.vertex_indices[idx].v_idx); + + if (((ovi * 3 + axes[0]) >= v.size()) || + ((ovi * 3 + axes[1]) >= v.size())) { + // ??? + continue; + } + real_t tx = v[ovi * 3 + axes[0]]; + real_t ty = v[ovi * 3 + axes[1]]; + if (pnpoly(3, vx, vy, tx, ty)) { + overlap = true; + break; + } + } + + if (overlap) { + guess_vert += 1; + continue; + } + + // this triangle is an ear + { + index_t idx0, idx1, idx2; + idx0.vertex_index = ind[0].v_idx; + idx0.normal_index = ind[0].vn_idx; + idx0.texcoord_index = ind[0].vt_idx; + idx1.vertex_index = ind[1].v_idx; + idx1.normal_index = ind[1].vn_idx; + idx1.texcoord_index = ind[1].vt_idx; + idx2.vertex_index = ind[2].v_idx; + idx2.normal_index = ind[2].vn_idx; + idx2.texcoord_index = ind[2].vt_idx; + + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); + + shape->mesh.num_face_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); + shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); + } + + // remove v1 from the list + size_t removed_vert_index = (guess_vert + 1) % npolys; + while (removed_vert_index + 1 < npolys) { + remainingFace.vertex_indices[removed_vert_index] = + remainingFace.vertex_indices[removed_vert_index + 1]; + removed_vert_index += 1; + } + remainingFace.vertex_indices.pop_back(); + } + + if (remainingFace.vertex_indices.size() == 3) { + i0 = remainingFace.vertex_indices[0]; + i1 = remainingFace.vertex_indices[1]; + i2 = remainingFace.vertex_indices[2]; + { + index_t idx0, idx1, idx2; + idx0.vertex_index = i0.v_idx; + idx0.normal_index = i0.vn_idx; + idx0.texcoord_index = i0.vt_idx; + idx1.vertex_index = i1.v_idx; + idx1.normal_index = i1.vn_idx; + idx1.texcoord_index = i1.vt_idx; + idx2.vertex_index = i2.v_idx; + idx2.normal_index = i2.vn_idx; + idx2.texcoord_index = i2.vt_idx; + + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); + + shape->mesh.num_face_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); + shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); + } + } + } else { + for (size_t k = 0; k < npolys; k++) { + index_t idx; + idx.vertex_index = face.vertex_indices[k].v_idx; + idx.normal_index = face.vertex_indices[k].vn_idx; + idx.texcoord_index = face.vertex_indices[k].vt_idx; + shape->mesh.indices.push_back(idx); + } + + shape->mesh.num_face_vertices.push_back( + static_cast(npolys)); + shape->mesh.material_ids.push_back(material_id); // per face + shape->mesh.smoothing_group_ids.push_back( + face.smoothing_group_id); // per face + } + } + + shape->mesh.tags = tags; + } + + // line + if (!prim_group.lineGroup.empty()) { + // Flatten indices + for (size_t i = 0; i < prim_group.lineGroup.size(); i++) { + for (size_t j = 0; j < prim_group.lineGroup[i].vertex_indices.size(); + j++) { + const vertex_index_t &vi = prim_group.lineGroup[i].vertex_indices[j]; + + index_t idx; + idx.vertex_index = vi.v_idx; + idx.normal_index = vi.vn_idx; + idx.texcoord_index = vi.vt_idx; + + shape->lines.indices.push_back(idx); + } + + shape->lines.num_line_vertices.push_back( + int(prim_group.lineGroup[i].vertex_indices.size())); + } + } + + // points + if (!prim_group.pointsGroup.empty()) { + // Flatten & convert indices + for (size_t i = 0; i < prim_group.pointsGroup.size(); i++) { + for (size_t j = 0; j < prim_group.pointsGroup[i].vertex_indices.size(); + j++) { + const vertex_index_t &vi = prim_group.pointsGroup[i].vertex_indices[j]; + + index_t idx; + idx.vertex_index = vi.v_idx; + idx.normal_index = vi.vn_idx; + idx.texcoord_index = vi.vt_idx; + + shape->points.indices.push_back(idx); + } + } + } + + return true; +} + +// Split a string with specified delimiter character. +// http://stackoverflow.com/questions/236129/split-a-string-in-c +static void SplitString(const std::string &s, char delim, + std::vector &elems) { + std::stringstream ss; + ss.str(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } +} + +void LoadMtl(std::map *material_map, + std::vector *materials, std::istream *inStream, + std::string *warning, std::string *err) { + (void)err; + + // Create a default material anyway. + material_t material; + InitMaterial(&material); + + // Issue 43. `d` wins against `Tr` since `Tr` is not in the MTL specification. + bool has_d = false; + bool has_tr = false; + + std::stringstream warn_ss; + + size_t line_no = 0; + std::string linebuf; + while (inStream->peek() != -1) { + safeGetline(*inStream, linebuf); + line_no++; + + // Trim trailing whitespace. + if (linebuf.size() > 0) { + linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1); + } + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // new mtl + if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { + // flush previous material. + if (!material.name.empty()) { + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); + } + + // initial temporary material + InitMaterial(&material); + + has_d = false; + has_tr = false; + + // set new mtl name + token += 7; + { + std::stringstream sstr; + sstr << token; + material.name = sstr.str(); + } + continue; + } + + // ambient + if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.ambient[0] = r; + material.ambient[1] = g; + material.ambient[2] = b; + continue; + } + + // diffuse + if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.diffuse[0] = r; + material.diffuse[1] = g; + material.diffuse[2] = b; + continue; + } + + // specular + if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.specular[0] = r; + material.specular[1] = g; + material.specular[2] = b; + continue; + } + + // transmittance + if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) || + (token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.transmittance[0] = r; + material.transmittance[1] = g; + material.transmittance[2] = b; + continue; + } + + // ior(index of refraction) + if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { + token += 2; + material.ior = parseReal(&token); + continue; + } + + // emission + if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.emission[0] = r; + material.emission[1] = g; + material.emission[2] = b; + continue; + } + + // shininess + if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.shininess = parseReal(&token); + continue; + } + + // illum model + if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { + token += 6; + material.illum = parseInt(&token); + continue; + } + + // dissolve + if ((token[0] == 'd' && IS_SPACE(token[1]))) { + token += 1; + material.dissolve = parseReal(&token); + + if (has_tr) { + warn_ss << "Both `d` and `Tr` parameters defined for \"" + << material.name + << "\". Use the value of `d` for dissolve (line " << line_no + << " in .mtl.)" << std::endl; + } + has_d = true; + continue; + } + if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + if (has_d) { + // `d` wins. Ignore `Tr` value. + warn_ss << "Both `d` and `Tr` parameters defined for \"" + << material.name + << "\". Use the value of `d` for dissolve (line " << line_no + << " in .mtl.)" << std::endl; + } else { + // We invert value of Tr(assume Tr is in range [0, 1]) + // NOTE: Interpretation of Tr is application(exporter) dependent. For + // some application(e.g. 3ds max obj exporter), Tr = d(Issue 43) + material.dissolve = static_cast(1.0) - parseReal(&token); + } + has_tr = true; + continue; + } + + // PBR: roughness + if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + material.roughness = parseReal(&token); + continue; + } + + // PBR: metallic + if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) { + token += 2; + material.metallic = parseReal(&token); + continue; + } + + // PBR: sheen + if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.sheen = parseReal(&token); + continue; + } + + // PBR: clearcoat thickness + if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) { + token += 2; + material.clearcoat_thickness = parseReal(&token); + continue; + } + + // PBR: clearcoat roughness + if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) { + token += 4; + material.clearcoat_roughness = parseReal(&token); + continue; + } + + // PBR: anisotropy + if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) { + token += 6; + material.anisotropy = parseReal(&token); + continue; + } + + // PBR: anisotropy rotation + if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) { + token += 7; + material.anisotropy_rotation = parseReal(&token); + continue; + } + + // ambient texture + if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.ambient_texname), + &(material.ambient_texopt), token); + continue; + } + + // diffuse texture + if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.diffuse_texname), + &(material.diffuse_texopt), token); + continue; + } + + // specular texture + if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.specular_texname), + &(material.specular_texopt), token); + continue; + } + + // specular highlight texture + if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.specular_highlight_texname), + &(material.specular_highlight_texopt), token); + continue; + } + + // bump texture + if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { + token += 9; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token); + continue; + } + + // bump texture + if ((0 == strncmp(token, "map_Bump", 8)) && IS_SPACE(token[8])) { + token += 9; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token); + continue; + } + + // bump texture + if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token); + continue; + } + + // alpha texture + if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { + token += 6; + material.alpha_texname = token; + ParseTextureNameAndOption(&(material.alpha_texname), + &(material.alpha_texopt), token); + continue; + } + + // displacement texture + if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.displacement_texname), + &(material.displacement_texopt), token); + continue; + } + + // reflection map + if ((0 == strncmp(token, "refl", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.reflection_texname), + &(material.reflection_texopt), token); + continue; + } + + // PBR: roughness texture + if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.roughness_texname), + &(material.roughness_texopt), token); + continue; + } + + // PBR: metallic texture + if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.metallic_texname), + &(material.metallic_texopt), token); + continue; + } + + // PBR: sheen texture + if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.sheen_texname), + &(material.sheen_texopt), token); + continue; + } + + // PBR: emissive texture + if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.emissive_texname), + &(material.emissive_texopt), token); + continue; + } + + // PBR: normal map texture + if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.normal_texname), + &(material.normal_texopt), token); + continue; + } + + // unknown parameter + const char *_space = strchr(token, ' '); + if (!_space) { + _space = strchr(token, '\t'); + } + if (_space) { + std::ptrdiff_t len = _space - token; + std::string key(token, static_cast(len)); + std::string value = _space + 1; + material.unknown_parameter.insert( + std::pair(key, value)); + } + } + // flush last material. + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); + + if (warning) { + (*warning) = warn_ss.str(); + } +} + +bool MaterialFileReader::operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *warn, std::string *err) { + std::string filepath; + + if (!m_mtlBaseDir.empty()) { + filepath = std::string(m_mtlBaseDir) + matId; + } else { + filepath = matId; + } + + std::ifstream matIStream(filepath.c_str()); + if (!matIStream) { + std::stringstream ss; + ss << "Material file [ " << filepath << " ] not found." << std::endl; + if (warn) { + (*warn) += ss.str(); + } + return false; + } + + LoadMtl(matMap, materials, &matIStream, warn, err); + + return true; +} + +bool MaterialStreamReader::operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *warn, std::string *err) { + (void)err; + (void)matId; + if (!m_inStream) { + std::stringstream ss; + ss << "Material stream in error state. " << std::endl; + if (warn) { + (*warn) += ss.str(); + } + return false; + } + + LoadMtl(matMap, materials, &m_inStream, warn, err); + + return true; +} + +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *warn, + std::string *err, const char *filename, const char *mtl_basedir, + bool trianglulate, bool default_vcols_fallback) { + attrib->vertices.clear(); + attrib->normals.clear(); + attrib->texcoords.clear(); + attrib->colors.clear(); + shapes->clear(); + + std::stringstream errss; + + std::ifstream ifs(filename); + if (!ifs) { + errss << "Cannot open file [" << filename << "]" << std::endl; + if (err) { + (*err) = errss.str(); + } + return false; + } + + std::string baseDir = mtl_basedir ? mtl_basedir : ""; + if (!baseDir.empty()) { +#ifndef _WIN32 + const char dirsep = '/'; +#else + const char dirsep = '\\'; +#endif + if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep; + } + MaterialFileReader matFileReader(baseDir); + + return LoadObj(attrib, shapes, materials, warn, err, &ifs, &matFileReader, + trianglulate, default_vcols_fallback); +} + +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *warn, + std::string *err, std::istream *inStream, + MaterialReader *readMatFn /*= NULL*/, bool triangulate, + bool default_vcols_fallback) { + std::stringstream errss; + + std::vector v; + std::vector vn; + std::vector vt; + std::vector vc; + std::vector tags; + PrimGroup prim_group; + std::string name; + + // material + std::map material_map; + int material = -1; + + // smoothing group id + unsigned int current_smoothing_id = + 0; // Initial value. 0 means no smoothing. + + int greatest_v_idx = -1; + int greatest_vn_idx = -1; + int greatest_vt_idx = -1; + + shape_t shape; + + bool found_all_colors = true; + + size_t line_num = 0; + std::string linebuf; + while (inStream->peek() != -1) { + safeGetline(*inStream, linebuf); + + line_num++; + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // vertex + if (token[0] == 'v' && IS_SPACE((token[1]))) { + token += 2; + real_t x, y, z; + real_t r, g, b; + + found_all_colors &= parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token); + + v.push_back(x); + v.push_back(y); + v.push_back(z); + + if (found_all_colors || default_vcols_fallback) { + vc.push_back(r); + vc.push_back(g); + vc.push_back(b); + } + + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y, z; + parseReal3(&x, &y, &z, &token); + vn.push_back(x); + vn.push_back(y); + vn.push_back(z); + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y; + parseReal2(&x, &y, &token); + vt.push_back(x); + vt.push_back(y); + continue; + } + + // line + if (token[0] == 'l' && IS_SPACE((token[1]))) { + token += 2; + + __line_t line; + + while (!IS_NEW_LINE(token[0])) { + vertex_index_t vi; + if (!parseTriple(&token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2), &vi)) { + if (err) { + std::stringstream ss; + ss << "Failed parse `l' line(e.g. zero value for vertex index. " + "line " + << line_num << ".)\n"; + (*err) += ss.str(); + } + return false; + } + + line.vertex_indices.push_back(vi); + + size_t n = strspn(token, " \t\r"); + token += n; + } + + prim_group.lineGroup.push_back(line); + + continue; + } + + // points + if (token[0] == 'p' && IS_SPACE((token[1]))) { + token += 2; + + __points_t pts; + + while (!IS_NEW_LINE(token[0])) { + vertex_index_t vi; + if (!parseTriple(&token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2), &vi)) { + if (err) { + std::stringstream ss; + ss << "Failed parse `p' line(e.g. zero value for vertex index. " + "line " + << line_num << ".)\n"; + (*err) += ss.str(); + } + return false; + } + + pts.vertex_indices.push_back(vi); + + size_t n = strspn(token, " \t\r"); + token += n; + } + + prim_group.pointsGroup.push_back(pts); + + continue; + } + + // face + if (token[0] == 'f' && IS_SPACE((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + face_t face; + + face.smoothing_group_id = current_smoothing_id; + face.vertex_indices.reserve(3); + + while (!IS_NEW_LINE(token[0])) { + vertex_index_t vi; + if (!parseTriple(&token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2), &vi)) { + if (err) { + std::stringstream ss; + ss << "Failed parse `f' line(e.g. zero value for face index. line " + << line_num << ".)\n"; + (*err) += ss.str(); + } + return false; + } + + greatest_v_idx = greatest_v_idx > vi.v_idx ? greatest_v_idx : vi.v_idx; + greatest_vn_idx = + greatest_vn_idx > vi.vn_idx ? greatest_vn_idx : vi.vn_idx; + greatest_vt_idx = + greatest_vt_idx > vi.vt_idx ? greatest_vt_idx : vi.vt_idx; + + face.vertex_indices.push_back(vi); + size_t n = strspn(token, " \t\r"); + token += n; + } + + // replace with emplace_back + std::move on C++11 + prim_group.faceGroup.push_back(face); + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { + token += 7; + std::stringstream ss; + ss << token; + std::string namebuf = ss.str(); + + int newMaterialId = -1; + if (material_map.find(namebuf) != material_map.end()) { + newMaterialId = material_map[namebuf]; + } else { + // { error!! material not found } + } + + if (newMaterialId != material) { + // Create per-face material. Thus we don't add `shape` to `shapes` at + // this time. + // just clear `faceGroup` after `exportGroupsToShape()` call. + exportGroupsToShape(&shape, prim_group, tags, material, name, + triangulate, v); + prim_group.faceGroup.clear(); + material = newMaterialId; + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + if (readMatFn) { + token += 7; + + std::vector filenames; + SplitString(std::string(token), ' ', filenames); + + if (filenames.empty()) { + if (warn) { + std::stringstream ss; + ss << "Looks like empty filename for mtllib. Use default " + "material (line " + << line_num << ".)\n"; + + (*warn) += ss.str(); + } + } else { + bool found = false; + for (size_t s = 0; s < filenames.size(); s++) { + std::string warn_mtl; + std::string err_mtl; + bool ok = (*readMatFn)(filenames[s].c_str(), materials, + &material_map, &warn_mtl, &err_mtl); + if (warn && (!warn_mtl.empty())) { + (*warn) += warn_mtl; + } + + if (err && (!err_mtl.empty())) { + (*err) += err_mtl; + } + + if (ok) { + found = true; + break; + } + } + + if (!found) { + if (warn) { + (*warn) += + "Failed to load material file(s). Use default " + "material.\n"; + } + } + } + } + + continue; + } + + // group name + if (token[0] == 'g' && IS_SPACE((token[1]))) { + // flush previous face group. + bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, + triangulate, v); + (void)ret; // return value not used. + + if (shape.mesh.indices.size() > 0) { + shapes->push_back(shape); + } + + shape = shape_t(); + + // material = -1; + prim_group.clear(); + + std::vector names; + + while (!IS_NEW_LINE(token[0])) { + std::string str = parseString(&token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + // names[0] must be 'g' + + if (names.size() < 2) { + // 'g' with empty names + if (warn) { + std::stringstream ss; + ss << "Empty group name. line: " << line_num << "\n"; + (*warn) += ss.str(); + name = ""; + } + } else { + std::stringstream ss; + ss << names[1]; + + // tinyobjloader does not support multiple groups for a primitive. + // Currently we concatinate multiple group names with a space to get + // single group name. + + for (size_t i = 2; i < names.size(); i++) { + ss << " " << names[i]; + } + + name = ss.str(); + } + + continue; + } + + // object name + if (token[0] == 'o' && IS_SPACE((token[1]))) { + // flush previous face group. + bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, + triangulate, v); + if (ret) { + shapes->push_back(shape); + } + + // material = -1; + prim_group.clear(); + shape = shape_t(); + + // @todo { multiple object name? } + token += 2; + std::stringstream ss; + ss << token; + name = ss.str(); + + continue; + } + + if (token[0] == 't' && IS_SPACE(token[1])) { + const int max_tag_nums = 8192; // FIXME(syoyo): Parameterize. + tag_t tag; + + token += 2; + + tag.name = parseString(&token); + + tag_sizes ts = parseTagTriple(&token); + + if (ts.num_ints < 0) { + ts.num_ints = 0; + } + if (ts.num_ints > max_tag_nums) { + ts.num_ints = max_tag_nums; + } + + if (ts.num_reals < 0) { + ts.num_reals = 0; + } + if (ts.num_reals > max_tag_nums) { + ts.num_reals = max_tag_nums; + } + + if (ts.num_strings < 0) { + ts.num_strings = 0; + } + if (ts.num_strings > max_tag_nums) { + ts.num_strings = max_tag_nums; + } + + tag.intValues.resize(static_cast(ts.num_ints)); + + for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { + tag.intValues[i] = parseInt(&token); + } + + tag.floatValues.resize(static_cast(ts.num_reals)); + for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { + tag.floatValues[i] = parseReal(&token); + } + + tag.stringValues.resize(static_cast(ts.num_strings)); + for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { + tag.stringValues[i] = parseString(&token); + } + + tags.push_back(tag); + + continue; + } + + if (token[0] == 's' && IS_SPACE(token[1])) { + // smoothing group id + token += 2; + + // skip space. + token += strspn(token, " \t"); // skip space + + if (token[0] == '\0') { + continue; + } + + if (token[0] == '\r' || token[1] == '\n') { + continue; + } + + if (strlen(token) >= 3) { + if (token[0] == 'o' && token[1] == 'f' && token[2] == 'f') { + current_smoothing_id = 0; + } + } else { + // assume number + int smGroupId = parseInt(&token); + if (smGroupId < 0) { + // parse error. force set to 0. + // FIXME(syoyo): Report warning. + current_smoothing_id = 0; + } else { + current_smoothing_id = static_cast(smGroupId); + } + } + + continue; + } // smoothing group id + + // Ignore unknown command. + } + + // not all vertices have colors, no default colors desired? -> clear colors + if (!found_all_colors && !default_vcols_fallback) { + vc.clear(); + } + + if (greatest_v_idx >= static_cast(v.size() / 3)) { + if (warn) { + std::stringstream ss; + ss << "Vertex indices out of bounds (line " << line_num << ".)\n" + << std::endl; + (*warn) += ss.str(); + } + } + if (greatest_vn_idx >= static_cast(vn.size() / 3)) { + if (warn) { + std::stringstream ss; + ss << "Vertex normal indices out of bounds (line " << line_num << ".)\n" + << std::endl; + (*warn) += ss.str(); + } + } + if (greatest_vt_idx >= static_cast(vt.size() / 2)) { + if (warn) { + std::stringstream ss; + ss << "Vertex texcoord indices out of bounds (line " << line_num << ".)\n" + << std::endl; + (*warn) += ss.str(); + } + } + + bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, + triangulate, v); + // exportGroupsToShape return false when `usemtl` is called in the last + // line. + // we also add `shape` to `shapes` when `shape.mesh` has already some + // faces(indices) + if (ret || shape.mesh.indices + .size()) { // FIXME(syoyo): Support other prims(e.g. lines) + shapes->push_back(shape); + } + prim_group.clear(); // for safety + + if (err) { + (*err) += errss.str(); + } + + attrib->vertices.swap(v); + attrib->vertex_weights.swap(v); + attrib->normals.swap(vn); + attrib->texcoords.swap(vt); + attrib->texcoord_ws.swap(vt); + attrib->colors.swap(vc); + + return true; +} + +bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, + void *user_data /*= NULL*/, + MaterialReader *readMatFn /*= NULL*/, + std::string *warn, /* = NULL*/ + std::string *err /*= NULL*/) { + std::stringstream errss; + + // material + std::map material_map; + int material_id = -1; // -1 = invalid + + std::vector indices; + std::vector materials; + std::vector names; + names.reserve(2); + std::vector names_out; + + std::string linebuf; + while (inStream.peek() != -1) { + safeGetline(inStream, linebuf); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // vertex + if (token[0] == 'v' && IS_SPACE((token[1]))) { + token += 2; + // TODO(syoyo): Support parsing vertex color extension. + real_t x, y, z, w; // w is optional. default = 1.0 + parseV(&x, &y, &z, &w, &token); + if (callback.vertex_cb) { + callback.vertex_cb(user_data, x, y, z, w); + } + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y, z; + parseReal3(&x, &y, &z, &token); + if (callback.normal_cb) { + callback.normal_cb(user_data, x, y, z); + } + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y, z; // y and z are optional. default = 0.0 + parseReal3(&x, &y, &z, &token); + if (callback.texcoord_cb) { + callback.texcoord_cb(user_data, x, y, z); + } + continue; + } + + // face + if (token[0] == 'f' && IS_SPACE((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + indices.clear(); + while (!IS_NEW_LINE(token[0])) { + vertex_index_t vi = parseRawTriple(&token); + + index_t idx; + idx.vertex_index = vi.v_idx; + idx.normal_index = vi.vn_idx; + idx.texcoord_index = vi.vt_idx; + + indices.push_back(idx); + size_t n = strspn(token, " \t\r"); + token += n; + } + + if (callback.index_cb && indices.size() > 0) { + callback.index_cb(user_data, &indices.at(0), + static_cast(indices.size())); + } + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { + token += 7; + std::stringstream ss; + ss << token; + std::string namebuf = ss.str(); + + int newMaterialId = -1; + if (material_map.find(namebuf) != material_map.end()) { + newMaterialId = material_map[namebuf]; + } else { + // { error!! material not found } + } + + if (newMaterialId != material_id) { + material_id = newMaterialId; + } + + if (callback.usemtl_cb) { + callback.usemtl_cb(user_data, namebuf.c_str(), material_id); + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + if (readMatFn) { + token += 7; + + std::vector filenames; + SplitString(std::string(token), ' ', filenames); + + if (filenames.empty()) { + if (warn) { + (*warn) += + "Looks like empty filename for mtllib. Use default " + "material. \n"; + } + } else { + bool found = false; + for (size_t s = 0; s < filenames.size(); s++) { + std::string warn_mtl; + std::string err_mtl; + bool ok = (*readMatFn)(filenames[s].c_str(), &materials, + &material_map, &warn_mtl, &err_mtl); + + if (warn && (!warn_mtl.empty())) { + (*warn) += warn_mtl; // This should be warn message. + } + + if (err && (!err_mtl.empty())) { + (*err) += err_mtl; + } + + if (ok) { + found = true; + break; + } + } + + if (!found) { + if (warn) { + (*warn) += + "Failed to load material file(s). Use default " + "material.\n"; + } + } else { + if (callback.mtllib_cb) { + callback.mtllib_cb(user_data, &materials.at(0), + static_cast(materials.size())); + } + } + } + } + + continue; + } + + // group name + if (token[0] == 'g' && IS_SPACE((token[1]))) { + names.clear(); + + while (!IS_NEW_LINE(token[0])) { + std::string str = parseString(&token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + assert(names.size() > 0); + + if (callback.group_cb) { + if (names.size() > 1) { + // create const char* array. + names_out.resize(names.size() - 1); + for (size_t j = 0; j < names_out.size(); j++) { + names_out[j] = names[j + 1].c_str(); + } + callback.group_cb(user_data, &names_out.at(0), + static_cast(names_out.size())); + + } else { + callback.group_cb(user_data, NULL, 0); + } + } + + continue; + } + + // object name + if (token[0] == 'o' && IS_SPACE((token[1]))) { + // @todo { multiple object name? } + token += 2; + + std::stringstream ss; + ss << token; + std::string object_name = ss.str(); + + if (callback.object_cb) { + callback.object_cb(user_data, object_name.c_str()); + } + + continue; + } + +#if 0 // @todo + if (token[0] == 't' && IS_SPACE(token[1])) { + tag_t tag; + + token += 2; + std::stringstream ss; + ss << token; + tag.name = ss.str(); + + token += tag.name.size() + 1; + + tag_sizes ts = parseTagTriple(&token); + + tag.intValues.resize(static_cast(ts.num_ints)); + + for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { + tag.intValues[i] = atoi(token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.floatValues.resize(static_cast(ts.num_reals)); + for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { + tag.floatValues[i] = parseReal(&token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.stringValues.resize(static_cast(ts.num_strings)); + for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { + std::stringstream ss; + ss << token; + tag.stringValues[i] = ss.str(); + token += tag.stringValues[i].size() + 1; + } + + tags.push_back(tag); + } +#endif + + // Ignore unknown command. + } + + if (err) { + (*err) += errss.str(); + } + + return true; +} + +bool ObjReader::ParseFromFile(const std::string &filename, + const ObjReaderConfig &config) { + std::string mtl_search_path; + + if (config.mtl_search_path.empty()) { + // + // split at last '/'(for unixish system) or '\\'(for windows) to get + // the base directory of .obj file + // + if (filename.find_last_of("/\\") != std::string::npos) { + mtl_search_path = filename.substr(0, filename.find_last_of("/\\")); + } + } + + valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_, + filename.c_str(), mtl_search_path.c_str(), + config.triangulate, config.vertex_color); + + return valid_; +} + +bool ObjReader::ParseFromString(const std::string &obj_text, + const std::string &mtl_text, + const ObjReaderConfig &config) { + std::stringbuf obj_buf(obj_text); + std::stringbuf mtl_buf(mtl_text); + + std::istream obj_ifs(&obj_buf); + std::istream mtl_ifs(&mtl_buf); + + MaterialStreamReader mtl_ss(mtl_ifs); + + valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_, + &obj_ifs, &mtl_ss, config.triangulate, config.vertex_color); + + return valid_; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} // namespace tinyobj + +#endif diff --git a/include/bounce/bounce.h b/include/bounce/bounce.h index 3826bb5..d6bcbd6 100644 --- a/include/bounce/bounce.h +++ b/include/bounce/bounce.h @@ -22,7 +22,6 @@ // Include this file header in your project to directly access Bounce objects. #include -#include #include #include @@ -31,14 +30,18 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include #include +#include #include #include @@ -46,11 +49,17 @@ #include #include #include +#include +#include +#include +#include #include #include +#include #include #include +#include #include #include @@ -63,17 +72,25 @@ #include +#include + #include #include #include -#include -#include -#include -#include +#include +#include + +#include +#include +#include +#include + +#include #include #include #include +#include #include #include @@ -83,5 +100,11 @@ #include #include #include +#include + +#include +#include + +#include #endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index 9419b23..5e1cf18 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -25,102 +25,113 @@ #include #include -class b3World; +struct b3ClothMesh; -struct b3ParticleDef; -class b3Particle; +struct b3ClothParticleDef; +class b3ClothParticle; struct b3ForceDef; class b3Force; -class b3ClothTriangle; +struct b3ClothSphereShapeDef; +class b3ClothSphereShape; -struct b3ClothMesh; +struct b3ClothCapsuleShapeDef; +class b3ClothCapsuleShape; -class b3RayCastListener; +struct b3ClothTriangleShapeDef; +class b3ClothTriangleShape; + +struct b3ClothWorldShapeDef; +class b3ClothWorldShape; struct b3RayCastInput; struct b3RayCastOutput; +struct b3ClothTimeStep; + struct b3ClothRayCastSingleOutput { - u32 triangle; - float32 fraction; + b3ClothTriangleShape* triangle; + scalar fraction; b3Vec3 normal; }; // Cloth definition -// This requires defining a cloth mesh which is typically bound to a render mesh +// This requires defining a cloth mesh which is typically bound to a render mesh +// and some uniform parameters. struct b3ClothDef { b3ClothDef() { mesh = nullptr; - density = 0.0f; - streching = 0.0f; - shearing = 0.0f; - bending = 0.0f; - sewing = 0.0f; - damping = 0.0f; - thickness = 0.0f; - friction = 0.2f; + density = scalar(0); + streching = scalar(0); + strechDamping = scalar(0); + shearing = scalar(0); + shearDamping = scalar(0); + bending = scalar(0); + bendDamping = scalar(0); + sewing = scalar(0); + sewDamping = scalar(0); + thickness = scalar(0); + friction = scalar(0.2); } // Cloth mesh const b3ClothMesh* mesh; // Cloth density in kg/m^2 - float32 density; + scalar density; // Streching stiffness - float32 streching; + scalar streching; + // Strech damping stiffness + scalar strechDamping; + // Shearing stiffness - float32 shearing; + scalar shearing; + + // Shear damping stiffness + scalar shearDamping; // Bending stiffness - float32 bending; + scalar bending; - // Sewing stiffness - float32 sewing; + // Bend damping stiffness + scalar bendDamping; - // Damping stiffness - float32 damping; + // Sewing stiffness + scalar sewing; + + // Sew damping stiffness + scalar sewDamping; - // Cloth thickness - float32 thickness; + // Shape thickness + scalar thickness; - // Cloth coefficient of friction - float32 friction; + // Shape coefficient of friction + scalar friction; }; // A cloth represents a deformable surface as a collection of particles. -// Particles may be connected with each other by springs. +// Particles may be connected with each other by forces. class b3Cloth { public: + b3Cloth(); b3Cloth(const b3ClothDef& def); ~b3Cloth(); - // Set the acceleration of gravity. - void SetGravity(const b3Vec3& gravity); - - // Get the acceleration of gravity. - b3Vec3 GetGravity() const; - - // Attach a world to this cloth. - // The cloth will be able to respond to collisions with the rigid bodies in the attached world. - void SetWorld(b3World* world); - - // Get the world attached to this cloth. - const b3World* GetWorld() const; - b3World* GetWorld(); - // Create a particle. - b3Particle* CreateParticle(const b3ParticleDef& def); + b3ClothParticle* CreateParticle(const b3ClothParticleDef& def); // Destroy a given particle. - void DestroyParticle(b3Particle* particle); + void DestroyParticle(b3ClothParticle* particle); + + // Return the list of particles in this cloth. + const b3List2& GetParticleList() const; // Create a force. b3Force* CreateForce(const b3ForceDef& def); @@ -128,82 +139,158 @@ public: // Destroy a given force. void DestroyForce(b3Force* force); - // Perform a ray cast with the cloth. - bool RayCastSingle(b3ClothRayCastSingleOutput* output, const b3Vec3& p1, const b3Vec3& p2) const; - - // Perform a ray cast with a given cloth mesh triangle. - bool RayCast(b3RayCastOutput* output, const b3RayCastInput* input, u32 triangleIndex) const; - - // Return the cloth mesh proxy. - const b3ClothMesh* GetMesh() const; - - // Return the cloth particle given the vertex index. - b3Particle* GetParticle(u32 i); - - // Return the cloth triangle given the triangle index. - b3ClothTriangle* GetTriangle(u32 i); - - // Return the list of particles in this cloth. - const b3List2& GetParticleList() const; - // Return the list of forces in this cloth. const b3List2& GetForceList() const; - // Return the kinetic (or dynamic) energy in this system. - float32 GetEnergy() const; + // Create a sphere shape. + b3ClothSphereShape* CreateSphereShape(const b3ClothSphereShapeDef& def); + + // Destroy a given sphere shape. + void DestroySphereShape(b3ClothSphereShape* shape); + + // Return the list of sphere shapes in this cloth. + const b3List2& GetSphereShapeList() const; + + // Create a capsule shape. + b3ClothCapsuleShape* CreateCapsuleShape(const b3ClothCapsuleShapeDef& def); + + // Destroy a given capsule shape. + void DestroyCapsuleShape(b3ClothCapsuleShape* shape); + + // Return the list of capsule shapes in this cloth. + const b3List2& GetCapsuleShapeList() const; + + // Create a triangle shape. + b3ClothTriangleShape* CreateTriangleShape(const b3ClothTriangleShapeDef& def); + + // Destroy a given triangle shape. + void DestroyTriangleShape(b3ClothTriangleShape* shape); + + // Return the list of triangle shapes in this cloth. + const b3List2& GetTriangleShapeList() const; + + // Create a new world shape. + b3ClothWorldShape* CreateWorldShape(const b3ClothWorldShapeDef& def); + + // Destroy a given world shape. + void DestroyWorldShape(b3ClothWorldShape* shape); + + // Return the list of world shapes in this cloth. + const b3List2& GetWorldShapeList() const; + + // Set the acceleration of gravity. + void SetGravity(const b3Vec3& gravity); + + // Get the acceleration of gravity. + b3Vec3 GetGravity() const; // Perform a time step. - void Step(float32 dt, u32 velocityIterations, u32 positionIterations); + void Step(scalar dt, u32 velocityIterations, u32 positionIterations); - // Debug draw the cloth using the associated cloth mesh. + // Perform a ray cast with the cloth. + bool RayCastSingle(b3ClothRayCastSingleOutput* output, const b3Vec3& p1, const b3Vec3& p2) const; + + // Get the cloth mesh. + const b3ClothMesh* GetMesh() const; + + // Get mesh particle. + b3ClothParticle* GetParticle(u32 index); + + // Get mesh sphere. + b3ClothSphereShape* GetSphere(u32 index); + + // Get mesh triangle. + b3ClothTriangleShape* GetTriangle(u32 index); + + // Enable or disable self-collision. + void EnableSelfCollision(bool flag); + + // Is self-collision enabled? + bool IsSelfCollisionEnabled() const; + + // Return the kinetic (or dynamic) energy in this system. + scalar GetEnergy() const; + + // Debug draw the cloth entities. void Draw() const; private: - friend class b3Particle; - friend class b3ClothTriangle; + friend class b3ClothParticle; + friend class b3ClothSphereShape; + friend class b3ClothCapsuleShape; + friend class b3ClothTriangleShape; + friend class b3ClothWorldShape; friend class b3ShearForce; - friend class b3StrechForce; + friend class b3StretchForce; friend class b3SpringForce; friend class b3MouseForce; friend class b3ClothContactManager; - - // Compute mass of each particle. - void ComputeMass(); + + // Rest the mass data of the cloth. + void ResetMass(); // Solve - void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); + void Solve(const b3ClothTimeStep& step); // Stack allocator b3StackAllocator m_stackAllocator; - // The world attached to this cloth - b3World* m_world; - // Gravity acceleration b3Vec3 m_gravity; - // Proxy mesh - const b3ClothMesh* m_mesh; - - // Particles - b3Particle** m_particles; - - // Triangles - b3ClothTriangle* m_triangles; - - // Cloth density - float32 m_density; - // Pool of particles b3BlockPool m_particleBlocks; + // Pool of sphere shapes + b3BlockPool m_sphereShapeBlocks; + + // Pool of capsule shapes + b3BlockPool m_capsuleShapeBlocks; + + // Pool of triangle shapes + b3BlockPool m_triangleShapeBlocks; + + // Pool of world shapes + b3BlockPool m_worldShapeBlocks; + // List of particles - b3List2 m_particleList; + b3List2 m_particleList; // List of forces b3List2 m_forceList; + // List of sphere shapes + b3List2 m_sphereShapeList; + + // List of capsule shapes + b3List2 m_capsuleShapeList; + + // List of triangle shapes + b3List2 m_triangleShapeList; + + // List of world shapes + b3List2 m_worldShapeList; + // Contact manager b3ClothContactManager m_contactManager; + + // Used to compute the time step ratio to + // support variable time steps. + scalar m_inv_dt0; + + // Mesh + const b3ClothMesh* m_mesh; + + // Mesh vertex particles + b3ClothParticle** m_particles; + + // Mesh vertex sphere shapes + b3ClothSphereShape** m_spheres; + + // Mesh triangle triangle shapes + b3ClothTriangleShape** m_triangles; + + // Self-collision activation flag + bool m_enableSelfCollision; }; inline void b3Cloth::SetGravity(const b3Vec3& gravity) @@ -216,24 +303,9 @@ inline b3Vec3 b3Cloth::GetGravity() const return m_gravity; } -inline const b3World* b3Cloth::GetWorld() const +inline bool b3Cloth::IsSelfCollisionEnabled() const { - return m_world; -} - -inline b3World* b3Cloth::GetWorld() -{ - return m_world; -} - -inline const b3ClothMesh* b3Cloth::GetMesh() const -{ - return m_mesh; -} - -inline const b3List2& b3Cloth::GetParticleList() const -{ - return m_particleList; + return m_enableSelfCollision; } inline const b3List2& b3Cloth::GetForceList() const @@ -241,4 +313,34 @@ inline const b3List2& b3Cloth::GetForceList() const return m_forceList; } +inline const b3List2& b3Cloth::GetParticleList() const +{ + return m_particleList; +} + +inline const b3List2& b3Cloth::GetSphereShapeList() const +{ + return m_sphereShapeList; +} + +inline const b3List2& b3Cloth::GetCapsuleShapeList() const +{ + return m_capsuleShapeList; +} + +inline const b3List2& b3Cloth::GetTriangleShapeList() const +{ + return m_triangleShapeList; +} + +inline const b3List2& b3Cloth::GetWorldShapeList() const +{ + return m_worldShapeList; +} + +inline const b3ClothMesh* b3Cloth::GetMesh() const +{ + return m_mesh; +} + #endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_contact_manager.h b/include/bounce/cloth/cloth_contact_manager.h index c1e8bd7..031b104 100644 --- a/include/bounce/cloth/cloth_contact_manager.h +++ b/include/bounce/cloth/cloth_contact_manager.h @@ -19,8 +19,9 @@ #ifndef B3_CLOTH_CONTACT_MANAGER_H #define B3_CLOTH_CONTACT_MANAGER_H -#include -#include +#include +#include +#include #include #include #include @@ -34,30 +35,27 @@ public: b3ClothContactManager(); void FindNewContacts(); - void AddPair(void* data1, void* data2); - void FindNewClothContacts(); - - void AddPSPair(b3Particle* p1, b3Shape* s2); - void FindNewBodyContacts(); - void UpdateContacts(); - void UpdateClothContacts(); - void UpdateBodyContacts(); - b3ParticleTriangleContact* CreateParticleTriangleContact(); - void Destroy(b3ParticleTriangleContact* c); + b3ClothSphereAndTriangleContact* CreateSphereAndTriangleContact(); + void Destroy(b3ClothSphereAndTriangleContact* c); - b3ParticleBodyContact* CreateParticleBodyContact(); - void Destroy(b3ParticleBodyContact* c); + b3ClothSphereAndShapeContact* CreateSphereAndShapeContact(); + void Destroy(b3ClothSphereAndShapeContact* c); - b3BlockPool m_particleTriangleContactBlocks; - b3BlockPool m_particleBodyContactBlocks; + b3ClothCapsuleAndCapsuleContact* CreateCapsuleAndCapsuleContact(); + void Destroy(b3ClothCapsuleAndCapsuleContact* c); + + b3BlockPool m_sphereAndTriangleContactBlocks; + b3BlockPool m_sphereAndShapeContactBlocks; + b3BlockPool m_capsuleAndCapsuleContactBlocks; b3Cloth* m_cloth; b3BroadPhase m_broadPhase; - b3List2 m_particleTriangleContactList; - b3List2 m_particleBodyContactList; + b3List2 m_sphereAndTriangleContactList; + b3List2 m_sphereAndShapeContactList; + b3List2 m_capsuleAndCapsuleContactList; }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_force_solver.h b/include/bounce/cloth/cloth_force_solver.h index 2762c7f..b411189 100644 --- a/include/bounce/cloth/cloth_force_solver.h +++ b/include/bounce/cloth/cloth_force_solver.h @@ -21,10 +21,11 @@ #include #include +#include class b3StackAllocator; -class b3Particle; +class b3ClothParticle; class b3Force; struct b3DenseVec3; @@ -34,9 +35,10 @@ struct b3SparseMat33View; struct b3ClothForceSolverDef { + b3ClothTimeStep step; b3StackAllocator* stack; u32 particleCount; - b3Particle** particles; + b3ClothParticle** particles; u32 forceCount; b3Force** forces; }; @@ -59,14 +61,16 @@ public: b3ClothForceSolver(const b3ClothForceSolverDef& def); ~b3ClothForceSolver(); - void Solve(float32 dt, const b3Vec3& gravity); + void Solve(const b3Vec3& gravity); private: void ApplyForces(); - b3StackAllocator* m_allocator; + b3ClothTimeStep m_step; + + b3StackAllocator* m_stack; u32 m_particleCount; - b3Particle** m_particles; + b3ClothParticle** m_particles; u32 m_forceCount; b3Force** m_forces; diff --git a/include/bounce/cloth/cloth_mesh.h b/include/bounce/cloth/cloth_mesh.h index 98c9205..b60c3b1 100644 --- a/include/bounce/cloth/cloth_mesh.h +++ b/include/bounce/cloth/cloth_mesh.h @@ -34,6 +34,16 @@ struct b3ClothMeshMesh u32 startTriangle; }; +struct b3ClothMeshShearingLine +{ + u32 v1, v2; +}; + +struct b3ClothMeshBendingLine +{ + u32 v1, v2; +}; + struct b3ClothMeshSewingLine { u32 s1, s2; @@ -48,6 +58,10 @@ struct b3ClothMesh b3ClothMeshTriangle* triangles; u32 meshCount; b3ClothMeshMesh* meshes; + u32 shearingLineCount; + b3ClothMeshShearingLine* shearingLines; + u32 bendingLineCount; + b3ClothMeshBendingLine* bendingLines; u32 sewingLineCount; b3ClothMeshSewingLine* sewingLines; }; diff --git a/include/bounce/cloth/cloth_particle.h b/include/bounce/cloth/cloth_particle.h new file mode 100644 index 0000000..41c22fa --- /dev/null +++ b/include/bounce/cloth/cloth_particle.h @@ -0,0 +1,291 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_PARTICLE_H +#define B3_CLOTH_PARTICLE_H + +#include +#include + +class b3Cloth; + +// Static particle: Zero mass. Can be moved manually. +// Kinematic particle: Zero mass. Non-zero velocity, can be moved by the solver. +// Dynamic particle: Non-zero mass. Non-zero velocity determined by force, can be moved by the solver. +enum b3ClothParticleType +{ + e_staticClothParticle, + e_kinematicClothParticle, + e_dynamicClothParticle +}; + +// Particle definition +struct b3ClothParticleDef +{ + b3ClothParticleDef() + { + type = e_staticClothParticle; + position.SetZero(); + velocity.SetZero(); + force.SetZero(); + meshIndex = B3_MAX_U32; + userData = nullptr; + } + + b3ClothParticleType type; + b3Vec3 position; + b3Vec3 velocity; + b3Vec3 force; + u32 meshIndex; + void* userData; +}; + +// A cloth particle. +class b3ClothParticle +{ +public: + // Set the particle type. + void SetType(b3ClothParticleType type); + + // Get the particle type. + b3ClothParticleType GetType() const; + + // Set the particle position. + // If the particle is dynamic changing the position directly might lead + // to physically incorrect simulation behaviour. + void SetPosition(const b3Vec3& position); + + // Get the particle position. + const b3Vec3& GetPosition() const; + + // Set the particle velocity. + void SetVelocity(const b3Vec3& velocity); + + // Get the particle velocity. + const b3Vec3& GetVelocity() const; + + // Get the particle mass. + scalar GetMass() const; + + // Get the applied force. + const b3Vec3& GetForce() const; + + // Apply a force. + void ApplyForce(const b3Vec3& force); + + // Get the applied translation. + const b3Vec3& GetTranslation() const; + + // Apply a translation. + void ApplyTranslation(const b3Vec3& translation); + + // Get the mesh index. + u32 GetMeshIndex() const; + + // Set the user data. + void SetUserData(void* userData); + + // Get the user data. + const void* GetUserData() const; + void* GetUserData(); + + // Get the next particle in the cloth list of particles. + b3ClothParticle* GetNext(); + const b3ClothParticle* GetNext() const; +private: + friend class b3List2; + friend class b3Cloth; + friend class b3ClothContactManager; + friend class b3ClothSolver; + friend class b3ClothForceSolver; + friend class b3ClothSphereShape; + friend class b3ClothCapsuleShape; + friend class b3ClothTriangleShape; + friend class b3ClothSphereAndShapeContact; + friend class b3ClothSphereAndTriangleContact; + friend class b3ClothCapsuleAndCapsuleContact; + friend class b3ClothContactSolver; + friend class b3Force; + friend class b3StretchForce; + friend class b3ShearForce; + friend class b3SpringForce; + friend class b3MouseForce; + friend class b3ElementForce; + + b3ClothParticle(const b3ClothParticleDef& def, b3Cloth* cloth); + ~b3ClothParticle(); + + // Synchronize spheres + void SynchronizeSpheres(); + + // Synchronize capsules + void SynchronizeCapsules(); + + // Synchronize triangles + void SynchronizeTriangles(); + + // Destroy spheres. + void DestroySpheres(); + + // Destroy capsules. + void DestroyCapsules(); + + // Destroy triangles. + void DestroyTriangles(); + + // Destroy forces. + void DestroyForces(); + + // Destroy contacts. + void DestroyContacts(); + + // Type + b3ClothParticleType m_type; + + // Position + b3Vec3 m_position; + + // Velocity + b3Vec3 m_velocity; + + // Applied external force + b3Vec3 m_force; + + // Applied translation + b3Vec3 m_translation; + + // Mass + scalar m_mass; + + // Inverse mass + scalar m_invMass; + + // Mesh index. + u32 m_meshIndex; + + // Solver temp identifier + u32 m_solverId; + + // Solver temp solution + b3Vec3 m_x; + + // User data + void* m_userData; + + // Cloth + b3Cloth* m_cloth; + + // Links to the cloth particle list. + b3ClothParticle* m_prev; + b3ClothParticle* m_next; +}; + +inline b3ClothParticleType b3ClothParticle::GetType() const +{ + return m_type; +} + +inline void b3ClothParticle::SetPosition(const b3Vec3& position) +{ + m_position = position; + m_translation.SetZero(); + + SynchronizeSpheres(); + SynchronizeCapsules(); + SynchronizeTriangles(); +} + +inline const b3Vec3& b3ClothParticle::GetPosition() const +{ + return m_position; +} + +inline void b3ClothParticle::SetVelocity(const b3Vec3& velocity) +{ + if (m_type == e_staticClothParticle) + { + return; + } + m_velocity = velocity; +} + +inline const b3Vec3& b3ClothParticle::GetVelocity() const +{ + return m_velocity; +} + +inline scalar b3ClothParticle::GetMass() const +{ + return m_mass; +} + +inline const b3Vec3& b3ClothParticle::GetForce() const +{ + return m_force; +} + +inline void b3ClothParticle::ApplyForce(const b3Vec3& force) +{ + if (m_type != e_dynamicClothParticle) + { + return; + } + m_force += force; +} + +inline const b3Vec3& b3ClothParticle::GetTranslation() const +{ + return m_translation; +} + +inline void b3ClothParticle::ApplyTranslation(const b3Vec3& translation) +{ + m_translation += translation; +} + +inline u32 b3ClothParticle::GetMeshIndex() const +{ + return m_meshIndex; +} + +inline void b3ClothParticle::SetUserData(void* userData) +{ + m_userData = userData; +} + +inline const void* b3ClothParticle::GetUserData() const +{ + return m_userData; +} + +inline void* b3ClothParticle::GetUserData() +{ + return m_userData; +} + +inline b3ClothParticle* b3ClothParticle::GetNext() +{ + return m_next; +} + +inline const b3ClothParticle* b3ClothParticle::GetNext() const +{ + return m_next; +} + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_solver.h b/include/bounce/cloth/cloth_solver.h index 75636f6..eab66d7 100644 --- a/include/bounce/cloth/cloth_solver.h +++ b/include/bounce/cloth/cloth_solver.h @@ -24,18 +24,22 @@ class b3StackAllocator; -class b3Particle; +class b3ClothParticle; class b3Force; -class b3ParticleBodyContact; -class b3ParticleTriangleContact; +class b3ClothSphereAndShapeContact; +class b3ClothSphereAndTriangleContact; +class b3ClothCapsuleAndCapsuleContact; + +struct b3ClothTimeStep; struct b3ClothSolverDef { b3StackAllocator* stack; u32 particleCapacity; u32 forceCapacity; - u32 bodyContactCapacity; + u32 shapeContactCapacity; u32 triangleContactCapacity; + u32 capsuleContactCapacity; }; class b3ClothSolver @@ -44,30 +48,35 @@ public: b3ClothSolver(const b3ClothSolverDef& def); ~b3ClothSolver(); - void Add(b3Particle* p); + void Add(b3ClothParticle* p); void Add(b3Force* f); - void Add(b3ParticleBodyContact* c); - void Add(b3ParticleTriangleContact* c); + void Add(b3ClothSphereAndShapeContact* c); + void Add(b3ClothSphereAndTriangleContact* c); + void Add(b3ClothCapsuleAndCapsuleContact* c); - void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); + void Solve(const b3ClothTimeStep& step, const b3Vec3& gravity); private: - b3StackAllocator* m_allocator; + b3StackAllocator* m_stack; u32 m_particleCapacity; u32 m_particleCount; - b3Particle** m_particles; + b3ClothParticle** m_particles; u32 m_forceCapacity; u32 m_forceCount; b3Force** m_forces; - u32 m_bodyContactCapacity; - u32 m_bodyContactCount; - b3ParticleBodyContact** m_bodyContacts; + u32 m_shapeContactCapacity; + u32 m_shapeContactCount; + b3ClothSphereAndShapeContact** m_shapeContacts; u32 m_triangleContactCapacity; u32 m_triangleContactCount; - b3ParticleTriangleContact** m_triangleContacts; + b3ClothSphereAndTriangleContact** m_triangleContacts; + + u32 m_capsuleContactCapacity; + u32 m_capsuleContactCount; + b3ClothCapsuleAndCapsuleContact** m_capsuleContacts; }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_collision.h b/include/bounce/cloth/cloth_time_step.h similarity index 75% rename from include/bounce/cloth/cloth_collision.h rename to include/bounce/cloth/cloth_time_step.h index 58335f5..9efad1b 100644 --- a/include/bounce/cloth/cloth_collision.h +++ b/include/bounce/cloth/cloth_time_step.h @@ -16,23 +16,19 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef B3_CLOTH_COLLISION_H -#define B3_CLOTH_COLLISION_H +#include -#include +#ifndef B3_CLOTH_TIME_STEP_H +#define B3_CLOTH_TIME_STEP_H -// Cloth primitive type -enum b3ClothAABBProxyType +// Time step parameters +struct b3ClothTimeStep { - e_particleProxy, - e_triangleProxy -}; - -// Cloth primitive broadphase proxy -struct b3ClothAABBProxy -{ - b3ClothAABBProxyType type; - void* owner; + scalar dt; + scalar inv_dt; + scalar dt_ratio; + u32 velocityIterations; + u32 positionIterations; }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_triangle.h b/include/bounce/cloth/cloth_triangle.h deleted file mode 100644 index f4c09a2..0000000 --- a/include/bounce/cloth/cloth_triangle.h +++ /dev/null @@ -1,113 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef B3_CLOTH_TRIANGLE_H -#define B3_CLOTH_TRIANGLE_H - -#include -#include "bounce/cloth/cloth.h" - -// A cloth triangle -class b3ClothTriangle -{ -public: - // Return the triangle index. - u32 GetTriangle() const; - - // Set the triangle radius. - void SetRadius(float32 radius); - - // Return the triangle radius. - float32 GetRadius() const; - - // Set the triangle coefficient of friction. - void SetFriction(float32 friction); - - // Return the triangle coefficient of friction. - float32 GetFriction() const; -private: - friend class b3Cloth; - friend class b3Particle; - friend class b3ShearForce; - friend class b3StrechForce; - friend class b3MouseForce; - friend class b3ClothContactManager; - friend class b3ParticleTriangleContact; - friend class b3ClothSolver; - friend class b3ClothContactSolver; - - b3ClothTriangle() { } - ~b3ClothTriangle() { } - - // Synchronize AABB - void Synchronize(const b3Vec3& displacement); - - // Cloth - b3Cloth* m_cloth; - - // Triangle index - u32 m_triangle; - - // Radius - float32 m_radius; - - // Coefficient of friction - float32 m_friction; - - // AABB Proxy - b3ClothAABBProxy m_aabbProxy; - - // Broadphase ID - u32 m_broadPhaseId; - - // Alpha - float32 m_alpha; - - // Strech matrix - float32 m_du1, m_dv1; - float32 m_du2, m_dv2; - float32 m_inv_det; -}; - -inline u32 b3ClothTriangle::GetTriangle() const -{ - return m_triangle; -} - -inline void b3ClothTriangle::SetRadius(float32 radius) -{ - m_radius = radius; - Synchronize(b3Vec3_zero); -} - -inline float32 b3ClothTriangle::GetRadius() const -{ - return m_radius; -} - -inline void b3ClothTriangle::SetFriction(float32 friction) -{ - m_friction = friction; -} - -inline float32 b3ClothTriangle::GetFriction() const -{ - return m_friction; -} - -#endif diff --git a/include/bounce/cloth/contacts/cloth_particle_triangle_contact.h b/include/bounce/cloth/contacts/cloth_capsule_capsule_contact.h similarity index 59% rename from include/bounce/cloth/contacts/cloth_particle_triangle_contact.h rename to include/bounce/cloth/contacts/cloth_capsule_capsule_contact.h index a5cc347..01879b4 100644 --- a/include/bounce/cloth/contacts/cloth_particle_triangle_contact.h +++ b/include/bounce/cloth/contacts/cloth_capsule_capsule_contact.h @@ -16,50 +16,48 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef B3_CLOTH_PARTICLE_TRIANGLE_CONTACT_H -#define B3_CLOTH_PARTICLE_TRIANGLE_CONTACT_H +#ifndef B3_CLOTH_CAPSULE_AND_CAPSULE_CONTACT_H +#define B3_CLOTH_CAPSULE_AND_CAPSULE_CONTACT_H #include +#include +#include -class b3Particle; -class b3ClothTriangle; +class b3ClothCapsuleShape; -// Contact between particle and a triangle -class b3ParticleTriangleContact +// Contact between capsules +class b3ClothCapsuleAndCapsuleContact { public: private: - friend class b3List2; friend class b3Cloth; - friend class b3Particle; - friend class b3ClothTriangle; + friend class b3ClothParticle; + friend class b3ClothCapsuleShape; friend class b3ClothContactManager; friend class b3ClothContactSolver; + friend class b3List2; - b3ParticleTriangleContact() { } - ~b3ParticleTriangleContact() { } + b3ClothCapsuleAndCapsuleContact() { } + ~b3ClothCapsuleAndCapsuleContact() { } void Update(); - // Particle - b3Particle* m_p1; - - // Triangle - b3ClothTriangle* m_t2; - b3Particle* m_p2; - b3Particle* m_p3; - b3Particle* m_p4; - - float32 m_w2, m_w3, m_w4; - - float32 m_normalImpulse; - float32 m_tangentImpulse1; - float32 m_tangentImpulse2; + b3ClothCapsuleShape* m_s1; + b3ClothCapsuleShape* m_s2; bool m_active; - b3ParticleTriangleContact* m_prev; - b3ParticleTriangleContact* m_next; + scalar m_w1, m_w2, m_w3, m_w4; + + b3Vec3 m_normal1; + scalar m_normalImpulse; + + b3Vec3 m_tangent1; + b3Vec3 m_tangent2; + b3Vec2 m_tangentImpulse; + + b3ClothCapsuleAndCapsuleContact* m_prev; + b3ClothCapsuleAndCapsuleContact* m_next; }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/contacts/cloth_contact_solver.h b/include/bounce/cloth/contacts/cloth_contact_solver.h index 2b33265..5b4f8af 100644 --- a/include/bounce/cloth/contacts/cloth_contact_solver.h +++ b/include/bounce/cloth/contacts/cloth_contact_solver.h @@ -21,35 +21,65 @@ #include #include +#include class b3StackAllocator; -class b3Particle; +class b3ClothParticle; class b3Body; -class b3ParticleBodyContact; -class b3ParticleTriangleContact; +class b3ClothSphereAndShapeContact; +class b3ClothSphereAndTriangleContact; +class b3ClothCapsuleAndCapsuleContact; -struct b3ClothSolverBodyContactVelocityConstraint +struct b3ClothSolverShapeContactVelocityConstraint { u32 indexA; - float32 invMassA; - b3Mat33 invIA; + scalar invMassA; - b3Body* bodyB; - float32 invMassB; - b3Mat33 invIB; - - float32 friction; - - b3Vec3 point; - b3Vec3 rA; - b3Vec3 rB; + scalar friction; b3Vec3 normal; - float32 normalMass; - float32 normalImpulse; - float32 velocityBias; + scalar normalMass; + scalar normalImpulse; + + b3Vec3 tangent1; + b3Vec3 tangent2; + scalar tangentMass; + b3Vec2 tangentImpulse; +}; + +struct b3ClothSolverShapeContactPositionConstraint +{ + u32 indexA; + scalar invMassA; + scalar radiusA; + + scalar radiusB; + + b3Vec3 normal; + b3Vec3 pointB; +}; + +struct b3ClothSolverTriangleContactVelocityConstraint +{ + u32 indexA; + scalar invMassA; + + u32 indexB; + scalar invMassB; + u32 indexC; + scalar invMassC; + u32 indexD; + scalar invMassD; + + scalar wB, wC, wD; + + b3Vec3 normal; + scalar normalMass; + scalar normalImpulse; + + scalar friction; b3Vec3 tangent1; b3Vec3 tangent2; @@ -57,88 +87,85 @@ struct b3ClothSolverBodyContactVelocityConstraint b3Vec2 tangentImpulse; }; -struct b3ClothSolverBodyContactPositionConstraint -{ - u32 indexA; - float32 invMassA; - b3Mat33 invIA; - float32 radiusA; - b3Vec3 localCenterA; - - b3Body* bodyB; - float32 invMassB; - b3Mat33 invIB; - float32 radiusB; - b3Vec3 localCenterB; - - b3Vec3 rA; - b3Vec3 rB; - - b3Vec3 normalA; - b3Vec3 localPointA; - b3Vec3 localPointB; -}; - -struct b3ClothSolverTriangleContactVelocityConstraint -{ - u32 indexA; - float32 invMassA; - - u32 indexB; - float32 invMassB; - u32 indexC; - float32 invMassC; - u32 indexD; - float32 invMassD; - - float32 wB, wC, wD; - - b3Vec3 normal; - float32 normalMass; - float32 normalImpulse; - - float32 friction; - - b3Vec3 tangent1; - b3Vec3 tangent2; - float32 tangentMass1; - float32 tangentMass2; - float32 tangentImpulse1; - float32 tangentImpulse2; -}; - struct b3ClothSolverTriangleContactPositionConstraint { u32 indexA; - float32 invMassA; - float32 radiusA; + scalar invMassA; + scalar radiusA; u32 indexB; - float32 invMassB; + scalar invMassB; u32 indexC; - float32 invMassC; + scalar invMassC; u32 indexD; - float32 invMassD; - float32 triangleRadius; + scalar invMassD; + scalar triangleRadius; - float32 wB, wC, wD; + scalar wB, wC, wD; +}; + +struct b3ClothSolverCapsuleContactVelocityConstraint +{ + u32 indexA; + scalar invMassA; + u32 indexB; + scalar invMassB; + + u32 indexC; + scalar invMassC; + u32 indexD; + scalar invMassD; + + scalar wA, wB, wC, wD; + + b3Vec3 normal; + scalar normalMass; + scalar normalImpulse; + + scalar friction; + + b3Vec3 tangent1; + b3Vec3 tangent2; + b3Mat22 tangentMass; + b3Vec2 tangentImpulse; +}; + +struct b3ClothSolverCapsuleContactPositionConstraint +{ + u32 indexA; + scalar invMassA; + u32 indexB; + scalar invMassB; + scalar radiusA; + + u32 indexC; + scalar invMassC; + u32 indexD; + scalar invMassD; + scalar radiusB; + + scalar wA, wB, wC, wD; }; struct b3ClothContactSolverDef { + b3ClothTimeStep step; b3StackAllocator* allocator; b3Vec3* positions; b3Vec3* velocities; - u32 bodyContactCount; - b3ParticleBodyContact** bodyContacts; + u32 shapeContactCount; + b3ClothSphereAndShapeContact** shapeContacts; u32 triangleContactCount; - b3ParticleTriangleContact** triangleContacts; + b3ClothSphereAndTriangleContact** triangleContacts; + + u32 capsuleContactCount; + b3ClothCapsuleAndCapsuleContact** capsuleContacts; }; -inline float32 b3MixFriction(float32 u1, float32 u2) +inline scalar b3MixFriction(scalar u1, scalar u2) { return b3Sqrt(u1 * u2); } @@ -149,34 +176,45 @@ public: b3ClothContactSolver(const b3ClothContactSolverDef& def); ~b3ClothContactSolver(); - void InitializeBodyContactConstraints(); + void InitializeShapeContactConstraints(); void InitializeTriangleContactConstraints(); + void InitializeCapsuleContactConstraints(); - void WarmStartBodyContactConstraints(); + void WarmStartShapeContactConstraints(); void WarmStartTriangleContactConstraints(); + void WarmStartCapsuleContactConstraints(); - void SolveBodyContactVelocityConstraints(); + void SolveShapeContactVelocityConstraints(); void SolveTriangleContactVelocityConstraints(); + void SolveCapsuleContactVelocityConstraints(); void StoreImpulses(); - bool SolveBodyContactPositionConstraints(); + bool SolveShapeContactPositionConstraints(); bool SolveTriangleContactPositionConstraints(); + bool SolveCapsuleContactPositionConstraints(); protected: + b3ClothTimeStep m_step; + b3StackAllocator* m_allocator; b3Vec3* m_positions; b3Vec3* m_velocities; - u32 m_bodyContactCount; - b3ParticleBodyContact** m_bodyContacts; - b3ClothSolverBodyContactVelocityConstraint* m_bodyVelocityConstraints; - b3ClothSolverBodyContactPositionConstraint* m_bodyPositionConstraints; + u32 m_shapeContactCount; + b3ClothSphereAndShapeContact** m_shapeContacts; + b3ClothSolverShapeContactVelocityConstraint* m_shapeVelocityConstraints; + b3ClothSolverShapeContactPositionConstraint* m_shapePositionConstraints; u32 m_triangleContactCount; - b3ParticleTriangleContact** m_triangleContacts; + b3ClothSphereAndTriangleContact** m_triangleContacts; b3ClothSolverTriangleContactVelocityConstraint* m_triangleVelocityConstraints; b3ClothSolverTriangleContactPositionConstraint* m_trianglePositionConstraints; + + u32 m_capsuleContactCount; + b3ClothCapsuleAndCapsuleContact** m_capsuleContacts; + b3ClothSolverCapsuleContactVelocityConstraint* m_capsuleVelocityConstraints; + b3ClothSolverCapsuleContactPositionConstraint* m_capsulePositionConstraints; }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/contacts/cloth_particle_body_contact.h b/include/bounce/cloth/contacts/cloth_sphere_shape_contact.h similarity index 61% rename from include/bounce/cloth/contacts/cloth_particle_body_contact.h rename to include/bounce/cloth/contacts/cloth_sphere_shape_contact.h index c774329..24eb1fb 100644 --- a/include/bounce/cloth/contacts/cloth_particle_body_contact.h +++ b/include/bounce/cloth/contacts/cloth_sphere_shape_contact.h @@ -16,61 +16,60 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef B3_CLOTH_PARTICLE_BODY_CONTACT_H -#define B3_CLOTH_PARTICLE_BODY_CONTACT_H +#ifndef B3_CLOTH_SPHERE_AND_SHAPE_CONTACT_H +#define B3_CLOTH_SPHERE_AND_SHAPE_CONTACT_H #include #include #include #include -class b3Particle; -class b3Shape; +class b3ClothSphereShape; +class b3ClothWorldShape; -// A contact between a particle and a body -class b3ParticleBodyContact +// A contact between a sphere and a shape +class b3ClothSphereAndShapeContact { public: private: - friend class b3List2; + friend class b3List2; friend class b3Cloth; - friend class b3Particle; + friend class b3ClothParticle; + friend class b3ClothSphereShape; + friend class b3ClothWorldShape; friend class b3ClothContactManager; friend class b3ClothSolver; friend class b3ClothContactSolver; - friend struct b3ParticleBodyContactWorldPoint; + friend struct b3ClothSphereAndShapeContactWorldPoint; - b3ParticleBodyContact() { } - ~b3ParticleBodyContact() { } + b3ClothSphereAndShapeContact() { } + ~b3ClothSphereAndShapeContact() { } void Update(); - b3Particle* m_p1; - b3Shape* m_s2; + b3ClothSphereShape* m_s1; + b3ClothWorldShape* m_s2; bool m_active; - // Contact constraint - b3Vec3 m_normal1; - b3Vec3 m_localPoint1; - b3Vec3 m_localPoint2; - float32 m_normalImpulse; + b3Vec3 m_normal2; + b3Vec3 m_point2; + scalar m_normalImpulse; - // Friction constraint b3Vec3 m_tangent1, m_tangent2; b3Vec2 m_tangentImpulse; - b3ParticleBodyContact* m_prev; - b3ParticleBodyContact* m_next; + b3ClothSphereAndShapeContact* m_prev; + b3ClothSphereAndShapeContact* m_next; }; -struct b3ParticleBodyContactWorldPoint +struct b3ClothSphereAndShapeContactWorldPoint { - void Initialize(const b3ParticleBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB); + void Initialize(const b3ClothSphereAndShapeContact* c, scalar rA, const b3Vec3& cA, scalar rB); b3Vec3 point; b3Vec3 normal; - float32 separation; + scalar separation; }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/contacts/cloth_sphere_triangle_contact.h b/include/bounce/cloth/contacts/cloth_sphere_triangle_contact.h new file mode 100644 index 0000000..9bb2c23 --- /dev/null +++ b/include/bounce/cloth/contacts/cloth_sphere_triangle_contact.h @@ -0,0 +1,65 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_SPHERE_AND_TRIANGLE_CONTACT_H +#define B3_CLOTH_SPHERE_AND_TRIANGLE_CONTACT_H + +#include +#include +#include + +class b3ClothSphereShape; +class b3ClothTriangleShape; + +// Contact between sphere and a triangle +class b3ClothSphereAndTriangleContact +{ +public: +private: + friend class b3List2; + friend class b3Cloth; + friend class b3ClothParticle; + friend class b3ClothSphereShape; + friend class b3ClothTriangleShape; + friend class b3ClothContactManager; + friend class b3ClothContactSolver; + + b3ClothSphereAndTriangleContact() { } + ~b3ClothSphereAndTriangleContact() { } + + void Update(); + + b3ClothSphereShape* m_s1; + b3ClothTriangleShape* m_s2; + + bool m_active; + + scalar m_w1, m_w2, m_w3; + + b3Vec3 m_normal1; + scalar m_normalImpulse; + + b3Vec3 m_tangent1; + b3Vec3 m_tangent2; + b3Vec2 m_tangentImpulse; + + b3ClothSphereAndTriangleContact* m_prev; + b3ClothSphereAndTriangleContact* m_next; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/forces/element_force.h b/include/bounce/cloth/forces/element_force.h new file mode 100644 index 0000000..f0cad74 --- /dev/null +++ b/include/bounce/cloth/forces/element_force.h @@ -0,0 +1,121 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_ELEMENT_FORCE_H +#define B3_ELEMENT_FORCE_H + +#include +#include +#include + +// Element force definition. +// This requires defining the triangle in the rest state and +// some material parameters. +struct b3ElementForceDef : public b3ForceDef +{ + b3ElementForceDef() + { + type = e_elementForce; + E_x = scalar(0); + E_y = scalar(0); + E_s = scalar(0); + nu_xy = scalar(0); + nu_yx = scalar(0); + } + + // Particle 1 + b3ClothParticle* p1; + + // Particle 2 + b3ClothParticle* p2; + + // Particle 3 + b3ClothParticle* p3; + + // Triangle vertices in rest state + b3Vec3 v1, v2, v3; + + // Young Modulus in x direction + scalar E_x; + + // Young Modulus in y direction + scalar E_y; + + // Shear Modulus + scalar E_s; + + // x, y Poisson's Ratio + scalar nu_xy; + + // y, x Poisson's Ratio + scalar nu_yx; +}; + +// Element force acting on a cloth triangle. +class b3ElementForce : public b3Force +{ +public: + // Has this force a given particle? + bool HasParticle(const b3ClothParticle* particle) const; +private: + friend class b3Force; + friend class b3Cloth; + + b3ElementForce(const b3ElementForceDef* def); + ~b3ElementForce(); + + void Apply(const b3ClothForceSolverData* data); + + // Particle 1 + b3ClothParticle* m_p1; + + // Particle 2 + b3ClothParticle* m_p2; + + // Particle 3 + b3ClothParticle* m_p3; + + // Rest triangle vertices in 2D + b3Vec2 m_x1, m_x2, m_x3; + + // Rest triangle area in 2D + scalar m_A; + + // Initial inverse deformation in 2D + b3Mat22 m_invS; + + // Young Modulus in 2D + scalar m_E_x, m_E_y, m_E_s; + + // Poisson Ratio in 2D + scalar m_nu_xy, m_nu_yx; + + // Elasticity tensor + // This is a 3x3 matrix + b3Mat33 m_C; + + // Shape functions (barycentric) matrix + // This is a 3x6 matrix + scalar m_B[18]; + + // Blocked stiffness matrix + // This is a 6x6 matrix + b3Mat22 m_K[9]; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/forces/force.h b/include/bounce/cloth/forces/force.h index 27873f6..87bf598 100644 --- a/include/bounce/cloth/forces/force.h +++ b/include/bounce/cloth/forces/force.h @@ -22,17 +22,18 @@ #include #include -struct b3ClothForceSolverData; +class b3ClothParticle; -class b3Particle; +struct b3ClothForceSolverData; // Force types enum b3ForceType { - e_strechForce, + e_stretchForce, e_shearForce, e_springForce, e_mouseForce, + e_elementForce, }; struct b3ForceDef @@ -40,24 +41,26 @@ struct b3ForceDef b3ForceType type; }; -// +// A force acts on a set of particles. class b3Force { public: - // + // Get the force type. b3ForceType GetType() const; - // - b3Force* GetNext(); + // Has this force a given particle? + virtual bool HasParticle(const b3ClothParticle* particle) const = 0; - // - virtual bool HasParticle(const b3Particle* particle) const = 0; + // Get the next force in the cloth force list. + const b3Force* GetNext() const; + b3Force* GetNext(); protected: friend class b3List2; friend class b3Cloth; + friend class b3ClothParticle; friend class b3ClothForceSolver; - friend class b3Particle; + // Factory create and destroy. static b3Force* Create(const b3ForceDef* def); static void Destroy(b3Force* f); @@ -66,13 +69,9 @@ protected: virtual void Apply(const b3ClothForceSolverData* data) = 0; - // Force type b3ForceType m_type; - // b3Force* m_prev; - - // b3Force* m_next; }; @@ -81,6 +80,11 @@ inline b3ForceType b3Force::GetType() const return m_type; } +inline const b3Force* b3Force::GetNext() const +{ + return m_next; +} + inline b3Force* b3Force::GetNext() { return m_next; diff --git a/include/bounce/cloth/forces/mouse_force.h b/include/bounce/cloth/forces/mouse_force.h index 9646359..639406d 100644 --- a/include/bounce/cloth/forces/mouse_force.h +++ b/include/bounce/cloth/forces/mouse_force.h @@ -21,51 +21,93 @@ #include -class b3ClothTriangle; - +// Mouse force definition. +// This requires defining a particle and a triangle +// and the coordinates of the particle local to the triangle +// in the rest state using a barycentric coordinate system. +// You must also provide spring parameters. struct b3MouseForceDef : public b3ForceDef { b3MouseForceDef() { - type = e_mouseForce; + type = e_mouseForce; + w2 = scalar(0); + w3 = scalar(0); + w4 = scalar(0); + restLength = scalar(0); + mouse = scalar(0); + damping = scalar(0); } - // Particle - b3Particle* particle; - - // Triangle - b3ClothTriangle* triangle; + // Particle 1 + b3ClothParticle* p1; + // Particle 2 + b3ClothParticle* p2; + + // Particle 3 + b3ClothParticle* p3; + + // Particle 4 + b3ClothParticle* p4; + // Barycentric coordinates on triangle - float32 w2, w3, w4; + scalar w2, w3, w4; // Mouse stiffness - float32 mouse; + scalar mouse; // Damping stiffness - float32 damping; + scalar damping; + + // Rest length + scalar restLength; }; // Mouse force acting on a particle and triangle. +// This force will keep a point on one particle and the other on the +// triangle to a given desired distance. class b3MouseForce : public b3Force { public: - bool HasParticle(const b3Particle* particle) const; + // Has this force a given particle? + bool HasParticle(const b3ClothParticle* particle) const; - b3Particle* GetParticle() const; + // Get the particle 1. + const b3ClothParticle* GetParticle1() const; + b3ClothParticle* GetParticle1(); + + // Get the particle 2. + const b3ClothParticle* GetParticle2() const; + b3ClothParticle* GetParticle2(); - b3ClothTriangle* GetTriangle() const; + // Get the particle 3. + const b3ClothParticle* GetParticle3() const; + b3ClothParticle* GetParticle3(); + + // Get the particle 4. + const b3ClothParticle* GetParticle4() const; + b3ClothParticle* GetParticle4(); + + // Get the natural spring length. + scalar GetRestLenght() const; - float32 GetMouseStiffness() const; + // Get the mouse stiffness. + scalar GetMouseStiffness() const; - float32 GetDampingStiffness() const; + // Get the damping stiffness. + scalar GetDampingStiffness() const; + // Get the force acting on particle 1. b3Vec3 GetActionForce1() const; + // Get the force acting on particle 2. b3Vec3 GetActionForce2() const; + // Get the force acting on particle 3. b3Vec3 GetActionForce3() const; + // Get the force acting on particle 4. b3Vec3 GetActionForce4() const; private: friend class b3Force; @@ -76,43 +118,85 @@ private: void Apply(const b3ClothForceSolverData* data); - // Solver shared + // Particle 1 + b3ClothParticle* m_p1; - // Particle - b3Particle* m_particle; + // Particle 2 + b3ClothParticle* m_p2; - // Triangle - b3ClothTriangle* m_triangle; - - // Barycentric coordinates - float32 m_w2, m_w3, m_w4; + // Particle 3 + b3ClothParticle* m_p3; + + // Particle 4 + b3ClothParticle* m_p4; + + // Barycentric coordinates in the rest state + scalar m_w2, m_w3, m_w4; // Mouse stiffness - float32 m_km; + scalar m_km; // Damping stiffness - float32 m_kd; + scalar m_kd; + + // Rest length + scalar m_L0; // Action forces b3Vec3 m_f1, m_f2, m_f3, m_f4; }; -inline b3Particle* b3MouseForce::GetParticle() const +inline const b3ClothParticle* b3MouseForce::GetParticle1() const { - return m_particle; + return m_p1; } -inline b3ClothTriangle* b3MouseForce::GetTriangle() const +inline b3ClothParticle* b3MouseForce::GetParticle1() { - return m_triangle; + return m_p1; } -inline float32 b3MouseForce::GetMouseStiffness() const +inline const b3ClothParticle* b3MouseForce::GetParticle2() const +{ + return m_p2; +} + +inline b3ClothParticle* b3MouseForce::GetParticle2() +{ + return m_p2; +} + +inline const b3ClothParticle* b3MouseForce::GetParticle3() const +{ + return m_p3; +} + +inline b3ClothParticle* b3MouseForce::GetParticle3() +{ + return m_p3; +} + +inline const b3ClothParticle* b3MouseForce::GetParticle4() const +{ + return m_p4; +} + +inline b3ClothParticle* b3MouseForce::GetParticle4() +{ + return m_p4; +} + +inline scalar b3MouseForce::GetRestLenght() const +{ + return m_L0; +} + +inline scalar b3MouseForce::GetMouseStiffness() const { return m_km; } -inline float32 b3MouseForce::GetDampingStiffness() const +inline scalar b3MouseForce::GetDampingStiffness() const { return m_kd; } diff --git a/include/bounce/cloth/forces/shear_force.h b/include/bounce/cloth/forces/shear_force.h index 9092100..7cdb680 100644 --- a/include/bounce/cloth/forces/shear_force.h +++ b/include/bounce/cloth/forces/shear_force.h @@ -21,41 +21,88 @@ #include -class b3ClothTriangle; - +// Shear force definition. +// This requires defining the (u, v) coordinates +// of the triangle and some parameters. struct b3ShearForceDef : public b3ForceDef { b3ShearForceDef() { type = e_shearForce; + u1 = scalar(0); + v1 = scalar(0); + u2 = scalar(0); + v2 = scalar(0); + u3 = scalar(0); + v3 = scalar(0); + alpha = scalar(0); + shearing = scalar(0); + damping = scalar(0); } + + // Initialize this definition from rest positions + void Initialize(const b3Vec3& p1, const b3Vec3& p2, const b3Vec3& p3); + + // Particle 1 + b3ClothParticle* p1; - // Triangle - b3ClothTriangle* triangle; + // Particle 2 + b3ClothParticle* p2; + + // Particle 3 + b3ClothParticle* p3; + + // (u, v) coordinates for vertex 1 in the rest state + scalar u1, v1; + + // (u, v) coordinates for vertex 2 in the rest state + scalar u2, v2; + + // (u, v) coordinates for vertex 3 in the rest state + scalar u3, v3; + + // Typically this is the triangle area in the rest state. + scalar alpha; // Shearing stiffness - float32 shearing; + scalar shearing; // Damping stiffness - float32 damping; + scalar damping; }; // Shear force acting on a cloth triangle. class b3ShearForce : public b3Force { public: - bool HasParticle(const b3Particle* particle) const; + // Has this force a given particle? + bool HasParticle(const b3ClothParticle* particle) const; - b3ClothTriangle* GetTriangle() const; + // Get the particle 1. + const b3ClothParticle* GetParticle1() const { return m_p1; } + b3ClothParticle* GetParticle1() { return m_p1; } - float32 GetShearingStiffness() const; + // Get the particle 2. + const b3ClothParticle* GetParticle2() const { return m_p2; } + b3ClothParticle* GetParticle2() { return m_p2; } + + // Get the particle 3. + const b3ClothParticle* GetParticle3() const { return m_p3; } + b3ClothParticle* GetParticle3() { return m_p3; } - float32 GetDampingStiffness() const; + // Get the shearing stiffness. + scalar GetShearingStiffness() const; + // Get the damping stiffness. + scalar GetDampingStiffness() const; + + // Get the force acting on particle 1. b3Vec3 GetActionForce1() const; + // Get the force acting on particle 2. b3Vec3 GetActionForce2() const; + // Get the force acting on particle 3. b3Vec3 GetActionForce3() const; private: friend class b3Force; @@ -66,32 +113,42 @@ private: void Apply(const b3ClothForceSolverData* data); - // Solver shared + // Particle 1 + b3ClothParticle* m_p1; - // Triangle - b3ClothTriangle* m_triangle; + // Particle 2 + b3ClothParticle* m_p2; + + // Particle 3 + b3ClothParticle* m_p3; + + // Alpha + scalar m_alpha; + + // (u, v) matrix + scalar m_du1, m_dv1; + scalar m_du2, m_dv2; + scalar m_inv_det; + + // dwudx, dwvdx + b3Vec3 m_dwudx, m_dwvdx; // Shearing stiffness - float32 m_ks; + scalar m_ks; // Damping stiffness - float32 m_kd; + scalar m_kd; // Action forces b3Vec3 m_f1, m_f2, m_f3; }; -inline b3ClothTriangle* b3ShearForce::GetTriangle() const -{ - return m_triangle; -} - -inline float32 b3ShearForce::GetShearingStiffness() const +inline scalar b3ShearForce::GetShearingStiffness() const { return m_ks; } -inline float32 b3ShearForce::GetDampingStiffness() const +inline scalar b3ShearForce::GetDampingStiffness() const { return m_kd; } diff --git a/include/bounce/cloth/forces/spring_force.h b/include/bounce/cloth/forces/spring_force.h index 3cd0a41..4060f0b 100644 --- a/include/bounce/cloth/forces/spring_force.h +++ b/include/bounce/cloth/forces/spring_force.h @@ -21,54 +21,68 @@ #include +// Spring force definition. +// This requires defining two particles, the +// natural spring rest length, and the spring parameters. struct b3SpringForceDef : public b3ForceDef { b3SpringForceDef() { type = e_springForce; - p1 = nullptr; - p2 = nullptr; - restLength = 0.0f; - structural = 0.0f; - damping = 0.0f; + restLength = scalar(0); + structural = scalar(0); + damping = scalar(0); } - // - void Initialize(b3Particle* particle1, b3Particle* particle2, float32 structuralStiffness, float32 dampingStiffness); + // Initialize this definition from particles and stiffnesses. + void Initialize(b3ClothParticle* particle1, b3ClothParticle* particle2, scalar structuralStiffness, scalar dampingStiffness); // Particle 1 - b3Particle* p1; + b3ClothParticle* p1; // Particle 2 - b3Particle* p2; + b3ClothParticle* p2; // Rest length - float32 restLength; + scalar restLength; // Structural stiffness - float32 structural; + scalar structural; // Damping stiffness - float32 damping; + scalar damping; }; -// +// A spring force acting on two particles +// to maintain them at a desired distance. class b3SpringForce : public b3Force { public: - b3Particle* GetParticle1(); + // Does this force contain a given particle? + bool HasParticle(const b3ClothParticle* particle) const; - b3Particle* GetParticle2(); + // Get the particle 1. + const b3ClothParticle* GetParticle1() const { return m_p1; } + b3ClothParticle* GetParticle1() { return m_p1; } - float32 GetRestLenght() const; + // Get the particle 2. + const b3ClothParticle* GetParticle2() const { return m_p2; } + b3ClothParticle* GetParticle2() { return m_p2; } - float32 GetStructuralStiffness() const; + // Get the spring natural rest length. + scalar GetRestLenght() const; - float32 GetDampingStiffness() const; + // Get the spring stiffness. + scalar GetStructuralStiffness() const; + // Get the damping stiffness. + scalar GetDampingStiffness() const; + + // Get the force acting on particle 1. b3Vec3 GetActionForce() const; - bool HasParticle(const b3Particle* particle) const; + // Get the force acting on particle 2. + b3Vec3 GetReactionForce() const; private: friend class b3Force; friend class b3Cloth; @@ -78,60 +92,48 @@ private: void Apply(const b3ClothForceSolverData* data); - // Solver shared - // Particle 1 - b3Particle* m_p1; + b3ClothParticle* m_p1; // Particle 2 - b3Particle* m_p2; + b3ClothParticle* m_p2; - // Rest length - float32 m_L0; + // Spring natural rest length + scalar m_L0; - // Structural stiffness - float32 m_ks; + // Spring stiffness + scalar m_ks; // Damping stiffness - float32 m_kd; + scalar m_kd; - // Applied internal force (on particle 1) - b3Vec3 m_f; + // Action forces + b3Vec3 m_f1, m_f2; }; -inline b3Particle * b3SpringForce::GetParticle1() -{ - return m_p1; -} - -inline b3Particle* b3SpringForce::GetParticle2() -{ - return m_p2; -} - -inline float32 b3SpringForce::GetRestLenght() const +inline scalar b3SpringForce::GetRestLenght() const { return m_L0; } -inline float32 b3SpringForce::GetStructuralStiffness() const +inline scalar b3SpringForce::GetStructuralStiffness() const { return m_ks; } -inline float32 b3SpringForce::GetDampingStiffness() const +inline scalar b3SpringForce::GetDampingStiffness() const { return m_kd; } inline b3Vec3 b3SpringForce::GetActionForce() const { - return m_f; + return m_f1; } -inline bool b3SpringForce::HasParticle(const b3Particle* particle) const +inline b3Vec3 b3SpringForce::GetReactionForce() const { - return m_p1 == particle || m_p2 == particle; + return m_f2; } #endif \ No newline at end of file diff --git a/include/bounce/cloth/forces/strech_force.h b/include/bounce/cloth/forces/strech_force.h deleted file mode 100644 index ecbb5a2..0000000 --- a/include/bounce/cloth/forces/strech_force.h +++ /dev/null @@ -1,126 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef B3_STRECH_FORCE_H -#define B3_STRECH_FORCE_H - -#include - -class b3ClothTriangle; - -struct b3StrechForceDef : public b3ForceDef -{ - b3StrechForceDef() - { - type = e_strechForce; - } - - // Triangle - b3ClothTriangle* triangle; - - // Streching stiffness - float32 streching; - - // Damping stiffness - float32 damping; - - // Desired strechiness in u direction - float32 bu; - - // Desired strechiness in v direction - float32 bv; -}; - -// Strech force acting on a cloth triangle. -class b3StrechForce : public b3Force -{ -public: - bool HasParticle(const b3Particle* particle) const; - - b3ClothTriangle* GetTriangle() const; - - float32 GetStrechingStiffness() const; - - float32 GetDampingStiffness() const; - - b3Vec3 GetActionForce1() const; - - b3Vec3 GetActionForce2() const; - - b3Vec3 GetActionForce3() const; -private: - friend class b3Force; - friend class b3Cloth; - - b3StrechForce(const b3StrechForceDef* def); - ~b3StrechForce(); - - void Apply(const b3ClothForceSolverData* data); - - // Solver shared - - // Triangle - b3ClothTriangle* m_triangle; - - // Streching stiffness - float32 m_ks; - - // Damping stiffness - float32 m_kd; - - // Desired strechiness in u direction - float32 m_bu; - - // Desired strechiness in v direction - float32 m_bv; - - // Action forces - b3Vec3 m_f1, m_f2, m_f3; -}; - -inline b3ClothTriangle* b3StrechForce::GetTriangle() const -{ - return m_triangle; -} - -inline float32 b3StrechForce::GetStrechingStiffness() const -{ - return m_ks; -} - -inline float32 b3StrechForce::GetDampingStiffness() const -{ - return m_kd; -} - -inline b3Vec3 b3StrechForce::GetActionForce1() const -{ - return m_f1; -} - -inline b3Vec3 b3StrechForce::GetActionForce2() const -{ - return m_f2; -} - -inline b3Vec3 b3StrechForce::GetActionForce3() const -{ - return m_f3; -} - -#endif \ No newline at end of file diff --git a/include/bounce/cloth/forces/stretch_force.h b/include/bounce/cloth/forces/stretch_force.h new file mode 100644 index 0000000..66df526 --- /dev/null +++ b/include/bounce/cloth/forces/stretch_force.h @@ -0,0 +1,264 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_STRETCH_FORCE_H +#define B3_STRETCH_FORCE_H + +#include + +// Stretch force definition. +// This requires defining the (u, v) coordinates +// of the triangle and some parameters. +struct b3StretchForceDef : public b3ForceDef +{ + b3StretchForceDef() + { + type = e_stretchForce; + u1 = scalar(0); + v1 = scalar(0); + u2 = scalar(0); + v2 = scalar(0); + u3 = scalar(0); + v3 = scalar(0); + alpha = scalar(0); + stretching_u = scalar(0); + damping_u = scalar(0); + b_u = scalar(1); + stretching_v = scalar(0); + damping_v = scalar(0); + b_v = scalar(1); + } + + // Initialize this definition from rest positions + void Initialize(const b3Vec3& p1, const b3Vec3& p2, const b3Vec3& p3); + + // Particle 1 + b3ClothParticle* p1; + + // Particle 2 + b3ClothParticle* p2; + + // Particle 3 + b3ClothParticle* p3; + + // (u, v) coordinates for vertex 1 in the rest state + scalar u1, v1; + + // (u, v) coordinates for vertex 2 in the rest state + scalar u2, v2; + + // (u, v) coordinates for vertex 3 in the rest state + scalar u3, v3; + + // Typically this is the triangle area in the rest state. + scalar alpha; + + // Stretching stiffness in u direction + scalar stretching_u; + + // Damping stiffness in u direction + scalar damping_u; + + // Desired strechiness in u direction + scalar b_u; + + // Stretching stiffness in v direction + scalar stretching_v; + + // Damping stiffness in v direction + scalar damping_v; + + // Desired strechiness in v direction + scalar b_v; +}; + +// Stretch force acting on a cloth triangle. +// This maintains the triangle edge lengths in the (u, v) +// frame of reference at a given desired normalized rest distance +// in the direction of the u and v coordinates. +class b3StretchForce : public b3Force +{ +public: + // Has this force a given particle? + bool HasParticle(const b3ClothParticle* particle) const; + + // Get the particle 1. + b3ClothParticle* GetParticle1(); + const b3ClothParticle* GetParticle1() const; + + // Get the particle 2. + b3ClothParticle* GetParticle2(); + const b3ClothParticle* GetParticle2() const; + + // Get the particle 2. + b3ClothParticle* GetParticle3(); + const b3ClothParticle* GetParticle3() const; + + // Get the spring stiffness in the u direction. + scalar GetStrechingStiffnessU() const; + + // Get the damping stiffness in the u direction. + scalar GetDampingStiffnessU() const; + + // Get the normalized rest lenght in the u direction. + scalar GetBU() const; + + // Get the spring stiffness in the v direction. + scalar GetStrechingStiffnessV() const; + + // Get the damping stiffness in the v direction. + scalar GetDampingStiffnessV() const; + + // Get the normalized rest lenght in the v direction. + scalar GetBV() const; + + // Get the force acting on particle 1. + b3Vec3 GetActionForce1() const; + + // Get the force acting on particle 2. + b3Vec3 GetActionForce2() const; + + // Get the force acting on particle 3. + b3Vec3 GetActionForce3() const; +private: + friend class b3Force; + friend class b3Cloth; + + b3StretchForce(const b3StretchForceDef* def); + ~b3StretchForce(); + + void Apply(const b3ClothForceSolverData* data); + + // Particle 1 + b3ClothParticle* m_p1; + + // Particle 2 + b3ClothParticle* m_p2; + + // Particle 3 + b3ClothParticle* m_p3; + + // Alpha + scalar m_alpha; + + // (u, v) matrix + scalar m_du1, m_dv1; + scalar m_du2, m_dv2; + scalar m_inv_det; + + // dwudx, dwvdx + b3Vec3 m_dwudx, m_dwvdx; + + // Streching stiffness in u direction + scalar m_ks_u; + + // Damping stiffness in u direction + scalar m_kd_u; + + // Desired strechiness in u direction + scalar m_b_u; + + // Streching stiffness in v direction + scalar m_ks_v; + + // Damping stiffness in v direction + scalar m_kd_v; + + // Desired strechiness in v direction + scalar m_b_v; + + // Action forces + b3Vec3 m_f1, m_f2, m_f3; +}; + +inline b3ClothParticle* b3StretchForce::GetParticle1() +{ + return m_p1; +} + +inline const b3ClothParticle* b3StretchForce::GetParticle1() const +{ + return m_p1; +} + +inline b3ClothParticle* b3StretchForce::GetParticle2() +{ + return m_p2; +} + +inline const b3ClothParticle* b3StretchForce::GetParticle2() const +{ + return m_p2; +} + +inline b3ClothParticle* b3StretchForce::GetParticle3() +{ + return m_p3; +} + +inline const b3ClothParticle* b3StretchForce::GetParticle3() const +{ + return m_p3; +} + +inline scalar b3StretchForce::GetStrechingStiffnessU() const +{ + return m_ks_u; +} + +inline scalar b3StretchForce::GetDampingStiffnessU() const +{ + return m_kd_u; +} + +inline scalar b3StretchForce::GetBU() const +{ + return m_b_u; +} + +inline scalar b3StretchForce::GetStrechingStiffnessV() const +{ + return m_ks_v; +} + +inline scalar b3StretchForce::GetDampingStiffnessV() const +{ + return m_kd_v; +} + +inline scalar b3StretchForce::GetBV() const +{ + return m_b_v; +} + +inline b3Vec3 b3StretchForce::GetActionForce1() const +{ + return m_f1; +} + +inline b3Vec3 b3StretchForce::GetActionForce2() const +{ + return m_f2; +} + +inline b3Vec3 b3StretchForce::GetActionForce3() const +{ + return m_f3; +} + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/garment/garment.h b/include/bounce/cloth/garment/garment.h index 202abe2..0a82b18 100644 --- a/include/bounce/cloth/garment/garment.h +++ b/include/bounce/cloth/garment/garment.h @@ -40,7 +40,7 @@ struct b3RectangleGarment : public b3Garment b3RectanglePattern rectangle; b3SewingPattern* rectanglePatterns[1]; - b3RectangleGarment(float32 ex = 1.0f, float32 ey = 1.0f) : rectangle(ex, ey) + b3RectangleGarment(scalar ex = scalar(1), scalar ey = scalar(1)) : rectangle(ex, ey) { rectanglePatterns[0] = &rectangle; @@ -57,7 +57,7 @@ struct b3CircleGarment : public b3Garment b3CirclePattern<> circle; b3SewingPattern* circlePatterns[1]; - b3CircleGarment(float32 r = 1.0f) : circle(r) + b3CircleGarment(scalar r = scalar(1)) : circle(r) { circlePatterns[0] = &circle; diff --git a/include/bounce/cloth/garment/garment_mesh.h b/include/bounce/cloth/garment/garment_mesh.h index 9057d24..f1999f1 100644 --- a/include/bounce/cloth/garment/garment_mesh.h +++ b/include/bounce/cloth/garment/garment_mesh.h @@ -57,7 +57,7 @@ struct b3GarmentMesh b3GarmentMesh(); ~b3GarmentMesh(); - void Set(b3Garment* garment, float32 desiredArea); + void Set(b3Garment* garment, scalar desiredArea); }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/garment/sewing_pattern.h b/include/bounce/cloth/garment/sewing_pattern.h index 635bed9..5b37e8d 100644 --- a/include/bounce/cloth/garment/sewing_pattern.h +++ b/include/bounce/cloth/garment/sewing_pattern.h @@ -32,10 +32,10 @@ struct b3RectanglePattern : public b3SewingPattern { b3Vec2 rectangleVertices[4]; - b3RectanglePattern(float32 ex = 1.0f, float32 ey = 1.0f) + b3RectanglePattern(scalar ex = scalar(1), scalar ey = scalar(1)) { - B3_ASSERT(ex > 0.0f); - B3_ASSERT(ey > 0.0f); + B3_ASSERT(ex > scalar(0)); + B3_ASSERT(ey > scalar(0)); // R, CCW rectangleVertices[0].Set(ex, -ey); @@ -55,19 +55,19 @@ struct b3CirclePattern : public b3SewingPattern { b3Vec2 circleVertices[E]; - b3CirclePattern(float32 radius = 1.0f) + b3CirclePattern(scalar radius = scalar(1)) { b3Vec2 center = b3Vec2_zero; - float32 x = 2.0f * B3_PI / float32(E); - float32 c = cos(x); - float32 s = sin(x); + scalar x = scalar(2) * B3_PI / scalar(E); + scalar c = cos(x); + scalar s = sin(x); b3Mat22 R; R.x.Set(c, s); R.y.Set(-s, c); - b3Vec2 n(1.0f, 0.0f); + b3Vec2 n(1, 0); circleVertices[0] = center + radius * n; for (u32 i = 1; i < E; ++i) { diff --git a/include/bounce/cloth/grid_cloth_mesh.h b/include/bounce/cloth/grid_cloth_mesh.h index 474daeb..63d4886 100644 --- a/include/bounce/cloth/grid_cloth_mesh.h +++ b/include/bounce/cloth/grid_cloth_mesh.h @@ -22,33 +22,36 @@ #include // A (H + 1) x (W + 1) grid mesh stored in row-major order. -// v(i, j) = i + (W + 1) + j template struct b3GridClothMesh : public b3ClothMesh { b3Vec3 gridVertices[(H + 1) * (W + 1)]; b3ClothMeshTriangle gridTriangles[2 * H * W]; b3ClothMeshMesh gridMesh; + b3ClothMeshShearingLine gridShearingLines[2 * H * W]; + b3ClothMeshBendingLine gridBendingLines[(H + 1) * (W - 1) + (W + 1) * (H - 1)]; // Set this grid to a W (width) per H (height) dimensioned grid centered at the origin and aligned // with the world x-z axes. b3GridClothMesh() { vertexCount = 0; - for (u32 i = 0; i <= H; ++i) + for (u32 j = 0; j <= W; ++j) { - for (u32 j = 0; j <= W; ++j) + for (u32 i = 0; i <= H; ++i) { - gridVertices[vertexCount++].Set(float32(j), 0.0f, float32(i)); + u32 vertex = GetVertex(i, j); + gridVertices[vertex].Set(scalar(j), scalar(0), scalar(i)); + ++vertexCount; } } - B3_ASSERT(vertexCount == (W + 1) * (H + 1)); - + B3_ASSERT(vertexCount == (H + 1) * (W + 1)); + b3Vec3 translation; - translation.x = -0.5f * float32(W); - translation.y = 0.0f; - translation.z = -0.5f * float32(H); + translation.x = scalar(-0.5) * scalar(W); + translation.y = scalar(0); + translation.z = scalar(-0.5) * scalar(H); for (u32 i = 0; i < vertexCount; ++i) { @@ -60,25 +63,82 @@ struct b3GridClothMesh : public b3ClothMesh { for (u32 j = 0; j < W; ++j) { - u32 v1 = i * (W + 1) + j; - u32 v2 = (i + 1) * (W + 1) + j; - u32 v3 = (i + 1) * (W + 1) + (j + 1); - u32 v4 = i * (W + 1) + (j + 1); + // 1*|----|*4 + // |----| + // 2*|----|*3 + u32 v1 = GetVertex(i, j); + u32 v2 = GetVertex(i + 1, j); + u32 v3 = GetVertex(i + 1, j + 1); + u32 v4 = GetVertex(i, j + 1); b3ClothMeshTriangle* t1 = gridTriangles + triangleCount++; - t1->v1 = v3; + t1->v1 = v1; t1->v2 = v2; - t1->v3 = v1; + t1->v3 = v3; b3ClothMeshTriangle* t2 = gridTriangles + triangleCount++; - t2->v1 = v1; + t2->v1 = v3; t2->v2 = v4; - t2->v3 = v3; + t2->v3 = v1; } } - + B3_ASSERT(triangleCount == 2 * H * W); + shearingLineCount = 0; + for (u32 i = 0; i < H; ++i) + { + for (u32 j = 0; j < W; ++j) + { + // 1*|----|*4 + // |----| + // 2*|----|*3 + u32 v1 = GetVertex(i, j); + u32 v2 = GetVertex(i + 1, j); + u32 v3 = GetVertex(i + 1, j + 1); + u32 v4 = GetVertex(i, j + 1); + + gridShearingLines[shearingLineCount].v1 = v1; + gridShearingLines[shearingLineCount].v2 = v3; + ++shearingLineCount; + + gridShearingLines[shearingLineCount].v1 = v4; + gridShearingLines[shearingLineCount].v2 = v2; + ++shearingLineCount; + } + } + + B3_ASSERT(shearingLineCount == 2 * H * W); + + bendingLineCount = 0; + for (u32 i = 0; i <= H; ++i) + { + for (u32 j = 0; j < W - 1; ++j) + { + u32 v1 = GetVertex(i, j); + u32 v2 = GetVertex(i, j + 2); + + gridBendingLines[bendingLineCount].v1 = v1; + gridBendingLines[bendingLineCount].v2 = v2; + ++bendingLineCount; + } + } + + for (u32 j = 0; j <= W; ++j) + { + for (u32 i = 0; i < H - 1; ++i) + { + u32 v1 = GetVertex(i, j); + u32 v2 = GetVertex(i + 2, j); + + gridBendingLines[bendingLineCount].v1 = v1; + gridBendingLines[bendingLineCount].v2 = v2; + ++bendingLineCount; + } + } + + B3_ASSERT(bendingLineCount == (H + 1) * (W - 1) + (W + 1) * (H - 1)); + gridMesh.startTriangle = 0; gridMesh.triangleCount = triangleCount; gridMesh.startVertex = 0; @@ -88,9 +148,18 @@ struct b3GridClothMesh : public b3ClothMesh triangles = gridTriangles; meshCount = 1; meshes = &gridMesh; + shearingLines = gridShearingLines; + bendingLines = gridBendingLines; sewingLineCount = 0; sewingLines = nullptr; } + + u32 GetVertex(u32 i, u32 j) + { + B3_ASSERT(i < H + 1); + B3_ASSERT(j < W + 1); + return i * (W + 1) + j; + } }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h deleted file mode 100644 index a4d9aef..0000000 --- a/include/bounce/cloth/particle.h +++ /dev/null @@ -1,281 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef B3_PARTICLE_H -#define B3_PARTICLE_H - -#include -#include - -class b3Cloth; - -// Static particle: Can be moved manually. -// Kinematic particle: Non-zero velocity, can be moved by the solver. -// Dynamic particle: Non-zero velocity determined by force, can be moved by the solver. -enum b3ParticleType -{ - e_staticParticle, - e_kinematicParticle, - e_dynamicParticle -}; - -// Particle definition -struct b3ParticleDef -{ - b3ParticleDef() - { - type = e_staticParticle; - mass = 0.0f; - position.SetZero(); - velocity.SetZero(); - force.SetZero(); - radius = 0.0f; - friction = 0.0f; - userData = nullptr; - } - - b3ParticleType type; - float32 mass; - b3Vec3 position; - b3Vec3 velocity; - b3Vec3 force; - float32 radius; - float32 friction; - void* userData; -}; - -// A cloth particle. -class b3Particle -{ -public: - // Set the particle type. - void SetType(b3ParticleType type); - - // Get the particle type. - b3ParticleType GetType() const; - - // Get the vertex index. - u32 GetVertex() const; - - // Set the particle position. - // If the particle is dynamic changing the position directly might lead - // to physically incorrect simulation behaviour. - void SetPosition(const b3Vec3& position); - - // Get the particle position. - const b3Vec3& GetPosition() const; - - // Set the particle velocity. - void SetVelocity(const b3Vec3& velocity); - - // Get the particle velocity. - const b3Vec3& GetVelocity() const; - - // Get the particle mass. - float32 GetMass() const; - - // Set the particle radius. - void SetRadius(float32 radius); - - // Get the particle radius. - float32 GetRadius() const; - - // Set the particle coefficient of friction. - void SetFriction(float32 friction); - - // Get the particle coefficient of friction. - float32 GetFriction() const; - - // Apply a force. - void ApplyForce(const b3Vec3& force); - - // Apply a translation. - void ApplyTranslation(const b3Vec3& translation); - - // Get the next particle. - b3Particle* GetNext(); -private: - friend class b3List2; - friend class b3Cloth; - friend class b3ClothContactManager; - friend class b3ClothSolver; - friend class b3ClothForceSolver; - friend class b3ClothTriangle; - friend class b3ParticleBodyContact; - friend class b3ParticleTriangleContact; - friend class b3ClothContactSolver; - friend class b3Force; - friend class b3StrechForce; - friend class b3ShearForce; - friend class b3SpringForce; - friend class b3MouseForce; - - b3Particle(const b3ParticleDef& def, b3Cloth* cloth); - ~b3Particle(); - - // Synchronize particle AABB - void Synchronize(const b3Vec3& displacement); - - // Synchronize triangles AABB - void SynchronizeTriangles(); - - // Destroy contacts. - void DestroyContacts(); - - // Type - b3ParticleType m_type; - - // Position - b3Vec3 m_position; - - // Velocity - b3Vec3 m_velocity; - - // Applied external force - b3Vec3 m_force; - - // Applied translation - b3Vec3 m_translation; - - // Mass - float32 m_mass; - - // Inverse mass - float32 m_invMass; - - // Radius - float32 m_radius; - - // Coefficient of friction - float32 m_friction; - - // User data. - void* m_userData; - - // Cloth mesh vertex index. - u32 m_vertex; - - // Solver temp - - // Identifier - u32 m_solverId; - - // Solution - b3Vec3 m_x; - - // Parent cloth - b3Cloth* m_cloth; - - // AABB Proxy - b3ClothAABBProxy m_aabbProxy; - - // Broadphase ID - u32 m_broadPhaseId; - - // Links to the cloth particle list. - b3Particle* m_prev; - b3Particle* m_next; -}; - -inline b3ParticleType b3Particle::GetType() const -{ - return m_type; -} - -inline u32 b3Particle::GetVertex() const -{ - return m_vertex; -} - -inline void b3Particle::SetPosition(const b3Vec3& position) -{ - b3Vec3 displacement = position - m_position; - - m_position = position; - m_translation.SetZero(); - - Synchronize(displacement); - SynchronizeTriangles(); -} - -inline const b3Vec3& b3Particle::GetPosition() const -{ - return m_position; -} - -inline void b3Particle::SetVelocity(const b3Vec3& velocity) -{ - if (m_type == e_staticParticle) - { - return; - } - m_velocity = velocity; -} - -inline const b3Vec3& b3Particle::GetVelocity() const -{ - return m_velocity; -} - -inline float32 b3Particle::GetMass() const -{ - return m_mass; -} - -inline void b3Particle::SetRadius(float32 radius) -{ - m_radius = radius; - - Synchronize(b3Vec3_zero); -} - -inline float32 b3Particle::GetRadius() const -{ - return m_radius; -} - -inline void b3Particle::SetFriction(float32 friction) -{ - m_friction = friction; -} - -inline float32 b3Particle::GetFriction() const -{ - return m_friction; -} - -inline void b3Particle::ApplyForce(const b3Vec3& force) -{ - if (m_type != e_dynamicParticle) - { - return; - } - m_force += force; -} - -inline void b3Particle::ApplyTranslation(const b3Vec3& translation) -{ - m_translation += translation; -} - -inline b3Particle* b3Particle::GetNext() -{ - return m_next; -} - -#endif \ No newline at end of file diff --git a/include/bounce/cloth/shapes/cloth_capsule_shape.h b/include/bounce/cloth/shapes/cloth_capsule_shape.h new file mode 100644 index 0000000..3b4c0b6 --- /dev/null +++ b/include/bounce/cloth/shapes/cloth_capsule_shape.h @@ -0,0 +1,83 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_CAPSULE_SHAPE_H +#define B3_CLOTH_CAPSULE_SHAPE_H + +#include +#include +#include + +class b3ClothParticle; + +struct b3ClothCapsuleShapeDef : b3ClothShapeDef +{ + b3ClothParticle* p1; + b3ClothParticle* p2; +}; + +// A cloth capsule shape +class b3ClothCapsuleShape : public b3ClothShape +{ +public: + // Return the particle 1. + b3ClothParticle* GetParticle1() { return m_p1; } + const b3ClothParticle* GetParticle1() const { return m_p1; } + + // Return the particle 2. + b3ClothParticle* GetParticle2() { return m_p2; } + const b3ClothParticle* GetParticle2() const { return m_p2; } + + // Return the next capsule in the cloth capsule list. + b3ClothCapsuleShape* GetNext() { return m_next; } + const b3ClothCapsuleShape* GetNext() const { return m_next; } +private: + friend class b3Cloth; + friend class b3ClothParticle; + friend class b3ShearForce; + friend class b3StretchForce; + friend class b3MouseForce; + friend class b3ClothContactManager; + friend class b3ClothSphereAndTriangleContact; + friend class b3ClothCapsuleAndCapsuleContact; + friend class b3ClothSolver; + friend class b3ClothContactSolver; + friend class b3List2; + + b3ClothCapsuleShape(const b3ClothCapsuleShapeDef& def, b3Cloth* cloth); + ~b3ClothCapsuleShape(); + + // Compute AABB + b3AABB ComputeAABB() const; + + // Synchronize AABB + void Synchronize(const b3Vec3& displacement); + + // Destroy contacts + void DestroyContacts(); + + // Particles + b3ClothParticle* m_p1; + b3ClothParticle* m_p2; + + // Links to the cloth capsule shape list. + b3ClothCapsuleShape* m_prev; + b3ClothCapsuleShape* m_next; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/shapes/cloth_shape.h b/include/bounce/cloth/shapes/cloth_shape.h new file mode 100644 index 0000000..92d7761 --- /dev/null +++ b/include/bounce/cloth/shapes/cloth_shape.h @@ -0,0 +1,139 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_SHAPE_H +#define B3_CLOTH_SHAPE_H + +#include + +class b3Cloth; + +// Cloth shape type +enum b3ClothShapeType +{ + e_clothSphereShape, + e_clothCapsuleShape, + e_clothTriangleShape, + e_clothWorldShape, + e_maxClothShapes +}; + +// Cloth shape definition +struct b3ClothShapeDef +{ + b3ClothShapeDef() + { + radius = scalar(0); + friction = scalar(0); + density = scalar(0); + meshIndex = B3_MAX_U32; + } + + scalar radius; + scalar friction; + scalar density; + u32 meshIndex; +}; + +// Cloth shape +class b3ClothShape +{ +public: + // Get the shape type. + b3ClothShapeType GetType() const; + + // Get the cloth. + b3Cloth* GetCloth(); + const b3Cloth* GetCloth() const; + + // Get the shape radius. + scalar GetRadius() const; + + // Get the shape density. + scalar GetDensity() const; + + // Set the coefficient of friction of this shape. + void SetFriction(scalar friction); + + // Get the coefficient of friction of this shape. + // This represents both static and dynamic friction. + scalar GetFriction() const; +protected: + friend class b3Cloth; + friend class b3ClothParticle; + friend class b3ClothContactManager; + friend class b3ClothContactSolver; + + // Type + b3ClothShapeType m_type; + + // Cloth + b3Cloth* m_cloth; + + // Radius + scalar m_radius; + + // Coefficient of friction + scalar m_friction; + + // Density + scalar m_density; + + // Broadphase ID + u32 m_broadPhaseId; + + // Mesh index + u32 m_meshIndex; +}; + +inline b3ClothShapeType b3ClothShape::GetType() const +{ + return m_type; +} + +inline b3Cloth* b3ClothShape::GetCloth() +{ + return m_cloth; +} + +inline const b3Cloth* b3ClothShape::GetCloth() const +{ + return m_cloth; +} + +inline scalar b3ClothShape::GetRadius() const +{ + return m_radius; +} + +inline void b3ClothShape::SetFriction(scalar friction) +{ + m_friction = friction; +} + +inline scalar b3ClothShape::GetFriction() const +{ + return m_friction; +} + +inline scalar b3ClothShape::GetDensity() const +{ + return m_density; +} + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/shapes/cloth_sphere_shape.h b/include/bounce/cloth/shapes/cloth_sphere_shape.h new file mode 100644 index 0000000..29fc492 --- /dev/null +++ b/include/bounce/cloth/shapes/cloth_sphere_shape.h @@ -0,0 +1,74 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_SPHERE_SHAPE_H +#define B3_CLOTH_SPHERE_SHAPE_H + +#include +#include +#include + +class b3ClothParticle; + +struct b3ClothSphereShapeDef : b3ClothShapeDef +{ + b3ClothParticle* p; +}; + +// A cloth sphere shape +class b3ClothSphereShape : public b3ClothShape +{ +public: + // Return the cloth particle. + b3ClothParticle* GetParticle() { return m_p; } + const b3ClothParticle* GetParticle() const { return m_p; } + + // Return the next sphere in the cloth. + b3ClothSphereShape* GetNext() { return m_next; }; + const b3ClothSphereShape* GetNext() const { return m_next; }; +private: + friend class b3Cloth; + friend class b3ClothParticle; + friend class b3ClothContactManager; + friend class b3ClothSphereAndTriangleContact; + friend class b3ClothSphereAndShapeContact; + friend class b3ClothSolver; + friend class b3ClothContactSolver; + friend class b3List2; + + b3ClothSphereShape(const b3ClothSphereShapeDef& def, b3Cloth* cloth); + ~b3ClothSphereShape(); + + // Compute AABB + b3AABB ComputeAABB() const; + + // Synchronize AABB + void Synchronize(const b3Vec3& displacement); + + // Destroy contacts + void DestroyContacts(); + + // Particle + b3ClothParticle* m_p; + + // Links to the cloth sphere shape list. + b3ClothSphereShape* m_prev; + b3ClothSphereShape* m_next; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/shapes/cloth_triangle_shape.h b/include/bounce/cloth/shapes/cloth_triangle_shape.h new file mode 100644 index 0000000..58601fd --- /dev/null +++ b/include/bounce/cloth/shapes/cloth_triangle_shape.h @@ -0,0 +1,93 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_TRIANGLE_SHAPE_H +#define B3_CLOTH_TRIANGLE_SHAPE_H + +#include +#include +#include + +class b3ClothParticle; + +struct b3ClothTriangleShapeDef : public b3ClothShapeDef +{ + b3ClothParticle* p1; + b3ClothParticle* p2; + b3ClothParticle* p3; + + b3Vec3 v1, v2, v3; +}; + +// A triangle shape +class b3ClothTriangleShape : public b3ClothShape +{ +public: + // Return the particle 1. + b3ClothParticle* GetParticle1() { return m_p1; } + const b3ClothParticle* GetParticle1() const { return m_p1; } + + // Return the particle 2. + b3ClothParticle* GetParticle2() { return m_p2; } + const b3ClothParticle* GetParticle2() const { return m_p2; } + + // Return the particle 3. + b3ClothParticle* GetParticle3() { return m_p3; } + const b3ClothParticle* GetParticle3() const { return m_p3; } + + // Return the next triangle shape in the cloth list of shapes. + b3ClothTriangleShape* GetNext() { return m_next; } + const b3ClothTriangleShape* GetNext() const { return m_next; } +private: + friend class b3Cloth; + friend class b3ClothParticle; + friend class b3ShearForce; + friend class b3StretchForce; + friend class b3MouseForce; + friend class b3ClothContactManager; + friend class b3ClothSphereAndTriangleContact; + friend class b3ClothSolver; + friend class b3ClothContactSolver; + friend class b3List2; + + b3ClothTriangleShape(const b3ClothTriangleShapeDef& def, b3Cloth* cloth); + ~b3ClothTriangleShape(); + + // Compute AABB + b3AABB ComputeAABB() const; + + // Synchronize the AABB + void Synchronize(const b3Vec3& displacement); + + // Destroy shape contacts + void DestroyContacts(); + + // Particles + b3ClothParticle* m_p1; + b3ClothParticle* m_p2; + b3ClothParticle* m_p3; + + // Rest area. Used for computing the mass of the particles. + scalar m_area; + + // Links to the cloth triangle shape list. + b3ClothTriangleShape* m_prev; + b3ClothTriangleShape* m_next; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/shapes/cloth_world_shape.h b/include/bounce/cloth/shapes/cloth_world_shape.h new file mode 100644 index 0000000..f6f2101 --- /dev/null +++ b/include/bounce/cloth/shapes/cloth_world_shape.h @@ -0,0 +1,71 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_WORLD_SHAPE_H +#define B3_CLOTH_WORLD_SHAPE_H + +#include +#include +#include + +class b3Cloth; +class b3Shape; + +struct b3ClothWorldShapeDef : b3ClothShapeDef +{ + const b3Shape* shape; +}; + +class b3ClothWorldShape : public b3ClothShape +{ +public: + // Return the attached world shape. + const b3Shape* GetShape() { return m_shape; } + const b3Shape* GetShape() const { return m_shape; } + + // Return the next world shape in the cloth list of world shapes. + b3ClothWorldShape* GetNext() { return m_next; } + const b3ClothWorldShape* GetNext() const { return m_next; } +private: + friend class b3Cloth; + friend class b3ClothContactManager; + friend class b3ClothSphereAndShapeContact; + friend class b3ClothContactSolver; + friend class b3List2; + + b3ClothWorldShape(const b3ClothWorldShapeDef& def, b3Cloth* cloth); + ~b3ClothWorldShape(); + + // Compute AABB. + b3AABB ComputeAABB() const; + + // Synchronize AABB. + void Synchronize(const b3Vec3& displacement); + + // Destroy contacts. + void DestroyContacts(); + + // Shape in the world. + const b3Shape* m_shape; + + // Cloth list links. + b3ClothWorldShape* m_prev; + b3ClothWorldShape* m_next; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/collision/broad_phase.h b/include/bounce/collision/broad_phase.h index 9c37a6e..0b3b4a1 100644 --- a/include/bounce/collision/broad_phase.h +++ b/include/bounce/collision/broad_phase.h @@ -41,7 +41,7 @@ public: ~b3BroadPhase(); // Create a proxy and return a index to it. - u32 CreateProxy(const b3AABB3& aabb, void* userData); + u32 CreateProxy(const b3AABB& aabb, void* userData); // Destroy a given proxy and remove it from the broadphase. void DestroyProxy(u32 proxyId); @@ -49,13 +49,13 @@ public: // Update an existing proxy AABB with a given AABB and a displacement. // displacement = dt * velocity // Return true if the proxy has moved. - bool MoveProxy(u32 proxyId, const b3AABB3& aabb, const b3Vec3& displacement); + bool MoveProxy(u32 proxyId, const b3AABB& aabb, const b3Vec3& displacement); // Force move the proxy void TouchProxy(u32 proxyId); // Get the AABB of a given proxy. - const b3AABB3& GetAABB(u32 proxyId) const; + const b3AABB& GetAABB(u32 proxyId) const; // Get the user data attached to a proxy. void* GetUserData(u32 proxyId) const; @@ -68,7 +68,7 @@ public: // Notify the client callback the AABBs that are overlapping with the passed AABB. template - void QueryAABB(T* callback, const b3AABB3& aabb) const; + void QueryAABB(T* callback, const b3AABB& aabb) const; // Notify the client callback the AABBs that are overlapping the // passed ray. @@ -114,7 +114,7 @@ private : u32 m_pairCount; }; -inline const b3AABB3& b3BroadPhase::GetAABB(u32 proxyId) const +inline const b3AABB& b3BroadPhase::GetAABB(u32 proxyId) const { return m_tree.GetAABB(proxyId); } @@ -130,7 +130,7 @@ inline u32 b3BroadPhase::GetProxyCount() const } template -inline void b3BroadPhase::QueryAABB(T* callback, const b3AABB3& aabb) const +inline void b3BroadPhase::QueryAABB(T* callback, const b3AABB& aabb) const { return m_tree.QueryAABB(callback, aabb); } @@ -174,7 +174,7 @@ inline void b3BroadPhase::FindPairs(T* callback) continue; } - const b3AABB3& aabb = m_tree.GetAABB(m_queryProxyId); + const b3AABB& aabb = m_tree.GetAABB(m_queryProxyId); m_tree.QueryAABB(this, aabb); } diff --git a/include/bounce/collision/collision.h b/include/bounce/collision/collision.h index 4201dc3..f6334fd 100644 --- a/include/bounce/collision/collision.h +++ b/include/bounce/collision/collision.h @@ -19,25 +19,24 @@ #ifndef B3_COLLISION_H #define B3_COLLISION_H -#include -#include +#include // Input for a ray cast. struct b3RayCastInput { b3Vec3 p1; // first point on segment b3Vec3 p2; // second point on segment - float32 maxFraction; // maximum intersection + scalar maxFraction; // maximum intersection }; // Output of a ray cast. struct b3RayCastOutput { - float32 fraction; // time of intersection on ray-segment + scalar fraction; // time of intersection on ray-segment b3Vec3 normal; // surface normal of intersection }; -// Perform a ray-cast on a triangle. +// Perform a ray-cast against a triangle. bool b3RayCast(b3RayCastOutput* output, const b3RayCastInput* input, const b3Vec3& v1, const b3Vec3& v2, const b3Vec3& v3); diff --git a/include/bounce/collision/gjk/gjk.h b/include/bounce/collision/gjk/gjk.h index 0d85e50..7539433 100644 --- a/include/bounce/collision/gjk/gjk.h +++ b/include/bounce/collision/gjk/gjk.h @@ -31,7 +31,7 @@ struct b3SimplexVertex b3Vec3 point1; // support vertex on proxy 1 b3Vec3 point2; // support vertex on proxy 2 b3Vec3 point; // minkowski vertex - float32 weight; // barycentric coordinate for point + scalar weight; // barycentric coordinate for point u32 index1; // support 1 vertex index u32 index2; // support 2 vertex index }; @@ -56,7 +56,7 @@ struct b3Simplex void WriteCache(b3SimplexCache* cache) const; - float32 GetMetric() const; + scalar GetMetric() const; }; // The output of the GJK algorithm. @@ -67,13 +67,13 @@ struct b3GJKOutput { b3Vec3 point1; // closest point on proxy 1 b3Vec3 point2; // closest point on proxy 2 - float32 distance; // euclidean distance between the closest points + scalar distance; // euclidean distance between the closest points u32 iterations; // number of GJK iterations }; // Find the closest points and distance between two proxies. b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, - const b3Transform& xf2, const b3GJKProxy& proxy2); + const b3Transform& xf2, const b3GJKProxy& proxy2, bool applyRadius); /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -84,7 +84,7 @@ b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, // for the first time. struct b3SimplexCache { - float32 metric; // distance or area or volume + scalar metric; // lenght or area or volume u32 iterations; // number of GJK iterations u16 count; // number of support vertices u8 index1[4]; // support vertices on proxy 1 @@ -114,18 +114,4 @@ b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, const b3Transform& xf2, const b3GJKProxy& proxy2, bool applyRadius, b3SimplexCache* cache); -// The output of the GJK-based shape cast algorithm. -struct b3GJKShapeCastOutput -{ - float32 t; // time of impact - b3Vec3 point; // contact point at t - b3Vec3 normal; // contact normal at t - u32 iterations; // number of iterations -}; - -// Find the time of impact between two proxies given the relative target translation vector. -bool b3GJKShapeCast(b3GJKShapeCastOutput* output, - const b3Transform& xf1, const b3GJKProxy& proxy1, - const b3Transform& xf2, const b3GJKProxy& proxy2, const b3Vec3& translation2); - #endif \ No newline at end of file diff --git a/include/bounce/collision/gjk/gjk_proxy.h b/include/bounce/collision/gjk/gjk_proxy.h index 71638ea..c89eb39 100644 --- a/include/bounce/collision/gjk/gjk_proxy.h +++ b/include/bounce/collision/gjk/gjk_proxy.h @@ -26,7 +26,7 @@ struct b3GJKProxy { const b3Vec3* vertices; // vertices in this proxy u32 vertexCount; // number of vertices - float32 radius; // proxy radius + scalar radius; // proxy radius b3Vec3 vertexBuffer[3]; // vertex buffer for convenience // Get the number of vertices in this proxy. @@ -57,10 +57,10 @@ inline const b3Vec3& b3GJKProxy::GetVertex(u32 index) const inline u32 b3GJKProxy::GetSupportIndex(const b3Vec3& d) const { u32 maxIndex = 0; - float32 maxProjection = b3Dot(d, vertices[maxIndex]); + scalar maxProjection = b3Dot(d, vertices[maxIndex]); for (u32 i = 1; i < vertexCount; ++i) { - float32 projection = b3Dot(d, vertices[i]); + scalar projection = b3Dot(d, vertices[i]); if (projection > maxProjection) { maxIndex = i; diff --git a/include/bounce/collision/sat/sat.h b/include/bounce/collision/sat/sat.h index a21fa12..77a1144 100644 --- a/include/bounce/collision/sat/sat.h +++ b/include/bounce/collision/sat/sat.h @@ -28,10 +28,10 @@ struct b3Hull; struct b3FaceQuery { u32 index; - float32 separation; + scalar separation; }; -float32 b3Project(const b3Hull* hull, const b3Plane& plane); +scalar b3Project(const b3Hull* hull, const b3Plane& plane); b3FaceQuery b3QueryFaceSeparation(const b3Transform& xf1, const b3Hull* hull1, const b3Transform& xf2, const b3Hull* hull2); @@ -42,12 +42,12 @@ struct b3EdgeQuery { u32 index1; u32 index2; - float32 separation; + scalar separation; }; bool b3IsMinkowskiFace(const b3Vec3& A, const b3Vec3& B, const b3Vec3& B_x_A, const b3Vec3& C, const b3Vec3& D, const b3Vec3& D_x_C); -float32 b3Project(const b3Vec3& P1, const b3Vec3& E1, const b3Vec3& P2, const b3Vec3& E2, const b3Vec3& C1); +scalar b3Project(const b3Vec3& P1, const b3Vec3& E1, const b3Vec3& P2, const b3Vec3& E2, const b3Vec3& C1); b3EdgeQuery b3QueryEdgeSeparation(const b3Transform& xf1, const b3Hull* hull1, const b3Transform& xf2, const b3Hull* hull2); @@ -94,13 +94,13 @@ struct b3FeatureCache // Read the current state of the cache. // Return e_unkown if neither a separation or penetration was detected. b3SATCacheType ReadState(const b3Transform& xf1, const b3Hull* hull1, - const b3Transform& xf2, const b3Hull* hull2, float32 totalRadius); + const b3Transform& xf2, const b3Hull* hull2, scalar totalRadius); b3SATCacheType ReadEdge(const b3Transform& xf1, const b3Hull* hull1, - const b3Transform& xf2, const b3Hull* hull2, float32 totalRadius); + const b3Transform& xf2, const b3Hull* hull2, scalar totalRadius); b3SATCacheType ReadFace(const b3Transform& xf1, const b3Hull* hull1, - const b3Transform& xf2, const b3Hull* hull2, float32 totalRadius); + const b3Transform& xf2, const b3Hull* hull2, scalar totalRadius); b3SATFeaturePair m_featurePair; }; diff --git a/include/bounce/collision/sat/sat_edge_and_hull.h b/include/bounce/collision/sat/sat_hull_and_edge.h similarity index 77% rename from include/bounce/collision/sat/sat_edge_and_hull.h rename to include/bounce/collision/sat/sat_hull_and_edge.h index 8998a40..1eb67e3 100644 --- a/include/bounce/collision/sat/sat_edge_and_hull.h +++ b/include/bounce/collision/sat/sat_hull_and_edge.h @@ -16,8 +16,8 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef B3_EDGE_HULL_SAT_H -#define B3_EDGE_HULL_SAT_H +#ifndef B3_HULL_AND_EDGE_SAT_H +#define B3_HULL_AND_EDGE_SAT_H #include @@ -25,16 +25,16 @@ struct b3Capsule; /////////////////////////////////////////////////////////////////////////////////////////////////// -float32 b3ProjectEdge(const b3Capsule* hull, const b3Plane& plane); +scalar b3ProjectEdge(const b3Capsule* hull, const b3Plane& plane); -b3FaceQuery b3QueryFaceSeparation(const b3Transform& xf1, const b3Capsule* hull1, - const b3Transform& xf2, const b3Hull* hull2); +b3FaceQuery b3QueryFaceSeparation(const b3Transform& xf1, const b3Hull* hull1, + const b3Transform& xf2, const b3Capsule* hull2); /////////////////////////////////////////////////////////////////////////////////////////////////// -float32 b3ProjectEdge(const b3Vec3& P1, const b3Vec3& E1, const b3Vec3& P2, const b3Vec3& E2, const b3Vec3& C2); +scalar b3ProjectEdge(const b3Vec3& P1, const b3Vec3& E1, const b3Vec3& P2, const b3Vec3& E2, const b3Vec3& C2); -b3EdgeQuery b3QueryEdgeSeparation(const b3Transform& xf1, const b3Capsule* hull1, - const b3Transform& xf2, const b3Hull* hull2); +b3EdgeQuery b3QueryEdgeSeparation(const b3Transform& xf1, const b3Hull* hull1, + const b3Transform& xf2, const b3Capsule* hull2); #endif \ No newline at end of file diff --git a/include/bounce/collision/sat/sat_vertex_and_hull.h b/include/bounce/collision/sat/sat_hull_and_vertex.h similarity index 85% rename from include/bounce/collision/sat/sat_vertex_and_hull.h rename to include/bounce/collision/sat/sat_hull_and_vertex.h index 4f78c32..c4299df 100644 --- a/include/bounce/collision/sat/sat_vertex_and_hull.h +++ b/include/bounce/collision/sat/sat_hull_and_vertex.h @@ -16,8 +16,8 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef B3_VERTEX_HULL_SAT_H -#define B3_VERTEX_HULL_SAT_H +#ifndef B3_HULL_AND_VERTEX_SAT_H +#define B3_HULL_AND_VERTEX_SAT_H #include @@ -25,9 +25,9 @@ struct b3Sphere; /////////////////////////////////////////////////////////////////////////////////////////////////// -float32 b3ProjectVertex(const b3Sphere* hull, const b3Plane& plane); +scalar b3ProjectVertex(const b3Sphere* hull, const b3Plane& plane); -b3FaceQuery b3QueryFaceSeparation(const b3Transform& xf1, const b3Sphere* hull1, - const b3Transform& xf2, const b3Hull* hull2); +b3FaceQuery b3QueryFaceSeparation(const b3Transform& xf1, const b3Hull* hull1, + const b3Transform& xf2, const b3Sphere* hull2); #endif diff --git a/include/bounce/collision/shapes/aabb.h b/include/bounce/collision/shapes/aabb.h new file mode 100644 index 0000000..8046d84 --- /dev/null +++ b/include/bounce/collision/shapes/aabb.h @@ -0,0 +1,370 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_AABB_H +#define B3_AABB_H + +#include +#include + +// A min-max representation of a three-dimensional AABB. +struct b3AABB +{ + // Constructor. Does nothing for performance. + b3AABB() { } + + // Get the support vertex in a given direction. + b3Vec3 GetSupportVertex(const b3Vec3& direction) const + { + b3Vec3 support; + support.x = direction.x < scalar(0) ? lowerBound.x : upperBound.x; + support.y = direction.y < scalar(0) ? lowerBound.y : upperBound.y; + support.z = direction.z < scalar(0) ? lowerBound.z : upperBound.z; + return support; + } + + // Set this AABB from a list of points. + void Set(const b3Vec3* points, u32 count) + { + lowerBound = upperBound = points[0]; + for (u32 i = 1; i < count; ++i) + { + lowerBound = b3Min(lowerBound, points[i]); + upperBound = b3Max(upperBound, points[i]); + } + } + + // Set this AABB from a list of points and a transform. + void Set(const b3Vec3* points, u32 count, const b3Transform& xf) + { + lowerBound = upperBound = b3Mul(xf, points[0]); + for (u32 i = 1; i < count; ++i) + { + b3Vec3 v = b3Mul(xf, points[i]); + lowerBound = b3Min(lowerBound, v); + upperBound = b3Max(upperBound, v); + } + } + + // Set this AABB from a list of points, a local scale, and a transform. + void Set(const b3Vec3* points, u32 count, const b3Vec3& scale, const b3Transform& xf) + { + lowerBound = upperBound = b3Mul(xf, b3MulCW(scale, points[0])); + for (u32 i = 1; i < count; ++i) + { + b3Vec3 v = b3Mul(xf, b3MulCW(scale, points[i])); + lowerBound = b3Min(lowerBound, v); + upperBound = b3Max(upperBound, v); + } + } + + // Set this AABB from two points. + void SetSegment(const b3Vec3& p1, const b3Vec3& p2) + { + lowerBound = b3Min(p1, p2); + upperBound = b3Max(p1, p2); + } + + // Set this AABB from three points. + void SetTriangle(const b3Vec3& p1, const b3Vec3& p2, const b3Vec3& p3) + { + lowerBound = b3Min(p1, b3Min(p2, p3)); + upperBound = b3Max(p1, b3Max(p2, p3)); + } + + // Set this AABB from a center point and a radius vector. + void Set(const b3Vec3& center, const b3Vec3& r) + { + lowerBound = center - r; + upperBound = center + r; + } + + // Set this AABB from a center point and a radius value. + void Set(const b3Vec3& center, scalar radius) + { + b3Vec3 r(radius, radius, radius); + Set(center, r); + } + + // Extend this AABB by a radius value. + void Extend(scalar s) + { + b3Vec3 r(s, s, s); + lowerBound -= r; + upperBound += r; + } + + // Extend this AABB by a radius vector. + void Extend(const b3Vec3& r) + { + lowerBound -= r; + upperBound += r; + } + + // Get the center of this AABB. + b3Vec3 GetCenter() const + { + return scalar(0.5) * (lowerBound + upperBound); + } + + // Get the half-extents of this AABB. + b3Vec3 GetExtents() const + { + return scalar(0.5) * (upperBound - lowerBound); + } + + // Get the width of this AABB. + scalar GetWidth() const + { + return upperBound.x - lowerBound.x; + } + + // Get the height of this AABB. + scalar GetHeight() const + { + return upperBound.y - lowerBound.y; + } + + // Get the depth of this AABB. + scalar GetDepth() const + { + return upperBound.z - lowerBound.z; + } + + // Get the volume of this AABB. + scalar GetVolume() const + { + b3Vec3 d = upperBound - lowerBound; + return d.x * d.y * d.z; + } + + // Get the surface area of this AABB. + scalar GetSurfaceArea() const + { + b3Vec3 d = upperBound - lowerBound; + return scalar(2) * (d.x * d.y + d.y * d.z + d.z * d.x); + } + + // Get the index of the longest axis of this AABB. + u32 GetLongestAxisIndex() const + { + b3Vec3 d = upperBound - lowerBound; + + scalar maxValue = d.x; + u32 maxIndex = 0; + + if (d.y > maxValue) + { + maxValue = d.y; + maxIndex = 1; + } + + if (d.z > maxValue) + { + maxValue = d.z; + maxIndex = 2; + } + + return maxIndex; + } + + // Test if this AABB contains a point. + bool Contains(const b3Vec3& point) const + { + return lowerBound.x <= point.x && point.x <= upperBound.x && + lowerBound.y <= point.y && point.y <= upperBound.y && + lowerBound.z <= point.z && point.z <= upperBound.z; + } + + // Test if this AABB contains another AABB. + bool Contains(const b3AABB& aabb) const + { + return Contains(aabb.lowerBound) && Contains(aabb.upperBound); + } + + // Perform a ray-cast against this AABB. + bool RayCast(b3RayCastOutput* output, const b3RayCastInput& input) const + { + b3Vec3 p1 = input.p1; + b3Vec3 p2 = input.p2; + b3Vec3 d = p2 - p1; + + b3Vec3 normals[6]; + normals[0].Set(scalar(-1), scalar(0), scalar(0)); + normals[1].Set(scalar(1), scalar(0), scalar(0)); + normals[2].Set(scalar(0), scalar(-1), scalar(0)); + normals[3].Set(scalar(0), scalar(1), scalar(0)); + normals[4].Set(scalar(0), scalar(0), scalar(-1)); + normals[5].Set(scalar(0), scalar(0), scalar(1)); + + u32 index = B3_MAX_U32; + + scalar lower = scalar(0); + scalar upper = input.maxFraction; + + u32 planeIndex = 0; + + for (u32 i = 0; i < 3; ++i) + { + scalar numerators[2], denominators[2]; + + numerators[0] = p1[i] - lowerBound[i]; + numerators[1] = upperBound[i] - p1[i]; + + denominators[0] = -d[i]; + denominators[1] = d[i]; + + for (u32 j = 0; j < 2; ++j) + { + scalar numerator = numerators[j]; + scalar denominator = denominators[j]; + + if (denominator == scalar(0)) + { + // s is parallel to this half-space. + if (numerator < scalar(0)) + { + // s is outside of this half-space. + // dot(n, p1) and dot(n, p2) < 0. + return false; + } + } + else + { + if (denominator < scalar(0)) + { + // s enters this half-space. + if (numerator < lower * denominator) + { + // Increase lower. + lower = numerator / denominator; + index = planeIndex; + } + } + else + { + // s exits the half-space. + if (numerator < upper * denominator) + { + // Decrease upper. + upper = numerator / denominator; + } + } + // Exit if intersection becomes empty. + if (upper < lower) + { + return false; + } + } + + ++planeIndex; + } + } + + B3_ASSERT(lower >= scalar(0) && lower <= input.maxFraction); + + if (index != B3_MAX_U32) + { + output->fraction = lower; + output->normal = normals[index]; + return true; + } + + return false; + } + + b3Vec3 lowerBound; // lower vertex + b3Vec3 upperBound; // upper vertex +}; + +// Compute an AABB that encloses two AABBs. +inline b3AABB b3Combine(const b3AABB& a, const b3AABB& b) +{ + b3AABB aabb; + aabb.lowerBound = b3Min(a.lowerBound, b.lowerBound); + aabb.upperBound = b3Max(a.upperBound, b.upperBound); + return aabb; +} + +// Test if two AABBs are overlapping. +inline bool b3TestOverlap(const b3AABB& a, const b3AABB& b) +{ + return (a.lowerBound.x <= b.upperBound.x) && (a.lowerBound.y <= b.upperBound.y) && (a.lowerBound.z <= b.upperBound.z) && + (a.upperBound.x >= b.lowerBound.x) && (a.upperBound.y >= b.lowerBound.y) && (a.upperBound.z >= b.lowerBound.z); +} + +// Transform a AABB by a given frame. +inline b3AABB b3TransformAABB(const b3AABB& local_aabb, const b3Transform& xf) +{ + b3Vec3 local_center = local_aabb.GetCenter(); + b3Vec3 local_radius = local_aabb.GetExtents(); + + b3Vec3 center = xf * local_center; + + b3Mat33 rotation = b3Abs(b3QuatMat33(xf.rotation)); + + b3Vec3 radius; + radius.x = b3Dot(local_radius, rotation.x); + radius.y = b3Dot(local_radius, rotation.y); + radius.z = b3Dot(local_radius, rotation.z); + + b3AABB aabb; + aabb.Set(center, radius); + + return aabb; +} + +// Transform a AABB given a local scale by a given frame. +inline b3AABB b3TransformAABB(const b3AABB& local_aabb, const b3Vec3& local_scale, const b3Transform& xf) +{ + b3Vec3 local_center = local_aabb.GetCenter(); + b3Vec3 local_radius = b3MulCW(b3Abs(local_scale), local_aabb.GetExtents()); + + b3Vec3 center = xf * local_center; + + b3Mat33 rotation = b3Abs(b3QuatMat33(xf.rotation)); + + b3Vec3 radius; + radius.x = b3Dot(local_radius, rotation.x); + radius.y = b3Dot(local_radius, rotation.y); + radius.z = b3Dot(local_radius, rotation.z); + + b3AABB aabb; + aabb.Set(center, radius); + + return aabb; +} + +// Scale an AABB by a scale. +// The scale can be non-uniform and negative. +inline b3AABB b3ScaleAABB(const b3AABB& aabb, const b3Vec3& scale) +{ + b3AABB scaled_aabb; + + scaled_aabb.lowerBound.x = scale.x > scalar(0) ? scale.x * aabb.lowerBound.x : scale.x * aabb.upperBound.x; + scaled_aabb.lowerBound.y = scale.y > scalar(0) ? scale.y * aabb.lowerBound.y : scale.y * aabb.upperBound.y; + scaled_aabb.lowerBound.z = scale.z > scalar(0) ? scale.z * aabb.lowerBound.z : scale.z * aabb.upperBound.z; + + scaled_aabb.upperBound.x = scale.x > scalar(0) ? scale.x * aabb.upperBound.x : scale.x * aabb.lowerBound.x; + scaled_aabb.upperBound.y = scale.y > scalar(0) ? scale.y * aabb.upperBound.y : scale.y * aabb.lowerBound.y; + scaled_aabb.upperBound.z = scale.z > scalar(0) ? scale.z * aabb.upperBound.z : scale.z * aabb.lowerBound.z; + + return scaled_aabb; +} + +#endif \ No newline at end of file diff --git a/include/bounce/collision/shapes/aabb3.h b/include/bounce/collision/shapes/aabb3.h deleted file mode 100644 index 51ba36e..0000000 --- a/include/bounce/collision/shapes/aabb3.h +++ /dev/null @@ -1,253 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef B3_AABB_3_H -#define B3_AABB_3_H - -#include - -// A min-max representation of a three-dimensional AABB. -struct b3AABB3 -{ - b3Vec3 m_lower; // lower vertex - b3Vec3 m_upper; // upper vertex - - // Get the support vertex in a given direction. - b3Vec3 GetSupportVertex(const b3Vec3& direction) const - { - b3Vec3 support; - support.x = direction.x < 0.0f ? m_lower.x : m_upper.x; - support.y = direction.y < 0.0f ? m_lower.y : m_upper.y; - support.z = direction.z < 0.0f ? m_lower.z : m_upper.z; - return support; - } - - // Set this AABB from a list of points. - void Set(const b3Vec3* points, u32 count) - { - m_lower = m_upper = points[0]; - for (u32 i = 1; i < count; ++i) - { - m_lower = b3Min(m_lower, points[i]); - m_upper = b3Max(m_upper, points[i]); - } - } - - // Set this AABB from a list of points and a transform. - void Set(const b3Vec3* points, u32 count, const b3Transform& xf) - { - m_lower = m_upper = b3Mul(xf, points[0]); - for (u32 i = 1; i < count; ++i) - { - b3Vec3 v = b3Mul(xf, points[i]); - m_lower = b3Min(m_lower, v); - m_upper = b3Max(m_upper, v); - } - } - - // Set this AABB from a triangle. - void Set(const b3Vec3& v1, const b3Vec3& v2, const b3Vec3& v3) - { - m_lower = b3Min(v1, b3Min(v2, v3)); - m_upper = b3Max(v1, b3Max(v2, v3)); - } - - // Set this AABB from a center point and a radius vector. - void Set(const b3Vec3& center, const b3Vec3& r) - { - m_lower = center - r; - m_upper = center + r; - } - - // Set this AABB from a center point and a radius value. - void Set(const b3Vec3& center, float32 radius) - { - b3Vec3 r(radius, radius, radius); - Set(center, r); - } - - // Extend this AABB by a scalar. - void Extend(float32 s) - { - b3Vec3 r(s, s, s); - m_lower -= r; - m_upper += r; - } - - // Extend this AABB by a radius vector. - void Extend(const b3Vec3& r) - { - m_lower -= r; - m_upper += r; - } - - // Compute the centroid of this AABB. - b3Vec3 Centroid() const - { - return 0.5f * (m_lower + m_upper); - } - - // Compute the width of this AABB. - float32 Width() const - { - return m_upper.x - m_lower.x; - } - - // Compute the height of this AABB. - float32 Height() const - { - return m_upper.y - m_lower.y; - } - - // Compute the depth of this AABB. - float32 Depth() const - { - return m_upper.z - m_lower.z; - } - - // Compute the total of cubic units contained in this AABB. - float32 Volume() const - { - return Width() * Height() * Depth(); - } - - // Compute the surface area of this AABB. - float32 SurfaceArea() const - { - return 2.0f * (Width() * Depth() + Width() * Height() + Depth() * Height()); - } - - // Read the index of the longest axis of this AABB. - u32 GetLongestAxisIndex() const - { - b3Vec3 c = Centroid(); - b3Vec3 r = m_upper - c; - float32 max = r[0]; - u32 i = 0; - if (r[1] > max) - { - max = r[1]; - i = 1; - } - if (r[2] > max) - { - max = r[2]; - i = 2; - } - return i; - } - - // Test if this AABB contains a point. - bool Contains(const b3Vec3& point) const - { - return m_lower.x <= point.x && point.x <= m_upper.x && - m_lower.y <= point.y && point.y <= m_upper.y && - m_lower.z <= point.z && point.z <= m_upper.z; - } - - // Test if this AABB contains another AABB. - bool Contains(const b3AABB3& aabb) const - { - return Contains(aabb.m_lower) && Contains(aabb.m_upper); - } - - // Test if a ray intersects this AABB. - bool TestRay(float32& minFraction, const b3Vec3& p1, const b3Vec3& p2, float32 maxFraction) const - { - b3Vec3 d = p2 - p1; - - float32 lower = 0.0f; - float32 upper = maxFraction; - - for (u32 i = 0; i < 3; ++i) - { - float32 numerators[2], denominators[2]; - - numerators[0] = p1[i] - m_lower[i]; - numerators[1] = m_upper[i] - p1[i]; - - denominators[0] = -d[i]; - denominators[1] = d[i]; - - for (u32 j = 0; j < 2; ++j) - { - float32 numerator = numerators[j]; - float32 denominator = denominators[j]; - - if (denominator == 0.0f) - { - // s is parallel to this half-space. - if (numerator < 0.0f) - { - // s is outside of this half-space. - // dot(n, p1) and dot(n, p2) < 0. - return false; - } - } - else - { - if (denominator < 0.0f) - { - // s enters this half-space. - if (numerator < lower * denominator) - { - // Increase lower. - lower = numerator / denominator; - } - } - else - { - // s exits the half-space. - if (numerator < upper * denominator) - { - // Decrease upper. - upper = numerator / denominator; - } - } - // Exit if intersection becomes empty. - if (upper < lower) - { - return false; - } - } - } - } - - B3_ASSERT(lower >= 0.0f && lower <= maxFraction); - minFraction = lower; - return true; - } -}; - -// Compute an AABB that encloses two AABBs. -inline b3AABB3 b3Combine(const b3AABB3& a, const b3AABB3& b) -{ - b3AABB3 aabb; - aabb.m_lower = b3Min(a.m_lower, b.m_lower); - aabb.m_upper = b3Max(a.m_upper, b.m_upper); - return aabb; -} - -// Test if two AABBs are overlapping. -inline bool b3TestOverlap(const b3AABB3& a, const b3AABB3& b) -{ - return (a.m_lower.x <= b.m_upper.x) && (a.m_lower.y <= b.m_upper.y) && (a.m_lower.z <= b.m_upper.z) && - (a.m_upper.x >= b.m_lower.x) && (a.m_upper.y >= b.m_lower.y) && (a.m_upper.z >= b.m_lower.z); -} - -#endif \ No newline at end of file diff --git a/include/bounce/collision/shapes/box_hull.h b/include/bounce/collision/shapes/box_hull.h index b655c18..0bcc4ab 100644 --- a/include/bounce/collision/shapes/box_hull.h +++ b/include/bounce/collision/shapes/box_hull.h @@ -32,140 +32,74 @@ struct b3BoxHull : public b3Hull b3BoxHull() { } // Construct this box from three extents and centered at the origin. - b3BoxHull(float32 ex, float32 ey, float32 ez) + b3BoxHull(scalar ex, scalar ey, scalar ez) { - Set(ex, ey, ez); + SetExtents(ex, ey, ez); } - + // Set this box to the unit box centered at the origin. void SetIdentity() { - boxVertices[0] = b3Vec3(1.0f, 1.0f, -1.0f); - boxVertices[1] = b3Vec3(-1.0f, 1.0f, -1.0f); - boxVertices[2] = b3Vec3(-1.0f, -1.0f, -1.0f); - boxVertices[3] = b3Vec3(1.0f, -1.0f, -1.0f); - boxVertices[4] = b3Vec3(1.0f, 1.0f, 1.0f); - boxVertices[5] = b3Vec3(-1.0f, 1.0f, 1.0f); - boxVertices[6] = b3Vec3(-1.0f, -1.0f, 1.0f); - boxVertices[7] = b3Vec3(1.0f, -1.0f, 1.0f); + boxVertices[0] = b3Vec3(-1, -1, 1); + boxVertices[1] = b3Vec3(1, -1, -1); + boxVertices[2] = b3Vec3(1, 1, 1); + boxVertices[3] = b3Vec3(-1, 1, -1); + boxVertices[4] = b3Vec3(1, 1, -1); + boxVertices[5] = b3Vec3(-1, 1, 1); + boxVertices[6] = b3Vec3(1, -1, 1); + boxVertices[7] = b3Vec3(-1, -1, -1); - boxEdges[0] = b3MakeEdge(1, 1, 0, 2); - boxEdges[1] = b3MakeEdge(2, 0, 5, 21); - boxEdges[2] = b3MakeEdge(2, 3, 0, 4); - boxEdges[3] = b3MakeEdge(6, 2, 2, 18); - boxEdges[4] = b3MakeEdge(6, 5, 0, 6); - boxEdges[5] = b3MakeEdge(5, 4, 4, 17); - boxEdges[6] = b3MakeEdge(5, 7, 0, 0); - boxEdges[7] = b3MakeEdge(1, 6, 3, 22); - boxEdges[8] = b3MakeEdge(4, 9, 1, 10); - boxEdges[9] = b3MakeEdge(7, 8, 4, 23); - boxEdges[10] = b3MakeEdge(7, 11, 1, 12); - boxEdges[11] = b3MakeEdge(3, 10, 2, 16); - boxEdges[12] = b3MakeEdge(3, 13, 1, 14); - boxEdges[13] = b3MakeEdge(0, 12, 5, 19); - boxEdges[14] = b3MakeEdge(0, 15, 1, 8); - boxEdges[15] = b3MakeEdge(4, 14, 3, 20); - boxEdges[16] = b3MakeEdge(7, 17, 2, 3); - boxEdges[17] = b3MakeEdge(6, 16, 4, 9); - boxEdges[18] = b3MakeEdge(2, 19, 2, 11); - boxEdges[19] = b3MakeEdge(3, 18, 5, 1); - boxEdges[20] = b3MakeEdge(0, 21, 3, 7); - boxEdges[21] = b3MakeEdge(1, 20, 5, 13); - boxEdges[22] = b3MakeEdge(5, 23, 3, 15); - boxEdges[23] = b3MakeEdge(4, 22, 4, 5); + boxEdges[0] = b3MakeEdge(0, 1, 0, 6, 2); + boxEdges[1] = b3MakeEdge(5, 0, 1, 12, 8); + boxEdges[2] = b3MakeEdge(5, 3, 0, 0, 4); + boxEdges[3] = b3MakeEdge(3, 2, 5, 19, 13); + boxEdges[4] = b3MakeEdge(3, 5, 0, 2, 6); + boxEdges[5] = b3MakeEdge(7, 4, 3, 15, 18); + boxEdges[6] = b3MakeEdge(7, 7, 0, 4, 0); + boxEdges[7] = b3MakeEdge(0, 6, 2, 9, 14); + boxEdges[8] = b3MakeEdge(0, 9, 1, 1, 10); + boxEdges[9] = b3MakeEdge(6, 8, 2, 16, 7); + boxEdges[10] = b3MakeEdge(6, 11, 1, 8, 12); + boxEdges[11] = b3MakeEdge(2, 10, 4, 22, 17); + boxEdges[12] = b3MakeEdge(2, 13, 1, 10, 1); + boxEdges[13] = b3MakeEdge(5, 12, 5, 3, 23); + boxEdges[14] = b3MakeEdge(7, 15, 2, 7, 16); + boxEdges[15] = b3MakeEdge(1, 14, 3, 20, 5); + boxEdges[16] = b3MakeEdge(1, 17, 2, 14, 9); + boxEdges[17] = b3MakeEdge(6, 16, 4, 11, 21); + boxEdges[18] = b3MakeEdge(3, 19, 3, 5, 20); + boxEdges[19] = b3MakeEdge(4, 18, 5, 23, 3); + boxEdges[20] = b3MakeEdge(4, 21, 3, 18, 15); + boxEdges[21] = b3MakeEdge(1, 20, 4, 17, 22); + boxEdges[22] = b3MakeEdge(4, 23, 4, 21, 11); + boxEdges[23] = b3MakeEdge(2, 22, 5, 13, 19); - boxFaces[0].edge = 6; - boxFaces[1].edge = 14; - boxFaces[2].edge = 18; + boxFaces[0].edge = 0; + boxFaces[1].edge = 8; + boxFaces[2].edge = 7; boxFaces[3].edge = 15; - boxFaces[4].edge = 9; - boxFaces[5].edge = 21; + boxFaces[4].edge = 21; + boxFaces[5].edge = 23; - boxPlanes[0] = b3Plane(b3Vec3(-1.0f, 0.0f, 0.0f), boxVertices[1]); - boxPlanes[1] = b3Plane(b3Vec3(1.0f, 0.0f, 0.0f), boxVertices[0]); - boxPlanes[2] = b3Plane(b3Vec3(0.0f, -1.0f, 0.0f), boxVertices[2]); - boxPlanes[3] = b3Plane(b3Vec3(0.0f, 1.0f, 0.0f), boxVertices[1]); - boxPlanes[4] = b3Plane(b3Vec3(0.0f, 0.0f, 1.0f), boxVertices[4]); - boxPlanes[5] = b3Plane(b3Vec3(0.0f, 0.0f, -1.0f), boxVertices[0]); - - centroid = b3Vec3(0.0f, 0.0f, 0.0f); - vertices = boxVertices; - vertexCount = 8; - edges = boxEdges; - edgeCount = 24; - faces = boxFaces; - planes = boxPlanes; - faceCount = 6; + boxPlanes[0].normal = b3Vec3(-1, 0, 0); + boxPlanes[0].offset = 1; - Validate(); - } - - // Set this box from three extents and centered at the origin. - void Set(float32 ex, float32 ey, float32 ez) - { - b3Transform xf; - xf.position.SetZero(); - xf.rotation = b3Diagonal(ex, ey, ez); - SetTransform(xf); - } + boxPlanes[1].normal = b3Vec3(0, 0, 1); + boxPlanes[1].offset = 1; + + boxPlanes[2].normal = b3Vec3(0, -1, 0); + boxPlanes[2].offset = 1; + + boxPlanes[3].normal = b3Vec3(0, 0, -1); + boxPlanes[3].offset = 1; + + boxPlanes[4].normal = b3Vec3(1, 0, 0); + boxPlanes[4].offset = 1; + + boxPlanes[5].normal = b3Vec3(0, 1, 0); + boxPlanes[5].offset = 1; - // Set this box to the unit box and transform it. - void SetTransform(const b3Transform& T) - { - boxVertices[0] = b3Vec3(1.0f, 1.0f, -1.0f); - boxVertices[1] = b3Vec3(-1.0f, 1.0f, -1.0f); - boxVertices[2] = b3Vec3(-1.0f, -1.0f, -1.0f); - boxVertices[3] = b3Vec3(1.0f, -1.0f, -1.0f); - boxVertices[4] = b3Vec3(1.0f, 1.0f, 1.0f); - boxVertices[5] = b3Vec3(-1.0f, 1.0f, 1.0f); - boxVertices[6] = b3Vec3(-1.0f, -1.0f, 1.0f); - boxVertices[7] = b3Vec3(1.0f, -1.0f, 1.0f); - - for (u32 i = 0; i < 8; ++i) - { - boxVertices[i] = T * boxVertices[i]; - } - - boxEdges[0] = b3MakeEdge(1, 1, 0, 2); - boxEdges[1] = b3MakeEdge(2, 0, 5, 21); - boxEdges[2] = b3MakeEdge(2, 3, 0, 4); - boxEdges[3] = b3MakeEdge(6, 2, 2, 18); - boxEdges[4] = b3MakeEdge(6, 5, 0, 6); - boxEdges[5] = b3MakeEdge(5, 4, 4, 17); - boxEdges[6] = b3MakeEdge(5, 7, 0, 0); - boxEdges[7] = b3MakeEdge(1, 6, 3, 22); - boxEdges[8] = b3MakeEdge(4, 9, 1, 10); - boxEdges[9] = b3MakeEdge(7, 8, 4, 23); - boxEdges[10] = b3MakeEdge(7, 11, 1, 12); - boxEdges[11] = b3MakeEdge(3, 10, 2, 16); - boxEdges[12] = b3MakeEdge(3, 13, 1, 14); - boxEdges[13] = b3MakeEdge(0, 12, 5, 19); - boxEdges[14] = b3MakeEdge(0, 15, 1, 8); - boxEdges[15] = b3MakeEdge(4, 14, 3, 20); - boxEdges[16] = b3MakeEdge(7, 17, 2, 3); - boxEdges[17] = b3MakeEdge(6, 16, 4, 9); - boxEdges[18] = b3MakeEdge(2, 19, 2, 11); - boxEdges[19] = b3MakeEdge(3, 18, 5, 1); - boxEdges[20] = b3MakeEdge(0, 21, 3, 7); - boxEdges[21] = b3MakeEdge(1, 20, 5, 13); - boxEdges[22] = b3MakeEdge(5, 23, 3, 15); - boxEdges[23] = b3MakeEdge(4, 22, 4, 5); - - boxFaces[0].edge = 6; - boxFaces[1].edge = 14; - boxFaces[2].edge = 18; - boxFaces[3].edge = 15; - boxFaces[4].edge = 9; - boxFaces[5].edge = 21; - - boxPlanes[0] = b3Plane(b3Vec3(-1.0f, 0.0f, 0.0f), boxVertices[1]); - boxPlanes[1] = b3Plane(b3Vec3(1.0f, 0.0f, 0.0f), boxVertices[0]); - boxPlanes[2] = b3Plane(b3Vec3(0.0f, -1.0f, 0.0f), boxVertices[2]); - boxPlanes[3] = b3Plane(b3Vec3(0.0f, 1.0f, 0.0f), boxVertices[1]); - boxPlanes[4] = b3Plane(b3Vec3(0.0f, 0.0f, 1.0f), boxVertices[4]); - boxPlanes[5] = b3Plane(b3Vec3(0.0f, 0.0f, -1.0f), boxVertices[0]); - - centroid = b3Vec3(0.0f, 0.0f, 0.0f); + centroid = b3Vec3(0, 0, 0); vertices = boxVertices; vertexCount = 8; edges = boxEdges; @@ -174,10 +108,17 @@ struct b3BoxHull : public b3Hull planes = boxPlanes; faceCount = 6; - centroid = T * centroid; - Validate(); } + + // Set this box from three extents and centered and aligned at the origin. + void SetExtents(scalar ex, scalar ey, scalar ez) + { + SetIdentity(); + + b3Vec3 scale(ex, ey, ez); + Scale(scale); + } }; extern const b3BoxHull b3BoxHull_identity; diff --git a/include/bounce/collision/shapes/capsule.h b/include/bounce/collision/shapes/capsule.h index ea434e5..4d3f8a8 100644 --- a/include/bounce/collision/shapes/capsule.h +++ b/include/bounce/collision/shapes/capsule.h @@ -23,23 +23,18 @@ struct b3Capsule { - // + b3Vec3 vertex1, vertex2; + scalar radius; + b3Capsule() { } - // - b3Capsule(const b3Vec3& v1, const b3Vec3& v2, float32 r) + b3Capsule(const b3Vec3& v1, const b3Vec3& v2, scalar r) { - vertices[0] = v1; - vertices[1] = v2; + vertex1 = v1; + vertex2 = v2; radius = r; } - // - ~b3Capsule() { } - - b3Vec3 vertices[2]; - float32 radius; - const b3Vec3& GetVertex(u32 index) const; u32 GetSupportVertex(const b3Vec3& direction) const; }; @@ -49,12 +44,12 @@ extern const b3Capsule b3Capsule_identity; inline const b3Vec3& b3Capsule::GetVertex(u32 index) const { - return vertices[index]; + return (&vertex1)[index]; } inline u32 b3Capsule::GetSupportVertex(const b3Vec3& d) const { - if (b3Dot(d, vertices[0]) > b3Dot(d, vertices[1])) + if (b3Dot(d, vertex1) > b3Dot(d, vertex2)) { return 0; } diff --git a/include/bounce/collision/shapes/cone_hull.h b/include/bounce/collision/shapes/cone_hull.h new file mode 100644 index 0000000..7e033d6 --- /dev/null +++ b/include/bounce/collision/shapes/cone_hull.h @@ -0,0 +1,240 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CONE_HULL_H +#define B3_CONE_HULL_H + +#include + +// A cone with 20 segments. +struct b3ConeHull : public b3Hull +{ + b3Vec3 coneVertices[21]; + b3HalfEdge coneEdges[80]; + b3Face coneFaces[21]; + b3Plane conePlanes[21]; + + // Does nothing for performance. + b3ConeHull() + { + + } + + // Construct this cone from radius and y extent centered at the origin. + // The cone will have radius and extent set to 1; + b3ConeHull(scalar radius, scalar ey) + { + SetExtents(radius, ey); + } + + // Set this cone to the unit cylinder centered at the origin. + void SetIdentity() + { + coneVertices[0] = b3Vec3(-0.95105665922164916992, -1.00000000000000000000, -0.30901667475700378418); + coneVertices[1] = b3Vec3(-0.95105648040771484375, -1.00000000000000000000, 0.30901741981506347656); + coneVertices[2] = b3Vec3(0.30901747941970825195, -1.00000000000000000000, 0.95105648040771484375); + coneVertices[3] = b3Vec3(0.95105648040771484375, -1.00000000000000000000, -0.30901703238487243652); + coneVertices[4] = b3Vec3(-0.58778500556945800781, -1.00000000000000000000, 0.80901730060577392578); + coneVertices[5] = b3Vec3(-0.30901724100112915039, -1.00000000000000000000, -0.95105648040771484375); + coneVertices[6] = b3Vec3(0.30901682376861572266, -1.00000000000000000000, -0.95105654001235961914); + coneVertices[7] = b3Vec3(0.80901741981506347656, -1.00000000000000000000, 0.58778494596481323242); + coneVertices[8] = b3Vec3(-0.80901724100112915039, -1.00000000000000000000, -0.58778500556945800781); + coneVertices[9] = b3Vec3(0.95105683803558349609, -1.00000000000000000000, 0.30901655554771423340); + coneVertices[10] = b3Vec3(-0.30901664495468139648, -1.00000000000000000000, 0.95105671882629394531); + coneVertices[11] = b3Vec3(0.80901694297790527344, -1.00000000000000000000, -0.58778530359268188477); + coneVertices[12] = b3Vec3(0.58778578042984008789, -1.00000000000000000000, 0.80901682376861572266); + coneVertices[13] = b3Vec3(0.58778512477874755859, -1.00000000000000000000, -0.80901706218719482422); + coneVertices[14] = b3Vec3(-0.80901682376861572266, -1.00000000000000000000, 0.58778566122055053711); + coneVertices[15] = b3Vec3(-0.58778554201126098633, -1.00000000000000000000, -0.80901688337326049805); + coneVertices[16] = b3Vec3(0.00000044982880353928, -1.00000000000000000000, 1.00000011920928955078); + coneVertices[17] = b3Vec3(-0.00000022072345018387, -1.00000000000000000000, -1.00000000000000000000); + coneVertices[18] = b3Vec3(-1.00000011920928955078, -1.00000000000000000000, 0.00000039115548133850); + coneVertices[19] = b3Vec3(0.00000000000000000000, 1.00000000000000000000, 0.00000000000000000000); + coneVertices[20] = b3Vec3(1.00000000000000000000, -1.00000000000000000000, 0.00000000000000000000); + + coneEdges[0] = b3MakeEdge(0, 1, 0, 4, 2); + coneEdges[1] = b3MakeEdge(19, 0, 1, 8, 6); + coneEdges[2] = b3MakeEdge(19, 3, 0, 0, 4); + coneEdges[3] = b3MakeEdge(8, 2, 17, 11, 71); + coneEdges[4] = b3MakeEdge(8, 5, 0, 2, 0); + coneEdges[5] = b3MakeEdge(0, 4, 2, 7, 10); + coneEdges[6] = b3MakeEdge(0, 7, 1, 1, 8); + coneEdges[7] = b3MakeEdge(18, 6, 2, 44, 5); + coneEdges[8] = b3MakeEdge(18, 9, 1, 6, 1); + coneEdges[9] = b3MakeEdge(19, 8, 3, 46, 45); + coneEdges[10] = b3MakeEdge(8, 11, 2, 5, 12); + coneEdges[11] = b3MakeEdge(15, 10, 17, 71, 3); + coneEdges[12] = b3MakeEdge(15, 13, 2, 10, 14); + coneEdges[13] = b3MakeEdge(5, 12, 12, 67, 70); + coneEdges[14] = b3MakeEdge(5, 15, 2, 12, 16); + coneEdges[15] = b3MakeEdge(17, 14, 11, 68, 66); + coneEdges[16] = b3MakeEdge(17, 17, 2, 14, 18); + coneEdges[17] = b3MakeEdge(6, 16, 14, 73, 69); + coneEdges[18] = b3MakeEdge(6, 19, 2, 16, 20); + coneEdges[19] = b3MakeEdge(13, 18, 13, 74, 72); + coneEdges[20] = b3MakeEdge(13, 21, 2, 18, 22); + coneEdges[21] = b3MakeEdge(11, 20, 20, 61, 75); + coneEdges[22] = b3MakeEdge(11, 23, 2, 20, 24); + coneEdges[23] = b3MakeEdge(3, 22, 8, 57, 60); + coneEdges[24] = b3MakeEdge(3, 25, 2, 22, 26); + coneEdges[25] = b3MakeEdge(20, 24, 7, 58, 56); + coneEdges[26] = b3MakeEdge(20, 27, 2, 24, 28); + coneEdges[27] = b3MakeEdge(9, 26, 18, 79, 59); + coneEdges[28] = b3MakeEdge(9, 29, 2, 26, 30); + coneEdges[29] = b3MakeEdge(7, 28, 16, 77, 78); + coneEdges[30] = b3MakeEdge(7, 31, 2, 28, 32); + coneEdges[31] = b3MakeEdge(12, 30, 15, 55, 76); + coneEdges[32] = b3MakeEdge(12, 33, 2, 30, 34); + coneEdges[33] = b3MakeEdge(2, 32, 6, 51, 54); + coneEdges[34] = b3MakeEdge(2, 35, 2, 32, 36); + coneEdges[35] = b3MakeEdge(16, 34, 5, 52, 50); + coneEdges[36] = b3MakeEdge(16, 37, 2, 34, 38); + coneEdges[37] = b3MakeEdge(10, 36, 19, 65, 53); + coneEdges[38] = b3MakeEdge(10, 39, 2, 36, 40); + coneEdges[39] = b3MakeEdge(4, 38, 10, 63, 64); + coneEdges[40] = b3MakeEdge(4, 41, 2, 38, 42); + coneEdges[41] = b3MakeEdge(14, 40, 9, 49, 62); + coneEdges[42] = b3MakeEdge(14, 43, 2, 40, 44); + coneEdges[43] = b3MakeEdge(1, 42, 4, 47, 48); + coneEdges[44] = b3MakeEdge(1, 45, 2, 42, 7); + coneEdges[45] = b3MakeEdge(18, 44, 3, 9, 46); + coneEdges[46] = b3MakeEdge(1, 47, 3, 45, 9); + coneEdges[47] = b3MakeEdge(19, 46, 4, 48, 43); + coneEdges[48] = b3MakeEdge(14, 49, 4, 43, 47); + coneEdges[49] = b3MakeEdge(19, 48, 9, 62, 41); + coneEdges[50] = b3MakeEdge(2, 51, 5, 35, 52); + coneEdges[51] = b3MakeEdge(19, 50, 6, 54, 33); + coneEdges[52] = b3MakeEdge(19, 53, 5, 50, 35); + coneEdges[53] = b3MakeEdge(16, 52, 19, 37, 65); + coneEdges[54] = b3MakeEdge(12, 55, 6, 33, 51); + coneEdges[55] = b3MakeEdge(19, 54, 15, 76, 31); + coneEdges[56] = b3MakeEdge(3, 57, 7, 25, 58); + coneEdges[57] = b3MakeEdge(19, 56, 8, 60, 23); + coneEdges[58] = b3MakeEdge(19, 59, 7, 56, 25); + coneEdges[59] = b3MakeEdge(20, 58, 18, 27, 79); + coneEdges[60] = b3MakeEdge(11, 61, 8, 23, 57); + coneEdges[61] = b3MakeEdge(19, 60, 20, 75, 21); + coneEdges[62] = b3MakeEdge(4, 63, 9, 41, 49); + coneEdges[63] = b3MakeEdge(19, 62, 10, 64, 39); + coneEdges[64] = b3MakeEdge(10, 65, 10, 39, 63); + coneEdges[65] = b3MakeEdge(19, 64, 19, 53, 37); + coneEdges[66] = b3MakeEdge(5, 67, 11, 15, 68); + coneEdges[67] = b3MakeEdge(19, 66, 12, 70, 13); + coneEdges[68] = b3MakeEdge(19, 69, 11, 66, 15); + coneEdges[69] = b3MakeEdge(17, 68, 14, 17, 73); + coneEdges[70] = b3MakeEdge(15, 71, 12, 13, 67); + coneEdges[71] = b3MakeEdge(19, 70, 17, 3, 11); + coneEdges[72] = b3MakeEdge(6, 73, 13, 19, 74); + coneEdges[73] = b3MakeEdge(19, 72, 14, 69, 17); + coneEdges[74] = b3MakeEdge(19, 75, 13, 72, 19); + coneEdges[75] = b3MakeEdge(13, 74, 20, 21, 61); + coneEdges[76] = b3MakeEdge(7, 77, 15, 31, 55); + coneEdges[77] = b3MakeEdge(19, 76, 16, 78, 29); + coneEdges[78] = b3MakeEdge(9, 79, 16, 29, 77); + coneEdges[79] = b3MakeEdge(19, 78, 18, 59, 27); + + coneFaces[0].edge = 0; + coneFaces[1].edge = 6; + coneFaces[2].edge = 5; + coneFaces[3].edge = 46; + coneFaces[4].edge = 43; + coneFaces[5].edge = 50; + coneFaces[6].edge = 33; + coneFaces[7].edge = 56; + coneFaces[8].edge = 23; + coneFaces[9].edge = 62; + coneFaces[10].edge = 39; + coneFaces[11].edge = 66; + coneFaces[12].edge = 13; + coneFaces[13].edge = 72; + coneFaces[14].edge = 17; + coneFaces[15].edge = 76; + coneFaces[16].edge = 29; + coneFaces[17].edge = 3; + coneFaces[18].edge = 27; + coneFaces[19].edge = 37; + coneFaces[20].edge = 21; + + conePlanes[0].normal = b3Vec3(-0.79889804124832153320, 0.44279259443283081055, -0.40705847740173339844); + conePlanes[0].offset = 0.44279259443283081055; + conePlanes[1].normal = b3Vec3(-0.88558518886566162109, 0.44279265403747558594, -0.14026281237602233887); + conePlanes[1].offset = 0.44279262423515319824; + conePlanes[2].normal = b3Vec3(0.00000000000000000000, -1.00000000000000000000, 0.00000000000000000000); + conePlanes[2].offset = 1.00000000000000000000; + conePlanes[3].normal = b3Vec3(-0.88558501005172729492, 0.44279259443283081055, 0.14026330411434173584); + conePlanes[3].offset = 0.44279262423515319824; + conePlanes[4].normal = b3Vec3(-0.79889774322509765625, 0.44279259443283081055, 0.40705913305282592773); + conePlanes[4].offset = 0.44279265403747558594; + conePlanes[5].normal = b3Vec3(0.14026331901550292969, 0.44279259443283081055, 0.88558501005172729492); + conePlanes[5].offset = 0.44279265403747558594; + conePlanes[6].normal = b3Vec3(0.40705907344818115234, 0.44279259443283081055, 0.79889774322509765625); + conePlanes[6].offset = 0.44279265403747558594; + conePlanes[7].normal = b3Vec3(0.88558512926101684570, 0.44279256463050842285, -0.14026297628879547119); + conePlanes[7].offset = 0.44279259443283081055; + conePlanes[8].normal = b3Vec3(0.79889786243438720703, 0.44279253482818603516, -0.40705886483192443848); + conePlanes[8].offset = 0.44279256463050842285; + conePlanes[9].normal = b3Vec3(-0.63400870561599731445, 0.44279262423515319824, 0.63400918245315551758); + conePlanes[9].offset = 0.44279256463050842285; + conePlanes[10].normal = b3Vec3(-0.40705844759941101074, 0.44279259443283081055, 0.79889804124832153320); + conePlanes[10].offset = 0.44279265403747558594; + conePlanes[11].normal = b3Vec3(-0.14026300609111785889, 0.44279259443283081055, -0.88558506965637207031); + conePlanes[11].offset = 0.44279253482818603516; + conePlanes[12].normal = b3Vec3(-0.40705892443656921387, 0.44279259443283081055, -0.79889780282974243164); + conePlanes[12].offset = 0.44279259443283081055; + conePlanes[13].normal = b3Vec3(0.40705865621566772461, 0.44279253482818603516, -0.79889798164367675781); + conePlanes[13].offset = 0.44279259443283081055; + conePlanes[14].normal = b3Vec3(0.14026281237602233887, 0.44279256463050842285, -0.88558518886566162109); + conePlanes[14].offset = 0.44279259443283081055; + conePlanes[15].normal = b3Vec3(0.63400930166244506836, 0.44279262423515319824, 0.63400864601135253906); + conePlanes[15].offset = 0.44279265403747558594; + conePlanes[16].normal = b3Vec3(0.79889810085296630859, 0.44279265403747558594, 0.40705841779708862305); + conePlanes[16].offset = 0.44279265403747558594; + conePlanes[17].normal = b3Vec3(-0.63400924205780029297, 0.44279265403747558594, -0.63400876522064208984); + conePlanes[17].offset = 0.44279265403747558594; + conePlanes[18].normal = b3Vec3(0.88558530807495117188, 0.44279265403747558594, 0.14026220142841339111); + conePlanes[18].offset = 0.44279268383979797363; + conePlanes[19].normal = b3Vec3(-0.14026261866092681885, 0.44279259443283081055, 0.88558512926101684570); + conePlanes[19].offset = 0.44279259443283081055; + conePlanes[20].normal = b3Vec3(0.63400888442993164063, 0.44279256463050842285, -0.63400906324386596680); + conePlanes[20].offset = 0.44279259443283081055; + + centroid = b3Vec3(-0.00000003081002830641, -0.50000011920928955078, 0.00000003992893837790); + vertices = coneVertices; + vertexCount = 21; + edges = coneEdges; + edgeCount = 80; + faces = coneFaces; + planes = conePlanes; + faceCount = 21; + + Validate(); + } + + // Construct this cylinder from radius and y extent centered at the origin. + void SetExtents(scalar radius, scalar ey) + { + SetIdentity(); + + b3Vec3 scale(radius, ey, radius); + Scale(scale); + } +}; + +extern const b3ConeHull b3ConeHull_identity; + +#endif \ No newline at end of file diff --git a/include/bounce/collision/shapes/cylinder_hull.h b/include/bounce/collision/shapes/cylinder_hull.h new file mode 100644 index 0000000..56d6dbd --- /dev/null +++ b/include/bounce/collision/shapes/cylinder_hull.h @@ -0,0 +1,322 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CYLINDER_HULL_H +#define B3_CYLINDER_HULL_H + +#include + +// A cylinder with 20 segments. +struct b3CylinderHull : public b3Hull +{ + b3Vec3 cylinderVertices[40]; + b3HalfEdge cylinderEdges[120]; + b3Face cylinderFaces[22]; + b3Plane cylinderPlanes[22]; + + // Does nothing for performance. + b3CylinderHull() + { + + } + + // Construct this cylinder from radius and y extent centered at the origin. + b3CylinderHull(scalar radius, scalar ey) + { + SetExtents(radius, ey); + } + + // Set this cylinder to the unit cylinder centered at the origin. + void SetIdentity() + { + cylinderVertices[0] = b3Vec3(0.80901741981506347656, 1, 0.58778494596481323242); + cylinderVertices[1] = b3Vec3(-0.80901724100112915039, -1, -0.58778500556945800781); + cylinderVertices[2] = b3Vec3(-0.30901724100112915039, 1, -0.95105648040771484375); + cylinderVertices[3] = b3Vec3(0.30901747941970825195, 1, 0.95105648040771484375); + cylinderVertices[4] = b3Vec3(0.95105648040771484375, 1, -0.30901703238487243652); + cylinderVertices[5] = b3Vec3(-0.95105648040771484375, -1, 0.30901741981506347656); + cylinderVertices[6] = b3Vec3(0.58778512477874755859, -1, -0.80901706218719482422); + cylinderVertices[7] = b3Vec3(0.95105683803558349609, -1, 0.30901655554771423340); + cylinderVertices[8] = b3Vec3(-0.58778500556945800781, 1, 0.80901730060577392578); + cylinderVertices[9] = b3Vec3(0.30901747941970825195, -1, 0.95105648040771484375); + cylinderVertices[10] = b3Vec3(-0.30901724100112915039, -1, -0.95105648040771484375); + cylinderVertices[11] = b3Vec3(-0.95105648040771484375, 1, 0.30901741981506347656); + cylinderVertices[12] = b3Vec3(0.95105648040771484375, -1, -0.30901703238487243652); + cylinderVertices[13] = b3Vec3(0.58778512477874755859, 1, -0.80901706218719482422); + cylinderVertices[14] = b3Vec3(-0.80901724100112915039, 1, -0.58778500556945800781); + cylinderVertices[15] = b3Vec3(-0.58778500556945800781, -1, 0.80901730060577392578); + cylinderVertices[16] = b3Vec3(-0.30901664495468139648, -1, 0.95105671882629394531); + cylinderVertices[17] = b3Vec3(0.95105683803558349609, 1, 0.30901655554771423340); + cylinderVertices[18] = b3Vec3(-0.95105665922164916992, 1, -0.30901667475700378418); + cylinderVertices[19] = b3Vec3(0.30901682376861572266, 1, -0.95105654001235961914); + cylinderVertices[20] = b3Vec3(-0.95105665922164916992, -1, -0.30901667475700378418); + cylinderVertices[21] = b3Vec3(0.30901682376861572266, -1, -0.95105654001235961914); + cylinderVertices[22] = b3Vec3(0.80901741981506347656, -1, 0.58778494596481323242); + cylinderVertices[23] = b3Vec3(-0.30901664495468139648, 1, 0.95105671882629394531); + cylinderVertices[24] = b3Vec3(-0.80901682376861572266, 1, 0.58778566122055053711); + cylinderVertices[25] = b3Vec3(0.80901694297790527344, -1, -0.58778530359268188477); + cylinderVertices[26] = b3Vec3(-0.58778554201126098633, -1, -0.80901688337326049805); + cylinderVertices[27] = b3Vec3(0.58778578042984008789, -1, 0.80901682376861572266); + cylinderVertices[28] = b3Vec3(0.58778578042984008789, 1, 0.80901682376861572266); + cylinderVertices[29] = b3Vec3(-0.80901682376861572266, -1, 0.58778566122055053711); + cylinderVertices[30] = b3Vec3(0.80901694297790527344, 1, -0.58778530359268188477); + cylinderVertices[31] = b3Vec3(-0.58778554201126098633, 1, -0.80901688337326049805); + cylinderVertices[32] = b3Vec3(1, 1, 0); + cylinderVertices[33] = b3Vec3(0.00000044982880353928, -1, 1.00000011920928955078); + cylinderVertices[34] = b3Vec3(-1.00000011920928955078, 1, 0.00000039115548133850); + cylinderVertices[35] = b3Vec3(-0.00000022072345018387, -1, -1); + cylinderVertices[36] = b3Vec3(0.00000044982880353928, 1, 1.00000011920928955078); + cylinderVertices[37] = b3Vec3(-0.00000022072345018387, 1, -1); + cylinderVertices[38] = b3Vec3(1, -1, 0); + cylinderVertices[39] = b3Vec3(-1.00000011920928955078, -1, 0.00000039115548133850); + + cylinderEdges[0] = b3MakeEdge(0, 1, 0, 6, 2); + cylinderEdges[1] = b3MakeEdge(22, 0, 1, 12, 8); + cylinderEdges[2] = b3MakeEdge(22, 3, 0, 0, 4); + cylinderEdges[3] = b3MakeEdge(7, 2, 4, 72, 13); + cylinderEdges[4] = b3MakeEdge(7, 5, 0, 2, 6); + cylinderEdges[5] = b3MakeEdge(17, 4, 16, 15, 73); + cylinderEdges[6] = b3MakeEdge(17, 7, 0, 4, 0); + cylinderEdges[7] = b3MakeEdge(0, 6, 2, 9, 14); + cylinderEdges[8] = b3MakeEdge(0, 9, 1, 1, 10); + cylinderEdges[9] = b3MakeEdge(28, 8, 2, 48, 7); + cylinderEdges[10] = b3MakeEdge(28, 11, 1, 8, 12); + cylinderEdges[11] = b3MakeEdge(27, 10, 9, 75, 49); + cylinderEdges[12] = b3MakeEdge(27, 13, 1, 10, 1); + cylinderEdges[13] = b3MakeEdge(22, 12, 4, 3, 74); + cylinderEdges[14] = b3MakeEdge(17, 15, 2, 7, 16); + cylinderEdges[15] = b3MakeEdge(32, 14, 16, 101, 5); + cylinderEdges[16] = b3MakeEdge(32, 17, 2, 14, 18); + cylinderEdges[17] = b3MakeEdge(4, 16, 10, 102, 100); + cylinderEdges[18] = b3MakeEdge(4, 19, 2, 16, 20); + cylinderEdges[19] = b3MakeEdge(30, 18, 11, 104, 103); + cylinderEdges[20] = b3MakeEdge(30, 21, 2, 18, 22); + cylinderEdges[21] = b3MakeEdge(13, 20, 15, 115, 105); + cylinderEdges[22] = b3MakeEdge(13, 23, 2, 20, 24); + cylinderEdges[23] = b3MakeEdge(19, 22, 14, 112, 114); + cylinderEdges[24] = b3MakeEdge(19, 25, 2, 22, 26); + cylinderEdges[25] = b3MakeEdge(37, 24, 21, 93, 113); + cylinderEdges[26] = b3MakeEdge(37, 27, 2, 24, 28); + cylinderEdges[27] = b3MakeEdge(2, 26, 6, 94, 92); + cylinderEdges[28] = b3MakeEdge(2, 29, 2, 26, 30); + cylinderEdges[29] = b3MakeEdge(31, 28, 7, 91, 95); + cylinderEdges[30] = b3MakeEdge(31, 31, 2, 28, 32); + cylinderEdges[31] = b3MakeEdge(14, 30, 5, 55, 90); + cylinderEdges[32] = b3MakeEdge(14, 33, 2, 30, 34); + cylinderEdges[33] = b3MakeEdge(18, 32, 3, 52, 54); + cylinderEdges[34] = b3MakeEdge(18, 35, 2, 32, 36); + cylinderEdges[35] = b3MakeEdge(34, 34, 20, 111, 53); + cylinderEdges[36] = b3MakeEdge(34, 37, 2, 34, 38); + cylinderEdges[37] = b3MakeEdge(11, 36, 13, 109, 110); + cylinderEdges[38] = b3MakeEdge(11, 39, 2, 36, 40); + cylinderEdges[39] = b3MakeEdge(24, 38, 12, 106, 108); + cylinderEdges[40] = b3MakeEdge(24, 41, 2, 38, 42); + cylinderEdges[41] = b3MakeEdge(8, 40, 17, 116, 107); + cylinderEdges[42] = b3MakeEdge(8, 43, 2, 40, 44); + cylinderEdges[43] = b3MakeEdge(23, 42, 18, 118, 117); + cylinderEdges[44] = b3MakeEdge(23, 45, 2, 42, 46); + cylinderEdges[45] = b3MakeEdge(36, 44, 19, 97, 119); + cylinderEdges[46] = b3MakeEdge(36, 47, 2, 44, 48); + cylinderEdges[47] = b3MakeEdge(3, 46, 8, 98, 96); + cylinderEdges[48] = b3MakeEdge(3, 49, 2, 46, 9); + cylinderEdges[49] = b3MakeEdge(28, 48, 9, 11, 99); + cylinderEdges[50] = b3MakeEdge(1, 51, 3, 54, 52); + cylinderEdges[51] = b3MakeEdge(20, 50, 4, 88, 56); + cylinderEdges[52] = b3MakeEdge(20, 53, 3, 50, 33); + cylinderEdges[53] = b3MakeEdge(18, 52, 20, 35, 89); + cylinderEdges[54] = b3MakeEdge(14, 55, 3, 33, 50); + cylinderEdges[55] = b3MakeEdge(1, 54, 5, 57, 31); + cylinderEdges[56] = b3MakeEdge(1, 57, 4, 51, 58); + cylinderEdges[57] = b3MakeEdge(26, 56, 5, 90, 55); + cylinderEdges[58] = b3MakeEdge(26, 59, 4, 56, 60); + cylinderEdges[59] = b3MakeEdge(10, 58, 7, 95, 91); + cylinderEdges[60] = b3MakeEdge(10, 61, 4, 58, 62); + cylinderEdges[61] = b3MakeEdge(35, 60, 6, 92, 94); + cylinderEdges[62] = b3MakeEdge(35, 63, 4, 60, 64); + cylinderEdges[63] = b3MakeEdge(21, 62, 21, 113, 93); + cylinderEdges[64] = b3MakeEdge(21, 65, 4, 62, 66); + cylinderEdges[65] = b3MakeEdge(6, 64, 14, 114, 112); + cylinderEdges[66] = b3MakeEdge(6, 67, 4, 64, 68); + cylinderEdges[67] = b3MakeEdge(25, 66, 15, 105, 115); + cylinderEdges[68] = b3MakeEdge(25, 69, 4, 66, 70); + cylinderEdges[69] = b3MakeEdge(12, 68, 11, 103, 104); + cylinderEdges[70] = b3MakeEdge(12, 71, 4, 68, 72); + cylinderEdges[71] = b3MakeEdge(38, 70, 10, 100, 102); + cylinderEdges[72] = b3MakeEdge(38, 73, 4, 70, 3); + cylinderEdges[73] = b3MakeEdge(7, 72, 16, 5, 101); + cylinderEdges[74] = b3MakeEdge(27, 75, 4, 13, 76); + cylinderEdges[75] = b3MakeEdge(9, 74, 9, 99, 11); + cylinderEdges[76] = b3MakeEdge(9, 77, 4, 74, 78); + cylinderEdges[77] = b3MakeEdge(33, 76, 8, 96, 98); + cylinderEdges[78] = b3MakeEdge(33, 79, 4, 76, 80); + cylinderEdges[79] = b3MakeEdge(16, 78, 19, 119, 97); + cylinderEdges[80] = b3MakeEdge(16, 81, 4, 78, 82); + cylinderEdges[81] = b3MakeEdge(15, 80, 18, 117, 118); + cylinderEdges[82] = b3MakeEdge(15, 83, 4, 80, 84); + cylinderEdges[83] = b3MakeEdge(29, 82, 17, 107, 116); + cylinderEdges[84] = b3MakeEdge(29, 85, 4, 82, 86); + cylinderEdges[85] = b3MakeEdge(5, 84, 12, 108, 106); + cylinderEdges[86] = b3MakeEdge(5, 87, 4, 84, 88); + cylinderEdges[87] = b3MakeEdge(39, 86, 13, 110, 109); + cylinderEdges[88] = b3MakeEdge(39, 89, 4, 86, 51); + cylinderEdges[89] = b3MakeEdge(20, 88, 20, 53, 111); + cylinderEdges[90] = b3MakeEdge(31, 91, 5, 31, 57); + cylinderEdges[91] = b3MakeEdge(26, 90, 7, 59, 29); + cylinderEdges[92] = b3MakeEdge(37, 93, 6, 27, 61); + cylinderEdges[93] = b3MakeEdge(35, 92, 21, 63, 25); + cylinderEdges[94] = b3MakeEdge(10, 95, 6, 61, 27); + cylinderEdges[95] = b3MakeEdge(2, 94, 7, 29, 59); + cylinderEdges[96] = b3MakeEdge(36, 97, 8, 47, 77); + cylinderEdges[97] = b3MakeEdge(33, 96, 19, 79, 45); + cylinderEdges[98] = b3MakeEdge(9, 99, 8, 77, 47); + cylinderEdges[99] = b3MakeEdge(3, 98, 9, 49, 75); + cylinderEdges[100] = b3MakeEdge(32, 101, 10, 17, 71); + cylinderEdges[101] = b3MakeEdge(38, 100, 16, 73, 15); + cylinderEdges[102] = b3MakeEdge(12, 103, 10, 71, 17); + cylinderEdges[103] = b3MakeEdge(4, 102, 11, 19, 69); + cylinderEdges[104] = b3MakeEdge(25, 105, 11, 69, 19); + cylinderEdges[105] = b3MakeEdge(30, 104, 15, 21, 67); + cylinderEdges[106] = b3MakeEdge(29, 107, 12, 85, 39); + cylinderEdges[107] = b3MakeEdge(24, 106, 17, 41, 83); + cylinderEdges[108] = b3MakeEdge(11, 109, 12, 39, 85); + cylinderEdges[109] = b3MakeEdge(5, 108, 13, 87, 37); + cylinderEdges[110] = b3MakeEdge(34, 111, 13, 37, 87); + cylinderEdges[111] = b3MakeEdge(39, 110, 20, 89, 35); + cylinderEdges[112] = b3MakeEdge(21, 113, 14, 65, 23); + cylinderEdges[113] = b3MakeEdge(19, 112, 21, 25, 63); + cylinderEdges[114] = b3MakeEdge(13, 115, 14, 23, 65); + cylinderEdges[115] = b3MakeEdge(6, 114, 15, 67, 21); + cylinderEdges[116] = b3MakeEdge(15, 117, 17, 83, 41); + cylinderEdges[117] = b3MakeEdge(8, 116, 18, 43, 81); + cylinderEdges[118] = b3MakeEdge(16, 119, 18, 81, 43); + cylinderEdges[119] = b3MakeEdge(23, 118, 19, 45, 79); + + cylinderFaces[0].edge = 0; + cylinderFaces[1].edge = 8; + cylinderFaces[2].edge = 7; + cylinderFaces[3].edge = 50; + cylinderFaces[4].edge = 56; + cylinderFaces[5].edge = 55; + cylinderFaces[6].edge = 27; + cylinderFaces[7].edge = 95; + cylinderFaces[8].edge = 47; + cylinderFaces[9].edge = 99; + cylinderFaces[10].edge = 17; + cylinderFaces[11].edge = 103; + cylinderFaces[12].edge = 85; + cylinderFaces[13].edge = 109; + cylinderFaces[14].edge = 65; + cylinderFaces[15].edge = 115; + cylinderFaces[16].edge = 73; + cylinderFaces[17].edge = 41; + cylinderFaces[18].edge = 117; + cylinderFaces[19].edge = 79; + cylinderFaces[20].edge = 53; + cylinderFaces[21].edge = 113; + + cylinderPlanes[0].normal = b3Vec3(0.89100682735443115234, 0, 0.45399010181427001953); + cylinderPlanes[0].offset = 0.98768866062164306641; + + cylinderPlanes[1].normal = b3Vec3(0.70710718631744384766, 0, 0.70710641145706176758); + cylinderPlanes[1].offset = 0.98768854141235351563; + + cylinderPlanes[2].normal = b3Vec3(0, 1, 0); + cylinderPlanes[2].offset = 1; + + cylinderPlanes[3].normal = b3Vec3(-0.89100670814514160156, 0, -0.45399013161659240723); + cylinderPlanes[3].offset = 0.98768842220306396484; + + cylinderPlanes[4].normal = b3Vec3(0, -1, 0); + cylinderPlanes[4].offset = 1; + + cylinderPlanes[5].normal = b3Vec3(-0.70710712671279907227, 0, -0.70710653066635131836); + cylinderPlanes[5].offset = 0.98768848180770874023; + + cylinderPlanes[6].normal = b3Vec3(-0.15643458068370819092, 0, -0.98768836259841918945); + cylinderPlanes[6].offset = 0.98768842220306396484; + + cylinderPlanes[7].normal = b3Vec3(-0.45399063825607299805, 0, -0.89100646972656250000); + cylinderPlanes[7].offset = 0.98768842220306396484; + + cylinderPlanes[8].normal = b3Vec3(0.15643493831157684326, 0, 0.98768830299377441406); + cylinderPlanes[8].offset = 0.98768848180770874023; + + cylinderPlanes[9].normal = b3Vec3(0.45399075746536254883, 0, 0.89100635051727294922); + cylinderPlanes[9].offset = 0.98768836259841918945; + + cylinderPlanes[10].normal = b3Vec3(0.98768830299377441406, 0, -0.15643455088138580322); + cylinderPlanes[10].offset = 0.98768830299377441406; + + cylinderPlanes[11].normal = b3Vec3(0.89100646972656250000, 0, -0.45399051904678344727); + cylinderPlanes[11].offset = 0.98768830299377441406; + + cylinderPlanes[12].normal = b3Vec3(-0.89100635051727294922, 0, 0.45399084687232971191); + cylinderPlanes[12].offset = 0.98768842220306396484; + + cylinderPlanes[13].normal = b3Vec3(-0.98768830299377441406, 0, 0.15643493831157684326); + cylinderPlanes[13].offset = 0.98768842220306396484; + + cylinderPlanes[14].normal = b3Vec3(0.45399031043052673340, 0, -0.89100658893585205078); + cylinderPlanes[14].offset = 0.98768830299377441406; + + cylinderPlanes[15].normal = b3Vec3(0.70710670948028564453, 0, -0.70710688829421997070); + cylinderPlanes[15].offset = 0.98768830299377441406; + + cylinderPlanes[16].normal = b3Vec3(0.98768848180770874023, 0, 0.15643368661403656006); + cylinderPlanes[16].offset = 0.98768854141235351563; + + cylinderPlanes[17].normal = b3Vec3(-0.70710653066635131836, 0, 0.70710712671279907227); + cylinderPlanes[17].offset = 0.98768854141235351563; + + cylinderPlanes[18].normal = b3Vec3(-0.45399010181427001953, 0, 0.89100670814514160156); + cylinderPlanes[18].offset = 0.98768842220306396484; + + cylinderPlanes[19].normal = b3Vec3(-0.15643416345119476318, 0, 0.98768842220306396484); + cylinderPlanes[19].offset = 0.98768848180770874023; + + cylinderPlanes[20].normal = b3Vec3(-0.98768842220306396484, 0, -0.15643437206745147705); + cylinderPlanes[20].offset = 0.98768848180770874023; + + cylinderPlanes[21].normal = b3Vec3(0.15643437206745147705, 0, -0.98768842220306396484); + cylinderPlanes[21].offset = 0.98768836259841918945; + + centroid = b3Vec3(0, 0, 0); + vertices = cylinderVertices; + vertexCount = 40; + edges = cylinderEdges; + edgeCount = 120; + faces = cylinderFaces; + planes = cylinderPlanes; + faceCount = 22; + + Validate(); + } + + // Construct this cylinder from radius and y extent centered at the origin. + void SetExtents(scalar radius, scalar ey) + { + SetIdentity(); + + b3Vec3 scale(radius, ey, radius); + Scale(scale); + } +}; + +extern const b3CylinderHull b3CylinderHull_identity; + +#endif \ No newline at end of file diff --git a/include/bounce/collision/shapes/grid_mesh.h b/include/bounce/collision/shapes/grid_mesh.h index f334569..e7474c7 100644 --- a/include/bounce/collision/shapes/grid_mesh.h +++ b/include/bounce/collision/shapes/grid_mesh.h @@ -26,28 +26,30 @@ template struct b3GridMesh : public b3Mesh { - b3Vec3 gridVertices[ (H + 1) * (W + 1) ]; - b3Triangle gridTriangles[2 * H * W]; + b3Vec3 gridVertices[(H + 1) * (W + 1)]; + b3MeshTriangle gridTriangles[2 * H * W]; // Set this grid to a W (width) per H (height) dimensioned grid centered at the origin and aligned // with the world x-z axes. b3GridMesh() { vertexCount = 0; - for (u32 i = 0; i <= H; ++i) + for (u32 j = 0; j <= W; ++j) { - for (u32 j = 0; j <= W; ++j) + for (u32 i = 0; i <= H; ++i) { - gridVertices[vertexCount++].Set(float32(j), 0.0f, float32(i)); + u32 vertex = GetVertex(i, j); + gridVertices[vertex].Set(scalar(j), 0, scalar(i)); + ++vertexCount; } } B3_ASSERT(vertexCount == (H + 1) * (W + 1)); b3Vec3 translation; - translation.x = -0.5f * float32(W); - translation.y = 0.0f; - translation.z = -0.5f * float32(H); + translation.x = scalar(-0.5) * scalar(W); + translation.y = 0; + translation.z = scalar(-0.5) * scalar(H); for (u32 i = 0; i < vertexCount; ++i) { @@ -59,20 +61,23 @@ struct b3GridMesh : public b3Mesh { for (u32 j = 0; j < W; ++j) { - u32 v1 = i * (W + 1) + j; - u32 v2 = (i + 1) * (W + 1) + j; - u32 v3 = (i + 1) * (W + 1) + (j + 1); - u32 v4 = i * (W + 1) + (j + 1); + // 1*|----|*4 + // |----| + // 2*|----|*3 + u32 v1 = GetVertex(i, j); + u32 v2 = GetVertex(i + 1, j); + u32 v3 = GetVertex(i + 1, j + 1); + u32 v4 = GetVertex(i, j + 1); - b3Triangle* t1 = gridTriangles + triangleCount++; - t1->v1 = v3; + b3MeshTriangle* t1 = gridTriangles + triangleCount++; + t1->v1 = v1; t1->v2 = v2; - t1->v3 = v1; + t1->v3 = v3; - b3Triangle* t2 = gridTriangles + triangleCount++; - t2->v1 = v1; + b3MeshTriangle* t2 = gridTriangles + triangleCount++; + t2->v1 = v3; t2->v2 = v4; - t2->v3 = v3; + t2->v3 = v1; } } @@ -81,6 +86,13 @@ struct b3GridMesh : public b3Mesh vertices = gridVertices; triangles = gridTriangles; } + + u32 GetVertex(u32 i, u32 j) + { + B3_ASSERT(i < H + 1); + B3_ASSERT(j < W + 1); + return i * (W + 1) + j; + } }; #endif \ No newline at end of file diff --git a/include/bounce/collision/shapes/hull.h b/include/bounce/collision/shapes/hull.h index 48f152d..7949eda 100644 --- a/include/bounce/collision/shapes/hull.h +++ b/include/bounce/collision/shapes/hull.h @@ -31,6 +31,7 @@ struct b3HalfEdge u32 origin; u32 twin; u32 face; + u32 prev; u32 next; }; @@ -61,6 +62,15 @@ struct b3Hull void Validate() const; void Validate(const b3Face* face) const; void Validate(const b3HalfEdge* edge) const; + + void Dump() const; + + void Scale(const b3Vec3& scale); + void Rotate(const b3Quat& rotation); + void Translate(const b3Vec3& translation); + + // Scale -> Rotate -> Translate + void Transform(const b3Transform& xf, const b3Vec3& scale); }; #include diff --git a/include/bounce/collision/shapes/hull.inl b/include/bounce/collision/shapes/hull.inl index 5f887a9..e6c69c5 100644 --- a/include/bounce/collision/shapes/hull.inl +++ b/include/bounce/collision/shapes/hull.inl @@ -1,9 +1,10 @@ -inline b3HalfEdge b3MakeEdge(u32 origin, u32 twin, u32 face, u32 next) +inline b3HalfEdge b3MakeEdge(u32 origin, u32 twin, u32 face, u32 prev, u32 next) { b3HalfEdge edge; edge.origin = origin; edge.twin = twin; edge.face = face; + edge.prev = prev; edge.next = next; return edge; } @@ -31,10 +32,10 @@ inline const b3Plane& b3Hull::GetPlane(u32 index) const inline u32 b3Hull::GetSupportVertex(const b3Vec3& direction) const { u32 maxIndex = 0; - float32 maxProjection = b3Dot(direction, vertices[maxIndex]); + scalar maxProjection = b3Dot(direction, vertices[maxIndex]); for (u32 i = 1; i < vertexCount; ++i) { - float32 projection = b3Dot(direction, vertices[i]); + scalar projection = b3Dot(direction, vertices[i]); if (projection > maxProjection) { maxIndex = i; @@ -47,10 +48,10 @@ inline u32 b3Hull::GetSupportVertex(const b3Vec3& direction) const inline u32 b3Hull::GetSupportFace(const b3Vec3& direction) const { u32 maxIndex = 0; - float32 maxProjection = b3Dot(direction, planes[maxIndex].normal); + scalar maxProjection = b3Dot(direction, planes[maxIndex].normal); for (u32 i = 1; i < faceCount; ++i) { - float32 projection = b3Dot(direction, planes[i].normal); + scalar projection = b3Dot(direction, planes[i].normal); if (projection > maxProjection) { maxIndex = i; diff --git a/include/bounce/collision/shapes/mesh.h b/include/bounce/collision/shapes/mesh.h index 60586b1..452a023 100644 --- a/include/bounce/collision/shapes/mesh.h +++ b/include/bounce/collision/shapes/mesh.h @@ -19,40 +19,72 @@ #ifndef B3_MESH_H #define B3_MESH_H +#include #include -struct b3Triangle +#define B3_NULL_VERTEX B3_MAX_U32 + +// Mesh triangle. +struct b3MeshTriangle { - // Test if this triangle contains a given vertex. - bool TestVertex(u32 v) const - { - return v == v1 || v == v2 || v == v3; - } + // Write an indexed vertex to this triangle. + u32& GetVertex(u32 i) { return (&v1)[i]; } - // Test if this triangle contains two vertices. - bool TestEdge(u32 _v1, u32 _v2) const - { - return TestVertex(_v1) && TestVertex(_v2); - } + // Read an indexed vertex from this triangle. + u32 GetVertex(u32 i) const { return (&v1)[i]; } + // The triangle vertices in the mesh. u32 v1, v2, v3; }; +// Mesh triangle adjacency. +// This is used for smooth edge collision. +struct b3MeshTriangleWings +{ + // Write an indexed edge wing vertex to this triangle. + u32& GetVertex(u32 i) { return (&u1)[i]; } + + // Read an indexed edge wing vertex from this triangle. + u32 GetVertex(u32 i) const { return (&u1)[i]; } + + // The wing vertex of each edge in this triangle. + // An edge is a boundary if its wing vertex is set to B3_NULL_VERTEX. + u32 u1, u2, u3; +}; + struct b3Mesh { u32 vertexCount; b3Vec3* vertices; u32 triangleCount; - b3Triangle* triangles; + b3MeshTriangle* triangles; + b3MeshTriangleWings* triangleWings; + b3StaticTree tree; + b3Mesh(); + ~b3Mesh(); + + // Build the static AABB tree. + void BuildTree(); + + // Build mesh adjacency. + // This won't work properly if there are non-manifold edges. + void BuildAdjacency(); + const b3Vec3& GetVertex(u32 index) const; - const b3Triangle& GetTriangle(u32 index) const; + const b3MeshTriangle* GetTriangle(u32 index) const; + const b3MeshTriangleWings* GetTriangleWings(u32 index) const; + b3AABB GetTriangleAABB(u32 index) const; + u32 GetSize() const; - b3AABB3 GetTriangleAABB(u32 index) const; + void Scale(const b3Vec3& scale); + void Rotate(const b3Quat& rotation); + void Translate(const b3Vec3& translation); - void BuildTree(); + // Scale -> Rotate -> Translate + void Transform(const b3Transform& xf, const b3Vec3& scale); }; inline const b3Vec3& b3Mesh::GetVertex(u32 index) const @@ -60,9 +92,24 @@ inline const b3Vec3& b3Mesh::GetVertex(u32 index) const return vertices[index]; } -inline const b3Triangle& b3Mesh::GetTriangle(u32 index) const +inline const b3MeshTriangle* b3Mesh::GetTriangle(u32 index) const { - return triangles[index]; + return triangles + index; +} + +inline const b3MeshTriangleWings* b3Mesh::GetTriangleWings(u32 index) const +{ + return triangleWings + index; +} + +inline b3AABB b3Mesh::GetTriangleAABB(u32 index) const +{ + const b3MeshTriangle* triangle = triangles + index; + + b3AABB aabb; + aabb.SetTriangle(vertices[triangle->v1], vertices[triangle->v2], vertices[triangle->v3]); + + return aabb; } inline u32 b3Mesh::GetSize() const @@ -70,32 +117,10 @@ inline u32 b3Mesh::GetSize() const u32 size = 0; size += sizeof(b3Mesh); size += sizeof(b3Vec3) * vertexCount; - size += sizeof(b3Triangle) * triangleCount; + size += sizeof(b3MeshTriangle) * triangleCount; + size += sizeof(b3MeshTriangleWings) * triangleCount; size += tree.GetSize(); return size; } -inline b3AABB3 b3Mesh::GetTriangleAABB(u32 index) const -{ - const b3Triangle* triangle = triangles + index; - - b3AABB3 aabb; - aabb.Set(vertices[triangle->v1], vertices[triangle->v2], vertices[triangle->v3]); - - return aabb; -} - -inline void b3Mesh::BuildTree() -{ - b3AABB3* aabbs = (b3AABB3*)b3Alloc(triangleCount * sizeof(b3AABB3)); - for (u32 i = 0; i < triangleCount; ++i) - { - aabbs[i] = GetTriangleAABB(i); - } - - tree.Build(aabbs, triangleCount); - - b3Free(aabbs); -} - -#endif +#endif \ No newline at end of file diff --git a/include/bounce/collision/shapes/qhull.h b/include/bounce/collision/shapes/qhull.h index 25836de..d8e8b50 100644 --- a/include/bounce/collision/shapes/qhull.h +++ b/include/bounce/collision/shapes/qhull.h @@ -52,15 +52,15 @@ struct b3QHull : public b3Hull // Set this hull as a sphere located at the origin // given the radius. - void SetAsSphere(float32 radius = 1.0f); + void SetAsSphere(scalar radius = 1, u32 subdivisions = 0); // Set this hull as a cylinder located at the origin - // given the radius and extent along the y axis. - void SetAsCylinder(float32 radius = 1.0f, float32 ey = 1.0f); + // given the radius, extent along the y axis, and number of segments. + void SetAsCylinder(scalar radius = 1, scalar ey = 1, u32 segments = 20); // Set this hull as a cone located at the origin - // given the radius and extent along the y axis. - void SetAsCone(float32 radius = 1.0f, float32 ey = 1.0f); + // given the radius, extent along the y axis, and number of segments. + void SetAsCone(scalar radius = 1, scalar ey = 1, u32 segments = 20); }; #endif \ No newline at end of file diff --git a/include/bounce/collision/shapes/sdf.h b/include/bounce/collision/shapes/sdf.h new file mode 100644 index 0000000..e6d8a88 --- /dev/null +++ b/include/bounce/collision/shapes/sdf.h @@ -0,0 +1,168 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SDF_H +#define B3_SDF_H + +#include + +struct b3MultiIndex +{ + unsigned int& operator[](unsigned int i) + { + return v[i]; + } + + const unsigned int& operator[](unsigned int i) const + { + return v[i]; + } + + unsigned int v[3]; +}; + +struct b3Cell32 +{ + unsigned int v[32]; +}; + +// This class stores a discretized signed distance function (SDF) +// generated by Discregrid. +// Discregrid is available at https://github.com/InteractiveComputerGraphics/Discregrid +// Inside Discregrid, there is a tool called GenerateSDF that can +// generate an SDF of a triangle mesh stored in .obj file format. +// You may call this tool from a command line. +// For example, the following command will generate an SDF for a given .obj mesh. +// GenerateSDF -r "32 32 32" -d "-5 -5 -5 5 5 5" teapot.obj +// The parameters are: +// 1. r - resolution +// 2. d - domain (an AABB) +// 3. input filename +// You will need to set a reasonable large domain depending on the radius of +// the vertices that can collide against the SDF because the SDF +// can only return valid output values for points that are inside the domain. +// Therefore, its a good idea to set the domain to the AABB containing the +// associated object extended by twice the largest vertex radius that can collide against this SDF. +// Generally, the greater the SDF resolution the more accurate is the result of the signed distance function. +class b3SDF +{ +public: + // Construct this SDF + b3SDF(); + + // Destruct this SDF + ~b3SDF(); + + // Read this SDF from a .cdf (binary) file given the filename. + // Returns true if this SDF was loaded sucessfuly and false otherwise. + bool Load(const char* filename); + + // Return the domain (AABB) of this SDF. + const b3AABB& GetDomain() const + { + return m_domain; + } + + // Evaluate the signed distance function for a point if the point is inside the domain of this SDF. + // Optionally output the normal at the SDF boundary. + // Return true if the output values are valid and false otherwise. + bool Evaluate(const b3Vec3& point, double& distance, b3Vec3* normal = nullptr) const + { + return interpolate(0, distance, point, normal); + } +private: + bool interpolate(unsigned int field_id, double& dist, b3Vec3 const& x, b3Vec3* gradient = nullptr) const; + + b3MultiIndex singleToMultiIndex(unsigned int i) const; + unsigned int multiToSingleIndex(b3MultiIndex const& ijk) const; + + b3AABB subdomain(b3MultiIndex const& ijk) const; + b3AABB subdomain(unsigned int l) const; + + b3AABB m_domain; + unsigned int m_resolution[3]; + b3Vec3 m_cell_size; + b3Vec3 m_inv_cell_size; + std::size_t m_n_cells; + std::size_t m_n_fields; + + struct b3SDFNodeArray + { + double& operator[](u32 i) + { + B3_ASSERT(i < count); + return values[i]; + } + + const double& operator[](u32 i) const + { + B3_ASSERT(i < count); + return values[i]; + } + + u32 count; + double* values; + }; + + struct b3SDFCellArray + { + b3Cell32& operator[](u32 i) + { + B3_ASSERT(i < count); + return values[i]; + } + + const b3Cell32& operator[](u32 i) const + { + B3_ASSERT(i < count); + return values[i]; + } + + u32 count; + b3Cell32* values; + }; + + struct b3SDFCellMapArray + { + unsigned int& operator[](u32 i) + { + B3_ASSERT(i < count); + return values[i]; + } + + const unsigned int& operator[](u32 i) const + { + B3_ASSERT(i < count); + return values[i]; + } + + u32 count; + unsigned int* values; + }; + + u32 m_nodeCount; + b3SDFNodeArray* m_nodes; + + u32 m_cellCount; + b3SDFCellArray* m_cells; + + u32 m_cellMapCount; + b3SDFCellMapArray* m_cell_map; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/collision/shapes/sphere.h b/include/bounce/collision/shapes/sphere.h index b0bc4bb..873f917 100644 --- a/include/bounce/collision/shapes/sphere.h +++ b/include/bounce/collision/shapes/sphere.h @@ -27,7 +27,7 @@ struct b3Sphere b3Sphere() { } // - b3Sphere(const b3Vec3& v, float32 r) + b3Sphere(const b3Vec3& v, scalar r) { vertex = v; radius = r; @@ -37,7 +37,7 @@ struct b3Sphere ~b3Sphere() { } b3Vec3 vertex; - float32 radius; + scalar radius; const b3Vec3& GetVertex(u32 index) const; u32 GetSupportVertex(const b3Vec3& direction) const; diff --git a/include/bounce/collision/shapes/triangle_hull.h b/include/bounce/collision/shapes/triangle_hull.h index 92c67ef..b675461 100644 --- a/include/bounce/collision/shapes/triangle_hull.h +++ b/include/bounce/collision/shapes/triangle_hull.h @@ -37,20 +37,20 @@ struct b3TriangleHull : public b3Hull void Set(const b3Vec3& A, const b3Vec3& B, const b3Vec3& C) { - centroid = (A + B + C) / 3.0f; + centroid = (A + B + C) / scalar(3); triangleVertices[0] = A; triangleVertices[1] = B; triangleVertices[2] = C; // Each edge must be followed by its twin. - triangleEdges[0] = b3MakeEdge(0, 1, 0, 2); // Face 0 - Edge 0 - triangleEdges[2] = b3MakeEdge(1, 3, 0, 4); // Face 0 - Edge 1 - triangleEdges[4] = b3MakeEdge(2, 5, 0, 0); // Face 0 - Edge 2 + triangleEdges[0] = b3MakeEdge(0, 1, 0, 4, 2); // Face 0 - Edge 0 + triangleEdges[2] = b3MakeEdge(1, 3, 0, 0, 4); // Face 0 - Edge 1 + triangleEdges[4] = b3MakeEdge(2, 5, 0, 2, 0); // Face 0 - Edge 2 - triangleEdges[1] = b3MakeEdge(1, 0, 1, 3); // Face 1 - Edge 0 - triangleEdges[3] = b3MakeEdge(2, 2, 1, 5); // Face 1 - Edge 1 - triangleEdges[5] = b3MakeEdge(0, 4, 1, 1); // Face 1 - Edge 2 + triangleEdges[1] = b3MakeEdge(1, 0, 1, 3, 5); // Face 1 - Edge 0 + triangleEdges[3] = b3MakeEdge(2, 2, 1, 5, 1); // Face 1 - Edge 1 + triangleEdges[5] = b3MakeEdge(0, 4, 1, 1, 3); // Face 1 - Edge 2 triangleFaces[0].edge = 0; triangleFaces[1].edge = 1; diff --git a/include/bounce/collision/time_of_impact.h b/include/bounce/collision/time_of_impact.h new file mode 100644 index 0000000..755549b --- /dev/null +++ b/include/bounce/collision/time_of_impact.h @@ -0,0 +1,71 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_TIME_OF_IMPACT_H +#define B3_TIME_OF_IMPACT_H + +#include +#include +#include + +// Input parameters for b3TimeOfImpact +struct b3TOIInput +{ + b3GJKProxy proxyA; + b3GJKProxy proxyB; + b3Sweep sweepA; + b3Sweep sweepB; + scalar tMax; // sweep interval in [0, 1] +}; + +// Output parameters of b3TimeOfImpact +struct b3TOIOutput +{ + enum State + { + e_unknown, + e_failed, + e_overlapped, + e_touching, + e_separated + }; + + State state; + scalar t; + u32 iterations; +}; + +// Compute the time of impact between two shapes. +// This is represented as a fraction between [0, tMax]. +// Use b3GJK to compute the contact point and normal at the time of impact. +b3TOIOutput b3TimeOfImpact(const b3TOIInput& input); + +// Compute the time of impact between two shapes. +// This is represented as a fraction between [0, 1]. +// You must supply the linear displacements of each shape. +// Use b3GJK to compute the contact point and normal at the time of impact. +b3TOIOutput b3TimeOfImpact(const b3Transform& xf1, const b3GJKProxy& proxy1, const b3Vec3& d1, + const b3Transform& xf2, const b3GJKProxy& proxy2, const b3Vec3& d2, + u32 maxIterations = 20); + +// Compute the time of impact between two AABBs. +// This is represented as a fraction between [0, 1]. +// You must supply the linear displacements of each AABB center. +b3TOIOutput b3TimeOfImpact(const b3AABB& aabb1, const b3Vec3& d1, const b3AABB& aabb2, const b3Vec3& d2); + +#endif \ No newline at end of file diff --git a/include/bounce/collision/trees/dynamic_tree.h b/include/bounce/collision/trees/dynamic_tree.h index 969f46d..497b74c 100644 --- a/include/bounce/collision/trees/dynamic_tree.h +++ b/include/bounce/collision/trees/dynamic_tree.h @@ -20,29 +20,29 @@ #define B3_DYNAMIC_TREE_H #include -#include +#include #include #define B3_NULL_NODE_D (0xFFFFFFFF) // AABB tree for dynamic AABBs. -class b3DynamicTree +class b3DynamicTree { -public : +public: b3DynamicTree(); ~b3DynamicTree(); // Insert a node into the tree and return its ID. - u32 InsertNode(const b3AABB3& aabb, void* userData); - + u32 InsertNode(const b3AABB& aabb, void* userData); + // Remove a node from the tree. void RemoveNode(u32 proxyId); // Update a node AABB. - void UpdateNode(u32 proxyId, const b3AABB3& aabb); + void UpdateNode(u32 proxyId, const b3AABB& aabb); // Get the (fat) AABB of a given proxy. - const b3AABB3& GetAABB(u32 proxyId) const; + const b3AABB& GetAABB(u32 proxyId) const; // Get the data associated with a given proxy. void* GetUserData(u32 proxyId) const; @@ -53,8 +53,8 @@ public : // Keep reporting the client callback the AABBs that are overlapping with // the given AABB. The client callback must return true if the query // must be stopped or false to continue looking for more overlapping pairs. - template - void QueryAABB(T* callback, const b3AABB3& aabb) const; + template + void QueryAABB(T* callback, const b3AABB& aabb) const; // Keep reporting the client callback all AABBs that are overlapping with // the given ray. The client callback must return the new intersection fraction. @@ -67,23 +67,23 @@ public : // Draw this tree. void Draw() const; -private : - struct b3Node +private: + struct b3Node { // Is this node a leaf? - bool IsLeaf() const + bool IsLeaf() const { //A node is a leaf if child 2 == B3_NULL_NODE_D or height == 0. return child1 == B3_NULL_NODE_D; } // The fattened node AABB. - b3AABB3 aabb; + b3AABB aabb; // The associated user data. void* userData; - union + union { u32 parent; u32 next; @@ -96,18 +96,18 @@ private : // leaf if 0, free node if -1 i32 height; }; - + // Insert a node into the tree. void InsertLeaf(u32 node); - + // Remove a node from the tree. void RemoveLeaf(u32 node); // Rebuild the hierarchy starting from the given node. - void WalkBackNodeAndCombineVolumes(u32 node); - - // Find the best node that can be merged with a given AABB. - u32 FindBest(const b3AABB3& aabb) const; + void Refit(u32 node); + + // Pick the best node that can be merged with a given AABB. + u32 PickBest(const b3AABB& aabb) const; // Peel a node from the free list and insert into the node array. // Allocate a new node if necessary. The function returns the new node index. @@ -129,7 +129,7 @@ private : u32 m_freeList; }; -inline const b3AABB3& b3DynamicTree::GetAABB(u32 proxyId) const +inline const b3AABB& b3DynamicTree::GetAABB(u32 proxyId) const { B3_ASSERT(proxyId != B3_NULL_NODE_D && proxyId < m_nodeCapacity); return m_nodes[proxyId].aabb; @@ -149,12 +149,12 @@ inline bool b3DynamicTree::TestOverlap(u32 proxy1, u32 proxy2) const } template -inline void b3DynamicTree::QueryAABB(T* callback, const b3AABB3& aabb) const +inline void b3DynamicTree::QueryAABB(T* callback, const b3AABB& aabb) const { b3Stack stack; stack.Push(m_root); - while (stack.IsEmpty() == false) + while (stack.IsEmpty() == false) { u32 nodeIndex = stack.Top(); stack.Pop(); @@ -166,16 +166,16 @@ inline void b3DynamicTree::QueryAABB(T* callback, const b3AABB3& aabb) const const b3Node* node = m_nodes + nodeIndex; - if (b3TestOverlap(node->aabb, aabb) == true) + if (b3TestOverlap(node->aabb, aabb) == true) { - if (node->IsLeaf() == true) + if (node->IsLeaf() == true) { - if (callback->Report(nodeIndex) == false) + if (callback->Report(nodeIndex) == false) { return; } } - else + else { stack.Push(node->child1); stack.Push(node->child2); @@ -185,55 +185,142 @@ inline void b3DynamicTree::QueryAABB(T* callback, const b3AABB3& aabb) const } template -inline void b3DynamicTree::RayCast(T* callback, const b3RayCastInput& input) const +inline void b3DynamicTree::RayCast(T* callback, const b3RayCastInput& input) const { b3Vec3 p1 = input.p1; b3Vec3 p2 = input.p2; - b3Vec3 d = p2 - p1; - float32 maxFraction = input.maxFraction; + b3Vec3 r = p2 - p1; + B3_ASSERT(b3LengthSquared(r) > scalar(0)); + r.Normalize(); + + scalar maxFraction = input.maxFraction; + + // Build an AABB for the segment. + b3Vec3 q2; + b3AABB segmentAABB; + { + q2 = p1 + maxFraction * (p2 - p1); + segmentAABB.lowerBound = b3Min(p1, q2); + segmentAABB.upperBound = b3Max(p1, q2); + } - // Ensure non-degenerate segment. - B3_ASSERT(b3Dot(d, d) > B3_EPSILON * B3_EPSILON); + b3Vec3 e1 = b3Vec3_x; + b3Vec3 e2 = b3Vec3_y; + b3Vec3 e3 = b3Vec3_z; b3Stack stack; stack.Push(m_root); - while (stack.IsEmpty() == false) + while (stack.IsEmpty() == false) { u32 nodeIndex = stack.Top(); - + stack.Pop(); - if (nodeIndex == B3_NULL_NODE_D) + if (nodeIndex == B3_NULL_NODE_D) { continue; } const b3Node* node = m_nodes + nodeIndex; - float32 minFraction; - if (node->aabb.TestRay(minFraction, p1, p2, maxFraction) == true) + if (b3TestOverlap(segmentAABB, node->aabb) == false) { - if (node->IsLeaf() == true) - { - b3RayCastInput subInput; - subInput.p1 = input.p1; - subInput.p2 = input.p2; - subInput.maxFraction = maxFraction; + continue; + } + + // Separating axis for segment (Gino, p80). + b3Vec3 c = node->aabb.GetCenter(); + b3Vec3 h = node->aabb.GetExtents(); - float32 newFraction = callback->Report(subInput, nodeIndex); + b3Vec3 s = p1 - c; + b3Vec3 t = q2 - c; - if (newFraction == 0.0f) - { - // The client has stopped the query. - return; - } - } - else + // |sigma + tau| > |sigma - tau| + 2 * eta + scalar sigma_1 = s.x; + scalar tau_1 = t.x; + scalar eta_1 = h.x; + + scalar s1 = b3Abs(sigma_1 + tau_1) - (b3Abs(sigma_1 - tau_1) + scalar(2) * eta_1); + if (s1 > scalar(0)) + { + continue; + } + + scalar sigma_2 = s.y; + scalar tau_2 = t.y; + scalar eta_2 = h.y; + + scalar s2 = b3Abs(sigma_2 + tau_2) - (b3Abs(sigma_2 - tau_2) + scalar(2) * eta_2); + if (s2 > scalar(0)) + { + continue; + } + + scalar sigma_3 = s.z; + scalar tau_3 = t.z; + scalar eta_3 = h.z; + + scalar s3 = b3Abs(sigma_3 + tau_3) - (b3Abs(sigma_3 - tau_3) + scalar(2) * eta_3); + if (s3 > scalar(0)) + { + continue; + } + + // v = cross(ei, r) + // |dot(v, s)| > dot(|v|, h) + b3Vec3 v1 = b3Cross(e1, r); + b3Vec3 abs_v1 = b3Abs(v1); + scalar s4 = b3Abs(b3Dot(v1, s)) - b3Dot(abs_v1, h); + if (s4 > scalar(0)) + { + continue; + } + + b3Vec3 v2 = b3Cross(e2, r); + b3Vec3 abs_v2 = b3Abs(v2); + scalar s5 = b3Abs(b3Dot(v2, s)) - b3Dot(abs_v2, h); + if (s5 > scalar(0)) + { + continue; + } + + b3Vec3 v3 = b3Cross(e3, r); + b3Vec3 abs_v3 = b3Abs(v3); + scalar s6 = b3Abs(b3Dot(v3, s)) - b3Dot(abs_v3, h); + if (s6 > scalar(0)) + { + continue; + } + + if (node->IsLeaf() == true) + { + b3RayCastInput subInput; + subInput.p1 = input.p1; + subInput.p2 = input.p2; + subInput.maxFraction = maxFraction; + + scalar newMaxFraction = callback->Report(subInput, nodeIndex); + + if (newMaxFraction == scalar(0)) { - stack.Push(node->child1); - stack.Push(node->child2); + // The client has stopped the query. + return; } + + if (newMaxFraction > scalar(0)) + { + // Update the segment AABB. + maxFraction = newMaxFraction; + q2 = p1 + maxFraction * (p2 - p1); + segmentAABB.lowerBound = b3Min(p1, q2); + segmentAABB.upperBound = b3Max(p1, q2); + } + } + else + { + stack.Push(node->child1); + stack.Push(node->child2); } } } diff --git a/include/bounce/collision/trees/static_tree.h b/include/bounce/collision/trees/static_tree.h index 4a07ba1..c32b201 100644 --- a/include/bounce/collision/trees/static_tree.h +++ b/include/bounce/collision/trees/static_tree.h @@ -20,7 +20,7 @@ #define B3_STATIC_TREE_H #include -#include +#include #include #define B3_NULL_NODE_S (0xFFFFFFFF) @@ -33,10 +33,10 @@ public: ~b3StaticTree(); // Build this tree from a list of AABBs. - void Build(const b3AABB3* aabbs, u32 count); + void Build(const b3AABB* aabbs, u32 count); // Get the AABB of a given proxy. - const b3AABB3& GetAABB(u32 proxyId) const; + const b3AABB& GetAABB(u32 proxyId) const; // Get the user data associated with a given proxy. u32 GetUserData(u32 proxyId) const; @@ -45,7 +45,7 @@ public: // the given AABB. The client callback must return true if the query // must be stopped or false to continue looking for more overlapping pairs. template - void QueryAABB(T* callback, const b3AABB3& aabb) const; + void QueryAABB(T* callback, const b3AABB& aabb) const; // Report the client callback all AABBs that are overlapping with // the given ray. The client callback must return the new intersection fraction @@ -56,12 +56,13 @@ public: // Draw this tree. void Draw() const; + // Get the size in bytes of this tree. u32 GetSize() const; private : // A node in a static tree. struct b3Node { - b3AABB3 aabb; + b3AABB aabb; u32 child1; union { @@ -76,15 +77,18 @@ private : } }; - // - void Build(const b3AABB3* set, b3Node* node, u32* indices, u32 count, u32 minObjectsPerLeaf, u32 nodeCapacity, u32& leafCount, u32& internalCount); + // Build this tree recursively. + void RecurseBuild(const b3AABB* set, b3Node* node, u32* indices, u32 count, u32 minObjectsPerLeaf, u32 nodeCapacity, u32& leafCount, u32& internalCount); + + // The root of this tree. + u32 m_root; // The nodes of this tree stored in an array. u32 m_nodeCount; b3Node* m_nodes; }; -inline const b3AABB3& b3StaticTree::GetAABB(u32 proxyId) const +inline const b3AABB& b3StaticTree::GetAABB(u32 proxyId) const { B3_ASSERT(proxyId < m_nodeCount); return m_nodes[proxyId].aabb; @@ -98,17 +102,15 @@ inline u32 b3StaticTree::GetUserData(u32 proxyId) const } template -inline void b3StaticTree::QueryAABB(T* callback, const b3AABB3& aabb) const +inline void b3StaticTree::QueryAABB(T* callback, const b3AABB& aabb) const { if (m_nodeCount == 0) { return; } - u32 root = 0; - b3Stack stack; - stack.Push(root); + stack.Push(m_root); while (stack.IsEmpty() == false) { @@ -151,20 +153,32 @@ inline void b3StaticTree::RayCast(T* callback, const b3RayCastInput& input) cons b3Vec3 p1 = input.p1; b3Vec3 p2 = input.p2; - b3Vec3 d = p2 - p1; - float32 maxFraction = input.maxFraction; + b3Vec3 r = p2 - p1; + B3_ASSERT(b3LengthSquared(r) > scalar(0)); + r.Normalize(); - // Ensure non-degenerate segment. - B3_ASSERT(b3Dot(d, d) > B3_EPSILON * B3_EPSILON); + scalar maxFraction = input.maxFraction; - u32 root = 0; + // Build an AABB for the segment. + b3Vec3 q2; + b3AABB segmentAABB; + { + q2 = p1 + maxFraction * (p2 - p1); + segmentAABB.lowerBound = b3Min(p1, q2); + segmentAABB.upperBound = b3Max(p1, q2); + } + + b3Vec3 e1 = b3Vec3_x; + b3Vec3 e2 = b3Vec3_y; + b3Vec3 e3 = b3Vec3_z; b3Stack stack; - stack.Push(root); - - while (stack.IsEmpty() == false) + stack.Push(m_root); + + while (stack.IsEmpty() == false) { - u32 nodeIndex = stack.Top(); + u32 nodeIndex = stack.Top(); + stack.Pop(); if (nodeIndex == B3_NULL_NODE_S) @@ -174,29 +188,103 @@ inline void b3StaticTree::RayCast(T* callback, const b3RayCastInput& input) cons const b3Node* node = m_nodes + nodeIndex; - float32 minFraction; - if (node->aabb.TestRay(minFraction, p1, p2, maxFraction) == true) + if (b3TestOverlap(segmentAABB, node->aabb) == false) { - if (node->IsLeaf() == true) - { - b3RayCastInput subInput; - subInput.p1 = input.p1; - subInput.p2 = input.p2; - subInput.maxFraction = maxFraction; + continue; + } - float32 newFraction = callback->Report(subInput, nodeIndex); + // Separating axis for segment (Gino, p80). + b3Vec3 c = node->aabb.GetCenter(); + b3Vec3 h = node->aabb.GetExtents(); - if (newFraction == 0.0f) - { - // The client has stopped the query. - return; - } - } - else + b3Vec3 s = p1 - c; + b3Vec3 t = q2 - c; + + // |sigma + tau| > |sigma - tau| + 2 * eta + scalar sigma_1 = s.x; + scalar tau_1 = t.x; + scalar eta_1 = h.x; + + scalar s1 = b3Abs(sigma_1 + tau_1) - (b3Abs(sigma_1 - tau_1) + scalar(2) * eta_1); + if (s1 > scalar(0)) + { + continue; + } + + scalar sigma_2 = s.y; + scalar tau_2 = t.y; + scalar eta_2 = h.y; + + scalar s2 = b3Abs(sigma_2 + tau_2) - (b3Abs(sigma_2 - tau_2) + scalar(2) * eta_2); + if (s2 > scalar(0)) + { + continue; + } + + scalar sigma_3 = s.z; + scalar tau_3 = t.z; + scalar eta_3 = h.z; + + scalar s3 = b3Abs(sigma_3 + tau_3) - (b3Abs(sigma_3 - tau_3) + scalar(2) * eta_3); + if (s3 > scalar(0)) + { + continue; + } + + // v = cross(ei, r) + // |dot(v, s)| > dot(|v|, h) + b3Vec3 v1 = b3Cross(e1, r); + b3Vec3 abs_v1 = b3Abs(v1); + scalar s4 = b3Abs(b3Dot(v1, s)) - b3Dot(abs_v1, h); + if (s4 > scalar(0)) + { + continue; + } + + b3Vec3 v2 = b3Cross(e2, r); + b3Vec3 abs_v2 = b3Abs(v2); + scalar s5 = b3Abs(b3Dot(v2, s)) - b3Dot(abs_v2, h); + if (s5 > scalar(0)) + { + continue; + } + + b3Vec3 v3 = b3Cross(e3, r); + b3Vec3 abs_v3 = b3Abs(v3); + scalar s6 = b3Abs(b3Dot(v3, s)) - b3Dot(abs_v3, h); + if (s6 > scalar(0)) + { + continue; + } + + if (node->IsLeaf() == true) + { + b3RayCastInput subInput; + subInput.p1 = input.p1; + subInput.p2 = input.p2; + subInput.maxFraction = maxFraction; + + scalar newMaxFraction = callback->Report(subInput, nodeIndex); + + if (newMaxFraction == scalar(0)) { - stack.Push(node->child1); - stack.Push(node->child2); + // The client has stopped the query. + return; } + + if (newMaxFraction > scalar(0)) + { + // Update the segment AABB. + maxFraction = newMaxFraction; + q2 = p1 + maxFraction * (p2 - p1); + segmentAABB.lowerBound = b3Min(p1, q2); + segmentAABB.upperBound = b3Max(p1, q2); + } + } + else + { + stack.Push(node->child1); + stack.Push(node->child2); } } } diff --git a/include/bounce/common/draw.h b/include/bounce/common/draw.h index bc5f1d9..b915ab8 100644 --- a/include/bounce/common/draw.h +++ b/include/bounce/common/draw.h @@ -20,16 +20,16 @@ #define B3_DRAW_H #include -#include +#include // Color channels used by the debug draw interface. struct b3Color { b3Color() { } - b3Color(float32 R, float32 G, float32 B, float32 A = 1.0f) : r(R), g(G), b(B), a(A) { } + b3Color(scalar R, scalar G, scalar B, scalar A = scalar(1)) : r(R), g(G), b(B), a(A) { } - float32 r, g, b, a; + scalar r, g, b, a; }; // Color pallete commonly used by the debug draw interface. @@ -72,7 +72,7 @@ public : void AppendFlags(u32 flags); // Draw a point. - virtual void DrawPoint(const b3Vec3& p, float32 size, const b3Color& color) = 0; + virtual void DrawPoint(const b3Vec3& p, scalar size, const b3Color& color) = 0; // Draw a line segment. virtual void DrawSegment(const b3Vec3& p1, const b3Vec3& p2, const b3Color& color) = 0; @@ -90,31 +90,31 @@ public : virtual void DrawSolidPolygon(const b3Vec3& normal, const b3Vec3* vertices, u32 count, const b3Color& color) = 0; // Draw a circle with center, normal, and radius. - virtual void DrawCircle(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) = 0; + virtual void DrawCircle(const b3Vec3& normal, const b3Vec3& center, scalar radius, const b3Color& color) = 0; // Draw a solid circle with center, normal, and radius. - virtual void DrawSolidCircle(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) = 0; + virtual void DrawSolidCircle(const b3Vec3& normal, const b3Vec3& center, scalar radius, const b3Color& color) = 0; // Draw a plane with center, normal and radius. - virtual void DrawPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) = 0; + virtual void DrawPlane(const b3Vec3& normal, const b3Vec3& center, scalar radius, const b3Color& color) = 0; // Draw a solid plane with center, normal and radius. - virtual void DrawSolidPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) = 0; + virtual void DrawSolidPlane(const b3Vec3& normal, const b3Vec3& center, scalar radius, const b3Color& color) = 0; // Draw a sphere with center, and radius. - virtual void DrawSphere(const b3Vec3& center, float32 radius, const b3Color& color) = 0; + virtual void DrawSphere(const b3Vec3& center, scalar radius, const b3Color& color) = 0; // Draw a solid sphere with center, radius, and rotation. - virtual void DrawSolidSphere(const b3Vec3& center, float32 radius, const b3Mat33& rotation, const b3Color& color) = 0; + virtual void DrawSolidSphere(const b3Vec3& center, scalar radius, const b3Quat& rotation, const b3Color& color) = 0; // Draw a capsule with segment, and radius. - virtual void DrawCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color) = 0; + virtual void DrawCapsule(const b3Vec3& p1, const b3Vec3& p2, scalar radius, const b3Color& color) = 0; // Draw a solid capsule with segment, radius, and rotation. - virtual void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Mat33& rotation, const b3Color& color) = 0; + virtual void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, scalar radius, const b3Quat& rotation, const b3Color& color) = 0; // Draw a AABB. - virtual void DrawAABB(const b3AABB3& aabb, const b3Color& color) = 0; + virtual void DrawAABB(const b3AABB& aabb, const b3Color& color) = 0; // Draw a transform. virtual void DrawTransform(const b3Transform& xf) = 0; diff --git a/include/bounce/common/geometry.h b/include/bounce/common/geometry.h index 50adc14..3da16f9 100644 --- a/include/bounce/common/geometry.h +++ b/include/bounce/common/geometry.h @@ -37,7 +37,7 @@ struct b3Ray3 b3Vec3 direction; b3Vec3 origin; - float32 fraction; + scalar fraction; }; // A plane in constant normal form. @@ -48,7 +48,7 @@ struct b3Plane b3Plane() { } // Set this plane from a normal and a signed distance from its origin. - b3Plane(const b3Vec3& _normal, float32 _offset) + b3Plane(const b3Vec3& _normal, scalar _offset) { normal = _normal; offset = _offset; @@ -70,14 +70,14 @@ struct b3Plane } b3Vec3 normal; - float32 offset; + scalar offset; }; // Transform a plane by a given frame. inline b3Plane b3Mul(const b3Transform& T, const b3Plane& plane) { b3Vec3 normal = b3Mul(T.rotation, plane.normal); - return b3Plane(normal, plane.offset + b3Dot(normal, T.position)); + return b3Plane(normal, plane.offset + b3Dot(normal, T.translation)); } // Transform a plane by a given frame. @@ -87,7 +87,7 @@ inline b3Plane operator*(const b3Transform& T, const b3Plane& plane) } // Compute the distance between a point and a plane. -inline float32 b3Distance(const b3Vec3& P, const b3Plane& plane) +inline scalar b3Distance(const b3Vec3& P, const b3Plane& plane) { return b3Dot(plane.normal, P) - plane.offset; } @@ -95,14 +95,14 @@ inline float32 b3Distance(const b3Vec3& P, const b3Plane& plane) // Project a point onto a normal plane. inline b3Vec3 b3ClosestPointOnPlane(const b3Vec3& P, const b3Plane& plane) { - float32 fraction = b3Distance(P, plane); + scalar fraction = b3Distance(P, plane); return P - fraction * plane.normal; } // Convert a point Q from Cartesian coordinates to Barycentric coordinates (u, v) // with respect to a segment AB. // The last output value is the divisor. -inline void b3BarycentricCoordinates(float32 out[3], +inline void b3BarycentricCoordinates(scalar out[3], const b3Vec3& A, const b3Vec3& B, const b3Vec3& Q) { @@ -110,16 +110,17 @@ inline void b3BarycentricCoordinates(float32 out[3], b3Vec3 QA = A - Q; b3Vec3 QB = B - Q; - float32 divisor = b3Dot(AB, AB); + scalar divisor = b3Dot(AB, AB); out[0] = b3Dot(QB, AB); out[1] = -b3Dot(QA, AB); out[2] = divisor; } + // Convert a point Q from Cartesian coordinates to Barycentric coordinates (u, v, w) // with respect to a triangle ABC. // The last output value is the divisor. -inline void b3BarycentricCoordinates(float32 out[4], +inline void b3BarycentricCoordinates(scalar out[4], const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, const b3Vec3& Q) { @@ -136,7 +137,7 @@ inline void b3BarycentricCoordinates(float32 out[4], b3Vec3 AB_x_AC = b3Cross(AB, AC); - //float32 divisor = b3Dot(AB_x_AC, AB_x_AC); + //scalar divisor = b3Dot(AB_x_AC, AB_x_AC); out[0] = b3Dot(QB_x_QC, AB_x_AC); out[1] = b3Dot(QC_x_QA, AB_x_AC); @@ -147,7 +148,7 @@ inline void b3BarycentricCoordinates(float32 out[4], // Convert a point Q from Cartesian coordinates to Barycentric coordinates (u, v, w, x) // with respect to a tetrahedron ABCD. // The last output value is the (positive) divisor. -inline void b3BarycentricCoordinates(float32 out[5], +inline void b3BarycentricCoordinates(scalar out[5], const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, const b3Vec3& D, const b3Vec3& Q) { @@ -160,8 +161,8 @@ inline void b3BarycentricCoordinates(float32 out[5], b3Vec3 QC = C - Q; b3Vec3 QD = D - Q; - float32 divisor = b3Det(AB, AC, AD); - float32 sign = b3Sign(divisor); + scalar divisor = b3Det(AB, AC, AD); + scalar sign = b3Sign(divisor); out[0] = sign * b3Det(QB, QC, QD); out[1] = sign * b3Det(QA, QD, QC); @@ -173,22 +174,22 @@ inline void b3BarycentricCoordinates(float32 out[5], // Project a point onto a segment AB. inline b3Vec3 b3ClosestPointOnSegment(const b3Vec3& P, const b3Vec3& A, const b3Vec3& B) { - float32 wAB[3]; + scalar wAB[3]; b3BarycentricCoordinates(wAB, A, B, P); - if (wAB[1] <= 0.0f) + if (wAB[1] <= scalar(0)) { return A; } - if (wAB[0] <= 0.0f) + if (wAB[0] <= scalar(0)) { return B; } - float32 s = 1.0f / wAB[2]; - float32 wA = s * wAB[0]; - float32 wB = s * wAB[1]; + scalar s = scalar(1) / wAB[2]; + scalar wA = s * wAB[0]; + scalar wB = s * wAB[1]; return wA * A + wB * B; } diff --git a/include/bounce/common/math/mat.h b/include/bounce/common/math/mat.h index 0f9cd93..1858e9a 100644 --- a/include/bounce/common/math/mat.h +++ b/include/bounce/common/math/mat.h @@ -134,7 +134,7 @@ struct b3Mat43 }; // a * 3x4 = 3x4 -inline b3Mat34 operator*(float32 s, const b3Mat34& A) +inline b3Mat34 operator*(scalar s, const b3Mat34& A) { return b3Mat34(s * A.x, s * A.y, s * A.z, s * A.w); } @@ -152,7 +152,7 @@ inline b3Vec3 operator*(const b3Mat34& A, const b3Vec4& v) } // 1x3 * 3x1 = 1x1 -inline float32 operator*(const b3Vec3& A, const b3Vec3& B) +inline scalar operator*(const b3Vec3& A, const b3Vec3& B) { return A.x * B.x + A.y * B.y + A.z * B.z; } @@ -181,13 +181,13 @@ inline b3Vec3 operator*(const b3Mat32& A, const b3Vec2& B) return B.x * A.x + B.y * A.y; } -// 2x3 * 2x1 = 2x1 +// 2x3 * 3x1 = 2x1 inline b3Vec2 operator*(const b3Mat23& A, const b3Vec3& B) { return B.x * A.x + B.y * A.y + B.z * A.z; } -// 2x3 * 2x2 = 2x2 +// 2x3 * 3x2 = 2x2 inline b3Mat22 operator*(const b3Mat23& A, const b3Mat32& B) { return b3Mat22(A * B.x, A * B.y); @@ -223,7 +223,7 @@ inline b3Mat23 operator*(const b3Mat24& A, const b3Mat43& B) return b3Mat23(A * B.x, A * B.y, A * B.z); } -// 2x4 * 2x4 = 2x4 +// 2x4 * 4x4 = 2x4 inline b3Mat24 operator*(const b3Mat24& A, const b3Mat44& B) { return b3Mat24(A * B.x, A * B.y, A * B.z, A * B.w); diff --git a/include/bounce/common/math/mat22.h b/include/bounce/common/math/mat22.h index b976cf1..6aabfc7 100644 --- a/include/bounce/common/math/mat22.h +++ b/include/bounce/common/math/mat22.h @@ -29,6 +29,30 @@ struct b3Mat22 // Set this matrix from two vectors. b3Mat22(const b3Vec2& _x, const b3Vec2& _y) : x(_x), y(_y) { } + + // Read an indexed column vector from this matrix. + const b3Vec2& operator[](u32 i) const + { + return (&x)[i]; + } + + // Write an indexed column vector to this matrix. + b3Vec2& operator[](u32 i) + { + return (&x)[i]; + } + + // Read an indexed element from this matrix. + scalar operator()(u32 i, u32 j) const + { + return (&x.x)[i + 2 * j]; + } + + // Write an indexed element from this matrix. + scalar& operator()(u32 i, u32 j) + { + return (&x.x)[i + 2 * j]; + } // Set this matrix to the zero matrix. void SetZero() @@ -37,6 +61,13 @@ struct b3Mat22 y.SetZero(); } + // Set this matrix to the identity matrix. + void SetIdentity() + { + x.Set(scalar(1), scalar(0)); + y.Set(scalar(0), scalar(1)); + } + // Solve Ax = b. // It doesn't compute the inverse. // Therefore, is more efficient. @@ -64,15 +95,42 @@ inline b3Mat22 operator+(const b3Mat22& A, const b3Mat22& B) return b3Mat22(A.x + B.x, A.y + B.y); } +// Subtract two matrices. +inline b3Mat22 operator-(const b3Mat22& A, const b3Mat22& B) +{ + return b3Mat22(A.x - B.x, A.y - B.y); +} + +// Multiply two matrices. +inline b3Mat22 operator*(const b3Mat22& A, const b3Mat22& B) +{ + return b3Mat22(A * B.x, A * B.y); +} + +// Multiply a scalar times a matrix. +inline b3Mat22 operator*(scalar s, const b3Mat22& A) +{ + return b3Mat22(s * A.x, s * A.y); +} + // Multiply a matrix times a vector. inline b3Vec2 b3Mul(const b3Mat22& A, const b3Vec2& v) { return v.x * A.x + v.y * A.y; } +// Transpose a matrix. +inline b3Mat22 b3Transpose(const b3Mat22& A) +{ + return b3Mat22( + b3Vec2(A.x.x, A.y.x), + b3Vec2(A.x.y, A.y.y) + ); +} + // Invert a matrix. // If the matrix determinant is zero this returns // the zero matrix. b3Mat22 b3Inverse(const b3Mat22& A); -#endif +#endif \ No newline at end of file diff --git a/include/bounce/common/math/mat33.h b/include/bounce/common/math/mat33.h index 89da221..0a2f190 100644 --- a/include/bounce/common/math/mat33.h +++ b/include/bounce/common/math/mat33.h @@ -43,13 +43,13 @@ struct b3Mat33 } // Read an indexed element from this matrix. - float32 operator()(u32 i, u32 j) const + scalar operator()(u32 i, u32 j) const { return (&x.x)[i + 3 * j]; } // Write an indexed element from this matrix. - float32& operator()(u32 i, u32 j) + scalar& operator()(u32 i, u32 j) { return (&x.x)[i + 3 * j]; } @@ -81,9 +81,9 @@ struct b3Mat33 // Set this matrix to the identity matrix. void SetIdentity() { - x.Set(1.0f, 0.0f, 0.0f); - y.Set(0.0f, 1.0f, 0.0f); - z.Set(0.0f, 0.0f, 1.0f); + x.Set(scalar(1), scalar(0), scalar(0)); + y.Set(scalar(0), scalar(1), scalar(0)); + z.Set(scalar(0), scalar(0), scalar(1)); } // Solve Ax = b. @@ -112,7 +112,7 @@ inline b3Mat33 operator-(const b3Mat33& A, const b3Mat33& B) } // Multiply a scalar times a matrix. -inline b3Mat33 operator*(float32 s, const b3Mat33& A) +inline b3Mat33 operator*(scalar s, const b3Mat33& A) { return b3Mat33(s * A.x, s * A.y, s * A.z); } @@ -120,7 +120,7 @@ inline b3Mat33 operator*(float32 s, const b3Mat33& A) // Negate a matrix. inline b3Mat33 operator-(const b3Mat33& A) { - return -1.0f * A; + return scalar(-1) * A; } // Multiply a matrix times a vector. If the matrix @@ -168,6 +168,12 @@ inline b3Mat33 b3MulT(const b3Mat33& A, const b3Mat33& B) b3Vec3(b3Dot(A.x, B.z), b3Dot(A.y, B.z), b3Dot(A.z, B.z))); } +// Return the absolute matrix of a given matrix. +inline b3Mat33 b3Abs(const b3Mat33& A) +{ + return b3Mat33(b3Abs(A.x), b3Abs(A.y), b3Abs(A.z)); +} + // Transpose a matrix. inline b3Mat33 b3Transpose(const b3Mat33& A) { @@ -179,23 +185,21 @@ inline b3Mat33 b3Transpose(const b3Mat33& A) } // Uniform scale matrix. -inline b3Mat33 b3Diagonal(float32 s) +inline b3Mat33 b3Diagonal(scalar s) { return b3Mat33( - b3Vec3(s, 0.0f, 0.0f), - b3Vec3(0.0f, s, 0.0f), - b3Vec3(0.0f, 0.0f, s) - ); + b3Vec3(s, scalar(0), scalar(0)), + b3Vec3(scalar(0), s, scalar(0)), + b3Vec3(scalar(0), scalar(0), s)); } // Uniform or non-uniform scale matrix. -inline b3Mat33 b3Diagonal(float32 x, float32 y, float32 z) +inline b3Mat33 b3Diagonal(scalar x, scalar y, scalar z) { return b3Mat33( - b3Vec3(x, 0.0f, 0.0f), - b3Vec3(0.0f, y, 0.0f), - b3Vec3(0.0f, 0.0f, z) - ); + b3Vec3(x, scalar(0), scalar(0)), + b3Vec3(scalar(0), y, scalar(0)), + b3Vec3(scalar(0), scalar(0), z)); } // Invert a matrix. @@ -212,14 +216,13 @@ b3Mat33 b3SymInverse(const b3Mat33& A); inline b3Mat33 b3Skew(const b3Vec3& v) { return b3Mat33( - b3Vec3(0.0f, v.z, -v.y), - b3Vec3(-v.z, 0.0f, v.x), - b3Vec3(v.y, -v.x, 0.0f) - ); + b3Vec3(scalar(0), v.z, -v.y), + b3Vec3(-v.z, scalar(0), v.x), + b3Vec3(v.y, -v.x, scalar(0))); } // Compute the dot product of two vectors. -inline float32 b3Inner(const b3Vec3& a, const b3Vec3& b) +inline scalar b3Inner(const b3Vec3& a, const b3Vec3& b) { return b3Dot(a, b); } @@ -235,14 +238,19 @@ inline b3Mat33 b3Outer(const b3Vec3& a, const b3Vec3& b) // The vector must be normalized. inline void b3ComputeBasis(const b3Vec3& a, b3Vec3& b, b3Vec3& c) { - // https://box2d.org/2014/02/computing-a-basis/ - if (b3Abs(a.x) >= float32(0.57735027)) + // https://box2d.org/2014/02/computing-a-basis/, (Erin) + // Suppose vector a has all equal components and is a unit vector : a = (s, s, s) + // Then 3*s*s = 1, s = sqrt(1/3). + // This means that at least one component of a unit vector must be greater or equal to s. + static const scalar sqrt_inv3 = b3Sqrt(scalar(1) / scalar(3)); + + if (b3Abs(a.x) >= sqrt_inv3) { - b.Set(a.y, -a.x, 0.0f); + b.Set(a.y, -a.x, scalar(0)); } else { - b.Set(0.0f, a.z, -a.y); + b.Set(scalar(0), a.z, -a.y); } b.Normalize(); @@ -250,41 +258,41 @@ inline void b3ComputeBasis(const b3Vec3& a, b3Vec3& b, b3Vec3& c) } // Rotation about the x-axis. -inline b3Mat33 b3Mat33RotationX(float32 angle) +inline b3Mat33 b3Mat33RotationX(scalar angle) { - float32 c = cos(angle); - float32 s = sin(angle); + scalar c = cos(angle); + scalar s = sin(angle); b3Mat33 R; - R.x.Set(1.0f, 0.0f, 0.0f); - R.y.Set(0.0f, c, s); - R.z.Set(0.0f, -s, c); + R.x.Set(scalar(1), scalar(0), scalar(0)); + R.y.Set(scalar(0), c, s); + R.z.Set(scalar(0), -s, c); return R; } // Rotation about the y-axis. -inline b3Mat33 b3Mat33RotationY(float32 angle) +inline b3Mat33 b3Mat33RotationY(scalar angle) { - float32 c = cos(angle); - float32 s = sin(angle); + scalar c = cos(angle); + scalar s = sin(angle); b3Mat33 R; - R.x.Set(c, 0.0f, -s); - R.y.Set(0.0f, 1.0f, 0.0f); - R.z.Set(s, 0.0f, c); + R.x.Set(c, scalar(0), -s); + R.y.Set(scalar(0), scalar(1), scalar(0)); + R.z.Set(s, scalar(0), c); return R; } // Rotation about the z-axis. -inline b3Mat33 b3Mat33RotationZ(float32 angle) +inline b3Mat33 b3Mat33RotationZ(scalar angle) { - float32 c = cos(angle); - float32 s = sin(angle); + scalar c = cos(angle); + scalar s = sin(angle); b3Mat33 R; - R.x.Set(c, s, 0.0f); - R.y.Set(-s, c, 0.0f); - R.z.Set(0.0f, 0.0f, 1.0f); + R.x.Set(c, s, scalar(0)); + R.y.Set(-s, c, scalar(0)); + R.z.Set(scalar(0), scalar(0), scalar(1)); return R; } diff --git a/include/bounce/common/math/mat44.h b/include/bounce/common/math/mat44.h index 57691e2..d528835 100644 --- a/include/bounce/common/math/mat44.h +++ b/include/bounce/common/math/mat44.h @@ -46,7 +46,7 @@ struct b3Mat44 }; // -inline b3Mat44 operator*(float32 s, const b3Mat44& A) +inline b3Mat44 operator*(scalar s, const b3Mat44& A) { return b3Mat44(s * A.x, s * A.y, s * A.z, s * A.w); } @@ -63,4 +63,9 @@ inline b3Mat44 operator*(const b3Mat44& A, const b3Mat44& B) return b3Mat44(A * B.x, A * B.y, A * B.z, A * B.w); } +inline b3Mat44 operator-(const b3Mat44& A) +{ + return scalar(-1) * A; +} + #endif \ No newline at end of file diff --git a/include/bounce/common/math/math.h b/include/bounce/common/math/math.h index 3521d30..d669a7d 100644 --- a/include/bounce/common/math/math.h +++ b/include/bounce/common/math/math.h @@ -23,23 +23,22 @@ #include // For abs() with integral types #include -inline bool b3IsInf(float32 x) +inline bool b3IsInf(scalar x) { return std::isinf(x); } -inline bool b3IsNaN(float32 x) +inline bool b3IsNaN(scalar x) { return std::isnan(x); } -inline bool b3IsValid(float32 fx) +inline bool b3IsValid(scalar fx) { - i32 ix = *(i32*)(&fx); - return (ix & 0x7F800000) != 0x7F800000; + return std::isfinite(fx); } -inline float32 b3Sqrt(float32 x) +inline scalar b3Sqrt(scalar x) { return std::sqrt(x); } @@ -83,7 +82,7 @@ inline T b3Sign(T x) } template -inline u32 b3UniqueCount(T* V, u32 N) +inline u32 b3UniqueCount(const T* V, u32 N) { u32 count = 0; for (u32 i = 0; i < N; ++i) @@ -104,4 +103,49 @@ inline u32 b3UniqueCount(T* V, u32 N) return count; } -#endif +// Multiply two matrices stored in column-major order. +// C = A * B +inline void b3Mul(scalar* C, const scalar* A, u32 AM, u32 AN, const scalar* B, u32 BM, u32 BN) +{ + B3_ASSERT(AN == BM); + + for (u32 i = 0; i < AM; ++i) + { + for (u32 j = 0; j < BN; ++j) + { + C[i + AM * j] = scalar(0); + + for (u32 k = 0; k < AN; ++k) + { + C[i + AM * j] += A[i + AM * k] * B[k + BM * j]; + } + } + } +} + +// Return the transpose of a given matrix stored in column-major order. +// B = A^T +inline void b3Transpose(scalar* B, const scalar* A, u32 AM, u32 AN) +{ + for (u32 i = 0; i < AM; ++i) + { + for (u32 j = 0; j < AN; ++j) + { + B[j + AN * i] = A[i + AM * j]; + } + } +} + +// Return the lenght of a given vector. +// ||v|| +inline scalar b3Length(const scalar* v, u32 n) +{ + scalar result(0); + for (u32 i = 0; i < n; ++i) + { + result += v[i] * v[i]; + } + return b3Sqrt(result); +} + +#endif \ No newline at end of file diff --git a/include/bounce/common/math/quat.h b/include/bounce/common/math/quat.h index a027a99..284c6ef 100644 --- a/include/bounce/common/math/quat.h +++ b/include/bounce/common/math/quat.h @@ -29,116 +29,165 @@ struct b3Quat b3Quat() { } // Set this quaternion from four values. - b3Quat(float32 _x, float32 _y, float32 _z, float32 _w) : x(_x), y(_y), z(_z), w(_w) { } + b3Quat(scalar _x, scalar _y, scalar _z, scalar _s) : v(_x, _y, _z), s(_s) { } - // Set this quaternion from an axis and an angle - // of rotation about the axis. - b3Quat(const b3Vec3& axis, float32 angle) - { - Set(axis, angle); - } - - // Write an indexed value to this quaternion. - float32& operator[](u32 i) - { - return (&x)[i]; - } - - // Read an indexed value from this quaternion. - float32 operator[](u32 i) const - { - return (&x)[i]; - } - // Add a quaternion to this quaternion. void operator+=(const b3Quat& q) { - x += q.x; - y += q.y; - z += q.z; - w += q.w; + v.x += q.v.x; + v.y += q.v.y; + v.z += q.v.z; + s += q.s; } // Subtract a quaternion from this quaternion. void operator-=(const b3Quat& q) { - x -= q.x; - y -= q.y; - z -= q.z; - w -= q.w; + v.x -= q.v.x; + v.y -= q.v.y; + v.z -= q.v.z; + s -= q.s; } // Set this quaternion to identity. void SetIdentity() { - x = y = z = 0.0f; - w = 1.0f; + v.x = v.y = v.z = scalar(0); + s = scalar(1); } // Set this quaternion from four values. - void Set(float32 _x, float32 _y, float32 _z, float32 _w) + void Set(scalar _x, scalar _y, scalar _z, scalar _s) { - x = _x; - y = _y; - z = _z; - w = _w; + v.x = _x; + v.y = _y; + v.z = _z; + s = _s; } // Convert this quaternion to the unit quaternion. Return the length. - float32 Normalize() + scalar Normalize() { - float32 length = b3Sqrt(x * x + y * y + z * z + w * w); - if (length > B3_EPSILON) + scalar len = b3Sqrt(v.x * v.x + v.y * v.y + v.z * v.z + s * s); + if (len > B3_EPSILON) { - float32 s = 1.0f / length; - x *= s; - y *= s; - z *= s; - w *= s; + scalar inv_len = scalar(1) / len; + v *= inv_len; + s *= inv_len; } - return length; + return len; } // Set this quaternion from an axis and full angle // of rotation about the axis. - void Set(const b3Vec3& axis, float32 angle) + void SetAxisAngle(const b3Vec3& axis, scalar angle) { - // half angle - float32 theta = 0.5f * angle; + scalar theta = scalar(0.5) * angle; - float32 sine = sin(theta); - x = sine * axis.x; - y = sine * axis.y; - z = sine * axis.z; - - w = cos(theta); + v = sin(theta) * axis; + s = cos(theta); } // If this quaternion represents an orientation output // the axis and angle of rotation about the axis. - void GetAxisAngle(b3Vec3* axis, float32* angle) const + void GetAxisAngle(b3Vec3* axis, scalar* angle) const { // sin^2 = 1 - cos^2 // sin = sqrt( sin^2 ) = ||v|| // axis = v / sin - b3Vec3 v(x, y, z); - float32 sine = b3Length(v); + scalar sine = b3Length(v); axis->SetZero(); if (sine > B3_EPSILON) { - float32 s = 1.0f / sine; - *axis = s * v; + scalar inv_sine = scalar(1) / sine; + *axis = inv_sine * v; } - + // cosine check - float32 cosine = b3Clamp(w, -1.0f, 1.0f); + scalar cosine = b3Clamp(s, scalar(-1), scalar(1)); + // half angle - float32 theta = acos(cosine); + scalar theta = acos(cosine); + // full angle - *angle = 2.0f * theta; + *angle = scalar(2) * theta; } - float32 x, y, z, w; + // Get the x axis. + b3Vec3 GetXAxis() const + { + scalar x = v.x, y = v.y, z = v.z, w = s; + + scalar x2 = x + x, y2 = y + y, z2 = z + z; + scalar xx = x * x2, xy = x * y2, xz = x * z2; + scalar yy = y * y2, yz = y * z2, zz = z * z2; + scalar wx = w * x2, wy = w * y2, wz = w * z2; + + return b3Vec3(scalar(1) - (yy + zz), xy + wz, xz - wy); + } + + // Get the y axis. + b3Vec3 GetYAxis() const + { + scalar x = v.x, y = v.y, z = v.z, w = s; + + scalar x2 = x + x, y2 = y + y, z2 = z + z; + scalar xx = x * x2, xy = x * y2, xz = x * z2; + scalar yy = y * y2, yz = y * z2, zz = z * z2; + scalar wx = w * x2, wy = w * y2, wz = w * z2; + + return b3Vec3(xy - wz, scalar(1) - (xx + zz), yz + wx); + } + + // Get the z axis. + b3Vec3 GetZAxis() const + { + scalar x = v.x, y = v.y, z = v.z, w = s; + + scalar x2 = x + x, y2 = y + y, z2 = z + z; + scalar xx = x * x2, xy = x * y2, xz = x * z2; + scalar yy = y * y2, yz = y * z2, zz = z * z2; + scalar wx = w * x2, wy = w * y2, wz = w * z2; + + return b3Vec3(xz + wy, yz - wx, scalar(1) - (xx + yy)); + } + + // Get the x, y, z axes. + b3Mat33 GetXYZAxes() const + { + scalar x = v.x, y = v.y, z = v.z, w = s; + + scalar x2 = x + x, y2 = y + y, z2 = z + z; + scalar xx = x * x2, xy = x * y2, xz = x * z2; + scalar yy = y * y2, yz = y * z2, zz = z * z2; + scalar wx = w * x2, wy = w * y2, wz = w * z2; + + return b3Mat33( + b3Vec3(scalar(1) - (yy + zz), xy + wz, xz - wy), + b3Vec3(xy - wz, scalar(1) - (xx + zz), yz + wx), + b3Vec3(xz + wy, yz - wx, scalar(1) - (xx + yy))); + } + + // Get the angle about the x axis. + scalar GetXAngle() const + { + return atan2(v.x, s); + } + + // Get the angle about the y axis. + scalar GetYAngle() const + { + return atan2(v.y, s); + } + + // Get the angle about the z axis. + scalar GetZAngle() const + { + return atan2(v.z, s); + } + + b3Vec3 v; + scalar s; }; // Identity quaternion @@ -147,35 +196,34 @@ extern const b3Quat b3Quat_identity; // Add two quaternions. inline b3Quat operator+(const b3Quat& a, const b3Quat& b) { - return b3Quat(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); + return b3Quat(a.v.x + b.v.x, a.v.y + b.v.y, a.v.z + b.v.z, a.s + b.s); } // Subtract two quaternions. inline b3Quat operator-(const b3Quat& a, const b3Quat& b) { - return b3Quat(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); + return b3Quat(a.v.x - b.v.x, a.v.y - b.v.y, a.v.z - b.v.z, a.s - b.s); } // Multiply a quaternion by a scalar. -inline b3Quat operator*(float32 s, const b3Quat& q) +inline b3Quat operator*(scalar s, const b3Quat& q) { - return b3Quat(s * q.x, s * q.y, s * q.z, s * q.w); + return b3Quat(s * q.v.x, s * q.v.y, s * q.v.z, s * q.s); } // Negate a quaternion. inline b3Quat operator-(const b3Quat& q) { - return b3Quat(-q.x, -q.y, -q.z, -q.w); + return b3Quat(-q.v.x, -q.v.y, -q.v.z, -q.s); } // Multiply two quaternions. inline b3Quat b3Mul(const b3Quat& a, const b3Quat& b) { - return b3Quat( - a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, - a.w * b.y + a.y * b.w + a.z * b.x - a.x * b.z, - a.w * b.z + a.z * b.w + a.x * b.y - a.y * b.x, - a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z); + b3Quat result; + result.v = b3Cross(a.v, b.v) + a.s * b.v + b.s * a.v; + result.s = a.s * b.s - b3Dot(a.v, b.v); + return result; } // Multiply two quaternions. @@ -185,54 +233,51 @@ inline b3Quat operator*(const b3Quat& a, const b3Quat& b) } // Perform the dot poduct of two quaternions. -inline float32 b3Dot(const b3Quat& a, const b3Quat& b) +inline scalar b3Dot(const b3Quat& a, const b3Quat& b) { - return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; + return a.v.x * b.v.x + a.v.y * b.v.y + a.v.z * b.v.z + a.s * b.s; } // Return the conjugate of a quaternion. // If the quaternion is unit this returns its inverse. inline b3Quat b3Conjugate(const b3Quat& q) { - return b3Quat(-q.x, -q.y, -q.z, q.w); + return b3Quat(-q.v.x, -q.v.y, -q.v.z, q.s); } // Multiply the conjugate of a quaternion times another quaternion. -inline b3Quat b3MulT(const b3Quat& a, const b3Quat& b) +inline b3Quat b3MulC(const b3Quat& a, const b3Quat& b) { return b3Mul(b3Conjugate(a), b); } // Return the length of a quaternion. -inline float32 b3Length(const b3Quat& q) +inline scalar b3Length(const b3Quat& q) { - return b3Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w); + return b3Sqrt(q.v.x * q.v.x + q.v.y * q.v.y + q.v.z * q.v.z + q.s * q.s); } // Convert a quaternion to the unit quaternion. inline b3Quat b3Normalize(const b3Quat& q) { - float32 s = b3Length(q); + scalar s = b3Length(q); if (s > B3_EPSILON) { - s = 1.0f / s; + s = scalar(1) / s; return s * q; } - return b3Quat(0.0f, 0.0f, 0.0f, 1.0f); + return b3Quat(scalar(0), scalar(0), scalar(0), scalar(1)); } // Rotate a vector. inline b3Vec3 b3Mul(const b3Quat& q, const b3Vec3& v) { - b3Vec3 qv(q.x, q.y, q.z); - float32 qs = q.w; - - b3Vec3 t = 2.0f * b3Cross(qv, v); - return v + qs * t + b3Cross(qv, t); + b3Vec3 t = scalar(2) * b3Cross(q.v, v); + return v + q.s * t + b3Cross(q.v, t); } // Inverse rotate a vector. -inline b3Vec3 b3MulT(const b3Quat& q, const b3Vec3& v) +inline b3Vec3 b3MulC(const b3Quat& q, const b3Vec3& v) { return b3Mul(b3Conjugate(q), v); } @@ -241,19 +286,20 @@ inline b3Vec3 b3MulT(const b3Quat& q, const b3Vec3& v) inline b3Quat b3Mat33Quat(const b3Mat33& m) { // Check the diagonal. - float32 trace = m[0][0] + m[1][1] + m[2][2]; + scalar trace = m[0][0] + m[1][1] + m[2][2]; - if (trace > 0.0f) + if (trace > scalar(0)) { b3Quat result; - - float32 s = b3Sqrt(trace + 1.0f); - result.w = 0.5f * s; - - float32 t = 0.5f / s; - result.x = t * (m[1][2] - m[2][1]); - result.y = t * (m[2][0] - m[0][2]); - result.z = t * (m[0][1] - m[1][0]); + + scalar s = b3Sqrt(trace + scalar(1)); + result.s = scalar(0.5) * s; + + scalar t = scalar(0.5) / s; + result.v.x = t * (m[1][2] - m[2][1]); + result.v.y = t * (m[2][0] - m[0][2]); + result.v.z = t * (m[0][1] - m[1][0]); + return result; } @@ -275,15 +321,15 @@ inline b3Quat b3Mat33Quat(const b3Mat33& m) u32 j = next[i]; u32 k = next[j]; - float32 s = sqrt((m[i][i] - (m[j][j] + m[k][k])) + 1.0f); + scalar s = b3Sqrt((m[i][i] - (m[j][j] + m[k][k])) + scalar(1)); - float32 q[4]; - q[i] = s * 0.5f; + scalar q[4]; + q[i] = s * scalar(0.5); - float32 t; - if (s != 0.0f) + scalar t; + if (s != scalar(0)) { - t = 0.5f / s; + t = scalar(0.5) / s; } else { @@ -295,64 +341,55 @@ inline b3Quat b3Mat33Quat(const b3Mat33& m) q[k] = t * (m[i][k] + m[k][i]); b3Quat result; - result.x = q[0]; - result.y = q[1]; - result.z = q[2]; - result.w = q[3]; + result.v.x = q[0]; + result.v.y = q[1]; + result.v.z = q[2]; + result.s = q[3]; return result; } // Convert a rotation quaternion to a 3-by-3 rotation matrix. inline b3Mat33 b3QuatMat33(const b3Quat& q) { - float32 x = q.x, y = q.y, z = q.z, w = q.w; - float32 x2 = x + x, y2 = y + y, z2 = z + z; - float32 xx = x * x2, xy = x * y2, xz = x * z2; - float32 yy = y * y2, yz = y * z2, zz = z * z2; - float32 wx = w * x2, wy = w * y2, wz = w * z2; - - return b3Mat33( - b3Vec3(1.0f - (yy + zz), xy + wz, xz - wy), - b3Vec3( xy - wz, 1.0f - (xx + zz), yz + wx), - b3Vec3( xz + wy, yz - wx, 1.0f - (xx + yy))); + return q.GetXYZAxes(); } // Rotation about the x-axis. -inline b3Quat b3QuatRotationX(float32 angle) +inline b3Quat b3QuatRotationX(scalar angle) { - float32 x = 0.5f * angle; + scalar x = scalar(0.5) * angle; b3Quat q; - q.x = sin(x); - q.y = 0.0f; - q.z = 0.0f; - q.w = cos(x); + q.v.x = sin(x); + q.v.y = scalar(0); + q.v.z = scalar(0); + q.s = cos(x); return q; } // Rotation about the y-axis. -inline b3Quat b3QuatRotationY(float32 angle) +inline b3Quat b3QuatRotationY(scalar angle) { - float32 x = 0.5f * angle; + scalar x = scalar(0.5) * angle; b3Quat q; - q.x = 0.0f; - q.y = sin(x); - q.z = 0.0f; - q.w = cos(x); + q.v.x = scalar(0); + q.v.y = sin(x); + q.v.z = scalar(0); + q.s = cos(x); return q; } // Rotation about the z-axis. -inline b3Quat b3QuatRotationZ(float32 angle) +inline b3Quat b3QuatRotationZ(scalar angle) { - float32 x = 0.5f * angle; + scalar x = scalar(0.5) * angle; b3Quat q; - q.x = 0.0f; - q.y = 0.0f; - q.z = sin(x); - q.w = cos(x); + q.v.x = scalar(0); + q.v.y = scalar(0); + q.v.z = sin(x); + q.s = cos(x); return q; } diff --git a/include/bounce/common/math/transform.h b/include/bounce/common/math/transform.h index da6530e..0e4624f 100644 --- a/include/bounce/common/math/transform.h +++ b/include/bounce/common/math/transform.h @@ -25,36 +25,25 @@ // A transform represents a rigid frame. // It has a translation representing a position -// and a rotation matrix representing an orientation +// and a rotation quaternion representing an orientation // relative to some reference frame. struct b3Transform { // Default ctor does nothing for performance. b3Transform() { } - - // Set this transform from a rotation matrix and a translation vector. - b3Transform(const b3Mat33& _rotation, const b3Vec3& _translation) - { - rotation = _rotation; - position = _translation; - } - - // Set this transform from a rotation quaternion and a translation vector. - b3Transform(const b3Quat& _rotation, const b3Vec3& _translation) - { - rotation = b3QuatMat33(_rotation); - position = _translation; - } - + + // Set this transform from a translation vector and a rotation quaternion. + b3Transform(const b3Vec3& _translation, const b3Quat& _rotation) : translation(_translation), rotation(_rotation) { } + // Set this transform to the identity transform. void SetIdentity() { + translation.SetZero(); rotation.SetIdentity(); - position.SetZero(); } - b3Mat33 rotation; - b3Vec3 position; // in fact a translation + b3Vec3 translation; + b3Quat rotation; }; // Identity transformation @@ -63,17 +52,20 @@ extern const b3Transform b3Transform_identity; // Convert a transform to a 4-by-4 transformation matrix. inline b3Mat44 b3TransformMat44(const b3Transform& T) { + b3Vec3 t = T.translation; + b3Mat33 R = b3QuatMat33(T.rotation); + return b3Mat44( - b3Vec4(T.rotation.x.x, T.rotation.x.y, T.rotation.x.z, 0.0f), - b3Vec4(T.rotation.y.x, T.rotation.y.y, T.rotation.y.z, 0.0f), - b3Vec4(T.rotation.z.x, T.rotation.z.y, T.rotation.z.z, 0.0f), - b3Vec4(T.position.x, T.position.y, T.position.z, 1.0f)); + b3Vec4(R.x.x, R.x.y, R.x.z, scalar(0)), + b3Vec4(R.y.x, R.y.y, R.y.z, scalar(0)), + b3Vec4(R.z.x, R.z.y, R.z.z, scalar(0)), + b3Vec4(t.x, t.y, t.z, scalar(1))); } // Multiply a transform times a vector. inline b3Vec3 b3Mul(const b3Transform& T, const b3Vec3& v) { - return b3Mul(T.rotation, v) + T.position; + return b3Mul(T.rotation, v) + T.translation; } // Multiply a transform times another transform. @@ -83,7 +75,7 @@ inline b3Transform b3Mul(const b3Transform& A, const b3Transform& B) // [0 1][0 1] [0 1 ] b3Transform C; C.rotation = b3Mul(A.rotation, B.rotation); - C.position = b3Mul(A.rotation, B.position) + A.position; + C.translation = b3Mul(A.rotation, B.translation) + A.translation; return C; } @@ -94,8 +86,8 @@ inline b3Transform b3MulT(const b3Transform& A, const b3Transform& B) //[A^-1 -A^-1*y][B x] = [A^-1*B A^-1(x-y)] //[0 1 ][0 1] [0 1 ] b3Transform C; - C.rotation = b3MulT(A.rotation, B.rotation); - C.position = b3MulT(A.rotation, B.position - A.position); + C.rotation = b3MulC(A.rotation, B.rotation); + C.translation = b3MulC(A.rotation, B.translation - A.translation); return C; } @@ -106,15 +98,15 @@ inline b3Vec3 b3MulT(const b3Transform& A, const b3Vec3& v) { //[A^-1 -A^-1*y][x] = A^-1*x - A^-1*y = A^-1 * (x - y) //[0 1 ][1] - return b3MulT(A.rotation, v - A.position); + return b3MulC(A.rotation, v - A.translation); } // Inverse transform. inline b3Transform b3Inverse(const b3Transform& T) { b3Transform B; - B.rotation = b3Transpose(T.rotation); - B.position = b3MulT(T.rotation, -T.position); + B.rotation = b3Conjugate(T.rotation); + B.translation = b3MulC(T.rotation, -T.translation); return B; } @@ -132,115 +124,43 @@ inline b3Transform operator*(const b3Transform& A, const b3Transform& B) return b3Mul(A, B); } -// A quaternion-based transform. -struct b3TransformQT -{ - // Default ctor does nothing for performance. - b3TransformQT() { } - - // Set this transform from a rotation matrix and a translation vector. - b3TransformQT(const b3Mat33& _rotation, const b3Vec3& _translation) - { - rotation = b3Mat33Quat(_rotation); - translation = _translation; - } - - // Set this transform to the identity transform. - void SetIdentity() - { - rotation.SetIdentity(); - translation.SetZero(); - } - - b3Quat rotation; - b3Vec3 translation; -}; - -// Convert a quaternion based transform to a matrix based transform. -inline b3Transform b3ConvertToTransform(const b3TransformQT& T) -{ - return b3Transform(T.rotation, T.translation); -} - -// Multiply a transform times another transform. -inline b3TransformQT b3Mul(const b3TransformQT& A, const b3TransformQT& B) -{ - b3TransformQT C; - C.rotation = b3Mul(A.rotation, B.rotation); - C.translation = b3Mul(A.rotation, B.translation) + A.translation; - return C; -} - -// Multiply the transpose of one transform (inverse -// transform) times another transform (composed transform). -inline b3TransformQT b3MulT(const b3TransformQT& A, const b3TransformQT& B) -{ - b3TransformQT C; - C.rotation = b3MulT(A.rotation, B.rotation); - C.translation = b3MulT(A.rotation, B.translation - A.translation); - return C; -} - -inline b3TransformQT operator*(const b3TransformQT& A, const b3TransformQT& B) -{ - return b3Mul(A, B); -} - -// Inverse transform a vector. -inline b3Vec3 b3MulT(const b3TransformQT& A, const b3Vec3& v) -{ - return b3MulT(A.rotation, v - A.translation); -} - -// Inverse transform. -inline b3TransformQT b3Inverse(const b3TransformQT& T) -{ - b3TransformQT B; - B.rotation = b3Conjugate(T.rotation); - B.translation = b3MulT(T.rotation, -T.translation); - return B; -} - // Motion proxy for TOI computation. struct b3Sweep { // Get this sweep transform at a given time between [0, 1] - b3Transform GetTransform(float32 t) const; - - // Advance to a new initial state. - void Advance(float32 t); + b3Transform GetTransform(scalar t) const; b3Vec3 localCenter; // local center b3Quat orientation0; // last orientation b3Vec3 worldCenter0; // last world center - float32 t0; // last fraction between [0, 1] + scalar t0; // last fraction between [0, 1] b3Quat orientation; // world orientation b3Vec3 worldCenter; // world center }; -inline b3Transform b3Sweep::GetTransform(float32 t) const +inline b3Transform b3Sweep::GetTransform(scalar t) const { - b3Vec3 c = (1.0f - t) * worldCenter0 + t * worldCenter; - b3Quat q = (1.0f - t) * orientation0 + t * orientation; + b3Vec3 c = (scalar(1) - t) * worldCenter0 + t * worldCenter; + + b3Quat q1 = orientation0; + b3Quat q2 = orientation; + + if (b3Dot(q1, q2) < scalar(0)) + { + q1 = -q1; + } + + b3Quat q = (scalar(1) - t) * q1 + t * q2; q.Normalize(); b3Transform xf; - xf.rotation = b3QuatMat33(q); - xf.position = c - b3Mul(q, localCenter); + xf.translation = c - b3Mul(q, localCenter); + xf.rotation = q; + return xf; } -inline void b3Sweep::Advance(float32 t) -{ - B3_ASSERT(t < 1.0f); - float32 dt = (t - t0) / (1.0f / t0); - worldCenter += dt * (worldCenter - worldCenter0); - orientation += dt * (orientation - orientation0); - orientation.Normalize(); - t0 = t; -} - #endif \ No newline at end of file diff --git a/include/bounce/common/math/vec2.h b/include/bounce/common/math/vec2.h index b833016..f83d72b 100644 --- a/include/bounce/common/math/vec2.h +++ b/include/bounce/common/math/vec2.h @@ -28,13 +28,13 @@ struct b3Vec2 b3Vec2() { } // Set this vector from two components. - b3Vec2(float32 _x, float32 _y) : x(_x), y(_y) { } + b3Vec2(scalar _x, scalar _y) : x(_x), y(_y) { } // Read an indexed component from this vector. - float32 operator[](u32 i) const { return (&x)[i]; } + scalar operator[](u32 i) const { return (&x)[i]; } // Write an indexed component to this vector. - float32& operator[](u32 i) { return (&x)[i]; } + scalar& operator[](u32 i) { return (&x)[i]; } // Add a vector to this vector. void operator+=(const b3Vec2& v) @@ -51,14 +51,14 @@ struct b3Vec2 } // Scale this vector. - void operator*=(float32 s) + void operator*=(scalar s) { x *= s; y *= s; } // Scale this vector. - void operator/=(float32 s) + void operator/=(scalar s) { x /= s; y /= s; @@ -67,11 +67,11 @@ struct b3Vec2 // Set this vector to the zero vector. void SetZero() { - x = y = 0.0f; + x = y = scalar(0); } // Set this vector from two components. - void Set(float32 _x, float32 _y) + void Set(scalar _x, scalar _y) { x = _x; y = _y; @@ -80,7 +80,7 @@ struct b3Vec2 // Normalize this vector. void Normalize() { - float32 s = b3Sqrt(x * x + y * y); + scalar s = b3Sqrt(x * x + y * y); if (s > B3_EPSILON) { x /= s; @@ -88,7 +88,7 @@ struct b3Vec2 } } - float32 x, y; + scalar x, y; }; // Zero vector @@ -119,49 +119,55 @@ inline b3Vec2 operator-(const b3Vec2& a, const b3Vec2& b) } // Multiply a vector by a scalar. -inline b3Vec2 operator*(float32 s, const b3Vec2& v) +inline b3Vec2 operator*(scalar s, const b3Vec2& v) { return b3Vec2(s * v.x, s * v.y); } // Multiply a vector by a scalar. -inline b3Vec2 operator*(const b3Vec2& v, float32 s) +inline b3Vec2 operator*(const b3Vec2& v, scalar s) { return s * v; } // Compute the dot product of two vectors. -inline float32 b3Dot(const b3Vec2& a, const b3Vec2& b) +inline scalar b3Dot(const b3Vec2& a, const b3Vec2& b) { return a.x * b.x + a.y * b.y; } // Compute the length of a vector. -inline float32 b3Length(const b3Vec2& v) +inline scalar b3Length(const b3Vec2& v) { return b3Sqrt(v.x * v.x + v.y * v.y); } +// Compute the squared length of a vector. +inline scalar b3LengthSquared(const b3Vec2& v) +{ + return v.x * v.x + v.y * v.y; +} + // Normalize a vector. inline b3Vec2 b3Normalize(const b3Vec2& v) { - float32 length = b3Length(v); + scalar length = b3Length(v); if (length > B3_EPSILON) { - float32 s = 1.0f / length; + scalar s = scalar(1) / length; return s * v; } return v; } // Compute the euclidean distance between two points. -inline float32 b3Distance(const b3Vec2& a, const b3Vec2& b) +inline scalar b3Distance(const b3Vec2& a, const b3Vec2& b) { return b3Length(a - b); } // Compute the determinant of two 2D vectors. -inline float32 b3Det(const b3Vec2& a, const b3Vec2& b) +inline scalar b3Det(const b3Vec2& a, const b3Vec2& b) { return a.x * b.y - a.y * b.x; } diff --git a/include/bounce/common/math/vec3.h b/include/bounce/common/math/vec3.h index 67c2524..5e933c8 100644 --- a/include/bounce/common/math/vec3.h +++ b/include/bounce/common/math/vec3.h @@ -28,16 +28,16 @@ struct b3Vec3 b3Vec3() { } // Set this vector from three components. - b3Vec3(float32 _x, float32 _y, float32 _z) : x(_x), y(_y), z(_z) { } + b3Vec3(scalar _x, scalar _y, scalar _z) : x(_x), y(_y), z(_z) { } // Read an indexed component from this vector. - float32 operator[](u32 i) const + scalar operator[](u32 i) const { return (&x)[i]; } // Write an indexed component to this vector. - float32& operator[](u32 i) + scalar& operator[](u32 i) { return (&x)[i]; } @@ -59,7 +59,7 @@ struct b3Vec3 } // Scale this vector. - void operator*=(float32 s) + void operator*=(scalar s) { x *= s; y *= s; @@ -67,9 +67,9 @@ struct b3Vec3 } // Scale this vector. - void operator/=(float32 a) + void operator/=(scalar a) { - float32 s = 1.0f / a; + scalar s = scalar(1) / a; x *= s; y *= s; z *= s; @@ -78,11 +78,11 @@ struct b3Vec3 // Set this vector to the zero vector. void SetZero() { - x = y = z = 0.0f; + x = y = z = scalar(0); } // Set this vector from three coordinates. - void Set(float32 _x, float32 _y, float32 _z) + void Set(scalar _x, scalar _y, scalar _z) { x = _x; y = _y; @@ -90,12 +90,12 @@ struct b3Vec3 } // Convert this vector to the unit vector. Return the length. - float32 Normalize() + scalar Normalize() { - float32 length = b3Sqrt(x * x + y * y + z * z); + scalar length = b3Sqrt(x * x + y * y + z * z); if (length > B3_EPSILON) { - float32 s = 1.0f / length; + scalar s = scalar(1) / length; x *= s; y *= s; z *= s; @@ -103,7 +103,7 @@ struct b3Vec3 return length; } - float32 x, y, z; + scalar x, y, z; }; // Zero vector @@ -137,25 +137,25 @@ inline b3Vec3 operator-(const b3Vec3& a, const b3Vec3& b) } // Compute a scalar-vector product. -inline b3Vec3 operator*(float32 s, const b3Vec3& v) +inline b3Vec3 operator*(scalar s, const b3Vec3& v) { return b3Vec3(s * v.x, s * v.y, s * v.z); } // Compute a scalar-vector product. -inline b3Vec3 operator*(const b3Vec3& v, float32 s) +inline b3Vec3 operator*(const b3Vec3& v, scalar s) { return s * v; } // Inverse multiply a scalar-vector. -inline b3Vec3 operator/(const b3Vec3& v, float32 s) +inline b3Vec3 operator/(const b3Vec3& v, scalar s) { return b3Vec3(v.x / s, v.y / s, v.z / s); } // Compute the dot-product of two vectors. -inline float32 b3Dot(const b3Vec3& a, const b3Vec3& b) +inline scalar b3Dot(const b3Vec3& a, const b3Vec3& b) { return a.x * b.x + a.y * b.y + a.z * b.z; } @@ -168,19 +168,19 @@ inline b3Vec3 b3Cross(const b3Vec3& a, const b3Vec3& b) // Compute the determinant of a matrix whose columns are three given vectors. // Useful property: det(a, b, c) = det(c, a, b) = det(b, c, a). -inline float32 b3Det(const b3Vec3& a, const b3Vec3& b, const b3Vec3& c) +inline scalar b3Det(const b3Vec3& a, const b3Vec3& b, const b3Vec3& c) { return b3Dot(a, b3Cross(b, c)); } // Compute the length of a vector. -inline float32 b3Length(const b3Vec3& v) +inline scalar b3Length(const b3Vec3& v) { return b3Sqrt(v.x * v.x + v.y * v.y + v.z * v.z); } // Compute the squared length of a vector. -inline float32 b3LengthSquared(const b3Vec3& v) +inline scalar b3LengthSquared(const b3Vec3& v) { return v.x * v.x + v.y * v.y + v.z * v.z; } @@ -188,54 +188,53 @@ inline float32 b3LengthSquared(const b3Vec3& v) // Compute the normalized vector of a (non-zero!) vector. inline b3Vec3 b3Normalize(const b3Vec3& v) { - float32 length = b3Length(v); + scalar length = b3Length(v); if (length > B3_EPSILON) { - float32 s = 1.0f / length; + scalar s = scalar(1) / length; return s * v; } return v; } // Compute the euclidean distance between two points. -inline float32 b3Distance(const b3Vec3& a, const b3Vec3& b) +inline scalar b3Distance(const b3Vec3& a, const b3Vec3& b) { return b3Length(a - b); } // Compute the squared distance between two points. -inline float32 b3DistanceSquared(const b3Vec3& a, const b3Vec3& b) +inline scalar b3DistanceSquared(const b3Vec3& a, const b3Vec3& b) { - b3Vec3 v = a - b; - return b3LengthSquared(v); + return b3LengthSquared(a - b); } // Compute the triangle area. -inline float32 b3Area(const b3Vec3& a, const b3Vec3& b, const b3Vec3& c) +inline scalar b3Area(const b3Vec3& a, const b3Vec3& b, const b3Vec3& c) { - return 0.5f * b3Length(b3Cross(b - a, c - a)); + return scalar(0.5) * b3Length(b3Cross(b - a, c - a)); } // Compute the squared triangle area. -inline float32 b3AreaSquared(const b3Vec3& a, const b3Vec3& b, const b3Vec3& c) +inline scalar b3AreaSquared(const b3Vec3& a, const b3Vec3& b, const b3Vec3& c) { - return 0.25f * b3LengthSquared(b3Cross(b - a, c - a)); + return scalar(0.25) * b3LengthSquared(b3Cross(b - a, c - a)); } -// Compute the tetrahedron volume. -inline float32 b3Volume(const b3Vec3& a, const b3Vec3& b, const b3Vec3& c, const b3Vec3& d) +// Compute the positive tetrahedron volume. +inline scalar b3Volume(const b3Vec3& a, const b3Vec3& b, const b3Vec3& c, const b3Vec3& d) { - float32 volume = b3Det(b - a, c - a, d - a); - // Force a positive volume. - float32 sign = b3Sign(volume); - const float32 inv6 = 1.0f / 6.0f; + const scalar inv6 = scalar(1) / scalar(6); + + scalar volume = b3Det(b - a, c - a, d - a); + scalar sign = b3Sign(volume); return sign * inv6 * volume; } // Compute the squared tetrahedron volume. -inline float32 b3VolumeSquared(const b3Vec3& a, const b3Vec3& b, const b3Vec3& c, const b3Vec3& d) +inline scalar b3VolumeSquared(const b3Vec3& a, const b3Vec3& b, const b3Vec3& c, const b3Vec3& d) { - float32 volume = b3Volume(a, b, c, d); + scalar volume = b3Volume(a, b, c, d); return volume * volume; } @@ -251,19 +250,41 @@ inline b3Vec3 b3Max(const b3Vec3& a, const b3Vec3& b) return b3Vec3(b3Max(a.x, b.x), b3Max(a.y, b.y), b3Max(a.z, b.z)); } -// Find a perpendicular vector to a vector. +// Compute the absolute vector of a given vector (per-element). +inline b3Vec3 b3Abs(const b3Vec3& v) +{ + return b3Vec3(b3Abs(v.x), b3Abs(v.y), b3Abs(v.z)); +} + +// Compute a perpendicular unit vector to a given unit vector. inline b3Vec3 b3Perp(const b3Vec3& v) { - // Box2D - // Suppose vector a has all equal components and is a unit vector: a = (s, s, s) - // Then 3*s*s = 1, s = sqrt(1/3) = 0.57735. This means that at least one component of a - // unit vector must be greater or equal to 0.57735. - if (b3Abs(v.x) >= float32(0.57735027)) + static const scalar sqrt_inv3 = b3Sqrt(scalar(1) / scalar(3)); + + b3Vec3 u; + if (b3Abs(v.x) >= sqrt_inv3) { - return b3Vec3(v.y, -v.x, 0.0f); + u.Set(v.y, -v.x, scalar(0)); + } + else + { + u.Set(scalar(0), v.z, -v.y); } - return b3Vec3(0.0f, v.z, -v.y); + u.Normalize(); + return u; +} + +// Multiply two vectors component-wise. +inline b3Vec3 b3MulCW(const b3Vec3& a, const b3Vec3& b) +{ + return b3Vec3(a.x * b.x, a.y * b.y, a.z * b.z); +} + +// Divide two vectors component-wise. +inline b3Vec3 b3DivCW(const b3Vec3& a, const b3Vec3& b) +{ + return b3Vec3(a.x / b.x, a.y / b.y, a.z / b.z); } #endif \ No newline at end of file diff --git a/include/bounce/common/math/vec4.h b/include/bounce/common/math/vec4.h index 8b44ec8..f37a2e1 100644 --- a/include/bounce/common/math/vec4.h +++ b/include/bounce/common/math/vec4.h @@ -28,7 +28,7 @@ struct b3Vec4 b3Vec4() { } // - b3Vec4(float32 _x, float32 _y, float32 _z, float32 _w) + b3Vec4(scalar _x, scalar _y, scalar _z, scalar _w) { x = _x; y = _y; @@ -39,13 +39,10 @@ struct b3Vec4 // void SetZero() { - x = 0.0f; - y = 0.0f; - z = 0.0f; - w = 0.0f; + x = y = z = w = scalar(0); } - float32 x, y, z, w; + scalar x, y, z, w; }; // @@ -61,13 +58,13 @@ inline b3Vec4 operator-(const b3Vec4& a, const b3Vec4& b) } // -inline b3Vec4 operator*(float32 s, const b3Vec4& v) +inline b3Vec4 operator*(scalar s, const b3Vec4& v) { return b3Vec4(s * v.x, s * v.y, s * v.z, s * v.w); } // -inline float32 operator*(const b3Vec4& A, const b3Vec4& B) +inline scalar operator*(const b3Vec4& A, const b3Vec4& B) { return A.x * B.x + A.y * B.y + A.z * B.z + A.w * B.w; } diff --git a/include/bounce/common/memory/frame_allocator.h b/include/bounce/common/memory/frame_allocator.h new file mode 100644 index 0000000..e727426 --- /dev/null +++ b/include/bounce/common/memory/frame_allocator.h @@ -0,0 +1,59 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_FRAME_ALLOCATOR_H +#define B3_FRAME_ALLOCATOR_H + +#include + +// Allocate 1 MiB from the stack. +// Increase as you want. +const u32 b3_maxFrameSize = B3_MiB(1); + +// A small frame allocator. +class b3FrameAllocator +{ +public: + b3FrameAllocator(); + ~b3FrameAllocator(); + + // Allocate a block of memory. + // Allocate using b3Alloc if the memory is full. + void* Allocate(u32 size); + + // Free the memory if it was allocated by b3Alloc. + void Free(void* q); + + // Reset the memory pointer. + // This function should be called at the beginning of a frame. + void Reset(); +private: + // Block header + struct b3Block + { + u32 size; + void* p; + bool parent; + }; + + u8 m_memory[b3_maxFrameSize]; + u8* m_p; + u32 m_allocatedSize; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/common/profiler.h b/include/bounce/common/profiler.h new file mode 100644 index 0000000..99e0bed --- /dev/null +++ b/include/bounce/common/profiler.h @@ -0,0 +1,101 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_PROFILER_H +#define B3_PROFILER_H + +#include +#include +#include + +// Profiler node statistics +struct b3ProfilerNodeStat +{ + const char* name; + scalar64 minElapsed; + scalar64 maxElapsed; + b3ProfilerNodeStat* next; +}; + +// A profiler node +struct b3ProfilerNode +{ + const char* name; + scalar64 t0; + scalar64 t1; + + scalar64 elapsed; // total elapsed time + u32 callCount; // number of calls inside the parent node + u32 callCountRec; // used for detecting recursive calls + + b3ProfilerNode* parent; // parent node + b3ProfilerNode* head; // list of children + b3ProfilerNode* next; // link to the next node in the parent node list of children + + b3ProfilerNodeStat* stat; // global node statistics +}; + +// A hierarchical profiler +class b3Profiler +{ +public: + b3Profiler(); + + ~b3Profiler(); + + // Must be called before profiling. + void Begin(); + + // Must be called after profiling. + void End(); + + // Begin a new scope. + void BeginScope(const char* name); + + // End the top scope. + void EndScope(); + + // Get the root node. + b3ProfilerNode* GetRoot() { return m_root; } + + // Get the top node. + b3ProfilerNode* GetTop() { return m_top; } + + // Get the list of node statistics. + b3ProfilerNodeStat* GetStats() const { return m_stats; } +private: + b3ProfilerNode* CreateNode(); + void DestroyNode(b3ProfilerNode* node); + + void RecurseDestroyNode(b3ProfilerNode* node); + + b3ProfilerNode* FindNode(const char* name); + + b3ProfilerNodeStat* CreateStat(); + + b3ProfilerNodeStat* FindStat(const char* name); + + b3BlockPool m_nodePool; // pool of nodes + b3BlockPool m_statPool; // pool of node stats + b3Time m_time; // timer + b3ProfilerNode* m_root; // tree root node + b3ProfilerNode* m_top; // top node + b3ProfilerNodeStat* m_stats; // node stats +}; + +#endif \ No newline at end of file diff --git a/include/bounce/common/settings.h b/include/bounce/common/settings.h index 431ab59..f37f0e3 100644 --- a/include/bounce/common/settings.h +++ b/include/bounce/common/settings.h @@ -31,19 +31,29 @@ typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; typedef unsigned long long u64; -typedef double float64; -typedef float float32; +typedef float scalar; +typedef double scalar64; // You can modify the following parameters as long // as you know what you're doing. -#define B3_PI (3.14159265359f) -#define B3_MAX_FLOAT (FLT_MAX) -#define B3_EPSILON (FLT_EPSILON) - #define B3_MAX_U8 (0xFF) #define B3_MAX_U32 (0xFFFFFFFF) +// This is a scalar type dependent variable. +// If scalar is float, you must set this constant to FLT_MAX. +// If scalar is double, you must set this constant to DBL_MAX. +#define B3_MAX_SCALAR (FLT_MAX) + +// This is scalar type dependent variable. +// If scalar is float, you must set this constant to FLT_EPSILON. +// If scalar is double, you must set this constant to DBL_EPSILON. +#define B3_EPSILON (FLT_EPSILON) + +// This is scalar type dependent variable. +// This is computed using double precision by default. +#define B3_PI scalar(3.14159265358979323846) + // Collision // How much an AABB in the broad-phase should be extended by @@ -51,20 +61,19 @@ typedef float float32; // A larger value increases performance when there are // no objects closer to the AABB because no contacts are // even created. -#define B3_AABB_EXTENSION (0.2f) +#define B3_AABB_EXTENSION scalar(0.2) // This is used to extend AABBs in the broad-phase. // Is used to predict the future position based on the current displacement. // This is a dimensionless multiplier. -#define B3_AABB_MULTIPLIER (2.0f) +#define B3_AABB_MULTIPLIER scalar(2) // Collision and constraint tolerance. -#define B3_LINEAR_SLOP (0.005f) -#define B3_ANGULAR_SLOP (2.0f / 180.0f * B3_PI) +#define B3_LINEAR_SLOP scalar(0.005) +#define B3_ANGULAR_SLOP (scalar(2.0) / scalar(180) * B3_PI) // The radius of the hull shape skin. -#define B3_HULL_RADIUS (0.0f * B3_LINEAR_SLOP) -#define B3_HULL_RADIUS_SUM (2.0f * B3_HULL_RADIUS) +#define B3_HULL_RADIUS (scalar(0.0) * B3_LINEAR_SLOP) // Dynamics @@ -82,32 +91,30 @@ typedef float float32; // Maximum translation per step to prevent numerical instability // due to large linear velocity. -#define B3_MAX_TRANSLATION (2.0f) +#define B3_MAX_TRANSLATION scalar(2.0) #define B3_MAX_TRANSLATION_SQUARED (B3_MAX_TRANSLATION * B3_MAX_TRANSLATION) // Maximum rotation per step to prevent numerical instability due to // large angular velocity. -#define B3_MAX_ROTATION (0.5f * B3_PI) +#define B3_MAX_ROTATION (scalar(0.5) * B3_PI) #define B3_MAX_ROTATION_SQUARED (B3_MAX_ROTATION * B3_MAX_ROTATION) // The maximum position correction used when solving constraints. This helps to // prevent overshoot. -#define B3_MAX_LINEAR_CORRECTION (0.2f) -#define B3_MAX_ANGULAR_CORRECTION (8.0f / 180.0f * B3_PI) +#define B3_MAX_LINEAR_CORRECTION scalar(0.2) +#define B3_MAX_ANGULAR_CORRECTION (scalar(8.0) / scalar(180) * B3_PI) // This controls how faster overlaps should be resolved per step. // This is less than and would be close to 1, so that the all overlap is resolved per step. // However values very close to 1 may lead to overshoot. -#define B3_BAUMGARTE (0.1f) +#define B3_BAUMGARTE scalar(0.1) // If the relative velocity of a contact point is below // the threshold then restitution is not applied. -#define B3_VELOCITY_THRESHOLD (1.0f) +#define B3_VELOCITY_THRESHOLD scalar(1.0) -// Sleep -#define B3_TIME_TO_SLEEP (0.2f) -#define B3_SLEEP_LINEAR_TOL (0.05f) -#define B3_SLEEP_ANGULAR_TOL (2.0f / 180.0f * B3_PI) +// Time to sleep in seconds +#define B3_TIME_TO_SLEEP scalar(0.2) // Memory diff --git a/include/bounce/common/template/array.h b/include/bounce/common/template/array.h index 49b4069..c6d09b4 100644 --- a/include/bounce/common/template/array.h +++ b/include/bounce/common/template/array.h @@ -238,7 +238,7 @@ public : void operator=(const b3StackArray& other) { - b3Array::Swap((const b3Array&) (other)); + b3Array::Swap((const b3Array& )other); } void operator=(const b3Array& other) diff --git a/include/bounce/common/template/list.h b/include/bounce/common/template/list.h index 8d83141..993c46c 100644 --- a/include/bounce/common/template/list.h +++ b/include/bounce/common/template/list.h @@ -28,7 +28,7 @@ class b3List1 public: b3List1() { - m_head = NULL; + m_head = nullptr; m_count = 0; } @@ -58,7 +58,7 @@ class b3List2 public: b3List2() { - m_head = NULL; + m_head = nullptr; m_count = 0; } @@ -66,7 +66,7 @@ public: void PushFront(T* link) { - link->m_prev = NULL; + link->m_prev = nullptr; link->m_next = m_head; if (m_head) { @@ -80,9 +80,9 @@ public: { link->m_prev = prev; - if (prev->m_next == NULL) + if (prev->m_next == nullptr) { - link->m_next = NULL; + link->m_next = nullptr; } else { diff --git a/include/bounce/common/template/queue.h b/include/bounce/common/template/queue.h index 0992b4d..72a07fa 100644 --- a/include/bounce/common/template/queue.h +++ b/include/bounce/common/template/queue.h @@ -44,7 +44,7 @@ public: { if ((m_count + 1) == N) { - return NULL; + return nullptr; } B3_ASSERT(m_back < N); diff --git a/include/bounce/common/template/stack.h b/include/bounce/common/template/stack.h index ca938d0..7b353d5 100644 --- a/include/bounce/common/template/stack.h +++ b/include/bounce/common/template/stack.h @@ -41,7 +41,7 @@ public: { b3Free(m_elements); } - m_elements = NULL; + m_elements = nullptr; } const T& Top() const diff --git a/include/bounce/common/time.h b/include/bounce/common/time.h index f7bf0d6..f1fd281 100644 --- a/include/bounce/common/time.h +++ b/include/bounce/common/time.h @@ -37,7 +37,7 @@ #if B3_PLATFORM == B3_WINDOWS -#include +#include // A timer class that accumulates time. // Usefull for measuring elapsed times between code sections. @@ -54,13 +54,13 @@ public: } // Get the accumulated time in miliseconds from this timer. - float64 GetCurrentMilis() const + double GetCurrentMilis() const { return m_t; } // Get the elapsed time since this timer was updated. - float64 GetElapsedMilis() const + double GetElapsedMilis() const { return m_t - m_t0; } @@ -68,37 +68,37 @@ public: // Add the elapsed time since this function was called to this timer. void Update() { - static float64 inv_frequency = 0.0; + static double inv_frequency = 0.0; if (inv_frequency == 0.0) { LARGE_INTEGER c; QueryPerformanceFrequency(&c); - float64 cycles_per_s = float64(c.QuadPart); - float64 s_per_cycle = 1.0 / cycles_per_s; - float64 ms_per_cycle = 1000.0 * s_per_cycle; + double cycles_per_s = double(c.QuadPart); + double s_per_cycle = 1.0 / cycles_per_s; + double ms_per_cycle = 1000.0 * s_per_cycle; inv_frequency = ms_per_cycle; } LARGE_INTEGER c; QueryPerformanceCounter(&c); - - float64 dt = inv_frequency * float64(c.QuadPart - m_c0); + + double dt = inv_frequency * double(c.QuadPart - m_c0); m_c0 = c.QuadPart; Add(dt); } // Add time to this timer. - void Add(float64 dt) + void Add(double dt) { m_t0 = m_t; m_t += dt; } private: - u64 m_c0; - float64 m_t0; - float64 m_t; + LONGLONG m_c0; + double m_t0; + double m_t; }; #elif B3_PLATFORM == B3_MAC @@ -207,6 +207,7 @@ private: double m_t; }; + #else #include @@ -240,7 +241,7 @@ public: { struct timespec c; clock_gettime(CLOCK_MONOTONIC, &c); - double dt = (double)(c.tv_nsec - m_c0.tv_nsec) * 1.0e-6; + double dt = 1000.0 * double(c.tv_sec - m_c0.tv_sec) + 1.0e-6 * double(c.tv_nsec - m_c0.tv_nsec); m_c0 = c; Add(dt); } diff --git a/include/bounce/dynamics/body.h b/include/bounce/dynamics/body.h index b945ee8..3ce7bc0 100644 --- a/include/bounce/dynamics/body.h +++ b/include/bounce/dynamics/body.h @@ -50,17 +50,20 @@ struct b3BodyDef { type = e_staticBody; awake = true; + allowSleep = true; fixedRotationX = false; fixedRotationY = false; fixedRotationZ = false; - userData = NULL; + userData = nullptr; position.SetZero(); orientation.SetIdentity(); linearVelocity.SetZero(); angularVelocity.SetZero(); - gravityScale = 1.0f; - linearDamping = 0.0f; - angularDamping = 0.0f; + gravityScale.Set(scalar(1), scalar(1), scalar(1)); + linearDamping.SetZero(); + angularDamping.SetZero(); + linearSleepTolerance = scalar(0.05); + angularSleepTolerance = scalar(2) / scalar(180) * B3_PI; } // @@ -69,6 +72,9 @@ struct b3BodyDef // bool awake; + // + bool allowSleep; + // bool fixedRotationX; @@ -94,13 +100,19 @@ struct b3BodyDef b3Vec3 angularVelocity; // - float32 linearDamping; + b3Vec3 linearDamping; // - float32 angularDamping; + b3Vec3 angularDamping; // - float32 gravityScale; + b3Vec3 gravityScale; + + // + scalar linearSleepTolerance; + + // + scalar angularSleepTolerance; }; class b3Body @@ -127,6 +139,10 @@ public: // Therefore you can create shapes on the stack memory. b3Shape* CreateShape(const b3ShapeDef& def); + // Get the list of all joints connected to this body. + const b3List2& GetJointList() const; + b3List2& GetJointList(); + // Destroy a given shape from the body. void DestroyShape(b3Shape* shape); @@ -135,11 +151,14 @@ public: // Get the body world transform. const b3Transform& GetTransform() const; - - // Set the body world transform from a position, axis of rotation and an angle - // of rotation about the axis. + + // Set the body world transform from a position and orientation quaternion. // However, manipulating a body transform during the simulation may cause non-physical behaviour. - void SetTransform(const b3Vec3& position, const b3Vec3& axis, float32 angle); + void SetTransform(const b3Vec3& position, const b3Quat& orientation); + + // Set the body world transform from a position and orientation matrix. + // However, manipulating a body transform during the simulation may cause non-physical behaviour. + void SetTransform(const b3Vec3& position, const b3Mat33& orientation); // Get the position of the world body origin. b3Vec3 GetPosition() const; @@ -195,10 +214,10 @@ public: void ApplyAngularImpulse(const b3Vec3& impulse, bool wake); // Get the mass of the body. Typically in kg/m^3. - float32 GetMass() const; + scalar GetMass() const; // Get the inverse mass of the body. Typically in kg/m^3. - float32 GetInverseMass() const; + scalar GetInverseMass() const; // Get the rotational inertia of the body about the local center of mass. Typically in kg/m^3. const b3Mat33& GetInertia() const; @@ -221,13 +240,13 @@ public: void ResetMass(); // Get the linear kinetic energy of the body in Joules (kg m^2/s^2). - float32 GetLinearEnergy() const; + scalar GetLinearEnergy() const; // Get the angular kinetic energy of the body in Joules (kg m^2/s^2). - float32 GetAngularEnergy() const; + scalar GetAngularEnergy() const; // Get the total kinetic energy of the body in Joules (kg m^2/s^2). - float32 GetEnergy() const; + scalar GetEnergy() const; // Transform a vector to the local space of this body. b3Vec3 GetLocalVector(const b3Vec3& vector) const; @@ -241,6 +260,12 @@ public: // Transform a point to the world space. b3Vec3 GetWorldPoint(const b3Vec3& localPoint) const; + // Transform a frame to the local space of this body. + b3Quat GetLocalFrame(const b3Quat& frame) const; + + // Transform a frame to the world space. + b3Quat GetWorldFrame(const b3Quat& localFrame) const; + // Transform a frame to the local space of this body. b3Transform GetLocalFrame(const b3Transform& frame) const; @@ -248,22 +273,22 @@ public: b3Transform GetWorldFrame(const b3Transform& localFrame) const; // Get the linear damping of the body. - float32 GetLinearDamping() const; + const b3Vec3& GetLinearDamping() const; // Set the linear damping of the body. Zero is set by default. - void SetLinearDamping(float32 damping); + void SetLinearDamping(const b3Vec3& damping); // Get the angular damping of the body. - float32 GetAngularDamping() const; + const b3Vec3& GetAngularDamping() const; // Set the angular damping of the body. Zero is set by default. - void SetAngularDamping(float32 damping); + void SetAngularDamping(const b3Vec3& damping); // Get the gravity scale of the body. - float32 GetGravityScale() const; + const b3Vec3& GetGravityScale() const; // Set the gravity scale of the body. One is set by default. - void SetGravityScale(float32 scale); + void SetGravityScale(const b3Vec3& scale); // See if the body is awake. bool IsAwake() const; @@ -278,6 +303,27 @@ public: // Set the user data to the body. void SetUserData(void* _userData); + // Set the fixed rotation along the world axes. + void SetFixedRotation(bool flagX, bool flagY, bool flagZ); + + // Set the linear sleep tolerance in meters per second. + void SetLinearSleepTolerance(scalar tolerance); + + // Get the linear sleep tolerance in meters per second. + scalar GetLinearSleepTolerance() const; + + // Set the angular sleep tolerance in radians per second. + void SetAngularSleepTolerance(scalar tolerance); + + // Get the angular sleep tolerance in radians per second. + scalar GetAngularSleepTolerance() const; + + // Set if automatic sleeping is allowed. + void SetSleepingAllowed(bool bit); + + // Is automatic sleeping allowed? + bool IsSleepingAllowed() const; + // Get the next body in the world body list. const b3Body* GetNext() const; b3Body* GetNext(); @@ -303,9 +349,14 @@ private: friend class b3RevoluteJoint; friend class b3SphereJoint; friend class b3ConeJoint; + friend class b3FrictionJoint; + friend class b3MotorJoint; + friend class b3PrismaticJoint; + friend class b3WheelJoint; friend class b3List2; + friend class b3Cloth; friend class b3ClothSolver; friend class b3ClothContactSolver; @@ -317,9 +368,10 @@ private: { e_awakeFlag = 0x0001, e_islandFlag = 0x0002, - e_fixedRotationX = 0x0004, - e_fixedRotationY = 0x0008, - e_fixedRotationZ = 0x0010, + e_autoSleepFlag = 0x0004, + e_fixedRotationX = 0x0008, + e_fixedRotationY = 0x0010, + e_fixedRotationZ = 0x0020 }; b3Body(const b3BodyDef& def, b3World* world); @@ -343,7 +395,11 @@ private: b3BodyType m_type; u32 m_islandID; u32 m_flags; - float32 m_sleepTime; + + // Body sleeping + scalar m_linearSleepTolerance; + scalar m_angularSleepTolerance; + scalar m_sleepTime; // The shapes attached to this body. b3List1 m_shapeList; @@ -355,10 +411,10 @@ private: void* m_userData; // Body mass. - float32 m_mass; + scalar m_mass; // Inverse body mass. - float32 m_invMass; + scalar m_invMass; // Inertia about the body local center of mass. b3Mat33 m_I; @@ -374,9 +430,9 @@ private: b3Vec3 m_linearVelocity; b3Vec3 m_angularVelocity; - float32 m_linearDamping; - float32 m_angularDamping; - float32 m_gravityScale; + b3Vec3 m_linearDamping; + b3Vec3 m_angularDamping; + b3Vec3 m_gravityScale; // Motion proxy for CCD. b3Sweep m_sweep; @@ -437,30 +493,56 @@ inline b3List1& b3Body::GetShapeList() return m_shapeList; } +inline const b3List2& b3Body::GetJointList() const +{ + return m_jointEdges; +} + +inline b3List2& b3Body::GetJointList() +{ + return m_jointEdges; +} + inline const b3Transform& b3Body::GetTransform() const { return m_xf; } -inline void b3Body::SetTransform(const b3Vec3& position, const b3Vec3& axis, float32 angle) +inline void b3Body::SetTransform(const b3Vec3& position, const b3Quat& orientation) { - b3Quat q = b3Quat(axis, angle); - - m_xf.position = position; - m_xf.rotation = b3QuatMat33(q); + m_xf.translation = position; + m_xf.rotation = orientation; m_sweep.worldCenter = b3Mul(m_xf, m_sweep.localCenter); - m_sweep.orientation = q; + m_sweep.orientation = orientation; m_sweep.worldCenter0 = m_sweep.worldCenter; m_sweep.orientation0 = m_sweep.orientation; + m_worldInvI = b3RotateToFrame(m_invI, orientation); + + SynchronizeShapes(); +} + +inline void b3Body::SetTransform(const b3Vec3& position, const b3Mat33& orientation) +{ + m_xf.translation = position; + m_xf.rotation = b3Mat33Quat(orientation); + + m_sweep.worldCenter = b3Mul(m_xf, m_sweep.localCenter); + m_sweep.orientation = m_xf.rotation; + + m_sweep.worldCenter0 = m_sweep.worldCenter; + m_sweep.orientation0 = m_sweep.orientation; + + m_worldInvI = b3RotateToFrame(m_invI, orientation); + SynchronizeShapes(); } inline b3Vec3 b3Body::GetPosition() const { - return m_xf.position; + return m_xf.translation; } inline b3Quat b3Body::GetOrientation() const @@ -480,7 +562,7 @@ inline b3Vec3 b3Body::GetLocalCenter() const inline b3Vec3 b3Body::GetLocalVector(const b3Vec3& vector) const { - return b3MulT(m_xf.rotation, vector); + return b3MulC(m_xf.rotation, vector); } inline b3Vec3 b3Body::GetWorldVector(const b3Vec3& localVector) const @@ -498,6 +580,16 @@ inline b3Vec3 b3Body::GetWorldPoint(const b3Vec3& point) const return b3Mul(m_xf, point); } +inline b3Quat b3Body::GetLocalFrame(const b3Quat& frame) const +{ + return b3MulC(m_sweep.orientation, frame); +} + +inline b3Quat b3Body::GetWorldFrame(const b3Quat& localFrame) const +{ + return b3Mul(m_sweep.orientation, localFrame); +} + inline b3Transform b3Body::GetLocalFrame(const b3Transform& xf) const { return b3MulT(m_xf, xf); @@ -525,13 +617,13 @@ inline void b3Body::SetAwake(bool flag) if (!IsAwake()) { m_flags |= e_awakeFlag; - m_sleepTime = 0.0f; + m_sleepTime = scalar(0); } } else { m_flags &= ~e_awakeFlag; - m_sleepTime = 0.0f; + m_sleepTime = scalar(0); m_force.SetZero(); m_torque.SetZero(); m_linearVelocity.SetZero(); @@ -539,32 +631,32 @@ inline void b3Body::SetAwake(bool flag) } } -inline float32 b3Body::GetLinearDamping() const +inline const b3Vec3& b3Body::GetLinearDamping() const { return m_linearDamping; } -inline void b3Body::SetLinearDamping(float32 damping) +inline void b3Body::SetLinearDamping(const b3Vec3& damping) { m_linearDamping = damping; } -inline float32 b3Body::GetAngularDamping() const +inline const b3Vec3& b3Body::GetAngularDamping() const { return m_angularDamping; } -inline void b3Body::SetAngularDamping(float32 damping) +inline void b3Body::SetAngularDamping(const b3Vec3& damping) { m_angularDamping = damping; } -inline float32 b3Body::GetGravityScale() const +inline const b3Vec3& b3Body::GetGravityScale() const { return m_gravityScale; } -inline void b3Body::SetGravityScale(float32 scale) +inline void b3Body::SetGravityScale(const b3Vec3& scale) { if (m_type != e_staticBody) { @@ -589,7 +681,7 @@ inline void b3Body::SetLinearVelocity(const b3Vec3& linearVelocity) return; } - if (b3Dot(linearVelocity, linearVelocity) > 0.0f) + if (b3Dot(linearVelocity, linearVelocity) > scalar(0)) { SetAwake(true); } @@ -609,7 +701,7 @@ inline void b3Body::SetAngularVelocity(const b3Vec3& angularVelocity) return; } - if (b3Dot(angularVelocity, angularVelocity) > 0.0f) + if (b3Dot(angularVelocity, angularVelocity) > scalar(0)) { SetAwake(true); } @@ -617,12 +709,12 @@ inline void b3Body::SetAngularVelocity(const b3Vec3& angularVelocity) m_angularVelocity = angularVelocity; } -inline float32 b3Body::GetMass() const +inline scalar b3Body::GetMass() const { return m_mass; } -inline float32 b3Body::GetInverseMass() const +inline scalar b3Body::GetInverseMass() const { return m_invMass; } @@ -637,24 +729,24 @@ inline const b3Mat33& b3Body::GetInertia() const return m_I; } -inline float32 b3Body::GetLinearEnergy() const +inline scalar b3Body::GetLinearEnergy() const { b3Vec3 P = m_mass * m_linearVelocity; return b3Dot(P, m_linearVelocity); } -inline float32 b3Body::GetAngularEnergy() const +inline scalar b3Body::GetAngularEnergy() const { b3Mat33 I = b3RotateToFrame(m_I, m_xf.rotation); b3Vec3 L = I * m_angularVelocity; return b3Dot(L, m_angularVelocity); } -inline float32 b3Body::GetEnergy() const +inline scalar b3Body::GetEnergy() const { - float32 e1 = GetLinearEnergy(); - float32 e2 = GetAngularEnergy(); - return 0.5f * (e1 + e2); + scalar e1 = GetLinearEnergy(); + scalar e2 = GetAngularEnergy(); + return scalar(0.5) * (e1 + e2); } inline void b3Body::ApplyForce(const b3Vec3& force, const b3Vec3& point, bool wake) @@ -749,4 +841,32 @@ inline void b3Body::ApplyAngularImpulse(const b3Vec3& impulse, bool wake) } } +inline scalar b3Body::GetLinearSleepTolerance() const +{ + return m_linearSleepTolerance; +} + +inline scalar b3Body::GetAngularSleepTolerance() const +{ + return m_angularSleepTolerance; +} + +inline void b3Body::SetSleepingAllowed(bool flag) +{ + if (flag) + { + m_flags |= e_autoSleepFlag; + } + else + { + m_flags &= ~e_autoSleepFlag; + SetAwake(true); + } +} + +inline bool b3Body::IsSleepingAllowed() const +{ + return (m_flags & e_autoSleepFlag) == e_autoSleepFlag; +} + #endif diff --git a/include/bounce/dynamics/contact_manager.h b/include/bounce/dynamics/contact_manager.h index 5be9470..a59dc35 100644 --- a/include/bounce/dynamics/contact_manager.h +++ b/include/bounce/dynamics/contact_manager.h @@ -22,9 +22,9 @@ #include #include #include +#include class b3Shape; -class b3Contact; class b3ContactFilter; class b3ContactListener; struct b3MeshContactLink; @@ -51,7 +51,8 @@ public: b3BlockPool m_convexBlocks; b3BlockPool m_meshBlocks; - + b3BlockPool* m_allocators[e_maxContact]; + b3BroadPhase m_broadPhase; b3List2 m_contactList; b3List2 m_meshContactList; diff --git a/include/bounce/dynamics/contacts/collide/clip.h b/include/bounce/dynamics/contacts/collide/clip.h index ced23e1..2973fdd 100644 --- a/include/bounce/dynamics/contacts/collide/clip.h +++ b/include/bounce/dynamics/contacts/collide/clip.h @@ -27,18 +27,18 @@ // A combination of features used to uniquely identify a vertex on a feature. struct b3FeaturePair { - u32 inEdge1; // incoming edge on hull 1 - u32 outEdge1; // outgoing edge on hull 1 - u32 inEdge2; // incoming edge on hull 2 + u32 inEdge1; // incoming side plane edge on hull 1 + u32 outEdge1; // outgoing side plane edge on hull 1 + u32 inEdge2; // incoming edge on hull 2 u32 outEdge2; // outgoing edge on hull 2 }; -inline b3FeaturePair b3MakePair(u32 inEdge1, u32 inEdge2, u32 outEdge1, u32 outEdge2) +inline b3FeaturePair b3MakePair(u32 inEdge1, u32 outEdge1, u32 inEdge2, u32 outEdge2) { b3FeaturePair out; out.inEdge1 = inEdge1; - out.inEdge2 = inEdge2; out.outEdge1 = outEdge1; + out.inEdge2 = inEdge2; out.outEdge2 = outEdge2; return out; } @@ -57,7 +57,7 @@ typedef b3Array b3ClipPolygon; struct b3ClipPlane { b3Plane plane; - u32 id; + u32 edge; }; struct b3Hull; @@ -90,10 +90,10 @@ u32 b3ClipEdgeToFace(b3ClipVertex vOut[2], // Clip a segment by a hull face (side planes). // Return the number of output points. u32 b3ClipEdgeToFace(b3ClipVertex vOut[2], - const b3ClipVertex vIn[2], const b3Transform& xf, float32 r, u32 index, const b3Hull* hull); + const b3ClipVertex vIn[2], const b3Transform& xf, scalar r, u32 index, const b3Hull* hull); // Clip a polygon by a hull face (side planes). void b3ClipPolygonToFace(b3ClipPolygon& pOut, - const b3ClipPolygon& pIn, const b3Transform& xf, float32 r, u32 index, const b3Hull* hull); + const b3ClipPolygon& pIn, const b3Transform& xf, scalar r, u32 index, const b3Hull* hull); #endif \ No newline at end of file diff --git a/include/bounce/dynamics/contacts/collide/collide.h b/include/bounce/dynamics/contacts/collide/collide.h index f3741b9..ede932e 100644 --- a/include/bounce/dynamics/contacts/collide/collide.h +++ b/include/bounce/dynamics/contacts/collide/collide.h @@ -22,14 +22,14 @@ #include #include #include -#include -#include +#include +#include class b3Shape; class b3SphereShape; class b3CapsuleShape; +class b3TriangleShape; class b3HullShape; -class b3MeshShape; struct b3Manifold; @@ -70,25 +70,41 @@ void b3CollideSphereAndSphere(b3Manifold& manifold, const b3Transform& xf1, const b3SphereShape* shape1, const b3Transform& xf2, const b3SphereShape* shape2); -// Compute a manifold for a sphere and a hull. -void b3CollideSphereAndHull(b3Manifold& manifold, - const b3Transform& xf1, const b3SphereShape* shape1, - const b3Transform& xf2, const b3HullShape* shape2); - -// Compute a manifold for a sphere and a capsule. -void b3CollideSphereAndCapsule(b3Manifold& manifold, - const b3Transform& xf1, const b3SphereShape* shape1, - const b3Transform& xf2, const b3CapsuleShape* shape2); +// Compute a manifold for a capsule and a sphere. +void b3CollideCapsuleAndSphere(b3Manifold& manifold, + const b3Transform& xf1, const b3CapsuleShape* shape1, + const b3Transform& xf2, const b3SphereShape* shape2); // Compute a manifold for two capsules. void b3CollideCapsuleAndCapsule(b3Manifold& manifold, const b3Transform& xf1, const b3CapsuleShape* shape1, const b3Transform& xf2, const b3CapsuleShape* shape2); -// Compute a manifold for a capsule and a hull. -void b3CollideCapsuleAndHull(b3Manifold& manifold, - const b3Transform& xf1, const b3CapsuleShape* shape1, - const b3Transform& xf2, const b3HullShape* shape2); +// Compute a manifold for a triangle and a sphere. +void b3CollideTriangleAndSphere(b3Manifold& manifold, + const b3Transform& xf1, const b3TriangleShape* shape1, + const b3Transform& xf2, const b3SphereShape* shape2); + +// Compute a manifold for a triangle and a capsule. +void b3CollideTriangleAndCapsule(b3Manifold& manifold, + const b3Transform& xf1, const b3TriangleShape* shape1, + const b3Transform& xf2, const b3CapsuleShape* shape2); + +// Compute a manifold for a triangle and a hull. +void b3CollideTriangleAndHull(b3Manifold& manifold, + const b3Transform& xf1, const b3TriangleShape* shape1, + const b3Transform& xf2, const b3HullShape* shape2, + b3ConvexCache* cache); + +// Compute a manifold for a hull and a sphere. +void b3CollideHullAndSphere(b3Manifold& manifold, + const b3Transform& xf1, const b3HullShape* shape1, + const b3Transform& xf2, const b3SphereShape* shape2); + +// Compute a manifold for a hull and a capsule. +void b3CollideHullAndCapsule(b3Manifold& manifold, + const b3Transform& xf1, const b3HullShape* shape1, + const b3Transform& xf2, const b3CapsuleShape* shape2); // Compute a manifold for two hulls. void b3CollideHullAndHull(b3Manifold& manifold, diff --git a/include/bounce/dynamics/contacts/contact.h b/include/bounce/dynamics/contacts/contact.h index c934c5f..9790323 100644 --- a/include/bounce/dynamics/contacts/contact.h +++ b/include/bounce/dynamics/contacts/contact.h @@ -22,8 +22,11 @@ #include #include #include +#include #include +class b3BlockPool; + class b3Shape; class b3Body; class b3Contact; @@ -53,12 +56,6 @@ struct b3OverlappingPair b3ContactEdge edgeB; }; -// todo -struct b3TOIEvent -{ - float32 t; -}; - enum b3ContactType { e_convexContact, @@ -66,6 +63,23 @@ enum b3ContactType e_maxContact }; +typedef b3Contact* b3ContactCreateFcn(b3Shape* shapeA, b3Shape* shapeB, b3BlockPool* allocator); +typedef void b3ContactDestroyFcn(b3Contact* contact, b3BlockPool* allocator); + +struct b3ContactRegister +{ + b3ContactRegister() + { + createFcn = nullptr; + destroyFcn = nullptr; + } + + b3ContactType contactType; + b3ContactCreateFcn* createFcn; + b3ContactDestroyFcn* destroyFcn; + bool primary; +}; + class b3Contact { public: @@ -96,6 +110,12 @@ public: // Are the shapes in this contact overlapping? bool IsOverlapping() const; + // Has this contact at least one sensor shape? + bool IsSensorContact() const; + + // Has this contact at least one dynamic body? + bool HasDynamicBody() const; + // Get the next contact in the world contact list. const b3Contact* GetNext() const; b3Contact* GetNext(); @@ -113,32 +133,42 @@ protected: e_islandFlag = 0x0002, }; - b3Contact() { } + b3Contact(b3Shape* shapeA, b3Shape* shapeB); virtual ~b3Contact() { } + static b3ContactRegister s_registers[e_maxShapes][e_maxShapes]; + static bool s_initialized; + + static void AddPrimaryRegister(b3ContactCreateFcn* createFcn, b3ContactDestroyFcn* destoryFcn, + b3ShapeType type1, b3ShapeType type2, + b3ContactType contactType); + + static void InitializePrimaryRegisters(); + + // Factory create. + static b3Contact* Create(b3Shape* shapeA, b3Shape* shapeB, b3BlockPool* allocators[e_maxContact]); + + // Factory destroy. + static void Destroy(b3Contact* contact, b3BlockPool* allocators[e_maxContact]); + // Update the contact state. void Update(b3ContactListener* listener); // Test if the shapes in this contact are overlapping. virtual bool TestOverlap() = 0; - // Initialize contact constraits. + // Initialize contact constraints. virtual void Collide() = 0; b3ContactType m_type; u32 m_flags; b3OverlappingPair m_pair; - // Collision event from discrete collision to - // discrete physics. + // Contact manifolds. u32 m_manifoldCapacity; b3Manifold* m_manifolds; u32 m_manifoldCount; - // Time of impact event from continuous collision - // to continuous physics. - //b3TOIEvent m_toi; - // Links to the world contact list. b3Contact* m_prev; b3Contact* m_next; diff --git a/include/bounce/dynamics/contacts/contact_cluster.h b/include/bounce/dynamics/contacts/contact_cluster.h index f2a0de7..bf576f0 100644 --- a/include/bounce/dynamics/contacts/contact_cluster.h +++ b/include/bounce/dynamics/contacts/contact_cluster.h @@ -81,7 +81,7 @@ public: // void Run(b3Manifold mOut[3], u32& numOut, const b3Manifold* mIn, u32 numIn, - const b3Transform& xfA, float32 radiusA, const b3Transform& xfB, float32 radiusB); + const b3Transform& xfA, scalar radiusA, const b3Transform& xfB, scalar radiusB); // void Solve(); @@ -97,8 +97,8 @@ private: u32 m_iterations; - b3StackArray m_observations; - b3StackArray m_clusters; + b3StackArray m_observations; + b3StackArray m_clusters; }; inline void b3ClusterSolver::AddObservation(const b3Observation& observation) diff --git a/include/bounce/dynamics/contacts/contact_solver.h b/include/bounce/dynamics/contacts/contact_solver.h index 51a3f4e..76284f4 100644 --- a/include/bounce/dynamics/contacts/contact_solver.h +++ b/include/bounce/dynamics/contacts/contact_solver.h @@ -45,15 +45,15 @@ struct b3PositionConstraintManifold struct b3ContactPositionConstraint { u32 indexA; - float32 invMassA; + scalar invMassA; b3Mat33 localInvIA; - float32 radiusA; + scalar radiusA; b3Vec3 localCenterA; u32 indexB; b3Vec3 localCenterB; - float32 invMassB; + scalar invMassB; b3Mat33 localInvIB; - float32 radiusB; + scalar radiusB; b3PositionConstraintManifold* manifolds; u32 manifoldCount; }; @@ -64,9 +64,9 @@ struct b3VelocityConstraintPoint b3Vec3 rB; b3Vec3 normal; - float32 normalMass; - float32 normalImpulse; - float32 velocityBias; + scalar normalMass; + scalar normalImpulse; + scalar velocityBias; }; struct b3VelocityConstraintManifold @@ -75,41 +75,32 @@ struct b3VelocityConstraintManifold b3Vec3 rB; b3Vec3 normal; - b3Vec3 tangent1; - b3Vec3 tangent2; - //float32 leverArm; + scalar motorImpulse; + scalar motorMass; + scalar motorSpeed; + + b3Vec3 tangent1; + scalar tangentSpeed1; + b3Vec3 tangent2; + scalar tangentSpeed2; b3Mat22 tangentMass; b3Vec2 tangentImpulse; - float32 motorImpulse; - float32 motorMass; - + b3VelocityConstraintPoint* points; u32 pointCount; }; -// The idea is to allow anything to bounce off an inelastic surface. -inline float32 b3MixRestitution(float32 e1, float32 e2) -{ - return b3Max(e1, e2); -} - -// The idea is to drive the restitution to zero. -inline float32 b3MixFriction(float32 u1, float32 u2) -{ - return b3Sqrt(u1 * u2); -} - struct b3ContactVelocityConstraint { u32 indexA; - float32 invMassA; + scalar invMassA; b3Mat33 invIA; - float32 invMassB; + scalar invMassB; u32 indexB; b3Mat33 invIB; - float32 friction; - float32 restitution; + scalar friction; + scalar restitution; b3VelocityConstraintManifold* manifolds; u32 manifoldCount; }; @@ -122,9 +113,21 @@ struct b3ContactSolverDef b3Contact** contacts; u32 count; b3StackAllocator* allocator; - float32 dt; + scalar dt; }; +// The idea is to allow anything to bounce off an inelastic surface. +inline scalar b3MixRestitution(scalar e1, scalar e2) +{ + return b3Max(e1, e2); +} + +// The idea is to drive the restitution to zero. +inline scalar b3MixFriction(scalar u1, scalar u2) +{ + return b3Sqrt(u1 * u2); +} + class b3ContactSolver { public: @@ -146,7 +149,7 @@ protected: b3ContactPositionConstraint* m_positionConstraints; b3ContactVelocityConstraint* m_velocityConstraints; u32 m_count; - float32 m_dt, m_invDt; + scalar m_dt, m_invDt; b3StackAllocator* m_allocator; }; diff --git a/include/bounce/dynamics/contacts/convex_contact.h b/include/bounce/dynamics/contacts/convex_contact.h index 0610b3e..5998e17 100644 --- a/include/bounce/dynamics/contacts/convex_contact.h +++ b/include/bounce/dynamics/contacts/convex_contact.h @@ -26,8 +26,8 @@ class b3ConvexContact : public b3Contact { public: -private: - friend class b3ContactManager; + static b3Contact* Create(b3Shape* shapeA, b3Shape* shapeB, b3BlockPool* allocator); + static void Destroy(b3Contact* contact, b3BlockPool* allocator); b3ConvexContact(b3Shape* shapeA, b3Shape* shapeB); ~b3ConvexContact() { } diff --git a/include/bounce/dynamics/contacts/manifold.h b/include/bounce/dynamics/contacts/manifold.h index b47e763..2607fbe 100644 --- a/include/bounce/dynamics/contacts/manifold.h +++ b/include/bounce/dynamics/contacts/manifold.h @@ -71,8 +71,11 @@ struct b3ManifoldPoint b3ManifoldPointKey key; // point identifier - float32 normalImpulse; // normal impulse - u32 persisting; // is this point persistent? + scalar normalImpulse; // normal impulse + u64 persistCount; // number of time steps this point is persisting + + b3FeaturePair featurePair; // internal use + bool edgeContact; // internal use }; // A contact manifold is a group of contact points with similar contact normal. @@ -89,21 +92,25 @@ struct b3Manifold u32 pointCount; // number of manifold points b3Vec2 tangentImpulse; - float32 motorImpulse; + scalar motorImpulse; + + scalar motorSpeed; // target angular speed along the normal + scalar tangentSpeed1; // target speed along the first tangent + scalar tangentSpeed2; // target speed along the second tangent }; struct b3WorldManifoldPoint { - void Initialize(const b3ManifoldPoint* p, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB); + void Initialize(const b3ManifoldPoint* p, scalar rA, const b3Transform& xfA, scalar rB, const b3Transform& xfB); b3Vec3 point; b3Vec3 normal; - float32 separation; + scalar separation; }; struct b3WorldManifold { - void Initialize(const b3Manifold* m, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB); + void Initialize(const b3Manifold* m, scalar rA, const b3Transform& xfA, scalar rB, const b3Transform& xfB); u32 pointCount; b3WorldManifoldPoint points[B3_MAX_MANIFOLD_POINTS]; diff --git a/include/bounce/dynamics/contacts/mesh_contact.h b/include/bounce/dynamics/contacts/mesh_contact.h index 0a626e4..e5fce39 100644 --- a/include/bounce/dynamics/contacts/mesh_contact.h +++ b/include/bounce/dynamics/contacts/mesh_contact.h @@ -22,7 +22,7 @@ #include #include #include -#include +#include // This structure helps replicate the convex contact per convex-triangle pair scenario, // but efficiently. There is no need to store a manifold here since they're reduced @@ -46,10 +46,8 @@ struct b3MeshContactLink class b3MeshContact : public b3Contact { public: -private: - friend class b3ContactManager; - friend class b3List2; - friend class b3StaticTree; + static b3Contact* Create(b3Shape* shapeA, b3Shape* shapeB, b3BlockPool* allocator); + static void Destroy(b3Contact* contact, b3BlockPool* allocator); b3MeshContact(b3Shape* shapeA, b3Shape* shapeB); ~b3MeshContact(); @@ -57,12 +55,10 @@ private: bool TestOverlap(); void Collide(); - - void CollideSphere(); void SynchronizeShapes(); - bool MoveAABB(const b3AABB3& aabb, const b3Vec3& displacement); + bool MoveAABB(const b3AABB& aabb, const b3Vec3& displacement); void FindNewPairs(); @@ -70,10 +66,10 @@ private: bool Report(u32 proxyId); // Did the AABB move significantly? - bool m_aabbMoved; + bool m_aabbBMoved; - // The AABB A relative to shape B's origin. - b3AABB3 m_aabbA; + // The AABB B relative to shape A's origin. + b3AABB m_aabbB; // Triangles potentially overlapping with the first shape. u32 m_triangleCapacity; diff --git a/include/bounce/dynamics/island.h b/include/bounce/dynamics/island.h index 2a74da7..231a147 100644 --- a/include/bounce/dynamics/island.h +++ b/include/bounce/dynamics/island.h @@ -22,6 +22,7 @@ #include class b3StackAllocator; +class b3ContactListener; class b3Contact; class b3Joint; class b3Body; @@ -29,10 +30,12 @@ struct b3Velocity; struct b3Position; struct b3Profile; +struct b3ContactVelocityConstraint; + class b3Island { public : - b3Island(b3StackAllocator* stack, u32 bodyCapacity, u32 contactCapacity, u32 jointCapacity); + b3Island(b3StackAllocator* stack, u32 bodyCapacity, u32 contactCapacity, u32 jointCapacity, b3ContactListener* listener); ~b3Island(); void Clear(); @@ -41,7 +44,7 @@ public : void Add(b3Contact* contact); void Add(b3Joint* joint); - void Solve(const b3Vec3& gravity, float32 dt, u32 velocityIterations, u32 positionIterations, u32 flags); + void Solve(const b3Vec3& gravity, scalar dt, u32 velocityIterations, u32 positionIterations, u32 flags); private : enum b3IslandFlags { @@ -51,8 +54,11 @@ private : friend class b3World; + void Report(); + b3StackAllocator* m_allocator; - + b3ContactListener* m_listener; + b3Body** m_bodies; u32 m_bodyCapacity; u32 m_bodyCount; diff --git a/include/bounce/dynamics/joints/cone_joint.h b/include/bounce/dynamics/joints/cone_joint.h index 1e092b7..16295e4 100644 --- a/include/bounce/dynamics/joints/cone_joint.h +++ b/include/bounce/dynamics/joints/cone_joint.h @@ -26,59 +26,84 @@ struct b3ConeJointDef : public b3JointDef b3ConeJointDef() { type = e_coneJoint; - localFrameA.SetIdentity(); - localFrameB.SetIdentity(); - enableLimit = false; - coneAngle = 0.0f; + localAnchorA.SetZero(); + localRotationA.SetIdentity(); + localAnchorB.SetZero(); + localRotationB.SetIdentity(); + referenceRotation.SetIdentity(); + enableConeLimit = false; + coneAngle = scalar(0); + enableTwistLimit = false; + lowerAngle = scalar(0); + upperAngle = scalar(0); } // Initialize this definition from bodies, cone axis, anchor point, and full cone angle in radians. - void Initialize(b3Body* bodyA, b3Body* bodyB, const b3Vec3& axis, const b3Vec3& anchor, float32 angle); + void Initialize(b3Body* bodyA, b3Body* bodyB, const b3Vec3& axis, const b3Vec3& anchor, scalar coneAngle); + + // The joint anchor relative to body A's origin. + b3Vec3 localAnchorA; // The joint frame relative to body A's frame. - b3Transform localFrameA; + b3Quat localRotationA; + + // The joint frame relative to body B's origin. + b3Vec3 localAnchorB; // The joint frame relative to body B's frame. - b3Transform localFrameB; + b3Quat localRotationB; + + // Rotation from A to B in reference state + b3Quat referenceRotation; // Enable cone angle limit. - bool enableLimit; + bool enableConeLimit; // The full cone angle in radians. - float32 coneAngle; + scalar coneAngle; + + // Enable the twist limit. + bool enableTwistLimit; + + // The lower twist angle in radians + scalar lowerAngle; + + // The upper twist angle in radians + scalar upperAngle; }; // This joint constrains the bodies to share a common point (cone tip). // It also constrains the relative rotation about an axis perpendicular // to the cone axis. // You can limit the relative rotation with a cone angle limit. -// This joint can be used to create structures such as ragdolls. +// You can limit the relative rotation around the cone axis with a +// twist limit. +// This joint was designed to create structures such as ragdolls. class b3ConeJoint : public b3Joint { public: - // Get the joint frame on body A in world coordinates. - b3Transform GetFrameA() const; - - // Get the joint frame on body B in world coordinates. - b3Transform GetFrameB() const; - - // Get the joint frame relative to body A's frame. - const b3Transform& GetLocalFrameA() const; - - // Get the joint frame relative to body B's frame. - const b3Transform& GetLocalFrameB() const; - - // Is the joint limit enabled? - bool IsLimitEnabled() const; - // Set the joint limit enabled. - void SetEnableLimit(bool bit); + void SetEnableConeLimit(bool bit); - // Get the cone angle in radians. - float32 GetConeAngle() const; + // Is the joint cone limit enabled? + bool IsConeLimitEnabled() const; // Set the cone angle in radians. - void SetConeAngle(float32 angle); + void SetConeAngle(scalar angle); + + // Get the cone angle in radians. + scalar GetConeAngle() const; + + // Set the joint twist limit enabled. + void SetEnableTwistLimit(bool bit); + + // Is the joint twist limit enabled? + bool IsTwistLimitEnabled() const; + + // Set/get the twist angles in radians. + void SetTwistLimits(scalar lowerAngle, scalar upperAngle); + scalar GetTwistLowerAngle() const; + scalar GetTwistUpperAngle() const; // Draw this joint. void Draw() const; @@ -98,16 +123,24 @@ private: virtual bool SolvePositionConstraints(const b3SolverData* data); // Solver shared - b3Transform m_localFrameA; - b3Transform m_localFrameB; - float32 m_coneAngle; - bool m_enableLimit; + b3Vec3 m_localAnchorA; + b3Quat m_localRotationA; + b3Vec3 m_localAnchorB; + b3Quat m_localRotationB; + b3Quat m_referenceRotation; + + scalar m_coneAngle; + bool m_enableConeLimit; + + scalar m_lowerAngle; + scalar m_upperAngle; + bool m_enableTwistLimit; // Solver temp u32 m_indexA; u32 m_indexB; - float32 m_mA; - float32 m_mB; + scalar m_mA; + scalar m_mB; b3Mat33 m_iA; b3Mat33 m_iB; @@ -117,17 +150,23 @@ private: b3Vec3 m_localCenterA; b3Vec3 m_localCenterB; - // Point-to-point + // Linear b3Vec3 m_rA; b3Vec3 m_rB; b3Mat33 m_mass; b3Vec3 m_impulse; - // Limit - b3Vec3 m_limitAxis; - float32 m_limitMass; - float32 m_limitImpulse; - b3LimitState m_limitState; + // Cone limit + b3Vec3 m_coneAxis; + scalar m_coneMass; + scalar m_coneImpulse; + b3LimitState m_coneState; + + // Twist limit + b3Vec3 m_twistAxis; + scalar m_twistMass; + scalar m_twistImpulse; + b3LimitState m_twistState; }; #endif \ No newline at end of file diff --git a/include/bounce/dynamics/joints/friction_joint.h b/include/bounce/dynamics/joints/friction_joint.h new file mode 100644 index 0000000..039346b --- /dev/null +++ b/include/bounce/dynamics/joints/friction_joint.h @@ -0,0 +1,123 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_FRICTION_JOINT_H +#define B3_FRICTION_JOINT_H + +#include + +struct b3FrictionJointDef : public b3JointDef +{ + b3FrictionJointDef() + { + type = e_frictionJoint; + localAnchorA.SetZero(); + localAnchorB.SetZero(); + maxForce = scalar(0); + maxTorque = scalar(0); + } + + // Initialize this definition from bodies and world anchor point. + void Initialize(b3Body* bodyA, b3Body* bodyB, const b3Vec3& anchor); + + // The anchor point relative to body A's origin + b3Vec3 localAnchorA; + + // The anchor point relative to body B's origin + b3Vec3 localAnchorB; + + // The maximum friction force in N. + scalar maxForce; + + // The maximum friction torque in N-m. + scalar maxTorque; +}; + +// Friction joint. +// This joint provides 3D linear and angular friction. +class b3FrictionJoint : public b3Joint +{ +public: + // Get the anchor point on body A in world coordinates. + b3Vec3 GetAnchorA() const; + + // Get the anchor point on body B in world coordinates. + b3Vec3 GetAnchorB() const; + + // Get the local anchor point relative to body A's origin. + const b3Vec3& GetLocalAnchorA() const + { + return m_localAnchorA; + } + + // Get the local anchor point relative to body B's origin. + const b3Vec3& GetLocalAnchorB() const + { + return m_localAnchorB; + } + + // Set the maximum friction force in N. + void SetMaxForce(scalar force); + + // Get the maximum friction force in N. + scalar GetMaxForce() const; + + // Set the maximum friction torque in N*m. + void SetMaxTorque(scalar torque); + + // Get the maximum friction torque in N*m. + scalar GetMaxTorque() const; + + // Draw this joint. + void Draw() const; +private: + friend class b3Joint; + friend class b3JointManager; + friend class b3JointSolver; + + b3FrictionJoint(const b3FrictionJointDef* def); + + virtual void InitializeConstraints(const b3SolverData* data); + virtual void WarmStart(const b3SolverData* data); + virtual void SolveVelocityConstraints(const b3SolverData* data); + virtual bool SolvePositionConstraints(const b3SolverData* data); + + // Solver shared + b3Vec3 m_localAnchorA; + b3Vec3 m_localAnchorB; + b3Vec3 m_linearImpulse; + b3Vec3 m_angularImpulse; + scalar m_maxForce; + scalar m_maxTorque; + + // Solver temp + u32 m_indexA; + u32 m_indexB; + scalar m_mA; + scalar m_mB; + b3Mat33 m_iA; + b3Mat33 m_iB; + + b3Vec3 m_rA; + b3Vec3 m_rB; + + b3Mat33 m_linearMass; + b3Mat33 m_angularMass; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/dynamics/joints/joint.h b/include/bounce/dynamics/joints/joint.h index 6130f3f..8f911fb 100644 --- a/include/bounce/dynamics/joints/joint.h +++ b/include/bounce/dynamics/joints/joint.h @@ -37,6 +37,10 @@ enum b3JointType e_revoluteJoint, e_sphereJoint, e_coneJoint, + e_frictionJoint, + e_motorJoint, + e_prismaticJoint, + e_wheelJoint, e_maxJoints, }; @@ -45,9 +49,9 @@ struct b3JointDef b3JointDef() { type = e_unknownJoint; - bodyA = NULL; - bodyB = NULL; - userData = NULL; + bodyA = nullptr; + bodyB = nullptr; + userData = nullptr; collideLinked = false; } diff --git a/include/bounce/dynamics/joints/joint_solver.h b/include/bounce/dynamics/joints/joint_solver.h index ef36fa9..db5877f 100644 --- a/include/bounce/dynamics/joints/joint_solver.h +++ b/include/bounce/dynamics/joints/joint_solver.h @@ -34,7 +34,7 @@ struct b3Jacobian struct b3JointSolverDef { - float32 dt; + scalar dt; u32 count; b3Joint** joints; b3Position* positions; diff --git a/include/bounce/dynamics/joints/motor_joint.h b/include/bounce/dynamics/joints/motor_joint.h new file mode 100644 index 0000000..28676ff --- /dev/null +++ b/include/bounce/dynamics/joints/motor_joint.h @@ -0,0 +1,135 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_MOTOR_JOINT_H +#define B3_MOTOR_JOINT_H + +#include + +// Motor joint definition. +struct b3MotorJointDef : public b3JointDef +{ + b3MotorJointDef() + { + type = e_motorJoint; + linearOffset.SetZero(); + angularOffset.SetIdentity(); + maxForce = scalar(0); + maxTorque = scalar(0); + correctionFactor = 0.2f; + } + + // Initialize the bodies and offsets using the current transforms. + void Initialize(b3Body* bodyA, b3Body* bodyB); + + // Position of body B minus the position of body A, relative body A's origin, in meters. + b3Vec3 linearOffset; + + // The body B rotation minus body A rotation. + b3Quat angularOffset; + + // Maximum motor force in N. + scalar maxForce; + + // Maximum torque in N-m. + scalar maxTorque; + + // Position correction factor in the range [0, 1]. + scalar correctionFactor; +}; + +// A motor joint can be used to control the relative movement between two bodies. +// Commonly it is used to control the motion of a dynamic body with respect +// to a static body. +class b3MotorJoint : public b3Joint +{ +public: + b3Vec3 GetAnchorA() const; + b3Vec3 GetAnchorB() const; + + // Set the target linear offset, in frame A, in meters. + void SetLinearOffset(const b3Vec3& linearOffset); + + // Get the target linear offset, in frame A, in meters. + const b3Vec3& GetLinearOffset() const; + + // Set the target angular offset, in frame A, in meters. + void SetAngularOffset(const b3Quat& angularOffset); + + // Get the target angular offset, in frame A, in meters. + const b3Quat& GetAngularOffset() const; + + // Set the maximum friction force in N. + void SetMaxForce(scalar force); + + // Get the maximum friction force in N. + scalar GetMaxForce() const; + + // Set the maximum friction torque in N-m. + void SetMaxTorque(scalar force); + + // Get the maximum friction torque in N-m. + scalar GetMaxTorque() const; + + // Set the position correction factor in the range [0, 1]. + void SetCorrectionFactor(scalar factor); + + // Get the position correction factor in the range [0, 1]. + scalar GetCorrectionFactor() const; + + // Draw this joint. + void Draw() const; +private: + friend class b3Joint; + friend class b3JointManager; + friend class b3JointSolver; + + b3MotorJoint(const b3MotorJointDef* def); + + virtual void InitializeConstraints(const b3SolverData* data); + virtual void WarmStart(const b3SolverData* data); + virtual void SolveVelocityConstraints(const b3SolverData* data); + virtual bool SolvePositionConstraints(const b3SolverData* data); + + // Solver shared + b3Vec3 m_linearOffset; + b3Quat m_angularOffset; + b3Vec3 m_linearImpulse; + b3Vec3 m_angularImpulse; + scalar m_correctionFactor; + scalar m_maxForce; + scalar m_maxTorque; + + // Solver temp + u32 m_indexA; + u32 m_indexB; + scalar m_mA; + scalar m_mB; + b3Mat33 m_iA; + b3Mat33 m_iB; + b3Vec3 m_localCenterA; + b3Vec3 m_localCenterB; + b3Vec3 m_linearError; + b3Vec3 m_angularVelocity; + b3Vec3 m_rA; + b3Vec3 m_rB; + b3Mat33 m_linearMass; + b3Mat33 m_angularMass; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/dynamics/joints/mouse_joint.h b/include/bounce/dynamics/joints/mouse_joint.h index a2a35e0..f67dada 100644 --- a/include/bounce/dynamics/joints/mouse_joint.h +++ b/include/bounce/dynamics/joints/mouse_joint.h @@ -29,15 +29,27 @@ struct b3MouseJointDef : public b3JointDef { type = e_mouseJoint; target.SetZero(); - maxForce = 0.0f; + maxForce = scalar(0); + frequencyHz = scalar(6); + dampingRatio = scalar(0.8); } // The initial world target point. Initially is assumed - // to be coincident to the body anchor (satisfied constraint). + // to be coincident to the body anchor. b3Vec3 target; - // Maximum joint reaction force in newtons. - float32 maxForce; + // Maximum joint reaction force in N. + // Typically this is defined proportional to the body weight + // (k * m * g) + scalar maxForce; + + // The mass-spring-damper frequency in Hz + scalar frequencyHz; + + // The damping ration in the interval [0, 1] + // 0 = undamped spring + // 1 = critical damping + scalar dampingRatio; }; // A mouse joint is used to make a local point on a body @@ -56,6 +68,30 @@ public: // Set the world target point. void SetTarget(const b3Vec3& target); + + // Get the damper frequency in Hz. + scalar GetFrequency() const + { + return m_frequencyHz; + } + + // Set the damper frequency in Hz. + void SetFrequency(scalar frequency) + { + m_frequencyHz = frequency; + } + + // Get the damping ratio. + scalar GetDampingRatio() const + { + return m_dampingRatio; + } + + // Set the damping ratio. + void SetDampingRatio(scalar ratio) + { + m_dampingRatio = ratio; + } // Draw this joint. void Draw() const; @@ -74,17 +110,21 @@ private: // Solver shared b3Vec3 m_worldTargetA; b3Vec3 m_localAnchorB; - float32 m_maxForce; + scalar m_maxForce; + scalar m_frequencyHz; + scalar m_dampingRatio; + + b3Mat33 m_gamma; + b3Vec3 m_bias; // Solver temp u32 m_indexB; - float32 m_mB; + scalar m_mB; b3Mat33 m_iB; b3Vec3 m_localCenterB; b3Mat33 m_mass; b3Vec3 m_rB; b3Vec3 m_impulse; - b3Vec3 m_C; }; #endif \ No newline at end of file diff --git a/include/bounce/dynamics/joints/prismatic_joint.h b/include/bounce/dynamics/joints/prismatic_joint.h new file mode 100644 index 0000000..6df2adb --- /dev/null +++ b/include/bounce/dynamics/joints/prismatic_joint.h @@ -0,0 +1,208 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_PRISMATIC_JOINT_H +#define B3_PRISMATIC_JOINT_H + +#include + +// Prismatic joint definition. +// This requires defining an axis of motion and an anchor point. +// The definition uses local anchor points and a local axis so that the initial configuration +// can violate the constraint slightly. The joint translation is zero +// when the local anchor points coincide in world space. Using local +// anchors and a local axis helps when saving and loading a game. +struct b3PrismaticJointDef : public b3JointDef +{ + b3PrismaticJointDef() + { + type = e_prismaticJoint; + localAnchorA.SetZero(); + localAnchorB.SetZero(); + localAxisA.Set(scalar(1), scalar(0), scalar(0)); + referenceRotation.SetIdentity(); + enableLimit = false; + lowerTranslation = scalar(0); + upperTranslation = scalar(0); + enableMotor = false; + maxMotorForce = scalar(0); + motorSpeed = scalar(0); + } + + // Initialize the bodies, anchors, axis, and reference angle using the world + // anchor and unit world axis. + void Initialize(b3Body* bodyA, b3Body* bodyB, const b3Vec3& anchor, const b3Vec3& axis); + + // The local anchor point relative to bodyA's origin. + b3Vec3 localAnchorA; + + // The local anchor point relative to bodyB's origin. + b3Vec3 localAnchorB; + + // The local translation unit axis in body A's frame. + b3Vec3 localAxisA; + + // The constrained angle between the bodies. + b3Quat referenceRotation; + + // Enable/disable the joint limit. + bool enableLimit; + + // The lower translation limit, usually in meters. + scalar lowerTranslation; + + // The upper translation limit, usually in meters. + scalar upperTranslation; + + // Enable/disable the joint motor. + bool enableMotor; + + // The maximum motor torque, usually in N-m. + scalar maxMotorForce; + + // The desired motor speed in radians per second. + scalar motorSpeed; +}; + +// A prismatic joint. +// This joint provides translation (one degree of freedom) +// along an axis fixed in body A. +// Relative rotation is prevented. +// You can use a joint limit to restrict the range of motion +// and a joint motor to drive the motion or to model joint friction. +class b3PrismaticJoint : public b3Joint +{ +public: + b3Vec3 GetAnchorA() const; + b3Vec3 GetAnchorB() const; + + // The local anchor point relative to body A's origin. + const b3Vec3& GetLocalAnchorA() const { return m_localAnchorA; } + + // The local anchor point relative to body B's origin. + const b3Vec3& GetLocalAnchorB() const { return m_localAnchorB; } + + // The local joint axis relative to bodyA. + const b3Vec3& GetLocalAxisA() const { return m_localXAxisA; } + + // Get the reference rotation. + const b3Quat& GetReferenceRotation() const { return m_referenceRotation; } + + // Get the current joint translation, usually in meters. + scalar GetJointTranslation() const; + + // Get the current joint translation speed, usually in meters per second. + scalar GetJointSpeed() const; + + // Is the joint limit enabled? + bool IsLimitEnabled() const; + + // Enable/disable the joint limit. + void EnableLimit(bool flag); + + // Get the lower joint limit, usually in meters. + scalar GetLowerLimit() const; + + // Get the upper joint limit, usually in meters. + scalar GetUpperLimit() const; + + // Set the joint limits, usually in meters. + void SetLimits(scalar lower, scalar upper); + + // Is the joint motor enabled? + bool IsMotorEnabled() const; + + // Enable/disable the joint motor. + void EnableMotor(bool flag); + + // Set the motor speed, usually in meters per second. + void SetMotorSpeed(scalar speed); + + // Get the motor speed, usually in meters per second. + scalar GetMotorSpeed() const; + + // Set the maximum motor force, usually in N. + void SetMaxMotorForce(scalar force); + + // Get the maximum motor force, usually in N. + scalar GetMaxMotorForce() const { return m_maxMotorForce; } + + // Draw this joint + void Draw() const; +protected: + friend class b3Joint; + + b3PrismaticJoint(const b3PrismaticJointDef* def); + + void InitializeConstraints(const b3SolverData* data); + void WarmStart(const b3SolverData* data); + void SolveVelocityConstraints(const b3SolverData* data); + bool SolvePositionConstraints(const b3SolverData* data); + + // Solver shared + b3Vec3 m_localAnchorA; + b3Vec3 m_localAnchorB; + b3Vec3 m_localXAxisA; + b3Vec3 m_localYAxisA; + b3Vec3 m_localZAxisA; + b3Quat m_referenceRotation; + b3Vec2 m_linearImpulse; + b3Vec3 m_angularImpulse; + scalar m_limitImpulse; + scalar m_motorImpulse; + scalar m_lowerTranslation; + scalar m_upperTranslation; + scalar m_maxMotorForce; + scalar m_motorSpeed; + bool m_enableLimit; + bool m_enableMotor; + b3LimitState m_limitState; + + // Solver temp + u32 m_indexA; + u32 m_indexB; + b3Vec3 m_localCenterA; + b3Vec3 m_localCenterB; + b3Mat33 m_localInvIA; + b3Mat33 m_localInvIB; + scalar m_mA; + scalar m_mB; + b3Mat33 m_iA; + b3Mat33 m_iB; + + b3Vec3 m_axis; + b3Vec3 m_a1, m_a2; + scalar m_motorMass; + + b3Vec3 m_perp1; + b3Vec3 m_s1, m_s2; + + b3Vec3 m_perp2; + b3Vec3 m_s3, m_s4; + + b3Mat22 m_linearMass; + + b3Mat33 m_angularMass; +}; + +inline scalar b3PrismaticJoint::GetMotorSpeed() const +{ + return m_motorSpeed; +} + +#endif \ No newline at end of file diff --git a/include/bounce/dynamics/joints/revolute_joint.h b/include/bounce/dynamics/joints/revolute_joint.h index 12e707d..e196e65 100644 --- a/include/bounce/dynamics/joints/revolute_joint.h +++ b/include/bounce/dynamics/joints/revolute_joint.h @@ -32,15 +32,15 @@ struct b3RevoluteJointDef : public b3JointDef localRotationB.SetIdentity(); referenceRotation.SetIdentity(); enableLimit = false; - lowerAngle = 0.0f; - upperAngle = 0.0f; + lowerAngle = scalar(0); + upperAngle = scalar(0); enableMotor = false; - motorSpeed = 0.0f; - maxMotorTorque = 0.0f; + motorSpeed = scalar(0); + maxMotorTorque = scalar(0); } // Initialize this definition from hinge axis, anchor point, and the lower and upper angle limits in radians. - void Initialize(b3Body* bodyA, b3Body* bodyB, const b3Vec3& axis, const b3Vec3& anchor, float32 lowerAngle, float32 upperAngle); + void Initialize(b3Body* bodyA, b3Body* bodyB, const b3Vec3& axis, const b3Vec3& anchor, scalar lowerAngle, scalar upperAngle); // The joint anchor relative body A's origin. b3Vec3 localAnchorA; @@ -61,19 +61,19 @@ struct b3RevoluteJointDef : public b3JointDef bool enableLimit; // The hinge lower angle limit in radians. - float32 lowerAngle; + scalar lowerAngle; // The hinge upper angle limit in radians. - float32 upperAngle; + scalar upperAngle; // Enable the joint motor. bool enableMotor; // The desired motor speed in radians per second. - float32 motorSpeed; + scalar motorSpeed; // The maximum motor torque in Newton per meter. - float32 maxMotorTorque; + scalar maxMotorTorque; }; // A revolute joint constrains two bodies to share a point and an axis while @@ -105,13 +105,13 @@ public: void SetEnableLimit(bool bit); // Get the lower angle limit. - float32 GetLowerLimit() const; + scalar GetLowerLimit() const; // Get the upper angle limit. - float32 GetUpperLimit() const; + scalar GetUpperLimit() const; // Set the angle limits. - void SetLimits(float32 lowerAngle, float32 upperAngle); + void SetLimits(scalar lowerAngle, scalar upperAngle); // Is the joint motor enabled? bool IsMotorEnabled() const; @@ -120,16 +120,16 @@ public: void SetEnableMotor(bool bit); // Get the desired motor speed in radians per second. - float32 GetMotorSpeed() const; + scalar GetMotorSpeed() const; // Set the desired motor speed in radians per second. - void SetMotorSpeed(float32 speed); + void SetMotorSpeed(scalar speed); // Get the maximum motor torque in Newton per meter. - float32 GetMaxMotorTorque() const; + scalar GetMaxMotorTorque() const; // Set the maximum motor torque in Newton per meter. - void SetMaxMotorTorque(float32 torque); + void SetMaxMotorTorque(scalar torque); // Draw this joint. void Draw() const; @@ -155,18 +155,18 @@ private: b3Quat m_localRotationB; bool m_enableMotor; - float32 m_motorSpeed; - float32 m_maxMotorTorque; + scalar m_motorSpeed; + scalar m_maxMotorTorque; bool m_enableLimit; - float32 m_lowerAngle; - float32 m_upperAngle; + scalar m_lowerAngle; + scalar m_upperAngle; // Solver temp u32 m_indexA; u32 m_indexB; - float32 m_mA; - float32 m_mB; + scalar m_mA; + scalar m_mB; b3Mat33 m_iA; b3Mat33 m_iB; b3Vec3 m_localCenterA; @@ -174,30 +174,26 @@ private: b3Mat33 m_localInvIA; b3Mat33 m_localInvIB; - // Hinge motor - b3Vec3 m_motor_J1; // 1x3 (row) - b3Vec3 m_motor_J2; // 1x3 (row) - float32 m_motorMass; - float32 m_motorImpulse; + // Motor + scalar m_motorMass; + scalar m_motorImpulse; + b3Vec3 m_motorAxis; - // Hinge limit - // The limit axis and constraint space mass are the same as the motor's - b3LimitState m_limitState; // constraint state - float32 m_limitImpulse; + // Angle limit + // This reuses the motor Jacobian and mass + b3LimitState m_limitState; + scalar m_limitImpulse; - // Spherical + // Linear b3Vec3 m_rA; b3Vec3 m_rB; - b3Mat33 m_mass; - b3Vec3 m_impulse; + b3Mat33 m_linearMass; + b3Vec3 m_linearImpulse; - // Hinge - b3Mat23 m_J1; - b3Mat23 m_J2; - b3Mat32 m_J1T; - b3Mat32 m_J2T; - b3Mat22 m_K; - b3Vec2 m_axisImpulse; + // Angular + b3Vec3 m_a1, m_a2; + b3Mat22 m_angularMass; + b3Vec2 m_angularImpulse; }; #endif \ No newline at end of file diff --git a/include/bounce/dynamics/joints/sphere_joint.h b/include/bounce/dynamics/joints/sphere_joint.h index 16d82ab..9c81076 100644 --- a/include/bounce/dynamics/joints/sphere_joint.h +++ b/include/bounce/dynamics/joints/sphere_joint.h @@ -51,6 +51,18 @@ public: // Get the anchor point on body B in world coordinates. b3Vec3 GetAnchorB() const; + // Get the local anchor point relative to body A's origin. + const b3Vec3& GetLocalAnchorA() const + { + return m_localAnchorA; + } + + // Get the local anchor point relative to body B's origin. + const b3Vec3& GetLocalAnchorB() const + { + return m_localAnchorB; + } + // Draw this joint. void Draw() const; private: @@ -72,8 +84,8 @@ private: // Solver temp u32 m_indexA; u32 m_indexB; - float32 m_mA; - float32 m_mB; + scalar m_mA; + scalar m_mB; b3Mat33 m_iA; b3Mat33 m_iB; diff --git a/include/bounce/dynamics/joints/spring_joint.h b/include/bounce/dynamics/joints/spring_joint.h index 32f57db..8c4b68c 100644 --- a/include/bounce/dynamics/joints/spring_joint.h +++ b/include/bounce/dynamics/joints/spring_joint.h @@ -28,9 +28,9 @@ struct b3SpringJointDef : public b3JointDef type = e_springJoint; localAnchorA.SetZero(); localAnchorB.SetZero(); - length = 0.0f; - frequencyHz = 0.0f; - dampingRatio = 0.0f; + length = scalar(0); + frequencyHz = scalar(0); + dampingRatio = scalar(0); } // Initialize this definition from bodies and world anchors. @@ -43,16 +43,16 @@ struct b3SpringJointDef : public b3JointDef b3Vec3 localAnchorB; // The spring rest length. - float32 length; + scalar length; // The mass-spring-damper frequency in Hz // 0 = disable softness - float32 frequencyHz; + scalar frequencyHz; // The damping ration in the interval [0, 1] // 0 = undamped spring // 1 = critical damping - float32 dampingRatio; + scalar dampingRatio; }; // A spring joint constrains the bodies to rotate relative to each @@ -76,22 +76,22 @@ public: const b3Vec3& GetLocalAnchorB() const; // Get the natural spring length. - float32 GetLength() const; + scalar GetLength() const; // Set the natural spring length. - void SetLength(float32 length); + void SetLength(scalar length); // Get the damper frequency in Hz. - float32 GetFrequency() const; + scalar GetFrequency() const; // Set the damper frequency in Hz. - void SetFrequency(float32 frequency); + void SetFrequency(scalar frequency); // Get the damping ratio. - float32 GetDampingRatio() const; + scalar GetDampingRatio() const; // Set the damping ratio. - void SetDampingRatio(float32 ratio); + void SetDampingRatio(scalar ratio); // Draw this joint. void Draw() const; @@ -110,15 +110,15 @@ private: // Solver shared b3Vec3 m_localAnchorA; b3Vec3 m_localAnchorB; - float32 m_length; - float32 m_frequencyHz; - float32 m_dampingRatio; + scalar m_length; + scalar m_frequencyHz; + scalar m_dampingRatio; // Solver temp u32 m_indexA; u32 m_indexB; - float32 m_mA; - float32 m_mB; + scalar m_mA; + scalar m_mB; b3Mat33 m_iA; b3Mat33 m_iB; b3Vec3 m_localCenterA; @@ -126,13 +126,13 @@ private: b3Mat33 m_localInvIA; b3Mat33 m_localInvIB; - float32 m_bias; - float32 m_gamma; + scalar m_bias; + scalar m_gamma; b3Vec3 m_n; b3Vec3 m_rA; b3Vec3 m_rB; - float32 m_mass; - float32 m_impulse; + scalar m_mass; + scalar m_impulse; }; #endif \ No newline at end of file diff --git a/include/bounce/dynamics/joints/weld_joint.h b/include/bounce/dynamics/joints/weld_joint.h index 29cfb47..7f18b63 100644 --- a/include/bounce/dynamics/joints/weld_joint.h +++ b/include/bounce/dynamics/joints/weld_joint.h @@ -29,6 +29,8 @@ struct b3WeldJointDef : public b3JointDef localAnchorA.SetZero(); localAnchorB.SetZero(); referenceRotation.SetIdentity(); + frequencyHz = scalar(0); + dampingRatio = scalar(0); } // Initialize this definition from bodies and world anchor point. @@ -42,20 +44,79 @@ struct b3WeldJointDef : public b3JointDef // The initial relative rotation from body A to body B. b3Quat referenceRotation; + + // The mass-spring-damper frequency in Hertz. + // Disable softness with a value of 0. + // Rotation only. + scalar frequencyHz; + + // The damping ratio. + // 0 = no damping. + // 1 = critical damping. + scalar dampingRatio; }; // A weld joint removes the relative movement between two bodies. // You need to specify the relative rotation and the local anchor points. -// @todo Soft this constraint. class b3WeldJoint : public b3Joint { public: + // Set the joint anchor point in world coordinates. + // Calling this function will set the target relative rotation + // from body A to body B using the current body rotations. + void SetAnchor(const b3Vec3& anchor); + + // Set the target relative rotation from body A to body B. + void SetReferenceRotation(const b3Quat& referenceRotation); + // Get the anchor point on body A in world coordinates. b3Vec3 GetAnchorA() const; // Get the anchor point on body B in world coordinates. b3Vec3 GetAnchorB() const; + // Get the local anchor point relative to bodyA's origin. + const b3Vec3& GetLocalAnchorA() const + { + return m_localAnchorA; + } + + // Get the local anchor point relative to bodyB's origin. + const b3Vec3& GetLocalAnchorB() const + { + return m_localAnchorB; + } + + // Get the reference orientation. + const b3Quat& GetReferenceRotation() const + { + return m_referenceRotation; + } + + // Set the frequency in Hz. + void SetFrequency(scalar hz) + { + m_frequencyHz = hz; + } + + // Get the frequency in Hz. + scalar GetFrequency() const + { + return m_frequencyHz; + } + + // Set the damping ratio. + void SetDampingRatio(scalar ratio) + { + m_dampingRatio = ratio; + } + + // Get the damping ratio. + scalar GetDampingRatio() const + { + return m_dampingRatio; + } + // Draw this joint. void Draw() const; private: @@ -74,30 +135,32 @@ private: b3Vec3 m_localAnchorA; b3Vec3 m_localAnchorB; b3Quat m_referenceRotation; + scalar m_frequencyHz; + scalar m_dampingRatio; // Solver temp u32 m_indexA; u32 m_indexB; - float32 m_mA; - float32 m_mB; + scalar m_mA; + scalar m_mB; b3Mat33 m_iA; b3Mat33 m_iB; b3Vec3 m_localCenterA; b3Vec3 m_localCenterB; + b3Mat33 m_localInvIA; + b3Mat33 m_localInvIB; - // Point-to-point + // Linear b3Vec3 m_rA; b3Vec3 m_rB; - b3Vec3 m_impulse; - b3Mat33 m_mass; + b3Vec3 m_linearImpulse; + b3Mat33 m_linearMass; - // Weld constraint - b3Mat33 m_J1; - b3Mat33 m_J2; - b3Mat33 m_J1T; - b3Mat33 m_J2T; - b3Vec3 m_axisImpulse; - b3Mat33 m_K; + // Angular + b3Vec3 m_angularImpulse; + b3Mat33 m_angularMass; + b3Mat33 m_gamma; + b3Vec3 m_bias; }; #endif \ No newline at end of file diff --git a/include/bounce/dynamics/joints/wheel_joint.h b/include/bounce/dynamics/joints/wheel_joint.h new file mode 100644 index 0000000..70fa871 --- /dev/null +++ b/include/bounce/dynamics/joints/wheel_joint.h @@ -0,0 +1,242 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_WHEEL_JOINT_H +#define B3_WHEEL_JOINT_H + +#include + +// Wheel joint definition. +// This requires defining two axis and an anchor point. +// The anchor point usually coincides with the wheel position. +// The definition uses local anchor points and a local axes so that +// the initial configuration can violate the constraint slightly. +// The joint translation is zero when the local anchor points coincide in world space. +// Using local anchors and a local axes helps when saving and loading a game. +struct b3WheelJointDef : public b3JointDef +{ + b3WheelJointDef() + { + type = e_wheelJoint; + localAnchorA.SetZero(); + localAnchorB.SetZero(); + localAxisA.Set(scalar(0), scalar(1), scalar(0)); + localAxisB.Set(scalar(1), scalar(0), scalar(0)); + enableMotor = false; + maxMotorTorque = scalar(0); + motorSpeed = scalar(0); + frequencyHz = scalar(4); + dampingRatio = scalar(0.7); + } + + // Initialize the bodies, anchors, axis, and reference angle using the world + // anchor and world axes. + void Initialize(b3Body* bodyA, b3Body* bodyB, const b3Vec3& anchor, const b3Vec3& axisA, const b3Vec3& axisB); + + // The local anchor point relative to bodyA's origin. + b3Vec3 localAnchorA; + + // The local anchor point relative to bodyB's origin. + b3Vec3 localAnchorB; + + // The local translation axis in body A. + b3Vec3 localAxisA; + + // The local rotation axis in body B. + b3Vec3 localAxisB; + + // Enable/disable the joint motor. + bool enableMotor; + + // The maximum motor torque, usually in N-m. + scalar maxMotorTorque; + + // The desired motor velocity in radians per second. + scalar motorSpeed; + + // Suspension frequency, zero indicates no suspension + scalar frequencyHz; + + // Suspension damping ratio, one indicates critical damping + scalar dampingRatio; +}; + +// A wheel joint. +// This joint will keep the motion of the anchor point along a translation +// axis fixed in body A. +// This joint maintains the initial angle between an axis on +// body A and another axis on body B. +// The anchor point usually coincides with the wheel position. +// You can control the spring parameters along the translation axis on body A. +// You can control the desired angular motor speed along the axis on body B. +// This joint is designed for vehicle suspensions. +class b3WheelJoint : public b3Joint +{ +public: + b3Vec3 GetAnchorA() const; + b3Vec3 GetAnchorB() const; + + // The local anchor point relative to body A's origin. + const b3Vec3& GetLocalAnchorA() const { return m_localAnchorA; } + + // The local anchor point relative to body B's origin. + const b3Vec3& GetLocalAnchorB() const { return m_localAnchorB; } + + // The local joint axis relative to bodyA. + const b3Vec3& GetLocalAxisA() const { return m_localXAxisA; } + + // Get the current joint translation, usually in meters. + scalar GetJointTranslation() const; + + // Get the current joint linear speed, usually in meters per second. + scalar GetJointLinearSpeed() const; + + // Get the current joint rotation quaternion. + b3Quat GetJointRotation() const; + + // Get the current joint angular speed in radians per second. + scalar GetJointAngularSpeed() const; + + // Is the joint motor enabled? + bool IsMotorEnabled() const; + + // Enable/disable the joint motor. + void EnableMotor(bool flag); + + // Set the motor angular speed, usually in radians per second. + void SetMotorSpeed(scalar speed); + + // Get the motor angular speed, usually in radians per second. + scalar GetMotorSpeed() const; + + // Set/Get the maximum motor force, usually in N-m. + void SetMaxMotorTorque(scalar torque); + scalar GetMaxMotorTorque() const; + + // Set/Get the spring frequency in hertz. Setting the frequency to zero disables the spring. + void SetSpringFrequencyHz(scalar hz); + scalar GetSpringFrequencyHz() const; + + // Set/Get the spring damping ratio + void SetSpringDampingRatio(scalar ratio); + scalar GetSpringDampingRatio() const; + + // Draw this joint. + void Draw() const; +protected: + friend class b3Joint; + + b3WheelJoint(const b3WheelJointDef* def); + + void InitializeConstraints(const b3SolverData* data); + void WarmStart(const b3SolverData* data); + void SolveVelocityConstraints(const b3SolverData* data); + bool SolvePositionConstraints(const b3SolverData* data); + + scalar m_frequencyHz; + scalar m_dampingRatio; + + // Solver shared + b3Vec3 m_localAnchorA; + b3Vec3 m_localAnchorB; + + b3Vec3 m_localXAxisA; + b3Vec3 m_localYAxisA; + b3Vec3 m_localZAxisA; + + b3Vec3 m_localXAxisB; + + scalar m_referenceCosine; + scalar m_referenceAngle; + + b3Vec2 m_linearImpulse; + scalar m_motorImpulse; + scalar m_springImpulse; + + b3Vec3 m_u; + scalar m_angularMass; + scalar m_angularImpulse; + + scalar m_maxMotorTorque; + scalar m_motorSpeed; + bool m_enableMotor; + + // Solver temp + u32 m_indexA; + u32 m_indexB; + b3Vec3 m_localCenterA; + b3Vec3 m_localCenterB; + scalar m_mA; + scalar m_mB; + b3Mat33 m_iA; + b3Mat33 m_iB; + b3Mat33 m_localInvIA; + b3Mat33 m_localInvIB; + + b3Vec3 m_a1, m_a2; + + b3Vec3 m_perp1; + b3Vec3 m_s1, m_s2; + + b3Vec3 m_perp2; + b3Vec3 m_s3, m_s4; + + b3Mat22 m_linearMass; + + b3Vec3 m_axisA; + b3Vec3 m_axisB; + + scalar m_mass; + scalar m_motorMass; + scalar m_springMass; + + scalar m_bias; + scalar m_gamma; +}; + +inline scalar b3WheelJoint::GetMotorSpeed() const +{ + return m_motorSpeed; +} + +inline scalar b3WheelJoint::GetMaxMotorTorque() const +{ + return m_maxMotorTorque; +} + +inline void b3WheelJoint::SetSpringFrequencyHz(scalar hz) +{ + m_frequencyHz = hz; +} + +inline scalar b3WheelJoint::GetSpringFrequencyHz() const +{ + return m_frequencyHz; +} + +inline void b3WheelJoint::SetSpringDampingRatio(scalar ratio) +{ + m_dampingRatio = ratio; +} + +inline scalar b3WheelJoint::GetSpringDampingRatio() const +{ + return m_dampingRatio; +} + +#endif \ No newline at end of file diff --git a/include/bounce/dynamics/shapes/capsule_shape.h b/include/bounce/dynamics/shapes/capsule_shape.h index 2c3d089..143f409 100644 --- a/include/bounce/dynamics/shapes/capsule_shape.h +++ b/include/bounce/dynamics/shapes/capsule_shape.h @@ -29,9 +29,9 @@ public: void Swap(const b3CapsuleShape& other); - void ComputeMass(b3MassData* data, float32 density) const; + void ComputeMass(b3MassData* data, scalar density) const; - void ComputeAABB(b3AABB3* aabb, const b3Transform& xf) const; + void ComputeAABB(b3AABB* aabb, const b3Transform& xf) const; bool TestSphere(const b3Sphere& sphere, const b3Transform& xf) const; @@ -39,7 +39,7 @@ public: bool RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf) const; - b3Vec3 m_centers[2]; + b3Vec3 m_vertex1, m_vertex2; }; #endif \ No newline at end of file diff --git a/include/bounce/dynamics/shapes/hull_shape.h b/include/bounce/dynamics/shapes/hull_shape.h index 7cc3f7e..d36c5a0 100644 --- a/include/bounce/dynamics/shapes/hull_shape.h +++ b/include/bounce/dynamics/shapes/hull_shape.h @@ -25,15 +25,15 @@ struct b3Hull; class b3HullShape : public b3Shape { -public : +public: b3HullShape(); ~b3HullShape(); void Swap(const b3HullShape& other); - void ComputeMass(b3MassData* data, float32 density) const; + void ComputeMass(b3MassData* data, scalar density) const; - void ComputeAABB(b3AABB3* aabb, const b3Transform& xf) const; + void ComputeAABB(b3AABB* aabb, const b3Transform& xf) const; bool TestSphere(const b3Sphere& sphere, const b3Transform& xf) const; @@ -44,4 +44,4 @@ public : const b3Hull* m_hull; }; -#endif +#endif \ No newline at end of file diff --git a/include/bounce/dynamics/shapes/mesh_shape.h b/include/bounce/dynamics/shapes/mesh_shape.h index e75775c..0792440 100644 --- a/include/bounce/dynamics/shapes/mesh_shape.h +++ b/include/bounce/dynamics/shapes/mesh_shape.h @@ -31,11 +31,11 @@ public: void Swap(const b3MeshShape& other); - void ComputeMass(b3MassData* data, float32 density) const; + void ComputeMass(b3MassData* data, scalar density) const; - void ComputeAABB(b3AABB3* output, const b3Transform& xf) const; + void ComputeAABB(b3AABB* output, const b3Transform& xf) const; - void ComputeAABB(b3AABB3* output, const b3Transform& xf, u32 childIndex) const; + void ComputeAABB(b3AABB* output, const b3Transform& xf, u32 childIndex) const; bool TestSphere(const b3Sphere& sphere, const b3Transform& xf) const; @@ -48,6 +48,8 @@ public: bool RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf, u32 childIndex) const; const b3Mesh* m_mesh; + + b3Vec3 m_scale; }; -#endif +#endif \ No newline at end of file diff --git a/src/bounce/cloth/cloth_triangle.cpp b/include/bounce/dynamics/shapes/sdf_shape.h similarity index 57% rename from src/bounce/cloth/cloth_triangle.cpp rename to include/bounce/dynamics/shapes/sdf_shape.h index bd5771a..0d3a9eb 100644 --- a/src/bounce/cloth/cloth_triangle.cpp +++ b/include/bounce/dynamics/shapes/sdf_shape.h @@ -16,26 +16,34 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include -#include -#include +#ifndef B3_SDF_SHAPE_H +#define B3_SDF_SHAPE_H -void b3ClothTriangle::Synchronize(const b3Vec3& displacement) +#include + +class b3SDF; + +class b3SDFShape : public b3Shape { - b3ClothMeshTriangle* triangle = m_cloth->m_mesh->triangles + m_triangle; +public: + b3SDFShape(); + ~b3SDFShape(); - b3Particle* p1 = m_cloth->m_particles[triangle->v1]; - b3Particle* p2 = m_cloth->m_particles[triangle->v2]; - b3Particle* p3 = m_cloth->m_particles[triangle->v3]; + void Swap(const b3SDFShape& other); - b3Vec3 x1 = p1->m_position; - b3Vec3 x2 = p2->m_position; - b3Vec3 x3 = p3->m_position; + void ComputeMass(b3MassData* data, scalar density) const; - b3AABB3 aabb; - aabb.Set(x1, x2, x3); - aabb.Extend(m_radius); + void ComputeAABB(b3AABB* output, const b3Transform& xf) const; - m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); -} \ No newline at end of file + bool TestSphere(const b3Sphere& sphere, const b3Transform& xf) const; + + bool TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, const b3Transform& xf) const; + + bool RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf) const; + + const b3SDF* m_sdf; + + b3Vec3 m_scale; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/dynamics/shapes/shape.h b/include/bounce/dynamics/shapes/shape.h index b397f58..754b572 100644 --- a/include/bounce/dynamics/shapes/shape.h +++ b/include/bounce/dynamics/shapes/shape.h @@ -22,6 +22,7 @@ #include #include #include +#include #include struct b3ContactEdge; @@ -33,36 +34,39 @@ enum b3ShapeType { e_sphereShape, e_capsuleShape, + e_triangleShape, e_hullShape, e_meshShape, + e_sdfShape, e_maxShapes }; +// c2 = center +// separation = dot(c2 - c1, normal) - r1 - r2 struct b3TestSphereOutput { - b3Vec3 point; - float32 separation; - b3Vec3 normal; + b3Vec3 point; // contact point on the shape + b3Vec3 normal; // contact normal on the shape towards the sphere }; struct b3ShapeDef { b3ShapeDef() { - shape = NULL; - userData = NULL; + shape = nullptr; + userData = nullptr; isSensor = false; - density = 0.0f; - friction = 0.3f; - restitution = 0.0f; + density = scalar(0); + friction = scalar(0.3); + restitution = scalar(0); } const b3Shape* shape; void* userData; bool isSensor; - float32 density; - float32 restitution; - float32 friction; + scalar density; + scalar restitution; + scalar friction; }; // This structure stores the mass-related data of a shape. @@ -72,7 +76,7 @@ struct b3MassData b3Vec3 center; // The mass of the shape, typically in kg. - float32 mass; + scalar mass; // The rotational inertia of the shape about the local origin. b3Mat33 I; @@ -92,17 +96,16 @@ public: b3Body* GetBody(); // Calculate the mass data for this shape given the shape density. - virtual void ComputeMass(b3MassData* data, float32 density) const = 0; + virtual void ComputeMass(b3MassData* data, scalar density) const = 0; // Compute the shape world AABB. - virtual void ComputeAABB(b3AABB3* aabb, const b3Transform& xf) const = 0; + virtual void ComputeAABB(b3AABB* aabb, const b3Transform& xf) const = 0; // Test if a sphere is contained inside this shape. virtual bool TestSphere(const b3Sphere& sphere, const b3Transform& xf) const = 0; // Test if a sphere is contained inside this shape. - // If the sphere is inside this shape then return the minimum separation distance and normal. - // The direction of the normal points from this shape to the sphere. + // If the sphere is colliding with this shape then return the contact point and normal on the other shape. virtual bool TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, const b3Transform& xf) const = 0; // Compute the ray intersection point, normal of surface, and fraction. @@ -115,24 +118,24 @@ public: bool IsSensor() const; // Get the shape density. - float32 GetDensity() const; + scalar GetDensity() const; // Set the shape density. - void SetDensity(float32 density); + void SetDensity(scalar density); // Get the shape coefficient of restitution. - float32 GetRestitution() const; + scalar GetRestitution() const; // Set the shape coefficient of restitution. // This is a value in the range [0, 1]. - void SetRestitution(float32 restitution); + void SetRestitution(scalar restitution); // Get the shape coefficient of friction. - float32 GetFriction() const; + scalar GetFriction() const; // Set the shape coefficient of friction. // This is a value in the range [0, 1]. - void SetFriction(float32 friction); + void SetFriction(scalar friction); // Get the user data associated with this shape. void* GetUserData() const; @@ -141,7 +144,11 @@ public: void SetUserData(void* data); // Get broadphase AABB - const b3AABB3& GetAABB() const; + const b3AABB& GetAABB() const; + + // Get the list of contacts that contains this body. + const b3List2& GetContactList() const; + b3List2& GetContactList(); // Dump this shape to the log file. void Dump(u32 bodyIndex) const; @@ -150,7 +157,10 @@ public: const b3Shape* GetNext() const; b3Shape* GetNext(); - float32 m_radius; + // This function is used internally. + void SetShape(b3Shape* shape); + + scalar m_radius; protected: friend class b3World; friend class b3Body; @@ -171,9 +181,9 @@ protected: b3ShapeType m_type; bool m_isSensor; void* m_userData; - float32 m_density; - float32 m_restitution; - float32 m_friction; + scalar m_density; + scalar m_restitution; + scalar m_friction; u32 m_broadPhaseID; // Contact edges for this shape contact graph. @@ -191,32 +201,32 @@ inline b3ShapeType b3Shape::GetType() const return m_type; } -inline float32 b3Shape::GetDensity() const +inline scalar b3Shape::GetDensity() const { return m_density; } -inline void b3Shape::SetDensity(float32 density) +inline void b3Shape::SetDensity(scalar density) { m_density = density; } -inline float32 b3Shape::GetRestitution() const +inline scalar b3Shape::GetRestitution() const { return m_restitution; } -inline void b3Shape::SetRestitution(float32 restitution) +inline void b3Shape::SetRestitution(scalar restitution) { m_restitution = restitution; } -inline float32 b3Shape::GetFriction() const +inline scalar b3Shape::GetFriction() const { return m_friction; } -inline void b3Shape::SetFriction(float32 friction) +inline void b3Shape::SetFriction(scalar friction) { m_friction = friction; } @@ -246,6 +256,16 @@ inline b3Body* b3Shape::GetBody() return m_body; } +inline const b3List2& b3Shape::GetContactList() const +{ + return m_contactEdges; +} + +inline b3List2& b3Shape::GetContactList() +{ + return m_contactEdges; +} + inline const b3Shape* b3Shape::GetNext() const { return m_next; diff --git a/include/bounce/dynamics/shapes/sphere_shape.h b/include/bounce/dynamics/shapes/sphere_shape.h index 7e398d6..307c1d0 100644 --- a/include/bounce/dynamics/shapes/sphere_shape.h +++ b/include/bounce/dynamics/shapes/sphere_shape.h @@ -29,9 +29,9 @@ public : void Swap(const b3SphereShape& other); - void ComputeMass(b3MassData* data, float32 density) const; + void ComputeMass(b3MassData* data, scalar density) const; - void ComputeAABB(b3AABB3* aabb, const b3Transform& xf) const; + void ComputeAABB(b3AABB* aabb, const b3Transform& xf) const; bool TestSphere(const b3Sphere& sphere, const b3Transform& xf) const; diff --git a/include/bounce/dynamics/shapes/triangle_shape.h b/include/bounce/dynamics/shapes/triangle_shape.h new file mode 100644 index 0000000..1a4fe8f --- /dev/null +++ b/include/bounce/dynamics/shapes/triangle_shape.h @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_TRIANGLE_SHAPE_H +#define B3_TRIANGLE_SHAPE_H + +#include + +class b3TriangleShape : public b3Shape +{ +public: + b3TriangleShape(); + ~b3TriangleShape(); + + // Set this triangle as an isolated triangle. + void Set(const b3Vec3& v1, const b3Vec3& v2, const b3Vec3& v3); + + void Swap(const b3TriangleShape& other); + + void ComputeMass(b3MassData* data, scalar density) const; + + void ComputeAABB(b3AABB* aabb, const b3Transform& xf) const; + + bool TestSphere(const b3Sphere& sphere, const b3Transform& xf) const; + + bool TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, const b3Transform& xf) const; + + bool RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf) const; + + // The triangle vertices. + b3Vec3 m_vertex1, m_vertex2, m_vertex3; + + // Optional edge wing vertices. + // These are used for smooth edge collision. + bool m_hasE1Vertex, m_hasE2Vertex, m_hasE3Vertex; + b3Vec3 m_e1Vertex, m_e2Vertex, m_e3Vertex; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/dynamics/time_step.h b/include/bounce/dynamics/time_step.h index 6736ffe..e6c60cb 100644 --- a/include/bounce/dynamics/time_step.h +++ b/include/bounce/dynamics/time_step.h @@ -40,8 +40,8 @@ struct b3SolverData b3Position* positions; b3Velocity* velocities; b3Mat33* invInertias; - float32 dt; - float32 invdt; + scalar dt; + scalar invdt; }; enum b3LimitState @@ -57,9 +57,9 @@ enum b3LimitState // The result equals to transpose( skew(v) ) * skew(v) or diagonal(v^2) - outer(v, v) inline b3Mat33 b3Steiner(const b3Vec3& v) { - float32 xx = v.x * v.x; - float32 yy = v.y * v.y; - float32 zz = v.z * v.z; + scalar xx = v.x * v.x; + scalar yy = v.y * v.y; + scalar zz = v.z * v.z; b3Mat33 S; @@ -95,7 +95,7 @@ inline b3Mat33 b3RotateToFrame(const b3Mat33& inertia, const b3Mat33& rotation) // of the body frame relative to the inertial frame. inline b3Mat33 b3RotateToFrame(const b3Mat33& inertia, const b3Quat& rotation) { - b3Mat33 R = b3QuatMat33(rotation); + b3Mat33 R = rotation.GetXYZAxes(); return R * inertia * b3Transpose(R); } @@ -104,19 +104,53 @@ inline b3Mat33 b3RotateToFrame(const b3Mat33& inertia, const b3Quat& rotation) // the angular velocity of the rotating frame represented by the orientation. inline b3Quat b3Derivative(const b3Quat& orientation, const b3Vec3& velocity) { - b3Quat xf(0.5f * velocity.x, 0.5f * velocity.y, 0.5f * velocity.z, 0.0f); - return xf * orientation; + b3Quat w(velocity.x, velocity.y, velocity.z, scalar(0)); + return scalar(0.5) * w * orientation; } // Integrate an orientation over a time step given // the current orientation, angular velocity of the rotating frame // represented by the orientation, and the time step dt. -inline b3Quat b3Integrate(const b3Quat& orientation, const b3Vec3& velocity, float32 dt) +inline b3Quat b3Integrate(const b3Quat& orientation, const b3Vec3& omega, scalar dt) { - // Integrate from [t0, t0 + h] using the explicit Euler method - b3Quat qdot = b3Derivative(orientation, velocity); - b3Quat integral = dt * qdot; - return orientation + integral; + // "Practical Parameterization of Rotations Using the Exponential Map", Grassia + scalar h = dt; + + scalar x = b3Length(omega); + + const scalar kTol = scalar(10.0e-4); + + b3Vec3 qv; + if (scalar(0.5) * h * x < kTol) + { + // Use first three terms of Taylor expansion of sin(h * x / 2) + + // f'(0) / 1! * x = h / 2 * x + // f''(0) / 2! * x^2 = 0 * 2 / x ^ 2 = 0 + // f'''(0) / 3! * x^3 = -x^3 * h^3 / 48 + + // Sum up, divide by x, and simplify (expand) + // s = h / 2 - (h * h * h) * x * x / 48 + const scalar kInv48 = scalar(1) / scalar(48); + + scalar s = scalar(0.5) * h - kInv48 * (h * h * h) * x * x; + + qv = s * omega; + } + else + { + scalar s = sin(scalar(0.5) * h * x) / x; + + qv = s * omega; + } + + b3Quat q; + q.v = qv; + q.s = cos(scalar(0.5) * h * x); + + b3Quat q1 = q * orientation; + q1.Normalize(); + return q1; } -#endif +#endif \ No newline at end of file diff --git a/include/bounce/dynamics/world.h b/include/bounce/dynamics/world.h index a60d72d..5f8e7fa 100644 --- a/include/bounce/dynamics/world.h +++ b/include/bounce/dynamics/world.h @@ -30,17 +30,35 @@ struct b3BodyDef; class b3Body; + class b3QueryListener; +class b3QueryFilter; + class b3RayCastListener; +class b3RayCastFilter; + +class b3ConvexCastListener; +class b3ConvexCastFilter; + class b3ContactListener; class b3ContactFilter; +// Output of b3World::RayCastSingle struct b3RayCastSingleOutput { b3Shape* shape; // shape b3Vec3 point; // intersection point on surface b3Vec3 normal; // surface normal of intersection - float32 fraction; // time of intersection on segment + scalar fraction; // time of intersection on segment +}; + +// Output of b3World::ConvexCastSingle +struct b3ConvexCastSingleOutput +{ + b3Shape* shape; // shape + b3Vec3 point; // intersection point on surface + b3Vec3 normal; // surface normal of intersection + scalar fraction; // time of intersection on displacement }; // Use a physics world to create/destroy rigid bodies, execute ray cast and volume queries. @@ -69,6 +87,9 @@ public: // The acceleration has units of m/s^2. void SetGravity(const b3Vec3& gravity); + // Get the acceleration due to gravity force in m/s^2. + const b3Vec3& GetGravity() const; + // Create a new rigid body. b3Body* CreateBody(const b3BodyDef& def); @@ -84,28 +105,50 @@ public: // Simulate a physics step. // The function parameters are the ammount of time to simulate, // and the number of constraint solver iterations. - void Step(float32 dt, u32 velocityIterations, u32 positionIterations); + void Step(scalar dt, u32 velocityIterations, u32 positionIterations); // Perform a ray cast with the world. // The given ray cast listener will be notified when a ray intersects a shape // in the world. + // You can control on which shapes the ray-cast is performed using + // a ray-cast filter. // The ray cast output is the intercepted shape, the intersection // point in world space, the face normal on the shape associated with the point, // and the intersection fraction. - void RayCast(b3RayCastListener* listener, const b3Vec3& p1, const b3Vec3& p2) const; + void RayCast(b3RayCastListener* listener, b3RayCastFilter* filter, const b3Vec3& p1, const b3Vec3& p2) const; // Perform a ray cast with the world. // If the ray doesn't intersect with a shape in the world then return false. + // You can control on which shapes the ray-cast is performed using + // a ray-cast filter. // The ray cast output is the intercepted shape, the intersection // point in world space, the face normal on the shape associated with the point, // and the intersection fraction. - bool RayCastSingle(b3RayCastSingleOutput* output, const b3Vec3& p1, const b3Vec3& p2) const; + bool RayCastSingle(b3RayCastSingleOutput* output, b3RayCastFilter* filter, const b3Vec3& p1, const b3Vec3& p2) const; + + // Perform a convex cast with the world. This only works for given convex shapes. + // You must supply a listener, filter, the shape and the displacement of the shape. + // The shape must belong to this world. + // The given convex cast listener will be notified when a convex intersects a shape + // in the world. + // You can control on which shapes the convex-cast is performed using + // a filter. + void ConvexCast(b3ConvexCastListener* listener, b3ConvexCastFilter* filter, const b3Shape* shape, const b3Vec3& displacement) const; + + // Perform a convex cast with the world. This only works for given convex shapes. + // You must supply a filter, the shape and the displacement of the shape. + // The shape must belong to this world. + // If the convex doesn't intersect with any shape in the world then return false. + // You can control on which shapes the convex-cast is performed using + // the given filter. + bool ConvexCastSingle(b3ConvexCastSingleOutput* output, b3ConvexCastFilter* filter, const b3Shape* shape, const b3Vec3& displacement) const; // Perform a AABB query with the world. // The query listener will be notified when two shape AABBs are overlapping. + // You can control which shapes are reported using a query filter. // If the listener returns false then the query is stopped immediately. // Otherwise, it continues searching for new overlapping shape AABBs. - void QueryAABB(b3QueryListener* listener, const b3AABB3& aabb) const; + void QueryAABB(b3QueryListener* listener, b3QueryFilter* filter, const b3AABB& aabb) const; // Get the list of bodies in this world. const b3List2& GetBodyList() const; @@ -144,7 +187,7 @@ private: friend class b3MeshContact; friend class b3Joint; - void Solve(float32 dt, u32 velocityIterations, u32 positionIterations); + void Solve(scalar dt, u32 velocityIterations, u32 positionIterations); bool m_sleeping; bool m_warmStarting; @@ -181,6 +224,11 @@ inline void b3World::SetGravity(const b3Vec3& gravity) m_gravity = gravity; } +inline const b3Vec3& b3World::GetGravity() const +{ + return m_gravity; +} + inline void b3World::SetWarmStart(bool flag) { m_warmStarting = flag; diff --git a/include/bounce/dynamics/world_listeners.h b/include/bounce/dynamics/world_listeners.h index 275f505..18755d2 100644 --- a/include/bounce/dynamics/world_listeners.h +++ b/include/bounce/dynamics/world_listeners.h @@ -34,6 +34,16 @@ public: virtual bool ReportShape(b3Shape* shape) = 0; }; +// Implement this class to provide query filtering. +class b3QueryFilter +{ +public: + virtual ~b3QueryFilter() { } + + // Return true if this shape should be reported to the listener. + virtual bool ShouldReport(b3Shape* shape) = 0; +}; + class b3RayCastListener { public: @@ -45,35 +55,94 @@ public: // The reported information are the shape hit by the ray, // the intersection point on the shape, the surface normal associated with the point, and the // intersection fraction for the ray. - virtual float32 ReportShape(b3Shape* shape, const b3Vec3& point, const b3Vec3& normal, float32 fraction) = 0; + virtual scalar ReportShape(b3Shape* shape, const b3Vec3& point, const b3Vec3& normal, scalar fraction) = 0; }; -class b3ContactListener +// Implement this class to provide ray-cast filtering. +class b3RayCastFilter { public: - // @warning You cannot create/destroy Bounce objects inside these callbacks. - // Inherit from this class and set it in the world to listen for collision events. - // Call the functions below to inspect when a shape start/end colliding with another shape. - - // A contact has begun. - virtual void BeginContact(b3Contact* contact) = 0; + virtual ~b3RayCastFilter() { } - // A contact has ended. - virtual void EndContact(b3Contact* contact) = 0; + // Return true if ray-cast calculations should be performed on this shape. + virtual bool ShouldRayCast(b3Shape* shape) = 0; +}; + +// Implement this class to provide convex-cast filtering. +class b3ConvexCastFilter +{ +public: + virtual ~b3ConvexCastFilter() { } + + // Return true if convex-cast calculations should be performed on this shape. + virtual bool ShouldConvexCast(b3Shape* shape) = 0; +}; + +class b3ConvexCastListener +{ +public: + // The user must return the new convex cast fraction. + // If fraction equals zero then the convex cast query will be canceled immediately. + virtual ~b3ConvexCastListener() { } + + // Report that a shape was hit by the ray to this contact listener. + // The reported information are the shape hit by the convex, + // the intersection point on the shape, the surface normal associated with the point, and the + // intersection fraction for the displacement vector. + virtual scalar ReportShape(b3Shape* shape, const b3Vec3& point, const b3Vec3& normal, scalar fraction) = 0; +}; + +// Inherit from this class and set it in the world to listen for collision events. +// Call the functions below to inspect when a shape start/end colliding with another shape. +// @warning You cannot create/destroy Bounce objects inside these callbacks. +class b3ContactListener +{ +public: + virtual ~b3ContactListener() { } + + // Called when two shapes begin to overlap. + virtual void BeginContact(b3Contact* contact) + { + B3_NOT_USED(contact); + } + + // Called when two shapes cease to overlap. + virtual void EndContact(b3Contact* contact) + { + B3_NOT_USED(contact); + } - // The contact will be solved after this notification. - virtual void PreSolve(b3Contact* contact) = 0; + // Called after a dynamic contact is updated. + virtual void PreSolve(b3Contact* contact) + { + B3_NOT_USED(contact); + } + + // Called after a contact is solved. + // This is usefull for inspecting impulses. + virtual void PostSolve(b3Contact* contact) + { + B3_NOT_USED(contact); + } }; // By implementing this interface the contact filter will -// be notified before a contact between two shapes is created and updated. +// be notified before a contact between two shapes is created, updated, and solved. class b3ContactFilter { public: virtual ~b3ContactFilter() { } - // Should the two shapes collide? + // Should the two shapes collide with each other? virtual bool ShouldCollide(b3Shape* shapeA, b3Shape* shapeB) = 0; + + // Should one or both shapes respond to a contact with each other? + virtual bool ShouldRespond(b3Shape* shapeA, b3Shape* shapeB) + { + B3_NOT_USED(shapeA); + B3_NOT_USED(shapeB); + return true; + } }; -#endif +#endif \ No newline at end of file diff --git a/include/bounce/quickhull/qh_hull.h b/include/bounce/quickhull/qh_hull.h index 877097f..3114033 100644 --- a/include/bounce/quickhull/qh_hull.h +++ b/include/bounce/quickhull/qh_hull.h @@ -53,7 +53,7 @@ struct qhFace b3Vec3 center; b3Plane plane; - float32 area; + scalar area; qhFaceMark mark; @@ -172,7 +172,7 @@ private: qhList m_faceList; // Coplanarity tolerance - float32 m_tolerance; + scalar m_tolerance; // Number of Quickhull iterations u32 m_iterations; diff --git a/include/bounce/quickhull/qh_hull.inl b/include/bounce/quickhull/qh_hull.inl index be0da6c..ff5f321 100644 --- a/include/bounce/quickhull/qh_hull.inl +++ b/include/bounce/quickhull/qh_hull.inl @@ -5,7 +5,7 @@ template inline void qhList::PushFront(T* link) { - link->prev = NULL; + link->prev = nullptr; link->next = head; if (head) { @@ -33,8 +33,8 @@ inline T* qhList::Remove(T* link) head = link->next; } - link->prev = NULL; - link->next = NULL; + link->prev = nullptr; + link->next = nullptr; --count; return next; diff --git a/include/bounce/rope/rope.h b/include/bounce/rope/rope.h index 92e782a..cabae47 100644 --- a/include/bounce/rope/rope.h +++ b/include/bounce/rope/rope.h @@ -28,19 +28,19 @@ struct b3RopeDef { b3RopeDef() { - vertices = NULL; - masses = NULL; + vertices = nullptr; + masses = nullptr; count = 0; gravity.SetZero(); - linearDamping = 0.6f; - angularDamping = 0.6f; + linearDamping = scalar(0.6); + angularDamping = scalar(0.6); } // b3Vec3* vertices; // - float32* masses; + scalar* masses; // u32 count; @@ -49,10 +49,10 @@ struct b3RopeDef b3Vec3 gravity; // - float32 linearDamping; + scalar linearDamping; // - float32 angularDamping; + scalar angularDamping; }; // @@ -81,13 +81,13 @@ public: } // - void Step(float32 dt); + void Step(scalar dt); // void Draw() const; private: // - float32 m_kd1, m_kd2; + scalar m_kd1, m_kd2; // b3Vec3 m_gravity; diff --git a/include/bounce/rope/spatial.h b/include/bounce/rope/spatial.h index a1b6bd6..55c6350 100644 --- a/include/bounce/rope/spatial.h +++ b/include/bounce/rope/spatial.h @@ -72,19 +72,19 @@ inline b3MotionVec operator-(const b3MotionVec& a) } // a * s -inline b3MotionVec operator*(const b3MotionVec& a, float32 s) +inline b3MotionVec operator*(const b3MotionVec& a, scalar s) { return b3MotionVec(s * a.w, s * a.v); } // s * a -inline b3MotionVec operator*(float32 s, const b3MotionVec& a) +inline b3MotionVec operator*(scalar s, const b3MotionVec& a) { return b3MotionVec(s * a.w, s * a.v); } // a / s -inline b3MotionVec operator/(const b3MotionVec& a, float32 s) +inline b3MotionVec operator/(const b3MotionVec& a, scalar s) { return b3MotionVec(a.w / s, a.v / s); } @@ -151,26 +151,26 @@ inline b3ForceVec operator-(const b3ForceVec& a) } // a * s -inline b3ForceVec operator*(const b3ForceVec& a, float32 s) +inline b3ForceVec operator*(const b3ForceVec& a, scalar s) { return b3ForceVec(s * a.n, s * a.f); } // s * a -inline b3ForceVec operator*(float32 s, const b3ForceVec& a) +inline b3ForceVec operator*(scalar s, const b3ForceVec& a) { return b3ForceVec(s * a.n, s * a.f); } // a / s -inline b3ForceVec operator/(const b3ForceVec& a, float32 s) +inline b3ForceVec operator/(const b3ForceVec& a, scalar s) { return b3ForceVec(a.n / s, a.f / s); } // a^T = [a.b^T, a.a^T] // a^T * b = a.b * b.a + a.a * b.b -inline float32 b3Dot(const b3MotionVec& a, const b3ForceVec& b) +inline scalar b3Dot(const b3MotionVec& a, const b3ForceVec& b) { return b3Dot(a.v, b.n) + b3Dot(a.w, b.f); } @@ -191,7 +191,7 @@ struct b3SpInertia // Set this matrix from mass and rotational inertia // about the local center of mass (zero vector). - void SetLocalInertia(float32 m, const b3Mat33& I) + void SetLocalInertia(scalar m, const b3Mat33& I) { A.SetZero(); B = b3Diagonal(m); @@ -233,9 +233,9 @@ inline b3MotionVec b3SpInertia::Solve(const b3ForceVec& b) const invA_D = b3Transpose(invA_A); b3Mat33 T = A * invA_A; - T[0][0] -= 1.0f; - T[1][1] -= 1.0f; - T[2][2] -= 1.0f; + T[0][0] -= scalar(1); + T[1][1] -= scalar(1); + T[2][2] -= scalar(1); invA_C = NinvB * T; diff --git a/include/bounce/softbody/block_softbody_mesh.h b/include/bounce/softbody/block_softbody_mesh.h index 19dba1e..4a99193 100644 --- a/include/bounce/softbody/block_softbody_mesh.h +++ b/include/bounce/softbody/block_softbody_mesh.h @@ -24,100 +24,262 @@ template struct b3BlockSoftBodyMesh : public b3SoftBodyMesh { - b3Vec3 blockVertices[(W + 1) * (H + 1) * (D + 1)]; - b3SoftBodyMeshTetrahedron blockTetrahedrons[5 * W * H * D]; + b3Vec3 blockVertices[(H + 1) * (W + 1) * (D + 1)]; + b3SoftBodyMeshTriangle blockTriangles[4 * H * W + 4 * H * D + 4 * W * D]; + b3SoftBodyMeshTetrahedron blockTetrahedrons[5 * H * W * D]; b3BlockSoftBodyMesh() { vertexCount = 0; - for (u32 x = 0; x <= W; ++x) + + for (u32 j = 0; j <= W; ++j) { - for (u32 y = 0; y <= H; ++y) + for (u32 i = 0; i <= H; ++i) { - for (u32 z = 0; z <= D; ++z) + for (u32 k = 0; k <= D; ++k) { - blockVertices[vertexCount++].Set(float32(x), float32(y), float32(z)); + u32 vertex = GetVertex(i, j, k); + blockVertices[vertex].Set(scalar(j), scalar(i), scalar(k)); + ++vertexCount; } } } - B3_ASSERT(vertexCount == (W + 1) * (H + 1) * (D + 1)); + B3_ASSERT(vertexCount == (H + 1) * (W + 1) * (D + 1)); b3Vec3 translation; - translation.x = -0.5f * float32(W); - translation.y = -0.5f * float32(H); - translation.z = -0.5f * float32(D); + translation.x = scalar(-0.5) * scalar(W); + translation.y = scalar(-0.5) * scalar(H); + translation.z = scalar(-0.5) * scalar(D); for (u32 i = 0; i < vertexCount; ++i) { blockVertices[i] += translation; } - tetrahedronCount = 0; - for (u32 x = 0; x < W; ++x) + triangleCount = 0; + + // x-y plane + for (u32 i = 0; i < H; ++i) { - for (u32 y = 0; y < H; ++y) + for (u32 j = 0; j < W; ++j) { - for (u32 z = 0; z < D; ++z) + // 2*-----*3 + // /| /| + // / | / | + // *-----* | + // | 1*--|--*4 + // | / | / + // |/ |/ + // *-----* { - // 4*-----*7 - // /| /| - // / | / | - // 5*-----*6 | - // | 0*--|--*3 - // | / | / - // |/ |/ - // 1*-----*2 - u32 v0 = (x * (H + 1) + y) * (D + 1) + z; - u32 v1 = v0 + 1; - u32 v3 = ((x + 1) * (H + 1) + y) * (D + 1) + z; - u32 v2 = v3 + 1; - u32 v7 = ((x + 1) * (H + 1) + (y + 1)) * (D + 1) + z; - u32 v6 = v7 + 1; - u32 v4 = (x * (H + 1) + (y + 1)) * (D + 1) + z; - u32 v5 = v4 + 1; + u32 v1 = GetVertex(i, j, 0); + u32 v2 = GetVertex(i + 1, j, 0); + u32 v3 = GetVertex(i + 1, j + 1, 0); + u32 v4 = GetVertex(i, j + 1, 0); - if ((x + y + z) % 2 == 1) + blockTriangles[triangleCount].v1 = v1; + blockTriangles[triangleCount].v2 = v2; + blockTriangles[triangleCount].v3 = v3; + + ++triangleCount; + + blockTriangles[triangleCount].v1 = v3; + blockTriangles[triangleCount].v2 = v4; + blockTriangles[triangleCount].v3 = v1; + + ++triangleCount; + } + + { + u32 v1 = GetVertex(i, j, D); + u32 v2 = GetVertex(i + 1, j, D); + u32 v3 = GetVertex(i + 1, j + 1, D); + u32 v4 = GetVertex(i, j + 1, D); + + blockTriangles[triangleCount].v1 = v3; + blockTriangles[triangleCount].v2 = v2; + blockTriangles[triangleCount].v3 = v1; + + ++triangleCount; + + blockTriangles[triangleCount].v1 = v1; + blockTriangles[triangleCount].v2 = v4; + blockTriangles[triangleCount].v3 = v3; + + ++triangleCount; + } + } + } + + // y-z plane + for (u32 i = 0; i < H; ++i) + { + for (u32 k = 0; k < D; ++k) + { + // 4*-----* + // /| /| + // / | / | + // 3*-----* | + // | 1*--|--* + // | / | / + // |/ |/ + // 2*-----* + { + u32 v1 = GetVertex(i, 0, k); + u32 v2 = GetVertex(i, 0, k + 1); + u32 v3 = GetVertex(i + 1, 0, k + 1); + u32 v4 = GetVertex(i + 1, 0, k); + + blockTriangles[triangleCount].v1 = v1; + blockTriangles[triangleCount].v2 = v2; + blockTriangles[triangleCount].v3 = v3; + + ++triangleCount; + + blockTriangles[triangleCount].v1 = v3; + blockTriangles[triangleCount].v2 = v4; + blockTriangles[triangleCount].v3 = v1; + + ++triangleCount; + } + + { + u32 v1 = GetVertex(i, W, k); + u32 v2 = GetVertex(i, W, k + 1); + u32 v3 = GetVertex(i + 1, W, k + 1); + u32 v4 = GetVertex(i + 1, W, k); + + blockTriangles[triangleCount].v1 = v3; + blockTriangles[triangleCount].v2 = v2; + blockTriangles[triangleCount].v3 = v1; + + ++triangleCount; + + blockTriangles[triangleCount].v1 = v1; + blockTriangles[triangleCount].v2 = v4; + blockTriangles[triangleCount].v3 = v3; + + ++triangleCount; + } + } + } + + // x-z plane + for (u32 j = 0; j < W; ++j) + { + for (u32 k = 0; k < D; ++k) + { + // *-----* + // /| /| + // / | / | + // *-----* | + // | 1*----2* + // | / | / + // |/ |/ + // 4*-----3* + { + u32 v1 = GetVertex(0, j, k); + u32 v2 = GetVertex(0, j + 1, k); + u32 v3 = GetVertex(0, j + 1, k + 1); + u32 v4 = GetVertex(0, j, k + 1); + + blockTriangles[triangleCount].v1 = v1; + blockTriangles[triangleCount].v2 = v2; + blockTriangles[triangleCount].v3 = v3; + + ++triangleCount; + + blockTriangles[triangleCount].v1 = v3; + blockTriangles[triangleCount].v2 = v4; + blockTriangles[triangleCount].v3 = v1; + + ++triangleCount; + } + + { + u32 v1 = GetVertex(H, j, k); + u32 v2 = GetVertex(H, j + 1, k); + u32 v3 = GetVertex(H, j + 1, k + 1); + u32 v4 = GetVertex(H, j, k + 1); + + blockTriangles[triangleCount].v1 = v3; + blockTriangles[triangleCount].v2 = v2; + blockTriangles[triangleCount].v3 = v1; + + ++triangleCount; + + blockTriangles[triangleCount].v1 = v1; + blockTriangles[triangleCount].v2 = v4; + blockTriangles[triangleCount].v3 = v3; + + ++triangleCount; + } + } + } + + B3_ASSERT(triangleCount == 4 * H * W + 4 * H * D + 4 * W * D); + + tetrahedronCount = 0; + for (u32 i = 0; i < H; ++i) + { + for (u32 j = 0; j < W; ++j) + { + for (u32 k = 0; k < D; ++k) + { + // 4*-----8* + // /| /| + // / | / | + // 3*-----7* | + // | 1*--|--5* + // | / | / + // |/ | / + // 2*-----6* + + u32 v1 = GetVertex(i, j, k); + u32 v2 = GetVertex(i, j, k + 1); + u32 v3 = GetVertex(i + 1, j, k + 1); + u32 v4 = GetVertex(i + 1, j, k); + + u32 v5 = GetVertex(i, j + 1, k); + u32 v6 = GetVertex(i, j + 1, k + 1); + u32 v7 = GetVertex(i + 1, j + 1, k + 1); + u32 v8 = GetVertex(i + 1, j + 1, k); + + if ((i + j + k) % 2 == 1) { - // CCW - //blockTetrahedrons[tetrahedronCount++] = { v1, v2, v6, v3 }; - //blockTetrahedrons[tetrahedronCount++] = { v3, v6, v4, v7 }; - //blockTetrahedrons[tetrahedronCount++] = { v1, v4, v6, v5 }; - //blockTetrahedrons[tetrahedronCount++] = { v1, v3, v4, v0 }; - //blockTetrahedrons[tetrahedronCount++] = { v1, v6, v4, v3 }; - - // CW - blockTetrahedrons[tetrahedronCount++] = { v2, v1, v6, v3 }; - blockTetrahedrons[tetrahedronCount++] = { v6, v3, v4, v7 }; - blockTetrahedrons[tetrahedronCount++] = { v4, v1, v6, v5 }; - blockTetrahedrons[tetrahedronCount++] = { v3, v1, v4, v0 }; - blockTetrahedrons[tetrahedronCount++] = { v6, v1, v4, v3 }; + blockTetrahedrons[tetrahedronCount++] = { v2, v6, v7, v5 }; + blockTetrahedrons[tetrahedronCount++] = { v5, v7, v4, v8 }; + blockTetrahedrons[tetrahedronCount++] = { v2, v4, v7, v3 }; + blockTetrahedrons[tetrahedronCount++] = { v2, v5, v4, v1 }; + blockTetrahedrons[tetrahedronCount++] = { v2, v7, v4, v5 }; } else { - // CCW - //blockTetrahedrons[tetrahedronCount++] = { v2, v0, v5, v1 }; - //blockTetrahedrons[tetrahedronCount++] = { v2, v7, v0, v3 }; - //blockTetrahedrons[tetrahedronCount++] = { v2, v5, v7, v6 }; - //blockTetrahedrons[tetrahedronCount++] = { v0, v7, v5, v4 }; - //blockTetrahedrons[tetrahedronCount++] = { v2, v0, v7, v5 }; - - // CW - blockTetrahedrons[tetrahedronCount++] = { v0, v2, v5, v1 }; - blockTetrahedrons[tetrahedronCount++] = { v7, v2, v0, v3 }; - blockTetrahedrons[tetrahedronCount++] = { v5, v2, v7, v6 }; - blockTetrahedrons[tetrahedronCount++] = { v7, v0, v5, v4 }; - blockTetrahedrons[tetrahedronCount++] = { v0, v2, v7, v5 }; + blockTetrahedrons[tetrahedronCount++] = { v6, v1, v3, v2 }; + blockTetrahedrons[tetrahedronCount++] = { v6, v8, v1, v5 }; + blockTetrahedrons[tetrahedronCount++] = { v6, v3, v8, v7 }; + blockTetrahedrons[tetrahedronCount++] = { v1, v8, v3, v4 }; + blockTetrahedrons[tetrahedronCount++] = { v6, v1, v8, v3 }; } } } } - B3_ASSERT(tetrahedronCount == 5 * W * H * D); + B3_ASSERT(tetrahedronCount == 5 * H * W * D); vertices = blockVertices; + triangles = blockTriangles; tetrahedrons = blockTetrahedrons; } + + u32 GetVertex(u32 i, u32 j, u32 k) + { + B3_ASSERT(i < H + 1); + B3_ASSERT(j < W + 1); + B3_ASSERT(k < D + 1); + return k + (D + 1) * (j + (W + 1) * i); + } }; #endif \ No newline at end of file diff --git a/include/bounce/softbody/contacts/softbody_contact_solver.h b/include/bounce/softbody/contacts/softbody_contact_solver.h index 4187ab4..762c688 100644 --- a/include/bounce/softbody/contacts/softbody_contact_solver.h +++ b/include/bounce/softbody/contacts/softbody_contact_solver.h @@ -19,79 +19,56 @@ #ifndef B3_SOFT_BODY_CONTACT_SOLVER_H #define B3_SOFT_BODY_CONTACT_SOLVER_H -#include -#include +#include +#include +#include class b3StackAllocator; +class b3SoftBodyNode; +class b3SoftBodySphereAndShapeContact; -struct b3SoftBodyNode; -class b3Body; - -struct b3NodeBodyContact; - -struct b3DenseVec3; - -struct b3SoftBodySolverBodyContactVelocityConstraint +struct b3SoftBodySolverShapeContactVelocityConstraint { u32 indexA; - float32 invMassA; - b3Mat33 invIA; + scalar invMassA; - b3Body* bodyB; - float32 invMassB; - b3Mat33 invIB; - - float32 friction; - - b3Vec3 point; - b3Vec3 rA; - b3Vec3 rB; + scalar friction; b3Vec3 normal; - float32 normalMass; - float32 normalImpulse; - float32 velocityBias; + scalar normalMass; + scalar normalImpulse; b3Vec3 tangent1; b3Vec3 tangent2; - b3Mat22 tangentMass; + scalar tangentMass; b3Vec2 tangentImpulse; }; -struct b3SoftBodySolverBodyContactPositionConstraint +struct b3SoftBodySolverShapeContactPositionConstraint { u32 indexA; - float32 invMassA; - b3Mat33 invIA; - float32 radiusA; - b3Vec3 localCenterA; + scalar invMassA; + scalar radiusA; - b3Body* bodyB; - float32 invMassB; - b3Mat33 invIB; - float32 radiusB; - b3Vec3 localCenterB; + scalar radiusB; - b3Vec3 rA; - b3Vec3 rB; - - b3Vec3 normalA; - b3Vec3 localPointA; - b3Vec3 localPointB; + b3Vec3 normalB; + b3Vec3 pointB; }; struct b3SoftBodyContactSolverDef { + b3SoftBodyTimeStep step; b3StackAllocator* allocator; b3Vec3* positions; b3Vec3* velocities; - u32 bodyContactCount; - b3NodeBodyContact** bodyContacts; + u32 shapeContactCount; + b3SoftBodySphereAndShapeContact** shapeContacts; }; -inline float32 b3MixFriction(float32 u1, float32 u2) +inline scalar b3MixFriction(scalar u1, scalar u2) { return b3Sqrt(u1 * u2); } @@ -102,26 +79,28 @@ public: b3SoftBodyContactSolver(const b3SoftBodyContactSolverDef& def); ~b3SoftBodyContactSolver(); - void InitializeBodyContactConstraints(); + void InitializeShapeContactConstraints(); void WarmStart(); - void SolveBodyContactVelocityConstraints(); + void SolveShapeContactVelocityConstraints(); void StoreImpulses(); - bool SolveBodyContactPositionConstraints(); + bool SolveShapeContactPositionConstraints(); protected: + b3SoftBodyTimeStep m_step; + b3StackAllocator* m_allocator; b3Vec3* m_positions; b3Vec3* m_velocities; - u32 m_bodyContactCount; - b3NodeBodyContact** m_bodyContacts; + u32 m_shapeContactCount; + b3SoftBodySphereAndShapeContact** m_shapeContacts; - b3SoftBodySolverBodyContactVelocityConstraint* m_bodyVelocityConstraints; - b3SoftBodySolverBodyContactPositionConstraint* m_bodyPositionConstraints; + b3SoftBodySolverShapeContactVelocityConstraint* m_shapeVelocityConstraints; + b3SoftBodySolverShapeContactPositionConstraint* m_shapePositionConstraints; }; #endif \ No newline at end of file diff --git a/include/bounce/softbody/contacts/softbody_node_body_contact.h b/include/bounce/softbody/contacts/softbody_sphere_shape_contact.h similarity index 61% rename from include/bounce/softbody/contacts/softbody_node_body_contact.h rename to include/bounce/softbody/contacts/softbody_sphere_shape_contact.h index 8a8bb5e..d63646d 100644 --- a/include/bounce/softbody/contacts/softbody_node_body_contact.h +++ b/include/bounce/softbody/contacts/softbody_sphere_shape_contact.h @@ -16,63 +16,64 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef B3_SOFT_BODY_NODE_BODY_CONTACT_H -#define B3_SOFT_BODY_NODE_BODY_CONTACT_H +#ifndef B3_SOFTBODY_SPHERE_AND_SHAPE_CONTACT_H +#define B3_SOFTBODY_SPHERE_AND_SHAPE_CONTACT_H #include #include #include #include -struct b3SoftBodyNode; -class b3Shape; +class b3SoftBodySphereShape; +class b3SoftBodyWorldShape; -// A contact between a node and a body -class b3NodeBodyContact +// A contact between a sphere and a shape +class b3SoftBodySphereAndShapeContact { public: private: - friend class b3List2; friend class b3SoftBody; + friend class b3SoftBodySphereShape; + friend class b3SoftBodyWorldShape; friend class b3SoftBodyContactManager; - friend struct b3SoftBodyNode; + friend class b3SoftBodyNode; friend class b3SoftBodySolver; friend class b3SoftBodyContactSolver; - friend struct b3NodeBodyContactWorldPoint; + friend struct b3SoftBodySphereAndShapeContactWorldPoint; + friend class b3List2; - b3NodeBodyContact() { } - ~b3NodeBodyContact() { } + b3SoftBodySphereAndShapeContact() { } + ~b3SoftBodySphereAndShapeContact() { } void Update(); - b3SoftBodyNode* m_n1; - b3Shape* m_s2; + b3SoftBodySphereShape* m_s1; + b3SoftBodyWorldShape* m_s2; // Is the contact active? bool m_active; // Contact constraint - b3Vec3 m_normal1; - b3Vec3 m_localPoint1; - b3Vec3 m_localPoint2; - float32 m_normalImpulse; + b3Vec3 m_normal2; + b3Vec3 m_point2; + scalar m_normalImpulse; // Friction constraint b3Vec3 m_tangent1, m_tangent2; b3Vec2 m_tangentImpulse; // List pointers into the soft body - b3NodeBodyContact* m_prev; - b3NodeBodyContact* m_next; + b3SoftBodySphereAndShapeContact* m_prev; + b3SoftBodySphereAndShapeContact* m_next; }; -struct b3NodeBodyContactWorldPoint +struct b3SoftBodySphereAndShapeContactWorldPoint { - void Initialize(const b3NodeBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB); + void Initialize(const b3SoftBodySphereAndShapeContact* c, scalar rA, const b3Vec3& cA, scalar rB); b3Vec3 point; b3Vec3 normal; - float32 separation; + scalar separation; }; #endif \ No newline at end of file diff --git a/include/bounce/softbody/joints/softbody_anchor.h b/include/bounce/softbody/joints/softbody_anchor.h new file mode 100644 index 0000000..748a192 --- /dev/null +++ b/include/bounce/softbody/joints/softbody_anchor.h @@ -0,0 +1,102 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFT_BODY_ANCHOR_H +#define B3_SOFT_BODY_ANCHOR_H + +#include +#include + +class b3Body; +class b3SoftBodyNode; + +struct b3SoftBodySolverData; + +// Soft body anchor joint definition. +// This requires defining local anchor points. +struct b3SoftBodyAnchorDef +{ + // Initialize this joint using body, node and an anchor point in world coordinates. + void Initialize(b3Body* bodyA, b3SoftBodyNode* nodeB, const b3Vec3& anchor); + + // Body A + b3Body* bodyA; + + // Node B + b3SoftBodyNode* nodeB; + + // The anchor point relative to body A's origin + b3Vec3 localAnchorA; + + // The anchor point relative to node B's origin + b3Vec3 localAnchorB; +}; + +// Use soft body anchors to attach a body to a soft body node with bilateral response. +class b3SoftBodyAnchor +{ +public: + // Get the world anchor point A. + b3Vec3 GetAnchorA() const; + + // Get the world anchor point B. + b3Vec3 GetAnchorB() const; + + // Get the local anchor point relative to body A's origin. + const b3Vec3& GetLocalAnchorA() const { return m_localAnchorA; } + + // Get the local anchor point relative to node B's origin. + const b3Vec3& GetLocalAnchorB() const { return m_localAnchorB; } +private: + friend class b3SoftBody; + friend class b3SoftBodySolver; + friend class b3List2; + + b3SoftBodyAnchor(const b3SoftBodyAnchorDef& def); + ~b3SoftBodyAnchor() { } + + void InitializeConstraints(const b3SoftBodySolverData* data); + void WarmStart(const b3SoftBodySolverData* data); + void SolveVelocityConstraints(const b3SoftBodySolverData* data); + bool SolvePositionConstraints(const b3SoftBodySolverData* data); + + // Solver shared + b3Body* m_bodyA; + b3SoftBodyNode* m_nodeB; + + b3Vec3 m_localAnchorA; + b3Vec3 m_localAnchorB; + + // Solver temp + scalar m_mA; + b3Mat33 m_iA; + + scalar m_mB; + u32 m_indexB; + + b3Vec3 m_rA; + + b3Mat33 m_mass; + b3Vec3 m_impulse; + b3Vec3 m_velocityBias; + + b3SoftBodyAnchor* m_prev; + b3SoftBodyAnchor* m_next; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/softbody/shapes/softbody_shape.h b/include/bounce/softbody/shapes/softbody_shape.h new file mode 100644 index 0000000..3921331 --- /dev/null +++ b/include/bounce/softbody/shapes/softbody_shape.h @@ -0,0 +1,82 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFTBODY_SHAPE_H +#define B3_SOFTBODY_SHAPE_H + +#include + +class b3SoftBody; + +// Soft body shape type +enum b3SoftBodyShapeType +{ + e_softBodySphereShape, + e_softBodyWorldShape, + e_maxSoftBodyShapes +}; + +struct b3SoftBodyShapeDef +{ + b3SoftBodyShapeDef() + { + density = scalar(0); + radius = scalar(0); + friction = scalar(0); + } + + // Density + scalar density; + + // Radius + scalar radius; + + // Coefficient of friction + scalar friction; + +}; + +// Soft body shape +class b3SoftBodyShape +{ +public: +protected: + friend class b3SoftBody; + friend class b3SoftBodyContactManager; + friend class b3SoftBodyContactSolver; + + // Type + b3SoftBodyShapeType m_type; + + // Density + scalar m_density; + + // Radius + scalar m_radius; + + // Coefficient of friction + scalar m_friction; + + // Broadphase ID + u32 m_broadPhaseId; + + // Soft body + b3SoftBody* m_body; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/softbody/shapes/softbody_sphere_shape.h b/include/bounce/softbody/shapes/softbody_sphere_shape.h new file mode 100644 index 0000000..d91c322 --- /dev/null +++ b/include/bounce/softbody/shapes/softbody_sphere_shape.h @@ -0,0 +1,62 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFTBODY_SPHERE_SHAPE_H +#define B3_SOFTBODY_SPHERE_SHAPE_H + +#include +#include +#include + +class b3SoftBodyNode; + +struct b3SoftBodySphereShapeDef : public b3SoftBodyShapeDef +{ + b3SoftBodyNode* node; +}; + +class b3SoftBodySphereShape : public b3SoftBodyShape +{ +public: +private: + friend class b3SoftBody; + friend class b3SoftBodyNode; + friend class b3SoftBodyContactManager; + friend class b3SoftBodyContactSolver; + friend class b3SoftBodySphereAndShapeContact; + friend class b3List2; + + b3SoftBodySphereShape() { } + ~b3SoftBodySphereShape() { } + + // Compute AABB + b3AABB ComputeAABB() const; + + // Synchronize AABB + void Synchronize(const b3Vec3& displacement); + + // Destroy contacts + void DestroyContacts(); + + b3SoftBodyNode* m_node; + + b3SoftBodySphereShape* m_prev; + b3SoftBodySphereShape* m_next; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/softbody/shapes/softbody_world_shape.h b/include/bounce/softbody/shapes/softbody_world_shape.h new file mode 100644 index 0000000..3ead012 --- /dev/null +++ b/include/bounce/softbody/shapes/softbody_world_shape.h @@ -0,0 +1,61 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFTBODY_WORLD_SHAPE_H +#define B3_SOFTBODY_WORLD_SHAPE_H + +#include +#include +#include + +class b3Shape; + +struct b3SoftBodyWorldShapeDef +{ + const b3Shape* shape; +}; + +class b3SoftBodyWorldShape : public b3SoftBodyShape +{ +public: +private: + friend class b3SoftBody; + friend class b3SoftBodyContactManager; + friend class b3SoftBodyContactSolver; + friend class b3SoftBodySphereAndShapeContact; + friend class b3List2; + + b3SoftBodyWorldShape() { } + ~b3SoftBodyWorldShape() { } + + // Compute AABB + b3AABB ComputeAABB() const; + + // Synchronize AABB + void Synchronize(const b3Vec3& displacement); + + // Destroy contacts + void DestroyContacts(); + + const b3Shape* m_shape; + + b3SoftBodyWorldShape* m_prev; + b3SoftBodyWorldShape* m_next; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index f3394c7..6f1ebe4 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -19,51 +19,38 @@ #ifndef B3_SOFT_BODY_H #define B3_SOFT_BODY_H -#include #include - -class b3World; +#include +#include +#include struct b3SoftBodyMesh; -struct b3SoftBodyNode; -struct b3SoftBodyElement; +struct b3SoftBodyTimeStep; + +class b3SoftBodyNode; +class b3SoftBodyElement; + +struct b3SoftBodySphereShapeDef; +class b3SoftBodySphereShape; + +struct b3SoftBodyWorldShapeDef; +class b3SoftBodyWorldShape; + +struct b3SoftBodyAnchorDef; +class b3SoftBodyAnchor; struct b3RayCastInput; struct b3RayCastOutput; struct b3SoftBodyRayCastSingleOutput { - u32 tetrahedron; - u32 v1, v2, v3; - float32 fraction; + u32 triangle; + scalar fraction; b3Vec3 normal; }; -// Soft body tetrahedron element -struct b3SoftBodyElement -{ - float32 E; - float32 nu; - - float32 c_yield; - float32 c_creep; - float32 c_max; - - b3Mat33 K[16]; // 12 x 12 - b3Mat33 invE; - b3Quat q; - float32 B[72]; // 6 x 12 - float32 P[72]; // V * BT * E -> 12 x 6 - float32 epsilon_plastic[6]; // 6 x 1 -}; - -// Soft body tetrahedron triangle -struct b3SoftBodyTriangle -{ - u32 v1, v2, v3; - u32 tetrahedron; -}; +struct b3SparseMat33Pattern; // Soft body definition // This requires defining a soft body mesh which is typically bound to a render mesh @@ -73,39 +60,55 @@ struct b3SoftBodyDef b3SoftBodyDef() { mesh = nullptr; - density = 0.1f; - E = 100.0f; - nu = 0.3f; - c_yield = B3_MAX_FLOAT; - c_creep = 0.0f; - c_max = 0.0f; + density = scalar(0.1); + E = scalar(100); + nu = scalar(0.3); + c_yield = B3_MAX_SCALAR; + c_creep = scalar(0); + c_max = scalar(0); + radius = scalar(0); + friction = scalar(0.2); + massDamping = scalar(0); + stiffnessDamping = scalar(0); } // Soft body mesh const b3SoftBodyMesh* mesh; // Density in kg/m^3 - float32 density; + scalar density; // Material Young's modulus in [0, inf] // Units are 1e3N/m^2 - float32 E; + scalar E; // Material Poisson ratio in [0, 0.5] // This is a dimensionless value - float32 nu; + scalar nu; // Material elastic strain yield in [0, inf] // This is a dimensionless value - float32 c_yield; + scalar c_yield; // Material creep rate in [0, 1 / dt] // Units are Hz - float32 c_creep; + scalar c_creep; // Material maximum plastic strain in [0, inf] // This is a dimensionless value - float32 c_max; + scalar c_max; + + // Soft body radius + scalar radius; + + // Soft body coefficient of friction + scalar friction; + + // Soft body mass damping coefficient + scalar massDamping; + + // Soft body stiffness damping coefficient + scalar stiffnessDamping; }; // A soft body represents a deformable volume as a collection of nodes and elements. @@ -122,30 +125,64 @@ public: void SetGravity(const b3Vec3& gravity); // Get the acceleration of gravity. - b3Vec3 GetGravity() const; + const b3Vec3& GetGravity() const; - // Attach a world to this soft body. - // The soft body will be able to respond to collisions with the bodies in the attached world. - void SetWorld(b3World* world); + // Set the coefficient of mass damping. + void SetMassDamping(scalar damping); - // Get the world attached to this soft body. - const b3World* GetWorld() const; - b3World* GetWorld(); + // Get the coefficient of mass damping. + scalar GetMassDamping() const; + + // Set the coefficient of stiffness damping. + void SetStiffnessDamping(scalar damping); + + // Get the coefficient of stiffness damping. + scalar GetStiffnessDamping() const; + + // Create a sphere shape. + b3SoftBodySphereShape* CreateSphereShape(const b3SoftBodySphereShapeDef& def); + + // Destroy a given sphere shape. + void DestroySphereShape(b3SoftBodySphereShape* shape); + + // Return the list of sphere shapes. + b3List2& GetSphereShapeList(); + const b3List2& GetSphereShapeList() const; + + // Create a world shape. + b3SoftBodyWorldShape* CreateWorldShape(const b3SoftBodyWorldShapeDef& def); + + // Destroy a given world shape. + void DestroyWorldShape(b3SoftBodyWorldShape* shape); + + // Return the list of world shapes. + b3List2& GetWorldShapeList(); + const b3List2& GetWorldShapeList() const; + + // Create a soft body anchor joint. + b3SoftBodyAnchor* CreateAnchor(const b3SoftBodyAnchorDef& def); + + // Destroy a given soft body anchor joint. + void DestroyAnchor(b3SoftBodyAnchor* anchor); + + // Get the list of anchor joints in this soft body. + const b3List2& GetAnchorList() const; + b3List2& GetAnchorList(); // Return the soft body mesh proxy. const b3SoftBodyMesh* GetMesh() const; // Return the node associated with the given vertex. - b3SoftBodyNode* GetVertexNode(u32 i); + b3SoftBodyNode* GetNode(u32 i); // Return the element associated with the given tetrahedron. - b3SoftBodyElement* GetTetrahedronElement(u32 i); + b3SoftBodyElement* GetElement(u32 i); // Return the kinetic (or dynamic) energy in this system. - float32 GetEnergy() const; + scalar GetEnergy() const; // Perform a time step. - void Step(float32 dt, u32 velocityIterations, u32 positionIterations); + void Step(scalar dt, u32 velocityIterations, u32 positionIterations); // Debug draw the body using the associated mesh. void Draw() const; @@ -153,40 +190,65 @@ private: friend class b3SoftBodyContactManager; friend class b3SoftBodySolver; friend class b3SoftBodyForceSolver; - friend struct b3SoftBodyNode; + friend class b3SoftBodyNode; + friend class b3SoftBodyElement; + friend class b3SoftBodySphereShape; + friend class b3SoftBodyWorldShape; // Compute mass of each node. void ComputeMass(); // Solve - void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); + void Solve(const b3SoftBodyTimeStep& step); // Stack allocator b3StackAllocator m_stackAllocator; + // Pool of sphere shapes + b3BlockPool m_sphereShapeBlocks; + + // Pool of world shapes + b3BlockPool m_worldShapeBlocks; + // Gravity acceleration b3Vec3 m_gravity; + // Soft body density + scalar m_density; + + // Mass damping coefficient + scalar m_massDamping; + + // Stiffness damping coefficient + scalar m_stiffnessDamping; + // Proxy mesh const b3SoftBodyMesh* m_mesh; - // Soft body density - float32 m_density; - // Soft body nodes b3SoftBodyNode* m_nodes; // Soft body elements b3SoftBodyElement* m_elements; - // Soft body triangles - b3SoftBodyTriangle* m_triangles; - // Contact manager b3SoftBodyContactManager m_contactManager; - // Attached world - b3World* m_world; + // Anchor joints + b3List2 m_anchorList; + + // Sphere shapes + b3List2 m_sphereShapeList; + + // World shapes + b3List2 m_worldShapeList; + + // Stiffness matrix sparsity pattern + b3SparseMat33Pattern* m_KP; + + // Used to compute the ration to support + // variable time steps + scalar m_inv_dt0; }; inline void b3SoftBody::SetGravity(const b3Vec3& gravity) @@ -194,19 +256,61 @@ inline void b3SoftBody::SetGravity(const b3Vec3& gravity) m_gravity = gravity; } -inline b3Vec3 b3SoftBody::GetGravity() const +inline const b3Vec3& b3SoftBody::GetGravity() const { return m_gravity; } -inline const b3World* b3SoftBody::GetWorld() const +inline void b3SoftBody::SetMassDamping(scalar damping) { - return m_world; + B3_ASSERT(damping >= scalar(0)); + m_massDamping = damping; } -inline b3World* b3SoftBody::GetWorld() +inline scalar b3SoftBody::GetMassDamping() const { - return m_world; + return m_massDamping; +} + +inline void b3SoftBody::SetStiffnessDamping(scalar damping) +{ + B3_ASSERT(damping >= scalar(0)); + m_stiffnessDamping = damping; +} + +inline scalar b3SoftBody::GetStiffnessDamping() const +{ + return m_stiffnessDamping; +} + +inline b3List2& b3SoftBody::GetSphereShapeList() +{ + return m_sphereShapeList; +} + +inline const b3List2& b3SoftBody::GetSphereShapeList() const +{ + return m_sphereShapeList; +} + +inline b3List2& b3SoftBody::GetWorldShapeList() +{ + return m_worldShapeList; +} + +inline const b3List2& b3SoftBody::GetWorldShapeList() const +{ + return m_worldShapeList; +} + +inline const b3List2& b3SoftBody::GetAnchorList() const +{ + return m_anchorList; +} + +inline b3List2& b3SoftBody::GetAnchorList() +{ + return m_anchorList; } inline const b3SoftBodyMesh* b3SoftBody::GetMesh() const diff --git a/include/bounce/softbody/softbody_contact_manager.h b/include/bounce/softbody/softbody_contact_manager.h index 5e57310..baf7b24 100644 --- a/include/bounce/softbody/softbody_contact_manager.h +++ b/include/bounce/softbody/softbody_contact_manager.h @@ -19,10 +19,10 @@ #ifndef B3_SOFTBODY_CONTACT_MANAGER_H #define B3_SOFTBODY_CONTACT_MANAGER_H -#include -#include -#include #include +#include +#include +#include class b3SoftBody; @@ -32,17 +32,17 @@ class b3SoftBodyContactManager public: b3SoftBodyContactManager(); - void FindNewBodyContacts(); - void AddNSPair(b3SoftBodyNode* n1, b3Shape* s2); - void UpdateBodyContacts(); + void FindNewContacts(); + void AddPair(void* data1, void* data2); + void UpdateContacts(); - b3NodeBodyContact* CreateNodeBodyContact(); - void Destroy(b3NodeBodyContact* c); + b3SoftBodySphereAndShapeContact* CreateSphereAndShapeContact(); + void Destroy(b3SoftBodySphereAndShapeContact* c); - b3BlockPool m_nodeBodyContactBlocks; + b3BlockPool m_sphereAndShapeContactBlocks; b3SoftBody* m_body; b3BroadPhase m_broadPhase; - b3List2 m_nodeBodyContactList; + b3List2 m_sphereAndShapeContactList; }; #endif \ No newline at end of file diff --git a/include/bounce/softbody/softbody_element.h b/include/bounce/softbody/softbody_element.h new file mode 100644 index 0000000..e00b2dc --- /dev/null +++ b/include/bounce/softbody/softbody_element.h @@ -0,0 +1,159 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFTBODY_ELEMENT_H +#define B3_SOFTBODY_ELEMENT_H + +#include + +class b3SoftBody; + +// Soft body tetrahedron element +class b3SoftBodyElement +{ +public: + // Set the material Young Modulus of elasticity in [0, inf]. + void SetE(scalar E); + + // Get the material Young Modulus of elasticity in [0, inf]. + scalar GetE() const; + + // Set the Poisson's ratio in [0, 0.5]. + void SetNU(scalar nu); + + // Get the Poisson's ratio in [0, 0.5]. + scalar GetNU() const; + + // Set the elastic strain yield in range [0, inf]. + // Set this value to inf to disable plasticity. + void SetCYield(scalar yield); + + // Get the elastic strain yield in range [0, inf]. + scalar GetCYield() const; + + // Set the material creep rate in hertz. + void SetCCreep(scalar hz); + + // Get the material creep rate in hertz. + scalar GetCCreep() const; + + // Set the material maximum plastic strain in the range [0, inf]. + void SetCMax(scalar max); + + // Get the material maximum plastic strain in the range [0, inf]. + scalar GetCMax() const; +private: + friend class b3SoftBody; + friend class b3SoftBodySolver; + friend class b3SoftBodyForceSolver; + + b3SoftBodyElement() { } + + ~b3SoftBodyElement() { } + + void ComputeMatrices(); + + // Reference volume + scalar m_V; + + // Elasticity + scalar m_E; + scalar m_nu; + + // Plasticity + scalar m_c_yield; + scalar m_c_creep; + scalar m_c_max; + scalar m_epsilon_plastic[6]; // 6 x 1 + + // Solver shared + b3Mat33 m_invE; // 3 x 3 + b3Quat m_q; // 3 x 3 + b3Mat33* m_Kp[16]; // 12 x 12 + b3Mat33 m_K[16]; // 12 x 12 + scalar m_B[72]; // 6 x 12 + scalar m_P[72]; // V * BT * E -> 12 x 6 + + // Soft body + b3SoftBody* m_body; +}; + +inline void b3SoftBodyElement::SetE(scalar E) +{ + B3_ASSERT(E > scalar(0)); + if (E != m_E) + { + m_E = E; + ComputeMatrices(); + } +} + +inline scalar b3SoftBodyElement::GetE() const +{ + return m_E; +} + +inline void b3SoftBodyElement::SetNU(scalar nu) +{ + B3_ASSERT(nu >= scalar(0) && nu <= scalar(0.5)); + if (nu != m_nu) + { + m_nu = nu; + ComputeMatrices(); + } +} + +inline scalar b3SoftBodyElement::GetNU() const +{ + return m_nu; +} + +inline void b3SoftBodyElement::SetCYield(scalar yield) +{ + B3_ASSERT(yield >= scalar(0)); + m_c_yield = yield; +} + +inline scalar b3SoftBodyElement::GetCYield() const +{ + return m_c_yield; +} + +inline void b3SoftBodyElement::SetCCreep(scalar hz) +{ + B3_ASSERT(hz >= scalar(0)); + m_c_creep = hz; +} + +inline scalar b3SoftBodyElement::GetCCreep() const +{ + return m_c_creep; +} + +inline void b3SoftBodyElement::SetCMax(scalar max) +{ + B3_ASSERT(max >= scalar(0)); + m_c_max = max; +} + +inline scalar b3SoftBodyElement::GetCMax() const +{ + return m_c_max; +} + +#endif \ No newline at end of file diff --git a/include/bounce/softbody/softbody_force_solver.h b/include/bounce/softbody/softbody_force_solver.h index 1c2f35b..f495267 100644 --- a/include/bounce/softbody/softbody_force_solver.h +++ b/include/bounce/softbody/softbody_force_solver.h @@ -21,17 +21,19 @@ #include #include +#include class b3StackAllocator; class b3SoftBody; -class b3SoftBodyMesh; +struct b3SoftBodyMesh; -struct b3SoftBodyNode; -struct b3SoftBodyElement; +class b3SoftBodyNode; +class b3SoftBodyElement; struct b3SoftBodyForceSolverDef { + b3SoftBodyTimeStep step; b3SoftBody* body; }; @@ -41,10 +43,11 @@ public: b3SoftBodyForceSolver(const b3SoftBodyForceSolverDef& def); ~b3SoftBodyForceSolver(); - void Solve(float32 dt, const b3Vec3& gravity); + void Solve(const b3Vec3& gravity); private: + b3SoftBodyTimeStep m_step; b3SoftBody* m_body; - b3StackAllocator* m_allocator; + b3StackAllocator* m_stack; const b3SoftBodyMesh* m_mesh; b3SoftBodyNode* m_nodes; b3SoftBodyElement* m_elements; diff --git a/include/bounce/softbody/softbody_mesh.h b/include/bounce/softbody/softbody_mesh.h index 4ffc686..af5d75d 100644 --- a/include/bounce/softbody/softbody_mesh.h +++ b/include/bounce/softbody/softbody_mesh.h @@ -21,6 +21,11 @@ #include +struct b3SoftBodyMeshTriangle +{ + u32 v1, v2, v3; +}; + struct b3SoftBodyMeshTetrahedron { u32 v1, v2, v3, v4; @@ -30,6 +35,8 @@ struct b3SoftBodyMesh { u32 vertexCount; b3Vec3* vertices; + u32 triangleCount; + b3SoftBodyMeshTriangle* triangles; u32 tetrahedronCount; b3SoftBodyMeshTetrahedron* tetrahedrons; }; @@ -39,9 +46,9 @@ struct b3QSoftBodyMesh : public b3SoftBodyMesh b3QSoftBodyMesh(); ~b3QSoftBodyMesh(); - void SetAsSphere(float32 radius, u32 subdivisions); + void SetAsSphere(scalar radius, u32 subdivisions); - void SetAsCylinder(float32 radius, float32 ey, u32 segments); + void SetAsCylinder(scalar radius, scalar ey, u32 segments); }; #endif \ No newline at end of file diff --git a/include/bounce/softbody/softbody_node.h b/include/bounce/softbody/softbody_node.h index 8e53062..04100ae 100644 --- a/include/bounce/softbody/softbody_node.h +++ b/include/bounce/softbody/softbody_node.h @@ -19,9 +19,7 @@ #ifndef B3_SOFT_BODY_NODE_H #define B3_SOFT_BODY_NODE_H -#include #include -#include class b3SoftBody; @@ -36,7 +34,7 @@ enum b3SoftBodyNodeType }; // A soft body node. -struct b3SoftBodyNode +class b3SoftBodyNode { public: // Set the node type. @@ -45,9 +43,6 @@ public: // Get the node type. b3SoftBodyNodeType GetType() const; - // Get the vertex index. - u32 GetVertex() const; - // Set the node position. // If the node is dynamic changing the position directly might lead // to physically incorrect simulation behaviour. @@ -63,46 +58,40 @@ public: const b3Vec3& GetVelocity() const; // Get the node mass. - float32 GetMass() const; + scalar GetMass() const; - // Set the node mass damping. - void SetMassDamping(float32 damping); + // Get the mesh vertex index. + u32 GetMeshIndex() const; - // Get the node mass damping. - float32 GetMassDamping() const; - - // Set the node radius. - void SetRadius(float32 radius); - - // Get the node radius. - float32 GetRadius() const; - - // Set the node coefficient of friction. - void SetFriction(float32 friction); - - // Get the node coefficient of friction. - float32 GetFriction() const; - // Apply a force. void ApplyForce(const b3Vec3& force); + + // Get the applied force. + const b3Vec3& GetForce() const; + + // Apply a translation. + void ApplyTranslation(const b3Vec3& translation); + + // Get the applied translation. + const b3Vec3& GetTranslation() const; private: friend class b3SoftBody; friend class b3SoftBodyContactManager; friend class b3SoftBodySolver; friend class b3SoftBodyForceSolver; friend class b3SoftBodyContactSolver; - friend class b3NodeBodyContact; + friend class b3SoftBodySphereAndShapeContact; + friend class b3SoftBodyAnchor; b3SoftBodyNode() { } - ~b3SoftBodyNode() { } - // Synchronize node - void Synchronize(const b3Vec3& displacement); + // Synchronize spheres + void SynchronizeSpheres(); - // Destroy associated contacts + // Destroy contacts void DestroyContacts(); - + // Type b3SoftBodyNodeType m_type; @@ -115,70 +104,31 @@ private: // Applied external force b3Vec3 m_force; + // Applied external translation + b3Vec3 m_translation; + // Mass - float32 m_mass; + scalar m_mass; // Inverse mass - float32 m_invMass; + scalar m_invMass; - // Mass damping - float32 m_massDamping; - - // Radius - float32 m_radius; - - // Coefficient of friction - float32 m_friction; - - // User data. - void* m_userData; - - // Soft body mesh vertex index. - u32 m_vertex; - - // Broadphase proxy - u32 m_broadPhaseId; + // Mesh index + u32 m_meshIndex; // Soft body b3SoftBody* m_body; }; -inline void b3SoftBodyNode::SetType(b3SoftBodyNodeType type) -{ - if (m_type == type) - { - return; - } - - m_type = type; - m_force.SetZero(); - - if (type == e_staticSoftBodyNode) - { - m_velocity.SetZero(); - Synchronize(b3Vec3_zero); - } - - DestroyContacts(); -} - inline b3SoftBodyNodeType b3SoftBodyNode::GetType() const { return m_type; } -inline u32 b3SoftBodyNode::GetVertex() const -{ - return m_vertex; -} - inline void b3SoftBodyNode::SetPosition(const b3Vec3& position) { - b3Vec3 displacement = position - m_position; - m_position = position; - - Synchronize(displacement); + SynchronizeSpheres(); } inline const b3Vec3& b3SoftBodyNode::GetPosition() const @@ -200,43 +150,11 @@ inline const b3Vec3& b3SoftBodyNode::GetVelocity() const return m_velocity; } -inline float32 b3SoftBodyNode::GetMass() const +inline scalar b3SoftBodyNode::GetMass() const { return m_mass; } -inline void b3SoftBodyNode::SetMassDamping(float32 massDamping) -{ - m_massDamping = massDamping; -} - -inline float32 b3SoftBodyNode::GetMassDamping() const -{ - return m_massDamping; -} - -inline void b3SoftBodyNode::SetRadius(float32 radius) -{ - m_radius = radius; - - Synchronize(b3Vec3_zero); -} - -inline float32 b3SoftBodyNode::GetRadius() const -{ - return m_radius; -} - -inline void b3SoftBodyNode::SetFriction(float32 friction) -{ - m_friction = friction; -} - -inline float32 b3SoftBodyNode::GetFriction() const -{ - return m_friction; -} - inline void b3SoftBodyNode::ApplyForce(const b3Vec3& force) { if (m_type != e_dynamicSoftBodyNode) @@ -246,4 +164,24 @@ inline void b3SoftBodyNode::ApplyForce(const b3Vec3& force) m_force += force; } +inline const b3Vec3& b3SoftBodyNode::GetForce() const +{ + return m_force; +} + +inline void b3SoftBodyNode::ApplyTranslation(const b3Vec3& translation) +{ + m_translation += translation; +} + +inline const b3Vec3& b3SoftBodyNode::GetTranslation() const +{ + return m_translation; +} + +inline u32 b3SoftBodyNode::GetMeshIndex() const +{ + return m_meshIndex; +} + #endif \ No newline at end of file diff --git a/include/bounce/softbody/softbody_solver.h b/include/bounce/softbody/softbody_solver.h index e0a3396..48541c1 100644 --- a/include/bounce/softbody/softbody_solver.h +++ b/include/bounce/softbody/softbody_solver.h @@ -21,40 +21,55 @@ #include #include +#include class b3StackAllocator; class b3SoftBody; -class b3SoftBodyMesh; +struct b3SoftBodyMesh; -struct b3SoftBodyNode; -struct b3SoftBodyElement; +class b3SoftBodyNode; +class b3SoftBodyElement; -struct b3NodeBodyContact; +class b3SoftBodySphereAndShapeContact; +class b3SoftBodyAnchor; struct b3SoftBodySolverDef { b3SoftBody* body; }; +struct b3SoftBodySolverData +{ + b3SoftBodyTimeStep step; + b3Vec3* positions; + b3Vec3* velocities; +}; + class b3SoftBodySolver { public: b3SoftBodySolver(const b3SoftBodySolverDef& def); ~b3SoftBodySolver(); - void Add(b3NodeBodyContact* c); + void Add(b3SoftBodySphereAndShapeContact* c); + void Add(b3SoftBodyAnchor* a); - void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); + void Solve(const b3SoftBodyTimeStep& step, const b3Vec3& gravity); private: b3SoftBody* m_body; - b3StackAllocator* m_allocator; + b3StackAllocator* m_stack; const b3SoftBodyMesh* m_mesh; b3SoftBodyNode* m_nodes; b3SoftBodyElement* m_elements; - u32 m_bodyContactCapacity; - u32 m_bodyContactCount; - b3NodeBodyContact** m_bodyContacts; + + u32 m_shapeContactCapacity; + u32 m_shapeContactCount; + b3SoftBodySphereAndShapeContact** m_shapeContacts; + + u32 m_anchorCapacity; + u32 m_anchorCount; + b3SoftBodyAnchor** m_anchors; }; #endif \ No newline at end of file diff --git a/include/bounce/softbody/softbody_time_step.h b/include/bounce/softbody/softbody_time_step.h new file mode 100644 index 0000000..39d8ce7 --- /dev/null +++ b/include/bounce/softbody/softbody_time_step.h @@ -0,0 +1,34 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +#ifndef B3_SOFTBODY_TIME_STEP_H +#define B3_SOFTBODY_TIME_STEP_H + +// Time step parameters +struct b3SoftBodyTimeStep +{ + scalar dt; + scalar inv_dt; + scalar dt_ratio; + u32 velocityIterations; + u32 positionIterations; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/sparse/dense_vec3.h b/include/bounce/sparse/dense_vec3.h index ffc4e42..1c44aab 100644 --- a/include/bounce/sparse/dense_vec3.h +++ b/include/bounce/sparse/dense_vec3.h @@ -20,6 +20,7 @@ #define B3_DENSE_VEC_3_H #include +#include struct b3DenseVec3 { @@ -32,20 +33,20 @@ struct b3DenseVec3 b3DenseVec3(u32 _n) { n = _n; - v = (b3Vec3*)b3Alloc(n * sizeof(b3Vec3)); + v = (b3Vec3*)b3FrameAllocator_sparseAllocator->Allocate(n * sizeof(b3Vec3)); } b3DenseVec3(const b3DenseVec3& _v) { n = _v.n; - v = (b3Vec3*)b3Alloc(n * sizeof(b3Vec3)); + v = (b3Vec3*)b3FrameAllocator_sparseAllocator->Allocate(n * sizeof(b3Vec3)); Copy(_v); } ~b3DenseVec3() { - b3Free(v); + b3FrameAllocator_sparseAllocator->Free(v); } const b3Vec3& operator[](u32 i) const @@ -73,10 +74,10 @@ struct b3DenseVec3 return *this; } - b3Free(v); + b3FrameAllocator_sparseAllocator->Free(v); n = _v.n; - v = (b3Vec3*)b3Alloc(n * sizeof(b3Vec3)); + v = (b3Vec3*)b3FrameAllocator_sparseAllocator->Allocate(n * sizeof(b3Vec3)); Copy(_v); @@ -121,7 +122,7 @@ inline void b3Sub(b3DenseVec3& out, const b3DenseVec3& a, const b3DenseVec3& b) } } -inline void b3Mul(b3DenseVec3& out, float32 a, const b3DenseVec3& b) +inline void b3Mul(b3DenseVec3& out, scalar a, const b3DenseVec3& b) { B3_ASSERT(out.n == b.n); @@ -133,14 +134,14 @@ inline void b3Mul(b3DenseVec3& out, float32 a, const b3DenseVec3& b) inline void b3Negate(b3DenseVec3& out, const b3DenseVec3& v) { - b3Mul(out, -1.0f, v); + b3Mul(out, scalar(-1), v); } -inline float32 b3Dot(const b3DenseVec3& a, const b3DenseVec3& b) +inline scalar b3Dot(const b3DenseVec3& a, const b3DenseVec3& b) { B3_ASSERT(a.n == b.n); - float32 result = 0.0f; + scalar result(0); for (u32 i = 0; i < a.n; ++i) { @@ -150,6 +151,23 @@ inline float32 b3Dot(const b3DenseVec3& a, const b3DenseVec3& b) return result; } +inline scalar b3LengthSquared(const b3DenseVec3& v) +{ + scalar result(0); + + for (u32 i = 0; i < v.n; ++i) + { + result += b3LengthSquared(v[i]); + } + + return result; +} + +inline scalar b3Length(const b3DenseVec3& v) +{ + return b3Sqrt(b3LengthSquared(v)); +} + inline b3DenseVec3 operator+(const b3DenseVec3& a, const b3DenseVec3& b) { b3DenseVec3 result(a.n); @@ -164,7 +182,7 @@ inline b3DenseVec3 operator-(const b3DenseVec3& a, const b3DenseVec3& b) return result; } -inline b3DenseVec3 operator*(float32 a, const b3DenseVec3& b) +inline b3DenseVec3 operator*(scalar a, const b3DenseVec3& b) { b3DenseVec3 result(b.n); b3Mul(result, a, b); diff --git a/include/bounce/sparse/diag_mat33.h b/include/bounce/sparse/diag_mat33.h index f7a1486..e3c91ab 100644 --- a/include/bounce/sparse/diag_mat33.h +++ b/include/bounce/sparse/diag_mat33.h @@ -20,6 +20,7 @@ #define B3_DIAG_MAT_33_H #include +#include #include // Diagonal matrix storing only the diagonal elements of the @@ -35,20 +36,20 @@ struct b3DiagMat33 b3DiagMat33(u32 _n) { n = _n; - v = (b3Mat33*)b3Alloc(n * sizeof(b3Mat33)); + v = (b3Mat33*)b3FrameAllocator_sparseAllocator->Allocate(n * sizeof(b3Mat33)); } b3DiagMat33(const b3DiagMat33& _v) { n = _v.n; - v = (b3Mat33*)b3Alloc(n * sizeof(b3Mat33)); + v = (b3Mat33*)b3FrameAllocator_sparseAllocator->Allocate(n * sizeof(b3Mat33)); Copy(_v); } ~b3DiagMat33() { - b3Free(v); + b3FrameAllocator_sparseAllocator->Free(v); } const b3Mat33& operator[](u32 i) const @@ -76,10 +77,10 @@ struct b3DiagMat33 return *this; } - b3Free(v); + b3FrameAllocator_sparseAllocator->Free(v); n = _v.n; - v = (b3Mat33*)b3Alloc(n * sizeof(b3Mat33)); + v = (b3Mat33*)b3FrameAllocator_sparseAllocator->Allocate(n * sizeof(b3Mat33)); Copy(_v); @@ -132,7 +133,7 @@ inline void b3Sub(b3DiagMat33& out, const b3DiagMat33& a, const b3DiagMat33& b) } } -inline void b3Mul(b3DiagMat33& out, float32 a, const b3DiagMat33& b) +inline void b3Mul(b3DiagMat33& out, scalar a, const b3DiagMat33& b) { B3_ASSERT(out.n == b.n); @@ -154,7 +155,7 @@ inline void b3Mul(b3DenseVec3& out, const b3DiagMat33& a, const b3DenseVec3& b) inline void b3Negate(b3DiagMat33& out, const b3DiagMat33& v) { - b3Mul(out, -1.0f, v); + b3Mul(out, scalar(-1), v); } inline b3DiagMat33 operator+(const b3DiagMat33& a, const b3DiagMat33& b) @@ -171,7 +172,7 @@ inline b3DiagMat33 operator-(const b3DiagMat33& a, const b3DiagMat33& b) return result; } -inline b3DiagMat33 operator*(float32 a, const b3DiagMat33& b) +inline b3DiagMat33 operator*(scalar a, const b3DiagMat33& b) { b3DiagMat33 result(b.n); b3Mul(result, a, b); diff --git a/include/bounce/sparse/sparse.h b/include/bounce/sparse/sparse.h new file mode 100644 index 0000000..6266a88 --- /dev/null +++ b/include/bounce/sparse/sparse.h @@ -0,0 +1,29 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SPARSE_H +#define B3_SPARSE_H + +#include + +// The sparse allocator used by Bounce. +// You should set this to an implementation +// before calling any function that uses the object. +extern b3FrameAllocator* b3FrameAllocator_sparseAllocator; + +#endif \ No newline at end of file diff --git a/include/bounce/sparse/sparse_mat33.h b/include/bounce/sparse/sparse_mat33.h index 7bebd1a..5237e2b 100644 --- a/include/bounce/sparse/sparse_mat33.h +++ b/include/bounce/sparse/sparse_mat33.h @@ -20,39 +20,11 @@ #define B3_SPARSE_MAT_33_H #include +#include +#include #include #include -// An element in a sparse matrix. -struct b3RowValue -{ - u32 column; - b3Mat33 value; - b3RowValue* next; -}; - -// Singly linked list of row elements. -struct b3RowValueList -{ - b3RowValueList() - { - head = nullptr; - count = 0; - } - - ~b3RowValueList() { } - - void PushFront(b3RowValue* link) - { - link->next = head; - head = link; - ++count; - } - - b3RowValue* head; - u32 count; -}; - // A sparse matrix. // Each row is a list of non-zero elements in the row. struct b3SparseMat33 @@ -66,6 +38,9 @@ struct b3SparseMat33 // b3SparseMat33(const b3SparseMat33& _m); + // + b3SparseMat33(const b3SparseMat33Pattern& _m); + // ~b3SparseMat33(); @@ -90,6 +65,12 @@ struct b3SparseMat33 // void operator-=(const b3SparseMat33& m); + // + void operator+=(const b3DiagMat33& m); + + // + void operator-=(const b3DiagMat33& m); + u32 rowCount; b3RowValueList* rows; }; @@ -103,7 +84,7 @@ inline b3SparseMat33::b3SparseMat33() inline b3SparseMat33::b3SparseMat33(u32 m) { rowCount = m; - rows = (b3RowValueList*)b3Alloc(rowCount * sizeof(b3RowValueList)); + rows = (b3RowValueList*)b3FrameAllocator_sparseAllocator->Allocate(rowCount * sizeof(b3RowValueList)); for (u32 i = 0; i < rowCount; ++i) { new (rows + i)b3RowValueList(); @@ -113,7 +94,7 @@ inline b3SparseMat33::b3SparseMat33(u32 m) inline b3SparseMat33::b3SparseMat33(const b3SparseMat33& m) { rowCount = m.rowCount; - rows = (b3RowValueList*)b3Alloc(rowCount * sizeof(b3RowValueList)); + rows = (b3RowValueList*)b3FrameAllocator_sparseAllocator->Allocate(rowCount * sizeof(b3RowValueList)); for (u32 i = 0; i < rowCount; ++i) { new (rows + i)b3RowValueList(); @@ -122,6 +103,31 @@ inline b3SparseMat33::b3SparseMat33(const b3SparseMat33& m) Copy(m); } +inline b3SparseMat33::b3SparseMat33(const b3SparseMat33Pattern& m) +{ + rowCount = m.rowCount; + rows = (b3RowValueList*)b3FrameAllocator_sparseAllocator->Allocate(rowCount * sizeof(b3RowValueList)); + for (u32 i = 0; i < rowCount; ++i) + { + b3RowValueList* list1 = m.rows + i; + + b3RowValueList* list2 = rows + i; + new (list2) b3RowValueList(); + + b3RowValue* v1 = list1->head; + while(v1) + { + b3RowValue* v2 = (b3RowValue*)b3FrameAllocator_sparseAllocator->Allocate(sizeof(b3RowValue)); + v2->column = v1->column; + v2->value = v1->value; + + list2->PushFront(v2); + + v1 = v1->next; + } + } +} + inline b3SparseMat33::~b3SparseMat33() { Destroy(); @@ -137,14 +143,14 @@ inline void b3SparseMat33::Destroy() while (v) { b3RowValue* v0 = v->next; - b3Free(v); + b3FrameAllocator_sparseAllocator->Free(v); v = v0; } vs->~b3RowValueList(); } - b3Free(rows); + b3FrameAllocator_sparseAllocator->Free(rows); } inline b3SparseMat33& b3SparseMat33::operator=(const b3SparseMat33& _m) @@ -157,7 +163,7 @@ inline b3SparseMat33& b3SparseMat33::operator=(const b3SparseMat33& _m) Destroy(); rowCount = _m.rowCount; - rows = (b3RowValueList*)b3Alloc(rowCount * sizeof(b3RowValueList)); + rows = (b3RowValueList*)b3FrameAllocator_sparseAllocator->Allocate(rowCount * sizeof(b3RowValueList)); for (u32 i = 0; i < rowCount; ++i) { new (rows + i)b3RowValueList(); @@ -181,7 +187,7 @@ inline void b3SparseMat33::Copy(const b3SparseMat33& _m) for (b3RowValue* v1 = vs1->head; v1; v1 = v1->next) { - b3RowValue* v2 = (b3RowValue*)b3Alloc(sizeof(b3RowValue)); + b3RowValue* v2 = (b3RowValue*)b3FrameAllocator_sparseAllocator->Allocate(sizeof(b3RowValue)); v2->column = v1->column; v2->value = v1->value; @@ -224,7 +230,7 @@ inline b3Mat33& b3SparseMat33::operator()(u32 i, u32 j) } } - b3RowValue* v = (b3RowValue*)b3Alloc(sizeof(b3RowValue)); + b3RowValue* v = (b3RowValue*)b3FrameAllocator_sparseAllocator->Allocate(sizeof(b3RowValue)); v->column = j; v->value.SetZero(); @@ -267,6 +273,26 @@ inline void b3SparseMat33::operator-=(const b3SparseMat33& m) } } +inline void b3SparseMat33::operator+=(const b3DiagMat33& m) +{ + B3_ASSERT(rowCount == m.n); + + for (u32 i = 0; i < m.n; ++i) + { + (*this)(i, i) += m[i]; + } +} + +inline void b3SparseMat33::operator-=(const b3DiagMat33& m) +{ + B3_ASSERT(rowCount == m.n); + + for (u32 i = 0; i < m.n; ++i) + { + (*this)(i, i) -= m[i]; + } +} + inline void b3Add(b3SparseMat33& out, const b3SparseMat33& a, const b3SparseMat33& b) { out = a; @@ -279,6 +305,36 @@ inline void b3Sub(b3SparseMat33& out, const b3SparseMat33& a, const b3SparseMat3 out -= b; } +inline void b3Add(b3SparseMat33& out, const b3SparseMat33& a, const b3DiagMat33& b) +{ + out = a; + out += b; +} + +inline void b3Sub(b3SparseMat33& out, const b3SparseMat33& a, const b3DiagMat33& b) +{ + out = a; + out -= b; +} + +inline void b3Add(b3SparseMat33& out, const b3DiagMat33& a, const b3SparseMat33& b) +{ + out = b; + out += a; +} + +inline void b3Sub(b3SparseMat33& out, const b3DiagMat33& a, const b3SparseMat33& b) +{ + B3_ASSERT(out.rowCount == a.n); + + for (u32 i = 0; i < a.n; ++i) + { + out(i, i) = a[i]; + } + + out -= b; +} + inline void b3Mul(b3DenseVec3& out, const b3SparseMat33& A, const b3DenseVec3& v) { B3_ASSERT(A.rowCount == out.n); @@ -299,11 +355,11 @@ inline void b3Mul(b3DenseVec3& out, const b3SparseMat33& A, const b3DenseVec3& v } } -inline void b3Mul(b3SparseMat33& out, float32 s, const b3SparseMat33& B) +inline void b3Mul(b3SparseMat33& out, scalar s, const b3SparseMat33& B) { B3_ASSERT(out.rowCount == B.rowCount); - if (s == 0.0f) + if (s == scalar(0)) { return; } @@ -334,7 +390,35 @@ inline b3SparseMat33 operator-(const b3SparseMat33& A, const b3SparseMat33& B) return result; } -inline b3SparseMat33 operator*(float32 A, const b3SparseMat33& B) +inline b3SparseMat33 operator+(const b3SparseMat33& A, const b3DiagMat33& B) +{ + b3SparseMat33 result(A.rowCount); + b3Add(result, A, B); + return result; +} + +inline b3SparseMat33 operator-(const b3SparseMat33& A, const b3DiagMat33& B) +{ + b3SparseMat33 result(A.rowCount); + b3Sub(result, A, B); + return result; +} + +inline b3SparseMat33 operator+(const b3DiagMat33& A, const b3SparseMat33& B) +{ + b3SparseMat33 result(B.rowCount); + b3Add(result, A, B); + return result; +} + +inline b3SparseMat33 operator-(const b3DiagMat33& A, const b3SparseMat33& B) +{ + b3SparseMat33 result(B.rowCount); + b3Sub(result, A, B); + return result; +} + +inline b3SparseMat33 operator*(scalar A, const b3SparseMat33& B) { b3SparseMat33 result(B.rowCount); b3Mul(result, A, B); diff --git a/include/bounce/sparse/sparse_mat33_pattern.h b/include/bounce/sparse/sparse_mat33_pattern.h new file mode 100644 index 0000000..adf17ee --- /dev/null +++ b/include/bounce/sparse/sparse_mat33_pattern.h @@ -0,0 +1,174 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SPARSE_MAT_33_PATTERN_H +#define B3_SPARSE_MAT_33_PATTERN_H + +#include + +// An element in a sparse matrix. +struct b3RowValue +{ + u32 column; + b3Mat33 value; + b3RowValue* next; +}; + +// Singly linked list of row elements. +struct b3RowValueList +{ + b3RowValueList() + { + head = nullptr; + count = 0; + } + + ~b3RowValueList() { } + + void PushFront(b3RowValue* link) + { + link->next = head; + head = link; + ++count; + } + + b3RowValue* head; + u32 count; +}; + +// A sparse matrix pattern. +// Each row is a list of non-zero elements in the row. +// This class uses b3Alloc/b3Free and not the sparse allocator. +struct b3SparseMat33Pattern +{ + // + b3SparseMat33Pattern(); + + // + b3SparseMat33Pattern(u32 m); + + // + ~b3SparseMat33Pattern(); + + // + b3Mat33* CreateElement(u32 i, u32 j); + + // + const b3Mat33& operator()(u32 i, u32 j) const; + + // + void SetZero(); + + u32 rowCount; + b3RowValueList* rows; +}; + +inline b3SparseMat33Pattern::b3SparseMat33Pattern() +{ + rowCount = 0; + rows = nullptr; +} + +inline b3SparseMat33Pattern::b3SparseMat33Pattern(u32 m) +{ + rowCount = m; + rows = (b3RowValueList*)b3Alloc(rowCount * sizeof(b3RowValueList)); + for (u32 i = 0; i < rowCount; ++i) + { + new (rows + i)b3RowValueList(); + } +} + +inline b3SparseMat33Pattern::~b3SparseMat33Pattern() +{ + for (u32 i = 0; i < rowCount; ++i) + { + b3RowValueList* vs = rows + i; + + b3RowValue* v = vs->head; + while (v) + { + b3RowValue* v0 = v->next; + b3Free(v); + v = v0; + } + + vs->~b3RowValueList(); + } + + b3Free(rows); +} + +inline const b3Mat33& b3SparseMat33Pattern::operator()(u32 i, u32 j) const +{ + B3_ASSERT(i < rowCount); + B3_ASSERT(j < rowCount); + + b3RowValueList* vs = rows + i; + + for (b3RowValue* v = vs->head; v; v = v->next) + { + if (v->column == j) + { + return v->value; + } + } + + return b3Mat33_zero; +} + +inline b3Mat33* b3SparseMat33Pattern::CreateElement(u32 i, u32 j) +{ + B3_ASSERT(i < rowCount); + B3_ASSERT(j < rowCount); + + b3RowValueList* vs = rows + i; + + for (b3RowValue* v = vs->head; v; v = v->next) + { + if (v->column == j) + { + return &v->value; + } + } + + b3RowValue* v = (b3RowValue*)b3Alloc(sizeof(b3RowValue)); + v->column = j; + v->value.SetZero(); + + vs->PushFront(v); + + return &v->value; +} + +inline void b3SparseMat33Pattern::SetZero() +{ + for (u32 i = 0; i < rowCount; ++i) + { + b3RowValueList* vs = rows + i; + + b3RowValue* v = vs->head; + while (v) + { + v->value.SetZero(); + v = v->next; + } + } +} + +#endif \ No newline at end of file diff --git a/include/bounce/sparse/sparse_mat33_view.h b/include/bounce/sparse/sparse_mat33_view.h index 9d92520..25680d2 100644 --- a/include/bounce/sparse/sparse_mat33_view.h +++ b/include/bounce/sparse/sparse_mat33_view.h @@ -20,6 +20,7 @@ #define B3_SPARSE_MAT_33_VIEW_H #include +#include struct b3ArrayRowValue { @@ -36,15 +37,44 @@ struct b3RowValueArray // A read-only sparse matrix. struct b3SparseMat33View { - // + // Construct this sparse matrix view from a sparse matrix. b3SparseMat33View(const b3SparseMat33& _m); - // + // Destruct this matrix view. ~b3SparseMat33View(); - // + // Read an indexed element from this matrix. const b3Mat33& operator()(u32 i, u32 j) const; + // Return the total number of elements in the original matrix. + u32 GetElementCount() const + { + return 3 * rowCount * 3 * rowCount; + } + + // Return an element in the original matrix given the element indices + // in the corresponding original matrix. + scalar GetElement(u32 i, u32 j) const + { + B3_ASSERT(i < 3 * rowCount); + B3_ASSERT(j < 3 * rowCount); + + u32 i0 = i / 3; + u32 j0 = j / 3; + + const b3Mat33& a = (*this)(i0, j0); + + u32 ii = i - 3 * i0; + u32 jj = j - 3 * j0; + + return a(ii, jj); + } + + // Create the original matrix. + // The output matrix is stored in column-major order. + // Use the function GetElementCount() for computing the required output memory size. + void CreateMatrix(scalar* out) const; + u32 rowCount; b3RowValueArray* rows; }; @@ -52,14 +82,14 @@ struct b3SparseMat33View inline b3SparseMat33View::b3SparseMat33View(const b3SparseMat33& _m) { rowCount = _m.rowCount; - rows = (b3RowValueArray*)b3Alloc(rowCount * sizeof(b3RowValueArray)); + rows = (b3RowValueArray*)b3FrameAllocator_sparseAllocator->Allocate(rowCount * sizeof(b3RowValueArray)); for (u32 i = 0; i < _m.rowCount; ++i) { b3RowValueList* rowList = _m.rows + i; b3RowValueArray* rowArray = rows + i; rowArray->count = rowList->count; - rowArray->values = (b3ArrayRowValue*)b3Alloc(rowArray->count * sizeof(b3ArrayRowValue)); + rowArray->values = (b3ArrayRowValue*)b3FrameAllocator_sparseAllocator->Allocate(rowArray->count * sizeof(b3ArrayRowValue)); u32 valueIndex = 0; for (b3RowValue* v = rowList->head; v; v = v->next) @@ -76,9 +106,9 @@ inline b3SparseMat33View::~b3SparseMat33View() for (u32 i = 0; i < rowCount; ++i) { b3RowValueArray* rowArray = rows + i; - b3Free(rowArray->values); + b3FrameAllocator_sparseAllocator->Free(rowArray->values); } - b3Free(rows); + b3FrameAllocator_sparseAllocator->Free(rows); } inline const b3Mat33& b3SparseMat33View::operator()(u32 i, u32 j) const @@ -88,9 +118,9 @@ inline const b3Mat33& b3SparseMat33View::operator()(u32 i, u32 j) const b3RowValueArray* vs = rows + i; - for (u32 c = 0; c < vs->count; ++c) + for (u32 k = 0; k < vs->count; ++k) { - b3ArrayRowValue* rv = vs->values + c; + b3ArrayRowValue* rv = vs->values + k; if (rv->column == j) { return rv->value; @@ -100,6 +130,42 @@ inline const b3Mat33& b3SparseMat33View::operator()(u32 i, u32 j) const return b3Mat33_zero; } +inline void b3SparseMat33View::CreateMatrix(scalar* out) const +{ + u32 AM = 3 * rowCount; + u32 AN = AM; + scalar* A = out; + + for (u32 i = 0; i < AM * AN; ++i) + { + A[i] = scalar(0); + } + + for (u32 i = 0; i < rowCount; ++i) + { + b3RowValueArray* vs = rows + i; + + for (u32 k = 0; k < vs->count; ++k) + { + b3ArrayRowValue* v = vs->values + k; + + u32 j = v->column; + b3Mat33 a = v->value; + + for (u32 ii = 0; ii < 3; ++ii) + { + for (u32 jj = 0; jj < 3; ++jj) + { + u32 row = 3 * i + ii; + u32 col = 3 * j + jj; + + A[row + AM * col] = a(ii, jj); + } + } + } + } +} + inline void b3Mul(b3DenseVec3& out, const b3SparseMat33View& A, const b3DenseVec3& v) { B3_ASSERT(A.rowCount == out.n); @@ -110,9 +176,9 @@ inline void b3Mul(b3DenseVec3& out, const b3SparseMat33View& A, const b3DenseVec { b3RowValueArray* rowArray = A.rows + i; - for (u32 c = 0; c < rowArray->count; ++c) + for (u32 k = 0; k < rowArray->count; ++k) { - b3ArrayRowValue* rv = rowArray->values + c; + b3ArrayRowValue* rv = rowArray->values + k; u32 j = rv->column; b3Mat33 a = rv->value; diff --git a/premake5.lua b/premake5.lua index cc2c029..4812911 100644 --- a/premake5.lua +++ b/premake5.lua @@ -45,12 +45,13 @@ workspace(solution_name) warnings 'Extra' filter "system:windows" - platforms { "x86", "x86_x64" } - defaultplatform "x86_64" + platforms { "x86", "x86_64" } + defaultplatform "x86_64" defines { "_CRT_SECURE_NO_WARNINGS", "_WIN32", "WIN32", "_WINDOWS" } filter "system:linux" - platforms { "x86_64" } + platforms { "x86", "x86_64" } + defaultplatform "x86_64" cppdialect "C++11" filter {} @@ -96,7 +97,7 @@ workspace(solution_name) includedirs { external_dir } vpaths { ["Headers"] = "**.h", ["Sources"] = "**.c" } - filter { "system:windows", "options:gfxapi=opengl_2" } + filter { "options:gfxapi=opengl_2" } files { external_dir .. "/glad_2/khrplatform.h", @@ -104,7 +105,7 @@ workspace(solution_name) external_dir .. "/glad_2/glad.c", } - filter { "system:windows", "options:gfxapi=opengl_4" } + filter { "options:gfxapi=opengl_4" } files { external_dir .. "/glad_4/khrplatform.h", @@ -115,7 +116,6 @@ workspace(solution_name) filter { "system:linux", "options:gfxapi=opengl_2" } files { - external_dir .. "/glad_2/khrplatform.h", external_dir .. "/glad_2/glad_glx.h", external_dir .. "/glad_2/glad_glx.c", } @@ -123,7 +123,6 @@ workspace(solution_name) filter { "system:linux", "options:gfxapi=opengl_4" } files { - external_dir .. "/glad_4/khrplatform.h", external_dir .. "/glad_4/glad_glx.h", external_dir .. "/glad_4/glad_glx.c", } @@ -157,15 +156,17 @@ workspace(solution_name) external_dir .. "/glfw/win32_joystick.h", external_dir .. "/glfw/wgl_context.h", external_dir .. "/glfw/egl_context.h", + external_dir .. "/glfw/osmesa_context.h", external_dir .. "/glfw/win32_init.c", external_dir .. "/glfw/win32_joystick.c", external_dir .. "/glfw/win32_monitor.c", external_dir .. "/glfw/win32_time.c", - external_dir .. "/glfw/win32_tls.c", + external_dir .. "/glfw/win32_thread.c", external_dir .. "/glfw/win32_window.c", external_dir .. "/glfw/wgl_context.c", - external_dir .. "/glfw/egl_context.c", + external_dir .. "/glfw/egl_context.c", + external_dir .. "/glfw/osmesa_context.c", } filter "system:linux" @@ -174,21 +175,23 @@ workspace(solution_name) { external_dir .. "/glfw/x11_platform.h", external_dir .. "/glfw/xkb_unicode.h", - external_dir .. "/glfw/linux_joystick.h", external_dir .. "/glfw/posix_time.h", - external_dir .. "/glfw/posix_tls.h", + external_dir .. "/glfw/posix_thread.h", external_dir .. "/glfw/glx_context.h", external_dir .. "/glfw/egl_context.h", + external_dir .. "/glfw/osmesa_context.h", + external_dir .. "/glfw/linux_joystick.h", external_dir .. "/glfw/x11_init.c", external_dir .. "/glfw/x11_monitor.c", external_dir .. "/glfw/x11_window.c", external_dir .. "/glfw/xkb_unicode.c", - external_dir .. "/glfw/linux_joystick.c", external_dir .. "/glfw/posix_time.c", - external_dir .. "/glfw/posix_tls.c", - external_dir .. "/glfw/egl_context.c", - external_dir .. "/glfw/glx_context.c", + external_dir .. "/glfw/posix_thread.c", + external_dir .. "/glfw/glx_context.c", + external_dir .. "/glfw/egl_context.c", + external_dir .. "/glfw/osmesa_context.c", + external_dir .. "/glfw/linux_joystick.c", } project "imgui" @@ -204,27 +207,32 @@ workspace(solution_name) external_dir .. "/imgui/imgui.h", external_dir .. "/imgui/imgui_internal.h", - external_dir .. "/imgui/stb_rect_pack.h", - external_dir .. "/imgui/stb_textedit.h", - external_dir .. "/imgui/stb_truetype.h", + external_dir .. "/imgui/imstb_rect_pack.h", + external_dir .. "/imgui/imstb_textedit.h", + external_dir .. "/imgui/imstb_truetype.h", + + external_dir .. "/imgui/imgui_impl_glfw.h", external_dir .. "/imgui/imgui.cpp", + external_dir .. "/imgui/imgui_widgets.cpp", external_dir .. "/imgui/imgui_demo.cpp", - external_dir .. "/imgui/imgui_draw.cpp" + external_dir .. "/imgui/imgui_draw.cpp", + + external_dir .. "/imgui/imgui_impl_glfw.cpp" } filter "options:gfxapi=opengl_2" files { - external_dir .. "/imgui/imgui_impl_glfw_gl2.h", - external_dir .. "/imgui/imgui_impl_glfw_gl2.cpp" + external_dir .. "/imgui/imgui_impl_opengl2.h", + external_dir .. "/imgui/imgui_impl_opengl2.cpp" } filter "options:gfxapi=opengl_4" files { - external_dir .. "/imgui/imgui_impl_glfw_gl3.h", - external_dir .. "/imgui/imgui_impl_glfw_gl3.cpp" + external_dir .. "/imgui/imgui_impl_opengl3.h", + external_dir .. "/imgui/imgui_impl_opengl3.cpp" } project "rapidjson" @@ -240,6 +248,19 @@ workspace(solution_name) external_dir .. "/rapidjson/**.cpp" } + project "tinyobjloader" + kind "StaticLib" + language "C++" + location ( solution_dir .. action ) + includedirs { external_dir } + vpaths { ["Headers"] = "**.h", ["Sources"] = "**.cc" } + + files + { + external_dir .. "/tinyobjloader/**.h", + external_dir .. "/tinyobjloader/**.cc" + } + project "triangle" kind "StaticLib" language "C" @@ -259,12 +280,10 @@ workspace(solution_name) location ( solution_dir .. action ) includedirs { external_dir, bounce_inc_dir, examples_inc_dir } vpaths { [""] = "testbed" } - + files { examples_inc_dir .. "/testbed/framework/draw.h", - examples_inc_dir .. "/testbed/framework/profiler.h", - examples_inc_dir .. "/testbed/framework/profiler_st.h", examples_inc_dir .. "/testbed/framework/json_profiler.h", examples_inc_dir .. "/testbed/framework/model.h", @@ -284,8 +303,6 @@ workspace(solution_name) examples_inc_dir .. "/testbed/tests/**.h", examples_src_dir .. "/testbed/framework/draw.cpp", - examples_src_dir .. "/testbed/framework/profiler.cpp", - examples_src_dir .. "/testbed/framework/profiler_st.cpp", examples_src_dir .. "/testbed/framework/json_profiler.cpp", examples_inc_dir .. "/testbed/framework/model.cpp", @@ -315,10 +332,11 @@ workspace(solution_name) filter "system:linux" links { "GL", "X11", "Xrandr", "Xinerama", "Xcursor", "pthread", "dl" } + linkoptions { "-no-pie" } filter {} - links { "glfw", "glad", "imgui", "bounce", "triangle" } + links { "glad", "glfw", "imgui", "tinyobjloader", "bounce", "triangle" } project "hello_world" kind "ConsoleApp" @@ -326,13 +344,18 @@ workspace(solution_name) location ( solution_dir .. action ) includedirs { bounce_inc_dir, examples_inc_dir } vpaths { ["Headers"] = "**.h", ["Sources"] = "**.cpp" } - + files { examples_inc_dir .. "/hello_world/**.h", examples_src_dir .. "/hello_world/**.cpp" } + filter { "system:linux" } + linkoptions { "-no-pie" } + + filter {} + links { "bounce" } -- build @@ -358,12 +381,22 @@ if os.istarget("windows") then end } + newaction + { + trigger = "solution_vs2019", + description = "Generate solution", + execute = function () + os.execute ( "premake5 clean" ) + os.execute ( "premake5 vs2019" ) + end + } + newaction { trigger = "doc", description = "Generate documentation", execute = function () - os.execute ( "doxygen doxyfile" ) + os.execute ( "doxygen doc\\doxyfile" ) os.execute ( "start doc\\api\\html\\index.html" ) end } @@ -375,7 +408,6 @@ newaction trigger = "clean", description = "Clean solution", execute = function () - os.rmdir( "doc" ) - os.rmdir( solution_dir ) + os.rmdir( solution_dir ) end -} \ No newline at end of file +} diff --git a/readme.md b/readme.md index c90f4ab..1f1b12e 100644 --- a/readme.md +++ b/readme.md @@ -14,38 +14,35 @@ Bounce uses [premake](https://premake.github.io/) for generating project files i * Put premake into bounce/. -### Visual Studio 2017 +### Windows -* Ensure you have installed the Visual Studio 2015 libraries. -* Say { premake5 vs2017 } on a command line. -* Open build/vs2017/bounce.sln. +#### Visual Studio 2019 + +* Say { premake5 vs2019 } on a command line. +* Open build/vs2019/bounce.sln. * Set testbed as the startup project. -* In the testbed debugging properties, set the Working Directory to ..\\..\examples\testbed. +* In the testbed debugging properties, set Working Directory to ..\\..\examples\testbed. * Press F5 to run. ### Linux -* On a clean Ubuntu 16.04 install these packages first: +#### GNU Make -* mesa-common-dev +##### x86 -* libgl1-mesa-dev - -* libglu1-mesa-dev - -#### x32 - -* Say { premake5 gmake } on a terminal. -* From build/gmake say { make config="debug_x32" }. +* Say { ./premake5 gmake2 } on a terminal. +* From build/gmake2 say { make config="debug_x86" }. * Set the testbed directory as the working directory. -* Open testbed from /bin/x32/testbed/. +* From bin/x86/debug/testbed say { ./testbed }. -#### x64 +##### x64 -* Say { premake5 gmake } on a terminal. -* From build/gmake say { make config="debug_x64" }. +* Say { ./premake5 gmake2 } on a terminal. +* From build/gmake2 say { make config="debug_x86_64" }. * Set the testbed directory as the working directory. -* Open testbed from /bin/x64/testbed/. +* From bin/x86_64/debug/testbed say { ./testbed }. + +**Note**: If any errors appears during the compilation then there is probably a missing package. In this case you can search the error in the Internet to identify the package that needs to be installed. ### Mac @@ -78,16 +75,25 @@ Below are the external dependencies for testbed. If you don't care about testbed * [GLAD](https://glad.dav1d.de/) * [imgui](https://github.com/ocornut/imgui) * [RapidJSON](http://rapidjson.org/index.html) +* [tinyobjloader](https://github.com/syoyo/tinyobjloader) ## Features ### Common * Efficient data structures with no use of STL -* Stack and small block allocators +* Frame, stack, and pool allocators * Built-in math library * Tunable settings used across the entire library +### Sparse + +* Fairly efficient data structures for representing sparse linear systems + +### MeshGen + +* Sphere, cylinder mesh generators + ### Quickhull * Robust 3D convex hull creation and simplification @@ -98,14 +104,16 @@ Below are the external dependencies for testbed. If you don't care about testbed * Static tree "midphase" * SAT * GJK -* Spheres, capsules, convex hulls, triangle meshes +* Spheres, capsules, convex hulls, triangle meshes, signed distance fields * Optimized pair management ### Dynamics * Rigid bodies +* Gyroscopic motion solver * Contact, friction, restitution -* Mouse, spring, sphere, cone, revolute joint types +* Mouse, spring, sphere, cone, revolute, friction, weld, motor, prismatic, wheel joint types +* Soft constraints * Quaternion constraints * Joint motors, limits * Constraint graphs @@ -115,7 +123,7 @@ Below are the external dependencies for testbed. If you don't care about testbed * One-shot contact manifolds * Contact clustering, reduction, and persistence * Contact callbacks: begin, pre-solve, post-solve -* Ray-casting and volume queries +* Ray-casting, convex-casting, and volume queries ### Rope @@ -125,6 +133,7 @@ Below are the external dependencies for testbed. If you don't care about testbed ### Cloth * Cloth +* Grid, garment mesh types * Vertex contact, friction * Strech, shear, spring, mouse force types * Linear time solver @@ -142,7 +151,7 @@ Below are the external dependencies for testbed. If you don't care about testbed ### Testbed -* OpenGL with GLFW and GLAD +* OpenGL 2/4 with GLFW and GLAD * UI by imgui * Mouse picking * premake build system diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index b5f957b..3b44c78 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -18,336 +18,436 @@ #include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include #include -static B3_FORCE_INLINE u32 b3NextIndex(u32 i) +b3Cloth::b3Cloth() : + m_particleBlocks(sizeof(b3ClothParticle)), + m_sphereShapeBlocks(sizeof(b3ClothSphereShape)), + m_capsuleShapeBlocks(sizeof(b3ClothCapsuleShape)), + m_triangleShapeBlocks(sizeof(b3ClothTriangleShape)), + m_worldShapeBlocks(sizeof(b3ClothWorldShape)) { - return i + 1 < 3 ? i + 1 : 0; -} - -struct b3SharedEdge -{ - u32 v1, v2; - u32 nsv1, nsv2; -}; - -static u32 b3FindSharedEdges(b3SharedEdge* sharedEdges, const b3ClothMesh* m) -{ - u32 sharedCount = 0; - - for (u32 i = 0; i < m->triangleCount; ++i) - { - b3ClothMeshTriangle* t1 = m->triangles + i; - u32 i1s[3] = { t1->v1, t1->v2, t1->v3 }; - - for (u32 j1 = 0; j1 < 3; ++j1) - { - u32 k1 = j1 + 1 < 3 ? j1 + 1 : 0; - - u32 t1v1 = i1s[j1]; - u32 t1v2 = i1s[k1]; - - for (u32 j = i + 1; j < m->triangleCount; ++j) - { - b3ClothMeshTriangle* t2 = m->triangles + j; - u32 i2s[3] = { t2->v1, t2->v2, t2->v3 }; - - for (u32 j2 = 0; j2 < 3; ++j2) - { - u32 k2 = j2 + 1 < 3 ? j2 + 1 : 0; - - u32 t2v1 = i2s[j2]; - u32 t2v2 = i2s[k2]; - - if (t1v1 == t2v2 && t1v2 == t2v1) - { - // The triangles are adjacent. - u32 k3 = k1 + 1 < 3 ? k1 + 1 : 0; - u32 t1v3 = i1s[k3]; - - u32 k4 = k2 + 1 < 3 ? k2 + 1 : 0; - u32 t2v3 = i2s[k4]; - - // Add shared edge and non-shared vertices. - b3SharedEdge se; - se.v1 = t1v1; - se.v2 = t1v2; - se.nsv1 = t1v3; - se.nsv2 = t2v3; - - sharedEdges[sharedCount++] = se; - - break; - } - } - } - } - } - - return sharedCount; + m_contactManager.m_cloth = this; + m_gravity.SetZero(); + m_enableSelfCollision = false; + m_inv_dt0 = scalar(0); + m_mesh = nullptr; + m_particles = nullptr; + m_spheres = nullptr; + m_triangles = nullptr; } b3Cloth::b3Cloth(const b3ClothDef& def) : - m_particleBlocks(sizeof(b3Particle)) + m_particleBlocks(sizeof(b3ClothParticle)), + m_sphereShapeBlocks(sizeof(b3ClothSphereShape)), + m_capsuleShapeBlocks(sizeof(b3ClothCapsuleShape)), + m_triangleShapeBlocks(sizeof(b3ClothTriangleShape)), + m_worldShapeBlocks(sizeof(b3ClothWorldShape)) { B3_ASSERT(def.mesh); - B3_ASSERT(def.density > 0.0f); + B3_ASSERT(def.density > scalar(0)); - m_mesh = def.mesh; - m_density = def.density; m_contactManager.m_cloth = this; + m_gravity.SetZero(); + m_enableSelfCollision = false; + m_inv_dt0 = scalar(0); + m_mesh = def.mesh; - const b3ClothMesh* m = m_mesh; + const b3ClothMesh* m = def.mesh; - // Initialize particles - m_particles = (b3Particle**)b3Alloc(m->vertexCount * sizeof(b3Particle*)); + // Create particles and spheres + m_particles = (b3ClothParticle * *)b3Alloc(m->vertexCount * sizeof(b3ClothParticle*)); + m_spheres = (b3ClothSphereShape * *)b3Alloc(m->vertexCount * sizeof(b3ClothSphereShape*)); for (u32 i = 0; i < m->vertexCount; ++i) { - b3ParticleDef pd; - pd.type = e_dynamicParticle; - pd.mass = 1.0f; - pd.radius = def.thickness; - pd.friction = def.friction; + b3ClothParticleDef pd; + pd.type = e_dynamicClothParticle; pd.position = m->vertices[i]; + pd.meshIndex = i; - b3Particle* p = CreateParticle(pd); + m_particles[i] = nullptr; + b3ClothParticle* p = CreateParticle(pd); - p->m_vertex = i; - m_particles[i] = p; + b3ClothSphereShapeDef sd; + sd.p = p; + sd.radius = def.thickness; + sd.friction = def.friction; + sd.meshIndex = i; + + m_spheres[i] = nullptr; + b3ClothSphereShape* s = CreateSphereShape(sd); } - // Compute mass - ComputeMass(); - - // Initialize triangles - m_triangles = (b3ClothTriangle*)b3Alloc(m_mesh->triangleCount * sizeof(b3ClothTriangle)); - for (u32 i = 0; i < m_mesh->triangleCount; ++i) + // Create triangles and capsules + m_triangles = (b3ClothTriangleShape * *)b3Alloc(m->triangleCount * sizeof(b3ClothTriangleShape*)); + for (u32 i = 0; i < m->triangleCount; ++i) { - b3ClothMeshTriangle* meshTriangle = m_mesh->triangles + i; - b3ClothTriangle* triangle = m_triangles + i; + b3ClothMeshTriangle* meshTriangle = m->triangles + i; - triangle->m_cloth = this; - triangle->m_radius = def.thickness; - triangle->m_friction = def.friction; - triangle->m_triangle = i; + u32 v1 = meshTriangle->v1; + u32 v2 = meshTriangle->v2; + u32 v3 = meshTriangle->v3; - b3Vec3 A = m_mesh->vertices[meshTriangle->v1]; - b3Vec3 B = m_mesh->vertices[meshTriangle->v2]; - b3Vec3 C = m_mesh->vertices[meshTriangle->v3]; - - b3AABB3 aabb; - aabb.Set(A, B, C); - aabb.Extend(triangle->m_radius); - - triangle->m_aabbProxy.type = e_triangleProxy; - triangle->m_aabbProxy.owner = triangle; - triangle->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, &triangle->m_aabbProxy); - - b3Vec3 AB = B - A; - b3Vec3 AC = C - A; - - // uv1 - float32 u1 = 0.0f; - float32 v1 = 0.0f; - - // uv2 - float32 u2 = b3Length(AB); - float32 v2 = 0.0f; - - // uv3 - B3_ASSERT(u2 > 0.0f); - b3Vec3 n_AB = AB / u2; - - // A = b * h / 2 - // h = (A * 2) / b - float32 A2 = b3Length(b3Cross(AB, AC)); - B3_ASSERT(A2 > 0.0f); - - float32 u3 = b3Dot(AC, n_AB); - float32 v3 = A2 / u2; - - // Strech matrix - float32 du1 = u2 - u1; - float32 dv1 = v2 - v1; - float32 du2 = u3 - u1; - float32 dv2 = v3 - v1; + b3ClothParticle* p1 = m_particles[v1]; + b3ClothParticle* p2 = m_particles[v2]; + b3ClothParticle* p3 = m_particles[v3]; - triangle->m_du1 = du1; - triangle->m_dv1 = dv1; - triangle->m_du2 = du2; - triangle->m_dv2 = dv2; + b3ClothTriangleShapeDef td; + td.p1 = p1; + td.p2 = p2; + td.p3 = p3; + td.v1 = m->vertices[v1]; + td.v2 = m->vertices[v2]; + td.v3 = m->vertices[v3]; + td.radius = def.thickness; + td.friction = def.friction; + td.meshIndex = i; - float32 det = du1 * dv2 - du2 * dv1; - B3_ASSERT(det != 0.0f); - triangle->m_inv_det = 1.0f / det; + m_triangles[i] = nullptr; + b3ClothTriangleShape* ts = CreateTriangleShape(td); - // Area - triangle->m_alpha = 0.5f * A2; - - // Create strech force - b3StrechForceDef sfdef; - sfdef.triangle = triangle; - sfdef.streching = def.streching; - sfdef.damping = def.damping; - sfdef.bu = 1.0f; - sfdef.bv = 1.0f; - - if (def.streching > 0.0f) { - CreateForce(sfdef); + b3ClothCapsuleShapeDef sd; + sd.p1 = p1; + sd.p2 = p2; + sd.radius = def.thickness; + sd.friction = def.friction; + + CreateCapsuleShape(sd); } - b3ShearForceDef shdef; - shdef.triangle = triangle; - shdef.shearing = def.shearing; - shdef.damping = def.damping; - - if (def.shearing > 0.0f) { - CreateForce(shdef); + b3ClothCapsuleShapeDef sd; + sd.p1 = p2; + sd.p2 = p3; + sd.radius = def.thickness; + sd.friction = def.friction; + + CreateCapsuleShape(sd); + } + + { + b3ClothCapsuleShapeDef sd; + sd.p1 = p3; + sd.p2 = p1; + sd.radius = def.thickness; + sd.friction = def.friction; + + CreateCapsuleShape(sd); } } - // Initialize forces - b3StackAllocator* allocator = &m_stackAllocator; - - // Worst-case edge memory - u32 edgeCount = 3 * m->triangleCount; - - b3SharedEdge* sharedEdges = (b3SharedEdge*)allocator->Allocate(edgeCount * sizeof(b3SharedEdge)); - u32 sharedCount = b3FindSharedEdges(sharedEdges, m); - - // Bending - for (u32 i = 0; i < sharedCount; ++i) + if (def.streching > scalar(0)) { - b3SharedEdge* e = sharedEdges + i; - - b3Particle* p1 = m_particles[e->v1]; - b3Particle* p2 = m_particles[e->v2]; - b3Particle* p3 = m_particles[e->nsv1]; - b3Particle* p4 = m_particles[e->nsv2]; - - b3SpringForceDef fd; - fd.Initialize(p3, p4, def.bending, def.damping); - - if (def.bending > 0.0f) + // Streching + for (u32 i = 0; i < m->triangleCount; ++i) { - CreateForce(fd); - } - } - - allocator->Free(sharedEdges); + b3ClothMeshTriangle* t = m->triangles + i; - // Sewing - for (u32 i = 0; i < m->sewingLineCount; ++i) - { - b3ClothMeshSewingLine* line = m->sewingLines + i; + u32 v1 = t->v1; + u32 v2 = t->v2; + u32 v3 = t->v3; - b3Particle* p1 = m_particles[line->v1]; - b3Particle* p2 = m_particles[line->v2]; + b3ClothParticle* p1 = m_particles[v1]; + b3ClothParticle* p2 = m_particles[v2]; + b3ClothParticle* p3 = m_particles[v3]; - b3SpringForceDef fd; - fd.Initialize(p1, p2, def.sewing, def.damping); + b3Vec3 x1 = m->vertices[v1]; + b3Vec3 x2 = m->vertices[v2]; + b3Vec3 x3 = m->vertices[v3]; + + b3StretchForceDef fd; + fd.Initialize(x1, x2, x3); + + fd.p1 = p1; + fd.p2 = p2; + fd.p3 = p3; + fd.stretching_u = def.streching; + fd.damping_u = def.strechDamping; + fd.b_u = scalar(1); + fd.stretching_v = def.streching; + fd.damping_v = def.strechDamping; + fd.b_v = scalar(1); - if (def.sewing > 0.0f) - { CreateForce(fd); } } - m_gravity.SetZero(); - m_world = nullptr; + if (def.shearing > scalar(0)) + { + // Shearing + for (u32 i = 0; i < m->shearingLineCount; ++i) + { + b3ClothMeshShearingLine* line = m->shearingLines + i; + + b3ClothParticle* p1 = m_particles[line->v1]; + b3ClothParticle* p2 = m_particles[line->v2]; + + b3SpringForceDef fd; + fd.Initialize(p1, p2, def.shearing, def.shearDamping); + + CreateForce(fd); + } + } + + if (def.bending > scalar(0)) + { + // Bending + for (u32 i = 0; i < m->bendingLineCount; ++i) + { + b3ClothMeshBendingLine* line = m->bendingLines + i; + + b3ClothParticle* p1 = m_particles[line->v1]; + b3ClothParticle* p2 = m_particles[line->v2]; + + b3SpringForceDef fd; + fd.Initialize(p1, p2, def.bending, def.bendDamping); + + CreateForce(fd); + } + } + + if (def.sewing > scalar(0)) + { + // Sewing + for (u32 i = 0; i < m->sewingLineCount; ++i) + { + b3ClothMeshSewingLine* line = m->sewingLines + i; + + b3ClothParticle* p1 = m_particles[line->v1]; + b3ClothParticle* p2 = m_particles[line->v2]; + + b3SpringForceDef fd; + fd.Initialize(p1, p2, def.sewing, def.sewDamping); + + CreateForce(fd); + } + } } b3Cloth::~b3Cloth() { - b3Free(m_particles); - b3Free(m_triangles); - - b3Particle* p = m_particleList.m_head; - while (p) - { - b3Particle* p0 = p; - p = p->m_next; - p0->~b3Particle(); - } - b3Force* f = m_forceList.m_head; while (f) { - b3Force* f0 = f; + b3Force* boom = f; f = f->m_next; - b3Force::Destroy(f0); - } -} - -void b3Cloth::SetWorld(b3World* world) -{ - if (!world && m_world) - { - // Destroy body contacts - b3ParticleBodyContact* c = m_contactManager.m_particleBodyContactList.m_head; - while (c) - { - b3ParticleBodyContact* boom = c; - c = c->m_next; - m_contactManager.Destroy(boom); - } + b3Force::Destroy(boom); } - m_world = world; + b3Free(m_particles); + b3Free(m_spheres); + b3Free(m_triangles); } -b3Particle* b3Cloth::CreateParticle(const b3ParticleDef& def) +b3ClothParticle* b3Cloth::CreateParticle(const b3ClothParticleDef& def) { void* mem = m_particleBlocks.Allocate(); - b3Particle* p = new(mem) b3Particle(def, this); - - b3AABB3 aabb; - aabb.Set(p->m_position, p->m_radius); - - p->m_aabbProxy.type = e_particleProxy; - p->m_aabbProxy.owner = p; - p->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, &p->m_aabbProxy); + b3ClothParticle* p = new(mem) b3ClothParticle(def, this); + + if (p->m_meshIndex != B3_MAX_U32) + { + B3_ASSERT(m_particles[p->m_meshIndex] == nullptr); + m_particles[p->m_meshIndex] = p; + } m_particleList.PushFront(p); return p; } -void b3Cloth::DestroyParticle(b3Particle* particle) +void b3Cloth::DestroyParticle(b3ClothParticle* particle) { - B3_ASSERT(particle->m_vertex == ~0); - - // Destroy particle forces - b3Force* f = m_forceList.m_head; - while (f) + if (particle->m_meshIndex != B3_MAX_U32) { - b3Force* f0 = f; - f = f->m_next; - - if (f0->HasParticle(particle)) + m_particles[particle->m_meshIndex] = nullptr; + } + + // Destroy shapes + particle->DestroySpheres(); + particle->DestroyCapsules(); + particle->DestroyTriangles(); + + // Destroy forces + particle->DestroyForces(); + + // Destroy contacts + particle->DestroyContacts(); + + m_particleList.Remove(particle); + particle->~b3ClothParticle(); + m_particleBlocks.Free(particle); +} + +b3ClothSphereShape* b3Cloth::CreateSphereShape(const b3ClothSphereShapeDef& def) +{ + // Check if the shape exists. + for (b3ClothSphereShape* s = m_sphereShapeList.m_head; s; s = s->m_next) + { + if (s->m_p == def.p) { - m_forceList.Remove(f0); - b3Force::Destroy(f0); + return s; } } - // Destroy particle contacts - particle->DestroyContacts(); + void* mem = m_sphereShapeBlocks.Allocate(); + b3ClothSphereShape* s = new (mem)b3ClothSphereShape(def, this); - // Destroy AABB proxy - m_contactManager.m_broadPhase.DestroyProxy(particle->m_broadPhaseId); + s->m_radius = def.radius; + s->m_friction = def.friction; + s->m_density = def.density; + s->m_meshIndex = def.meshIndex; - m_particleList.Remove(particle); - particle->~b3Particle(); - m_particleBlocks.Free(particle); + if (s->m_meshIndex != B3_MAX_U32) + { + B3_ASSERT(m_spheres[s->m_meshIndex] == nullptr); + m_spheres[s->m_meshIndex] = s; + } + + b3AABB aabb = s->ComputeAABB(); + s->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, s); + + m_sphereShapeList.PushFront(s); + + return s; +} + +void b3Cloth::DestroySphereShape(b3ClothSphereShape* shape) +{ + if (shape->m_meshIndex != B3_MAX_U32) + { + m_spheres[shape->m_meshIndex] = nullptr; + } + + // Destroy contacts + shape->DestroyContacts(); + + // Remove shape from broadphase + m_contactManager.m_broadPhase.DestroyProxy(shape->m_broadPhaseId); + + m_sphereShapeList.Remove(shape); + shape->~b3ClothSphereShape(); + m_sphereShapeBlocks.Free(shape); +} + +b3ClothCapsuleShape* b3Cloth::CreateCapsuleShape(const b3ClothCapsuleShapeDef& def) +{ + // Check if the shape exists. + for (b3ClothCapsuleShape* c = m_capsuleShapeList.m_head; c; c = c->m_next) + { + if (c->m_p1 == def.p1 && c->m_p2 == def.p2) + { + return c; + } + + if (c->m_p1 == def.p2 && c->m_p2 == def.p1) + { + return c; + } + } + + void* mem = m_capsuleShapeBlocks.Allocate(); + b3ClothCapsuleShape* c = new (mem)b3ClothCapsuleShape(def, this); + + c->m_radius = def.radius; + c->m_friction = def.friction; + c->m_density = def.density; + c->m_meshIndex = def.meshIndex; + + b3AABB aabb = c->ComputeAABB(); + c->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, c); + + m_capsuleShapeList.PushFront(c); + + return c; +} + +void b3Cloth::DestroyCapsuleShape(b3ClothCapsuleShape* shape) +{ + // Destroy contacts + shape->DestroyContacts(); + + // Remove shape from broadphase + m_contactManager.m_broadPhase.DestroyProxy(shape->m_broadPhaseId); + + m_capsuleShapeList.Remove(shape); + shape->~b3ClothCapsuleShape(); + m_capsuleShapeBlocks.Free(shape); +} + +b3ClothTriangleShape* b3Cloth::CreateTriangleShape(const b3ClothTriangleShapeDef& def) +{ + b3ClothParticle* p1 = def.p1; + b3ClothParticle* p2 = def.p2; + b3ClothParticle* p3 = def.p3; + + for (b3ClothTriangleShape* t = m_triangleShapeList.m_head; t; t = t->m_next) + { + bool hasP1 = t->m_p1 == p1 || t->m_p2 == p1 || t->m_p3 == p1; + bool hasP2 = t->m_p1 == p2 || t->m_p2 == p2 || t->m_p3 == p2; + bool hasP3 = t->m_p1 == p3 || t->m_p2 == p3 || t->m_p3 == p3; + + if (hasP1 && hasP2 && hasP3) + { + return t; + } + } + + void* mem = m_triangleShapeBlocks.Allocate(); + b3ClothTriangleShape* t = new (mem)b3ClothTriangleShape(def, this); + + t->m_radius = def.radius; + t->m_friction = def.friction; + t->m_density = def.density; + t->m_meshIndex = def.meshIndex; + + if (t->m_meshIndex != B3_MAX_U32) + { + B3_ASSERT(m_triangles[t->m_meshIndex] == nullptr); + m_triangles[t->m_meshIndex] = t; + } + + b3AABB aabb = t->ComputeAABB(); + t->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, t); + + m_triangleShapeList.PushFront(t); + + // Reset the cloth mass + ResetMass(); + + return t; +} + +void b3Cloth::DestroyTriangleShape(b3ClothTriangleShape* shape) +{ + if (shape->m_meshIndex != B3_MAX_U32) + { + m_triangles[shape->m_meshIndex] = nullptr; + } + + // Destroy contacts + shape->DestroyContacts(); + + // Remove shape from broadphase + m_contactManager.m_broadPhase.DestroyProxy(shape->m_broadPhaseId); + + m_triangleShapeList.Remove(shape); + shape->~b3ClothTriangleShape(); + m_triangleShapeBlocks.Free(shape); + + // Reset the cloth mass + ResetMass(); } b3Force* b3Cloth::CreateForce(const b3ForceDef& def) @@ -363,93 +463,163 @@ void b3Cloth::DestroyForce(b3Force* force) b3Force::Destroy(force); } -float32 b3Cloth::GetEnergy() const +b3ClothWorldShape* b3Cloth::CreateWorldShape(const b3ClothWorldShapeDef& def) { - float32 E = 0.0f; - for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) + void* mem = m_worldShapeBlocks.Allocate(); + b3ClothWorldShape* s = new (mem)b3ClothWorldShape(def, this); + + b3AABB aabb = s->ComputeAABB(); + s->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, s); + + m_worldShapeList.PushFront(s); + + return s; +} + +void b3Cloth::DestroyWorldShape(b3ClothWorldShape* shape) +{ + // Destroy contacts + shape->DestroyContacts(); + + // Remote from broadphase + m_contactManager.m_broadPhase.DestroyProxy(shape->m_broadPhaseId); + + m_worldShapeList.Remove(shape); +} + +b3ClothParticle* b3Cloth::GetParticle(u32 index) +{ + B3_ASSERT(index < m_mesh->vertexCount); + return m_particles[index]; +} + +b3ClothSphereShape* b3Cloth::GetSphere(u32 index) +{ + B3_ASSERT(index < m_mesh->vertexCount); + return m_spheres[index]; +} + +b3ClothTriangleShape* b3Cloth::GetTriangle(u32 index) +{ + B3_ASSERT(index < m_mesh->triangleCount); + return m_triangles[index]; +} + +void b3Cloth::EnableSelfCollision(bool flag) +{ + if (m_enableSelfCollision == true && flag == false) + { + { + // Destroy triangle contacts + b3ClothSphereAndTriangleContact* c = m_contactManager.m_sphereAndTriangleContactList.m_head; + while (c) + { + b3ClothSphereAndTriangleContact* boom = c; + c = c->m_next; + m_contactManager.Destroy(boom); + } + } + + { + // Destroy capsule contacts + b3ClothCapsuleAndCapsuleContact* c = m_contactManager.m_capsuleAndCapsuleContactList.m_head; + while (c) + { + b3ClothCapsuleAndCapsuleContact* boom = c; + c = c->m_next; + m_contactManager.Destroy(boom); + } + } + } + + m_enableSelfCollision = flag; +} + +scalar b3Cloth::GetEnergy() const +{ + scalar E = scalar(0); + for (b3ClothParticle* p = m_particleList.m_head; p; p = p->m_next) { E += p->m_mass * b3Dot(p->m_velocity, p->m_velocity); } - return 0.5f * E; + return scalar(0.5) * E; } -b3Particle* b3Cloth::GetParticle(u32 i) +void b3Cloth::ResetMass() { - B3_ASSERT(i < m_mesh->vertexCount); - return m_particles[i]; -} - -b3ClothTriangle* b3Cloth::GetTriangle(u32 i) -{ - B3_ASSERT(i < m_mesh->triangleCount); - return m_triangles + i; -} - -void b3Cloth::ComputeMass() -{ - for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) + for (b3ClothTriangleShape* t = m_triangleShapeList.m_head; t; t = t->m_next) { - p->m_mass = 0.0f; - p->m_invMass = 0.0f; + t->m_p1->m_mass = scalar(0); + t->m_p2->m_mass = scalar(0); + t->m_p3->m_mass = scalar(0); } - const float32 inv3 = 1.0f / 3.0f; - const float32 rho = m_density; + const scalar inv3 = scalar(1) / scalar(3); - for (u32 i = 0; i < m_mesh->triangleCount; ++i) + for (b3ClothTriangleShape* t = m_triangleShapeList.m_head; t; t = t->m_next) { - b3ClothMeshTriangle* triangle = m_mesh->triangles + i; - - b3Vec3 v1 = m_mesh->vertices[triangle->v1]; - b3Vec3 v2 = m_mesh->vertices[triangle->v2]; - b3Vec3 v3 = m_mesh->vertices[triangle->v3]; - - float32 area = b3Area(v1, v2, v3); - B3_ASSERT(area > 0.0f); - - float32 mass = rho * area; - - b3Particle* p1 = m_particles[triangle->v1]; - b3Particle* p2 = m_particles[triangle->v2]; - b3Particle* p3 = m_particles[triangle->v3]; + b3ClothParticle* p1 = t->m_p1; + b3ClothParticle* p2 = t->m_p2; + b3ClothParticle* p3 = t->m_p3; + scalar mass = t->m_density * t->m_area; + p1->m_mass += inv3 * mass; p2->m_mass += inv3 * mass; p3->m_mass += inv3 * mass; } // Invert - for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) + for (b3ClothParticle* p = m_particleList.m_head; p; p = p->m_next) { - B3_ASSERT(p->m_mass > 0.0f); - p->m_invMass = 1.0f / p->m_mass; + // Static and kinematic particles have zero mass. + if (p->m_type == e_staticClothParticle || p->m_type == e_kinematicClothParticle) + { + p->m_mass = scalar(0); + p->m_invMass = scalar(0); + continue; + } + + if (p->m_mass > scalar(0)) + { + p->m_invMass = scalar(1) / p->m_mass; + } + else + { + // Force all dynamic particles to have non-zero mass. + p->m_mass = scalar(1); + p->m_invMass = scalar(1); + } } } struct b3ClothRayCastSingleCallback { - float32 Report(const b3RayCastInput& input, u32 proxyId) + scalar Report(const b3RayCastInput& input, u32 proxyId) { - // Get primitive associated with the proxy. + // Get shape associated with the proxy. void* userData = broadPhase->GetUserData(proxyId); - b3ClothAABBProxy* proxy = (b3ClothAABBProxy*)userData; + b3ClothShape* shape = (b3ClothShape*)userData; - if (proxy->type != e_triangleProxy) + if (shape->GetType() != e_clothTriangleShape) { // Continue search from where we stopped. return input.maxFraction; } - b3ClothTriangle* triangle = (b3ClothTriangle*)proxy->owner; - u32 triangleIndex = triangle->GetTriangle(); + b3ClothTriangleShape* triangleShape = (b3ClothTriangleShape*)shape; + + b3Vec3 v1 = triangleShape->GetParticle1()->GetPosition(); + b3Vec3 v2 = triangleShape->GetParticle2()->GetPosition(); + b3Vec3 v3 = triangleShape->GetParticle3()->GetPosition(); b3RayCastOutput subOutput; - if (cloth->RayCast(&subOutput, &input, triangleIndex)) + if (b3RayCast(&subOutput, &input, v1, v2, v3)) { // Ray hits triangle. if (subOutput.fraction < output0.fraction) { - triangle0 = triangleIndex; + triangle0 = triangleShape; output0.fraction = subOutput.fraction; output0.normal = subOutput.normal; } @@ -461,7 +631,7 @@ struct b3ClothRayCastSingleCallback const b3Cloth* cloth; const b3BroadPhase* broadPhase; - u32 triangle0; + b3ClothTriangleShape* triangle0; b3RayCastOutput output0; }; @@ -470,17 +640,17 @@ bool b3Cloth::RayCastSingle(b3ClothRayCastSingleOutput* output, const b3Vec3& p1 b3RayCastInput input; input.p1 = p1; input.p2 = p2; - input.maxFraction = 1.0f; + input.maxFraction = scalar(1); b3ClothRayCastSingleCallback callback; callback.cloth = this; callback.broadPhase = &m_contactManager.m_broadPhase; - callback.triangle0 = ~0; - callback.output0.fraction = B3_MAX_FLOAT; + callback.triangle0 = nullptr; + callback.output0.fraction = B3_MAX_SCALAR; m_contactManager.m_broadPhase.RayCast(&callback, input); - if (callback.triangle0 != ~0) + if (callback.triangle0 != nullptr) { output->triangle = callback.triangle0; output->fraction = callback.output0.fraction; @@ -492,19 +662,7 @@ bool b3Cloth::RayCastSingle(b3ClothRayCastSingleOutput* output, const b3Vec3& p1 return false; } -bool b3Cloth::RayCast(b3RayCastOutput* output, const b3RayCastInput* input, u32 triangleIndex) const -{ - B3_ASSERT(triangleIndex < m_mesh->triangleCount); - b3ClothMeshTriangle* triangle = m_mesh->triangles + triangleIndex; - - b3Vec3 v1 = m_particles[triangle->v1]->m_position; - b3Vec3 v2 = m_particles[triangle->v2]->m_position; - b3Vec3 v3 = m_particles[triangle->v3]->m_position; - - return b3RayCast(output, input, v1, v2, v3); -} - -void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) +void b3Cloth::Solve(const b3ClothTimeStep& step) { B3_PROFILE("Cloth Solve"); @@ -513,12 +671,13 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u solverDef.stack = &m_stackAllocator; solverDef.particleCapacity = m_particleList.m_count; solverDef.forceCapacity = m_forceList.m_count; - solverDef.bodyContactCapacity = m_contactManager.m_particleBodyContactList.m_count; - solverDef.triangleContactCapacity = m_contactManager.m_particleTriangleContactList.m_count; - + solverDef.shapeContactCapacity = m_contactManager.m_sphereAndShapeContactList.m_count; + solverDef.triangleContactCapacity = m_contactManager.m_sphereAndTriangleContactList.m_count; + solverDef.capsuleContactCapacity = m_contactManager.m_capsuleAndCapsuleContactList.m_count; + b3ClothSolver solver(solverDef); - for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) + for (b3ClothParticle* p = m_particleList.m_head; p; p = p->m_next) { solver.Add(p); } @@ -528,7 +687,7 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u solver.Add(f); } - for (b3ParticleTriangleContact* c = m_contactManager.m_particleTriangleContactList.m_head; c; c = c->m_next) + for (b3ClothSphereAndTriangleContact* c = m_contactManager.m_sphereAndTriangleContactList.m_head; c; c = c->m_next) { if (c->m_active) { @@ -536,7 +695,7 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u } } - for (b3ParticleBodyContact* c = m_contactManager.m_particleBodyContactList.m_head; c; c = c->m_next) + for (b3ClothCapsuleAndCapsuleContact* c = m_contactManager.m_capsuleAndCapsuleContactList.m_head; c; c = c->m_next) { if (c->m_active) { @@ -544,56 +703,102 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u } } + for (b3ClothSphereAndShapeContact* c = m_contactManager.m_sphereAndShapeContactList.m_head; c; c = c->m_next) + { + if (c->m_active) + { + solver.Add(c); + } + } + // Solve - solver.Solve(dt, gravity, velocityIterations, positionIterations); + solver.Solve(step, m_gravity); } -void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) +void b3Cloth::Step(scalar dt, u32 velocityIterations, u32 positionIterations) { B3_PROFILE("Cloth Step"); // Update contacts m_contactManager.UpdateContacts(); + // Time step parameters + b3ClothTimeStep step; + step.dt = dt; + step.velocityIterations = velocityIterations; + step.positionIterations = positionIterations; + step.inv_dt = dt > scalar(0) ? scalar(1) / dt : scalar(0); + step.dt_ratio = m_inv_dt0 * dt; + // Integrate state, solve constraints. - if (dt > 0.0f) + if (step.dt > scalar(0)) { - Solve(dt, m_gravity, velocityIterations, positionIterations); + Solve(step); + } + + if (step.dt > scalar(0)) + { + m_inv_dt0 = step.inv_dt; } // Clear external applied forces and translations - for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) + for (b3ClothParticle* p = m_particleList.m_head; p; p = p->m_next) { p->m_force.SetZero(); p->m_translation.SetZero(); } - - // Synchronize particles - for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) + + // Synchronize sphere shapes + for (b3ClothSphereShape* s = m_sphereShapeList.m_head; s; s = s->m_next) { - b3Vec3 displacement = dt * p->m_velocity; + b3ClothParticle* p = s->m_p; - p->Synchronize(displacement); + // Synchronize unconditionally because all particles can be translated. + b3Vec3 displacement = dt * p->m_velocity; + + s->Synchronize(displacement); } - // Synchronize triangles - for (u32 i = 0; i < m_mesh->triangleCount; ++i) + // Synchronize capsule shapes + for (b3ClothCapsuleShape* c = m_capsuleShapeList.m_head; c; c = c->m_next) { - b3ClothMeshTriangle* triangle = m_mesh->triangles + i; + b3ClothParticle* p1 = c->m_p1; + b3ClothParticle* p2 = c->m_p2; - b3Particle* p1 = m_particles[triangle->v1]; - b3Particle* p2 = m_particles[triangle->v2]; - b3Particle* p3 = m_particles[triangle->v3]; + b3Vec3 v1 = p1->m_velocity; + b3Vec3 v2 = p2->m_velocity; + + // Center velocity + b3Vec3 velocity = scalar(0.5) * (v1 + v2); + + b3Vec3 displacement = dt * velocity; + + c->Synchronize(displacement); + } + + // Synchronize triangle shapes + for (b3ClothTriangleShape* t = m_triangleShapeList.m_head; t; t = t->m_next) + { + b3ClothParticle* p1 = t->m_p1; + b3ClothParticle* p2 = t->m_p2; + b3ClothParticle* p3 = t->m_p3; b3Vec3 v1 = p1->m_velocity; b3Vec3 v2 = p2->m_velocity; b3Vec3 v3 = p3->m_velocity; - b3Vec3 velocity = (v1 + v2 + v3) / 3.0f; + // Center velocity + b3Vec3 velocity = (v1 + v2 + v3) / scalar(3); b3Vec3 displacement = dt * velocity; - m_triangles[i].Synchronize(displacement); + t->Synchronize(displacement); + } + + // Synchronize world shapes + for (b3ClothWorldShape* s = m_worldShapeList.m_head; s; s = s->m_next) + { + s->Synchronize(b3Vec3_zero); } // Find new contacts @@ -602,74 +807,95 @@ void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) void b3Cloth::Draw() const { - for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) + for (b3ClothParticle* p = m_particleList.m_head; p; p = p->m_next) { - if (p->m_type == e_staticParticle) + if (p->m_type == e_staticClothParticle) { - b3Draw_draw->DrawPoint(p->m_position, 4.0f, b3Color_white); + b3Draw_draw->DrawPoint(p->m_position, 4.0, b3Color_white); } - if (p->m_type == e_kinematicParticle) + if (p->m_type == e_kinematicClothParticle) { - b3Draw_draw->DrawPoint(p->m_position, 4.0f, b3Color_blue); + b3Draw_draw->DrawPoint(p->m_position, 4.0, b3Color_blue); } - if (p->m_type == e_dynamicParticle) + if (p->m_type == e_dynamicClothParticle) { - b3Draw_draw->DrawPoint(p->m_position, 4.0f, b3Color_green); + b3Draw_draw->DrawPoint(p->m_position, 4.0, b3Color_green); } } - for (b3Force* f = m_forceList.m_head; f; f = f->m_next) + for (b3ClothCapsuleShape* c = m_capsuleShapeList.m_head; c; c = c->m_next) { - if (f->m_type == e_springForce) - { - b3SpringForce* s = (b3SpringForce*)f; - b3Particle* p1 = s->m_p1; - b3Particle* p2 = s->m_p2; + b3ClothParticle* p1 = c->m_p1; + b3ClothParticle* p2 = c->m_p2; - b3Draw_draw->DrawSegment(p1->m_position, p2->m_position, b3Color_black); - } + b3Draw_draw->DrawSegment(p1->m_position, p2->m_position, b3Color_black); } - const b3ClothMesh* m = m_mesh; - - for (u32 i = 0; i < m->sewingLineCount; ++i) + for (b3ClothTriangleShape* t = m_triangleShapeList.m_head; t; t = t->m_next) { - b3ClothMeshSewingLine* s = m->sewingLines + i; - b3Particle* p1 = m_particles[s->v1]; - b3Particle* p2 = m_particles[s->v2]; - - b3Draw_draw->DrawSegment(p1->m_position, p2->m_position, b3Color_white); - } - - for (u32 i = 0; i < m->triangleCount; ++i) - { - b3ClothMeshTriangle* t = m->triangles + i; - - b3Particle* p1 = m_particles[t->v1]; - b3Particle* p2 = m_particles[t->v2]; - b3Particle* p3 = m_particles[t->v3]; + b3ClothParticle* p1 = t->m_p1; + b3ClothParticle* p2 = t->m_p2; + b3ClothParticle* p3 = t->m_p3; b3Vec3 v1 = p1->m_position; b3Vec3 v2 = p2->m_position; b3Vec3 v3 = p3->m_position; + + b3Vec3 c = (v1 + v2 + v3) / scalar(3); - b3Draw_draw->DrawTriangle(v1, v2, v3, b3Color_black); - - b3Vec3 c = (v1 + v2 + v3) / 3.0f; - - float32 s = 0.9f; + scalar s(0.9); v1 = s * (v1 - c) + c; v2 = s * (v2 - c) + c; v3 = s * (v3 - c) + c; - b3Vec3 n1 = b3Cross(v2 - v1, v3 - v1); - n1.Normalize(); - b3Draw_draw->DrawSolidTriangle(n1, v1, v2, v3, b3Color_blue); + b3Vec3 n = b3Cross(v2 - v1, v3 - v1); + n.Normalize(); - b3Vec3 n2 = -n1; - b3Draw_draw->DrawSolidTriangle(n2, v3, v2, v1, b3Color_blue); + // Solid radius + const scalar rs(0.05); + + // Frame radius plus a small tolerance to prevent z-fighting + const scalar rf = rs + scalar(0.005); + + b3Color frontSolidColor(scalar(0), scalar(0), scalar(1)); + b3Color frontFrameColor(scalar(0), scalar(0), scalar(0.5)); + + b3Color backSolidColor(scalar(0.5), scalar(0.5), scalar(0.5)); + b3Color backFrameColor(scalar(0.25), scalar(0.25), scalar(0.25)); + + { + b3Vec3 x1 = v1 + rf * n; + b3Vec3 x2 = v2 + rf * n; + b3Vec3 x3 = v3 + rf * n; + + b3Draw_draw->DrawTriangle(x1, x2, x3, frontFrameColor); + } + + { + b3Vec3 x1 = v1 - rf * n; + b3Vec3 x2 = v2 - rf * n; + b3Vec3 x3 = v3 - rf * n; + + b3Draw_draw->DrawTriangle(x1, x2, x3, backFrameColor); + } + + { + b3Vec3 x1 = v1 + rs * n; + b3Vec3 x2 = v2 + rs * n; + b3Vec3 x3 = v3 + rs * n; + + b3Draw_draw->DrawSolidTriangle(n, x1, x2, x3, frontSolidColor); + } + + { + b3Vec3 x1 = v1 - rs * n; + b3Vec3 x2 = v2 - rs * n; + b3Vec3 x3 = v3 - rs * n; + + b3Draw_draw->DrawSolidTriangle(-n, x3, x2, x1, backSolidColor); + } } } \ No newline at end of file diff --git a/src/bounce/cloth/cloth_contact_manager.cpp b/src/bounce/cloth/cloth_contact_manager.cpp index 77c09bd..1033c82 100644 --- a/src/bounce/cloth/cloth_contact_manager.cpp +++ b/src/bounce/cloth/cloth_contact_manager.cpp @@ -18,298 +18,421 @@ #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include b3ClothContactManager::b3ClothContactManager() : - m_particleTriangleContactBlocks(sizeof(b3ParticleTriangleContact)), - m_particleBodyContactBlocks(sizeof(b3ParticleBodyContact)) + m_sphereAndTriangleContactBlocks(sizeof(b3ClothSphereAndTriangleContact)), + m_sphereAndShapeContactBlocks(sizeof(b3ClothSphereAndShapeContact)), + m_capsuleAndCapsuleContactBlocks(sizeof(b3ClothCapsuleAndCapsuleContact)) { } void b3ClothContactManager::FindNewContacts() { - FindNewClothContacts(); - FindNewBodyContacts(); -} + B3_PROFILE("Cloth Find New Contacts"); -void b3ClothContactManager::FindNewClothContacts() -{ - B3_PROFILE("Cloth Find New Cloth Contacts"); - m_broadPhase.FindPairs(this); } -class b3ClothContactManagerFindNewBodyContactsQueryListener : public b3QueryListener -{ -public: - virtual bool ReportShape(b3Shape* s2) - { - cm->AddPSPair(p1, s2); - - // Keep looking for overlaps - return true; - } - - b3ClothContactManager* cm; - b3Particle* p1; -}; - -void b3ClothContactManager::FindNewBodyContacts() -{ - B3_PROFILE("Cloth Find New Body Contacts"); - - // Is there a world attached to this cloth? - if (m_cloth->m_world == nullptr) - { - return; - } - - for (b3Particle* p = m_cloth->m_particleList.m_head; p; p = p->m_next) - { - if (p->m_type != e_dynamicParticle) - { - continue; - } - - b3AABB3 aabb = m_broadPhase.GetAABB(p->m_broadPhaseId); - - b3ClothContactManagerFindNewBodyContactsQueryListener listener; - listener.cm = this; - listener.p1 = p; - - m_cloth->m_world->QueryAABB(&listener, aabb); - } -} - -void b3ClothContactManager::AddPSPair(b3Particle* p1, b3Shape* s2) -{ - // Check if there is a contact between the two entities. - for (b3ParticleBodyContact* c = m_particleBodyContactList.m_head; c; c = c->m_next) - { - if (c->m_p1 == p1 && c->m_s2 == s2) - { - // A contact already exists. - return; - } - } - - bool isntDynamic1 = p1->m_type != e_dynamicParticle; - bool isntDynamic2 = s2->GetBody()->GetType() != e_dynamicBody; - - if (isntDynamic1 && isntDynamic2) - { - // The entities must not collide with each other. - return; - } - - // Create a new contact. - b3ParticleBodyContact* c = CreateParticleBodyContact(); - - c->m_p1 = p1; - c->m_s2 = s2; - c->m_active = false; - c->m_normalImpulse = 0.0f; - c->m_tangentImpulse.SetZero(); - - // Add the contact to the body contact list. - m_particleBodyContactList.PushFront(c); -} - void b3ClothContactManager::AddPair(void* data1, void* data2) { - b3ClothAABBProxy* proxy1 = (b3ClothAABBProxy*)data1; - b3ClothAABBProxy* proxy2 = (b3ClothAABBProxy*)data2; + b3ClothShape* shape1 = (b3ClothShape*)data1; + b3ClothShape* shape2 = (b3ClothShape*)data2; - if (proxy1->type == e_particleProxy && proxy2->type == e_particleProxy) + if (shape1->m_type > shape2->m_type) + { + // Ensure type1 < type2. + b3Swap(shape1, shape2); + } + + if (shape1->m_type == e_clothSphereShape && shape2->m_type == e_clothSphereShape) { - // Particle-particle contacts are not supported. return; } - if (proxy1->type == e_triangleProxy && proxy2->type == e_triangleProxy) + if (shape1->m_type == e_clothSphereShape && shape2->m_type == e_clothCapsuleShape) { - // Triangle-triangle contacts are not supported. return; } - if (proxy1->type == e_triangleProxy) + if (shape1->m_type == e_clothCapsuleShape && shape2->m_type == e_clothTriangleShape) { - // Ensure proxy1 is a particle and proxy 2 a triangle. - b3Swap(proxy1, proxy2); + return; + } + + if (shape1->m_type == e_clothCapsuleShape && shape2->m_type == e_clothWorldShape) + { + return; + } + + if (shape1->m_type == e_clothTriangleShape && shape2->m_type == e_clothTriangleShape) + { + return; } - B3_ASSERT(proxy1->type == e_particleProxy); - B3_ASSERT(proxy2->type == e_triangleProxy); - - b3Particle* p1 = (b3Particle*)proxy1->owner; - - b3ClothTriangle* t2 = (b3ClothTriangle*)proxy2->owner; - b3ClothMeshTriangle* triangle = m_cloth->m_mesh->triangles + t2->m_triangle; - b3Particle* p2 = m_cloth->m_particles[triangle->v1]; - b3Particle* p3 = m_cloth->m_particles[triangle->v2]; - b3Particle* p4 = m_cloth->m_particles[triangle->v3]; - - // Check if there is a contact between the two entities. - for (b3ParticleTriangleContact* c = m_particleTriangleContactList.m_head; c; c = c->m_next) + if (shape1->m_type == e_clothTriangleShape && shape2->m_type == e_clothWorldShape) { - if (c->m_p1 == p1 && c->m_t2 == t2) + return; + } + + if (shape1->m_type == e_clothWorldShape && shape2->m_type == e_clothWorldShape) + { + return; + } + + if (shape1->m_type == e_clothSphereShape && shape2->m_type == e_clothWorldShape) + { + b3ClothSphereShape* s1 = (b3ClothSphereShape*)shape1; + b3ClothParticle* p1 = s1->m_p; + + b3ClothWorldShape* ws2 = (b3ClothWorldShape*)shape2; + const b3Shape* s2 = ws2->m_shape; + const b3Body* b2 = s2->GetBody(); + + if (b2->GetType() != e_staticBody) { - // A contact already exists. + // The cloth can't collide with non-static shapes. return; } - } - bool isntDynamic1 = p1->m_type != e_dynamicParticle; - bool isntDynamic2 = p2->m_type != e_dynamicParticle && p3->m_type != e_dynamicParticle && p4->m_type != e_dynamicParticle; + if (p1->GetType() != e_dynamicClothParticle) + { + // The shapes must not collide with each other. + return; + } + + // Check if there is a contact between the two entities. + for (b3ClothSphereAndShapeContact* c = m_sphereAndShapeContactList.m_head; c; c = c->m_next) + { + if (c->m_s1 == s1 && c->m_s2 == ws2) + { + // A contact already exists. + return; + } + } + + // Create a new contact. + b3ClothSphereAndShapeContact* c = CreateSphereAndShapeContact(); + + c->m_s1 = s1; + c->m_s2 = ws2; + c->m_active = false; + c->m_normalImpulse = scalar(0); + c->m_tangentImpulse.SetZero(); + + // Add the contact to the contact list. + m_sphereAndShapeContactList.PushFront(c); - if (isntDynamic1 && isntDynamic2) - { - // The entities must not collide with each other. return; } - if (p1 == p2 || p1 == p3 || p1 == p4) + if (m_cloth->m_enableSelfCollision == true && + shape1->m_type == e_clothCapsuleShape && shape2->m_type == e_clothCapsuleShape) { - // The entities must not collide with each other. + b3ClothCapsuleShape* s1 = (b3ClothCapsuleShape*)shape1; + b3ClothCapsuleShape* s2 = (b3ClothCapsuleShape*)shape2; + + b3ClothParticle* p1 = s1->m_p1; + b3ClothParticle* p2 = s1->m_p2; + + b3ClothParticle* p3 = s2->m_p1; + b3ClothParticle* p4 = s2->m_p2; + + bool isntDynamic1 = p1->m_type != e_dynamicClothParticle && p2->m_type != e_dynamicClothParticle; + bool isntDynamic2 = p3->m_type != e_dynamicClothParticle && p4->m_type != e_dynamicClothParticle; + + if (isntDynamic1 && isntDynamic2) + { + // The entities must not collide with each other. + return; + } + + // Do the edges share a vertex? + if (p1 == p3 || p1 == p4) + { + return; + } + + if (p2 == p3 || p2 == p4) + { + return; + } + + // Check if there is a contact between the two capsules. + for (b3ClothCapsuleAndCapsuleContact* c = m_capsuleAndCapsuleContactList.m_head; c; c = c->m_next) + { + if (c->m_s1 == s1 && c->m_s2 == s2) + { + // A contact already exists. + return; + } + + if (c->m_s1 == s2 && c->m_s2 == s1) + { + // A contact already exists. + return; + } + } + + // Create a new contact. + b3ClothCapsuleAndCapsuleContact* c = CreateCapsuleAndCapsuleContact(); + + c->m_s1 = s1; + c->m_s2 = s2; + c->m_normalImpulse = scalar(0); + c->m_tangentImpulse.SetZero(); + c->m_active = false; + + // Add the contact to the cloth contact list. + m_capsuleAndCapsuleContactList.PushFront(c); + return; } - - // Create a new contact. - b3ParticleTriangleContact* c = CreateParticleTriangleContact(); - c->m_p1 = p1; - c->m_t2 = t2; - c->m_p2 = p2; - c->m_p3 = p3; - c->m_p4 = p4; - c->m_normalImpulse = 0.0f; - c->m_tangentImpulse1 = 0.0f; - c->m_tangentImpulse2 = 0.0f; - c->m_active = false; + if (m_cloth->m_enableSelfCollision == true && + shape1->m_type == e_clothSphereShape && shape2->m_type == e_clothTriangleShape) + { + b3ClothSphereShape* s1 = (b3ClothSphereShape*)shape1; + b3ClothTriangleShape* s2 = (b3ClothTriangleShape*)shape2; - // Add the contact to the cloth contact list. - m_particleTriangleContactList.PushFront(c); + b3ClothParticle* p1 = s1->m_p; + + b3ClothParticle* p2 = s2->m_p1; + b3ClothParticle* p3 = s2->m_p2; + b3ClothParticle* p4 = s2->m_p3; + + bool isntDynamic1 = p1->m_type != e_dynamicClothParticle; + bool isntDynamic2 = p2->m_type != e_dynamicClothParticle && p3->m_type != e_dynamicClothParticle && p4->m_type != e_dynamicClothParticle; + + if (isntDynamic1 && isntDynamic2) + { + // The entities must not collide with each other. + return; + } + + if (p1 == p2 || p1 == p3 || p1 == p4) + { + // The entities must not collide with each other. + return; + } + + // Check if there is a contact between the two entities. + for (b3ClothSphereAndTriangleContact* c = m_sphereAndTriangleContactList.m_head; c; c = c->m_next) + { + if (c->m_s1 == s1 && c->m_s2 == s2) + { + // A contact already exists. + return; + } + } + + // Create a new contact. + b3ClothSphereAndTriangleContact* c = CreateSphereAndTriangleContact(); + + c->m_s1 = s1; + c->m_s2 = s2; + c->m_normalImpulse = scalar(0); + c->m_tangentImpulse.SetZero(); + c->m_active = false; + + // Add the contact to the cloth contact list. + m_sphereAndTriangleContactList.PushFront(c); + + return; + } } -b3ParticleTriangleContact* b3ClothContactManager::CreateParticleTriangleContact() +b3ClothSphereAndTriangleContact* b3ClothContactManager::CreateSphereAndTriangleContact() { - void* block = m_particleTriangleContactBlocks.Allocate(); - return new(block) b3ParticleTriangleContact(); + void* block = m_sphereAndTriangleContactBlocks.Allocate(); + return new(block) b3ClothSphereAndTriangleContact(); } -void b3ClothContactManager::Destroy(b3ParticleTriangleContact* c) +void b3ClothContactManager::Destroy(b3ClothSphereAndTriangleContact* c) { - m_particleTriangleContactList.Remove(c); - - c->~b3ParticleTriangleContact(); - - m_particleTriangleContactBlocks.Free(c); + m_sphereAndTriangleContactList.Remove(c); + c->~b3ClothSphereAndTriangleContact(); + m_sphereAndTriangleContactBlocks.Free(c); } -b3ParticleBodyContact* b3ClothContactManager::CreateParticleBodyContact() +b3ClothSphereAndShapeContact* b3ClothContactManager::CreateSphereAndShapeContact() { - void* block = m_particleBodyContactBlocks.Allocate(); - return new(block) b3ParticleBodyContact(); + void* block = m_sphereAndShapeContactBlocks.Allocate(); + return new(block) b3ClothSphereAndShapeContact(); } -void b3ClothContactManager::Destroy(b3ParticleBodyContact* c) +void b3ClothContactManager::Destroy(b3ClothSphereAndShapeContact* c) { - m_particleBodyContactList.Remove(c); + m_sphereAndShapeContactList.Remove(c); + c->~b3ClothSphereAndShapeContact(); + m_sphereAndShapeContactBlocks.Free(c); +} - c->~b3ParticleBodyContact(); +b3ClothCapsuleAndCapsuleContact* b3ClothContactManager::CreateCapsuleAndCapsuleContact() +{ + void* block = m_capsuleAndCapsuleContactBlocks.Allocate(); + return new(block) b3ClothCapsuleAndCapsuleContact(); +} - m_particleBodyContactBlocks.Free(c); +void b3ClothContactManager::Destroy(b3ClothCapsuleAndCapsuleContact* c) +{ + m_capsuleAndCapsuleContactList.Remove(c); + c->~b3ClothCapsuleAndCapsuleContact(); + m_capsuleAndCapsuleContactBlocks.Free(c); } void b3ClothContactManager::UpdateContacts() { - UpdateClothContacts(); - UpdateBodyContacts(); -} - -void b3ClothContactManager::UpdateClothContacts() -{ - B3_PROFILE("Cloth Update Cloth Contacts"); + B3_PROFILE("Cloth Update Contacts"); - // Update the state of particle-triangle contacts. - b3ParticleTriangleContact* c = m_particleTriangleContactList.m_head; - while (c) { - bool isntDynamic1 = c->m_p1->m_type != e_dynamicParticle; - bool isntDynamic2 = c->m_p2->m_type != e_dynamicParticle && c->m_p3->m_type != e_dynamicParticle && c->m_p4->m_type != e_dynamicParticle; - - // Destroy the contact if primitives must not collide with each other. - if (isntDynamic1 && isntDynamic2) + // Update the state of sphere and shape contacts. + b3ClothSphereAndShapeContact* c = m_sphereAndShapeContactList.m_head; + while (c) { - b3ParticleTriangleContact* quack = c; + b3ClothSphereShape* s1 = c->m_s1; + b3ClothParticle* p1 = s1->m_p; + + b3ClothWorldShape* ws2 = c->m_s2; + const b3Shape* s2 = ws2->m_shape; + const b3Body* b2 = s2->GetBody(); + + // Cease the contact if body became non-static. + if (b2->GetType() != e_staticBody) + { + b3ClothSphereAndShapeContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + // Cease the contact if entities must not collide with each other. + if (p1->m_type != e_dynamicClothParticle) + { + b3ClothSphereAndShapeContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + u32 proxy1 = c->m_s1->m_broadPhaseId; + u32 proxy2 = c->m_s2->m_broadPhaseId; + + // Destroy the contact if AABBs are not overlapping. + bool overlap = m_broadPhase.TestOverlap(proxy1, proxy2); + if (overlap == false) + { + b3ClothSphereAndShapeContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + // The contact persists. + c->Update(); + c = c->m_next; - Destroy(quack); - continue; } - - u32 proxy1 = c->m_p1->m_broadPhaseId; - u32 proxy2 = c->m_t2->m_broadPhaseId; - - // Destroy the contact if primitive AABBs are not overlapping. - bool overlap = m_broadPhase.TestOverlap(proxy1, proxy2); - if (overlap == false) - { - b3ParticleTriangleContact* quack = c; - c = c->m_next; - Destroy(quack); - continue; - } - - // The contact persists. - c->Update(); - - c = c->m_next; } -} -void b3ClothContactManager::UpdateBodyContacts() -{ - B3_PROFILE("Cloth Update Body Contacts"); - - // Update the state of particle-body contacts. - b3ParticleBodyContact* c = m_particleBodyContactList.m_head; - while (c) { - bool isntDynamic1 = c->m_p1->m_type != e_dynamicParticle; - bool isntDynamic2 = c->m_s2->GetBody()->GetType() != e_dynamicBody; - - // Cease the contact if entities must not collide with each other. - if (isntDynamic1 && isntDynamic2) + // Update the state of sphere and triangle contacts. + b3ClothSphereAndTriangleContact* c = m_sphereAndTriangleContactList.m_head; + while (c) { - b3ParticleBodyContact* quack = c; + b3ClothSphereShape* s1 = c->m_s1; + b3ClothParticle* p1 = s1->m_p; + + b3ClothTriangleShape* s2 = c->m_s2; + b3ClothParticle* p2 = s2->m_p1; + b3ClothParticle* p3 = s2->m_p2; + b3ClothParticle* p4 = s2->m_p3; + + bool isntDynamic1 = p1->m_type != e_dynamicClothParticle; + + bool isntDynamic2 = + p2->m_type != e_dynamicClothParticle && + p3->m_type != e_dynamicClothParticle && + p4->m_type != e_dynamicClothParticle; + + // Destroy the contact if shapes must not collide with each other. + if (isntDynamic1 && isntDynamic2) + { + b3ClothSphereAndTriangleContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + u32 proxy1 = c->m_s1->m_broadPhaseId; + u32 proxy2 = c->m_s2->m_broadPhaseId; + + // Destroy the contact if primitive AABBs are not overlapping. + bool overlap = m_broadPhase.TestOverlap(proxy1, proxy2); + if (overlap == false) + { + b3ClothSphereAndTriangleContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + // The contact persists. + c->Update(); + c = c->m_next; - Destroy(quack); - continue; } + } - b3AABB3 aabb1 = m_broadPhase.GetAABB(c->m_p1->m_broadPhaseId); - b3AABB3 aabb2 = c->m_s2->GetAABB(); - - // Destroy the contact if entities AABBs are not overlapping. - bool overlap = b3TestOverlap(aabb1, aabb2); - if (overlap == false) + { + // Update the state of capsule contacts. + b3ClothCapsuleAndCapsuleContact* c = m_capsuleAndCapsuleContactList.m_head; + while (c) { - b3ParticleBodyContact* quack = c; + b3ClothCapsuleShape* s1 = c->m_s1; + b3ClothParticle* p1 = s1->m_p1; + b3ClothParticle* p2 = s1->m_p2; + + b3ClothCapsuleShape* s2 = c->m_s2; + b3ClothParticle* p3 = s2->m_p1; + b3ClothParticle* p4 = s2->m_p2; + + bool isntDynamic1 = p1->m_type != e_dynamicClothParticle && p2->m_type != e_dynamicClothParticle; + bool isntDynamic2 = p3->m_type != e_dynamicClothParticle && p4->m_type != e_dynamicClothParticle; + + // Destroy the contact if primitives must not collide with each other. + if (isntDynamic1 && isntDynamic2) + { + b3ClothCapsuleAndCapsuleContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + u32 proxy1 = c->m_s1->m_broadPhaseId; + u32 proxy2 = c->m_s2->m_broadPhaseId; + + // Destroy the contact if AABBs are not overlapping. + bool overlap = m_broadPhase.TestOverlap(proxy1, proxy2); + if (overlap == false) + { + b3ClothCapsuleAndCapsuleContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + // The contact persists. + c->Update(); + c = c->m_next; - Destroy(quack); - continue; } - - // The contact persists. - c->Update(); - - c = c->m_next; } } \ No newline at end of file diff --git a/src/bounce/cloth/cloth_force_solver.cpp b/src/bounce/cloth/cloth_force_solver.cpp index f801174..46b2c9b 100644 --- a/src/bounce/cloth/cloth_force_solver.cpp +++ b/src/bounce/cloth/cloth_force_solver.cpp @@ -17,7 +17,7 @@ */ #include -#include +#include #include #include #include @@ -29,14 +29,12 @@ // described in the paper: // "Large Steps in Cloth Simulation - David Baraff, Andrew Witkin". -// Some improvements for the original MPCG algorithm are described in the paper: -// "On the modified conjugate gradient method in cloth simulation - Uri M. Ascher, Eddy Boxerman". - u32 b3_clothSolverIterations = 0; b3ClothForceSolver::b3ClothForceSolver(const b3ClothForceSolverDef& def) { - m_allocator = def.stack; + m_step = def.step; + m_stack = def.stack; m_particleCount = def.particleCount; m_particles = def.particles; @@ -57,47 +55,46 @@ void b3ClothForceSolver::ApplyForces() } } -// Solve Ax = b +// Solve A * x = b static void b3SolveMPCG(b3DenseVec3& x, const b3SparseMat33View& A, const b3DenseVec3& b, - const b3DiagMat33& S, const b3DenseVec3& z, - const b3DenseVec3& y, const b3DiagMat33& I, u32 maxIterations = 20) + const b3DenseVec3& z, const b3DiagMat33& S, u32 maxIterations = 20) { B3_PROFILE("Cloth Solve MPCG"); - // Jacobi preconditioner - // P = diag(A) - b3DiagMat33 inv_P(A.rowCount); + // Jacobi preconditioner + // P = diag(A) + b3DiagMat33 P(A.rowCount); + b3DiagMat33 invP(A.rowCount); + for (u32 i = 0; i < A.rowCount; ++i) { b3Mat33 a = A(i, i); // Sylvester Criterion to ensure PD-ness - B3_ASSERT(b3Det(a.x, a.y, a.z) > 0.0f); + B3_ASSERT(b3Det(a.x, a.y, a.z) > scalar(0)); - B3_ASSERT(a.x.x > 0.0f); - float32 xx = 1.0f / a.x.x; + B3_ASSERT(a.x.x > scalar(0)); + scalar xx = scalar(1) / a.x.x; - B3_ASSERT(a.y.y > 0.0f); - float32 yy = 1.0f / a.y.y; + B3_ASSERT(a.y.y > scalar(0)); + scalar yy = scalar(1) / a.y.y; - B3_ASSERT(a.z.z > 0.0f); - float32 zz = 1.0f / a.z.z; + B3_ASSERT(a.z.z > scalar(0)); + scalar zz = scalar(1) / a.z.z; - inv_P[i] = b3Diagonal(xx, yy, zz); + P[i] = b3Diagonal(a.x.x, a.y.y, a.z.z); + invP[i] = b3Diagonal(xx, yy, zz); } - x = (S * y) + (I - S) * z; + x = z; - b3DenseVec3 b_hat = S * (b - A * ((I - S) * z)); - - float32 b_delta = b3Dot(b_hat, inv_P * b_hat); + scalar delta_0 = b3Dot(S * b, P * (S * b)); b3DenseVec3 r = S * (b - A * x); + b3DenseVec3 c = S * (invP * r); - b3DenseVec3 p = S * (inv_P * r); - - float32 delta_new = b3Dot(r, p); + scalar delta_new = b3Dot(r, c); u32 iteration = 0; for (;;) @@ -107,27 +104,27 @@ static void b3SolveMPCG(b3DenseVec3& x, break; } - if (delta_new <= B3_EPSILON * B3_EPSILON * b_delta) + if (delta_new <= B3_EPSILON * B3_EPSILON * delta_0) { break; } - b3DenseVec3 s = S * (A * p); + b3DenseVec3 q = S * (A * c); - float32 alpha = delta_new / b3Dot(p, s); + scalar alpha = delta_new / b3Dot(c, q); - x = x + alpha * p; - r = r - alpha * s; + x = x + alpha * c; + r = r - alpha * q; - b3DenseVec3 h = inv_P * r; + b3DenseVec3 s = invP * r; - float32 delta_old = delta_new; + scalar delta_old = delta_new; - delta_new = b3Dot(r, h); + delta_new = b3Dot(r, s); - float32 beta = delta_new / delta_old; + scalar beta = delta_new / delta_old; - p = S * (h + beta * p); + c = S * (s + beta * c); ++iteration; } @@ -135,22 +132,19 @@ static void b3SolveMPCG(b3DenseVec3& x, b3_clothSolverIterations = iteration; } -void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) +void b3ClothForceSolver::Solve(const b3Vec3& gravity) { - float32 h = dt; + scalar h = m_step.dt; b3DenseVec3 sx(m_particleCount); b3DenseVec3 sv(m_particleCount); b3DenseVec3 sf(m_particleCount); b3DenseVec3 sy(m_particleCount); b3DenseVec3 sz(m_particleCount); - b3DenseVec3 sx0(m_particleCount); - b3SparseMat33 M(m_particleCount); + b3DiagMat33 M(m_particleCount); b3SparseMat33 dfdx(m_particleCount); b3SparseMat33 dfdv(m_particleCount); b3DiagMat33 S(m_particleCount); - b3DiagMat33 I(m_particleCount); - I.SetIdentity(); m_solverData.x = &sx; m_solverData.v = &sv; @@ -163,28 +157,31 @@ void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) for (u32 i = 0; i < m_particleCount; ++i) { - b3Particle* p = m_particles[i]; + b3ClothParticle* p = m_particles[i]; - M(i, i) = b3Diagonal(p->m_mass); - sx[i] = p->m_position; sv[i] = p->m_velocity; sf[i] = p->m_force; sz[i].SetZero(); - if (p->m_type == e_dynamicParticle) + if (p->m_type == e_dynamicClothParticle) { + B3_ASSERT(p->m_mass > scalar(0)); + M[i] = b3Diagonal(p->m_mass); + S[i].SetIdentity(); + // Apply weight sf[i] += p->m_mass * gravity; - S[i].SetIdentity(); } else { + // Ensure a non-zero mass because zero masses + // can make the system unsolvable. + M[i] = b3Diagonal(scalar(1)); S[i].SetZero(); } sy[i] = p->m_translation; - sx0[i] = p->m_x; } // Apply internal forces @@ -198,14 +195,14 @@ void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) b3SparseMat33 A = M - h * dfdv - (h * h) * dfdx; // View for A - b3SparseMat33View viewA(A); + b3SparseMat33View AV(A); // b b3DenseVec3 b = h * (sf + h * (dfdx * sv) + dfdx * sy); // x b3DenseVec3 x(m_particleCount); - b3SolveMPCG(x, viewA, b, S, sz, sx0, I); + b3SolveMPCG(x, AV, b, sz, S); // Velocity update sv = sv + x; @@ -214,7 +211,7 @@ void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) // Copy state buffers back to the particle for (u32 i = 0; i < m_particleCount; ++i) { - b3Particle* p = m_particles[i]; + b3ClothParticle* p = m_particles[i]; p->m_x = x[i]; p->m_position = sx[i]; diff --git a/src/bounce/cloth/cloth_particle.cpp b/src/bounce/cloth/cloth_particle.cpp new file mode 100644 index 0000000..a478f58 --- /dev/null +++ b/src/bounce/cloth/cloth_particle.cpp @@ -0,0 +1,239 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include + +b3ClothParticle::b3ClothParticle(const b3ClothParticleDef& def, b3Cloth* cloth) +{ + m_cloth = cloth; + m_type = def.type; + m_position = def.position; + m_velocity = def.velocity; + m_force = def.force; + m_translation.SetZero(); + + if (m_type == e_dynamicClothParticle) + { + m_mass = scalar(1); + m_invMass = scalar(1); + } + else + { + m_mass = scalar(0); + m_invMass = scalar(0); + } + + m_x.SetZero(); + m_meshIndex = def.meshIndex; + m_userData = def.userData; +} + +b3ClothParticle::~b3ClothParticle() +{ + +} + +void b3ClothParticle::SetType(b3ClothParticleType type) +{ + if (m_type == type) + { + return; + } + + m_type = type; + + if (m_type == e_staticClothParticle || m_type == e_kinematicClothParticle) + { + m_mass = scalar(0); + m_invMass = scalar(0); + } + else + { + m_cloth->ResetMass(); + } + + m_force.SetZero(); + + if (type == e_staticClothParticle) + { + m_velocity.SetZero(); + m_translation.SetZero(); + + SynchronizeSpheres(); + SynchronizeCapsules(); + SynchronizeTriangles(); + } +} + +void b3ClothParticle::DestroySpheres() +{ + b3ClothSphereShape* s = m_cloth->m_sphereShapeList.m_head; + while (s) + { + b3ClothSphereShape* s0 = s; + s = s->m_next; + + if (s0->m_p == this) + { + m_cloth->DestroySphereShape(s0); + } + } +} + +void b3ClothParticle::DestroyCapsules() +{ + b3ClothCapsuleShape* c = m_cloth->m_capsuleShapeList.m_head; + while (c) + { + b3ClothCapsuleShape* c0 = c; + c = c->m_next; + + if (c0->m_p1 == this || c0->m_p2 == this) + { + m_cloth->DestroyCapsuleShape(c0); + } + } +} + +void b3ClothParticle::DestroyTriangles() +{ + b3ClothTriangleShape* t = m_cloth->m_triangleShapeList.m_head; + while (t) + { + b3ClothTriangleShape* t0 = t; + t = t->m_next; + + if (t0->m_p1 == this || t0->m_p2 == this || t0->m_p3 == this) + { + m_cloth->DestroyTriangleShape(t0); + } + } +} + +void b3ClothParticle::DestroyForces() +{ + b3Force* f = m_cloth->m_forceList.m_head; + while (f) + { + b3Force* f0 = f; + f = f->m_next; + + if (f0->HasParticle(this)) + { + m_cloth->DestroyForce(f0); + } + } +} + +void b3ClothParticle::DestroyContacts() +{ + { + // Destroy shape contacts + b3ClothSphereAndShapeContact* c = m_cloth->m_contactManager.m_sphereAndShapeContactList.m_head; + while (c) + { + if (c->m_s1->m_p == this) + { + b3ClothSphereAndShapeContact* quack = c; + c = c->m_next; + m_cloth->m_contactManager.Destroy(quack); + continue; + } + + c = c->m_next; + } + } + + { + // Destroy capsule contacts + b3ClothCapsuleAndCapsuleContact* c = m_cloth->m_contactManager.m_capsuleAndCapsuleContactList.m_head; + while (c) + { + if (c->m_s1->m_p1 == this || + c->m_s1->m_p2 == this || + c->m_s2->m_p1 == this || + c->m_s2->m_p2 == this) + { + b3ClothCapsuleAndCapsuleContact* quack = c; + c = c->m_next; + m_cloth->m_contactManager.Destroy(quack); + continue; + } + + c = c->m_next; + } + } + + { + // Destroy triangle contacts + b3ClothSphereAndTriangleContact* c = m_cloth->m_contactManager.m_sphereAndTriangleContactList.m_head; + while (c) + { + if (c->m_s1->m_p == this || + c->m_s2->m_p1 == this || + c->m_s2->m_p2 == this || + c->m_s2->m_p3 == this) + { + b3ClothSphereAndTriangleContact* quack = c; + c = c->m_next; + m_cloth->m_contactManager.Destroy(quack); + continue; + } + + c = c->m_next; + } + } +} + +void b3ClothParticle::SynchronizeSpheres() +{ + for (b3ClothSphereShape* s = m_cloth->m_sphereShapeList.m_head; s; s = s->m_next) + { + if (s->m_p == this) + { + s->Synchronize(b3Vec3_zero); + } + } +} + +void b3ClothParticle::SynchronizeCapsules() +{ + for (b3ClothCapsuleShape* c = m_cloth->m_capsuleShapeList.m_head; c; c = c->m_next) + { + if (c->m_p1 == this || c->m_p2 == this) + { + c->Synchronize(b3Vec3_zero); + } + } +} + +void b3ClothParticle::SynchronizeTriangles() +{ + for (b3ClothTriangleShape* t = m_cloth->m_triangleShapeList.m_head; t; t = t->m_next) + { + if (t->m_p1 == this || t->m_p2 == this || t->m_p3 == this) + { + t->Synchronize(b3Vec3_zero); + } + } +} \ No newline at end of file diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 44203a2..252f1a6 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -20,41 +20,46 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include b3ClothSolver::b3ClothSolver(const b3ClothSolverDef& def) { - m_allocator = def.stack; + m_stack = def.stack; m_particleCapacity = def.particleCapacity; m_particleCount = 0; - m_particles = (b3Particle**)m_allocator->Allocate(m_particleCapacity * sizeof(b3Particle*)); + m_particles = (b3ClothParticle**)m_stack->Allocate(m_particleCapacity * sizeof(b3ClothParticle*)); m_forceCapacity = def.forceCapacity; m_forceCount = 0; - m_forces = (b3Force**)m_allocator->Allocate(m_forceCapacity * sizeof(b3Force*));; + m_forces = (b3Force**)m_stack->Allocate(m_forceCapacity * sizeof(b3Force*));; - m_bodyContactCapacity = def.bodyContactCapacity; - m_bodyContactCount = 0; - m_bodyContacts = (b3ParticleBodyContact**)m_allocator->Allocate(m_bodyContactCapacity * sizeof(b3ParticleBodyContact*));; + m_shapeContactCapacity = def.shapeContactCapacity; + m_shapeContactCount = 0; + m_shapeContacts = (b3ClothSphereAndShapeContact**)m_stack->Allocate(m_shapeContactCapacity * sizeof(b3ClothSphereAndShapeContact*)); m_triangleContactCapacity = def.triangleContactCapacity; m_triangleContactCount = 0; - m_triangleContacts = (b3ParticleTriangleContact**)m_allocator->Allocate(m_triangleContactCapacity * sizeof(b3ParticleTriangleContact*));; + m_triangleContacts = (b3ClothSphereAndTriangleContact**)m_stack->Allocate(m_triangleContactCapacity * sizeof(b3ClothSphereAndTriangleContact*)); + + m_capsuleContactCapacity = def.capsuleContactCapacity; + m_capsuleContactCount = 0; + m_capsuleContacts = (b3ClothCapsuleAndCapsuleContact* *)m_stack->Allocate(m_capsuleContactCapacity * sizeof(b3ClothCapsuleAndCapsuleContact*)); } b3ClothSolver::~b3ClothSolver() { - m_allocator->Free(m_triangleContacts); - m_allocator->Free(m_bodyContacts); - m_allocator->Free(m_forces); - m_allocator->Free(m_particles); + m_stack->Free(m_capsuleContacts); + m_stack->Free(m_triangleContacts); + m_stack->Free(m_shapeContacts); + m_stack->Free(m_forces); + m_stack->Free(m_particles); } -void b3ClothSolver::Add(b3Particle* p) +void b3ClothSolver::Add(b3ClothParticle* p) { p->m_solverId = m_particleCount; m_particles[m_particleCount++] = p; @@ -65,22 +70,28 @@ void b3ClothSolver::Add(b3Force* f) m_forces[m_forceCount++] = f; } -void b3ClothSolver::Add(b3ParticleBodyContact* c) +void b3ClothSolver::Add(b3ClothSphereAndShapeContact* c) { - m_bodyContacts[m_bodyContactCount++] = c; + m_shapeContacts[m_shapeContactCount++] = c; } -void b3ClothSolver::Add(b3ParticleTriangleContact* c) +void b3ClothSolver::Add(b3ClothSphereAndTriangleContact* c) { m_triangleContacts[m_triangleContactCount++] = c; } -void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) +void b3ClothSolver::Add(b3ClothCapsuleAndCapsuleContact* c) +{ + m_capsuleContacts[m_capsuleContactCount++] = c; +} + +void b3ClothSolver::Solve(const b3ClothTimeStep& step, const b3Vec3& gravity) { { // Solve internal dynamics b3ClothForceSolverDef forceSolverDef; - forceSolverDef.stack = m_allocator; + forceSolverDef.step = step; + forceSolverDef.stack = m_stack; forceSolverDef.particleCount = m_particleCount; forceSolverDef.particles = m_particles; forceSolverDef.forceCount = m_forceCount; @@ -88,12 +99,12 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati b3ClothForceSolver forceSolver(forceSolverDef); - forceSolver.Solve(dt, gravity); + forceSolver.Solve(gravity); } // Copy particle state to state buffer - b3Vec3* positions = (b3Vec3*)m_allocator->Allocate(m_particleCount * sizeof(b3Vec3)); - b3Vec3* velocities = (b3Vec3*)m_allocator->Allocate(m_particleCount * sizeof(b3Vec3)); + b3Vec3* positions = (b3Vec3*)m_stack->Allocate(m_particleCount * sizeof(b3Vec3)); + b3Vec3* velocities = (b3Vec3*)m_stack->Allocate(m_particleCount * sizeof(b3Vec3)); for (u32 i = 0; i < m_particleCount; ++i) { positions[i] = m_particles[i]->m_position; @@ -103,34 +114,40 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati { // Solve constraints b3ClothContactSolverDef contactSolverDef; - contactSolverDef.allocator = m_allocator; + contactSolverDef.step = step; + contactSolverDef.allocator = m_stack; contactSolverDef.positions = positions; contactSolverDef.velocities = velocities; - contactSolverDef.bodyContactCount = m_bodyContactCount; - contactSolverDef.bodyContacts = m_bodyContacts; + contactSolverDef.shapeContactCount = m_shapeContactCount; + contactSolverDef.shapeContacts = m_shapeContacts; contactSolverDef.triangleContactCount = m_triangleContactCount; contactSolverDef.triangleContacts = m_triangleContacts; + contactSolverDef.capsuleContactCount = m_capsuleContactCount; + contactSolverDef.capsuleContacts = m_capsuleContacts; b3ClothContactSolver contactSolver(contactSolverDef); { // Initialize constraints - contactSolver.InitializeBodyContactConstraints(); + contactSolver.InitializeShapeContactConstraints(); contactSolver.InitializeTriangleContactConstraints(); + contactSolver.InitializeCapsuleContactConstraints(); } { // Warm start velocity constraints - contactSolver.WarmStartBodyContactConstraints(); + contactSolver.WarmStartShapeContactConstraints(); contactSolver.WarmStartTriangleContactConstraints(); + contactSolver.WarmStartCapsuleContactConstraints(); } { // Solve velocity constraints - for (u32 i = 0; i < velocityIterations; ++i) + for (u32 i = 0; i < step.velocityIterations; ++i) { - contactSolver.SolveBodyContactVelocityConstraints(); + contactSolver.SolveShapeContactVelocityConstraints(); contactSolver.SolveTriangleContactVelocityConstraints(); + contactSolver.SolveCapsuleContactVelocityConstraints(); } } @@ -140,7 +157,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati } // Integrate positions - float32 h = dt; + scalar h = step.dt; for (u32 i = 0; i < m_particleCount; ++i) { positions[i] += h * velocities[i]; @@ -149,12 +166,13 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati { // Solve position constraints bool positionSolved = false; - for (u32 i = 0; i < positionIterations; ++i) + for (u32 i = 0; i < step.positionIterations; ++i) { - bool bodyContactsSolved = contactSolver.SolveBodyContactPositionConstraints(); + bool bodyContactsSolved = contactSolver.SolveShapeContactPositionConstraints(); bool triangleContactsSolved = contactSolver.SolveTriangleContactPositionConstraints(); + bool capsuleContactsSolved = contactSolver.SolveCapsuleContactPositionConstraints(); - if (bodyContactsSolved && triangleContactsSolved) + if (bodyContactsSolved && triangleContactsSolved && capsuleContactsSolved) { // Early out if the position errors are small. positionSolved = true; @@ -162,23 +180,6 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati } } } - - // Synchronize bodies - for (u32 i = 0; i < m_bodyContactCount; ++i) - { - b3Body* body = m_bodyContacts[i]->m_s2->GetBody(); - - if (body->GetType() == e_staticBody) - { - continue; - } - - body->SynchronizeTransform(); - - body->m_worldInvI = b3RotateToFrame(body->m_invI, body->m_xf.rotation); - - body->SynchronizeShapes(); - } } // Copy state buffers back to the particles @@ -188,6 +189,6 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati m_particles[i]->m_velocity = velocities[i]; } - m_allocator->Free(velocities); - m_allocator->Free(positions); + m_stack->Free(velocities); + m_stack->Free(positions); } \ No newline at end of file diff --git a/src/bounce/cloth/contacts/cloth_capsule_capsule_contact.cpp b/src/bounce/cloth/contacts/cloth_capsule_capsule_contact.cpp new file mode 100644 index 0000000..50ff322 --- /dev/null +++ b/src/bounce/cloth/contacts/cloth_capsule_capsule_contact.cpp @@ -0,0 +1,208 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include + +// Compute the closest point on a segment to a point. +static b3Vec3 b3ClosestPointOnSegment(const b3Vec3& Q, const b3Capsule& hull) +{ + b3Vec3 A = hull.vertex1; + b3Vec3 B = hull.vertex2; + b3Vec3 AB = B - A; + + // Barycentric coordinates for Q + scalar u = b3Dot(B - Q, AB); + scalar v = b3Dot(Q - A, AB); + + if (v <= scalar(0)) + { + return A; + } + + if (u <= scalar(0)) + { + return B; + } + + scalar w = b3Dot(AB, AB); + if (w <= B3_LINEAR_SLOP * B3_LINEAR_SLOP) + { + return A; + } + + scalar den = scalar(1) / w; + b3Vec3 P = den * (u * A + v * B); + return P; +} + +// Compute the closest points between two line segments. +static void b3ClosestPoints(b3Vec3& C1, b3Vec3& C2, + const b3Capsule& hull1, const b3Capsule& hull2) +{ + b3Vec3 P1 = hull1.vertex1; + b3Vec3 Q1 = hull1.vertex2; + + b3Vec3 P2 = hull2.vertex1; + b3Vec3 Q2 = hull2.vertex2; + + b3Vec3 E1 = Q1 - P1; + scalar L1 = b3Length(E1); + + b3Vec3 E2 = Q2 - P2; + scalar L2 = b3Length(E2); + + if (L1 <= B3_LINEAR_SLOP && L2 <= B3_LINEAR_SLOP) + { + C1 = P1; + C2 = P2; + return; + } + + if (L1 <= B3_LINEAR_SLOP) + { + C1 = P1; + C2 = b3ClosestPointOnSegment(P1, hull2); + return; + } + + if (L2 <= B3_LINEAR_SLOP) + { + C1 = b3ClosestPointOnSegment(P2, hull1); + C2 = P2; + return; + } + + B3_ASSERT(L1 > scalar(0)); + b3Vec3 N1 = E1 / L1; + + B3_ASSERT(L2 > scalar(0)); + b3Vec3 N2 = E2 / L2; + + scalar b = b3Dot(N1, N2); + + scalar den = scalar(1) - b * b; + + if (den != scalar(0)) + { + // The segments are paralell. + scalar inv_den = scalar(1) / den; + + b3Vec3 E3 = P1 - P2; + + scalar d = b3Dot(N1, E3); + scalar e = b3Dot(N2, E3); + + scalar s = inv_den * (b * e - d); + scalar t = inv_den * (e - b * d); + + C1 = P1 + s * N1; + C2 = P2 + t * N2; + } + else + { + C1 = P1; + C2 = P2; + } + + C1 = b3ClosestPointOnSegment(C1, hull1); + + C2 = b3ClosestPointOnSegment(C1, hull2); + + C1 = b3ClosestPointOnSegment(C2, hull1); +} + +void b3ClothCapsuleAndCapsuleContact::Update() +{ + m_active = false; + + b3ClothParticle* p1 = m_s1->m_p1; + b3ClothParticle* p2 = m_s1->m_p2; + b3ClothParticle* p3 = m_s2->m_p1; + b3ClothParticle* p4 = m_s2->m_p2; + + b3Vec3 P1 = p1->m_position; + b3Vec3 Q1 = p2->m_position; + + b3Vec3 P2 = p3->m_position; + b3Vec3 Q2 = p4->m_position; + + b3Capsule hull1; + hull1.vertex1 = P1; + hull1.vertex2 = Q1; + + b3Capsule hull2; + hull2.vertex1 = P2; + hull2.vertex2 = Q2; + + b3Vec3 point1, point2; + b3ClosestPoints(point1, point2, hull1, hull2); + + scalar distance = b3Distance(point1, point2); + + scalar r1 = m_s1->m_radius, r2 = m_s2->m_radius; + + scalar totalRadius = r1 + r2; + + if (distance > totalRadius) + { + return; + } + + if (distance < scalar(10) * B3_EPSILON) + { + // The segments are intersecting. + return; + } + + scalar w12[3]; + b3BarycentricCoordinates(w12, P1, Q1, point1); + if (w12[2] <= B3_LINEAR_SLOP * B3_LINEAR_SLOP) + { + m_w1 = scalar(1); + m_w2 = scalar(0); + } + else + { + m_w1 = w12[0] / w12[2]; + m_w2 = w12[1] / w12[2]; + } + + scalar w23[3]; + b3BarycentricCoordinates(w23, P2, Q2, point2); + if (w23[2] <= B3_LINEAR_SLOP * B3_LINEAR_SLOP) + { + m_w3 = scalar(1); + m_w4 = scalar(0); + } + else + { + m_w3 = w23[0] / w23[2]; + m_w4 = w23[1] / w23[2]; + } + + // Activate the contact + m_active = true; + + m_normal1 = (point2 - point1) / distance; + m_tangent1 = b3Perp(m_normal1); + m_tangent2 = b3Cross(m_tangent1, m_normal1); +} \ No newline at end of file diff --git a/src/bounce/cloth/contacts/cloth_contact_solver.cpp b/src/bounce/cloth/contacts/cloth_contact_solver.cpp index ae715c6..8c7ea39 100644 --- a/src/bounce/cloth/contacts/cloth_contact_solver.cpp +++ b/src/bounce/cloth/contacts/cloth_contact_solver.cpp @@ -18,291 +18,342 @@ #include #include -#include -#include -#include +#include +#include +#include +#include +#include #include #include +#include b3ClothContactSolver::b3ClothContactSolver(const b3ClothContactSolverDef& def) { + m_step = def.step; m_allocator = def.allocator; m_positions = def.positions; m_velocities = def.velocities; - m_bodyContactCount = def.bodyContactCount; - m_bodyContacts = def.bodyContacts; - m_bodyVelocityConstraints = (b3ClothSolverBodyContactVelocityConstraint*)m_allocator->Allocate(m_bodyContactCount * sizeof(b3ClothSolverBodyContactVelocityConstraint)); - m_bodyPositionConstraints = (b3ClothSolverBodyContactPositionConstraint*)m_allocator->Allocate(m_bodyContactCount * sizeof(b3ClothSolverBodyContactPositionConstraint)); + m_shapeContactCount = def.shapeContactCount; + m_shapeContacts = def.shapeContacts; + m_shapeVelocityConstraints = (b3ClothSolverShapeContactVelocityConstraint*)m_allocator->Allocate(m_shapeContactCount * sizeof(b3ClothSolverShapeContactVelocityConstraint)); + m_shapePositionConstraints = (b3ClothSolverShapeContactPositionConstraint*)m_allocator->Allocate(m_shapeContactCount * sizeof(b3ClothSolverShapeContactPositionConstraint)); m_triangleContactCount = def.triangleContactCount; m_triangleContacts = def.triangleContacts; m_triangleVelocityConstraints = (b3ClothSolverTriangleContactVelocityConstraint*)m_allocator->Allocate(m_triangleContactCount * sizeof(b3ClothSolverTriangleContactVelocityConstraint)); m_trianglePositionConstraints = (b3ClothSolverTriangleContactPositionConstraint*)m_allocator->Allocate(m_triangleContactCount * sizeof(b3ClothSolverTriangleContactPositionConstraint)); + + m_capsuleContactCount = def.capsuleContactCount; + m_capsuleContacts = def.capsuleContacts; + m_capsuleVelocityConstraints = (b3ClothSolverCapsuleContactVelocityConstraint*)m_allocator->Allocate(m_capsuleContactCount * sizeof(b3ClothSolverCapsuleContactVelocityConstraint)); + m_capsulePositionConstraints = (b3ClothSolverCapsuleContactPositionConstraint*)m_allocator->Allocate(m_capsuleContactCount * sizeof(b3ClothSolverCapsuleContactPositionConstraint)); } b3ClothContactSolver::~b3ClothContactSolver() { + m_allocator->Free(m_capsulePositionConstraints); + m_allocator->Free(m_capsuleVelocityConstraints); + m_allocator->Free(m_trianglePositionConstraints); m_allocator->Free(m_triangleVelocityConstraints); - m_allocator->Free(m_bodyPositionConstraints); - m_allocator->Free(m_bodyVelocityConstraints); + m_allocator->Free(m_shapePositionConstraints); + m_allocator->Free(m_shapeVelocityConstraints); } -void b3ClothContactSolver::InitializeBodyContactConstraints() +void b3ClothContactSolver::InitializeShapeContactConstraints() { - for (u32 i = 0; i < m_bodyContactCount; ++i) + for (u32 i = 0; i < m_shapeContactCount; ++i) { - b3ParticleBodyContact* c = m_bodyContacts[i]; - b3ClothSolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; - b3ClothSolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; + b3ClothSphereAndShapeContact* c = m_shapeContacts[i]; + b3ClothSolverShapeContactVelocityConstraint* vc = m_shapeVelocityConstraints + i; + b3ClothSolverShapeContactPositionConstraint* pc = m_shapePositionConstraints + i; - vc->indexA = c->m_p1->m_solverId; - vc->bodyB = c->m_s2->GetBody(); + b3ClothSphereShape* s1 = c->m_s1; + b3ClothParticle* p1 = s1->m_p; + + const b3Shape* s2 = c->m_s2->m_shape; - vc->invMassA = c->m_p1->m_type == e_staticParticle ? 0.0f : c->m_p1->m_invMass; - vc->invMassB = vc->bodyB->GetInverseMass(); + vc->indexA = p1->m_solverId; + vc->invMassA = p1->m_invMass; + vc->friction = b3MixFriction(s1->m_friction, s2->GetFriction()); - vc->invIA.SetZero(); - vc->invIB = vc->bodyB->GetWorldInverseInertia(); + pc->indexA = p1->m_solverId; + pc->invMassA = p1->m_invMass; + pc->radiusA = s1->m_radius; - vc->friction = b3MixFriction(c->m_p1->m_friction, c->m_s2->GetFriction()); - - pc->indexA = c->m_p1->m_solverId; - pc->bodyB = vc->bodyB; - - pc->invMassA = c->m_p1->m_type == e_staticParticle ? 0.0f : c->m_p1->m_invMass; - pc->invMassB = vc->bodyB->m_invMass; - - pc->invIA.SetZero(); - pc->invIB = vc->bodyB->m_worldInvI; - - pc->radiusA = c->m_p1->m_radius; - pc->radiusB = c->m_s2->m_radius; - - pc->localCenterA.SetZero(); - pc->localCenterB = pc->bodyB->m_sweep.localCenter; - - pc->normalA = c->m_normal1; - pc->localPointA = c->m_localPoint1; - pc->localPointB = c->m_localPoint2; + pc->radiusB = s2->m_radius; + + pc->normal = c->m_normal2; + pc->pointB = c->m_point2; } - for (u32 i = 0; i < m_bodyContactCount; ++i) + for (u32 i = 0; i < m_shapeContactCount; ++i) { - b3ParticleBodyContact* c = m_bodyContacts[i]; - b3ClothSolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; - b3ClothSolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; + b3ClothSphereAndShapeContact* c = m_shapeContacts[i]; + b3ClothSolverShapeContactVelocityConstraint* vc = m_shapeVelocityConstraints + i; + b3ClothSolverShapeContactPositionConstraint* pc = m_shapePositionConstraints + i; u32 indexA = vc->indexA; - b3Body* bodyB = vc->bodyB; + scalar mA = vc->invMassA; + b3Vec3 cA = m_positions[indexA]; - float32 mA = vc->invMassA; - float32 mB = vc->invMassB; - - b3Mat33 iA = vc->invIA; - b3Mat33 iB = vc->invIB; - - b3Vec3 xA = m_positions[indexA]; - b3Vec3 xB = bodyB->m_sweep.worldCenter; - - b3Quat qA; qA.SetIdentity(); - b3Quat qB = bodyB->m_sweep.orientation; - - b3Vec3 localCenterA = pc->localCenterA; - b3Vec3 localCenterB = pc->localCenterB; - - b3Transform xfA; - xfA.rotation = b3QuatMat33(qA); - xfA.position = xA - b3Mul(xfA.rotation, localCenterA); - - b3Transform xfB; - xfB.rotation = b3QuatMat33(qB); - xfB.position = xB - b3Mul(xfB.rotation, localCenterB); - - b3ParticleBodyContactWorldPoint wp; - wp.Initialize(c, pc->radiusA, xfA, pc->radiusB, xfB); + b3ClothSphereAndShapeContactWorldPoint wp; + wp.Initialize(c, pc->radiusA, cA, pc->radiusB); vc->normal = wp.normal; vc->tangent1 = c->m_tangent1; vc->tangent2 = c->m_tangent2; - vc->point = wp.point; - b3Vec3 point = vc->point; - - b3Vec3 rA = point - xA; - b3Vec3 rB = point - xB; - - vc->rA = rA; - vc->rB = rB; - - vc->normalImpulse = c->m_normalImpulse; - vc->tangentImpulse = c->m_tangentImpulse; + vc->normalImpulse = m_step.dt_ratio * c->m_normalImpulse; + vc->tangentImpulse = m_step.dt_ratio * c->m_tangentImpulse; { - b3Vec3 n = vc->normal; - - b3Vec3 rnA = b3Cross(rA, n); - b3Vec3 rnB = b3Cross(rB, n); - - float32 K = mA + mB + b3Dot(iA * rnA, rnA) + b3Dot(iB * rnB, rnB); - - vc->normalMass = K > 0.0f ? 1.0f / K : 0.0f; - vc->velocityBias = 0.0f; + vc->normalMass = mA > scalar(0) ? scalar(1) / mA : scalar(0); } { - b3Vec3 t1 = vc->tangent1; - b3Vec3 t2 = vc->tangent2; - - b3Vec3 rn1A = b3Cross(rA, t1); - b3Vec3 rn1B = b3Cross(rB, t1); - b3Vec3 rn2A = b3Cross(rA, t2); - b3Vec3 rn2B = b3Cross(rB, t2); - - // dot(t1, t2) = 0 - // J1_l1 * M1 * J2_l1 = J1_l2 * M2 * J2_l2 = 0 - float32 k11 = mA + mB + b3Dot(iA * rn1A, rn1A) + b3Dot(iB * rn1B, rn1B); - float32 k12 = b3Dot(iA * rn1A, rn2A) + b3Dot(iB * rn1B, rn2B); - float32 k22 = mA + mB + b3Dot(iA * rn2A, rn2A) + b3Dot(iB * rn2B, rn2B); - - b3Mat22 K; - K.x.Set(k11, k12); - K.y.Set(k12, k22); - - vc->tangentMass = b3Inverse(K); + vc->tangentMass = mA > scalar(0) ? scalar(1) / mA : scalar(0); } } } + void b3ClothContactSolver::InitializeTriangleContactConstraints() { for (u32 i = 0; i < m_triangleContactCount; ++i) { - b3ParticleTriangleContact* c = m_triangleContacts[i]; + b3ClothSphereAndTriangleContact* c = m_triangleContacts[i]; b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; b3ClothSolverTriangleContactPositionConstraint* pc = m_trianglePositionConstraints + i; - vc->indexA = c->m_p1->m_solverId; - vc->invMassA = c->m_p1->m_type == e_staticParticle ? 0.0f : c->m_p1->m_invMass; + b3ClothSphereShape* s1 = c->m_s1; + b3ClothParticle* p1 = s1->m_p; + + b3ClothTriangleShape* s2 = c->m_s2; + b3ClothParticle* p2 = s2->m_p1; + b3ClothParticle* p3 = s2->m_p2; + b3ClothParticle* p4 = s2->m_p3; - vc->indexB = c->m_p2->m_solverId; - vc->invMassB = c->m_p2->m_type == e_staticParticle ? 0.0f : c->m_p2->m_invMass; + vc->indexA = p1->m_solverId; + vc->invMassA = p1->m_invMass; - vc->indexC = c->m_p3->m_solverId; - vc->invMassC = c->m_p3->m_type == e_staticParticle ? 0.0f : c->m_p3->m_invMass; + vc->indexB = p2->m_solverId; + vc->invMassB = p2->m_invMass; - vc->indexD = c->m_p4->m_solverId; - vc->invMassD = c->m_p4->m_type == e_staticParticle ? 0.0f : c->m_p4->m_invMass; + vc->indexC = p3->m_solverId; + vc->invMassC = p3->m_invMass; - pc->indexA = c->m_p1->m_solverId; - pc->invMassA = c->m_p1->m_type == e_staticParticle ? 0.0f : c->m_p1->m_invMass; - pc->radiusA = c->m_p1->m_radius; + vc->indexD = p4->m_solverId; + vc->invMassD = p4->m_invMass; - pc->indexB = c->m_p2->m_solverId; - pc->invMassB = c->m_p2->m_type == e_staticParticle ? 0.0f : c->m_p2->m_invMass; + pc->indexA = p1->m_solverId; + pc->invMassA = p1->m_invMass; + pc->radiusA = s1->m_radius; - pc->indexC = c->m_p3->m_solverId; - pc->invMassC = c->m_p3->m_type == e_staticParticle ? 0.0f : c->m_p3->m_invMass; + pc->indexB = p2->m_solverId; + pc->invMassB = p2->m_invMass; - pc->indexD = c->m_p4->m_solverId; - pc->invMassD = c->m_p4->m_type == e_staticParticle ? 0.0f : c->m_p4->m_invMass; + pc->indexC = p3->m_solverId; + pc->invMassC = p3->m_invMass; - pc->triangleRadius = c->m_t2->m_radius; + pc->indexD = p4->m_solverId; + pc->invMassD = p4->m_invMass; - pc->wB = c->m_w2; - pc->wC = c->m_w3; - pc->wD = c->m_w4; + pc->triangleRadius = s2->m_radius; + + pc->wB = c->m_w1; + pc->wC = c->m_w2; + pc->wD = c->m_w3; u32 indexA = pc->indexA; - float32 mA = pc->invMassA; + scalar mA = pc->invMassA; u32 indexB = pc->indexB; - float32 mB = pc->invMassB; + scalar mB = pc->invMassB; u32 indexC = pc->indexC; - float32 mC = pc->invMassC; + scalar mC = pc->invMassC; u32 indexD = pc->indexD; - float32 mD = pc->invMassD; + scalar mD = pc->invMassD; b3Vec3 xA = m_positions[indexA]; b3Vec3 xB = m_positions[indexB]; b3Vec3 xC = m_positions[indexC]; b3Vec3 xD = m_positions[indexD]; - float32 wB = pc->wB; - float32 wC = pc->wC; - float32 wD = pc->wD; + scalar wB = pc->wB; + scalar wC = pc->wC; + scalar wD = pc->wD; - b3Vec3 cB = wB * xB + wC * xC + wD * xD; - - b3Vec3 n = cB - xA; - n.Normalize(); - - vc->normal = n; vc->wB = wB; vc->wC = wC; vc->wD = wD; - float32 K = mA + mB * wB * wB + mC * wC * wC + mD * wD * wD; + vc->normal = c->m_normal1; + vc->normalImpulse = m_step.dt_ratio * c->m_normalImpulse; - vc->normalMass = K > 0.0f ? 1.0f / K : 0.0f; - vc->normalImpulse = c->m_normalImpulse; + vc->friction = b3MixFriction(s1->m_friction, s2->m_friction); + vc->tangent1 = c->m_tangent1; + vc->tangent2 = c->m_tangent2; + vc->tangentImpulse = m_step.dt_ratio * c->m_tangentImpulse; - vc->friction = b3MixFriction(c->m_p1->m_friction, c->m_t2->m_friction); - vc->tangent1 = b3Perp(n); - vc->tangent2 = b3Cross(vc->tangent1, n); - vc->tangentMass1 = vc->normalMass; - vc->tangentMass2 = vc->normalMass; - vc->tangentImpulse1 = c->m_tangentImpulse1; - vc->tangentImpulse2 = c->m_tangentImpulse2; + { + scalar K = mA + mB * wB * wB + mC * wC * wC + mD * wD * wD; + + vc->normalMass = K > scalar(0) ? scalar(1) / K : scalar(0); + } + + { + // dot(t1, t2) = 0 + // w * w * dot(t1, t2) = 0 + // J * m * J = m * w * w + // k22 = k11 + // k21 = k12 = 0 + scalar k11 = mA + mB * wB * wB + mC * wC * wC + mD * wD * wD; + + scalar inv_k11 = k11 > scalar(0) ? scalar(1) / k11 : scalar(0); + + b3Mat22 invK; + invK.x.Set(inv_k11, scalar(0)); + invK.y.Set(scalar(0), inv_k11); + + vc->tangentMass = invK; + } } } -void b3ClothContactSolver::WarmStartBodyContactConstraints() +void b3ClothContactSolver::InitializeCapsuleContactConstraints() { - for (u32 i = 0; i < m_bodyContactCount; ++i) + for (u32 i = 0; i < m_capsuleContactCount; ++i) { - b3ClothSolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; + b3ClothCapsuleAndCapsuleContact* c = m_capsuleContacts[i]; + b3ClothSolverCapsuleContactVelocityConstraint* vc = m_capsuleVelocityConstraints + i; + b3ClothSolverCapsuleContactPositionConstraint* pc = m_capsulePositionConstraints + i; + + b3ClothCapsuleShape* s1 = c->m_s1; + b3ClothParticle* p1 = s1->m_p1; + b3ClothParticle* p2 = s1->m_p2; + + b3ClothCapsuleShape* s2 = c->m_s2; + b3ClothParticle* p3 = s2->m_p1; + b3ClothParticle* p4 = s2->m_p2; + + vc->indexA = p1->m_solverId; + vc->invMassA = p1->m_invMass; + + vc->indexB = p2->m_solverId; + vc->invMassB = p2->m_invMass; + + vc->indexC = p3->m_solverId; + vc->invMassC = p3->m_invMass; + + vc->indexD = p4->m_solverId; + vc->invMassD = p4->m_invMass; + + pc->indexA = p1->m_solverId; + pc->invMassA = p1->m_invMass; + + pc->indexB = p2->m_solverId; + pc->invMassB = p2->m_invMass; + + pc->radiusA = s1->m_radius; + + pc->indexC = p3->m_solverId; + pc->invMassC = p3->m_invMass; + + pc->indexD = p4->m_solverId; + pc->invMassD = p4->m_invMass; + + pc->radiusB = s2->m_radius; + + pc->wA = c->m_w1; + pc->wB = c->m_w2; + pc->wC = c->m_w3; + pc->wD = c->m_w4; + + u32 indexA = pc->indexA; + scalar mA = pc->invMassA; + + u32 indexB = pc->indexB; + scalar mB = pc->invMassB; + + u32 indexC = pc->indexC; + scalar mC = pc->invMassC; + + u32 indexD = pc->indexD; + scalar mD = pc->invMassD; + + b3Vec3 xA = m_positions[indexA]; + b3Vec3 xB = m_positions[indexB]; + b3Vec3 xC = m_positions[indexC]; + b3Vec3 xD = m_positions[indexD]; + + scalar wA = pc->wA; + scalar wB = pc->wB; + scalar wC = pc->wC; + scalar wD = pc->wD; + + vc->wA = wA; + vc->wB = wB; + vc->wC = wC; + vc->wD = wD; + + vc->normal = c->m_normal1; + vc->normalImpulse = m_step.dt_ratio * c->m_normalImpulse; + + vc->friction = b3MixFriction(s1->m_friction, s2->m_friction); + vc->tangent1 = c->m_tangent1; + vc->tangent2 = c->m_tangent2; + vc->tangentImpulse = m_step.dt_ratio * c->m_tangentImpulse; + + { + scalar K = mA * wA * wA + mB * wB * wB + mC * wC * wC + mD * wD * wD; + + vc->normalMass = K > scalar(0) ? scalar(1) / K : scalar(0); + } + + { + // dot(t1, t2) = 0 + // w * w * dot(t1, t2) = 0 + // J * m * J = m * w * w + // k22 = k11 + // k21 = k12 = 0 + scalar k11 = mA * wA * wA + mB * wB * wB + mC * wC * wC + mD * wD * wD; + + scalar inv_k11 = k11 > scalar(0) ? scalar(1) / k11 : scalar(0); + + b3Mat22 invK; + invK.x.Set(inv_k11, scalar(0)); + invK.y.Set(scalar(0), inv_k11); + + vc->tangentMass = invK; + } + } +} + +void b3ClothContactSolver::WarmStartShapeContactConstraints() +{ + for (u32 i = 0; i < m_shapeContactCount; ++i) + { + b3ClothSolverShapeContactVelocityConstraint* vc = m_shapeVelocityConstraints + i; u32 indexA = vc->indexA; - b3Body* bodyB = vc->bodyB; - b3Vec3 vA = m_velocities[indexA]; - b3Vec3 vB = bodyB->GetLinearVelocity(); - - b3Vec3 wA; wA.SetZero(); - b3Vec3 wB = bodyB->GetAngularVelocity(); - - float32 mA = vc->invMassA; - float32 mB = vc->invMassB; - - b3Mat33 iA = vc->invIA; - b3Mat33 iB = vc->invIB; + scalar mA = vc->invMassA; b3Vec3 P = vc->normalImpulse * vc->normal; - vA -= mA * P; - wA -= iA * b3Cross(vc->rA, P); - - vB += mB * P; - wB += iB * b3Cross(vc->rB, P); + vA += mA * P; b3Vec3 P1 = vc->tangentImpulse.x * vc->tangent1; b3Vec3 P2 = vc->tangentImpulse.y * vc->tangent2; - vA -= mA * (P1 + P2); - wA -= iA * b3Cross(vc->rA, P1 + P2); - - vB += mB * (P1 + P2); - wB += iB * b3Cross(vc->rB, P1 + P2); + vA += mA * (P1 + P2); m_velocities[indexA] = vA; - - bodyB->SetLinearVelocity(vB); - bodyB->SetAngularVelocity(wB); } } @@ -322,10 +373,10 @@ void b3ClothContactSolver::WarmStartTriangleContactConstraints() b3Vec3 vC = m_velocities[indexC]; b3Vec3 vD = m_velocities[indexD]; - float32 mA = vc->invMassA; - float32 mB = vc->invMassB; - float32 mC = vc->invMassC; - float32 mD = vc->invMassD; + scalar mA = vc->invMassA; + scalar mB = vc->invMassB; + scalar mC = vc->invMassC; + scalar mD = vc->invMassD; { b3Vec3 JA = -vc->normal; @@ -350,10 +401,10 @@ void b3ClothContactSolver::WarmStartTriangleContactConstraints() b3Vec3 JC = vc->wC * vc->tangent1; b3Vec3 JD = vc->wD * vc->tangent1; - b3Vec3 PA = vc->tangentImpulse1 * JA; - b3Vec3 PB = vc->tangentImpulse1 * JB; - b3Vec3 PC = vc->tangentImpulse1 * JC; - b3Vec3 PD = vc->tangentImpulse1 * JD; + b3Vec3 PA = vc->tangentImpulse.x * JA; + b3Vec3 PB = vc->tangentImpulse.x * JB; + b3Vec3 PC = vc->tangentImpulse.x * JC; + b3Vec3 PD = vc->tangentImpulse.x * JD; vA += mA * PA; vB += mB * PB; @@ -367,10 +418,10 @@ void b3ClothContactSolver::WarmStartTriangleContactConstraints() b3Vec3 JC = vc->wC * vc->tangent2; b3Vec3 JD = vc->wD * vc->tangent2; - b3Vec3 PA = vc->tangentImpulse2 * JA; - b3Vec3 PB = vc->tangentImpulse2 * JB; - b3Vec3 PC = vc->tangentImpulse2 * JC; - b3Vec3 PD = vc->tangentImpulse2 * JD; + b3Vec3 PA = vc->tangentImpulse.y * JA; + b3Vec3 PB = vc->tangentImpulse.y * JB; + b3Vec3 PC = vc->tangentImpulse.y * JC; + b3Vec3 PD = vc->tangentImpulse.y * JD; vA += mA * PA; vB += mB * PB; @@ -385,66 +436,123 @@ void b3ClothContactSolver::WarmStartTriangleContactConstraints() } } -void b3ClothContactSolver::SolveBodyContactVelocityConstraints() +void b3ClothContactSolver::WarmStartCapsuleContactConstraints() { - for (u32 i = 0; i < m_bodyContactCount; ++i) + for (u32 i = 0; i < m_capsuleContactCount; ++i) { - b3ClothSolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; + b3ClothSolverCapsuleContactVelocityConstraint* vc = m_capsuleVelocityConstraints + i; u32 indexA = vc->indexA; - b3Body* bodyB = vc->bodyB; + u32 indexB = vc->indexB; + u32 indexC = vc->indexC; + u32 indexD = vc->indexD; b3Vec3 vA = m_velocities[indexA]; - b3Vec3 vB = bodyB->GetLinearVelocity(); + b3Vec3 vB = m_velocities[indexB]; + b3Vec3 vC = m_velocities[indexC]; + b3Vec3 vD = m_velocities[indexD]; - b3Vec3 wA; wA.SetZero(); - b3Vec3 wB = bodyB->GetAngularVelocity(); + scalar mA = vc->invMassA; + scalar mB = vc->invMassB; + scalar mC = vc->invMassC; + scalar mD = vc->invMassD; - float32 mA = vc->invMassA; - float32 mB = vc->invMassB; + { + b3Vec3 JA = -vc->wA * vc->normal; + b3Vec3 JB = -vc->wB * vc->normal; + b3Vec3 JC = vc->wC * vc->normal; + b3Vec3 JD = vc->wD * vc->normal; - b3Mat33 iA = vc->invIA; - b3Mat33 iB = vc->invIB; + b3Vec3 PA = vc->normalImpulse * JA; + b3Vec3 PB = vc->normalImpulse * JB; + b3Vec3 PC = vc->normalImpulse * JC; + b3Vec3 PD = vc->normalImpulse * JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } + + { + b3Vec3 JA = -vc->wA * vc->tangent1; + b3Vec3 JB = -vc->wB * vc->tangent1; + b3Vec3 JC = vc->wC * vc->tangent1; + b3Vec3 JD = vc->wD * vc->tangent1; + + b3Vec3 PA = vc->tangentImpulse.x * JA; + b3Vec3 PB = vc->tangentImpulse.x * JB; + b3Vec3 PC = vc->tangentImpulse.x * JC; + b3Vec3 PD = vc->tangentImpulse.x * JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } + + { + b3Vec3 JA = -vc->wA * vc->tangent2; + b3Vec3 JB = -vc->wB * vc->tangent2; + b3Vec3 JC = vc->wC * vc->tangent2; + b3Vec3 JD = vc->wD * vc->tangent2; + + b3Vec3 PA = vc->tangentImpulse.y * JA; + b3Vec3 PB = vc->tangentImpulse.y * JB; + b3Vec3 PC = vc->tangentImpulse.y * JC; + b3Vec3 PD = vc->tangentImpulse.y * JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } + + m_velocities[indexA] = vA; + m_velocities[indexB] = vB; + m_velocities[indexC] = vC; + m_velocities[indexD] = vD; + } +} + +void b3ClothContactSolver::SolveShapeContactVelocityConstraints() +{ + for (u32 i = 0; i < m_shapeContactCount; ++i) + { + b3ClothSolverShapeContactVelocityConstraint* vc = m_shapeVelocityConstraints + i; + + u32 indexA = vc->indexA; + b3Vec3 vA = m_velocities[indexA]; + scalar mA = vc->invMassA; b3Vec3 normal = vc->normal; - b3Vec3 point = vc->point; - - b3Vec3 rA = vc->rA; - b3Vec3 rB = vc->rB; // Solve normal constraint. { - b3Vec3 dv = vB + b3Cross(wB, rB) - vA - b3Cross(wA, rA); - float32 Cdot = b3Dot(normal, dv); + scalar Cdot = b3Dot(normal, vA); - float32 impulse = vc->normalMass * (-Cdot + vc->velocityBias); + scalar impulse = -vc->normalMass * Cdot; - float32 oldImpulse = vc->normalImpulse; - vc->normalImpulse = b3Max(vc->normalImpulse + impulse, 0.0f); + scalar oldImpulse = vc->normalImpulse; + vc->normalImpulse = b3Max(vc->normalImpulse + impulse, scalar(0)); impulse = vc->normalImpulse - oldImpulse; b3Vec3 P = impulse * normal; - vA -= mA * P; - wA -= iA * b3Cross(rA, P); - - vB += mB * P; - wB += iB * b3Cross(rB, P); + vA += mA * P; } // Solve tangent constraints. { - b3Vec3 dv = vB + b3Cross(wB, rB) - vA - b3Cross(wA, rA); - b3Vec2 Cdot; - Cdot.x = b3Dot(dv, vc->tangent1); - Cdot.y = b3Dot(dv, vc->tangent2); + Cdot.x = b3Dot(vA, vc->tangent1); + Cdot.y = b3Dot(vA, vc->tangent2); b3Vec2 impulse = vc->tangentMass * -Cdot; b3Vec2 oldImpulse = vc->tangentImpulse; vc->tangentImpulse += impulse; - float32 maxImpulse = vc->friction * vc->normalImpulse; + scalar maxImpulse = vc->friction * vc->normalImpulse; if (b3Dot(vc->tangentImpulse, vc->tangentImpulse) > maxImpulse * maxImpulse) { vc->tangentImpulse.Normalize(); @@ -457,17 +565,10 @@ void b3ClothContactSolver::SolveBodyContactVelocityConstraints() b3Vec3 P2 = impulse.y * vc->tangent2; b3Vec3 P = P1 + P2; - vA -= mA * P; - wA -= iA * b3Cross(rA, P); - - vB += mB * P; - wB += iB * b3Cross(rB, P); + vA += mA * P; } m_velocities[indexA] = vA; - - bodyB->SetLinearVelocity(vB); - bodyB->SetAngularVelocity(wB); } } @@ -478,20 +579,20 @@ void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; u32 indexA = vc->indexA; - float32 mA = vc->invMassA; + scalar mA = vc->invMassA; u32 indexB = vc->indexB; - float32 mB = vc->invMassB; + scalar mB = vc->invMassB; u32 indexC = vc->indexC; - float32 mC = vc->invMassC; + scalar mC = vc->invMassC; u32 indexD = vc->indexD; - float32 mD = vc->invMassD; + scalar mD = vc->invMassD; - float32 wB = vc->wB; - float32 wC = vc->wC; - float32 wD = vc->wD; + scalar wB = vc->wB; + scalar wC = vc->wC; + scalar wD = vc->wD; b3Vec3 vA = m_velocities[indexA]; b3Vec3 vB = m_velocities[indexB]; @@ -502,12 +603,12 @@ void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() { b3Vec3 vCB = wB * vB + wC * vC + wD * vD; - float32 Cdot = b3Dot(vCB - vA, vc->normal); + scalar Cdot = b3Dot(vCB - vA, vc->normal); - float32 impulse = -vc->normalMass * Cdot; + scalar impulse = -vc->normalMass * Cdot; - float32 oldImpulse = vc->normalImpulse; - vc->normalImpulse = b3Max(vc->normalImpulse + impulse, 0.0f); + scalar oldImpulse = vc->normalImpulse; + vc->normalImpulse = b3Max(vc->normalImpulse + impulse, scalar(0)); impulse = vc->normalImpulse - oldImpulse; b3Vec3 JA = -vc->normal; @@ -526,25 +627,116 @@ void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() vD += mD * PD; } - // Solve tangent constraint. + // Solve tangent constraints. { - float32 hi = vc->friction * vc->normalImpulse; - float32 lo = -hi; - b3Vec3 vCB = wB * vB + wC * vC + wD * vD; - float32 Cdot = b3Dot(vCB - vA, vc->tangent1); + b3Vec3 dv = vCB - vA; - float32 impulse = -vc->tangentMass1 * Cdot; + b3Vec2 Cdot; + Cdot.x = b3Dot(dv, vc->tangent1); + Cdot.y = b3Dot(dv, vc->tangent2); - float32 oldImpulse = vc->tangentImpulse1; - vc->tangentImpulse1 = b3Clamp(vc->tangentImpulse1 + impulse, lo, hi); - impulse = vc->tangentImpulse1 - oldImpulse; + b3Vec2 impulse = vc->tangentMass * -Cdot; + b3Vec2 oldImpulse = vc->tangentImpulse; + vc->tangentImpulse += impulse; - b3Vec3 JA = -vc->tangent1; - b3Vec3 JB = wB * vc->tangent1; - b3Vec3 JC = wC * vc->tangent1; - b3Vec3 JD = wD * vc->tangent1; + scalar maxImpulse = vc->friction * vc->normalImpulse; + if (b3Dot(vc->tangentImpulse, vc->tangentImpulse) > maxImpulse * maxImpulse) + { + vc->tangentImpulse.Normalize(); + vc->tangentImpulse *= maxImpulse; + } + + impulse = vc->tangentImpulse - oldImpulse; + + { + b3Vec3 JA = -vc->tangent1; + b3Vec3 JB = wB * vc->tangent1; + b3Vec3 JC = wC * vc->tangent1; + b3Vec3 JD = wD * vc->tangent1; + + b3Vec3 PA = impulse.x * JA; + b3Vec3 PB = impulse.x * JB; + b3Vec3 PC = impulse.x * JC; + b3Vec3 PD = impulse.x * JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } + + { + b3Vec3 JA = -vc->tangent2; + b3Vec3 JB = wB * vc->tangent2; + b3Vec3 JC = wC * vc->tangent2; + b3Vec3 JD = wD * vc->tangent2; + + b3Vec3 PA = impulse.y * JA; + b3Vec3 PB = impulse.y * JB; + b3Vec3 PC = impulse.y * JC; + b3Vec3 PD = impulse.y * JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } + } + + m_velocities[indexA] = vA; + m_velocities[indexB] = vB; + m_velocities[indexC] = vC; + m_velocities[indexD] = vD; + } +} + +void b3ClothContactSolver::SolveCapsuleContactVelocityConstraints() +{ + for (u32 i = 0; i < m_capsuleContactCount; ++i) + { + b3ClothSolverCapsuleContactVelocityConstraint* vc = m_capsuleVelocityConstraints + i; + + u32 indexA = vc->indexA; + scalar mA = vc->invMassA; + + u32 indexB = vc->indexB; + scalar mB = vc->invMassB; + + u32 indexC = vc->indexC; + scalar mC = vc->invMassC; + + u32 indexD = vc->indexD; + scalar mD = vc->invMassD; + + scalar wA = vc->wA; + scalar wB = vc->wB; + scalar wC = vc->wC; + scalar wD = vc->wD; + + b3Vec3 vA = m_velocities[indexA]; + b3Vec3 vB = m_velocities[indexB]; + b3Vec3 vC = m_velocities[indexC]; + b3Vec3 vD = m_velocities[indexD]; + + // Solve normal constraint. + { + b3Vec3 vCA = wA * vA + wB * vB; + b3Vec3 vCB = wC * vC + wD * vD; + + scalar Cdot = b3Dot(vCB - vCA, vc->normal); + + scalar impulse = -vc->normalMass * Cdot; + + scalar oldImpulse = vc->normalImpulse; + vc->normalImpulse = b3Max(vc->normalImpulse + impulse, scalar(0)); + impulse = vc->normalImpulse - oldImpulse; + + b3Vec3 JA = -wA * vc->normal; + b3Vec3 JB = -wB * vc->normal; + b3Vec3 JC = wC * vc->normal; + b3Vec3 JD = wD * vc->normal; b3Vec3 PA = impulse * JA; b3Vec3 PB = impulse * JB; @@ -557,35 +749,63 @@ void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() vD += mD * PD; } - // Solve bitangent constraint. + // Solve tangent constraints. { - float32 hi = vc->friction * vc->normalImpulse; - float32 lo = -hi; + b3Vec3 vCA = wA * vA + wB * vB; + b3Vec3 vCB = wC * vC + wD * vD; - b3Vec3 vCB = wB * vB + wC * vC + wD * vD; + b3Vec3 dv = vCB - vCA; - float32 Cdot = b3Dot(vCB - vA, vc->tangent2); + b3Vec2 Cdot; + Cdot.x = b3Dot(dv, vc->tangent1); + Cdot.y = b3Dot(dv, vc->tangent2); - float32 impulse = -vc->tangentMass2 * Cdot; + b3Vec2 impulse = vc->tangentMass * -Cdot; + b3Vec2 oldImpulse = vc->tangentImpulse; + vc->tangentImpulse += impulse; - float32 oldImpulse = vc->tangentImpulse2; - vc->tangentImpulse2 = b3Clamp(vc->tangentImpulse2 + impulse, lo, hi); - impulse = vc->tangentImpulse2 - oldImpulse; + scalar maxImpulse = vc->friction * vc->normalImpulse; + if (b3Dot(vc->tangentImpulse, vc->tangentImpulse) > maxImpulse * maxImpulse) + { + vc->tangentImpulse.Normalize(); + vc->tangentImpulse *= maxImpulse; + } - b3Vec3 JA = -vc->tangent2; - b3Vec3 JB = wB * vc->tangent2; - b3Vec3 JC = wC * vc->tangent2; - b3Vec3 JD = wD * vc->tangent2; + impulse = vc->tangentImpulse - oldImpulse; - b3Vec3 PA = impulse * JA; - b3Vec3 PB = impulse * JB; - b3Vec3 PC = impulse * JC; - b3Vec3 PD = impulse * JD; + { + b3Vec3 JA = -wA * vc->tangent1; + b3Vec3 JB = -wB * vc->tangent1; + b3Vec3 JC = wC * vc->tangent1; + b3Vec3 JD = wD * vc->tangent1; - vA += mA * PA; - vB += mB * PB; - vC += mC * PC; - vD += mD * PD; + b3Vec3 PA = impulse.x * JA; + b3Vec3 PB = impulse.x * JB; + b3Vec3 PC = impulse.x * JC; + b3Vec3 PD = impulse.x * JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } + + { + b3Vec3 JA = -wA * vc->tangent2; + b3Vec3 JB = -wB * vc->tangent2; + b3Vec3 JC = wC * vc->tangent2; + b3Vec3 JD = wD * vc->tangent2; + + b3Vec3 PA = impulse.y * JA; + b3Vec3 PB = impulse.y * JB; + b3Vec3 PC = impulse.y * JC; + b3Vec3 PD = impulse.y * JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } } m_velocities[indexA] = vA; @@ -597,10 +817,10 @@ void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() void b3ClothContactSolver::StoreImpulses() { - for (u32 i = 0; i < m_bodyContactCount; ++i) + for (u32 i = 0; i < m_shapeContactCount; ++i) { - b3ParticleBodyContact* c = m_bodyContacts[i]; - b3ClothSolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; + b3ClothSphereAndShapeContact* c = m_shapeContacts[i]; + b3ClothSolverShapeContactVelocityConstraint* vc = m_shapeVelocityConstraints + i; c->m_normalImpulse = vc->normalImpulse; c->m_tangentImpulse = vc->tangentImpulse; @@ -608,141 +828,111 @@ void b3ClothContactSolver::StoreImpulses() for (u32 i = 0; i < m_triangleContactCount; ++i) { - b3ParticleTriangleContact* c = m_triangleContacts[i]; + b3ClothSphereAndTriangleContact* c = m_triangleContacts[i]; b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; c->m_normalImpulse = vc->normalImpulse; - c->m_tangentImpulse1 = vc->tangentImpulse1; - c->m_tangentImpulse2 = vc->tangentImpulse2; + c->m_tangentImpulse = vc->tangentImpulse; + } + + for (u32 i = 0; i < m_capsuleContactCount; ++i) + { + b3ClothCapsuleAndCapsuleContact* c = m_capsuleContacts[i]; + b3ClothSolverCapsuleContactVelocityConstraint* vc = m_capsuleVelocityConstraints + i; + + c->m_normalImpulse = vc->normalImpulse; + c->m_tangentImpulse = vc->tangentImpulse; } } struct b3ClothSolverBodyContactSolverPoint { - void Initialize(const b3ClothSolverBodyContactPositionConstraint* pc, const b3Transform& xfA, const b3Transform& xfB) + void Initialize(const b3ClothSolverShapeContactPositionConstraint* pc, const b3Vec3 cA) { - b3Vec3 nA = pc->normalA; + b3Vec3 nB = pc->normal; + b3Vec3 cB = pc->pointB; - b3Vec3 cA = b3Mul(xfA, pc->localPointA); - b3Vec3 cB = b3Mul(xfB, pc->localPointB); - - float32 rA = pc->radiusA; - float32 rB = pc->radiusB; + scalar rA = pc->radiusA; + scalar rB = pc->radiusB; - b3Vec3 pA = cA + rA * nA; - b3Vec3 pB = cB - rB * nA; + b3Vec3 pA = cA - rA * nB; + b3Vec3 pB = cB + rB * nB; point = cB; - normal = nA; - separation = b3Dot(cB - cA, nA) - rA - rB; + normal = nB; + separation = b3Dot(cA - cB, nB) - rA - rB; } b3Vec3 normal; b3Vec3 point; - float32 separation; + scalar separation; }; -bool b3ClothContactSolver::SolveBodyContactPositionConstraints() +bool b3ClothContactSolver::SolveShapeContactPositionConstraints() { - float32 minSeparation = 0.0f; + scalar minSeparation(0); - for (u32 i = 0; i < m_bodyContactCount; ++i) + for (u32 i = 0; i < m_shapeContactCount; ++i) { - b3ClothSolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; + b3ClothSolverShapeContactPositionConstraint* pc = m_shapePositionConstraints + i; u32 indexA = pc->indexA; - float32 mA = pc->invMassA; - b3Mat33 iA = pc->invIA; - b3Vec3 localCenterA = pc->localCenterA; - - b3Body* bodyB = pc->bodyB; - float32 mB = pc->invMassB; - b3Mat33 iB = pc->invIB; - b3Vec3 localCenterB = pc->localCenterB; + scalar mA = pc->invMassA; b3Vec3 cA = m_positions[indexA]; - b3Quat qA; qA.SetIdentity(); - - b3Vec3 cB = bodyB->m_sweep.worldCenter; - b3Quat qB = bodyB->m_sweep.orientation; // Solve normal constraint - b3Transform xfA; - xfA.rotation = b3QuatMat33(qA); - xfA.position = cA - b3Mul(xfA.rotation, localCenterA); - - b3Transform xfB; - xfB.rotation = b3QuatMat33(qB); - xfB.position = cB - b3Mul(xfB.rotation, localCenterB); - b3ClothSolverBodyContactSolverPoint cpcp; - cpcp.Initialize(pc, xfA, xfB); + cpcp.Initialize(pc, cA); b3Vec3 normal = cpcp.normal; b3Vec3 point = cpcp.point; - float32 separation = cpcp.separation; + scalar separation = cpcp.separation; // Update max constraint error. minSeparation = b3Min(minSeparation, separation); // Allow some slop and prevent large corrections. - float32 C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, 0.0f); - - // Compute effective mass. - b3Vec3 rA = point - cA; - b3Vec3 rB = point - cB; - - b3Vec3 rnA = b3Cross(rA, normal); - b3Vec3 rnB = b3Cross(rB, normal); - float32 K = mA + mB + b3Dot(rnA, iA * rnA) + b3Dot(rnB, iB * rnB); + scalar C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, scalar(0)); // Compute normal impulse. - float32 impulse = K > 0.0f ? -C / K : 0.0f; + scalar impulse = mA > scalar(0) ? -C / mA : scalar(0); b3Vec3 P = impulse * normal; - cA -= mA * P; - qA -= b3Derivative(qA, iA * b3Cross(rA, P)); - qA.Normalize(); - - cB += mB * P; - qB += b3Derivative(qB, iB * b3Cross(rB, P)); - qB.Normalize(); + cA += mA * P; m_positions[indexA] = cA; - - bodyB->m_sweep.worldCenter = cB; - bodyB->m_sweep.orientation = qB; } - return minSeparation >= -3.0f * B3_LINEAR_SLOP; + return minSeparation >= scalar(-3.0) * B3_LINEAR_SLOP; } bool b3ClothContactSolver::SolveTriangleContactPositionConstraints() { - float32 minSeparation = 0.0f; + scalar minSeparation(0); for (u32 i = 0; i < m_triangleContactCount; ++i) { b3ClothSolverTriangleContactPositionConstraint* pc = m_trianglePositionConstraints + i; u32 indexA = pc->indexA; - float32 mA = pc->invMassA; - float32 radiusA = pc->radiusA; + scalar mA = pc->invMassA; + scalar radiusA = pc->radiusA; u32 indexB = pc->indexB; - float32 mB = pc->invMassB; + scalar mB = pc->invMassB; u32 indexC = pc->indexC; - float32 mC = pc->invMassC; + scalar mC = pc->invMassC; u32 indexD = pc->indexD; - float32 mD = pc->invMassD; + scalar mD = pc->invMassD; - float32 triangleRadius = pc->triangleRadius; + scalar triangleRadius = pc->triangleRadius; - float32 wB = pc->wB; - float32 wC = pc->wC; - float32 wD = pc->wD; + scalar wB = pc->wB; + scalar wC = pc->wC; + scalar wD = pc->wD; b3Vec3 xA = m_positions[indexA]; b3Vec3 xB = m_positions[indexB]; @@ -753,15 +943,15 @@ bool b3ClothContactSolver::SolveTriangleContactPositionConstraints() b3Vec3 n = cB - xA; - float32 distance = n.Normalize(); + scalar distance = n.Normalize(); - float32 separation = distance - radiusA - triangleRadius; + scalar separation = distance - radiusA - triangleRadius; // Update max constraint error. minSeparation = b3Min(minSeparation, separation); // Allow some slop and prevent large corrections. - float32 C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, 0.0f); + scalar C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, scalar(0)); b3Vec3 JA = -n; b3Vec3 JB = wB * n; @@ -769,10 +959,10 @@ bool b3ClothContactSolver::SolveTriangleContactPositionConstraints() b3Vec3 JD = wD * n; // Compute effective mass. - float32 K = mA + mB * wB * wB + mC * wC * wC + mD * wD * wD; + scalar K = mA + mB * wB * wB + mC * wC * wC + mD * wD * wD; // Compute normal impulse. - float32 impulse = K > 0.0f ? -C / K : 0.0f; + scalar impulse = K > scalar(0) ? -C / K : scalar(0); b3Vec3 PA = impulse * JA; b3Vec3 PB = impulse * JB; @@ -790,5 +980,84 @@ bool b3ClothContactSolver::SolveTriangleContactPositionConstraints() m_positions[indexD] = xD; } - return minSeparation >= -3.0f * B3_LINEAR_SLOP; + return minSeparation >= scalar(-3.0) * B3_LINEAR_SLOP; +} + +bool b3ClothContactSolver::SolveCapsuleContactPositionConstraints() +{ + scalar minSeparation(0); + + for (u32 i = 0; i < m_capsuleContactCount; ++i) + { + b3ClothSolverCapsuleContactPositionConstraint* pc = m_capsulePositionConstraints + i; + + u32 indexA = pc->indexA; + scalar mA = pc->invMassA; + + u32 indexB = pc->indexB; + scalar mB = pc->invMassB; + + scalar radiusA = pc->radiusA; + + u32 indexC = pc->indexC; + scalar mC = pc->invMassC; + + u32 indexD = pc->indexD; + scalar mD = pc->invMassD; + + scalar radiusB = pc->radiusB; + + scalar wA = pc->wA; + scalar wB = pc->wB; + scalar wC = pc->wC; + scalar wD = pc->wD; + + b3Vec3 xA = m_positions[indexA]; + b3Vec3 xB = m_positions[indexB]; + b3Vec3 xC = m_positions[indexC]; + b3Vec3 xD = m_positions[indexD]; + + b3Vec3 cA = wA * xA + wB * xB; + b3Vec3 cB = wC * xC + wD * xD; + + b3Vec3 n = cB - cA; + + scalar distance = n.Normalize(); + + scalar separation = distance - radiusA - radiusB; + + // Update max constraint error. + minSeparation = b3Min(minSeparation, separation); + + // Allow some slop and prevent large corrections. + scalar C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, scalar(0)); + + b3Vec3 JA = -wA * n; + b3Vec3 JB = -wB * n; + b3Vec3 JC = wC * n; + b3Vec3 JD = wD * n; + + // Compute effective mass. + scalar K = mA * wA * wA + mB * wB * wB + mC * wC * wC + mD * wD * wD; + + // Compute normal impulse. + scalar impulse = K > scalar(0) ? -C / K : scalar(0); + + b3Vec3 PA = impulse * JA; + b3Vec3 PB = impulse * JB; + b3Vec3 PC = impulse * JC; + b3Vec3 PD = impulse * JD; + + xA += mA * PA; + xB += mB * PB; + xC += mC * PC; + xD += mD * PD; + + m_positions[indexA] = xA; + m_positions[indexB] = xB; + m_positions[indexC] = xC; + m_positions[indexD] = xD; + } + + return minSeparation >= scalar(-3.0) * B3_LINEAR_SLOP; } \ No newline at end of file diff --git a/src/bounce/cloth/contacts/cloth_particle_body_contact.cpp b/src/bounce/cloth/contacts/cloth_sphere_shape_contact.cpp similarity index 56% rename from src/bounce/cloth/contacts/cloth_particle_body_contact.cpp rename to src/bounce/cloth/contacts/cloth_sphere_shape_contact.cpp index fb580f9..a8a3893 100644 --- a/src/bounce/cloth/contacts/cloth_particle_body_contact.cpp +++ b/src/bounce/cloth/contacts/cloth_sphere_shape_contact.cpp @@ -16,47 +16,47 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include +#include +#include +#include +#include #include #include -void b3ParticleBodyContact::Update() +void b3ClothSphereAndShapeContact::Update() { + m_active = false; + b3Sphere sphere; - sphere.radius = m_p1->m_radius; - sphere.vertex = m_p1->m_position; + sphere.vertex = m_s1->m_p->m_position; + sphere.radius = m_s1->m_radius; - b3Shape* shape = m_s2; - b3Body* body = shape->GetBody(); + const b3Shape* shape = m_s2->m_shape; + const b3Body* body = shape->GetBody(); b3Transform xf = body->GetTransform(); b3TestSphereOutput out; if (shape->TestSphere(&out, sphere, xf) == false) { - m_active = false; return; } m_active = true; - m_normal1 = -out.normal; - m_localPoint1.SetZero(); - m_localPoint2 = body->GetLocalPoint(out.point); - m_tangent1 = b3Perp(m_normal1); - m_tangent2 = b3Cross(m_tangent1, m_normal1); + m_normal2 = out.normal; + m_point2 = out.point; + m_tangent1 = b3Perp(m_normal2); + m_tangent2 = b3Cross(m_tangent1, m_normal2); } -void b3ParticleBodyContactWorldPoint::Initialize(const b3ParticleBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) +void b3ClothSphereAndShapeContactWorldPoint::Initialize(const b3ClothSphereAndShapeContact* c, scalar rA, const b3Vec3& cA, scalar rB) { - b3Vec3 nA = c->m_normal1; + b3Vec3 nB = c->m_normal2; + b3Vec3 cB = c->m_point2; - b3Vec3 cA = xfA * c->m_localPoint1; - b3Vec3 cB = xfB * c->m_localPoint2; + b3Vec3 pA = cA - rA * nB; + b3Vec3 pB = cB + rB * nB; - b3Vec3 pA = cA + rA * nA; - b3Vec3 pB = cB - rB * nA; - - point = 0.5f * (pA + pB); - normal = nA; - separation = b3Dot(cB - cA, nA) - rA - rB; -} + point = scalar(0.5) * (pA + pB); + normal = nB; + separation = b3Dot(cA - cB, nB) - rA - rB; +} \ No newline at end of file diff --git a/src/bounce/cloth/contacts/cloth_particle_triangle_contact.cpp b/src/bounce/cloth/contacts/cloth_sphere_triangle_contact.cpp similarity index 51% rename from src/bounce/cloth/contacts/cloth_particle_triangle_contact.cpp rename to src/bounce/cloth/contacts/cloth_sphere_triangle_contact.cpp index 64d4073..33a4bcd 100644 --- a/src/bounce/cloth/contacts/cloth_particle_triangle_contact.cpp +++ b/src/bounce/cloth/contacts/cloth_sphere_triangle_contact.cpp @@ -16,156 +16,159 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include -#include -#include +#include +#include +#include +#include #include // Solve constrained Barycentric coordinates for point Q -static void b3Solve3(float32 out[3], +static void b3Solve3(scalar out[3], const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, const b3Vec3& Q) { // Test vertex regions - float32 wAB[3], wBC[3], wCA[3]; + scalar wAB[3], wBC[3], wCA[3]; b3BarycentricCoordinates(wAB, A, B, Q); b3BarycentricCoordinates(wBC, B, C, Q); b3BarycentricCoordinates(wCA, C, A, Q); // R A - if (wAB[1] <= 0.0f && wCA[0] <= 0.0f) + if (wAB[1] <= scalar(0) && wCA[0] <= scalar(0)) { - out[0] = 1.0f; - out[1] = 0.0f; - out[2] = 0.0f; + out[0] = scalar(1); + out[1] = scalar(0); + out[2] = scalar(0); return; } // R B - if (wAB[0] <= 0.0f && wBC[1] <= 0.0f) + if (wAB[0] <= scalar(0) && wBC[1] <= scalar(0)) { - out[0] = 0.0f; - out[1] = 1.0f; - out[2] = 0.0f; + out[0] = scalar(0); + out[1] = scalar(1); + out[2] = scalar(0); return; } // R C - if (wBC[0] <= 0.0f && wCA[1] <= 0.0f) + if (wBC[0] <= scalar(0) && wCA[1] <= scalar(0)) { - out[0] = 0.0f; - out[1] = 0.0f; - out[2] = 1.0f; + out[0] = scalar(0); + out[1] = scalar(0); + out[2] = scalar(1); return; } // Test edge regions - float32 wABC[4]; + scalar wABC[4]; b3BarycentricCoordinates(wABC, A, B, C, Q); // R AB - if (wAB[0] > 0.0f && wAB[1] > 0.0f && wABC[3] * wABC[2] <= 0.0f) + if (wAB[0] > scalar(0) && wAB[1] > scalar(0) && wABC[3] * wABC[2] <= scalar(0)) { - float32 divisor = wAB[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wAB[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; out[0] = s * wAB[0]; out[1] = s * wAB[1]; - out[2] = 0.0f; + out[2] = scalar(0); return; } // R BC - if (wBC[0] > 0.0f && wBC[1] > 0.0f && wABC[3] * wABC[0] <= 0.0f) + if (wBC[0] > scalar(0) && wBC[1] > scalar(0) && wABC[3] * wABC[0] <= scalar(0)) { - float32 divisor = wBC[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; - out[0] = 0.0f; + scalar divisor = wBC[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; + out[0] = scalar(0); out[1] = s * wBC[0]; out[2] = s * wBC[1]; return; } // R CA - if (wCA[0] > 0.0f && wCA[1] > 0.0f && wABC[3] * wABC[1] <= 0.0f) + if (wCA[0] > scalar(0) && wCA[1] > scalar(0) && wABC[3] * wABC[1] <= scalar(0)) { - float32 divisor = wCA[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wCA[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; out[0] = s * wCA[1]; - out[1] = 0.0f; + out[1] = scalar(0); out[2] = s * wCA[0]; return; } // R ABC/ACB - float32 divisor = wABC[3]; - if (divisor == 0.0f) + scalar divisor = wABC[3]; + if (divisor == scalar(0)) { - float32 s = 1.0f / 3.0f; + scalar s = scalar(1) / scalar(3); out[0] = s; out[1] = s; out[2] = s; return; } - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; out[0] = s * wABC[0]; out[1] = s * wABC[1]; out[2] = s * wABC[2]; } -void b3ParticleTriangleContact::Update() +void b3ClothSphereAndTriangleContact::Update() { - b3Vec3 A = m_p2->m_position; - b3Vec3 B = m_p3->m_position; - b3Vec3 C = m_p4->m_position; + m_active = false; + + b3ClothParticle* p1 = m_s1->m_p; + b3ClothParticle* p2 = m_s2->m_p1; + b3ClothParticle* p3 = m_s2->m_p2; + b3ClothParticle* p4 = m_s2->m_p3; + + b3Vec3 A = p2->m_position; + b3Vec3 B = p3->m_position; + b3Vec3 C = p4->m_position; b3Vec3 N = b3Cross(B - A, C - A); - float32 len = N.Normalize(); + scalar len = N.Normalize(); // Is ABC degenerate? - if (len == 0.0f) + if (len == scalar(0)) { - m_active = false; return; } - float32 r1 = m_p1->m_radius; - float32 r2 = m_t2->m_radius; + scalar r1 = m_s1->m_radius; + scalar r2 = m_s2->m_radius; - float32 totalRadius = r1 + r2; + scalar totalRadius = r1 + r2; - b3Vec3 P1 = m_p1->m_position; + b3Vec3 P1 = p1->m_position; - float32 distance = b3Dot(N, P1 - A); + scalar distance = b3Dot(N, P1 - A); // Is P1 below the plane? if (distance < -totalRadius) { - m_active = false; return; } // Is P1 above the plane? if (distance > totalRadius) { - m_active = false; return; } // Closest point on ABC to P1 - float32 wABC[3]; + scalar wABC[3]; b3Solve3(wABC, A, B, C, P1); b3Vec3 P2 = wABC[0] * A + wABC[1] * B + wABC[2] * C; if (b3DistanceSquared(P1, P2) > totalRadius * totalRadius) { - m_active = false; return; } @@ -173,7 +176,17 @@ void b3ParticleTriangleContact::Update() m_active = true; // Store Barycentric coordinates for P1 - m_w2 = wABC[0]; - m_w3 = wABC[1]; - m_w4 = wABC[2]; + m_w1 = wABC[0]; + m_w2 = wABC[1]; + m_w3 = wABC[2]; + + b3Vec3 n = P2 - P1; + if (n.Normalize() < B3_EPSILON) + { + n = N; + } + + m_normal1 = n; + m_tangent1 = b3Perp(m_normal1); + m_tangent2 = b3Cross(m_tangent1, m_normal1); } \ No newline at end of file diff --git a/src/bounce/cloth/forces/element_force.cpp b/src/bounce/cloth/forces/element_force.cpp new file mode 100644 index 0000000..844577d --- /dev/null +++ b/src/bounce/cloth/forces/element_force.cpp @@ -0,0 +1,377 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include + +// Compute the orthotropic elastic tensor given Young Modulus and Poisson's Ratio. +// This is a 3x3 matrix. +static B3_FORCE_INLINE b3Mat33 b3ComputeC(scalar Ex, scalar Ey, scalar Es, + scalar nu_xy, scalar nu_yx) +{ + scalar s = scalar(1) - nu_xy * nu_yx; + B3_ASSERT(s != scalar(0)); + + b3Mat33 C; + C.x.x = Ex / s; + C.x.y = Ey * nu_xy / s; + C.x.z = scalar(0); + + C.y.x = Ex * nu_yx / s; + C.y.y = Ey / s; + C.y.z = scalar(0); + + C.z.x = scalar(0); + C.z.y = scalar(0); + C.z.z = Es; + + return C; +} + +// Compute B = S * N, +// where S is the operational matrix and N are the shape functions. +// This is a 3 x 6 matrix. +// B = [dN1/dx 0 dN2/dx 0 dN3/dx 0] +// [0 dN1/dy 0 dN2/dy 0 dN3/dy] +// [dN1/dy dN1/dx dN2/dy dN2/dx dN3/dy dN3/dx] +// +// The shape functions are the Barycentric coordinates for point to a triangle in 2D: +// +// N_1 = (a1 + b1 * x + c1 * y) / 2*A +// N_2 = (a2 + b2 * x + c2 * y) / 2*A +// N_3 = (a3 + b3 * x + c3 * y) / 2*A +// +// a1 = x2 * y3 - x3 * y2 +// a2 = x3 * y1 - x1 * y3 +// a3 = x1 * y2 - x2 * y1 +// +// b1 = y2 - y3 +// b2 = y3 - y1 +// b3 = y1 - y2 +// +// c1 = x3 - x2 +// c2 = x1 - x3 +// c3 = x2 - x1 +static B3_FORCE_INLINE void b3ComputeB(scalar out[18], + const b3Vec2& p1, const b3Vec2& p2, const b3Vec2& p3, + scalar invA2) +{ + scalar x1 = p1.x, y1 = p1.y; + scalar x2 = p2.x, y2 = p2.y; + scalar x3 = p3.x, y3 = p3.y; + + scalar dN1_dx = invA2 * (y2 - y3); + scalar dN1_dy = invA2 * (x3 - x2); + + scalar dN2_dx = invA2 * (y3 - y1); + scalar dN2_dy = invA2 * (x1 - x3); + + scalar dN3_dx = invA2 * (y1 - y2); + scalar dN3_dy = invA2 * (x2 - x1); + + scalar B[18] + { + dN1_dx, 0, dN1_dy, + 0, dN1_dy, dN1_dx, + + dN2_dx, 0, dN2_dy, + 0, dN2_dy, dN2_dx, + + dN3_dx, 0, dN3_dy, + 0, dN3_dy, dN3_dx, + }; + + for (u32 i = 0; i < 18; ++i) + { + out[i] = B[i]; + } +} + +// Extract rotation from a matrix. +static B3_FORCE_INLINE b3Mat22 b3ExtractRotation(const b3Mat22& M) +{ + // Polar Decomposition + // https://research.cs.wisc.edu/graphics/Courses/838-s2002/Papers/polar-decomp.pdf + scalar m11 = M.x.x, m12 = M.y.x; + scalar m21 = M.x.y, m22 = M.y.y; + + scalar det = m11 * m22 - m12 * m21; + + b3Mat22 A; + A.x.x = M.y.y; + A.x.y = -M.y.x; + A.y.x = -M.x.y; + A.y.y = M.x.x; + + b3Mat22 Q = M + b3Sign(det) * A; + + Q.x.Normalize(); + Q.y.Normalize(); + + return Q; +} + +static B3_FORCE_INLINE scalar& b3GetElement(b3Mat22 K[9], u32 i, u32 j) +{ + B3_ASSERT(i < 6); + B3_ASSERT(j < 6); + + u32 i0 = i / 2; + u32 j0 = j / 2; + + b3Mat22& a = K[i0 + 3 * j0]; + + u32 ii = i - 2 * i0; + u32 jj = j - 2 * j0; + + return a(ii, jj); +} + +// Convert a 6x6 matrix to 2x2 block form. +static B3_FORCE_INLINE void b3SetK(b3Mat22 K[9], scalar Ke[36]) +{ + for (u32 i = 0; i < 6; ++i) + { + for (u32 j = 0; j < 6; ++j) + { + scalar k1 = Ke[i + 6 * j]; + scalar& k2 = b3GetElement(K, i, j); + + k2 = k1; + } + } +} + +b3ElementForce::b3ElementForce(const b3ElementForceDef* def) +{ + m_type = e_elementForce; + m_p1 = def->p1; + m_p2 = def->p2; + m_p3 = def->p3; + m_E_x = def->E_x; + m_E_y = def->E_y; + m_E_s = def->E_s; + m_nu_xy = def->nu_xy; + m_nu_yx = def->nu_yx; + + b3Vec3 p1 = def->v1; + b3Vec3 p2 = def->v2; + b3Vec3 p3 = def->v3; + + b3Vec3 n = b3Cross(p2 - p1, p3 - p1); + scalar A2 = n.Normalize(); + + // Create a basis B = [px py n] for the plane. + // B rotates vectors from plane space to world space. + b3Vec3 px, py; + b3ComputeBasis(n, px, py); + + // 2x3 + scalar P[6] = + { + px.x, py.x, + px.y, py.y, + px.z, py.z + }; + + b3Vec2 x1, x2, x3; + b3Mul(&x1.x, P, 2, 3, &p1.x, 3, 1); + b3Mul(&x2.x, P, 2, 3, &p2.x, 3, 1); + b3Mul(&x3.x, P, 2, 3, &p3.x, 3, 1); + + // Store initial positions in 2D + m_x1 = x1; + m_x2 = x2; + m_x3 = x3; + + b3Mat22 S; + S.x = x2 - x1; + S.y = x3 - x1; + + // S^-1 + B3_ASSERT(A2 > scalar(0)); + scalar invA2 = scalar(1) / A2; + m_invS = b3Inverse(S); + + // Area + m_A = scalar(0.5) * A2; + + // 3x3 + m_C = b3ComputeC(m_E_x, m_E_y, m_E_s, m_nu_xy, m_nu_yx); + + // 3x6 + b3ComputeB(m_B, x1, x2, x3, invA2); + + // 6x3 + scalar BT[18]; + b3Transpose(BT, m_B, 3, 6); + + // 6x3 + scalar BT_C[18]; + b3Mul(BT_C, BT, 6, 3, &m_C.x.x, 3, 3); + + // 6x6 + scalar K[36]; + b3Mul(K, BT_C, 6, 3, m_B, 3, 6); + for (u32 i = 0; i < 36; ++i) + { + K[i] *= m_A; + } + + // Convert to block form + b3SetK(m_K, K); +} + +b3ElementForce::~b3ElementForce() +{ + +} + +bool b3ElementForce::HasParticle(const b3ClothParticle* particle) const +{ + return m_p1 == particle || m_p2 == particle || m_p3 == particle; +} + +// https://animation.rwth-aachen.de/media/papers/2013-CAG-AdaptiveCloth.pdf +void b3ElementForce::Apply(const b3ClothForceSolverData* data) +{ + u32 i1 = m_p1->m_solverId; + u32 i2 = m_p2->m_solverId; + u32 i3 = m_p3->m_solverId; + + b3DenseVec3& p = *data->x; + b3DenseVec3& f = *data->f; + b3SparseMat33& dfdx = *data->dfdx; + + b3Vec3 p1 = p[i1]; + b3Vec3 p2 = p[i2]; + b3Vec3 p3 = p[i3]; + + b3Vec3 n = b3Cross(p2 - p1, p3 - p1); + n.Normalize(); + + b3Vec3 px, py; + b3ComputeBasis(n, px, py); + + // 2x3 + scalar P[6] = + { + px.x, py.x, + px.y, py.y, + px.z, py.z + }; + + // 3x2 + scalar PT[6] = + { + px.x, px.y, px.z, + py.x, py.y, py.z + }; + + // Project the positions to 2D + b3Vec2 x1, x2, x3; + b3Mul(&x1.x, P, 2, 3, &p1.x, 3, 1); + b3Mul(&x2.x, P, 2, 3, &p2.x, 3, 1); + b3Mul(&x3.x, P, 2, 3, &p3.x, 3, 1); + + b3Mat22 T; + T.x = x2 - x1; + T.y = x3 - x1; + + // Deformation gradient + b3Mat22 A = T * m_invS; + + // Extract rotation + b3Mat22 R = b3ExtractRotation(A); + + // Inverse rotation + b3Mat22 RT = b3Transpose(R); + + // 2D displacements in unrotated frame + b3Vec2 us[3]; + us[0] = RT * x1 - m_x1; + us[1] = RT * x2 - m_x2; + us[2] = RT * x3 - m_x3; + + // 2D forces in unrotated frame + b3Vec2 fs[3]; + + for (u32 i = 0; i < 3; ++i) + { + fs[i].SetZero(); + + for (u32 j = 0; j < 3; ++j) + { + b3Mat22 k = m_K[i + 3 * j]; + + fs[i] += k * us[j]; + } + } + + // Rotate the forces to deformed frame + fs[0] = R * fs[0]; + fs[1] = R * fs[1]; + fs[2] = R * fs[2]; + + // Project the forces to 3D + b3Vec3 f1, f2, f3; + b3Mul(&f1.x, PT, 3, 2, &fs[0].x, 2, 1); + b3Mul(&f2.x, PT, 3, 2, &fs[1].x, 2, 1); + b3Mul(&f3.x, PT, 3, 2, &fs[2].x, 2, 1); + + // Negate f + f[i1] -= f1; + f[i2] -= f2; + f[i3] -= f3; + + // 3D corotated stiffness matrix + u32 vs[3] = { i1, i2, i3 }; + + for (u32 i = 0; i < 3; ++i) + { + u32 vi = vs[i]; + + for (u32 j = 0; j < 3; ++j) + { + u32 vj = vs[j]; + + // 2D corotated stiffness matrix + b3Mat22 k = R * m_K[i + 3 * j] * RT; + + // In the paper, Jan uses P^T * R. + // Here, I use + // K = P^T * k * P + // We can do both ways, but + // the latter is more practical than the former. + + // P^T * k = 3x2 * 2x2 = 3x2 + scalar PT_k[6]; + b3Mul(PT_k, PT, 3, 2, &k.x.x, 2, 2); + + // (P^T * k) * P = 3x2 * 2x3 = 3x3 + b3Mat33 Ke; + b3Mul(&Ke.x.x, PT_k, 3, 2, P, 2, 3); + + // Negate K + dfdx(vi, vj) -= Ke; + } + } +} \ No newline at end of file diff --git a/src/bounce/cloth/forces/force.cpp b/src/bounce/cloth/forces/force.cpp index c1368b5..26fb8e5 100644 --- a/src/bounce/cloth/forces/force.cpp +++ b/src/bounce/cloth/forces/force.cpp @@ -17,20 +17,21 @@ */ #include -#include +#include #include #include #include +#include b3Force* b3Force::Create(const b3ForceDef* def) { - b3Force* force = NULL; + b3Force* force = nullptr; switch (def->type) { - case e_strechForce: + case e_stretchForce: { - void* block = b3Alloc(sizeof(b3StrechForce)); - force = new (block) b3StrechForce((b3StrechForceDef*)def); + void* block = b3Alloc(sizeof(b3StretchForce)); + force = new (block) b3StretchForce((b3StretchForceDef*)def); break; } case e_shearForce: @@ -51,6 +52,12 @@ b3Force* b3Force::Create(const b3ForceDef* def) force = new (block) b3MouseForce((b3MouseForceDef*)def); break; } + case e_elementForce: + { + void* block = b3Alloc(sizeof(b3ElementForce)); + force = new (block) b3ElementForce((b3ElementForceDef*)def); + break; + } default: { B3_ASSERT(false); @@ -67,10 +74,10 @@ void b3Force::Destroy(b3Force* force) b3ForceType type = force->GetType(); switch (type) { - case e_strechForce: + case e_stretchForce: { - b3StrechForce* o = (b3StrechForce*)force; - o->~b3StrechForce(); + b3StretchForce* o = (b3StretchForce*)force; + o->~b3StretchForce(); b3Free(force); break; } @@ -95,6 +102,13 @@ void b3Force::Destroy(b3Force* force) b3Free(force); break; } + case e_elementForce: + { + b3ElementForce* o = (b3ElementForce*)force; + o->~b3ElementForce(); + b3Free(force); + break; + } default: { B3_ASSERT(false); diff --git a/src/bounce/cloth/forces/mouse_force.cpp b/src/bounce/cloth/forces/mouse_force.cpp index 51810b7..61f6fa9 100644 --- a/src/bounce/cloth/forces/mouse_force.cpp +++ b/src/bounce/cloth/forces/mouse_force.cpp @@ -17,10 +17,7 @@ */ #include -#include -#include -#include -#include +#include #include #include #include @@ -28,13 +25,16 @@ b3MouseForce::b3MouseForce(const b3MouseForceDef* def) { m_type = e_mouseForce; - m_particle = def->particle; - m_triangle = def->triangle; + m_p1 = def->p1; + m_p2 = def->p2; + m_p3 = def->p3; + m_p4 = def->p4; m_w2 = def->w2; m_w3 = def->w3; m_w4 = def->w4; m_km = def->mouse; m_kd = def->damping; + m_L0 = def->restLength; m_f1.SetZero(); m_f2.SetZero(); m_f3.SetZero(); @@ -46,34 +46,17 @@ b3MouseForce::~b3MouseForce() } -bool b3MouseForce::HasParticle(const b3Particle* particle) const +bool b3MouseForce::HasParticle(const b3ClothParticle* particle) const { - b3Cloth* cloth = m_triangle->m_cloth; - u32 triangleIndex = m_triangle->m_triangle; - b3ClothMeshTriangle* triangle = cloth->m_mesh->triangles + triangleIndex; - - b3Particle* p1 = cloth->m_particles[triangle->v1]; - b3Particle* p2 = cloth->m_particles[triangle->v2]; - b3Particle* p3 = cloth->m_particles[triangle->v3]; - - return m_particle == particle || p1 == particle || p2 == particle || p3 == particle; + return m_p1 == particle || m_p2 == particle || m_p3 == particle || m_p4 == particle; } void b3MouseForce::Apply(const b3ClothForceSolverData* data) { - b3Cloth* cloth = m_triangle->m_cloth; - u32 triangleIndex = m_triangle->m_triangle; - b3ClothMeshTriangle* triangle = cloth->m_mesh->triangles + triangleIndex; - - b3Particle* p1 = m_particle; - b3Particle* p2 = cloth->m_particles[triangle->v1]; - b3Particle* p3 = cloth->m_particles[triangle->v2]; - b3Particle* p4 = cloth->m_particles[triangle->v3]; - - u32 i1 = p1->m_solverId; - u32 i2 = p2->m_solverId; - u32 i3 = p3->m_solverId; - u32 i4 = p4->m_solverId; + u32 i1 = m_p1->m_solverId; + u32 i2 = m_p2->m_solverId; + u32 i3 = m_p3->m_solverId; + u32 i4 = m_p4->m_solverId; b3DenseVec3& x = *data->x; b3DenseVec3& v = *data->v; @@ -98,83 +81,121 @@ void b3MouseForce::Apply(const b3ClothForceSolverData* data) m_f3.SetZero(); m_f4.SetZero(); - b3Vec3 c2 = m_w2 * x2 + m_w3 * x3 + m_w4 * x4; + scalar w2 = m_w2; + scalar w3 = m_w3; + scalar w4 = m_w4; + + b3Vec3 c2 = w2 * x2 + w3 * x3 + w4 * x4; b3Vec3 d = x1 - c2; - float32 len = b3Length(d); + scalar L = b3Length(d); - if (len > 0.0f) + if (L > scalar(0)) { - b3Vec3 n = d / len; + scalar inv_L = scalar(1) / L; + + b3Vec3 n = inv_L * d; // Jacobian b3Vec3 dCdx[4]; dCdx[0] = n; - dCdx[1] = -m_w2 * n; - dCdx[2] = -m_w3 * n; - dCdx[3] = -m_w4 * n; + dCdx[1] = -w2 * n; + dCdx[2] = -w3 * n; + dCdx[3] = -w4 * n; - if (m_km > 0.0f) + if (m_km > scalar(0)) { - float32 C = len; - - // Force - b3Vec3 fs[4]; - for (u32 i = 0; i < 4; ++i) + if (L > m_L0) { - fs[i] = -m_km * C * dCdx[i]; - } + scalar C = L - m_L0; - m_f1 += fs[0]; - m_f2 += fs[1]; - m_f3 += fs[2]; - m_f4 += fs[3]; - - // Force derivative - b3Mat33 K[4][4]; - for (u32 i = 0; i < 4; ++i) - { - for (u32 j = 0; j < 4; ++j) + // Force + b3Vec3 fs[4]; + for (u32 i = 0; i < 4; ++i) { - //b3Mat33 d2Cvxij; - //b3Mat33 Kij = -m_km * (b3Outer(dCdx[i], dCdx[j]) + C * d2Cvxij); - b3Mat33 Kij = -m_km * b3Outer(dCdx[i], dCdx[j]); - - K[i][j] = Kij; + fs[i] = -m_km * C * dCdx[i]; } + + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + m_f4 += fs[3]; + + // Verification: + // http://www.matrixcalculus.org/ + + // Force derivative + scalar L3 = L * L * L; + + scalar inv_L3 = L3 > scalar(0) ? scalar(1) / L3 : scalar(0); + + b3Mat33 d2Cdxij[4][4]; + + b3Mat33 A = inv_L * I - inv_L3 * b3Outer(d, d); + + d2Cdxij[0][0] = A; + d2Cdxij[0][1] = -w2 * A; + d2Cdxij[0][2] = -w3 * A; + d2Cdxij[0][3] = -w4 * A; + + b3Mat33 B = inv_L3 * b3Outer(d, d) - inv_L * I; + + d2Cdxij[1][0] = w2 * B; + d2Cdxij[1][1] = -w2 * w2 * B; + d2Cdxij[1][2] = -w2 * w3 * B; + d2Cdxij[1][3] = -w2 * w4 * B; + + d2Cdxij[2][0] = w3 * B; + d2Cdxij[2][1] = -w2 * w3 * B; + d2Cdxij[2][2] = -w3 * w3 * B; + d2Cdxij[2][3] = -w3 * w4 * B; + + d2Cdxij[3][0] = w4 * B; + d2Cdxij[3][1] = -w2 * w4 * B; + d2Cdxij[3][2] = -w3 * w4 * B; + d2Cdxij[3][3] = -w4 * w4 * B; + + b3Mat33 K[4][4]; + for (u32 i = 0; i < 4; ++i) + { + for (u32 j = 0; j < 4; ++j) + { + b3Mat33 d2Cdx = d2Cdxij[i][j]; + + b3Mat33 Kij = -m_km * (b3Outer(dCdx[i], dCdx[j]) + C * d2Cdx); + + K[i][j] = Kij; + } + } + + dfdx(i1, i1) += K[0][0]; + dfdx(i1, i2) += K[0][1]; + dfdx(i1, i3) += K[0][2]; + dfdx(i1, i4) += K[0][3]; + + dfdx(i2, i1) += K[1][0]; + dfdx(i2, i2) += K[1][1]; + dfdx(i2, i3) += K[1][2]; + dfdx(i2, i4) += K[1][3]; + + dfdx(i3, i1) += K[2][0]; + dfdx(i3, i2) += K[2][1]; + dfdx(i3, i3) += K[2][2]; + dfdx(i3, i4) += K[2][3]; + + dfdx(i4, i1) += K[3][0]; + dfdx(i4, i2) += K[3][1]; + dfdx(i4, i3) += K[3][2]; + dfdx(i4, i4) += K[3][3]; } - - dfdx(i1, i1) += K[0][0]; - dfdx(i1, i2) += K[0][1]; - dfdx(i1, i3) += K[0][2]; - dfdx(i1, i4) += K[0][3]; - - dfdx(i2, i1) += K[1][0]; - dfdx(i2, i2) += K[1][1]; - dfdx(i2, i3) += K[1][2]; - dfdx(i2, i4) += K[1][3]; - - dfdx(i3, i1) += K[2][0]; - dfdx(i3, i2) += K[2][1]; - dfdx(i3, i3) += K[2][2]; - dfdx(i3, i4) += K[2][3]; - - dfdx(i4, i1) += K[3][0]; - dfdx(i4, i2) += K[3][1]; - dfdx(i4, i3) += K[3][2]; - dfdx(i4, i4) += K[3][3]; } - - if (m_kd > 0.0f) + + if (m_kd > scalar(0)) { - b3Vec3 vs[4] = { v1, v2, v3, v4 }; - - float32 dCdt = 0.0f; - for (u32 i = 0; i < 4; ++i) - { - dCdt += b3Dot(dCdx[i], vs[i]); - } + b3Vec3 vc2 = m_w2 * v2 + m_w3 * v3 + m_w4 * v4; + scalar dCdt = b3Dot(v1 - vc2, n); + // Force b3Vec3 fs[4]; for (u32 i = 0; i < 4; ++i) diff --git a/src/bounce/cloth/forces/shear_force.cpp b/src/bounce/cloth/forces/shear_force.cpp index 07a35f4..2818da0 100644 --- a/src/bounce/cloth/forces/shear_force.cpp +++ b/src/bounce/cloth/forces/shear_force.cpp @@ -17,23 +17,82 @@ */ #include -#include -#include -#include -#include +#include #include #include #include +void b3ShearForceDef::Initialize(const b3Vec3& p1, const b3Vec3& p2, const b3Vec3& p3) +{ + b3Vec3 A = p1, B = p2, C = p3; + + b3Vec3 AB = B - A; + b3Vec3 AC = C - A; + + // (u, v) 1 + u1 = scalar(0); + v1 = scalar(0); + + // (u, v) 2 + u2 = b3Length(AB); + v2 = scalar(0); + + // (u, v) 3 + B3_ASSERT(u2 > scalar(0)); + b3Vec3 n_AB = AB / u2; + + // a = b * h / 2 + // h = (a * 2) / b + scalar a2 = b3Length(b3Cross(AB, AC)); + B3_ASSERT(a2 > scalar(0)); + + u3 = b3Dot(AC, n_AB); + v3 = a2 / u2; + + alpha = scalar(0.5) * a2; +} + b3ShearForce::b3ShearForce(const b3ShearForceDef* def) { m_type = e_shearForce; - m_triangle = def->triangle; + m_p1 = def->p1; + m_p2 = def->p2; + m_p3 = def->p3; m_ks = def->shearing; m_kd = def->damping; m_f1.SetZero(); m_f2.SetZero(); m_f3.SetZero(); + m_alpha = def->alpha; + + scalar u1 = def->u1, v1 = def->v1; + scalar u2 = def->u2, v2 = def->v2; + scalar u3 = def->u3, v3 = def->v3; + + // (u, v) matrix + scalar du1 = u2 - u1; + scalar dv1 = v2 - v1; + scalar du2 = u3 - u1; + scalar dv2 = v3 - v1; + + m_du1 = du1; + m_dv1 = dv1; + m_du2 = du2; + m_dv2 = dv2; + + scalar det = du1 * dv2 - du2 * dv1; + B3_ASSERT(det != scalar(0)); + m_inv_det = scalar(1) / det; + + scalar inv_det = m_inv_det; + + m_dwudx.x = inv_det * (dv1 - dv2); + m_dwudx.y = inv_det * dv2; + m_dwudx.z = -inv_det * dv1; + + m_dwvdx.x = inv_det * (du2 - du1); + m_dwvdx.y = -inv_det * du2; + m_dwvdx.z = inv_det * du1; } b3ShearForce::~b3ShearForce() @@ -41,39 +100,25 @@ b3ShearForce::~b3ShearForce() } -bool b3ShearForce::HasParticle(const b3Particle* particle) const +bool b3ShearForce::HasParticle(const b3ClothParticle* particle) const { - b3Cloth* cloth = m_triangle->m_cloth; - u32 triangleIndex = m_triangle->m_triangle; - b3ClothMeshTriangle* triangle = cloth->m_mesh->triangles + triangleIndex; - - b3Particle* p1 = cloth->m_particles[triangle->v1]; - b3Particle* p2 = cloth->m_particles[triangle->v2]; - b3Particle* p3 = cloth->m_particles[triangle->v3]; - - return p1 == particle || p2 == particle || p3 == particle; + return m_p1 == particle || m_p2 == particle || m_p3 == particle; } void b3ShearForce::Apply(const b3ClothForceSolverData* data) { - b3Cloth* cloth = m_triangle->m_cloth; - u32 triangleIndex = m_triangle->m_triangle; - b3ClothMeshTriangle* triangle = cloth->m_mesh->triangles + triangleIndex; + scalar alpha = m_alpha; + scalar du1 = m_du1; + scalar dv1 = m_dv1; + scalar du2 = m_du2; + scalar dv2 = m_dv2; + scalar inv_det = m_inv_det; + b3Vec3 dwudx = m_dwudx; + b3Vec3 dwvdx = m_dwvdx; - float32 alpha = m_triangle->m_alpha; - float32 du1 = m_triangle->m_du1; - float32 dv1 = m_triangle->m_dv1; - float32 du2 = m_triangle->m_du2; - float32 dv2 = m_triangle->m_dv2; - float32 inv_det = m_triangle->m_inv_det; - - b3Particle* p1 = cloth->m_particles[triangle->v1]; - b3Particle* p2 = cloth->m_particles[triangle->v2]; - b3Particle* p3 = cloth->m_particles[triangle->v3]; - - u32 i1 = p1->m_solverId; - u32 i2 = p2->m_solverId; - u32 i3 = p3->m_solverId; + u32 i1 = m_p1->m_solverId; + u32 i2 = m_p2->m_solverId; + u32 i3 = m_p3->m_solverId; b3DenseVec3& x = *data->x; b3DenseVec3& v = *data->v; @@ -97,16 +142,6 @@ void b3ShearForce::Apply(const b3ClothForceSolverData* data) b3Vec3 wu = inv_det * (dv2 * dx1 - dv1 * dx2); b3Vec3 wv = inv_det * (-du2 * dx1 + du1 * dx2); - b3Vec3 dwudx; - dwudx[0] = inv_det * (dv1 - dv2); - dwudx[1] = inv_det * dv2; - dwudx[2] = -inv_det * dv1; - - b3Vec3 dwvdx; - dwvdx[0] = inv_det * (du2 - du1); - dwvdx[1] = -inv_det * du2; - dwvdx[2] = inv_det * du1; - m_f1.SetZero(); m_f2.SetZero(); m_f3.SetZero(); @@ -118,9 +153,9 @@ void b3ShearForce::Apply(const b3ClothForceSolverData* data) dCdx[i] = alpha * (dwudx[i] * wv + dwvdx[i] * wu); } - if (m_ks > 0.0f) + if (m_ks > scalar(0)) { - float32 C = alpha * b3Dot(wu, wv); + scalar C = alpha * b3Dot(wu, wv); // Force b3Vec3 fs[3]; @@ -139,10 +174,9 @@ void b3ShearForce::Apply(const b3ClothForceSolverData* data) { for (u32 j = 0; j < 3; ++j) { - //b3Mat33 d2Cxij = alpha * (dwudx[i] * dwvdx[j] + dwudx[j] * dwvdx[i]) * I; - //b3Mat33 Kij = -m_ks * (b3Outer(dCdx[i], dCdx[j]) + C * d2Cxij); - b3Mat33 Kij = -m_ks * b3Outer(dCdx[i], dCdx[j]); - + b3Mat33 d2Cxij = alpha * (dwudx[i] * dwvdx[j] + dwudx[j] * dwvdx[i]) * I; + b3Mat33 Kij = -m_ks * (b3Outer(dCdx[i], dCdx[j]) + C * d2Cxij); + K[i][j] = Kij; } } @@ -160,11 +194,11 @@ void b3ShearForce::Apply(const b3ClothForceSolverData* data) dfdx(i3, i3) += K[2][2]; } - if (m_kd > 0.0f) + if (m_kd > scalar(0)) { b3Vec3 vs[3] = { v1, v2, v3 }; - float32 dCdt = 0.0f; + scalar dCdt = scalar(0); for (u32 i = 0; i < 3; ++i) { dCdt += b3Dot(dCdx[i], vs[i]); diff --git a/src/bounce/cloth/forces/spring_force.cpp b/src/bounce/cloth/forces/spring_force.cpp index a037254..d4d15ad 100644 --- a/src/bounce/cloth/forces/spring_force.cpp +++ b/src/bounce/cloth/forces/spring_force.cpp @@ -17,12 +17,12 @@ */ #include -#include +#include #include #include #include -void b3SpringForceDef::Initialize(b3Particle* particle1, b3Particle* particle2, float32 structuralStiffness, float32 dampingStiffness) +void b3SpringForceDef::Initialize(b3ClothParticle* particle1, b3ClothParticle* particle2, scalar structuralStiffness, scalar dampingStiffness) { type = e_springForce; p1 = particle1; @@ -42,7 +42,8 @@ b3SpringForce::b3SpringForce(const b3SpringForceDef* def) m_L0 = def->restLength; m_ks = def->structural; m_kd = def->damping; - m_f.SetZero(); + m_f1.SetZero(); + m_f2.SetZero(); } b3SpringForce::~b3SpringForce() @@ -50,6 +51,11 @@ b3SpringForce::~b3SpringForce() } +bool b3SpringForce::HasParticle(const b3ClothParticle* particle) const +{ + return m_p1 == particle || m_p2 == particle; +} + void b3SpringForce::Apply(const b3ClothForceSolverData* data) { b3DenseVec3& x = *data->x; @@ -69,52 +75,60 @@ void b3SpringForce::Apply(const b3ClothForceSolverData* data) b3Mat33 I; I.SetIdentity(); - m_f.SetZero(); + m_f1.SetZero(); + m_f2.SetZero(); - if (m_ks > 0.0f) + b3Vec3 dx = x1 - x2; + + scalar L = b3Length(dx); + + if (L > scalar(0)) { - b3Vec3 dx = x1 - x2; + b3Vec3 n = dx / L; - float32 L = b3Length(dx); - - if (L > m_L0) + if (m_ks > scalar(0)) { - // Jacobian - b3Vec3 dCdx = dx / L; + if (L > m_L0) + { + scalar C = L - m_L0; - m_f += -m_ks * (L - m_L0) * dCdx; + m_f1 += -m_ks * C * n; + m_f2 -= -m_ks * C * n; + + // Force derivative + b3Mat33 K11 = -m_ks * (b3Outer(dx, dx) + (scalar(1) - m_L0 / L) * (I - b3Outer(dx, dx))); + b3Mat33 K12 = -K11; + b3Mat33 K21 = K12; + b3Mat33 K22 = K11; + + dfdx(i1, i1) += K11; + dfdx(i1, i2) += K12; + dfdx(i2, i1) += K21; + dfdx(i2, i2) += K22; + } + } + + if (m_kd > scalar(0)) + { + scalar dCdt = b3Dot(n, v1 - v2); + + // Force + m_f1 += -m_kd * dCdt * n; + m_f2 -= -m_kd * dCdt * n; // Force derivative - b3Mat33 K11 = -m_ks * (b3Outer(dx, dx) + (1.0f - m_L0 / L) * (I - b3Outer(dx, dx))); + b3Mat33 K11 = -m_kd * b3Outer(n, n); b3Mat33 K12 = -K11; b3Mat33 K21 = K12; b3Mat33 K22 = K11; - dfdx(i1, i1) += K11; - dfdx(i1, i2) += K12; - dfdx(i2, i1) += K21; - dfdx(i2, i2) += K22; + dfdv(i1, i1) += K11; + dfdv(i1, i2) += K12; + dfdv(i2, i1) += K21; + dfdv(i2, i2) += K22; } } - if (m_kd > 0.0f) - { - // C * J - b3Vec3 dv = v1 - v2; - - m_f += -m_kd * dv; - - b3Mat33 K11 = -m_kd * I; - b3Mat33 K12 = -K11; - b3Mat33 K21 = K12; - b3Mat33 K22 = K11; - - dfdv(i1, i1) += K11; - dfdv(i1, i2) += K12; - dfdv(i2, i1) += K21; - dfdv(i2, i2) += K22; - } - - f[i1] += m_f; - f[i2] -= m_f; + f[i1] += m_f1; + f[i2] += m_f2; } \ No newline at end of file diff --git a/src/bounce/cloth/forces/strech_force.cpp b/src/bounce/cloth/forces/stretch_force.cpp similarity index 61% rename from src/bounce/cloth/forces/strech_force.cpp rename to src/bounce/cloth/forces/stretch_force.cpp index 65dec70..be2b645 100644 --- a/src/bounce/cloth/forces/strech_force.cpp +++ b/src/bounce/cloth/forces/stretch_force.cpp @@ -16,66 +16,113 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include -#include -#include -#include +#include +#include #include #include #include -b3StrechForce::b3StrechForce(const b3StrechForceDef* def) +void b3StretchForceDef::Initialize(const b3Vec3& p1, const b3Vec3& p2, const b3Vec3& p3) { - m_type = e_strechForce; - m_triangle = def->triangle; - m_ks = def->streching; - m_kd = def->damping; - m_bu = def->bu; - m_bv = def->bv; + b3Vec3 A = p1, B = p2, C = p3; + + b3Vec3 AB = B - A; + b3Vec3 AC = C - A; + + // (u, v) 1 + u1 = scalar(0); + v1 = scalar(0); + + // (u, v) 2 + u2 = b3Length(AB); + v2 = scalar(0); + + // (u, v) 3 + B3_ASSERT(u2 > scalar(0)); + b3Vec3 n_AB = AB / u2; + + // a = b * h / 2 + // h = (a * 2) / b + scalar a2 = b3Length(b3Cross(AB, AC)); + B3_ASSERT(a2 > scalar(0)); + + u3 = b3Dot(AC, n_AB); + v3 = a2 / u2; + + alpha = scalar(0.5) * a2; +} + +b3StretchForce::b3StretchForce(const b3StretchForceDef* def) +{ + m_type = e_stretchForce; + m_p1 = def->p1; + m_p2 = def->p2; + m_p3 = def->p3; + m_ks_u = def->stretching_u; + m_kd_u = def->damping_u; + m_b_u = def->b_u; + m_ks_v = def->stretching_v; + m_kd_v = def->damping_v; + m_b_v = def->b_v; m_f1.SetZero(); m_f2.SetZero(); m_f3.SetZero(); + m_alpha = def->alpha; + + scalar u1 = def->u1, v1 = def->v1; + scalar u2 = def->u2, v2 = def->v2; + scalar u3 = def->u3, v3 = def->v3; + + // (u, v) matrix + scalar du1 = u2 - u1; + scalar dv1 = v2 - v1; + scalar du2 = u3 - u1; + scalar dv2 = v3 - v1; + + m_du1 = du1; + m_dv1 = dv1; + m_du2 = du2; + m_dv2 = dv2; + + scalar det = du1 * dv2 - du2 * dv1; + B3_ASSERT(det != scalar(0)); + m_inv_det = scalar(1) / det; + + scalar inv_det = m_inv_det; + + m_dwudx.x = inv_det * (dv1 - dv2); + m_dwudx.y = inv_det * dv2; + m_dwudx.z = -inv_det * dv1; + + m_dwvdx.x = inv_det * (du2 - du1); + m_dwvdx.y = -inv_det * du2; + m_dwvdx.z = inv_det * du1; } -b3StrechForce::~b3StrechForce() +b3StretchForce::~b3StretchForce() { } -bool b3StrechForce::HasParticle(const b3Particle* particle) const +bool b3StretchForce::HasParticle(const b3ClothParticle* particle) const { - b3Cloth* cloth = m_triangle->m_cloth; - u32 triangleIndex = m_triangle->m_triangle; - b3ClothMeshTriangle* triangle = cloth->m_mesh->triangles + triangleIndex; - - b3Particle* p1 = cloth->m_particles[triangle->v1]; - b3Particle* p2 = cloth->m_particles[triangle->v2]; - b3Particle* p3 = cloth->m_particles[triangle->v3]; - - return p1 == particle || p2 == particle || p3 == particle; + return m_p1 == particle || m_p2 == particle || m_p3 == particle; } -void b3StrechForce::Apply(const b3ClothForceSolverData* data) +void b3StretchForce::Apply(const b3ClothForceSolverData* data) { - b3Cloth* cloth = m_triangle->m_cloth; - u32 triangleIndex = m_triangle->m_triangle; - b3ClothMeshTriangle* triangle = cloth->m_mesh->triangles + triangleIndex; + scalar alpha = m_alpha; + scalar du1 = m_du1; + scalar dv1 = m_dv1; + scalar du2 = m_du2; + scalar dv2 = m_dv2; + scalar inv_det = m_inv_det; + b3Vec3 dwudx = m_dwudx; + b3Vec3 dwvdx = m_dwvdx; - float32 alpha = m_triangle->m_alpha; - float32 du1 = m_triangle->m_du1; - float32 dv1 = m_triangle->m_dv1; - float32 du2 = m_triangle->m_du2; - float32 dv2 = m_triangle->m_dv2; - float32 inv_det = m_triangle->m_inv_det; - - b3Particle* p1 = cloth->m_particles[triangle->v1]; - b3Particle* p2 = cloth->m_particles[triangle->v2]; - b3Particle* p3 = cloth->m_particles[triangle->v3]; - - u32 i1 = p1->m_solverId; - u32 i2 = p2->m_solverId; - u32 i3 = p3->m_solverId; + u32 i1 = m_p1->m_solverId; + u32 i2 = m_p2->m_solverId; + u32 i3 = m_p3->m_solverId; b3DenseVec3& x = *data->x; b3DenseVec3& v = *data->v; @@ -97,28 +144,18 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) b3Vec3 dx2 = x3 - x1; b3Vec3 wu = inv_det * (dv2 * dx1 - dv1 * dx2); - float32 len_wu = b3Length(wu); + scalar len_wu = b3Length(wu); b3Vec3 wv = inv_det * (-du2 * dx1 + du1 * dx2); - float32 len_wv = b3Length(wv); - - b3Vec3 dwudx; - dwudx[0] = inv_det * (dv1 - dv2); - dwudx[1] = inv_det * dv2; - dwudx[2] = -inv_det * dv1; - - b3Vec3 dwvdx; - dwvdx[0] = inv_det * (du2 - du1); - dwvdx[1] = -inv_det * du2; - dwvdx[2] = inv_det * du1; + scalar len_wv = b3Length(wv); m_f1.SetZero(); m_f2.SetZero(); m_f3.SetZero(); - if (len_wu > 0.0f) + if (len_wu > scalar(0)) { - float32 inv_len_wu = 1.0f / len_wu; + scalar inv_len_wu = scalar(1) / len_wu; b3Vec3 n_wu = inv_len_wu * wu; // Jacobian @@ -128,17 +165,17 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) dCudx[i] = alpha * dwudx[i] * n_wu; } - if (m_ks > 0.0f) + if (m_ks_u > scalar(0)) { - if (len_wu > m_bu) + if (len_wu > m_b_u) { - float32 Cu = alpha * (len_wu - m_bu); + scalar Cu = alpha * (len_wu - m_b_u); // Force b3Vec3 fs[3]; for (u32 i = 0; i < 3; ++i) { - fs[i] = -m_ks * Cu * dCudx[i]; + fs[i] = -m_ks_u * Cu * dCudx[i]; } m_f1 += fs[0]; @@ -153,7 +190,7 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) { b3Mat33 d2Cuxij = (alpha * inv_len_wu * dwudx[i] * dwudx[j]) * (I - b3Outer(n_wu, n_wu)); - b3Mat33 Kij = -m_ks * (b3Outer(dCudx[i], dCudx[j]) + Cu * d2Cuxij); + b3Mat33 Kij = -m_ks_u * (b3Outer(dCudx[i], dCudx[j]) + Cu * d2Cuxij); K[i][j] = Kij; } @@ -173,10 +210,10 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) } } - if (m_kd > 0.0f) + if (m_kd_u > scalar(0)) { b3Vec3 vs[3] = { v1, v2, v3 }; - float32 dCudt = 0.0f; + scalar dCudt = scalar(0); for (u32 i = 0; i < 3; ++i) { dCudt += b3Dot(dCudx[i], vs[i]); @@ -186,7 +223,7 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) b3Vec3 fs[3]; for (u32 i = 0; i < 3; ++i) { - fs[i] = -m_kd * dCudt * dCudx[i]; + fs[i] = -m_kd_u * dCudt * dCudx[i]; } m_f1 += fs[0]; @@ -199,7 +236,7 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) { for (u32 j = 0; j < 3; ++j) { - b3Mat33 Kij = -m_kd * b3Outer(dCudx[i], dCudx[j]); + b3Mat33 Kij = -m_kd_u * b3Outer(dCudx[i], dCudx[j]); K[i][j] = Kij; } @@ -219,9 +256,9 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) } } - if (len_wv > 0.0f) + if (len_wv > scalar(0)) { - float32 inv_len_wv = 1.0f / len_wv; + scalar inv_len_wv = scalar(1) / len_wv; b3Vec3 n_wv = inv_len_wv * wv; // Jacobian @@ -231,17 +268,17 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) dCvdx[i] = alpha * dwvdx[i] * n_wv; } - if (m_ks > 0.0f) + if (m_ks_v > scalar(0)) { - if (len_wv > m_bv) + if (len_wv > m_b_v) { - float32 Cv = alpha * (len_wv - m_bv); + scalar Cv = alpha * (len_wv - m_b_v); // Force b3Vec3 fs[3]; for (u32 i = 0; i < 3; ++i) { - fs[i] = -m_ks * Cv * dCvdx[i]; + fs[i] = -m_ks_v * Cv * dCvdx[i]; } m_f1 += fs[0]; @@ -256,7 +293,7 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) { b3Mat33 d2Cvxij = (alpha * inv_len_wv * dwvdx[i] * dwvdx[j]) * (I - b3Outer(n_wv, n_wv)); - b3Mat33 Kij = -m_ks * (b3Outer(dCvdx[i], dCvdx[j]) + Cv * d2Cvxij); + b3Mat33 Kij = -m_ks_v * (b3Outer(dCvdx[i], dCvdx[j]) + Cv * d2Cvxij); K[i][j] = Kij; } @@ -276,11 +313,11 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) } } - if (m_kd > 0.0f) + if (m_kd_v > scalar(0)) { b3Vec3 vs[3] = { v1, v2, v3 }; - float32 dCvdt = 0.0f; + scalar dCvdt = scalar(0); for (u32 i = 0; i < 3; ++i) { dCvdt += b3Dot(dCvdx[i], vs[i]); @@ -290,7 +327,7 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) b3Vec3 fs[3]; for (u32 i = 0; i < 3; ++i) { - fs[i] = -m_kd * dCvdt * dCvdx[i]; + fs[i] = -m_kd_v * dCvdt * dCvdx[i]; } m_f1 += fs[0]; @@ -303,7 +340,7 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) { for (u32 j = 0; j < 3; ++j) { - b3Mat33 Kij = -m_kd * b3Outer(dCvdx[i], dCvdx[j]); + b3Mat33 Kij = -m_kd_v * b3Outer(dCvdx[i], dCvdx[j]); K[i][j] = Kij; } diff --git a/src/bounce/cloth/garment/garment_mesh.cpp b/src/bounce/cloth/garment/garment_mesh.cpp index 2c7adb2..0994bb0 100644 --- a/src/bounce/cloth/garment/garment_mesh.cpp +++ b/src/bounce/cloth/garment/garment_mesh.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -26,7 +26,7 @@ extern "C" { - #include "../../../../external/triangle/triangle.h" + #include } b3GarmentMesh::b3GarmentMesh() @@ -51,7 +51,7 @@ b3GarmentMesh::~b3GarmentMesh() } // -static void b3Set(b3SewingPatternMesh* mesh, float32 desiredArea, const b3SewingPattern* pattern) +static void b3Set(b3SewingPatternMesh* mesh, scalar desiredArea, const b3SewingPattern* pattern) { B3_ASSERT(desiredArea > B3_EPSILON); @@ -59,12 +59,12 @@ static void b3Set(b3SewingPatternMesh* mesh, float32 desiredArea, const b3Sewing // Prepare the input structure in.pointlist = (REAL*)malloc(pattern->vertexCount * 2 * sizeof(REAL)); - const float32* fp = (float32*)pattern->vertices; + const scalar* fp = (scalar*)pattern->vertices; for (u32 i = 0; i < 2 * pattern->vertexCount; ++i) { in.pointlist[i] = (REAL)fp[i]; } - in.pointattributelist = NULL; + in.pointattributelist = nullptr; in.pointmarkerlist = (int*)malloc(pattern->vertexCount * sizeof(int)); for (u32 i = 0; i < pattern->vertexCount; ++i) { @@ -74,43 +74,43 @@ static void b3Set(b3SewingPatternMesh* mesh, float32 desiredArea, const b3Sewing in.numberofpoints = pattern->vertexCount; in.numberofpointattributes = 0; - in.trianglelist = NULL; - in.triangleattributelist = NULL; + in.trianglelist = nullptr; + in.triangleattributelist = nullptr; - in.trianglearealist = NULL; + in.trianglearealist = nullptr; in.numberoftriangles = 0; in.numberofcorners = 0; in.numberoftriangleattributes = 0; - in.segmentlist = NULL; - in.segmentmarkerlist = NULL; + in.segmentlist = nullptr; + in.segmentmarkerlist = nullptr; in.numberofsegments = 0; - in.holelist = NULL; + in.holelist = nullptr; in.numberofholes = 0; - in.regionlist = NULL; + in.regionlist = nullptr; in.numberofregions = 0; // Prepare the middle structure - mid.pointlist = NULL; - mid.pointmarkerlist = NULL; + mid.pointlist = nullptr; + mid.pointmarkerlist = nullptr; - mid.trianglelist = NULL; - mid.triangleattributelist = NULL; - mid.trianglearealist = NULL; - mid.neighborlist = NULL; + mid.trianglelist = nullptr; + mid.triangleattributelist = nullptr; + mid.trianglearealist = nullptr; + mid.neighborlist = nullptr; - mid.segmentlist = NULL; - mid.segmentmarkerlist = NULL; + mid.segmentlist = nullptr; + mid.segmentmarkerlist = nullptr; // Run triangulation // Q - quiet // z - zero based indices // p - PSLG // c - preserve the convex hull - triangulate("Qzpc", &in, &mid, NULL); + triangulate("Qzpc", &in, &mid, nullptr); // Refine @@ -122,14 +122,14 @@ static void b3Set(b3SewingPatternMesh* mesh, float32 desiredArea, const b3Sewing } // Prepare output structure - out.pointlist = NULL; - out.pointmarkerlist = NULL; + out.pointlist = nullptr; + out.pointmarkerlist = nullptr; - out.trianglelist = NULL; - out.trianglearealist = NULL; + out.trianglelist = nullptr; + out.trianglearealist = nullptr; - out.segmentlist = NULL; - out.segmentmarkerlist = NULL; + out.segmentlist = nullptr; + out.segmentmarkerlist = nullptr; // Run triangulation // Q - quiet @@ -137,7 +137,7 @@ static void b3Set(b3SewingPatternMesh* mesh, float32 desiredArea, const b3Sewing // p - PSLG // c - preserve the convex hull // r - read triangles - triangulate("Qzpcra", &mid, &out, NULL); + triangulate("Qzpcra", &mid, &out, nullptr); // The first vertices of the output structure must be the vertices of the input structure. for (int i = 0; i < in.numberofpoints; ++i) @@ -158,8 +158,8 @@ static void b3Set(b3SewingPatternMesh* mesh, float32 desiredArea, const b3Sewing mesh->vertexCount = out.numberofpoints; for (int i = 0; i < out.numberofpoints; ++i) { - mesh->vertices[i].x = (float32)out.pointlist[2 * i + 0]; - mesh->vertices[i].y = (float32)out.pointlist[2 * i + 1]; + mesh->vertices[i].x = (scalar)out.pointlist[2 * i + 0]; + mesh->vertices[i].y = (scalar)out.pointlist[2 * i + 1]; } mesh->triangles = (b3SewingPatternMeshTriangle*)b3Alloc(out.numberoftriangles * sizeof(b3SewingPatternMeshTriangle)); @@ -197,7 +197,7 @@ static void b3Set(b3SewingPatternMesh* mesh, float32 desiredArea, const b3Sewing free(out.segmentmarkerlist); } -void b3GarmentMesh::Set(b3Garment* g, float32 desiredArea) +void b3GarmentMesh::Set(b3Garment* g, scalar desiredArea) { garment = g; meshCount = garment->patternCount; @@ -223,4 +223,4 @@ void b3GarmentMesh::Set(b3Garment* g, float32 desiredArea) line->s2 = sewingLine->p2; line->v2 = sewingLine->v2; } -} +} \ No newline at end of file diff --git a/src/bounce/cloth/garment_cloth_mesh.cpp b/src/bounce/cloth/garment_cloth_mesh.cpp index 5f09f49..965684e 100644 --- a/src/bounce/cloth/garment_cloth_mesh.cpp +++ b/src/bounce/cloth/garment_cloth_mesh.cpp @@ -29,6 +29,10 @@ b3GarmentClothMesh::b3GarmentClothMesh() triangles = nullptr; meshCount = 0; meshes = nullptr; + shearingLineCount = 0; + shearingLines = nullptr; + bendingLineCount = 0; + bendingLines = nullptr; sewingLineCount = 0; sewingLines = nullptr; } @@ -38,6 +42,8 @@ b3GarmentClothMesh::~b3GarmentClothMesh() b3Free(vertices); b3Free(triangles); b3Free(meshes); + b3Free(shearingLines); + b3Free(bendingLines); b3Free(sewingLines); } @@ -77,7 +83,7 @@ void b3GarmentClothMesh::Set(const b3GarmentMesh* garment) { vertices[vertex_count].x = garment->meshes[pattern_index].vertices[pattern_vertex].x; vertices[vertex_count].y = garment->meshes[pattern_index].vertices[pattern_vertex].y; - vertices[vertex_count].z = 0.0f; + vertices[vertex_count].z = scalar(0); ++vertex_count; } diff --git a/src/bounce/cloth/particle.cpp b/src/bounce/cloth/particle.cpp deleted file mode 100644 index c1da933..0000000 --- a/src/bounce/cloth/particle.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#include -#include -#include -#include - -b3Particle::b3Particle(const b3ParticleDef& def, b3Cloth* cloth) -{ - m_cloth = cloth; - m_type = def.type; - m_position = def.position; - m_velocity = def.velocity; - m_force = def.force; - m_translation.SetZero(); - m_mass = def.mass; - - if (m_mass == 0.0f) - { - m_type = e_staticParticle; - m_mass = 1.0f; - m_invMass = 1.0f; - } - else - { - m_type = e_dynamicParticle; - m_invMass = 1.0f / m_mass; - } - - m_radius = def.radius; - m_friction = def.friction; - m_userData = nullptr; - m_x.SetZero(); - m_vertex = ~0; -} - -b3Particle::~b3Particle() -{ - -} - -void b3Particle::Synchronize(const b3Vec3& displacement) -{ - b3AABB3 aabb; - aabb.Set(m_position, m_radius); - - m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); -} - -void b3Particle::SynchronizeTriangles() -{ - if (m_vertex == ~0) - { - return; - } - - for (u32 i = 0; i < m_cloth->m_mesh->triangleCount; ++i) - { - b3ClothMeshTriangle* triangle = m_cloth->m_mesh->triangles + i; - - if (triangle->v1 == m_vertex || triangle->v2 == m_vertex || triangle->v3 == m_vertex) - { - m_cloth->GetTriangle(i)->Synchronize(b3Vec3_zero); - } - } -} - -void b3Particle::DestroyContacts() -{ - { - // Destroy body contacts - b3ParticleBodyContact* c = m_cloth->m_contactManager.m_particleBodyContactList.m_head; - while (c) - { - if (c->m_p1 == this) - { - b3ParticleBodyContact* quack = c; - c = c->m_next; - m_cloth->m_contactManager.Destroy(quack); - continue; - } - - c = c->m_next; - } - } - - { - // Destroy triangle contacts - b3ParticleTriangleContact* c = m_cloth->m_contactManager.m_particleTriangleContactList.m_head; - while (c) - { - if (c->m_p1 == this) - { - b3ParticleTriangleContact* quack = c; - c = c->m_next; - m_cloth->m_contactManager.Destroy(quack); - continue; - } - - c = c->m_next; - } - } -} - -void b3Particle::SetType(b3ParticleType type) -{ - if (m_type == type) - { - return; - } - - m_type = type; - m_force.SetZero(); - - if (type == e_staticParticle) - { - m_velocity.SetZero(); - m_translation.SetZero(); - - Synchronize(b3Vec3_zero); - SynchronizeTriangles(); - } - - DestroyContacts(); - - // Move the proxy so new contacts can be created. - m_cloth->m_contactManager.m_broadPhase.TouchProxy(m_broadPhaseId); -} \ No newline at end of file diff --git a/src/bounce/cloth/shapes/cloth_capsule_shape.cpp b/src/bounce/cloth/shapes/cloth_capsule_shape.cpp new file mode 100644 index 0000000..c56ee2e --- /dev/null +++ b/src/bounce/cloth/shapes/cloth_capsule_shape.cpp @@ -0,0 +1,65 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include + +b3ClothCapsuleShape::b3ClothCapsuleShape(const b3ClothCapsuleShapeDef& def, b3Cloth* cloth) +{ + m_type = e_clothCapsuleShape; + m_cloth = cloth; + m_p1 = def.p1; + m_p2 = def.p2; +} + +b3ClothCapsuleShape::~b3ClothCapsuleShape() +{ + +} + +void b3ClothCapsuleShape::DestroyContacts() +{ + b3ClothCapsuleAndCapsuleContact* c = m_cloth->m_contactManager.m_capsuleAndCapsuleContactList.m_head; + while (c) + { + if (c->m_s1 == this || c->m_s2 == this) + { + b3ClothCapsuleAndCapsuleContact* quack = c; + c = c->m_next; + m_cloth->m_contactManager.Destroy(quack); + continue; + } + + c = c->m_next; + } +} + +b3AABB b3ClothCapsuleShape::ComputeAABB() const +{ + b3AABB aabb; + aabb.SetSegment(m_p1->m_position, m_p2->m_position); + aabb.Extend(m_radius); + return aabb; +} + +void b3ClothCapsuleShape::Synchronize(const b3Vec3& displacement) +{ + b3AABB aabb = ComputeAABB(); + m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); +} \ No newline at end of file diff --git a/src/bounce/cloth/shapes/cloth_sphere_shape.cpp b/src/bounce/cloth/shapes/cloth_sphere_shape.cpp new file mode 100644 index 0000000..dfd4487 --- /dev/null +++ b/src/bounce/cloth/shapes/cloth_sphere_shape.cpp @@ -0,0 +1,86 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include + +b3ClothSphereShape::b3ClothSphereShape(const b3ClothSphereShapeDef& def, b3Cloth* cloth) +{ + m_type = e_clothSphereShape; + m_cloth = cloth; + m_p = def.p; +} + +b3ClothSphereShape::~b3ClothSphereShape() +{ + +} + +b3AABB b3ClothSphereShape::ComputeAABB() const +{ + b3AABB aabb; + aabb.Set(m_p->m_position, m_radius); + return aabb; +} + +void b3ClothSphereShape::Synchronize(const b3Vec3& displacement) +{ + b3AABB aabb; + aabb.Set(m_p->m_position, m_radius); + + m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); +} + +void b3ClothSphereShape::DestroyContacts() +{ + + { + // Destroy shape contacts + b3ClothSphereAndShapeContact* c = m_cloth->m_contactManager.m_sphereAndShapeContactList.m_head; + while (c) + { + if (c->m_s1 == this) + { + b3ClothSphereAndShapeContact* quack = c; + c = c->m_next; + m_cloth->m_contactManager.Destroy(quack); + continue; + } + + c = c->m_next; + } + } + + { + // Destroy triangle contacts + b3ClothSphereAndTriangleContact* c = m_cloth->m_contactManager.m_sphereAndTriangleContactList.m_head; + while (c) + { + if (c->m_s1 == this) + { + b3ClothSphereAndTriangleContact* quack = c; + c = c->m_next; + m_cloth->m_contactManager.Destroy(quack); + continue; + } + + c = c->m_next; + } + } +} \ No newline at end of file diff --git a/src/bounce/cloth/shapes/cloth_triangle_shape.cpp b/src/bounce/cloth/shapes/cloth_triangle_shape.cpp new file mode 100644 index 0000000..0f31554 --- /dev/null +++ b/src/bounce/cloth/shapes/cloth_triangle_shape.cpp @@ -0,0 +1,70 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include + +b3ClothTriangleShape::b3ClothTriangleShape(const b3ClothTriangleShapeDef& def, b3Cloth* cloth) +{ + m_type = e_clothTriangleShape; + m_cloth = cloth; + m_p1 = def.p1; + m_p2 = def.p2; + m_p3 = def.p3; + m_area = b3Area(def.v1, def.v2, def.v3); +} + +b3ClothTriangleShape::~b3ClothTriangleShape() +{ + +} + +b3AABB b3ClothTriangleShape::ComputeAABB() const +{ + b3AABB aabb; + aabb.SetTriangle(m_p1->m_position, m_p2->m_position, m_p3->m_position); + aabb.Extend(m_radius); + return aabb; +} + +void b3ClothTriangleShape::DestroyContacts() +{ + { + // Destroy sphere contacts + b3ClothSphereAndTriangleContact* c = m_cloth->m_contactManager.m_sphereAndTriangleContactList.m_head; + while (c) + { + if (c->m_s2 == this) + { + b3ClothSphereAndTriangleContact* quack = c; + c = c->m_next; + m_cloth->m_contactManager.Destroy(quack); + continue; + } + + c = c->m_next; + } + } +} + +void b3ClothTriangleShape::Synchronize(const b3Vec3& displacement) +{ + b3AABB aabb = ComputeAABB(); + m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); +} \ No newline at end of file diff --git a/src/bounce/cloth/shapes/cloth_world_shape.cpp b/src/bounce/cloth/shapes/cloth_world_shape.cpp new file mode 100644 index 0000000..aa17d32 --- /dev/null +++ b/src/bounce/cloth/shapes/cloth_world_shape.cpp @@ -0,0 +1,65 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include + +b3ClothWorldShape::b3ClothWorldShape(const b3ClothWorldShapeDef& def, b3Cloth* cloth) +{ + m_type = e_clothWorldShape; + m_shape = def.shape; + m_cloth = cloth; +} + +b3ClothWorldShape::~b3ClothWorldShape() +{ + +} + +b3AABB b3ClothWorldShape::ComputeAABB() const +{ + const b3Body* b = m_shape->GetBody(); + b3Transform xf = b->GetTransform(); + b3AABB aabb; + m_shape->ComputeAABB(&aabb, xf); + return aabb; +} + +void b3ClothWorldShape::Synchronize(const b3Vec3& displacement) +{ + b3AABB aabb = ComputeAABB(); + m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); +} + +void b3ClothWorldShape::DestroyContacts() +{ + b3ClothSphereAndShapeContact* c = m_cloth->m_contactManager.m_sphereAndShapeContactList.m_head; + while (c) + { + b3ClothSphereAndShapeContact* c0 = c; + c = c->m_next; + + if (c0->m_s2 == this) + { + m_cloth->m_contactManager.Destroy(c0); + } + } +} \ No newline at end of file diff --git a/src/bounce/collision/broad_phase.cpp b/src/bounce/collision/broad_phase.cpp index 6f0d5bb..1e297e9 100644 --- a/src/bounce/collision/broad_phase.cpp +++ b/src/bounce/collision/broad_phase.cpp @@ -75,9 +75,9 @@ bool b3BroadPhase::TestOverlap(u32 proxy1, u32 proxy2) const return m_tree.TestOverlap(proxy1, proxy2); } -u32 b3BroadPhase::CreateProxy(const b3AABB3& aabb, void* userData) +u32 b3BroadPhase::CreateProxy(const b3AABB& aabb, void* userData) { - b3AABB3 fatAABB = aabb; + b3AABB fatAABB = aabb; fatAABB.Extend(B3_AABB_EXTENSION); u32 proxyId = m_tree.InsertNode(fatAABB, userData); @@ -96,7 +96,7 @@ void b3BroadPhase::DestroyProxy(u32 proxyId) m_tree.RemoveNode(proxyId); } -bool b3BroadPhase::MoveProxy(u32 proxyId, const b3AABB3& aabb, const b3Vec3& displacement) +bool b3BroadPhase::MoveProxy(u32 proxyId, const b3AABB& aabb, const b3Vec3& displacement) { if (m_tree.GetAABB(proxyId).Contains(aabb)) { @@ -105,37 +105,37 @@ bool b3BroadPhase::MoveProxy(u32 proxyId, const b3AABB3& aabb, const b3Vec3& dis } // Extend the AABB. - b3AABB3 fatAABB = aabb; + b3AABB fatAABB = aabb; fatAABB.Extend(B3_AABB_EXTENSION); // Predict AABB displacement. b3Vec3 d = B3_AABB_MULTIPLIER * displacement; - if (d.x < 0.0f) + if (d.x < scalar(0)) { - fatAABB.m_lower.x += d.x; + fatAABB.lowerBound.x += d.x; } else { - fatAABB.m_upper.x += d.x; + fatAABB.upperBound.x += d.x; } - if (d.y < 0.0f) + if (d.y < scalar(0)) { - fatAABB.m_lower.y += d.y; + fatAABB.lowerBound.y += d.y; } else { - fatAABB.m_upper.y += d.y; + fatAABB.upperBound.y += d.y; } - if (d.z < 0.0f) + if (d.z < scalar(0)) { - fatAABB.m_lower.z += d.z; + fatAABB.lowerBound.z += d.z; } else { - fatAABB.m_upper.z += d.z; + fatAABB.upperBound.z += d.z; } // Update proxy with the extented AABB. diff --git a/src/bounce/collision/collision.cpp b/src/bounce/collision/collision.cpp index 9246b4f..b27b64f 100644 --- a/src/bounce/collision/collision.cpp +++ b/src/bounce/collision/collision.cpp @@ -21,12 +21,18 @@ #include #include #include +#include +#include -const b3Sphere b3Sphere_identity(b3Vec3_zero, 1.0f); +const b3Sphere b3Sphere_identity(b3Vec3_zero, scalar(1)); -const b3Capsule b3Capsule_identity(b3Vec3(0.0f, -0.5f, 0.0f), b3Vec3(0.0f, 0.5f, 0.0f), 1.0f); +const b3Capsule b3Capsule_identity(b3Vec3(scalar(0), scalar(-0.5), scalar(0)), b3Vec3(scalar(0), scalar(0.5), scalar(0)), scalar(1)); -const b3BoxHull b3BoxHull_identity(1.0f, 1.0f, 1.0f); +const b3BoxHull b3BoxHull_identity(scalar(1), scalar(1), scalar(1)); + +const b3CylinderHull b3CylinderHull_identity(scalar(1), scalar(1)); + +const b3ConeHull b3ConeHull_identity(scalar(1), scalar(1)); bool b3RayCast(b3RayCastOutput* output, const b3RayCastInput* input, @@ -34,7 +40,7 @@ bool b3RayCast(b3RayCastOutput* output, { b3Vec3 p1 = input->p1; b3Vec3 p2 = input->p2; - float32 maxFraction = input->maxFraction; + scalar maxFraction = input->maxFraction; b3Vec3 d = p2 - p1; @@ -44,27 +50,27 @@ bool b3RayCast(b3RayCastOutput* output, } b3Vec3 n = b3Cross(v2 - v1, v3 - v1); - float32 len = b3Length(n); + scalar len = b3Length(n); - if (len == 0.0f) + if (len == scalar(0)) { return false; } n /= len; - float32 num = b3Dot(n, v1 - p1); - float32 den = b3Dot(n, d); + scalar num = b3Dot(n, v1 - p1); + scalar den = b3Dot(n, d); - if (den == 0.0f) + if (den == scalar(0)) { return false; } - float32 fraction = num / den; + scalar fraction = num / den; // Is the intersection not on the segment? - if (fraction < 0.0f || maxFraction < fraction) + if (fraction < scalar(0) || maxFraction < fraction) { return false; } @@ -89,17 +95,17 @@ bool b3RayCast(b3RayCastOutput* output, b3Vec3 AB_x_AC = b3Cross(AB, AC); // Barycentric coordinates for Q - float32 u = b3Dot(QB_x_QC, AB_x_AC); - float32 v = b3Dot(QC_x_QA, AB_x_AC); - float32 w = b3Dot(QA_x_QB, AB_x_AC); + scalar u = b3Dot(QB_x_QC, AB_x_AC); + scalar v = b3Dot(QC_x_QA, AB_x_AC); + scalar w = b3Dot(QA_x_QB, AB_x_AC); // Is the intersection on the triangle? - if (u >= 0.0f && v >= 0.0f && w >= 0.0f) + if (u >= scalar(0) && v >= scalar(0) && w >= scalar(0)) { output->fraction = fraction; // Does the ray start from below or above the triangle? - if (num > 0.0f) + if (num > scalar(0)) { output->normal = -n; } diff --git a/src/bounce/collision/gjk/gjk.cpp b/src/bounce/collision/gjk/gjk.cpp index ed3a024..cc19e90 100644 --- a/src/bounce/collision/gjk/gjk.cpp +++ b/src/bounce/collision/gjk/gjk.cpp @@ -29,7 +29,7 @@ u32 b3_gjkCalls = 0, b3_gjkIters = 0, b3_gjkMaxIters = 0; // Convert a point Q from Cartesian coordinates to Barycentric coordinates (u, v) // with respect to a segment AB. // The last output value is the divisor. -static B3_FORCE_INLINE void b3Barycentric(float32 out[3], +static B3_FORCE_INLINE void b3Barycentric(scalar out[3], const b3Vec3& A, const b3Vec3& B, const b3Vec3& Q) { @@ -37,7 +37,7 @@ static B3_FORCE_INLINE void b3Barycentric(float32 out[3], b3Vec3 QA = A - Q; b3Vec3 QB = B - Q; - //float32 divisor = b3Dot(AB, AB); + //scalar divisor = b3Dot(AB, AB); out[0] = b3Dot(QB, AB); out[1] = -b3Dot(QA, AB); @@ -47,7 +47,7 @@ static B3_FORCE_INLINE void b3Barycentric(float32 out[3], // Convert a point Q from Cartesian coordinates to Barycentric coordinates (u, v, w) // with respect to a triangle ABC. // The last output value is the divisor. -static B3_FORCE_INLINE void b3Barycentric(float32 out[4], +static B3_FORCE_INLINE void b3Barycentric(scalar out[4], const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, const b3Vec3& Q) { @@ -64,7 +64,7 @@ static B3_FORCE_INLINE void b3Barycentric(float32 out[4], b3Vec3 AB_x_AC = b3Cross(AB, AC); - //float32 divisor = b3Dot(AB_x_AC, AB_x_AC); + //scalar divisor = b3Dot(AB_x_AC, AB_x_AC); out[0] = b3Dot(QB_x_QC, AB_x_AC); out[1] = b3Dot(QC_x_QA, AB_x_AC); @@ -75,7 +75,7 @@ static B3_FORCE_INLINE void b3Barycentric(float32 out[4], // Convert a point Q from Cartesian coordinates to Barycentric coordinates (u, v, w, x) // with respect to a tetrahedron ABCD. // The last output value is the (positive) divisor. -static B3_FORCE_INLINE void b3Barycentric(float32 out[5], +static B3_FORCE_INLINE void b3Barycentric(scalar out[5], const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, const b3Vec3& D, const b3Vec3& Q) { @@ -88,8 +88,8 @@ static B3_FORCE_INLINE void b3Barycentric(float32 out[5], b3Vec3 QC = C - Q; b3Vec3 QD = D - Q; - float32 divisor = b3Det(AB, AC, AD); - float32 sign = b3Sign(divisor); + scalar divisor = b3Det(AB, AC, AD); + scalar sign = b3Sign(divisor); out[0] = sign * b3Det(QB, QC, QD); out[1] = sign * b3Det(QA, QD, QC); @@ -127,8 +127,8 @@ b3Vec3 b3Simplex::GetSearchDirection(const b3Vec3& Q) const b3Vec3 AC = C - A; b3Vec3 AQ = Q - A; b3Vec3 AB_x_AC = b3Cross(AB, AC); - float32 sign = b3Dot(AB_x_AC, AQ); - if (sign > 0.0f) + scalar sign = b3Dot(AB_x_AC, AQ); + if (sign > scalar(0)) { return AB_x_AC; } @@ -140,7 +140,7 @@ b3Vec3 b3Simplex::GetSearchDirection(const b3Vec3& Q) const default: { B3_ASSERT(false); - return b3Vec3(0.0f, 0.0f, 0.0f); + return b3Vec3(scalar(0), scalar(0), scalar(0)); } } } @@ -151,7 +151,7 @@ b3Vec3 b3Simplex::GetClosestPoint() const { case 0: B3_ASSERT(false); - return b3Vec3(0.0f, 0.0f, 0.0f); + return b3Vec3(scalar(0), scalar(0), scalar(0)); case 1: return m_vertices[0].point; case 2: @@ -159,10 +159,10 @@ b3Vec3 b3Simplex::GetClosestPoint() const case 3: return m_vertices[0].weight * m_vertices[0].point + m_vertices[1].weight * m_vertices[1].point + m_vertices[2].weight * m_vertices[2].point; case 4: - return b3Vec3(0.0f, 0.0f, 0.0f); + return b3Vec3(scalar(0), scalar(0), scalar(0)); default: B3_ASSERT(false); - return b3Vec3(0.0f, 0.0f, 0.0f); + return b3Vec3(scalar(0), scalar(0), scalar(0)); } } @@ -204,30 +204,30 @@ void b3Simplex::Solve2(const b3Vec3& Q) b3SimplexVertex B = m_vertices[1]; // Test vertex regions - float32 wAB[3]; + scalar wAB[3]; b3Barycentric(wAB, A.point, B.point, Q); // R A - if (wAB[1] <= 0.0f) + if (wAB[1] <= scalar(0)) { m_count = 1; m_vertices[0] = A; - m_vertices[0].weight = 1.0f; + m_vertices[0].weight = scalar(1); return; } // R B - if (wAB[0] <= 0.0f) + if (wAB[0] <= scalar(0)) { m_count = 1; m_vertices[0] = B; - m_vertices[0].weight = 1.0f; + m_vertices[0].weight = scalar(1); return; } // R AB - float32 divisor = wAB[2]; - float32 s = 1.0f / divisor; + scalar divisor = wAB[2]; + scalar s = scalar(1) / divisor; m_count = 2; m_vertices[0] = A; m_vertices[0].weight = s * wAB[0]; @@ -244,52 +244,52 @@ void b3Simplex::Solve3(const b3Vec3& Q) b3SimplexVertex C = m_vertices[2]; // Test vertex regions - float32 wAB[3], wBC[3], wCA[3]; + scalar wAB[3], wBC[3], wCA[3]; b3Barycentric(wAB, A.point, B.point, Q); b3Barycentric(wBC, B.point, C.point, Q); b3Barycentric(wCA, C.point, A.point, Q); // R A - if (wAB[1] <= 0.0f && wCA[0] <= 0.0f) + if (wAB[1] <= scalar(0) && wCA[0] <= scalar(0)) { m_count = 1; m_vertices[0] = A; - m_vertices[0].weight = 1.0f; + m_vertices[0].weight = scalar(1); return; } // R B - if (wAB[0] <= 0.0f && wBC[1] <= 0.0f) + if (wAB[0] <= scalar(0) && wBC[1] <= scalar(0)) { m_count = 1; m_vertices[0] = B; - m_vertices[0].weight = 1.0f; + m_vertices[0].weight = scalar(1); return; } // R C - if (wBC[0] <= 0.0f && wCA[1] <= 0.0f) + if (wBC[0] <= scalar(0) && wCA[1] <= scalar(0)) { m_count = 1; m_vertices[0] = C; - m_vertices[0].weight = 1.0f; + m_vertices[0].weight = scalar(1); return; } // Test edge regions - float32 wABC[4]; + scalar wABC[4]; b3Barycentric(wABC, A.point, B.point, C.point, Q); // This is used to help testing if the face degenerates // into an edge. - float32 area = wABC[3]; + scalar area = wABC[3]; // R AB - if (wAB[0] > 0.0f && wAB[1] > 0.0f && area * wABC[2] <= 0.0f) + if (wAB[0] > scalar(0) && wAB[1] > scalar(0) && area * wABC[2] <= scalar(0)) { - float32 divisor = wAB[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wAB[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 2; m_vertices[0] = A; m_vertices[0].weight = s * wAB[0]; @@ -299,11 +299,11 @@ void b3Simplex::Solve3(const b3Vec3& Q) } // R BC - if (wBC[0] > 0.0f && wBC[1] > 0.0f && area * wABC[0] <= 0.0f) + if (wBC[0] > scalar(0) && wBC[1] > scalar(0) && area * wABC[0] <= scalar(0)) { - float32 divisor = wBC[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wBC[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 2; m_vertices[0] = B; m_vertices[0].weight = s * wBC[0]; @@ -313,11 +313,11 @@ void b3Simplex::Solve3(const b3Vec3& Q) } // R CA - if (wCA[0] > 0.0f && wCA[1] > 0.0f && area * wABC[1] <= 0.0f) + if (wCA[0] > scalar(0) && wCA[1] > scalar(0) && area * wABC[1] <= scalar(0)) { - float32 divisor = wCA[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wCA[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 2; m_vertices[0] = C; m_vertices[0].weight = s * wCA[0]; @@ -327,15 +327,15 @@ void b3Simplex::Solve3(const b3Vec3& Q) } // R ABC/ACB - float32 divisor = wABC[3]; - if (divisor <= 0.0f) + scalar divisor = wABC[3]; + if (divisor <= scalar(0)) { // Give up. return; } - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 3; m_vertices[0] = A; m_vertices[0].weight = s * wABC[0]; @@ -354,7 +354,7 @@ void b3Simplex::Solve4(const b3Vec3& Q) b3SimplexVertex D = m_vertices[3]; // Test vertex regions - float32 wAB[3], wAC[3], wAD[3], wBC[3], wCD[3], wDB[3]; + scalar wAB[3], wAC[3], wAD[3], wBC[3], wCD[3], wDB[3]; b3Barycentric(wAB, A.point, B.point, Q); b3Barycentric(wBC, B.point, C.point, Q); b3Barycentric(wAC, A.point, C.point, Q); @@ -363,54 +363,54 @@ void b3Simplex::Solve4(const b3Vec3& Q) b3Barycentric(wDB, D.point, B.point, Q); // R A - if (wAB[1] <= 0.0f && wAC[1] <= 0.0f && wAD[1] <= 0.0f) + if (wAB[1] <= scalar(0) && wAC[1] <= scalar(0) && wAD[1] <= scalar(0)) { m_count = 1; m_vertices[0] = A; - m_vertices[0].weight = 1.0f; + m_vertices[0].weight = scalar(1); return; } // R B - if (wAB[0] <= 0.0f && wDB[0] <= 0.0f && wBC[1] <= 0.0f) + if (wAB[0] <= scalar(0) && wDB[0] <= scalar(0) && wBC[1] <= scalar(0)) { m_count = 1; m_vertices[0] = B; - m_vertices[0].weight = 1.0f; + m_vertices[0].weight = scalar(1); return; } // R C - if (wAC[0] <= 0.0f && wBC[0] <= 0.0f && wCD[1] <= 0.0f) + if (wAC[0] <= scalar(0) && wBC[0] <= scalar(0) && wCD[1] <= scalar(0)) { m_count = 1; m_vertices[0] = C; - m_vertices[0].weight = 1.0f; + m_vertices[0].weight = scalar(1); return; } // R D - if (wAD[0] <= 0.0f && wCD[0] <= 0.0f && wDB[1] <= 0.0f) + if (wAD[0] <= scalar(0) && wCD[0] <= scalar(0) && wDB[1] <= scalar(0)) { m_count = 1; m_vertices[0] = D; - m_vertices[0].weight = 1.0f; + m_vertices[0].weight = scalar(1); return; } // Test edge regions - float32 wACB[4], wABD[4], wADC[4], wBCD[4]; + scalar wACB[4], wABD[4], wADC[4], wBCD[4]; b3Barycentric(wACB, A.point, C.point, B.point, Q); b3Barycentric(wABD, A.point, B.point, D.point, Q); b3Barycentric(wADC, A.point, D.point, C.point, Q); b3Barycentric(wBCD, B.point, C.point, D.point, Q); // R AB - if (wABD[2] <= 0.0f && wACB[1] <= 0.0f && wAB[0] > 0.0f && wAB[1] > 0.0f) + if (wABD[2] <= scalar(0) && wACB[1] <= scalar(0) && wAB[0] > scalar(0) && wAB[1] > scalar(0)) { - float32 divisor = wAB[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wAB[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 2; m_vertices[0] = A; m_vertices[0].weight = s * wAB[0]; @@ -420,11 +420,11 @@ void b3Simplex::Solve4(const b3Vec3& Q) } // R AC - if (wACB[2] <= 0.0f && wADC[1] <= 0.0f && wAC[0] > 0.0f && wAC[1] > 0.0f) + if (wACB[2] <= scalar(0) && wADC[1] <= scalar(0) && wAC[0] > scalar(0) && wAC[1] > scalar(0)) { - float32 divisor = wAC[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wAC[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 2; m_vertices[0] = A; m_vertices[0].weight = s * wAC[0]; @@ -434,11 +434,11 @@ void b3Simplex::Solve4(const b3Vec3& Q) } // R AD - if (wADC[2] <= 0.0f && wABD[1] <= 0.0f && wAD[0] > 0.0f && wAD[1] > 0.0f) + if (wADC[2] <= scalar(0) && wABD[1] <= scalar(0) && wAD[0] > scalar(0) && wAD[1] > scalar(0)) { - float32 divisor = wAD[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wAD[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 2; m_vertices[0] = A; m_vertices[0].weight = s * wAD[0]; @@ -448,11 +448,11 @@ void b3Simplex::Solve4(const b3Vec3& Q) } // R BC - if (wACB[0] <= 0.0f && wBCD[2] <= 0.0f && wBC[0] > 0.0f && wBC[1] > 0.0f) + if (wACB[0] <= scalar(0) && wBCD[2] <= scalar(0) && wBC[0] > scalar(0) && wBC[1] > scalar(0)) { - float32 divisor = wBC[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wBC[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 2; m_vertices[0] = B; m_vertices[0].weight = s * wBC[0]; @@ -462,11 +462,11 @@ void b3Simplex::Solve4(const b3Vec3& Q) } // R CD - if (wADC[0] <= 0.0f && wBCD[0] <= 0.0f && wCD[0] > 0.0f && wCD[1] > 0.0f) + if (wADC[0] <= scalar(0) && wBCD[0] <= scalar(0) && wCD[0] > scalar(0) && wCD[1] > scalar(0)) { - float32 divisor = wCD[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wCD[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 2; m_vertices[0] = C; m_vertices[0].weight = s * wCD[0]; @@ -476,11 +476,11 @@ void b3Simplex::Solve4(const b3Vec3& Q) } // R DB - if (wABD[0] <= 0.0f && wBCD[1] <= 0.0f && wDB[0] > 0.0f && wDB[1] > 0.0f) + if (wABD[0] <= scalar(0) && wBCD[1] <= scalar(0) && wDB[0] > scalar(0) && wDB[1] > scalar(0)) { - float32 divisor = wDB[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wDB[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 2; m_vertices[0] = D; m_vertices[0].weight = s * wDB[0]; @@ -490,15 +490,15 @@ void b3Simplex::Solve4(const b3Vec3& Q) } // Test face regions - float32 wABCD[5]; + scalar wABCD[5]; b3Barycentric(wABCD, A.point, B.point, C.point, D.point, Q); // R ACB - if (wABCD[3] <= 0.0f && wACB[0] > 0.0f && wACB[1] > 0.0f && wACB[2] > 0.0f) + if (wABCD[3] <= scalar(0) && wACB[0] > scalar(0) && wACB[1] > scalar(0) && wACB[2] > scalar(0)) { - float32 divisor = wACB[0] + wACB[1] + wACB[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wACB[0] + wACB[1] + wACB[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 3; m_vertices[0] = A; m_vertices[0].weight = s * wACB[0]; @@ -510,11 +510,11 @@ void b3Simplex::Solve4(const b3Vec3& Q) } // R ABD - if (wABCD[2] <= 0.0f && wABD[0] > 0.0f && wABD[1] > 0.0f && wABD[2] > 0.0f) + if (wABCD[2] <= scalar(0) && wABD[0] > scalar(0) && wABD[1] > scalar(0) && wABD[2] > scalar(0)) { - float32 divisor = wABD[0] + wABD[1] + wABD[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wABD[0] + wABD[1] + wABD[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 3; m_vertices[0] = A; m_vertices[0].weight = s * wABD[0]; @@ -526,11 +526,11 @@ void b3Simplex::Solve4(const b3Vec3& Q) } // R ADC - if (wABCD[1] <= 0.0f && wADC[0] > 0.0f && wADC[1] > 0.0f && wADC[2] > 0.0f) + if (wABCD[1] <= scalar(0) && wADC[0] > scalar(0) && wADC[1] > scalar(0) && wADC[2] > scalar(0)) { - float32 divisor = wADC[0] + wADC[1] + wADC[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wADC[0] + wADC[1] + wADC[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 3; m_vertices[0] = A; m_vertices[0].weight = s * wADC[0]; @@ -542,11 +542,11 @@ void b3Simplex::Solve4(const b3Vec3& Q) } // R BCD - if (wABCD[0] <= 0.0f && wBCD[0] > 0.0f && wBCD[1] > 0.0f && wBCD[2] > 0.0f) + if (wABCD[0] <= scalar(0) && wBCD[0] > scalar(0) && wBCD[1] > scalar(0) && wBCD[2] > scalar(0)) { - float32 divisor = wBCD[0] + wBCD[1] + wBCD[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + scalar divisor = wBCD[0] + wBCD[1] + wBCD[2]; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 3; m_vertices[0] = B; m_vertices[0].weight = s * wBCD[0]; @@ -558,15 +558,15 @@ void b3Simplex::Solve4(const b3Vec3& Q) } // R ABCD - float32 divisor = wABCD[0] + wABCD[1] + wABCD[2] + wABCD[3]; - if (divisor <= 0.0f) + scalar divisor = wABCD[0] + wABCD[1] + wABCD[2] + wABCD[3]; + if (divisor <= scalar(0)) { // Give up. return; } - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; + B3_ASSERT(divisor > scalar(0)); + scalar s = scalar(1) / divisor; m_count = 4; m_vertices[0].weight = s * wABCD[0]; m_vertices[1].weight = s * wABCD[1]; @@ -596,9 +596,9 @@ b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, // Last iteration squared distance for checking if we're getting close // to the origin and prevent cycling. - float32 distSq1 = B3_MAX_FLOAT; + scalar distSq1 = B3_MAX_SCALAR; - const b3Vec3 kOrigin(0.0f, 0.0f, 0.0f); + const b3Vec3 kOrigin(scalar(0), scalar(0), scalar(0)); // Limit number of iterations to prevent cycling. const u32 kMaxIters = 20; @@ -643,7 +643,7 @@ b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, // Compute the closest point. b3Vec3 p = simplex.GetClosestPoint(); - float32 distSq2 = b3Dot(p, p); + scalar distSq2 = b3Dot(p, p); // Ensure we're getting close to the origin. if (distSq2 >= distSq1) { @@ -662,9 +662,9 @@ b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, // Compute a tentative new simplex vertex using support points. b3SimplexVertex* vertex = vertices + simplex.m_count; - vertex->index1 = proxy1.GetSupportIndex(b3MulT(xf1.rotation, -d)); + vertex->index1 = proxy1.GetSupportIndex(b3MulC(xf1.rotation, -d)); vertex->point1 = b3Mul(xf1, proxy1.GetVertex(vertex->index1)); - vertex->index2 = proxy2.GetSupportIndex(b3MulT(xf2.rotation, d)); + vertex->index2 = proxy2.GetSupportIndex(b3MulC(xf2.rotation, d)); vertex->point2 = b3Mul(xf2, proxy2.GetVertex(vertex->index2)); vertex->point = vertex->point2 - vertex->point1; @@ -708,8 +708,8 @@ b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, // Apply radius if requested. if (applyRadius) { - float32 r1 = proxy1.radius; - float32 r2 = proxy2.radius; + scalar r1 = proxy1.radius; + scalar r2 = proxy2.radius; if (output.distance > r1 + r2 && output.distance > B3_EPSILON) { @@ -725,10 +725,10 @@ b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, { // Shapes are overlapped when radii are considered. // Move the witness points to the middle. - b3Vec3 p = 0.5f * (output.point1 + output.point2); + b3Vec3 p = scalar(0.5) * (output.point1 + output.point2); output.point1 = p; output.point2 = p; - output.distance = 0.0f; + output.distance = scalar(0); } } @@ -756,7 +756,7 @@ void b3Simplex::ReadCache(const b3SimplexCache* cache, v->point1 = xf1 * wALocal; v->point2 = xf2 * wBLocal; v->point = v->point2 - v->point1; - v->weight = 0.0f; + v->weight = scalar(0); } // Compute the new simplex metric @@ -764,9 +764,9 @@ void b3Simplex::ReadCache(const b3SimplexCache* cache, // old metric then flush the simplex. if (m_count > 1) { - float32 metric1 = cache->metric; - float32 metric2 = GetMetric(); - if (metric2 < 0.5f * metric1 || 2.0f * metric1 < metric2 || metric2 < B3_EPSILON) + scalar metric1 = cache->metric; + scalar metric2 = GetMetric(); + if (metric2 < scalar(0.5) * metric1 || scalar(2) * metric1 < metric2 || metric2 < B3_EPSILON) { // Flush m_count = 0; @@ -786,7 +786,7 @@ void b3Simplex::ReadCache(const b3SimplexCache* cache, v->point1 = b3Mul(xf1, w1Local); v->point2 = b3Mul(xf2, w2Local); v->point = v->point2 - v->point1; - v->weight = 1.0f; + v->weight = scalar(1); v->index1 = 0; v->index2 = 0; m_count = 1; @@ -804,15 +804,15 @@ void b3Simplex::WriteCache(b3SimplexCache* cache) const } } -float32 b3Simplex::GetMetric() const +scalar b3Simplex::GetMetric() const { switch (m_count) { case 0: B3_ASSERT(false); - return 0.0f; + return scalar(0); case 1: - return 0.0f; + return scalar(0); case 2: // Magnitude return b3Distance(m_vertices[0].point, m_vertices[1].point); @@ -829,142 +829,56 @@ float32 b3Simplex::GetMetric() const b3Vec3 E1 = m_vertices[1].point - m_vertices[0].point; b3Vec3 E2 = m_vertices[2].point - m_vertices[0].point; b3Vec3 E3 = m_vertices[3].point - m_vertices[0].point; - float32 det = b3Det(E1, E2, E3); - float32 sign = b3Sign(det); - float32 volume = sign * det; + scalar det = b3Det(E1, E2, E3); + scalar sign = b3Sign(det); + scalar volume = sign * det; return volume; } default: B3_ASSERT(false); - return 0.0f; + return scalar(0); } } /////////////////////////////////////////////////////////////////////////////////////////////////// b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, - const b3Transform& xf2, const b3GJKProxy& proxy2) + const b3Transform& xf2, const b3GJKProxy& proxy2, bool applyRadius) { b3SimplexCache cache; cache.count = 0; - return b3GJK(xf1, proxy1, xf2, proxy2, false, &cache); + return b3GJK(xf1, proxy1, xf2, proxy2, applyRadius, &cache); } /////////////////////////////////////////////////////////////////////////////////////////////////// -// Brian Mirtich -// "Conservative Advancement" -bool b3GJKShapeCast(b3GJKShapeCastOutput* output, - const b3Transform& xf1, const b3GJKProxy& proxy1, - const b3Transform& xf2, const b3GJKProxy& proxy2, const b3Vec3& translation2) +// The output of the GJK-based shape cast algorithm. +struct b3ShapeCastOutput { - float32 r1 = proxy1.radius; - float32 r2 = proxy2.radius; - float32 radius = r1 + r2; - - float32 bound = b3Length(translation2); - B3_ASSERT(bound > 0.0f); - - float32 t = 0.0f; - b3SimplexCache cache; - cache.count = 0; - b3GJKOutput gjkOut = b3GJK(xf1, proxy1, xf2, proxy2, false, &cache); - float32 d = gjkOut.distance; - - if (d == 0.0f) - { - // Overlap - output->iterations = 0; - return false; - } - - const float32 tolerance = 0.25f * B3_LINEAR_SLOP; - - b3Vec3 n = gjkOut.point2 - gjkOut.point1; - n /= d; - - if (d < radius + tolerance) - { - // Touch - output->t = t; - output->point = gjkOut.point1 + r1 * n; - output->normal = n; - output->iterations = 0; - return true; - } - - u32 iter = 0; - for (;;) - { - ++iter; - - B3_ASSERT(d >= radius); - float32 dt = (d - radius) / bound; - t += dt; - - if (t >= 1.0f) - { - // No overlap - output->iterations = iter; - return false; - } - - b3Transform txf1 = xf1; - - b3Transform txf2; - txf2.rotation = xf2.rotation; - txf2.position = (1.0f - t) * xf2.position + t * (xf2.position + translation2); - - gjkOut = b3GJK(txf1, proxy1, txf2, proxy2, false, &cache); - d = gjkOut.distance; - - if (d == 0.0f) - { - break; - } - - n = gjkOut.point2 - gjkOut.point1; - n /= d; - - if (d < radius + tolerance) - { - break; - } - } - - if (d > 0.0f) - { - n = gjkOut.point2 - gjkOut.point1; - n /= d; - } - - output->t = t; - output->point = gjkOut.point1 + r1 * n; - output->normal = n; - output->iterations = iter; - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// + scalar t; // time of impact + b3Vec3 point; // contact point at t + b3Vec3 normal; // contact normal at t + u32 iterations; // number of iterations +}; // Gino van der Bergen // "Smooth Mesh Contacts with GJK" // Game Physics Pearls 2010, page 99 -bool b3GJKShapeCastCSO(b3GJKShapeCastOutput* output, +bool b3ShapeCast(b3ShapeCastOutput* output, const b3Transform& xf1, const b3GJKProxy& proxy1, const b3Transform& xf2, const b3GJKProxy& proxy2, const b3Vec3& translation2) { - float32 r1 = proxy1.radius; - float32 r2 = proxy2.radius; - float32 radius = r1 + r2; + scalar r1 = proxy1.radius; + scalar r2 = proxy2.radius; + scalar radius = r1 + r2; b3Vec3 r = translation2; - float32 t = 0.0f; + scalar t = scalar(0); b3Vec3 n = b3Vec3_zero; - u32 index1 = proxy1.GetSupportIndex(b3MulT(xf1.rotation, -r)); - u32 index2 = proxy2.GetSupportIndex(b3MulT(xf2.rotation, r)); + u32 index1 = proxy1.GetSupportIndex(b3MulC(xf1.rotation, -r)); + u32 index2 = proxy2.GetSupportIndex(b3MulC(xf2.rotation, r)); b3Vec3 w1 = xf1 * proxy1.GetVertex(index1); b3Vec3 w2 = xf2 * proxy2.GetVertex(index2); b3Vec3 v = w1 - w2; @@ -978,32 +892,32 @@ bool b3GJKShapeCastCSO(b3GJKShapeCastOutput* output, u32 saveCount = 0; const u32 kMaxIters = 20; - const float32 kTolerance = 10.0f * B3_EPSILON; + const scalar kTolerance = scalar(10) * B3_EPSILON; - float32 maxTolerance = 1.0f; + scalar maxTolerance = scalar(1); u32 iter = 0; while (iter < kMaxIters && b3Abs(b3LengthSquared(v) - radius * radius) > kTolerance * maxTolerance) { // Support in direction -v - index1 = proxy1.GetSupportIndex(b3MulT(xf1.rotation, -v)); - index2 = proxy2.GetSupportIndex(b3MulT(xf2.rotation, v)); + index1 = proxy1.GetSupportIndex(b3MulC(xf1.rotation, -v)); + index2 = proxy2.GetSupportIndex(b3MulC(xf2.rotation, v)); w1 = xf1 * proxy1.GetVertex(index1); w2 = xf2 * proxy2.GetVertex(index2); b3Vec3 p = w1 - w2; // Support plane on boundary of CSO is (-v, p) // -v is normal at p - float32 vp = b3Dot(v, p); - float32 vr = b3Dot(v, r); + scalar vp = b3Dot(v, p); + scalar vr = b3Dot(v, r); if (vp - radius > t * vr) { - if (vr > 0.0f) + if (vr > scalar(0)) { t = (vp - radius) / vr; - if (t > 1.0f) + if (t > scalar(1)) { output->iterations = iter; return false; diff --git a/src/bounce/collision/gjk/gjk_feature_pair.cpp b/src/bounce/collision/gjk/gjk_feature_pair.cpp index 42a0283..6baad00 100644 --- a/src/bounce/collision/gjk/gjk_feature_pair.cpp +++ b/src/bounce/collision/gjk/gjk_feature_pair.cpp @@ -202,6 +202,29 @@ b3GJKFeaturePair b3GetFeaturePair(const b3SimplexCache& cache) return pair; } + if (uniqueCount1 == 3 && uniqueCount2 == 3) + { + // FF + b3GJKFeaturePair pair; + pair.count1 = 3; + pair.count2 = 3; + pair.index1[0] = cache.index1[0]; + pair.index1[1] = cache.index1[1]; + pair.index1[2] = cache.index1[2]; + pair.index2[0] = cache.index2[0]; + pair.index2[1] = cache.index2[1]; + pair.index2[2] = cache.index2[2]; + + B3_ASSERT(pair.index1[0] != pair.index1[1]); + B3_ASSERT(pair.index1[1] != pair.index1[2]); + B3_ASSERT(pair.index1[2] != pair.index1[0]); + + B3_ASSERT(pair.index2[0] != pair.index2[1]); + B3_ASSERT(pair.index2[1] != pair.index2[2]); + B3_ASSERT(pair.index2[2] != pair.index2[0]); + return pair; + } + B3_ASSERT(false); } diff --git a/src/bounce/collision/sat/sat.cpp b/src/bounce/collision/sat/sat.cpp index 6d38811..bd0b15d 100644 --- a/src/bounce/collision/sat/sat.cpp +++ b/src/bounce/collision/sat/sat.cpp @@ -23,7 +23,7 @@ // convex hulls. Thanks to Dirk Gregorius for his presentation // at GDC 2013. -float32 b3Project(const b3Hull* hull, const b3Plane& plane) +scalar b3Project(const b3Hull* hull, const b3Plane& plane) { b3Vec3 support = hull->GetVertex(hull->GetSupportVertex(-plane.normal)); return b3Distance(support, plane); @@ -38,12 +38,12 @@ b3FaceQuery b3QueryFaceSeparation(const b3Transform& xf1, const b3Hull* hull1, // Here greater means less than since is a signed distance. u32 maxIndex = 0; - float32 maxSeparation = -B3_MAX_FLOAT; + scalar maxSeparation = -B3_MAX_SCALAR; for (u32 i = 0; i < hull1->faceCount; ++i) { b3Plane plane = xf * hull1->GetPlane(i); - float32 separation = b3Project(hull2, plane); + scalar separation = b3Project(hull2, plane); if (separation > maxSeparation) { maxIndex = i; @@ -59,46 +59,48 @@ b3FaceQuery b3QueryFaceSeparation(const b3Transform& xf1, const b3Hull* hull1, bool b3IsMinkowskiFace(const b3Vec3& A, const b3Vec3& B, const b3Vec3& B_x_A, const b3Vec3& C, const b3Vec3& D, const b3Vec3& D_x_C) { - float32 ADC = b3Dot(A, D_x_C); - float32 BDC = b3Dot(B, D_x_C); + scalar ADC = b3Dot(A, D_x_C); + scalar BDC = b3Dot(B, D_x_C); - float32 CBA = b3Dot(C, B_x_A); - float32 DBA = b3Dot(D, B_x_A); + scalar CBA = b3Dot(C, B_x_A); + scalar DBA = b3Dot(D, B_x_A); - return CBA * DBA < 0.0f && // Test arc CD against AB plane. - ADC * BDC < 0.0f && // Test arc AB against DC plane. - CBA * BDC > 0.0f; // Test if arcs AB and CD are on the same hemisphere. + return CBA * DBA < scalar(0) && // Test arc CD against AB plane. + ADC * BDC < scalar(0) && // Test arc AB against DC plane. + CBA * BDC > scalar(0); // Test if arcs AB and CD are on the same hemisphere. } -float32 b3Project(const b3Vec3& P1, const b3Vec3& E1, const b3Vec3& P2, const b3Vec3& E2, const b3Vec3& C1) +scalar b3Project(const b3Vec3& P1, const b3Vec3& Q1, const b3Vec3& E1, const b3Vec3& P2, const b3Vec3& E2, const b3Vec3& C1) { - float32 L1 = b3Length(E1); + scalar L1 = b3Length(E1); B3_ASSERT(L1 > B3_LINEAR_SLOP); if (L1 < B3_LINEAR_SLOP) { - return -B3_MAX_FLOAT; + return -B3_MAX_SCALAR; } - float32 L2 = b3Length(E2); + scalar L2 = b3Length(E2); B3_ASSERT(L2 > B3_LINEAR_SLOP); if (L2 < B3_LINEAR_SLOP) { - return -B3_MAX_FLOAT; + return -B3_MAX_SCALAR; } // Skip over almost parallel edges. - const float32 kTol = 0.005f; + const scalar kTol = scalar(0.005); b3Vec3 E1_x_E2 = b3Cross(E1, E2); - float32 L = b3Length(E1_x_E2); + scalar L = b3Length(E1_x_E2); if (L < kTol * L1 * L2) { - return -B3_MAX_FLOAT; + return -B3_MAX_SCALAR; } + b3Vec3 E1C = scalar(0.5) * (P1 + Q1); + // Ensure consistent normal orientation to hull B. - b3Vec3 N = (1.0f / L) * E1_x_E2; - if (b3Dot(N, P1 - C1) < 0.0f) + b3Vec3 N = (scalar(1) / L) * E1_x_E2; + if (b3Dot(N, E1C - C1) < scalar(0)) { N = -N; } @@ -117,7 +119,7 @@ b3EdgeQuery b3QueryEdgeSeparation(const b3Transform& xf1, const b3Hull* hull1, u32 maxIndex1 = 0; u32 maxIndex2 = 0; - float32 maxSeparation = -B3_MAX_FLOAT; + scalar maxSeparation = -B3_MAX_SCALAR; // Loop through the first hull's unique edges. for (u32 i = 0; i < hull1->edgeCount; i += 2) @@ -132,8 +134,8 @@ b3EdgeQuery b3QueryEdgeSeparation(const b3Transform& xf1, const b3Hull* hull1, b3Vec3 E1 = Q1 - P1; // The Gauss Map of edge 1. - b3Vec3 U1 = xf.rotation * hull1->GetPlane(edge1->face).normal; - b3Vec3 V1 = xf.rotation * hull1->GetPlane(twin1->face).normal; + b3Vec3 U1 = b3Mul(xf.rotation, hull1->GetPlane(edge1->face).normal); + b3Vec3 V1 = b3Mul(xf.rotation, hull1->GetPlane(twin1->face).normal); // Loop through the second hull's unique edges. for (u32 j = 0; j < hull2->edgeCount; j += 2) @@ -154,7 +156,7 @@ b3EdgeQuery b3QueryEdgeSeparation(const b3Transform& xf1, const b3Hull* hull1, // Negate the Gauss Map 2 for account for the MD. if (b3IsMinkowskiFace(U1, V1, -E1, -U2, -V2, -E2)) { - float32 separation = b3Project(P1, E1, P2, E2, C1); + scalar separation = b3Project(P1, Q1, E1, P2, E2, C1); if (separation > maxSeparation) { maxSeparation = separation; @@ -176,7 +178,7 @@ b3EdgeQuery b3QueryEdgeSeparation(const b3Transform& xf1, const b3Hull* hull1, b3SATCacheType b3FeatureCache::ReadState( const b3Transform& xf1, const b3Hull* hull1, - const b3Transform& xf2, const b3Hull* hull2, float32 totalRadius) + const b3Transform& xf2, const b3Hull* hull2, scalar totalRadius) { // If the cache was empty or flushed choose an arbitrary feature pair. if (m_featurePair.state == b3SATCacheType::e_empty) @@ -207,12 +209,12 @@ b3SATCacheType b3FeatureCache::ReadState( b3SATCacheType b3FeatureCache::ReadFace( const b3Transform& xf1, const b3Hull* hull1, - const b3Transform& xf2, const b3Hull* hull2, float32 totalRadius) + const b3Transform& xf2, const b3Hull* hull2, scalar totalRadius) { // Perform computations in the local space of the second hull. b3Transform xf = b3MulT(xf2, xf1); b3Plane plane = xf * hull1->GetPlane(m_featurePair.index1); - float32 separation = b3Project(hull2, plane); + scalar separation = b3Project(hull2, plane); if (separation > totalRadius) { return e_separation; @@ -222,7 +224,7 @@ b3SATCacheType b3FeatureCache::ReadFace( b3SATCacheType b3FeatureCache::ReadEdge( const b3Transform& xf1, const b3Hull* hull1, - const b3Transform& xf2, const b3Hull* hull2, float32 totalRadius) + const b3Transform& xf2, const b3Hull* hull2, scalar totalRadius) { u32 i = m_featurePair.index1; u32 j = m_featurePair.index2; @@ -242,8 +244,8 @@ b3SATCacheType b3FeatureCache::ReadEdge( b3Vec3 E1 = Q1 - P1; // The Gauss Map of edge 1. - b3Vec3 U1 = xf.rotation * hull1->GetPlane(edge1->face).normal; - b3Vec3 V1 = xf.rotation * hull1->GetPlane(twin1->face).normal; + b3Vec3 U1 = b3Mul(xf.rotation, hull1->GetPlane(edge1->face).normal); + b3Vec3 V1 = b3Mul(xf.rotation, hull1->GetPlane(twin1->face).normal); const b3HalfEdge* edge2 = hull2->GetEdge(j); const b3HalfEdge* twin2 = hull2->GetEdge(j + 1); @@ -261,7 +263,7 @@ b3SATCacheType b3FeatureCache::ReadEdge( // Negate the Gauss Map 2 for account for the MD. if (b3IsMinkowskiFace(U1, V1, -E1, -U2, -V2, -E2)) { - float32 separation = b3Project(P1, E1, P2, E2, C1); + scalar separation = b3Project(P1, Q1, E1, P2, E2, C1); if (separation > totalRadius) { return b3SATCacheType::e_separation; diff --git a/src/bounce/collision/sat/sat_edge_and_hull.cpp b/src/bounce/collision/sat/sat_hull_and_edge.cpp similarity index 56% rename from src/bounce/collision/sat/sat_edge_and_hull.cpp rename to src/bounce/collision/sat/sat_hull_and_edge.cpp index 3df7dc5..596b6dc 100644 --- a/src/bounce/collision/sat/sat_edge_and_hull.cpp +++ b/src/bounce/collision/sat/sat_hull_and_edge.cpp @@ -16,30 +16,34 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include +#include #include +#include -float32 b3ProjectEdge(const b3Capsule* hull, const b3Plane& plane) +scalar b3ProjectEdge(const b3Capsule* hull, const b3Plane& plane) { b3Vec3 support = hull->GetVertex(hull->GetSupportVertex(-plane.normal)); return b3Distance(support, plane); } -b3FaceQuery b3QueryFaceSeparation(const b3Transform& xf1, const b3Capsule* hull1, - const b3Transform& xf2, const b3Hull* hull2) +b3FaceQuery b3QueryFaceSeparation(const b3Transform& xf1, const b3Hull* hull1, + const b3Transform& xf2, const b3Capsule* hull2) { // Perform computations in the local space of the first hull. b3Transform xf = b3MulT(xf1, xf2); + b3Capsule localHull2; + localHull2.vertex1 = xf * hull2->vertex1; + localHull2.vertex2 = xf * hull2->vertex2; + // Here greater means less than since is a signed distance. u32 maxIndex = 0; - float32 maxSeparation = -B3_MAX_FLOAT; + scalar maxSeparation = -B3_MAX_SCALAR; - for (u32 i = 0; i < hull2->faceCount; ++i) + for (u32 i = 0; i < hull1->faceCount; ++i) { - b3Plane plane = b3Mul(xf, hull2->GetPlane(i)); - float32 separation = b3ProjectEdge(hull1, plane); + b3Plane plane = hull1->GetPlane(i); + scalar separation = b3ProjectEdge(&localHull2, plane); if (separation > maxSeparation) { maxIndex = i; @@ -56,37 +60,37 @@ b3FaceQuery b3QueryFaceSeparation(const b3Transform& xf1, const b3Capsule* hull1 // Qualify the two hull normals against the plane of the ring of the capsule. bool b3IsMinkowskiFaceEdge(const b3Vec3& N, const b3Vec3& C, const b3Vec3& D) { - return b3Dot(N, C) * b3Dot(N, D) < 0.0f; + return b3Dot(N, C) * b3Dot(N, D) < scalar(0); } -float32 b3ProjectEdge(const b3Vec3& P1, const b3Vec3& E1, - const b3Vec3& P2, const b3Vec3& E2, const b3Vec3& C2) +scalar b3ProjectEdge(const b3Vec3& P1, const b3Vec3& E1, const b3Vec3& C1, + const b3Vec3& P2, const b3Vec3& E2) { - float32 L1 = b3Length(E1); + scalar L1 = b3Length(E1); if (L1 < B3_LINEAR_SLOP) { - return -B3_MAX_FLOAT; + return -B3_MAX_SCALAR; } - float32 L2 = b3Length(E2); + scalar L2 = b3Length(E2); if (L2 < B3_LINEAR_SLOP) { - return -B3_MAX_FLOAT; + return -B3_MAX_SCALAR; } // Skip over almost parallel edges. - const float32 kTol = 0.005f; + const scalar kTol = 0.005f; b3Vec3 E1_x_E2 = b3Cross(E1, E2); - float32 L = b3Length(E1_x_E2); + scalar L = b3Length(E1_x_E2); if (L < kTol * L1 * L2) { - return -B3_MAX_FLOAT; + return -B3_MAX_SCALAR; } // Ensure consistent normal orientation to hull B. - b3Vec3 N = (1.0f / L) * E1_x_E2; - if (b3Dot(N, P2 - C2) > 0.0f) + b3Vec3 N = (scalar(1) / L) * E1_x_E2; + if (b3Dot(N, P1 - C1) < scalar(0)) { N = -N; } @@ -95,38 +99,37 @@ float32 b3ProjectEdge(const b3Vec3& P1, const b3Vec3& E1, return b3Dot(N, P2 - P1); } -b3EdgeQuery b3QueryEdgeSeparation(const b3Transform& xf1, const b3Capsule* hull1, const b3Transform& xf2, const b3Hull* hull2) +b3EdgeQuery b3QueryEdgeSeparation(const b3Transform& xf1, const b3Hull* hull1, const b3Transform& xf2, const b3Capsule* hull2) { // Query minimum edge separation. u32 maxIndex = 0; - float32 maxSeparation = -B3_MAX_FLOAT; + scalar maxSeparation = -B3_MAX_SCALAR; - // Perform computations in the local space of the second hull. - b3Transform xf = b3MulT(xf2, xf1); + // Perform computations in the local space of the first hull. + b3Transform xf = b3MulT(xf1, xf2); + b3Vec3 C1 = hull1->centroid; - b3Vec3 P1 = b3Mul(xf, hull1->vertices[0]); - b3Vec3 Q1 = b3Mul(xf, hull1->vertices[1]); - b3Vec3 E1 = Q1 - P1; + b3Vec3 P2 = xf * hull2->vertex1; + b3Vec3 Q2 = xf * hull2->vertex2; + b3Vec3 E2 = Q2 - P2; - b3Vec3 C2 = hull2->centroid; - - for (u32 i = 0; i < hull2->edgeCount; i += 2) + for (u32 i = 0; i < hull1->edgeCount; i += 2) { - const b3HalfEdge* edge2 = hull2->GetEdge(i); - const b3HalfEdge* twin2 = hull2->GetEdge(i + 1); + const b3HalfEdge* edge1 = hull1->GetEdge(i); + const b3HalfEdge* twin1 = hull1->GetEdge(i + 1); - B3_ASSERT(edge2->twin == i + 1 && twin2->twin == i); + B3_ASSERT(edge1->twin == i + 1 && twin1->twin == i); - b3Vec3 P2 = hull2->GetVertex(edge2->origin); - b3Vec3 Q2 = hull2->GetVertex(twin2->origin); - b3Vec3 E2 = Q2 - P2; + b3Vec3 P1 = hull1->GetVertex(edge1->origin); + b3Vec3 Q1 = hull1->GetVertex(twin1->origin); + b3Vec3 E1 = Q1 - P1; - b3Vec3 U2 = hull2->GetPlane(edge2->face).normal; - b3Vec3 V2 = hull2->GetPlane(twin2->face).normal; + b3Vec3 U1 = hull1->GetPlane(edge1->face).normal; + b3Vec3 V1 = hull1->GetPlane(twin1->face).normal; - if (b3IsMinkowskiFaceEdge(E1, U2, V2)) + if (b3IsMinkowskiFaceEdge(E2, U1, V1)) { - float32 separation = b3ProjectEdge(P1, E1, P2, E2, C2); + scalar separation = b3ProjectEdge(P1, E1, C1, P2, E2); if (separation > maxSeparation) { maxSeparation = separation; @@ -136,8 +139,8 @@ b3EdgeQuery b3QueryEdgeSeparation(const b3Transform& xf1, const b3Capsule* hull1 } b3EdgeQuery out; - out.index1 = 0; - out.index2 = maxIndex; + out.index1 = maxIndex; + out.index2 = B3_MAX_U32; out.separation = maxSeparation; return out; -} +} \ No newline at end of file diff --git a/src/bounce/collision/sat/sat_vertex_and_hull.cpp b/src/bounce/collision/sat/sat_hull_and_vertex.cpp similarity index 74% rename from src/bounce/collision/sat/sat_vertex_and_hull.cpp rename to src/bounce/collision/sat/sat_hull_and_vertex.cpp index 10a90ce..80157e7 100644 --- a/src/bounce/collision/sat/sat_vertex_and_hull.cpp +++ b/src/bounce/collision/sat/sat_hull_and_vertex.cpp @@ -16,29 +16,29 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include +#include #include +#include -float32 b3ProjectVertex(const b3Sphere* hull, const b3Plane& plane) +scalar b3ProjectVertex(const b3Sphere* hull, const b3Plane& plane) { b3Vec3 support = hull->GetVertex(hull->GetSupportVertex(-plane.normal)); return b3Distance(support, plane); } -b3FaceQuery b3QueryFaceSeparation(const b3Transform& xf1, const b3Sphere* hull, - const b3Transform& xf2, const b3Hull* hull2) +b3FaceQuery b3QueryFaceSeparation(const b3Transform& xf1, const b3Hull* hull1, + const b3Transform& xf2, const b3Sphere* hull2) { - // Perform computations in the local space of the second hull. - b3Vec3 support = b3MulT(xf2, b3Mul(xf1, hull->vertex)); + // Perform computations in the local space of the first hull. + b3Vec3 support = b3MulT(xf1, b3Mul(xf2, hull2->vertex)); u32 maxIndex = 0; - float32 maxSeparation = -B3_MAX_FLOAT; + scalar maxSeparation = -B3_MAX_SCALAR; - for (u32 i = 0; i < hull2->faceCount; ++i) + for (u32 i = 0; i < hull1->faceCount; ++i) { - b3Plane plane = hull2->GetPlane(i); - float32 separation = b3Distance(support, plane); + b3Plane plane = hull1->GetPlane(i); + scalar separation = b3Distance(support, plane); if (separation > maxSeparation) { diff --git a/src/bounce/collision/shapes/hull.cpp b/src/bounce/collision/shapes/hull.cpp index 74cecd7..0090641 100644 --- a/src/bounce/collision/shapes/hull.cpp +++ b/src/bounce/collision/shapes/hull.cpp @@ -56,7 +56,7 @@ void b3Hull::Validate(const b3Face* face) const void b3Hull::Validate(const b3HalfEdge* e) const { - u8 edgeIndex = (u8)(e - edges); + u32 edgeIndex = (u32)(e - edges); const b3HalfEdge* twin = edges + e->twin; @@ -75,4 +75,149 @@ void b3Hull::Validate(const b3HalfEdge* e) const const b3HalfEdge* next = edges + e->next; e = edges + next->twin; } while (e != begin); -} \ No newline at end of file +} + +void b3Hull::Scale(const b3Vec3& scale) +{ + // https://irlanrobson.github.io/2019/10/01/how-to-transform-a-plane,-with-scale/ + B3_ASSERT(scale.x > scalar(0)); + B3_ASSERT(scale.y > scalar(0)); + B3_ASSERT(scale.z > scalar(0)); + + for (u32 i = 0; i < vertexCount; ++i) + { + vertices[i] = b3MulCW(scale, vertices[i]); + } + + // M = S + // M^-1 = S^-1 + // (M^-1)^T = (S^-1)^T = S^-1 + b3Vec3 inv_scale; + inv_scale.x = scalar(1) / scale.x; + inv_scale.y = scalar(1) / scale.y; + inv_scale.z = scalar(1) / scale.z; + + for (u32 i = 0; i < faceCount; ++i) + { + b3Plane oldPlane = planes[i]; + + b3Plane newPlane; + newPlane.normal = b3MulCW(inv_scale, oldPlane.normal); + + scalar len = newPlane.normal.Normalize(); + B3_ASSERT(len > scalar(0)); + + newPlane.offset = oldPlane.offset / len; + + planes[i] = newPlane; + } + + centroid = b3MulCW(scale, centroid); +} + +void b3Hull::Rotate(const b3Quat& rotation) +{ + for (u32 i = 0; i < vertexCount; ++i) + { + vertices[i] = b3Mul(rotation, vertices[i]); + } + + for (u32 i = 0; i < faceCount; ++i) + { + planes[i].normal = b3Mul(rotation, planes[i].normal); + } +} + +void b3Hull::Translate(const b3Vec3& translation) +{ + for (u32 i = 0; i < vertexCount; ++i) + { + vertices[i] += translation; + } + + for (u32 i = 0; i < faceCount; ++i) + { + planes[i].offset += b3Dot(planes[i].normal, translation); + } +} + +void b3Hull::Transform(const b3Transform& xf, const b3Vec3& scale) +{ + // https://irlanrobson.github.io/2019/10/01/how-to-transform-a-plane,-with-scale/ + B3_ASSERT(scale.x > scalar(0)); + B3_ASSERT(scale.y > scalar(0)); + B3_ASSERT(scale.z > scalar(0)); + + for (u32 i = 0; i < vertexCount; ++i) + { + vertices[i] = b3Mul(xf.rotation, b3MulCW(scale, vertices[i])) + xf.translation; + } + + // M = R * S + // M^-1 = S^-1 * R^-1 + // (M^-1)^T = (R^-1)^T * (S^-1)^T = R * S^-1 + b3Vec3 inv_scale; + inv_scale.x = scalar(1) / scale.x; + inv_scale.y = scalar(1) / scale.y; + inv_scale.z = scalar(1) / scale.z; + + for (u32 i = 0; i < faceCount; ++i) + { + b3Plane oldPlane = planes[i]; + + b3Plane newPlane; + newPlane.normal = b3Mul(xf.rotation, b3MulCW(inv_scale, oldPlane.normal)); + + scalar len = newPlane.normal.Normalize(); + B3_ASSERT(len > scalar(0)); + + newPlane.offset = oldPlane.offset / len + b3Dot(newPlane.normal, xf.translation); + + planes[i] = newPlane; + } + + centroid = b3Mul(xf.rotation, b3MulCW(scale, centroid)) + xf.translation; +} + +void b3Hull::Dump() const +{ + b3Log("b3Vec3 vertices[%d]\n", vertexCount); + b3Log("b3HalfEdge edges[%d]\n", edgeCount); + b3Log("b3Face faces[%d]\n", faceCount); + b3Log("b3Plane planes[%d]\n", faceCount); + + for (u32 i = 0; i < vertexCount; ++i) + { + b3Vec3 v = vertices[i]; + + b3Log("vertices[%d] = b3Vec3(%.20f, %.20f, %.20f);\n", i, v.x, v.y, v.z); + } + + b3Log("\n"); + + for (u32 i = 0; i < edgeCount; ++i) + { + b3HalfEdge* he = edges + i; + + b3Log("edges[%d] = b3MakeEdge(%d, %d, %d, %d, %d);\n", i, he->origin, he->twin, he->face, he->prev, he->next); + } + + b3Log("\n"); + + for (u32 i = 0; i < faceCount; ++i) + { + b3Log("faces[%d].edge = %d;\n", i, faces[i].edge); + } + + b3Log("\n"); + + for (u32 i = 0; i < faceCount; ++i) + { + b3Plane plane = planes[i]; + + b3Log("planes[%d].normal = b3Vec3(%.20f, %.20f, %.20f);\n", i, plane.normal.x, plane.normal.y, plane.normal.z); + b3Log("planes[%d].offset = %.20f;\n", i, plane.offset); + } + + b3Log("centroid = b3Vec3(%.20f, %.20f, %.20f);\n", centroid.x, centroid.y, centroid.z); +} diff --git a/src/bounce/collision/shapes/mesh.cpp b/src/bounce/collision/shapes/mesh.cpp new file mode 100644 index 0000000..bf610b3 --- /dev/null +++ b/src/bounce/collision/shapes/mesh.cpp @@ -0,0 +1,169 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +b3Mesh::b3Mesh() +{ + triangleWings = nullptr; +} + +b3Mesh::~b3Mesh() +{ + b3Free(triangleWings); +} + +void b3Mesh::BuildTree() +{ + b3AABB* aabbs = (b3AABB*)b3Alloc(triangleCount * sizeof(b3AABB)); + for (u32 i = 0; i < triangleCount; ++i) + { + aabbs[i] = GetTriangleAABB(i); + } + + tree.Build(aabbs, triangleCount); + + b3Free(aabbs); +} + +void b3Mesh::BuildAdjacency() +{ + B3_ASSERT(triangleWings == nullptr); + triangleWings = (b3MeshTriangleWings*)b3Alloc(triangleCount * sizeof(b3MeshTriangleWings)); + + // Assume the edges are open edges. + for (u32 i = 0; i < triangleCount; ++i) + { + b3MeshTriangleWings* ws = triangleWings + i; + + ws->u1 = B3_NULL_VERTEX; + ws->u2 = B3_NULL_VERTEX; + ws->u3 = B3_NULL_VERTEX; + } + + // Connect the edges. + for (u32 i = 0; i < triangleCount; ++i) + { + b3MeshTriangle* t1 = triangles + i; + b3MeshTriangleWings* t1ws = triangleWings + i; + + for (u32 j1 = 0; j1 < 3; ++j1) + { + u32 k1 = j1 + 1 < 3 ? j1 + 1 : 0; + + u32 t1v1 = t1->GetVertex(j1); + u32 t1v2 = t1->GetVertex(k1); + + u32& u1 = t1ws->GetVertex(j1); + + if (u1 != B3_NULL_VERTEX) + { + // The edge is already connected. + continue; + } + + for (u32 j = i + 1; j < triangleCount; ++j) + { + b3MeshTriangle* t2 = triangles + j; + b3MeshTriangleWings* t2ws = triangleWings + j; + + for (u32 j2 = 0; j2 < 3; ++j2) + { + u32 k2 = j2 + 1 < 3 ? j2 + 1 : 0; + + u32 t2v1 = t2->GetVertex(j2); + u32 t2v2 = t2->GetVertex(k2); + + u32& u2 = t2ws->GetVertex(j2); + + if (t1v1 == t2v2 && t1v2 == t2v1) + { + // Both triangles have the same order. + + // The triangles are adjacent. + + // Non-shared vertex on triangle 1. + u32 k3 = k1 + 1 < 3 ? k1 + 1 : 0; + u2 = t1->GetVertex(k3); + + // Non-shared vertex on triangle 2. + u32 k4 = k2 + 1 < 3 ? k2 + 1 : 0; + u1 = t2->GetVertex(k4); + + break; + } + + if (t1v1 == t2v1 && t1v2 == t2v2) + { + // The triangles have different order. + + // The triangles are adjacent. + + // Non-shared vertex on triangle 1. + u32 k3 = k1 + 1 < 3 ? k1 + 1 : 0; + u2 = t1->GetVertex(k3); + + // Non-shared vertex on triangle 2. + u32 k4 = k2 + 1 < 3 ? k2 + 1 : 0; + u1 = t2->GetVertex(k4); + + break; + } + } + + if (u1 != B3_NULL_VERTEX) + { + // The edge has been connected. + break; + } + } + } + } +} + +void b3Mesh::Scale(const b3Vec3& scale) +{ + for (u32 i = 0; i < vertexCount; ++i) + { + vertices[i] = b3MulCW(scale, vertices[i]); + } +} + +void b3Mesh::Rotate(const b3Quat& rotation) +{ + for (u32 i = 0; i < vertexCount; ++i) + { + vertices[i] = b3Mul(rotation, vertices[i]); + } +} + +void b3Mesh::Translate(const b3Vec3& translation) +{ + for (u32 i = 0; i < vertexCount; ++i) + { + vertices[i] += translation; + } +} + +void b3Mesh::Transform(const b3Transform& xf, const b3Vec3& scale) +{ + for (u32 i = 0; i < vertexCount; ++i) + { + vertices[i] = b3Mul(xf, b3MulCW(scale, vertices[i])); + } +} \ No newline at end of file diff --git a/src/bounce/collision/shapes/qhull.cpp b/src/bounce/collision/shapes/qhull.cpp index 1c5b259..3b77093 100644 --- a/src/bounce/collision/shapes/qhull.cpp +++ b/src/bounce/collision/shapes/qhull.cpp @@ -49,7 +49,7 @@ static b3Vec3 b3ComputeCentroid(b3QHull* hull) B3_ASSERT(hull->vertexCount >= 4); - float32 volume = 0.0f; + scalar volume = scalar(0); b3Vec3 centroid; centroid.SetZero(); @@ -59,7 +59,7 @@ static b3Vec3 b3ComputeCentroid(b3QHull* hull) { s += hull->vertices[i]; } - s /= float32(hull->vertexCount); + s /= scalar(hull->vertexCount); for (u32 i = 0; i < hull->faceCount; ++i) { @@ -80,7 +80,7 @@ static b3Vec3 b3ComputeCentroid(b3QHull* hull) b3Vec3 v3 = hull->GetVertex(i3) - s; // Signed tetrahedron volume - float32 D = b3Det(v1, v2, v3); + scalar D = b3Det(v1, v2, v3); // Contribution to the mass volume += D; @@ -96,7 +96,7 @@ static b3Vec3 b3ComputeCentroid(b3QHull* hull) // Centroid B3_ASSERT(volume > B3_EPSILON); - centroid /= 4.0f * volume; + centroid /= scalar(4) * volume; centroid += s; return centroid; } @@ -159,7 +159,7 @@ void b3QHull::Set(u32 vtxStride, const void* vtxBase, u32 vtxCount, bool simplif { s += v->position; } - s /= float32(primary.GetVertexList().count); + s /= scalar(primary.GetVertexList().count); primary.Translate(-s); @@ -171,7 +171,7 @@ void b3QHull::Set(u32 vtxStride, const void* vtxBase, u32 vtxCount, bool simplif for (qhFace* f = primary.GetFaceList().head; f; f = f->next) { b3Plane plane = f->plane; - B3_ASSERT(plane.offset > 0.0f); + B3_ASSERT(plane.offset > scalar(0)); b3Vec3 v = plane.normal / plane.offset; b3Vec3 vn = plane.normal; @@ -184,7 +184,7 @@ void b3QHull::Set(u32 vtxStride, const void* vtxBase, u32 vtxCount, bool simplif b3Vec3 dvn = b3Normalize(dv); // ~45 degrees - const float32 kTol = 0.7f; + const scalar kTol = scalar(0.7); if (b3Dot(vn, dvn) > kTol) { @@ -225,7 +225,7 @@ void b3QHull::Set(u32 vtxStride, const void* vtxBase, u32 vtxCount, bool simplif for (qhFace* f = dual.GetFaceList().head; f; f = f->next) { b3Plane plane = f->plane; - B3_ASSERT(plane.offset > 0.0f); + B3_ASSERT(plane.offset > scalar(0)); b3Vec3 v = plane.normal / plane.offset; bool unique = true; @@ -268,13 +268,13 @@ void b3QHull::Set(u32 vtxStride, const void* vtxBase, u32 vtxCount, bool simplif b3UniqueStackArray es; // Add vertices to the map - for (qhVertex* vertex = hull.GetVertexList().head; vertex != NULL; vertex = vertex->next) + for (qhVertex* vertex = hull.GetVertexList().head; vertex != nullptr; vertex = vertex->next) { vs.PushBack(vertex); } // Add half-edges to the map - for (qhFace* face = hull.GetFaceList().head; face != NULL; face = face->next) + for (qhFace* face = hull.GetFaceList().head; face != nullptr; face = face->next) { // Add half-edges qhHalfEdge* begin = face->edge; @@ -298,7 +298,7 @@ void b3QHull::Set(u32 vtxStride, const void* vtxBase, u32 vtxCount, bool simplif // Build and link the features u32 iface = 0; - for (qhFace* face = hull.GetFaceList().head; face != NULL; face = face->next) + for (qhFace* face = hull.GetFaceList().head; face != nullptr; face = face->next) { // Build and link the half-edges b3Face* hface = hullFaces.Get(iface); @@ -329,7 +329,11 @@ void b3QHull::Set(u32 vtxStride, const void* vtxBase, u32 vtxCount, bool simplif qhHalfEdge* next = edge->next; u32 inext = es.PushBack(next); + + qhHalfEdge* prev = edge->prev; + u32 iprev = es.PushBack(prev); + hullEdges[iedge].prev = iprev; hullEdges[iedge].next = inext; edge = next; @@ -353,12 +357,12 @@ void b3QHull::Set(u32 vtxStride, const void* vtxBase, u32 vtxCount, bool simplif centroid = b3ComputeCentroid(this); } -void b3QHull::SetAsSphere(float32 radius) +void b3QHull::SetAsSphere(scalar radius, u32 subdivisions) { - B3_ASSERT(radius > 0.0f); + B3_ASSERT(radius > scalar(0)); smMesh mesh; - smCreateMesh(mesh, 2); + smCreateMesh(mesh, subdivisions); for (u32 i = 0; i < mesh.vertexCount; ++i) { @@ -368,15 +372,15 @@ void b3QHull::SetAsSphere(float32 radius) Set(sizeof(b3Vec3), mesh.vertices, mesh.vertexCount, false); } -void b3QHull::SetAsCylinder(float32 radius, float32 ey) +void b3QHull::SetAsCylinder(scalar radius, scalar ey, u32 segments) { - B3_ASSERT(radius > 0.0f); - B3_ASSERT(ey > 0.0f); + B3_ASSERT(radius > scalar(0)); + B3_ASSERT(ey > scalar(0)); - float32 height = 2.0f * ey; + scalar height = scalar(2) * ey; cymMesh mesh; - cymCreateMesh(mesh, 20); + cymCreateMesh(mesh, segments); for (u32 i = 0; i < mesh.vertexCount; ++i) { @@ -388,24 +392,23 @@ void b3QHull::SetAsCylinder(float32 radius, float32 ey) Set(sizeof(b3Vec3), mesh.vertices, mesh.vertexCount, false); } -void b3QHull::SetAsCone(float32 radius, float32 ey) +void b3QHull::SetAsCone(scalar radius, scalar ey, u32 segments) { - B3_ASSERT(radius > 0.0f); - B3_ASSERT(ey > 0.0f); + B3_ASSERT(radius > scalar(0)); + B3_ASSERT(ey > scalar(0)); - const u32 kEdgeCount = 20; - const u32 kVertexCount = 2 * kEdgeCount + 1; - b3Vec3 vs[kVertexCount]; + u32 vertexCount = 2 * segments + 1; + b3Vec3* vs = (b3Vec3*)b3Alloc(vertexCount * sizeof(b3Vec3)); u32 count = 0; - float32 kAngleInc = 2.0f * B3_PI / float32(kEdgeCount); - b3Quat q = b3QuatRotationY(kAngleInc); + scalar angleInc = scalar(2) * B3_PI / scalar(segments); + b3Quat q = b3QuatRotationY(angleInc); - b3Vec3 center(0.0f, -ey, 0.0f); - b3Vec3 n1(1.0f, 0.0f, 0.0f); + b3Vec3 center(scalar(0), -ey, scalar(0)); + b3Vec3 n1(scalar(1), scalar(0), scalar(0)); b3Vec3 v1 = center + radius * n1; - for (u32 i = 0; i < kEdgeCount; ++i) + for (u32 i = 0; i < segments; ++i) { b3Vec3 n2 = b3Mul(q, n1); b3Vec3 v2 = center + radius * n2; @@ -417,8 +420,10 @@ void b3QHull::SetAsCone(float32 radius, float32 ey) v1 = v2; } - vs[count++].Set(0.0f, ey, 0.0f); + vs[count++].Set(scalar(0), ey, scalar(0)); // Set Set(sizeof(b3Vec3), vs, count, false); + + b3Free(vs); } \ No newline at end of file diff --git a/src/bounce/collision/shapes/sdf.cpp b/src/bounce/collision/shapes/sdf.cpp new file mode 100644 index 0000000..3e7368b --- /dev/null +++ b/src/bounce/collision/shapes/sdf.cpp @@ -0,0 +1,614 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +#include +#include + +struct b3ByteStream +{ + b3ByteStream(const char* _stream, u32 _size) + { + stream = _stream; + size = _size; + offset = 0; + } + + template + void Read(T& out) + { + u32 out_size = sizeof(T); + B3_ASSERT(offset + out_size <= size); + memcpy((char*)& out, stream + offset, out_size); + offset += out_size; + } + + const char* stream; + u32 size; + u32 offset; +}; + +b3SDF::b3SDF() +{ +} + +b3SDF::~b3SDF() +{ + for (u32 i = 0; i < m_nodeCount; ++i) + { + b3Free(m_nodes[i].values); + } + b3Free(m_nodes); + + for (u32 i = 0; i < m_cellCount; ++i) + { + b3Free(m_cells[i].values); + } + b3Free(m_cells); + + for (u32 i = 0; i < m_cellMapCount; ++i) + { + b3Free(m_cell_map[i].values); + } + b3Free(m_cell_map); +} + +bool b3SDF::Load(const char* filename) +{ + FILE* file = fopen(filename, "rb"); + if (file == nullptr) + { + return false; + } + + fseek(file, 0, SEEK_END); + long file_size = ftell(file); + fseek(file, 0, SEEK_SET); + char* file_buffer = (char*)b3Alloc(file_size); + fread(file_buffer, file_size, 1, file); + fclose(file); + + b3ByteStream stream(file_buffer, file_size); + + { + double out[6]; + stream.Read(out); + + m_domain.lowerBound[0] = out[0]; + m_domain.lowerBound[1] = out[1]; + m_domain.lowerBound[2] = out[2]; + + m_domain.upperBound[0] = out[3]; + m_domain.upperBound[1] = out[4]; + m_domain.upperBound[2] = out[5]; + } + + { + unsigned int out[3]; + stream.Read(out); + + m_resolution[0] = out[0]; + m_resolution[1] = out[1]; + m_resolution[2] = out[2]; + } + + { + double out[3]; + stream.Read(out); + + m_cell_size[0] = out[0]; + m_cell_size[1] = out[1]; + m_cell_size[2] = out[2]; + } + + { + double out[3]; + stream.Read(out); + + m_inv_cell_size[0] = out[0]; + m_inv_cell_size[1] = out[1]; + m_inv_cell_size[2] = out[2]; + } + + { + std::size_t out; + stream.Read(out); + + m_n_cells = out; + } + + { + std::size_t out; + stream.Read(out); + + m_n_fields = out; + } + + { + std::size_t node_count; + stream.Read(node_count); + m_nodeCount = node_count; + m_nodes = (b3SDFNodeArray*)b3Alloc(node_count * sizeof(b3SDFNodeArray)); + for (std::size_t i = 0; i < node_count; ++i) + { + std::size_t value_count; + stream.Read(value_count); + + b3SDFNodeArray* node = m_nodes + i; + node->count = value_count; + node->values = (double*)b3Alloc(value_count * sizeof(double)); + for (std::size_t j = 0; j < value_count; ++j) + { + double& out = node->values[j]; + stream.Read(out); + } + } + } + + { + std::size_t cell_count; + stream.Read(cell_count); + m_cellCount = cell_count; + m_cells = (b3SDFCellArray*)b3Alloc(cell_count * sizeof(b3SDFCellArray)); + for (std::size_t i = 0; i < cell_count; ++i) + { + std::size_t value_count; + stream.Read(value_count); + + b3SDFCellArray* cell = m_cells + i; + cell->count = value_count; + cell->values = (b3Cell32*)b3Alloc(value_count * sizeof(b3Cell32)); + for (std::size_t j = 0; j < value_count; ++j) + { + b3Cell32& out = cell->values[j]; + stream.Read(out); + } + } + } + + { + std::size_t cell_map_count; + stream.Read(cell_map_count); + m_cellMapCount = cell_map_count; + m_cell_map = (b3SDFCellMapArray*)b3Alloc(cell_map_count * sizeof(b3SDFCellMapArray)); + for (std::size_t i = 0; i < cell_map_count; ++i) + { + std::size_t value_count; + stream.Read(value_count); + + b3SDFCellMapArray* maps = m_cell_map + i; + maps->count = value_count; + maps->values = (unsigned int*)b3Alloc(value_count * sizeof(unsigned int)); + for (std::size_t j = 0; j < value_count; ++j) + { + unsigned int& out = maps->values[j]; + stream.Read(out); + } + } + } + + B3_ASSERT(stream.offset == stream.size); + + b3Free(file_buffer); + + return true; +} + +b3MultiIndex b3SDF::singleToMultiIndex(unsigned int l) const +{ + unsigned int n01 = m_resolution[0] * m_resolution[1]; + unsigned int k = l / n01; + unsigned int temp = l % n01; + unsigned int j = temp / m_resolution[0]; + unsigned int i = temp % m_resolution[0]; + + b3MultiIndex mi; + mi.v[0] = i; + mi.v[1] = j; + mi.v[2] = k; + + return mi; +} + +unsigned int b3SDF::multiToSingleIndex(b3MultiIndex const& ijk) const +{ + return m_resolution[1] * m_resolution[0] * ijk[2] + m_resolution[0] * ijk[1] + ijk[0]; +} + +b3AABB b3SDF::subdomain(b3MultiIndex const& ijk) const +{ + b3Vec3 tmp; + tmp[0] = m_cell_size[0] * (double)ijk[0]; + tmp[1] = m_cell_size[1] * (double)ijk[1]; + tmp[2] = m_cell_size[2] * (double)ijk[2]; + + b3Vec3 origin = m_domain.lowerBound + tmp; + + b3AABB aabb; + aabb.lowerBound = origin; + aabb.upperBound = origin + m_cell_size; + + return aabb; +} + +b3AABB b3SDF::subdomain(unsigned int l) const +{ + return subdomain(singleToMultiIndex(l)); +} + +struct b3ShapeMatrix +{ + double& operator[](int i) + { + return v[i]; + } + + const double& operator[](int i) const + { + return v[i]; + } + + double v[32]; +}; + +struct b3ShapeGradients +{ + void bottomRowsMul(int row, double s) + { + for (int i = 32 - row; i < 32; ++i) + { + v[i] *= s; + } + } + + void topRowsDiv(int row, double s) + { + for (int i = 0; i < row; ++i) + { + v[i] /= s; + } + } + + scalar& operator()(int i, int j) + { + return v[i][j]; + } + + b3Vec3 v[32]; +}; + +static b3ShapeMatrix shape_function_(b3Vec3 const& xi, b3ShapeGradients* gradient = nullptr) +{ + b3ShapeMatrix res; + + double x = (double)xi[0]; + double y = (double)xi[1]; + double z = (double)xi[2]; + + double x2 = x * x; + double y2 = y * y; + double z2 = z * z; + + double _1mx = 1.0 - x; + double _1my = 1.0 - y; + double _1mz = 1.0 - z; + + double _1px = 1.0 + x; + double _1py = 1.0 + y; + double _1pz = 1.0 + z; + + double _1m3x = 1.0 - 3.0 * x; + double _1m3y = 1.0 - 3.0 * y; + double _1m3z = 1.0 - 3.0 * z; + + double _1p3x = 1.0 + 3.0 * x; + double _1p3y = 1.0 + 3.0 * y; + double _1p3z = 1.0 + 3.0 * z; + + double _1mxt1my = _1mx * _1my; + double _1mxt1py = _1mx * _1py; + double _1pxt1my = _1px * _1my; + double _1pxt1py = _1px * _1py; + + double _1mxt1mz = _1mx * _1mz; + double _1mxt1pz = _1mx * _1pz; + double _1pxt1mz = _1px * _1mz; + double _1pxt1pz = _1px * _1pz; + + double _1myt1mz = _1my * _1mz; + double _1myt1pz = _1my * _1pz; + double _1pyt1mz = _1py * _1mz; + double _1pyt1pz = _1py * _1pz; + + double _1mx2 = 1.0 - x2; + double _1my2 = 1.0 - y2; + double _1mz2 = 1.0 - z2; + + // Corner nodes. + double fac = 1.0 / 64.0 * (9.0 * (x2 + y2 + z2) - 19.0); + res[0] = fac * _1mxt1my * _1mz; + res[1] = fac * _1pxt1my * _1mz; + res[2] = fac * _1mxt1py * _1mz; + res[3] = fac * _1pxt1py * _1mz; + res[4] = fac * _1mxt1my * _1pz; + res[5] = fac * _1pxt1my * _1pz; + res[6] = fac * _1mxt1py * _1pz; + res[7] = fac * _1pxt1py * _1pz; + + // Edge nodes. + + fac = 9.0 / 64.0 * _1mx2; + double fact1m3x = fac * _1m3x; + double fact1p3x = fac * _1p3x; + res[8] = fact1m3x * _1myt1mz; + res[9] = fact1p3x * _1myt1mz; + res[10] = fact1m3x * _1myt1pz; + res[11] = fact1p3x * _1myt1pz; + res[12] = fact1m3x * _1pyt1mz; + res[13] = fact1p3x * _1pyt1mz; + res[14] = fact1m3x * _1pyt1pz; + res[15] = fact1p3x * _1pyt1pz; + + fac = 9.0 / 64.0 * _1my2; + double fact1m3y = fac * _1m3y; + double fact1p3y = fac * _1p3y; + res[16] = fact1m3y * _1mxt1mz; + res[17] = fact1p3y * _1mxt1mz; + res[18] = fact1m3y * _1pxt1mz; + res[19] = fact1p3y * _1pxt1mz; + res[20] = fact1m3y * _1mxt1pz; + res[21] = fact1p3y * _1mxt1pz; + res[22] = fact1m3y * _1pxt1pz; + res[23] = fact1p3y * _1pxt1pz; + + fac = 9.0 / 64.0 * _1mz2; + double fact1m3z = fac * _1m3z; + double fact1p3z = fac * _1p3z; + res[24] = fact1m3z * _1mxt1my; + res[25] = fact1p3z * _1mxt1my; + res[26] = fact1m3z * _1mxt1py; + res[27] = fact1p3z * _1mxt1py; + res[28] = fact1m3z * _1pxt1my; + res[29] = fact1p3z * _1pxt1my; + res[30] = fact1m3z * _1pxt1py; + res[31] = fact1p3z * _1pxt1py; + + if (gradient) + { + b3ShapeGradients& dN = *gradient; + + double _9t3x2py2pz2m19 = 9.0 * (3.0 * x2 + y2 + z2) - 19.0; + double _9tx2p3y2pz2m19 = 9.0 * (x2 + 3.0 * y2 + z2) - 19.0; + double _9tx2py2p3z2m19 = 9.0 * (x2 + y2 + 3.0 * z2) - 19.0; + double _18x = 18.0 * x; + double _18y = 18.0 * y; + double _18z = 18.0 * z; + + double _3m9x2 = 3.0 - 9.0 * x2; + double _3m9y2 = 3.0 - 9.0 * y2; + double _3m9z2 = 3.0 - 9.0 * z2; + + double _2x = 2.0 * x; + double _2y = 2.0 * y; + double _2z = 2.0 * z; + + double _18xm9t3x2py2pz2m19 = _18x - _9t3x2py2pz2m19; + double _18xp9t3x2py2pz2m19 = _18x + _9t3x2py2pz2m19; + double _18ym9tx2p3y2pz2m19 = _18y - _9tx2p3y2pz2m19; + double _18yp9tx2p3y2pz2m19 = _18y + _9tx2p3y2pz2m19; + double _18zm9tx2py2p3z2m19 = _18z - _9tx2py2p3z2m19; + double _18zp9tx2py2p3z2m19 = _18z + _9tx2py2p3z2m19; + + dN(0, 0) = _18xm9t3x2py2pz2m19 * _1myt1mz; + dN(0, 1) = _1mxt1mz * _18ym9tx2p3y2pz2m19; + dN(0, 2) = _1mxt1my * _18zm9tx2py2p3z2m19; + dN(1, 0) = _18xp9t3x2py2pz2m19 * _1myt1mz; + dN(1, 1) = _1pxt1mz * _18ym9tx2p3y2pz2m19; + dN(1, 2) = _1pxt1my * _18zm9tx2py2p3z2m19; + dN(2, 0) = _18xm9t3x2py2pz2m19 * _1pyt1mz; + dN(2, 1) = _1mxt1mz * _18yp9tx2p3y2pz2m19; + dN(2, 2) = _1mxt1py * _18zm9tx2py2p3z2m19; + dN(3, 0) = _18xp9t3x2py2pz2m19 * _1pyt1mz; + dN(3, 1) = _1pxt1mz * _18yp9tx2p3y2pz2m19; + dN(3, 2) = _1pxt1py * _18zm9tx2py2p3z2m19; + dN(4, 0) = _18xm9t3x2py2pz2m19 * _1myt1pz; + dN(4, 1) = _1mxt1pz * _18ym9tx2p3y2pz2m19; + dN(4, 2) = _1mxt1my * _18zp9tx2py2p3z2m19; + dN(5, 0) = _18xp9t3x2py2pz2m19 * _1myt1pz; + dN(5, 1) = _1pxt1pz * _18ym9tx2p3y2pz2m19; + dN(5, 2) = _1pxt1my * _18zp9tx2py2p3z2m19; + dN(6, 0) = _18xm9t3x2py2pz2m19 * _1pyt1pz; + dN(6, 1) = _1mxt1pz * _18yp9tx2p3y2pz2m19; + dN(6, 2) = _1mxt1py * _18zp9tx2py2p3z2m19; + dN(7, 0) = _18xp9t3x2py2pz2m19 * _1pyt1pz; + dN(7, 1) = _1pxt1pz * _18yp9tx2p3y2pz2m19; + dN(7, 2) = _1pxt1py * _18zp9tx2py2p3z2m19; + + dN.topRowsDiv(8, 64.0); + + double _m3m9x2m2x = -_3m9x2 - _2x; + double _p3m9x2m2x = _3m9x2 - _2x; + double _1mx2t1m3x = _1mx2 * _1m3x; + double _1mx2t1p3x = _1mx2 * _1p3x; + dN(8, 0) = _m3m9x2m2x * _1myt1mz, + dN(8, 1) = -_1mx2t1m3x * _1mz, + dN(8, 2) = -_1mx2t1m3x * _1my; + dN(9, 0) = _p3m9x2m2x * _1myt1mz, + dN(9, 1) = -_1mx2t1p3x * _1mz, + dN(9, 2) = -_1mx2t1p3x * _1my; + dN(10, 0) = _m3m9x2m2x * _1myt1pz, + dN(10, 1) = -_1mx2t1m3x * _1pz, + dN(10, 2) = _1mx2t1m3x * _1my; + dN(11, 0) = _p3m9x2m2x * _1myt1pz, + dN(11, 1) = -_1mx2t1p3x * _1pz, + dN(11, 2) = _1mx2t1p3x * _1my; + dN(12, 0) = _m3m9x2m2x * _1pyt1mz, + dN(12, 1) = _1mx2t1m3x * _1mz, + dN(12, 2) = -_1mx2t1m3x * _1py; + dN(13, 0) = _p3m9x2m2x * _1pyt1mz, + dN(13, 1) = _1mx2t1p3x * _1mz, + dN(13, 2) = -_1mx2t1p3x * _1py; + dN(14, 0) = _m3m9x2m2x * _1pyt1pz, + dN(14, 1) = _1mx2t1m3x * _1pz, + dN(14, 2) = _1mx2t1m3x * _1py; + dN(15, 0) = _p3m9x2m2x * _1pyt1pz, + dN(15, 1) = _1mx2t1p3x * _1pz, + dN(15, 2) = _1mx2t1p3x * _1py; + + double _m3m9y2m2y = -_3m9y2 - _2y; + double _p3m9y2m2y = _3m9y2 - _2y; + double _1my2t1m3y = _1my2 * _1m3y; + double _1my2t1p3y = _1my2 * _1p3y; + dN(16, 0) = -_1my2t1m3y * _1mz, + dN(16, 1) = _m3m9y2m2y * _1mxt1mz, + dN(16, 2) = -_1my2t1m3y * _1mx; + dN(17, 0) = -_1my2t1p3y * _1mz, + dN(17, 1) = _p3m9y2m2y * _1mxt1mz, + dN(17, 2) = -_1my2t1p3y * _1mx; + dN(18, 0) = _1my2t1m3y * _1mz, + dN(18, 1) = _m3m9y2m2y * _1pxt1mz, + dN(18, 2) = -_1my2t1m3y * _1px; + dN(19, 0) = _1my2t1p3y * _1mz, + dN(19, 1) = _p3m9y2m2y * _1pxt1mz, + dN(19, 2) = -_1my2t1p3y * _1px; + dN(20, 0) = -_1my2t1m3y * _1pz, + dN(20, 1) = _m3m9y2m2y * _1mxt1pz, + dN(20, 2) = _1my2t1m3y * _1mx; + dN(21, 0) = -_1my2t1p3y * _1pz, + dN(21, 1) = _p3m9y2m2y * _1mxt1pz, + dN(21, 2) = _1my2t1p3y * _1mx; + dN(22, 0) = _1my2t1m3y * _1pz, + dN(22, 1) = _m3m9y2m2y * _1pxt1pz, + dN(22, 2) = _1my2t1m3y * _1px; + dN(23, 0) = _1my2t1p3y * _1pz, + dN(23, 1) = _p3m9y2m2y * _1pxt1pz, + dN(23, 2) = _1my2t1p3y * _1px; + + double _m3m9z2m2z = -_3m9z2 - _2z; + double _p3m9z2m2z = _3m9z2 - _2z; + double _1mz2t1m3z = _1mz2 * _1m3z; + double _1mz2t1p3z = _1mz2 * _1p3z; + dN(24, 0) = -_1mz2t1m3z * _1my, + dN(24, 1) = -_1mz2t1m3z * _1mx, + dN(24, 2) = _m3m9z2m2z * _1mxt1my; + dN(25, 0) = -_1mz2t1p3z * _1my, + dN(25, 1) = -_1mz2t1p3z * _1mx, + dN(25, 2) = _p3m9z2m2z * _1mxt1my; + dN(26, 0) = -_1mz2t1m3z * _1py, + dN(26, 1) = _1mz2t1m3z * _1mx, + dN(26, 2) = _m3m9z2m2z * _1mxt1py; + dN(27, 0) = -_1mz2t1p3z * _1py, + dN(27, 1) = _1mz2t1p3z * _1mx, + dN(27, 2) = _p3m9z2m2z * _1mxt1py; + dN(28, 0) = _1mz2t1m3z * _1my, + dN(28, 1) = -_1mz2t1m3z * _1px, + dN(28, 2) = _m3m9z2m2z * _1pxt1my; + dN(29, 0) = _1mz2t1p3z * _1my, + dN(29, 1) = -_1mz2t1p3z * _1px, + dN(29, 2) = _p3m9z2m2z * _1pxt1my; + dN(30, 0) = _1mz2t1m3z * _1py, + dN(30, 1) = _1mz2t1m3z * _1px, + dN(30, 2) = _m3m9z2m2z * _1pxt1py; + dN(31, 0) = _1mz2t1p3z * _1py, + dN(31, 1) = _1mz2t1p3z * _1px, + dN(31, 2) = _p3m9z2m2z * _1pxt1py; + + dN.bottomRowsMul(32u - 8u, 9.0 / 64.0); + } + + return res; +} + +bool b3SDF::interpolate(unsigned int field_id, double& dist, b3Vec3 const& x, + b3Vec3* gradient) const +{ + if (!m_domain.Contains(x)) + return false; + + b3Vec3 tmpmi = b3MulCW(x - m_domain.lowerBound, m_inv_cell_size); + + unsigned int mi[3] = { (unsigned int)tmpmi[0], (unsigned int)tmpmi[1], (unsigned int)tmpmi[2] }; + if (mi[0] >= m_resolution[0]) + mi[0] = m_resolution[0] - 1; + if (mi[1] >= m_resolution[1]) + mi[1] = m_resolution[1] - 1; + if (mi[2] >= m_resolution[2]) + mi[2] = m_resolution[2] - 1; + b3MultiIndex mui; + mui[0] = mi[0]; + mui[1] = mi[1]; + mui[2] = mi[2]; + unsigned int i = multiToSingleIndex(mui); + unsigned int i_ = m_cell_map[field_id][i]; + if (i_ == UINT_MAX) + return false; + + b3AABB sd = subdomain(i); + i = i_; + b3Vec3 d = sd.upperBound - sd.lowerBound; + + b3Vec3 denom = (sd.upperBound - sd.lowerBound); + b3Vec3 c0 = b3DivCW(b3Vec3(2.0, 2.0, 2.0), denom); + b3Vec3 c1 = b3DivCW(sd.upperBound + sd.lowerBound, denom); + b3Vec3 xi = b3MulCW(c0, x) - c1; + + b3Cell32 const& cell = m_cells[field_id][i]; + if (!gradient) + { + //auto phi = m_coefficients[field_id][i].dot(shape_function_(xi, 0)); + double phi = 0.0; + b3ShapeMatrix N = shape_function_(xi, 0); + for (unsigned int j = 0u; j < 32u; ++j) + { + unsigned int v = cell.v[j]; + double c = m_nodes[field_id][v]; + if (c == DBL_MAX) + { + return false; + } + phi += c * N[j]; + } + + dist = phi; + return true; + } + + b3ShapeGradients dN; + b3ShapeMatrix N = shape_function_(xi, &dN); + + double phi = 0.0; + gradient->SetZero(); + for (unsigned int j = 0u; j < 32u; ++j) + { + unsigned int v = cell.v[j]; + double c = m_nodes[field_id][v]; + if (c == DBL_MAX) + { + gradient->SetZero(); + return false; + } + phi += c * N[j]; + (*gradient)[0] += c * dN(j, 0); + (*gradient)[1] += c * dN(j, 1); + (*gradient)[2] += c * dN(j, 2); + } + *gradient = b3MulCW(*gradient, c0); + dist = phi; + return true; +} diff --git a/src/bounce/collision/time_of_impact.cpp b/src/bounce/collision/time_of_impact.cpp new file mode 100644 index 0000000..21eb3bd --- /dev/null +++ b/src/bounce/collision/time_of_impact.cpp @@ -0,0 +1,983 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include + +u32 b3_toiCalls = 0; +u32 b3_toiMaxIters = 0; + +// Compute the closest point on a segment to a point. +static b3Vec3 b3ClosestPointOnSegment(const b3Vec3& Q, + const b3Vec3& A, const b3Vec3& B) +{ + b3Vec3 AB = B - A; + + // Barycentric coordinates for Q + scalar u = b3Dot(B - Q, AB); + scalar v = b3Dot(Q - A, AB); + + if (v <= scalar(0)) + { + return A; + } + + if (u <= scalar(0)) + { + return B; + } + + scalar w = b3Dot(AB, AB); + if (w <= B3_LINEAR_SLOP * B3_LINEAR_SLOP) + { + return A; + } + + scalar den = scalar(1) / w; + b3Vec3 P = den * (u * A + v * B); + return P; +} + +// Compute the closest points between two segments. +static void b3ClosestPoints(b3Vec3& C1, b3Vec3& C2, + const b3Vec3& P1, const b3Vec3& Q1, const b3Vec3& E1, scalar L1, + const b3Vec3& P2, const b3Vec3& Q2, const b3Vec3& E2, scalar L2) +{ + if (L1 < B3_LINEAR_SLOP && L2 < B3_LINEAR_SLOP) + { + C1 = P1; + C2 = P2; + return; + } + + if (L1 < B3_LINEAR_SLOP) + { + C1 = P1; + C2 = b3ClosestPointOnSegment(P1, P2, Q2); + return; + } + + if (L2 < B3_LINEAR_SLOP) + { + C1 = b3ClosestPointOnSegment(P2, P1, Q1); + C2 = P2; + return; + } + + B3_ASSERT(L1 > scalar(0)); + b3Vec3 N1 = E1 / L1; + + B3_ASSERT(L2 > scalar(0)); + b3Vec3 N2 = E2 / L2; + + // Solve Ax = b + // [1 -dot(n1, n2)][x1] = [-dot(n1, p1 - p2)] + // [dot(n2, n1) -1][x2] = [-dot(n2, p1 - p2)] + scalar b = b3Dot(N1, N2); + scalar den = scalar(1) - b * b; + + if (den != scalar(0)) + { + scalar inv_den = scalar(1) / den; + + b3Vec3 E3 = P1 - P2; + + scalar d = b3Dot(N1, E3); + scalar e = b3Dot(N2, E3); + + scalar s = inv_den * (b * e - d); + scalar t = inv_den * (e - b * d); + + C1 = P1 + s * N1; + C2 = P2 + t * N2; + } + else + { + C1 = P1; + C2 = P2; + } + + C1 = b3ClosestPointOnSegment(C1, P1, Q1); + + C2 = b3ClosestPointOnSegment(C1, P2, Q2); + + C1 = b3ClosestPointOnSegment(C2, P1, Q1); +} + +// Separation function evaluator +struct b3SeparationFunction +{ + enum Type + { + e_points, + e_edgeA, + e_faceA, + e_edgeB, + e_faceB, + e_edges + }; + + void Initialize(const b3SimplexCache& cache, + const b3GJKProxy& proxyA, const b3Sweep& sweepA, + const b3GJKProxy& proxyB, const b3Sweep& sweepB, + scalar t1) + { + m_proxyA = &proxyA; + m_proxyB = &proxyB; + u32 count = cache.count; + B3_ASSERT(count > 0 && count < 4); + + m_sweepA = sweepA; + m_sweepB = sweepB; + + b3Transform xfA = m_sweepA.GetTransform(t1); + b3Transform xfB = m_sweepB.GetTransform(t1); + + // Extract the closest features + b3GJKFeaturePair fp = b3GetFeaturePair(cache); + + u32 countA = fp.count1; + u32* indexA = fp.index1; + + u32 countB = fp.count2; + u32* indexB = fp.index2; + + if (countA == 1 && countB == 1) + { + m_type = e_points; + + b3Vec3 localA = m_proxyA->GetVertex(indexA[0]); + b3Vec3 localB = m_proxyB->GetVertex(indexB[0]); + + b3Vec3 A = xfA * localA; + b3Vec3 B = xfB * localB; + + m_axis = B - A; + m_axis.Normalize(); + } + else if (countA == 2 && countB == 1) + { + m_type = e_edgeA; + + b3Vec3 A = m_proxyA->GetVertex(indexA[0]); + b3Vec3 B = m_proxyA->GetVertex(indexA[1]); + b3Vec3 C = m_proxyB->GetVertex(indexB[0]); + C = b3MulT(xfA, xfB * C); + + b3Vec3 AB = B - A; + b3Vec3 AC = C - A; + b3Vec3 AB_x_AC = b3Cross(AB, AC); + b3Vec3 PC = b3Cross(AB_x_AC, AB); + + m_axis = b3Mul(xfA.rotation, PC); + m_axis.Normalize(); + } + else if (countA == 3) + { + m_type = e_faceA; + + b3Vec3 A = m_proxyA->GetVertex(indexA[0]); + b3Vec3 B = m_proxyA->GetVertex(indexA[1]); + b3Vec3 C = m_proxyA->GetVertex(indexA[2]); + + b3Vec3 D = m_proxyB->GetVertex(indexB[0]); + D = b3MulT(xfA, xfB * D); + + b3Vec3 N = b3Cross(B - A, C - A); + + N = -N; + + if (b3Dot(D - A, N) < scalar(0)) + { + N = -N; + } + + m_axis = N; + m_axis.Normalize(); + m_localPoint = A; + } + else if (countB == 2 && countA == 1) + { + m_type = e_edgeB; + + b3Vec3 A = m_proxyB->GetVertex(indexB[0]); + b3Vec3 B = m_proxyB->GetVertex(indexB[1]); + b3Vec3 C = m_proxyA->GetVertex(indexA[0]); + C = b3MulT(xfB, xfA * C); + + b3Vec3 AB = B - A; + b3Vec3 AC = C - A; + b3Vec3 AB_x_AC = b3Cross(AB, AC); + b3Vec3 PC = b3Cross(AB_x_AC, AB); + + m_axis = b3Mul(xfB.rotation, PC); + m_axis.Normalize(); + } + else if (countB == 3) + { + m_type = e_faceB; + + b3Vec3 A = m_proxyB->GetVertex(indexB[0]); + b3Vec3 B = m_proxyB->GetVertex(indexB[1]); + b3Vec3 C = m_proxyB->GetVertex(indexB[2]); + b3Vec3 D = m_proxyA->GetVertex(indexA[0]); + D = b3MulT(xfB, xfA * D); + + b3Vec3 N = b3Cross(B - A, C - A); + + N = -N; + + if (b3Dot(D - A, N) < scalar(0)) + { + N = -N; + } + + m_axis = N; + m_axis.Normalize(); + m_localPoint = A; + } + else if (countA == 2 && countB == 2) + { + m_type = e_edges; + + b3Vec3 A = m_proxyA->GetVertex(indexA[0]); + b3Vec3 B = m_proxyA->GetVertex(indexA[1]); + + b3Vec3 C = m_proxyB->GetVertex(indexB[0]); + C = b3MulT(xfA, xfB * C); + + b3Vec3 D = m_proxyB->GetVertex(indexB[1]); + D = b3MulT(xfA, xfB * D); + + b3Vec3 E1 = B - A; + b3Vec3 E2 = D - C; + + scalar L1 = b3Length(E1); + B3_ASSERT(L1 > B3_LINEAR_SLOP); + + scalar L2 = b3Length(E2); + B3_ASSERT(L2 > B3_LINEAR_SLOP); + + // Check for almost parallel edges. + const scalar kTol = scalar(0.005); + + b3Vec3 E1_x_E2 = b3Cross(E1, E2); + scalar L = b3Length(E1_x_E2); + if (L < kTol * L1 * L2) + { + // Edges are paralell + // Use the closest points between the edges as separating axis. + b3Vec3 C1, C2; + b3ClosestPoints(C1, C2, A, B, E1, L1, C, D, E2, L2); + + C1 = xfA * C1; + C2 = xfA * C2; + + m_axis = C2 - C1; + m_axis.Normalize(); + + m_edgesParalell = true; + return; + } + + m_edgesParalell = false; + + b3Vec3 C1 = scalar(0.5) * (A + B); + b3Vec3 C2 = scalar(0.5) * (C + D); + + // Ensure consistent normal orientation to proxy B. + b3Vec3 N = (scalar(1) / L) * E1_x_E2; + if (b3Dot(N, C2 - C1) < scalar(0)) + { + N = -N; + } + + m_axis = N; + m_localPoint = C1; + } + else + { + B3_ASSERT(false); + } + } + + scalar FindMinSeparation(u32* indexA, u32* indexB, scalar t) const + { + b3Transform xfA = m_sweepA.GetTransform(t); + b3Transform xfB = m_sweepB.GetTransform(t); + + b3Quat qA = xfA.rotation; + b3Quat qB = xfB.rotation; + + switch (m_type) + { + case e_points: + { + b3Vec3 axisA = b3MulC(qA, m_axis); + b3Vec3 axisB = b3MulC(qB, -m_axis); + + *indexA = m_proxyA->GetSupportIndex(axisA); + *indexB = m_proxyB->GetSupportIndex(axisB); + + b3Vec3 localA = m_proxyA->GetVertex(*indexA); + b3Vec3 localB = m_proxyB->GetVertex(*indexB); + + b3Vec3 A = xfA * localA; + b3Vec3 B = xfB * localB; + + scalar separation = b3Dot(B - A, m_axis); + return separation; + } + case e_edgeA: + { + b3Vec3 axisA = b3MulC(qA, m_axis); + b3Vec3 axisB = b3MulC(qB, -m_axis); + + *indexA = m_proxyA->GetSupportIndex(axisA); + *indexB = m_proxyB->GetSupportIndex(axisB); + + b3Vec3 localA = m_proxyA->GetVertex(*indexA); + b3Vec3 localB = m_proxyB->GetVertex(*indexB); + + b3Vec3 A = xfA * localA; + b3Vec3 B = xfB * localB; + + scalar separation = b3Dot(B - A, m_axis); + return separation; + } + case e_faceA: + { + b3Vec3 N = b3Mul(qA, m_axis); + b3Vec3 A = xfA * m_localPoint; + + b3Vec3 axisB = b3MulC(qB, -N); + + *indexA = B3_MAX_U32; + *indexB = m_proxyB->GetSupportIndex(axisB); + + b3Vec3 localB = m_proxyB->GetVertex(*indexB); + b3Vec3 B = xfB * localB; + + scalar separation = b3Dot(B - A, N); + return separation; + } + case e_edgeB: + { + b3Vec3 axisB = b3MulC(qB, m_axis); + b3Vec3 axisA = b3MulC(qA, -m_axis); + + *indexA = m_proxyA->GetSupportIndex(axisA); + *indexB = m_proxyB->GetSupportIndex(axisB); + + b3Vec3 localA = m_proxyA->GetVertex(*indexA); + b3Vec3 localB = m_proxyB->GetVertex(*indexB); + + b3Vec3 A = xfA * localA; + b3Vec3 B = xfB * localB; + + scalar separation = b3Dot(A - B, m_axis); + return separation; + } + case e_faceB: + { + b3Vec3 N = b3Mul(qB, m_axis); + b3Vec3 B = xfB * m_localPoint; + + b3Vec3 axisA = b3MulC(qA, -N); + + *indexA = m_proxyA->GetSupportIndex(axisA); + *indexB = B3_MAX_U32; + + b3Vec3 localA = m_proxyA->GetVertex(*indexA); + b3Vec3 A = xfA * localA; + + scalar separation = b3Dot(A - B, N); + return separation; + } + case e_edges: + { + if (m_edgesParalell) + { + b3Vec3 axisA = b3MulC(qA, m_axis); + b3Vec3 axisB = b3MulC(qB, -m_axis); + + *indexA = m_proxyA->GetSupportIndex(axisA); + *indexB = m_proxyB->GetSupportIndex(axisB); + + b3Vec3 localA = m_proxyA->GetVertex(*indexA); + b3Vec3 localB = m_proxyB->GetVertex(*indexB); + + b3Vec3 A = xfA * localA; + b3Vec3 B = xfB * localB; + + scalar separation = b3Dot(B - A, m_axis); + return separation; + } + + b3Vec3 N = b3Mul(qA, m_axis); + b3Vec3 A = xfA * m_localPoint; + + b3Vec3 axisB = b3MulC(qB, -N); + + *indexA = B3_MAX_U32; + *indexB = m_proxyB->GetSupportIndex(axisB); + + b3Vec3 localB = m_proxyB->GetVertex(*indexB); + b3Vec3 B = xfB * localB; + + scalar separation = b3Dot(B - A, N); + return separation; + } + default: + { + B3_ASSERT(false); + *indexA = B3_MAX_U32; + *indexB = B3_MAX_U32; + return -B3_MAX_SCALAR; + } + } + } + + scalar Evaluate(u32 indexA, u32 indexB, scalar t) const + { + b3Transform xfA = m_sweepA.GetTransform(t); + b3Transform xfB = m_sweepB.GetTransform(t); + + b3Quat qA = xfA.rotation; + b3Quat qB = xfB.rotation; + + switch (m_type) + { + case e_points: + { + b3Vec3 localA = m_proxyA->GetVertex(indexA); + b3Vec3 localB = m_proxyB->GetVertex(indexB); + + b3Vec3 A = xfA * localA; + b3Vec3 B = xfB * localB; + + scalar separation = b3Dot(B - A, m_axis); + return separation; + } + case e_edgeA: + { + b3Vec3 localA = m_proxyA->GetVertex(indexA); + b3Vec3 localB = m_proxyB->GetVertex(indexB); + + b3Vec3 A = xfA * localA; + b3Vec3 B = xfB * localB; + + scalar separation = b3Dot(B - A, m_axis); + return separation; + } + case e_faceA: + { + b3Vec3 N = b3Mul(qA, m_axis); + b3Vec3 A = xfA * m_localPoint; + + b3Vec3 localB = m_proxyB->GetVertex(indexB); + b3Vec3 B = xfB * localB; + + scalar separation = b3Dot(B - A, N); + return separation; + } + case e_edgeB: + { + b3Vec3 localA = m_proxyA->GetVertex(indexA); + b3Vec3 localB = m_proxyB->GetVertex(indexB); + + b3Vec3 A = xfA * localA; + b3Vec3 B = xfB * localB; + + scalar separation = b3Dot(A - B, m_axis); + return separation; + } + case e_faceB: + { + b3Vec3 N = b3Mul(qB, m_axis); + b3Vec3 B = xfB * m_localPoint; + + b3Vec3 localA = m_proxyA->GetVertex(indexA); + b3Vec3 A = xfA * localA; + + scalar separation = b3Dot(A - B, N); + return separation; + } + case e_edges: + { + if (m_edgesParalell) + { + b3Vec3 localA = m_proxyA->GetVertex(indexA); + b3Vec3 localB = m_proxyB->GetVertex(indexB); + + b3Vec3 A = xfA * localA; + b3Vec3 B = xfB * localB; + + scalar separation = b3Dot(B - A, m_axis); + return separation; + } + + b3Vec3 N = b3Mul(qA, m_axis); + b3Vec3 A = xfA * m_localPoint; + + b3Vec3 localB = m_proxyB->GetVertex(indexB); + b3Vec3 B = xfB * localB; + + scalar separation = b3Dot(B - A, N); + return separation; + } + default: + B3_ASSERT(false); + return scalar(0); + } + } + + const b3GJKProxy* m_proxyA; + const b3GJKProxy* m_proxyB; + b3Sweep m_sweepA, m_sweepB; + Type m_type; + b3Vec3 m_localPoint; + b3Vec3 m_axis; + bool m_edgesParalell; +}; + +// "Continuous Collision" (Erin) +// CCD via the local separating axis method, a CA improvement. +b3TOIOutput b3TimeOfImpact(const b3TOIInput& input) +{ + ++b3_toiCalls; + + b3TOIOutput output; + output.state = b3TOIOutput::e_unknown; + output.t = input.tMax; + output.iterations = 0; + + const b3GJKProxy& proxyA = input.proxyA; + const b3GJKProxy& proxyB = input.proxyB; + + b3Sweep sweepA = input.sweepA; + b3Sweep sweepB = input.sweepB; + + scalar tMax = input.tMax; + + scalar totalRadius = proxyA.radius + proxyB.radius; + scalar target = b3Max(B3_LINEAR_SLOP, totalRadius - scalar(3) * B3_LINEAR_SLOP); + scalar tolerance = scalar(0.25) * B3_LINEAR_SLOP; + B3_ASSERT(target > tolerance); + + scalar t1 = scalar(0); + const u32 kMaxIterations = 20; + u32 iteration = 0; + + b3SimplexCache cache; + cache.count = 0; + + for (;;) + { + b3Transform xfA = sweepA.GetTransform(t1); + b3Transform xfB = sweepB.GetTransform(t1); + + // Get the distance between shapes. We can also use the results + // to get a separating axis. + b3GJKOutput gjkOutput = b3GJK(xfA, proxyA, xfB, proxyB, false, &cache); + + // If the shapes are overlapped, we give up on continuous collision. + if (gjkOutput.distance == scalar(0)) + { + // Failure! + output.state = b3TOIOutput::e_overlapped; + output.t = scalar(0); + output.iterations = iteration; + break; + } + + if (gjkOutput.distance < target + tolerance) + { + // Victory! + output.state = b3TOIOutput::e_touching; + output.t = t1; + output.iterations = iteration; + break; + } + + // Initialize the separating axis. + b3SeparationFunction fcn; + fcn.Initialize(cache, proxyA, sweepA, proxyB, sweepB, t1); + + // Compute the TOI on the separating axis. + // We do this by successively resolving the deepest point. + // This loop is bounded by the number of vertices. + bool done = false; + scalar t2 = tMax; + u32 pushBackIter = 0; + for (;;) + { + // Compute the deepest point at t2. + // Store the closest point indices. + u32 indexA, indexB; + scalar s2 = fcn.FindMinSeparation(&indexA, &indexB, t2); + + // Is the final configuration separated? + if (s2 > target + tolerance) + { + // Victory! + output.state = b3TOIOutput::e_separated; + output.t = tMax; + output.iterations = iteration; + done = true; + break; + } + + // Has the separation reached tolerance? + if (s2 > target - tolerance) + { + // Advance the sweeps + t1 = t2; + break; + } + + // Compute the initial separation of the witness points. + scalar s1 = fcn.Evaluate(indexA, indexB, t1); + + // Check for initial overlap. This might happen if the root finder + // runs out of iterations. + if (s1 < target - tolerance) + { + output.state = b3TOIOutput::e_failed; + output.t = t1; + output.iterations = iteration; + done = true; + break; + } + + // Check for touching + if (s1 <= target + tolerance) + { + // t1 should hold the TOI. It can be 0.0. + output.state = b3TOIOutput::e_touching; + output.t = t1; + output.iterations = iteration; + done = true; + break; + } + + // Compute 1D root of: f(x) - target = 0 + u32 rootIteration = 0; + scalar a1 = t1, a2 = t2; + for (;;) + { + // Use a mix of the secant rule and bisection. + scalar t; + if (rootIteration & 1) + { + // Secant rule to improve convergence. + t = a1 + (target - s1) * (a2 - a1) / (s2 - s1); + } + else + { + // Bisection to guarantee progress. + t = scalar(0.5) * (a1 + a2); + } + + ++rootIteration; + + scalar s = fcn.Evaluate(indexA, indexB, t); + + if (b3Abs(s - target) < tolerance) + { + // t2 holds a tentative value for t1 + t2 = t; + break; + } + + // Ensure we continue to bracket the root. + if (s > target) + { + a1 = t; + s1 = s; + } + else + { + a2 = t; + s2 = s; + } + + if (rootIteration == 50) + { + break; + } + } + + ++pushBackIter; + + if (pushBackIter == 64) + { + break; + } + } + + ++iteration; + + if (done) + { + break; + } + + if (iteration == kMaxIterations) + { + // Root finder got stuck. + output.state = b3TOIOutput::e_failed; + output.t = t1; + output.iterations = iteration; + break; + } + } + + b3_toiMaxIters = b3Max(b3_toiMaxIters, iteration); + + output.iterations = iteration; + + return output; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// Brian Mirtich +// "Conservative Advancement" +b3TOIOutput b3TimeOfImpact( + const b3Transform& xf1, const b3GJKProxy& proxy1, const b3Vec3& d1, + const b3Transform& xf2, const b3GJKProxy& proxy2, const b3Vec3& d2, u32 maxIterations) +{ + scalar r1 = proxy1.radius; + scalar r2 = proxy2.radius; + scalar totalRadius = r1 + r2; + + scalar target = b3Max(B3_LINEAR_SLOP, totalRadius - scalar(3) * B3_LINEAR_SLOP); + scalar tolerance = scalar(0.25) * B3_LINEAR_SLOP; + B3_ASSERT(target > tolerance); + + b3SimplexCache cache; + cache.count = 0; + + scalar t = scalar(0); + b3Transform xf1t = xf1; + b3Transform xf2t = xf2; + + u32 iteration = 0; + for (;;) + { + b3GJKOutput query = b3GJK(xf1t, proxy1, xf2t, proxy2, false, &cache); + + scalar d = query.distance; + + // If the shapes are overlapped, we give up on continuous collision. + if (d == scalar(0)) + { + // Failure! + b3TOIOutput output; + output.state = b3TOIOutput::e_overlapped; + output.t = scalar(0); + output.iterations = iteration; + return output; + } + + if (d < target + tolerance) + { + // Victory! + b3TOIOutput output; + output.state = b3TOIOutput::e_touching; + output.t = t; + output.iterations = iteration; + return output; + } + + b3Vec3 p1 = query.point1; + b3Vec3 p2 = query.point2; + b3Vec3 n = (p2 - p1) / d; + + scalar denominator = b3Dot(d2 - d1, n); + if (denominator >= scalar(0)) + { + // Victory! + b3TOIOutput output; + output.state = b3TOIOutput::e_separated; + output.t = scalar(1); + output.iterations = iteration; + return output; + } + + // Advance sweep + scalar bound = -denominator; + B3_ASSERT(d >= target); + scalar dt = (d - target) / bound; + t += dt; + + if (t >= scalar(1)) + { + // Victory! + b3TOIOutput output; + output.state = b3TOIOutput::e_separated; + output.t = scalar(1); + output.iterations = iteration; + return output; + } + + xf1t.translation = xf1.translation + t * d1; + xf2t.translation = xf2.translation + t * d2; + + ++iteration; + + if (iteration == maxIterations) + { + break; + } + } + + // Failure! + b3TOIOutput output; + output.state = b3TOIOutput::e_unknown; + output.t = t; + output.iterations = iteration; + return output; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// "Real Time Collision Detection", page 232. +b3TOIOutput b3TimeOfImpact(const b3AABB& aabb1, const b3Vec3& d1, const b3AABB& aabb2, const b3Vec3& d2) +{ + // If the shapes are overlapped, we give up on continuous collision. + if (b3TestOverlap(aabb1, aabb2)) + { + // Failure! + b3TOIOutput output; + output.state = b3TOIOutput::e_overlapped; + output.t = scalar(0); + output.iterations = 0; + return output; + } + + b3AABB A = aabb1; + b3AABB B = aabb2; + + b3Vec3 d = d2 - d1; + + b3Vec3 t1(scalar(0), scalar(0), scalar(0)); + b3Vec3 t2(scalar(1), scalar(1), scalar(1)); + + for (u32 i = 0; i < 3; ++i) + { + if (d[i] == scalar(0)) + { + if (A.lowerBound[i] > B.upperBound[i] || A.upperBound[i] < B.lowerBound[i]) + { + // Victory! + b3TOIOutput output; + output.state = b3TOIOutput::e_separated; + output.t = scalar(1); + output.iterations = 0; + return output; + } + } + + if (d[i] < scalar(0)) + { + if (B.upperBound[i] < A.lowerBound[i]) + { + // Victory! + b3TOIOutput output; + output.state = b3TOIOutput::e_separated; + output.t = scalar(1); + output.iterations = 0; + return output; + } + else + { + t2[i] = (A.lowerBound[i] - B.upperBound[i]) / d[i]; + } + + if (B.lowerBound[i] > A.upperBound[i]) + { + t1[i] = (A.upperBound[i] - B.lowerBound[i]) / d[i]; + + if (t1[i] >= scalar(1)) + { + // Victory! + b3TOIOutput output; + output.state = b3TOIOutput::e_separated; + output.t = scalar(1); + output.iterations = 0; + return output; + } + } + } + + if (d[i] > scalar(0)) + { + if (B.lowerBound[i] > A.upperBound[i]) + { + // Victory! + b3TOIOutput output; + output.state = b3TOIOutput::e_separated; + output.t = scalar(1); + output.iterations = 0; + return output; + } + else + { + t2[i] = (A.upperBound[i] - B.lowerBound[i]) / d[i]; + } + + if (B.upperBound[i] < A.lowerBound[i]) + { + t1[i] = (A.lowerBound[i] - B.upperBound[i]) / d[i]; + + if (t1[i] >= scalar(1)) + { + // Victory! + b3TOIOutput output; + output.state = b3TOIOutput::e_separated; + output.t = scalar(1); + output.iterations = 0; + return output; + } + } + } + } + + scalar t1_max = b3Max(t1.x, b3Max(t1.y, t1.z)); + scalar t2_min = b3Min(t2.x, b3Min(t2.y, t2.z)); + + if (t1_max > t2_min) + { + // Failure! + b3TOIOutput output; + output.state = b3TOIOutput::e_separated; + output.t = scalar(0); + output.iterations = 0; + return output; + } + + // Victory! + b3TOIOutput output; + output.state = b3TOIOutput::e_touching; + output.t = t1_max; + output.iterations = 0; + return output; +} \ No newline at end of file diff --git a/src/bounce/collision/trees/dynamic_tree.cpp b/src/bounce/collision/trees/dynamic_tree.cpp index ca30f1c..40a0f7c 100644 --- a/src/bounce/collision/trees/dynamic_tree.cpp +++ b/src/bounce/collision/trees/dynamic_tree.cpp @@ -70,7 +70,7 @@ u32 b3DynamicTree::AllocateNode() m_nodes[node].child1 = B3_NULL_NODE_D; m_nodes[node].child2 = B3_NULL_NODE_D; m_nodes[node].height = 0; - m_nodes[node].userData = NULL; + m_nodes[node].userData = nullptr; ++m_nodeCount; @@ -104,7 +104,7 @@ void b3DynamicTree::AddToFreeList(u32 node) m_freeList = node; } -u32 b3DynamicTree::InsertNode(const b3AABB3& aabb, void* userData) +u32 b3DynamicTree::InsertNode(const b3AABB& aabb, void* userData) { // Insert into the array. u32 node = AllocateNode(); @@ -128,7 +128,7 @@ void b3DynamicTree::RemoveNode(u32 proxyId) FreeNode(proxyId); } -void b3DynamicTree::UpdateNode(u32 proxyId, const b3AABB3& aabb) +void b3DynamicTree::UpdateNode(u32 proxyId, const b3AABB& aabb) { B3_ASSERT(m_root != B3_NULL_NODE_D); B3_ASSERT(m_nodes[proxyId].IsLeaf()); @@ -141,53 +141,53 @@ void b3DynamicTree::UpdateNode(u32 proxyId, const b3AABB3& aabb) InsertLeaf(proxyId); } -u32 b3DynamicTree::FindBest(const b3AABB3& leafAABB) const +u32 b3DynamicTree::PickBest(const b3AABB& leafAABB) const { u32 index = m_root; while (!m_nodes[index].IsLeaf()) { - float32 branchArea = m_nodes[index].aabb.SurfaceArea(); + scalar branchArea = m_nodes[index].aabb.GetSurfaceArea(); // Minumum cost of pushing the leaf down the tree. - b3AABB3 combinedAABB = b3Combine(leafAABB, m_nodes[index].aabb); - float32 combinedArea = combinedAABB.SurfaceArea(); + b3AABB combinedAABB = b3Combine(leafAABB, m_nodes[index].aabb); + scalar combinedArea = combinedAABB.GetSurfaceArea(); // Cost for creating a new parent node. - float32 branchCost = 2.0f * combinedArea; + scalar branchCost = scalar(2) * combinedArea; - float32 inheritanceCost = 2.0f * (combinedArea - branchArea); + scalar inheritanceCost = scalar(2) * (combinedArea - branchArea); // The branch node child nodes cost. u32 child1 = m_nodes[index].child1; u32 child2 = m_nodes[index].child2; // Cost of descending onto child1. - float32 childCost1 = 0.0f; + scalar childCost1 = scalar(0); if (m_nodes[child1].IsLeaf()) { - b3AABB3 aabb = b3Combine(leafAABB, m_nodes[child1].aabb); - childCost1 = aabb.SurfaceArea(); + b3AABB aabb = b3Combine(leafAABB, m_nodes[child1].aabb); + childCost1 = aabb.GetSurfaceArea(); } else { - b3AABB3 aabb = b3Combine(leafAABB, m_nodes[child1].aabb); - float32 oldArea = m_nodes[child1].aabb.SurfaceArea(); - float32 newArea = aabb.SurfaceArea(); + b3AABB aabb = b3Combine(leafAABB, m_nodes[child1].aabb); + scalar oldArea = m_nodes[child1].aabb.GetSurfaceArea(); + scalar newArea = aabb.GetSurfaceArea(); childCost1 = (newArea - oldArea) + inheritanceCost; } // Cost of descending onto child1. - float32 childCost2 = 0.0f; + scalar childCost2 = scalar(0); if (m_nodes[child2].IsLeaf()) { - b3AABB3 aabb = b3Combine(leafAABB, m_nodes[child2].aabb); - childCost2 = aabb.SurfaceArea(); + b3AABB aabb = b3Combine(leafAABB, m_nodes[child2].aabb); + childCost2 = aabb.GetSurfaceArea(); } else { - b3AABB3 aabb = b3Combine(leafAABB, m_nodes[child2].aabb); - float32 oldArea = m_nodes[child2].aabb.SurfaceArea(); - float32 newArea = aabb.SurfaceArea(); + b3AABB aabb = b3Combine(leafAABB, m_nodes[child2].aabb); + scalar oldArea = m_nodes[child2].aabb.GetSurfaceArea(); + scalar newArea = aabb.GetSurfaceArea(); childCost2 = (newArea - oldArea) + inheritanceCost; } @@ -216,10 +216,10 @@ void b3DynamicTree::InsertLeaf(u32 leaf) } // Get the inserted leaf AABB. - b3AABB3 leafAabb = m_nodes[leaf].aabb; + b3AABB leafAabb = m_nodes[leaf].aabb; // Search for the best branch node of this tree starting from the tree root node. - u32 sibling = FindBest(leafAabb); + u32 sibling = PickBest(leafAabb); u32 oldParent = m_nodes[sibling].parent; @@ -230,7 +230,7 @@ void b3DynamicTree::InsertLeaf(u32 leaf) m_nodes[sibling].parent = newParent; m_nodes[newParent].child2 = leaf; m_nodes[leaf].parent = newParent; - m_nodes[newParent].userData = NULL; + m_nodes[newParent].userData = nullptr; m_nodes[newParent].aabb = b3Combine(leafAabb, m_nodes[sibling].aabb); m_nodes[newParent].height = m_nodes[sibling].height + 1; @@ -256,7 +256,7 @@ void b3DynamicTree::InsertLeaf(u32 leaf) } // If we have ancestor nodes then adjust its AABBs. - WalkBackNodeAndCombineVolumes(newParent); + Refit(newParent); } void b3DynamicTree::RemoveLeaf(u32 leaf) @@ -295,7 +295,7 @@ void b3DynamicTree::RemoveLeaf(u32 leaf) FreeNode(parent); // If we have ancestor then nodes adjust its AABBs. - WalkBackNodeAndCombineVolumes(grandParent); + Refit(grandParent); } else { @@ -306,7 +306,7 @@ void b3DynamicTree::RemoveLeaf(u32 leaf) } } -void b3DynamicTree::WalkBackNodeAndCombineVolumes(u32 node) +void b3DynamicTree::Refit(u32 node) { while (node != B3_NULL_NODE_D) { diff --git a/src/bounce/collision/trees/static_tree.cpp b/src/bounce/collision/trees/static_tree.cpp index 7dd0436..9dd90df 100644 --- a/src/bounce/collision/trees/static_tree.cpp +++ b/src/bounce/collision/trees/static_tree.cpp @@ -19,10 +19,12 @@ #include #include #include +#include b3StaticTree::b3StaticTree() { - m_nodes = NULL; + m_root = B3_NULL_NODE_S; + m_nodes = nullptr; m_nodeCount = 0; } @@ -31,51 +33,36 @@ b3StaticTree::~b3StaticTree() b3Free(m_nodes); } -static B3_FORCE_INLINE bool b3SortPredicate(const b3AABB3* set, u32 axis, u32 a, u32 b) +struct b3SortPredicate { - b3Vec3 c1 = set[a].Centroid(); - b3Vec3 c2 = set[b].Centroid(); + b3SortPredicate() { } - return c1[axis] < c2[axis]; -} - -static void b3Sort(const b3AABB3* set, u32 axis, u32* ids, u32 count) -{ - if (count <= 1) + bool operator()(u32 a, u32 b) { - return; + b3Vec3 ca = set[a].GetCenter(); + b3Vec3 cb = set[b].GetCenter(); + + return ca[axis] < cb[axis]; } - u32 pivot = ids[count - 1]; - u32 low = 0; - for (u32 i = 0; i < count - 1; ++i) - { - if (b3SortPredicate(set, axis, ids[i], pivot)) - { - u32 tmp = ids[i]; - ids[i] = ids[low]; - ids[low] = tmp; - low++; - } - } + const b3AABB* set; + u32 axis; +}; - ids[count - 1] = ids[low]; - ids[low] = pivot; - - b3Sort(set, axis, ids, low); - b3Sort(set, axis, ids + low + 1, count - 1 - low); -} - -static u32 b3Partition(const b3AABB3& setAABB, const b3AABB3* set, u32* ids, u32 count) +static u32 b3Partition(const b3AABB& setAABB, const b3AABB* set, u32* ids, u32 count) { // Choose a partitioning axis. u32 splitAxis = setAABB.GetLongestAxisIndex(); // Choose a split point. - float32 splitPos = setAABB.Centroid()[splitAxis]; + scalar splitPos = setAABB.GetCenter()[splitAxis]; // Sort along the split axis. - b3Sort(set, splitAxis, ids, count); + b3SortPredicate predicate; + predicate.set = set; + predicate.axis = splitAxis; + + std::sort(ids, ids + count, predicate); // Find the AABB that splits the set in two subsets. u32 left = 0; @@ -83,7 +70,7 @@ static u32 b3Partition(const b3AABB3& setAABB, const b3AABB3* set, u32* ids, u32 u32 middle = left; while (middle < right) { - b3Vec3 center = set[ids[middle]].Centroid(); + b3Vec3 center = set[ids[middle]].GetCenter(); if (center[splitAxis] > splitPos) { // Found median. @@ -104,12 +91,12 @@ static u32 b3Partition(const b3AABB3& setAABB, const b3AABB3* set, u32* ids, u32 return middle; } -void b3StaticTree::Build(const b3AABB3* set, b3Node* node, u32* ids, u32 count, u32 minObjectsPerLeaf, u32 nodeCapacity, u32& leafCount, u32& internalCount) +void b3StaticTree::RecurseBuild(const b3AABB* set, b3Node* node, u32* ids, u32 count, u32 minObjectsPerLeaf, u32 nodeCapacity, u32& leafCount, u32& internalCount) { B3_ASSERT(count > 0); // Enclose set - b3AABB3 setAABB = set[ids[0]]; + b3AABB setAABB = set[ids[0]]; for (u32 i = 1; i < count; ++i) { setAABB = b3Combine(setAABB, set[ids[i]]); @@ -141,12 +128,12 @@ void b3StaticTree::Build(const b3AABB3* set, b3Node* node, u32* ids, u32 count, ++m_nodeCount; // Build left and right subtrees - Build(set, m_nodes + node->child1, ids, middle, minObjectsPerLeaf, nodeCapacity, leafCount, internalCount); - Build(set, m_nodes + node->child2, ids + middle, count - middle, minObjectsPerLeaf, nodeCapacity, leafCount, internalCount); + RecurseBuild(set, m_nodes + node->child1, ids, middle, minObjectsPerLeaf, nodeCapacity, leafCount, internalCount); + RecurseBuild(set, m_nodes + node->child2, ids + middle, count - middle, minObjectsPerLeaf, nodeCapacity, leafCount, internalCount); } } -void b3StaticTree::Build(const b3AABB3* set, u32 count) +void b3StaticTree::Build(const b3AABB* set, u32 count) { B3_ASSERT(count > 0); @@ -167,10 +154,11 @@ void b3StaticTree::Build(const b3AABB3* set, u32 count) u32 internalCount = 0; u32 leafCount = 0; + m_root = 0; m_nodes = (b3Node*)b3Alloc(nodeCapacity * sizeof(b3Node)); m_nodeCount = 1; - Build(set, m_nodes, ids, count, kMinObjectsPerLeaf, nodeCapacity, leafCount, internalCount); + RecurseBuild(set, m_nodes, ids, count, kMinObjectsPerLeaf, nodeCapacity, leafCount, internalCount); b3Free(ids); @@ -186,10 +174,8 @@ void b3StaticTree::Draw() const return; } - u32 root = 0; - b3Stack stack; - stack.Push(root); + stack.Push(m_root); while (!stack.IsEmpty()) { diff --git a/src/bounce/common/math/math.cpp b/src/bounce/common/math/math.cpp index 793e5c5..b5465d4 100644 --- a/src/bounce/common/math/math.cpp +++ b/src/bounce/common/math/math.cpp @@ -24,48 +24,48 @@ #include #include -const b3Vec2 b3Vec2_zero(0.0f, 0.0f); -const b3Vec2 b3Vec2_x(1.0f, 0.0f); -const b3Vec2 b3Vec2_y(0.0f, 1.0f); +const b3Vec2 b3Vec2_zero(scalar(0), scalar(0)); +const b3Vec2 b3Vec2_x(scalar(1), scalar(0)); +const b3Vec2 b3Vec2_y(scalar(0), scalar(1)); -const b3Vec3 b3Vec3_zero(0.0f, 0.0f, 0.0f); -const b3Vec3 b3Vec3_x(1.0f, 0.0f, 0.0f); -const b3Vec3 b3Vec3_y(0.0f, 1.0f, 0.0f); -const b3Vec3 b3Vec3_z(0.0f, 0.0f, 1.0f); +const b3Vec3 b3Vec3_zero(scalar(0), scalar(0), scalar(0)); +const b3Vec3 b3Vec3_x(scalar(1), scalar(0), scalar(0)); +const b3Vec3 b3Vec3_y(scalar(0), scalar(1), scalar(0)); +const b3Vec3 b3Vec3_z(scalar(0), scalar(0), scalar(1)); const b3Mat22 b3Mat22_zero( - b3Vec2(0.0f, 0.0f), - b3Vec2(0.0f, 0.0f)); + b3Vec2(scalar(0), scalar(0)), + b3Vec2(scalar(0), scalar(0))); const b3Mat22 b3Mat22_identity( - b3Vec2(1.0f, 0.0f), - b3Vec2(0.0f, 1.0f)); + b3Vec2(scalar(1), scalar(0)), + b3Vec2(scalar(0), scalar(1))); const b3Mat33 b3Mat33_zero( - b3Vec3(0.0f, 0.0f, 0.0f), - b3Vec3(0.0f, 0.0f, 0.0f), - b3Vec3(0.0f, 0.0f, 0.0f)); + b3Vec3(scalar(0), scalar(0), scalar(0)), + b3Vec3(scalar(0), scalar(0), scalar(0)), + b3Vec3(scalar(0), scalar(0), scalar(0))); const b3Mat33 b3Mat33_identity( - b3Vec3(1.0f, 0.0f, 0.0f), - b3Vec3(0.0f, 1.0f, 0.0f), - b3Vec3(0.0f, 0.0f, 1.0f)); + b3Vec3(scalar(1), scalar(0), scalar(0)), + b3Vec3(scalar(0), scalar(1), scalar(0)), + b3Vec3(scalar(0), scalar(0), scalar(1))); -const b3Transform b3Transform_identity(b3Mat33_identity, b3Vec3_zero); +const b3Transform b3Transform_identity(b3Vec3_zero, b3Quat_identity); -const b3Quat b3Quat_identity(0.0f, 0.0f, 0.0f, 1.0f); +const b3Quat b3Quat_identity(scalar(0), scalar(0), scalar(0), scalar(1)); b3Vec2 b3Mat22::Solve(const b3Vec2& b) const { // Cramer's rule - float32 a11 = x.x, a12 = y.x; - float32 a21 = x.y, a22 = y.y; + scalar a11 = x.x, a12 = y.x; + scalar a21 = x.y, a22 = y.y; - float32 det = a11 * a22 - a12 * a21; + scalar det = a11 * a22 - a12 * a21; - if (det != 0.0f) + if (det != scalar(0)) { - det = 1.0f / det; + det = scalar(1) / det; } b3Vec2 xn; @@ -76,14 +76,14 @@ b3Vec2 b3Mat22::Solve(const b3Vec2& b) const b3Mat22 b3Inverse(const b3Mat22& A) { - float32 a11 = A.x.x, a12 = A.y.x; - float32 a21 = A.x.y, a22 = A.y.y; + scalar a11 = A.x.x, a12 = A.y.x; + scalar a21 = A.x.y, a22 = A.y.y; - float32 det = a11 * a22 - a12 * a21; + scalar det = a11 * a22 - a12 * a21; - if (det != 0.0f) + if (det != scalar(0)) { - det = 1.0f / det; + det = scalar(1) / det; } b3Mat22 B; @@ -95,10 +95,10 @@ b3Mat22 b3Inverse(const b3Mat22& A) b3Vec3 b3Mat33::Solve(const b3Vec3& b) const { // Cramer's rule - float32 det = b3Det(x, y, z); - if (det != 0.0f) + scalar det = b3Det(x, y, z); + if (det != scalar(0)) { - det = 1.0f / det; + det = scalar(1) / det; } b3Vec3 xn; @@ -124,25 +124,25 @@ static B3_FORCE_INLINE b3Mat33 b3Adjucate(const b3Mat33& A) b3Mat33 b3Inverse(const b3Mat33& A) { // Cofactor method - float32 det = b3Det(A.x, A.y, A.z); - if (det != 0.0f) + scalar det = b3Det(A.x, A.y, A.z); + if (det != scalar(0)) { - det = 1.0f / det; + det = scalar(1) / det; } return det * b3Adjucate(A); } b3Mat33 b3SymInverse(const b3Mat33& A) { - float32 det = b3Det(A.x, A.y, A.z); - if (det != 0.0f) + scalar det = b3Det(A.x, A.y, A.z); + if (det != scalar(0)) { - det = 1.0f / det; + det = scalar(1) / det; } - float32 a11 = A.x.x, a12 = A.y.x, a13 = A.z.x; - float32 a22 = A.y.y, a23 = A.z.y; - float32 a33 = A.z.z; + scalar a11 = A.x.x, a12 = A.y.x, a13 = A.z.x; + scalar a22 = A.y.y, a23 = A.z.y; + scalar a33 = A.z.z; b3Mat33 M; diff --git a/src/bounce/common/memory/block_pool.cpp b/src/bounce/common/memory/block_pool.cpp index e6a0619..30c443b 100644 --- a/src/bounce/common/memory/block_pool.cpp +++ b/src/bounce/common/memory/block_pool.cpp @@ -23,7 +23,7 @@ b3BlockPool::b3BlockPool(u32 blockSize) m_blockSize = blockSize; m_chunkSize = b3_blockCount * m_blockSize; - m_chunks = NULL; + m_chunks = nullptr; m_chunkCount = 0; // Pre-allocate some chunks @@ -42,7 +42,7 @@ b3BlockPool::b3BlockPool(u32 blockSize) current->next = (b3Block*)((u8*)chunk->freeBlocks + (i + 1) * blockSize); } b3Block* last = (b3Block*)((u8*)chunk->freeBlocks + (b3_blockCount - 1) * blockSize); - last->next = NULL; + last->next = nullptr; // Push back the new chunk of the singly-linked list of chunks. chunk->next = m_chunks; @@ -90,7 +90,7 @@ void* b3BlockPool::Allocate() current->next = (b3Block*)((u8*)chunk->freeBlocks + (i + 1) * m_blockSize); } b3Block* last = (b3Block*)((u8*)chunk->freeBlocks + (b3_blockCount - 1) * m_blockSize); - last->next = NULL; + last->next = nullptr; // Push back the new chunk of the singly-linked list of chunks. chunk->next = m_chunks; diff --git a/src/bounce/common/memory/frame_allocator.cpp b/src/bounce/common/memory/frame_allocator.cpp new file mode 100644 index 0000000..e32b59c --- /dev/null +++ b/src/bounce/common/memory/frame_allocator.cpp @@ -0,0 +1,72 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +b3FrameAllocator::b3FrameAllocator() +{ + m_p = m_memory; + m_allocatedSize = 0; +} + +b3FrameAllocator::~b3FrameAllocator() +{ +} + +void* b3FrameAllocator::Allocate(u32 size) +{ + u32 totalSize = sizeof(b3Block) + size; + + if (m_allocatedSize + totalSize > b3_maxFrameSize) + { + u8* p = (u8*)b3Alloc(totalSize); + + b3Block* block = (b3Block*)p; + block->p = p; + block->size = size; + block->parent = true; + return p + sizeof(b3Block); + } + + u8* p = m_p; + m_p += totalSize; + m_allocatedSize += totalSize; + + b3Block* block = (b3Block*)p; + block->p = p; + block->size = size; + block->parent = false; + return p + sizeof(b3Block); +} + +void b3FrameAllocator::Free(void* q) +{ + u8* p = (u8*)(q) - sizeof(b3Block); + b3Block* block = (b3Block*)p; + B3_ASSERT(block->p == p); + if (block->parent) + { + b3Free(p); + } +} + +void b3FrameAllocator::Reset() +{ + m_p = m_memory; + m_allocatedSize = 0; +} \ No newline at end of file diff --git a/src/bounce/common/profiler.cpp b/src/bounce/common/profiler.cpp new file mode 100644 index 0000000..6756526 --- /dev/null +++ b/src/bounce/common/profiler.cpp @@ -0,0 +1,215 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +b3Profiler::b3Profiler() : + m_nodePool(sizeof(b3ProfilerNode)), + m_statPool(sizeof(b3ProfilerNodeStat)) +{ + m_root = nullptr; + m_top = nullptr; + m_stats = nullptr; +} + +b3Profiler::~b3Profiler() +{ + B3_ASSERT(m_root == nullptr); + B3_ASSERT(m_top == nullptr); + + b3ProfilerNodeStat* s = m_stats; + while (s) + { + b3ProfilerNodeStat* boom = s; + s = s->next; + boom->~b3ProfilerNodeStat(); + m_statPool.Free(boom); + } +} + +b3ProfilerNodeStat* b3Profiler::FindStat(const char* name) +{ + b3ProfilerNodeStat* s = m_stats; + while (s) + { + if (s->name == name) + { + return s; + } + s = s->next; + } + + return nullptr; +} + +b3ProfilerNodeStat* b3Profiler::CreateStat() +{ + void* block = m_statPool.Allocate(); + return new (block) b3ProfilerNodeStat(); +} + +b3ProfilerNode* b3Profiler::CreateNode() +{ + void* block = m_nodePool.Allocate(); + return new (block) b3ProfilerNode(); +} + +void b3Profiler::DestroyNode(b3ProfilerNode* node) +{ + node->~b3ProfilerNode(); + m_nodePool.Free(node); +} + +void b3Profiler::RecurseDestroyNode(b3ProfilerNode* node) +{ + b3ProfilerNode* n = node->head; + while (n) + { + b3ProfilerNode* boom = n; + n = n->next; + RecurseDestroyNode(boom); + } + + DestroyNode(node); +} + +b3ProfilerNode* b3Profiler::FindNode(const char* name) +{ + if (m_top) + { + b3ProfilerNode* n = m_top->head; + while (n) + { + if (n->name == name) + { + return n; + } + n = n->next; + } + } + + return nullptr; +} + +void b3Profiler::BeginScope(const char* name) +{ + b3ProfilerNode* fn = FindNode(name); + + if (fn) + { + m_top = fn; + ++fn->callCount; + + if (fn->callCountRec == 0) + { + m_time.Update(); + fn->t0 = m_time.GetCurrentMilis(); + } + + ++fn->callCountRec; + return; + } + + b3ProfilerNode* n = CreateNode(); + n->name = name; + m_time.Update(); + n->t0 = m_time.GetCurrentMilis(); + n->elapsed = scalar64(0); + n->callCount = 1; + n->callCountRec = 1; + n->stat = nullptr; + n->parent = m_top; + n->head = nullptr; + n->next = nullptr; + + if (m_root == nullptr) + { + m_root = n; + m_top = n; + + return; + } + + if (m_top) + { + n->next = m_top->head; + m_top->head = n; + } + + m_top = n; +} + +void b3Profiler::EndScope() +{ + B3_ASSERT(m_top != nullptr); + + --m_top->callCountRec; + if (m_top->callCountRec > 0) + { + return; + } + + m_time.Update(); + m_top->t1 = m_time.GetCurrentMilis(); + + scalar64 elapsedTime = m_top->t1 - m_top->t0; + + m_top->elapsed += elapsedTime; + + b3ProfilerNodeStat* stat = FindStat(m_top->name); + + if (stat == nullptr) + { + stat = CreateStat(); + stat->name = m_top->name; + stat->minElapsed = elapsedTime; + stat->maxElapsed = elapsedTime; + stat->next = m_stats; + m_stats = stat; + } + else + { + stat->minElapsed = b3Min(stat->minElapsed, elapsedTime); + stat->maxElapsed = b3Max(stat->maxElapsed, elapsedTime); + } + + if (m_top->stat == nullptr) + { + m_top->stat = stat; + } + + B3_ASSERT(m_top->stat == stat); + + m_top = m_top->parent; +} + +void b3Profiler::Begin() +{ + B3_ASSERT(m_top == nullptr); +} + +void b3Profiler::End() +{ + B3_ASSERT(m_top == nullptr); + + if (m_root) + { + RecurseDestroyNode(m_root); + m_root = nullptr; + } +} \ No newline at end of file diff --git a/src/bounce/common/settings.cpp b/src/bounce/common/settings.cpp index 9c663c4..5d5f9bd 100644 --- a/src/bounce/common/settings.cpp +++ b/src/bounce/common/settings.cpp @@ -25,7 +25,7 @@ u32 b3_allocCalls = 0; u32 b3_maxAllocCalls = 0; -b3Version b3_version = { 1, 0, 0 }; +b3Version b3_version = { 0, 0, 0 }; void* b3Alloc(u32 size) { diff --git a/src/bounce/dynamics/body.cpp b/src/bounce/dynamics/body.cpp index caf19c8..be28a84 100644 --- a/src/bounce/dynamics/body.cpp +++ b/src/bounce/dynamics/body.cpp @@ -33,10 +33,15 @@ b3Body::b3Body(const b3BodyDef& def, b3World* world) m_flags |= e_awakeFlag; } + if (def.allowSleep) + { + m_flags |= e_autoSleepFlag; + } + if (m_type == e_dynamicBody) { - m_mass = 1.0f; - m_invMass = 1.0f; + m_mass = scalar(1); + m_invMass = scalar(1); if (def.fixedRotationX) { @@ -55,8 +60,8 @@ b3Body::b3Body(const b3BodyDef& def, b3World* world) } else { - m_mass = 0.0f; - m_invMass = 0.0f; + m_mass = scalar(0); + m_invMass = scalar(0); } m_I.SetZero(); @@ -74,16 +79,19 @@ b3Body::b3Body(const b3BodyDef& def, b3World* world) m_sweep.orientation = def.orientation; m_sweep.worldCenter0 = def.position; m_sweep.orientation0 = def.orientation; - m_sweep.t0 = 0.0f; + m_sweep.t0 = scalar(0); - m_xf.position = m_sweep.worldCenter; - m_xf.rotation = b3QuatMat33(m_sweep.orientation); + m_xf.translation = m_sweep.worldCenter; + m_xf.rotation = m_sweep.orientation; m_linearDamping = def.linearDamping; m_angularDamping = def.angularDamping; m_gravityScale = def.gravityScale; m_userData = def.userData; - m_sleepTime = 0.0f; + + m_linearSleepTolerance = def.linearSleepTolerance; + m_angularSleepTolerance = def.angularSleepTolerance; + m_sleepTime = scalar(0); } b3Shape* b3Body::CreateShape(const b3ShapeDef& def) @@ -102,7 +110,7 @@ b3Shape* b3Body::CreateShape(const b3ShapeDef& def) // Since a new shape was added the new mass properties of // this body need to be recomputed. - if (shape->m_density > 0.0f) + if (shape->m_density > scalar(0)) { ResetMass(); } @@ -110,7 +118,7 @@ b3Shape* b3Body::CreateShape(const b3ShapeDef& def) // Compute the world AABB of the new shape and assign a broad-phase proxy to it. b3Transform xf = m_xf; - b3AABB3 aabb; + b3AABB aabb; shape->ComputeAABB(&aabb, xf); shape->m_broadPhaseID = m_world->m_contactMan.m_broadPhase.CreateProxy(aabb, shape); @@ -175,27 +183,27 @@ void b3Body::DestroyShapes() void b3Body::SynchronizeTransform() { - m_xf = m_sweep.GetTransform(1.0f); + m_xf = m_sweep.GetTransform(scalar(1)); } void b3Body::SynchronizeShapes() { - b3Transform xf1 = m_sweep.GetTransform(0.0f); + b3Transform xf1 = m_sweep.GetTransform(scalar(0)); b3Transform xf2 = m_xf; - b3Vec3 displacement = xf2.position - xf1.position; + b3Vec3 displacement = xf2.translation - xf1.translation; // Update all shape AABBs. b3BroadPhase* broadPhase = &m_world->m_contactMan.m_broadPhase; for (b3Shape* s = m_shapeList.m_head; s; s = s->m_next) { // Compute an AABB that encloses the swept shape AABB. - b3AABB3 aabb1, aabb2; + b3AABB aabb1, aabb2; s->ComputeAABB(&aabb1, xf1); s->ComputeAABB(&aabb2, xf2); - b3AABB3 aabb = b3Combine(aabb1, aabb2); + b3AABB aabb = b3Combine(aabb1, aabb2); broadPhase->MoveProxy(s->m_broadPhaseID, aabb, displacement); } @@ -203,15 +211,14 @@ void b3Body::SynchronizeShapes() bool b3Body::ShouldCollide(const b3Body* other) const { - if (m_type != e_dynamicBody && other->m_type != e_dynamicBody) + // At least one body must be kinematic or dynamic. + if (m_type == e_staticBody && other->m_type == e_staticBody) { - // At least one body should be kinematic or dynamic. return false; } - // Check if there are joints that connects - // this body with the other body and if the joint was configured - // to let not their collision occur. + // Check if there are joints that connects this body with the other body + // and if the joint was configured to let not their collision occur. for (b3JointEdge* je = m_jointEdges.m_head; je; je = je->m_next) { b3Joint* j = je->joint; @@ -229,8 +236,8 @@ bool b3Body::ShouldCollide(const b3Body* other) const void b3Body::ResetMass() { - m_mass = 0.0f; - m_invMass = 0.0f; + m_mass = scalar(0); + m_invMass = scalar(0); m_I.SetZero(); m_invI.SetZero(); m_worldInvI.SetZero(); @@ -239,8 +246,8 @@ void b3Body::ResetMass() // Static and kinematic bodies have zero mass. if (m_type == e_staticBody || m_type == e_kinematicBody) { - m_sweep.worldCenter0 = m_xf.position; - m_sweep.worldCenter = m_xf.position; + m_sweep.worldCenter0 = m_xf.translation; + m_sweep.worldCenter = m_xf.translation; m_sweep.orientation0 = m_sweep.orientation; return; } @@ -250,7 +257,7 @@ void b3Body::ResetMass() localCenter.SetZero(); for (b3Shape* s = m_shapeList.m_head; s; s = s->m_next) { - if (s->m_density == 0.0f) + if (s->m_density == scalar(0)) { continue; } @@ -263,18 +270,18 @@ void b3Body::ResetMass() m_I += massData.I; } - if (m_mass > 0.0f) + if (m_mass > scalar(0)) { // Compute local center of mass. - m_invMass = 1.0f / m_mass; + m_invMass = scalar(1) / m_mass; localCenter *= m_invMass; // Shift inertia about the body origin into the body local center of mass. m_I = m_I - m_mass * b3Steiner(localCenter); - B3_ASSERT(m_I.x.x > 0.0f); - B3_ASSERT(m_I.y.y > 0.0f); - B3_ASSERT(m_I.z.z > 0.0f); + B3_ASSERT(m_I.x.x > scalar(0)); + B3_ASSERT(m_I.y.y > scalar(0)); + B3_ASSERT(m_I.z.z > scalar(0)); // Compute inverse inertia about the body local center of mass. m_invI = b3Inverse(m_I); @@ -285,48 +292,48 @@ void b3Body::ResetMass() // Fix rotation. if (m_flags & e_fixedRotationX) { - m_invI.y.y = 0.0f; - m_invI.z.y = 0.0f; - m_invI.y.z = 0.0f; - m_invI.z.z = 0.0f; + m_invI.y.y = scalar(0); + m_invI.z.y = scalar(0); + m_invI.y.z = scalar(0); + m_invI.z.z = scalar(0); - m_worldInvI.y.y = 0.0f; - m_worldInvI.z.y = 0.0f; - m_worldInvI.y.z = 0.0f; - m_worldInvI.z.z = 0.0f; + m_worldInvI.y.y = scalar(0); + m_worldInvI.z.y = scalar(0); + m_worldInvI.y.z = scalar(0); + m_worldInvI.z.z = scalar(0); } if (m_flags & e_fixedRotationY) { - m_invI.x.x = 0.0f; - m_invI.x.z = 0.0f; - m_invI.z.x = 0.0f; - m_invI.z.z = 0.0f; + m_invI.x.x = scalar(0); + m_invI.x.z = scalar(0); + m_invI.z.x = scalar(0); + m_invI.z.z = scalar(0); - m_worldInvI.x.x = 0.0f; - m_worldInvI.x.z = 0.0f; - m_worldInvI.z.x = 0.0f; - m_worldInvI.z.z = 0.0f; + m_worldInvI.x.x = scalar(0); + m_worldInvI.x.z = scalar(0); + m_worldInvI.z.x = scalar(0); + m_worldInvI.z.z = scalar(0); } if (m_flags & e_fixedRotationZ) { - m_invI.x.x = 0.0f; - m_invI.x.y = 0.0f; - m_invI.y.x = 0.0f; - m_invI.y.y = 0.0f; + m_invI.x.x = scalar(0); + m_invI.x.y = scalar(0); + m_invI.y.x = scalar(0); + m_invI.y.y = scalar(0); - m_worldInvI.x.x = 0.0f; - m_worldInvI.x.y = 0.0f; - m_worldInvI.y.x = 0.0f; - m_worldInvI.y.y = 0.0f; + m_worldInvI.x.x = scalar(0); + m_worldInvI.x.y = scalar(0); + m_worldInvI.y.x = scalar(0); + m_worldInvI.y.y = scalar(0); } } else { // Force all dynamic bodies to have positive mass. - m_mass = 1.0f; - m_invMass = 1.0f; + m_mass = scalar(1); + m_invMass = scalar(1); } // Move center of mass. @@ -353,67 +360,67 @@ void b3Body::SetMassData(const b3MassData* massData) return; } - m_invMass = 0.0f; + m_invMass = scalar(0); m_I.SetZero(); m_invI.SetZero(); m_worldInvI.SetZero(); m_mass = massData->mass; - if (m_mass > 0.0f) + if (m_mass > scalar(0)) { - m_invMass = 1.0f / m_mass; + m_invMass = scalar(1) / m_mass; m_I = massData->I - m_mass * b3Steiner(massData->center); - B3_ASSERT(m_I.x.x > 0.0f); - B3_ASSERT(m_I.y.y > 0.0f); - B3_ASSERT(m_I.z.z > 0.0f); + B3_ASSERT(m_I.x.x > scalar(0)); + B3_ASSERT(m_I.y.y > scalar(0)); + B3_ASSERT(m_I.z.z > scalar(0)); m_invI = b3Inverse(m_I); m_worldInvI = b3RotateToFrame(m_invI, m_xf.rotation); if (m_flags & e_fixedRotationX) { - m_invI.y.y = 0.0f; - m_invI.z.y = 0.0f; - m_invI.y.z = 0.0f; - m_invI.z.z = 0.0f; + m_invI.y.y = scalar(0); + m_invI.z.y = scalar(0); + m_invI.y.z = scalar(0); + m_invI.z.z = scalar(0); - m_worldInvI.y.y = 0.0f; - m_worldInvI.z.y = 0.0f; - m_worldInvI.y.z = 0.0f; - m_worldInvI.z.z = 0.0f; + m_worldInvI.y.y = scalar(0); + m_worldInvI.z.y = scalar(0); + m_worldInvI.y.z = scalar(0); + m_worldInvI.z.z = scalar(0); } if (m_flags & e_fixedRotationY) { - m_invI.x.x = 0.0f; - m_invI.x.z = 0.0f; - m_invI.z.x = 0.0f; - m_invI.z.z = 0.0f; + m_invI.x.x = scalar(0); + m_invI.x.z = scalar(0); + m_invI.z.x = scalar(0); + m_invI.z.z = scalar(0); - m_worldInvI.x.x = 0.0f; - m_worldInvI.x.z = 0.0f; - m_worldInvI.z.x = 0.0f; - m_worldInvI.z.z = 0.0f; + m_worldInvI.x.x = scalar(0); + m_worldInvI.x.z = scalar(0); + m_worldInvI.z.x = scalar(0); + m_worldInvI.z.z = scalar(0); } if (m_flags & e_fixedRotationZ) { - m_invI.x.x = 0.0f; - m_invI.x.y = 0.0f; - m_invI.y.x = 0.0f; - m_invI.y.y = 0.0f; + m_invI.x.x = scalar(0); + m_invI.x.y = scalar(0); + m_invI.y.x = scalar(0); + m_invI.y.y = scalar(0); - m_worldInvI.x.x = 0.0f; - m_worldInvI.x.y = 0.0f; - m_worldInvI.y.x = 0.0f; - m_worldInvI.y.y = 0.0f; + m_worldInvI.x.x = scalar(0); + m_worldInvI.x.y = scalar(0); + m_worldInvI.y.x = scalar(0); + m_worldInvI.y.y = scalar(0); } } else { - m_mass = 1.0f; - m_invMass = 1.0f; + m_mass = scalar(1); + m_invMass = scalar(1); } // Move center of mass. @@ -461,6 +468,61 @@ void b3Body::SetType(b3BodyType type) } } +void b3Body::SetFixedRotation(bool flagX, bool flagY, bool flagZ) +{ + bool statusX = (m_flags & e_fixedRotationX) == e_fixedRotationX; + bool statusY = (m_flags & e_fixedRotationY) == e_fixedRotationY; + bool statusZ = (m_flags & e_fixedRotationZ) == e_fixedRotationZ; + + if (statusX == flagX && statusY == flagY && statusZ == flagZ) + { + return; + } + + if (flagX) + { + m_flags |= e_fixedRotationX; + } + else + { + m_flags &= ~e_fixedRotationX; + } + + if (flagY) + { + m_flags |= e_fixedRotationY; + } + else + { + m_flags &= ~e_fixedRotationY; + } + + if (flagZ) + { + m_flags |= e_fixedRotationZ; + } + else + { + m_flags &= ~e_fixedRotationZ; + } + + m_angularVelocity.SetZero(); + + ResetMass(); +} + +void b3Body::SetLinearSleepTolerance(scalar tolerance) +{ + B3_ASSERT(tolerance >= scalar(0)); + m_linearSleepTolerance = tolerance; +} + +void b3Body::SetAngularSleepTolerance(scalar tolerance) +{ + B3_ASSERT(tolerance >= scalar(0)); + m_angularSleepTolerance = tolerance; +} + void b3Body::Dump() const { u32 bodyIndex = m_islandID; @@ -469,11 +531,19 @@ void b3Body::Dump() const b3Log(" b3BodyDef bd;\n"); b3Log(" bd.type = (b3BodyType) %d;\n", m_type); b3Log(" bd.position.Set(%f, %f, %f);\n", m_sweep.worldCenter.x, m_sweep.worldCenter.y, m_sweep.worldCenter.z); - b3Log(" bd.orientation.Set(%f, %f, %f, %f);\n", m_sweep.orientation.x, m_sweep.orientation.y, m_sweep.orientation.z, m_sweep.orientation.w); + b3Log(" bd.orientation.Set(%f, %f, %f, %f);\n", m_sweep.orientation.v.x, m_sweep.orientation.v.y, m_sweep.orientation.v.z, m_sweep.orientation.s); b3Log(" bd.linearVelocity.Set(%f, %f, %f);\n", m_linearVelocity.x, m_linearVelocity.y, m_linearVelocity.z); b3Log(" bd.angularVelocity.Set(%f, %f, %f);\n", m_angularVelocity.x, m_angularVelocity.y, m_angularVelocity.z); - b3Log(" bd.gravityScale = %f;\n", m_gravityScale); + b3Log(" bd.gravityScale.Set(%f, %f, %f);\n", m_gravityScale.x, m_gravityScale.y, m_gravityScale.z); + b3Log(" bd.linearDamping.Set(%f, %f, %f);\n", m_linearDamping.x, m_linearDamping.y, m_linearDamping.z); + b3Log(" bd.angularDamping.Set(%f, %f, %f);\n", m_angularDamping.x, m_angularDamping.y, m_angularDamping.z); b3Log(" bd.awake = %d;\n", m_flags & e_awakeFlag); + b3Log(" bd.allowSleep = %d;\n", m_flags & e_autoSleepFlag); + b3Log(" bd.fixedRotationX = %d;\n", m_flags & e_fixedRotationX); + b3Log(" bd.fixedRotationY = %d;\n", m_flags & e_fixedRotationY); + b3Log(" bd.fixedRotationZ = %d;\n", m_flags & e_fixedRotationZ); + b3Log(" bd.linearSleepTolerance = %f;\n", m_linearSleepTolerance); + b3Log(" bd.angularSleepTolerance = %f;\n", m_angularSleepTolerance); b3Log(" \n"); b3Log(" bodies[%d] = world.CreateBody(bd);\n"); b3Log(" \n"); diff --git a/src/bounce/dynamics/contact_manager.cpp b/src/bounce/dynamics/contact_manager.cpp index 715298d..dbe57e6 100644 --- a/src/bounce/dynamics/contact_manager.cpp +++ b/src/bounce/dynamics/contact_manager.cpp @@ -23,23 +23,26 @@ #include #include -b3ContactManager::b3ContactManager() : +b3ContactManager::b3ContactManager() : m_convexBlocks(sizeof(b3ConvexContact)), m_meshBlocks(sizeof(b3MeshContact)) { - m_contactListener = NULL; - m_contactFilter = NULL; + m_contactListener = nullptr; + m_contactFilter = nullptr; + + m_allocators[e_convexContact] = &m_convexBlocks; + m_allocators[e_meshContact] = &m_meshBlocks; } -void b3ContactManager::AddPair(void* dataA, void* dataB) +void b3ContactManager::AddPair(void* dataA, void* dataB) { b3Shape* shapeA = (b3Shape*)dataA; b3Shape* shapeB = (b3Shape*)dataB; b3Body* bodyA = shapeA->GetBody(); b3Body* bodyB = shapeB->GetBody(); - - if (bodyA == bodyB) + + if (bodyA == bodyB) { // Two shapes that belong to the same body cannot collide. return; @@ -52,7 +55,7 @@ void b3ContactManager::AddPair(void* dataA, void* dataB) if (ce->other == shapeA) { b3Contact* c = ce->contact; - + b3Shape* sA = c->GetShapeA(); b3Shape* sB = c->GetShapeB(); @@ -70,7 +73,8 @@ void b3ContactManager::AddPair(void* dataA, void* dataB) } } - // Check if a joint prevents collision between the bodies. + // Is at least one of the bodies kinematic or dynamic? + // Does a joint prevent the collision? if (bodyA->ShouldCollide(bodyB) == false) { // The bodies must not collide with each other. @@ -88,13 +92,12 @@ void b3ContactManager::AddPair(void* dataA, void* dataB) // Create contact. b3Contact* c = Create(shapeA, shapeB); - if (c == NULL) + if (c == nullptr) { return; } - // Get the shapes from the contact again - // because contact creation will swap the shapes if typeA > typeB. + // Get the shapes from the contact again because contact creation can swap the shapes. shapeA = c->GetShapeA(); shapeB = c->GetShapeB(); bodyA = shapeA->GetBody(); @@ -106,7 +109,7 @@ void b3ContactManager::AddPair(void* dataA, void* dataB) // Initialize edge A pair->edgeA.contact = c; pair->edgeA.other = shapeB; - + // Add edge A to shape A's contact list. shapeA->m_contactEdges.PushFront(&pair->edgeA); @@ -118,7 +121,7 @@ void b3ContactManager::AddPair(void* dataA, void* dataB) shapeB->m_contactEdges.PushFront(&pair->edgeB); // Awake the bodies if both are not sensors. - if (!shapeA->IsSensor() && !shapeB->IsSensor()) + if (!shapeA->IsSensor() && !shapeB->IsSensor()) { bodyA->SetAwake(true); bodyB->SetAwake(true); @@ -126,12 +129,12 @@ void b3ContactManager::AddPair(void* dataA, void* dataB) // Add the contact to the world contact list. m_contactList.PushFront(c); - + if (c->m_type == e_meshContact) { // Add the contact to the world mesh contact list. b3MeshContact* mc = (b3MeshContact*)c; - + // Find new shape-child overlapping pairs. mc->FindNewPairs(); @@ -164,10 +167,10 @@ void b3ContactManager::FindNewContacts() } } -void b3ContactManager::UpdateContacts() -{ +void b3ContactManager::UpdateContacts() +{ B3_PROFILE("Update Contacts"); - + // Update the state of all contacts. b3Contact* c = m_contactList.m_head; while (c) @@ -177,11 +180,11 @@ void b3ContactManager::UpdateContacts() b3Shape* shapeA = pair->shapeA; u32 proxyA = shapeA->m_broadPhaseID; b3Body* bodyA = shapeA->m_body; - + b3Shape* shapeB = pair->shapeB; u32 proxyB = shapeB->m_broadPhaseID; b3Body* bodyB = shapeB->m_body; - + // Check if the bodies must not collide with each other. if (bodyA->ShouldCollide(bodyB) == false) { @@ -207,7 +210,7 @@ void b3ContactManager::UpdateContacts() // At least one body must be dynamic or kinematic. bool activeA = bodyA->IsAwake() && bodyA->m_type != e_staticBody; bool activeB = bodyB->IsAwake() && bodyB->m_type != e_staticBody; - if (activeA == false && activeB == false) + if (activeA == false && activeB == false) { c = c->m_next; continue; @@ -230,83 +233,40 @@ void b3ContactManager::UpdateContacts() } } -b3Contact* b3ContactManager::Create(b3Shape* shapeA, b3Shape* shapeB) +b3Contact* b3ContactManager::Create(b3Shape* shapeA, b3Shape* shapeB) { - b3ShapeType typeA = shapeA->GetType(); - b3ShapeType typeB = shapeB->GetType(); - - if (typeA > typeB) - { - b3Swap(typeA, typeB); - b3Swap(shapeA, shapeB); - } - - B3_ASSERT(typeA <= typeB); - - b3Contact* c = NULL; - if (typeA != e_meshShape && typeB != e_meshShape) - { - void* block = m_convexBlocks.Allocate(); - b3ConvexContact* cxc = new (block) b3ConvexContact(shapeA, shapeB); - c = cxc; - } - else - { - if (typeB == e_meshShape) - { - void* block = m_meshBlocks.Allocate(); - b3MeshContact* mxc = new (block) b3MeshContact(shapeA, shapeB); - c = mxc; - } - else - { - // Collisions between meshes are not implemented. - return NULL; - } - } - - // The shapes might be swapped. - c->m_pair.shapeA = shapeA; - c->m_pair.shapeB = shapeB; - return c; + return b3Contact::Create(shapeA, shapeB, m_allocators); } -void b3ContactManager::Destroy(b3Contact* c) +void b3ContactManager::Destroy(b3Contact* c) { // Report to the contact listener the contact will be destroyed. - if (m_contactListener) + if (m_contactListener) { - if (c->IsOverlapping()) + if (c->IsOverlapping()) { m_contactListener->EndContact(c); } } - + b3OverlappingPair* pair = &c->m_pair; - + b3Shape* shapeA = c->GetShapeA(); b3Shape* shapeB = c->GetShapeB(); - + shapeA->m_contactEdges.Remove(&pair->edgeA); shapeB->m_contactEdges.Remove(&pair->edgeB); // Remove the contact from the world contact list. m_contactList.Remove(c); - if (c->m_type == e_convexContact) + if (c->m_type == e_meshContact) { - b3ConvexContact* cc = (b3ConvexContact*)c; - cc->~b3ConvexContact(); - m_convexBlocks.Free(cc); - } - else - { - b3MeshContact* mc = (b3MeshContact*)c; - // Remove the mesh contact from the world mesh contact list. + b3MeshContact* mc = (b3MeshContact*)c; m_meshContactList.Remove(&mc->m_link); - - mc->~b3MeshContact(); - m_meshBlocks.Free(mc); } + + // Free the contact. + b3Contact::Destroy(c, m_allocators); } \ No newline at end of file diff --git a/src/bounce/dynamics/contacts/collide/clip.cpp b/src/bounce/dynamics/contacts/collide/clip.cpp index 78be3c8..8369e49 100644 --- a/src/bounce/dynamics/contacts/collide/clip.cpp +++ b/src/bounce/dynamics/contacts/collide/clip.cpp @@ -23,11 +23,11 @@ void b3BuildEdge(b3ClipVertex vOut[2], const b3Capsule* hull) { - vOut[0].position = hull->vertices[0]; - vOut[0].pair = b3MakePair(0, B3_NULL_EDGE, 0, B3_NULL_EDGE); + vOut[0].position = hull->vertex1; + vOut[0].pair = b3MakePair(B3_NULL_EDGE, B3_NULL_EDGE, 0, 0); - vOut[1].position = hull->vertices[1]; - vOut[1].pair = b3MakePair(1, B3_NULL_EDGE, 0, B3_NULL_EDGE); + vOut[1].position = hull->vertex2; + vOut[1].pair = b3MakePair(B3_NULL_EDGE, B3_NULL_EDGE, 1, 0); } void b3BuildPolygon(b3ClipPolygon& pOut, @@ -40,13 +40,14 @@ void b3BuildPolygon(b3ClipPolygon& pOut, const b3HalfEdge* edge = begin; do { - const b3HalfEdge* twin = hull->GetEdge(edge->twin); - u32 edgeId = twin->twin; - u32 previousEdgeId = twin->next; + const b3HalfEdge* twin = hull->GetEdge(edge->twin); + + u32 inEdge = edge->prev; + u32 outEdge = twin->twin; b3ClipVertex clipVertex; clipVertex.position = b3Mul(xf, hull->GetVertex(edge->origin)); - clipVertex.pair = b3MakePair(previousEdgeId, B3_NULL_EDGE, edgeId, B3_NULL_EDGE); + clipVertex.pair = b3MakePair(B3_NULL_EDGE, B3_NULL_EDGE, inEdge, outEdge); pOut.PushBack(clipVertex); @@ -62,33 +63,33 @@ u32 b3ClipEdgeToPlane(b3ClipVertex vOut[2], { u32 numOut = 0; - float32 distance1 = b3Distance(vIn[0].position, plane.plane); - float32 distance2 = b3Distance(vIn[1].position, plane.plane); + scalar distance1 = b3Distance(vIn[0].position, plane.plane); + scalar distance2 = b3Distance(vIn[1].position, plane.plane); // If the points are behind the plane keep them - if (distance1 <= 0.0f) + if (distance1 <= scalar(0)) { vOut[numOut++] = vIn[0]; } - if (distance2 <= 0.0f) + if (distance2 <= scalar(0)) { vOut[numOut++] = vIn[1]; } // If the points are on opposite sides keep intersection - if (distance1 <= 0.0f && distance2 > 0.0f) + if (distance1 <= scalar(0) && distance2 > scalar(0)) { - float32 fraction = distance1 / (distance1 - distance2); + scalar fraction = distance1 / (distance1 - distance2); vOut[numOut].position = vIn[0].position + fraction * (vIn[1].position - vIn[0].position); - vOut[numOut].pair = b3MakePair(vIn[0].pair.inEdge1, B3_NULL_EDGE, B3_NULL_EDGE, plane.id); + vOut[numOut].pair = b3MakePair(plane.edge, B3_NULL_EDGE, vIn[0].pair.outEdge2, B3_NULL_EDGE); ++numOut; } - else if (distance1 > 0.0f && distance2 <= 0.0f) + else if (distance1 > scalar(0) && distance2 <= scalar(0)) { - float32 fraction = distance1 / (distance1 - distance2); + scalar fraction = distance1 / (distance1 - distance2); vOut[numOut].position = vIn[0].position + fraction * (vIn[1].position - vIn[0].position); - vOut[numOut].pair = b3MakePair(vIn[1].pair.inEdge1, plane.id, B3_NULL_EDGE, B3_NULL_EDGE); + vOut[numOut].pair = b3MakePair(B3_NULL_EDGE, plane.edge, B3_NULL_EDGE, vIn[1].pair.inEdge2); ++numOut; } @@ -103,40 +104,40 @@ void b3ClipPolygonToPlane(b3ClipPolygon& pOut, B3_ASSERT(pOut.Count() == 0); b3ClipVertex v1 = pIn.Back(); - float32 distance1 = b3Distance(v1.position, plane.plane); + scalar distance1 = b3Distance(v1.position, plane.plane); for (u32 i = 0; i < pIn.Count(); ++i) { b3ClipVertex v2 = pIn[i]; - float32 distance2 = b3Distance(v2.position, plane.plane); + scalar distance2 = b3Distance(v2.position, plane.plane); - if (distance1 <= 0.0f && distance2 <= 0.0f) + if (distance1 <= scalar(0) && distance2 <= scalar(0)) { // Both vertices are behind or lying on the plane. // Keep v2 pOut.PushBack(v2); } - else if (distance1 <= 0.0f && distance2 > 0.0f) + else if (distance1 <= scalar(0) && distance2 > scalar(0)) { // v1 is behind and v2 in front // Keep intersection - float32 fraction = distance1 / (distance1 - distance2); + scalar fraction = distance1 / (distance1 - distance2); b3ClipVertex vertex; vertex.position = v1.position + fraction * (v2.position - v1.position); - vertex.pair = b3MakePair(v1.pair.inEdge1, B3_NULL_EDGE, B3_NULL_EDGE, plane.id); + vertex.pair = b3MakePair(plane.edge, B3_NULL_EDGE, v1.pair.outEdge2, B3_NULL_EDGE); pOut.PushBack(vertex); } - else if (distance1 > 0.0f && distance2 <= 0.0f) + else if (distance1 > scalar(0) && distance2 <= scalar(0)) { // v2 is behind and v1 in front // Keep intersection and v2 - float32 fraction = distance1 / (distance1 - distance2); + scalar fraction = distance1 / (distance1 - distance2); b3ClipVertex vertex; vertex.position = v1.position + fraction * (v2.position - v1.position); - vertex.pair = b3MakePair(v1.pair.inEdge1, plane.id, B3_NULL_EDGE, B3_NULL_EDGE); + vertex.pair = b3MakePair(B3_NULL_EDGE, plane.edge, B3_NULL_EDGE, v2.pair.inEdge2); pOut.PushBack(vertex); pOut.PushBack(v2); @@ -157,15 +158,15 @@ u32 b3ClipEdgeToFace(b3ClipVertex vOut[2], vOut[1] = vIn[1]; u32 numOut = 0; - b3Vec3 P1 = hull->vertices[0]; - b3Vec3 Q1 = hull->vertices[1]; + b3Vec3 P1 = hull->vertex1; + b3Vec3 Q1 = hull->vertex2; b3Vec3 E1 = Q1 - P1; B3_ASSERT(b3Dot(E1, E1) > B3_EPSILON * B3_EPSILON); b3ClipPlane clipPlane1; clipPlane1.plane.normal = b3Normalize(E1); clipPlane1.plane.offset = b3Dot(clipPlane1.plane.normal, Q1); - clipPlane1.id = 0; + clipPlane1.edge = 0; b3ClipVertex clipEdge1[2]; numOut = b3ClipEdgeToPlane(clipEdge1, vOut, clipPlane1); @@ -181,7 +182,7 @@ u32 b3ClipEdgeToFace(b3ClipVertex vOut[2], b3ClipPlane clipPlane2; clipPlane2.plane.normal = -clipPlane1.plane.normal; clipPlane2.plane.offset = b3Dot(clipPlane2.plane.normal, P1); - clipPlane2.id = 1; + clipPlane2.edge = 1; b3ClipVertex clipEdge2[2]; numOut = b3ClipEdgeToPlane(clipEdge2, vOut, clipPlane2); @@ -199,7 +200,7 @@ u32 b3ClipEdgeToFace(b3ClipVertex vOut[2], // Clip a segment to face side planes. u32 b3ClipEdgeToFace(b3ClipVertex vOut[2], - const b3ClipVertex vIn[2], const b3Transform& xf, float32 r, u32 index, const b3Hull* hull) + const b3ClipVertex vIn[2], const b3Transform& xf, scalar r, u32 index, const b3Hull* hull) { // Start from somewhere. vOut[0] = vIn[0]; @@ -218,7 +219,7 @@ u32 b3ClipEdgeToFace(b3ClipVertex vOut[2], plane.offset += r; b3ClipPlane clipPlane; - clipPlane.id = edgeId; + clipPlane.edge = edgeId; clipPlane.plane = b3Mul(xf, plane); b3ClipVertex clipEdge[2]; @@ -241,7 +242,7 @@ u32 b3ClipEdgeToFace(b3ClipVertex vOut[2], // Clip a polygon to face side planes. void b3ClipPolygonToFace(b3ClipPolygon& pOut, - const b3ClipPolygon& pIn, const b3Transform& xf, float32 r, u32 index, const b3Hull* hull) + const b3ClipPolygon& pIn, const b3Transform& xf, scalar r, u32 index, const b3Hull* hull) { B3_ASSERT(pIn.Count() > 0); B3_ASSERT(pOut.Count() == 0); @@ -261,7 +262,7 @@ void b3ClipPolygonToFace(b3ClipPolygon& pOut, plane.offset += r; b3ClipPlane clipPlane; - clipPlane.id = edgeId; + clipPlane.edge = edgeId; clipPlane.plane = b3Mul(xf, plane); b3StackArray clipPolygon; diff --git a/src/bounce/dynamics/contacts/collide/collide.cpp b/src/bounce/dynamics/contacts/collide/collide.cpp index 3e716ec..7c74b6d 100644 --- a/src/bounce/dynamics/contacts/collide/collide.cpp +++ b/src/bounce/dynamics/contacts/collide/collide.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -43,10 +44,18 @@ void b3ShapeGJKProxy::Set(const b3Shape* shape, u32 index) { const b3CapsuleShape* capsule = (b3CapsuleShape*)shape; vertexCount = 2; - vertices = capsule->m_centers; + vertices = &capsule->m_vertex1; radius = capsule->m_radius; break; } + case e_triangleShape: + { + const b3TriangleShape* triangle = (b3TriangleShape*)shape; + vertexCount = 3; + vertices = &triangle->m_vertex1; + radius = triangle->m_radius; + break; + } case e_hullShape: { const b3HullShape* hull = (b3HullShape*)shape; @@ -62,11 +71,11 @@ void b3ShapeGJKProxy::Set(const b3Shape* shape, u32 index) B3_ASSERT(index >= 0); B3_ASSERT(index < mesh->m_mesh->triangleCount); - const b3Triangle& triangle = mesh->m_mesh->GetTriangle(index); + const b3MeshTriangle* triangle = mesh->m_mesh->GetTriangle(index); - vertexBuffer[0] = mesh->m_mesh->vertices[triangle.v1]; - vertexBuffer[1] = mesh->m_mesh->vertices[triangle.v2]; - vertexBuffer[2] = mesh->m_mesh->vertices[triangle.v3]; + vertexBuffer[0] = b3MulCW(mesh->m_scale, mesh->m_mesh->vertices[triangle->v1]); + vertexBuffer[1] = b3MulCW(mesh->m_scale, mesh->m_mesh->vertices[triangle->v2]); + vertexBuffer[2] = b3MulCW(mesh->m_scale, mesh->m_mesh->vertices[triangle->v3]); vertexCount = 3; vertices = vertexBuffer; @@ -90,7 +99,7 @@ bool b3TestOverlap(const b3Transform& xfA, u32 indexA, const b3Shape* shapeA, b3GJKOutput distance = b3GJK(xfA, proxyA, xfB, proxyB, true, &cache->simplexCache); - const float32 kTol = 10.0f * B3_EPSILON; + const scalar kTol = scalar(10) * B3_EPSILON; return distance.distance <= kTol; } @@ -100,56 +109,88 @@ void b3CollideSphereAndSphereShapes(b3Manifold& manifold, b3ConvexCache* cache) { B3_NOT_USED(cache); - b3SphereShape* hullA = (b3SphereShape*)shapeA; - b3SphereShape* hullB = (b3SphereShape*)shapeB; - b3CollideSphereAndSphere(manifold, xfA, hullA, xfB, hullB); + b3SphereShape* sphereA = (b3SphereShape*)shapeA; + b3SphereShape* sphereB = (b3SphereShape*)shapeB; + b3CollideSphereAndSphere(manifold, xfA, sphereA, xfB, sphereB); } -void b3CollideSphereAndHullShapes(b3Manifold& manifold, +void b3CollideCapsuleAndSphereShapes(b3Manifold& manifold, const b3Transform& xfA, const b3Shape* shapeA, const b3Transform& xfB, const b3Shape* shapeB, b3ConvexCache* cache) { B3_NOT_USED(cache); - b3SphereShape* hullA = (b3SphereShape*)shapeA; + b3CapsuleShape* capsuleA = (b3CapsuleShape*)shapeA; + b3SphereShape* sphereB = (b3SphereShape*)shapeB; + b3CollideCapsuleAndSphere(manifold, xfA, capsuleA, xfB, sphereB); +} + +void b3CollideCapsuleAndCapsuleShapes(b3Manifold& manifold, + const b3Transform& xfA, const b3Shape* shapeA, + const b3Transform& xfB, const b3Shape* shapeB, + b3ConvexCache* cache) +{ + B3_NOT_USED(cache); + b3CapsuleShape* capsuleA = (b3CapsuleShape*)shapeA; + b3CapsuleShape* capsuleB = (b3CapsuleShape*)shapeB; + b3CollideCapsuleAndCapsule(manifold, xfA, capsuleA, xfB, capsuleB); +} + +void b3CollideTriangleAndSphereShapes(b3Manifold& manifold, + const b3Transform& xfA, const b3Shape* shapeA, + const b3Transform& xfB, const b3Shape* shapeB, + b3ConvexCache* cache) +{ + B3_NOT_USED(cache); + b3TriangleShape* triangleA = (b3TriangleShape*)shapeA; + b3SphereShape* sphereB = (b3SphereShape*)shapeB; + b3CollideTriangleAndSphere(manifold, xfA, triangleA, xfB, sphereB); +} + +void b3CollideTriangleAndCapsuleShapes(b3Manifold& manifold, + const b3Transform& xfA, const b3Shape* shapeA, + const b3Transform& xfB, const b3Shape* shapeB, + b3ConvexCache* cache) +{ + B3_NOT_USED(cache); + b3TriangleShape* triangleA = (b3TriangleShape*)shapeA; + b3CapsuleShape* capsuleB = (b3CapsuleShape*)shapeB; + b3CollideTriangleAndCapsule(manifold, xfA, triangleA, xfB, capsuleB); +} + +void b3CollideTriangleAndHullShapes(b3Manifold& manifold, + const b3Transform& xfA, const b3Shape* shapeA, + const b3Transform& xfB, const b3Shape* shapeB, + b3ConvexCache* cache) +{ + b3TriangleShape* triangleA = (b3TriangleShape*)shapeA; b3HullShape* hullB = (b3HullShape*)shapeB; - b3CollideSphereAndHull(manifold, xfA, hullA, xfB, hullB); + b3CollideTriangleAndHull(manifold, xfA, triangleA, xfB, hullB, cache); } -void b3CollideSphereAndCapsuleShapes(b3Manifold& manifold, +void b3CollideHullAndSphereShapes(b3Manifold& manifold, const b3Transform& xfA, const b3Shape* shapeA, const b3Transform& xfB, const b3Shape* shapeB, b3ConvexCache* cache) { B3_NOT_USED(cache); - b3SphereShape* hullA = (b3SphereShape*)shapeA; - b3CapsuleShape* hullB = (b3CapsuleShape*)shapeB; - b3CollideSphereAndCapsule(manifold, xfA, hullA, xfB, hullB); + b3HullShape* hullA = (b3HullShape*)shapeA; + b3SphereShape* sphereB = (b3SphereShape*)shapeB; + b3CollideHullAndSphere(manifold, xfA, hullA, xfB, sphereB); } -void b3CollideCapsuleAndCapsuleShapes(b3Manifold& manifold, +void b3CollideHullAndCapsuleShapes(b3Manifold& manifold, const b3Transform& xfA, const b3Shape* shapeA, const b3Transform& xfB, const b3Shape* shapeB, b3ConvexCache* cache) { B3_NOT_USED(cache); - b3CapsuleShape* hullA = (b3CapsuleShape*)shapeA; - b3CapsuleShape* hullB = (b3CapsuleShape*)shapeB; - b3CollideCapsuleAndCapsule(manifold, xfA, hullA, xfB, hullB); + b3HullShape* hullA = (b3HullShape*)shapeA; + b3CapsuleShape* capsuleB = (b3CapsuleShape*)shapeB; + b3CollideHullAndCapsule(manifold, xfA, hullA, xfB, capsuleB); } -void b3CollideCapsuleAndHullShapes(b3Manifold& manifold, - const b3Transform& xfA, const b3Shape* shapeA, - const b3Transform& xfB, const b3Shape* shapeB, - b3ConvexCache* cache) -{ - B3_NOT_USED(cache); - b3CapsuleShape* hullA = (b3CapsuleShape*)shapeA; - b3HullShape* hullB = (b3HullShape*)shapeB; - b3CollideCapsuleAndHull(manifold, xfA, hullA, xfB, hullB); -} - -void b3CollideHullAndHullShapes(b3Manifold& manifold, +void b3CollideHullAndHullShapes(b3Manifold& manifold, const b3Transform& xfA, const b3Shape* shapeA, const b3Transform& xfB, const b3Shape* shapeB, b3ConvexCache* cache) @@ -159,30 +200,54 @@ void b3CollideHullAndHullShapes(b3Manifold& manifold, b3CollideHullAndHull(manifold, xfA, hullA, xfB, hullB, cache); } +typedef void(*b3CollideFcn)(b3Manifold&, + const b3Transform&, const b3Shape*, + const b3Transform&, const b3Shape*, + b3ConvexCache*); + +static b3CollideFcn s_functions[e_maxShapes][e_maxShapes]; + +static void b3SetCollideFunction(b3ShapeType typeA, b3ShapeType typeB, b3CollideFcn fcn) +{ + B3_ASSERT(0 <= typeA && typeA < e_maxShapes); + B3_ASSERT(0 <= typeB && typeB < e_maxShapes); + + s_functions[typeA][typeB] = fcn; +} + +static void b3InitializeCollideFunctions() +{ + b3SetCollideFunction(e_sphereShape, e_sphereShape, &b3CollideSphereAndSphereShapes); + + b3SetCollideFunction(e_capsuleShape, e_sphereShape, &b3CollideCapsuleAndSphereShapes); + b3SetCollideFunction(e_capsuleShape, e_capsuleShape, &b3CollideCapsuleAndCapsuleShapes); + + b3SetCollideFunction(e_triangleShape, e_sphereShape, &b3CollideTriangleAndSphereShapes); + b3SetCollideFunction(e_triangleShape, e_capsuleShape, &b3CollideTriangleAndCapsuleShapes); + b3SetCollideFunction(e_triangleShape, e_hullShape, &b3CollideTriangleAndHullShapes); + + b3SetCollideFunction(e_hullShape, e_capsuleShape, &b3CollideHullAndCapsuleShapes); + b3SetCollideFunction(e_hullShape, e_sphereShape, &b3CollideHullAndSphereShapes); + b3SetCollideFunction(e_hullShape, e_hullShape, &b3CollideHullAndHullShapes); +} + void b3CollideShapeAndShape(b3Manifold& manifold, const b3Transform& xfA, const b3Shape* shapeA, const b3Transform& xfB, const b3Shape* shapeB, b3ConvexCache* cache) { - typedef void(*b3CollideFunction)(b3Manifold&, - const b3Transform&, const b3Shape*, - const b3Transform&, const b3Shape*, - b3ConvexCache*); - - static const b3CollideFunction s_CollideMatrix[e_maxShapes][e_maxShapes] = + static bool b3Collide_initilized = false; + if (b3Collide_initilized == false) { - { &b3CollideSphereAndSphereShapes, &b3CollideSphereAndCapsuleShapes, &b3CollideSphereAndHullShapes }, - { NULL, &b3CollideCapsuleAndCapsuleShapes, &b3CollideCapsuleAndHullShapes }, - { NULL, NULL, &b3CollideHullAndHullShapes }, - }; + b3InitializeCollideFunctions(); + b3Collide_initilized = true; + } b3ShapeType typeA = shapeA->GetType(); b3ShapeType typeB = shapeB->GetType(); - B3_ASSERT(typeA <= typeB); + b3CollideFcn fcn = s_functions[typeA][typeB]; - b3CollideFunction CollideFunc = s_CollideMatrix[typeA][typeB]; - - B3_ASSERT(CollideFunc); - CollideFunc(manifold, xfA, shapeA, xfB, shapeB, cache); + B3_ASSERT(fcn != nullptr); + fcn(manifold, xfA, shapeA, xfB, shapeB, cache); } \ No newline at end of file diff --git a/src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp b/src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp deleted file mode 100644 index fef0862..0000000 --- a/src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void b3BuildEdgeContact(b3Manifold& manifold, - const b3Transform& xf1, const b3CapsuleShape* s1, - const b3Transform& xf2, u32 index2, const b3HullShape* s2) -{ - b3Vec3 P1 = xf1 * s1->m_centers[0]; - b3Vec3 Q1 = xf1 * s1->m_centers[1]; - b3Vec3 E1 = Q1 - P1; - b3Vec3 N1 = E1; - float32 L1 = N1.Normalize(); - B3_ASSERT(L1 > 0.0f); - - const b3Hull* hull2 = s2->m_hull; - const b3HalfEdge* edge2 = hull2->GetEdge(index2); - const b3HalfEdge* twin2 = hull2->GetEdge(index2 + 1); - - b3Vec3 C2 = xf2 * hull2->centroid; - b3Vec3 P2 = xf2 * hull2->GetVertex(edge2->origin); - b3Vec3 Q2 = xf2 * hull2->GetVertex(twin2->origin); - b3Vec3 E2 = Q2 - P2; - b3Vec3 N2 = E2; - float32 L2 = N2.Normalize(); - B3_ASSERT(L2 > 0.0f); - - // Compute the closest points on the two lines. - float32 b = b3Dot(N1, N2); - float32 den = 1.0f - b * b; - if (den == 0.0f) - { - return; - } - - float32 inv_den = 1.0f / den; - - b3Vec3 E3 = P1 - P2; - - float32 d = b3Dot(N1, E3); - float32 e = b3Dot(N2, E3); - - float32 s = inv_den * (b * e - d); - float32 t = inv_den * (e - b * d); - - b3Vec3 c1 = P1 + s * N1; - b3Vec3 c2 = P2 + t * N2; - - // Ensure normal orientation to hull 2. - b3Vec3 N = b3Cross(E1, E2); - float32 LN = N.Normalize(); - B3_ASSERT(LN > 0.0f); - if (b3Dot(N, P2 - C2) > 0.0f) - { - N = -N; - } - - b3FeaturePair pair = b3MakePair(0, 1, index2, index2 + 1); - - manifold.pointCount = 1; - manifold.points[0].localNormal1 = b3MulT(xf1.rotation, N); - manifold.points[0].localPoint1 = b3MulT(xf1, c1); - manifold.points[0].localPoint2 = b3MulT(xf2, c2); - manifold.points[0].key = b3MakeKey(pair); -} - -static void b3BuildFaceContact(b3Manifold& manifold, - const b3Transform& xf1, const b3CapsuleShape* s1, - const b3Transform& xf2, u32 index2, const b3HullShape* s2) -{ - // Clip edge 1 against the side planes of the face 2. - const b3Capsule hull1(xf1 * s1->m_centers[0], xf1 * s1->m_centers[1], 0.0f); - float32 r1 = s1->m_radius; - - b3ClipVertex edge1[2]; - b3BuildEdge(edge1, &hull1); - - const b3Hull* hull2 = s2->m_hull; - float32 r2 = s2->m_radius; - - b3ClipVertex clipEdge1[2]; - u32 clipCount = b3ClipEdgeToFace(clipEdge1, edge1, xf2, r2, index2, hull2); - - // Project clipped edge 1 onto face plane 2. - b3Plane localPlane2 = hull2->GetPlane(index2); - b3Plane plane2 = xf2 * localPlane2; - const b3Face* face2 = hull2->GetFace(index2); - const b3HalfEdge* edge2 = hull2->GetEdge(face2->edge); - b3Vec3 localPoint2 = hull2->GetVertex(edge2->origin); - - // Ensure normal orientation to hull 2. - b3Vec3 n1 = -plane2.normal; - - float32 totalRadius = r1 + r2; - - u32 pointCount = 0; - for (u32 i = 0; i < clipCount; ++i) - { - b3Vec3 c1 = clipEdge1[i].position; - float32 s = b3Distance(c1, plane2); - if (s <= totalRadius) - { - b3Vec3 c2 = b3ClosestPointOnPlane(c1, plane2); - - b3ManifoldPoint* mp = manifold.points + pointCount; - mp->localNormal1 = b3MulT(xf1.rotation, n1); - mp->localPoint1 = b3MulT(xf1, c1); - mp->localPoint2 = b3MulT(xf2, c2); - mp->key = b3MakeKey(clipEdge1[i].pair); - - ++pointCount; - } - } - - manifold.pointCount = pointCount; -} - -void b3CollideCapsuleAndHull(b3Manifold& manifold, - const b3Transform& xf1, const b3CapsuleShape* s1, - const b3Transform& xf2, const b3HullShape* s2) -{ - b3ShapeGJKProxy proxy1(s1, 0); - b3ShapeGJKProxy proxy2(s2, 0); - - b3GJKOutput gjk = b3GJK(xf1, proxy1, xf2, proxy2); - - float32 r1 = s1->m_radius; - float32 r2 = s2->m_radius; - - float32 totalRadius = r1 + r2; - - if (gjk.distance > totalRadius) - { - return; - } - - const b3Capsule hull1(s1->m_centers[0], s1->m_centers[1], 0.0f); - const b3Hull* hull2 = s2->m_hull; - - if (gjk.distance > B3_EPSILON) - { - // Define incident face. - b3Vec3 N1 = (gjk.point2 - gjk.point1) / gjk.distance; - b3Vec3 localN1 = b3MulT(xf2.rotation, N1); - - // Search reference face. - u32 index2 = hull2->GetSupportFace(-localN1); - b3Vec3 localN2 = hull2->GetPlane(index2).normal; - b3Vec3 N2 = xf2.rotation * localN2; - - // Paralell vectors |v1xv2| = sin(theta) - const float32 kTol = 0.005f; - b3Vec3 N = b3Cross(N1, N2); - float32 L = b3Dot(N, N); - if (L < kTol * kTol) - { - // Reference face found. - // Try to build a face contact. - b3BuildFaceContact(manifold, xf1, s1, xf2, index2, s2); - if (manifold.pointCount == 2) - { - return; - } - } - - manifold.pointCount = 1; - manifold.points[0].localNormal1 = b3MulT(xf1.rotation, N1); - manifold.points[0].localPoint1 = b3MulT(xf1, gjk.point1); - manifold.points[0].localPoint2 = b3MulT(xf2, gjk.point2); - manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; - manifold.points[0].key.key1 = 0; - manifold.points[0].key.key2 = 0; - - return; - } - - b3FaceQuery faceQuery2 = b3QueryFaceSeparation(xf1, &hull1, xf2, hull2); - if (faceQuery2.separation > totalRadius) - { - return; - } - - b3EdgeQuery edgeQuery = b3QueryEdgeSeparation(xf1, &hull1, xf2, hull2); - if (edgeQuery.separation > totalRadius) - { - return; - } - - const float32 kTol = 0.1f * B3_LINEAR_SLOP; - if (edgeQuery.separation > faceQuery2.separation + kTol) - { - b3BuildEdgeContact(manifold, xf1, s1, xf2, edgeQuery.index2, s2); - } - else - { - b3BuildFaceContact(manifold, xf1, s1, xf2, faceQuery2.index, s2); - } -} \ No newline at end of file diff --git a/src/bounce/dynamics/contacts/collide/collide_sphere_capsule.cpp b/src/bounce/dynamics/contacts/collide/collide_capsule_sphere.cpp similarity index 58% rename from src/bounce/dynamics/contacts/collide/collide_sphere_capsule.cpp rename to src/bounce/dynamics/contacts/collide/collide_capsule_sphere.cpp index 0ffc545..cf6e35c 100644 --- a/src/bounce/dynamics/contacts/collide/collide_sphere_capsule.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_capsule_sphere.cpp @@ -18,47 +18,48 @@ #include #include -#include #include +#include -void b3CollideSphereAndCapsule(b3Manifold& manifold, - const b3Transform& xf1, const b3SphereShape* s1, - const b3Transform& xf2, const b3CapsuleShape* s2) +void b3CollideCapsuleAndSphere(b3Manifold& manifold, + const b3Transform& xf1, const b3CapsuleShape* s1, + const b3Transform& xf2, const b3SphereShape* s2) { - b3Vec3 Q = b3Mul(xf1, s1->m_center); + // The sphere center in the frame of the capsule. + b3Vec3 Q = b3MulT(xf1, b3Mul(xf2, s2->m_center)); - b3Vec3 A = b3Mul(xf2, s2->m_centers[0]); - b3Vec3 B = b3Mul(xf2, s2->m_centers[1]); + b3Vec3 A = s1->m_vertex1, B = s1->m_vertex2; + b3Vec3 AB = B - A; // Barycentric coordinates for Q - float32 u = b3Dot(B - Q, AB); - float32 v = b3Dot(Q - A, AB); + scalar u = b3Dot(B - Q, AB); + scalar v = b3Dot(Q - A, AB); - float32 radius = s1->m_radius + s2->m_radius; + scalar radius = s1->m_radius + s2->m_radius; - if (v <= 0.0f) + if (v <= scalar(0)) { // A b3Vec3 P = A; - b3Vec3 d = P - Q; - float32 dd = b3Dot(d, d); + b3Vec3 d = Q - P; + scalar dd = b3Dot(d, d); if (dd > radius * radius) { return; } - b3Vec3 n(0.0f, 1.0f, 0.0f); - float32 len = b3Length(d); + b3Vec3 n(scalar(0), scalar(1), scalar(0)); + scalar len = b3Length(d); if (len > B3_EPSILON) { n = d / len; } manifold.pointCount = 1; - manifold.points[0].localNormal1 = b3MulT(xf1.rotation, n); - manifold.points[0].localPoint1 = s1->m_center; - manifold.points[0].localPoint2 = s2->m_centers[0]; + manifold.points[0].localNormal1 = n; + manifold.points[0].localPoint1 = P; + manifold.points[0].localPoint2 = s2->m_center; manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; manifold.points[0].key.key1 = 0; manifold.points[0].key.key2 = 0; @@ -66,28 +67,28 @@ void b3CollideSphereAndCapsule(b3Manifold& manifold, return; } - if (u <= 0.0f) + if (u <= scalar(0)) { // B b3Vec3 P = B; - b3Vec3 d = P - Q; - float32 dd = b3Dot(d, d); + b3Vec3 d = Q - P; + scalar dd = b3Dot(d, d); if (dd > radius * radius) { return; } - b3Vec3 n(0.0f, 1.0f, 0.0f); - float32 len = b3Length(d); + b3Vec3 n(scalar(0), scalar(1), scalar(0)); + scalar len = b3Length(d); if (len > B3_EPSILON) { n = d / len; } manifold.pointCount = 1; - manifold.points[0].localNormal1 = b3MulT(xf1.rotation, n); - manifold.points[0].localPoint1 = s1->m_center; - manifold.points[0].localPoint2 = s2->m_centers[1]; + manifold.points[0].localNormal1 = n; + manifold.points[0].localPoint1 = P; + manifold.points[0].localPoint2 = s2->m_center; manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; manifold.points[0].key.key1 = 0; manifold.points[0].key.key2 = 0; @@ -96,38 +97,30 @@ void b3CollideSphereAndCapsule(b3Manifold& manifold, } // AB - float32 s = b3Dot(AB, AB); - //B3_ASSERT(s > 0.0f); - b3Vec3 P; - if (s < B3_LINEAR_SLOP * B3_LINEAR_SLOP) - { - P = A; - } - else - { - P = (u * A + v * B) / s; - } - - b3Vec3 d = P - Q; - float32 dd = b3Dot(d, d); + scalar s = b3Dot(AB, AB); + B3_ASSERT(s > scalar(0)); + b3Vec3 P = (u * A + v * B) / s; + + b3Vec3 d = Q - P; + scalar dd = b3Dot(d, d); if (dd > radius * radius) { return; } - b3Vec3 QA = A - Q; - b3Vec3 e = b3Cross(AB, QA); - b3Vec3 n = b3Cross(AB, e); - if (b3Dot(n, QA) < 0.0f) + b3Vec3 AQ = Q - A; + b3Vec3 AB_x_AQ = b3Cross(AB, AQ); + b3Vec3 n = b3Cross(AB_x_AQ, AB); + if (b3Dot(n, AQ) < scalar(0)) { n = -n; } n.Normalize(); manifold.pointCount = 1; - manifold.points[0].localNormal1 = b3MulT(xf1.rotation, n); - manifold.points[0].localPoint1 = s1->m_center; - manifold.points[0].localPoint2 = b3MulT(xf2, P); + manifold.points[0].localNormal1 = n; + manifold.points[0].localPoint1 = P; + manifold.points[0].localPoint2 = s2->m_center; manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; manifold.points[0].key.key1 = 0; manifold.points[0].key.key2 = 0; diff --git a/src/bounce/dynamics/contacts/collide/collide_capsules.cpp b/src/bounce/dynamics/contacts/collide/collide_capsules.cpp index 51d70c2..7ee3bf8 100644 --- a/src/bounce/dynamics/contacts/collide/collide_capsules.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_capsules.cpp @@ -26,31 +26,31 @@ // Compute the closest point on a segment to a point. static b3Vec3 b3ClosestPointOnSegment(const b3Vec3& Q, const b3Capsule& hull) { - b3Vec3 A = hull.vertices[0]; - b3Vec3 B = hull.vertices[1]; + b3Vec3 A = hull.vertex1; + b3Vec3 B = hull.vertex2; b3Vec3 AB = B - A; // Barycentric coordinates for Q - float32 u = b3Dot(B - Q, AB); - float32 v = b3Dot(Q - A, AB); + scalar u = b3Dot(B - Q, AB); + scalar v = b3Dot(Q - A, AB); - if (v <= 0.0f) + if (v <= scalar(0)) { return A; } - if (u <= 0.0f) + if (u <= scalar(0)) { return B; } - float32 w = b3Dot(AB, AB); + scalar w = b3Dot(AB, AB); if (w <= B3_LINEAR_SLOP * B3_LINEAR_SLOP) { return A; } - float32 den = 1.0f / w; + scalar den = scalar(1) / w; b3Vec3 P = den * (u * A + v * B); return P; } @@ -59,17 +59,17 @@ static b3Vec3 b3ClosestPointOnSegment(const b3Vec3& Q, const b3Capsule& hull) static void b3ClosestPoints(b3Vec3& C1, b3Vec3& C2, const b3Capsule& hull1, const b3Capsule& hull2) { - b3Vec3 P1 = hull1.vertices[0]; - b3Vec3 Q1 = hull1.vertices[1]; + b3Vec3 P1 = hull1.vertex1; + b3Vec3 Q1 = hull1.vertex2; - b3Vec3 P2 = hull2.vertices[0]; - b3Vec3 Q2 = hull2.vertices[1]; + b3Vec3 P2 = hull2.vertex1; + b3Vec3 Q2 = hull2.vertex2; b3Vec3 E1 = Q1 - P1; - float32 L1 = b3Length(E1); + scalar L1 = b3Length(E1); b3Vec3 E2 = Q2 - P2; - float32 L2 = b3Length(E2); + scalar L2 = b3Length(E2); if (L1 < B3_LINEAR_SLOP && L2 < B3_LINEAR_SLOP) { @@ -92,54 +92,35 @@ static void b3ClosestPoints(b3Vec3& C1, b3Vec3& C2, return; } - B3_ASSERT(L1 > 0.0f); + B3_ASSERT(L1 > scalar(0)); b3Vec3 N1 = E1 / L1; - B3_ASSERT(L2 > 0.0f); + B3_ASSERT(L2 > scalar(0)); b3Vec3 N2 = E2 / L2; - // [1 -dot(n1, n2)][x1] = [-dot(n1, P1 - P2)] - // [dot(n2, n1) -1][x2] = [-dot(n2, P1 - P2)] - // Ax = b - float32 a11 = 1.0f, a12 = -b3Dot(N1, N2); - float32 a21 = -a12, a22 = -1.0f; + // Solve Ax = b + // [1 -dot(n1, n2)][x1] = [-dot(n1, p1 - p2)] + // [dot(n2, n1) -1][x2] = [-dot(n2, p1 - p2)] + scalar b = b3Dot(N1, N2); + scalar den = scalar(1) - b * b; - b3Mat22 A; - A.x.Set(a11, a21); - A.y.Set(a12, a22); - - float32 norm_A = b3Max(b3Abs(A.x.x) + b3Abs(A.x.y), b3Abs(A.y.x) + b3Abs(A.y.y)); - - float32 det_A = 1.0f / (a11 * a22 - a12 * a21); - - b3Mat22 inv_A; - inv_A.x.Set(det_A * a22, -det_A * a21); - inv_A.y.Set(-det_A * a12, det_A * a11); - - float32 norm_inv_A = b3Max(b3Abs(inv_A.x.x) + b3Abs(inv_A.x.y), b3Abs(inv_A.y.x) + b3Abs(inv_A.y.y)); - - float32 k_A = norm_A * norm_inv_A; - - const float32 kMaxConditionNumber = 1000.0f; - - // Ensure a reasonable condition number. - if (b3IsInf(k_A) == false && k_A < kMaxConditionNumber) + if (den != scalar(0)) { - // A is safe to invert. + scalar inv_den = scalar(1) / den; + b3Vec3 E3 = P1 - P2; - b3Vec2 b; - b.x = -b3Dot(N1, E3); - b.y = -b3Dot(N2, E3); + scalar d = b3Dot(N1, E3); + scalar e = b3Dot(N2, E3); - b3Vec2 x = inv_A * b; + scalar s = inv_den * (b * e - d); + scalar t = inv_den * (e - b * d); - C1 = P1 + x.x * N1; - C2 = P2 + x.y * N2; + C1 = P1 + s * N1; + C2 = P2 + t * N2; } else { - // The lines are intersecting. C1 = P1; C2 = P2; } @@ -153,22 +134,22 @@ static void b3ClosestPoints(b3Vec3& C1, b3Vec3& C2, static bool b3AreParalell(const b3Capsule& hull1, const b3Capsule& hull2) { - b3Vec3 E1 = hull1.vertices[1] - hull1.vertices[0]; - float32 L1 = b3Length(E1); + b3Vec3 E1 = hull1.vertex2 - hull1.vertex1; + scalar L1 = b3Length(E1); if (L1 < B3_LINEAR_SLOP) { return false; } - b3Vec3 E2 = hull2.vertices[1] - hull2.vertices[0]; - float32 L2 = b3Length(E2); + b3Vec3 E2 = hull2.vertex2 - hull2.vertex1; + scalar L2 = b3Length(E2); if (L2 < B3_LINEAR_SLOP) { return false; } // |e1 x e2| = sin(theta) * |e1| * |e2| - const float32 kTol = 0.005f; + const scalar kTol = scalar(0.005f); b3Vec3 N = b3Cross(E1, E2); return b3Length(N) < kTol * L1 * L2; } @@ -178,27 +159,27 @@ void b3CollideCapsuleAndCapsule(b3Manifold& manifold, const b3Transform& xf2, const b3CapsuleShape* s2) { b3Capsule hull1; - hull1.vertices[0] = xf1 * s1->m_centers[0]; - hull1.vertices[1] = xf1 * s1->m_centers[1]; + hull1.vertex1 = xf1 * s1->m_vertex1; + hull1.vertex2 = xf1 * s1->m_vertex2; b3Capsule hull2; - hull2.vertices[0] = xf2 * s2->m_centers[0]; - hull2.vertices[1] = xf2 * s2->m_centers[1]; + hull2.vertex1 = xf2 * s2->m_vertex1; + hull2.vertex2 = xf2 * s2->m_vertex2; b3Vec3 point1, point2; b3ClosestPoints(point1, point2, hull1, hull2); - float32 distance = b3Distance(point1, point2); + scalar distance = b3Distance(point1, point2); - float32 r1 = s1->m_radius; - float32 r2 = s2->m_radius; - float32 totalRadius = r1 + r2; + scalar r1 = s1->m_radius; + scalar r2 = s2->m_radius; + scalar totalRadius = r1 + r2; if (distance > totalRadius) { return; } - if (distance > B3_EPSILON) + if (distance > scalar(0)) { if (b3AreParalell(hull1, hull2)) { @@ -214,28 +195,28 @@ void b3CollideCapsuleAndCapsule(b3Manifold& manifold, b3Vec3 cp1 = b3ClosestPointOnSegment(clipEdge1[0].position, hull2); b3Vec3 cp2 = b3ClosestPointOnSegment(clipEdge1[1].position, hull2); - float32 d1 = b3Distance(clipEdge1[0].position, cp1); - float32 d2 = b3Distance(clipEdge1[1].position, cp2); + scalar d1 = b3Distance(clipEdge1[0].position, cp1); + scalar d2 = b3Distance(clipEdge1[1].position, cp2); if (d1 > B3_EPSILON && d1 <= totalRadius && d2 > B3_EPSILON && d2 <= totalRadius) { b3Vec3 n1 = (cp1 - clipEdge1[0].position) / d1; b3Vec3 n2 = (cp2 - clipEdge1[1].position) / d2; - b3Vec3 p1 = 0.5f * (clipEdge1[0].position + r1 * n1 + cp1 - r2 * n1); - b3Vec3 p2 = 0.5f * (clipEdge1[1].position + r1 * n2 + cp2 - r2 * n2); + b3Vec3 p1 = scalar(0.5) * (clipEdge1[0].position + r1 * n1 + cp1 - r2 * n1); + b3Vec3 p2 = scalar(0.5) * (clipEdge1[1].position + r1 * n2 + cp2 - r2 * n2); - b3Vec3 center = 0.5f * (p1 + p2); + b3Vec3 center = scalar(0.5) * (p1 + p2); b3Vec3 normal = b3Normalize(n1 + n2); manifold.pointCount = 2; - manifold.points[0].localNormal1 = b3MulT(xf1.rotation, n1); + manifold.points[0].localNormal1 = b3MulC(xf1.rotation, n1); manifold.points[0].localPoint1 = b3MulT(xf1, clipEdge1[0].position); manifold.points[0].localPoint2 = b3MulT(xf2, cp1); manifold.points[0].key = b3MakeKey(clipEdge1[0].pair); - manifold.points[1].localNormal1 = b3MulT(xf1.rotation, n2); + manifold.points[1].localNormal1 = b3MulC(xf1.rotation, n2); manifold.points[1].localPoint1 = b3MulT(xf1, clipEdge1[1].position); manifold.points[1].localPoint2 = b3MulT(xf2, cp2); manifold.points[1].key = b3MakeKey(clipEdge1[1].pair); @@ -248,7 +229,7 @@ void b3CollideCapsuleAndCapsule(b3Manifold& manifold, b3Vec3 normal = (point2 - point1) / distance; manifold.pointCount = 1; - manifold.points[0].localNormal1 = b3MulT(xf1.rotation, normal); + manifold.points[0].localNormal1 = b3MulC(xf1.rotation, normal); manifold.points[0].localPoint1 = b3MulT(xf1, point1); manifold.points[0].localPoint2 = b3MulT(xf2, point2); manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; diff --git a/src/bounce/dynamics/contacts/collide/collide_hull_capsule.cpp b/src/bounce/dynamics/contacts/collide/collide_hull_capsule.cpp new file mode 100644 index 0000000..ae5ad80 --- /dev/null +++ b/src/bounce/dynamics/contacts/collide/collide_hull_capsule.cpp @@ -0,0 +1,210 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include +#include + +static void b3BuildEdgeContact(b3Manifold& manifold, + const b3Transform& xf1, const b3Hull* hull1, u32 index1, + const b3Transform& xf2, const b3Capsule* hull2) +{ + const b3HalfEdge* edge1 = hull1->GetEdge(index1); + const b3HalfEdge* twin1 = hull1->GetEdge(index1 + 1); + + b3Vec3 C1 = xf1 * hull1->centroid; + b3Vec3 P1 = xf1 * hull1->GetVertex(edge1->origin); + b3Vec3 Q1 = xf1 * hull1->GetVertex(twin1->origin); + b3Vec3 E1 = Q1 - P1; + b3Vec3 N1 = E1; + scalar L1 = N1.Normalize(); + B3_ASSERT(L1 > scalar(0)); + + b3Vec3 P2 = xf2 * hull2->vertex1; + b3Vec3 Q2 = xf2 * hull2->vertex2; + b3Vec3 E2 = Q2 - P2; + b3Vec3 N2 = E2; + scalar L2 = N2.Normalize(); + B3_ASSERT(L2 > scalar(0)); + + // Compute the closest points on the two lines. + scalar b = b3Dot(N1, N2); + scalar den = scalar(1) - b * b; + if (den == scalar(0)) + { + return; + } + + scalar inv_den = scalar(1) / den; + + b3Vec3 E3 = P1 - P2; + + scalar d = b3Dot(N1, E3); + scalar e = b3Dot(N2, E3); + + scalar s = inv_den * (b * e - d); + scalar t = inv_den * (e - b * d); + + b3Vec3 c1 = P1 + s * N1; + b3Vec3 c2 = P2 + t * N2; + + // Ensure normal orientation to capsule. + b3Vec3 N = b3Cross(E1, E2); + scalar LN = N.Normalize(); + B3_ASSERT(LN > scalar(0)); + if (b3Dot(N, P1 - C1) < scalar(0)) + { + N = -N; + } + + b3FeaturePair pair = b3MakePair(index1, index1 + 1, 0, 1); + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = b3MulC(xf1.rotation, N); + manifold.points[0].localPoint1 = b3MulT(xf1, c1); + manifold.points[0].localPoint2 = b3MulT(xf2, c2); + manifold.points[0].key = b3MakeKey(pair); +} + +static void b3BuildFaceContact(b3Manifold& manifold, + const b3Transform& xf1, const b3Hull* hull1, u32 index1, scalar r1, + const b3Transform& xf2, const b3Capsule* hull2, scalar r2) +{ + b3Capsule worldHull2(xf2 * hull2->vertex1, xf2 * hull2->vertex2, r2); + b3ClipVertex edge2[2]; + b3BuildEdge(edge2, &worldHull2); + + // Clip the capsule segment against the side planes of the reference face. + b3ClipVertex clipEdge2[2]; + u32 clipCount = b3ClipEdgeToFace(clipEdge2, edge2, xf1, r1, index1, hull1); + + // Project clipped segment on the reference face. + b3Plane localPlane1 = hull1->GetPlane(index1); + b3Plane plane1 = xf1 * localPlane1; + + scalar totalRadius = r1 + r2; + + u32 pointCount = 0; + for (u32 i = 0; i < clipCount; ++i) + { + b3Vec3 c2 = clipEdge2[i].position; + scalar s = b3Distance(c2, plane1); + if (s <= totalRadius) + { + b3Vec3 c1 = b3ClosestPointOnPlane(c2, plane1); + + b3ManifoldPoint* mp = manifold.points + pointCount; + mp->localNormal1 = localPlane1.normal; + mp->localPoint1 = b3MulT(xf1, c1); + mp->localPoint2 = b3MulT(xf2, c2); + mp->key = b3MakeKey(clipEdge2[i].pair); + + ++pointCount; + } + } + + manifold.pointCount = pointCount; +} + +void b3CollideHullAndCapsule(b3Manifold& manifold, + const b3Transform& xf1, const b3HullShape* s1, + const b3Transform& xf2, const b3CapsuleShape* s2) +{ + scalar r1 = s1->m_radius, r2 = s2->m_radius; + + scalar totalRadius = r1 + r2; + + b3ShapeGJKProxy proxy1(s1, 0); + b3ShapeGJKProxy proxy2(s2, 0); + + b3GJKOutput gjk = b3GJK(xf1, proxy1, xf2, proxy2, false); + + if (gjk.distance > totalRadius) + { + return; + } + + const b3Hull* hull1 = s1->m_hull; + const b3Capsule hull2(s2->m_vertex1, s2->m_vertex2, r2); + + if (gjk.distance > scalar(0)) + { + b3Vec3 c1 = gjk.point1; + b3Vec3 c2 = gjk.point2; + scalar d = gjk.distance; + + // Define reference normal. + b3Vec3 N1 = (c2 - c1) / d; + + // Search reference face. + b3Vec3 localN1 = b3MulC(xf1.rotation, N1); + u32 index1 = hull1->GetSupportFace(localN1); + b3Vec3 localFaceN1 = hull1->GetPlane(index1).normal; + + // Paralell vectors |v1xv2| = sin(theta) + const scalar kTol = scalar(0.005); + b3Vec3 N = b3Cross(localN1, localFaceN1); + scalar L = b3Dot(N, N); + if (L < kTol * kTol) + { + // Reference face found. + // Try to build a face contact. + b3BuildFaceContact(manifold, xf1, hull1, index1, r1, xf2, &hull2, r2); + if (manifold.pointCount == 2) + { + return; + } + } + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = localN1; + manifold.points[0].localPoint1 = b3MulT(xf1, c1); + manifold.points[0].localPoint2 = b3MulT(xf2, c2); + manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; + manifold.points[0].key.key1 = 0; + manifold.points[0].key.key2 = 0; + + return; + } + + b3FaceQuery faceQuery1 = b3QueryFaceSeparation(xf1, hull1, xf2, &hull2); + if (faceQuery1.separation > totalRadius) + { + return; + } + + b3EdgeQuery edgeQuery = b3QueryEdgeSeparation(xf1, hull1, xf2, &hull2); + if (edgeQuery.separation > totalRadius) + { + return; + } + + const scalar kTol = scalar(0.1) * B3_LINEAR_SLOP; + if (edgeQuery.separation > faceQuery1.separation + kTol) + { + b3BuildEdgeContact(manifold, xf1, hull1, edgeQuery.index1, xf2, &hull2); + } + else + { + b3BuildFaceContact(manifold, xf1, hull1, faceQuery1.index, r1, xf2, &hull2, r2); + } +} \ No newline at end of file diff --git a/src/bounce/dynamics/contacts/collide/collide_hull_sphere.cpp b/src/bounce/dynamics/contacts/collide/collide_hull_sphere.cpp new file mode 100644 index 0000000..2ba9080 --- /dev/null +++ b/src/bounce/dynamics/contacts/collide/collide_hull_sphere.cpp @@ -0,0 +1,120 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include + +void b3CollideHullAndSphere(b3Manifold& manifold, + const b3Transform& xf1, const b3HullShape* s1, + const b3Transform& xf2, const b3SphereShape* s2) +{ + scalar radius = s1->m_radius + s2->m_radius; + const b3Hull* hull1 = s1->m_hull; + + // Sphere center in the frame of the hull. + b3Vec3 cLocal = b3MulT(xf1, b3Mul(xf2, s2->m_center)); + + // Find the minimum separation face. + u32 faceIndex = 0; + scalar separation = -B3_MAX_SCALAR; + + for (u32 i = 0; i < hull1->faceCount; ++i) + { + b3Plane plane = hull1->GetPlane(i); + scalar s = b3Distance(cLocal, plane); + + if (s > radius) + { + // Early out. + return; + } + + if (s > separation) + { + faceIndex = i; + separation = s; + } + } + + if (separation < scalar(0)) + { + // The center is inside the hull. + b3Plane localPlane1 = hull1->planes[faceIndex]; + + b3Vec3 c1 = b3ClosestPointOnPlane(cLocal, localPlane1); + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = localPlane1.normal; + manifold.points[0].localPoint1 = c1; + manifold.points[0].localPoint2 = s2->m_center; + manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; + manifold.points[0].key.key1 = 0; + manifold.points[0].key.key2 = 0; + return; + } + + // Reference face polygon. + b3StackArray referencePolygon; + + const b3Face* face = hull1->GetFace(faceIndex); + const b3HalfEdge* begin = hull1->GetEdge(face->edge); + const b3HalfEdge* edge = begin; + do + { + b3Vec3 vertex = hull1->GetVertex(edge->origin); + referencePolygon.PushBack(vertex); + edge = hull1->GetEdge(edge->next); + } while (edge != begin); + + b3GJKProxy proxy1; + proxy1.vertexCount = referencePolygon.Count(); + proxy1.vertices = referencePolygon.Begin(); + + b3GJKProxy proxy2; + proxy2.vertexBuffer[0] = s2->m_center; + proxy2.vertexCount = 1; + proxy2.vertices = proxy2.vertexBuffer; + + b3GJKOutput query = b3GJK(xf1, proxy1, xf2, proxy2, false); + + if (query.distance > radius) + { + return; + } + + if (query.distance > scalar(0)) + { + b3Vec3 c1 = query.point1; + b3Vec3 c2 = query.point2; + scalar d = query.distance; + + b3Vec3 normal = (c2 - c1) / d; + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = b3MulC(xf1.rotation, normal); + manifold.points[0].localPoint1 = b3MulT(xf1, c1); + manifold.points[0].localPoint2 = s2->m_center; + manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; + manifold.points[0].key.key1 = 0; + manifold.points[0].key.key2 = 0; + } +} \ No newline at end of file diff --git a/src/bounce/dynamics/contacts/collide/collide_hulls.cpp b/src/bounce/dynamics/contacts/collide/collide_hulls.cpp index 63ae512..8739f8c 100644 --- a/src/bounce/dynamics/contacts/collide/collide_hulls.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_hulls.cpp @@ -21,9 +21,13 @@ #include #include #include +#include #include -void b3BuildEdgeContact(b3Manifold& manifold, +bool b3_convexCache = true; +u32 b3_convexCalls = 0, b3_convexCacheHits = 0; + +static void b3BuildEdgeContact(b3Manifold& manifold, const b3Transform& xf1, u32 index1, const b3HullShape* s1, const b3Transform& xf2, u32 index2, const b3HullShape* s2) { @@ -36,7 +40,7 @@ void b3BuildEdgeContact(b3Manifold& manifold, b3Vec3 Q1 = xf1 * hull1->GetVertex(twin1->origin); b3Vec3 E1 = Q1 - P1; b3Vec3 N1 = E1; - float32 L1 = N1.Normalize(); + scalar L1 = N1.Normalize(); B3_ASSERT(L1 > B3_LINEAR_SLOP); const b3Hull* hull2 = s2->m_hull; @@ -48,35 +52,35 @@ void b3BuildEdgeContact(b3Manifold& manifold, b3Vec3 Q2 = xf2 * hull2->GetVertex(twin2->origin); b3Vec3 E2 = Q2 - P2; b3Vec3 N2 = E2; - float32 L2 = N2.Normalize(); + scalar L2 = N2.Normalize(); B3_ASSERT(L2 > B3_LINEAR_SLOP); // Compute the closest points on the two lines. - float32 b = b3Dot(N1, N2); - float32 den = 1.0f - b * b; - if (den == 0.0f) + scalar b = b3Dot(N1, N2); + scalar den = scalar(1) - b * b; + if (den == scalar(0)) { return; } - float32 inv_den = 1.0f / den; + scalar inv_den = scalar(1) / den; b3Vec3 E3 = P1 - P2; - float32 d = b3Dot(N1, E3); - float32 e = b3Dot(N2, E3); + scalar d = b3Dot(N1, E3); + scalar e = b3Dot(N2, E3); - float32 s = inv_den * (b * e - d); - float32 t = inv_den * (e - b * d); + scalar s = inv_den * (b * e - d); + scalar t = inv_den * (e - b * d); b3Vec3 c1 = P1 + s * N1; b3Vec3 c2 = P2 + t * N2; // Ensure normal orientation to hull 2. b3Vec3 N = b3Cross(E1, E2); - float32 LN = N.Normalize(); - B3_ASSERT(LN > 0.0f); - if (b3Dot(N, P1 - C1) < 0.0f) + scalar LN = N.Normalize(); + B3_ASSERT(LN > scalar(0)); + if (b3Dot(N, P1 - C1) < scalar(0)) { N = -N; } @@ -84,24 +88,26 @@ void b3BuildEdgeContact(b3Manifold& manifold, b3FeaturePair pair = b3MakePair(index1, index1 + 1, index2, index2 + 1); manifold.pointCount = 1; - manifold.points[0].localNormal1 = b3MulT(xf1.rotation, N); + manifold.points[0].localNormal1 = b3MulC(xf1.rotation, N); manifold.points[0].localPoint1 = b3MulT(xf1, c1); manifold.points[0].localPoint2 = b3MulT(xf2, c2); manifold.points[0].key = b3MakeKey(pair); + manifold.points[0].featurePair = pair; + manifold.points[0].edgeContact = true; } -void b3BuildFaceContact(b3Manifold& manifold, +static void b3BuildFaceContact(b3Manifold& manifold, const b3Transform& xf1, u32 index1, const b3HullShape* s1, const b3Transform& xf2, const b3HullShape* s2, bool flipNormal) { const b3Hull* hull1 = s1->m_hull; - float32 r1 = s1->m_radius; + scalar r1 = s1->m_radius; const b3Hull* hull2 = s2->m_hull; - float32 r2 = s2->m_radius; + scalar r2 = s2->m_radius; - float32 totalRadius = r1 + r2; + scalar totalRadius = r1 + r2; // 1. Define the reference face plane (1). const b3Face* face1 = hull1->GetFace(index1); @@ -114,7 +120,7 @@ void b3BuildFaceContact(b3Manifold& manifold, // 2. Find the incident face polygon (2). // Put the reference plane normal in the frame of the incident hull (2). - b3Vec3 normal1 = b3MulT(xf2.rotation, plane1.normal); + b3Vec3 normal1 = b3MulC(xf2.rotation, plane1.normal); // Find the support face polygon in the *negated* direction. b3StackArray polygon2; @@ -134,12 +140,12 @@ void b3BuildFaceContact(b3Manifold& manifold, b3StackArray polygon1; u32 minIndex = 0; - float32 minSeparation = B3_MAX_FLOAT; + scalar minSeparation = B3_MAX_SCALAR; for (u32 i = 0; i < clipPolygon2.Count(); ++i) { b3ClipVertex v2 = clipPolygon2[i]; - float32 separation = b3Distance(v2.position, plane1); + scalar separation = b3Distance(v2.position, plane1); if (separation <= totalRadius) { @@ -184,38 +190,42 @@ void b3BuildFaceContact(b3Manifold& manifold, if (flipNormal) { // Swap the feature pairs. - b3FeaturePair pair = b3MakePair(v2.pair.inEdge2, v2.pair.inEdge1, v2.pair.outEdge2, v2.pair.outEdge1); + b3FeaturePair pair = b3MakePair(v2.pair.inEdge2, v2.pair.outEdge2, v2.pair.inEdge1, v2.pair.outEdge1); - mp->localNormal1 = b3MulT(xf2.rotation, s_normal); + mp->localNormal1 = b3MulC(xf2.rotation, s_normal); mp->localPoint1 = b3MulT(xf2, v2.position); mp->localPoint2 = b3MulT(xf1, v1); mp->key = b3MakeKey(pair); + mp->featurePair = pair; + mp->edgeContact = false; } else { - mp->localNormal1 = b3MulT(xf1.rotation, normal); + mp->localNormal1 = b3MulC(xf1.rotation, normal); mp->localPoint1 = b3MulT(xf1, v1); mp->localPoint2 = b3MulT(xf2, v2.position); mp->key = b3MakeKey(v2.pair); + mp->featurePair = v2.pair; + mp->edgeContact = false; } } manifold.pointCount = pointCount; } -void b3CollideHulls(b3Manifold& manifold, +static void b3CollideHulls(b3Manifold& manifold, const b3Transform& xf1, const b3HullShape* s1, const b3Transform& xf2, const b3HullShape* s2) { B3_ASSERT(manifold.pointCount == 0); const b3Hull* hull1 = s1->m_hull; - float32 r1 = s1->m_radius; + scalar r1 = s1->m_radius; const b3Hull* hull2 = s2->m_hull; - float32 r2 = s2->m_radius; + scalar r2 = s2->m_radius; - float32 totalRadius = r1 + r2; + scalar totalRadius = r1 + r2; b3FaceQuery faceQuery1 = b3QueryFaceSeparation(xf1, hull1, xf2, hull2); if (faceQuery1.separation > totalRadius) @@ -235,7 +245,7 @@ void b3CollideHulls(b3Manifold& manifold, return; } - const float32 kTol = 0.1f * B3_LINEAR_SLOP; + const scalar kTol = scalar(0.1) * B3_LINEAR_SLOP; if (edgeQuery.separation > b3Max(faceQuery1.separation, faceQuery2.separation) + kTol) { b3BuildEdgeContact(manifold, xf1, edgeQuery.index1, s1, xf2, edgeQuery.index2, s2); @@ -277,24 +287,311 @@ void b3CollideHulls(b3Manifold& manifold, // When both convex hulls are not simplified clipping might fail and create no contact points. // For example, when a hull contains tiny faces, coplanar faces, and/or non-sharped edges. - // So we simply create a contact point between the segments. - // The hulls might overlap, but is better than solving no contact points. - if (manifold.pointCount == 0) - { - b3BuildEdgeContact(manifold, xf1, edgeQuery.index1, s1, xf2, edgeQuery.index2, s2); - } - - // If the shapes are overlapping then at least on point must be created. - B3_ASSERT(manifold.pointCount > 0); + // So we need to use an heuristic to create a correct contact point. } -bool b3_convexCache = true; -u32 b3_convexCalls = 0, b3_convexCacheHits = 0; +/////////////////////////////////////////////////////////////////////////////////////////////////// -void b3CollideHulls(b3Manifold& manifold, +static void b3RebuildEdgeContact(b3Manifold& manifold, + const b3Transform& xf1, u32 index1, const b3HullShape* s1, + const b3Transform& xf2, u32 index2, const b3HullShape* s2) +{ + const b3Hull* hull1 = s1->m_hull; + const b3HalfEdge* edge1 = hull1->GetEdge(index1); + const b3HalfEdge* twin1 = hull1->GetEdge(index1 + 1); + + b3Vec3 C1 = xf1 * hull1->centroid; + b3Vec3 P1 = xf1 * hull1->GetVertex(edge1->origin); + b3Vec3 Q1 = xf1 * hull1->GetVertex(twin1->origin); + b3Vec3 E1 = Q1 - P1; + b3Vec3 N1 = E1; + scalar L1 = N1.Normalize(); + B3_ASSERT(L1 > scalar(0)); + + const b3Hull* hull2 = s2->m_hull; + const b3HalfEdge* edge2 = hull2->GetEdge(index2); + const b3HalfEdge* twin2 = hull2->GetEdge(index2 + 1); + + b3Vec3 C2 = xf2 * hull2->centroid; + b3Vec3 P2 = xf2 * hull2->GetVertex(edge2->origin); + b3Vec3 Q2 = xf2 * hull2->GetVertex(twin2->origin); + b3Vec3 E2 = Q2 - P2; + b3Vec3 N2 = E2; + scalar L2 = N2.Normalize(); + B3_ASSERT(L2 > scalar(0)); + + // Compute the closest points on the two lines. + scalar b = b3Dot(N1, N2); + scalar den = scalar(1) - b * b; + if (den == scalar(0)) + { + return; + } + + scalar inv_den = scalar(1) / den; + + b3Vec3 E3 = P1 - P2; + + scalar d = b3Dot(N1, E3); + scalar e = b3Dot(N2, E3); + + scalar s = inv_den * (b * e - d); + scalar t = inv_den * (e - b * d); + + b3Vec3 c1 = P1 + s * N1; + b3Vec3 c2 = P2 + t * N2; + + // Check if the closest points are still lying on the opposite segments + // using Barycentric coordinates. + scalar w2[3]; + b3BarycentricCoordinates(w2, P1, Q1, c2); + + scalar w1[3]; + b3BarycentricCoordinates(w1, P2, Q2, c1); + + if (w2[1] > scalar(0) && w2[1] <= w2[2] && + w1[1] > scalar(0) && w1[1] <= w1[2]) + { + b3Vec3 N = b3Cross(E1, E2); + scalar LN = N.Normalize(); + B3_ASSERT(LN > scalar(0)); + if (b3Dot(N, P1 - C1) < scalar(0)) + { + N = -N; + } + + b3FeaturePair pair = b3MakePair(index1, index1 + 1, index2, index2 + 1); + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = b3MulC(xf1.rotation, N); + manifold.points[0].localPoint1 = b3MulT(xf1, c1); + manifold.points[0].localPoint2 = b3MulT(xf2, c2); + manifold.points[0].key = b3MakeKey(pair); + manifold.points[0].featurePair = pair; + manifold.points[0].edgeContact = true; + } +} + +static void b3RebuildFaceContact(b3Manifold& manifold, + const b3Transform& xf1, u32 index1, const b3HullShape* s1, + const b3Transform& xf2, const b3HullShape* s2, bool flipNormal) +{ + const b3Body* body1 = s1->GetBody(); + const b3Body* body2 = s2->GetBody(); + + const b3Sweep& sweep1 = body1->GetSweep(); + b3Quat q10 = sweep1.orientation0; + b3Quat q1 = sweep1.orientation; + + const b3Sweep& sweep2 = body2->GetSweep(); + b3Quat q20 = sweep2.orientation0; + b3Quat q2 = sweep2.orientation; + + // Check if the relative orientation has changed. + // Here the second orientation seen by the first orientation. + // dp = p2 - p1 + // dq * q1 = q2 + // dq = inv(q1) * q2 + + // The old relative rotation. + // "q0(2) - q0(1)" + b3Quat dq0 = b3Conjugate(q10) * q20; + + // The new relative rotation. + // "q(2) - q(1)" + b3Quat dq = b3Conjugate(q1) * q2; + + // Relative rotation between the new relative rotation and the old relative rotation. + // "dq(2) - dq0(1)" + b3Quat q = b3Conjugate(dq0) * dq; + + // Check the relative absolute cosine because + // we want to check orientation similarity. + const scalar kCosTol = scalar(0.995); + + if (b3Abs(q.s) > kCosTol) + { + b3BuildFaceContact(manifold, xf1, index1, s1, xf2, s2, flipNormal); + } +} + +static void b3CollideCache(b3Manifold& manifold, const b3Transform& xf1, const b3HullShape* s1, const b3Transform& xf2, const b3HullShape* s2, - b3FeatureCache* cache); + b3FeatureCache* cache) +{ + B3_ASSERT(cache->m_featurePair.state == b3SATCacheType::e_empty); + + const b3Hull* hull1 = s1->m_hull; + scalar r1 = s1->m_radius; + + const b3Hull* hull2 = s2->m_hull; + scalar r2 = s2->m_radius; + + scalar totalRadius = r1 + r2; + + b3FaceQuery faceQuery1 = b3QueryFaceSeparation(xf1, hull1, xf2, hull2); + if (faceQuery1.separation > totalRadius) + { + // Write a separation cache. + cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_separation, b3SATFeatureType::e_face1, faceQuery1.index, faceQuery1.index); + return; + } + + b3FaceQuery faceQuery2 = b3QueryFaceSeparation(xf2, hull2, xf1, hull1); + if (faceQuery2.separation > totalRadius) + { + // Write a separation cache. + cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_separation, b3SATFeatureType::e_face2, faceQuery2.index, faceQuery2.index); + return; + } + + b3EdgeQuery edgeQuery = b3QueryEdgeSeparation(xf1, hull1, xf2, hull2); + if (edgeQuery.separation > totalRadius) + { + // Write a separation cache. + cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_separation, b3SATFeatureType::e_edge1, edgeQuery.index1, edgeQuery.index2); + return; + } + + const scalar kTol = scalar(0.1) * B3_LINEAR_SLOP; + if (edgeQuery.separation > b3Max(faceQuery1.separation, faceQuery2.separation) + kTol) + { + b3BuildEdgeContact(manifold, xf1, edgeQuery.index1, s1, xf2, edgeQuery.index2, s2); + + // Write an overlap cache. + cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_edge1, edgeQuery.index1, edgeQuery.index2); + return; + } + else + { + if (faceQuery1.separation + kTol > faceQuery2.separation) + { + b3BuildFaceContact(manifold, xf1, faceQuery1.index, s1, xf2, s2, false); + if (manifold.pointCount > 0) + { + // Write an overlap cache. + cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_face1, faceQuery1.index, faceQuery1.index); + return; + } + } + else + { + b3BuildFaceContact(manifold, xf2, faceQuery2.index, s2, xf1, s1, true); + if (manifold.pointCount > 0) + { + // Write an overlap cache. + cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_face2, faceQuery2.index, faceQuery2.index); + return; + } + } + } + + // Heuristic failed. Fallback. + if (edgeQuery.separation > b3Max(faceQuery1.separation, faceQuery2.separation)) + { + b3BuildEdgeContact(manifold, xf1, edgeQuery.index1, s1, xf2, edgeQuery.index2, s2); + // Write an overlap cache. + cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_edge1, edgeQuery.index1, edgeQuery.index2); + return; + } + else + { + if (faceQuery1.separation > faceQuery2.separation) + { + b3BuildFaceContact(manifold, xf1, faceQuery1.index, s1, xf2, s2, false); + if (manifold.pointCount > 0) + { + // Write an overlap cache. + cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_face1, faceQuery1.index, faceQuery1.index); + return; + } + } + else + { + b3BuildFaceContact(manifold, xf2, faceQuery2.index, s2, xf1, s1, true); + if (manifold.pointCount > 0) + { + // Write an overlap cache. + cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_face2, faceQuery2.index, faceQuery2.index); + return; + } + } + } + + // When both convex hulls are not simplified clipping might fail and create no contact points. + // For example, when a hull contains tiny faces, coplanar faces, and/or non-sharped edges. + // So we need to use an heuristic to create a correct contact point. +} + +static void b3CollideHulls(b3Manifold& manifold, + const b3Transform& xf1, const b3HullShape* s1, + const b3Transform& xf2, const b3HullShape* s2, + b3FeatureCache* cache) +{ + const b3Hull* hull1 = s1->m_hull; + scalar r1 = s1->m_radius; + + const b3Hull* hull2 = s2->m_hull; + scalar r2 = s2->m_radius; + + scalar totalRadius = r1 + r2; + + // Read cache + b3SATCacheType state0 = cache->m_featurePair.state; + b3SATCacheType state1 = cache->ReadState(xf1, hull1, xf2, hull2, totalRadius); + + if (state0 == b3SATCacheType::e_separation && + state1 == b3SATCacheType::e_separation) + { + // Separation cache hit. + ++b3_convexCacheHits; + return; + } + + if (state0 == b3SATCacheType::e_overlap && + state1 == b3SATCacheType::e_overlap) + { + // Try to rebuild or reclip the features. + switch (cache->m_featurePair.type) + { + case b3SATFeatureType::e_edge1: + { + b3RebuildEdgeContact(manifold, xf1, cache->m_featurePair.index1, s1, xf2, cache->m_featurePair.index2, s2); + break; + } + case b3SATFeatureType::e_face1: + { + b3RebuildFaceContact(manifold, xf1, cache->m_featurePair.index1, s1, xf2, s2, false); + break; + } + case b3SATFeatureType::e_face2: + { + b3RebuildFaceContact(manifold, xf2, cache->m_featurePair.index1, s2, xf1, s1, true); + break; + } + default: + { + break; + } + } + + if (manifold.pointCount > 0) + { + // Overlap cache hit. + ++b3_convexCacheHits; + return; + } + } + + // Separation cache miss. + // Overlap cache miss. + // Flush the cache. + cache->m_featurePair.state = b3SATCacheType::e_empty; + b3CollideCache(manifold, xf1, s1, xf2, s2, cache); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// void b3CollideHullAndHull(b3Manifold& manifold, const b3Transform& xf1, const b3HullShape* s1, diff --git a/src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp b/src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp deleted file mode 100644 index bcdc59e..0000000 --- a/src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#include -#include -#include -#include -#include -#include - -void b3BuildEdgeContact(b3Manifold& manifold, - const b3Transform& xf1, u32 index1, const b3HullShape* s1, - const b3Transform& xf2, u32 index2, const b3HullShape* s2); - -void b3BuildFaceContact(b3Manifold& manifold, - const b3Transform& xf1, u32 index1, const b3HullShape* s1, - const b3Transform& xf2, const b3HullShape* s2, - bool flipNormal); - -static void b3RebuildEdgeContact(b3Manifold& manifold, - const b3Transform& xf1, u32 index1, const b3HullShape* s1, - const b3Transform& xf2, u32 index2, const b3HullShape* s2) -{ - const b3Hull* hull1 = s1->m_hull; - const b3HalfEdge* edge1 = hull1->GetEdge(index1); - const b3HalfEdge* twin1 = hull1->GetEdge(index1 + 1); - - b3Vec3 C1 = xf1 * hull1->centroid; - b3Vec3 P1 = xf1 * hull1->GetVertex(edge1->origin); - b3Vec3 Q1 = xf1 * hull1->GetVertex(twin1->origin); - b3Vec3 E1 = Q1 - P1; - b3Vec3 N1 = E1; - float32 L1 = N1.Normalize(); - B3_ASSERT(L1 > 0.0f); - - const b3Hull* hull2 = s2->m_hull; - const b3HalfEdge* edge2 = hull2->GetEdge(index2); - const b3HalfEdge* twin2 = hull2->GetEdge(index2 + 1); - - b3Vec3 C2 = xf2 * hull2->centroid; - b3Vec3 P2 = xf2 * hull2->GetVertex(edge2->origin); - b3Vec3 Q2 = xf2 * hull2->GetVertex(twin2->origin); - b3Vec3 E2 = Q2 - P2; - b3Vec3 N2 = E2; - float32 L2 = N2.Normalize(); - B3_ASSERT(L2 > 0.0f); - - // Compute the closest points on the two lines. - float32 b = b3Dot(N1, N2); - float32 den = 1.0f - b * b; - if (den == 0.0f) - { - return; - } - - float32 inv_den = 1.0f / den; - - b3Vec3 E3 = P1 - P2; - - float32 d = b3Dot(N1, E3); - float32 e = b3Dot(N2, E3); - - float32 s = inv_den * (b * e - d); - float32 t = inv_den * (e - b * d); - - b3Vec3 c1 = P1 + s * N1; - b3Vec3 c2 = P2 + t * N2; - - // Check if the closest points are still lying on the opposite segments - // using Barycentric coordinates. - float32 w2[3]; - b3BarycentricCoordinates(w2, P1, Q1, c2); - - float32 w1[3]; - b3BarycentricCoordinates(w1, P2, Q2, c1); - - if (w2[1] > 0.0f && w2[1] <= w2[2] && - w1[1] > 0.0f && w1[1] <= w1[2]) - { - b3Vec3 N = b3Cross(E1, E2); - float32 LN = N.Normalize(); - B3_ASSERT(LN > 0.0f); - if (b3Dot(N, P1 - C1) < 0.0f) - { - N = -N; - } - - b3FeaturePair pair = b3MakePair(index1, index1 + 1, index2, index2 + 1); - - manifold.pointCount = 1; - manifold.points[0].localNormal1 = b3MulT(xf1.rotation, N); - manifold.points[0].localPoint1 = b3MulT(xf1, c1); - manifold.points[0].localPoint2 = b3MulT(xf2, c2); - manifold.points[0].key = b3MakeKey(pair); - } -} - -static void b3RebuildFaceContact(b3Manifold& manifold, - const b3Transform& xf1, u32 index1, const b3HullShape* s1, - const b3Transform& xf2, const b3HullShape* s2, bool flipNormal) -{ - const b3Body* body1 = s1->GetBody(); - const b3Body* body2 = s2->GetBody(); - - const b3Sweep& sweep1 = body1->GetSweep(); - b3Quat q10 = sweep1.orientation0; - b3Quat q1 = sweep1.orientation; - - const b3Sweep& sweep2 = body2->GetSweep(); - b3Quat q20 = sweep2.orientation0; - b3Quat q2 = sweep2.orientation; - - // Check if the relative orientation has changed. - // Here the second orientation seen by the first orientation. - // dp = p2 - p1 - // dq * q1 = q2 - // dq = inv(q1) * q2 - - // The old relative rotation. - // "q0(2) - q0(1)" - b3Quat dq0 = b3Conjugate(q10) * q20; - - // The new relative rotation. - // "q(2) - q(1)" - b3Quat dq = b3Conjugate(q1) * q2; - - // Relative rotation between the new relative rotation and the old relative rotation. - // "dq(2) - dq0(1)" - b3Quat q = b3Conjugate(dq0) * dq; - - // Check the relative absolute cosine because - // we want to check orientation similarity. - const float32 kTol = 0.995f; - if (b3Abs(q.w) > kTol) - { - b3BuildFaceContact(manifold, xf1, index1, s1, xf2, s2, flipNormal); - } -} - -void b3CollideCache(b3Manifold& manifold, - const b3Transform& xf1, const b3HullShape* s1, - const b3Transform& xf2, const b3HullShape* s2, - b3FeatureCache* cache) -{ - B3_ASSERT(cache->m_featurePair.state == b3SATCacheType::e_empty); - - const b3Hull* hull1 = s1->m_hull; - float32 r1 = s1->m_radius; - - const b3Hull* hull2 = s2->m_hull; - float32 r2 = s2->m_radius; - - float32 totalRadius = r1 + r2; - - b3FaceQuery faceQuery1 = b3QueryFaceSeparation(xf1, hull1, xf2, hull2); - if (faceQuery1.separation > totalRadius) - { - // Write a separation cache. - cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_separation, b3SATFeatureType::e_face1, faceQuery1.index, faceQuery1.index); - return; - } - - b3FaceQuery faceQuery2 = b3QueryFaceSeparation(xf2, hull2, xf1, hull1); - if (faceQuery2.separation > totalRadius) - { - // Write a separation cache. - cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_separation, b3SATFeatureType::e_face2, faceQuery2.index, faceQuery2.index); - return; - } - - b3EdgeQuery edgeQuery = b3QueryEdgeSeparation(xf1, hull1, xf2, hull2); - if (edgeQuery.separation > totalRadius) - { - // Write a separation cache. - cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_separation, b3SATFeatureType::e_edge1, edgeQuery.index1, edgeQuery.index2); - return; - } - - const float32 kTol = 0.1f * B3_LINEAR_SLOP; - if (edgeQuery.separation > b3Max(faceQuery1.separation, faceQuery2.separation) + kTol) - { - b3BuildEdgeContact(manifold, xf1, edgeQuery.index1, s1, xf2, edgeQuery.index2, s2); - if (manifold.pointCount > 0) - { - // Write an overlap cache. - cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_edge1, edgeQuery.index1, edgeQuery.index2); - return; - } - } - else - { - if (faceQuery1.separation + kTol > faceQuery2.separation) - { - b3BuildFaceContact(manifold, xf1, faceQuery1.index, s1, xf2, s2, false); - if (manifold.pointCount > 0) - { - // Write an overlap cache. - cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_face1, faceQuery1.index, faceQuery1.index); - return; - } - } - else - { - b3BuildFaceContact(manifold, xf2, faceQuery2.index, s2, xf1, s1, true); - if (manifold.pointCount > 0) - { - // Write an overlap cache. - cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_face2, faceQuery2.index, faceQuery2.index); - return; - } - } - } - - // Heuristic failed. Fallback. - if (edgeQuery.separation > b3Max(faceQuery1.separation, faceQuery2.separation)) - { - b3BuildEdgeContact(manifold, xf1, edgeQuery.index1, s1, xf2, edgeQuery.index2, s2); - if (manifold.pointCount > 0) - { - // Write an overlap cache. - cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_edge1, edgeQuery.index1, edgeQuery.index2); - return; - } - } - else - { - if (faceQuery1.separation > faceQuery2.separation) - { - b3BuildFaceContact(manifold, xf1, faceQuery1.index, s1, xf2, s2, false); - if (manifold.pointCount > 0) - { - // Write an overlap cache. - cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_face1, faceQuery1.index, faceQuery1.index); - return; - } - } - else - { - b3BuildFaceContact(manifold, xf2, faceQuery2.index, s2, xf1, s1, true); - if (manifold.pointCount > 0) - { - // Write an overlap cache. - cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_face2, faceQuery2.index, faceQuery2.index); - return; - } - } - } - - // When both convex hulls are not simplified clipping might fail and create no contact points. - // For example, when a hull contains tiny faces, coplanar faces, and/or non-sharped edges. - // So we simply create a contact point between the segments. - // The hulls might overlap, but is better than solving no contact points. - b3BuildEdgeContact(manifold, xf1, edgeQuery.index1, s1, xf2, edgeQuery.index2, s2); - - // If the shapes are overlapping then at least on point must be created. - B3_ASSERT(manifold.pointCount > 0); - - // Write an overlap cache. - cache->m_featurePair = b3MakeFeaturePair(b3SATCacheType::e_overlap, b3SATFeatureType::e_edge1, edgeQuery.index1, edgeQuery.index2); -} - -extern u32 b3_convexCacheHits; - -void b3CollideHulls(b3Manifold& manifold, - const b3Transform& xf1, const b3HullShape* s1, - const b3Transform& xf2, const b3HullShape* s2, - b3FeatureCache* cache) -{ - const b3Hull* hull1 = s1->m_hull; - float32 r1 = s1->m_radius; - - const b3Hull* hull2 = s2->m_hull; - float32 r2 = s2->m_radius; - - float32 totalRadius = r1 + r2; - - // Read cache - b3SATCacheType state0 = cache->m_featurePair.state; - b3SATCacheType state1 = cache->ReadState(xf1, hull1, xf2, hull2, totalRadius); - - if (state0 == b3SATCacheType::e_separation && - state1 == b3SATCacheType::e_separation) - { - // Separation cache hit. - ++b3_convexCacheHits; - return; - } - - if (state0 == b3SATCacheType::e_overlap && - state1 == b3SATCacheType::e_overlap) - { - // Try to rebuild or reclip the features. - switch (cache->m_featurePair.type) - { - case b3SATFeatureType::e_edge1: - { - b3RebuildEdgeContact(manifold, xf1, cache->m_featurePair.index1, s1, xf2, cache->m_featurePair.index2, s2); - break; - } - case b3SATFeatureType::e_face1: - { - b3RebuildFaceContact(manifold, xf1, cache->m_featurePair.index1, s1, xf2, s2, false); - break; - } - case b3SATFeatureType::e_face2: - { - b3RebuildFaceContact(manifold, xf2, cache->m_featurePair.index1, s2, xf1, s1, true); - break; - } - default: - { - break; - } - } - - if (manifold.pointCount > 0) - { - // Overlap cache hit. - ++b3_convexCacheHits; - return; - } - } - - // Separation cache miss. - // Overlap cache miss. - // Flush the cache. - cache->m_featurePair.state = b3SATCacheType::e_empty; - b3CollideCache(manifold, xf1, s1, xf2, s2, cache); -} \ No newline at end of file diff --git a/src/bounce/dynamics/contacts/collide/collide_sphere_hull.cpp b/src/bounce/dynamics/contacts/collide/collide_sphere_hull.cpp deleted file mode 100644 index 15ebb47..0000000 --- a/src/bounce/dynamics/contacts/collide/collide_sphere_hull.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#include -#include -#include -#include -#include -#include - -void b3CollideSphereAndHull(b3Manifold& manifold, - const b3Transform& xf1, const b3SphereShape* s1, - const b3Transform& xf2, const b3HullShape* s2) -{ - b3ShapeGJKProxy proxy1(s1, 0); - b3ShapeGJKProxy proxy2(s2, 0); - - b3GJKOutput gjk = b3GJK(xf1, proxy1, xf2, proxy2); - - float32 r1 = s1->m_radius; - float32 r2 = s2->m_radius; - - float32 totalRadius = r1 + r2; - - if (gjk.distance > totalRadius) - { - return; - } - - if (gjk.distance > 0.0f) - { - b3Vec3 c1 = gjk.point1; - b3Vec3 c2 = gjk.point2; - b3Vec3 normal = (c2 - c1) / gjk.distance; - - manifold.pointCount = 1; - manifold.points[0].localNormal1 = b3MulT(xf1.rotation, normal); - manifold.points[0].localPoint1 = s1->m_center; - manifold.points[0].localPoint2 = b3MulT(xf2, c2); - manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; - manifold.points[0].key.key1 = 0; - manifold.points[0].key.key2 = 0; - - return; - } - - const b3Sphere hull1(s1->m_center, 0.0f); - const b3Hull* hull2 = s2->m_hull; - - b3FaceQuery faceQuery = b3QueryFaceSeparation(xf1, &hull1, xf2, hull2); - if (faceQuery.separation > totalRadius) - { - return; - } - - b3Plane localPlane2 = hull2->planes[faceQuery.index]; - b3Plane plane2 = xf2 * localPlane2; - - b3Vec3 c1 = xf1 * hull1.vertex; - b3Vec3 c2 = b3ClosestPointOnPlane(c1, plane2); - - // Ensure normal orientation to shape 2 - b3Vec3 n1 = -plane2.normal; - - manifold.pointCount = 1; - manifold.points[0].localNormal1 = b3MulT(xf1.rotation, n1); - manifold.points[0].localPoint1 = s1->m_center; - manifold.points[0].localPoint2 = b3MulT(xf2, c2); - manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; - manifold.points[0].key.key1 = 0; - manifold.points[0].key.key2 = 0; -} \ No newline at end of file diff --git a/src/bounce/dynamics/contacts/collide/collide_spheres.cpp b/src/bounce/dynamics/contacts/collide/collide_spheres.cpp index 66d8c4f..9c0a060 100644 --- a/src/bounce/dynamics/contacts/collide/collide_spheres.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_spheres.cpp @@ -26,28 +26,28 @@ void b3CollideSphereAndSphere(b3Manifold& manifold, const b3Transform& xf2, const b3SphereShape* s2) { b3Vec3 c1 = xf1 * s1->m_center; - float32 r1 = s1->m_radius; + scalar r1 = s1->m_radius; b3Vec3 c2 = xf2 * s2->m_center; - float32 r2 = s2->m_radius; + scalar r2 = s2->m_radius; b3Vec3 d = c2 - c1; - float32 dd = b3Dot(d, d); - float32 totalRadius = r1 + r2; + scalar dd = b3Dot(d, d); + scalar totalRadius = r1 + r2; if (dd > totalRadius * totalRadius) { return; } - float32 distance = b3Length(d); - b3Vec3 normal(0.0f, 1.0f, 0.0f); + scalar distance = b3Length(d); + b3Vec3 normal(scalar(0), scalar(1), scalar(0)); if (distance > B3_EPSILON) { normal = d / distance; } manifold.pointCount = 1; - manifold.points[0].localNormal1 = b3MulT(xf1.rotation, normal); + manifold.points[0].localNormal1 = b3MulC(xf1.rotation, normal); manifold.points[0].localPoint1 = s1->m_center; manifold.points[0].localPoint2 = s2->m_center; manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; diff --git a/src/bounce/dynamics/contacts/collide/collide_triangle_capsule.cpp b/src/bounce/dynamics/contacts/collide/collide_triangle_capsule.cpp new file mode 100644 index 0000000..084ebfe --- /dev/null +++ b/src/bounce/dynamics/contacts/collide/collide_triangle_capsule.cpp @@ -0,0 +1,302 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include +#include + +static void b3BuildEdgeContact(b3Manifold& manifold, + const b3Transform& xf1, const b3TriangleHull* hull1, u32 index1, + const b3Transform& xf2, const b3Capsule* hull2) +{ + const b3HalfEdge* edge1 = hull1->GetEdge(index1); + const b3HalfEdge* twin1 = hull1->GetEdge(index1 + 1); + + b3Vec3 C1 = xf1 * hull1->centroid; + b3Vec3 P1 = xf1 * hull1->GetVertex(edge1->origin); + b3Vec3 Q1 = xf1 * hull1->GetVertex(twin1->origin); + b3Vec3 E1 = Q1 - P1; + b3Vec3 N1 = E1; + scalar L1 = N1.Normalize(); + B3_ASSERT(L1 > scalar(0)); + + b3Vec3 P2 = xf2 * hull2->vertex1; + b3Vec3 Q2 = xf2 * hull2->vertex2; + b3Vec3 E2 = Q2 - P2; + b3Vec3 N2 = E2; + scalar L2 = N2.Normalize(); + B3_ASSERT(L2 > scalar(0)); + + // Compute the closest points on the two lines. + scalar b = b3Dot(N1, N2); + scalar den = scalar(1) - b * b; + if (den == scalar(0)) + { + return; + } + + scalar inv_den = scalar(1) / den; + + b3Vec3 E3 = P1 - P2; + + scalar d = b3Dot(N1, E3); + scalar e = b3Dot(N2, E3); + + scalar s = inv_den * (b * e - d); + scalar t = inv_den * (e - b * d); + + b3Vec3 c1 = P1 + s * N1; + b3Vec3 c2 = P2 + t * N2; + + // Ensure normal orientation to capsule. + b3Vec3 N = b3Cross(E1, E2); + scalar LN = N.Normalize(); + B3_ASSERT(LN > scalar(0)); + if (b3Dot(N, P1 - C1) < scalar(0)) + { + N = -N; + } + + b3FeaturePair pair = b3MakePair(index1, index1 + 1, 0, 1); + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = b3MulC(xf1.rotation, N); + manifold.points[0].localPoint1 = b3MulT(xf1, c1); + manifold.points[0].localPoint2 = b3MulT(xf2, c2); + manifold.points[0].key = b3MakeKey(pair); +} + +static void b3BuildFaceContact(b3Manifold& manifold, + const b3Transform& xf1, const b3TriangleHull* hull1, u32 index1, scalar r1, + const b3Transform& xf2, const b3Capsule* hull2, scalar r2) +{ + b3Capsule worldHull2(xf2 * hull2->vertex1, xf2 * hull2->vertex2, r2); + b3ClipVertex edge2[2]; + b3BuildEdge(edge2, &worldHull2); + + // Clip edge 2 against the side planes of the reference face. + b3ClipVertex clipEdge2[2]; + u32 clipCount = b3ClipEdgeToFace(clipEdge2, edge2, xf1, r1, index1, hull1); + + // Project clipped edge 2 onto the reference face. + b3Plane localPlane1 = hull1->GetPlane(index1); + b3Plane plane1 = xf1 * localPlane1; + + scalar totalRadius = r1 + r2; + + u32 pointCount = 0; + for (u32 i = 0; i < clipCount; ++i) + { + b3Vec3 c2 = clipEdge2[i].position; + scalar s = b3Distance(c2, plane1); + if (s <= totalRadius) + { + b3Vec3 c1 = b3ClosestPointOnPlane(c2, plane1); + + b3ManifoldPoint* mp = manifold.points + pointCount; + mp->localNormal1 = localPlane1.normal; + mp->localPoint1 = b3MulT(xf1, c1); + mp->localPoint2 = b3MulT(xf2, c2); + mp->key = b3MakeKey(clipEdge2[i].pair); + + ++pointCount; + } + } + + manifold.pointCount = pointCount; +} + +void b3CollideTriangleAndCapsule(b3Manifold& manifold, + const b3Transform& xf1, const b3TriangleShape* s1, + const b3Transform& xf2, const b3CapsuleShape* s2) +{ + scalar r1 = s1->m_radius; + scalar r2 = s2->m_radius; + + scalar totalRadius = r1 + r2; + + b3ShapeGJKProxy proxy1(s1, 0); + b3ShapeGJKProxy proxy2(s2, 0); + + b3SimplexCache simplex; + simplex.count = 0; + + b3GJKOutput gjk = b3GJK(xf1, proxy1, xf2, proxy2, false, &simplex); + + if (gjk.distance > totalRadius) + { + return; + } + + b3TriangleHull hull1(s1->m_vertex1, s1->m_vertex2, s1->m_vertex3); + b3Capsule hull2(s2->m_vertex1, s2->m_vertex2, r2); + + if (gjk.distance > scalar(0)) + { + b3Vec3 c1 = gjk.point1; + b3Vec3 c2 = gjk.point2; + scalar d = gjk.distance; + + // Define reference normal. + b3Vec3 N1 = (c2 - c1) / d; + + // Search reference face. + b3Vec3 localN1 = b3MulC(xf1.rotation, N1); + u32 index1 = hull1.GetSupportFace(localN1); + b3Vec3 localFaceN1 = hull1.GetPlane(index1).normal; + + // Paralell vectors |v1xv2| = sin(theta) + const scalar kTol = scalar(0.005); + b3Vec3 N = b3Cross(localN1, localFaceN1); + scalar L = b3Dot(N, N); + if (L < kTol * kTol) + { + // Reference face found. + // Try to build a face contact. + b3BuildFaceContact(manifold, xf1, &hull1, index1, r1, xf2, &hull2, r2); + if (manifold.pointCount == 2) + { + return; + } + } + + if (s1->m_hasE1Vertex || s1->m_hasE2Vertex || s1->m_hasE3Vertex) + { + b3GJKFeaturePair featurePair = b3GetFeaturePair(simplex); + + if (featurePair.count1 == 2) + { + u32 v1 = featurePair.index1[0]; + u32 v2 = featurePair.index1[1]; + + b3Vec3 vertices[3] = { s1->m_vertex1, s1->m_vertex2, s1->m_vertex3 }; + bool hasWing[3] = { s1->m_hasE1Vertex, s1->m_hasE2Vertex, s1->m_hasE3Vertex }; + b3Vec3 edgeWings[3] = { s1->m_e1Vertex, s1->m_e2Vertex, s1->m_e3Vertex }; + + bool edgeFound = false; + u32 edgeIndex; + for (u32 i = 0; i < 3; ++i) + { + u32 j = i + 1 < 3 ? i + 1 : 0; + + if (v1 == i && v2 == j) + { + edgeFound = true; + edgeIndex = i; + break; + } + + if (v2 == i && v1 == j) + { + edgeFound = true; + edgeIndex = i; + break; + } + } + + B3_ASSERT(edgeFound == true); + + if (hasWing[edgeIndex] == true) + { + u32 ev1 = edgeIndex; + u32 ev2 = edgeIndex + 1 < 3 ? edgeIndex + 1 : 0; + + // Put the closest point on the capsule to the frame of the triangle. + b3Vec3 Q = b3MulT(xf1, c2); + + // Adjacent face triangle + b3Vec3 A1 = edgeWings[edgeIndex]; + b3Vec3 B1 = vertices[ev2]; + b3Vec3 C1 = vertices[ev1]; + + scalar wABC1[4]; + b3BarycentricCoordinates(wABC1, A1, B1, C1, Q); + + // Is the closest point on the capsule in the Region ABC of the adjacent face? + if (wABC1[0] > scalar(0) && wABC1[1] > scalar(0) && wABC1[2] > scalar(0)) + { + return; + } + + b3Vec3 center1 = (A1 + B1 + C1) / scalar(3); + + b3Plane frontPlane = hull1.trianglePlanes[0]; + + scalar pd = b3Distance(center1, frontPlane); + + const scalar kCoplanarTol = 0.005f; + + // Is the edge coplanar? + if (pd > -kCoplanarTol && pd < kCoplanarTol) + { + b3Vec3 n = frontPlane.normal; + + if (b3Dot(n, localN1) < scalar(0)) + { + n = -n; + } + + // c1 is constant + // c2 = c1 + s * n1 + // c1 = c2 - s * n1 + b3Vec3 nc2 = c1 + d * b3Mul(xf1.rotation, n); + + localN1 = n; + c2 = nc2; + } + } + } + } + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = localN1; + manifold.points[0].localPoint1 = b3MulT(xf1, c1); + manifold.points[0].localPoint2 = b3MulT(xf2, c2); + manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; + manifold.points[0].key.key1 = 0; + manifold.points[0].key.key2 = 0; + + return; + } + + b3FaceQuery faceQuery1 = b3QueryFaceSeparation(xf1, &hull1, xf2, &hull2); + if (faceQuery1.separation > totalRadius) + { + return; + } + + b3EdgeQuery edgeQuery = b3QueryEdgeSeparation(xf1, &hull1, xf2, &hull2); + if (edgeQuery.separation > totalRadius) + { + return; + } + + const scalar kTol = scalar(0.1) * B3_LINEAR_SLOP; + if (edgeQuery.separation > faceQuery1.separation + kTol) + { + b3BuildEdgeContact(manifold, xf1, &hull1, edgeQuery.index1, xf2, &hull2); + } + else + { + b3BuildFaceContact(manifold, xf1, &hull1, faceQuery1.index, r1, xf2, &hull2, r2); + } +} \ No newline at end of file diff --git a/src/bounce/dynamics/contacts/collide/collide_triangle_hull.cpp b/src/bounce/dynamics/contacts/collide/collide_triangle_hull.cpp new file mode 100644 index 0000000..75a1149 --- /dev/null +++ b/src/bounce/dynamics/contacts/collide/collide_triangle_hull.cpp @@ -0,0 +1,163 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include + +// Half-edge to edge map +struct b3TriangleHullEdges +{ + bool IsEdgeCoplanar(u32 index) const; + + const b3TriangleHull* m_triangleHull; + bool m_hasWing[3]; + b3Vec3 m_edgeWings[3]; + u32 m_halfEdgeEdges[6]; +}; + +bool b3TriangleHullEdges::IsEdgeCoplanar(u32 halfEdgeIndex) const +{ + u32 edgeIndex = m_halfEdgeEdges[halfEdgeIndex]; + + if (m_hasWing[edgeIndex] == false) + { + return false; + } + + u32 ev1 = edgeIndex; + u32 ev2 = edgeIndex + 1 < 3 ? edgeIndex + 1 : 0; + + // Adjacent triangle + b3Vec3 A1 = m_edgeWings[edgeIndex]; + b3Vec3 B1 = m_triangleHull->triangleVertices[ev2]; + b3Vec3 C1 = m_triangleHull->triangleVertices[ev1]; + + b3Vec3 center1 = (A1 + B1 + C1) / scalar(3); + + b3Plane frontPlane = m_triangleHull->trianglePlanes[0]; + + scalar d = b3Distance(center1, frontPlane); + + const scalar kCoplanarTol = 0.005f; + + if (d > -kCoplanarTol && d < kCoplanarTol) + { + return true; + } + + return false; +} + +void b3CollideTriangleAndHull(b3Manifold& manifold, + const b3Transform& xf1, const b3TriangleShape* s1, + const b3Transform& xf2, const b3HullShape* s2, + b3ConvexCache* cache) +{ + const b3TriangleHull triangleHull1(s1->m_vertex1, s1->m_vertex2, s1->m_vertex3); + const b3Hull* hull2 = s2->m_hull; + + b3HullShape hullShape1; + hullShape1.SetShape((b3Shape*)s1); + hullShape1.m_hull = &triangleHull1; + hullShape1.m_radius = s1->m_radius; + + b3CollideHullAndHull(manifold, xf1, &hullShape1, xf2, s2, cache); + + b3TriangleHullEdges triangleHullEdges; + triangleHullEdges.m_triangleHull = &triangleHull1; + + triangleHullEdges.m_hasWing[0] = s1->m_hasE1Vertex; + triangleHullEdges.m_hasWing[1] = s1->m_hasE2Vertex; + triangleHullEdges.m_hasWing[2] = s1->m_hasE3Vertex; + + triangleHullEdges.m_edgeWings[0] = s1->m_e1Vertex; + triangleHullEdges.m_edgeWings[1] = s1->m_e2Vertex; + triangleHullEdges.m_edgeWings[2] = s1->m_e3Vertex; + + triangleHullEdges.m_halfEdgeEdges[0] = 0; + triangleHullEdges.m_halfEdgeEdges[2] = 1; + triangleHullEdges.m_halfEdgeEdges[4] = 2; + + triangleHullEdges.m_halfEdgeEdges[1] = 0; + triangleHullEdges.m_halfEdgeEdges[3] = 1; + triangleHullEdges.m_halfEdgeEdges[5] = 2; + + b3Vec3 C1 = xf1 * triangleHull1.centroid; + b3Plane plane1 = xf1 * triangleHull1.planes[0]; + + b3Vec3 C2 = xf2 * hull2->centroid; + + for (u32 i = 0; i < manifold.pointCount; ++i) + { + b3ManifoldPoint* mp = manifold.points + i; + + b3FeaturePair pair = mp->featurePair; + + u32 e1; + if (mp->edgeContact) + { + e1 = pair.inEdge1; + } + else + { + if (pair.inEdge1 == B3_NULL_EDGE) + { + e1 = pair.outEdge1; + } + else + { + e1 = pair.inEdge1; + } + + if (e1 == B3_NULL_EDGE) + { + continue; + } + } + + b3Vec3 localN1 = mp->localNormal1; + b3Vec3 localC1 = mp->localPoint1; + b3Vec3 localC2 = mp->localPoint2; + + b3Vec3 n1 = b3Mul(xf1.rotation, localN1); + b3Vec3 c1 = xf1 * localC1; + b3Vec3 c2 = xf2 * localC2; + scalar s = b3Dot(c2 - c1, n1); + + if (triangleHullEdges.IsEdgeCoplanar(e1)) + { + b3Vec3 n = plane1.normal; + + if (b3Dot(n, C2 - C1) < scalar(0)) + { + n = -n; + } + + // c1 is constant + // c2 = c1 + s * n1 + // c1 = c2 - s * n1 + b3Vec3 nc2 = c1 + s * n; + + mp->localNormal1 = b3MulC(xf1.rotation, n); + mp->localPoint2 = b3MulT(xf2, nc2); + } + } +} \ No newline at end of file diff --git a/src/bounce/dynamics/contacts/collide/collide_triangle_sphere.cpp b/src/bounce/dynamics/contacts/collide/collide_triangle_sphere.cpp new file mode 100644 index 0000000..b8483e2 --- /dev/null +++ b/src/bounce/dynamics/contacts/collide/collide_triangle_sphere.cpp @@ -0,0 +1,311 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include + +void b3CollideTriangleAndSphere(b3Manifold& manifold, + const b3Transform& xf1, const b3TriangleShape* s1, + const b3Transform& xf2, const b3SphereShape* s2) +{ + // Put the sphere center in the frame of the triangle. + b3Vec3 Q = b3MulT(xf1, b3Mul(xf2, s2->m_center)); + + // ABC + b3Vec3 A = s1->m_vertex1, B = s1->m_vertex2, C = s1->m_vertex3; + + scalar radius = s1->m_radius + s2->m_radius; + + // Test vertex regions + scalar wAB[3], wBC[3], wCA[3]; + b3BarycentricCoordinates(wAB, A, B, Q); + b3BarycentricCoordinates(wBC, B, C, Q); + b3BarycentricCoordinates(wCA, C, A, Q); + + // R A + if (wAB[1] <= scalar(0) && wCA[0] <= scalar(0)) + { + b3Vec3 P = A; + b3Vec3 d = Q - P; + scalar dd = b3Dot(d, d); + if (dd > radius * radius) + { + return; + } + + b3Vec3 n(0, 1, 0); + scalar len = b3Length(d); + if (len > B3_EPSILON) + { + n = d / len; + } + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = n; + manifold.points[0].localPoint1 = P; + manifold.points[0].localPoint2 = s2->m_center; + manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; + manifold.points[0].key.key1 = 0; + manifold.points[0].key.key2 = 0; + + return; + } + + // R B + if (wAB[0] <= scalar(0) && wBC[1] <= scalar(0)) + { + b3Vec3 P = B; + b3Vec3 d = Q - P; + scalar dd = b3Dot(d, d); + if (dd > radius * radius) + { + return; + } + + b3Vec3 n(0, 1, 0); + scalar len = b3Length(d); + if (len > B3_EPSILON) + { + n = d / len; + } + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = n; + manifold.points[0].localPoint1 = P; + manifold.points[0].localPoint2 = s2->m_center; + manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; + manifold.points[0].key.key1 = 0; + manifold.points[0].key.key2 = 0; + + return; + } + + // R C + if (wBC[0] <= scalar(0) && wCA[1] <= scalar(0)) + { + b3Vec3 P = C; + b3Vec3 d = Q - P; + scalar dd = b3Dot(d, d); + if (dd > radius * radius) + { + return; + } + + b3Vec3 n(0, 1, 0); + scalar len = b3Length(d); + if (len > B3_EPSILON) + { + n = d / len; + } + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = n; + manifold.points[0].localPoint1 = P; + manifold.points[0].localPoint2 = s2->m_center; + manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; + manifold.points[0].key.key1 = 0; + manifold.points[0].key.key2 = 0; + + return; + } + + // Test edge regions + scalar wABC[4]; + b3BarycentricCoordinates(wABC, A, B, C, Q); + + // R AB + if (wAB[0] > scalar(0) && wAB[1] > scalar(0) && wABC[3] * wABC[2] <= scalar(0)) + { + B3_ASSERT(wAB[2] > scalar(0)); + scalar den = scalar(1) / wAB[2]; + + b3Vec3 P = den * (wAB[0] * A + wAB[1] * B); + b3Vec3 d = Q - P; + scalar dd = b3Dot(d, d); + if (dd > radius * radius) + { + return; + } + + // Is there a face connected to AB? + if (s1->m_hasE1Vertex) + { + b3Vec3 A1 = s1->m_e1Vertex; + b3Vec3 B1 = B; + b3Vec3 C1 = A; + + scalar wABC1[4]; + b3BarycentricCoordinates(wABC1, A1, B1, C1, Q); + + // Is the sphere in the Region ABC of the adjacent face? + if (wABC1[0] > scalar(0) && wABC1[1] > scalar(0) && wABC1[2] > scalar(0)) + { + return; + } + } + + b3Vec3 n(0, 1, 0); + scalar len = b3Length(d); + if (len > B3_EPSILON) + { + n = d / len; + } + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = n; + manifold.points[0].localPoint1 = P; + manifold.points[0].localPoint2 = s2->m_center; + manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; + manifold.points[0].key.key1 = 0; + manifold.points[0].key.key2 = 0; + + return; + } + + // R BC + if (wBC[0] > scalar(0) && wBC[1] > scalar(0) && wABC[3] * wABC[0] <= scalar(0)) + { + B3_ASSERT(wBC[2] > scalar(0)); + scalar den = scalar(1) / wBC[2]; + + b3Vec3 P = den * (wBC[0] * B + wBC[1] * C); + b3Vec3 d = Q - P; + scalar dd = b3Dot(d, d); + if (dd > radius * radius) + { + return; + } + + // Is there a face connected to AC? + if (s1->m_hasE2Vertex) + { + b3Vec3 A1 = s1->m_e2Vertex; + b3Vec3 B1 = C; + b3Vec3 C1 = B; + + scalar wABC1[4]; + b3BarycentricCoordinates(wABC1, A1, B1, C1, Q); + + // Is the sphere in the Region ABC of the adjacent face? + if (wABC1[0] > scalar(0) && wABC1[1] > scalar(0) && wABC1[2] > scalar(0)) + { + return; + } + } + + b3Vec3 n(0, 1, 0); + scalar len = b3Length(d); + if (len > B3_EPSILON) + { + n = d / len; + } + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = n; + manifold.points[0].localPoint1 = P; + manifold.points[0].localPoint2 = s2->m_center; + manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; + manifold.points[0].key.key1 = 0; + manifold.points[0].key.key2 = 0; + + return; + } + + // R CA + if (wCA[0] > scalar(0) && wCA[1] > scalar(0) && wABC[3] * wABC[1] <= scalar(0)) + { + B3_ASSERT(wCA[2] > scalar(0)); + scalar den = scalar(1) / wCA[2]; + + b3Vec3 P = den * (wCA[0] * C + wCA[1] * A); + b3Vec3 d = Q - P; + scalar dd = b3Dot(d, d); + if (dd > radius * radius) + { + return; + } + + // Is there a face connected to CA? + if (s1->m_hasE3Vertex) + { + b3Vec3 A1 = s1->m_e3Vertex; + b3Vec3 B1 = A; + b3Vec3 C1 = C; + + scalar wABC1[4]; + b3BarycentricCoordinates(wABC1, A1, B1, C1, Q); + + // Is the sphere in the Region ABC of the adjacent face? + if (wABC1[0] > scalar(0) && wABC1[1] > scalar(0) && wABC1[2] > scalar(0)) + { + return; + } + } + + b3Vec3 n(0, 1, 0); + scalar len = b3Length(d); + if (len > B3_EPSILON) + { + n = d / len; + } + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = n; + manifold.points[0].localPoint1 = P; + manifold.points[0].localPoint2 = s2->m_center; + manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; + manifold.points[0].key.key1 = 0; + manifold.points[0].key.key2 = 0; + + return; + } + + // R ABC/ACB + if (wABC[3] == scalar(0)) + { + return; + } + + B3_ASSERT(wABC[3] > scalar(0)); + scalar den = scalar(1) / wABC[3]; + + b3Vec3 P = den * (wABC[0] * A + wABC[1] * B + wABC[2] * C); + b3Vec3 d = Q - P; + scalar dd = b3Dot(d, d); + if (dd > radius * radius) + { + return; + } + + b3Vec3 n(0, 1, 0); + scalar len = b3Length(d); + if (len > B3_EPSILON) + { + n = d / len; + } + + manifold.pointCount = 1; + manifold.points[0].localNormal1 = n; + manifold.points[0].localPoint1 = P; + manifold.points[0].localPoint2 = s2->m_center; + manifold.points[0].key.triangleKey = B3_NULL_TRIANGLE; + manifold.points[0].key.key1 = 0; + manifold.points[0].key.key2 = 0; +} \ No newline at end of file diff --git a/src/bounce/dynamics/contacts/contact.cpp b/src/bounce/dynamics/contacts/contact.cpp index 82da917..06c0a99 100644 --- a/src/bounce/dynamics/contacts/contact.cpp +++ b/src/bounce/dynamics/contacts/contact.cpp @@ -17,16 +17,139 @@ */ #include +#include +#include #include #include #include #include +#include + +bool b3Contact::s_initialized = false; +b3ContactRegister b3Contact::s_registers[e_maxShapes][e_maxShapes]; + +void b3Contact::AddPrimaryRegister(b3ContactCreateFcn* createFcn, b3ContactDestroyFcn* destoryFcn, + b3ShapeType type1, b3ShapeType type2, + b3ContactType contactType) +{ + B3_ASSERT(0 <= type1 && type1 < e_maxShapes); + B3_ASSERT(0 <= type2 && type2 < e_maxShapes); + + s_registers[type1][type2].createFcn = createFcn; + s_registers[type1][type2].destroyFcn = destoryFcn; + s_registers[type1][type2].primary = true; + s_registers[type1][type2].contactType = contactType; + + if (type1 != type2) + { + s_registers[type2][type1].createFcn = createFcn; + s_registers[type2][type1].destroyFcn = destoryFcn; + s_registers[type2][type1].primary = false; + s_registers[type2][type1].contactType = contactType; + } +} + +void b3Contact::InitializePrimaryRegisters() +{ + AddPrimaryRegister(b3ConvexContact::Create, b3ConvexContact::Destroy, e_sphereShape, e_sphereShape, e_convexContact); + + AddPrimaryRegister(b3ConvexContact::Create, b3ConvexContact::Destroy, e_capsuleShape, e_sphereShape, e_convexContact); + AddPrimaryRegister(b3ConvexContact::Create, b3ConvexContact::Destroy, e_capsuleShape, e_capsuleShape, e_convexContact); + + AddPrimaryRegister(b3ConvexContact::Create, b3ConvexContact::Destroy, e_triangleShape, e_sphereShape, e_convexContact); + AddPrimaryRegister(b3ConvexContact::Create, b3ConvexContact::Destroy, e_triangleShape, e_capsuleShape, e_convexContact); + AddPrimaryRegister(b3ConvexContact::Create, b3ConvexContact::Destroy, e_triangleShape, e_hullShape, e_convexContact); + + AddPrimaryRegister(b3ConvexContact::Create, b3ConvexContact::Destroy, e_hullShape, e_sphereShape, e_convexContact); + AddPrimaryRegister(b3ConvexContact::Create, b3ConvexContact::Destroy, e_hullShape, e_capsuleShape, e_convexContact); + AddPrimaryRegister(b3ConvexContact::Create, b3ConvexContact::Destroy, e_hullShape, e_hullShape, e_convexContact); + + AddPrimaryRegister(b3MeshContact::Create, b3MeshContact::Destroy, e_meshShape, e_sphereShape, e_meshContact); + AddPrimaryRegister(b3MeshContact::Create, b3MeshContact::Destroy, e_meshShape, e_capsuleShape, e_meshContact); + AddPrimaryRegister(b3MeshContact::Create, b3MeshContact::Destroy, e_meshShape, e_hullShape, e_meshContact); +} + +b3Contact* b3Contact::Create(b3Shape* shapeA, b3Shape* shapeB, b3BlockPool* allocators[e_maxContact]) +{ + if (s_initialized == false) + { + InitializePrimaryRegisters(); + s_initialized = true; + } + + b3ShapeType type1 = shapeA->GetType(); + b3ShapeType type2 = shapeB->GetType(); + + B3_ASSERT(0 <= type1 && type1 < e_maxShapes); + B3_ASSERT(0 <= type2 && type2 < e_maxShapes); + + const b3ContactRegister& contactRegister = s_registers[type1][type2]; + + b3ContactCreateFcn* createFcn = contactRegister.createFcn; + if (createFcn) + { + b3BlockPool* allocator = allocators[contactRegister.contactType]; + + if (s_registers[type1][type2].primary) + { + return createFcn(shapeA, shapeB, allocator); + } + else + { + return createFcn(shapeB, shapeA, allocator); + } + } + else + { + return nullptr; + } +} + +void b3Contact::Destroy(b3Contact* contact, b3BlockPool* allocators[e_maxContact]) +{ + B3_ASSERT(s_initialized == true); + + b3Shape* shapeA = contact->m_pair.shapeA; + b3Shape* shapeB = contact->m_pair.shapeB; + + for (u32 i = 0; i < contact->m_manifoldCount; ++i) + { + if (contact->m_manifolds[i].pointCount > 0) + { + if (shapeA->IsSensor() == false && shapeB->IsSensor() == false) + { + shapeA->GetBody()->SetAwake(true); + shapeB->GetBody()->SetAwake(true); + break; + } + } + } + + b3ShapeType type1 = shapeA->GetType(); + b3ShapeType type2 = shapeB->GetType(); + + B3_ASSERT(0 <= type1 && type1 < e_maxShapes); + B3_ASSERT(0 <= type2 && type2 < e_maxShapes); + + const b3ContactRegister& contactRegister = s_registers[type1][type2]; + + b3BlockPool* allocator = allocators[contactRegister.contactType]; + + b3ContactDestroyFcn* destroyFcn = contactRegister.destroyFcn; + destroyFcn(contact, allocator); +} + +b3Contact::b3Contact(b3Shape* shapeA, b3Shape* shapeB) +{ + m_pair.shapeA = shapeA; + m_pair.shapeB = shapeB; +} void b3Contact::GetWorldManifold(b3WorldManifold* out, u32 index) const { B3_ASSERT(index < m_manifoldCount); b3Manifold* m = m_manifolds + index; - + const b3Shape* shapeA = GetShapeA(); const b3Body* bodyA = shapeA->GetBody(); b3Transform xfA = bodyA->GetTransform(); @@ -50,9 +173,12 @@ void b3Contact::Update(b3ContactListener* listener) b3World* world = bodyA->GetWorld(); + b3StackAllocator* stack = &world->m_stackAllocator; + bool wasOverlapping = IsOverlapping(); bool isOverlapping = false; - bool isSensorContact = shapeA->IsSensor() || shapeB->IsSensor(); + bool isSensorContact = IsSensorContact(); + bool isDynamicContact = HasDynamicBody(); if (isSensorContact == true) { @@ -62,8 +188,8 @@ void b3Contact::Update(b3ContactListener* listener) else { // Copy the old contact points. - b3Manifold oldManifolds[B3_MAX_MANIFOLDS]; u32 oldManifoldCount = m_manifoldCount; + b3Manifold* oldManifolds = (b3Manifold*)stack->Allocate(oldManifoldCount * sizeof(b3Manifold)); memcpy(oldManifolds, m_manifolds, oldManifoldCount * sizeof(b3Manifold)); // Clear all contact points. @@ -90,6 +216,8 @@ void b3Contact::Update(b3ContactListener* listener) } } + stack->Free(oldManifolds); + // The shapes are overlapping if at least one contact // point was built. for (u32 i = 0; i < m_manifoldCount; ++i) @@ -116,11 +244,11 @@ void b3Contact::Update(b3ContactListener* listener) } else { - m_flags &= ~e_overlapFlag;; + m_flags &= ~e_overlapFlag; } // Notify the contact listener the new contact state. - if (listener != NULL) + if (listener != nullptr) { if (wasOverlapping == false && isOverlapping == true) { @@ -132,9 +260,19 @@ void b3Contact::Update(b3ContactListener* listener) listener->EndContact(this); } - if (isSensorContact == false && isOverlapping == true) + if (isOverlapping == true && isDynamicContact == true && isSensorContact == false) { listener->PreSolve(this); } } } + +bool b3Contact::IsSensorContact() const +{ + return m_pair.shapeA->IsSensor() || m_pair.shapeB->IsSensor(); +} + +bool b3Contact::HasDynamicBody() const +{ + return m_pair.shapeA->GetBody()->GetType() == e_dynamicBody || m_pair.shapeB->GetBody()->GetType() == e_dynamicBody; +} \ No newline at end of file diff --git a/src/bounce/dynamics/contacts/contact_cluster.cpp b/src/bounce/dynamics/contacts/contact_cluster.cpp index bdc4109..d2971c8 100644 --- a/src/bounce/dynamics/contacts/contact_cluster.cpp +++ b/src/bounce/dynamics/contacts/contact_cluster.cpp @@ -22,7 +22,7 @@ static B3_FORCE_INLINE bool b3IsCCW(const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, const b3Vec3& N) { b3Vec3 n = b3Cross(B - A, C - A); - return b3Dot(n, N) > 0.0f; + return b3Dot(n, N) > scalar(0); } static B3_FORCE_INLINE bool b3IsCCW(const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, const b3Vec3& D, const b3Vec3& N) @@ -62,7 +62,6 @@ void b3ReducePolygon(b3ClusterPolygon& pOut, B3_ASSERT(pOut.Count() == 0); B3_ASSERT(initialPoint < pIn.Count()); - pOut.Reserve(pIn.Count()); if (pIn.Count() <= B3_MAX_MANIFOLD_POINTS) { b3SortPolygon(pOut, pIn, pNormal); @@ -71,7 +70,7 @@ void b3ReducePolygon(b3ClusterPolygon& pOut, B3_ASSERT(pIn.Count() > B3_MAX_MANIFOLD_POINTS); - b3StackArray chosens; + b3StackArray chosens; chosens.Resize(pIn.Count()); for (u32 i = 0; i < chosens.Count(); ++i) { @@ -88,14 +87,14 @@ void b3ReducePolygon(b3ClusterPolygon& pOut, b3Vec3 A = pOut[0].position; u32 index = 0; - float32 max = -B3_MAX_FLOAT; + scalar max = -B3_MAX_SCALAR; for (u32 i = 0; i < pIn.Count(); ++i) { if (chosens[i]) { continue; } b3Vec3 B = pIn[i].position; b3Vec3 d = B - A; - float32 dd = b3Dot(d, d); + scalar dd = b3Dot(d, d); if (dd > max) { max = dd; @@ -118,15 +117,15 @@ void b3ReducePolygon(b3ClusterPolygon& pOut, b3Vec3 B = pOut[1].position; u32 index = 0; - float32 max = -B3_MAX_FLOAT; + scalar max = -B3_MAX_SCALAR; for (u32 i = 0; i < pIn.Count(); ++i) { if (chosens[i]) { continue; } b3Vec3 C = pIn[i].position; b3Vec3 N = b3Cross(B - A, C - A); - float32 sa2 = b3Dot(N, pNormal); - float32 a2 = b3Abs(sa2); + scalar sa2 = b3Dot(N, pNormal); + scalar a2 = b3Abs(sa2); if (a2 > max) { max = a2; @@ -136,8 +135,8 @@ void b3ReducePolygon(b3ClusterPolygon& pOut, // Colinearity check. // Use wanky tolerance for reasonable performance. - const float32 kAreaTol = 0.01f; - if (max < 2.0f * kAreaTol) + const scalar kAreaTol = scalar(0.01); + if (max < scalar(2) * kAreaTol) { // Return the largest segment AB. return; @@ -162,14 +161,14 @@ void b3ReducePolygon(b3ClusterPolygon& pOut, B3_ASSERT(b3IsCCW(A, B, C, pNormal)); u32 index = 0; - float32 min = B3_MAX_FLOAT; + scalar min = B3_MAX_SCALAR; for (u32 i = 0; i < pIn.Count(); ++i) { if (chosens[i]) { continue; } b3Vec3 D = pIn[i].position; b3Vec3 N = b3Cross(B - A, D - A); - float32 sa2 = b3Dot(N, pNormal); + scalar sa2 = b3Dot(N, pNormal); if (sa2 < min) { min = sa2; @@ -178,8 +177,8 @@ void b3ReducePolygon(b3ClusterPolygon& pOut, } // Colinearity check. - const float32 kAreaTol = 0.01f; - if (b3Abs(min) < 2.0f * kAreaTol) + const scalar kAreaTol = scalar(0.01); + if (b3Abs(min) < scalar(2) * kAreaTol) { // Return the face ABC. return; @@ -189,10 +188,10 @@ void b3ReducePolygon(b3ClusterPolygon& pOut, chosens[index] = true; } - // Weld output polygon + // Sort output polygon B3_ASSERT(pOut.Count() <= B3_MAX_MANIFOLD_POINTS); - b3StackArray quad; + b3StackArray quad; b3SortPolygon(quad, pOut, pNormal); // Output polygon @@ -228,7 +227,7 @@ void b3ClusterSolver::InitializeClusters() // This is used to skip observations that were // used to initialize a cluster centroid. - b3StackArray chosens; + b3StackArray chosens; chosens.Resize(m_observations.Count()); for (u32 i = 0; i < m_observations.Count(); ++i) { @@ -248,7 +247,7 @@ void b3ClusterSolver::InitializeClusters() b3Vec3 A = m_clusters[0].centroid; u32 index = 0; - float32 max = -B3_MAX_FLOAT; + scalar max = -B3_MAX_SCALAR; for (u32 i = 0; i < m_observations.Count(); ++i) { if (chosens[i]) { continue; } @@ -256,7 +255,7 @@ void b3ClusterSolver::InitializeClusters() const b3Observation& o = m_observations[i]; b3Vec3 B = o.point; - float32 dd = b3DistanceSquared(A, B); + scalar dd = b3DistanceSquared(A, B); if (dd > max) { max = dd; @@ -277,7 +276,7 @@ void b3ClusterSolver::InitializeClusters() b3Vec3 A = m_clusters[0].centroid; u32 index = 0; - float32 max = -B3_MAX_FLOAT; + scalar max = -B3_MAX_SCALAR; for (u32 i = 0; i < m_observations.Count(); ++i) { if (chosens[i]) { continue; } @@ -286,7 +285,7 @@ void b3ClusterSolver::InitializeClusters() b3Vec3 B = o.point; - float32 dd = b3DistanceSquared(A, B); + scalar dd = b3DistanceSquared(A, B); if (dd > max) { max = dd; @@ -308,7 +307,7 @@ void b3ClusterSolver::InitializeClusters() b3Vec3 B = m_clusters[1].centroid; u32 index = 0; - float32 max = -B3_MAX_FLOAT; + scalar max = -B3_MAX_SCALAR; for (u32 i = 0; i < m_observations.Count(); ++i) { if (chosens[i]) { continue; } @@ -318,7 +317,7 @@ void b3ClusterSolver::InitializeClusters() b3Vec3 C = o.point; b3Vec3 Q = b3ClosestPointOnSegment(C, A, B); - float32 dd = b3DistanceSquared(C, Q); + scalar dd = b3DistanceSquared(C, Q); if (dd > max) { max = dd; @@ -348,7 +347,7 @@ void b3ClusterSolver::AddCluster(const b3Vec3& centroid) b3Cluster& bestCluster = m_clusters[bestIndex]; // Should we merge the cluster? - const float32 kTol = 0.05f; + const scalar kTol = scalar(0.05); if (b3DistanceSquared(centroid, bestCluster.centroid) <= kTol * kTol) { @@ -367,11 +366,11 @@ void b3ClusterSolver::AddCluster(const b3Vec3& centroid) u32 b3ClusterSolver::FindCluster(const b3Vec3& point) const { u32 bestIndex = 0; - float32 bestValue = B3_MAX_FLOAT; + scalar bestValue = B3_MAX_SCALAR; for (u32 i = 0; i < m_clusters.Count(); ++i) { b3Vec3 centroid = m_clusters[i].centroid; - float32 metric = b3DistanceSquared(point, centroid); + scalar metric = b3DistanceSquared(point, centroid); if (metric < bestValue) { bestValue = metric; @@ -435,7 +434,7 @@ void b3ClusterSolver::Solve() if (pointCount > 0) { - centroid /= float32(pointCount); + centroid /= scalar(pointCount); cluster.centroid = centroid; } } @@ -446,7 +445,7 @@ void b3ClusterSolver::Solve() m_iterations = iter; // Remove empty clusters - b3StackArray usedClusters; + b3StackArray usedClusters; for (u32 i = 0; i < m_clusters.Count(); ++i) { @@ -474,7 +473,7 @@ void b3ClusterSolver::Solve() void b3ClusterSolver::Run(b3Manifold outManifolds[3], u32& numOut, const b3Manifold* inManifolds, u32 numIn, - const b3Transform& xfA, float32 radiusA, const b3Transform& xfB, float32 radiusB) + const b3Transform& xfA, scalar radiusA, const b3Transform& xfB, scalar radiusB) { // Initialize observations for (u32 i = 0; i < numIn; ++i) @@ -510,7 +509,7 @@ void b3ClusterSolver::Run(b3Manifold outManifolds[3], u32& numOut, b3Vec3 normal; normal.SetZero(); - b3StackArray polygonB; + b3StackArray polygonB; for (u32 j = 0; j < m_observations.Count(); ++j) { b3Observation& o = m_observations[j]; @@ -539,7 +538,7 @@ void b3ClusterSolver::Run(b3Manifold outManifolds[3], u32& numOut, continue; } - center /= float32(polygonB.Count()); + center /= scalar(polygonB.Count()); normal.Normalize(); B3_ASSERT(numOut < B3_MAX_MANIFOLDS); @@ -549,7 +548,7 @@ void b3ClusterSolver::Run(b3Manifold outManifolds[3], u32& numOut, // Reduce. // Ensure deepest point is contained. u32 minIndex = 0; - float32 minSeparation = B3_MAX_FLOAT; + scalar minSeparation = B3_MAX_SCALAR; for (u32 j = 0; j < polygonB.Count(); ++j) { const b3Observation* o = m_observations.Get(polygonB[j].clipIndex); @@ -559,7 +558,7 @@ void b3ClusterSolver::Run(b3Manifold outManifolds[3], u32& numOut, b3WorldManifoldPoint wmp; wmp.Initialize(inPoint, radiusA, xfA, radiusB, xfB); - float32 separation = wmp.separation; + scalar separation = wmp.separation; if (separation < minSeparation) { minIndex = j; @@ -569,7 +568,7 @@ void b3ClusterSolver::Run(b3Manifold outManifolds[3], u32& numOut, polygonB[j].position = polygonB[j].position + separation * normal; } - b3StackArray quadB; + b3StackArray quadB; b3ReducePolygon(quadB, polygonB, normal, minIndex); for (u32 j = 0; j < quadB.Count(); ++j) { diff --git a/src/bounce/dynamics/contacts/contact_solver.cpp b/src/bounce/dynamics/contacts/contact_solver.cpp index 4d33001..1386dba 100644 --- a/src/bounce/dynamics/contacts/contact_solver.cpp +++ b/src/bounce/dynamics/contacts/contact_solver.cpp @@ -37,7 +37,7 @@ b3ContactSolver::b3ContactSolver(const b3ContactSolverDef* def) m_positionConstraints = (b3ContactPositionConstraint*)m_allocator->Allocate(m_count * sizeof(b3ContactPositionConstraint)); m_velocityConstraints = (b3ContactVelocityConstraint*)m_allocator->Allocate(m_count * sizeof(b3ContactVelocityConstraint)); m_dt = def->dt; - m_invDt = m_dt != 0.0f ? 1.0f / m_dt : 0.0f; + m_invDt = m_dt != scalar(0) ? scalar(1) / m_dt : scalar(0); } b3ContactSolver::~b3ContactSolver() @@ -128,7 +128,10 @@ void b3ContactSolver::InitializeConstraints() vcm->tangentImpulse = m->tangentImpulse; vcm->motorImpulse = m->motorImpulse; - + vcm->motorSpeed = m->motorSpeed; + vcm->tangentSpeed1 = m->tangentSpeed1; + vcm->tangentSpeed2 = m->tangentSpeed2; + for (u32 k = 0; k < m->pointCount; ++k) { b3ManifoldPoint* cp = m->points + k; @@ -152,18 +155,18 @@ void b3ContactSolver::InitializeConstraints() b3ContactPositionConstraint* pc = m_positionConstraints + i; b3ContactVelocityConstraint* vc = m_velocityConstraints + i; - float32 radiusA = pc->radiusA; - float32 radiusB = pc->radiusB; + scalar radiusA = pc->radiusA; + scalar radiusB = pc->radiusB; b3Vec3 localCenterA = pc->localCenterA; b3Vec3 localCenterB = pc->localCenterB; u32 indexA = vc->indexA; - float32 mA = vc->invMassA; + scalar mA = vc->invMassA; b3Mat33 iA = vc->invIA; u32 indexB = vc->indexB; - float32 mB = vc->invMassB; + scalar mB = vc->invMassB; b3Mat33 iB = vc->invIB; b3Vec3 xA = m_positions[indexA].x; @@ -177,12 +180,12 @@ void b3ContactSolver::InitializeConstraints() b3Vec3 wB = m_velocities[indexB].w; b3Transform xfA; - xfA.rotation = b3QuatMat33(qA); - xfA.position = xA - b3Mul(xfA.rotation, localCenterA); + xfA.rotation = qA; + xfA.translation = xA - b3Mul(qA, localCenterA); b3Transform xfB; - xfB.rotation = b3QuatMat33(qB); - xfB.position = xB - b3Mul(xfB.rotation, localCenterB); + xfB.rotation = qB; + xfB.translation = xB - b3Mul(qB, localCenterB); for (u32 j = 0; j < manifoldCount; ++j) { @@ -220,13 +223,13 @@ void b3ContactSolver::InitializeConstraints() // Compute effective mass. b3Vec3 rnA = b3Cross(rA, normal); b3Vec3 rnB = b3Cross(rB, normal); - float32 K = mA + mB + b3Dot(iA * rnA, rnA) + b3Dot(iB * rnB, rnB); - vcp->normalMass = K > 0.0f ? 1.0f / K : 0.0f; + scalar K = mA + mB + b3Dot(iA * rnA, rnA) + b3Dot(iB * rnB, rnB); + vcp->normalMass = K > scalar(0) ? scalar(1) / K : scalar(0); // Add restitution to the velocity constraint. b3Vec3 dv = vB + b3Cross(wB, rB) - vA - b3Cross(wA, rA); - float32 vn = b3Dot(normal, dv); - vcp->velocityBias = 0.0f; + scalar vn = b3Dot(normal, dv); + vcp->velocityBias = scalar(0); if (vn < -B3_VELOCITY_THRESHOLD) { vcp->velocityBias = -vc->restitution * vn; @@ -256,9 +259,9 @@ void b3ContactSolver::InitializeConstraints() // dot(t1, t2) = 0 // J1_l1 * M1 * J2_l1 = J1_l2 * M2 * J2_l2 = 0 - float32 k11 = mA + mB + b3Dot(iA * rn1A, rn1A) + b3Dot(iB * rn1B, rn1B); - float32 k12 = b3Dot(iA * rn1A, rn2A) + b3Dot(iB * rn1B, rn2B); - float32 k22 = mA + mB + b3Dot(iA * rn2A, rn2A) + b3Dot(iB * rn2B, rn2B); + scalar k11 = mA + mB + b3Dot(iA * rn1A, rn1A) + b3Dot(iB * rn1B, rn1B); + scalar k12 = b3Dot(iA * rn1A, rn2A) + b3Dot(iB * rn1B, rn2B); + scalar k22 = mA + mB + b3Dot(iA * rn2A, rn2A) + b3Dot(iB * rn2B, rn2B); b3Mat22 K; K.x.Set(k11, k12); @@ -269,8 +272,8 @@ void b3ContactSolver::InitializeConstraints() // Add twist constraint. { - float32 mass = b3Dot(vcm->normal, (iA + iB) * vcm->normal); - vcm->motorMass = mass > 0.0f ? 1.0f / mass : 0.0f; + scalar mass = b3Dot(vcm->normal, (iA + iB) * vcm->normal); + vcm->motorMass = mass > scalar(0) ? scalar(1) / mass : scalar(0); } } } @@ -284,11 +287,11 @@ void b3ContactSolver::WarmStart() b3ContactVelocityConstraint* vc = m_velocityConstraints + i; u32 indexA = vc->indexA; - float32 mA = vc->invMassA; + scalar mA = vc->invMassA; b3Mat33 iA = vc->invIA; u32 indexB = vc->indexB; - float32 mB = vc->invMassB; + scalar mB = vc->invMassB; b3Mat33 iB = vc->invIB; u32 manifoldCount = vc->manifoldCount; @@ -345,11 +348,11 @@ void b3ContactSolver::SolveVelocityConstraints() u32 manifoldCount = vc->manifoldCount; u32 indexA = vc->indexA; - float32 mA = vc->invMassA; + scalar mA = vc->invMassA; b3Mat33 iA = vc->invIA; u32 indexB = vc->indexB; - float32 mB = vc->invMassB; + scalar mB = vc->invMassB; b3Mat33 iB = vc->invIB; b3Vec3 vA = m_velocities[indexA].v; @@ -362,21 +365,25 @@ void b3ContactSolver::SolveVelocityConstraints() b3VelocityConstraintManifold* vcm = vc->manifolds + j; u32 pointCount = vcm->pointCount; - float32 normalImpulse = 0.0f; + scalar motorSpeed = vcm->motorSpeed; + scalar tangentSpeed1 = vcm->tangentSpeed1; + scalar tangentSpeed2 = vcm->tangentSpeed2; + + scalar normalImpulse = scalar(0); for (u32 k = 0; k < pointCount; ++k) { b3VelocityConstraintPoint* vcp = vcm->points + k; - B3_ASSERT(vcp->normalImpulse >= 0.0f); + B3_ASSERT(vcp->normalImpulse >= scalar(0)); // Solve normal constraints. { b3Vec3 dv = vB + b3Cross(wB, vcp->rB) - vA - b3Cross(wA, vcp->rA); - float32 Cdot = b3Dot(vcp->normal, dv); + scalar Cdot = b3Dot(vcp->normal, dv); - float32 impulse = -vcp->normalMass * (Cdot - vcp->velocityBias); + scalar impulse = -vcp->normalMass * (Cdot - vcp->velocityBias); - float32 oldImpulse = vcp->normalImpulse; - vcp->normalImpulse = b3Max(vcp->normalImpulse + impulse, 0.0f); + scalar oldImpulse = vcp->normalImpulse; + vcp->normalImpulse = b3Max(vcp->normalImpulse + impulse, scalar(0)); impulse = vcp->normalImpulse - oldImpulse; b3Vec3 P = impulse * vcp->normal; @@ -398,14 +405,14 @@ void b3ContactSolver::SolveVelocityConstraints() b3Vec3 dv = vB + b3Cross(wB, vcm->rB) - vA - b3Cross(wA, vcm->rA); b3Vec2 Cdot; - Cdot.x = b3Dot(dv, vcm->tangent1); - Cdot.y = b3Dot(dv, vcm->tangent2); + Cdot.x = b3Dot(dv, vcm->tangent1) - tangentSpeed1; + Cdot.y = b3Dot(dv, vcm->tangent2) - tangentSpeed2; b3Vec2 impulse = vcm->tangentMass * -Cdot; b3Vec2 oldImpulse = vcm->tangentImpulse; vcm->tangentImpulse += impulse; - float32 maxImpulse = vc->friction * normalImpulse; + scalar maxImpulse = vc->friction * normalImpulse; if (b3Dot(vcm->tangentImpulse, vcm->tangentImpulse) > maxImpulse * maxImpulse) { vcm->tangentImpulse.Normalize(); @@ -427,10 +434,10 @@ void b3ContactSolver::SolveVelocityConstraints() // Solve motor constraint. { - float32 Cdot = b3Dot(vcm->normal, wB - wA); - float32 impulse = -vcm->motorMass * Cdot; - float32 oldImpulse = vcm->motorImpulse; - float32 maxImpulse = vc->friction * normalImpulse; + scalar Cdot = b3Dot(vcm->normal, wB - wA) - motorSpeed; + scalar impulse = -vcm->motorMass * Cdot; + scalar oldImpulse = vcm->motorImpulse; + scalar maxImpulse = vc->friction * normalImpulse; vcm->motorImpulse = b3Clamp(vcm->motorImpulse + impulse, -maxImpulse, maxImpulse); impulse = vcm->motorImpulse - oldImpulse; @@ -491,23 +498,23 @@ struct b3ContactPositionSolverPoint b3Vec3 normal; b3Vec3 point; - float32 separation; + scalar separation; }; bool b3ContactSolver::SolvePositionConstraints() { - float32 minSeparation = 0.0f; + scalar minSeparation = scalar(0); for (u32 i = 0; i < m_count; ++i) { b3ContactPositionConstraint* pc = m_positionConstraints + i; u32 indexA = pc->indexA; - float32 mA = pc->invMassA; + scalar mA = pc->invMassA; b3Vec3 localCenterA = pc->localCenterA; u32 indexB = pc->indexB; - float32 mB = pc->invMassB; + scalar mB = pc->invMassB; b3Vec3 localCenterB = pc->localCenterB; b3Vec3 cA = m_positions[indexA].x; @@ -531,25 +538,25 @@ bool b3ContactSolver::SolvePositionConstraints() b3PositionConstraintPoint* pcp = pcm->points + k; b3Transform xfA; - xfA.rotation = b3QuatMat33(qA); - xfA.position = cA - b3Mul(xfA.rotation, localCenterA); + xfA.rotation = qA; + xfA.translation = cA - b3Mul(qA, localCenterA); b3Transform xfB; - xfB.rotation = b3QuatMat33(qB); - xfB.position = cB - b3Mul(xfB.rotation, localCenterB); + xfB.rotation = qB; + xfB.translation = cB - b3Mul(qB, localCenterB); b3ContactPositionSolverPoint cpcp; cpcp.Initialize(pc, pcp, xfA, xfB); b3Vec3 normal = cpcp.normal; b3Vec3 point = cpcp.point; - float32 separation = cpcp.separation; + scalar separation = cpcp.separation; // Update max constraint error. minSeparation = b3Min(minSeparation, separation); // Allow some slop and prevent large corrections. - float32 C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, 0.0f); + scalar C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, scalar(0)); // Compute effective mass. b3Vec3 rA = point - cA; @@ -557,10 +564,10 @@ bool b3ContactSolver::SolvePositionConstraints() b3Vec3 rnA = b3Cross(rA, normal); b3Vec3 rnB = b3Cross(rB, normal); - float32 K = mA + mB + b3Dot(rnA, iA * rnA) + b3Dot(rnB, iB * rnB); + scalar K = mA + mB + b3Dot(rnA, iA * rnA) + b3Dot(rnB, iB * rnB); // Compute normal impulse. - float32 impulse = K > 0.0f ? -C / K : 0.0f; + scalar impulse = K > scalar(0) ? -C / K : scalar(0); b3Vec3 P = impulse * normal; cA -= mA * P; @@ -584,5 +591,5 @@ bool b3ContactSolver::SolvePositionConstraints() m_inertias[indexB] = iB; } - return minSeparation >= -3.0f * B3_LINEAR_SLOP; + return minSeparation >= scalar(-3) * B3_LINEAR_SLOP; } diff --git a/src/bounce/dynamics/contacts/convex_contact.cpp b/src/bounce/dynamics/contacts/convex_contact.cpp index fd4bc3d..7c7ec06 100644 --- a/src/bounce/dynamics/contacts/convex_contact.cpp +++ b/src/bounce/dynamics/contacts/convex_contact.cpp @@ -20,10 +20,24 @@ #include #include #include +#include -b3ConvexContact::b3ConvexContact(b3Shape* shapeA, b3Shape* shapeB) +b3Contact* b3ConvexContact::Create(b3Shape* shapeA, b3Shape* shapeB, b3BlockPool* allocator) { - B3_NOT_USED(shapeA); + void* mem = allocator->Allocate(); + return new (mem) b3ConvexContact(shapeA, shapeB); +} + +void b3ConvexContact::Destroy(b3Contact* contact, b3BlockPool* allocator) +{ + b3ConvexContact* c = (b3ConvexContact*)contact; + c->~b3ConvexContact(); + allocator->Free(c); +} + +b3ConvexContact::b3ConvexContact(b3Shape* shapeA, b3Shape* shapeB) : b3Contact(shapeA, shapeB) +{ + B3_NOT_USED(shapeA); B3_NOT_USED(shapeB); m_type = e_convexContact; diff --git a/src/bounce/dynamics/contacts/manifold.cpp b/src/bounce/dynamics/contacts/manifold.cpp index bbe0380..fb237db 100644 --- a/src/bounce/dynamics/contacts/manifold.cpp +++ b/src/bounce/dynamics/contacts/manifold.cpp @@ -22,12 +22,15 @@ void b3Manifold::Initialize() { pointCount = 0; tangentImpulse.SetZero(); - motorImpulse = 0.0f; + motorImpulse = scalar(0); + motorSpeed = scalar(0); + tangentSpeed1 = scalar(0); + tangentSpeed2 = scalar(0); for (u32 i = 0; i < B3_MAX_MANIFOLD_POINTS; ++i) { b3ManifoldPoint* p = points + i; - p->normalImpulse = 0.0f; - p->persisting = 0; + p->normalImpulse = scalar(0); + p->persistCount = 0; } } @@ -47,28 +50,28 @@ void b3Manifold::Initialize(const b3Manifold& oldManifold) if (p2->key == p1->key) { p1->normalImpulse = p2->normalImpulse; - p1->persisting = 1; + p1->persistCount = p2->persistCount + 1; break; } } } } -void b3WorldManifoldPoint::Initialize(const b3ManifoldPoint* p, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) +void b3WorldManifoldPoint::Initialize(const b3ManifoldPoint* p, scalar rA, const b3Transform& xfA, scalar rB, const b3Transform& xfB) { - b3Vec3 nA = xfA.rotation * p->localNormal1; + b3Vec3 nA = b3Mul(xfA.rotation, p->localNormal1); b3Vec3 cA = xfA * p->localPoint1; b3Vec3 cB = xfB * p->localPoint2; b3Vec3 pA = cA + rA * nA; b3Vec3 pB = cB - rB * nA; - point = 0.5f * (pA + pB); + point = scalar(0.5) * (pA + pB); normal = nA; separation = b3Dot(cB - cA, nA) - rA - rB; } -void b3WorldManifold::Initialize(const b3Manifold* m, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) +void b3WorldManifold::Initialize(const b3Manifold* m, scalar rA, const b3Transform& xfA, scalar rB, const b3Transform& xfB) { center.SetZero(); normal.SetZero(); @@ -86,7 +89,7 @@ void b3WorldManifold::Initialize(const b3Manifold* m, float32 rA, const b3Transf if (pointCount > 0) { - center /= float32(pointCount); + center /= scalar(pointCount); normal.Normalize(); tangent1 = b3Perp(normal); diff --git a/src/bounce/dynamics/contacts/mesh_contact.cpp b/src/bounce/dynamics/contacts/mesh_contact.cpp index f3aaf99..759d9e9 100644 --- a/src/bounce/dynamics/contacts/mesh_contact.cpp +++ b/src/bounce/dynamics/contacts/mesh_contact.cpp @@ -20,14 +20,27 @@ #include #include #include +#include #include #include -#include #include -#include #include +#include -b3MeshContact::b3MeshContact(b3Shape* shapeA, b3Shape* shapeB) +b3Contact* b3MeshContact::Create(b3Shape* shapeA, b3Shape* shapeB, b3BlockPool* allocator) +{ + void* mem = allocator->Allocate(); + return new (mem) b3MeshContact(shapeA, shapeB); +} + +void b3MeshContact::Destroy(b3Contact* contact, b3BlockPool* allocator) +{ + b3MeshContact* c = (b3MeshContact*)contact; + c->~b3MeshContact(); + allocator->Free(c); +} + +b3MeshContact::b3MeshContact(b3Shape* shapeA, b3Shape* shapeB) : b3Contact(shapeA, shapeB) { m_type = e_meshContact; @@ -38,15 +51,31 @@ b3MeshContact::b3MeshContact(b3Shape* shapeA, b3Shape* shapeB) b3Transform xfA = shapeA->GetBody()->GetTransform(); b3Transform xfB = shapeB->GetBody()->GetTransform(); - b3Transform xf = b3MulT(xfB, xfA); - - // The fat aabb relative to shape B's frame. - b3AABB3 fatAABB; - shapeA->ComputeAABB(&fatAABB, xf); + b3Transform xf = b3MulT(xfA, xfB); + + // The aabb B relative to the mesh frame. + b3AABB fatAABB; + shapeB->ComputeAABB(&fatAABB, xf); + + B3_ASSERT(shapeA->m_type == e_meshShape); + + b3MeshShape* meshShapeA = (b3MeshShape*)shapeA; + + B3_ASSERT(meshShapeA->m_scale.x != scalar(0)); + B3_ASSERT(meshShapeA->m_scale.y != scalar(0)); + B3_ASSERT(meshShapeA->m_scale.z != scalar(0)); + + b3Vec3 inv_scale; + inv_scale.x = scalar(1) / meshShapeA->m_scale.x; + inv_scale.y = scalar(1) / meshShapeA->m_scale.y; + inv_scale.z = scalar(1) / meshShapeA->m_scale.z; + + fatAABB = b3ScaleAABB(fatAABB, inv_scale); + fatAABB.Extend(B3_AABB_EXTENSION); - m_aabbA = fatAABB; - m_aabbMoved = true; + m_aabbB = fatAABB; + m_aabbBMoved = true; // Pre-allocate some indices m_triangleCapacity = 16; @@ -69,29 +98,42 @@ void b3MeshContact::SynchronizeShapes() b3Body* bodyB = shapeB->GetBody(); b3Transform xfB = bodyB->GetTransform(); - b3Sweep* sweepA = &bodyA->m_sweep; - b3Transform xfA0; - xfA0.position = sweepA->worldCenter0; - xfA0.rotation = b3QuatMat33(sweepA->orientation0); - - // Calculate the displacement of body A using its position at the last - // time step and the current position. - b3Vec3 displacement = xfA.position - xfA0.position; + b3Sweep* sweepB = &bodyB->m_sweep; + b3Transform xfB0; + xfB0.translation = sweepB->worldCenter0; + xfB0.rotation = sweepB->orientation0; - // Compute the AABB in the reference frame of shape B. - b3Transform xf = b3MulT(xfB, xfA); - - b3AABB3 aabb; - shapeA->ComputeAABB(&aabb, xf); + // Calculate the displacement of body B using its position at the last + // time step and the current position. + b3Vec3 displacement = xfB.translation - xfB0.translation; + + // Compute the AABB B in the reference frame of the mesh. + b3Transform xf = b3MulT(xfA, xfB); + + b3AABB aabbB; + shapeB->ComputeAABB(&aabbB, xf); + + b3MeshShape* meshShapeA = (b3MeshShape*)shapeA; + + B3_ASSERT(meshShapeA->m_scale.x != scalar(0)); + B3_ASSERT(meshShapeA->m_scale.y != scalar(0)); + B3_ASSERT(meshShapeA->m_scale.z != scalar(0)); + + b3Vec3 inv_scale; + inv_scale.x = scalar(1) / meshShapeA->m_scale.x; + inv_scale.y = scalar(1) / meshShapeA->m_scale.y; + inv_scale.z = scalar(1) / meshShapeA->m_scale.z; + + aabbB = b3ScaleAABB(aabbB, inv_scale); // Update the AABB with the new (transformed) AABB and buffer move. - m_aabbMoved = MoveAABB(aabb, displacement); + m_aabbBMoved = MoveAABB(aabbB, displacement); } -bool b3MeshContact::MoveAABB(const b3AABB3& aabb, const b3Vec3& displacement) +bool b3MeshContact::MoveAABB(const b3AABB& aabb, const b3Vec3& displacement) { // Do nothing if the new AABB is contained in the old AABB. - if (m_aabbA.Contains(aabb)) + if (m_aabbB.Contains(aabb)) { // Do nothing if the new AABB is contained in the old AABB. return false; @@ -100,38 +142,38 @@ bool b3MeshContact::MoveAABB(const b3AABB3& aabb, const b3Vec3& displacement) // Update the AABB with a fat and motion predicted AABB. // Extend the new (original) AABB. - b3AABB3 fatAABB = aabb; + b3AABB fatAABB = aabb; fatAABB.Extend(B3_AABB_EXTENSION); - if (displacement.x < 0.0f) + if (displacement.x < scalar(0)) { - fatAABB.m_lower.x += B3_AABB_MULTIPLIER * displacement.x; + fatAABB.lowerBound.x += B3_AABB_MULTIPLIER * displacement.x; } else { - fatAABB.m_upper.x += B3_AABB_MULTIPLIER * displacement.x; + fatAABB.upperBound.x += B3_AABB_MULTIPLIER * displacement.x; } - if (displacement.y < 0.0f) + if (displacement.y < scalar(0)) { - fatAABB.m_lower.y += B3_AABB_MULTIPLIER * displacement.y; + fatAABB.lowerBound.y += B3_AABB_MULTIPLIER * displacement.y; } else { - fatAABB.m_upper.y += B3_AABB_MULTIPLIER * displacement.y; + fatAABB.upperBound.y += B3_AABB_MULTIPLIER * displacement.y; } - if (displacement.z < 0.0f) + if (displacement.z < scalar(0)) { - fatAABB.m_lower.z += B3_AABB_MULTIPLIER * displacement.z; + fatAABB.lowerBound.z += B3_AABB_MULTIPLIER * displacement.z; } else { - fatAABB.m_upper.z += B3_AABB_MULTIPLIER * displacement.z; + fatAABB.upperBound.z += B3_AABB_MULTIPLIER * displacement.z; } // Update proxy with the extented AABB. - m_aabbA = fatAABB; + m_aabbB = fatAABB; // Notify the proxy has moved. return true; @@ -141,7 +183,7 @@ void b3MeshContact::FindNewPairs() { // Reuse the overlapping buffer if the AABB didn't move // significantly. - if (m_aabbMoved == false) + if (m_aabbBMoved == false) { return; } @@ -149,21 +191,21 @@ void b3MeshContact::FindNewPairs() // Clear the index cache. m_triangleCount = 0; - const b3MeshShape* meshShapeB = (b3MeshShape*)GetShapeB(); - const b3Mesh* meshB = meshShapeB->m_mesh; - const b3StaticTree* tree = &meshB->tree; + const b3MeshShape* meshShapeA = (b3MeshShape*)GetShapeA(); + const b3Mesh* meshA = meshShapeA->m_mesh; + const b3StaticTree* treeA = &meshA->tree; // Query and update the overlapping buffer. - tree->QueryAABB(this, m_aabbA); + treeA->QueryAABB(this, m_aabbB); } bool b3MeshContact::Report(u32 proxyId) { - b3MeshShape* meshShapeB = (b3MeshShape*)GetShapeB(); - const b3Mesh* meshB = meshShapeB->m_mesh; - const b3StaticTree* treeB = &meshB->tree; + b3MeshShape* meshShapeA = (b3MeshShape*)GetShapeA(); + const b3Mesh* meshA = meshShapeA->m_mesh; + const b3StaticTree* treeA = &meshA->tree; - u32 triangleIndex = treeB->GetUserData(proxyId); + u32 triangleIndex = treeA->GetUserData(proxyId); // Add the triangle to the overlapping buffer. if (m_triangleCount == m_triangleCapacity) @@ -175,13 +217,13 @@ bool b3MeshContact::Report(u32 proxyId) b3Free(oldElements); } - B3_ASSERT(m_triangleCount < m_triangleCapacity); + B3_ASSERT(m_triangleCount < m_triangleCapacity); b3TriangleCache* cache = m_triangles + m_triangleCount; cache->index = triangleIndex; cache->cache.simplexCache.count = 0; cache->cache.featureCache.m_featurePair.state = b3SATCacheType::e_empty; - + ++m_triangleCount; // Keep looking for triangles. @@ -193,7 +235,6 @@ bool b3MeshContact::TestOverlap() b3Shape* shapeA = GetShapeA(); b3Body* bodyA = shapeA->GetBody(); b3Transform xfA = bodyA->GetTransform(); - u32 indexA = 0; b3Shape* shapeB = GetShapeB(); b3Body* bodyB = shapeB->GetBody(); @@ -203,8 +244,8 @@ bool b3MeshContact::TestOverlap() for (u32 i = 0; i < m_triangleCount; ++i) { b3TriangleCache* cache = m_triangles + i; - u32 indexB = cache->index; - bool overlap = b3TestOverlap(xfA, indexA, shapeA, xfB, indexB, shapeB, &cache->cache); + u32 indexA = cache->index; + bool overlap = b3TestOverlap(xfA, indexA, shapeA, xfB, 0, shapeB, &cache->cache); if (overlap == true) { return true; @@ -219,12 +260,12 @@ void b3MeshContact::Collide() B3_ASSERT(m_manifoldCount == 0); b3Shape* shapeA = GetShapeA(); + b3MeshShape* meshShapeA = (b3MeshShape*)shapeA; b3Body* bodyA = shapeA->GetBody(); b3Transform xfA = bodyA->GetTransform(); b3Shape* shapeB = GetShapeB(); b3Body* bodyB = shapeB->GetBody(); - b3MeshShape* meshShapeB = (b3MeshShape*)shapeB; b3Transform xfB = bodyB->GetTransform(); b3World* world = bodyA->GetWorld(); @@ -234,42 +275,65 @@ void b3MeshContact::Collide() b3Manifold* tempManifolds = (b3Manifold*)allocator->Allocate(m_triangleCount * sizeof(b3Manifold)); u32 tempCount = 0; - const b3Mesh* meshB = meshShapeB->m_mesh; + const b3Mesh* meshA = meshShapeA->m_mesh; for (u32 i = 0; i < m_triangleCount; ++i) { b3TriangleCache* triangleCache = m_triangles + i; u32 triangleIndex = triangleCache->index; - b3Triangle* triangle = meshB->triangles + triangleIndex; + b3MeshTriangle* triangle = meshA->triangles + triangleIndex; + b3MeshTriangleWings* triangleWings = meshA->triangleWings + triangleIndex; - b3Vec3 v1 = meshB->vertices[triangle->v1]; - b3Vec3 v2 = meshB->vertices[triangle->v2]; - b3Vec3 v3 = meshB->vertices[triangle->v3]; + u32 u1 = triangleWings->u1; + u32 u2 = triangleWings->u2; + u32 u3 = triangleWings->u3; - b3TriangleHull hullB(v1, v2, v3); + b3Vec3 A = b3MulCW(meshShapeA->m_scale, meshA->vertices[triangle->v1]); + b3Vec3 B = b3MulCW(meshShapeA->m_scale, meshA->vertices[triangle->v2]); + b3Vec3 C = b3MulCW(meshShapeA->m_scale, meshA->vertices[triangle->v3]); + + b3TriangleShape triangleShapeA; + triangleShapeA.m_body = bodyA; + triangleShapeA.m_vertex1 = A; + triangleShapeA.m_vertex2 = B; + triangleShapeA.m_vertex3 = C; + triangleShapeA.m_radius = B3_HULL_RADIUS; + + if (u1 != B3_NULL_VERTEX) + { + triangleShapeA.m_hasE1Vertex = true; + triangleShapeA.m_e1Vertex = b3MulCW(meshShapeA->m_scale, meshA->vertices[u1]); + } + + if (u2 != B3_NULL_VERTEX) + { + triangleShapeA.m_hasE2Vertex = true; + triangleShapeA.m_e2Vertex = b3MulCW(meshShapeA->m_scale, meshA->vertices[u2]); + } + + if (u3 != B3_NULL_VERTEX) + { + triangleShapeA.m_hasE3Vertex = true; + triangleShapeA.m_e3Vertex = b3MulCW(meshShapeA->m_scale, meshA->vertices[u3]); + } - b3HullShape hullShapeB; - hullShapeB.m_body = bodyB; - hullShapeB.m_hull = &hullB; - hullShapeB.m_radius = B3_HULL_RADIUS; - b3Manifold* manifold = tempManifolds + tempCount; manifold->Initialize(); - - b3CollideShapeAndShape(*manifold, xfA, shapeA, xfB, &hullShapeB, &triangleCache->cache); - + + b3CollideShapeAndShape(*manifold, xfA, &triangleShapeA, xfB, shapeB, &triangleCache->cache); + for (u32 j = 0; j < manifold->pointCount; ++j) { manifold->points[j].key.triangleKey = triangleIndex; } - + ++tempCount; } // Send contact manifolds for clustering. This is an important optimization. B3_ASSERT(m_manifoldCount == 0); - + b3ClusterSolver clusterSolver; - clusterSolver.Run(m_stackManifolds, m_manifoldCount, tempManifolds, tempCount, xfA, shapeA->m_radius, xfB, B3_HULL_RADIUS); - + clusterSolver.Run(m_stackManifolds, m_manifoldCount, tempManifolds, tempCount, xfA, B3_HULL_RADIUS, xfB, shapeB->m_radius); + allocator->Free(tempManifolds); } \ No newline at end of file diff --git a/src/bounce/dynamics/draw_world.cpp b/src/bounce/dynamics/draw_world.cpp index 5bd5477..fb8e60a 100644 --- a/src/bounce/dynamics/draw_world.cpp +++ b/src/bounce/dynamics/draw_world.cpp @@ -18,13 +18,13 @@ #include -const b3Color b3Color_black(0.0f, 0.0f, 0.0f); -const b3Color b3Color_white(1.0f, 1.0f, 1.0f); -const b3Color b3Color_red(1.0f, 0.0f, 0.0f); -const b3Color b3Color_green(0.0f, 1.0f, 0.0f); -const b3Color b3Color_blue(0.0f, 0.0f, 1.0f); -const b3Color b3Color_yellow(1.0f, 1.0f, 0.0f); -const b3Color b3Color_pink(1.0f, 0.0f, 1.0f); +const b3Color b3Color_black(scalar(0), scalar(0), scalar(0)); +const b3Color b3Color_white(scalar(1), scalar(1), scalar(1)); +const b3Color b3Color_red(scalar(1), scalar(0), scalar(0)); +const b3Color b3Color_green(scalar(0), scalar(1), scalar(0)); +const b3Color b3Color_blue(scalar(0), scalar(0), scalar(1)); +const b3Color b3Color_yellow(scalar(1), scalar(1), scalar(0)); +const b3Color b3Color_pink(scalar(1), scalar(0), scalar(1)); b3Draw* b3Draw_draw(nullptr); @@ -38,8 +38,9 @@ void b3World::Draw() const { for (b3Body* b = m_bodyList.m_head; b; b = b->m_next) { - b3Transform xf = b->m_xf; - xf.position = b->m_sweep.worldCenter; + b3Transform xf; + xf.rotation = b->m_sweep.orientation; + xf.translation = b->m_sweep.worldCenter; b3Draw_draw->DrawTransform(xf); } } @@ -62,7 +63,7 @@ void b3World::Draw() const { for (b3Shape* s = b->m_shapeList.m_head; s; s = s->m_next) { - const b3AABB3& aabb = m_contactMan.m_broadPhase.GetAABB(s->m_broadPhaseID); + const b3AABB& aabb = m_contactMan.m_broadPhase.GetAABB(s->m_broadPhaseID); b3Draw_draw->DrawAABB(aabb, b3Color_pink); } } @@ -103,7 +104,7 @@ void b3World::Draw() const if (flags & b3Draw::e_contactPointsFlag) { - b3Draw_draw->DrawPoint(p, 4.0f, mp->persisting ? b3Color_green : b3Color_red); + b3Draw_draw->DrawPoint(p, scalar(4), mp->persistCount > 0 ? b3Color_green : b3Color_red); } if (flags & b3Draw::e_contactNormalsFlag) @@ -150,19 +151,30 @@ void b3World::DrawShape(const b3Transform& xf, const b3Shape* shape, const b3Col { const b3SphereShape* sphere = (b3SphereShape*)shape; b3Vec3 p = xf * sphere->m_center; - b3Draw_draw->DrawPoint(p, 4.0f, color); + b3Draw_draw->DrawPoint(p, scalar(4), color); break; } case e_capsuleShape: { const b3CapsuleShape* capsule = (b3CapsuleShape*)shape; - b3Vec3 p1 = xf * capsule->m_centers[0]; - b3Vec3 p2 = xf * capsule->m_centers[1]; - b3Draw_draw->DrawPoint(p1, 4.0f, color); - b3Draw_draw->DrawPoint(p2, 4.0f, color); + b3Vec3 p1 = xf * capsule->m_vertex1; + b3Vec3 p2 = xf * capsule->m_vertex2; + b3Draw_draw->DrawPoint(p1, scalar(4), color); + b3Draw_draw->DrawPoint(p2, scalar(4), color); b3Draw_draw->DrawSegment(p1, p2, color); break; } + case e_triangleShape: + { + const b3TriangleShape* triangle = (b3TriangleShape*)shape; + b3Vec3 v1 = xf * triangle->m_vertex1; + b3Vec3 v2 = xf * triangle->m_vertex2; + b3Vec3 v3 = xf * triangle->m_vertex3; + b3Vec3 n = b3Cross(v2 - v1, v3 - v1); + n.Normalize(); + b3Draw_draw->DrawTriangle(v1, v2, v3, color); + break; + } case e_hullShape: { const b3HullShape* hs = (b3HullShape*)shape; @@ -185,16 +197,23 @@ void b3World::DrawShape(const b3Transform& xf, const b3Shape* shape, const b3Col const b3Mesh* mesh = ms->m_mesh; for (u32 i = 0; i < mesh->triangleCount; ++i) { - const b3Triangle* t = mesh->triangles + i; + const b3MeshTriangle* t = mesh->triangles + i; - b3Vec3 p1 = xf * mesh->vertices[t->v1]; - b3Vec3 p2 = xf * mesh->vertices[t->v2]; - b3Vec3 p3 = xf * mesh->vertices[t->v3]; + b3Vec3 p1 = xf * b3MulCW(ms->m_scale, mesh->vertices[t->v1]); + b3Vec3 p2 = xf * b3MulCW(ms->m_scale, mesh->vertices[t->v2]); + b3Vec3 p3 = xf * b3MulCW(ms->m_scale, mesh->vertices[t->v3]); b3Draw_draw->DrawTriangle(p1, p2, p3, color); } break; } + case e_sdfShape: + { + const b3SDFShape* ms = (b3SDFShape*)shape; + const b3SDF* sdf = ms->m_sdf; + + break; + } default: { break; @@ -209,19 +228,19 @@ void b3World::DrawSolid() const b3Color c; if (b->IsAwake() == false) { - c = b3Color(0.5f, 0.25f, 0.25f, 1.0f); + c = b3Color(scalar(0.5), scalar(0.25), scalar(0.25), scalar(1)); } else if (b->GetType() == e_staticBody) { - c = b3Color(0.5f, 0.5f, 0.5f, 1.0f); + c = b3Color(scalar(0.5), scalar(0.5), scalar(0.5), scalar(1)); } else if (b->GetType() == e_dynamicBody) { - c = b3Color(1.0f, 0.5f, 0.5f, 1.0f); + c = b3Color(scalar(1), scalar(0.5), scalar(0.5), scalar(1)); } else { - c = b3Color(0.5f, 0.5f, 1.0f, 1.0f); + c = b3Color(scalar(0.5), scalar(0.5), scalar(1), scalar(1)); } b3Transform xf = b->GetTransform(); @@ -250,13 +269,29 @@ void b3World::DrawSolidShape(const b3Transform& xf, const b3Shape* shape, const { const b3CapsuleShape* capsule = (b3CapsuleShape*)shape; - b3Vec3 c1 = xf * capsule->m_centers[0]; - b3Vec3 c2 = xf * capsule->m_centers[1]; + b3Vec3 c1 = xf * capsule->m_vertex1; + b3Vec3 c2 = xf * capsule->m_vertex2; b3Draw_draw->DrawSolidCapsule(c1, c2, capsule->m_radius, xf.rotation, color); break; } + case e_triangleShape: + { + const b3TriangleShape* triangle = (b3TriangleShape*)shape; + + b3Vec3 v1 = xf * triangle->m_vertex1; + b3Vec3 v2 = xf * triangle->m_vertex2; + b3Vec3 v3 = xf * triangle->m_vertex3; + + b3Vec3 n = b3Cross(v2 - v1, v3 - v1); + n.Normalize(); + + b3Draw_draw->DrawSolidTriangle(-n, v3, v2, v1, color); + b3Draw_draw->DrawSolidTriangle(n, v1, v2, v3, color); + + break; + } case e_hullShape: { const b3HullShape* hullShape = (b3HullShape*)shape; @@ -268,7 +303,7 @@ void b3World::DrawSolidShape(const b3Transform& xf, const b3Shape* shape, const const b3Face* face = hull->GetFace(i); const b3HalfEdge* begin = hull->GetEdge(face->edge); - b3Vec3 n = xf.rotation * hull->planes[i].normal; + b3Vec3 n = b3Mul(xf.rotation, hull->planes[i].normal); const b3HalfEdge* edge = hull->GetEdge(begin->next); do @@ -297,11 +332,11 @@ void b3World::DrawSolidShape(const b3Transform& xf, const b3Shape* shape, const const b3Mesh* mesh = meshShape->m_mesh; for (u32 i = 0; i < mesh->triangleCount; ++i) { - const b3Triangle* t = mesh->triangles + i; + const b3MeshTriangle* t = mesh->triangles + i; - b3Vec3 p1 = xf * mesh->vertices[t->v1]; - b3Vec3 p2 = xf * mesh->vertices[t->v2]; - b3Vec3 p3 = xf * mesh->vertices[t->v3]; + b3Vec3 p1 = xf * b3MulCW(meshShape->m_scale, mesh->vertices[t->v1]); + b3Vec3 p2 = xf * b3MulCW(meshShape->m_scale, mesh->vertices[t->v2]); + b3Vec3 p3 = xf * b3MulCW(meshShape->m_scale, mesh->vertices[t->v3]); b3Vec3 n1 = b3Cross(p2 - p1, p3 - p1); n1.Normalize(); diff --git a/src/bounce/dynamics/island.cpp b/src/bounce/dynamics/island.cpp index d0e831a..0bf9d27 100644 --- a/src/bounce/dynamics/island.cpp +++ b/src/bounce/dynamics/island.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -25,9 +26,10 @@ #include #include -b3Island::b3Island(b3StackAllocator* allocator, u32 bodyCapacity, u32 contactCapacity, u32 jointCapacity) +b3Island::b3Island(b3StackAllocator* allocator, u32 bodyCapacity, u32 contactCapacity, u32 jointCapacity, b3ContactListener* listener) { m_allocator = allocator; + m_listener = listener; m_bodyCapacity = bodyCapacity; m_contactCapacity = contactCapacity; m_jointCapacity = jointCapacity; @@ -84,11 +86,11 @@ void b3Island::Add(b3Joint* j) ++m_jointCount; } -// Box2D -static B3_FORCE_INLINE b3Vec3 b3SolveGyro(const b3Quat& q, const b3Mat33& Ib, const b3Vec3& w1, float32 h) +// Numerical Methods (Erin, p60) +static B3_FORCE_INLINE b3Vec3 b3SolveGyro(const b3Quat& q, const b3Mat33& Ib, const b3Vec3& w1, scalar h) { // Convert angular velocity to body coordinates - b3Vec3 w1b = b3MulT(q, w1); + b3Vec3 w1b = b3MulC(q, w1); // Jacobian of f b3Mat33 J = Ib + h * (b3Skew(w1b) * Ib - b3Skew(Ib * w1b)); @@ -106,9 +108,9 @@ static B3_FORCE_INLINE b3Vec3 b3SolveGyro(const b3Quat& q, const b3Mat33& Ib, co return w2; } -void b3Island::Solve(const b3Vec3& gravity, float32 dt, u32 velocityIterations, u32 positionIterations, u32 flags) +void b3Island::Solve(const b3Vec3& gravity, scalar dt, u32 velocityIterations, u32 positionIterations, u32 flags) { - float32 h = dt; + scalar h = dt; // 1. Integrate velocities for (u32 i = 0; i < m_bodyCount; ++i) @@ -127,34 +129,21 @@ void b3Island::Solve(const b3Vec3& gravity, float32 dt, u32 velocityIterations, if (b->m_type == e_dynamicBody) { // Integrate forces - v += h * (b->m_gravityScale * gravity + b->m_invMass * b->m_force); + v += h * (b3MulCW(b->m_gravityScale, gravity) + b->m_invMass * b->m_force); // Clear forces b->m_force.SetZero(); // Integrate torques - - // Superposition Principle - // w2 - w1 = dw1 + dw2 - // w2 - w1 = h * I^1 * bt + h * I^1 * -gt - // w2 = w1 + dw1 + dw2 - - // Explicit Euler on current inertia and applied torque - // w2 = w1 + h * I1^1 * bt1 b3Vec3 dw1 = h * b->m_worldInvI * b->m_torque; + // "Numerical Methods", (Erin, p71) // Implicit Euler on next inertia and angular velocity - // w2 = w1 - h * I2^1 * cross(w2, I2 * w2) - // w2 - w1 = -I2^1 * h * cross(w2, I2 * w2) - // I2 * (w2 - w1) = -h * cross(w2, I2 * w2) - // I2 * (w2 - w1) + h * cross(w2, I2 * w2) = 0 - // Toss out I2 from f using local I2 (constant) and local w1 - // to remove its time dependency. b3Vec3 w2 = b3SolveGyro(q, b->m_I, w, h); b3Vec3 dw2 = w2 - w; w += dw1 + dw2; - + // Clear torques b->m_torque.SetZero(); @@ -165,8 +154,13 @@ void b3Island::Solve(const b3Vec3& gravity, float32 dt, u32 velocityIterations, // v2 = exp(-c * dt) * v1 // Padé approximation: // 1 / (1 + c * dt) - v *= 1.0f / (1.0f + h * b->m_linearDamping); - w *= 1.0f / (1.0f + h * b->m_angularDamping); + v.x *= scalar(1) / (scalar(1) + h * b->m_linearDamping.x); + v.y *= scalar(1) / (scalar(1) + h * b->m_linearDamping.y); + v.z *= scalar(1) / (scalar(1) + h * b->m_linearDamping.z); + + w.x *= scalar(1) / (scalar(1) + h * b->m_angularDamping.x); + w.y *= scalar(1) / (scalar(1) + h * b->m_angularDamping.y); + w.z *= scalar(1) / (scalar(1) + h * b->m_angularDamping.z); } m_velocities[i].v = v; @@ -241,25 +235,28 @@ void b3Island::Solve(const b3Vec3& gravity, float32 dt, u32 velocityIterations, b3Vec3 w = m_velocities[i].w; b3Mat33 invI = m_invInertias[i]; - // Prevent numerical instability due to large velocity changes. - b3Vec3 translation = h * v; - if (b3Dot(translation, translation) > B3_MAX_TRANSLATION_SQUARED) + if (b->m_type != e_staticBody) { - float32 ratio = B3_MAX_TRANSLATION / b3Length(translation); - v *= ratio; - } + // Prevent numerical instability due to large velocity changes. + b3Vec3 translation = h * v; + if (b3Dot(translation, translation) > B3_MAX_TRANSLATION_SQUARED) + { + scalar ratio = B3_MAX_TRANSLATION / b3Length(translation); + v *= ratio; + } - b3Vec3 rotation = h * w; - if (b3Dot(rotation, rotation) > B3_MAX_ROTATION_SQUARED) - { - float32 ratio = B3_MAX_ROTATION / b3Length(rotation); - w *= ratio; - } + b3Vec3 rotation = h * w; + if (b3Dot(rotation, rotation) > B3_MAX_ROTATION_SQUARED) + { + scalar ratio = B3_MAX_ROTATION / b3Length(rotation); + w *= ratio; + } - // Integrate - x += h * v; - q = b3Integrate(q, w, h); - invI = b3RotateToFrame(b->m_invI, q); + // Integrate + x += h * v; + q = b3Integrate(q, w, h); + invI = b3RotateToFrame(b->m_invI, q); + } m_positions[i].x = x; m_positions[i].q = q; @@ -269,10 +266,11 @@ void b3Island::Solve(const b3Vec3& gravity, float32 dt, u32 velocityIterations, } // 5. Solve position constraints + bool positionsSolved = false; + { B3_PROFILE("Solve Position Constraints"); - bool positionsSolved = false; for (u32 i = 0; i < positionIterations; ++i) { bool contactsSolved = contactSolver.SolvePositionConstraints(); @@ -292,7 +290,6 @@ void b3Island::Solve(const b3Vec3& gravity, float32 dt, u32 velocityIterations, b3Body* b = m_bodies[i]; b->m_sweep.worldCenter = m_positions[i].x; b->m_sweep.orientation = m_positions[i].q; - b->m_sweep.orientation.Normalize(); b->m_linearVelocity = m_velocities[i].v; b->m_angularVelocity = m_velocities[i].w; b->m_worldInvI = m_invInertias[i]; @@ -300,10 +297,14 @@ void b3Island::Solve(const b3Vec3& gravity, float32 dt, u32 velocityIterations, b->SynchronizeTransform(); } + // Post solve callback report + Report(); + // 7. Put bodies under unconsiderable motion to sleep if (flags & e_sleepBit) { - float32 minSleepTime = B3_MAX_FLOAT; + scalar minSleepTime = B3_MAX_SCALAR; + for (u32 i = 0; i < m_bodyCount; ++i) { b3Body* b = m_bodies[i]; @@ -313,24 +314,26 @@ void b3Island::Solve(const b3Vec3& gravity, float32 dt, u32 velocityIterations, } // Compute the linear and angular speed of the body. - float32 sqrLinVel = b3Dot(b->m_linearVelocity, b->m_linearVelocity); - float32 sqrAngVel = b3Dot(b->m_angularVelocity, b->m_angularVelocity); + scalar sqrLinVel = b3Dot(b->m_linearVelocity, b->m_linearVelocity); + scalar sqrAngVel = b3Dot(b->m_angularVelocity, b->m_angularVelocity); - if (sqrLinVel > B3_SLEEP_LINEAR_TOL || sqrAngVel > B3_SLEEP_ANGULAR_TOL) + if (b->IsSleepingAllowed() == false || + sqrLinVel > b->m_linearSleepTolerance * b->m_linearSleepTolerance || + sqrAngVel > b->m_angularSleepTolerance * b->m_angularSleepTolerance) { - minSleepTime = 0.0f; - b->m_sleepTime = 0.0f; + b->m_sleepTime = scalar(0); } else { b->m_sleepTime += h; - minSleepTime = b3Min(minSleepTime, b->m_sleepTime); } + + minSleepTime = b3Min(minSleepTime, b->m_sleepTime); } // Put the island to sleep so long as the minimum found sleep time // is below the threshold. - if (minSleepTime >= B3_TIME_TO_SLEEP) + if (minSleepTime >= B3_TIME_TO_SLEEP && positionsSolved) { for (u32 i = 0; i < m_bodyCount; ++i) { @@ -339,3 +342,16 @@ void b3Island::Solve(const b3Vec3& gravity, float32 dt, u32 velocityIterations, } } } + +void b3Island::Report() +{ + if (m_listener == nullptr) + { + return; + } + + for (u32 i = 0; i < m_contactCount; ++i) + { + m_listener->PostSolve(m_contacts[i]); + } +} \ No newline at end of file diff --git a/src/bounce/dynamics/joint_manager.cpp b/src/bounce/dynamics/joint_manager.cpp index fb29016..d130f48 100644 --- a/src/bounce/dynamics/joint_manager.cpp +++ b/src/bounce/dynamics/joint_manager.cpp @@ -33,7 +33,7 @@ b3Joint* b3JointManager::Create(const b3JointDef* def) B3_ASSERT(bodyA != bodyB); if (bodyA == bodyB) { - return NULL; + return nullptr; } // Allocate the new joint. diff --git a/src/bounce/dynamics/joints/cone_joint.cpp b/src/bounce/dynamics/joints/cone_joint.cpp index fcf54c2..df2b419 100644 --- a/src/bounce/dynamics/joints/cone_joint.cpp +++ b/src/bounce/dynamics/joints/cone_joint.cpp @@ -32,32 +32,52 @@ // C = angle / 2 - atan2( norm(u2 x u1), dot(u2, u1) ) > 0 void b3ConeJointDef::Initialize(b3Body* bA, b3Body* bB, - const b3Vec3& axis, const b3Vec3& anchor, float32 angle) + const b3Vec3& axis, const b3Vec3& anchor, scalar angle) { bodyA = bA; bodyB = bB; - b3Transform xf; - xf.rotation.y = axis; - xf.rotation.z = b3Perp(axis); - xf.rotation.x = b3Cross(xf.rotation.z, xf.rotation.y); - xf.position = anchor; + b3Mat33 rotation; + rotation.x = axis; + b3ComputeBasis(rotation.x, rotation.y, rotation.z); + + b3Quat q = b3Mat33Quat(rotation); + + localAnchorA = bodyA->GetLocalPoint(anchor); + localRotationA = bodyA->GetLocalFrame(q); + + localAnchorB = bodyB->GetLocalPoint(anchor); + localRotationB = bodyB->GetLocalFrame(q); + + referenceRotation.SetIdentity(); - localFrameA = bodyA->GetLocalFrame(xf); - localFrameB = bodyB->GetLocalFrame(xf); coneAngle = angle; } b3ConeJoint::b3ConeJoint(const b3ConeJointDef* def) { m_type = e_coneJoint; - m_localFrameA = def->localFrameA; - m_localFrameB = def->localFrameB; - m_enableLimit = def->enableLimit; + m_localAnchorA = def->localAnchorA; + m_localRotationA = def->localRotationA; + m_localAnchorB = def->localAnchorB; + m_localRotationB = def->localRotationB; + m_referenceRotation = def->referenceRotation; + + m_enableConeLimit = def->enableConeLimit; m_coneAngle = def->coneAngle; - m_limitState = e_inactiveLimit; - m_limitImpulse = 0.0f; - m_limitAxis.SetZero(); + + m_enableTwistLimit = def->enableTwistLimit; + m_lowerAngle = def->lowerAngle; + m_upperAngle = def->upperAngle; + + m_coneState = e_inactiveLimit; + m_coneImpulse = scalar(0); + m_coneAxis.SetZero(); + + m_twistState = e_inactiveLimit; + m_twistImpulse = scalar(0); + m_twistAxis.SetZero(); + m_impulse.SetZero(); } @@ -87,13 +107,16 @@ void b3ConeJoint::InitializeConstraints(const b3SolverData* data) b3Vec3 xB = data->positions[m_indexB].x; b3Quat qB = data->positions[m_indexB].q; - b3Transform xfA = m_bodyA->GetWorldFrame(m_localFrameA); - b3Transform xfB = m_bodyB->GetWorldFrame(m_localFrameB); + b3Quat fA = qA * m_localRotationA; + b3Quat fB = qB * m_localRotationB; - // Add point-to-point constraint. + b3Mat33 RfA = b3QuatMat33(fA); + b3Mat33 RfB = b3QuatMat33(fB); + + // Linear constraint. { - m_rA = b3Mul(qA, m_localFrameA.position - m_localCenterA); - m_rB = b3Mul(qB, m_localFrameB.position - m_localCenterB); + m_rA = b3Mul(qA, m_localAnchorA - m_localCenterA); + m_rB = b3Mul(qB, m_localAnchorB - m_localCenterB); // Compute effective mass matrix. b3Mat33 M = b3Diagonal(m_mA + m_mB); @@ -104,38 +127,92 @@ void b3ConeJoint::InitializeConstraints(const b3SolverData* data) m_mass = M + RA * m_iA * RAT + RB * m_iB * RBT; } - // Add limit constraint. - if (m_enableLimit) + // Cone limit constraint. + if (m_enableConeLimit) { - b3Vec3 u1 = xfA.rotation.y; - b3Vec3 u2 = xfB.rotation.y; + b3Vec3 u1 = RfA.x; + b3Vec3 u2 = RfB.x; - m_limitAxis = b3Cross(u2, u1); + m_coneAxis = b3Cross(u2, u1); - float32 mass = b3Dot((m_iA + m_iB) * m_limitAxis, m_limitAxis); - m_limitMass = mass > 0.0f ? 1.0f / mass : 0.0f; + scalar mass = b3Dot((m_iA + m_iB) * m_coneAxis, m_coneAxis); + m_coneMass = mass > scalar(0) ? scalar(1) / mass : scalar(0); // C = cone / 2 - angle >= 0 - float32 cosine = b3Dot(u2, u1); - float32 sine = b3Length(m_limitAxis); - float32 angle = atan2(sine, cosine); - if (0.5f * m_coneAngle < angle) + scalar cosine = b3Dot(u2, u1); + scalar sine = b3Length(m_coneAxis); + scalar angle = atan2(sine, cosine); + if (scalar(0.5) * m_coneAngle < angle) { - if (m_limitState != e_atLowerLimit) + if (m_coneState != e_atLowerLimit) { - m_limitState = e_atLowerLimit; - m_limitImpulse = 0.0f; + m_coneState = e_atLowerLimit; + m_coneImpulse = scalar(0); } } else { - m_limitState = e_inactiveLimit; - m_limitImpulse = 0.0f; + m_coneState = e_inactiveLimit; + m_coneImpulse = scalar(0); } } else { - m_limitState = e_inactiveLimit; + m_coneState = e_inactiveLimit; + } + + // Twist limit constraint + if (m_enableTwistLimit) + { + // Jacobian + m_twistAxis = RfA.x; + + // Compute effective mass + m_twistMass = b3Dot((m_iA + m_iB) * m_twistAxis, m_twistAxis); + if (m_twistMass > scalar(0)) + { + m_twistMass = scalar(1) / m_twistMass; + } + + // Joint rotation + b3Quat q = b3Conjugate(m_referenceRotation) * b3Conjugate(fA) * fB; + + // Joint angle + scalar angle = scalar(2) * atan2(q.v.x, q.s); + + if (b3Abs(m_upperAngle - m_lowerAngle) < scalar(2) * B3_ANGULAR_SLOP) + { + if (m_twistState != e_equalLimits) + { + m_twistState = e_equalLimits; + m_twistImpulse = scalar(0); + } + } + else if (angle <= m_lowerAngle) + { + if (m_twistState != e_atLowerLimit) + { + m_twistState = e_atLowerLimit; + m_twistImpulse = scalar(0); + } + } + else if (angle >= m_upperAngle) + { + if (m_twistState != e_atUpperLimit) + { + m_twistState = e_atUpperLimit; + m_twistImpulse = scalar(0); + } + } + else + { + m_twistState = e_inactiveLimit; + m_twistImpulse = scalar(0); + } + } + else + { + m_twistState = e_inactiveLimit; } } @@ -154,11 +231,20 @@ void b3ConeJoint::WarmStart(const b3SolverData* data) wB += m_iB * b3Cross(m_rB, m_impulse); } - if (m_enableLimit && m_limitState != e_inactiveLimit) + if (m_enableConeLimit && m_coneState != e_inactiveLimit) { - b3Vec3 P = m_limitImpulse * m_limitAxis; - wA -= m_iA * P; - wB += m_iB * P; + b3Vec3 L = m_coneImpulse * m_coneAxis; + + wA -= m_iA * L; + wB += m_iB * L; + } + + if (m_enableTwistLimit && m_twistState != e_inactiveLimit) + { + b3Vec3 L = m_twistImpulse * m_twistAxis; + + wA -= m_iA * L; + wB += m_iB * L; } data->velocities[m_indexA].v = vA; @@ -174,7 +260,7 @@ void b3ConeJoint::SolveVelocityConstraints(const b3SolverData* data) b3Vec3 vB = data->velocities[m_indexB].v; b3Vec3 wB = data->velocities[m_indexB].w; - // Solve point-to-point constraint. + // Solve linear constraint. { b3Vec3 Cdot = vB + b3Cross(wB, m_rB) - vA - b3Cross(wA, m_rA); b3Vec3 P = m_mass.Solve(-Cdot); @@ -188,16 +274,45 @@ void b3ConeJoint::SolveVelocityConstraints(const b3SolverData* data) wB += m_iB * b3Cross(m_rB, P); } - // Solve limit constraint. - if (m_enableLimit && m_limitState != e_inactiveLimit) + // Solve cone constraint. + if (m_enableConeLimit && m_coneState != e_inactiveLimit) { - float32 Cdot = b3Dot(m_limitAxis, wB - wA); - float32 impulse = -m_limitMass * Cdot; - float32 oldImpulse = m_limitImpulse; - m_limitImpulse = b3Max(m_limitImpulse + impulse, 0.0f); - impulse = m_limitImpulse - oldImpulse; + scalar Cdot = b3Dot(m_coneAxis, wB - wA); + scalar impulse = -m_coneMass * Cdot; + scalar oldImpulse = m_coneImpulse; + m_coneImpulse = b3Max(m_coneImpulse + impulse, scalar(0)); + impulse = m_coneImpulse - oldImpulse; - b3Vec3 P = impulse * m_limitAxis; + b3Vec3 P = impulse * m_coneAxis; + + wA -= m_iA * P; + wB += m_iB * P; + } + + // Solve twist constraint. + if (m_enableTwistLimit && m_twistState != e_inactiveLimit) + { + scalar Cdot = b3Dot(wB - wA, m_twistAxis); + scalar impulse = -m_twistMass * Cdot; + + if (m_twistState == e_equalLimits) + { + m_twistImpulse += impulse; + } + else if (m_twistState == e_atLowerLimit) + { + scalar oldImpulse = m_twistImpulse; + m_twistImpulse = b3Max(m_twistImpulse + impulse, scalar(0)); + impulse = m_twistImpulse - oldImpulse; + } + else if (m_twistState == e_atUpperLimit) + { + scalar oldImpulse = m_twistImpulse; + m_twistImpulse = b3Min(m_twistImpulse + impulse, scalar(0)); + impulse = m_twistImpulse - oldImpulse; + } + + b3Vec3 P = impulse * m_twistAxis; wA -= m_iA * P; wB += m_iB * P; @@ -218,14 +333,165 @@ bool b3ConeJoint::SolvePositionConstraints(const b3SolverData* data) b3Mat33 iA = data->invInertias[m_indexA]; b3Mat33 iB = data->invInertias[m_indexB]; - float32 mA = m_mA; - float32 mB = m_mB; + scalar mA = m_mA; + scalar mB = m_mB; - // Solve point-to-point constraint. - float32 linearError = 0.0f; + // Solve limit constraints. + scalar limitError = scalar(0); + if (m_enableConeLimit || m_enableTwistLimit) { - b3Vec3 rA = b3Mul(qA, m_localFrameA.position - m_localCenterA); - b3Vec3 rB = b3Mul(qB, m_localFrameB.position - m_localCenterB); + b3Quat fA = qA * m_localRotationA; + b3Quat fB = qB * m_localRotationB; + + b3Quat q1 = b3Conjugate(m_referenceRotation) * b3Conjugate(fA) * fB; + + b3Quat q = q1; + + // Make sure the scalar part is positive. + if (q.s < scalar(0)) + { + // Negating still represent the same orientation. + q = -q; + } + + // Twist + scalar x, xs; + + // Swing + scalar y, z; + + scalar s = b3Sqrt(q.v.x * q.v.x + q.s * q.s); + if (s < B3_EPSILON) + { + // Swing by 180 degrees is singularity. + // Assume the twist is zero. + x = scalar(0); + xs = scalar(1); + + y = q.v.y; + z = q.v.z; + } + else + { + scalar r = scalar(1) / s; + + x = q1.v.x * r; + xs = q1.s * r; + + y = (q.s * q.v.y - q.v.x * q.v.z) * r; + z = (q.s * q.v.z + q.v.x * q.v.y) * r; + } + + // Solve twist + if (m_enableTwistLimit) + { + // Joint angle + scalar angle = scalar(2) * atan2(q1.v.x, q1.s); + + scalar C = scalar(0); + if (b3Abs(m_upperAngle - m_lowerAngle) < scalar(2) * B3_ANGULAR_SLOP) + { + C = angle - m_lowerAngle; + + // Prevent large corrections + C = b3Clamp(C, -B3_MAX_ANGULAR_CORRECTION, B3_MAX_ANGULAR_CORRECTION); + } + else if (angle <= m_lowerAngle) + { + C = angle - m_lowerAngle; + + // Allow some slop and prevent large corrections + C = b3Clamp(C + B3_ANGULAR_SLOP, -B3_MAX_ANGULAR_CORRECTION, scalar(0)); + } + else if (angle >= m_upperAngle) + { + C = angle - m_upperAngle; + + // Allow some slop and prevent large corrections + C = b3Clamp(C - B3_ANGULAR_SLOP, scalar(0), B3_MAX_ANGULAR_CORRECTION); + } + + if (C != scalar(0)) + { + scalar theta = scalar(0.5) * (angle - C); + + x = sin(theta); + xs = cos(theta); + } + } + + // Twist + b3Quat qt(x, scalar(0), scalar(0), xs); + + // Solve cone + if (m_enableConeLimit) + { + // Half angle + scalar angle = scalar(0.5) * m_coneAngle; + + // Circle radius + scalar r = sin(scalar(0.5) * angle); + + // Circle clamp + b3Vec2 p(y, z); + if (b3LengthSquared(p) > r * r) + { + p.Normalize(); + + // Allow some slop + scalar rs = sin(scalar(0.5) * (angle + B3_ANGULAR_SLOP)); + + p *= rs; + + y = p.x; + z = p.y; + } + } + + // Swing + b3Quat qs(scalar(0), y, z, b3Sqrt(b3Max(scalar(0), scalar(1) - y * y - z * z))); + + b3Quat q2 = qs * qt; + + if (b3Dot(q1, q2) < scalar(0)) + { + q1 = -q1; + } + + // d * q1 = q2 + // d = q2 * q1^-1 + b3Quat d = q2 * b3Conjugate(q1); + + // Exact local errors + b3Vec3 v; + v.x = scalar(2) * atan2(d.v.x, d.s); + v.y = scalar(2) * atan2(d.v.y, d.s); + v.z = scalar(2) * atan2(d.v.z, d.s); + + limitError += b3Length(v); + + // Convert the local angular error to world's frame + // Negate the local error. + b3Vec3 C = b3Mul(fA, -v); + + b3Mat33 mass = iA + iB; + + b3Vec3 impulse = mass.Solve(-C); + + qA -= b3Derivative(qA, iA * impulse); + qA.Normalize(); + iA = b3RotateToFrame(m_localInvIA, qA); + + qB += b3Derivative(qB, iB * impulse); + qB.Normalize(); + iB = b3RotateToFrame(m_localInvIB, qB); + } + + // Solve linear constraint. + scalar linearError = scalar(0); + { + b3Vec3 rA = b3Mul(qA, m_localAnchorA - m_localCenterA); + b3Vec3 rB = b3Mul(qB, m_localAnchorB - m_localCenterB); b3Vec3 C = xB + rB - xA - rA; @@ -251,47 +517,6 @@ bool b3ConeJoint::SolvePositionConstraints(const b3SolverData* data) iB = b3RotateToFrame(m_localInvIB, qB); } - // Solve limit constraint. - float32 limitError = 0.0f; - if (m_enableLimit) - { - // Compute fresh Jacobian - b3Vec3 u1 = b3Mul(qA, m_localFrameA.rotation.y); - b3Vec3 u2 = b3Mul(qB, m_localFrameB.rotation.y); - b3Vec3 limitAxis = b3Cross(u2, u1); - - // Compute fresh effective mass. - float32 mass = b3Dot((iA + iB) * limitAxis, limitAxis); - float32 limitMass = mass > 0.0f ? 1.0f / mass : 0.0f; - - // Compute joint angle. - float32 cosine = b3Dot(u2, u1); - float32 sine = b3Length(limitAxis); - float32 angle = atan2(sine, cosine); - - float32 limitImpulse = 0.0f; - - if (0.5f * m_coneAngle < angle) - { - float32 C = 0.5f * m_coneAngle - angle; - limitError = -C; - - // Allow some slop and prevent large corrections - C = b3Clamp(C + B3_ANGULAR_SLOP, -B3_MAX_ANGULAR_CORRECTION, 0.0f); - limitImpulse = -C * limitMass; - } - - b3Vec3 P = limitImpulse * limitAxis; - - qA -= b3Derivative(qA, iA * P); - qA.Normalize(); - iA = b3RotateToFrame(m_localInvIA, qA); - - qB += b3Derivative(qB, iB * P); - qB.Normalize(); - iB = b3RotateToFrame(m_localInvIB, qB); - } - data->positions[m_indexA].x = xA; data->positions[m_indexA].q = qA; data->positions[m_indexB].x = xB; @@ -302,63 +527,87 @@ bool b3ConeJoint::SolvePositionConstraints(const b3SolverData* data) return linearError <= B3_LINEAR_SLOP && limitError <= B3_ANGULAR_SLOP; } -b3Transform b3ConeJoint::GetFrameA() const +void b3ConeJoint::SetEnableConeLimit(bool bit) { - return GetBodyA()->GetWorldFrame(m_localFrameA); -} - -b3Transform b3ConeJoint::GetFrameB() const -{ - return GetBodyB()->GetWorldFrame(m_localFrameB); -} - -const b3Transform& b3ConeJoint::GetLocalFrameA() const -{ - return m_localFrameA; -} - -const b3Transform& b3ConeJoint::GetLocalFrameB() const -{ - return m_localFrameB; -} - -bool b3ConeJoint::IsLimitEnabled() const -{ - return m_enableLimit; -} - -void b3ConeJoint::SetEnableLimit(bool bit) -{ - if (bit != m_enableLimit) + if (bit != m_enableConeLimit) { GetBodyA()->SetAwake(true); GetBodyB()->SetAwake(true); - m_limitImpulse = 0.0f; - m_limitState = e_inactiveLimit; - m_enableLimit = bit; + m_coneImpulse = scalar(0); + m_coneState = e_inactiveLimit; + m_enableConeLimit = bit; } } -float32 b3ConeJoint::GetConeAngle() const +bool b3ConeJoint::IsConeLimitEnabled() const { - return m_coneAngle; + return m_enableConeLimit; } -void b3ConeJoint::SetConeAngle(float32 angle) +void b3ConeJoint::SetConeAngle(scalar angle) { if (angle != m_coneAngle) { GetBodyA()->SetAwake(true); GetBodyB()->SetAwake(true); - m_limitImpulse = 0.0f; + m_coneImpulse = scalar(0); m_coneAngle = angle; } } +scalar b3ConeJoint::GetConeAngle() const +{ + return m_coneAngle; +} + +void b3ConeJoint::SetEnableTwistLimit(bool bit) +{ + if (bit != m_enableTwistLimit) + { + GetBodyA()->SetAwake(true); + GetBodyB()->SetAwake(true); + m_twistImpulse = scalar(0); + m_twistState = e_inactiveLimit; + m_enableTwistLimit = bit; + } +} + +bool b3ConeJoint::IsTwistLimitEnabled() const +{ + return m_enableTwistLimit; +} + +void b3ConeJoint::SetTwistLimits(scalar lower, scalar upper) +{ + B3_ASSERT(lower <= upper); + + if (lower != m_lowerAngle || upper != m_upperAngle) + { + GetBodyA()->SetAwake(true); + GetBodyB()->SetAwake(true); + m_twistImpulse = scalar(0); + m_lowerAngle = lower; + m_upperAngle = upper; + } +} + +scalar b3ConeJoint::GetTwistLowerAngle() const +{ + return m_lowerAngle; +} + +scalar b3ConeJoint::GetTwistUpperAngle() const +{ + return m_upperAngle; +} + void b3ConeJoint::Draw() const { - b3Transform xfA = GetFrameA(); + b3Transform xfA(m_localAnchorA, m_localRotationA); + xfA = GetBodyA()->GetWorldFrame(xfA); b3Draw_draw->DrawTransform(xfA); - b3Transform xfB = GetFrameB(); + + b3Transform xfB(m_localAnchorB, m_localRotationB); + xfB = GetBodyB()->GetWorldFrame(xfB); b3Draw_draw->DrawTransform(xfB); } \ No newline at end of file diff --git a/src/bounce/dynamics/joints/friction_joint.cpp b/src/bounce/dynamics/joints/friction_joint.cpp new file mode 100644 index 0000000..13685a6 --- /dev/null +++ b/src/bounce/dynamics/joints/friction_joint.cpp @@ -0,0 +1,204 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include + +void b3FrictionJointDef::Initialize(b3Body* bA, b3Body* bB, const b3Vec3& anchor) +{ + bodyA = bA; + bodyB = bB; + localAnchorA = bodyA->GetLocalPoint(anchor); + localAnchorB = bodyB->GetLocalPoint(anchor); +} + +b3FrictionJoint::b3FrictionJoint(const b3FrictionJointDef* def) +{ + m_type = b3JointType::e_frictionJoint; + + m_localAnchorA = def->localAnchorA; + m_localAnchorB = def->localAnchorB; + + m_linearImpulse.SetZero(); + m_angularImpulse.SetZero(); + + m_maxForce = def->maxForce; + m_maxTorque = def->maxTorque; +} + +void b3FrictionJoint::InitializeConstraints(const b3SolverData* data) +{ + b3Body* m_bodyA = GetBodyA(); + b3Body* m_bodyB = GetBodyB(); + + m_indexA = m_bodyA->m_islandID; + m_indexB = m_bodyB->m_islandID; + m_mA = m_bodyA->m_invMass; + m_mB = m_bodyB->m_invMass; + m_iA = data->invInertias[m_indexA]; + m_iB = data->invInertias[m_indexB]; + + b3Vec3 localCenterA = m_bodyA->m_sweep.localCenter; + b3Vec3 localCenterB = m_bodyB->m_sweep.localCenter; + + b3Quat qA = data->positions[m_indexA].q; + b3Quat qB = data->positions[m_indexB].q; + + // Compute effective mass for the block solver + m_rA = b3Mul(qA, m_localAnchorA - localCenterA); + m_rB = b3Mul(qB, m_localAnchorB - localCenterB); + + b3Mat33 RA = b3Skew(m_rA); + b3Mat33 RAT = b3Transpose(RA); + b3Mat33 RB = b3Skew(m_rB); + b3Mat33 RBT = b3Transpose(RB); + b3Mat33 M = b3Diagonal(m_mA + m_mB); + + m_linearMass = M + RA * m_iA * RAT + RB * m_iB * RBT; + m_angularMass = m_iA + m_iB; +} + +void b3FrictionJoint::WarmStart(const b3SolverData* data) +{ + b3Vec3 vA = data->velocities[m_indexA].v; + b3Vec3 wA = data->velocities[m_indexA].w; + b3Vec3 vB = data->velocities[m_indexB].v; + b3Vec3 wB = data->velocities[m_indexB].w; + + b3Vec3 P = m_linearImpulse; + + vA -= m_mA * P; + wA -= m_iA * (b3Cross(m_rA, P) + m_angularImpulse); + + vB += m_mB * P; + wB += m_iB * (b3Cross(m_rB, P) + m_angularImpulse); + + data->velocities[m_indexA].v = vA; + data->velocities[m_indexA].w = wA; + data->velocities[m_indexB].v = vB; + data->velocities[m_indexB].w = wB; +} + +void b3FrictionJoint::SolveVelocityConstraints(const b3SolverData* data) +{ + b3Vec3 vA = data->velocities[m_indexA].v; + b3Vec3 wA = data->velocities[m_indexA].w; + b3Vec3 vB = data->velocities[m_indexB].v; + b3Vec3 wB = data->velocities[m_indexB].w; + + scalar h = data->dt; + + // Solve angular friction. + { + b3Vec3 Cdot = wB - wA; + b3Vec3 impulse = m_angularMass.Solve(-Cdot); + + b3Vec3 oldImpulse = m_angularImpulse; + m_angularImpulse += impulse; + + scalar maxImpulse = h * m_maxTorque; + if (b3LengthSquared(m_angularImpulse) > maxImpulse * maxImpulse) + { + m_angularImpulse.Normalize(); + m_angularImpulse *= maxImpulse; + } + + impulse = m_angularImpulse - oldImpulse; + + wA -= m_iA * impulse; + wB += m_iB * impulse; + } + + // Solve linear friction + { + b3Vec3 Cdot = vB + b3Cross(wB, m_rB) - vA - b3Cross(wA, m_rA); + b3Vec3 impulse = m_linearMass.Solve(-Cdot); + b3Vec3 oldImpulse = m_linearImpulse; + m_linearImpulse += impulse; + + scalar maxImpulse = h * m_maxForce; + if (b3LengthSquared(m_linearImpulse) > maxImpulse * maxImpulse) + { + m_linearImpulse.Normalize(); + m_linearImpulse *= maxImpulse; + } + + impulse = m_linearImpulse - oldImpulse; + + vA -= m_mA * impulse; + wA -= m_iA * b3Cross(m_rA, impulse); + + vB += m_mB * impulse; + wB += m_iB * b3Cross(m_rB, impulse); + } + + data->velocities[m_indexA].v = vA; + data->velocities[m_indexA].w = wA; + data->velocities[m_indexB].v = vB; + data->velocities[m_indexB].w = wB; +} + +bool b3FrictionJoint::SolvePositionConstraints(const b3SolverData* data) +{ + B3_NOT_USED(data); + return true; +} + +b3Vec3 b3FrictionJoint::GetAnchorA() const +{ + return GetBodyA()->GetWorldPoint(m_localAnchorA); +} + +b3Vec3 b3FrictionJoint::GetAnchorB() const +{ + return GetBodyB()->GetWorldPoint(m_localAnchorB); +} + +void b3FrictionJoint::SetMaxForce(scalar force) +{ + B3_ASSERT(b3IsValid(force) && force >= scalar(0)); + m_maxForce = force; +} + +scalar b3FrictionJoint::GetMaxForce() const +{ + return m_maxForce; +} + +void b3FrictionJoint::SetMaxTorque(scalar torque) +{ + B3_ASSERT(b3IsValid(torque) && torque >= scalar(0)); + m_maxTorque = torque; +} + +scalar b3FrictionJoint::GetMaxTorque() const +{ + return m_maxTorque; +} + +void b3FrictionJoint::Draw() const +{ + b3Vec3 a = GetAnchorA(); + b3Draw_draw->DrawPoint(a, scalar(4), b3Color_red); + + b3Vec3 b = GetAnchorB(); + b3Draw_draw->DrawPoint(b, scalar(4), b3Color_green); + + b3Draw_draw->DrawSegment(a, b, b3Color_yellow); +} \ No newline at end of file diff --git a/src/bounce/dynamics/joints/joint.cpp b/src/bounce/dynamics/joints/joint.cpp index 1f380b7..0364093 100644 --- a/src/bounce/dynamics/joints/joint.cpp +++ b/src/bounce/dynamics/joints/joint.cpp @@ -23,10 +23,14 @@ #include #include #include +#include +#include +#include +#include b3Joint* b3Joint::Create(const b3JointDef* def) { - b3Joint* joint = NULL; + b3Joint* joint = nullptr; switch (def->type) { case e_mouseJoint: @@ -64,6 +68,30 @@ b3Joint* b3Joint::Create(const b3JointDef* def) joint = new (block) b3ConeJoint((b3ConeJointDef*)def); break; } + case e_frictionJoint: + { + void* block = b3Alloc(sizeof(b3FrictionJoint)); + joint = new (block) b3FrictionJoint((b3FrictionJointDef*)def); + break; + } + case e_motorJoint: + { + void* block = b3Alloc(sizeof(b3MotorJoint)); + joint = new (block) b3MotorJoint((b3MotorJointDef*)def); + break; + } + case e_prismaticJoint: + { + void* block = b3Alloc(sizeof(b3PrismaticJoint)); + joint = new (block) b3PrismaticJoint((b3PrismaticJointDef*)def); + break; + } + case e_wheelJoint: + { + void* block = b3Alloc(sizeof(b3WheelJoint)); + joint = new (block) b3WheelJoint((b3WheelJointDef*)def); + break; + } default: { B3_ASSERT(false); @@ -101,27 +129,55 @@ void b3Joint::Destroy(b3Joint* joint) b3Free(joint); break; } - case b3JointType::e_revoluteJoint: + case e_revoluteJoint: { b3RevoluteJoint* o = (b3RevoluteJoint*)joint; o->~b3RevoluteJoint(); b3Free(joint); break; } - case b3JointType::e_sphereJoint: + case e_sphereJoint: { b3SphereJoint* o = (b3SphereJoint*)joint; o->~b3SphereJoint(); b3Free(joint); break; } - case b3JointType::e_coneJoint: + case e_coneJoint: { b3ConeJoint* o = (b3ConeJoint*)joint; o->~b3ConeJoint(); b3Free(joint); break; } + case e_frictionJoint: + { + b3FrictionJoint* o = (b3FrictionJoint*)joint; + o->~b3FrictionJoint(); + b3Free(joint); + break; + } + case e_motorJoint: + { + b3MotorJoint* o = (b3MotorJoint*)joint; + o->~b3MotorJoint(); + b3Free(joint); + break; + } + case e_prismaticJoint: + { + b3PrismaticJoint* o = (b3PrismaticJoint*)joint; + o->~b3PrismaticJoint(); + b3Free(joint); + break; + } + case e_wheelJoint: + { + b3WheelJoint* o = (b3WheelJoint*)joint; + o->~b3WheelJoint(); + b3Free(joint); + break; + } default: { B3_ASSERT(false); diff --git a/src/bounce/dynamics/joints/joint_solver.cpp b/src/bounce/dynamics/joints/joint_solver.cpp index 7336a80..4804ef6 100644 --- a/src/bounce/dynamics/joints/joint_solver.cpp +++ b/src/bounce/dynamics/joints/joint_solver.cpp @@ -24,7 +24,7 @@ b3JointSolver::b3JointSolver(const b3JointSolverDef* def) m_count = def->count; m_joints = def->joints; m_solverData.dt = def->dt; - m_solverData.invdt = def->dt > 0.0f ? 1.0f / def->dt : 0.0f; + m_solverData.invdt = def->dt > scalar(0) ? scalar(1) / def->dt : scalar(0); m_solverData.positions = def->positions; m_solverData.velocities = def->velocities; m_solverData.invInertias = def->invInertias; diff --git a/src/bounce/dynamics/joints/motor_joint.cpp b/src/bounce/dynamics/joints/motor_joint.cpp new file mode 100644 index 0000000..a07069f --- /dev/null +++ b/src/bounce/dynamics/joints/motor_joint.cpp @@ -0,0 +1,290 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include + +void b3MotorJointDef::Initialize(b3Body* bA, b3Body* bB) +{ + bodyA = bA; + bodyB = bB; + + b3Vec3 xB = bodyB->GetPosition(); + linearOffset = bodyA->GetLocalPoint(xB); + + b3Quat qB = bodyB->GetOrientation(); + angularOffset = bodyA->GetLocalFrame(qB); +} + +b3MotorJoint::b3MotorJoint(const b3MotorJointDef* def) +{ + m_type = e_motorJoint; + m_linearOffset = def->linearOffset; + m_angularOffset = def->angularOffset; + m_linearImpulse.SetZero(); + m_angularImpulse.SetZero(); + + m_maxForce = def->maxForce; + m_maxTorque = def->maxTorque; + m_correctionFactor = def->correctionFactor; +} + +void b3MotorJoint::InitializeConstraints(const b3SolverData* data) +{ + scalar inv_h = data->invdt; + + b3Body* m_bodyA = GetBodyA(); + b3Body* m_bodyB = GetBodyB(); + + m_indexA = m_bodyA->m_islandID; + m_indexB = m_bodyB->m_islandID; + m_mA = m_bodyA->m_invMass; + m_mB = m_bodyB->m_invMass; + m_iA = data->invInertias[m_indexA]; + m_iB = data->invInertias[m_indexB]; + m_localCenterA = m_bodyA->m_sweep.localCenter; + m_localCenterB = m_bodyB->m_sweep.localCenter; + + b3Vec3 xA = data->positions[m_indexA].x; + b3Quat qA = data->positions[m_indexA].q; + + b3Vec3 xB = data->positions[m_indexB].x; + b3Quat qB = data->positions[m_indexB].q; + + { + // Compute effective mass for the block solver + m_rA = b3Mul(qA, m_linearOffset - m_localCenterA); + m_rB = b3Mul(qB, -m_localCenterB); + + b3Mat33 RA = b3Skew(m_rA); + b3Mat33 RAT = b3Transpose(RA); + b3Mat33 RB = b3Skew(m_rB); + b3Mat33 RBT = b3Transpose(RB); + b3Mat33 M = b3Diagonal(m_mA + m_mB); + + m_linearMass = M + RA * m_iA * RAT + RB * m_iB * RBT; + m_linearError = xB + m_rB - xA - m_rA; + } + + { + b3Quat q1 = b3Conjugate(qA) * qB; + b3Quat q2 = m_angularOffset; + + if (b3Dot(q1, q2) < scalar(0)) + { + q1 = -q1; + } + + // Apply finite difference + + // Angular velocity that will rotate q1 to q2 over h + b3Quat qw = inv_h * scalar(2) * (q2 - q1) * b3Conjugate(q1); + + // Convert the relative velocity to world's frame + m_angularVelocity = b3Mul(qA, qw.v); + + m_angularVelocity *= m_correctionFactor; + + m_angularMass = m_iA + m_iB; + } +} + +void b3MotorJoint::WarmStart(const b3SolverData* data) +{ + b3Vec3 vA = data->velocities[m_indexA].v; + b3Vec3 wA = data->velocities[m_indexA].w; + b3Vec3 vB = data->velocities[m_indexB].v; + b3Vec3 wB = data->velocities[m_indexB].w; + + { + vA -= m_mA * m_linearImpulse; + wA -= m_iA * b3Cross(m_rA, m_linearImpulse); + + vB += m_mB * m_linearImpulse; + wB += m_iB * b3Cross(m_rB, m_linearImpulse); + } + + { + wA -= m_iA * m_angularImpulse; + wB += m_iB * m_angularImpulse; + } + + data->velocities[m_indexA].v = vA; + data->velocities[m_indexA].w = wA; + data->velocities[m_indexB].v = vB; + data->velocities[m_indexB].w = wB; +} + +void b3MotorJoint::SolveVelocityConstraints(const b3SolverData* data) +{ + scalar h = data->dt; + scalar inv_h = data->invdt; + + b3Vec3 vA = data->velocities[m_indexA].v; + b3Vec3 wA = data->velocities[m_indexA].w; + b3Vec3 vB = data->velocities[m_indexB].v; + b3Vec3 wB = data->velocities[m_indexB].w; + + // Solve linear friction + { + b3Vec3 Cdot = vB + b3Cross(wB, m_rB) - vA - b3Cross(wA, m_rA) + inv_h * m_correctionFactor * m_linearError; + + b3Vec3 impulse = m_linearMass.Solve(-Cdot); + b3Vec3 oldImpulse = m_linearImpulse; + m_linearImpulse += impulse; + + scalar maxImpulse = h * m_maxForce; + + if (b3LengthSquared(m_linearImpulse) > maxImpulse * maxImpulse) + { + m_linearImpulse.Normalize(); + m_linearImpulse *= maxImpulse; + } + + impulse = m_linearImpulse - oldImpulse; + + vA -= m_mA * impulse; + wA -= m_iA * b3Cross(m_rA, impulse); + + vB += m_mB * impulse; + wB += m_iB * b3Cross(m_rB, impulse); + } + + // Solve angular friction + { + b3Vec3 Cdot = (wB - wA) - m_angularVelocity; + + b3Vec3 impulse = m_angularMass.Solve(-Cdot); + b3Vec3 oldImpulse = m_angularImpulse; + m_angularImpulse += impulse; + + scalar maxImpulse = h * m_maxTorque; + + if (b3LengthSquared(m_angularImpulse) > maxImpulse * maxImpulse) + { + m_angularImpulse.Normalize(); + m_angularImpulse *= maxImpulse; + } + + impulse = m_angularImpulse - oldImpulse; + + wA -= m_iA * impulse; + wB += m_iB * impulse; + } + + data->velocities[m_indexA].v = vA; + data->velocities[m_indexA].w = wA; + data->velocities[m_indexB].v = vB; + data->velocities[m_indexB].w = wB; +} + +bool b3MotorJoint::SolvePositionConstraints(const b3SolverData* data) +{ + B3_NOT_USED(data); + return true; +} + +b3Vec3 b3MotorJoint::GetAnchorA() const +{ + return GetBodyA()->GetPosition(); +} + +b3Vec3 b3MotorJoint::GetAnchorB() const +{ + return GetBodyB()->GetPosition(); +} + +void b3MotorJoint::SetLinearOffset(const b3Vec3& linearOffset) +{ + if (linearOffset.x != m_linearOffset.x || + linearOffset.y != m_linearOffset.y || + linearOffset.z != m_linearOffset.z) + { + GetBodyA()->SetAwake(true); + GetBodyB()->SetAwake(true); + m_linearOffset = linearOffset; + } +} + +const b3Vec3& b3MotorJoint::GetLinearOffset() const +{ + return m_linearOffset; +} + +void b3MotorJoint::SetAngularOffset(const b3Quat& angularOffset) +{ + if (angularOffset.v.x != m_angularOffset.v.x || + angularOffset.v.y != m_angularOffset.v.y || + angularOffset.v.z != m_angularOffset.v.z || + angularOffset.s != m_angularOffset.s) + { + GetBodyA()->SetAwake(true); + GetBodyB()->SetAwake(true); + m_angularOffset = angularOffset; + } +} + +const b3Quat& b3MotorJoint::GetAngularOffset() const +{ + return m_angularOffset; +} + +void b3MotorJoint::SetMaxTorque(scalar torque) +{ + B3_ASSERT(b3IsValid(torque) && torque >= scalar(0)); + m_maxTorque = torque; +} + +scalar b3MotorJoint::GetMaxTorque() const +{ + return m_maxTorque; +} + +void b3MotorJoint::SetMaxForce(scalar force) +{ + B3_ASSERT(b3IsValid(force) && force >= scalar(0)); + m_maxForce = force; +} + +scalar b3MotorJoint::GetMaxForce() const +{ + return m_maxForce; +} + +void b3MotorJoint::SetCorrectionFactor(scalar factor) +{ + B3_ASSERT(b3IsValid(factor) && factor >= scalar(0) && factor <= scalar(1)); + m_correctionFactor = factor; +} + +scalar b3MotorJoint::GetCorrectionFactor() const +{ + return m_correctionFactor; +} + +void b3MotorJoint::Draw() const +{ + b3Vec3 a = GetAnchorA(); + b3Draw_draw->DrawPoint(a, scalar(4), b3Color_red); + + b3Vec3 b = GetAnchorB(); + b3Draw_draw->DrawPoint(b, scalar(4), b3Color_green); + + b3Draw_draw->DrawSegment(a, b, b3Color_yellow); +} diff --git a/src/bounce/dynamics/joints/mouse_joint.cpp b/src/bounce/dynamics/joints/mouse_joint.cpp index e816c1f..414dd45 100644 --- a/src/bounce/dynamics/joints/mouse_joint.cpp +++ b/src/bounce/dynamics/joints/mouse_joint.cpp @@ -26,16 +26,36 @@ b3MouseJoint::b3MouseJoint(const b3MouseJointDef* def) m_worldTargetA = def->target; m_localAnchorB = def->bodyB->GetLocalPoint(def->target); m_maxForce = def->maxForce; + m_frequencyHz = def->frequencyHz; + m_dampingRatio = def->dampingRatio; m_impulse.SetZero(); } +static B3_FORCE_INLINE void b3ComputeSoftConstraintCoefficients(scalar& gamma, scalar& bias, + scalar frequencyHz, scalar dampingRatio, scalar m, scalar C, scalar h) +{ + // Frequency + scalar omega = scalar(2) * B3_PI * frequencyHz; + + // Spring stiffness + scalar k = omega * omega * m; + + // Damping coefficient + scalar d = scalar(2) * dampingRatio * omega * m; + + // Magic formulas + gamma = h * (d + h * k); + gamma = gamma != scalar(0) ? scalar(1) / gamma : scalar(0); + bias = gamma * h * k * C; +} + void b3MouseJoint::InitializeConstraints(const b3SolverData* data) { b3Body* m_bodyB = GetBodyB(); m_indexB = m_bodyB->m_islandID; m_mB = m_bodyB->m_invMass; - m_iB = m_bodyB->m_worldInvI; + m_iB = data->invInertias[m_indexB]; m_localCenterB = m_bodyB->m_sweep.localCenter; b3Vec3 xB = data->positions[m_indexB].x; @@ -46,9 +66,24 @@ void b3MouseJoint::InitializeConstraints(const b3SolverData* data) b3Mat33 M = b3Diagonal(m_mB); b3Mat33 RB = b3Skew(m_rB); b3Mat33 RBT = b3Transpose(RB); - m_mass = M + RB * m_iB * RBT; - - m_C = xB + m_rB - m_worldTargetA; + b3Mat33 invM = M + RB * m_iB * RBT; + b3Mat33 m = b3Inverse(invM); + + b3Vec3 C = xB + m_rB - m_worldTargetA; + + scalar h = data->dt; + + b3Vec3 gamma, bias; + + b3ComputeSoftConstraintCoefficients(gamma.x, bias.x, m_frequencyHz, m_dampingRatio, m.x.x, C.x, h); + b3ComputeSoftConstraintCoefficients(gamma.y, bias.y, m_frequencyHz, m_dampingRatio, m.y.y, C.y, h); + b3ComputeSoftConstraintCoefficients(gamma.z, bias.z, m_frequencyHz, m_dampingRatio, m.z.z, C.z, h); + + m_gamma = b3Diagonal(gamma.x, gamma.y, gamma.z); + m_bias = bias; + + invM += m_gamma; + m_mass = b3Inverse(invM); } void b3MouseJoint::WarmStart(const b3SolverData* data) @@ -64,10 +99,10 @@ void b3MouseJoint::SolveVelocityConstraints(const b3SolverData* data) b3Vec3 Cdot = vB + b3Cross(wB, m_rB); - b3Vec3 impulse = m_mass.Solve(-(Cdot + B3_BAUMGARTE * data->invdt * m_C)); + b3Vec3 impulse = -m_mass * (Cdot + m_bias + m_gamma * m_impulse); b3Vec3 oldImpulse = m_impulse; m_impulse += impulse; - float32 maxImpulse = data->dt * m_maxForce; + scalar maxImpulse = data->dt * m_maxForce; if (b3Dot(m_impulse, m_impulse) > maxImpulse * maxImpulse) { m_impulse *= maxImpulse / b3Length(m_impulse); @@ -114,7 +149,7 @@ void b3MouseJoint::Draw() const b3Vec3 a = GetAnchorA(); b3Vec3 b = GetAnchorB(); - b3Draw_draw->DrawPoint(a, 4.0f, b3Color_green); - b3Draw_draw->DrawPoint(b, 4.0f, b3Color_red); + b3Draw_draw->DrawPoint(a, scalar(4), b3Color_green); + b3Draw_draw->DrawPoint(b, scalar(4), b3Color_red); b3Draw_draw->DrawSegment(a, b, b3Color_yellow); } \ No newline at end of file diff --git a/src/bounce/dynamics/joints/prismatic_joint.cpp b/src/bounce/dynamics/joints/prismatic_joint.cpp new file mode 100644 index 0000000..77e86b8 --- /dev/null +++ b/src/bounce/dynamics/joints/prismatic_joint.cpp @@ -0,0 +1,594 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include + +void b3PrismaticJointDef::Initialize(b3Body* bA, b3Body* bB, const b3Vec3& anchor, const b3Vec3& axis) +{ + bodyA = bA; + bodyB = bB; + localAnchorA = bodyA->GetLocalPoint(anchor); + localAnchorB = bodyB->GetLocalPoint(anchor); + localAxisA = bodyA->GetLocalVector(axis); + + b3Quat qA = bodyA->GetOrientation(); + b3Quat qB = bodyB->GetOrientation(); + + referenceRotation = b3Conjugate(qA) * qB; +} + +b3PrismaticJoint::b3PrismaticJoint(const b3PrismaticJointDef* def) +{ + m_type = e_prismaticJoint; + m_localAnchorA = def->localAnchorA; + m_localAnchorB = def->localAnchorB; + m_localXAxisA = def->localAxisA; + m_localXAxisA.Normalize(); + b3ComputeBasis(m_localXAxisA, m_localYAxisA, m_localZAxisA); + + m_referenceRotation = def->referenceRotation; + + m_linearImpulse.SetZero(); + m_angularImpulse.SetZero(); + m_limitImpulse = scalar(0); + m_motorImpulse = scalar(0); + + m_lowerTranslation = def->lowerTranslation; + m_upperTranslation = def->upperTranslation; + m_maxMotorForce = def->maxMotorForce; + m_motorSpeed = def->motorSpeed; + m_enableLimit = def->enableLimit; + m_enableMotor = def->enableMotor; + m_limitState = e_inactiveLimit; +} + +void b3PrismaticJoint::InitializeConstraints(const b3SolverData* data) +{ + b3Body* m_bodyA = GetBodyA(); + b3Body* m_bodyB = GetBodyB(); + + m_indexA = m_bodyA->m_islandID; + m_indexB = m_bodyB->m_islandID; + m_mA = m_bodyA->m_invMass; + m_mB = m_bodyB->m_invMass; + m_localCenterA = m_bodyA->m_sweep.localCenter; + m_localCenterB = m_bodyB->m_sweep.localCenter; + m_localInvIA = m_bodyA->m_invI; + m_localInvIB = m_bodyB->m_invI; + m_iA = data->invInertias[m_indexA]; + m_iB = data->invInertias[m_indexB]; + + b3Vec3 xA = data->positions[m_indexA].x; + b3Quat qA = data->positions[m_indexA].q; + + b3Vec3 xB = data->positions[m_indexB].x; + b3Quat qB = data->positions[m_indexB].q; + + // Compute the effective masses. + b3Vec3 rA = b3Mul(qA, m_localAnchorA - m_localCenterA); + b3Vec3 rB = b3Mul(qB, m_localAnchorB - m_localCenterB); + + b3Vec3 d = xB + rB - xA - rA; + + scalar mA = m_mA, mB = m_mB; + b3Mat33 iA = m_iA, iB = m_iB; + + // Compute motor Jacobian and effective mass. + { + m_axis = b3Mul(qA, m_localXAxisA); + m_a1 = b3Cross(d + rA, m_axis); + m_a2 = b3Cross(rB, m_axis); + + m_motorMass = mA + mB + b3Dot(iA * m_a1, m_a1) + b3Dot(iB * m_a2, m_a2); + if (m_motorMass > scalar(0)) + { + m_motorMass = scalar(1) / m_motorMass; + } + } + + // Prismatic constraint. + { + m_perp1 = b3Mul(qA, m_localYAxisA); + m_s1 = b3Cross(d + rA, m_perp1); + m_s2 = b3Cross(rB, m_perp1); + + m_perp2 = b3Mul(qA, m_localZAxisA); + m_s3 = b3Cross(d + rA, m_perp2); + m_s4 = b3Cross(rB, m_perp2); + + scalar k11 = mA + mB + b3Dot(m_s1, iA * m_s1) + b3Dot(m_s2, iB * m_s2); + scalar k12 = b3Dot(m_s1, iA * m_s3) + b3Dot(m_s2, iB * m_s4); + scalar k22 = mA + mB + b3Dot(m_s3, iA * m_s3) + b3Dot(m_s4, iB * m_s4); + + b3Mat22 K; + K.x.x = k11; + K.x.y = k12; + K.y.x = k12; + K.y.y = k22; + + m_linearMass = b3Inverse(K); + } + + // Angular constraint. + { + m_angularMass = b3Inverse(iA + iB); + } + + // Compute motor and limit terms. + if (m_enableLimit) + { + scalar jointTranslation = b3Dot(m_axis, d); + if (b3Abs(m_upperTranslation - m_lowerTranslation) < scalar(2) * B3_LINEAR_SLOP) + { + m_limitState = e_equalLimits; + } + else if (jointTranslation <= m_lowerTranslation) + { + if (m_limitState != e_atLowerLimit) + { + m_limitState = e_atLowerLimit; + m_limitImpulse = scalar(0); + } + } + else if (jointTranslation >= m_upperTranslation) + { + if (m_limitState != e_atUpperLimit) + { + m_limitState = e_atUpperLimit; + m_limitImpulse = scalar(0); + } + } + else + { + m_limitState = e_inactiveLimit; + m_limitImpulse = scalar(0); + } + } + else + { + m_limitState = e_inactiveLimit; + m_limitImpulse = scalar(0); + } + + if (m_enableMotor == false) + { + m_motorImpulse = scalar(0); + } +} + +void b3PrismaticJoint::WarmStart(const b3SolverData* data) +{ + b3Vec3 vA = data->velocities[m_indexA].v; + b3Vec3 wA = data->velocities[m_indexA].w; + b3Vec3 vB = data->velocities[m_indexB].v; + b3Vec3 wB = data->velocities[m_indexB].w; + + b3Vec3 P = m_linearImpulse.x * m_perp1 + m_linearImpulse.y * m_perp2 + (m_motorImpulse + m_limitImpulse) * m_axis; + b3Vec3 LA = m_linearImpulse.x * m_s1 + m_linearImpulse.y * m_s3 + m_angularImpulse + (m_motorImpulse + m_limitImpulse) * m_a1; + b3Vec3 LB = m_linearImpulse.x * m_s2 + m_linearImpulse.y * m_s4 + m_angularImpulse + (m_motorImpulse + m_limitImpulse) * m_a2; + + vA -= m_mA * P; + wA -= m_iA * LA; + + vB += m_mB * P; + wB += m_iB * LB; + + data->velocities[m_indexA].v = vA; + data->velocities[m_indexA].w = wA; + data->velocities[m_indexB].v = vB; + data->velocities[m_indexB].w = wB; +} + +void b3PrismaticJoint::SolveVelocityConstraints(const b3SolverData* data) +{ + b3Vec3 vA = data->velocities[m_indexA].v; + b3Vec3 wA = data->velocities[m_indexA].w; + b3Vec3 vB = data->velocities[m_indexB].v; + b3Vec3 wB = data->velocities[m_indexB].w; + + scalar mA = m_mA, mB = m_mB; + b3Mat33 iA = m_iA, iB = m_iB; + + // Solve linear motor constraint. + if (m_enableMotor && m_limitState != e_equalLimits) + { + scalar Cdot = b3Dot(m_axis, vB - vA) + b3Dot(m_a2, wB) - b3Dot(m_a1, wA); + scalar impulse = m_motorMass * (m_motorSpeed - Cdot); + scalar oldImpulse = m_motorImpulse; + scalar maxImpulse = data->dt * m_maxMotorForce; + m_motorImpulse = b3Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse); + impulse = m_motorImpulse - oldImpulse; + + b3Vec3 P = impulse * m_axis; + b3Vec3 LA = impulse * m_a1; + b3Vec3 LB = impulse * m_a2; + + vA -= mA * P; + wA -= iA * LA; + + vB += mB * P; + wB += iB * LB; + } + + // Solve prismatic constraint in block form. + { + b3Vec2 Cdot; + Cdot.x = b3Dot(m_perp1, vB - vA) + b3Dot(m_s2, wB) - b3Dot(m_s1, wA); + Cdot.y = b3Dot(m_perp2, vB - vA) + b3Dot(m_s4, wB) - b3Dot(m_s3, wA); + + b3Vec2 impulse = m_linearMass * (-Cdot); + + m_linearImpulse += impulse; + + b3Vec3 P = impulse.x * m_perp1 + impulse.y * m_perp2; + b3Vec3 LA = impulse.x * m_s1 + impulse.y * m_s3; + b3Vec3 LB = impulse.x * m_s2 + impulse.y * m_s4; + + vA -= mA * P; + wA -= iA * LA; + + vB += mB * P; + wB += iB * LB; + } + + // Solve angular constraint in block form + { + b3Vec3 Cdot = wB - wA; + b3Vec3 impulse = m_angularMass * (-Cdot); + m_angularImpulse += impulse; + + wA -= iA * impulse; + wB += iB * impulse; + } + + // Solve limit constraint. + if (m_enableLimit && m_limitState != e_inactiveLimit) + { + scalar Cdot = b3Dot(m_axis, vB - vA) + b3Dot(m_a2, wB) - b3Dot(m_a1, wA); + scalar impulse = m_motorMass * (-Cdot); + scalar oldImpulse = m_limitImpulse; + + if (m_limitState == e_atLowerLimit) + { + m_limitImpulse = b3Max(m_limitImpulse + impulse, scalar(0)); + } + else if (m_limitState == e_atUpperLimit) + { + m_limitImpulse = b3Min(m_limitImpulse + impulse, scalar(0)); + } + + impulse = m_limitImpulse - oldImpulse; + + b3Vec3 P = impulse * m_axis; + b3Vec3 LA = impulse * m_a1; + b3Vec3 LB = impulse * m_a2; + + vA -= mA * P; + wA -= iA * LA; + + vB += mB * P; + wB += iB * LB; + } + + data->velocities[m_indexA].v = vA; + data->velocities[m_indexA].w = wA; + data->velocities[m_indexB].v = vB; + data->velocities[m_indexB].w = wB; +} + +bool b3PrismaticJoint::SolvePositionConstraints(const b3SolverData* data) +{ + b3Vec3 xA = data->positions[m_indexA].x; + b3Quat qA = data->positions[m_indexA].q; + b3Vec3 xB = data->positions[m_indexB].x; + b3Quat qB = data->positions[m_indexB].q; + b3Mat33 iA = data->invInertias[m_indexA]; + b3Mat33 iB = data->invInertias[m_indexB]; + + scalar mA = m_mA, mB = m_mB; + + // Compute fresh Jacobians + b3Vec3 rA = b3Mul(qA, m_localAnchorA - m_localCenterA); + b3Vec3 rB = b3Mul(qB, m_localAnchorB - m_localCenterB); + b3Vec3 d = xB + rB - xA - rA; + + b3Vec3 axis = b3Mul(qA, m_localXAxisA); + b3Vec3 a1 = b3Cross(d + rA, axis); + b3Vec3 a2 = b3Cross(rB, axis); + + b3Vec3 perp1 = b3Mul(qA, m_localYAxisA); + b3Vec3 s1 = b3Cross(d + rA, perp1); + b3Vec3 s2 = b3Cross(rB, perp1); + + b3Vec3 perp2 = b3Mul(qA, m_localZAxisA); + b3Vec3 s3 = b3Cross(d + rA, perp2); + b3Vec3 s4 = b3Cross(rB, perp2); + + scalar linearError = scalar(0); + + // Solve prismatic + { + b3Vec2 C; + C.x = b3Dot(perp1, d); + C.y = b3Dot(perp2, d); + + linearError += b3Abs(C.x) + b3Abs(C.y); + + scalar k11 = mA + mB + b3Dot(s1, iA * s1) + b3Dot(s2, iB * s2); + scalar k12 = b3Dot(s1, iA * s3) + b3Dot(s2, iB * s4); + scalar k22 = mA + mB + b3Dot(s3, iA * s3) + b3Dot(s4, iB * s4); + + b3Mat22 K; + K.x.x = k11; + K.x.y = k12; + K.y.x = k12; + K.y.y = k22; + + b3Mat22 linearMass = b3Inverse(K); + + b3Vec2 impulse = linearMass * (-C); + + b3Vec3 P = impulse.x * perp1 + impulse.y * perp2; + b3Vec3 LA = impulse.x * s1 + impulse.y * s3; + b3Vec3 LB = impulse.x * s2 + impulse.y * s4; + + xA -= mA * P; + qA -= b3Derivative(qA, iA * LA); + qA.Normalize(); + iA = b3RotateToFrame(m_localInvIA, qA); + + xB += mB * P; + qB += b3Derivative(qB, iB * LB); + qB.Normalize(); + iB = b3RotateToFrame(m_localInvIB, qB); + } + + scalar angularError = scalar(0); + + // Solve angular + { + b3Quat q1 = b3Conjugate(qA) * qB; + b3Quat q2 = m_referenceRotation; + + if (b3Dot(q1, q2) < scalar(0)) + { + q1 = -q1; + } + + // d * q1 = q2 + // d = q2 * q1^-1 + b3Quat d = q2 * b3Conjugate(q1); + + // Exact local errors + b3Vec3 v; + v.x = scalar(2) * atan2(d.v.x, d.s); + v.y = scalar(2) * atan2(d.v.y, d.s); + v.z = scalar(2) * atan2(d.v.z, d.s); + + angularError += b3Length(v); + + // Convert the local angular error to world's frame + // Negate the local error. + b3Vec3 C = b3Mul(qA, -v); + + b3Mat33 mass = iA + iB; + + b3Vec3 impulse = mass.Solve(-C); + + qA -= b3Derivative(qA, iA * impulse); + qA.Normalize(); + iA = b3RotateToFrame(m_localInvIA, qA); + + qB += b3Derivative(qB, iB * impulse); + qB.Normalize(); + iB = b3RotateToFrame(m_localInvIB, qB); + } + + if (m_enableLimit) + { + scalar C = scalar(0); + + scalar translation = b3Dot(axis, d); + if (b3Abs(m_upperTranslation - m_lowerTranslation) < scalar(2) * B3_LINEAR_SLOP) + { + // Prevent large angular corrections + C = b3Clamp(translation, -B3_MAX_LINEAR_CORRECTION, B3_MAX_LINEAR_CORRECTION); + linearError = b3Max(linearError, b3Abs(translation)); + } + else if (translation <= m_lowerTranslation) + { + // Prevent large linear corrections and allow some slop. + C = b3Clamp(translation - m_lowerTranslation + B3_LINEAR_SLOP, -B3_MAX_LINEAR_CORRECTION, scalar(0)); + linearError = b3Max(linearError, m_lowerTranslation - translation); + } + else if (translation >= m_upperTranslation) + { + // Prevent large linear corrections and allow some slop. + C = b3Clamp(translation - m_upperTranslation - B3_LINEAR_SLOP, scalar(0), B3_MAX_LINEAR_CORRECTION); + linearError = b3Max(linearError, translation - m_upperTranslation); + } + + scalar limitMass = mA + mB + b3Dot(iA * a1, a1) + b3Dot(iB * a2, a2); + limitMass = limitMass > scalar(0) ? scalar(1) / limitMass : scalar(0); + + scalar impulse = limitMass * -C; + + b3Vec3 P = impulse * axis; + b3Vec3 LA = impulse * m_a1; + b3Vec3 LB = impulse * m_a2; + + xA -= mA * P; + qA -= b3Derivative(qA, iA * LA); + qA.Normalize(); + iA = b3RotateToFrame(m_localInvIA, qA); + + xB += mB * P; + qB += b3Derivative(qB, iB * LB); + qB.Normalize(); + iB = b3RotateToFrame(m_localInvIB, qB); + } + + data->positions[m_indexA].x = xA; + data->positions[m_indexA].q = qA; + data->invInertias[m_indexA] = iA; + + data->positions[m_indexB].x = xB; + data->positions[m_indexB].q = qB; + data->invInertias[m_indexB] = iB; + + return linearError <= B3_LINEAR_SLOP && angularError <= B3_LINEAR_SLOP; +} + +b3Vec3 b3PrismaticJoint::GetAnchorA() const +{ + return GetBodyA()->GetWorldPoint(m_localAnchorA); +} + +b3Vec3 b3PrismaticJoint::GetAnchorB() const +{ + return GetBodyB()->GetWorldPoint(m_localAnchorB); +} + +scalar b3PrismaticJoint::GetJointTranslation() const +{ + b3Vec3 pA = GetBodyA()->GetWorldPoint(m_localAnchorA); + b3Vec3 pB = GetBodyB()->GetWorldPoint(m_localAnchorB); + b3Vec3 d = pB - pA; + b3Vec3 axis = GetBodyA()->GetWorldVector(m_localXAxisA); + + scalar translation = b3Dot(d, axis); + return translation; +} + +scalar b3PrismaticJoint::GetJointSpeed() const +{ + const b3Body* bA = GetBodyA(); + const b3Body* bB = GetBodyB(); + + b3Vec3 rA = b3Mul(bA->m_xf.rotation, m_localAnchorA - bA->m_sweep.localCenter); + b3Vec3 rB = b3Mul(bB->m_xf.rotation, m_localAnchorB - bB->m_sweep.localCenter); + + b3Vec3 p1 = bA->m_sweep.worldCenter + rA; + b3Vec3 p2 = bB->m_sweep.worldCenter + rB; + + b3Vec3 d = p2 - p1; + + b3Vec3 axis = b3Mul(bA->m_xf.rotation, m_localXAxisA); + + b3Vec3 vA = bA->m_linearVelocity; + b3Vec3 vB = bB->m_linearVelocity; + b3Vec3 wA = bA->m_angularVelocity; + b3Vec3 wB = bB->m_angularVelocity; + + scalar speed = b3Dot(d, b3Cross(wA, axis)) + b3Dot(axis, vB + b3Cross(wB, rB) - vA - b3Cross(wA, rA)); + return speed; +} + +bool b3PrismaticJoint::IsLimitEnabled() const +{ + return m_enableLimit; +} + +void b3PrismaticJoint::EnableLimit(bool flag) +{ + if (flag != m_enableLimit) + { + GetBodyA()->SetAwake(true); + GetBodyB()->SetAwake(true); + m_enableLimit = flag; + m_limitImpulse = scalar(0); + } +} + +scalar b3PrismaticJoint::GetLowerLimit() const +{ + return m_lowerTranslation; +} + +scalar b3PrismaticJoint::GetUpperLimit() const +{ + return m_upperTranslation; +} + +void b3PrismaticJoint::SetLimits(scalar lower, scalar upper) +{ + B3_ASSERT(lower <= upper); + if (lower != m_lowerTranslation || upper != m_upperTranslation) + { + GetBodyA()->SetAwake(true); + GetBodyB()->SetAwake(true); + m_lowerTranslation = lower; + m_upperTranslation = upper; + m_limitImpulse = scalar(0); + } +} + +bool b3PrismaticJoint::IsMotorEnabled() const +{ + return m_enableMotor; +} + +void b3PrismaticJoint::EnableMotor(bool flag) +{ + if (flag != m_enableMotor) + { + GetBodyA()->SetAwake(true); + GetBodyB()->SetAwake(true); + m_enableMotor = flag; + } +} + +void b3PrismaticJoint::SetMotorSpeed(scalar speed) +{ + if (speed != m_motorSpeed) + { + GetBodyA()->SetAwake(true); + GetBodyB()->SetAwake(true); + m_motorSpeed = speed; + } +} + +void b3PrismaticJoint::SetMaxMotorForce(scalar force) +{ + if (force != m_maxMotorForce) + { + GetBodyA()->SetAwake(true); + GetBodyB()->SetAwake(true); + m_maxMotorForce = force; + } +} + +void b3PrismaticJoint::Draw() const +{ + b3Vec3 pA = GetAnchorA(); + b3Draw_draw->DrawPoint(pA, scalar(4), b3Color_red); + + b3Vec3 pB = GetAnchorB(); + b3Draw_draw->DrawPoint(pB, scalar(4), b3Color_green); + + b3Mat33 localAxesA(m_localXAxisA, m_localYAxisA, m_localZAxisA); + b3Quat localRotationA = b3Mat33Quat(localAxesA); + + b3Transform xfA; + xfA.translation = pA; + xfA.rotation = GetBodyA()->GetWorldFrame(localRotationA); + + b3Draw_draw->DrawTransform(xfA); +} \ No newline at end of file diff --git a/src/bounce/dynamics/joints/revolute_joint.cpp b/src/bounce/dynamics/joints/revolute_joint.cpp index f596dbe..0768dfc 100644 --- a/src/bounce/dynamics/joints/revolute_joint.cpp +++ b/src/bounce/dynamics/joints/revolute_joint.cpp @@ -20,182 +20,29 @@ #include #include -/* - -Algebra: - -Q(p) * P(q) = P(q) * Q(p) -q' = 0.5 * w * q - -P = [0 1 0 0] - [0 0 1 0] - [0 0 0 1] - -Hinge projection matrix: - -P_hin = [x^T] * P = [0 1 0 0] - [y^T] [0 0 1 0] - -Constraint: - -q = conj(q1) * q2 - -C = P_hin * q - -Chain rule: - -q' = -conj(q1)' * q2 + conj(q1) * q2' = -conj(q1') * q2 + conj(q1) * q2' - -1st term: - -conj(q1') * q2 = -0.5 * conj(w1 * q1) * w2 = -0.5 * conj(q1) * conj(w1) * q2 = -0.5 * conj(q1) * -w1 * q2 = --0.5 * conj(q1) * w1 * q2 = --0.5 * Q(conj(q1)) * P(q2) * Q(w1) - -J1 = -0.5 * Q(conj(qA)) * P(qB) - -2nd term: - -conj(q1) * q2' = -0.5 * conj(q1) * w2 * q2 = -0.5 * Q(conj(q1)) * Q(w2) * Q(q2) = -0.5 * Q(conj(q1)) * P(q2) * Q(w2) - -J2 = 0.5 * Q(conj(q1)) * P(q2) - -C' = P_hin * q' = -P_hin * (J1 * P^T * w1 + J2 * P^T * w2) = -P_hin * J1 * P^T * w1 + P_hin * J2 * P^T * w2 - -New Jacobians: - -J1 = P_hin * J1 * P^T -J2 = P_hin * J2 * P^T - -Limit constraint: - -q = conj(q1) * q2 - -C = 2 * atan(q.z / q.w) - -Chain rule: - -f( g( q(t) ) ) = 2 * atan( g( q(t) ) ) -g( q(t) ) = q.z / q.w - -df / dt = del_f / del_g * del_g / del_q * dq / dt - -del_f / del_g = -1 / (g^2 + 1) = -1 / ((q.z / q.w)^2 + 1) = -q.w^2 / (q.w^2 + q.z^2) ~ -q.w^2 - -del_g / del_q = -[del_g / del_w | del_g / del_x | del_g / del_y | del_g / del_z] = -[-q.z/q.w^2 0 0 q.w/q.w^2] = -[-q.z/q.w^2 0 0 1/q.w] = -1 / q.w^2 * [-q.z 0 0 q.w] - -df / dt = -q.w^2 * 1 / q.w^2 * [-q.z 0 0 q.w] * dq / dt = -[-q.z 0 0 q.w] * dq / dt - -P_lim = [-q.z 0 0 q.w] - -C' = P_lim * (P_hinge * q') - target_speed - -*/ - -static B3_FORCE_INLINE b3Mat44 b3Mat44_Quat(const b3Quat& q) -{ - b3Mat44 Q; - Q.x = b3Vec4(q.w, q.x, q.y, q.z); - Q.y = b3Vec4(-q.x, q.w, q.z, -q.y); - Q.z = b3Vec4(-q.y, -q.z, q.w, q.x); - Q.w = b3Vec4(-q.z, q.y, -q.x, q.w); - return Q; -} - -static B3_FORCE_INLINE b3Mat44 b3Mat44_Projection(const b3Quat& q) -{ - b3Mat44 P; - P.x = b3Vec4(q.w, q.x, q.y, q.z); - P.y = b3Vec4(-q.x, q.w, -q.z, q.y); - P.z = b3Vec4(-q.y, q.z, q.w, -q.x); - P.w = b3Vec4(-q.z, -q.y, q.x, q.w); - return P; -} - -static B3_FORCE_INLINE b3Mat34 b3Mat34_Projection() -{ - b3Mat34 P; - P.x = b3Vec3(0.0f, 0.0f, 0.0f); - P.y = b3Vec3(1.0f, 0.0f, 0.0f); - P.z = b3Vec3(0.0f, 1.0f, 0.0f); - P.w = b3Vec3(0.0f, 0.0f, 1.0f); - return P; -} - -static B3_FORCE_INLINE b3Mat24 b3Mat24_Hinge_Projection() -{ - b3Mat24 P; - P.x = b3Vec2(0.0f, 0.0f); - P.y = b3Vec2(1.0f, 0.0f); - P.z = b3Vec2(0.0f, 1.0f); - P.w = b3Vec2(0.0f, 0.0f); - return P; -} - -static B3_FORCE_INLINE b3Vec4 b3Mat14_Hinge_Limit_Projection(const b3Quat& q) -{ - return b3Vec4(-q.z, 0.0f, 0.0f, q.w); -} - -static B3_FORCE_INLINE b3Vec4 b3Vec4_Quat(const b3Quat& q) -{ - return b3Vec4(q.w, q.x, q.y, q.z); -} - -static const b3Mat34 b3Mat34_P = b3Mat34_Projection(); -static const b3Mat43 b3Mat43_PT = b3Transpose(b3Mat34_P); -static const b3Mat24 b3Mat24_P_Hinge = b3Mat24_Hinge_Projection(); - void b3RevoluteJointDef::Initialize(b3Body* bA, b3Body* bB, const b3Vec3& axis, const b3Vec3& anchor, - float32 lower, float32 upper) + scalar lower, scalar upper) { - B3_ASSERT(b3Length(axis) > B3_EPSILON); - B3_ASSERT(lowerAngle <= upperAngle); + B3_ASSERT(lower <= upper); + + bodyA = bA; + bodyB = bB; b3Mat33 rotation; rotation.z = axis; - rotation.y = b3Perp(axis); - rotation.x = b3Cross(rotation.y, axis); + b3ComputeBasis(rotation.z, rotation.x, rotation.y); b3Quat q = b3Mat33Quat(rotation); - float32 len = q.Normalize(); - B3_ASSERT(len > B3_EPSILON); - b3Quat qA = bA->GetOrientation(); - b3Quat qB = bB->GetOrientation(); - - bodyA = bA; - bodyB = bB; - - localAnchorA = bA->GetLocalPoint(anchor); - localRotationA = b3Conjugate(qA) * q; + localAnchorA = bodyA->GetLocalPoint(anchor); + localRotationA = bodyA->GetLocalFrame(q); - localAnchorB = bB->GetLocalPoint(anchor); - localRotationB = b3Conjugate(qB) * q; + localAnchorB = bodyB->GetLocalPoint(anchor); + localRotationB = bodyB->GetLocalFrame(q); + + referenceRotation.SetIdentity(); - referenceRotation = b3Conjugate(qA * localRotationA) * qB * localRotationB; - lowerAngle = lower; upperAngle = upper; } @@ -218,14 +65,13 @@ b3RevoluteJoint::b3RevoluteJoint(const b3RevoluteJointDef* def) m_upperAngle = def->upperAngle; B3_ASSERT(m_lowerAngle <= m_upperAngle); - m_motorImpulse = 0.0f; + m_motorImpulse = scalar(0); m_limitState = e_inactiveLimit; - m_limitImpulse = 0.0f; + m_limitImpulse = scalar(0); - m_impulse.SetZero(); - - m_axisImpulse.SetZero(); + m_linearImpulse.SetZero(); + m_angularImpulse.SetZero(); } void b3RevoluteJoint::InitializeConstraints(const b3SolverData* data) @@ -248,50 +94,51 @@ void b3RevoluteJoint::InitializeConstraints(const b3SolverData* data) b3Quat qA = data->positions[m_indexA].q; b3Quat qB = data->positions[m_indexB].q; - float32 mA = m_mA; + scalar mA = m_mA; b3Mat33 iA = m_iA; - float32 mB = m_mB; + scalar mB = m_mB; b3Mat33 iB = m_iB; - // Joint rotation b3Quat fA = qA * m_localRotationA; b3Quat fB = qB * m_localRotationB; - b3Quat q = b3Conjugate(m_referenceRotation) * b3Conjugate(fA) * fB; - // Add motor constraint. + b3Mat33 RfA = b3QuatMat33(fA); + b3Mat33 RfB = b3QuatMat33(fB); + + b3Vec3 u1 = RfA.x; + b3Vec3 v1 = RfA.y; + b3Vec3 w1 = RfA.z; + + b3Vec3 u2 = RfB.x; + b3Vec3 v2 = RfB.y; + b3Vec3 w2 = RfB.z; + + // Motor constraint. if (m_enableMotor || m_enableLimit) { - b3Mat43 PT = b3Mat43_PT; - b3Vec4 P_limit = b3Mat14_Hinge_Limit_Projection(q); + m_motorAxis = w1; + m_motorAxis.Normalize(); - b3Mat44 G1 = -0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); - b3Mat44 G2 = 0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); - - b3Vec3 J1 = P_limit * G1 * PT; - b3Vec3 J2 = P_limit * G2 * PT; + scalar K = b3Dot((iA + iB) * m_motorAxis, m_motorAxis); - b3Vec3 J1T = J1; - b3Vec3 J2T = J2; - - m_motor_J1 = J1; - m_motor_J2 = J2; - - float32 K = J1 * iA * J1T + J2 * iB * J2T; - m_motorMass = K > 0.0f ? 1.0f / K : 0.0f; + m_motorMass = K > scalar(0) ? scalar(1) / K : scalar(0); } - // Add limit constraint. + // Limit constraint. if (m_enableLimit) { - // Compute joint angle - float32 angle = 2.0f * atan2(q.z, q.w); + // Joint rotation + b3Quat q = b3Conjugate(m_referenceRotation) * b3Conjugate(fA) * fB; - if (b3Abs(m_upperAngle - m_lowerAngle) < 2.0f * B3_ANGULAR_SLOP) + // Joint angle + scalar angle = scalar(2) * atan2(q.v.z, q.s); + + if (b3Abs(m_upperAngle - m_lowerAngle) < scalar(2) * B3_ANGULAR_SLOP) { if (m_limitState != e_equalLimits) { m_limitState = e_equalLimits; - m_limitImpulse = 0.0f; + m_limitImpulse = scalar(0); } } else if (angle <= m_lowerAngle) @@ -299,7 +146,7 @@ void b3RevoluteJoint::InitializeConstraints(const b3SolverData* data) if (m_limitState != e_atLowerLimit) { m_limitState = e_atLowerLimit; - m_limitImpulse = 0.0f; + m_limitImpulse = scalar(0); } } else if (angle >= m_upperAngle) @@ -307,13 +154,13 @@ void b3RevoluteJoint::InitializeConstraints(const b3SolverData* data) if (m_limitState != e_atUpperLimit) { m_limitState = e_atUpperLimit; - m_limitImpulse = 0.0f; + m_limitImpulse = scalar(0); } } else { m_limitState = e_inactiveLimit; - m_limitImpulse = 0.0f; + m_limitImpulse = scalar(0); } } else @@ -321,7 +168,7 @@ void b3RevoluteJoint::InitializeConstraints(const b3SolverData* data) m_limitState = e_inactiveLimit; } - // Add point-to-point constraints. + // Linear constraints. { m_rA = b3Mul(qA, m_localAnchorA - m_localCenterA); m_rB = b3Mul(qB, m_localAnchorB - m_localCenterB); @@ -332,31 +179,27 @@ void b3RevoluteJoint::InitializeConstraints(const b3SolverData* data) b3Mat33 RBT = b3Transpose(RB); b3Mat33 M = b3Diagonal(mA + mB); - m_mass = M + RA * iA * RAT + RB * iB * RBT; + m_linearMass = M + RA * iA * RAT + RB * iB * RBT; } - // Add hinge constraints. + // Angular constraints. { - b3Mat43 PT = b3Mat43_PT; - b3Mat24 P_hinge = b3Mat24_P_Hinge; + b3Vec3 s2 = b3Cross(u2, w1); + b3Vec3 s4 = b3Cross(v2, w1); - b3Mat44 G1 = -0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); - b3Mat44 G2 = 0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); + scalar k11 = b3Dot(s2, (iA + iB) * s2); + scalar k12 = b3Dot(s2, iA * s4) + b3Dot(s2, iB * s4); + scalar k22 = b3Dot(s4, (iA + iB) * s4); - b3Mat23 J1 = P_hinge * G1 * PT; - b3Mat23 J2 = P_hinge * G2 * PT; - - b3Mat32 J1T = b3Transpose(J1); - b3Mat32 J2T = b3Transpose(J2); - - m_J1 = J1; - m_J2 = J2; + b3Mat22 K; + K.x.x = k11; + K.x.y = k12; + K.y.x = k12; + K.y.y = k22; - m_J1T = J1T; - m_J2T = J2T; - - b3Mat22 K = J1 * iA * J1T + J2 * iB * J2T; - m_K = b3Inverse(K); + m_a1 = s2; + m_a2 = s4; + m_angularMass = b3Inverse(K); } } @@ -369,36 +212,33 @@ void b3RevoluteJoint::WarmStart(const b3SolverData* data) if (m_enableMotor && m_limitState != e_equalLimits) { - b3Vec3 P1 = m_motor_J1 * m_motorImpulse; - b3Vec3 P2 = m_motor_J2 * m_motorImpulse; + b3Vec3 P = m_motorAxis * m_motorImpulse; - wA += m_iA * P1; - wB += m_iB * P2; + wA -= m_iA * P; + wB += m_iB * P; } if (m_enableLimit && m_limitState != e_inactiveLimit) { - b3Vec3 P1 = m_motor_J1 * m_limitImpulse; - b3Vec3 P2 = m_motor_J2 * m_limitImpulse; + b3Vec3 P = m_motorAxis * m_limitImpulse; - wA += m_iA * P1; - wB += m_iB * P2; + wA -= m_iA * P; + wB += m_iB * P; } { - vA -= m_mA * m_impulse; - wA -= m_iA * b3Cross(m_rA, m_impulse); + vA -= m_mA * m_linearImpulse; + wA -= m_iA * b3Cross(m_rA, m_linearImpulse); - vB += m_mB * m_impulse; - wB += m_iB * b3Cross(m_rB, m_impulse); + vB += m_mB * m_linearImpulse; + wB += m_iB * b3Cross(m_rB, m_linearImpulse); } { - b3Vec3 P1 = m_J1T * m_axisImpulse; - b3Vec3 P2 = m_J2T * m_axisImpulse; + b3Vec3 L = m_angularImpulse.x * m_a1 + m_angularImpulse.y * m_a2; - wA += m_iA * P1; - wB += m_iB * P2; + wA -= m_iA * L; + wB += m_iB * L; } data->velocities[m_indexA].v = vA; @@ -420,26 +260,25 @@ void b3RevoluteJoint::SolveVelocityConstraints(const b3SolverData* data) // Solve motor constraint. if (m_enableMotor && m_limitState != e_equalLimits) { - float32 dw = m_motor_J1 * wA + m_motor_J2 * wB; - float32 Cdot = dw - m_motorSpeed; - float32 impulse = -m_motorMass * Cdot; - float32 oldImpulse = m_motorImpulse; - float32 maxImpulse = data->dt * m_maxMotorTorque; + scalar dw = b3Dot(wB - wA, m_motorAxis); + scalar Cdot = dw - m_motorSpeed; + scalar impulse = -m_motorMass * Cdot; + scalar oldImpulse = m_motorImpulse; + scalar maxImpulse = data->dt * m_maxMotorTorque; m_motorImpulse = b3Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse); impulse = m_motorImpulse - oldImpulse; - b3Vec3 P1 = m_motor_J1 * impulse; - b3Vec3 P2 = m_motor_J2 * impulse; + b3Vec3 P = m_motorAxis * impulse; - wA += iA * P1; - wB += iB * P2; + wA -= iA * P; + wB += iB * P; } // Solve limit constraint. if (m_enableLimit && m_limitState != e_inactiveLimit) { - float32 Cdot = m_motor_J1 * wA + m_motor_J2 * wB; - float32 impulse = -m_motorMass * Cdot; + scalar Cdot = b3Dot(wB - wA, m_motorAxis); + scalar impulse = -m_motorMass * Cdot; if (m_limitState == e_equalLimits) { @@ -447,30 +286,29 @@ void b3RevoluteJoint::SolveVelocityConstraints(const b3SolverData* data) } else if (m_limitState == e_atLowerLimit) { - float32 oldImpulse = m_limitImpulse; - m_limitImpulse = b3Max(m_limitImpulse + impulse, 0.0f); + scalar oldImpulse = m_limitImpulse; + m_limitImpulse = b3Max(m_limitImpulse + impulse, scalar(0)); impulse = m_limitImpulse - oldImpulse; } else if (m_limitState == e_atUpperLimit) { - float32 oldImpulse = m_limitImpulse; - m_limitImpulse = b3Min(m_limitImpulse + impulse, 0.0f); + scalar oldImpulse = m_limitImpulse; + m_limitImpulse = b3Min(m_limitImpulse + impulse, scalar(0)); impulse = m_limitImpulse - oldImpulse; } - b3Vec3 P1 = m_motor_J1 * impulse; - b3Vec3 P2 = m_motor_J2 * impulse; + b3Vec3 P = impulse * m_motorAxis; - wA += iA * P1; - wB += iB * P2; + wA -= iA * P; + wB += iB * P; } - // Solve point-to-point constraints. + // Solve linear constraints. { b3Vec3 Cdot = vB + b3Cross(wB, m_rB) - vA - b3Cross(wA, m_rA); - b3Vec3 impulse = m_mass.Solve(-Cdot); + b3Vec3 impulse = m_linearMass.Solve(-Cdot); - m_impulse += impulse; + m_linearImpulse += impulse; vA -= m_mA * impulse; wA -= m_iA * b3Cross(m_rA, impulse); @@ -479,18 +317,22 @@ void b3RevoluteJoint::SolveVelocityConstraints(const b3SolverData* data) wB += m_iB * b3Cross(m_rB, impulse); } - // Solve axes-to-axes constraint. + // Solve angular constraints. { - b3Vec2 Cdot = m_J1 * wA + m_J2 * wB; - b3Vec2 impulse = m_K * -Cdot; + b3Vec3 dw = wB - wA; - m_axisImpulse += impulse; + b3Vec2 Cdot; + Cdot.x = b3Dot(dw, m_a1); + Cdot.y = b3Dot(dw, m_a2); - b3Vec3 P1 = m_J1T * impulse; - b3Vec3 P2 = m_J2T * impulse; + b3Vec2 impulse = m_angularMass * -Cdot; - wA += m_iA * P1; - wB += m_iB * P2; + m_angularImpulse += impulse; + + b3Vec3 L = impulse.x * m_a1 + impulse.y * m_a2; + + wA -= m_iA * L; + wB += m_iB * L; } data->velocities[m_indexA].v = vA; @@ -508,80 +350,99 @@ bool b3RevoluteJoint::SolvePositionConstraints(const b3SolverData* data) b3Mat33 iA = data->invInertias[m_indexA]; b3Mat33 iB = data->invInertias[m_indexB]; - float32 mA = m_mA; - float32 mB = m_mB; + scalar mA = m_mA; + scalar mB = m_mB; + + // Solve angular constraints. + scalar angularError = scalar(0); - // Solve limit constraint. - float32 limitError = 0.0f; - - if (m_enableLimit) { b3Quat fA = qA * m_localRotationA; b3Quat fB = qB * m_localRotationB; - b3Quat q = b3Conjugate(m_referenceRotation) * b3Conjugate(fA) * fB; - b3Mat43 PT = b3Mat43_PT; - b3Vec4 P_limit = b3Mat14_Hinge_Limit_Projection(q); + b3Quat q1 = b3Conjugate(m_referenceRotation) * b3Conjugate(fA) * fB; - b3Mat44 G1 = -0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); - b3Mat44 G2 = 0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); + b3Quat q2; + q2.v.x = scalar(0); + q2.v.y = scalar(0); + q2.v.z = q1.v.z; + q2.s = q1.s; - b3Vec3 J1 = P_limit * G1 * PT; - b3Vec3 J2 = P_limit * G2 * PT; - - b3Vec3 J1T = J1; - b3Vec3 J2T = J2; - - float32 K = J1 * iA * J1T + J2 * iB * J2T; - float32 limitMass = K > 0.0f ? 1.0f / K : 0.0f; - - float32 limitImpulse = 0.0f; - - float32 angle = 2.0f * atan2(q.z, q.w); - - if (b3Abs(m_upperAngle - m_lowerAngle) < 2.0f * B3_ANGULAR_SLOP) + // Solve limit constraint + if (m_enableLimit) { - float32 C = angle - m_lowerAngle; - limitError = b3Abs(C); - - // Prevent large corrections - C = b3Clamp(C, -B3_MAX_ANGULAR_CORRECTION, B3_MAX_ANGULAR_CORRECTION); - limitImpulse = -C * limitMass; - } - else if (angle <= m_lowerAngle) - { - float32 C = angle - m_lowerAngle; - limitError = -C; + // Joint angle + scalar angle = scalar(2) * atan2(q2.v.z, q2.s); - // Allow some slop and prevent large corrections - C = b3Clamp(C + B3_ANGULAR_SLOP, -B3_MAX_ANGULAR_CORRECTION, 0.0f); - limitImpulse = -C * limitMass; - } - else if (angle >= m_upperAngle) - { - float32 C = angle - m_upperAngle; - limitError = C; + scalar C = scalar(0); + if (b3Abs(m_upperAngle - m_lowerAngle) < scalar(2) * B3_ANGULAR_SLOP) + { + C = angle - m_lowerAngle; - // Allow some slop and prevent large corrections - C = b3Clamp(C - B3_ANGULAR_SLOP, 0.0f, B3_MAX_ANGULAR_CORRECTION); - limitImpulse = -C * limitMass; + // Prevent large corrections + C = b3Clamp(C, -B3_MAX_ANGULAR_CORRECTION, B3_MAX_ANGULAR_CORRECTION); + } + else if (angle <= m_lowerAngle) + { + C = angle - m_lowerAngle; + + // Allow some slop and prevent large corrections + C = b3Clamp(C + B3_ANGULAR_SLOP, -B3_MAX_ANGULAR_CORRECTION, scalar(0)); + } + else if (angle >= m_upperAngle) + { + C = angle - m_upperAngle; + + // Allow some slop and prevent large corrections + C = b3Clamp(C - B3_ANGULAR_SLOP, scalar(0), B3_MAX_ANGULAR_CORRECTION); + } + + if (C != scalar(0)) + { + scalar theta = scalar(0.5) * (angle - C); + + q2.v.z = sin(theta); + q2.s = cos(theta); + } } - b3Vec3 P1 = J1T * limitImpulse; - b3Vec3 P2 = J2T * limitImpulse; + if (b3Dot(q1, q2) < scalar(0)) + { + q1 = -q1; + } - qA += b3Derivative(qA, iA * P1); + // d * q1 = q2 + // d = q2 * q1^-1 + b3Quat d = q2 * b3Conjugate(q1); + + // Exact local errors + b3Vec3 v; + v.x = scalar(2) * atan2(d.v.x, d.s); + v.y = scalar(2) * atan2(d.v.y, d.s); + v.z = scalar(2) * atan2(d.v.z, d.s); + + angularError += b3Length(v); + + // Convert the local angular error to world's frame + // Negate the local error. + b3Vec3 C = b3Mul(fA, -v); + + b3Mat33 mass = iA + iB; + + b3Vec3 impulse = mass.Solve(-C); + + qA -= b3Derivative(qA, iA * impulse); qA.Normalize(); iA = b3RotateToFrame(m_localInvIA, qA); - qB += b3Derivative(qB, iB * P2); + qB += b3Derivative(qB, iB * impulse); qB.Normalize(); iB = b3RotateToFrame(m_localInvIB, qB); } - - // Solve point-to-point constraints. - float32 linearError = 0.0f; - + + // Solve linear constraints. + scalar linearError = scalar(0); + { b3Vec3 rA = b3Mul(qA, m_localAnchorA - m_localCenterA); b3Vec3 rB = b3Mul(qB, m_localAnchorB - m_localCenterB); @@ -612,46 +473,6 @@ bool b3RevoluteJoint::SolvePositionConstraints(const b3SolverData* data) iB = b3RotateToFrame(m_localInvIB, qB); } - // Solve hinge constraints. - float32 angularError = 0.0f; - - { - b3Quat fA = qA * m_localRotationA; - b3Quat fB = qB * m_localRotationB; - b3Quat q = b3Conjugate(m_referenceRotation) * b3Conjugate(fA) * fB; - - b3Mat43 PT = b3Mat43_PT; - b3Mat24 P_hinge = b3Mat24_P_Hinge; - - b3Vec2 C = P_hinge * b3Vec4_Quat(q); - - angularError += b3Length(C); - - // Compute effective mass - b3Mat44 G1 = -0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); - b3Mat44 G2 = 0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); - - b3Mat23 J1 = P_hinge * G1 * PT; - b3Mat23 J2 = P_hinge * G2 * PT; - - b3Mat32 J1T = b3Transpose(J1); - b3Mat32 J2T = b3Transpose(J2); - - b3Mat22 mass = J1 * iA * J1T + J2 * iB * J2T; - b3Vec2 impulse = mass.Solve(-C); - - b3Vec3 P1 = J1T * impulse; - b3Vec3 P2 = J2T * impulse; - - qA += b3Derivative(qA, iA * P1); - qA.Normalize(); - iA = b3RotateToFrame(m_localInvIA, qA); - - qB += b3Derivative(qB, iB * P2); - qB.Normalize(); - iB = b3RotateToFrame(m_localInvIB, qB); - } - data->positions[m_indexA].x = xA; data->positions[m_indexA].q = qA; data->positions[m_indexB].x = xB; @@ -659,31 +480,29 @@ bool b3RevoluteJoint::SolvePositionConstraints(const b3SolverData* data) data->invInertias[m_indexA] = iA; data->invInertias[m_indexB] = iB; - return linearError <= B3_LINEAR_SLOP && - angularError <= B3_ANGULAR_SLOP && - limitError <= B3_ANGULAR_SLOP; + return linearError <= B3_LINEAR_SLOP && angularError <= B3_ANGULAR_SLOP; } b3Transform b3RevoluteJoint::GetFrameA() const { - b3Transform xf(m_localRotationA, m_localAnchorA); + b3Transform xf(m_localAnchorA, m_localRotationA); return GetBodyA()->GetWorldFrame(xf); } b3Transform b3RevoluteJoint::GetFrameB() const { - b3Transform xf(m_localRotationB, m_localAnchorB); + b3Transform xf(m_localAnchorB, m_localRotationB); return GetBodyB()->GetWorldFrame(xf); } b3Transform b3RevoluteJoint::GetLocalFrameA() const { - return b3Transform(m_localRotationA, m_localAnchorA); + return b3Transform(m_localAnchorA, m_localRotationA); } b3Transform b3RevoluteJoint::GetLocalFrameB() const { - return b3Transform(m_localRotationB, m_localAnchorB); + return b3Transform(m_localAnchorB, m_localRotationB); } bool b3RevoluteJoint::IsLimitEnabled() const @@ -697,30 +516,30 @@ void b3RevoluteJoint::SetEnableLimit(bool bit) { GetBodyA()->SetAwake(true); GetBodyB()->SetAwake(true); - m_limitImpulse = 0.0f; + m_limitImpulse = scalar(0); m_limitState = e_inactiveLimit; m_enableLimit = bit; } } -float32 b3RevoluteJoint::GetLowerLimit() const +scalar b3RevoluteJoint::GetLowerLimit() const { return m_lowerAngle; } -float32 b3RevoluteJoint::GetUpperLimit() const +scalar b3RevoluteJoint::GetUpperLimit() const { return m_upperAngle; } -void b3RevoluteJoint::SetLimits(float32 lower, float32 upper) +void b3RevoluteJoint::SetLimits(scalar lower, scalar upper) { B3_ASSERT(lower <= upper); if (lower != m_lowerAngle || upper != m_upperAngle) { GetBodyA()->SetAwake(true); GetBodyB()->SetAwake(true); - m_limitImpulse = 0.0f; + m_limitImpulse = scalar(0); m_lowerAngle = lower; m_upperAngle = upper; } @@ -737,29 +556,29 @@ void b3RevoluteJoint::SetEnableMotor(bool bit) { GetBodyA()->SetAwake(true); GetBodyB()->SetAwake(true); - m_motorImpulse = 0.0f; + m_motorImpulse = scalar(0); m_enableMotor = bit; } } -float32 b3RevoluteJoint::GetMotorSpeed() const +scalar b3RevoluteJoint::GetMotorSpeed() const { return m_motorSpeed; } -void b3RevoluteJoint::SetMotorSpeed(float32 speed) +void b3RevoluteJoint::SetMotorSpeed(scalar speed) { GetBodyA()->SetAwake(true); GetBodyB()->SetAwake(true); m_motorSpeed = speed; } -float32 b3RevoluteJoint::GetMaxMotorTorque() const +scalar b3RevoluteJoint::GetMaxMotorTorque() const { return m_maxMotorTorque; } -void b3RevoluteJoint::SetMaxMotorTorque(float32 torque) +void b3RevoluteJoint::SetMaxMotorTorque(scalar torque) { GetBodyA()->SetAwake(true); GetBodyB()->SetAwake(true); diff --git a/src/bounce/dynamics/joints/sphere_joint.cpp b/src/bounce/dynamics/joints/sphere_joint.cpp index 72bc346..0b755fe 100644 --- a/src/bounce/dynamics/joints/sphere_joint.cpp +++ b/src/bounce/dynamics/joints/sphere_joint.cpp @@ -169,10 +169,10 @@ b3Vec3 b3SphereJoint::GetAnchorB() const void b3SphereJoint::Draw() const { b3Vec3 a = GetAnchorA(); - b3Draw_draw->DrawPoint(a, 4.0f, b3Color_red); + b3Draw_draw->DrawPoint(a, scalar(4), b3Color_red); b3Vec3 b = GetAnchorB(); - b3Draw_draw->DrawPoint(b, 4.0f, b3Color_green); + b3Draw_draw->DrawPoint(b, scalar(4), b3Color_green); b3Draw_draw->DrawSegment(a, b, b3Color_yellow); } diff --git a/src/bounce/dynamics/joints/spring_joint.cpp b/src/bounce/dynamics/joints/spring_joint.cpp index 8ce5759..6c94c70 100644 --- a/src/bounce/dynamics/joints/spring_joint.cpp +++ b/src/bounce/dynamics/joints/spring_joint.cpp @@ -41,7 +41,7 @@ b3SpringJoint::b3SpringJoint(const b3SpringJointDef* def) m_length = def->length; m_frequencyHz = def->frequencyHz; m_dampingRatio = def->dampingRatio; - m_impulse = 0.0f; + m_impulse = scalar(0); } b3Vec3 b3SpringJoint::GetAnchorA() const @@ -64,32 +64,32 @@ const b3Vec3& b3SpringJoint::GetLocalAnchorB() const return m_localAnchorB; } -float32 b3SpringJoint::GetLength() const +scalar b3SpringJoint::GetLength() const { return m_length; } -void b3SpringJoint::SetLength(float32 length) +void b3SpringJoint::SetLength(scalar length) { m_length = length; } -float32 b3SpringJoint::GetFrequency() const +scalar b3SpringJoint::GetFrequency() const { return m_frequencyHz; } -void b3SpringJoint::SetFrequency(float32 frequency) +void b3SpringJoint::SetFrequency(scalar frequency) { m_frequencyHz = frequency; } -float32 b3SpringJoint::GetDampingRatio() const +scalar b3SpringJoint::GetDampingRatio() const { return m_dampingRatio; } -void b3SpringJoint::SetDampingRatio(float32 ratio) +void b3SpringJoint::SetDampingRatio(scalar ratio) { m_dampingRatio = ratio; } @@ -124,7 +124,7 @@ void b3SpringJoint::InitializeConstraints(const b3SolverData* data) // Singularity check. m_n = xB + m_rB - xA - m_rA; - float32 length = b3Length(m_n); + scalar length = b3Length(m_n); if (length > B3_LINEAR_SLOP) { m_n /= length; @@ -138,36 +138,46 @@ void b3SpringJoint::InitializeConstraints(const b3SolverData* data) b3Vec3 rnA = b3Cross(m_rA, m_n); b3Vec3 rnB = b3Cross(m_rB, m_n); - float32 mass = m_mA + m_mB + b3Dot(m_iA * rnA, rnA) + b3Dot(m_iB * rnB, rnB); + scalar mass = m_mA + m_mB + b3Dot(m_iA * rnA, rnA) + b3Dot(m_iB * rnB, rnB); - m_mass = mass > 0.0f ? 1.0f / mass : 0.0f; + m_mass = mass > scalar(0) ? scalar(1) / mass : scalar(0); - if (m_frequencyHz > 0.0f) + if (m_frequencyHz > scalar(0)) { - float32 C = length - m_length; + scalar C = length - m_length; // Angular frequency - float32 omega = 2.0f * B3_PI * m_frequencyHz; + scalar omega = scalar(2) * B3_PI * m_frequencyHz; // Damping coefficient - float32 d = 2.0f * m_mass * m_dampingRatio * omega; + scalar d = scalar(2) * m_mass * m_dampingRatio * omega; // Spring stiffness - float32 k = m_mass * omega * omega; + scalar k = m_mass * omega * omega; - // Box2D's Soft Constraints talk - float32 h = data->dt; + // Magic formulas + + // gamma = 1 / (d + h * k) + + // m_gamma = gamma / h = 1 / (h * (d + h * k)) + + // beta = gamma * h * k + + // bias = beta / h * C + // bias = (m_gamma * h * k) * C + + scalar h = data->dt; m_gamma = h * (d + h * k); - m_gamma = m_gamma != 0.0f ? 1.0f / m_gamma : 0.0f; - m_bias = h * C * k * m_gamma; + m_gamma = m_gamma != scalar(0) ? scalar(1) / m_gamma : scalar(0); + m_bias = m_gamma * h * k * C; mass += m_gamma; - m_mass = mass != 0.0f ? 1.0f / mass : 0.0f; + m_mass = mass != scalar(0) ? scalar(1) / mass : scalar(0); } else { - m_bias = 0.0f; - m_gamma = 0.0f; + m_bias = scalar(0); + m_gamma = scalar(0); } } @@ -189,9 +199,9 @@ void b3SpringJoint::SolveVelocityConstraints(const b3SolverData* data) b3Vec3 wB = data->velocities[m_indexB].w; b3Vec3 dv = vB + b3Cross(wB, m_rB) - vA - b3Cross(wA, m_rA); - float32 Cdot = b3Dot(m_n, dv); + scalar Cdot = b3Dot(m_n, dv); - float32 impulse = -m_mass * (Cdot + m_bias + m_gamma * m_impulse); + scalar impulse = -m_mass * (Cdot + m_bias + m_gamma * m_impulse); m_impulse += impulse; b3Vec3 P = impulse * m_n; @@ -210,7 +220,7 @@ void b3SpringJoint::SolveVelocityConstraints(const b3SolverData* data) bool b3SpringJoint::SolvePositionConstraints(const b3SolverData* data) { - if (m_frequencyHz > 0.0f) + if (m_frequencyHz > scalar(0)) { // There is no position correction for spring joints. B3_NOT_USED(data); @@ -223,15 +233,15 @@ bool b3SpringJoint::SolvePositionConstraints(const b3SolverData* data) b3Quat qB = data->positions[m_indexB].q; b3Mat33 iA = data->invInertias[m_indexA]; b3Mat33 iB = data->invInertias[m_indexB]; - float32 mA = m_mA; - float32 mB = m_mB; + scalar mA = m_mA; + scalar mB = m_mB; b3Vec3 rA = b3Mul(qA, m_localAnchorA - m_localCenterA); b3Vec3 rB = b3Mul(qB, m_localAnchorB - m_localCenterB); b3Vec3 n = xB + rB - xA - rA; - float32 length = b3Length(n); - float32 C = length - m_length; + scalar length = b3Length(n); + scalar C = length - m_length; C = b3Clamp(C, -B3_MAX_LINEAR_CORRECTION, B3_MAX_LINEAR_CORRECTION); // Compute effective mass @@ -239,9 +249,9 @@ bool b3SpringJoint::SolvePositionConstraints(const b3SolverData* data) b3Vec3 rnA = b3Cross(rA, n); b3Vec3 rnB = b3Cross(rB, n); - float32 kMass = mA + mB + b3Dot(iA * rnA, rnA) + b3Dot(iB * rnB, rnB); - float32 mass = kMass > 0.0f ? 1.0f / kMass : 0.0f; - float32 lambda = -mass * C; + scalar kMass = mA + mB + b3Dot(iA * rnA, rnA) + b3Dot(iB * rnB, rnB); + scalar mass = kMass > scalar(0) ? scalar(1) / kMass : scalar(0); + scalar lambda = -mass * C; b3Vec3 impulse = lambda * n; @@ -266,10 +276,10 @@ bool b3SpringJoint::SolvePositionConstraints(const b3SolverData* data) void b3SpringJoint::Draw() const { b3Vec3 a = GetBodyA()->GetWorldPoint(m_localAnchorA); - b3Draw_draw->DrawPoint(a, 4.0f, b3Color_red); + b3Draw_draw->DrawPoint(a, scalar(4), b3Color_red); b3Vec3 b = GetBodyB()->GetWorldPoint(m_localAnchorB); - b3Draw_draw->DrawPoint(b, 4.0f, b3Color_green); + b3Draw_draw->DrawPoint(b, scalar(4), b3Color_green); b3Draw_draw->DrawSegment(a, b, b3Color_yellow); } \ No newline at end of file diff --git a/src/bounce/dynamics/joints/weld_joint.cpp b/src/bounce/dynamics/joints/weld_joint.cpp index e9bd161..b6f4794 100644 --- a/src/bounce/dynamics/joints/weld_joint.cpp +++ b/src/bounce/dynamics/joints/weld_joint.cpp @@ -20,80 +20,11 @@ #include #include -/* -P = [0 1 0 0] - [0 0 1 0] - [0 0 0 1] - -q = conj(q1) * q2 - -C = P * q -C' = P * q' - -q' = -conj(q1)' * q2 + conj(q1) * q2' = -conj(q2') * q2 + conj(q1) * q2' - -J1 = -0.5 * Q(conj(q1)) * P(q2) -J2 = 0.5 * Q(conj(q1)) * P(q2) - -J1 = P * J1 * P^T -J2 = P * J2 * P^T -*/ - -static B3_FORCE_INLINE b3Mat44 b3Mat44_Quat(const b3Quat& q) -{ - b3Mat44 Q; - Q.x = b3Vec4(q.w, q.x, q.y, q.z); - Q.y = b3Vec4(-q.x, q.w, q.z, -q.y); - Q.z = b3Vec4(-q.y, -q.z, q.w, q.x); - Q.w = b3Vec4(-q.z, q.y, -q.x, q.w); - return Q; -} - -static B3_FORCE_INLINE b3Mat44 b3Mat44_Projection(const b3Quat& q) -{ - b3Mat44 P; - P.x = b3Vec4(q.w, q.x, q.y, q.z); - P.y = b3Vec4(-q.x, q.w, -q.z, q.y); - P.z = b3Vec4(-q.y, q.z, q.w, -q.x); - P.w = b3Vec4(-q.z, -q.y, q.x, q.w); - return P; -} - -static B3_FORCE_INLINE b3Mat34 b3Mat34_Projection() -{ - b3Mat34 P; - P.x = b3Vec3(0.0f, 0.0f, 0.0f); - P.y = b3Vec3(1.0f, 0.0f, 0.0f); - P.z = b3Vec3(0.0f, 1.0f, 0.0f); - P.w = b3Vec3(0.0f, 0.0f, 1.0f); - return P; -} - -static B3_FORCE_INLINE b3Mat34 b3Mat34_Weld_Projection() -{ - b3Mat34 P; - P.x = b3Vec3(0.0f, 0.0f, 0.0f); - P.y = b3Vec3(1.0f, 0.0f, 0.0f); - P.z = b3Vec3(0.0f, 1.0f, 0.0f); - P.w = b3Vec3(0.0f, 0.0f, 1.0f); - return P; -} - -static B3_FORCE_INLINE b3Vec4 b3Vec4_Quat(const b3Quat& q) -{ - return b3Vec4(q.w, q.x, q.y, q.z); -} - -static const b3Mat34 b3Mat34_P = b3Mat34_Projection(); -static const b3Mat43 b3Mat43_PT = b3Transpose(b3Mat34_P); -static const b3Mat34 b3Mat34_Weld_P = b3Mat34_Weld_Projection(); - void b3WeldJointDef::Initialize(b3Body* bA, b3Body* bB, const b3Vec3& anchor) { bodyA = bA; bodyB = bB; + localAnchorA = bodyA->GetLocalPoint(anchor); localAnchorB = bodyB->GetLocalPoint(anchor); @@ -109,8 +40,48 @@ b3WeldJoint::b3WeldJoint(const b3WeldJointDef* def) m_localAnchorA = def->localAnchorA; m_localAnchorB = def->localAnchorB; m_referenceRotation = def->referenceRotation; - m_impulse.SetZero(); - m_axisImpulse.SetZero(); + m_frequencyHz = def->frequencyHz; + m_dampingRatio = def->dampingRatio; + + m_linearImpulse.SetZero(); + m_angularImpulse.SetZero(); +} + +void b3WeldJoint::SetAnchor(const b3Vec3& anchor) +{ + const b3Body* bodyA = GetBodyA(); + const b3Body* bodyB = GetBodyB(); + + m_localAnchorA = bodyA->GetLocalPoint(anchor); + m_localAnchorB = bodyB->GetLocalPoint(anchor); + + b3Quat qA = bodyA->GetOrientation(); + b3Quat qB = bodyB->GetOrientation(); + + m_referenceRotation = b3Conjugate(qA) * qB; +} + +void b3WeldJoint::SetReferenceRotation(const b3Quat& referenceRotation) +{ + m_referenceRotation = referenceRotation; +} + +static B3_FORCE_INLINE void b3ComputeSoftConstraintCoefficients(scalar& gamma, scalar& bias, + scalar frequencyHz, scalar dampingRatio, scalar m, scalar C, scalar h) +{ + // Frequency + scalar omega = scalar(2) * B3_PI * frequencyHz; + + // Spring stiffness + scalar k = omega * omega * m; + + // Damping coefficient + scalar d = scalar(2) * dampingRatio * omega * m; + + // Magic formulas + gamma = h * (d + h * k); + gamma = gamma != scalar(0) ? scalar(1) / gamma : scalar(0); + bias = gamma * h * k * C; } void b3WeldJoint::InitializeConstraints(const b3SolverData* data) @@ -122,10 +93,12 @@ void b3WeldJoint::InitializeConstraints(const b3SolverData* data) m_indexB = m_bodyB->m_islandID; m_mA = m_bodyA->m_invMass; m_mB = m_bodyB->m_invMass; - m_iA = m_bodyA->m_worldInvI; - m_iB = m_bodyB->m_worldInvI; + m_iA = data->invInertias[m_indexA]; + m_iB = data->invInertias[m_indexB]; m_localCenterA = m_bodyA->m_sweep.localCenter; m_localCenterB = m_bodyB->m_sweep.localCenter; + m_localInvIA = m_bodyA->m_invI; + m_localInvIB = m_bodyB->m_invI; b3Quat qA = data->positions[m_indexA].q; b3Quat qB = data->positions[m_indexB].q; @@ -141,19 +114,47 @@ void b3WeldJoint::InitializeConstraints(const b3SolverData* data) b3Mat33 RBT = b3Transpose(RB); b3Mat33 M = b3Diagonal(m_mA + m_mB); - m_mass = M + RA * m_iA * RAT + RB * m_iB * RBT; + m_linearMass = M + RA * m_iA * RAT + RB * m_iB * RBT; } + if (m_frequencyHz > scalar(0)) { - b3Quat dq = b3Conjugate(m_referenceRotation) * b3Conjugate(qA) * qB; + b3Mat33 invM = m_iA + m_iB; + b3Mat33 m = b3Inverse(invM); - m_J1 = -0.5f * b3Mat34_Weld_P * b3Mat44_Quat(b3Conjugate(qA)) * b3Mat44_Projection(qB) * b3Mat43_PT; - m_J2 = 0.5f * b3Mat34_Weld_P * b3Mat44_Quat(b3Conjugate(qA)) * b3Mat44_Projection(qB) * b3Mat43_PT; + b3Quat q1 = b3Conjugate(qA) * qB; + b3Quat q2 = m_referenceRotation; + + scalar sign = b3Sign(b3Dot(q1, q2)); - m_J1T = b3Transpose(m_J1); - m_J2T = b3Transpose(m_J2); + q1 = sign * q1; - m_K = m_J1 * m_iA * m_J1T + m_J2 * m_iB * m_J2T; + // Apply finite difference + b3Quat q = scalar(2) * (q2 - q1) * b3Conjugate(q1); + + // Convert the relative angular error to world's frame + // Negate so we solve impulse = -m^1 * (Cdot - bias) + b3Vec3 C = b3Mul(qA, -q.v); + + scalar h = data->dt; + + b3Vec3 gamma, bias; + + b3ComputeSoftConstraintCoefficients(gamma.x, bias.x, m_frequencyHz, m_dampingRatio, m.x.x, C.x, h); + b3ComputeSoftConstraintCoefficients(gamma.y, bias.y, m_frequencyHz, m_dampingRatio, m.y.y, C.y, h); + b3ComputeSoftConstraintCoefficients(gamma.z, bias.z, m_frequencyHz, m_dampingRatio, m.z.z, C.z, h); + + m_gamma = b3Diagonal(gamma.x, gamma.y, gamma.z); + m_bias = bias; + + invM += m_gamma; + m_angularMass = b3Inverse(invM); + } + else + { + m_gamma.SetZero(); + m_bias.SetZero(); + m_angularMass = b3Inverse(m_iA + m_iB); } } @@ -165,19 +166,16 @@ void b3WeldJoint::WarmStart(const b3SolverData* data) b3Vec3 wB = data->velocities[m_indexB].w; { - vA -= m_mA * m_impulse; - wA -= m_iA * b3Cross(m_rA, m_impulse); + vA -= m_mA * m_linearImpulse; + wA -= m_iA * b3Cross(m_rA, m_linearImpulse); - vB += m_mB * m_impulse; - wB += m_iB * b3Cross(m_rB, m_impulse); + vB += m_mB * m_linearImpulse; + wB += m_iB * b3Cross(m_rB, m_linearImpulse); } { - b3Vec3 P1 = m_J1T * m_axisImpulse; - b3Vec3 P2 = m_J2T * m_axisImpulse; - - wA += m_iA * P1; - wB += m_iB * P2; + wA -= m_iA * m_angularImpulse; + wB += m_iB * m_angularImpulse; } data->velocities[m_indexA].v = vA; @@ -198,9 +196,9 @@ void b3WeldJoint::SolveVelocityConstraints(const b3SolverData* data) { b3Vec3 Cdot = vB + b3Cross(wB, m_rB) - vA - b3Cross(wA, m_rA); - b3Vec3 impulse = m_mass.Solve(-Cdot); + b3Vec3 impulse = m_linearMass.Solve(-Cdot); - m_impulse += impulse; + m_linearImpulse += impulse; vA -= m_mA * impulse; wA -= m_iA * b3Cross(m_rA, impulse); @@ -210,16 +208,12 @@ void b3WeldJoint::SolveVelocityConstraints(const b3SolverData* data) } { - b3Vec3 Cdot = m_J1 * wA + m_J2 * wB; - b3Vec3 impulse = m_K.Solve(-Cdot); - - m_axisImpulse += impulse; + b3Vec3 Cdot = wB - wA; + b3Vec3 impulse = -m_angularMass * (Cdot + m_bias + m_gamma * m_angularImpulse); + m_angularImpulse += impulse; - b3Vec3 P1 = m_J1T * impulse; - b3Vec3 P2 = m_J2T * impulse; - - wA += m_iA * P1; - wB += m_iB * P2; + wA -= m_iA * impulse; + wB += m_iB * impulse; } data->velocities[m_indexA].v = vA; @@ -234,8 +228,10 @@ bool b3WeldJoint::SolvePositionConstraints(const b3SolverData* data) b3Quat qA = data->positions[m_indexA].q; b3Vec3 xB = data->positions[m_indexB].x; b3Quat qB = data->positions[m_indexB].q; + b3Mat33 iA = data->invInertias[m_indexA]; + b3Mat33 iB = data->invInertias[m_indexB]; - float32 linearError = 0.0f; + scalar linearError = scalar(0); { // Compute effective mass @@ -248,55 +244,71 @@ bool b3WeldJoint::SolvePositionConstraints(const b3SolverData* data) b3Mat33 RB = b3Skew(rB); b3Mat33 RBT = b3Transpose(RB); - b3Mat33 mass = M + RA * m_iA * RAT + RB * m_iB * RBT; + b3Mat33 mass = M + RA * iA * RAT + RB * iB * RBT; b3Vec3 C = xB + rB - xA - rA; b3Vec3 impulse = mass.Solve(-C); xA -= m_mA * impulse; - qA -= b3Derivative(qA, b3Mul(m_iA, b3Cross(rA, impulse))); + qA -= b3Derivative(qA, b3Mul(iA, b3Cross(rA, impulse))); qA.Normalize(); + iA = b3RotateToFrame(m_localInvIA, qA); xB += m_mB * impulse; - qB += b3Derivative(qB, b3Mul(m_iB, b3Cross(rB, impulse))); + qB += b3Derivative(qB, b3Mul(iB, b3Cross(rB, impulse))); qB.Normalize(); + iB = b3RotateToFrame(m_localInvIB, qB); linearError += b3Length(C); } - float32 angularError = 0.0f; + scalar angularError = scalar(0); + if (m_frequencyHz == scalar(0)) { - b3Quat dq = b3Conjugate(m_referenceRotation) * b3Conjugate(qA) * qB; - b3Vec4 dq_v = b3Vec4_Quat(dq); + b3Quat q1 = b3Conjugate(qA) * qB; + b3Quat q2 = m_referenceRotation; - b3Vec3 C = b3Mat34_P * dq_v; + if (b3Dot(q1, q2) < scalar(0)) + { + q1 = -q1; + } - angularError += b3Length(C); + // d * q1 = q2 + // d = q2 * q1^-1 + b3Quat d = q2 * b3Conjugate(q1); + + // Exact local errors + b3Vec3 v; + v.x = scalar(2) * atan2(d.v.x, d.s); + v.y = scalar(2) * atan2(d.v.y, d.s); + v.z = scalar(2) * atan2(d.v.z, d.s); - b3Mat33 J1 = -0.5f * b3Mat34_Weld_P * b3Mat44_Quat(b3Conjugate(qA)) * b3Mat44_Projection(qB) * b3Mat43_PT; - b3Mat33 J2 = 0.5f * b3Mat34_Weld_P * b3Mat44_Quat(b3Conjugate(qA)) * b3Mat44_Projection(qB) * b3Mat43_PT; + angularError += b3Length(v); - b3Mat33 J1T = b3Transpose(J1); - b3Mat33 J2T = b3Transpose(J2); + // Convert the local angular error to world's frame + // Negate the local error. + b3Vec3 C = b3Mul(qA, -v); - b3Mat33 mass = J1 * m_iA * J1T + J2 * m_iB * J2T; + b3Mat33 mass = iA + iB; + b3Vec3 impulse = mass.Solve(-C); - b3Vec3 P1 = J1T * impulse; - b3Vec3 P2 = J2T * impulse; - - qA += b3Derivative(qA, m_iA * P1); + qA -= b3Derivative(qA, iA * impulse); qA.Normalize(); + iA = b3RotateToFrame(m_localInvIA, qA); - qB += b3Derivative(qB, m_iB * P2); + qB += b3Derivative(qB, iB * impulse); qB.Normalize(); + iB = b3RotateToFrame(m_localInvIB, qB); } data->positions[m_indexA].x = xA; data->positions[m_indexA].q = qA; data->positions[m_indexB].x = xB; data->positions[m_indexB].q = qB; + data->invInertias[m_indexA] = iA; + data->invInertias[m_indexB] = iB; return linearError <= B3_LINEAR_SLOP && angularError <= B3_ANGULAR_SLOP; } @@ -314,10 +326,10 @@ b3Vec3 b3WeldJoint::GetAnchorB() const void b3WeldJoint::Draw() const { b3Vec3 a = GetAnchorA(); - b3Draw_draw->DrawPoint(a, 4.0f, b3Color_red); + b3Draw_draw->DrawPoint(a, scalar(4), b3Color_red); b3Vec3 b = GetAnchorB(); - b3Draw_draw->DrawPoint(b, 4.0f, b3Color_green); + b3Draw_draw->DrawPoint(b, scalar(4), b3Color_green); b3Draw_draw->DrawSegment(a, b, b3Color_yellow); } \ No newline at end of file diff --git a/src/bounce/dynamics/joints/wheel_joint.cpp b/src/bounce/dynamics/joints/wheel_joint.cpp new file mode 100644 index 0000000..ab92ac8 --- /dev/null +++ b/src/bounce/dynamics/joints/wheel_joint.cpp @@ -0,0 +1,550 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include + +void b3WheelJointDef::Initialize(b3Body* bA, b3Body* bB, const b3Vec3& anchor, const b3Vec3& axisA, const b3Vec3& axisB) +{ + bodyA = bA; + bodyB = bB; + localAnchorA = bodyA->GetLocalPoint(anchor); + localAnchorB = bodyB->GetLocalPoint(anchor); + localAxisA = bodyA->GetLocalVector(axisA); + localAxisB = bodyB->GetLocalVector(axisB); +} + +b3WheelJoint::b3WheelJoint(const b3WheelJointDef* def) +{ + m_type = e_wheelJoint; + m_localAnchorA = def->localAnchorA; + m_localAnchorB = def->localAnchorB; + m_localXAxisA = def->localAxisA; + m_localXAxisA.Normalize(); + b3ComputeBasis(m_localXAxisA, m_localYAxisA, m_localZAxisA); + + m_localXAxisB = def->localAxisB; + m_localXAxisB.Normalize(); + + b3Vec3 axisA = def->bodyA->GetWorldVector(m_localXAxisA); + b3Vec3 axisB = def->bodyB->GetWorldVector(m_localXAxisB); + + b3Vec3 u = b3Cross(axisA, axisB); + + scalar x = b3Dot(axisA, axisB); + scalar y = b3Length(u); + + m_referenceCosine = x; + m_referenceAngle = atan2(y, x); + + m_maxMotorTorque = def->maxMotorTorque; + m_motorSpeed = def->motorSpeed; + m_enableMotor = def->enableMotor; + + m_frequencyHz = def->frequencyHz; + m_dampingRatio = def->dampingRatio; + + m_linearImpulse.SetZero(); + m_angularImpulse = scalar(0); + m_motorImpulse = scalar(0); + m_springMass = scalar(0); + m_springImpulse = scalar(0); +} + +void b3WheelJoint::InitializeConstraints(const b3SolverData* data) +{ + b3Body* m_bodyA = GetBodyA(); + b3Body* m_bodyB = GetBodyB(); + + m_indexA = m_bodyA->m_islandID; + m_indexB = m_bodyB->m_islandID; + m_mA = m_bodyA->m_invMass; + m_mB = m_bodyB->m_invMass; + m_localCenterA = m_bodyA->m_sweep.localCenter; + m_localCenterB = m_bodyB->m_sweep.localCenter; + m_localInvIA = m_bodyA->m_invI; + m_localInvIB = m_bodyB->m_invI; + m_iA = data->invInertias[m_indexA]; + m_iB = data->invInertias[m_indexB]; + + b3Vec3 xA = data->positions[m_indexA].x; + b3Quat qA = data->positions[m_indexA].q; + + b3Vec3 xB = data->positions[m_indexB].x; + b3Quat qB = data->positions[m_indexB].q; + + // Compute the effective masses. + b3Vec3 rA = b3Mul(qA, m_localAnchorA - m_localCenterA); + b3Vec3 rB = b3Mul(qB, m_localAnchorB - m_localCenterB); + + b3Vec3 d = xB + rB - xA - rA; + + scalar mA = m_mA, mB = m_mB; + b3Mat33 iA = m_iA, iB = m_iB; + + // Prismatic constraint. + { + m_perp1 = b3Mul(qA, m_localYAxisA); + m_s1 = b3Cross(d + rA, m_perp1); + m_s2 = b3Cross(rB, m_perp1); + + m_perp2 = b3Mul(qA, m_localZAxisA); + m_s3 = b3Cross(d + rA, m_perp2); + m_s4 = b3Cross(rB, m_perp2); + + scalar k11 = mA + mB + b3Dot(m_s1, iA * m_s1) + b3Dot(m_s2, iB * m_s2); + scalar k12 = b3Dot(m_s1, iA * m_s3) + b3Dot(m_s2, iB * m_s4); + scalar k22 = mA + mB + b3Dot(m_s3, iA * m_s3) + b3Dot(m_s4, iB * m_s4); + + b3Mat22 K; + K.x.x = k11; + K.x.y = k12; + K.y.x = k12; + K.y.y = k22; + + m_linearMass = b3Inverse(K); + } + + // Spring constraint. + m_springMass = scalar(0); + m_bias = scalar(0); + m_gamma = scalar(0); + if (m_frequencyHz > scalar(0)) + { + m_axisA = b3Mul(qA, m_localXAxisA); + m_a1 = b3Cross(d + rA, m_axisA); + m_a2 = b3Cross(rB, m_axisA); + + scalar invMass = mA + mB + b3Dot(iA * m_a1, m_a1) + b3Dot(iB * m_a2, m_a2); + if (invMass > scalar(0)) + { + m_springMass = scalar(1) / invMass; + + scalar C = b3Dot(d, m_axisA); + + // Frequency + scalar omega = scalar(2) * B3_PI * m_frequencyHz; + + // Damping coefficient + scalar d = scalar(2) * m_springMass * m_dampingRatio * omega; + + // Spring stiffness + scalar k = m_springMass * omega * omega; + + // magic formulas + scalar h = data->dt; + m_gamma = h * (d + h * k); + if (m_gamma > scalar(0)) + { + m_gamma = scalar(1) / m_gamma; + } + + m_bias = C * h * k * m_gamma; + + m_springMass = invMass + m_gamma; + if (m_springMass > scalar(0)) + { + m_springMass = scalar(1) / m_springMass; + } + } + } + else + { + m_springImpulse = scalar(0); + } + + if (m_enableMotor) + { + m_axisB = b3Mul(qB, m_localXAxisB); + + m_motorMass = b3Dot((iA + iB) * m_axisB, m_axisB); + if (m_motorMass > scalar(0)) + { + m_motorMass = scalar(1) / m_motorMass; + } + } + else + { + m_motorImpulse = scalar(0); + } + + // Angular constraint + { + b3Vec3 axisA = b3Mul(qA, m_localXAxisA); + b3Vec3 axisB = b3Mul(qB, m_localXAxisB); + + m_u = b3Cross(axisA, axisB); + + m_angularMass = b3Dot((iA + iB) * m_u, m_u); + if (m_angularMass > scalar(0)) + { + m_angularMass = scalar(1) / m_angularMass; + } + } +} + +void b3WheelJoint::WarmStart(const b3SolverData* data) +{ + b3Vec3 vA = data->velocities[m_indexA].v; + b3Vec3 wA = data->velocities[m_indexA].w; + b3Vec3 vB = data->velocities[m_indexB].v; + b3Vec3 wB = data->velocities[m_indexB].w; + + { + b3Vec3 P = m_linearImpulse.x * m_perp1 + m_linearImpulse.y * m_perp2 + m_springImpulse * m_axisA; + b3Vec3 LA = m_linearImpulse.x * m_s1 + m_linearImpulse.y * m_s3 + m_springImpulse * m_a1 + m_motorImpulse * m_axisB; + b3Vec3 LB = m_linearImpulse.x * m_s2 + m_linearImpulse.y * m_s4 + m_springImpulse * m_a2 + m_motorImpulse * m_axisB; + + vA -= m_mA * P; + wA -= m_iA * LA; + + vB += m_mB * P; + wB += m_iB * LB; + } + + { + b3Vec3 L = m_angularImpulse * m_u; + + wA += m_iA * L; + wB -= m_iB * L; + } + + data->velocities[m_indexA].v = vA; + data->velocities[m_indexA].w = wA; + data->velocities[m_indexB].v = vB; + data->velocities[m_indexB].w = wB; +} + +void b3WheelJoint::SolveVelocityConstraints(const b3SolverData* data) +{ + b3Vec3 vA = data->velocities[m_indexA].v; + b3Vec3 wA = data->velocities[m_indexA].w; + b3Vec3 vB = data->velocities[m_indexB].v; + b3Vec3 wB = data->velocities[m_indexB].w; + + scalar mA = m_mA, mB = m_mB; + b3Mat33 iA = m_iA, iB = m_iB; + + // Solve spring constraint. + { + scalar Cdot = b3Dot(m_axisA, vB - vA) + b3Dot(m_a2, wB) - b3Dot(m_a1, wA); + scalar impulse = -m_springMass * (Cdot + m_bias + m_gamma * m_springImpulse); + m_springImpulse += impulse; + + b3Vec3 P = impulse * m_axisA; + b3Vec3 LA = impulse * m_a1; + b3Vec3 LB = impulse * m_a2; + + vA -= mA * P; + wA -= iA * LA; + + vB += mB * P; + wB += iB * LB; + } + + // Solve rotational motor constraint in block form + if (m_enableMotor) + { + scalar dw = b3Dot(wB - wA, m_axisB); + scalar Cdot = dw - m_motorSpeed; + scalar impulse = -m_motorMass * Cdot; + scalar oldImpulse = m_motorImpulse; + scalar maxImpulse = data->dt * m_maxMotorTorque; + m_motorImpulse = b3Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse); + impulse = m_motorImpulse - oldImpulse; + + b3Vec3 P = m_axisB * impulse; + + wA -= iA * P; + wB += iB * P; + } + + // Solve prismatic constraint in block form. + { + b3Vec2 Cdot; + Cdot.x = b3Dot(m_perp1, vB - vA) + b3Dot(m_s2, wB) - b3Dot(m_s1, wA); + Cdot.y = b3Dot(m_perp2, vB - vA) + b3Dot(m_s4, wB) - b3Dot(m_s3, wA); + + b3Vec2 impulse = m_linearMass * (-Cdot); + + m_linearImpulse += impulse; + + b3Vec3 P = impulse.x * m_perp1 + impulse.y * m_perp2; + b3Vec3 LA = impulse.x * m_s1 + impulse.y * m_s3; + b3Vec3 LB = impulse.x * m_s2 + impulse.y * m_s4; + + vA -= mA * P; + wA -= iA * LA; + + vB += mB * P; + wB += iB * LB; + } + + // Solve angular constraint. + { + scalar Cdot = b3Dot(wA - wB, m_u); + scalar impulse = -m_angularMass * Cdot; + + m_angularImpulse += impulse; + + b3Vec3 L = impulse * m_u; + + wA += iA * L; + wB -= iB * L; + } + + data->velocities[m_indexA].v = vA; + data->velocities[m_indexA].w = wA; + data->velocities[m_indexB].v = vB; + data->velocities[m_indexB].w = wB; +} + +bool b3WheelJoint::SolvePositionConstraints(const b3SolverData* data) +{ + b3Vec3 xA = data->positions[m_indexA].x; + b3Quat qA = data->positions[m_indexA].q; + b3Vec3 xB = data->positions[m_indexB].x; + b3Quat qB = data->positions[m_indexB].q; + b3Mat33 iA = data->invInertias[m_indexA]; + b3Mat33 iB = data->invInertias[m_indexB]; + + scalar mA = m_mA, mB = m_mB; + + // Compute fresh Jacobians + b3Vec3 rA = b3Mul(qA, m_localAnchorA - m_localCenterA); + b3Vec3 rB = b3Mul(qB, m_localAnchorB - m_localCenterB); + b3Vec3 d = xB + rB - xA - rA; + + b3Vec3 axisA = b3Mul(qA, m_localXAxisA); + b3Vec3 a1 = b3Cross(d + rA, axisA); + b3Vec3 a2 = b3Cross(rB, axisA); + + b3Vec3 perp1 = b3Mul(qA, m_localYAxisA); + b3Vec3 s1 = b3Cross(d + rA, perp1); + b3Vec3 s2 = b3Cross(rB, perp1); + + b3Vec3 perp2 = b3Mul(qA, m_localZAxisA); + b3Vec3 s3 = b3Cross(d + rA, perp2); + b3Vec3 s4 = b3Cross(rB, perp2); + + b3Vec3 axisB = b3Mul(qB, m_localXAxisB); + + scalar linearError = scalar(0); + + // Solve prismatic + { + b3Vec2 C; + C.x = b3Dot(perp1, d); + C.y = b3Dot(perp2, d); + + linearError += b3Abs(C.x) + b3Abs(C.y); + + scalar k11 = mA + mB + b3Dot(s1, iA * s1) + b3Dot(s2, iB * s2); + scalar k12 = b3Dot(s1, iA * s3) + b3Dot(s2, iB * s4); + scalar k22 = mA + mB + b3Dot(s3, iA * s3) + b3Dot(s4, iB * s4); + + b3Mat22 K; + K.x.x = k11; + K.x.y = k12; + K.y.x = k12; + K.y.y = k22; + + b3Mat22 linearMass = b3Inverse(K); + + b3Vec2 impulse = linearMass * (-C); + + b3Vec3 P = impulse.x * perp1 + impulse.y * perp2; + b3Vec3 LA = impulse.x * s1 + impulse.y * s3; + b3Vec3 LB = impulse.x * s2 + impulse.y * s4; + + xA -= mA * P; + qA -= b3Derivative(qA, iA * LA); + qA.Normalize(); + iA = b3RotateToFrame(m_localInvIA, qA); + + xB += mB * P; + qB += b3Derivative(qB, iB * LB); + qB.Normalize(); + iB = b3RotateToFrame(m_localInvIB, qB); + } + + scalar angularError = scalar(0); + + // Solve angular + { + b3Vec3 u = b3Cross(axisA, axisB); + + scalar cosine = b3Dot(axisA, axisB); + + scalar C = cosine - m_referenceCosine; + + scalar sine = b3Length(u); + scalar angle = atan2(sine, cosine); + scalar angleC = angle - m_referenceAngle; + + angularError += b3Abs(angleC); + + scalar mass = b3Dot((iA + iB) * u, u); + if (mass > scalar(0)) + { + mass = scalar(1) / mass; + } + + scalar impulse = -mass * C; + + b3Vec3 P = impulse * u; + + qA += b3Derivative(qA, iA * P); + qA.Normalize(); + iA = b3RotateToFrame(m_localInvIA, qA); + + qB -= b3Derivative(qB, iB * P); + qB.Normalize(); + iB = b3RotateToFrame(m_localInvIB, qB); + } + + data->positions[m_indexA].x = xA; + data->positions[m_indexA].q = qA; + data->invInertias[m_indexA] = iA; + + data->positions[m_indexB].x = xB; + data->positions[m_indexB].q = qB; + data->invInertias[m_indexB] = iB; + + return linearError <= B3_LINEAR_SLOP && angularError <= B3_ANGULAR_SLOP; +} + +b3Vec3 b3WheelJoint::GetAnchorA() const +{ + return GetBodyA()->GetWorldPoint(m_localAnchorA); +} + +b3Vec3 b3WheelJoint::GetAnchorB() const +{ + return GetBodyB()->GetWorldPoint(m_localAnchorB); +} + +scalar b3WheelJoint::GetJointTranslation() const +{ + b3Vec3 pA = GetBodyA()->GetWorldPoint(m_localAnchorA); + b3Vec3 pB = GetBodyB()->GetWorldPoint(m_localAnchorB); + b3Vec3 d = pB - pA; + b3Vec3 axis = GetBodyA()->GetWorldVector(m_localXAxisA); + + scalar translation = b3Dot(d, axis); + return translation; +} + +scalar b3WheelJoint::GetJointLinearSpeed() const +{ + const b3Body* bA = GetBodyA(); + const b3Body* bB = GetBodyB(); + + b3Vec3 rA = b3Mul(bA->m_xf.rotation, m_localAnchorA - bA->m_sweep.localCenter); + b3Vec3 rB = b3Mul(bB->m_xf.rotation, m_localAnchorB - bB->m_sweep.localCenter); + + b3Vec3 p1 = bA->m_sweep.worldCenter + rA; + b3Vec3 p2 = bB->m_sweep.worldCenter + rB; + + b3Vec3 d = p2 - p1; + + b3Vec3 axis = b3Mul(bA->m_xf.rotation, m_localXAxisA); + + b3Vec3 vA = bA->m_linearVelocity; + b3Vec3 vB = bB->m_linearVelocity; + b3Vec3 wA = bA->m_angularVelocity; + b3Vec3 wB = bB->m_angularVelocity; + + scalar speed = b3Dot(d, b3Cross(wA, axis)) + b3Dot(axis, vB + b3Cross(wB, rB) - vA - b3Cross(wA, rA)); + return speed; +} + +b3Quat b3WheelJoint::GetJointRotation() const +{ + const b3Body* bA = GetBodyA(); + const b3Body* bB = GetBodyB(); + return b3Conjugate(bA->GetOrientation()) * bB->GetOrientation(); +} + +scalar b3WheelJoint::GetJointAngularSpeed() const +{ + const b3Body* bA = GetBodyA(); + const b3Body* bB = GetBodyB(); + return b3Dot(bB->GetAngularVelocity() - bA->GetAngularVelocity(), bB->GetWorldVector(m_localXAxisB)); +} + +bool b3WheelJoint::IsMotorEnabled() const +{ + return m_enableMotor; +} + +void b3WheelJoint::EnableMotor(bool flag) +{ + if (flag != m_enableMotor) + { + GetBodyA()->SetAwake(true); + GetBodyB()->SetAwake(true); + m_enableMotor = flag; + } +} + +void b3WheelJoint::SetMotorSpeed(scalar speed) +{ + if (speed != m_motorSpeed) + { + GetBodyA()->SetAwake(true); + GetBodyB()->SetAwake(true); + m_motorSpeed = speed; + } +} + +void b3WheelJoint::SetMaxMotorTorque(scalar torque) +{ + if (torque != m_maxMotorTorque) + { + GetBodyA()->SetAwake(true); + GetBodyB()->SetAwake(true); + m_maxMotorTorque = torque; + } +} + +void b3WheelJoint::Draw() const +{ + b3Vec3 pA = GetAnchorA(); + b3Draw_draw->DrawPoint(pA, scalar(4), b3Color_red); + + b3Vec3 pB = GetAnchorB(); + b3Draw_draw->DrawPoint(pB, scalar(4), b3Color_green); + + const b3Body* bA = GetBodyA(); + const b3Body* bB = GetBodyB(); + + b3Mat33 localAxes(m_localXAxisA, m_localYAxisA, m_localZAxisA); + b3Quat localRotationA = b3Mat33Quat(localAxes); + + b3Transform xfA; + xfA.translation = pA; + xfA.rotation = bA->GetWorldFrame(localRotationA); + + b3Draw_draw->DrawTransform(xfA); + + b3Vec3 axisB = bB->GetWorldVector(m_localXAxisB); + + b3Draw_draw->DrawSegment(pB, pB + axisB, b3Color_white); +} \ No newline at end of file diff --git a/src/bounce/dynamics/shapes/capsule_shape.cpp b/src/bounce/dynamics/shapes/capsule_shape.cpp index 06da598..5d146da 100644 --- a/src/bounce/dynamics/shapes/capsule_shape.cpp +++ b/src/bounce/dynamics/shapes/capsule_shape.cpp @@ -22,9 +22,7 @@ b3CapsuleShape::b3CapsuleShape() { m_type = e_capsuleShape; - m_radius = 0.0f; - m_centers[0].SetZero(); - m_centers[1].SetZero(); + m_radius = scalar(0); } b3CapsuleShape::~b3CapsuleShape() @@ -33,45 +31,45 @@ b3CapsuleShape::~b3CapsuleShape() void b3CapsuleShape::Swap(const b3CapsuleShape& other) { - m_centers[0] = other.m_centers[0]; - m_centers[1] = other.m_centers[1]; + m_vertex1 = other.m_vertex1; + m_vertex2 = other.m_vertex2; m_radius = other.m_radius; } -void b3CapsuleShape::ComputeMass(b3MassData* massData, float32 density) const +void b3CapsuleShape::ComputeMass(b3MassData* massData, scalar density) const { - b3Vec3 A = m_centers[0]; - b3Vec3 B = m_centers[1]; + b3Vec3 A = m_vertex1; + b3Vec3 B = m_vertex2; b3Vec3 d = B - A; - float32 h = b3Length(d); + scalar h = b3Length(d); B3_ASSERT(h > B3_LINEAR_SLOP); - float32 h2 = h * h; + scalar h2 = h * h; - float32 r = m_radius; - float32 r2 = r * r; - float32 r3 = r2 * r; + scalar r = m_radius; + scalar r2 = r * r; + scalar r3 = r2 * r; // - b3Vec3 center = 0.5f * (A + B); - float32 mass = 0.0f; + b3Vec3 center = scalar(0.5) * (A + B); + scalar mass = scalar(0); b3Mat33 I; I.SetZero(); b3Mat33 rotation; - rotation.y = (1.0f / h) * d; + rotation.y = (scalar(1) / h) * d; rotation.x = b3Perp(rotation.y); rotation.z = b3Cross(rotation.y, rotation.x); // Cylinder { // Mass - float32 cylinderVolume = B3_PI * r2 * h; - float32 cylinderMass = density * cylinderVolume; + scalar cylinderVolume = B3_PI * r2 * h; + scalar cylinderMass = density * cylinderVolume; // Inertia about the center of mass - float32 Ixx = (1.0f / 12.0f) * cylinderMass * (3.0f * r2 + h2); - float32 Iyy = 0.5f * cylinderMass * r2; + scalar Ixx = (scalar(1) / scalar(12)) * cylinderMass * (scalar(3) * r2 + h2); + scalar Iyy = scalar(0.5) * cylinderMass * r2; // Izz = Ixx b3Mat33 cylinderI = b3Diagonal(Ixx, Iyy, Ixx); @@ -89,23 +87,23 @@ void b3CapsuleShape::ComputeMass(b3MassData* massData, float32 density) const // Hemispheres { // Mass - float32 hemiVolume = (2.0f / 3.0f) * B3_PI * r3; - float32 hemiMass = density * hemiVolume; + scalar hemiVolume = (scalar(2) / scalar(3)) * B3_PI * r3; + scalar hemiMass = density * hemiVolume; // Hemisphere inertia about the origin - float32 Io = (2.0f / 5.0f) * hemiMass * r2; + scalar Io = (scalar(2) / scalar(5)) * hemiMass * r2; // Hemisphere center of mass relative to the origin - float32 coy = (3.0f / 8.0f) * r; + scalar coy = (scalar(3) / scalar(8)) * r; // Hemisphere inertia about the hemisphere/capsule center of mass - float32 Iyy = Io - hemiMass * coy * coy; + scalar Iyy = Io - hemiMass * coy * coy; // Hemisphere center of mass relative to the capsule center of mass - float32 ccy = coy + 0.5f * h; + scalar ccy = coy + scalar(0.5) * h; // Hemisphere inertia about the capsule the center of mass - float32 Ixx = Io + hemiMass * ccy * ccy; + scalar Ixx = Io + hemiMass * ccy * ccy; // Izz = Ixx b3Mat33 hemiI = b3Diagonal(Ixx, Iyy, Ixx); @@ -117,8 +115,8 @@ void b3CapsuleShape::ComputeMass(b3MassData* massData, float32 density) const hemiI += hemiMass * b3Steiner(center); // Contribute twice - mass += 2.0f * hemiMass; - I += 2.0f * hemiI; + mass += scalar(2) * hemiMass; + I += scalar(2) * hemiI; } // Centroid, total mass, inertia at the origin @@ -127,13 +125,13 @@ void b3CapsuleShape::ComputeMass(b3MassData* massData, float32 density) const massData->I = I; } -void b3CapsuleShape::ComputeAABB(b3AABB3* aabb, const b3Transform& xf) const +void b3CapsuleShape::ComputeAABB(b3AABB* aabb, const b3Transform& xf) const { - b3Vec3 c1 = b3Mul(xf, m_centers[0]); - b3Vec3 c2 = b3Mul(xf, m_centers[1]); + b3Vec3 c1 = b3Mul(xf, m_vertex1); + b3Vec3 c2 = b3Mul(xf, m_vertex2); b3Vec3 r(m_radius, m_radius, m_radius); - aabb->m_lower = b3Min(c1, c2) - r; - aabb->m_upper = b3Max(c1, c2) + r; + aabb->lowerBound = b3Min(c1, c2) - r; + aabb->upperBound = b3Max(c1, c2) + r; } bool b3CapsuleShape::TestSphere(const b3Sphere& sphere, const b3Transform& xf) const @@ -141,17 +139,17 @@ bool b3CapsuleShape::TestSphere(const b3Sphere& sphere, const b3Transform& xf) c // The point in the frame of the capsule b3Vec3 Q = b3MulT(xf, sphere.vertex); - b3Vec3 A = m_centers[0]; - b3Vec3 B = m_centers[1]; + b3Vec3 A = m_vertex1; + b3Vec3 B = m_vertex2; b3Vec3 AB = B - A; // Barycentric coordinates for Q - float32 u = b3Dot(B - Q, AB); - float32 v = b3Dot(Q - A, AB); + scalar u = b3Dot(B - Q, AB); + scalar v = b3Dot(Q - A, AB); - float32 radius = m_radius + sphere.radius; + scalar radius = m_radius + sphere.radius; - if (v <= 0.0f) + if (v <= scalar(0)) { // A if (b3DistanceSquared(A, Q) > radius * radius) @@ -161,7 +159,7 @@ bool b3CapsuleShape::TestSphere(const b3Sphere& sphere, const b3Transform& xf) c return true; } - if (u <= 0.0f) + if (u <= scalar(0)) { // B if (b3DistanceSquared(B, Q) > radius * radius) @@ -172,9 +170,9 @@ bool b3CapsuleShape::TestSphere(const b3Sphere& sphere, const b3Transform& xf) c } // AB - float32 s = b3Dot(AB, AB); - B3_ASSERT(s > 0.0f); - b3Vec3 P = (1.0f / s) * (u * A + v * B); + scalar s = b3Dot(AB, AB); + B3_ASSERT(s > scalar(0)); + b3Vec3 P = (scalar(1) / s) * (u * A + v * B); if (b3DistanceSquared(P, Q) > radius * radius) { return false; @@ -187,98 +185,87 @@ bool b3CapsuleShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphe { b3Vec3 Q = sphere.vertex; - b3Vec3 A = b3Mul(xf, m_centers[0]); - b3Vec3 B = b3Mul(xf, m_centers[1]); + b3Vec3 A = b3Mul(xf, m_vertex1); + b3Vec3 B = b3Mul(xf, m_vertex2); b3Vec3 AB = B - A; // Barycentric coordinates for Q - float32 u = b3Dot(B - Q, AB); - float32 v = b3Dot(Q - A, AB); + scalar u = b3Dot(B - Q, AB); + scalar v = b3Dot(Q - A, AB); - float32 radius = m_radius + sphere.radius; + scalar radius = m_radius + sphere.radius; - if (v <= 0.0f) + if (v <= scalar(0)) { // A b3Vec3 P = A; b3Vec3 d = Q - P; - float32 dd = b3Dot(d, d); + scalar dd = b3Dot(d, d); if (dd > radius * radius) { return false; } - b3Vec3 n(0.0f, 1.0f, 0.0f); - float32 len = b3Sqrt(dd); + b3Vec3 n(scalar(0), scalar(1), scalar(0)); + scalar len = b3Sqrt(dd); if (len > B3_EPSILON) { n = d / len; } - output->point = A; - output->separation = len - radius; + output->point = P; output->normal = n; return true; } - if (u <= 0.0f) + if (u <= scalar(0)) { // B b3Vec3 P = B; b3Vec3 d = Q - P; - float32 dd = b3Dot(d, d); + scalar dd = b3Dot(d, d); if (dd > radius * radius) { return false; } - b3Vec3 n(0.0f, 1.0f, 0.0f); - float32 len = b3Sqrt(dd); + b3Vec3 n(scalar(0), scalar(1), scalar(0)); + scalar len = b3Sqrt(dd); if (len > B3_EPSILON) { n = d / len; } - output->point = B; - output->separation = len - radius; + output->point = P; output->normal = n; return true; } // AB - float32 s = b3Dot(AB, AB); - //B3_ASSERT(s > 0.0f); - b3Vec3 P; - if (s < B3_LINEAR_SLOP * B3_LINEAR_SLOP) - { - P = A; - } - else - { - P = (u * A + v * B) / s; - } + scalar s = b3Dot(AB, AB); + //B3_ASSERT(s > scalar(0)); + b3Vec3 P = (u * A + v * B) / s; b3Vec3 d = Q - P; - float32 dd = b3Dot(d, d); + scalar dd = b3Dot(d, d); if (dd > radius * radius) { return false; } - b3Vec3 QA = A - Q; - b3Vec3 e = b3Cross(AB, QA); - b3Vec3 n = b3Cross(AB, e); - if (b3Dot(n, QA) < 0.0f) + b3Vec3 AQ = Q - A; + b3Vec3 AB_x_AQ = b3Cross(AB, AQ); + b3Vec3 n = b3Cross(AB_x_AQ, AB); + if (b3Dot(n, AQ) < scalar(0)) { n = -n; } n.Normalize(); output->point = P; - output->separation = b3Sqrt(dd) - radius; - output->normal = -n; + output->normal = n; return true; } @@ -288,7 +275,7 @@ bool b3CapsuleShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& inpu b3Vec3 B = input.p2; b3Vec3 n = B - A; - float32 nn = b3Dot(n, n); + scalar nn = b3Dot(n, n); // Check for short segment. if (nn < B3_EPSILON * B3_EPSILON) @@ -296,34 +283,34 @@ bool b3CapsuleShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& inpu return false; } - b3Vec3 P = b3Mul(xf, m_centers[0]); - b3Vec3 Q = b3Mul(xf, m_centers[1]); + b3Vec3 P = b3Mul(xf, m_vertex1); + b3Vec3 Q = b3Mul(xf, m_vertex2); b3Vec3 d = Q - P; - float32 dd = b3Dot(d, d); + scalar dd = b3Dot(d, d); // Check for short segment. if (dd < B3_EPSILON * B3_EPSILON) { - float32 a = nn; + scalar a = nn; b3Vec3 m = A - P; - float32 b = b3Dot(m, n); - float32 c = b3Dot(m, m) - m_radius * m_radius; + scalar b = b3Dot(m, n); + scalar c = b3Dot(m, m) - m_radius * m_radius; - float32 disc = b * b - a * c; + scalar disc = b * b - a * c; // Check for negative discriminant. - if (disc < 0.0f) + if (disc < scalar(0)) { return false; } // Find the minimum time of impact of the line with the sphere. - float32 t = -b - b3Sqrt(disc); + scalar t = -b - b3Sqrt(disc); // Is the intersection point on the segment? - if (t > 0.0f && t <= input.maxFraction * a) + if (t > scalar(0) && t <= input.maxFraction * a) { // Finish solution. t /= a; @@ -337,43 +324,43 @@ bool b3CapsuleShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& inpu // Solve quadratic equation. b3Vec3 m = A - P; - float32 nd = b3Dot(n, d); - float32 mm = b3Dot(m, m); - float32 md = b3Dot(m, d); - float32 mn = b3Dot(m, n); + scalar nd = b3Dot(n, d); + scalar mm = b3Dot(m, m); + scalar md = b3Dot(m, d); + scalar mn = b3Dot(m, n); - float32 a = dd * nn - nd * nd; - if (b3Abs(a) < 2.0f * (B3_EPSILON * B3_EPSILON)) + scalar a = dd * nn - nd * nd; + if (b3Abs(a) < scalar(2) * (B3_EPSILON * B3_EPSILON)) { return false; } - float32 b = dd * mn - nd * md; - float32 c = dd * (mm - m_radius * m_radius) - md * md; + scalar b = dd * mn - nd * md; + scalar c = dd * (mm - m_radius * m_radius) - md * md; - float32 disc = b * b - a * c; + scalar disc = b * b - a * c; // Check for negative discriminant. - if (disc < 0.0f) + if (disc < scalar(0)) { return false; } // Find minimum intersection of the line with the cylinder. - float32 t = -b - b3Sqrt(disc); + scalar t = -b - b3Sqrt(disc); t /= a; // Is the intersection on the segment? - if (t < 0.0f || t > 1.0f) + if (t < scalar(0) || t > scalar(1)) { return false; } // Hemisphere check. - float32 tp = md + t * nd; + scalar tp = md + t * nd; b3Vec3 C; - if (tp < 0.0f) + if (tp < scalar(0)) { // P C = P; @@ -387,7 +374,7 @@ bool b3CapsuleShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& inpu { // PQ // Intersection is on the cylinder. - b3Vec3 X = (1.0f - t) * A + t * B; + b3Vec3 X = (scalar(1) - t) * A + t * B; b3Vec3 e2 = b3Cross(d, X - P); b3Vec3 e3 = b3Cross(e2, d); e3.Normalize(); @@ -408,7 +395,7 @@ bool b3CapsuleShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& inpu disc = b * b - a * c; // Check for negative discriminant. - if (disc < 0.0f) + if (disc < scalar(0)) { return false; } @@ -417,7 +404,7 @@ bool b3CapsuleShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& inpu t = -b - b3Sqrt(disc); // Is the intersection point on the segment? - if (t > 0.0f && t <= input.maxFraction * a) + if (t > scalar(0) && t <= input.maxFraction * a) { // Finish solution. t /= a; diff --git a/src/bounce/dynamics/shapes/hull_shape.cpp b/src/bounce/dynamics/shapes/hull_shape.cpp index 81be848..e905791 100644 --- a/src/bounce/dynamics/shapes/hull_shape.cpp +++ b/src/bounce/dynamics/shapes/hull_shape.cpp @@ -18,15 +18,16 @@ #include #include -#include +#include #include #include +#include b3HullShape::b3HullShape() { m_type = e_hullShape; m_radius = B3_HULL_RADIUS; - m_hull = NULL; + m_hull = nullptr; } b3HullShape::~b3HullShape() @@ -39,7 +40,7 @@ void b3HullShape::Swap(const b3HullShape& other) m_hull = other.m_hull; } -void b3HullShape::ComputeMass(b3MassData* massData, float32 density) const +void b3HullShape::ComputeMass(b3MassData* massData, scalar density) const { // M. Kallay - "Computing the Moment of Inertia of a Solid Defined by a Triangle Mesh" // https://github.com/erich666/jgt-code/blob/master/Volume_11/Number_2/Kallay2006/Moment_of_Inertia.cpp @@ -73,18 +74,18 @@ void b3HullShape::ComputeMass(b3MassData* massData, float32 density) const { s += m_hull->vertices[i]; } - s /= float32(m_hull->vertexCount); + s /= scalar(m_hull->vertexCount); - float32 volume = 0.0f; + scalar volume = scalar(0); b3Vec3 center; center.SetZero(); - float32 xx = 0.0f; - float32 xy = 0.0f; - float32 yy = 0.0f; - float32 xz = 0.0f; - float32 zz = 0.0f; - float32 yz = 0.0f; + scalar xx = scalar(0); + scalar xy = scalar(0); + scalar yy = scalar(0); + scalar xz = scalar(0); + scalar zz = scalar(0); + scalar yz = scalar(0); for (u32 i = 0; i < m_hull->faceCount; ++i) { @@ -105,7 +106,7 @@ void b3HullShape::ComputeMass(b3MassData* massData, float32 density) const b3Vec3 v3 = m_hull->GetVertex(i3) - s; // Signed tetrahedron volume - float32 D = b3Det(v1, v2, v3); + scalar D = b3Det(v1, v2, v3); // Contribution to the mass volume += D; @@ -146,11 +147,11 @@ void b3HullShape::ComputeMass(b3MassData* massData, float32 density) const // Center of mass B3_ASSERT(volume > B3_EPSILON); - center /= 4.0f * volume; + center /= scalar(4) * volume; massData->center = center + s; // Inertia relative to the local origin (s). - massData->I = (density / 120.0f) * I; + massData->I = (density / scalar(120)) * I; // Shift the inertia to center of mass then to the body origin. // Ib = Ic - m * c^2 + m * m.c^2 @@ -159,7 +160,7 @@ void b3HullShape::ComputeMass(b3MassData* massData, float32 density) const massData->I += massData->mass * (b3Steiner(massData->center) - b3Steiner(center)); } -void b3HullShape::ComputeAABB(b3AABB3* aabb, const b3Transform& xf) const +void b3HullShape::ComputeAABB(b3AABB* aabb, const b3Transform& xf) const { aabb->Set(m_hull->vertices, m_hull->vertexCount, xf); aabb->Extend(m_radius); @@ -167,92 +168,109 @@ void b3HullShape::ComputeAABB(b3AABB3* aabb, const b3Transform& xf) const bool b3HullShape::TestSphere(const b3Sphere& sphere, const b3Transform& xf) const { - b3Vec3 support = b3MulT(xf, sphere.vertex); - float32 radius = m_radius + sphere.radius; + b3GJKProxy proxy1; + proxy1.vertexCount = m_hull->vertexCount; + proxy1.vertices = m_hull->vertices; - for (u32 i = 0; i < m_hull->faceCount; ++i) + b3GJKProxy proxy2; + proxy2.vertexBuffer[0] = b3MulT(xf, sphere.vertex); + proxy2.vertexCount = 1; + proxy2.vertices = proxy2.vertexBuffer; + + b3GJKOutput gjk = b3GJK(b3Transform_identity, proxy1, b3Transform_identity, proxy2, false); + + if (gjk.distance <= m_radius + sphere.radius) { - b3Plane plane = m_hull->GetPlane(i); - float32 separation = b3Distance(support, plane); - - if (separation > radius) - { - return false; - } + return true; } - return true; + return false; } bool b3HullShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, const b3Transform& xf) const { - b3Transform xf1 = xf; - b3Transform xf2 = b3Transform_identity; + scalar radius = m_radius + sphere.radius; + const b3Hull* hull = m_hull; + + // Sphere center in the frame of the hull. + b3Vec3 cLocal = b3MulT(xf, sphere.vertex); + + // Find the minimum separation face. + u32 faceIndex = 0; + scalar separation = -B3_MAX_SCALAR; + + for (u32 i = 0; i < hull->faceCount; ++i) + { + b3Plane plane = hull->GetPlane(i); + scalar s = b3Distance(cLocal, plane); + + if (s > radius) + { + // Early out. + return false; + } + + if (s > separation) + { + faceIndex = i; + separation = s; + } + } + + if (separation < scalar(0)) + { + // The center is inside the hull. + b3Plane plane = b3Mul(xf, m_hull->GetPlane(faceIndex)); + + output->point = b3ClosestPointOnPlane(sphere.vertex, plane); + output->normal = plane.normal; + return true; + } + + // Vertices that subtend the incident face. + b3StackArray incidentPolygon; + + const b3Face* face = hull->GetFace(faceIndex); + const b3HalfEdge* begin = hull->GetEdge(face->edge); + const b3HalfEdge* edge = begin; + do + { + b3Vec3 vertex = hull->GetVertex(edge->origin); + incidentPolygon.PushBack(vertex); + edge = hull->GetEdge(edge->next); + } while (edge != begin); + + b3GJKProxy proxy1; + proxy1.vertexCount = incidentPolygon.Count(); + proxy1.vertices = incidentPolygon.Begin(); - b3ShapeGJKProxy proxy1(this, 0); - b3GJKProxy proxy2; - proxy2.vertexCount = 1; proxy2.vertexBuffer[0] = sphere.vertex; + proxy2.vertexCount = 1; proxy2.vertices = proxy2.vertexBuffer; - proxy2.radius = sphere.radius; - b3GJKOutput gjk = b3GJK(xf1, proxy1, xf2, proxy2); + b3Transform xf1 = xf; + b3Transform xf2; xf2.SetIdentity(); - float32 r1 = proxy1.radius; - float32 r2 = proxy2.radius; + b3GJKOutput gjk = b3GJK(xf1, proxy1, xf2, proxy2, false); - float32 totalRadius = r1 + r2; - - if (gjk.distance > totalRadius) + if (gjk.distance > radius) { return false; } - if (gjk.distance > 0.0f) + if (gjk.distance > scalar(0)) { b3Vec3 c1 = gjk.point1; b3Vec3 c2 = gjk.point2; - b3Vec3 n = (c2 - c1) / gjk.distance; + b3Vec3 normal = (c2 - c1) / gjk.distance; output->point = c1; - output->normal = n; - output->separation = gjk.distance - totalRadius; - + output->normal = normal; return true; } - // Perform computations in the local space of the first hull. - b3Vec3 support = b3MulT(xf1, sphere.vertex); - - u32 maxIndex = ~0; - float32 maxSeparation = -B3_MAX_FLOAT; - - for (u32 i = 0; i < m_hull->faceCount; ++i) - { - b3Plane plane = m_hull->GetPlane(i); - float32 separation = b3Distance(support, plane); - - if (separation > totalRadius) - { - return false; - } - - if (separation > maxSeparation) - { - maxIndex = i; - maxSeparation = separation; - } - } - - B3_ASSERT(maxIndex != ~0); - - b3Plane plane = b3Mul(xf1, m_hull->GetPlane(maxIndex)); - - output->point = b3ClosestPointOnPlane(sphere.vertex, plane); - output->separation = maxSeparation - totalRadius; - output->normal = plane.normal; - return true; + return false; } bool b3HullShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf) const @@ -261,12 +279,12 @@ bool b3HullShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Plane* planes = m_hull->planes; // Put the segment into the poly's frame of reference. - b3Vec3 p1 = b3MulT(xf.rotation, input.p1 - xf.position); - b3Vec3 p2 = b3MulT(xf.rotation, input.p2 - xf.position); + b3Vec3 p1 = b3MulC(xf.rotation, input.p1 - xf.translation); + b3Vec3 p2 = b3MulC(xf.rotation, input.p2 - xf.translation); b3Vec3 d = p2 - p1; - float32 lower = 0.0f; - float32 upper = input.maxFraction; + scalar lower = scalar(0); + scalar upper = input.maxFraction; u32 index = B3_MAX_U32; @@ -284,13 +302,13 @@ bool b3HullShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, for (u32 i = 0; i < planeCount; ++i) { - float32 numerator = planes[i].offset - b3Dot(planes[i].normal, p1); - float32 denominator = b3Dot(planes[i].normal, d); + scalar numerator = planes[i].offset - b3Dot(planes[i].normal, p1); + scalar denominator = b3Dot(planes[i].normal, d); - if (denominator == 0.0f) + if (denominator == scalar(0)) { // s is parallel to this half-space. - if (numerator < 0.0f) + if (numerator < scalar(0)) { // s is outside of this half-space. // dot(n, p1) and dot(n, p2) < 0. @@ -305,7 +323,7 @@ bool b3HullShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, // Optimized predicates: // lower * denominator > numerator // upper * denominator > numerator - if (denominator < 0.0f) + if (denominator < scalar(0)) { // s enters this half-space. if (numerator < lower * denominator) @@ -333,7 +351,7 @@ bool b3HullShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, } } - B3_ASSERT(lower >= 0.0f && lower <= input.maxFraction); + B3_ASSERT(lower >= scalar(0) && lower <= input.maxFraction); if (index != B3_MAX_U32) { diff --git a/src/bounce/dynamics/shapes/mesh_shape.cpp b/src/bounce/dynamics/shapes/mesh_shape.cpp index 55724e6..6cdb80a 100644 --- a/src/bounce/dynamics/shapes/mesh_shape.cpp +++ b/src/bounce/dynamics/shapes/mesh_shape.cpp @@ -23,7 +23,8 @@ b3MeshShape::b3MeshShape() { m_type = e_meshShape; m_radius = B3_HULL_RADIUS; - m_mesh = NULL; + m_mesh = nullptr; + m_scale.Set(scalar(1), scalar(1), scalar(1)); } b3MeshShape::~b3MeshShape() @@ -34,42 +35,34 @@ void b3MeshShape::Swap(const b3MeshShape& other) { m_radius = other.m_radius; m_mesh = other.m_mesh; + m_scale = other.m_scale; } -void b3MeshShape::ComputeMass(b3MassData* massData, float32 density) const +void b3MeshShape::ComputeMass(b3MassData* massData, scalar density) const { - B3_NOT_USED(density); - u32 n = m_mesh->vertexCount; - b3Vec3 c(0.0f, 0.0f, 0.0f); - for (u32 i = 0; i < n; ++i) - { - c += m_mesh->vertices[i]; - } - if (n > 0) - { - c = 1.0f / float32(n) * c; - } - massData->center = c; - massData->mass = 0.0f; + B3_NOT_USED(density); + massData->center.SetZero(); + massData->mass = scalar(0); massData->I.SetZero(); } -void b3MeshShape::ComputeAABB(b3AABB3* output, const b3Transform& xf) const +void b3MeshShape::ComputeAABB(b3AABB* output, const b3Transform& xf) const { - output->Set(m_mesh->vertices, m_mesh->vertexCount, xf); - output->Extend(m_radius); + b3AABB aabb; + aabb.Set(m_mesh->vertices, m_mesh->vertexCount, m_scale, xf); + aabb.Extend(m_radius); + + *output = aabb; } -void b3MeshShape::ComputeAABB(b3AABB3* output, const b3Transform& xf, u32 index) const +void b3MeshShape::ComputeAABB(b3AABB* output, const b3Transform& xf, u32 index) const { B3_ASSERT(index < m_mesh->triangleCount); - const b3Triangle* triangle = m_mesh->triangles + index; - b3Vec3 v1 = b3Mul(xf, m_mesh->vertices[triangle->v1]); - b3Vec3 v2 = b3Mul(xf, m_mesh->vertices[triangle->v2]); - b3Vec3 v3 = b3Mul(xf, m_mesh->vertices[triangle->v3]); - - output->m_lower = b3Min(b3Min(v1, v2), v3); - output->m_upper = b3Max(b3Max(v1, v2), v3); + const b3MeshTriangle* triangle = m_mesh->triangles + index; + b3Vec3 v1 = b3Mul(xf, b3MulCW(m_scale, m_mesh->vertices[triangle->v1])); + b3Vec3 v2 = b3Mul(xf, b3MulCW(m_scale, m_mesh->vertices[triangle->v2])); + b3Vec3 v3 = b3Mul(xf, b3MulCW(m_scale, m_mesh->vertices[triangle->v3])); + output->SetTriangle(v1, v2, v3); output->Extend(m_radius); } @@ -100,11 +93,11 @@ bool b3MeshShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, bool b3MeshShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf, u32 index) const { B3_ASSERT(index < m_mesh->triangleCount); - b3Triangle* triangle = m_mesh->triangles + index; + b3MeshTriangle* triangle = m_mesh->triangles + index; - b3Vec3 v1 = m_mesh->vertices[triangle->v1]; - b3Vec3 v2 = m_mesh->vertices[triangle->v2]; - b3Vec3 v3 = m_mesh->vertices[triangle->v3]; + b3Vec3 v1 = b3MulCW(m_scale, m_mesh->vertices[triangle->v1]); + b3Vec3 v2 = b3MulCW(m_scale, m_mesh->vertices[triangle->v2]); + b3Vec3 v3 = b3MulCW(m_scale, m_mesh->vertices[triangle->v3]); // Put the ray into the mesh's frame of reference. b3Vec3 p1 = b3MulT(xf, input.p1); @@ -119,7 +112,7 @@ bool b3MeshShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, if (b3RayCast(&subOutput, &subInput, v1, v2, v3)) { output->fraction = subOutput.fraction; - output->normal = xf.rotation * subOutput.normal; + output->normal = b3Mul(xf.rotation, subOutput.normal); return true; } @@ -128,7 +121,7 @@ bool b3MeshShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, struct b3MeshShapeRayCastCallback { - float32 Report(const b3RayCastInput& subInput, u32 proxyId) + scalar Report(const b3RayCastInput& subInput, u32 proxyId) { B3_NOT_USED(subInput); @@ -145,7 +138,7 @@ struct b3MeshShapeRayCastCallback } } - return 1.0f; + return scalar(1); } b3RayCastInput input; @@ -163,13 +156,22 @@ bool b3MeshShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, callback.mesh = this; callback.xf = xf; callback.hit = false; - callback.output.fraction = B3_MAX_FLOAT; + callback.output.fraction = B3_MAX_SCALAR; - b3RayCastInput subInput; - subInput.p1 = b3MulT(xf, input.p1); - subInput.p2 = b3MulT(xf, input.p2); - subInput.maxFraction = input.maxFraction; - m_mesh->tree.RayCast(&callback, subInput); + B3_ASSERT(m_scale.x != scalar(0)); + B3_ASSERT(m_scale.y != scalar(0)); + B3_ASSERT(m_scale.z != scalar(0)); + + b3Vec3 inv_scale; + inv_scale.x = scalar(1) / m_scale.x; + inv_scale.y = scalar(1) / m_scale.y; + inv_scale.z = scalar(1) / m_scale.z; + + b3RayCastInput treeInput; + treeInput.p1 = b3MulCW(inv_scale, b3MulT(xf, input.p1)); + treeInput.p2 = b3MulCW(inv_scale, b3MulT(xf, input.p2)); + treeInput.maxFraction = input.maxFraction; + m_mesh->tree.RayCast(&callback, treeInput); output->fraction = callback.output.fraction; output->normal = callback.output.normal; diff --git a/src/bounce/dynamics/shapes/sdf_shape.cpp b/src/bounce/dynamics/shapes/sdf_shape.cpp new file mode 100644 index 0000000..e1435c8 --- /dev/null +++ b/src/bounce/dynamics/shapes/sdf_shape.cpp @@ -0,0 +1,122 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include + +b3SDFShape::b3SDFShape() +{ + m_type = e_sdfShape; + m_radius = B3_HULL_RADIUS; + m_sdf = nullptr; + m_scale.Set(scalar(1), scalar(1), scalar(1)); +} + +b3SDFShape::~b3SDFShape() +{ +} + +void b3SDFShape::Swap(const b3SDFShape& other) +{ + m_radius = other.m_radius; + m_sdf = other.m_sdf; + m_scale = other.m_scale; +} + +void b3SDFShape::ComputeMass(b3MassData* massData, scalar density) const +{ + B3_NOT_USED(density); + massData->center.SetZero(); + massData->mass = scalar(0); + massData->I.SetZero(); +} + +void b3SDFShape::ComputeAABB(b3AABB* output, const b3Transform& xf) const +{ + b3AABB aabb = b3TransformAABB(m_sdf->GetDomain(), m_scale, xf); + aabb.Extend(m_radius); + + *output = aabb; +} + +bool b3SDFShape::TestSphere(const b3Sphere& sphere, const b3Transform& xf) const +{ + B3_ASSERT(m_scale.x != scalar(0)); + B3_ASSERT(m_scale.y != scalar(0)); + B3_ASSERT(m_scale.z != scalar(0)); + + b3Vec3 inv_scale; + inv_scale.x = scalar(1) / m_scale.x; + inv_scale.y = scalar(1) / m_scale.y; + inv_scale.z = scalar(1) / m_scale.z; + + b3Vec3 vertex = b3MulCW(inv_scale, b3MulT(xf, sphere.vertex)); + scalar radius = m_radius + sphere.radius; + + double distance; + bool ok = m_sdf->Evaluate(vertex, distance); + if (ok) + { + return distance <= radius; + } + + return false; +} + +bool b3SDFShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, const b3Transform& xf) const +{ + B3_ASSERT(m_scale.x != scalar(0)); + B3_ASSERT(m_scale.y != scalar(0)); + B3_ASSERT(m_scale.z != scalar(0)); + + b3Vec3 inv_scale; + inv_scale.x = scalar(1) / m_scale.x; + inv_scale.y = scalar(1) / m_scale.y; + inv_scale.z = scalar(1) / m_scale.z; + + b3Vec3 vertex = b3MulCW(inv_scale, b3MulT(xf, sphere.vertex)); + scalar radius = m_radius + sphere.radius; + + // M = R * S + // M^-1 = S^-1 * R^-1 + // (M^-1)^T = (R^-1)^T * (S^-1)^T = R * S^-1 + + double distance; + b3Vec3 normal; + bool ok = m_sdf->Evaluate(vertex, distance, &normal); + if (ok) + { + if (distance <= radius) + { + normal.Normalize(); + + output->normal = b3Mul(xf.rotation, b3MulCW(inv_scale, normal)); + output->normal.Normalize(); + output->point = sphere.vertex - distance * output->normal; + + return true; + } + } + + return false; +} + +bool b3SDFShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf) const +{ + return false; +} diff --git a/src/bounce/dynamics/shapes/shape.cpp b/src/bounce/dynamics/shapes/shape.cpp index 077b206..95ff42b 100644 --- a/src/bounce/dynamics/shapes/shape.cpp +++ b/src/bounce/dynamics/shapes/shape.cpp @@ -19,8 +19,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -28,12 +30,13 @@ #include #include #include +#include b3Shape::b3Shape() { - m_density = 0.0f; - m_friction = 0.0f; - m_restitution = 0.0f; + m_density = scalar(0); + m_friction = scalar(0); + m_restitution = scalar(0); m_isSensor = false; m_userData = nullptr; @@ -65,11 +68,16 @@ void b3Shape::DestroyContacts() } } -const b3AABB3& b3Shape::GetAABB() const +const b3AABB& b3Shape::GetAABB() const { return m_body->GetWorld()->m_contactMan.m_broadPhase.GetAABB(m_broadPhaseID); } +void b3Shape::SetShape(b3Shape* shape) +{ + m_body = shape->GetBody(); +} + void b3Shape::Dump(u32 bodyIndex) const { switch (m_type) @@ -86,11 +94,21 @@ void b3Shape::Dump(u32 bodyIndex) const { b3CapsuleShape* capsule = (b3CapsuleShape*) this; b3Log(" b3CapsuleShape shape;\n"); - b3Log(" shape.m_centers[0].Set(%f, %f, %f);\n", capsule->m_centers[0].x, capsule->m_centers[0].y, capsule->m_centers[0].z); - b3Log(" shape.m_centers[1].Set(%f, %f, %f);\n", capsule->m_centers[1].x, capsule->m_centers[1].y, capsule->m_centers[1].z); + b3Log(" shape.m_centers[0].Set(%f, %f, %f);\n", capsule->m_vertex1.x, capsule->m_vertex1.y, capsule->m_vertex1.z); + b3Log(" shape.m_centers[1].Set(%f, %f, %f);\n", capsule->m_vertex2.x, capsule->m_vertex2.y, capsule->m_vertex2.z); b3Log(" shape.m_radius = %f;\n", capsule->m_radius); break; } + case e_triangleShape: + { + b3TriangleShape* triangle = (b3TriangleShape*)this; + b3Log(" b3TriangleShape shape;\n"); + b3Log(" shape.m_vertex1.Set(%f, %f, %f);\n", triangle->m_vertex1.x, triangle->m_vertex1.y, triangle->m_vertex1.z); + b3Log(" shape.m_vertex2.Set(%f, %f, %f);\n", triangle->m_vertex2.x, triangle->m_vertex2.y, triangle->m_vertex2.z); + b3Log(" shape.m_vertex3.Set(%f, %f, %f);\n", triangle->m_vertex3.x, triangle->m_vertex3.y, triangle->m_vertex3.z); + b3Log(" shape.m_radius = %f;\n", triangle->m_radius); + break; + } case e_hullShape: { b3HullShape* hs = (b3HullShape*) this; @@ -125,6 +143,7 @@ void b3Shape::Dump(u32 bodyIndex) const b3Log(" h->edges[%d].origin = %d;\n", i, e->origin); b3Log(" h->edges[%d].twin = %d;\n", i, e->twin); b3Log(" h->edges[%d].face = %d;\n", i, e->face); + b3Log(" h->edges[%d].prev = %d;\n", i, e->prev); b3Log(" h->edges[%d].next = %d;\n", i, e->next); } b3Log(" \n"); @@ -160,8 +179,8 @@ void b3Shape::Dump(u32 bodyIndex) const b3Log(" marker += 1 * sizeof(b3Mesh);\n"); b3Log(" m->vertices = (b3Vec3*)marker;\n"); b3Log(" marker += %d * sizeof(b3Vec3);\n", m->vertexCount); - b3Log(" m->triangles = (b3Triangle*)marker;\n"); - b3Log(" marker += %d * sizeof(b3Triangle);\n", m->triangleCount); + b3Log(" m->triangles = (b3MeshTriangle*)marker;\n"); + b3Log(" marker += %d * sizeof(b3MeshTriangle);\n", m->triangleCount); b3Log(" m->planes = (b3Plane*)marker;\n"); b3Log(" marker += %d * sizeof(b3Plane);\n", 2 * m->triangleCount); b3Log(" \n"); @@ -173,7 +192,7 @@ void b3Shape::Dump(u32 bodyIndex) const b3Log(" \n"); for (u32 i = 0; i < m->triangleCount; ++i) { - const b3Triangle* t = m->triangles + i; + const b3MeshTriangle* t = m->triangles + i; b3Log(" m->triangles[%d].v1 = %d;\n", i, t->v1); b3Log(" m->triangles[%d].v2 = %d;\n", i, t->v2); b3Log(" m->triangles[%d].v3 = %d;\n", i, t->v3); @@ -187,6 +206,10 @@ void b3Shape::Dump(u32 bodyIndex) const b3Log(" shape.m_radius = %f;\n", m_radius); break; } + case e_sdfShape: + { + break; + } default: { B3_ASSERT(false); @@ -207,7 +230,7 @@ void b3Shape::Dump(u32 bodyIndex) const b3Shape* b3Shape::Create(const b3ShapeDef& def) { - b3Shape* shape = NULL; + b3Shape* shape = nullptr; switch (def.shape->GetType()) { case e_sphereShape: @@ -231,6 +254,16 @@ b3Shape* b3Shape::Create(const b3ShapeDef& def) shape = caps2; break; } + case e_triangleShape: + { + // Grab pointer to the specific memory. + b3TriangleShape* triangle1 = (b3TriangleShape*)def.shape; + void* block = b3Alloc(sizeof(b3TriangleShape)); + b3TriangleShape* triangle2 = new (block)b3TriangleShape(); + triangle2->Swap(*triangle1); + shape = triangle2; + break; + } case e_hullShape: { // Grab pointer to the specific memory. @@ -252,6 +285,17 @@ b3Shape* b3Shape::Create(const b3ShapeDef& def) shape = mesh2; break; } + case e_sdfShape: + { + // Grab pointer to the specific memory. + b3SDFShape* sdf1 = (b3SDFShape*)def.shape; + void* block = b3Alloc(sizeof(b3SDFShape)); + b3SDFShape* sdf2 = new (block) b3SDFShape(); + // Clone the SDF. + sdf2->Swap(*sdf1); + shape = sdf2; + break; + } default: { B3_ASSERT(false); @@ -281,6 +325,13 @@ void b3Shape::Destroy(b3Shape* shape) b3Free(shape); break; } + case e_triangleShape: + { + b3TriangleShape* triangle = (b3TriangleShape*)shape; + triangle->~b3TriangleShape(); + b3Free(shape); + break; + } case e_hullShape: { b3HullShape* hull = (b3HullShape*)shape; @@ -295,6 +346,13 @@ void b3Shape::Destroy(b3Shape* shape) b3Free(shape); break; } + case e_sdfShape: + { + b3SDFShape* sdf = (b3SDFShape*)shape; + sdf->~b3SDFShape(); + b3Free(shape); + break; + } default: { B3_ASSERT(false); diff --git a/src/bounce/dynamics/shapes/sphere_shape.cpp b/src/bounce/dynamics/shapes/sphere_shape.cpp index ba600c2..24ccaa6 100644 --- a/src/bounce/dynamics/shapes/sphere_shape.cpp +++ b/src/bounce/dynamics/shapes/sphere_shape.cpp @@ -22,7 +22,7 @@ b3SphereShape::b3SphereShape() { m_type = e_sphereShape; - m_radius = 0.0f; + m_radius = scalar(0); m_center.SetZero(); } @@ -36,56 +36,59 @@ void b3SphereShape::Swap(const b3SphereShape& other) m_radius = other.m_radius; } -void b3SphereShape::ComputeMass(b3MassData* massData, float32 density) const +void b3SphereShape::ComputeMass(b3MassData* massData, scalar density) const { - float32 volume = (4.0f / 3.0f) * B3_PI * m_radius * m_radius * m_radius; - float32 mass = density * volume; + scalar volume = (scalar(4) / scalar(3)) * B3_PI * m_radius * m_radius * m_radius; + scalar mass = density * volume; // Inertia about the local shape center of mass // Then shift to the local body origin - float32 I = mass * (0.4f * m_radius * m_radius + b3Dot(m_center, m_center)); + scalar I = mass * (scalar(0.4) * m_radius * m_radius + b3Dot(m_center, m_center)); massData->center = m_center; massData->mass = mass; massData->I = b3Diagonal(I); } -void b3SphereShape::ComputeAABB(b3AABB3* aabb, const b3Transform& xf) const +void b3SphereShape::ComputeAABB(b3AABB* aabb, const b3Transform& xf) const { b3Vec3 center = b3Mul(xf, m_center); b3Vec3 r(m_radius, m_radius, m_radius); - aabb->m_lower = center - r; - aabb->m_upper = center + r; + aabb->lowerBound = center - r; + aabb->upperBound = center + r; } bool b3SphereShape::TestSphere(const b3Sphere& sphere, const b3Transform& xf) const { b3Vec3 center = b3Mul(xf, m_center); - float32 radius = m_radius + sphere.radius; - float32 rr = radius * radius; + scalar radius = m_radius + sphere.radius; + scalar rr = radius * radius; b3Vec3 d = sphere.vertex - center; - float32 dd = b3Dot(d, d); + scalar dd = b3Dot(d, d); return dd <= rr; } bool b3SphereShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, const b3Transform& xf) const { b3Vec3 center = b3Mul(xf, m_center); - float32 radius = m_radius + sphere.radius; - float32 rr = radius * radius; + scalar radius = m_radius + sphere.radius; + scalar rr = radius * radius; b3Vec3 d = sphere.vertex - center; - float32 dd = b3Dot(d, d); + scalar dd = b3Dot(d, d); if (dd <= rr) { - float32 d_len = b3Sqrt(dd); + scalar distance = b3Sqrt(dd); output->point = center; - output->separation = d_len - radius; - output->normal.Set(0.0f, 1.0, 0.0f); - if (d_len > B3_EPSILON) + + if (distance > B3_EPSILON) { - output->normal = d / d_len; + output->normal = d / distance; + } + else + { + output->normal.Set(0, 1, 0); } return true; @@ -104,7 +107,7 @@ bool b3SphereShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input // m = p1 - c // d = p2 - p1 b3Vec3 d = input.p2 - input.p1; - float32 a = b3Dot(d, d); + scalar a = b3Dot(d, d); // Check for short segment. if (a < B3_EPSILON * B3_EPSILON) @@ -119,14 +122,14 @@ bool b3SphereShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input // c = dot(m, m) - r^2 // t = -b +/- sqrt(b * b - a * c) / a b3Vec3 m = input.p1 - b3Mul(xf, m_center); - float32 b = b3Dot(m, d); - float32 c = b3Dot(m, m) - m_radius * m_radius; + scalar b = b3Dot(m, d); + scalar c = b3Dot(m, m) - m_radius * m_radius; - float32 disc = b * b - a * c; + scalar disc = b * b - a * c; // Check for negative discriminant. // Does the ray misses the sphere completely? - if (disc < 0.0f) + if (disc < scalar(0)) { return false; } @@ -134,10 +137,10 @@ bool b3SphereShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input // Find the minimum time of impact of the line with the sphere. // t_min = -b - sqrt(disc) // t_max = -b + sqrt(disc) - float32 t = -b - b3Sqrt(disc); + scalar t = -b - b3Sqrt(disc); // Is the intersection point on the segment? - if (t > 0.0f && t <= input.maxFraction * a) + if (t > scalar(0) && t <= input.maxFraction * a) { // Finish solution. t /= a; diff --git a/src/bounce/dynamics/shapes/triangle_shape.cpp b/src/bounce/dynamics/shapes/triangle_shape.cpp new file mode 100644 index 0000000..aa9b84a --- /dev/null +++ b/src/bounce/dynamics/shapes/triangle_shape.cpp @@ -0,0 +1,169 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include + +b3TriangleShape::b3TriangleShape() +{ + m_type = e_triangleShape; + m_radius = B3_HULL_RADIUS; + m_hasE1Vertex = false; + m_hasE2Vertex = false; + m_hasE3Vertex = false; +} + +b3TriangleShape::~b3TriangleShape() +{ +} + +void b3TriangleShape::Swap(const b3TriangleShape& other) +{ + m_radius = other.m_radius; + m_vertex1 = other.m_vertex1; + m_vertex2 = other.m_vertex2; + m_vertex3 = other.m_vertex3; + m_hasE1Vertex = other.m_hasE1Vertex; + m_hasE2Vertex = other.m_hasE2Vertex; + m_hasE3Vertex = other.m_hasE3Vertex; + m_e1Vertex = other.m_e1Vertex; + m_e2Vertex = other.m_e2Vertex; + m_e3Vertex = other.m_e3Vertex; +} + +void b3TriangleShape::Set(const b3Vec3& v1, const b3Vec3& v2, const b3Vec3& v3) +{ + m_vertex1 = v1; + m_vertex2 = v2; + m_vertex3 = v3; + m_hasE1Vertex = false; + m_hasE2Vertex = false; + m_hasE3Vertex = false; +} + +void b3TriangleShape::ComputeMass(b3MassData* massData, scalar density) const +{ + B3_NOT_USED(density); + massData->center = scalar(0.5) * (m_vertex1 + m_vertex2 + m_vertex3); + massData->mass = 0.0f; + massData->I.SetZero(); +} + +void b3TriangleShape::ComputeAABB(b3AABB* aabb, const b3Transform& xf) const +{ + b3Vec3 lower = b3Min(m_vertex1, b3Min(m_vertex2, m_vertex3)); + b3Vec3 upper = b3Max(m_vertex1, b3Max(m_vertex2, m_vertex3)); + + b3Vec3 r(m_radius, m_radius, m_radius); + aabb->lowerBound = lower - r; + aabb->upperBound = upper + r; +} + +bool b3TriangleShape::TestSphere(const b3Sphere& sphere, const b3Transform& xf) const +{ + b3ShapeGJKProxy proxy1; + proxy1.vertexBuffer[0] = m_vertex1; + proxy1.vertexBuffer[1] = m_vertex2; + proxy1.vertexBuffer[2] = m_vertex3; + proxy1.vertexCount = 3; + proxy1.vertices = proxy1.vertexBuffer; + + b3ShapeGJKProxy proxy2; + proxy2.vertexBuffer[0] = b3MulT(xf, sphere.vertex); + proxy2.vertexCount = 1; + proxy2.vertices = proxy2.vertexBuffer; + + b3GJKOutput query = b3GJK(b3Transform_identity, proxy1, b3Transform_identity, proxy2, false); + + if (query.distance <= m_radius + sphere.radius) + { + return true; + } + + return false; +} + +bool b3TriangleShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, const b3Transform& xf) const +{ + b3ShapeGJKProxy proxy1; + proxy1.vertexBuffer[0] = m_vertex1; + proxy1.vertexBuffer[1] = m_vertex2; + proxy1.vertexBuffer[2] = m_vertex3; + proxy1.vertexCount = 3; + proxy1.vertices = proxy1.vertexBuffer; + + b3ShapeGJKProxy proxy2; + proxy2.vertexBuffer[0] = b3MulT(xf, sphere.vertex); + proxy2.vertexCount = 1; + proxy2.vertices = proxy2.vertexBuffer; + + b3GJKOutput query = b3GJK(b3Transform_identity, proxy1, b3Transform_identity, proxy2, false); + + scalar radius = m_radius + sphere.radius; + + if (query.distance <= radius) + { + b3Vec3 p1 = b3Mul(xf, query.point1); + b3Vec3 p2 = b3Mul(xf, query.point2); + scalar distance = query.distance; + + b3Vec3 n; + if (distance > B3_EPSILON) + { + n = p2 - p1; + n.Normalize(); + } + else + { + n = b3Cross(m_vertex2 - m_vertex1, m_vertex3 - m_vertex1); + n.Normalize(); + n = b3Mul(xf.rotation, n); + } + + output->point = p1; + output->normal = n; + + return true; + } + + return false; +} + +bool b3TriangleShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf) const +{ + // Put the ray into the triangle's frame of reference. + b3Vec3 p1 = b3MulT(xf, input.p1); + b3Vec3 p2 = b3MulT(xf, input.p2); + + b3RayCastInput localInput; + localInput.p1 = p1; + localInput.p2 = p2; + localInput.maxFraction = input.maxFraction; + + b3RayCastOutput localOutput; + if (b3RayCast(&localOutput, &localInput, m_vertex1, m_vertex2, m_vertex3)) + { + output->fraction = localOutput.fraction; + output->normal = b3Mul(xf.rotation, localOutput.normal); + return true; + } + + return false; +} \ No newline at end of file diff --git a/src/bounce/dynamics/world.cpp b/src/bounce/dynamics/world.cpp index 700f284..02452ae 100644 --- a/src/bounce/dynamics/world.cpp +++ b/src/bounce/dynamics/world.cpp @@ -21,21 +21,27 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include +#include extern u32 b3_allocCalls, b3_maxAllocCalls; extern u32 b3_convexCalls, b3_convexCacheHits; extern u32 b3_gjkCalls, b3_gjkIters, b3_gjkMaxIters; extern bool b3_convexCache; -b3World::b3World() : +b3World::b3World() : m_bodyBlocks(sizeof(b3Body)) { b3_allocCalls = 0; b3_maxAllocCalls = 0; - + b3_gjkCalls = 0; b3_gjkIters = 0; @@ -43,11 +49,11 @@ b3World::b3World() : b3_convexCacheHits = 0; b3_convexCache = true; - + m_flags = e_clearForcesFlag; m_sleeping = false; m_warmStarting = true; - m_gravity.Set(0.0f, -9.8f, 0.0f); + m_gravity.Set(scalar(0), scalar(-9.8), scalar(0)); } b3World::~b3World() @@ -60,7 +66,7 @@ b3World::~b3World() b->DestroyJoints(); b = b->m_next; } - + b3_allocCalls = 0; b3_maxAllocCalls = 0; @@ -87,7 +93,7 @@ b3Body* b3World::CreateBody(const b3BodyDef& def) { void* mem = m_bodyBlocks.Allocate(); b3Body* b = new(mem) b3Body(def, this); - m_bodyList.PushFront(b); + m_bodyList.PushFront(b); return b; } @@ -96,7 +102,7 @@ void b3World::DestroyBody(b3Body* b) b->DestroyShapes(); b->DestroyJoints(); b->DestroyContacts(); - + m_bodyList.Remove(b); b->~b3Body(); m_bodyBlocks.Free(b); @@ -112,13 +118,13 @@ void b3World::DestroyJoint(b3Joint* j) m_jointMan.Destroy(j); } -void b3World::Step(float32 dt, u32 velocityIterations, u32 positionIterations) +void b3World::Step(scalar dt, u32 velocityIterations, u32 positionIterations) { B3_PROFILE("Step"); // Clear statistics b3_allocCalls = 0; - + b3_convexCalls = 0; b3_convexCacheHits = 0; @@ -137,16 +143,16 @@ void b3World::Step(float32 dt, u32 velocityIterations, u32 positionIterations) m_contactMan.UpdateContacts(); // Integrate velocities, clear forces and torques, solve constraints, integrate positions. - if (dt > 0.0f) + if (dt > scalar(0)) { Solve(dt, velocityIterations, positionIterations); } } -void b3World::Solve(float32 dt, u32 velocityIterations, u32 positionIterations) +void b3World::Solve(scalar dt, u32 velocityIterations, u32 positionIterations) { B3_PROFILE("Solve"); - + // Clear all visited flags for the depth first search. for (b3Body* b = m_bodyList.m_head; b; b = b->m_next) { @@ -170,11 +176,11 @@ void b3World::Solve(float32 dt, u32 velocityIterations, u32 positionIterations) b3Vec3 externalForce = m_gravity; // Create a worst case island. - b3Island island(&m_stackAllocator, m_bodyList.m_count, m_contactMan.m_contactList.m_count, m_jointMan.m_jointList.m_count); + b3Island island(&m_stackAllocator, m_bodyList.m_count, m_contactMan.m_contactList.m_count, m_jointMan.m_jointList.m_count, m_contactMan.m_contactListener); // Build and simulate awake islands. u32 stackSize = m_bodyList.m_count; - b3Body** stack = (b3Body**)m_stackAllocator.Allocate(stackSize * sizeof(b3Body*)); + b3Body** stack = (b3Body * *)m_stackAllocator.Allocate(stackSize * sizeof(b3Body*)); for (b3Body* seed = m_bodyList.m_head; seed; seed = seed->m_next) { // The seed must not be on an island. @@ -184,7 +190,7 @@ void b3World::Solve(float32 dt, u32 velocityIterations, u32 positionIterations) } // Bodies that are sleeping are not solved for performance. - if (!(seed->m_flags & b3Body::e_awakeFlag)) + if (seed->IsAwake() == false) { continue; } @@ -206,7 +212,7 @@ void b3World::Solve(float32 dt, u32 velocityIterations, u32 positionIterations) // Add this body to the island. b3Body* b = stack[--stackCount]; island.Add(b); - + // This body must be awake. b->m_flags |= b3Body::e_awakeFlag; @@ -235,6 +241,12 @@ void b3World::Solve(float32 dt, u32 velocityIterations, u32 positionIterations) continue; } + // The contact must have at least one dynamic body. + if (contact->HasDynamicBody() == false) + { + continue; + } + // A sensor can't respond to contacts. bool sensorA = contact->GetShapeA()->m_isSensor; bool sensorB = contact->GetShapeB()->m_isSensor; @@ -243,6 +255,15 @@ void b3World::Solve(float32 dt, u32 velocityIterations, u32 positionIterations) continue; } + // Does a contact filter prevent the contact response? + if (m_contactMan.m_contactFilter) + { + if (m_contactMan.m_contactFilter->ShouldRespond(contact->GetShapeA(), contact->GetShapeB()) == false) + { + continue; + } + } + // Add contact to the island and mark it. island.Add(contact); contact->m_flags |= b3Contact::e_islandFlag; @@ -294,7 +315,7 @@ void b3World::Solve(float32 dt, u32 velocityIterations, u32 positionIterations) // Integrate velocities, clear forces and torques, solve constraints, integrate positions. island.Solve(externalForce, dt, velocityIterations, positionIterations, islandFlags); - + // Allow static bodies to participate in other islands. for (u32 i = 0; i < island.m_bodyCount; ++i) { @@ -338,12 +359,19 @@ void b3World::Solve(float32 dt, u32 velocityIterations, u32 positionIterations) struct b3ShapeRayCastCallback { - float32 Report(const b3RayCastInput& input, u32 proxyId) + scalar Report(const b3RayCastInput& input, u32 proxyId) { // Get shape associated with the proxy. void* userData = broadPhase->GetUserData(proxyId); b3Shape* shape = (b3Shape*)userData; - + + // Does a ray-cast filter prevents the ray-cast? + if (filter->ShouldRayCast(shape) == false) + { + // Continue search from where we stopped. + return input.maxFraction; + } + // Calculate transformation from shape local space to world space. b3Transform xf = shape->GetBody()->GetTransform(); @@ -352,10 +380,10 @@ struct b3ShapeRayCastCallback if (hit) { // Ray hits shape. - float32 fraction = output.fraction; - float32 w1 = 1.0f - fraction; - float32 w2 = fraction; - + scalar fraction = output.fraction; + scalar w1 = scalar(1) - fraction; + scalar w2 = fraction; + b3Vec3 point = w1 * input.p1 + w2 * input.p2; b3Vec3 normal = output.normal; @@ -368,30 +396,39 @@ struct b3ShapeRayCastCallback } b3RayCastListener* listener; + b3RayCastFilter* filter; const b3BroadPhase* broadPhase; }; -void b3World::RayCast(b3RayCastListener* listener, const b3Vec3& p1, const b3Vec3& p2) const +void b3World::RayCast(b3RayCastListener* listener, b3RayCastFilter* filter, const b3Vec3& p1, const b3Vec3& p2) const { b3RayCastInput input; input.p1 = p1; input.p2 = p2; - input.maxFraction = 1.0f; - + input.maxFraction = scalar(1); + b3ShapeRayCastCallback callback; callback.listener = listener; + callback.filter = filter; callback.broadPhase = &m_contactMan.m_broadPhase; m_contactMan.m_broadPhase.RayCast(&callback, input); } struct b3RayCastSingleShapeCallback { - float32 Report(const b3RayCastInput& input, u32 proxyId) + scalar Report(const b3RayCastInput& input, u32 proxyId) { // Get shape associated with the proxy. void* userData = broadPhase->GetUserData(proxyId); b3Shape* shape = (b3Shape*)userData; + // Does a ray-cast filter prevents the ray-cast? + if (filter->ShouldRayCast(shape) == false) + { + // Continue search from where we stopped. + return input.maxFraction; + } + // Get map from shape local space to world space. b3Transform xf = shape->GetBody()->GetTransform(); @@ -412,59 +449,426 @@ struct b3RayCastSingleShapeCallback } b3Shape* shape0; - b3RayCastOutput output0; + b3RayCastOutput output0; const b3BroadPhase* broadPhase; + b3RayCastFilter* filter; }; -bool b3World::RayCastSingle(b3RayCastSingleOutput* output, const b3Vec3& p1, const b3Vec3& p2) const +bool b3World::RayCastSingle(b3RayCastSingleOutput* output, b3RayCastFilter* filter, const b3Vec3& p1, const b3Vec3& p2) const { b3RayCastInput input; input.p1 = p1; input.p2 = p2; - input.maxFraction = 1.0f; + input.maxFraction = scalar(1); b3RayCastSingleShapeCallback callback; - callback.shape0 = NULL; - callback.output0.fraction = B3_MAX_FLOAT; + callback.shape0 = nullptr; + callback.output0.fraction = B3_MAX_SCALAR; callback.broadPhase = &m_contactMan.m_broadPhase; - + callback.filter = filter; + // Perform the ray cast. m_contactMan.m_broadPhase.RayCast(&callback, input); if (callback.shape0) { // Ray hits closest shape. - float32 fraction = callback.output0.fraction; - b3Vec3 point = (1.0f - fraction) * input.p1 + fraction * input.p2; + scalar fraction = callback.output0.fraction; + b3Vec3 point = (scalar(1) - fraction) * input.p1 + fraction * input.p2; b3Vec3 normal = callback.output0.normal; output->shape = callback.shape0; output->point = point; output->normal = normal; output->fraction = fraction; - + return true; } - + return false; } +struct b3ConvexCastQueryCallback +{ + struct MeshCallback + { + bool Report(u32 proxyId) + { + u32 triangleIndex = callback->meshShapeB->m_mesh->tree.GetUserData(proxyId); + + b3Body* bodyB = callback->meshShapeB->GetBody(); + b3Transform xfB = bodyB->GetTransform(); + b3ShapeGJKProxy proxyB(callback->meshShapeB, triangleIndex); + + b3TOIOutput toi = b3TimeOfImpact(callback->xfA, *callback->proxyA, callback->dA, xfB, proxyB, b3Vec3_zero); + + b3TOIOutput::State state = toi.state; + scalar fraction = toi.t; + + if (state == b3TOIOutput::e_touching) + { + if (fraction > callback->maxFraction) + { + return true; + } + + if (fraction < callback->fraction0) + { + callback->fraction0 = fraction; + callback->shape0 = callback->meshShapeB; + callback->childIndex0 = triangleIndex; + } + + if (callback->listener) + { + b3Transform xf; + xf.rotation = callback->xfA.rotation; + xf.translation = callback->xfA.translation + fraction * callback->dA; + + b3Vec3 point, normal; + callback->Evaluate(&point, &normal, xf, *callback->proxyA, xfB, proxyB); + + callback->maxFraction = callback->listener->ReportShape(callback->meshShapeB, point, normal, fraction); + if (callback->maxFraction == scalar(0)) + { + return false; + } + } + + return true; + } + + return true; + } + + b3ConvexCastQueryCallback* callback; + }; + + bool Report(u32 proxyId) + { + void* userData = broadPhase->GetUserData(proxyId); + b3Shape* shapeB = (b3Shape*)userData; + + if (filter->ShouldConvexCast(shapeB) == false) + { + return true; + } + + if (shapeB->GetType() == e_sdfShape) + { + return true; + } + + b3Body* bodyB = shapeB->GetBody(); + b3Transform xfB = bodyB->GetTransform(); + + if (shapeB->GetType() == e_meshShape) + { + meshShapeB = (b3MeshShape*)shapeB; + + B3_ASSERT(meshShapeB->m_scale.x != scalar(0)); + B3_ASSERT(meshShapeB->m_scale.y != scalar(0)); + B3_ASSERT(meshShapeB->m_scale.z != scalar(0)); + + b3Vec3 inv_scale; + inv_scale.x = scalar(1) / meshShapeB->m_scale.x; + inv_scale.y = scalar(1) / meshShapeB->m_scale.y; + inv_scale.z = scalar(1) / meshShapeB->m_scale.z; + + b3Transform xf = b3MulT(xfB, xfA); + + // Compute the aabb in the space of the unscaled tree + b3AABB aabb; + shapeA->ComputeAABB(&aabb, xf); + aabb = b3ScaleAABB(aabb, inv_scale); + + // Compute the displacement in the space of the unscaled tree + b3Vec3 displacement = b3MulC(xfB.rotation, dA); + displacement = b3MulCW(inv_scale, displacement); + + if (displacement.x < scalar(0)) + { + aabb.lowerBound.x += displacement.x; + } + else + { + aabb.upperBound.x += displacement.x; + } + + if (displacement.y < scalar(0)) + { + aabb.lowerBound.y += displacement.y; + } + else + { + aabb.upperBound.y += displacement.y; + } + + if (displacement.z < scalar(0)) + { + aabb.lowerBound.z += displacement.z; + } + else + { + aabb.upperBound.z += displacement.z; + } + + MeshCallback callback; + callback.callback = this; + + meshShapeB->m_mesh->tree.QueryAABB(&callback, aabb); + + if (maxFraction == scalar(0)) + { + return false; + } + + return true; + } + + b3ShapeGJKProxy proxyB(shapeB, 0); + + b3TOIOutput toi = b3TimeOfImpact(xfA, *proxyA, dA, xfB, proxyB, b3Vec3_zero); + + b3TOIOutput::State state = toi.state; + scalar fraction = toi.t; + + if (state == b3TOIOutput::e_touching) + { + if (fraction > maxFraction) + { + return true; + } + + if (fraction < fraction0) + { + fraction0 = fraction; + shape0 = shapeB; + childIndex0 = 0; + } + + if (listener) + { + b3Transform xf; + xf.rotation = xfA.rotation; + xf.translation = xfA.translation + fraction * dA; + + b3Vec3 point, normal; + Evaluate(&point, &normal, xf, *proxyA, xfB, proxyB); + + maxFraction = listener->ReportShape(shapeB, point, normal, fraction); + if (maxFraction == scalar(0)) + { + return false; + } + } + + return true; + } + + return true; + } + + // Evaluate when touching + void Evaluate(b3Vec3* pointB, b3Vec3* normalB, + const b3Transform& xA, const b3GJKProxy& proxyA, const b3Transform& xB, const b3GJKProxy& proxyB) const + { + b3GJKOutput query = b3GJK(xA, proxyA, xB, proxyB, false); + + b3Vec3 pA = query.point1; + b3Vec3 pB = query.point2; + + b3Vec3 normal = b3Normalize(pA - pB); + + *pointB = pB + proxyB.radius * normal; + *normalB = normal; + } + + b3Transform xfA; + const b3Shape* shapeA; + const b3ShapeGJKProxy* proxyA; + b3Vec3 dA; + scalar maxFraction; + + b3ConvexCastListener* listener; + b3ConvexCastFilter* filter; + const b3BroadPhase* broadPhase; + + b3MeshShape* meshShapeB; + + b3Shape* shape0; + u32 childIndex0; + scalar fraction0; +}; + +void b3World::ConvexCast(b3ConvexCastListener* listener, b3ConvexCastFilter* filter, + const b3Shape* shape, const b3Vec3& displacement) const +{ + B3_ASSERT(shape->m_type != e_meshShape); + B3_ASSERT(shape->m_type != e_sdfShape); + + if (shape->m_type == e_meshShape || shape->m_type == e_sdfShape) + { + return; + } + + const b3Body* body = shape->GetBody(); + b3Transform xf = body->GetTransform(); + b3ShapeGJKProxy proxyA(shape, 0); + + b3AABB aabb = shape->GetAABB(); + + if (displacement.x < scalar(0)) + { + aabb.lowerBound.x += displacement.x; + } + else + { + aabb.upperBound.x += displacement.x; + } + + if (displacement.y < scalar(0)) + { + aabb.lowerBound.y += displacement.y; + } + else + { + aabb.upperBound.y += displacement.y; + } + + if (displacement.z < scalar(0)) + { + aabb.lowerBound.z += displacement.z; + } + else + { + aabb.upperBound.z += displacement.z; + } + + b3ConvexCastQueryCallback callback; + callback.listener = listener; + callback.filter = filter; + callback.proxyA = &proxyA; + callback.xfA = xf; + callback.shapeA = shape; + callback.dA = displacement; + callback.maxFraction = scalar(1); + callback.shape0 = nullptr; + callback.fraction0 = B3_MAX_SCALAR; + callback.childIndex0 = B3_MAX_U32; + callback.broadPhase = &m_contactMan.m_broadPhase; + + m_contactMan.m_broadPhase.QueryAABB(&callback, aabb); +} + +bool b3World::ConvexCastSingle(b3ConvexCastSingleOutput* output, b3ConvexCastFilter* filter, + const b3Shape* shape, const b3Vec3& displacement) const +{ + B3_ASSERT(shape->m_type != e_meshShape); + B3_ASSERT(shape->m_type != e_sdfShape); + + if (shape->m_type == e_meshShape || shape->m_type == e_sdfShape) + { + return false; + } + + const b3Body* body = shape->GetBody(); + b3Transform xf = body->GetTransform(); + b3ShapeGJKProxy proxyA(shape, 0); + + b3AABB aabb = shape->GetAABB(); + + if (displacement.x < scalar(0)) + { + aabb.lowerBound.x += displacement.x; + } + else + { + aabb.upperBound.x += displacement.x; + } + + if (displacement.y < scalar(0)) + { + aabb.lowerBound.y += displacement.y; + } + else + { + aabb.upperBound.y += displacement.y; + } + + if (displacement.z < scalar(0)) + { + aabb.lowerBound.z += displacement.z; + } + else + { + aabb.upperBound.z += displacement.z; + } + + b3ConvexCastQueryCallback callback; + callback.listener = nullptr; + callback.filter = filter; + callback.proxyA = &proxyA; + callback.xfA = xf; + callback.shapeA = shape; + callback.dA = displacement; + callback.maxFraction = scalar(1); + callback.shape0 = nullptr; + callback.childIndex0 = B3_MAX_U32; + callback.fraction0 = B3_MAX_SCALAR; + callback.broadPhase = &m_contactMan.m_broadPhase; + + m_contactMan.m_broadPhase.QueryAABB(&callback, aabb); + + if (callback.shape0 == nullptr) + { + return false; + } + + b3ShapeGJKProxy proxyB(callback.shape0, callback.childIndex0); + + b3Body* bodyB = callback.shape0->GetBody(); + b3Transform xfB = bodyB->GetTransform(); + + b3Transform xft; + xft.translation = xf.translation + callback.fraction0 * displacement; + xft.rotation = xf.rotation; + + b3Vec3 point, normal; + callback.Evaluate(&point, &normal, xft, proxyA, xfB, proxyB); + + output->shape = callback.shape0; + output->fraction = callback.fraction0; + output->point = point; + output->normal = normal; + + return true; +} + struct b3QueryAABBCallback { bool Report(u32 proxyID) { b3Shape* shape = (b3Shape*)broadPhase->GetUserData(proxyID); - return listener->ReportShape(shape); + + if (filter->ShouldReport(shape)) + { + return listener->ReportShape(shape); + } + + return true; } b3QueryListener* listener; + b3QueryFilter* filter; const b3BroadPhase* broadPhase; }; -void b3World::QueryAABB(b3QueryListener* listener, const b3AABB3& aabb) const +void b3World::QueryAABB(b3QueryListener* listener, b3QueryFilter* filter, const b3AABB& aabb) const { b3QueryAABBCallback callback; callback.listener = listener; + callback.filter = filter; callback.broadPhase = &m_contactMan.m_broadPhase; m_contactMan.m_broadPhase.QueryAABB(&callback, aabb); } diff --git a/src/bounce/garment/triangle/A.poly b/src/bounce/garment/triangle/A.poly deleted file mode 100644 index 166a717..0000000 --- a/src/bounce/garment/triangle/A.poly +++ /dev/null @@ -1,62 +0,0 @@ -29 2 1 0 -1 0.200000 -0.776400 -0.57 -2 0.220000 -0.773200 -0.55 -3 0.245600 -0.756400 -0.51 -4 0.277600 -0.702000 -0.53 -5 0.488800 -0.207600 0.28 -6 0.504800 -0.207600 0.30 -7 0.740800 -0.739600 0 -8 0.756000 -0.761200 -0.01 -9 0.774400 -0.772400 0 -10 0.800000 -0.776400 0.02 -11 0.800000 -0.792400 0.01 -12 0.579200 -0.792400 -0.21 -13 0.579200 -0.776400 -0.2 -14 0.621600 -0.771600 -0.15 -15 0.633600 -0.762800 -0.13 -16 0.639200 -0.744400 -0.1 -17 0.620800 -0.684400 -0.06 -18 0.587200 -0.604400 -0.01 -19 0.360800 -0.604400 -0.24 -20 0.319200 -0.706800 -0.39 -21 0.312000 -0.739600 -0.43 -22 0.318400 -0.761200 -0.44 -23 0.334400 -0.771600 -0.44 -24 0.371200 -0.776400 -0.41 -25 0.371200 -0.792400 -0.42 -26 0.374400 -0.570000 -0.2 -27 0.574400 -0.570000 0 -28 0.473600 -0.330800 0.14 -29 0.200000 -0.792400 -0.59 -29 0 -1 29 1 -2 1 2 -3 2 3 -4 3 4 -5 4 5 -6 5 6 -7 6 7 -8 7 8 -9 8 9 -10 9 10 -11 10 11 -12 11 12 -13 12 13 -14 13 14 -15 14 15 -16 15 16 -17 16 17 -18 17 18 -19 18 19 -20 19 20 -21 20 21 -22 21 22 -23 22 23 -24 23 24 -25 24 25 -26 25 29 -27 26 27 -28 27 28 -29 28 26 -1 -1 0.47 -0.5 diff --git a/src/bounce/garment/triangle/README b/src/bounce/garment/triangle/README deleted file mode 100644 index b33ea00..0000000 --- a/src/bounce/garment/triangle/README +++ /dev/null @@ -1,198 +0,0 @@ -Triangle -A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator. -Version 1.6 - -Show Me -A Display Program for Meshes and More. -Version 1.6 - -Copyright 1993, 1995, 1997, 1998, 2002, 2005 Jonathan Richard Shewchuk -2360 Woolsey #H -Berkeley, California 94705-1927 -Please send bugs and comments to jrs@cs.berkeley.edu - -Created as part of the Quake project (tools for earthquake simulation). -Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship. -There is no warranty whatsoever. Use at your own risk. - - -Triangle generates exact Delaunay triangulations, constrained Delaunay -triangulations, conforming Delaunay triangulations, Voronoi diagrams, and -high-quality triangular meshes. The latter can be generated with no small -or large angles, and are thus suitable for finite element analysis. -Show Me graphically displays the contents of the geometric files used by -Triangle. Show Me can also write images in PostScript form. - -Information on the algorithms used by Triangle, including complete -references, can be found in the comments at the beginning of the triangle.c -source file. Another listing of these references, with PostScript copies -of some of the papers, is available from the Web page - - http://www.cs.cmu.edu/~quake/triangle.research.html - ------------------------------------------------------------------------------- - -These programs may be freely redistributed under the condition that the -copyright notices (including the copy of this notice in the code comments -and the copyright notice printed when the `-h' switch is selected) are -not removed, and no compensation is received. Private, research, and -institutional use is free. You may distribute modified versions of this -code UNDER THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT -IN THE SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH -SOURCE AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND -CLEAR NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as -part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT -WITH THE AUTHOR. (If you are not directly supplying this code to a -customer, and you are instead telling them how they can obtain it for -free, then you are not required to make any arrangement with me.) - ------------------------------------------------------------------------------- - -The files included in this distribution are: - - README The file you're reading now. - triangle.c Complete C source code for Triangle. - showme.c Complete C source code for Show Me. - triangle.h Include file for calling Triangle from another program. - tricall.c Sample program that calls Triangle. - makefile Makefile for compiling Triangle and Show Me. - A.poly A sample input file. - -Each of Triangle and Show Me is a single portable C file. The easiest way -to compile them is to edit and use the included makefile. Before -compiling, read the makefile, which describes your options, and edit it -accordingly. You should specify: - - The source and binary directories. - - The C compiler and level of optimization. - - The "correct" directories for include files (especially X include files), - if necessary. - - Do you want single precision or double? (The default is double.) Do you - want to leave out some of Triangle's features to reduce the size of the - executable file? Investigate the SINGLE, REDUCED, and CDT_ONLY symbols. - - If yours is not a Unix system, define the NO_TIMER symbol to remove the - Unix-specific timing code. Also, don't try to compile Show Me; it only - works with X Windows. - - If you are compiling on an Intel x86 CPU and using gcc w/Linux or - Microsoft C, be sure to define the LINUX or CPU86 (for Microsoft) symbol - during compilation so that the exact arithmetic works right. - -Once you've done this, type "make" to compile the programs. Alternatively, -the files are usually easy to compile without a makefile: - - cc -O -o triangle triangle.c -lm - cc -O -o showme showme.c -lX11 - -On some systems, the C compiler won't be able to find the X include files -or libraries, and you'll need to specify an include path or library path: - - cc -O -I/usr/local/include -o showme showme.c -L/usr/local/lib -lX11 - -Some processors, including Intel x86 family and possibly Motorola 68xxx -family chips, are IEEE conformant but have extended length internal -floating-point registers that may defeat Triangle's exact arithmetic -routines by failing to cause enough roundoff error! Typically, there is a -way to set these internal registers so that they are rounded off to IEEE -single or double precision format. I believe (but I'm not certain) that -Triangle has the right incantations for x86 chips, if you have gcc running -under Linux (define the LINUX compiler symbol) or Microsoft C (define the -CPU86 compiler symbol). - -If you have a different processor or operating system, or if I got the -incantations wrong, you should check your C compiler or system manuals to -find out how to configure these internal registers to the precision you are -using. Otherwise, the exact arithmetic routines won't be exact at all. -See http://www.cs.cmu.edu/~quake/robust.pc.html for details. Triangle's -exact arithmetic hasn't a hope of working on machines like the Cray C90 or -Y-MP, which are not IEEE conformant and have inaccurate rounding. - -Triangle and Show Me have both text and HTML documentation. The latter is -illustrated. Find it on the Web at - - http://www.cs.cmu.edu/~quake/triangle.html - http://www.cs.cmu.edu/~quake/showme.html - -Complete text instructions are printed by invoking each program with the -`-h' switch: - - triangle -h - showme -h - -The instructions are long; you'll probably want to pipe the output to -`more' or `lpr' or redirect it to a file. - -Both programs give a short list of command line options if they are invoked -without arguments (that is, just type `triangle' or `showme'). - -Try out Triangle on the enclosed sample file, A.poly: - - triangle -p A - showme A.poly & - -Triangle will read the Planar Straight Line Graph defined by A.poly, and -write its constrained Delaunay triangulation to A.1.node and A.1.ele. -Show Me will display the figure defined by A.poly. There are two buttons -marked "ele" in the Show Me window; click on the top one. This will cause -Show Me to load and display the triangulation. - -For contrast, try running - - triangle -pq A - -Now, click on the same "ele" button. A new triangulation will be loaded; -this one having no angles smaller than 20 degrees. - -To see a Voronoi diagram, try this: - - cp A.poly A.node - triangle -v A - -Click the "ele" button again. You will see the Delaunay triangulation of -the points in A.poly, without the segments. Now click the top "voro" button. -You will see the Voronoi diagram corresponding to that Delaunay triangulation. -Click the "Reset" button to see the full extent of the diagram. - ------------------------------------------------------------------------------- - -If you wish to call Triangle from another program, instructions for doing -so are contained in the file `triangle.h' (but read Triangle's regular -instructions first!). Also look at `tricall.c', which provides an example -of how to call Triangle. - -Type "make trilibrary" to create triangle.o, a callable object file. -Alternatively, the object file is usually easy to compile without a -makefile: - - cc -DTRILIBRARY -O -c triangle.c - -Type "make distclean" to remove all the object and executable files created -by make. - ------------------------------------------------------------------------------- - -If you use Triangle, and especially if you use it to accomplish real work, -I would like very much to hear from you. A short letter or email (to -jrs@cs.berkeley.edu) describing how you use Triangle will mean a lot to me. -The more people I know are using this program, the more easily I can -justify spending time on improvements and on the three-dimensional -successor to Triangle, which in turn will benefit you. Also, I can put you -on a list to receive email whenever a new version of Triangle is available. - -If you use a mesh generated by Triangle or plotted by Show Me in a -publication, please include an acknowledgment as well. And please spell -Triangle with a capital `T'! If you want to include a citation, use -`Jonathan Richard Shewchuk, ``Triangle: Engineering a 2D Quality Mesh -Generator and Delaunay Triangulator,'' in Applied Computational Geometry: -Towards Geometric Engineering (Ming C. Lin and Dinesh Manocha, editors), -volume 1148 of Lecture Notes in Computer Science, pages 203-222, -Springer-Verlag, Berlin, May 1996. (From the First ACM Workshop on Applied -Computational Geometry.)' - - -Jonathan Richard Shewchuk -July 27, 2005 diff --git a/src/bounce/garment/triangle/makefile b/src/bounce/garment/triangle/makefile deleted file mode 100644 index b0c4f56..0000000 --- a/src/bounce/garment/triangle/makefile +++ /dev/null @@ -1,116 +0,0 @@ -# makefile for Triangle and Show Me -# -# Type "make" to compile Triangle and Show Me. -# -# After compiling, type "triangle -h" and "showme -h" to read instructions -# for using each of these programs. -# -# Type "make trilibrary" to compile Triangle as an object file (triangle.o). -# -# Type "make distclean" to delete all object and executable files. - -# SRC is the directory in which the C source files are, and BIN is the -# directory where you want to put the executable programs. By default, -# both are the current directory. - -SRC = ./ -BIN = ./ - -# CC should be set to the name of your favorite C compiler. - -CC = cc - -# CSWITCHES is a list of all switches passed to the C compiler. I strongly -# recommend using the best level of optimization. I also strongly -# recommend timing each level of optimization to see which is the -# best. For instance, when I had a DEC Alpha using DEC's optimizing -# compiler, the -O2 switch generated a notably faster version of Triangle -# than the -O3 switch. Go figure. -# -# By default, Triangle and Show Me use double precision floating point -# numbers. If you prefer single precision, use the -DSINGLE switch. -# Double precision uses more memory, but improves the resolution of -# the meshes you can generate with Triangle. It also reduces the -# likelihood of a floating exception due to overflow. Also, it is -# much faster than single precision on many architectures. I recommend -# double precision unless you want to generate a mesh for which you do -# not have enough memory to use double precision. -# -# If yours is not a Unix system, use the -DNO_TIMER switch to eliminate the -# Unix-specific timer code. Also, don't try to compile Show Me; it only -# works with X Windows. -# -# To get the exact arithmetic to work right on an Intel processor, use the -# -DCPU86 switch with Microsoft C, or the -DLINUX switch with gcc running -# on Linux. The floating-point arithmetic might not be robust otherwise. -# Please see http://www.cs.cmu.edu/~quake/robust.pc.html for details. -# -# If you are modifying Triangle, I recommend using the -DSELF_CHECK switch -# while you are debugging. Defining the SELF_CHECK symbol causes -# Triangle to include self-checking code. Triangle will execute more -# slowly, however, so be sure to remove this switch before compiling a -# production version. -# -# If the size of the Triangle binary is important to you, you may wish to -# generate a reduced version of Triangle. The -DREDUCED switch gets rid -# of all features that are primarily of research interest. Specifically, -# defining the REDUCED symbol eliminates the -i, -F, -s, and -C switches. -# The -DCDT_ONLY switch gets rid of all meshing algorithms above and beyond -# constrained Delaunay triangulation. Specifically, defining the CDT_ONLY -# symbol eliminates the -r, -q, -a, -u, -D, -S, and -s switches. The -# REDUCED and CDT_ONLY symbols may be particularly attractive when Triangle -# is called by another program that does not need all of Triangle's -# features; in this case, these switches should appear as part of -# "TRILIBDEFS" below. -# -# On some systems, you may need to include -I/usr/local/include and/or -# -L/usr/local/lib in the compiler options to ensure that the X include -# files and libraries that Show Me needs are found. If you get errors -# like "Can't find include file X11/Xlib.h", you need the former switch. -# Try compiling without them first; add them if that fails. -# -# An example CSWITCHES line is: -# -# CSWITCHES = -O -DNO_TIMER -DLINUX -I/usr/X11R6/include -L/usr/X11R6/lib - -CSWITCHES = -O -DLINUX -I/usr/X11R6/include -L/usr/X11R6/lib - -# TRILIBDEFS is a list of definitions used to compile an object code version -# of Triangle (triangle.o) to be called by another program. The file -# "triangle.h" contains detailed information on how to call triangle.o. -# -# The -DTRILIBRARY should always be used when compiling Triangle into an -# object file. -# -# An example TRILIBDEFS line is: -# -# TRILIBDEFS = -DTRILIBRARY -DREDUCED -DCDT_ONLY - -TRILIBDEFS = -DTRILIBRARY - -# RM should be set to the name of your favorite rm (file deletion program). - -RM = /bin/rm - -# The action starts here. - -all: $(BIN)triangle $(BIN)showme - -trilibrary: $(BIN)triangle.o $(BIN)tricall - -$(BIN)triangle: $(SRC)triangle.c - $(CC) $(CSWITCHES) -o $(BIN)triangle $(SRC)triangle.c -lm - -$(BIN)tricall: $(BIN)tricall.c $(BIN)triangle.o - $(CC) $(CSWITCHES) -o $(BIN)tricall $(SRC)tricall.c \ - $(BIN)triangle.o -lm - -$(BIN)triangle.o: $(SRC)triangle.c $(SRC)triangle.h - $(CC) $(CSWITCHES) $(TRILIBDEFS) -c -o $(BIN)triangle.o \ - $(SRC)triangle.c - -$(BIN)showme: $(SRC)showme.c - $(CC) $(CSWITCHES) -o $(BIN)showme $(SRC)showme.c -lX11 - -distclean: - $(RM) $(BIN)triangle $(BIN)triangle.o $(BIN)tricall $(BIN)showme diff --git a/src/bounce/garment/triangle/triangle.c b/src/bounce/garment/triangle/triangle.c deleted file mode 100644 index c75286d..0000000 --- a/src/bounce/garment/triangle/triangle.c +++ /dev/null @@ -1,16006 +0,0 @@ -/*****************************************************************************/ -/* */ -/* 888888888 ,o, / 888 */ -/* 888 88o88o " o8888o 88o8888o o88888o 888 o88888o */ -/* 888 888 888 88b 888 888 888 888 888 d888 88b */ -/* 888 888 888 o88^o888 888 888 "88888" 888 8888oo888 */ -/* 888 888 888 C888 888 888 888 / 888 q888 */ -/* 888 888 888 "88o^888 888 888 Cb 888 "88oooo" */ -/* "8oo8D */ -/* */ -/* A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator. */ -/* (triangle.c) */ -/* */ -/* Version 1.6 */ -/* July 28, 2005 */ -/* */ -/* Copyright 1993, 1995, 1997, 1998, 2002, 2005 */ -/* Jonathan Richard Shewchuk */ -/* 2360 Woolsey #H */ -/* Berkeley, California 94705-1927 */ -/* jrs@cs.berkeley.edu */ -/* */ -/* This program may be freely redistributed under the condition that the */ -/* copyright notices (including this entire header and the copyright */ -/* notice printed when the `-h' switch is selected) are not removed, and */ -/* no compensation is received. Private, research, and institutional */ -/* use is free. You may distribute modified versions of this code UNDER */ -/* THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE */ -/* SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH SOURCE */ -/* AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND CLEAR */ -/* NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as */ -/* part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT */ -/* WITH THE AUTHOR. (If you are not directly supplying this code to a */ -/* customer, and you are instead telling them how they can obtain it for */ -/* free, then you are not required to make any arrangement with me.) */ -/* */ -/* Hypertext instructions for Triangle are available on the Web at */ -/* */ -/* http://www.cs.cmu.edu/~quake/triangle.html */ -/* */ -/* Disclaimer: Neither I nor Carnegie Mellon warrant this code in any way */ -/* whatsoever. This code is provided "as-is". Use at your own risk. */ -/* */ -/* Some of the references listed below are marked with an asterisk. [*] */ -/* These references are available for downloading from the Web page */ -/* */ -/* http://www.cs.cmu.edu/~quake/triangle.research.html */ -/* */ -/* Three papers discussing aspects of Triangle are available. A short */ -/* overview appears in "Triangle: Engineering a 2D Quality Mesh */ -/* Generator and Delaunay Triangulator," in Applied Computational */ -/* Geometry: Towards Geometric Engineering, Ming C. Lin and Dinesh */ -/* Manocha, editors, Lecture Notes in Computer Science volume 1148, */ -/* pages 203-222, Springer-Verlag, Berlin, May 1996 (from the First ACM */ -/* Workshop on Applied Computational Geometry). [*] */ -/* */ -/* The algorithms are discussed in the greatest detail in "Delaunay */ -/* Refinement Algorithms for Triangular Mesh Generation," Computational */ -/* Geometry: Theory and Applications 22(1-3):21-74, May 2002. [*] */ -/* */ -/* More detail about the data structures may be found in my dissertation: */ -/* "Delaunay Refinement Mesh Generation," Ph.D. thesis, Technical Report */ -/* CMU-CS-97-137, School of Computer Science, Carnegie Mellon University, */ -/* Pittsburgh, Pennsylvania, 18 May 1997. [*] */ -/* */ -/* Triangle was created as part of the Quake Project in the School of */ -/* Computer Science at Carnegie Mellon University. For further */ -/* information, see Hesheng Bao, Jacobo Bielak, Omar Ghattas, Loukas F. */ -/* Kallivokas, David R. O'Hallaron, Jonathan R. Shewchuk, and Jifeng Xu, */ -/* "Large-scale Simulation of Elastic Wave Propagation in Heterogeneous */ -/* Media on Parallel Computers," Computer Methods in Applied Mechanics */ -/* and Engineering 152(1-2):85-102, 22 January 1998. */ -/* */ -/* Triangle's Delaunay refinement algorithm for quality mesh generation is */ -/* a hybrid of one due to Jim Ruppert, "A Delaunay Refinement Algorithm */ -/* for Quality 2-Dimensional Mesh Generation," Journal of Algorithms */ -/* 18(3):548-585, May 1995 [*], and one due to L. Paul Chew, "Guaranteed- */ -/* Quality Mesh Generation for Curved Surfaces," Proceedings of the Ninth */ -/* Annual Symposium on Computational Geometry (San Diego, California), */ -/* pages 274-280, Association for Computing Machinery, May 1993, */ -/* http://portal.acm.org/citation.cfm?id=161150 . */ -/* */ -/* The Delaunay refinement algorithm has been modified so that it meshes */ -/* domains with small input angles well, as described in Gary L. Miller, */ -/* Steven E. Pav, and Noel J. Walkington, "When and Why Ruppert's */ -/* Algorithm Works," Twelfth International Meshing Roundtable, pages */ -/* 91-102, Sandia National Laboratories, September 2003. [*] */ -/* */ -/* My implementation of the divide-and-conquer and incremental Delaunay */ -/* triangulation algorithms follows closely the presentation of Guibas */ -/* and Stolfi, even though I use a triangle-based data structure instead */ -/* of their quad-edge data structure. (In fact, I originally implemented */ -/* Triangle using the quad-edge data structure, but the switch to a */ -/* triangle-based data structure sped Triangle by a factor of two.) The */ -/* mesh manipulation primitives and the two aforementioned Delaunay */ -/* triangulation algorithms are described by Leonidas J. Guibas and Jorge */ -/* Stolfi, "Primitives for the Manipulation of General Subdivisions and */ -/* the Computation of Voronoi Diagrams," ACM Transactions on Graphics */ -/* 4(2):74-123, April 1985, http://portal.acm.org/citation.cfm?id=282923 .*/ -/* */ -/* Their O(n log n) divide-and-conquer algorithm is adapted from Der-Tsai */ -/* Lee and Bruce J. Schachter, "Two Algorithms for Constructing the */ -/* Delaunay Triangulation," International Journal of Computer and */ -/* Information Science 9(3):219-242, 1980. Triangle's improvement of the */ -/* divide-and-conquer algorithm by alternating between vertical and */ -/* horizontal cuts was introduced by Rex A. Dwyer, "A Faster Divide-and- */ -/* Conquer Algorithm for Constructing Delaunay Triangulations," */ -/* Algorithmica 2(2):137-151, 1987. */ -/* */ -/* The incremental insertion algorithm was first proposed by C. L. Lawson, */ -/* "Software for C1 Surface Interpolation," in Mathematical Software III, */ -/* John R. Rice, editor, Academic Press, New York, pp. 161-194, 1977. */ -/* For point location, I use the algorithm of Ernst P. Mucke, Isaac */ -/* Saias, and Binhai Zhu, "Fast Randomized Point Location Without */ -/* Preprocessing in Two- and Three-Dimensional Delaunay Triangulations," */ -/* Proceedings of the Twelfth Annual Symposium on Computational Geometry, */ -/* ACM, May 1996. [*] If I were to randomize the order of vertex */ -/* insertion (I currently don't bother), their result combined with the */ -/* result of Kenneth L. Clarkson and Peter W. Shor, "Applications of */ -/* Random Sampling in Computational Geometry II," Discrete & */ -/* Computational Geometry 4(1):387-421, 1989, would yield an expected */ -/* O(n^{4/3}) bound on running time. */ -/* */ -/* The O(n log n) sweepline Delaunay triangulation algorithm is taken from */ -/* Steven Fortune, "A Sweepline Algorithm for Voronoi Diagrams", */ -/* Algorithmica 2(2):153-174, 1987. A random sample of edges on the */ -/* boundary of the triangulation are maintained in a splay tree for the */ -/* purpose of point location. Splay trees are described by Daniel */ -/* Dominic Sleator and Robert Endre Tarjan, "Self-Adjusting Binary Search */ -/* Trees," Journal of the ACM 32(3):652-686, July 1985, */ -/* http://portal.acm.org/citation.cfm?id=3835 . */ -/* */ -/* The algorithms for exact computation of the signs of determinants are */ -/* described in Jonathan Richard Shewchuk, "Adaptive Precision Floating- */ -/* Point Arithmetic and Fast Robust Geometric Predicates," Discrete & */ -/* Computational Geometry 18(3):305-363, October 1997. (Also available */ -/* as Technical Report CMU-CS-96-140, School of Computer Science, */ -/* Carnegie Mellon University, Pittsburgh, Pennsylvania, May 1996.) [*] */ -/* An abbreviated version appears as Jonathan Richard Shewchuk, "Robust */ -/* Adaptive Floating-Point Geometric Predicates," Proceedings of the */ -/* Twelfth Annual Symposium on Computational Geometry, ACM, May 1996. [*] */ -/* Many of the ideas for my exact arithmetic routines originate with */ -/* Douglas M. Priest, "Algorithms for Arbitrary Precision Floating Point */ -/* Arithmetic," Tenth Symposium on Computer Arithmetic, pp. 132-143, IEEE */ -/* Computer Society Press, 1991. [*] Many of the ideas for the correct */ -/* evaluation of the signs of determinants are taken from Steven Fortune */ -/* and Christopher J. Van Wyk, "Efficient Exact Arithmetic for Computa- */ -/* tional Geometry," Proceedings of the Ninth Annual Symposium on */ -/* Computational Geometry, ACM, pp. 163-172, May 1993, and from Steven */ -/* Fortune, "Numerical Stability of Algorithms for 2D Delaunay Triangu- */ -/* lations," International Journal of Computational Geometry & Applica- */ -/* tions 5(1-2):193-213, March-June 1995. */ -/* */ -/* The method of inserting new vertices off-center (not precisely at the */ -/* circumcenter of every poor-quality triangle) is from Alper Ungor, */ -/* "Off-centers: A New Type of Steiner Points for Computing Size-Optimal */ -/* Quality-Guaranteed Delaunay Triangulations," Proceedings of LATIN */ -/* 2004 (Buenos Aires, Argentina), April 2004. */ -/* */ -/* For definitions of and results involving Delaunay triangulations, */ -/* constrained and conforming versions thereof, and other aspects of */ -/* triangular mesh generation, see the excellent survey by Marshall Bern */ -/* and David Eppstein, "Mesh Generation and Optimal Triangulation," in */ -/* Computing and Euclidean Geometry, Ding-Zhu Du and Frank Hwang, */ -/* editors, World Scientific, Singapore, pp. 23-90, 1992. [*] */ -/* */ -/* The time for incrementally adding PSLG (planar straight line graph) */ -/* segments to create a constrained Delaunay triangulation is probably */ -/* O(t^2) per segment in the worst case and O(t) per segment in the */ -/* common case, where t is the number of triangles that intersect the */ -/* segment before it is inserted. This doesn't count point location, */ -/* which can be much more expensive. I could improve this to O(d log d) */ -/* time, but d is usually quite small, so it's not worth the bother. */ -/* (This note does not apply when the -s switch is used, invoking a */ -/* different method is used to insert segments.) */ -/* */ -/* The time for deleting a vertex from a Delaunay triangulation is O(d^2) */ -/* in the worst case and O(d) in the common case, where d is the degree */ -/* of the vertex being deleted. I could improve this to O(d log d) time, */ -/* but d is usually quite small, so it's not worth the bother. */ -/* */ -/* Ruppert's Delaunay refinement algorithm typically generates triangles */ -/* at a linear rate (constant time per triangle) after the initial */ -/* triangulation is formed. There may be pathological cases where */ -/* quadratic time is required, but these never arise in practice. */ -/* */ -/* The geometric predicates (circumcenter calculations, segment */ -/* intersection formulae, etc.) appear in my "Lecture Notes on Geometric */ -/* Robustness" at http://www.cs.berkeley.edu/~jrs/mesh . */ -/* */ -/* If you make any improvements to this code, please please please let me */ -/* know, so that I may obtain the improvements. Even if you don't change */ -/* the code, I'd still love to hear what it's being used for. */ -/* */ -/*****************************************************************************/ - -/* For single precision (which will save some memory and reduce paging), */ -/* define the symbol SINGLE by using the -DSINGLE compiler switch or by */ -/* writing "#define SINGLE" below. */ -/* */ -/* For double precision (which will allow you to refine meshes to a smaller */ -/* edge length), leave SINGLE undefined. */ -/* */ -/* Double precision uses more memory, but improves the resolution of the */ -/* meshes you can generate with Triangle. It also reduces the likelihood */ -/* of a floating exception due to overflow. Finally, it is much faster */ -/* than single precision on 64-bit architectures like the DEC Alpha. I */ -/* recommend double precision unless you want to generate a mesh for which */ -/* you do not have enough memory. */ - -//#define SINGLE - -#ifdef SINGLE -#define REAL float -#else /* not SINGLE */ -#define REAL double -#endif /* not SINGLE */ - -/* If yours is not a Unix system, define the NO_TIMER compiler switch to */ -/* remove the Unix-specific timing code. */ - -#define NO_TIMER - -/* To insert lots of self-checks for internal errors, define the SELF_CHECK */ -/* symbol. This will slow down the program significantly. It is best to */ -/* define the symbol using the -DSELF_CHECK compiler switch, but you could */ -/* write "#define SELF_CHECK" below. If you are modifying this code, I */ -/* recommend you turn self-checks on until your work is debugged. */ - -/* #define SELF_CHECK */ - -/* To compile Triangle as a callable object library (triangle.o), define the */ -/* TRILIBRARY symbol. Read the file triangle.h for details on how to call */ -/* the procedure triangulate() that results. */ - -#define TRILIBRARY - -/* It is possible to generate a smaller version of Triangle using one or */ -/* both of the following symbols. Define the REDUCED symbol to eliminate */ -/* all features that are primarily of research interest; specifically, the */ -/* -i, -F, -s, and -C switches. Define the CDT_ONLY symbol to eliminate */ -/* all meshing algorithms above and beyond constrained Delaunay */ -/* triangulation; specifically, the -r, -q, -a, -u, -D, -S, and -s */ -/* switches. These reductions are most likely to be useful when */ -/* generating an object library (triangle.o) by defining the TRILIBRARY */ -/* symbol. */ - -/* #define REDUCED */ -/* #define CDT_ONLY */ - -/* On some machines, my exact arithmetic routines might be defeated by the */ -/* use of internal extended precision floating-point registers. The best */ -/* way to solve this problem is to set the floating-point registers to use */ -/* single or double precision internally. On 80x86 processors, this may */ -/* be accomplished by setting the CPU86 symbol for the Microsoft C */ -/* compiler, or the LINUX symbol for the gcc compiler running on Linux. */ -/* */ -/* An inferior solution is to declare certain values as `volatile', thus */ -/* forcing them to be stored to memory and rounded off. Unfortunately, */ -/* this solution might slow Triangle down quite a bit. To use volatile */ -/* values, write "#define INEXACT volatile" below. Normally, however, */ -/* INEXACT should be defined to be nothing. ("#define INEXACT".) */ -/* */ -/* For more discussion, see http://www.cs.cmu.edu/~quake/robust.pc.html . */ -/* For yet more discussion, see Section 5 of my paper, "Adaptive Precision */ -/* Floating-Point Arithmetic and Fast Robust Geometric Predicates" (also */ -/* available as Section 6.6 of my dissertation). */ - -/* #define CPU86 */ -/* #define LINUX */ - -#define INEXACT /* Nothing */ -/* #define INEXACT volatile */ - -/* Maximum number of characters in a file name (including the null). */ - -#define FILENAMESIZE 2048 - -/* Maximum number of characters in a line read from a file (including the */ -/* null). */ - -#define INPUTLINESIZE 1024 - -/* For efficiency, a variety of data structures are allocated in bulk. The */ -/* following constants determine how many of each structure is allocated */ -/* at once. */ - -#define TRIPERBLOCK 4092 /* Number of triangles allocated at once. */ -#define SUBSEGPERBLOCK 508 /* Number of subsegments allocated at once. */ -#define VERTEXPERBLOCK 4092 /* Number of vertices allocated at once. */ -#define VIRUSPERBLOCK 1020 /* Number of virus triangles allocated at once. */ -/* Number of encroached subsegments allocated at once. */ -#define BADSUBSEGPERBLOCK 252 -/* Number of skinny triangles allocated at once. */ -#define BADTRIPERBLOCK 4092 -/* Number of flipped triangles allocated at once. */ -#define FLIPSTACKERPERBLOCK 252 -/* Number of splay tree nodes allocated at once. */ -#define SPLAYNODEPERBLOCK 508 - -/* The vertex types. A DEADVERTEX has been deleted entirely. An */ -/* UNDEADVERTEX is not part of the mesh, but is written to the output */ -/* .node file and affects the node indexing in the other output files. */ - -#define INPUTVERTEX 0 -#define SEGMENTVERTEX 1 -#define FREEVERTEX 2 -#define DEADVERTEX -32768 -#define UNDEADVERTEX -32767 - -/* The next line is used to outsmart some very stupid compilers. If your */ -/* compiler is smarter, feel free to replace the "int" with "void". */ -/* Not that it matters. */ - -#define VOID int - -/* Two constants for algorithms based on random sampling. Both constants */ -/* have been chosen empirically to optimize their respective algorithms. */ - -/* Used for the point location scheme of Mucke, Saias, and Zhu, to decide */ -/* how large a random sample of triangles to inspect. */ - -#define SAMPLEFACTOR 11 - -/* Used in Fortune's sweepline Delaunay algorithm to determine what fraction */ -/* of boundary edges should be maintained in the splay tree for point */ -/* location on the front. */ - -#define SAMPLERATE 10 - -/* A number that speaks for itself, every kissable digit. */ - -#define PI 3.141592653589793238462643383279502884197169399375105820974944592308 - -/* Another fave. */ - -#define SQUAREROOTTWO 1.4142135623730950488016887242096980785696718753769480732 - -/* And here's one for those of you who are intimidated by math. */ - -#define ONETHIRD 0.333333333333333333333333333333333333333333333333333333333333 - -#include -#include -#include -#include -#ifndef NO_TIMER -#include -#endif /* not NO_TIMER */ -#ifdef CPU86 -#include -#endif /* CPU86 */ -#ifdef LINUX -#include -#endif /* LINUX */ -#ifdef TRILIBRARY -#include "triangle.h" -#endif /* TRILIBRARY */ - -/* A few forward declarations. */ - -#ifndef TRILIBRARY -char *readline(); -char *findfield(); -#endif /* not TRILIBRARY */ - -/* Labels that signify the result of point location. The result of a */ -/* search indicates that the point falls in the interior of a triangle, on */ -/* an edge, on a vertex, or outside the mesh. */ - -enum locateresult {INTRIANGLE, ONEDGE, ONVERTEX, OUTSIDE}; - -/* Labels that signify the result of vertex insertion. The result indicates */ -/* that the vertex was inserted with complete success, was inserted but */ -/* encroaches upon a subsegment, was not inserted because it lies on a */ -/* segment, or was not inserted because another vertex occupies the same */ -/* location. */ - -enum insertvertexresult {SUCCESSFULVERTEX, ENCROACHINGVERTEX, VIOLATINGVERTEX, - DUPLICATEVERTEX}; - -/* Labels that signify the result of direction finding. The result */ -/* indicates that a segment connecting the two query points falls within */ -/* the direction triangle, along the left edge of the direction triangle, */ -/* or along the right edge of the direction triangle. */ - -enum finddirectionresult {WITHIN, LEFTCOLLINEAR, RIGHTCOLLINEAR}; - -/*****************************************************************************/ -/* */ -/* The basic mesh data structures */ -/* */ -/* There are three: vertices, triangles, and subsegments (abbreviated */ -/* `subseg'). These three data structures, linked by pointers, comprise */ -/* the mesh. A vertex simply represents a mesh vertex and its properties. */ -/* A triangle is a triangle. A subsegment is a special data structure used */ -/* to represent an impenetrable edge of the mesh (perhaps on the outer */ -/* boundary, on the boundary of a hole, or part of an internal boundary */ -/* separating two triangulated regions). Subsegments represent boundaries, */ -/* defined by the user, that triangles may not lie across. */ -/* */ -/* A triangle consists of a list of three vertices, a list of three */ -/* adjoining triangles, a list of three adjoining subsegments (when */ -/* segments exist), an arbitrary number of optional user-defined */ -/* floating-point attributes, and an optional area constraint. The latter */ -/* is an upper bound on the permissible area of each triangle in a region, */ -/* used for mesh refinement. */ -/* */ -/* For a triangle on a boundary of the mesh, some or all of the neighboring */ -/* triangles may not be present. For a triangle in the interior of the */ -/* mesh, often no neighboring subsegments are present. Such absent */ -/* triangles and subsegments are never represented by NULL pointers; they */ -/* are represented by two special records: `dummytri', the triangle that */ -/* fills "outer space", and `dummysub', the omnipresent subsegment. */ -/* `dummytri' and `dummysub' are used for several reasons; for instance, */ -/* they can be dereferenced and their contents examined without violating */ -/* protected memory. */ -/* */ -/* However, it is important to understand that a triangle includes other */ -/* information as well. The pointers to adjoining vertices, triangles, and */ -/* subsegments are ordered in a way that indicates their geometric relation */ -/* to each other. Furthermore, each of these pointers contains orientation */ -/* information. Each pointer to an adjoining triangle indicates which face */ -/* of that triangle is contacted. Similarly, each pointer to an adjoining */ -/* subsegment indicates which side of that subsegment is contacted, and how */ -/* the subsegment is oriented relative to the triangle. */ -/* */ -/* The data structure representing a subsegment may be thought to be */ -/* abutting the edge of one or two triangle data structures: either */ -/* sandwiched between two triangles, or resting against one triangle on an */ -/* exterior boundary or hole boundary. */ -/* */ -/* A subsegment consists of a list of four vertices--the vertices of the */ -/* subsegment, and the vertices of the segment it is a part of--a list of */ -/* two adjoining subsegments, and a list of two adjoining triangles. One */ -/* of the two adjoining triangles may not be present (though there should */ -/* always be one), and neighboring subsegments might not be present. */ -/* Subsegments also store a user-defined integer "boundary marker". */ -/* Typically, this integer is used to indicate what boundary conditions are */ -/* to be applied at that location in a finite element simulation. */ -/* */ -/* Like triangles, subsegments maintain information about the relative */ -/* orientation of neighboring objects. */ -/* */ -/* Vertices are relatively simple. A vertex is a list of floating-point */ -/* numbers, starting with the x, and y coordinates, followed by an */ -/* arbitrary number of optional user-defined floating-point attributes, */ -/* followed by an integer boundary marker. During the segment insertion */ -/* phase, there is also a pointer from each vertex to a triangle that may */ -/* contain it. Each pointer is not always correct, but when one is, it */ -/* speeds up segment insertion. These pointers are assigned values once */ -/* at the beginning of the segment insertion phase, and are not used or */ -/* updated except during this phase. Edge flipping during segment */ -/* insertion will render some of them incorrect. Hence, don't rely upon */ -/* them for anything. */ -/* */ -/* Other than the exception mentioned above, vertices have no information */ -/* about what triangles, subfacets, or subsegments they are linked to. */ -/* */ -/*****************************************************************************/ - -/*****************************************************************************/ -/* */ -/* Handles */ -/* */ -/* The oriented triangle (`otri') and oriented subsegment (`osub') data */ -/* structures defined below do not themselves store any part of the mesh. */ -/* The mesh itself is made of `triangle's, `subseg's, and `vertex's. */ -/* */ -/* Oriented triangles and oriented subsegments will usually be referred to */ -/* as "handles." A handle is essentially a pointer into the mesh; it */ -/* allows you to "hold" one particular part of the mesh. Handles are used */ -/* to specify the regions in which one is traversing and modifying the mesh.*/ -/* A single `triangle' may be held by many handles, or none at all. (The */ -/* latter case is not a memory leak, because the triangle is still */ -/* connected to other triangles in the mesh.) */ -/* */ -/* An `otri' is a handle that holds a triangle. It holds a specific edge */ -/* of the triangle. An `osub' is a handle that holds a subsegment. It */ -/* holds either the left or right side of the subsegment. */ -/* */ -/* Navigation about the mesh is accomplished through a set of mesh */ -/* manipulation primitives, further below. Many of these primitives take */ -/* a handle and produce a new handle that holds the mesh near the first */ -/* handle. Other primitives take two handles and glue the corresponding */ -/* parts of the mesh together. The orientation of the handles is */ -/* important. For instance, when two triangles are glued together by the */ -/* bond() primitive, they are glued at the edges on which the handles lie. */ -/* */ -/* Because vertices have no information about which triangles they are */ -/* attached to, I commonly represent a vertex by use of a handle whose */ -/* origin is the vertex. A single handle can simultaneously represent a */ -/* triangle, an edge, and a vertex. */ -/* */ -/*****************************************************************************/ - -/* The triangle data structure. Each triangle contains three pointers to */ -/* adjoining triangles, plus three pointers to vertices, plus three */ -/* pointers to subsegments (declared below; these pointers are usually */ -/* `dummysub'). It may or may not also contain user-defined attributes */ -/* and/or a floating-point "area constraint." It may also contain extra */ -/* pointers for nodes, when the user asks for high-order elements. */ -/* Because the size and structure of a `triangle' is not decided until */ -/* runtime, I haven't simply declared the type `triangle' as a struct. */ - -typedef REAL **triangle; /* Really: typedef triangle *triangle */ - -/* An oriented triangle: includes a pointer to a triangle and orientation. */ -/* The orientation denotes an edge of the triangle. Hence, there are */ -/* three possible orientations. By convention, each edge always points */ -/* counterclockwise about the corresponding triangle. */ - -struct otri { - triangle *tri; - int orient; /* Ranges from 0 to 2. */ -}; - -/* The subsegment data structure. Each subsegment contains two pointers to */ -/* adjoining subsegments, plus four pointers to vertices, plus two */ -/* pointers to adjoining triangles, plus one boundary marker, plus one */ -/* segment number. */ - -typedef REAL **subseg; /* Really: typedef subseg *subseg */ - -/* An oriented subsegment: includes a pointer to a subsegment and an */ -/* orientation. The orientation denotes a side of the edge. Hence, there */ -/* are two possible orientations. By convention, the edge is always */ -/* directed so that the "side" denoted is the right side of the edge. */ - -struct osub { - subseg *ss; - int ssorient; /* Ranges from 0 to 1. */ -}; - -/* The vertex data structure. Each vertex is actually an array of REALs. */ -/* The number of REALs is unknown until runtime. An integer boundary */ -/* marker, and sometimes a pointer to a triangle, is appended after the */ -/* REALs. */ - -typedef REAL *vertex; - -/* A queue used to store encroached subsegments. Each subsegment's vertices */ -/* are stored so that we can check whether a subsegment is still the same. */ - -struct badsubseg { - subseg encsubseg; /* An encroached subsegment. */ - vertex subsegorg, subsegdest; /* Its two vertices. */ -}; - -/* A queue used to store bad triangles. The key is the square of the cosine */ -/* of the smallest angle of the triangle. Each triangle's vertices are */ -/* stored so that one can check whether a triangle is still the same. */ - -struct badtriang { - triangle poortri; /* A skinny or too-large triangle. */ - REAL key; /* cos^2 of smallest (apical) angle. */ - vertex triangorg, triangdest, triangapex; /* Its three vertices. */ - struct badtriang *nexttriang; /* Pointer to next bad triangle. */ -}; - -/* A stack of triangles flipped during the most recent vertex insertion. */ -/* The stack is used to undo the vertex insertion if the vertex encroaches */ -/* upon a subsegment. */ - -struct flipstacker { - triangle flippedtri; /* A recently flipped triangle. */ - struct flipstacker *prevflip; /* Previous flip in the stack. */ -}; - -/* A node in a heap used to store events for the sweepline Delaunay */ -/* algorithm. Nodes do not point directly to their parents or children in */ -/* the heap. Instead, each node knows its position in the heap, and can */ -/* look up its parent and children in a separate array. The `eventptr' */ -/* points either to a `vertex' or to a triangle (in encoded format, so */ -/* that an orientation is included). In the latter case, the origin of */ -/* the oriented triangle is the apex of a "circle event" of the sweepline */ -/* algorithm. To distinguish site events from circle events, all circle */ -/* events are given an invalid (smaller than `xmin') x-coordinate `xkey'. */ - -struct event { - REAL xkey, ykey; /* Coordinates of the event. */ - VOID *eventptr; /* Can be a vertex or the location of a circle event. */ - int heapposition; /* Marks this event's position in the heap. */ -}; - -/* A node in the splay tree. Each node holds an oriented ghost triangle */ -/* that represents a boundary edge of the growing triangulation. When a */ -/* circle event covers two boundary edges with a triangle, so that they */ -/* are no longer boundary edges, those edges are not immediately deleted */ -/* from the tree; rather, they are lazily deleted when they are next */ -/* encountered. (Since only a random sample of boundary edges are kept */ -/* in the tree, lazy deletion is faster.) `keydest' is used to verify */ -/* that a triangle is still the same as when it entered the splay tree; if */ -/* it has been rotated (due to a circle event), it no longer represents a */ -/* boundary edge and should be deleted. */ - -struct splaynode { - struct otri keyedge; /* Lprev of an edge on the front. */ - vertex keydest; /* Used to verify that splay node is still live. */ - struct splaynode *lchild, *rchild; /* Children in splay tree. */ -}; - -/* A type used to allocate memory. firstblock is the first block of items. */ -/* nowblock is the block from which items are currently being allocated. */ -/* nextitem points to the next slab of free memory for an item. */ -/* deaditemstack is the head of a linked list (stack) of deallocated items */ -/* that can be recycled. unallocateditems is the number of items that */ -/* remain to be allocated from nowblock. */ -/* */ -/* Traversal is the process of walking through the entire list of items, and */ -/* is separate from allocation. Note that a traversal will visit items on */ -/* the "deaditemstack" stack as well as live items. pathblock points to */ -/* the block currently being traversed. pathitem points to the next item */ -/* to be traversed. pathitemsleft is the number of items that remain to */ -/* be traversed in pathblock. */ -/* */ -/* alignbytes determines how new records should be aligned in memory. */ -/* itembytes is the length of a record in bytes (after rounding up). */ -/* itemsperblock is the number of items allocated at once in a single */ -/* block. itemsfirstblock is the number of items in the first block, */ -/* which can vary from the others. items is the number of currently */ -/* allocated items. maxitems is the maximum number of items that have */ -/* been allocated at once; it is the current number of items plus the */ -/* number of records kept on deaditemstack. */ - -struct memorypool { - VOID **firstblock, **nowblock; - VOID *nextitem; - VOID *deaditemstack; - VOID **pathblock; - VOID *pathitem; - int alignbytes; - int itembytes; - int itemsperblock; - int itemsfirstblock; - long items, maxitems; - int unallocateditems; - int pathitemsleft; -}; - - -/* Global constants. */ - -REAL splitter; /* Used to split REAL factors for exact multiplication. */ -REAL epsilon; /* Floating-point machine epsilon. */ -REAL resulterrbound; -REAL ccwerrboundA, ccwerrboundB, ccwerrboundC; -REAL iccerrboundA, iccerrboundB, iccerrboundC; -REAL o3derrboundA, o3derrboundB, o3derrboundC; - -/* Random number seed is not constant, but I've made it global anyway. */ - -unsigned long randomseed; /* Current random number seed. */ - - -/* Mesh data structure. Triangle operates on only one mesh, but the mesh */ -/* structure is used (instead of global variables) to allow reentrancy. */ - -struct mesh { - -/* Variables used to allocate memory for triangles, subsegments, vertices, */ -/* viri (triangles being eaten), encroached segments, bad (skinny or too */ -/* large) triangles, and splay tree nodes. */ - - struct memorypool triangles; - struct memorypool subsegs; - struct memorypool vertices; - struct memorypool viri; - struct memorypool badsubsegs; - struct memorypool badtriangles; - struct memorypool flipstackers; - struct memorypool splaynodes; - -/* Variables that maintain the bad triangle queues. The queues are */ -/* ordered from 4095 (highest priority) to 0 (lowest priority). */ - - struct badtriang *queuefront[4096]; - struct badtriang *queuetail[4096]; - int nextnonemptyq[4096]; - int firstnonemptyq; - -/* Variable that maintains the stack of recently flipped triangles. */ - - struct flipstacker *lastflip; - -/* Other variables. */ - - REAL xmin, xmax, ymin, ymax; /* x and y bounds. */ - REAL xminextreme; /* Nonexistent x value used as a flag in sweepline. */ - int invertices; /* Number of input vertices. */ - int inelements; /* Number of input triangles. */ - int insegments; /* Number of input segments. */ - int holes; /* Number of input holes. */ - int regions; /* Number of input regions. */ - int undeads; /* Number of input vertices that don't appear in the mesh. */ - long edges; /* Number of output edges. */ - int mesh_dim; /* Dimension (ought to be 2). */ - int nextras; /* Number of attributes per vertex. */ - int eextras; /* Number of attributes per triangle. */ - long hullsize; /* Number of edges in convex hull. */ - int steinerleft; /* Number of Steiner points not yet used. */ - int vertexmarkindex; /* Index to find boundary marker of a vertex. */ - int vertex2triindex; /* Index to find a triangle adjacent to a vertex. */ - int highorderindex; /* Index to find extra nodes for high-order elements. */ - int elemattribindex; /* Index to find attributes of a triangle. */ - int areaboundindex; /* Index to find area bound of a triangle. */ - int checksegments; /* Are there segments in the triangulation yet? */ - int checkquality; /* Has quality triangulation begun yet? */ - int readnodefile; /* Has a .node file been read? */ - long samples; /* Number of random samples for point location. */ - - long incirclecount; /* Number of incircle tests performed. */ - long counterclockcount; /* Number of counterclockwise tests performed. */ - long orient3dcount; /* Number of 3D orientation tests performed. */ - long hyperbolacount; /* Number of right-of-hyperbola tests performed. */ - long circumcentercount; /* Number of circumcenter calculations performed. */ - long circletopcount; /* Number of circle top calculations performed. */ - -/* Triangular bounding box vertices. */ - - vertex infvertex1, infvertex2, infvertex3; - -/* Pointer to the `triangle' that occupies all of "outer space." */ - - triangle *dummytri; - triangle *dummytribase; /* Keep base address so we can free() it later. */ - -/* Pointer to the omnipresent subsegment. Referenced by any triangle or */ -/* subsegment that isn't really connected to a subsegment at that */ -/* location. */ - - subseg *dummysub; - subseg *dummysubbase; /* Keep base address so we can free() it later. */ - -/* Pointer to a recently visited triangle. Improves point location if */ -/* proximate vertices are inserted sequentially. */ - - struct otri recenttri; - -}; /* End of `struct mesh'. */ - - -/* Data structure for command line switches and file names. This structure */ -/* is used (instead of global variables) to allow reentrancy. */ - -struct behavior { - -/* Switches for the triangulator. */ -/* poly: -p switch. refine: -r switch. */ -/* quality: -q switch. */ -/* minangle: minimum angle bound, specified after -q switch. */ -/* goodangle: cosine squared of minangle. */ -/* offconstant: constant used to place off-center Steiner points. */ -/* vararea: -a switch without number. */ -/* fixedarea: -a switch with number. */ -/* maxarea: maximum area bound, specified after -a switch. */ -/* usertest: -u switch. */ -/* regionattrib: -A switch. convex: -c switch. */ -/* weighted: 1 for -w switch, 2 for -W switch. jettison: -j switch */ -/* firstnumber: inverse of -z switch. All items are numbered starting */ -/* from `firstnumber'. */ -/* edgesout: -e switch. voronoi: -v switch. */ -/* neighbors: -n switch. geomview: -g switch. */ -/* nobound: -B switch. nopolywritten: -P switch. */ -/* nonodewritten: -N switch. noelewritten: -E switch. */ -/* noiterationnum: -I switch. noholes: -O switch. */ -/* noexact: -X switch. */ -/* order: element order, specified after -o switch. */ -/* nobisect: count of how often -Y switch is selected. */ -/* steiner: maximum number of Steiner points, specified after -S switch. */ -/* incremental: -i switch. sweepline: -F switch. */ -/* dwyer: inverse of -l switch. */ -/* splitseg: -s switch. */ -/* conformdel: -D switch. docheck: -C switch. */ -/* quiet: -Q switch. verbose: count of how often -V switch is selected. */ -/* usesegments: -p, -r, -q, or -c switch; determines whether segments are */ -/* used at all. */ -/* */ -/* Read the instructions to find out the meaning of these switches. */ - - int poly, refine, quality, vararea, fixedarea, usertest; - int regionattrib, convex, weighted, jettison; - int firstnumber; - int edgesout, voronoi, neighbors, geomview; - int nobound, nopolywritten, nonodewritten, noelewritten, noiterationnum; - int noholes, noexact, conformdel; - int incremental, sweepline, dwyer; - int splitseg; - int docheck; - int quiet, verbose; - int usesegments; - int order; - int nobisect; - int steiner; - REAL minangle, goodangle, offconstant; - REAL maxarea; - -/* Variables for file names. */ - -#ifndef TRILIBRARY - char innodefilename[FILENAMESIZE]; - char inelefilename[FILENAMESIZE]; - char inpolyfilename[FILENAMESIZE]; - char areafilename[FILENAMESIZE]; - char outnodefilename[FILENAMESIZE]; - char outelefilename[FILENAMESIZE]; - char outpolyfilename[FILENAMESIZE]; - char edgefilename[FILENAMESIZE]; - char vnodefilename[FILENAMESIZE]; - char vedgefilename[FILENAMESIZE]; - char neighborfilename[FILENAMESIZE]; - char offfilename[FILENAMESIZE]; -#endif /* not TRILIBRARY */ - -}; /* End of `struct behavior'. */ - - -/*****************************************************************************/ -/* */ -/* Mesh manipulation primitives. Each triangle contains three pointers to */ -/* other triangles, with orientations. Each pointer points not to the */ -/* first byte of a triangle, but to one of the first three bytes of a */ -/* triangle. It is necessary to extract both the triangle itself and the */ -/* orientation. To save memory, I keep both pieces of information in one */ -/* pointer. To make this possible, I assume that all triangles are aligned */ -/* to four-byte boundaries. The decode() routine below decodes a pointer, */ -/* extracting an orientation (in the range 0 to 2) and a pointer to the */ -/* beginning of a triangle. The encode() routine compresses a pointer to a */ -/* triangle and an orientation into a single pointer. My assumptions that */ -/* triangles are four-byte-aligned and that the `unsigned long' type is */ -/* long enough to hold a pointer are two of the few kludges in this program.*/ -/* */ -/* Subsegments are manipulated similarly. A pointer to a subsegment */ -/* carries both an address and an orientation in the range 0 to 1. */ -/* */ -/* The other primitives take an oriented triangle or oriented subsegment, */ -/* and return an oriented triangle or oriented subsegment or vertex; or */ -/* they change the connections in the data structure. */ -/* */ -/* Below, triangles and subsegments are denoted by their vertices. The */ -/* triangle abc has origin (org) a, destination (dest) b, and apex (apex) */ -/* c. These vertices occur in counterclockwise order about the triangle. */ -/* The handle abc may simultaneously denote vertex a, edge ab, and triangle */ -/* abc. */ -/* */ -/* Similarly, the subsegment ab has origin (sorg) a and destination (sdest) */ -/* b. If ab is thought to be directed upward (with b directly above a), */ -/* then the handle ab is thought to grasp the right side of ab, and may */ -/* simultaneously denote vertex a and edge ab. */ -/* */ -/* An asterisk (*) denotes a vertex whose identity is unknown. */ -/* */ -/* Given this notation, a partial list of mesh manipulation primitives */ -/* follows. */ -/* */ -/* */ -/* For triangles: */ -/* */ -/* sym: Find the abutting triangle; same edge. */ -/* sym(abc) -> ba* */ -/* */ -/* lnext: Find the next edge (counterclockwise) of a triangle. */ -/* lnext(abc) -> bca */ -/* */ -/* lprev: Find the previous edge (clockwise) of a triangle. */ -/* lprev(abc) -> cab */ -/* */ -/* onext: Find the next edge counterclockwise with the same origin. */ -/* onext(abc) -> ac* */ -/* */ -/* oprev: Find the next edge clockwise with the same origin. */ -/* oprev(abc) -> a*b */ -/* */ -/* dnext: Find the next edge counterclockwise with the same destination. */ -/* dnext(abc) -> *ba */ -/* */ -/* dprev: Find the next edge clockwise with the same destination. */ -/* dprev(abc) -> cb* */ -/* */ -/* rnext: Find the next edge (counterclockwise) of the adjacent triangle. */ -/* rnext(abc) -> *a* */ -/* */ -/* rprev: Find the previous edge (clockwise) of the adjacent triangle. */ -/* rprev(abc) -> b** */ -/* */ -/* org: Origin dest: Destination apex: Apex */ -/* org(abc) -> a dest(abc) -> b apex(abc) -> c */ -/* */ -/* bond: Bond two triangles together at the resepective handles. */ -/* bond(abc, bad) */ -/* */ -/* */ -/* For subsegments: */ -/* */ -/* ssym: Reverse the orientation of a subsegment. */ -/* ssym(ab) -> ba */ -/* */ -/* spivot: Find adjoining subsegment with the same origin. */ -/* spivot(ab) -> a* */ -/* */ -/* snext: Find next subsegment in sequence. */ -/* snext(ab) -> b* */ -/* */ -/* sorg: Origin sdest: Destination */ -/* sorg(ab) -> a sdest(ab) -> b */ -/* */ -/* sbond: Bond two subsegments together at the respective origins. */ -/* sbond(ab, ac) */ -/* */ -/* */ -/* For interacting tetrahedra and subfacets: */ -/* */ -/* tspivot: Find a subsegment abutting a triangle. */ -/* tspivot(abc) -> ba */ -/* */ -/* stpivot: Find a triangle abutting a subsegment. */ -/* stpivot(ab) -> ba* */ -/* */ -/* tsbond: Bond a triangle to a subsegment. */ -/* tsbond(abc, ba) */ -/* */ -/*****************************************************************************/ - -/********* Mesh manipulation primitives begin here *********/ -/** **/ -/** **/ - -/* Fast lookup arrays to speed some of the mesh manipulation primitives. */ - -int plus1mod3[3] = {1, 2, 0}; -int minus1mod3[3] = {2, 0, 1}; - -/********* Primitives for triangles *********/ -/* */ -/* */ - -/* decode() converts a pointer to an oriented triangle. The orientation is */ -/* extracted from the two least significant bits of the pointer. */ - -#define decode(ptr, otri) \ - (otri).orient = (int) ((unsigned long) (ptr) & (unsigned long) 3l); \ - (otri).tri = (triangle *) \ - ((unsigned long) (ptr) ^ (unsigned long) (otri).orient) - -/* encode() compresses an oriented triangle into a single pointer. It */ -/* relies on the assumption that all triangles are aligned to four-byte */ -/* boundaries, so the two least significant bits of (otri).tri are zero. */ - -#define encode(otri) \ - (triangle) ((unsigned long) (otri).tri | (unsigned long) (otri).orient) - -/* The following handle manipulation primitives are all described by Guibas */ -/* and Stolfi. However, Guibas and Stolfi use an edge-based data */ -/* structure, whereas I use a triangle-based data structure. */ - -/* sym() finds the abutting triangle, on the same edge. Note that the edge */ -/* direction is necessarily reversed, because the handle specified by an */ -/* oriented triangle is directed counterclockwise around the triangle. */ - -#define sym(otri1, otri2) \ - ptr = (otri1).tri[(otri1).orient]; \ - decode(ptr, otri2); - -#define symself(otri) \ - ptr = (otri).tri[(otri).orient]; \ - decode(ptr, otri); - -/* lnext() finds the next edge (counterclockwise) of a triangle. */ - -#define lnext(otri1, otri2) \ - (otri2).tri = (otri1).tri; \ - (otri2).orient = plus1mod3[(otri1).orient] - -#define lnextself(otri) \ - (otri).orient = plus1mod3[(otri).orient] - -/* lprev() finds the previous edge (clockwise) of a triangle. */ - -#define lprev(otri1, otri2) \ - (otri2).tri = (otri1).tri; \ - (otri2).orient = minus1mod3[(otri1).orient] - -#define lprevself(otri) \ - (otri).orient = minus1mod3[(otri).orient] - -/* onext() spins counterclockwise around a vertex; that is, it finds the */ -/* next edge with the same origin in the counterclockwise direction. This */ -/* edge is part of a different triangle. */ - -#define onext(otri1, otri2) \ - lprev(otri1, otri2); \ - symself(otri2); - -#define onextself(otri) \ - lprevself(otri); \ - symself(otri); - -/* oprev() spins clockwise around a vertex; that is, it finds the next edge */ -/* with the same origin in the clockwise direction. This edge is part of */ -/* a different triangle. */ - -#define oprev(otri1, otri2) \ - sym(otri1, otri2); \ - lnextself(otri2); - -#define oprevself(otri) \ - symself(otri); \ - lnextself(otri); - -/* dnext() spins counterclockwise around a vertex; that is, it finds the */ -/* next edge with the same destination in the counterclockwise direction. */ -/* This edge is part of a different triangle. */ - -#define dnext(otri1, otri2) \ - sym(otri1, otri2); \ - lprevself(otri2); - -#define dnextself(otri) \ - symself(otri); \ - lprevself(otri); - -/* dprev() spins clockwise around a vertex; that is, it finds the next edge */ -/* with the same destination in the clockwise direction. This edge is */ -/* part of a different triangle. */ - -#define dprev(otri1, otri2) \ - lnext(otri1, otri2); \ - symself(otri2); - -#define dprevself(otri) \ - lnextself(otri); \ - symself(otri); - -/* rnext() moves one edge counterclockwise about the adjacent triangle. */ -/* (It's best understood by reading Guibas and Stolfi. It involves */ -/* changing triangles twice.) */ - -#define rnext(otri1, otri2) \ - sym(otri1, otri2); \ - lnextself(otri2); \ - symself(otri2); - -#define rnextself(otri) \ - symself(otri); \ - lnextself(otri); \ - symself(otri); - -/* rprev() moves one edge clockwise about the adjacent triangle. */ -/* (It's best understood by reading Guibas and Stolfi. It involves */ -/* changing triangles twice.) */ - -#define rprev(otri1, otri2) \ - sym(otri1, otri2); \ - lprevself(otri2); \ - symself(otri2); - -#define rprevself(otri) \ - symself(otri); \ - lprevself(otri); \ - symself(otri); - -/* These primitives determine or set the origin, destination, or apex of a */ -/* triangle. */ - -#define org(otri, vertexptr) \ - vertexptr = (vertex) (otri).tri[plus1mod3[(otri).orient] + 3] - -#define dest(otri, vertexptr) \ - vertexptr = (vertex) (otri).tri[minus1mod3[(otri).orient] + 3] - -#define apex(otri, vertexptr) \ - vertexptr = (vertex) (otri).tri[(otri).orient + 3] - -#define setorg(otri, vertexptr) \ - (otri).tri[plus1mod3[(otri).orient] + 3] = (triangle) vertexptr - -#define setdest(otri, vertexptr) \ - (otri).tri[minus1mod3[(otri).orient] + 3] = (triangle) vertexptr - -#define setapex(otri, vertexptr) \ - (otri).tri[(otri).orient + 3] = (triangle) vertexptr - -/* Bond two triangles together. */ - -#define bond(otri1, otri2) \ - (otri1).tri[(otri1).orient] = encode(otri2); \ - (otri2).tri[(otri2).orient] = encode(otri1) - -/* Dissolve a bond (from one side). Note that the other triangle will still */ -/* think it's connected to this triangle. Usually, however, the other */ -/* triangle is being deleted entirely, or bonded to another triangle, so */ -/* it doesn't matter. */ - -#define dissolve(otri) \ - (otri).tri[(otri).orient] = (triangle) m->dummytri - -/* Copy an oriented triangle. */ - -#define otricopy(otri1, otri2) \ - (otri2).tri = (otri1).tri; \ - (otri2).orient = (otri1).orient - -/* Test for equality of oriented triangles. */ - -#define otriequal(otri1, otri2) \ - (((otri1).tri == (otri2).tri) && \ - ((otri1).orient == (otri2).orient)) - -/* Primitives to infect or cure a triangle with the virus. These rely on */ -/* the assumption that all subsegments are aligned to four-byte boundaries.*/ - -#define infect(otri) \ - (otri).tri[6] = (triangle) \ - ((unsigned long) (otri).tri[6] | (unsigned long) 2l) - -#define uninfect(otri) \ - (otri).tri[6] = (triangle) \ - ((unsigned long) (otri).tri[6] & ~ (unsigned long) 2l) - -/* Test a triangle for viral infection. */ - -#define infected(otri) \ - (((unsigned long) (otri).tri[6] & (unsigned long) 2l) != 0l) - -/* Check or set a triangle's attributes. */ - -#define elemattribute(otri, attnum) \ - ((REAL *) (otri).tri)[m->elemattribindex + (attnum)] - -#define setelemattribute(otri, attnum, value) \ - ((REAL *) (otri).tri)[m->elemattribindex + (attnum)] = value - -/* Check or set a triangle's maximum area bound. */ - -#define areabound(otri) ((REAL *) (otri).tri)[m->areaboundindex] - -#define setareabound(otri, value) \ - ((REAL *) (otri).tri)[m->areaboundindex] = value - -/* Check or set a triangle's deallocation. Its second pointer is set to */ -/* NULL to indicate that it is not allocated. (Its first pointer is used */ -/* for the stack of dead items.) Its fourth pointer (its first vertex) */ -/* is set to NULL in case a `badtriang' structure points to it. */ - -#define deadtri(tria) ((tria)[1] == (triangle) NULL) - -#define killtri(tria) \ - (tria)[1] = (triangle) NULL; \ - (tria)[3] = (triangle) NULL - -/********* Primitives for subsegments *********/ -/* */ -/* */ - -/* sdecode() converts a pointer to an oriented subsegment. The orientation */ -/* is extracted from the least significant bit of the pointer. The two */ -/* least significant bits (one for orientation, one for viral infection) */ -/* are masked out to produce the real pointer. */ - -#define sdecode(sptr, osub) \ - (osub).ssorient = (int) ((unsigned long) (sptr) & (unsigned long) 1l); \ - (osub).ss = (subseg *) \ - ((unsigned long) (sptr) & ~ (unsigned long) 3l) - -/* sencode() compresses an oriented subsegment into a single pointer. It */ -/* relies on the assumption that all subsegments are aligned to two-byte */ -/* boundaries, so the least significant bit of (osub).ss is zero. */ - -#define sencode(osub) \ - (subseg) ((unsigned long) (osub).ss | (unsigned long) (osub).ssorient) - -/* ssym() toggles the orientation of a subsegment. */ - -#define ssym(osub1, osub2) \ - (osub2).ss = (osub1).ss; \ - (osub2).ssorient = 1 - (osub1).ssorient - -#define ssymself(osub) \ - (osub).ssorient = 1 - (osub).ssorient - -/* spivot() finds the other subsegment (from the same segment) that shares */ -/* the same origin. */ - -#define spivot(osub1, osub2) \ - sptr = (osub1).ss[(osub1).ssorient]; \ - sdecode(sptr, osub2) - -#define spivotself(osub) \ - sptr = (osub).ss[(osub).ssorient]; \ - sdecode(sptr, osub) - -/* snext() finds the next subsegment (from the same segment) in sequence; */ -/* one whose origin is the input subsegment's destination. */ - -#define snext(osub1, osub2) \ - sptr = (osub1).ss[1 - (osub1).ssorient]; \ - sdecode(sptr, osub2) - -#define snextself(osub) \ - sptr = (osub).ss[1 - (osub).ssorient]; \ - sdecode(sptr, osub) - -/* These primitives determine or set the origin or destination of a */ -/* subsegment or the segment that includes it. */ - -#define sorg(osub, vertexptr) \ - vertexptr = (vertex) (osub).ss[2 + (osub).ssorient] - -#define sdest(osub, vertexptr) \ - vertexptr = (vertex) (osub).ss[3 - (osub).ssorient] - -#define setsorg(osub, vertexptr) \ - (osub).ss[2 + (osub).ssorient] = (subseg) vertexptr - -#define setsdest(osub, vertexptr) \ - (osub).ss[3 - (osub).ssorient] = (subseg) vertexptr - -#define segorg(osub, vertexptr) \ - vertexptr = (vertex) (osub).ss[4 + (osub).ssorient] - -#define segdest(osub, vertexptr) \ - vertexptr = (vertex) (osub).ss[5 - (osub).ssorient] - -#define setsegorg(osub, vertexptr) \ - (osub).ss[4 + (osub).ssorient] = (subseg) vertexptr - -#define setsegdest(osub, vertexptr) \ - (osub).ss[5 - (osub).ssorient] = (subseg) vertexptr - -/* These primitives read or set a boundary marker. Boundary markers are */ -/* used to hold user-defined tags for setting boundary conditions in */ -/* finite element solvers. */ - -#define mark(osub) (* (int *) ((osub).ss + 8)) - -#define setmark(osub, value) \ - * (int *) ((osub).ss + 8) = value - -/* Bond two subsegments together. */ - -#define sbond(osub1, osub2) \ - (osub1).ss[(osub1).ssorient] = sencode(osub2); \ - (osub2).ss[(osub2).ssorient] = sencode(osub1) - -/* Dissolve a subsegment bond (from one side). Note that the other */ -/* subsegment will still think it's connected to this subsegment. */ - -#define sdissolve(osub) \ - (osub).ss[(osub).ssorient] = (subseg) m->dummysub - -/* Copy a subsegment. */ - -#define subsegcopy(osub1, osub2) \ - (osub2).ss = (osub1).ss; \ - (osub2).ssorient = (osub1).ssorient - -/* Test for equality of subsegments. */ - -#define subsegequal(osub1, osub2) \ - (((osub1).ss == (osub2).ss) && \ - ((osub1).ssorient == (osub2).ssorient)) - -/* Check or set a subsegment's deallocation. Its second pointer is set to */ -/* NULL to indicate that it is not allocated. (Its first pointer is used */ -/* for the stack of dead items.) Its third pointer (its first vertex) */ -/* is set to NULL in case a `badsubseg' structure points to it. */ - -#define deadsubseg(sub) ((sub)[1] == (subseg) NULL) - -#define killsubseg(sub) \ - (sub)[1] = (subseg) NULL; \ - (sub)[2] = (subseg) NULL - -/********* Primitives for interacting triangles and subsegments *********/ -/* */ -/* */ - -/* tspivot() finds a subsegment abutting a triangle. */ - -#define tspivot(otri, osub) \ - sptr = (subseg) (otri).tri[6 + (otri).orient]; \ - sdecode(sptr, osub) - -/* stpivot() finds a triangle abutting a subsegment. It requires that the */ -/* variable `ptr' of type `triangle' be defined. */ - -#define stpivot(osub, otri) \ - ptr = (triangle) (osub).ss[6 + (osub).ssorient]; \ - decode(ptr, otri) - -/* Bond a triangle to a subsegment. */ - -#define tsbond(otri, osub) \ - (otri).tri[6 + (otri).orient] = (triangle) sencode(osub); \ - (osub).ss[6 + (osub).ssorient] = (subseg) encode(otri) - -/* Dissolve a bond (from the triangle side). */ - -#define tsdissolve(otri) \ - (otri).tri[6 + (otri).orient] = (triangle) m->dummysub - -/* Dissolve a bond (from the subsegment side). */ - -#define stdissolve(osub) \ - (osub).ss[6 + (osub).ssorient] = (subseg) m->dummytri - -/********* Primitives for vertices *********/ -/* */ -/* */ - -#define vertexmark(vx) ((int *) (vx))[m->vertexmarkindex] - -#define setvertexmark(vx, value) \ - ((int *) (vx))[m->vertexmarkindex] = value - -#define vertextype(vx) ((int *) (vx))[m->vertexmarkindex + 1] - -#define setvertextype(vx, value) \ - ((int *) (vx))[m->vertexmarkindex + 1] = value - -#define vertex2tri(vx) ((triangle *) (vx))[m->vertex2triindex] - -#define setvertex2tri(vx, value) \ - ((triangle *) (vx))[m->vertex2triindex] = value - -/** **/ -/** **/ -/********* Mesh manipulation primitives end here *********/ - -/********* User-defined triangle evaluation routine begins here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* triunsuitable() Determine if a triangle is unsuitable, and thus must */ -/* be further refined. */ -/* */ -/* You may write your own procedure that decides whether or not a selected */ -/* triangle is too big (and needs to be refined). There are two ways to do */ -/* this. */ -/* */ -/* (1) Modify the procedure `triunsuitable' below, then recompile */ -/* Triangle. */ -/* */ -/* (2) Define the symbol EXTERNAL_TEST (either by adding the definition */ -/* to this file, or by using the appropriate compiler switch). This way, */ -/* you can compile triangle.c separately from your test. Write your own */ -/* `triunsuitable' procedure in a separate C file (using the same prototype */ -/* as below). Compile it and link the object code with triangle.o. */ -/* */ -/* This procedure returns 1 if the triangle is too large and should be */ -/* refined; 0 otherwise. */ -/* */ -/*****************************************************************************/ - -#ifdef EXTERNAL_TEST - -int triunsuitable(); - -#else /* not EXTERNAL_TEST */ - -#ifdef ANSI_DECLARATORS -int triunsuitable(vertex triorg, vertex tridest, vertex triapex, REAL area) -#else /* not ANSI_DECLARATORS */ -int triunsuitable(triorg, tridest, triapex, area) -vertex triorg; /* The triangle's origin vertex. */ -vertex tridest; /* The triangle's destination vertex. */ -vertex triapex; /* The triangle's apex vertex. */ -REAL area; /* The area of the triangle. */ -#endif /* not ANSI_DECLARATORS */ - -{ - REAL dxoa, dxda, dxod; - REAL dyoa, dyda, dyod; - REAL oalen, dalen, odlen; - REAL maxlen; - - dxoa = triorg[0] - triapex[0]; - dyoa = triorg[1] - triapex[1]; - dxda = tridest[0] - triapex[0]; - dyda = tridest[1] - triapex[1]; - dxod = triorg[0] - tridest[0]; - dyod = triorg[1] - tridest[1]; - /* Find the squares of the lengths of the triangle's three edges. */ - oalen = dxoa * dxoa + dyoa * dyoa; - dalen = dxda * dxda + dyda * dyda; - odlen = dxod * dxod + dyod * dyod; - /* Find the square of the length of the longest edge. */ - maxlen = (dalen > oalen) ? dalen : oalen; - maxlen = (odlen > maxlen) ? odlen : maxlen; - - if (maxlen > 0.05 * (triorg[0] * triorg[0] + triorg[1] * triorg[1]) + 0.02) { - return 1; - } else { - return 0; - } -} - -#endif /* not EXTERNAL_TEST */ - -/** **/ -/** **/ -/********* User-defined triangle evaluation routine ends here *********/ - -/********* Memory allocation and program exit wrappers begin here *********/ -/** **/ -/** **/ - -#ifdef ANSI_DECLARATORS -void triexit(int status) -#else /* not ANSI_DECLARATORS */ -void triexit(status) -int status; -#endif /* not ANSI_DECLARATORS */ - -{ - exit(status); -} - -#ifdef ANSI_DECLARATORS -VOID *trimalloc(int size) -#else /* not ANSI_DECLARATORS */ -VOID *trimalloc(size) -int size; -#endif /* not ANSI_DECLARATORS */ - -{ - VOID *memptr; - - memptr = (VOID *) malloc((unsigned int) size); - if (memptr == (VOID *) NULL) { - printf("Error: Out of memory.\n"); - triexit(1); - } - return(memptr); -} - -#ifdef ANSI_DECLARATORS -void trifree(VOID *memptr) -#else /* not ANSI_DECLARATORS */ -void trifree(memptr) -VOID *memptr; -#endif /* not ANSI_DECLARATORS */ - -{ - free(memptr); -} - -/** **/ -/** **/ -/********* Memory allocation and program exit wrappers end here *********/ - -/********* User interaction routines begin here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* syntax() Print list of command line switches. */ -/* */ -/*****************************************************************************/ - -#ifndef TRILIBRARY - -void syntax() -{ -#ifdef CDT_ONLY -#ifdef REDUCED - printf("triangle [-pAcjevngBPNEIOXzo_lQVh] input_file\n"); -#else /* not REDUCED */ - printf("triangle [-pAcjevngBPNEIOXzo_iFlCQVh] input_file\n"); -#endif /* not REDUCED */ -#else /* not CDT_ONLY */ -#ifdef REDUCED - printf("triangle [-prq__a__uAcDjevngBPNEIOXzo_YS__lQVh] input_file\n"); -#else /* not REDUCED */ - printf("triangle [-prq__a__uAcDjevngBPNEIOXzo_YS__iFlsCQVh] input_file\n"); -#endif /* not REDUCED */ -#endif /* not CDT_ONLY */ - - printf(" -p Triangulates a Planar Straight Line Graph (.poly file).\n"); -#ifndef CDT_ONLY - printf(" -r Refines a previously generated mesh.\n"); - printf( - " -q Quality mesh generation. A minimum angle may be specified.\n"); - printf(" -a Applies a maximum triangle area constraint.\n"); - printf(" -u Applies a user-defined triangle constraint.\n"); -#endif /* not CDT_ONLY */ - printf( - " -A Applies attributes to identify triangles in certain regions.\n"); - printf(" -c Encloses the convex hull with segments.\n"); -#ifndef CDT_ONLY - printf(" -D Conforming Delaunay: all triangles are truly Delaunay.\n"); -#endif /* not CDT_ONLY */ -/* - printf(" -w Weighted Delaunay triangulation.\n"); - printf(" -W Regular triangulation (lower hull of a height field).\n"); -*/ - printf(" -j Jettison unused vertices from output .node file.\n"); - printf(" -e Generates an edge list.\n"); - printf(" -v Generates a Voronoi diagram.\n"); - printf(" -n Generates a list of triangle neighbors.\n"); - printf(" -g Generates an .off file for Geomview.\n"); - printf(" -B Suppresses output of boundary information.\n"); - printf(" -P Suppresses output of .poly file.\n"); - printf(" -N Suppresses output of .node file.\n"); - printf(" -E Suppresses output of .ele file.\n"); - printf(" -I Suppresses mesh iteration numbers.\n"); - printf(" -O Ignores holes in .poly file.\n"); - printf(" -X Suppresses use of exact arithmetic.\n"); - printf(" -z Numbers all items starting from zero (rather than one).\n"); - printf(" -o2 Generates second-order subparametric elements.\n"); -#ifndef CDT_ONLY - printf(" -Y Suppresses boundary segment splitting.\n"); - printf(" -S Specifies maximum number of added Steiner points.\n"); -#endif /* not CDT_ONLY */ -#ifndef REDUCED - printf(" -i Uses incremental method, rather than divide-and-conquer.\n"); - printf(" -F Uses Fortune's sweepline algorithm, rather than d-and-c.\n"); -#endif /* not REDUCED */ - printf(" -l Uses vertical cuts only, rather than alternating cuts.\n"); -#ifndef REDUCED -#ifndef CDT_ONLY - printf( - " -s Force segments into mesh by splitting (instead of using CDT).\n"); -#endif /* not CDT_ONLY */ - printf(" -C Check consistency of final mesh.\n"); -#endif /* not REDUCED */ - printf(" -Q Quiet: No terminal output except errors.\n"); - printf(" -V Verbose: Detailed information on what I'm doing.\n"); - printf(" -h Help: Detailed instructions for Triangle.\n"); - triexit(0); -} - -#endif /* not TRILIBRARY */ - -/*****************************************************************************/ -/* */ -/* info() Print out complete instructions. */ -/* */ -/*****************************************************************************/ - -#ifndef TRILIBRARY - -void info() -{ - printf("Triangle\n"); - printf( -"A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator.\n"); - printf("Version 1.6\n\n"); - printf( -"Copyright 1993, 1995, 1997, 1998, 2002, 2005 Jonathan Richard Shewchuk\n"); - printf("2360 Woolsey #H / Berkeley, California 94705-1927\n"); - printf("Bugs/comments to jrs@cs.berkeley.edu\n"); - printf( -"Created as part of the Quake project (tools for earthquake simulation).\n"); - printf( -"Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship.\n"); - printf("There is no warranty whatsoever. Use at your own risk.\n"); -#ifdef SINGLE - printf("This executable is compiled for single precision arithmetic.\n\n\n"); -#else /* not SINGLE */ - printf("This executable is compiled for double precision arithmetic.\n\n\n"); -#endif /* not SINGLE */ - printf( -"Triangle generates exact Delaunay triangulations, constrained Delaunay\n"); - printf( -"triangulations, conforming Delaunay triangulations, Voronoi diagrams, and\n"); - printf( -"high-quality triangular meshes. The latter can be generated with no small\n" -); - printf( -"or large angles, and are thus suitable for finite element analysis. If no\n" -); - printf( -"command line switch is specified, your .node input file is read, and the\n"); - printf( -"Delaunay triangulation is returned in .node and .ele output files. The\n"); - printf("command syntax is:\n\n"); - printf("triangle [-prq__a__uAcDjevngBPNEIOXzo_YS__iFlsCQVh] input_file\n\n"); - printf( -"Underscores indicate that numbers may optionally follow certain switches.\n"); - printf( -"Do not leave any space between a switch and its numeric parameter.\n"); - printf( -"input_file must be a file with extension .node, or extension .poly if the\n"); - printf( -"-p switch is used. If -r is used, you must supply .node and .ele files,\n"); - printf( -"and possibly a .poly file and an .area file as well. The formats of these\n" -); - printf("files are described below.\n\n"); - printf("Command Line Switches:\n\n"); - printf( -" -p Reads a Planar Straight Line Graph (.poly file), which can specify\n" -); - printf( -" vertices, segments, holes, regional attributes, and regional area\n"); - printf( -" constraints. Generates a constrained Delaunay triangulation (CDT)\n" -); - printf( -" fitting the input; or, if -s, -q, -a, or -u is used, a conforming\n"); - printf( -" constrained Delaunay triangulation (CCDT). If you want a truly\n"); - printf( -" Delaunay (not just constrained Delaunay) triangulation, use -D as\n"); - printf( -" well. When -p is not used, Triangle reads a .node file by default.\n" -); - printf( -" -r Refines a previously generated mesh. The mesh is read from a .node\n" -); - printf( -" file and an .ele file. If -p is also used, a .poly file is read\n"); - printf( -" and used to constrain segments in the mesh. If -a is also used\n"); - printf( -" (with no number following), an .area file is read and used to\n"); - printf( -" impose area constraints on the mesh. Further details on refinement\n" -); - printf(" appear below.\n"); - printf( -" -q Quality mesh generation by Delaunay refinement (a hybrid of Paul\n"); - printf( -" Chew's and Jim Ruppert's algorithms). Adds vertices to the mesh to\n" -); - printf( -" ensure that all angles are between 20 and 140 degrees. An\n"); - printf( -" alternative bound on the minimum angle, replacing 20 degrees, may\n"); - printf( -" be specified after the `q'. The specified angle may include a\n"); - printf( -" decimal point, but not exponential notation. Note that a bound of\n" -); - printf( -" theta degrees on the smallest angle also implies a bound of\n"); - printf( -" (180 - 2 theta) on the largest angle. If the minimum angle is 28.6\n" -); - printf( -" degrees or smaller, Triangle is mathematically guaranteed to\n"); - printf( -" terminate (assuming infinite precision arithmetic--Triangle may\n"); - printf( -" fail to terminate if you run out of precision). In practice,\n"); - printf( -" Triangle often succeeds for minimum angles up to 34 degrees. For\n"); - printf( -" some meshes, however, you might need to reduce the minimum angle to\n" -); - printf( -" avoid problems associated with insufficient floating-point\n"); - printf(" precision.\n"); - printf( -" -a Imposes a maximum triangle area. If a number follows the `a', no\n"); - printf( -" triangle is generated whose area is larger than that number. If no\n" -); - printf( -" number is specified, an .area file (if -r is used) or .poly file\n"); - printf( -" (if -r is not used) specifies a set of maximum area constraints.\n"); - printf( -" An .area file contains a separate area constraint for each\n"); - printf( -" triangle, and is useful for refining a finite element mesh based on\n" -); - printf( -" a posteriori error estimates. A .poly file can optionally contain\n" -); - printf( -" an area constraint for each segment-bounded region, thereby\n"); - printf( -" controlling triangle densities in a first triangulation of a PSLG.\n" -); - printf( -" You can impose both a fixed area constraint and a varying area\n"); - printf( -" constraint by invoking the -a switch twice, once with and once\n"); - printf( -" without a number following. Each area specified may include a\n"); - printf(" decimal point.\n"); - printf( -" -u Imposes a user-defined constraint on triangle size. There are two\n" -); - printf( -" ways to use this feature. One is to edit the triunsuitable()\n"); - printf( -" procedure in triangle.c to encode any constraint you like, then\n"); - printf( -" recompile Triangle. The other is to compile triangle.c with the\n"); - printf( -" EXTERNAL_TEST symbol set (compiler switch -DEXTERNAL_TEST), then\n"); - printf( -" link Triangle with a separate object file that implements\n"); - printf( -" triunsuitable(). In either case, the -u switch causes the user-\n"); - printf(" defined test to be applied to every triangle.\n"); - printf( -" -A Assigns an additional floating-point attribute to each triangle\n"); - printf( -" that identifies what segment-bounded region each triangle belongs\n"); - printf( -" to. Attributes are assigned to regions by the .poly file. If a\n"); - printf( -" region is not explicitly marked by the .poly file, triangles in\n"); - printf( -" that region are assigned an attribute of zero. The -A switch has\n"); - printf( -" an effect only when the -p switch is used and the -r switch is not.\n" -); - printf( -" -c Creates segments on the convex hull of the triangulation. If you\n"); - printf( -" are triangulating a vertex set, this switch causes a .poly file to\n" -); - printf( -" be written, containing all edges of the convex hull. If you are\n"); - printf( -" triangulating a PSLG, this switch specifies that the whole convex\n"); - printf( -" hull of the PSLG should be triangulated, regardless of what\n"); - printf( -" segments the PSLG has. If you do not use this switch when\n"); - printf( -" triangulating a PSLG, Triangle assumes that you have identified the\n" -); - printf( -" region to be triangulated by surrounding it with segments of the\n"); - printf( -" input PSLG. Beware: if you are not careful, this switch can cause\n" -); - printf( -" the introduction of an extremely thin angle between a PSLG segment\n" -); - printf( -" and a convex hull segment, which can cause overrefinement (and\n"); - printf( -" possibly failure if Triangle runs out of precision). If you are\n"); - printf( -" refining a mesh, the -c switch works differently: it causes a\n"); - printf( -" .poly file to be written containing the boundary edges of the mesh\n" -); - printf(" (useful if no .poly file was read).\n"); - printf( -" -D Conforming Delaunay triangulation: use this switch if you want to\n" -); - printf( -" ensure that all the triangles in the mesh are Delaunay, and not\n"); - printf( -" merely constrained Delaunay; or if you want to ensure that all the\n" -); - printf( -" Voronoi vertices lie within the triangulation. (Some finite volume\n" -); - printf( -" methods have this requirement.) This switch invokes Ruppert's\n"); - printf( -" original algorithm, which splits every subsegment whose diametral\n"); - printf( -" circle is encroached. It usually increases the number of vertices\n" -); - printf(" and triangles.\n"); - printf( -" -j Jettisons vertices that are not part of the final triangulation\n"); - printf( -" from the output .node file. By default, Triangle copies all\n"); - printf( -" vertices in the input .node file to the output .node file, in the\n"); - printf( -" same order, so their indices do not change. The -j switch prevents\n" -); - printf( -" duplicated input vertices, or vertices `eaten' by holes, from\n"); - printf( -" appearing in the output .node file. Thus, if two input vertices\n"); - printf( -" have exactly the same coordinates, only the first appears in the\n"); - printf( -" output. If any vertices are jettisoned, the vertex numbering in\n"); - printf( -" the output .node file differs from that of the input .node file.\n"); - printf( -" -e Outputs (to an .edge file) a list of edges of the triangulation.\n"); - printf( -" -v Outputs the Voronoi diagram associated with the triangulation.\n"); - printf( -" Does not attempt to detect degeneracies, so some Voronoi vertices\n"); - printf( -" may be duplicated. See the discussion of Voronoi diagrams below.\n"); - printf( -" -n Outputs (to a .neigh file) a list of triangles neighboring each\n"); - printf(" triangle.\n"); - printf( -" -g Outputs the mesh to an Object File Format (.off) file, suitable for\n" -); - printf(" viewing with the Geometry Center's Geomview package.\n"); - printf( -" -B No boundary markers in the output .node, .poly, and .edge output\n"); - printf( -" files. See the detailed discussion of boundary markers below.\n"); - printf( -" -P No output .poly file. Saves disk space, but you lose the ability\n"); - printf( -" to maintain constraining segments on later refinements of the mesh.\n" -); - printf(" -N No output .node file.\n"); - printf(" -E No output .ele file.\n"); - printf( -" -I No iteration numbers. Suppresses the output of .node and .poly\n"); - printf( -" files, so your input files won't be overwritten. (If your input is\n" -); - printf( -" a .poly file only, a .node file is written.) Cannot be used with\n"); - printf( -" the -r switch, because that would overwrite your input .ele file.\n"); - printf( -" Shouldn't be used with the -q, -a, -u, or -s switch if you are\n"); - printf( -" using a .node file for input, because no .node file is written, so\n" -); - printf(" there is no record of any added Steiner points.\n"); - printf(" -O No holes. Ignores the holes in the .poly file.\n"); - printf( -" -X No exact arithmetic. Normally, Triangle uses exact floating-point\n" -); - printf( -" arithmetic for certain tests if it thinks the inexact tests are not\n" -); - printf( -" accurate enough. Exact arithmetic ensures the robustness of the\n"); - printf( -" triangulation algorithms, despite floating-point roundoff error.\n"); - printf( -" Disabling exact arithmetic with the -X switch causes a small\n"); - printf( -" improvement in speed and creates the possibility that Triangle will\n" -); - printf(" fail to produce a valid mesh. Not recommended.\n"); - printf( -" -z Numbers all items starting from zero (rather than one). Note that\n" -); - printf( -" this switch is normally overridden by the value used to number the\n" -); - printf( -" first vertex of the input .node or .poly file. However, this\n"); - printf( -" switch is useful when calling Triangle from another program.\n"); - printf( -" -o2 Generates second-order subparametric elements with six nodes each.\n" -); - printf( -" -Y No new vertices on the boundary. This switch is useful when the\n"); - printf( -" mesh boundary must be preserved so that it conforms to some\n"); - printf( -" adjacent mesh. Be forewarned that you will probably sacrifice much\n" -); - printf( -" of the quality of the mesh; Triangle will try, but the resulting\n"); - printf( -" mesh may contain poorly shaped triangles. Works well if all the\n"); - printf( -" boundary vertices are closely spaced. Specify this switch twice\n"); - printf( -" (`-YY') to prevent all segment splitting, including internal\n"); - printf(" boundaries.\n"); - printf( -" -S Specifies the maximum number of Steiner points (vertices that are\n"); - printf( -" not in the input, but are added to meet the constraints on minimum\n" -); - printf( -" angle and maximum area). The default is to allow an unlimited\n"); - printf( -" number. If you specify this switch with no number after it,\n"); - printf( -" the limit is set to zero. Triangle always adds vertices at segment\n" -); - printf( -" intersections, even if it needs to use more vertices than the limit\n" -); - printf( -" you set. When Triangle inserts segments by splitting (-s), it\n"); - printf( -" always adds enough vertices to ensure that all the segments of the\n" -); - printf(" PLSG are recovered, ignoring the limit if necessary.\n"); - printf( -" -i Uses an incremental rather than a divide-and-conquer algorithm to\n"); - printf( -" construct a Delaunay triangulation. Try it if the divide-and-\n"); - printf(" conquer algorithm fails.\n"); - printf( -" -F Uses Steven Fortune's sweepline algorithm to construct a Delaunay\n"); - printf( -" triangulation. Warning: does not use exact arithmetic for all\n"); - printf(" calculations. An exact result is not guaranteed.\n"); - printf( -" -l Uses only vertical cuts in the divide-and-conquer algorithm. By\n"); - printf( -" default, Triangle alternates between vertical and horizontal cuts,\n" -); - printf( -" which usually improve the speed except with vertex sets that are\n"); - printf( -" small or short and wide. This switch is primarily of theoretical\n"); - printf(" interest.\n"); - printf( -" -s Specifies that segments should be forced into the triangulation by\n" -); - printf( -" recursively splitting them at their midpoints, rather than by\n"); - printf( -" generating a constrained Delaunay triangulation. Segment splitting\n" -); - printf( -" is true to Ruppert's original algorithm, but can create needlessly\n" -); - printf( -" small triangles. This switch is primarily of theoretical interest.\n" -); - printf( -" -C Check the consistency of the final mesh. Uses exact arithmetic for\n" -); - printf( -" checking, even if the -X switch is used. Useful if you suspect\n"); - printf(" Triangle is buggy.\n"); - printf( -" -Q Quiet: Suppresses all explanation of what Triangle is doing,\n"); - printf(" unless an error occurs.\n"); - printf( -" -V Verbose: Gives detailed information about what Triangle is doing.\n" -); - printf( -" Add more `V's for increasing amount of detail. `-V' is most\n"); - printf( -" useful; itgives information on algorithmic progress and much more\n"); - printf( -" detailed statistics. `-VV' gives vertex-by-vertex details, and\n"); - printf( -" prints so much that Triangle runs much more slowly. `-VVVV' gives\n" -); - printf(" information only a debugger could love.\n"); - printf(" -h Help: Displays these instructions.\n"); - printf("\n"); - printf("Definitions:\n"); - printf("\n"); - printf( -" A Delaunay triangulation of a vertex set is a triangulation whose\n"); - printf( -" vertices are the vertex set, that covers the convex hull of the vertex\n"); - printf( -" set. A Delaunay triangulation has the property that no vertex lies\n"); - printf( -" inside the circumscribing circle (circle that passes through all three\n"); - printf(" vertices) of any triangle in the triangulation.\n\n"); - printf( -" A Voronoi diagram of a vertex set is a subdivision of the plane into\n"); - printf( -" polygonal cells (some of which may be unbounded, meaning infinitely\n"); - printf( -" large), where each cell is the set of points in the plane that are closer\n" -); - printf( -" to some input vertex than to any other input vertex. The Voronoi diagram\n" -); - printf(" is a geometric dual of the Delaunay triangulation.\n\n"); - printf( -" A Planar Straight Line Graph (PSLG) is a set of vertices and segments.\n"); - printf( -" Segments are simply edges, whose endpoints are all vertices in the PSLG.\n" -); - printf( -" Segments may intersect each other only at their endpoints. The file\n"); - printf(" format for PSLGs (.poly files) is described below.\n\n"); - printf( -" A constrained Delaunay triangulation (CDT) of a PSLG is similar to a\n"); - printf( -" Delaunay triangulation, but each PSLG segment is present as a single edge\n" -); - printf( -" of the CDT. (A constrained Delaunay triangulation is not truly a\n"); - printf( -" Delaunay triangulation, because some of its triangles might not be\n"); - printf( -" Delaunay.) By definition, a CDT does not have any vertices other than\n"); - printf( -" those specified in the input PSLG. Depending on context, a CDT might\n"); - printf( -" cover the convex hull of the PSLG, or it might cover only a segment-\n"); - printf(" bounded region (e.g. a polygon).\n\n"); - printf( -" A conforming Delaunay triangulation of a PSLG is a triangulation in which\n" -); - printf( -" each triangle is truly Delaunay, and each PSLG segment is represented by\n" -); - printf( -" a linear contiguous sequence of edges of the triangulation. New vertices\n" -); - printf( -" (not part of the PSLG) may appear, and each input segment may have been\n"); - printf( -" subdivided into shorter edges (subsegments) by these additional vertices.\n" -); - printf( -" The new vertices are frequently necessary to maintain the Delaunay\n"); - printf(" property while ensuring that every segment is represented.\n\n"); - printf( -" A conforming constrained Delaunay triangulation (CCDT) of a PSLG is a\n"); - printf( -" triangulation of a PSLG whose triangles are constrained Delaunay. New\n"); - printf(" vertices may appear, and input segments may be subdivided into\n"); - printf( -" subsegments, but not to guarantee that segments are respected; rather, to\n" -); - printf( -" improve the quality of the triangles. The high-quality meshes produced\n"); - printf( -" by the -q switch are usually CCDTs, but can be made conforming Delaunay\n"); - printf(" with the -D switch.\n\n"); - printf("File Formats:\n\n"); - printf( -" All files may contain comments prefixed by the character '#'. Vertices,\n" -); - printf( -" triangles, edges, holes, and maximum area constraints must be numbered\n"); - printf( -" consecutively, starting from either 1 or 0. Whichever you choose, all\n"); - printf( -" input files must be consistent; if the vertices are numbered from 1, so\n"); - printf( -" must be all other objects. Triangle automatically detects your choice\n"); - printf( -" while reading the .node (or .poly) file. (When calling Triangle from\n"); - printf( -" another program, use the -z switch if you wish to number objects from\n"); - printf(" zero.) Examples of these file formats are given below.\n\n"); - printf(" .node files:\n"); - printf( -" First line: <# of vertices> <# of attributes>\n" -); - printf( -" <# of boundary markers (0 or 1)>\n" -); - printf( -" Remaining lines: [attributes] [boundary marker]\n"); - printf("\n"); - printf( -" The attributes, which are typically floating-point values of physical\n"); - printf( -" quantities (such as mass or conductivity) associated with the nodes of\n" -); - printf( -" a finite element mesh, are copied unchanged to the output mesh. If -q,\n" -); - printf( -" -a, -u, -D, or -s is selected, each new Steiner point added to the mesh\n" -); - printf(" has attributes assigned to it by linear interpolation.\n\n"); - printf( -" If the fourth entry of the first line is `1', the last column of the\n"); - printf( -" remainder of the file is assumed to contain boundary markers. Boundary\n" -); - printf( -" markers are used to identify boundary vertices and vertices resting on\n" -); - printf( -" PSLG segments; a complete description appears in a section below. The\n" -); - printf( -" .node file produced by Triangle contains boundary markers in the last\n"); - printf(" column unless they are suppressed by the -B switch.\n\n"); - printf(" .ele files:\n"); - printf( -" First line: <# of triangles> <# of attributes>\n"); - printf( -" Remaining lines: ... [attributes]\n"); - printf("\n"); - printf( -" Nodes are indices into the corresponding .node file. The first three\n"); - printf( -" nodes are the corner vertices, and are listed in counterclockwise order\n" -); - printf( -" around each triangle. (The remaining nodes, if any, depend on the type\n" -); - printf(" of finite element used.)\n\n"); - printf( -" The attributes are just like those of .node files. Because there is no\n" -); - printf( -" simple mapping from input to output triangles, Triangle attempts to\n"); - printf( -" interpolate attributes, and may cause a lot of diffusion of attributes\n" -); - printf( -" among nearby triangles as the triangulation is refined. Attributes do\n" -); - printf(" not diffuse across segments, so attributes used to identify\n"); - printf(" segment-bounded regions remain intact.\n\n"); - printf( -" In .ele files produced by Triangle, each triangular element has three\n"); - printf( -" nodes (vertices) unless the -o2 switch is used, in which case\n"); - printf( -" subparametric quadratic elements with six nodes each are generated.\n"); - printf( -" The first three nodes are the corners in counterclockwise order, and\n"); - printf( -" the fourth, fifth, and sixth nodes lie on the midpoints of the edges\n"); - printf( -" opposite the first, second, and third vertices, respectively.\n"); - printf("\n"); - printf(" .poly files:\n"); - printf( -" First line: <# of vertices> <# of attributes>\n" -); - printf( -" <# of boundary markers (0 or 1)>\n" -); - printf( -" Following lines: [attributes] [boundary marker]\n"); - printf(" One line: <# of segments> <# of boundary markers (0 or 1)>\n"); - printf( -" Following lines: [boundary marker]\n"); - printf(" One line: <# of holes>\n"); - printf(" Following lines: \n"); - printf( -" Optional line: <# of regional attributes and/or area constraints>\n"); - printf( -" Optional following lines: \n"); - printf("\n"); - printf( -" A .poly file represents a PSLG, as well as some additional information.\n" -); - printf( -" The first section lists all the vertices, and is identical to the\n"); - printf( -" format of .node files. <# of vertices> may be set to zero to indicate\n" -); - printf( -" that the vertices are listed in a separate .node file; .poly files\n"); - printf( -" produced by Triangle always have this format. A vertex set represented\n" -); - printf( -" this way has the advantage that it may easily be triangulated with or\n"); - printf( -" without segments (depending on whether the -p switch is invoked).\n"); - printf("\n"); - printf( -" The second section lists the segments. Segments are edges whose\n"); - printf( -" presence in the triangulation is enforced. (Depending on the choice of\n" -); - printf( -" switches, segment might be subdivided into smaller edges). Each\n"); - printf( -" segment is specified by listing the indices of its two endpoints. This\n" -); - printf( -" means that you must include its endpoints in the vertex list. Each\n"); - printf(" segment, like each point, may have a boundary marker.\n\n"); - printf( -" If -q, -a, -u, and -s are not selected, Triangle produces a constrained\n" -); - printf( -" Delaunay triangulation (CDT), in which each segment appears as a single\n" -); - printf( -" edge in the triangulation. If -q, -a, -u, or -s is selected, Triangle\n" -); - printf( -" produces a conforming constrained Delaunay triangulation (CCDT), in\n"); - printf( -" which segments may be subdivided into smaller edges. If -D is\n"); - printf( -" selected, Triangle produces a conforming Delaunay triangulation, so\n"); - printf( -" that every triangle is Delaunay, and not just constrained Delaunay.\n"); - printf("\n"); - printf( -" The third section lists holes (and concavities, if -c is selected) in\n"); - printf( -" the triangulation. Holes are specified by identifying a point inside\n"); - printf( -" each hole. After the triangulation is formed, Triangle creates holes\n"); - printf( -" by eating triangles, spreading out from each hole point until its\n"); - printf( -" progress is blocked by segments in the PSLG. You must be careful to\n"); - printf( -" enclose each hole in segments, or your whole triangulation might be\n"); - printf( -" eaten away. If the two triangles abutting a segment are eaten, the\n"); - printf( -" segment itself is also eaten. Do not place a hole directly on a\n"); - printf(" segment; if you do, Triangle chooses one side of the segment\n"); - printf(" arbitrarily.\n\n"); - printf( -" The optional fourth section lists regional attributes (to be assigned\n"); - printf( -" to all triangles in a region) and regional constraints on the maximum\n"); - printf( -" triangle area. Triangle reads this section only if the -A switch is\n"); - printf( -" used or the -a switch is used without a number following it, and the -r\n" -); - printf( -" switch is not used. Regional attributes and area constraints are\n"); - printf( -" propagated in the same manner as holes: you specify a point for each\n"); - printf( -" attribute and/or constraint, and the attribute and/or constraint\n"); - printf( -" affects the whole region (bounded by segments) containing the point.\n"); - printf( -" If two values are written on a line after the x and y coordinate, the\n"); - printf( -" first such value is assumed to be a regional attribute (but is only\n"); - printf( -" applied if the -A switch is selected), and the second value is assumed\n" -); - printf( -" to be a regional area constraint (but is only applied if the -a switch\n" -); - printf( -" is selected). You may specify just one value after the coordinates,\n"); - printf( -" which can serve as both an attribute and an area constraint, depending\n" -); - printf( -" on the choice of switches. If you are using the -A and -a switches\n"); - printf( -" simultaneously and wish to assign an attribute to some region without\n"); - printf(" imposing an area constraint, use a negative maximum area.\n\n"); - printf( -" When a triangulation is created from a .poly file, you must either\n"); - printf( -" enclose the entire region to be triangulated in PSLG segments, or\n"); - printf( -" use the -c switch, which automatically creates extra segments that\n"); - printf( -" enclose the convex hull of the PSLG. If you do not use the -c switch,\n" -); - printf( -" Triangle eats all triangles that are not enclosed by segments; if you\n"); - printf( -" are not careful, your whole triangulation may be eaten away. If you do\n" -); - printf( -" use the -c switch, you can still produce concavities by the appropriate\n" -); - printf( -" placement of holes just inside the boundary of the convex hull.\n"); - printf("\n"); - printf( -" An ideal PSLG has no intersecting segments, nor any vertices that lie\n"); - printf( -" upon segments (except, of course, the endpoints of each segment). You\n" -); - printf( -" aren't required to make your .poly files ideal, but you should be aware\n" -); - printf( -" of what can go wrong. Segment intersections are relatively safe--\n"); - printf( -" Triangle calculates the intersection points for you and adds them to\n"); - printf( -" the triangulation--as long as your machine's floating-point precision\n"); - printf( -" doesn't become a problem. You are tempting the fates if you have three\n" -); - printf( -" segments that cross at the same location, and expect Triangle to figure\n" -); - printf( -" out where the intersection point is. Thanks to floating-point roundoff\n" -); - printf( -" error, Triangle will probably decide that the three segments intersect\n" -); - printf( -" at three different points, and you will find a minuscule triangle in\n"); - printf( -" your output--unless Triangle tries to refine the tiny triangle, uses\n"); - printf( -" up the last bit of machine precision, and fails to terminate at all.\n"); - printf( -" You're better off putting the intersection point in the input files,\n"); - printf( -" and manually breaking up each segment into two. Similarly, if you\n"); - printf( -" place a vertex at the middle of a segment, and hope that Triangle will\n" -); - printf( -" break up the segment at that vertex, you might get lucky. On the other\n" -); - printf( -" hand, Triangle might decide that the vertex doesn't lie precisely on\n"); - printf( -" the segment, and you'll have a needle-sharp triangle in your output--or\n" -); - printf(" a lot of tiny triangles if you're generating a quality mesh.\n"); - printf("\n"); - printf( -" When Triangle reads a .poly file, it also writes a .poly file, which\n"); - printf( -" includes all the subsegments--the edges that are parts of input\n"); - printf( -" segments. If the -c switch is used, the output .poly file also\n"); - printf( -" includes all of the edges on the convex hull. Hence, the output .poly\n" -); - printf( -" file is useful for finding edges associated with input segments and for\n" -); - printf( -" setting boundary conditions in finite element simulations. Moreover,\n"); - printf( -" you will need the output .poly file if you plan to refine the output\n"); - printf( -" mesh, and don't want segments to be missing in later triangulations.\n"); - printf("\n"); - printf(" .area files:\n"); - printf(" First line: <# of triangles>\n"); - printf(" Following lines: \n"); - printf("\n"); - printf( -" An .area file associates with each triangle a maximum area that is used\n" -); - printf( -" for mesh refinement. As with other file formats, every triangle must\n"); - printf( -" be represented, and the triangles must be numbered consecutively. A\n"); - printf( -" triangle may be left unconstrained by assigning it a negative maximum\n"); - printf(" area.\n\n"); - printf(" .edge files:\n"); - printf(" First line: <# of edges> <# of boundary markers (0 or 1)>\n"); - printf( -" Following lines: [boundary marker]\n"); - printf("\n"); - printf( -" Endpoints are indices into the corresponding .node file. Triangle can\n" -); - printf( -" produce .edge files (use the -e switch), but cannot read them. The\n"); - printf( -" optional column of boundary markers is suppressed by the -B switch.\n"); - printf("\n"); - printf( -" In Voronoi diagrams, one also finds a special kind of edge that is an\n"); - printf( -" infinite ray with only one endpoint. For these edges, a different\n"); - printf(" format is used:\n\n"); - printf(" -1 \n\n"); - printf( -" The `direction' is a floating-point vector that indicates the direction\n" -); - printf(" of the infinite ray.\n\n"); - printf(" .neigh files:\n"); - printf( -" First line: <# of triangles> <# of neighbors per triangle (always 3)>\n" -); - printf( -" Following lines: \n"); - printf("\n"); - printf( -" Neighbors are indices into the corresponding .ele file. An index of -1\n" -); - printf( -" indicates no neighbor (because the triangle is on an exterior\n"); - printf( -" boundary). The first neighbor of triangle i is opposite the first\n"); - printf(" corner of triangle i, and so on.\n\n"); - printf( -" Triangle can produce .neigh files (use the -n switch), but cannot read\n" -); - printf(" them.\n\n"); - printf("Boundary Markers:\n\n"); - printf( -" Boundary markers are tags used mainly to identify which output vertices\n"); - printf( -" and edges are associated with which PSLG segment, and to identify which\n"); - printf( -" vertices and edges occur on a boundary of the triangulation. A common\n"); - printf( -" use is to determine where boundary conditions should be applied to a\n"); - printf( -" finite element mesh. You can prevent boundary markers from being written\n" -); - printf(" into files produced by Triangle by using the -B switch.\n\n"); - printf( -" The boundary marker associated with each segment in an output .poly file\n" -); - printf(" and each edge in an output .edge file is chosen as follows:\n"); - printf( -" - If an output edge is part or all of a PSLG segment with a nonzero\n"); - printf( -" boundary marker, then the edge is assigned the same marker.\n"); - printf( -" - Otherwise, if the edge lies on a boundary of the triangulation\n"); - printf( -" (even the boundary of a hole), then the edge is assigned the marker\n"); - printf(" one (1).\n"); - printf(" - Otherwise, the edge is assigned the marker zero (0).\n"); - printf( -" The boundary marker associated with each vertex in an output .node file\n"); - printf(" is chosen as follows:\n"); - printf( -" - If a vertex is assigned a nonzero boundary marker in the input file,\n" -); - printf( -" then it is assigned the same marker in the output .node file.\n"); - printf( -" - Otherwise, if the vertex lies on a PSLG segment (even if it is an\n"); - printf( -" endpoint of the segment) with a nonzero boundary marker, then the\n"); - printf( -" vertex is assigned the same marker. If the vertex lies on several\n"); - printf(" such segments, one of the markers is chosen arbitrarily.\n"); - printf( -" - Otherwise, if the vertex occurs on a boundary of the triangulation,\n"); - printf(" then the vertex is assigned the marker one (1).\n"); - printf(" - Otherwise, the vertex is assigned the marker zero (0).\n"); - printf("\n"); - printf( -" If you want Triangle to determine for you which vertices and edges are on\n" -); - printf( -" the boundary, assign them the boundary marker zero (or use no markers at\n" -); - printf( -" all) in your input files. In the output files, all boundary vertices,\n"); - printf(" edges, and segments will be assigned the value one.\n\n"); - printf("Triangulation Iteration Numbers:\n\n"); - printf( -" Because Triangle can read and refine its own triangulations, input\n"); - printf( -" and output files have iteration numbers. For instance, Triangle might\n"); - printf( -" read the files mesh.3.node, mesh.3.ele, and mesh.3.poly, refine the\n"); - printf( -" triangulation, and output the files mesh.4.node, mesh.4.ele, and\n"); - printf(" mesh.4.poly. Files with no iteration number are treated as if\n"); - printf( -" their iteration number is zero; hence, Triangle might read the file\n"); - printf( -" points.node, triangulate it, and produce the files points.1.node and\n"); - printf(" points.1.ele.\n\n"); - printf( -" Iteration numbers allow you to create a sequence of successively finer\n"); - printf( -" meshes suitable for multigrid methods. They also allow you to produce a\n" -); - printf( -" sequence of meshes using error estimate-driven mesh refinement.\n"); - printf("\n"); - printf( -" If you're not using refinement or quality meshing, and you don't like\n"); - printf( -" iteration numbers, use the -I switch to disable them. This switch also\n"); - printf( -" disables output of .node and .poly files to prevent your input files from\n" -); - printf( -" being overwritten. (If the input is a .poly file that contains its own\n"); - printf( -" points, a .node file is written. This can be quite convenient for\n"); - printf(" computing CDTs or quality meshes.)\n\n"); - printf("Examples of How to Use Triangle:\n\n"); - printf( -" `triangle dots' reads vertices from dots.node, and writes their Delaunay\n" -); - printf( -" triangulation to dots.1.node and dots.1.ele. (dots.1.node is identical\n"); - printf( -" to dots.node.) `triangle -I dots' writes the triangulation to dots.ele\n"); - printf( -" instead. (No additional .node file is needed, so none is written.)\n"); - printf("\n"); - printf( -" `triangle -pe object.1' reads a PSLG from object.1.poly (and possibly\n"); - printf( -" object.1.node, if the vertices are omitted from object.1.poly) and writes\n" -); - printf( -" its constrained Delaunay triangulation to object.2.node and object.2.ele.\n" -); - printf( -" The segments are copied to object.2.poly, and all edges are written to\n"); - printf(" object.2.edge.\n\n"); - printf( -" `triangle -pq31.5a.1 object' reads a PSLG from object.poly (and possibly\n" -); - printf( -" object.node), generates a mesh whose angles are all between 31.5 and 117\n" -); - printf( -" degrees and whose triangles all have areas of 0.1 or less, and writes the\n" -); - printf( -" mesh to object.1.node and object.1.ele. Each segment may be broken up\n"); - printf(" into multiple subsegments; these are written to object.1.poly.\n"); - printf("\n"); - printf( -" Here is a sample file `box.poly' describing a square with a square hole:\n" -); - printf("\n"); - printf( -" # A box with eight vertices in 2D, no attributes, one boundary marker.\n" -); - printf(" 8 2 0 1\n"); - printf(" # Outer box has these vertices:\n"); - printf(" 1 0 0 0\n"); - printf(" 2 0 3 0\n"); - printf(" 3 3 0 0\n"); - printf(" 4 3 3 33 # A special marker for this vertex.\n"); - printf(" # Inner square has these vertices:\n"); - printf(" 5 1 1 0\n"); - printf(" 6 1 2 0\n"); - printf(" 7 2 1 0\n"); - printf(" 8 2 2 0\n"); - printf(" # Five segments with boundary markers.\n"); - printf(" 5 1\n"); - printf(" 1 1 2 5 # Left side of outer box.\n"); - printf(" # Square hole has these segments:\n"); - printf(" 2 5 7 0\n"); - printf(" 3 7 8 0\n"); - printf(" 4 8 6 10\n"); - printf(" 5 6 5 0\n"); - printf(" # One hole in the middle of the inner square.\n"); - printf(" 1\n"); - printf(" 1 1.5 1.5\n"); - printf("\n"); - printf( -" Note that some segments are missing from the outer square, so you must\n"); - printf( -" use the `-c' switch. After `triangle -pqc box.poly', here is the output\n" -); - printf( -" file `box.1.node', with twelve vertices. The last four vertices were\n"); - printf( -" added to meet the angle constraint. Vertices 1, 2, and 9 have markers\n"); - printf( -" from segment 1. Vertices 6 and 8 have markers from segment 4. All the\n"); - printf( -" other vertices but 4 have been marked to indicate that they lie on a\n"); - printf(" boundary.\n\n"); - printf(" 12 2 0 1\n"); - printf(" 1 0 0 5\n"); - printf(" 2 0 3 5\n"); - printf(" 3 3 0 1\n"); - printf(" 4 3 3 33\n"); - printf(" 5 1 1 1\n"); - printf(" 6 1 2 10\n"); - printf(" 7 2 1 1\n"); - printf(" 8 2 2 10\n"); - printf(" 9 0 1.5 5\n"); - printf(" 10 1.5 0 1\n"); - printf(" 11 3 1.5 1\n"); - printf(" 12 1.5 3 1\n"); - printf(" # Generated by triangle -pqc box.poly\n"); - printf("\n"); - printf(" Here is the output file `box.1.ele', with twelve triangles.\n"); - printf("\n"); - printf(" 12 3 0\n"); - printf(" 1 5 6 9\n"); - printf(" 2 10 3 7\n"); - printf(" 3 6 8 12\n"); - printf(" 4 9 1 5\n"); - printf(" 5 6 2 9\n"); - printf(" 6 7 3 11\n"); - printf(" 7 11 4 8\n"); - printf(" 8 7 5 10\n"); - printf(" 9 12 2 6\n"); - printf(" 10 8 7 11\n"); - printf(" 11 5 1 10\n"); - printf(" 12 8 4 12\n"); - printf(" # Generated by triangle -pqc box.poly\n\n"); - printf( -" Here is the output file `box.1.poly'. Note that segments have been added\n" -); - printf( -" to represent the convex hull, and some segments have been subdivided by\n"); - printf( -" newly added vertices. Note also that <# of vertices> is set to zero to\n"); - printf(" indicate that the vertices should be read from the .node file.\n"); - printf("\n"); - printf(" 0 2 0 1\n"); - printf(" 12 1\n"); - printf(" 1 1 9 5\n"); - printf(" 2 5 7 1\n"); - printf(" 3 8 7 1\n"); - printf(" 4 6 8 10\n"); - printf(" 5 5 6 1\n"); - printf(" 6 3 10 1\n"); - printf(" 7 4 11 1\n"); - printf(" 8 2 12 1\n"); - printf(" 9 9 2 5\n"); - printf(" 10 10 1 1\n"); - printf(" 11 11 3 1\n"); - printf(" 12 12 4 1\n"); - printf(" 1\n"); - printf(" 1 1.5 1.5\n"); - printf(" # Generated by triangle -pqc box.poly\n"); - printf("\n"); - printf("Refinement and Area Constraints:\n"); - printf("\n"); - printf( -" The -r switch causes a mesh (.node and .ele files) to be read and\n"); - printf( -" refined. If the -p switch is also used, a .poly file is read and used to\n" -); - printf( -" specify edges that are constrained and cannot be eliminated (although\n"); - printf( -" they can be subdivided into smaller edges) by the refinement process.\n"); - printf("\n"); - printf( -" When you refine a mesh, you generally want to impose tighter constraints.\n" -); - printf( -" One way to accomplish this is to use -q with a larger angle, or -a\n"); - printf( -" followed by a smaller area than you used to generate the mesh you are\n"); - printf( -" refining. Another way to do this is to create an .area file, which\n"); - printf( -" specifies a maximum area for each triangle, and use the -a switch\n"); - printf( -" (without a number following). Each triangle's area constraint is applied\n" -); - printf( -" to that triangle. Area constraints tend to diffuse as the mesh is\n"); - printf( -" refined, so if there are large variations in area constraint between\n"); - printf( -" adjacent triangles, you may not get the results you want. In that case,\n" -); - printf( -" consider instead using the -u switch and writing a C procedure that\n"); - printf(" determines which triangles are too large.\n\n"); - printf( -" If you are refining a mesh composed of linear (three-node) elements, the\n" -); - printf( -" output mesh contains all the nodes present in the input mesh, in the same\n" -); - printf( -" order, with new nodes added at the end of the .node file. However, the\n"); - printf( -" refinement is not hierarchical: there is no guarantee that each output\n"); - printf( -" element is contained in a single input element. Often, an output element\n" -); - printf( -" can overlap two or three input elements, and some input edges are not\n"); - printf( -" present in the output mesh. Hence, a sequence of refined meshes forms a\n" -); - printf( -" hierarchy of nodes, but not a hierarchy of elements. If you refine a\n"); - printf( -" mesh of higher-order elements, the hierarchical property applies only to\n" -); - printf( -" the nodes at the corners of an element; the midpoint nodes on each edge\n"); - printf(" are discarded before the mesh is refined.\n\n"); - printf( -" Maximum area constraints in .poly files operate differently from those in\n" -); - printf( -" .area files. A maximum area in a .poly file applies to the whole\n"); - printf( -" (segment-bounded) region in which a point falls, whereas a maximum area\n"); - printf( -" in an .area file applies to only one triangle. Area constraints in .poly\n" -); - printf( -" files are used only when a mesh is first generated, whereas area\n"); - printf( -" constraints in .area files are used only to refine an existing mesh, and\n" -); - printf( -" are typically based on a posteriori error estimates resulting from a\n"); - printf(" finite element simulation on that mesh.\n\n"); - printf( -" `triangle -rq25 object.1' reads object.1.node and object.1.ele, then\n"); - printf( -" refines the triangulation to enforce a 25 degree minimum angle, and then\n" -); - printf( -" writes the refined triangulation to object.2.node and object.2.ele.\n"); - printf("\n"); - printf( -" `triangle -rpaa6.2 z.3' reads z.3.node, z.3.ele, z.3.poly, and z.3.area.\n" -); - printf( -" After reconstructing the mesh and its subsegments, Triangle refines the\n"); - printf( -" mesh so that no triangle has area greater than 6.2, and furthermore the\n"); - printf( -" triangles satisfy the maximum area constraints in z.3.area. No angle\n"); - printf( -" bound is imposed at all. The output is written to z.4.node, z.4.ele, and\n" -); - printf(" z.4.poly.\n\n"); - printf( -" The sequence `triangle -qa1 x', `triangle -rqa.3 x.1', `triangle -rqa.1\n"); - printf( -" x.2' creates a sequence of successively finer meshes x.1, x.2, and x.3,\n"); - printf(" suitable for multigrid.\n\n"); - printf("Convex Hulls and Mesh Boundaries:\n\n"); - printf( -" If the input is a vertex set (not a PSLG), Triangle produces its convex\n"); - printf( -" hull as a by-product in the output .poly file if you use the -c switch.\n"); - printf( -" There are faster algorithms for finding a two-dimensional convex hull\n"); - printf(" than triangulation, of course, but this one comes for free.\n\n"); - printf( -" If the input is an unconstrained mesh (you are using the -r switch but\n"); - printf( -" not the -p switch), Triangle produces a list of its boundary edges\n"); - printf( -" (including hole boundaries) as a by-product when you use the -c switch.\n"); - printf( -" If you also use the -p switch, the output .poly file contains all the\n"); - printf(" segments from the input .poly file as well.\n\n"); - printf("Voronoi Diagrams:\n\n"); - printf( -" The -v switch produces a Voronoi diagram, in files suffixed .v.node and\n"); - printf( -" .v.edge. For example, `triangle -v points' reads points.node, produces\n"); - printf( -" its Delaunay triangulation in points.1.node and points.1.ele, and\n"); - printf( -" produces its Voronoi diagram in points.1.v.node and points.1.v.edge. The\n" -); - printf( -" .v.node file contains a list of all Voronoi vertices, and the .v.edge\n"); - printf( -" file contains a list of all Voronoi edges, some of which may be infinite\n" -); - printf( -" rays. (The choice of filenames makes it easy to run the set of Voronoi\n"); - printf(" vertices through Triangle, if so desired.)\n\n"); - printf( -" This implementation does not use exact arithmetic to compute the Voronoi\n" -); - printf( -" vertices, and does not check whether neighboring vertices are identical.\n" -); - printf( -" Be forewarned that if the Delaunay triangulation is degenerate or\n"); - printf( -" near-degenerate, the Voronoi diagram may have duplicate vertices or\n"); - printf(" crossing edges.\n\n"); - printf( -" The result is a valid Voronoi diagram only if Triangle's output is a true\n" -); - printf( -" Delaunay triangulation. The Voronoi output is usually meaningless (and\n"); - printf( -" may contain crossing edges and other pathology) if the output is a CDT or\n" -); - printf( -" CCDT, or if it has holes or concavities. If the triangulated domain is\n"); - printf( -" convex and has no holes, you can use -D switch to force Triangle to\n"); - printf( -" construct a conforming Delaunay triangulation instead of a CCDT, so the\n"); - printf(" Voronoi diagram will be valid.\n\n"); - printf("Mesh Topology:\n\n"); - printf( -" You may wish to know which triangles are adjacent to a certain Delaunay\n"); - printf( -" edge in an .edge file, which Voronoi cells are adjacent to a certain\n"); - printf( -" Voronoi edge in a .v.edge file, or which Voronoi cells are adjacent to\n"); - printf( -" each other. All of this information can be found by cross-referencing\n"); - printf( -" output files with the recollection that the Delaunay triangulation and\n"); - printf(" the Voronoi diagram are planar duals.\n\n"); - printf( -" Specifically, edge i of an .edge file is the dual of Voronoi edge i of\n"); - printf( -" the corresponding .v.edge file, and is rotated 90 degrees counterclock-\n"); - printf( -" wise from the Voronoi edge. Triangle j of an .ele file is the dual of\n"); - printf( -" vertex j of the corresponding .v.node file. Voronoi cell k is the dual\n"); - printf(" of vertex k of the corresponding .node file.\n\n"); - printf( -" Hence, to find the triangles adjacent to a Delaunay edge, look at the\n"); - printf( -" vertices of the corresponding Voronoi edge. If the endpoints of a\n"); - printf( -" Voronoi edge are Voronoi vertices 2 and 6 respectively, then triangles 2\n" -); - printf( -" and 6 adjoin the left and right sides of the corresponding Delaunay edge,\n" -); - printf( -" respectively. To find the Voronoi cells adjacent to a Voronoi edge, look\n" -); - printf( -" at the endpoints of the corresponding Delaunay edge. If the endpoints of\n" -); - printf( -" a Delaunay edge are input vertices 7 and 12, then Voronoi cells 7 and 12\n" -); - printf( -" adjoin the right and left sides of the corresponding Voronoi edge,\n"); - printf( -" respectively. To find which Voronoi cells are adjacent to each other,\n"); - printf(" just read the list of Delaunay edges.\n\n"); - printf( -" Triangle does not write a list of the edges adjoining each Voronoi cell,\n" -); - printf( -" but you can reconstructed it straightforwardly. For instance, to find\n"); - printf( -" all the edges of Voronoi cell 1, search the output .edge file for every\n"); - printf( -" edge that has input vertex 1 as an endpoint. The corresponding dual\n"); - printf( -" edges in the output .v.edge file form the boundary of Voronoi cell 1.\n"); - printf("\n"); - printf( -" For each Voronoi vertex, the .neigh file gives a list of the three\n"); - printf( -" Voronoi vertices attached to it. You might find this more convenient\n"); - printf(" than the .v.edge file.\n\n"); - printf("Quadratic Elements:\n\n"); - printf( -" Triangle generates meshes with subparametric quadratic elements if the\n"); - printf( -" -o2 switch is specified. Quadratic elements have six nodes per element,\n" -); - printf( -" rather than three. `Subparametric' means that the edges of the triangles\n" -); - printf( -" are always straight, so that subparametric quadratic elements are\n"); - printf( -" geometrically identical to linear elements, even though they can be used\n" -); - printf( -" with quadratic interpolating functions. The three extra nodes of an\n"); - printf( -" element fall at the midpoints of the three edges, with the fourth, fifth,\n" -); - printf( -" and sixth nodes appearing opposite the first, second, and third corners\n"); - printf(" respectively.\n\n"); - printf("Domains with Small Angles:\n\n"); - printf( -" If two input segments adjoin each other at a small angle, clearly the -q\n" -); - printf( -" switch cannot remove the small angle. Moreover, Triangle may have no\n"); - printf( -" choice but to generate additional triangles whose smallest angles are\n"); - printf( -" smaller than the specified bound. However, these triangles only appear\n"); - printf( -" between input segments separated by small angles. Moreover, if you\n"); - printf( -" request a minimum angle of theta degrees, Triangle will generally produce\n" -); - printf( -" no angle larger than 180 - 2 theta, even if it is forced to compromise on\n" -); - printf(" the minimum angle.\n\n"); - printf("Statistics:\n\n"); - printf( -" After generating a mesh, Triangle prints a count of entities in the\n"); - printf( -" output mesh, including the number of vertices, triangles, edges, exterior\n" -); - printf( -" boundary edges (i.e. subsegments on the boundary of the triangulation,\n"); - printf( -" including hole boundaries), interior boundary edges (i.e. subsegments of\n" -); - printf( -" input segments not on the boundary), and total subsegments. If you've\n"); - printf( -" forgotten the statistics for an existing mesh, run Triangle on that mesh\n" -); - printf( -" with the -rNEP switches to read the mesh and print the statistics without\n" -); - printf( -" writing any files. Use -rpNEP if you've got a .poly file for the mesh.\n"); - printf("\n"); - printf( -" The -V switch produces extended statistics, including a rough estimate\n"); - printf( -" of memory use, the number of calls to geometric predicates, and\n"); - printf( -" histograms of the angles and the aspect ratios of the triangles in the\n"); - printf(" mesh.\n\n"); - printf("Exact Arithmetic:\n\n"); - printf( -" Triangle uses adaptive exact arithmetic to perform what computational\n"); - printf( -" geometers call the `orientation' and `incircle' tests. If the floating-\n" -); - printf( -" point arithmetic of your machine conforms to the IEEE 754 standard (as\n"); - printf( -" most workstations do), and does not use extended precision internal\n"); - printf( -" floating-point registers, then your output is guaranteed to be an\n"); - printf( -" absolutely true Delaunay or constrained Delaunay triangulation, roundoff\n" -); - printf( -" error notwithstanding. The word `adaptive' implies that these arithmetic\n" -); - printf( -" routines compute the result only to the precision necessary to guarantee\n" -); - printf( -" correctness, so they are usually nearly as fast as their approximate\n"); - printf(" counterparts.\n\n"); - printf( -" May CPUs, including Intel x86 processors, have extended precision\n"); - printf( -" floating-point registers. These must be reconfigured so their precision\n" -); - printf( -" is reduced to memory precision. Triangle does this if it is compiled\n"); - printf(" correctly. See the makefile for details.\n\n"); - printf( -" The exact tests can be disabled with the -X switch. On most inputs, this\n" -); - printf( -" switch reduces the computation time by about eight percent--it's not\n"); - printf( -" worth the risk. There are rare difficult inputs (having many collinear\n"); - printf( -" and cocircular vertices), however, for which the difference in speed\n"); - printf( -" could be a factor of two. Be forewarned that these are precisely the\n"); - printf( -" inputs most likely to cause errors if you use the -X switch. Hence, the\n" -); - printf(" -X switch is not recommended.\n\n"); - printf( -" Unfortunately, the exact tests don't solve every numerical problem.\n"); - printf( -" Exact arithmetic is not used to compute the positions of new vertices,\n"); - printf( -" because the bit complexity of vertex coordinates would grow without\n"); - printf( -" bound. Hence, segment intersections aren't computed exactly; in very\n"); - printf( -" unusual cases, roundoff error in computing an intersection point might\n"); - printf( -" actually lead to an inverted triangle and an invalid triangulation.\n"); - printf( -" (This is one reason to specify your own intersection points in your .poly\n" -); - printf( -" files.) Similarly, exact arithmetic is not used to compute the vertices\n" -); - printf(" of the Voronoi diagram.\n\n"); - printf( -" Another pair of problems not solved by the exact arithmetic routines is\n"); - printf( -" underflow and overflow. If Triangle is compiled for double precision\n"); - printf( -" arithmetic, I believe that Triangle's geometric predicates work correctly\n" -); - printf( -" if the exponent of every input coordinate falls in the range [-148, 201].\n" -); - printf( -" Underflow can silently prevent the orientation and incircle tests from\n"); - printf( -" being performed exactly, while overflow typically causes a floating\n"); - printf(" exception.\n\n"); - printf("Calling Triangle from Another Program:\n\n"); - printf(" Read the file triangle.h for details.\n\n"); - printf("Troubleshooting:\n\n"); - printf(" Please read this section before mailing me bugs.\n\n"); - printf(" `My output mesh has no triangles!'\n\n"); - printf( -" If you're using a PSLG, you've probably failed to specify a proper set\n" -); - printf( -" of bounding segments, or forgotten to use the -c switch. Or you may\n"); - printf( -" have placed a hole badly, thereby eating all your triangles. To test\n"); - printf(" these possibilities, try again with the -c and -O switches.\n"); - printf( -" Alternatively, all your input vertices may be collinear, in which case\n" -); - printf(" you can hardly expect to triangulate them.\n\n"); - printf(" `Triangle doesn't terminate, or just crashes.'\n\n"); - printf( -" Bad things can happen when triangles get so small that the distance\n"); - printf( -" between their vertices isn't much larger than the precision of your\n"); - printf( -" machine's arithmetic. If you've compiled Triangle for single-precision\n" -); - printf( -" arithmetic, you might do better by recompiling it for double-precision.\n" -); - printf( -" Then again, you might just have to settle for more lenient constraints\n" -); - printf( -" on the minimum angle and the maximum area than you had planned.\n"); - printf("\n"); - printf( -" You can minimize precision problems by ensuring that the origin lies\n"); - printf( -" inside your vertex set, or even inside the densest part of your\n"); - printf( -" mesh. If you're triangulating an object whose x-coordinates all fall\n"); - printf( -" between 6247133 and 6247134, you're not leaving much floating-point\n"); - printf(" precision for Triangle to work with.\n\n"); - printf( -" Precision problems can occur covertly if the input PSLG contains two\n"); - printf( -" segments that meet (or intersect) at an extremely small angle, or if\n"); - printf( -" such an angle is introduced by the -c switch. If you don't realize\n"); - printf( -" that a tiny angle is being formed, you might never discover why\n"); - printf( -" Triangle is crashing. To check for this possibility, use the -S switch\n" -); - printf( -" (with an appropriate limit on the number of Steiner points, found by\n"); - printf( -" trial-and-error) to stop Triangle early, and view the output .poly file\n" -); - printf( -" with Show Me (described below). Look carefully for regions where dense\n" -); - printf( -" clusters of vertices are forming and for small angles between segments.\n" -); - printf( -" Zoom in closely, as such segments might look like a single segment from\n" -); - printf(" a distance.\n\n"); - printf( -" If some of the input values are too large, Triangle may suffer a\n"); - printf( -" floating exception due to overflow when attempting to perform an\n"); - printf( -" orientation or incircle test. (Read the section on exact arithmetic\n"); - printf( -" above.) Again, I recommend compiling Triangle for double (rather\n"); - printf(" than single) precision arithmetic.\n\n"); - printf( -" Unexpected problems can arise if you use quality meshing (-q, -a, or\n"); - printf( -" -u) with an input that is not segment-bounded--that is, if your input\n"); - printf( -" is a vertex set, or you're using the -c switch. If the convex hull of\n" -); - printf( -" your input vertices has collinear vertices on its boundary, an input\n"); - printf( -" vertex that you think lies on the convex hull might actually lie just\n"); - printf( -" inside the convex hull. If so, the vertex and the nearby convex hull\n"); - printf( -" edge form an extremely thin triangle. When Triangle tries to refine\n"); - printf( -" the mesh to enforce angle and area constraints, Triangle might generate\n" -); - printf( -" extremely tiny triangles, or it might fail because of insufficient\n"); - printf(" floating-point precision.\n\n"); - printf( -" `The numbering of the output vertices doesn't match the input vertices.'\n" -); - printf("\n"); - printf( -" You may have had duplicate input vertices, or you may have eaten some\n"); - printf( -" of your input vertices with a hole, or by placing them outside the area\n" -); - printf( -" enclosed by segments. In any case, you can solve the problem by not\n"); - printf(" using the -j switch.\n\n"); - printf( -" `Triangle executes without incident, but when I look at the resulting\n"); - printf( -" mesh, it has overlapping triangles or other geometric inconsistencies.'\n"); - printf("\n"); - printf( -" If you select the -X switch, Triangle occasionally makes mistakes due\n"); - printf( -" to floating-point roundoff error. Although these errors are rare,\n"); - printf( -" don't use the -X switch. If you still have problems, please report the\n" -); - printf(" bug.\n\n"); - printf( -" `Triangle executes without incident, but when I look at the resulting\n"); - printf(" Voronoi diagram, it has overlapping edges or other geometric\n"); - printf(" inconsistencies.'\n"); - printf("\n"); - printf( -" If your input is a PSLG (-p), you can only expect a meaningful Voronoi\n" -); - printf( -" diagram if the domain you are triangulating is convex and free of\n"); - printf( -" holes, and you use the -D switch to construct a conforming Delaunay\n"); - printf(" triangulation (instead of a CDT or CCDT).\n\n"); - printf( -" Strange things can happen if you've taken liberties with your PSLG. Do\n"); - printf( -" you have a vertex lying in the middle of a segment? Triangle sometimes\n"); - printf( -" copes poorly with that sort of thing. Do you want to lay out a collinear\n" -); - printf( -" row of evenly spaced, segment-connected vertices? Have you simply\n"); - printf( -" defined one long segment connecting the leftmost vertex to the rightmost\n" -); - printf( -" vertex, and a bunch of vertices lying along it? This method occasionally\n" -); - printf( -" works, especially with horizontal and vertical lines, but often it\n"); - printf( -" doesn't, and you'll have to connect each adjacent pair of vertices with a\n" -); - printf(" separate segment. If you don't like it, tough.\n\n"); - printf( -" Furthermore, if you have segments that intersect other than at their\n"); - printf( -" endpoints, try not to let the intersections fall extremely close to PSLG\n" -); - printf(" vertices or each other.\n\n"); - printf( -" If you have problems refining a triangulation not produced by Triangle:\n"); - printf( -" Are you sure the triangulation is geometrically valid? Is it formatted\n"); - printf( -" correctly for Triangle? Are the triangles all listed so the first three\n" -); - printf( -" vertices are their corners in counterclockwise order? Are all of the\n"); - printf( -" triangles constrained Delaunay? Triangle's Delaunay refinement algorithm\n" -); - printf(" assumes that it starts with a CDT.\n\n"); - printf("Show Me:\n\n"); - printf( -" Triangle comes with a separate program named `Show Me', whose primary\n"); - printf( -" purpose is to draw meshes on your screen or in PostScript. Its secondary\n" -); - printf( -" purpose is to check the validity of your input files, and do so more\n"); - printf( -" thoroughly than Triangle does. Unlike Triangle, Show Me requires that\n"); - printf( -" you have the X Windows system. Sorry, Microsoft Windows users.\n"); - printf("\n"); - printf("Triangle on the Web:\n"); - printf("\n"); - printf(" To see an illustrated version of these instructions, check out\n"); - printf("\n"); - printf(" http://www.cs.cmu.edu/~quake/triangle.html\n"); - printf("\n"); - printf("A Brief Plea:\n"); - printf("\n"); - printf( -" If you use Triangle, and especially if you use it to accomplish real\n"); - printf( -" work, I would like very much to hear from you. A short letter or email\n"); - printf( -" (to jrs@cs.berkeley.edu) describing how you use Triangle will mean a lot\n" -); - printf( -" to me. The more people I know are using this program, the more easily I\n" -); - printf( -" can justify spending time on improvements, which in turn will benefit\n"); - printf( -" you. Also, I can put you on a list to receive email whenever a new\n"); - printf(" version of Triangle is available.\n\n"); - printf( -" If you use a mesh generated by Triangle in a publication, please include\n" -); - printf( -" an acknowledgment as well. And please spell Triangle with a capital `T'!\n" -); - printf( -" If you want to include a citation, use `Jonathan Richard Shewchuk,\n"); - printf( -" ``Triangle: Engineering a 2D Quality Mesh Generator and Delaunay\n"); - printf( -" Triangulator,'' in Applied Computational Geometry: Towards Geometric\n"); - printf( -" Engineering (Ming C. Lin and Dinesh Manocha, editors), volume 1148 of\n"); - printf( -" Lecture Notes in Computer Science, pages 203-222, Springer-Verlag,\n"); - printf( -" Berlin, May 1996. (From the First ACM Workshop on Applied Computational\n" -); - printf(" Geometry.)'\n\n"); - printf("Research credit:\n\n"); - printf( -" Of course, I can take credit for only a fraction of the ideas that made\n"); - printf( -" this mesh generator possible. Triangle owes its existence to the efforts\n" -); - printf( -" of many fine computational geometers and other researchers, including\n"); - printf( -" Marshall Bern, L. Paul Chew, Kenneth L. Clarkson, Boris Delaunay, Rex A.\n" -); - printf( -" Dwyer, David Eppstein, Steven Fortune, Leonidas J. Guibas, Donald E.\n"); - printf( -" Knuth, Charles L. Lawson, Der-Tsai Lee, Gary L. Miller, Ernst P. Mucke,\n"); - printf( -" Steven E. Pav, Douglas M. Priest, Jim Ruppert, Isaac Saias, Bruce J.\n"); - printf( -" Schachter, Micha Sharir, Peter W. Shor, Daniel D. Sleator, Jorge Stolfi,\n" -); - printf(" Robert E. Tarjan, Alper Ungor, Christopher J. Van Wyk, Noel J.\n"); - printf( -" Walkington, and Binhai Zhu. See the comments at the beginning of the\n"); - printf(" source code for references.\n\n"); - triexit(0); -} - -#endif /* not TRILIBRARY */ - -/*****************************************************************************/ -/* */ -/* internalerror() Ask the user to send me the defective product. Exit. */ -/* */ -/*****************************************************************************/ - -void internalerror() -{ - printf(" Please report this bug to jrs@cs.berkeley.edu\n"); - printf(" Include the message above, your input data set, and the exact\n"); - printf(" command line you used to run Triangle.\n"); - triexit(1); -} - -/*****************************************************************************/ -/* */ -/* parsecommandline() Read the command line, identify switches, and set */ -/* up options and file names. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void parsecommandline(int argc, char **argv, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void parsecommandline(argc, argv, b) -int argc; -char **argv; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ -#ifdef TRILIBRARY -#define STARTINDEX 0 -#else /* not TRILIBRARY */ -#define STARTINDEX 1 - int increment; - int meshnumber; -#endif /* not TRILIBRARY */ - int i, j, k; - char workstring[FILENAMESIZE]; - - b->poly = b->refine = b->quality = 0; - b->vararea = b->fixedarea = b->usertest = 0; - b->regionattrib = b->convex = b->weighted = b->jettison = 0; - b->firstnumber = 1; - b->edgesout = b->voronoi = b->neighbors = b->geomview = 0; - b->nobound = b->nopolywritten = b->nonodewritten = b->noelewritten = 0; - b->noiterationnum = 0; - b->noholes = b->noexact = 0; - b->incremental = b->sweepline = 0; - b->dwyer = 1; - b->splitseg = 0; - b->docheck = 0; - b->nobisect = 0; - b->conformdel = 0; - b->steiner = -1; - b->order = 1; - b->minangle = 0.0; - b->maxarea = -1.0; - b->quiet = b->verbose = 0; -#ifndef TRILIBRARY - b->innodefilename[0] = '\0'; -#endif /* not TRILIBRARY */ - - for (i = STARTINDEX; i < argc; i++) { -#ifndef TRILIBRARY - if (argv[i][0] == '-') { -#endif /* not TRILIBRARY */ - for (j = STARTINDEX; argv[i][j] != '\0'; j++) { - if (argv[i][j] == 'p') { - b->poly = 1; - } -#ifndef CDT_ONLY - if (argv[i][j] == 'r') { - b->refine = 1; - } - if (argv[i][j] == 'q') { - b->quality = 1; - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - b->minangle = (REAL) strtod(workstring, (char **) NULL); - } else { - b->minangle = 20.0; - } - } - if (argv[i][j] == 'a') { - b->quality = 1; - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - b->fixedarea = 1; - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - b->maxarea = (REAL) strtod(workstring, (char **) NULL); - if (b->maxarea <= 0.0) { - printf("Error: Maximum area must be greater than zero.\n"); - triexit(1); - } - } else { - b->vararea = 1; - } - } - if (argv[i][j] == 'u') { - b->quality = 1; - b->usertest = 1; - } -#endif /* not CDT_ONLY */ - if (argv[i][j] == 'A') { - b->regionattrib = 1; - } - if (argv[i][j] == 'c') { - b->convex = 1; - } - if (argv[i][j] == 'w') { - b->weighted = 1; - } - if (argv[i][j] == 'W') { - b->weighted = 2; - } - if (argv[i][j] == 'j') { - b->jettison = 1; - } - if (argv[i][j] == 'z') { - b->firstnumber = 0; - } - if (argv[i][j] == 'e') { - b->edgesout = 1; - } - if (argv[i][j] == 'v') { - b->voronoi = 1; - } - if (argv[i][j] == 'n') { - b->neighbors = 1; - } - if (argv[i][j] == 'g') { - b->geomview = 1; - } - if (argv[i][j] == 'B') { - b->nobound = 1; - } - if (argv[i][j] == 'P') { - b->nopolywritten = 1; - } - if (argv[i][j] == 'N') { - b->nonodewritten = 1; - } - if (argv[i][j] == 'E') { - b->noelewritten = 1; - } -#ifndef TRILIBRARY - if (argv[i][j] == 'I') { - b->noiterationnum = 1; - } -#endif /* not TRILIBRARY */ - if (argv[i][j] == 'O') { - b->noholes = 1; - } - if (argv[i][j] == 'X') { - b->noexact = 1; - } - if (argv[i][j] == 'o') { - if (argv[i][j + 1] == '2') { - j++; - b->order = 2; - } - } -#ifndef CDT_ONLY - if (argv[i][j] == 'Y') { - b->nobisect++; - } - if (argv[i][j] == 'S') { - b->steiner = 0; - while ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { - j++; - b->steiner = b->steiner * 10 + (int) (argv[i][j] - '0'); - } - } -#endif /* not CDT_ONLY */ -#ifndef REDUCED - if (argv[i][j] == 'i') { - b->incremental = 1; - } - if (argv[i][j] == 'F') { - b->sweepline = 1; - } -#endif /* not REDUCED */ - if (argv[i][j] == 'l') { - b->dwyer = 0; - } -#ifndef REDUCED -#ifndef CDT_ONLY - if (argv[i][j] == 's') { - b->splitseg = 1; - } - if ((argv[i][j] == 'D') || (argv[i][j] == 'L')) { - b->quality = 1; - b->conformdel = 1; - } -#endif /* not CDT_ONLY */ - if (argv[i][j] == 'C') { - b->docheck = 1; - } -#endif /* not REDUCED */ - if (argv[i][j] == 'Q') { - b->quiet = 1; - } - if (argv[i][j] == 'V') { - b->verbose++; - } -#ifndef TRILIBRARY - if ((argv[i][j] == 'h') || (argv[i][j] == 'H') || - (argv[i][j] == '?')) { - info(); - } -#endif /* not TRILIBRARY */ - } -#ifndef TRILIBRARY - } else { - strncpy(b->innodefilename, argv[i], FILENAMESIZE - 1); - b->innodefilename[FILENAMESIZE - 1] = '\0'; - } -#endif /* not TRILIBRARY */ - } -#ifndef TRILIBRARY - if (b->innodefilename[0] == '\0') { - syntax(); - } - if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".node")) { - b->innodefilename[strlen(b->innodefilename) - 5] = '\0'; - } - if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".poly")) { - b->innodefilename[strlen(b->innodefilename) - 5] = '\0'; - b->poly = 1; - } -#ifndef CDT_ONLY - if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 4], ".ele")) { - b->innodefilename[strlen(b->innodefilename) - 4] = '\0'; - b->refine = 1; - } - if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".area")) { - b->innodefilename[strlen(b->innodefilename) - 5] = '\0'; - b->refine = 1; - b->quality = 1; - b->vararea = 1; - } -#endif /* not CDT_ONLY */ -#endif /* not TRILIBRARY */ - b->usesegments = b->poly || b->refine || b->quality || b->convex; - b->goodangle = cos(b->minangle * PI / 180.0); - if (b->goodangle == 1.0) { - b->offconstant = 0.0; - } else { - b->offconstant = 0.475 * sqrt((1.0 + b->goodangle) / (1.0 - b->goodangle)); - } - b->goodangle *= b->goodangle; - if (b->refine && b->noiterationnum) { - printf( - "Error: You cannot use the -I switch when refining a triangulation.\n"); - triexit(1); - } - /* Be careful not to allocate space for element area constraints that */ - /* will never be assigned any value (other than the default -1.0). */ - if (!b->refine && !b->poly) { - b->vararea = 0; - } - /* Be careful not to add an extra attribute to each element unless the */ - /* input supports it (PSLG in, but not refining a preexisting mesh). */ - if (b->refine || !b->poly) { - b->regionattrib = 0; - } - /* Regular/weighted triangulations are incompatible with PSLGs */ - /* and meshing. */ - if (b->weighted && (b->poly || b->quality)) { - b->weighted = 0; - if (!b->quiet) { - printf("Warning: weighted triangulations (-w, -W) are incompatible\n"); - printf(" with PSLGs (-p) and meshing (-q, -a, -u). Weights ignored.\n" - ); - } - } - if (b->jettison && b->nonodewritten && !b->quiet) { - printf("Warning: -j and -N switches are somewhat incompatible.\n"); - printf(" If any vertices are jettisoned, you will need the output\n"); - printf(" .node file to reconstruct the new node indices."); - } - -#ifndef TRILIBRARY - strcpy(b->inpolyfilename, b->innodefilename); - strcpy(b->inelefilename, b->innodefilename); - strcpy(b->areafilename, b->innodefilename); - increment = 0; - strcpy(workstring, b->innodefilename); - j = 1; - while (workstring[j] != '\0') { - if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) { - increment = j + 1; - } - j++; - } - meshnumber = 0; - if (increment > 0) { - j = increment; - do { - if ((workstring[j] >= '0') && (workstring[j] <= '9')) { - meshnumber = meshnumber * 10 + (int) (workstring[j] - '0'); - } else { - increment = 0; - } - j++; - } while (workstring[j] != '\0'); - } - if (b->noiterationnum) { - strcpy(b->outnodefilename, b->innodefilename); - strcpy(b->outelefilename, b->innodefilename); - strcpy(b->edgefilename, b->innodefilename); - strcpy(b->vnodefilename, b->innodefilename); - strcpy(b->vedgefilename, b->innodefilename); - strcpy(b->neighborfilename, b->innodefilename); - strcpy(b->offfilename, b->innodefilename); - strcat(b->outnodefilename, ".node"); - strcat(b->outelefilename, ".ele"); - strcat(b->edgefilename, ".edge"); - strcat(b->vnodefilename, ".v.node"); - strcat(b->vedgefilename, ".v.edge"); - strcat(b->neighborfilename, ".neigh"); - strcat(b->offfilename, ".off"); - } else if (increment == 0) { - strcpy(b->outnodefilename, b->innodefilename); - strcpy(b->outpolyfilename, b->innodefilename); - strcpy(b->outelefilename, b->innodefilename); - strcpy(b->edgefilename, b->innodefilename); - strcpy(b->vnodefilename, b->innodefilename); - strcpy(b->vedgefilename, b->innodefilename); - strcpy(b->neighborfilename, b->innodefilename); - strcpy(b->offfilename, b->innodefilename); - strcat(b->outnodefilename, ".1.node"); - strcat(b->outpolyfilename, ".1.poly"); - strcat(b->outelefilename, ".1.ele"); - strcat(b->edgefilename, ".1.edge"); - strcat(b->vnodefilename, ".1.v.node"); - strcat(b->vedgefilename, ".1.v.edge"); - strcat(b->neighborfilename, ".1.neigh"); - strcat(b->offfilename, ".1.off"); - } else { - workstring[increment] = '%'; - workstring[increment + 1] = 'd'; - workstring[increment + 2] = '\0'; - sprintf(b->outnodefilename, workstring, meshnumber + 1); - strcpy(b->outpolyfilename, b->outnodefilename); - strcpy(b->outelefilename, b->outnodefilename); - strcpy(b->edgefilename, b->outnodefilename); - strcpy(b->vnodefilename, b->outnodefilename); - strcpy(b->vedgefilename, b->outnodefilename); - strcpy(b->neighborfilename, b->outnodefilename); - strcpy(b->offfilename, b->outnodefilename); - strcat(b->outnodefilename, ".node"); - strcat(b->outpolyfilename, ".poly"); - strcat(b->outelefilename, ".ele"); - strcat(b->edgefilename, ".edge"); - strcat(b->vnodefilename, ".v.node"); - strcat(b->vedgefilename, ".v.edge"); - strcat(b->neighborfilename, ".neigh"); - strcat(b->offfilename, ".off"); - } - strcat(b->innodefilename, ".node"); - strcat(b->inpolyfilename, ".poly"); - strcat(b->inelefilename, ".ele"); - strcat(b->areafilename, ".area"); -#endif /* not TRILIBRARY */ -} - -/** **/ -/** **/ -/********* User interaction routines begin here *********/ - -/********* Debugging routines begin here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* printtriangle() Print out the details of an oriented triangle. */ -/* */ -/* I originally wrote this procedure to simplify debugging; it can be */ -/* called directly from the debugger, and presents information about an */ -/* oriented triangle in digestible form. It's also used when the */ -/* highest level of verbosity (`-VVV') is specified. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void printtriangle(struct mesh *m, struct behavior *b, struct otri *t) -#else /* not ANSI_DECLARATORS */ -void printtriangle(m, b, t) -struct mesh *m; -struct behavior *b; -struct otri *t; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri printtri; - struct osub printsh; - vertex printvertex; - - printf("triangle x%lx with orientation %d:\n", (unsigned long) t->tri, - t->orient); - decode(t->tri[0], printtri); - if (printtri.tri == m->dummytri) { - printf(" [0] = Outer space\n"); - } else { - printf(" [0] = x%lx %d\n", (unsigned long) printtri.tri, - printtri.orient); - } - decode(t->tri[1], printtri); - if (printtri.tri == m->dummytri) { - printf(" [1] = Outer space\n"); - } else { - printf(" [1] = x%lx %d\n", (unsigned long) printtri.tri, - printtri.orient); - } - decode(t->tri[2], printtri); - if (printtri.tri == m->dummytri) { - printf(" [2] = Outer space\n"); - } else { - printf(" [2] = x%lx %d\n", (unsigned long) printtri.tri, - printtri.orient); - } - - org(*t, printvertex); - if (printvertex == (vertex) NULL) - printf(" Origin[%d] = NULL\n", (t->orient + 1) % 3 + 3); - else - printf(" Origin[%d] = x%lx (%.12g, %.12g)\n", - (t->orient + 1) % 3 + 3, (unsigned long) printvertex, - printvertex[0], printvertex[1]); - dest(*t, printvertex); - if (printvertex == (vertex) NULL) - printf(" Dest [%d] = NULL\n", (t->orient + 2) % 3 + 3); - else - printf(" Dest [%d] = x%lx (%.12g, %.12g)\n", - (t->orient + 2) % 3 + 3, (unsigned long) printvertex, - printvertex[0], printvertex[1]); - apex(*t, printvertex); - if (printvertex == (vertex) NULL) - printf(" Apex [%d] = NULL\n", t->orient + 3); - else - printf(" Apex [%d] = x%lx (%.12g, %.12g)\n", - t->orient + 3, (unsigned long) printvertex, - printvertex[0], printvertex[1]); - - if (b->usesegments) { - sdecode(t->tri[6], printsh); - if (printsh.ss != m->dummysub) { - printf(" [6] = x%lx %d\n", (unsigned long) printsh.ss, - printsh.ssorient); - } - sdecode(t->tri[7], printsh); - if (printsh.ss != m->dummysub) { - printf(" [7] = x%lx %d\n", (unsigned long) printsh.ss, - printsh.ssorient); - } - sdecode(t->tri[8], printsh); - if (printsh.ss != m->dummysub) { - printf(" [8] = x%lx %d\n", (unsigned long) printsh.ss, - printsh.ssorient); - } - } - - if (b->vararea) { - printf(" Area constraint: %.4g\n", areabound(*t)); - } -} - -/*****************************************************************************/ -/* */ -/* printsubseg() Print out the details of an oriented subsegment. */ -/* */ -/* I originally wrote this procedure to simplify debugging; it can be */ -/* called directly from the debugger, and presents information about an */ -/* oriented subsegment in digestible form. It's also used when the highest */ -/* level of verbosity (`-VVV') is specified. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void printsubseg(struct mesh *m, struct behavior *b, struct osub *s) -#else /* not ANSI_DECLARATORS */ -void printsubseg(m, b, s) -struct mesh *m; -struct behavior *b; -struct osub *s; -#endif /* not ANSI_DECLARATORS */ - -{ - struct osub printsh; - struct otri printtri; - vertex printvertex; - - printf("subsegment x%lx with orientation %d and mark %d:\n", - (unsigned long) s->ss, s->ssorient, mark(*s)); - sdecode(s->ss[0], printsh); - if (printsh.ss == m->dummysub) { - printf(" [0] = No subsegment\n"); - } else { - printf(" [0] = x%lx %d\n", (unsigned long) printsh.ss, - printsh.ssorient); - } - sdecode(s->ss[1], printsh); - if (printsh.ss == m->dummysub) { - printf(" [1] = No subsegment\n"); - } else { - printf(" [1] = x%lx %d\n", (unsigned long) printsh.ss, - printsh.ssorient); - } - - sorg(*s, printvertex); - if (printvertex == (vertex) NULL) - printf(" Origin[%d] = NULL\n", 2 + s->ssorient); - else - printf(" Origin[%d] = x%lx (%.12g, %.12g)\n", - 2 + s->ssorient, (unsigned long) printvertex, - printvertex[0], printvertex[1]); - sdest(*s, printvertex); - if (printvertex == (vertex) NULL) - printf(" Dest [%d] = NULL\n", 3 - s->ssorient); - else - printf(" Dest [%d] = x%lx (%.12g, %.12g)\n", - 3 - s->ssorient, (unsigned long) printvertex, - printvertex[0], printvertex[1]); - - decode(s->ss[6], printtri); - if (printtri.tri == m->dummytri) { - printf(" [6] = Outer space\n"); - } else { - printf(" [6] = x%lx %d\n", (unsigned long) printtri.tri, - printtri.orient); - } - decode(s->ss[7], printtri); - if (printtri.tri == m->dummytri) { - printf(" [7] = Outer space\n"); - } else { - printf(" [7] = x%lx %d\n", (unsigned long) printtri.tri, - printtri.orient); - } - - segorg(*s, printvertex); - if (printvertex == (vertex) NULL) - printf(" Segment origin[%d] = NULL\n", 4 + s->ssorient); - else - printf(" Segment origin[%d] = x%lx (%.12g, %.12g)\n", - 4 + s->ssorient, (unsigned long) printvertex, - printvertex[0], printvertex[1]); - segdest(*s, printvertex); - if (printvertex == (vertex) NULL) - printf(" Segment dest [%d] = NULL\n", 5 - s->ssorient); - else - printf(" Segment dest [%d] = x%lx (%.12g, %.12g)\n", - 5 - s->ssorient, (unsigned long) printvertex, - printvertex[0], printvertex[1]); -} - -/** **/ -/** **/ -/********* Debugging routines end here *********/ - -/********* Memory management routines begin here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* poolzero() Set all of a pool's fields to zero. */ -/* */ -/* This procedure should never be called on a pool that has any memory */ -/* allocated to it, as that memory would leak. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void poolzero(struct memorypool *pool) -#else /* not ANSI_DECLARATORS */ -void poolzero(pool) -struct memorypool *pool; -#endif /* not ANSI_DECLARATORS */ - -{ - pool->firstblock = (VOID **) NULL; - pool->nowblock = (VOID **) NULL; - pool->nextitem = (VOID *) NULL; - pool->deaditemstack = (VOID *) NULL; - pool->pathblock = (VOID **) NULL; - pool->pathitem = (VOID *) NULL; - pool->alignbytes = 0; - pool->itembytes = 0; - pool->itemsperblock = 0; - pool->itemsfirstblock = 0; - pool->items = 0; - pool->maxitems = 0; - pool->unallocateditems = 0; - pool->pathitemsleft = 0; -} - -/*****************************************************************************/ -/* */ -/* poolrestart() Deallocate all items in a pool. */ -/* */ -/* The pool is returned to its starting state, except that no memory is */ -/* freed to the operating system. Rather, the previously allocated blocks */ -/* are ready to be reused. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void poolrestart(struct memorypool *pool) -#else /* not ANSI_DECLARATORS */ -void poolrestart(pool) -struct memorypool *pool; -#endif /* not ANSI_DECLARATORS */ - -{ - unsigned long alignptr; - - pool->items = 0; - pool->maxitems = 0; - - /* Set the currently active block. */ - pool->nowblock = pool->firstblock; - /* Find the first item in the pool. Increment by the size of (VOID *). */ - alignptr = (unsigned long) (pool->nowblock + 1); - /* Align the item on an `alignbytes'-byte boundary. */ - pool->nextitem = (VOID *) - (alignptr + (unsigned long) pool->alignbytes - - (alignptr % (unsigned long) pool->alignbytes)); - /* There are lots of unallocated items left in this block. */ - pool->unallocateditems = pool->itemsfirstblock; - /* The stack of deallocated items is empty. */ - pool->deaditemstack = (VOID *) NULL; -} - -/*****************************************************************************/ -/* */ -/* poolinit() Initialize a pool of memory for allocation of items. */ -/* */ -/* This routine initializes the machinery for allocating items. A `pool' */ -/* is created whose records have size at least `bytecount'. Items will be */ -/* allocated in `itemcount'-item blocks. Each item is assumed to be a */ -/* collection of words, and either pointers or floating-point values are */ -/* assumed to be the "primary" word type. (The "primary" word type is used */ -/* to determine alignment of items.) If `alignment' isn't zero, all items */ -/* will be `alignment'-byte aligned in memory. `alignment' must be either */ -/* a multiple or a factor of the primary word size; powers of two are safe. */ -/* `alignment' is normally used to create a few unused bits at the bottom */ -/* of each item's pointer, in which information may be stored. */ -/* */ -/* Don't change this routine unless you understand it. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void poolinit(struct memorypool *pool, int bytecount, int itemcount, - int firstitemcount, int alignment) -#else /* not ANSI_DECLARATORS */ -void poolinit(pool, bytecount, itemcount, firstitemcount, alignment) -struct memorypool *pool; -int bytecount; -int itemcount; -int firstitemcount; -int alignment; -#endif /* not ANSI_DECLARATORS */ - -{ - /* Find the proper alignment, which must be at least as large as: */ - /* - The parameter `alignment'. */ - /* - sizeof(VOID *), so the stack of dead items can be maintained */ - /* without unaligned accesses. */ - if (alignment > sizeof(VOID *)) { - pool->alignbytes = alignment; - } else { - pool->alignbytes = sizeof(VOID *); - } - pool->itembytes = ((bytecount - 1) / pool->alignbytes + 1) * - pool->alignbytes; - pool->itemsperblock = itemcount; - if (firstitemcount == 0) { - pool->itemsfirstblock = itemcount; - } else { - pool->itemsfirstblock = firstitemcount; - } - - /* Allocate a block of items. Space for `itemsfirstblock' items and one */ - /* pointer (to point to the next block) are allocated, as well as space */ - /* to ensure alignment of the items. */ - pool->firstblock = (VOID **) - trimalloc(pool->itemsfirstblock * pool->itembytes + (int) sizeof(VOID *) + - pool->alignbytes); - /* Set the next block pointer to NULL. */ - *(pool->firstblock) = (VOID *) NULL; - poolrestart(pool); -} - -/*****************************************************************************/ -/* */ -/* pooldeinit() Free to the operating system all memory taken by a pool. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void pooldeinit(struct memorypool *pool) -#else /* not ANSI_DECLARATORS */ -void pooldeinit(pool) -struct memorypool *pool; -#endif /* not ANSI_DECLARATORS */ - -{ - while (pool->firstblock != (VOID **) NULL) { - pool->nowblock = (VOID **) *(pool->firstblock); - trifree((VOID *) pool->firstblock); - pool->firstblock = pool->nowblock; - } -} - -/*****************************************************************************/ -/* */ -/* poolalloc() Allocate space for an item. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -VOID *poolalloc(struct memorypool *pool) -#else /* not ANSI_DECLARATORS */ -VOID *poolalloc(pool) -struct memorypool *pool; -#endif /* not ANSI_DECLARATORS */ - -{ - VOID *newitem; - VOID **newblock; - unsigned long alignptr; - - /* First check the linked list of dead items. If the list is not */ - /* empty, allocate an item from the list rather than a fresh one. */ - if (pool->deaditemstack != (VOID *) NULL) { - newitem = pool->deaditemstack; /* Take first item in list. */ - pool->deaditemstack = * (VOID **) pool->deaditemstack; - } else { - /* Check if there are any free items left in the current block. */ - if (pool->unallocateditems == 0) { - /* Check if another block must be allocated. */ - if (*(pool->nowblock) == (VOID *) NULL) { - /* Allocate a new block of items, pointed to by the previous block. */ - newblock = (VOID **) trimalloc(pool->itemsperblock * pool->itembytes + - (int) sizeof(VOID *) + - pool->alignbytes); - *(pool->nowblock) = (VOID *) newblock; - /* The next block pointer is NULL. */ - *newblock = (VOID *) NULL; - } - - /* Move to the new block. */ - pool->nowblock = (VOID **) *(pool->nowblock); - /* Find the first item in the block. */ - /* Increment by the size of (VOID *). */ - alignptr = (unsigned long) (pool->nowblock + 1); - /* Align the item on an `alignbytes'-byte boundary. */ - pool->nextitem = (VOID *) - (alignptr + (unsigned long) pool->alignbytes - - (alignptr % (unsigned long) pool->alignbytes)); - /* There are lots of unallocated items left in this block. */ - pool->unallocateditems = pool->itemsperblock; - } - - /* Allocate a new item. */ - newitem = pool->nextitem; - /* Advance `nextitem' pointer to next free item in block. */ - pool->nextitem = (VOID *) ((char *) pool->nextitem + pool->itembytes); - pool->unallocateditems--; - pool->maxitems++; - } - pool->items++; - return newitem; -} - -/*****************************************************************************/ -/* */ -/* pooldealloc() Deallocate space for an item. */ -/* */ -/* The deallocated space is stored in a queue for later reuse. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void pooldealloc(struct memorypool *pool, VOID *dyingitem) -#else /* not ANSI_DECLARATORS */ -void pooldealloc(pool, dyingitem) -struct memorypool *pool; -VOID *dyingitem; -#endif /* not ANSI_DECLARATORS */ - -{ - /* Push freshly killed item onto stack. */ - *((VOID **) dyingitem) = pool->deaditemstack; - pool->deaditemstack = dyingitem; - pool->items--; -} - -/*****************************************************************************/ -/* */ -/* traversalinit() Prepare to traverse the entire list of items. */ -/* */ -/* This routine is used in conjunction with traverse(). */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void traversalinit(struct memorypool *pool) -#else /* not ANSI_DECLARATORS */ -void traversalinit(pool) -struct memorypool *pool; -#endif /* not ANSI_DECLARATORS */ - -{ - unsigned long alignptr; - - /* Begin the traversal in the first block. */ - pool->pathblock = pool->firstblock; - /* Find the first item in the block. Increment by the size of (VOID *). */ - alignptr = (unsigned long) (pool->pathblock + 1); - /* Align with item on an `alignbytes'-byte boundary. */ - pool->pathitem = (VOID *) - (alignptr + (unsigned long) pool->alignbytes - - (alignptr % (unsigned long) pool->alignbytes)); - /* Set the number of items left in the current block. */ - pool->pathitemsleft = pool->itemsfirstblock; -} - -/*****************************************************************************/ -/* */ -/* traverse() Find the next item in the list. */ -/* */ -/* This routine is used in conjunction with traversalinit(). Be forewarned */ -/* that this routine successively returns all items in the list, including */ -/* deallocated ones on the deaditemqueue. It's up to you to figure out */ -/* which ones are actually dead. Why? I don't want to allocate extra */ -/* space just to demarcate dead items. It can usually be done more */ -/* space-efficiently by a routine that knows something about the structure */ -/* of the item. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -VOID *traverse(struct memorypool *pool) -#else /* not ANSI_DECLARATORS */ -VOID *traverse(pool) -struct memorypool *pool; -#endif /* not ANSI_DECLARATORS */ - -{ - VOID *newitem; - unsigned long alignptr; - - /* Stop upon exhausting the list of items. */ - if (pool->pathitem == pool->nextitem) { - return (VOID *) NULL; - } - - /* Check whether any untraversed items remain in the current block. */ - if (pool->pathitemsleft == 0) { - /* Find the next block. */ - pool->pathblock = (VOID **) *(pool->pathblock); - /* Find the first item in the block. Increment by the size of (VOID *). */ - alignptr = (unsigned long) (pool->pathblock + 1); - /* Align with item on an `alignbytes'-byte boundary. */ - pool->pathitem = (VOID *) - (alignptr + (unsigned long) pool->alignbytes - - (alignptr % (unsigned long) pool->alignbytes)); - /* Set the number of items left in the current block. */ - pool->pathitemsleft = pool->itemsperblock; - } - - newitem = pool->pathitem; - /* Find the next item in the block. */ - pool->pathitem = (VOID *) ((char *) pool->pathitem + pool->itembytes); - pool->pathitemsleft--; - return newitem; -} - -/*****************************************************************************/ -/* */ -/* dummyinit() Initialize the triangle that fills "outer space" and the */ -/* omnipresent subsegment. */ -/* */ -/* The triangle that fills "outer space," called `dummytri', is pointed to */ -/* by every triangle and subsegment on a boundary (be it outer or inner) of */ -/* the triangulation. Also, `dummytri' points to one of the triangles on */ -/* the convex hull (until the holes and concavities are carved), making it */ -/* possible to find a starting triangle for point location. */ -/* */ -/* The omnipresent subsegment, `dummysub', is pointed to by every triangle */ -/* or subsegment that doesn't have a full complement of real subsegments */ -/* to point to. */ -/* */ -/* `dummytri' and `dummysub' are generally required to fulfill only a few */ -/* invariants: their vertices must remain NULL and `dummytri' must always */ -/* be bonded (at offset zero) to some triangle on the convex hull of the */ -/* mesh, via a boundary edge. Otherwise, the connections of `dummytri' and */ -/* `dummysub' may change willy-nilly. This makes it possible to avoid */ -/* writing a good deal of special-case code (in the edge flip, for example) */ -/* for dealing with the boundary of the mesh, places where no subsegment is */ -/* present, and so forth. Other entities are frequently bonded to */ -/* `dummytri' and `dummysub' as if they were real mesh entities, with no */ -/* harm done. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void dummyinit(struct mesh *m, struct behavior *b, int trianglebytes, - int subsegbytes) -#else /* not ANSI_DECLARATORS */ -void dummyinit(m, b, trianglebytes, subsegbytes) -struct mesh *m; -struct behavior *b; -int trianglebytes; -int subsegbytes; -#endif /* not ANSI_DECLARATORS */ - -{ - unsigned long alignptr; - - /* Set up `dummytri', the `triangle' that occupies "outer space." */ - m->dummytribase = (triangle *) trimalloc(trianglebytes + - m->triangles.alignbytes); - /* Align `dummytri' on a `triangles.alignbytes'-byte boundary. */ - alignptr = (unsigned long) m->dummytribase; - m->dummytri = (triangle *) - (alignptr + (unsigned long) m->triangles.alignbytes - - (alignptr % (unsigned long) m->triangles.alignbytes)); - /* Initialize the three adjoining triangles to be "outer space." These */ - /* will eventually be changed by various bonding operations, but their */ - /* values don't really matter, as long as they can legally be */ - /* dereferenced. */ - m->dummytri[0] = (triangle) m->dummytri; - m->dummytri[1] = (triangle) m->dummytri; - m->dummytri[2] = (triangle) m->dummytri; - /* Three NULL vertices. */ - m->dummytri[3] = (triangle) NULL; - m->dummytri[4] = (triangle) NULL; - m->dummytri[5] = (triangle) NULL; - - if (b->usesegments) { - /* Set up `dummysub', the omnipresent subsegment pointed to by any */ - /* triangle side or subsegment end that isn't attached to a real */ - /* subsegment. */ - m->dummysubbase = (subseg *) trimalloc(subsegbytes + - m->subsegs.alignbytes); - /* Align `dummysub' on a `subsegs.alignbytes'-byte boundary. */ - alignptr = (unsigned long) m->dummysubbase; - m->dummysub = (subseg *) - (alignptr + (unsigned long) m->subsegs.alignbytes - - (alignptr % (unsigned long) m->subsegs.alignbytes)); - /* Initialize the two adjoining subsegments to be the omnipresent */ - /* subsegment. These will eventually be changed by various bonding */ - /* operations, but their values don't really matter, as long as they */ - /* can legally be dereferenced. */ - m->dummysub[0] = (subseg) m->dummysub; - m->dummysub[1] = (subseg) m->dummysub; - /* Four NULL vertices. */ - m->dummysub[2] = (subseg) NULL; - m->dummysub[3] = (subseg) NULL; - m->dummysub[4] = (subseg) NULL; - m->dummysub[5] = (subseg) NULL; - /* Initialize the two adjoining triangles to be "outer space." */ - m->dummysub[6] = (subseg) m->dummytri; - m->dummysub[7] = (subseg) m->dummytri; - /* Set the boundary marker to zero. */ - * (int *) (m->dummysub + 8) = 0; - - /* Initialize the three adjoining subsegments of `dummytri' to be */ - /* the omnipresent subsegment. */ - m->dummytri[6] = (triangle) m->dummysub; - m->dummytri[7] = (triangle) m->dummysub; - m->dummytri[8] = (triangle) m->dummysub; - } -} - -/*****************************************************************************/ -/* */ -/* initializevertexpool() Calculate the size of the vertex data structure */ -/* and initialize its memory pool. */ -/* */ -/* This routine also computes the `vertexmarkindex' and `vertex2triindex' */ -/* indices used to find values within each vertex. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void initializevertexpool(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void initializevertexpool(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - int vertexsize; - - /* The index within each vertex at which the boundary marker is found, */ - /* followed by the vertex type. Ensure the vertex marker is aligned to */ - /* a sizeof(int)-byte address. */ - m->vertexmarkindex = ((m->mesh_dim + m->nextras) * sizeof(REAL) + - sizeof(int) - 1) / - sizeof(int); - vertexsize = (m->vertexmarkindex + 2) * sizeof(int); - if (b->poly) { - /* The index within each vertex at which a triangle pointer is found. */ - /* Ensure the pointer is aligned to a sizeof(triangle)-byte address. */ - m->vertex2triindex = (vertexsize + sizeof(triangle) - 1) / - sizeof(triangle); - vertexsize = (m->vertex2triindex + 1) * sizeof(triangle); - } - - /* Initialize the pool of vertices. */ - poolinit(&m->vertices, vertexsize, VERTEXPERBLOCK, - m->invertices > VERTEXPERBLOCK ? m->invertices : VERTEXPERBLOCK, - sizeof(REAL)); -} - -/*****************************************************************************/ -/* */ -/* initializetrisubpools() Calculate the sizes of the triangle and */ -/* subsegment data structures and initialize */ -/* their memory pools. */ -/* */ -/* This routine also computes the `highorderindex', `elemattribindex', and */ -/* `areaboundindex' indices used to find values within each triangle. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void initializetrisubpools(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void initializetrisubpools(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - int trisize; - - /* The index within each triangle at which the extra nodes (above three) */ - /* associated with high order elements are found. There are three */ - /* pointers to other triangles, three pointers to corners, and possibly */ - /* three pointers to subsegments before the extra nodes. */ - m->highorderindex = 6 + (b->usesegments * 3); - /* The number of bytes occupied by a triangle. */ - trisize = ((b->order + 1) * (b->order + 2) / 2 + (m->highorderindex - 3)) * - sizeof(triangle); - /* The index within each triangle at which its attributes are found, */ - /* where the index is measured in REALs. */ - m->elemattribindex = (trisize + sizeof(REAL) - 1) / sizeof(REAL); - /* The index within each triangle at which the maximum area constraint */ - /* is found, where the index is measured in REALs. Note that if the */ - /* `regionattrib' flag is set, an additional attribute will be added. */ - m->areaboundindex = m->elemattribindex + m->eextras + b->regionattrib; - /* If triangle attributes or an area bound are needed, increase the number */ - /* of bytes occupied by a triangle. */ - if (b->vararea) { - trisize = (m->areaboundindex + 1) * sizeof(REAL); - } else if (m->eextras + b->regionattrib > 0) { - trisize = m->areaboundindex * sizeof(REAL); - } - /* If a Voronoi diagram or triangle neighbor graph is requested, make */ - /* sure there's room to store an integer index in each triangle. This */ - /* integer index can occupy the same space as the subsegment pointers */ - /* or attributes or area constraint or extra nodes. */ - if ((b->voronoi || b->neighbors) && - (trisize < 6 * sizeof(triangle) + sizeof(int))) { - trisize = 6 * sizeof(triangle) + sizeof(int); - } - - /* Having determined the memory size of a triangle, initialize the pool. */ - poolinit(&m->triangles, trisize, TRIPERBLOCK, - (2 * m->invertices - 2) > TRIPERBLOCK ? (2 * m->invertices - 2) : - TRIPERBLOCK, 4); - - if (b->usesegments) { - /* Initialize the pool of subsegments. Take into account all eight */ - /* pointers and one boundary marker. */ - poolinit(&m->subsegs, 8 * sizeof(triangle) + sizeof(int), - SUBSEGPERBLOCK, SUBSEGPERBLOCK, 4); - - /* Initialize the "outer space" triangle and omnipresent subsegment. */ - dummyinit(m, b, m->triangles.itembytes, m->subsegs.itembytes); - } else { - /* Initialize the "outer space" triangle. */ - dummyinit(m, b, m->triangles.itembytes, 0); - } -} - -/*****************************************************************************/ -/* */ -/* triangledealloc() Deallocate space for a triangle, marking it dead. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void triangledealloc(struct mesh *m, triangle *dyingtriangle) -#else /* not ANSI_DECLARATORS */ -void triangledealloc(m, dyingtriangle) -struct mesh *m; -triangle *dyingtriangle; -#endif /* not ANSI_DECLARATORS */ - -{ - /* Mark the triangle as dead. This makes it possible to detect dead */ - /* triangles when traversing the list of all triangles. */ - killtri(dyingtriangle); - pooldealloc(&m->triangles, (VOID *) dyingtriangle); -} - -/*****************************************************************************/ -/* */ -/* triangletraverse() Traverse the triangles, skipping dead ones. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -triangle *triangletraverse(struct mesh *m) -#else /* not ANSI_DECLARATORS */ -triangle *triangletraverse(m) -struct mesh *m; -#endif /* not ANSI_DECLARATORS */ - -{ - triangle *newtriangle; - - do { - newtriangle = (triangle *) traverse(&m->triangles); - if (newtriangle == (triangle *) NULL) { - return (triangle *) NULL; - } - } while (deadtri(newtriangle)); /* Skip dead ones. */ - return newtriangle; -} - -/*****************************************************************************/ -/* */ -/* subsegdealloc() Deallocate space for a subsegment, marking it dead. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void subsegdealloc(struct mesh *m, subseg *dyingsubseg) -#else /* not ANSI_DECLARATORS */ -void subsegdealloc(m, dyingsubseg) -struct mesh *m; -subseg *dyingsubseg; -#endif /* not ANSI_DECLARATORS */ - -{ - /* Mark the subsegment as dead. This makes it possible to detect dead */ - /* subsegments when traversing the list of all subsegments. */ - killsubseg(dyingsubseg); - pooldealloc(&m->subsegs, (VOID *) dyingsubseg); -} - -/*****************************************************************************/ -/* */ -/* subsegtraverse() Traverse the subsegments, skipping dead ones. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -subseg *subsegtraverse(struct mesh *m) -#else /* not ANSI_DECLARATORS */ -subseg *subsegtraverse(m) -struct mesh *m; -#endif /* not ANSI_DECLARATORS */ - -{ - subseg *newsubseg; - - do { - newsubseg = (subseg *) traverse(&m->subsegs); - if (newsubseg == (subseg *) NULL) { - return (subseg *) NULL; - } - } while (deadsubseg(newsubseg)); /* Skip dead ones. */ - return newsubseg; -} - -/*****************************************************************************/ -/* */ -/* vertexdealloc() Deallocate space for a vertex, marking it dead. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void vertexdealloc(struct mesh *m, vertex dyingvertex) -#else /* not ANSI_DECLARATORS */ -void vertexdealloc(m, dyingvertex) -struct mesh *m; -vertex dyingvertex; -#endif /* not ANSI_DECLARATORS */ - -{ - /* Mark the vertex as dead. This makes it possible to detect dead */ - /* vertices when traversing the list of all vertices. */ - setvertextype(dyingvertex, DEADVERTEX); - pooldealloc(&m->vertices, (VOID *) dyingvertex); -} - -/*****************************************************************************/ -/* */ -/* vertextraverse() Traverse the vertices, skipping dead ones. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -vertex vertextraverse(struct mesh *m) -#else /* not ANSI_DECLARATORS */ -vertex vertextraverse(m) -struct mesh *m; -#endif /* not ANSI_DECLARATORS */ - -{ - vertex newvertex; - - do { - newvertex = (vertex) traverse(&m->vertices); - if (newvertex == (vertex) NULL) { - return (vertex) NULL; - } - } while (vertextype(newvertex) == DEADVERTEX); /* Skip dead ones. */ - return newvertex; -} - -/*****************************************************************************/ -/* */ -/* badsubsegdealloc() Deallocate space for a bad subsegment, marking it */ -/* dead. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -void badsubsegdealloc(struct mesh *m, struct badsubseg *dyingseg) -#else /* not ANSI_DECLARATORS */ -void badsubsegdealloc(m, dyingseg) -struct mesh *m; -struct badsubseg *dyingseg; -#endif /* not ANSI_DECLARATORS */ - -{ - /* Set subsegment's origin to NULL. This makes it possible to detect dead */ - /* badsubsegs when traversing the list of all badsubsegs . */ - dyingseg->subsegorg = (vertex) NULL; - pooldealloc(&m->badsubsegs, (VOID *) dyingseg); -} - -#endif /* not CDT_ONLY */ - -/*****************************************************************************/ -/* */ -/* badsubsegtraverse() Traverse the bad subsegments, skipping dead ones. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -struct badsubseg *badsubsegtraverse(struct mesh *m) -#else /* not ANSI_DECLARATORS */ -struct badsubseg *badsubsegtraverse(m) -struct mesh *m; -#endif /* not ANSI_DECLARATORS */ - -{ - struct badsubseg *newseg; - - do { - newseg = (struct badsubseg *) traverse(&m->badsubsegs); - if (newseg == (struct badsubseg *) NULL) { - return (struct badsubseg *) NULL; - } - } while (newseg->subsegorg == (vertex) NULL); /* Skip dead ones. */ - return newseg; -} - -#endif /* not CDT_ONLY */ - -/*****************************************************************************/ -/* */ -/* getvertex() Get a specific vertex, by number, from the list. */ -/* */ -/* The first vertex is number 'firstnumber'. */ -/* */ -/* Note that this takes O(n) time (with a small constant, if VERTEXPERBLOCK */ -/* is large). I don't care to take the trouble to make it work in constant */ -/* time. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -vertex getvertex(struct mesh *m, struct behavior *b, int number) -#else /* not ANSI_DECLARATORS */ -vertex getvertex(m, b, number) -struct mesh *m; -struct behavior *b; -int number; -#endif /* not ANSI_DECLARATORS */ - -{ - VOID **getblock; - char *foundvertex; - unsigned long alignptr; - int current; - - getblock = m->vertices.firstblock; - current = b->firstnumber; - - /* Find the right block. */ - if (current + m->vertices.itemsfirstblock <= number) { - getblock = (VOID **) *getblock; - current += m->vertices.itemsfirstblock; - while (current + m->vertices.itemsperblock <= number) { - getblock = (VOID **) *getblock; - current += m->vertices.itemsperblock; - } - } - - /* Now find the right vertex. */ - alignptr = (unsigned long) (getblock + 1); - foundvertex = (char *) (alignptr + (unsigned long) m->vertices.alignbytes - - (alignptr % (unsigned long) m->vertices.alignbytes)); - return (vertex) (foundvertex + m->vertices.itembytes * (number - current)); -} - -/*****************************************************************************/ -/* */ -/* triangledeinit() Free all remaining allocated memory. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void triangledeinit(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void triangledeinit(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - pooldeinit(&m->triangles); - trifree((VOID *) m->dummytribase); - if (b->usesegments) { - pooldeinit(&m->subsegs); - trifree((VOID *) m->dummysubbase); - } - pooldeinit(&m->vertices); -#ifndef CDT_ONLY - if (b->quality) { - pooldeinit(&m->badsubsegs); - if ((b->minangle > 0.0) || b->vararea || b->fixedarea || b->usertest) { - pooldeinit(&m->badtriangles); - pooldeinit(&m->flipstackers); - } - } -#endif /* not CDT_ONLY */ -} - -/** **/ -/** **/ -/********* Memory management routines end here *********/ - -/********* Constructors begin here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* maketriangle() Create a new triangle with orientation zero. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void maketriangle(struct mesh *m, struct behavior *b, struct otri *newotri) -#else /* not ANSI_DECLARATORS */ -void maketriangle(m, b, newotri) -struct mesh *m; -struct behavior *b; -struct otri *newotri; -#endif /* not ANSI_DECLARATORS */ - -{ - int i; - - newotri->tri = (triangle *) poolalloc(&m->triangles); - /* Initialize the three adjoining triangles to be "outer space". */ - newotri->tri[0] = (triangle) m->dummytri; - newotri->tri[1] = (triangle) m->dummytri; - newotri->tri[2] = (triangle) m->dummytri; - /* Three NULL vertices. */ - newotri->tri[3] = (triangle) NULL; - newotri->tri[4] = (triangle) NULL; - newotri->tri[5] = (triangle) NULL; - if (b->usesegments) { - /* Initialize the three adjoining subsegments to be the omnipresent */ - /* subsegment. */ - newotri->tri[6] = (triangle) m->dummysub; - newotri->tri[7] = (triangle) m->dummysub; - newotri->tri[8] = (triangle) m->dummysub; - } - for (i = 0; i < m->eextras; i++) { - setelemattribute(*newotri, i, 0.0); - } - if (b->vararea) { - setareabound(*newotri, -1.0); - } - - newotri->orient = 0; -} - -/*****************************************************************************/ -/* */ -/* makesubseg() Create a new subsegment with orientation zero. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void makesubseg(struct mesh *m, struct osub *newsubseg) -#else /* not ANSI_DECLARATORS */ -void makesubseg(m, newsubseg) -struct mesh *m; -struct osub *newsubseg; -#endif /* not ANSI_DECLARATORS */ - -{ - newsubseg->ss = (subseg *) poolalloc(&m->subsegs); - /* Initialize the two adjoining subsegments to be the omnipresent */ - /* subsegment. */ - newsubseg->ss[0] = (subseg) m->dummysub; - newsubseg->ss[1] = (subseg) m->dummysub; - /* Four NULL vertices. */ - newsubseg->ss[2] = (subseg) NULL; - newsubseg->ss[3] = (subseg) NULL; - newsubseg->ss[4] = (subseg) NULL; - newsubseg->ss[5] = (subseg) NULL; - /* Initialize the two adjoining triangles to be "outer space." */ - newsubseg->ss[6] = (subseg) m->dummytri; - newsubseg->ss[7] = (subseg) m->dummytri; - /* Set the boundary marker to zero. */ - setmark(*newsubseg, 0); - - newsubseg->ssorient = 0; -} - -/** **/ -/** **/ -/********* Constructors end here *********/ - -/********* Geometric primitives begin here *********/ -/** **/ -/** **/ - -/* The adaptive exact arithmetic geometric predicates implemented herein are */ -/* described in detail in my paper, "Adaptive Precision Floating-Point */ -/* Arithmetic and Fast Robust Geometric Predicates." See the header for a */ -/* full citation. */ - -/* Which of the following two methods of finding the absolute values is */ -/* fastest is compiler-dependent. A few compilers can inline and optimize */ -/* the fabs() call; but most will incur the overhead of a function call, */ -/* which is disastrously slow. A faster way on IEEE machines might be to */ -/* mask the appropriate bit, but that's difficult to do in C without */ -/* forcing the value to be stored to memory (rather than be kept in the */ -/* register to which the optimizer assigned it). */ - -#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) -/* #define Absolute(a) fabs(a) */ - -/* Many of the operations are broken up into two pieces, a main part that */ -/* performs an approximate operation, and a "tail" that computes the */ -/* roundoff error of that operation. */ -/* */ -/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), */ -/* Split(), and Two_Product() are all implemented as described in the */ -/* reference. Each of these macros requires certain variables to be */ -/* defined in the calling routine. The variables `bvirt', `c', `abig', */ -/* `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because */ -/* they store the result of an operation that may incur roundoff error. */ -/* The input parameter `x' (or the highest numbered `x_' parameter) must */ -/* also be declared `INEXACT'. */ - -#define Fast_Two_Sum_Tail(a, b, x, y) \ - bvirt = x - a; \ - y = b - bvirt - -#define Fast_Two_Sum(a, b, x, y) \ - x = (REAL) (a + b); \ - Fast_Two_Sum_Tail(a, b, x, y) - -#define Two_Sum_Tail(a, b, x, y) \ - bvirt = (REAL) (x - a); \ - avirt = x - bvirt; \ - bround = b - bvirt; \ - around = a - avirt; \ - y = around + bround - -#define Two_Sum(a, b, x, y) \ - x = (REAL) (a + b); \ - Two_Sum_Tail(a, b, x, y) - -#define Two_Diff_Tail(a, b, x, y) \ - bvirt = (REAL) (a - x); \ - avirt = x + bvirt; \ - bround = bvirt - b; \ - around = a - avirt; \ - y = around + bround - -#define Two_Diff(a, b, x, y) \ - x = (REAL) (a - b); \ - Two_Diff_Tail(a, b, x, y) - -#define Split(a, ahi, alo) \ - c = (REAL) (splitter * a); \ - abig = (REAL) (c - a); \ - ahi = c - abig; \ - alo = a - ahi - -#define Two_Product_Tail(a, b, x, y) \ - Split(a, ahi, alo); \ - Split(b, bhi, blo); \ - err1 = x - (ahi * bhi); \ - err2 = err1 - (alo * bhi); \ - err3 = err2 - (ahi * blo); \ - y = (alo * blo) - err3 - -#define Two_Product(a, b, x, y) \ - x = (REAL) (a * b); \ - Two_Product_Tail(a, b, x, y) - -/* Two_Product_Presplit() is Two_Product() where one of the inputs has */ -/* already been split. Avoids redundant splitting. */ - -#define Two_Product_Presplit(a, b, bhi, blo, x, y) \ - x = (REAL) (a * b); \ - Split(a, ahi, alo); \ - err1 = x - (ahi * bhi); \ - err2 = err1 - (alo * bhi); \ - err3 = err2 - (ahi * blo); \ - y = (alo * blo) - err3 - -/* Square() can be done more quickly than Two_Product(). */ - -#define Square_Tail(a, x, y) \ - Split(a, ahi, alo); \ - err1 = x - (ahi * ahi); \ - err3 = err1 - ((ahi + ahi) * alo); \ - y = (alo * alo) - err3 - -#define Square(a, x, y) \ - x = (REAL) (a * a); \ - Square_Tail(a, x, y) - -/* Macros for summing expansions of various fixed lengths. These are all */ -/* unrolled versions of Expansion_Sum(). */ - -#define Two_One_Sum(a1, a0, b, x2, x1, x0) \ - Two_Sum(a0, b , _i, x0); \ - Two_Sum(a1, _i, x2, x1) - -#define Two_One_Diff(a1, a0, b, x2, x1, x0) \ - Two_Diff(a0, b , _i, x0); \ - Two_Sum( a1, _i, x2, x1) - -#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \ - Two_One_Sum(a1, a0, b0, _j, _0, x0); \ - Two_One_Sum(_j, _0, b1, x3, x2, x1) - -#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \ - Two_One_Diff(a1, a0, b0, _j, _0, x0); \ - Two_One_Diff(_j, _0, b1, x3, x2, x1) - -/* Macro for multiplying a two-component expansion by a single component. */ - -#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \ - Split(b, bhi, blo); \ - Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ - Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ - Two_Sum(_i, _0, _k, x1); \ - Fast_Two_Sum(_j, _k, x3, x2) - -/*****************************************************************************/ -/* */ -/* exactinit() Initialize the variables used for exact arithmetic. */ -/* */ -/* `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in */ -/* floating-point arithmetic. `epsilon' bounds the relative roundoff */ -/* error. It is used for floating-point error analysis. */ -/* */ -/* `splitter' is used to split floating-point numbers into two half- */ -/* length significands for exact multiplication. */ -/* */ -/* I imagine that a highly optimizing compiler might be too smart for its */ -/* own good, and somehow cause this routine to fail, if it pretends that */ -/* floating-point arithmetic is too much like real arithmetic. */ -/* */ -/* Don't change this routine unless you fully understand it. */ -/* */ -/*****************************************************************************/ - -void exactinit() -{ - REAL half; - REAL check, lastcheck; - int every_other; -#ifdef LINUX - int cword; -#endif /* LINUX */ - -#ifdef CPU86 -#ifdef SINGLE - _control87(_PC_24, _MCW_PC); /* Set FPU control word for single precision. */ -#else /* not SINGLE */ - _control87(_PC_53, _MCW_PC); /* Set FPU control word for double precision. */ -#endif /* not SINGLE */ -#endif /* CPU86 */ -#ifdef LINUX -#ifdef SINGLE - /* cword = 4223; */ - cword = 4210; /* set FPU control word for single precision */ -#else /* not SINGLE */ - /* cword = 4735; */ - cword = 4722; /* set FPU control word for double precision */ -#endif /* not SINGLE */ - _FPU_SETCW(cword); -#endif /* LINUX */ - - every_other = 1; - half = 0.5; - epsilon = 1.0; - splitter = 1.0; - check = 1.0; - /* Repeatedly divide `epsilon' by two until it is too small to add to */ - /* one without causing roundoff. (Also check if the sum is equal to */ - /* the previous sum, for machines that round up instead of using exact */ - /* rounding. Not that these routines will work on such machines.) */ - do { - lastcheck = check; - epsilon *= half; - if (every_other) { - splitter *= 2.0; - } - every_other = !every_other; - check = 1.0 + epsilon; - } while ((check != 1.0) && (check != lastcheck)); - splitter += 1.0; - /* Error bounds for orientation and incircle tests. */ - resulterrbound = (3.0 + 8.0 * epsilon) * epsilon; - ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon; - ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon; - ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon; - iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; - iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon; - iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon; - o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon; - o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon; - o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon; -} - -/*****************************************************************************/ -/* */ -/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero */ -/* components from the output expansion. */ -/* */ -/* Sets h = e + f. See my Robust Predicates paper for details. */ -/* */ -/* If round-to-even is used (as with IEEE 754), maintains the strongly */ -/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */ -/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */ -/* properties. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -int fast_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, REAL *h) -#else /* not ANSI_DECLARATORS */ -int fast_expansion_sum_zeroelim(elen, e, flen, f, h) /* h cannot be e or f. */ -int elen; -REAL *e; -int flen; -REAL *f; -REAL *h; -#endif /* not ANSI_DECLARATORS */ - -{ - REAL Q; - INEXACT REAL Qnew; - INEXACT REAL hh; - INEXACT REAL bvirt; - REAL avirt, bround, around; - int eindex, findex, hindex; - REAL enow, fnow; - - enow = e[0]; - fnow = f[0]; - eindex = findex = 0; - if ((fnow > enow) == (fnow > -enow)) { - Q = enow; - enow = e[++eindex]; - } else { - Q = fnow; - fnow = f[++findex]; - } - hindex = 0; - if ((eindex < elen) && (findex < flen)) { - if ((fnow > enow) == (fnow > -enow)) { - Fast_Two_Sum(enow, Q, Qnew, hh); - enow = e[++eindex]; - } else { - Fast_Two_Sum(fnow, Q, Qnew, hh); - fnow = f[++findex]; - } - Q = Qnew; - if (hh != 0.0) { - h[hindex++] = hh; - } - while ((eindex < elen) && (findex < flen)) { - if ((fnow > enow) == (fnow > -enow)) { - Two_Sum(Q, enow, Qnew, hh); - enow = e[++eindex]; - } else { - Two_Sum(Q, fnow, Qnew, hh); - fnow = f[++findex]; - } - Q = Qnew; - if (hh != 0.0) { - h[hindex++] = hh; - } - } - } - while (eindex < elen) { - Two_Sum(Q, enow, Qnew, hh); - enow = e[++eindex]; - Q = Qnew; - if (hh != 0.0) { - h[hindex++] = hh; - } - } - while (findex < flen) { - Two_Sum(Q, fnow, Qnew, hh); - fnow = f[++findex]; - Q = Qnew; - if (hh != 0.0) { - h[hindex++] = hh; - } - } - if ((Q != 0.0) || (hindex == 0)) { - h[hindex++] = Q; - } - return hindex; -} - -/*****************************************************************************/ -/* */ -/* scale_expansion_zeroelim() Multiply an expansion by a scalar, */ -/* eliminating zero components from the */ -/* output expansion. */ -/* */ -/* Sets h = be. See my Robust Predicates paper for details. */ -/* */ -/* Maintains the nonoverlapping property. If round-to-even is used (as */ -/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */ -/* properties as well. (That is, if e has one of these properties, so */ -/* will h.) */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h) -#else /* not ANSI_DECLARATORS */ -int scale_expansion_zeroelim(elen, e, b, h) /* e and h cannot be the same. */ -int elen; -REAL *e; -REAL b; -REAL *h; -#endif /* not ANSI_DECLARATORS */ - -{ - INEXACT REAL Q, sum; - REAL hh; - INEXACT REAL product1; - REAL product0; - int eindex, hindex; - REAL enow; - INEXACT REAL bvirt; - REAL avirt, bround, around; - INEXACT REAL c; - INEXACT REAL abig; - REAL ahi, alo, bhi, blo; - REAL err1, err2, err3; - - Split(b, bhi, blo); - Two_Product_Presplit(e[0], b, bhi, blo, Q, hh); - hindex = 0; - if (hh != 0) { - h[hindex++] = hh; - } - for (eindex = 1; eindex < elen; eindex++) { - enow = e[eindex]; - Two_Product_Presplit(enow, b, bhi, blo, product1, product0); - Two_Sum(Q, product0, sum, hh); - if (hh != 0) { - h[hindex++] = hh; - } - Fast_Two_Sum(product1, sum, Q, hh); - if (hh != 0) { - h[hindex++] = hh; - } - } - if ((Q != 0.0) || (hindex == 0)) { - h[hindex++] = Q; - } - return hindex; -} - -/*****************************************************************************/ -/* */ -/* estimate() Produce a one-word estimate of an expansion's value. */ -/* */ -/* See my Robust Predicates paper for details. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -REAL estimate(int elen, REAL *e) -#else /* not ANSI_DECLARATORS */ -REAL estimate(elen, e) -int elen; -REAL *e; -#endif /* not ANSI_DECLARATORS */ - -{ - REAL Q; - int eindex; - - Q = e[0]; - for (eindex = 1; eindex < elen; eindex++) { - Q += e[eindex]; - } - return Q; -} - -/*****************************************************************************/ -/* */ -/* counterclockwise() Return a positive value if the points pa, pb, and */ -/* pc occur in counterclockwise order; a negative */ -/* value if they occur in clockwise order; and zero */ -/* if they are collinear. The result is also a rough */ -/* approximation of twice the signed area of the */ -/* triangle defined by the three points. */ -/* */ -/* Uses exact arithmetic if necessary to ensure a correct answer. The */ -/* result returned is the determinant of a matrix. This determinant is */ -/* computed adaptively, in the sense that exact arithmetic is used only to */ -/* the degree it is needed to ensure that the returned value has the */ -/* correct sign. Hence, this function is usually quite fast, but will run */ -/* more slowly when the input points are collinear or nearly so. */ -/* */ -/* See my Robust Predicates paper for details. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -REAL counterclockwiseadapt(vertex pa, vertex pb, vertex pc, REAL detsum) -#else /* not ANSI_DECLARATORS */ -REAL counterclockwiseadapt(pa, pb, pc, detsum) -vertex pa; -vertex pb; -vertex pc; -REAL detsum; -#endif /* not ANSI_DECLARATORS */ - -{ - INEXACT REAL acx, acy, bcx, bcy; - REAL acxtail, acytail, bcxtail, bcytail; - INEXACT REAL detleft, detright; - REAL detlefttail, detrighttail; - REAL det, errbound; - REAL B[4], C1[8], C2[12], D[16]; - INEXACT REAL B3; - int C1length, C2length, Dlength; - REAL u[4]; - INEXACT REAL u3; - INEXACT REAL s1, t1; - REAL s0, t0; - - INEXACT REAL bvirt; - REAL avirt, bround, around; - INEXACT REAL c; - INEXACT REAL abig; - REAL ahi, alo, bhi, blo; - REAL err1, err2, err3; - INEXACT REAL _i, _j; - REAL _0; - - acx = (REAL) (pa[0] - pc[0]); - bcx = (REAL) (pb[0] - pc[0]); - acy = (REAL) (pa[1] - pc[1]); - bcy = (REAL) (pb[1] - pc[1]); - - Two_Product(acx, bcy, detleft, detlefttail); - Two_Product(acy, bcx, detright, detrighttail); - - Two_Two_Diff(detleft, detlefttail, detright, detrighttail, - B3, B[2], B[1], B[0]); - B[3] = B3; - - det = estimate(4, B); - errbound = ccwerrboundB * detsum; - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - Two_Diff_Tail(pa[0], pc[0], acx, acxtail); - Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail); - Two_Diff_Tail(pa[1], pc[1], acy, acytail); - Two_Diff_Tail(pb[1], pc[1], bcy, bcytail); - - if ((acxtail == 0.0) && (acytail == 0.0) - && (bcxtail == 0.0) && (bcytail == 0.0)) { - return det; - } - - errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det); - det += (acx * bcytail + bcy * acxtail) - - (acy * bcxtail + bcx * acytail); - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - Two_Product(acxtail, bcy, s1, s0); - Two_Product(acytail, bcx, t1, t0); - Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); - u[3] = u3; - C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1); - - Two_Product(acx, bcytail, s1, s0); - Two_Product(acy, bcxtail, t1, t0); - Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); - u[3] = u3; - C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2); - - Two_Product(acxtail, bcytail, s1, s0); - Two_Product(acytail, bcxtail, t1, t0); - Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); - u[3] = u3; - Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D); - - return(D[Dlength - 1]); -} - -#ifdef ANSI_DECLARATORS -REAL counterclockwise(struct mesh *m, struct behavior *b, - vertex pa, vertex pb, vertex pc) -#else /* not ANSI_DECLARATORS */ -REAL counterclockwise(m, b, pa, pb, pc) -struct mesh *m; -struct behavior *b; -vertex pa; -vertex pb; -vertex pc; -#endif /* not ANSI_DECLARATORS */ - -{ - REAL detleft, detright, det; - REAL detsum, errbound; - - m->counterclockcount++; - - detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]); - detright = (pa[1] - pc[1]) * (pb[0] - pc[0]); - det = detleft - detright; - - if (b->noexact) { - return det; - } - - if (detleft > 0.0) { - if (detright <= 0.0) { - return det; - } else { - detsum = detleft + detright; - } - } else if (detleft < 0.0) { - if (detright >= 0.0) { - return det; - } else { - detsum = -detleft - detright; - } - } else { - return det; - } - - errbound = ccwerrboundA * detsum; - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - return counterclockwiseadapt(pa, pb, pc, detsum); -} - -/*****************************************************************************/ -/* */ -/* incircle() Return a positive value if the point pd lies inside the */ -/* circle passing through pa, pb, and pc; a negative value if */ -/* it lies outside; and zero if the four points are cocircular.*/ -/* The points pa, pb, and pc must be in counterclockwise */ -/* order, or the sign of the result will be reversed. */ -/* */ -/* Uses exact arithmetic if necessary to ensure a correct answer. The */ -/* result returned is the determinant of a matrix. This determinant is */ -/* computed adaptively, in the sense that exact arithmetic is used only to */ -/* the degree it is needed to ensure that the returned value has the */ -/* correct sign. Hence, this function is usually quite fast, but will run */ -/* more slowly when the input points are cocircular or nearly so. */ -/* */ -/* See my Robust Predicates paper for details. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -REAL incircleadapt(vertex pa, vertex pb, vertex pc, vertex pd, REAL permanent) -#else /* not ANSI_DECLARATORS */ -REAL incircleadapt(pa, pb, pc, pd, permanent) -vertex pa; -vertex pb; -vertex pc; -vertex pd; -REAL permanent; -#endif /* not ANSI_DECLARATORS */ - -{ - INEXACT REAL adx, bdx, cdx, ady, bdy, cdy; - REAL det, errbound; - - INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; - REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; - REAL bc[4], ca[4], ab[4]; - INEXACT REAL bc3, ca3, ab3; - REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32]; - int axbclen, axxbclen, aybclen, ayybclen, alen; - REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32]; - int bxcalen, bxxcalen, bycalen, byycalen, blen; - REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32]; - int cxablen, cxxablen, cyablen, cyyablen, clen; - REAL abdet[64]; - int ablen; - REAL fin1[1152], fin2[1152]; - REAL *finnow, *finother, *finswap; - int finlength; - - REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail; - INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1; - REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0; - REAL aa[4], bb[4], cc[4]; - INEXACT REAL aa3, bb3, cc3; - INEXACT REAL ti1, tj1; - REAL ti0, tj0; - REAL u[4], v[4]; - INEXACT REAL u3, v3; - REAL temp8[8], temp16a[16], temp16b[16], temp16c[16]; - REAL temp32a[32], temp32b[32], temp48[48], temp64[64]; - int temp8len, temp16alen, temp16blen, temp16clen; - int temp32alen, temp32blen, temp48len, temp64len; - REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8]; - int axtbblen, axtcclen, aytbblen, aytcclen; - REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8]; - int bxtaalen, bxtcclen, bytaalen, bytcclen; - REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8]; - int cxtaalen, cxtbblen, cytaalen, cytbblen; - REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8]; - int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen; - REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16]; - int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen; - REAL axtbctt[8], aytbctt[8], bxtcatt[8]; - REAL bytcatt[8], cxtabtt[8], cytabtt[8]; - int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen; - REAL abt[8], bct[8], cat[8]; - int abtlen, bctlen, catlen; - REAL abtt[4], bctt[4], catt[4]; - int abttlen, bcttlen, cattlen; - INEXACT REAL abtt3, bctt3, catt3; - REAL negate; - - INEXACT REAL bvirt; - REAL avirt, bround, around; - INEXACT REAL c; - INEXACT REAL abig; - REAL ahi, alo, bhi, blo; - REAL err1, err2, err3; - INEXACT REAL _i, _j; - REAL _0; - - adx = (REAL) (pa[0] - pd[0]); - bdx = (REAL) (pb[0] - pd[0]); - cdx = (REAL) (pc[0] - pd[0]); - ady = (REAL) (pa[1] - pd[1]); - bdy = (REAL) (pb[1] - pd[1]); - cdy = (REAL) (pc[1] - pd[1]); - - Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); - Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); - Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); - bc[3] = bc3; - axbclen = scale_expansion_zeroelim(4, bc, adx, axbc); - axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc); - aybclen = scale_expansion_zeroelim(4, bc, ady, aybc); - ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc); - alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet); - - Two_Product(cdx, ady, cdxady1, cdxady0); - Two_Product(adx, cdy, adxcdy1, adxcdy0); - Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); - ca[3] = ca3; - bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca); - bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca); - bycalen = scale_expansion_zeroelim(4, ca, bdy, byca); - byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca); - blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet); - - Two_Product(adx, bdy, adxbdy1, adxbdy0); - Two_Product(bdx, ady, bdxady1, bdxady0); - Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); - ab[3] = ab3; - cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab); - cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab); - cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab); - cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab); - clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet); - - ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); - finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); - - det = estimate(finlength, fin1); - errbound = iccerrboundB * permanent; - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - Two_Diff_Tail(pa[0], pd[0], adx, adxtail); - Two_Diff_Tail(pa[1], pd[1], ady, adytail); - Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); - Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); - Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); - Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); - if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) - && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) { - return det; - } - - errbound = iccerrboundC * permanent + resulterrbound * Absolute(det); - det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail) - - (bdy * cdxtail + cdx * bdytail)) - + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) - + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail) - - (cdy * adxtail + adx * cdytail)) - + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) - + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail) - - (ady * bdxtail + bdx * adytail)) - + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx)); - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - finnow = fin1; - finother = fin2; - - if ((bdxtail != 0.0) || (bdytail != 0.0) - || (cdxtail != 0.0) || (cdytail != 0.0)) { - Square(adx, adxadx1, adxadx0); - Square(ady, adyady1, adyady0); - Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]); - aa[3] = aa3; - } - if ((cdxtail != 0.0) || (cdytail != 0.0) - || (adxtail != 0.0) || (adytail != 0.0)) { - Square(bdx, bdxbdx1, bdxbdx0); - Square(bdy, bdybdy1, bdybdy0); - Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]); - bb[3] = bb3; - } - if ((adxtail != 0.0) || (adytail != 0.0) - || (bdxtail != 0.0) || (bdytail != 0.0)) { - Square(cdx, cdxcdx1, cdxcdx0); - Square(cdy, cdycdy1, cdycdy0); - Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]); - cc[3] = cc3; - } - - if (adxtail != 0.0) { - axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc); - temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, - temp16a); - - axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc); - temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b); - - axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb); - temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (adytail != 0.0) { - aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc); - temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, - temp16a); - - aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb); - temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b); - - aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc); - temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (bdxtail != 0.0) { - bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca); - temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, - temp16a); - - bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa); - temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b); - - bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc); - temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (bdytail != 0.0) { - bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca); - temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, - temp16a); - - bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc); - temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b); - - bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa); - temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (cdxtail != 0.0) { - cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab); - temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, - temp16a); - - cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb); - temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b); - - cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa); - temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (cdytail != 0.0) { - cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab); - temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, - temp16a); - - cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa); - temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b); - - cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb); - temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - - if ((adxtail != 0.0) || (adytail != 0.0)) { - if ((bdxtail != 0.0) || (bdytail != 0.0) - || (cdxtail != 0.0) || (cdytail != 0.0)) { - Two_Product(bdxtail, cdy, ti1, ti0); - Two_Product(bdx, cdytail, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); - u[3] = u3; - negate = -bdy; - Two_Product(cdxtail, negate, ti1, ti0); - negate = -bdytail; - Two_Product(cdx, negate, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); - v[3] = v3; - bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct); - - Two_Product(bdxtail, cdytail, ti1, ti0); - Two_Product(cdxtail, bdytail, tj1, tj0); - Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]); - bctt[3] = bctt3; - bcttlen = 4; - } else { - bct[0] = 0.0; - bctlen = 1; - bctt[0] = 0.0; - bcttlen = 1; - } - - if (adxtail != 0.0) { - temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a); - axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct); - temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, - temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - if (bdytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, - temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, - temp16a, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (cdytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, - temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, - temp16a, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - - temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, - temp32a); - axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt); - temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, - temp16a); - temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, - temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, - temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, - temp64, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (adytail != 0.0) { - temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a); - aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct); - temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, - temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - - - temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, - temp32a); - aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt); - temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, - temp16a); - temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, - temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, - temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, - temp64, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - if ((bdxtail != 0.0) || (bdytail != 0.0)) { - if ((cdxtail != 0.0) || (cdytail != 0.0) - || (adxtail != 0.0) || (adytail != 0.0)) { - Two_Product(cdxtail, ady, ti1, ti0); - Two_Product(cdx, adytail, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); - u[3] = u3; - negate = -cdy; - Two_Product(adxtail, negate, ti1, ti0); - negate = -cdytail; - Two_Product(adx, negate, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); - v[3] = v3; - catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat); - - Two_Product(cdxtail, adytail, ti1, ti0); - Two_Product(adxtail, cdytail, tj1, tj0); - Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]); - catt[3] = catt3; - cattlen = 4; - } else { - cat[0] = 0.0; - catlen = 1; - catt[0] = 0.0; - cattlen = 1; - } - - if (bdxtail != 0.0) { - temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a); - bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat); - temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, - temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - if (cdytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, - temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, - temp16a, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (adytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, - temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, - temp16a, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - - temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, - temp32a); - bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt); - temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, - temp16a); - temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, - temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, - temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, - temp64, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (bdytail != 0.0) { - temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a); - bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat); - temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, - temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - - - temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, - temp32a); - bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt); - temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, - temp16a); - temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, - temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, - temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, - temp64, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - if ((cdxtail != 0.0) || (cdytail != 0.0)) { - if ((adxtail != 0.0) || (adytail != 0.0) - || (bdxtail != 0.0) || (bdytail != 0.0)) { - Two_Product(adxtail, bdy, ti1, ti0); - Two_Product(adx, bdytail, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); - u[3] = u3; - negate = -ady; - Two_Product(bdxtail, negate, ti1, ti0); - negate = -adytail; - Two_Product(bdx, negate, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); - v[3] = v3; - abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt); - - Two_Product(adxtail, bdytail, ti1, ti0); - Two_Product(bdxtail, adytail, tj1, tj0); - Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]); - abtt[3] = abtt3; - abttlen = 4; - } else { - abt[0] = 0.0; - abtlen = 1; - abtt[0] = 0.0; - abttlen = 1; - } - - if (cdxtail != 0.0) { - temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a); - cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt); - temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, - temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - if (adytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, - temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, - temp16a, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (bdytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, - temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, - temp16a, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - - temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, - temp32a); - cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt); - temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, - temp16a); - temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, - temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, - temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, - temp64, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (cdytail != 0.0) { - temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a); - cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt); - temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, - temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - - - temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, - temp32a); - cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt); - temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, - temp16a); - temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, - temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, - temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, - temp64, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - - return finnow[finlength - 1]; -} - -#ifdef ANSI_DECLARATORS -REAL incircle(struct mesh *m, struct behavior *b, - vertex pa, vertex pb, vertex pc, vertex pd) -#else /* not ANSI_DECLARATORS */ -REAL incircle(m, b, pa, pb, pc, pd) -struct mesh *m; -struct behavior *b; -vertex pa; -vertex pb; -vertex pc; -vertex pd; -#endif /* not ANSI_DECLARATORS */ - -{ - REAL adx, bdx, cdx, ady, bdy, cdy; - REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; - REAL alift, blift, clift; - REAL det; - REAL permanent, errbound; - - m->incirclecount++; - - adx = pa[0] - pd[0]; - bdx = pb[0] - pd[0]; - cdx = pc[0] - pd[0]; - ady = pa[1] - pd[1]; - bdy = pb[1] - pd[1]; - cdy = pc[1] - pd[1]; - - bdxcdy = bdx * cdy; - cdxbdy = cdx * bdy; - alift = adx * adx + ady * ady; - - cdxady = cdx * ady; - adxcdy = adx * cdy; - blift = bdx * bdx + bdy * bdy; - - adxbdy = adx * bdy; - bdxady = bdx * ady; - clift = cdx * cdx + cdy * cdy; - - det = alift * (bdxcdy - cdxbdy) - + blift * (cdxady - adxcdy) - + clift * (adxbdy - bdxady); - - if (b->noexact) { - return det; - } - - permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift - + (Absolute(cdxady) + Absolute(adxcdy)) * blift - + (Absolute(adxbdy) + Absolute(bdxady)) * clift; - errbound = iccerrboundA * permanent; - if ((det > errbound) || (-det > errbound)) { - return det; - } - - return incircleadapt(pa, pb, pc, pd, permanent); -} - -/*****************************************************************************/ -/* */ -/* orient3d() Return a positive value if the point pd lies below the */ -/* plane passing through pa, pb, and pc; "below" is defined so */ -/* that pa, pb, and pc appear in counterclockwise order when */ -/* viewed from above the plane. Returns a negative value if */ -/* pd lies above the plane. Returns zero if the points are */ -/* coplanar. The result is also a rough approximation of six */ -/* times the signed volume of the tetrahedron defined by the */ -/* four points. */ -/* */ -/* Uses exact arithmetic if necessary to ensure a correct answer. The */ -/* result returned is the determinant of a matrix. This determinant is */ -/* computed adaptively, in the sense that exact arithmetic is used only to */ -/* the degree it is needed to ensure that the returned value has the */ -/* correct sign. Hence, this function is usually quite fast, but will run */ -/* more slowly when the input points are coplanar or nearly so. */ -/* */ -/* See my Robust Predicates paper for details. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -REAL orient3dadapt(vertex pa, vertex pb, vertex pc, vertex pd, - REAL aheight, REAL bheight, REAL cheight, REAL dheight, - REAL permanent) -#else /* not ANSI_DECLARATORS */ -REAL orient3dadapt(pa, pb, pc, pd, - aheight, bheight, cheight, dheight, permanent) -vertex pa; -vertex pb; -vertex pc; -vertex pd; -REAL aheight; -REAL bheight; -REAL cheight; -REAL dheight; -REAL permanent; -#endif /* not ANSI_DECLARATORS */ - -{ - INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight; - REAL det, errbound; - - INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; - REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; - REAL bc[4], ca[4], ab[4]; - INEXACT REAL bc3, ca3, ab3; - REAL adet[8], bdet[8], cdet[8]; - int alen, blen, clen; - REAL abdet[16]; - int ablen; - REAL *finnow, *finother, *finswap; - REAL fin1[192], fin2[192]; - int finlength; - - REAL adxtail, bdxtail, cdxtail; - REAL adytail, bdytail, cdytail; - REAL adheighttail, bdheighttail, cdheighttail; - INEXACT REAL at_blarge, at_clarge; - INEXACT REAL bt_clarge, bt_alarge; - INEXACT REAL ct_alarge, ct_blarge; - REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4]; - int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen; - INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1; - INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1; - REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0; - REAL adxt_cdy0, adxt_bdy0, bdxt_ady0; - INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1; - INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1; - REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0; - REAL adyt_cdx0, adyt_bdx0, bdyt_adx0; - REAL bct[8], cat[8], abt[8]; - int bctlen, catlen, abtlen; - INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1; - INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1; - REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0; - REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0; - REAL u[4], v[12], w[16]; - INEXACT REAL u3; - int vlength, wlength; - REAL negate; - - INEXACT REAL bvirt; - REAL avirt, bround, around; - INEXACT REAL c; - INEXACT REAL abig; - REAL ahi, alo, bhi, blo; - REAL err1, err2, err3; - INEXACT REAL _i, _j, _k; - REAL _0; - - adx = (REAL) (pa[0] - pd[0]); - bdx = (REAL) (pb[0] - pd[0]); - cdx = (REAL) (pc[0] - pd[0]); - ady = (REAL) (pa[1] - pd[1]); - bdy = (REAL) (pb[1] - pd[1]); - cdy = (REAL) (pc[1] - pd[1]); - adheight = (REAL) (aheight - dheight); - bdheight = (REAL) (bheight - dheight); - cdheight = (REAL) (cheight - dheight); - - Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); - Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); - Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); - bc[3] = bc3; - alen = scale_expansion_zeroelim(4, bc, adheight, adet); - - Two_Product(cdx, ady, cdxady1, cdxady0); - Two_Product(adx, cdy, adxcdy1, adxcdy0); - Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); - ca[3] = ca3; - blen = scale_expansion_zeroelim(4, ca, bdheight, bdet); - - Two_Product(adx, bdy, adxbdy1, adxbdy0); - Two_Product(bdx, ady, bdxady1, bdxady0); - Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); - ab[3] = ab3; - clen = scale_expansion_zeroelim(4, ab, cdheight, cdet); - - ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); - finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); - - det = estimate(finlength, fin1); - errbound = o3derrboundB * permanent; - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - Two_Diff_Tail(pa[0], pd[0], adx, adxtail); - Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); - Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); - Two_Diff_Tail(pa[1], pd[1], ady, adytail); - Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); - Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); - Two_Diff_Tail(aheight, dheight, adheight, adheighttail); - Two_Diff_Tail(bheight, dheight, bdheight, bdheighttail); - Two_Diff_Tail(cheight, dheight, cdheight, cdheighttail); - - if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && - (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0) && - (adheighttail == 0.0) && - (bdheighttail == 0.0) && - (cdheighttail == 0.0)) { - return det; - } - - errbound = o3derrboundC * permanent + resulterrbound * Absolute(det); - det += (adheight * ((bdx * cdytail + cdy * bdxtail) - - (bdy * cdxtail + cdx * bdytail)) + - adheighttail * (bdx * cdy - bdy * cdx)) + - (bdheight * ((cdx * adytail + ady * cdxtail) - - (cdy * adxtail + adx * cdytail)) + - bdheighttail * (cdx * ady - cdy * adx)) + - (cdheight * ((adx * bdytail + bdy * adxtail) - - (ady * bdxtail + bdx * adytail)) + - cdheighttail * (adx * bdy - ady * bdx)); - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - finnow = fin1; - finother = fin2; - - if (adxtail == 0.0) { - if (adytail == 0.0) { - at_b[0] = 0.0; - at_blen = 1; - at_c[0] = 0.0; - at_clen = 1; - } else { - negate = -adytail; - Two_Product(negate, bdx, at_blarge, at_b[0]); - at_b[1] = at_blarge; - at_blen = 2; - Two_Product(adytail, cdx, at_clarge, at_c[0]); - at_c[1] = at_clarge; - at_clen = 2; - } - } else { - if (adytail == 0.0) { - Two_Product(adxtail, bdy, at_blarge, at_b[0]); - at_b[1] = at_blarge; - at_blen = 2; - negate = -adxtail; - Two_Product(negate, cdy, at_clarge, at_c[0]); - at_c[1] = at_clarge; - at_clen = 2; - } else { - Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0); - Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0); - Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0, - at_blarge, at_b[2], at_b[1], at_b[0]); - at_b[3] = at_blarge; - at_blen = 4; - Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0); - Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0); - Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0, - at_clarge, at_c[2], at_c[1], at_c[0]); - at_c[3] = at_clarge; - at_clen = 4; - } - } - if (bdxtail == 0.0) { - if (bdytail == 0.0) { - bt_c[0] = 0.0; - bt_clen = 1; - bt_a[0] = 0.0; - bt_alen = 1; - } else { - negate = -bdytail; - Two_Product(negate, cdx, bt_clarge, bt_c[0]); - bt_c[1] = bt_clarge; - bt_clen = 2; - Two_Product(bdytail, adx, bt_alarge, bt_a[0]); - bt_a[1] = bt_alarge; - bt_alen = 2; - } - } else { - if (bdytail == 0.0) { - Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]); - bt_c[1] = bt_clarge; - bt_clen = 2; - negate = -bdxtail; - Two_Product(negate, ady, bt_alarge, bt_a[0]); - bt_a[1] = bt_alarge; - bt_alen = 2; - } else { - Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0); - Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0); - Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0, - bt_clarge, bt_c[2], bt_c[1], bt_c[0]); - bt_c[3] = bt_clarge; - bt_clen = 4; - Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0); - Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0); - Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0, - bt_alarge, bt_a[2], bt_a[1], bt_a[0]); - bt_a[3] = bt_alarge; - bt_alen = 4; - } - } - if (cdxtail == 0.0) { - if (cdytail == 0.0) { - ct_a[0] = 0.0; - ct_alen = 1; - ct_b[0] = 0.0; - ct_blen = 1; - } else { - negate = -cdytail; - Two_Product(negate, adx, ct_alarge, ct_a[0]); - ct_a[1] = ct_alarge; - ct_alen = 2; - Two_Product(cdytail, bdx, ct_blarge, ct_b[0]); - ct_b[1] = ct_blarge; - ct_blen = 2; - } - } else { - if (cdytail == 0.0) { - Two_Product(cdxtail, ady, ct_alarge, ct_a[0]); - ct_a[1] = ct_alarge; - ct_alen = 2; - negate = -cdxtail; - Two_Product(negate, bdy, ct_blarge, ct_b[0]); - ct_b[1] = ct_blarge; - ct_blen = 2; - } else { - Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0); - Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0); - Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0, - ct_alarge, ct_a[2], ct_a[1], ct_a[0]); - ct_a[3] = ct_alarge; - ct_alen = 4; - Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0); - Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0); - Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0, - ct_blarge, ct_b[2], ct_b[1], ct_b[0]); - ct_b[3] = ct_blarge; - ct_blen = 4; - } - } - - bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct); - wlength = scale_expansion_zeroelim(bctlen, bct, adheight, w); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, - finother); - finswap = finnow; finnow = finother; finother = finswap; - - catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat); - wlength = scale_expansion_zeroelim(catlen, cat, bdheight, w); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, - finother); - finswap = finnow; finnow = finother; finother = finswap; - - abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt); - wlength = scale_expansion_zeroelim(abtlen, abt, cdheight, w); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, - finother); - finswap = finnow; finnow = finother; finother = finswap; - - if (adheighttail != 0.0) { - vlength = scale_expansion_zeroelim(4, bc, adheighttail, v); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (bdheighttail != 0.0) { - vlength = scale_expansion_zeroelim(4, ca, bdheighttail, v); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (cdheighttail != 0.0) { - vlength = scale_expansion_zeroelim(4, ab, cdheighttail, v); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - - if (adxtail != 0.0) { - if (bdytail != 0.0) { - Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0); - Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheight, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - if (cdheighttail != 0.0) { - Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheighttail, - u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - if (cdytail != 0.0) { - negate = -adxtail; - Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0); - Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheight, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - if (bdheighttail != 0.0) { - Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheighttail, - u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - } - if (bdxtail != 0.0) { - if (cdytail != 0.0) { - Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0); - Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheight, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - if (adheighttail != 0.0) { - Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheighttail, - u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - if (adytail != 0.0) { - negate = -bdxtail; - Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0); - Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheight, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - if (cdheighttail != 0.0) { - Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheighttail, - u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - } - if (cdxtail != 0.0) { - if (adytail != 0.0) { - Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0); - Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheight, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - if (bdheighttail != 0.0) { - Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheighttail, - u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - if (bdytail != 0.0) { - negate = -cdxtail; - Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0); - Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheight, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - if (adheighttail != 0.0) { - Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheighttail, - u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - } - - if (adheighttail != 0.0) { - wlength = scale_expansion_zeroelim(bctlen, bct, adheighttail, w); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (bdheighttail != 0.0) { - wlength = scale_expansion_zeroelim(catlen, cat, bdheighttail, w); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (cdheighttail != 0.0) { - wlength = scale_expansion_zeroelim(abtlen, abt, cdheighttail, w); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - - return finnow[finlength - 1]; -} - -#ifdef ANSI_DECLARATORS -REAL orient3d(struct mesh *m, struct behavior *b, - vertex pa, vertex pb, vertex pc, vertex pd, - REAL aheight, REAL bheight, REAL cheight, REAL dheight) -#else /* not ANSI_DECLARATORS */ -REAL orient3d(m, b, pa, pb, pc, pd, aheight, bheight, cheight, dheight) -struct mesh *m; -struct behavior *b; -vertex pa; -vertex pb; -vertex pc; -vertex pd; -REAL aheight; -REAL bheight; -REAL cheight; -REAL dheight; -#endif /* not ANSI_DECLARATORS */ - -{ - REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight; - REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; - REAL det; - REAL permanent, errbound; - - m->orient3dcount++; - - adx = pa[0] - pd[0]; - bdx = pb[0] - pd[0]; - cdx = pc[0] - pd[0]; - ady = pa[1] - pd[1]; - bdy = pb[1] - pd[1]; - cdy = pc[1] - pd[1]; - adheight = aheight - dheight; - bdheight = bheight - dheight; - cdheight = cheight - dheight; - - bdxcdy = bdx * cdy; - cdxbdy = cdx * bdy; - - cdxady = cdx * ady; - adxcdy = adx * cdy; - - adxbdy = adx * bdy; - bdxady = bdx * ady; - - det = adheight * (bdxcdy - cdxbdy) - + bdheight * (cdxady - adxcdy) - + cdheight * (adxbdy - bdxady); - - if (b->noexact) { - return det; - } - - permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adheight) - + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdheight) - + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdheight); - errbound = o3derrboundA * permanent; - if ((det > errbound) || (-det > errbound)) { - return det; - } - - return orient3dadapt(pa, pb, pc, pd, aheight, bheight, cheight, dheight, - permanent); -} - -/*****************************************************************************/ -/* */ -/* nonregular() Return a positive value if the point pd is incompatible */ -/* with the circle or plane passing through pa, pb, and pc */ -/* (meaning that pd is inside the circle or below the */ -/* plane); a negative value if it is compatible; and zero if */ -/* the four points are cocircular/coplanar. The points pa, */ -/* pb, and pc must be in counterclockwise order, or the sign */ -/* of the result will be reversed. */ -/* */ -/* If the -w switch is used, the points are lifted onto the parabolic */ -/* lifting map, then they are dropped according to their weights, then the */ -/* 3D orientation test is applied. If the -W switch is used, the points' */ -/* heights are already provided, so the 3D orientation test is applied */ -/* directly. If neither switch is used, the incircle test is applied. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -REAL nonregular(struct mesh *m, struct behavior *b, - vertex pa, vertex pb, vertex pc, vertex pd) -#else /* not ANSI_DECLARATORS */ -REAL nonregular(m, b, pa, pb, pc, pd) -struct mesh *m; -struct behavior *b; -vertex pa; -vertex pb; -vertex pc; -vertex pd; -#endif /* not ANSI_DECLARATORS */ - -{ - if (b->weighted == 0) { - return incircle(m, b, pa, pb, pc, pd); - } else if (b->weighted == 1) { - return orient3d(m, b, pa, pb, pc, pd, - pa[0] * pa[0] + pa[1] * pa[1] - pa[2], - pb[0] * pb[0] + pb[1] * pb[1] - pb[2], - pc[0] * pc[0] + pc[1] * pc[1] - pc[2], - pd[0] * pd[0] + pd[1] * pd[1] - pd[2]); - } else { - return orient3d(m, b, pa, pb, pc, pd, pa[2], pb[2], pc[2], pd[2]); - } -} - -/*****************************************************************************/ -/* */ -/* findcircumcenter() Find the circumcenter of a triangle. */ -/* */ -/* The result is returned both in terms of x-y coordinates and xi-eta */ -/* (barycentric) coordinates. The xi-eta coordinate system is defined in */ -/* terms of the triangle: the origin of the triangle is the origin of the */ -/* coordinate system; the destination of the triangle is one unit along the */ -/* xi axis; and the apex of the triangle is one unit along the eta axis. */ -/* This procedure also returns the square of the length of the triangle's */ -/* shortest edge. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void findcircumcenter(struct mesh *m, struct behavior *b, - vertex torg, vertex tdest, vertex tapex, - vertex circumcenter, REAL *xi, REAL *eta, int offcenter) -#else /* not ANSI_DECLARATORS */ -void findcircumcenter(m, b, torg, tdest, tapex, circumcenter, xi, eta, - offcenter) -struct mesh *m; -struct behavior *b; -vertex torg; -vertex tdest; -vertex tapex; -vertex circumcenter; -REAL *xi; -REAL *eta; -int offcenter; -#endif /* not ANSI_DECLARATORS */ - -{ - REAL xdo, ydo, xao, yao; - REAL dodist, aodist, dadist; - REAL denominator; - REAL dx, dy, dxoff, dyoff; - - m->circumcentercount++; - - /* Compute the circumcenter of the triangle. */ - xdo = tdest[0] - torg[0]; - ydo = tdest[1] - torg[1]; - xao = tapex[0] - torg[0]; - yao = tapex[1] - torg[1]; - dodist = xdo * xdo + ydo * ydo; - aodist = xao * xao + yao * yao; - dadist = (tdest[0] - tapex[0]) * (tdest[0] - tapex[0]) + - (tdest[1] - tapex[1]) * (tdest[1] - tapex[1]); - if (b->noexact) { - denominator = 0.5 / (xdo * yao - xao * ydo); - } else { - /* Use the counterclockwise() routine to ensure a positive (and */ - /* reasonably accurate) result, avoiding any possibility of */ - /* division by zero. */ - denominator = 0.5 / counterclockwise(m, b, tdest, tapex, torg); - /* Don't count the above as an orientation test. */ - m->counterclockcount--; - } - dx = (yao * dodist - ydo * aodist) * denominator; - dy = (xdo * aodist - xao * dodist) * denominator; - - /* Find the (squared) length of the triangle's shortest edge. This */ - /* serves as a conservative estimate of the insertion radius of the */ - /* circumcenter's parent. The estimate is used to ensure that */ - /* the algorithm terminates even if very small angles appear in */ - /* the input PSLG. */ - if ((dodist < aodist) && (dodist < dadist)) { - if (offcenter && (b->offconstant > 0.0)) { - /* Find the position of the off-center, as described by Alper Ungor. */ - dxoff = 0.5 * xdo - b->offconstant * ydo; - dyoff = 0.5 * ydo + b->offconstant * xdo; - /* If the off-center is closer to the origin than the */ - /* circumcenter, use the off-center instead. */ - if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) { - dx = dxoff; - dy = dyoff; - } - } - } else if (aodist < dadist) { - if (offcenter && (b->offconstant > 0.0)) { - dxoff = 0.5 * xao + b->offconstant * yao; - dyoff = 0.5 * yao - b->offconstant * xao; - /* If the off-center is closer to the origin than the */ - /* circumcenter, use the off-center instead. */ - if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) { - dx = dxoff; - dy = dyoff; - } - } - } else { - if (offcenter && (b->offconstant > 0.0)) { - dxoff = 0.5 * (tapex[0] - tdest[0]) - - b->offconstant * (tapex[1] - tdest[1]); - dyoff = 0.5 * (tapex[1] - tdest[1]) + - b->offconstant * (tapex[0] - tdest[0]); - /* If the off-center is closer to the destination than the */ - /* circumcenter, use the off-center instead. */ - if (dxoff * dxoff + dyoff * dyoff < - (dx - xdo) * (dx - xdo) + (dy - ydo) * (dy - ydo)) { - dx = xdo + dxoff; - dy = ydo + dyoff; - } - } - } - - circumcenter[0] = torg[0] + dx; - circumcenter[1] = torg[1] + dy; - - /* To interpolate vertex attributes for the new vertex inserted at */ - /* the circumcenter, define a coordinate system with a xi-axis, */ - /* directed from the triangle's origin to its destination, and */ - /* an eta-axis, directed from its origin to its apex. */ - /* Calculate the xi and eta coordinates of the circumcenter. */ - *xi = (yao * dx - xao * dy) * (2.0 * denominator); - *eta = (xdo * dy - ydo * dx) * (2.0 * denominator); -} - -/** **/ -/** **/ -/********* Geometric primitives end here *********/ - -/*****************************************************************************/ -/* */ -/* triangleinit() Initialize some variables. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void triangleinit(struct mesh *m) -#else /* not ANSI_DECLARATORS */ -void triangleinit(m) -struct mesh *m; -#endif /* not ANSI_DECLARATORS */ - -{ - poolzero(&m->vertices); - poolzero(&m->triangles); - poolzero(&m->subsegs); - poolzero(&m->viri); - poolzero(&m->badsubsegs); - poolzero(&m->badtriangles); - poolzero(&m->flipstackers); - poolzero(&m->splaynodes); - - m->recenttri.tri = (triangle *) NULL; /* No triangle has been visited yet. */ - m->undeads = 0; /* No eliminated input vertices yet. */ - m->samples = 1; /* Point location should take at least one sample. */ - m->checksegments = 0; /* There are no segments in the triangulation yet. */ - m->checkquality = 0; /* The quality triangulation stage has not begun. */ - m->incirclecount = m->counterclockcount = m->orient3dcount = 0; - m->hyperbolacount = m->circletopcount = m->circumcentercount = 0; - randomseed = 1; - - exactinit(); /* Initialize exact arithmetic constants. */ -} - -/*****************************************************************************/ -/* */ -/* randomnation() Generate a random number between 0 and `choices' - 1. */ -/* */ -/* This is a simple linear congruential random number generator. Hence, it */ -/* is a bad random number generator, but good enough for most randomized */ -/* geometric algorithms. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -unsigned long randomnation(unsigned int choices) -#else /* not ANSI_DECLARATORS */ -unsigned long randomnation(choices) -unsigned int choices; -#endif /* not ANSI_DECLARATORS */ - -{ - randomseed = (randomseed * 1366l + 150889l) % 714025l; - return randomseed / (714025l / choices + 1); -} - -/********* Mesh quality testing routines begin here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* checkmesh() Test the mesh for topological consistency. */ -/* */ -/*****************************************************************************/ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -void checkmesh(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void checkmesh(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri triangleloop; - struct otri oppotri, oppooppotri; - vertex triorg, tridest, triapex; - vertex oppoorg, oppodest; - int horrors; - int saveexact; - triangle ptr; /* Temporary variable used by sym(). */ - - /* Temporarily turn on exact arithmetic if it's off. */ - saveexact = b->noexact; - b->noexact = 0; - if (!b->quiet) { - printf(" Checking consistency of mesh...\n"); - } - horrors = 0; - /* Run through the list of triangles, checking each one. */ - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - while (triangleloop.tri != (triangle *) NULL) { - /* Check all three edges of the triangle. */ - for (triangleloop.orient = 0; triangleloop.orient < 3; - triangleloop.orient++) { - org(triangleloop, triorg); - dest(triangleloop, tridest); - if (triangleloop.orient == 0) { /* Only test for inversion once. */ - /* Test if the triangle is flat or inverted. */ - apex(triangleloop, triapex); - if (counterclockwise(m, b, triorg, tridest, triapex) <= 0.0) { - printf(" !! !! Inverted "); - printtriangle(m, b, &triangleloop); - horrors++; - } - } - /* Find the neighboring triangle on this edge. */ - sym(triangleloop, oppotri); - if (oppotri.tri != m->dummytri) { - /* Check that the triangle's neighbor knows it's a neighbor. */ - sym(oppotri, oppooppotri); - if ((triangleloop.tri != oppooppotri.tri) - || (triangleloop.orient != oppooppotri.orient)) { - printf(" !! !! Asymmetric triangle-triangle bond:\n"); - if (triangleloop.tri == oppooppotri.tri) { - printf(" (Right triangle, wrong orientation)\n"); - } - printf(" First "); - printtriangle(m, b, &triangleloop); - printf(" Second (nonreciprocating) "); - printtriangle(m, b, &oppotri); - horrors++; - } - /* Check that both triangles agree on the identities */ - /* of their shared vertices. */ - org(oppotri, oppoorg); - dest(oppotri, oppodest); - if ((triorg != oppodest) || (tridest != oppoorg)) { - printf(" !! !! Mismatched edge coordinates between two triangles:\n" - ); - printf(" First mismatched "); - printtriangle(m, b, &triangleloop); - printf(" Second mismatched "); - printtriangle(m, b, &oppotri); - horrors++; - } - } - } - triangleloop.tri = triangletraverse(m); - } - if (horrors == 0) { - if (!b->quiet) { - printf(" In my studied opinion, the mesh appears to be consistent.\n"); - } - } else if (horrors == 1) { - printf(" !! !! !! !! Precisely one festering wound discovered.\n"); - } else { - printf(" !! !! !! !! %d abominations witnessed.\n", horrors); - } - /* Restore the status of exact arithmetic. */ - b->noexact = saveexact; -} - -#endif /* not REDUCED */ - -/*****************************************************************************/ -/* */ -/* checkdelaunay() Ensure that the mesh is (constrained) Delaunay. */ -/* */ -/*****************************************************************************/ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -void checkdelaunay(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void checkdelaunay(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri triangleloop; - struct otri oppotri; - struct osub opposubseg; - vertex triorg, tridest, triapex; - vertex oppoapex; - int shouldbedelaunay; - int horrors; - int saveexact; - triangle ptr; /* Temporary variable used by sym(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - /* Temporarily turn on exact arithmetic if it's off. */ - saveexact = b->noexact; - b->noexact = 0; - if (!b->quiet) { - printf(" Checking Delaunay property of mesh...\n"); - } - horrors = 0; - /* Run through the list of triangles, checking each one. */ - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - while (triangleloop.tri != (triangle *) NULL) { - /* Check all three edges of the triangle. */ - for (triangleloop.orient = 0; triangleloop.orient < 3; - triangleloop.orient++) { - org(triangleloop, triorg); - dest(triangleloop, tridest); - apex(triangleloop, triapex); - sym(triangleloop, oppotri); - apex(oppotri, oppoapex); - /* Only test that the edge is locally Delaunay if there is an */ - /* adjoining triangle whose pointer is larger (to ensure that */ - /* each pair isn't tested twice). */ - shouldbedelaunay = (oppotri.tri != m->dummytri) && - !deadtri(oppotri.tri) && (triangleloop.tri < oppotri.tri) && - (triorg != m->infvertex1) && (triorg != m->infvertex2) && - (triorg != m->infvertex3) && - (tridest != m->infvertex1) && (tridest != m->infvertex2) && - (tridest != m->infvertex3) && - (triapex != m->infvertex1) && (triapex != m->infvertex2) && - (triapex != m->infvertex3) && - (oppoapex != m->infvertex1) && (oppoapex != m->infvertex2) && - (oppoapex != m->infvertex3); - if (m->checksegments && shouldbedelaunay) { - /* If a subsegment separates the triangles, then the edge is */ - /* constrained, so no local Delaunay test should be done. */ - tspivot(triangleloop, opposubseg); - if (opposubseg.ss != m->dummysub){ - shouldbedelaunay = 0; - } - } - if (shouldbedelaunay) { - if (nonregular(m, b, triorg, tridest, triapex, oppoapex) > 0.0) { - if (!b->weighted) { - printf(" !! !! Non-Delaunay pair of triangles:\n"); - printf(" First non-Delaunay "); - printtriangle(m, b, &triangleloop); - printf(" Second non-Delaunay "); - } else { - printf(" !! !! Non-regular pair of triangles:\n"); - printf(" First non-regular "); - printtriangle(m, b, &triangleloop); - printf(" Second non-regular "); - } - printtriangle(m, b, &oppotri); - horrors++; - } - } - } - triangleloop.tri = triangletraverse(m); - } - if (horrors == 0) { - if (!b->quiet) { - printf( - " By virtue of my perceptive intelligence, I declare the mesh Delaunay.\n"); - } - } else if (horrors == 1) { - printf( - " !! !! !! !! Precisely one terrifying transgression identified.\n"); - } else { - printf(" !! !! !! !! %d obscenities viewed with horror.\n", horrors); - } - /* Restore the status of exact arithmetic. */ - b->noexact = saveexact; -} - -#endif /* not REDUCED */ - -/*****************************************************************************/ -/* */ -/* enqueuebadtriang() Add a bad triangle data structure to the end of a */ -/* queue. */ -/* */ -/* The queue is actually a set of 4096 queues. I use multiple queues to */ -/* give priority to smaller angles. I originally implemented a heap, but */ -/* the queues are faster by a larger margin than I'd suspected. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -void enqueuebadtriang(struct mesh *m, struct behavior *b, - struct badtriang *badtri) -#else /* not ANSI_DECLARATORS */ -void enqueuebadtriang(m, b, badtri) -struct mesh *m; -struct behavior *b; -struct badtriang *badtri; -#endif /* not ANSI_DECLARATORS */ - -{ - REAL length, multiplier; - int exponent, expincrement; - int queuenumber; - int posexponent; - int i; - - if (b->verbose > 2) { - printf(" Queueing bad triangle:\n"); - printf(" (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", - badtri->triangorg[0], badtri->triangorg[1], - badtri->triangdest[0], badtri->triangdest[1], - badtri->triangapex[0], badtri->triangapex[1]); - } - - /* Determine the appropriate queue to put the bad triangle into. */ - /* Recall that the key is the square of its shortest edge length. */ - if (badtri->key >= 1.0) { - length = badtri->key; - posexponent = 1; - } else { - /* `badtri->key' is 2.0 to a negative exponent, so we'll record that */ - /* fact and use the reciprocal of `badtri->key', which is > 1.0. */ - length = 1.0 / badtri->key; - posexponent = 0; - } - /* `length' is approximately 2.0 to what exponent? The following code */ - /* determines the answer in time logarithmic in the exponent. */ - exponent = 0; - while (length > 2.0) { - /* Find an approximation by repeated squaring of two. */ - expincrement = 1; - multiplier = 0.5; - while (length * multiplier * multiplier > 1.0) { - expincrement *= 2; - multiplier *= multiplier; - } - /* Reduce the value of `length', then iterate if necessary. */ - exponent += expincrement; - length *= multiplier; - } - /* `length' is approximately squareroot(2.0) to what exponent? */ - exponent = 2.0 * exponent + (length > SQUAREROOTTWO); - /* `exponent' is now in the range 0...2047 for IEEE double precision. */ - /* Choose a queue in the range 0...4095. The shortest edges have the */ - /* highest priority (queue 4095). */ - if (posexponent) { - queuenumber = 2047 - exponent; - } else { - queuenumber = 2048 + exponent; - } - - /* Are we inserting into an empty queue? */ - if (m->queuefront[queuenumber] == (struct badtriang *) NULL) { - /* Yes, we are inserting into an empty queue. */ - /* Will this become the highest-priority queue? */ - if (queuenumber > m->firstnonemptyq) { - /* Yes, this is the highest-priority queue. */ - m->nextnonemptyq[queuenumber] = m->firstnonemptyq; - m->firstnonemptyq = queuenumber; - } else { - /* No, this is not the highest-priority queue. */ - /* Find the queue with next higher priority. */ - i = queuenumber + 1; - while (m->queuefront[i] == (struct badtriang *) NULL) { - i++; - } - /* Mark the newly nonempty queue as following a higher-priority queue. */ - m->nextnonemptyq[queuenumber] = m->nextnonemptyq[i]; - m->nextnonemptyq[i] = queuenumber; - } - /* Put the bad triangle at the beginning of the (empty) queue. */ - m->queuefront[queuenumber] = badtri; - } else { - /* Add the bad triangle to the end of an already nonempty queue. */ - m->queuetail[queuenumber]->nexttriang = badtri; - } - /* Maintain a pointer to the last triangle of the queue. */ - m->queuetail[queuenumber] = badtri; - /* Newly enqueued bad triangle has no successor in the queue. */ - badtri->nexttriang = (struct badtriang *) NULL; -} - -#endif /* not CDT_ONLY */ - -/*****************************************************************************/ -/* */ -/* enqueuebadtri() Add a bad triangle to the end of a queue. */ -/* */ -/* Allocates a badtriang data structure for the triangle, then passes it to */ -/* enqueuebadtriang(). */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -void enqueuebadtri(struct mesh *m, struct behavior *b, struct otri *enqtri, - REAL minedge, vertex enqapex, vertex enqorg, vertex enqdest) -#else /* not ANSI_DECLARATORS */ -void enqueuebadtri(m, b, enqtri, minedge, enqapex, enqorg, enqdest) -struct mesh *m; -struct behavior *b; -struct otri *enqtri; -REAL minedge; -vertex enqapex; -vertex enqorg; -vertex enqdest; -#endif /* not ANSI_DECLARATORS */ - -{ - struct badtriang *newbad; - - /* Allocate space for the bad triangle. */ - newbad = (struct badtriang *) poolalloc(&m->badtriangles); - newbad->poortri = encode(*enqtri); - newbad->key = minedge; - newbad->triangapex = enqapex; - newbad->triangorg = enqorg; - newbad->triangdest = enqdest; - enqueuebadtriang(m, b, newbad); -} - -#endif /* not CDT_ONLY */ - -/*****************************************************************************/ -/* */ -/* dequeuebadtriang() Remove a triangle from the front of the queue. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -struct badtriang *dequeuebadtriang(struct mesh *m) -#else /* not ANSI_DECLARATORS */ -struct badtriang *dequeuebadtriang(m) -struct mesh *m; -#endif /* not ANSI_DECLARATORS */ - -{ - struct badtriang *result; - - /* If no queues are nonempty, return NULL. */ - if (m->firstnonemptyq < 0) { - return (struct badtriang *) NULL; - } - /* Find the first triangle of the highest-priority queue. */ - result = m->queuefront[m->firstnonemptyq]; - /* Remove the triangle from the queue. */ - m->queuefront[m->firstnonemptyq] = result->nexttriang; - /* If this queue is now empty, note the new highest-priority */ - /* nonempty queue. */ - if (result == m->queuetail[m->firstnonemptyq]) { - m->firstnonemptyq = m->nextnonemptyq[m->firstnonemptyq]; - } - return result; -} - -#endif /* not CDT_ONLY */ - -/*****************************************************************************/ -/* */ -/* checkseg4encroach() Check a subsegment to see if it is encroached; add */ -/* it to the list if it is. */ -/* */ -/* A subsegment is encroached if there is a vertex in its diametral lens. */ -/* For Ruppert's algorithm (-D switch), the "diametral lens" is the */ -/* diametral circle. For Chew's algorithm (default), the diametral lens is */ -/* just big enough to enclose two isosceles triangles whose bases are the */ -/* subsegment. Each of the two isosceles triangles has two angles equal */ -/* to `b->minangle'. */ -/* */ -/* Chew's algorithm does not require diametral lenses at all--but they save */ -/* time. Any vertex inside a subsegment's diametral lens implies that the */ -/* triangle adjoining the subsegment will be too skinny, so it's only a */ -/* matter of time before the encroaching vertex is deleted by Chew's */ -/* algorithm. It's faster to simply not insert the doomed vertex in the */ -/* first place, which is why I use diametral lenses with Chew's algorithm. */ -/* */ -/* Returns a nonzero value if the subsegment is encroached. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -int checkseg4encroach(struct mesh *m, struct behavior *b, - struct osub *testsubseg) -#else /* not ANSI_DECLARATORS */ -int checkseg4encroach(m, b, testsubseg) -struct mesh *m; -struct behavior *b; -struct osub *testsubseg; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri neighbortri; - struct osub testsym; - struct badsubseg *encroachedseg; - REAL dotproduct; - int encroached; - int sides; - vertex eorg, edest, eapex; - triangle ptr; /* Temporary variable used by stpivot(). */ - - encroached = 0; - sides = 0; - - sorg(*testsubseg, eorg); - sdest(*testsubseg, edest); - /* Check one neighbor of the subsegment. */ - stpivot(*testsubseg, neighbortri); - /* Does the neighbor exist, or is this a boundary edge? */ - if (neighbortri.tri != m->dummytri) { - sides++; - /* Find a vertex opposite this subsegment. */ - apex(neighbortri, eapex); - /* Check whether the apex is in the diametral lens of the subsegment */ - /* (the diametral circle if `conformdel' is set). A dot product */ - /* of two sides of the triangle is used to check whether the angle */ - /* at the apex is greater than (180 - 2 `minangle') degrees (for */ - /* lenses; 90 degrees for diametral circles). */ - dotproduct = (eorg[0] - eapex[0]) * (edest[0] - eapex[0]) + - (eorg[1] - eapex[1]) * (edest[1] - eapex[1]); - if (dotproduct < 0.0) { - if (b->conformdel || - (dotproduct * dotproduct >= - (2.0 * b->goodangle - 1.0) * (2.0 * b->goodangle - 1.0) * - ((eorg[0] - eapex[0]) * (eorg[0] - eapex[0]) + - (eorg[1] - eapex[1]) * (eorg[1] - eapex[1])) * - ((edest[0] - eapex[0]) * (edest[0] - eapex[0]) + - (edest[1] - eapex[1]) * (edest[1] - eapex[1])))) { - encroached = 1; - } - } - } - /* Check the other neighbor of the subsegment. */ - ssym(*testsubseg, testsym); - stpivot(testsym, neighbortri); - /* Does the neighbor exist, or is this a boundary edge? */ - if (neighbortri.tri != m->dummytri) { - sides++; - /* Find the other vertex opposite this subsegment. */ - apex(neighbortri, eapex); - /* Check whether the apex is in the diametral lens of the subsegment */ - /* (or the diametral circle, if `conformdel' is set). */ - dotproduct = (eorg[0] - eapex[0]) * (edest[0] - eapex[0]) + - (eorg[1] - eapex[1]) * (edest[1] - eapex[1]); - if (dotproduct < 0.0) { - if (b->conformdel || - (dotproduct * dotproduct >= - (2.0 * b->goodangle - 1.0) * (2.0 * b->goodangle - 1.0) * - ((eorg[0] - eapex[0]) * (eorg[0] - eapex[0]) + - (eorg[1] - eapex[1]) * (eorg[1] - eapex[1])) * - ((edest[0] - eapex[0]) * (edest[0] - eapex[0]) + - (edest[1] - eapex[1]) * (edest[1] - eapex[1])))) { - encroached += 2; - } - } - } - - if (encroached && (!b->nobisect || ((b->nobisect == 1) && (sides == 2)))) { - if (b->verbose > 2) { - printf( - " Queueing encroached subsegment (%.12g, %.12g) (%.12g, %.12g).\n", - eorg[0], eorg[1], edest[0], edest[1]); - } - /* Add the subsegment to the list of encroached subsegments. */ - /* Be sure to get the orientation right. */ - encroachedseg = (struct badsubseg *) poolalloc(&m->badsubsegs); - if (encroached == 1) { - encroachedseg->encsubseg = sencode(*testsubseg); - encroachedseg->subsegorg = eorg; - encroachedseg->subsegdest = edest; - } else { - encroachedseg->encsubseg = sencode(testsym); - encroachedseg->subsegorg = edest; - encroachedseg->subsegdest = eorg; - } - } - - return encroached; -} - -#endif /* not CDT_ONLY */ - -/*****************************************************************************/ -/* */ -/* testtriangle() Test a triangle for quality and size. */ -/* */ -/* Tests a triangle to see if it satisfies the minimum angle condition and */ -/* the maximum area condition. Triangles that aren't up to spec are added */ -/* to the bad triangle queue. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -void testtriangle(struct mesh *m, struct behavior *b, struct otri *testtri) -#else /* not ANSI_DECLARATORS */ -void testtriangle(m, b, testtri) -struct mesh *m; -struct behavior *b; -struct otri *testtri; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri tri1, tri2; - struct osub testsub; - vertex torg, tdest, tapex; - vertex base1, base2; - vertex org1, dest1, org2, dest2; - vertex joinvertex; - REAL dxod, dyod, dxda, dyda, dxao, dyao; - REAL dxod2, dyod2, dxda2, dyda2, dxao2, dyao2; - REAL apexlen, orglen, destlen, minedge; - REAL angle; - REAL area; - REAL dist1, dist2; - subseg sptr; /* Temporary variable used by tspivot(). */ - triangle ptr; /* Temporary variable used by oprev() and dnext(). */ - - org(*testtri, torg); - dest(*testtri, tdest); - apex(*testtri, tapex); - dxod = torg[0] - tdest[0]; - dyod = torg[1] - tdest[1]; - dxda = tdest[0] - tapex[0]; - dyda = tdest[1] - tapex[1]; - dxao = tapex[0] - torg[0]; - dyao = tapex[1] - torg[1]; - dxod2 = dxod * dxod; - dyod2 = dyod * dyod; - dxda2 = dxda * dxda; - dyda2 = dyda * dyda; - dxao2 = dxao * dxao; - dyao2 = dyao * dyao; - /* Find the lengths of the triangle's three edges. */ - apexlen = dxod2 + dyod2; - orglen = dxda2 + dyda2; - destlen = dxao2 + dyao2; - - if ((apexlen < orglen) && (apexlen < destlen)) { - /* The edge opposite the apex is shortest. */ - minedge = apexlen; - /* Find the square of the cosine of the angle at the apex. */ - angle = dxda * dxao + dyda * dyao; - angle = angle * angle / (orglen * destlen); - base1 = torg; - base2 = tdest; - otricopy(*testtri, tri1); - } else if (orglen < destlen) { - /* The edge opposite the origin is shortest. */ - minedge = orglen; - /* Find the square of the cosine of the angle at the origin. */ - angle = dxod * dxao + dyod * dyao; - angle = angle * angle / (apexlen * destlen); - base1 = tdest; - base2 = tapex; - lnext(*testtri, tri1); - } else { - /* The edge opposite the destination is shortest. */ - minedge = destlen; - /* Find the square of the cosine of the angle at the destination. */ - angle = dxod * dxda + dyod * dyda; - angle = angle * angle / (apexlen * orglen); - base1 = tapex; - base2 = torg; - lprev(*testtri, tri1); - } - - if (b->vararea || b->fixedarea || b->usertest) { - /* Check whether the area is larger than permitted. */ - area = 0.5 * (dxod * dyda - dyod * dxda); - if (b->fixedarea && (area > b->maxarea)) { - /* Add this triangle to the list of bad triangles. */ - enqueuebadtri(m, b, testtri, minedge, tapex, torg, tdest); - return; - } - - /* Nonpositive area constraints are treated as unconstrained. */ - if ((b->vararea) && (area > areabound(*testtri)) && - (areabound(*testtri) > 0.0)) { - /* Add this triangle to the list of bad triangles. */ - enqueuebadtri(m, b, testtri, minedge, tapex, torg, tdest); - return; - } - - if (b->usertest) { - /* Check whether the user thinks this triangle is too large. */ - if (triunsuitable(torg, tdest, tapex, area)) { - enqueuebadtri(m, b, testtri, minedge, tapex, torg, tdest); - return; - } - } - } - - /* Check whether the angle is smaller than permitted. */ - if (angle > b->goodangle) { - /* Use the rules of Miller, Pav, and Walkington to decide that certain */ - /* triangles should not be split, even if they have bad angles. */ - /* A skinny triangle is not split if its shortest edge subtends a */ - /* small input angle, and both endpoints of the edge lie on a */ - /* concentric circular shell. For convenience, I make a small */ - /* adjustment to that rule: I check if the endpoints of the edge */ - /* both lie in segment interiors, equidistant from the apex where */ - /* the two segments meet. */ - /* First, check if both points lie in segment interiors. */ - if ((vertextype(base1) == SEGMENTVERTEX) && - (vertextype(base2) == SEGMENTVERTEX)) { - /* Check if both points lie in a common segment. If they do, the */ - /* skinny triangle is enqueued to be split as usual. */ - tspivot(tri1, testsub); - if (testsub.ss == m->dummysub) { - /* No common segment. Find a subsegment that contains `torg'. */ - otricopy(tri1, tri2); - do { - oprevself(tri1); - tspivot(tri1, testsub); - } while (testsub.ss == m->dummysub); - /* Find the endpoints of the containing segment. */ - segorg(testsub, org1); - segdest(testsub, dest1); - /* Find a subsegment that contains `tdest'. */ - do { - dnextself(tri2); - tspivot(tri2, testsub); - } while (testsub.ss == m->dummysub); - /* Find the endpoints of the containing segment. */ - segorg(testsub, org2); - segdest(testsub, dest2); - /* Check if the two containing segments have an endpoint in common. */ - joinvertex = (vertex) NULL; - if ((dest1[0] == org2[0]) && (dest1[1] == org2[1])) { - joinvertex = dest1; - } else if ((org1[0] == dest2[0]) && (org1[1] == dest2[1])) { - joinvertex = org1; - } - if (joinvertex != (vertex) NULL) { - /* Compute the distance from the common endpoint (of the two */ - /* segments) to each of the endpoints of the shortest edge. */ - dist1 = ((base1[0] - joinvertex[0]) * (base1[0] - joinvertex[0]) + - (base1[1] - joinvertex[1]) * (base1[1] - joinvertex[1])); - dist2 = ((base2[0] - joinvertex[0]) * (base2[0] - joinvertex[0]) + - (base2[1] - joinvertex[1]) * (base2[1] - joinvertex[1])); - /* If the two distances are equal, don't split the triangle. */ - if ((dist1 < 1.001 * dist2) && (dist1 > 0.999 * dist2)) { - /* Return now to avoid enqueueing the bad triangle. */ - return; - } - } - } - } - - /* Add this triangle to the list of bad triangles. */ - enqueuebadtri(m, b, testtri, minedge, tapex, torg, tdest); - } -} - -#endif /* not CDT_ONLY */ - -/** **/ -/** **/ -/********* Mesh quality testing routines end here *********/ - -/********* Point location routines begin here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* makevertexmap() Construct a mapping from vertices to triangles to */ -/* improve the speed of point location for segment */ -/* insertion. */ -/* */ -/* Traverses all the triangles, and provides each corner of each triangle */ -/* with a pointer to that triangle. Of course, pointers will be */ -/* overwritten by other pointers because (almost) each vertex is a corner */ -/* of several triangles, but in the end every vertex will point to some */ -/* triangle that contains it. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void makevertexmap(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void makevertexmap(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri triangleloop; - vertex triorg; - - if (b->verbose) { - printf(" Constructing mapping from vertices to triangles.\n"); - } - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - while (triangleloop.tri != (triangle *) NULL) { - /* Check all three vertices of the triangle. */ - for (triangleloop.orient = 0; triangleloop.orient < 3; - triangleloop.orient++) { - org(triangleloop, triorg); - setvertex2tri(triorg, encode(triangleloop)); - } - triangleloop.tri = triangletraverse(m); - } -} - -/*****************************************************************************/ -/* */ -/* preciselocate() Find a triangle or edge containing a given point. */ -/* */ -/* Begins its search from `searchtri'. It is important that `searchtri' */ -/* be a handle with the property that `searchpoint' is strictly to the left */ -/* of the edge denoted by `searchtri', or is collinear with that edge and */ -/* does not intersect that edge. (In particular, `searchpoint' should not */ -/* be the origin or destination of that edge.) */ -/* */ -/* These conditions are imposed because preciselocate() is normally used in */ -/* one of two situations: */ -/* */ -/* (1) To try to find the location to insert a new point. Normally, we */ -/* know an edge that the point is strictly to the left of. In the */ -/* incremental Delaunay algorithm, that edge is a bounding box edge. */ -/* In Ruppert's Delaunay refinement algorithm for quality meshing, */ -/* that edge is the shortest edge of the triangle whose circumcenter */ -/* is being inserted. */ -/* */ -/* (2) To try to find an existing point. In this case, any edge on the */ -/* convex hull is a good starting edge. You must screen out the */ -/* possibility that the vertex sought is an endpoint of the starting */ -/* edge before you call preciselocate(). */ -/* */ -/* On completion, `searchtri' is a triangle that contains `searchpoint'. */ -/* */ -/* This implementation differs from that given by Guibas and Stolfi. It */ -/* walks from triangle to triangle, crossing an edge only if `searchpoint' */ -/* is on the other side of the line containing that edge. After entering */ -/* a triangle, there are two edges by which one can leave that triangle. */ -/* If both edges are valid (`searchpoint' is on the other side of both */ -/* edges), one of the two is chosen by drawing a line perpendicular to */ -/* the entry edge (whose endpoints are `forg' and `fdest') passing through */ -/* `fapex'. Depending on which side of this perpendicular `searchpoint' */ -/* falls on, an exit edge is chosen. */ -/* */ -/* This implementation is empirically faster than the Guibas and Stolfi */ -/* point location routine (which I originally used), which tends to spiral */ -/* in toward its target. */ -/* */ -/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */ -/* is a handle whose origin is the existing vertex. */ -/* */ -/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */ -/* handle whose primary edge is the edge on which the point lies. */ -/* */ -/* Returns INTRIANGLE if the point lies strictly within a triangle. */ -/* `searchtri' is a handle on the triangle that contains the point. */ -/* */ -/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */ -/* handle whose primary edge the point is to the right of. This might */ -/* occur when the circumcenter of a triangle falls just slightly outside */ -/* the mesh due to floating-point roundoff error. It also occurs when */ -/* seeking a hole or region point that a foolish user has placed outside */ -/* the mesh. */ -/* */ -/* If `stopatsubsegment' is nonzero, the search will stop if it tries to */ -/* walk through a subsegment, and will return OUTSIDE. */ -/* */ -/* WARNING: This routine is designed for convex triangulations, and will */ -/* not generally work after the holes and concavities have been carved. */ -/* However, it can still be used to find the circumcenter of a triangle, as */ -/* long as the search is begun from the triangle in question. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -enum locateresult preciselocate(struct mesh *m, struct behavior *b, - vertex searchpoint, struct otri *searchtri, - int stopatsubsegment) -#else /* not ANSI_DECLARATORS */ -enum locateresult preciselocate(m, b, searchpoint, searchtri, stopatsubsegment) -struct mesh *m; -struct behavior *b; -vertex searchpoint; -struct otri *searchtri; -int stopatsubsegment; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri backtracktri; - struct osub checkedge; - vertex forg, fdest, fapex; - REAL orgorient, destorient; - int moveleft; - triangle ptr; /* Temporary variable used by sym(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - if (b->verbose > 2) { - printf(" Searching for point (%.12g, %.12g).\n", - searchpoint[0], searchpoint[1]); - } - /* Where are we? */ - org(*searchtri, forg); - dest(*searchtri, fdest); - apex(*searchtri, fapex); - while (1) { - if (b->verbose > 2) { - printf(" At (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", - forg[0], forg[1], fdest[0], fdest[1], fapex[0], fapex[1]); - } - /* Check whether the apex is the point we seek. */ - if ((fapex[0] == searchpoint[0]) && (fapex[1] == searchpoint[1])) { - lprevself(*searchtri); - return ONVERTEX; - } - /* Does the point lie on the other side of the line defined by the */ - /* triangle edge opposite the triangle's destination? */ - destorient = counterclockwise(m, b, forg, fapex, searchpoint); - /* Does the point lie on the other side of the line defined by the */ - /* triangle edge opposite the triangle's origin? */ - orgorient = counterclockwise(m, b, fapex, fdest, searchpoint); - if (destorient > 0.0) { - if (orgorient > 0.0) { - /* Move left if the inner product of (fapex - searchpoint) and */ - /* (fdest - forg) is positive. This is equivalent to drawing */ - /* a line perpendicular to the line (forg, fdest) and passing */ - /* through `fapex', and determining which side of this line */ - /* `searchpoint' falls on. */ - moveleft = (fapex[0] - searchpoint[0]) * (fdest[0] - forg[0]) + - (fapex[1] - searchpoint[1]) * (fdest[1] - forg[1]) > 0.0; - } else { - moveleft = 1; - } - } else { - if (orgorient > 0.0) { - moveleft = 0; - } else { - /* The point we seek must be on the boundary of or inside this */ - /* triangle. */ - if (destorient == 0.0) { - lprevself(*searchtri); - return ONEDGE; - } - if (orgorient == 0.0) { - lnextself(*searchtri); - return ONEDGE; - } - return INTRIANGLE; - } - } - - /* Move to another triangle. Leave a trace `backtracktri' in case */ - /* floating-point roundoff or some such bogey causes us to walk */ - /* off a boundary of the triangulation. */ - if (moveleft) { - lprev(*searchtri, backtracktri); - fdest = fapex; - } else { - lnext(*searchtri, backtracktri); - forg = fapex; - } - sym(backtracktri, *searchtri); - - if (m->checksegments && stopatsubsegment) { - /* Check for walking through a subsegment. */ - tspivot(backtracktri, checkedge); - if (checkedge.ss != m->dummysub) { - /* Go back to the last triangle. */ - otricopy(backtracktri, *searchtri); - return OUTSIDE; - } - } - /* Check for walking right out of the triangulation. */ - if (searchtri->tri == m->dummytri) { - /* Go back to the last triangle. */ - otricopy(backtracktri, *searchtri); - return OUTSIDE; - } - - apex(*searchtri, fapex); - } -} - -/*****************************************************************************/ -/* */ -/* locate() Find a triangle or edge containing a given point. */ -/* */ -/* Searching begins from one of: the input `searchtri', a recently */ -/* encountered triangle `recenttri', or from a triangle chosen from a */ -/* random sample. The choice is made by determining which triangle's */ -/* origin is closest to the point we are searching for. Normally, */ -/* `searchtri' should be a handle on the convex hull of the triangulation. */ -/* */ -/* Details on the random sampling method can be found in the Mucke, Saias, */ -/* and Zhu paper cited in the header of this code. */ -/* */ -/* On completion, `searchtri' is a triangle that contains `searchpoint'. */ -/* */ -/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */ -/* is a handle whose origin is the existing vertex. */ -/* */ -/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */ -/* handle whose primary edge is the edge on which the point lies. */ -/* */ -/* Returns INTRIANGLE if the point lies strictly within a triangle. */ -/* `searchtri' is a handle on the triangle that contains the point. */ -/* */ -/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */ -/* handle whose primary edge the point is to the right of. This might */ -/* occur when the circumcenter of a triangle falls just slightly outside */ -/* the mesh due to floating-point roundoff error. It also occurs when */ -/* seeking a hole or region point that a foolish user has placed outside */ -/* the mesh. */ -/* */ -/* WARNING: This routine is designed for convex triangulations, and will */ -/* not generally work after the holes and concavities have been carved. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -enum locateresult locate(struct mesh *m, struct behavior *b, - vertex searchpoint, struct otri *searchtri) -#else /* not ANSI_DECLARATORS */ -enum locateresult locate(m, b, searchpoint, searchtri) -struct mesh *m; -struct behavior *b; -vertex searchpoint; -struct otri *searchtri; -#endif /* not ANSI_DECLARATORS */ - -{ - VOID **sampleblock; - char *firsttri; - struct otri sampletri; - vertex torg, tdest; - unsigned long alignptr; - REAL searchdist, dist; - REAL ahead; - long samplesperblock, totalsamplesleft, samplesleft; - long population, totalpopulation; - triangle ptr; /* Temporary variable used by sym(). */ - - if (b->verbose > 2) { - printf(" Randomly sampling for a triangle near point (%.12g, %.12g).\n", - searchpoint[0], searchpoint[1]); - } - /* Record the distance from the suggested starting triangle to the */ - /* point we seek. */ - org(*searchtri, torg); - searchdist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + - (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); - if (b->verbose > 2) { - printf(" Boundary triangle has origin (%.12g, %.12g).\n", - torg[0], torg[1]); - } - - /* If a recently encountered triangle has been recorded and has not been */ - /* deallocated, test it as a good starting point. */ - if (m->recenttri.tri != (triangle *) NULL) { - if (!deadtri(m->recenttri.tri)) { - org(m->recenttri, torg); - if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) { - otricopy(m->recenttri, *searchtri); - return ONVERTEX; - } - dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + - (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); - if (dist < searchdist) { - otricopy(m->recenttri, *searchtri); - searchdist = dist; - if (b->verbose > 2) { - printf(" Choosing recent triangle with origin (%.12g, %.12g).\n", - torg[0], torg[1]); - } - } - } - } - - /* The number of random samples taken is proportional to the cube root of */ - /* the number of triangles in the mesh. The next bit of code assumes */ - /* that the number of triangles increases monotonically (or at least */ - /* doesn't decrease enough to matter). */ - while (SAMPLEFACTOR * m->samples * m->samples * m->samples < - m->triangles.items) { - m->samples++; - } - - /* We'll draw ceiling(samples * TRIPERBLOCK / maxitems) random samples */ - /* from each block of triangles (except the first)--until we meet the */ - /* sample quota. The ceiling means that blocks at the end might be */ - /* neglected, but I don't care. */ - samplesperblock = (m->samples * TRIPERBLOCK - 1) / m->triangles.maxitems + 1; - /* We'll draw ceiling(samples * itemsfirstblock / maxitems) random samples */ - /* from the first block of triangles. */ - samplesleft = (m->samples * m->triangles.itemsfirstblock - 1) / - m->triangles.maxitems + 1; - totalsamplesleft = m->samples; - population = m->triangles.itemsfirstblock; - totalpopulation = m->triangles.maxitems; - sampleblock = m->triangles.firstblock; - sampletri.orient = 0; - while (totalsamplesleft > 0) { - /* If we're in the last block, `population' needs to be corrected. */ - if (population > totalpopulation) { - population = totalpopulation; - } - /* Find a pointer to the first triangle in the block. */ - alignptr = (unsigned long) (sampleblock + 1); - firsttri = (char *) (alignptr + - (unsigned long) m->triangles.alignbytes - - (alignptr % - (unsigned long) m->triangles.alignbytes)); - - /* Choose `samplesleft' randomly sampled triangles in this block. */ - do { - sampletri.tri = (triangle *) (firsttri + - (randomnation((unsigned int) population) * - m->triangles.itembytes)); - if (!deadtri(sampletri.tri)) { - org(sampletri, torg); - dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + - (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); - if (dist < searchdist) { - otricopy(sampletri, *searchtri); - searchdist = dist; - if (b->verbose > 2) { - printf(" Choosing triangle with origin (%.12g, %.12g).\n", - torg[0], torg[1]); - } - } - } - - samplesleft--; - totalsamplesleft--; - } while ((samplesleft > 0) && (totalsamplesleft > 0)); - - if (totalsamplesleft > 0) { - sampleblock = (VOID **) *sampleblock; - samplesleft = samplesperblock; - totalpopulation -= population; - population = TRIPERBLOCK; - } - } - - /* Where are we? */ - org(*searchtri, torg); - dest(*searchtri, tdest); - /* Check the starting triangle's vertices. */ - if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) { - return ONVERTEX; - } - if ((tdest[0] == searchpoint[0]) && (tdest[1] == searchpoint[1])) { - lnextself(*searchtri); - return ONVERTEX; - } - /* Orient `searchtri' to fit the preconditions of calling preciselocate(). */ - ahead = counterclockwise(m, b, torg, tdest, searchpoint); - if (ahead < 0.0) { - /* Turn around so that `searchpoint' is to the left of the */ - /* edge specified by `searchtri'. */ - symself(*searchtri); - } else if (ahead == 0.0) { - /* Check if `searchpoint' is between `torg' and `tdest'. */ - if (((torg[0] < searchpoint[0]) == (searchpoint[0] < tdest[0])) && - ((torg[1] < searchpoint[1]) == (searchpoint[1] < tdest[1]))) { - return ONEDGE; - } - } - return preciselocate(m, b, searchpoint, searchtri, 0); -} - -/** **/ -/** **/ -/********* Point location routines end here *********/ - -/********* Mesh transformation routines begin here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* insertsubseg() Create a new subsegment and insert it between two */ -/* triangles. */ -/* */ -/* The new subsegment is inserted at the edge described by the handle */ -/* `tri'. Its vertices are properly initialized. The marker `subsegmark' */ -/* is applied to the subsegment and, if appropriate, its vertices. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void insertsubseg(struct mesh *m, struct behavior *b, struct otri *tri, - int subsegmark) -#else /* not ANSI_DECLARATORS */ -void insertsubseg(m, b, tri, subsegmark) -struct mesh *m; -struct behavior *b; -struct otri *tri; /* Edge at which to insert the new subsegment. */ -int subsegmark; /* Marker for the new subsegment. */ -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri oppotri; - struct osub newsubseg; - vertex triorg, tridest; - triangle ptr; /* Temporary variable used by sym(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - org(*tri, triorg); - dest(*tri, tridest); - /* Mark vertices if possible. */ - if (vertexmark(triorg) == 0) { - setvertexmark(triorg, subsegmark); - } - if (vertexmark(tridest) == 0) { - setvertexmark(tridest, subsegmark); - } - /* Check if there's already a subsegment here. */ - tspivot(*tri, newsubseg); - if (newsubseg.ss == m->dummysub) { - /* Make new subsegment and initialize its vertices. */ - makesubseg(m, &newsubseg); - setsorg(newsubseg, tridest); - setsdest(newsubseg, triorg); - setsegorg(newsubseg, tridest); - setsegdest(newsubseg, triorg); - /* Bond new subsegment to the two triangles it is sandwiched between. */ - /* Note that the facing triangle `oppotri' might be equal to */ - /* `dummytri' (outer space), but the new subsegment is bonded to it */ - /* all the same. */ - tsbond(*tri, newsubseg); - sym(*tri, oppotri); - ssymself(newsubseg); - tsbond(oppotri, newsubseg); - setmark(newsubseg, subsegmark); - if (b->verbose > 2) { - printf(" Inserting new "); - printsubseg(m, b, &newsubseg); - } - } else { - if (mark(newsubseg) == 0) { - setmark(newsubseg, subsegmark); - } - } -} - -/*****************************************************************************/ -/* */ -/* Terminology */ -/* */ -/* A "local transformation" replaces a small set of triangles with another */ -/* set of triangles. This may or may not involve inserting or deleting a */ -/* vertex. */ -/* */ -/* The term "casing" is used to describe the set of triangles that are */ -/* attached to the triangles being transformed, but are not transformed */ -/* themselves. Think of the casing as a fixed hollow structure inside */ -/* which all the action happens. A "casing" is only defined relative to */ -/* a single transformation; each occurrence of a transformation will */ -/* involve a different casing. */ -/* */ -/*****************************************************************************/ - -/*****************************************************************************/ -/* */ -/* flip() Transform two triangles to two different triangles by flipping */ -/* an edge counterclockwise within a quadrilateral. */ -/* */ -/* Imagine the original triangles, abc and bad, oriented so that the */ -/* shared edge ab lies in a horizontal plane, with the vertex b on the left */ -/* and the vertex a on the right. The vertex c lies below the edge, and */ -/* the vertex d lies above the edge. The `flipedge' handle holds the edge */ -/* ab of triangle abc, and is directed left, from vertex a to vertex b. */ -/* */ -/* The triangles abc and bad are deleted and replaced by the triangles cdb */ -/* and dca. The triangles that represent abc and bad are NOT deallocated; */ -/* they are reused for dca and cdb, respectively. Hence, any handles that */ -/* may have held the original triangles are still valid, although not */ -/* directed as they were before. */ -/* */ -/* Upon completion of this routine, the `flipedge' handle holds the edge */ -/* dc of triangle dca, and is directed down, from vertex d to vertex c. */ -/* (Hence, the two triangles have rotated counterclockwise.) */ -/* */ -/* WARNING: This transformation is geometrically valid only if the */ -/* quadrilateral adbc is convex. Furthermore, this transformation is */ -/* valid only if there is not a subsegment between the triangles abc and */ -/* bad. This routine does not check either of these preconditions, and */ -/* it is the responsibility of the calling routine to ensure that they are */ -/* met. If they are not, the streets shall be filled with wailing and */ -/* gnashing of teeth. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void flip(struct mesh *m, struct behavior *b, struct otri *flipedge) -#else /* not ANSI_DECLARATORS */ -void flip(m, b, flipedge) -struct mesh *m; -struct behavior *b; -struct otri *flipedge; /* Handle for the triangle abc. */ -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri botleft, botright; - struct otri topleft, topright; - struct otri top; - struct otri botlcasing, botrcasing; - struct otri toplcasing, toprcasing; - struct osub botlsubseg, botrsubseg; - struct osub toplsubseg, toprsubseg; - vertex leftvertex, rightvertex, botvertex; - vertex farvertex; - triangle ptr; /* Temporary variable used by sym(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - /* Identify the vertices of the quadrilateral. */ - org(*flipedge, rightvertex); - dest(*flipedge, leftvertex); - apex(*flipedge, botvertex); - sym(*flipedge, top); -#ifdef SELF_CHECK - if (top.tri == m->dummytri) { - printf("Internal error in flip(): Attempt to flip on boundary.\n"); - lnextself(*flipedge); - return; - } - if (m->checksegments) { - tspivot(*flipedge, toplsubseg); - if (toplsubseg.ss != m->dummysub) { - printf("Internal error in flip(): Attempt to flip a segment.\n"); - lnextself(*flipedge); - return; - } - } -#endif /* SELF_CHECK */ - apex(top, farvertex); - - /* Identify the casing of the quadrilateral. */ - lprev(top, topleft); - sym(topleft, toplcasing); - lnext(top, topright); - sym(topright, toprcasing); - lnext(*flipedge, botleft); - sym(botleft, botlcasing); - lprev(*flipedge, botright); - sym(botright, botrcasing); - /* Rotate the quadrilateral one-quarter turn counterclockwise. */ - bond(topleft, botlcasing); - bond(botleft, botrcasing); - bond(botright, toprcasing); - bond(topright, toplcasing); - - if (m->checksegments) { - /* Check for subsegments and rebond them to the quadrilateral. */ - tspivot(topleft, toplsubseg); - tspivot(botleft, botlsubseg); - tspivot(botright, botrsubseg); - tspivot(topright, toprsubseg); - if (toplsubseg.ss == m->dummysub) { - tsdissolve(topright); - } else { - tsbond(topright, toplsubseg); - } - if (botlsubseg.ss == m->dummysub) { - tsdissolve(topleft); - } else { - tsbond(topleft, botlsubseg); - } - if (botrsubseg.ss == m->dummysub) { - tsdissolve(botleft); - } else { - tsbond(botleft, botrsubseg); - } - if (toprsubseg.ss == m->dummysub) { - tsdissolve(botright); - } else { - tsbond(botright, toprsubseg); - } - } - - /* New vertex assignments for the rotated quadrilateral. */ - setorg(*flipedge, farvertex); - setdest(*flipedge, botvertex); - setapex(*flipedge, rightvertex); - setorg(top, botvertex); - setdest(top, farvertex); - setapex(top, leftvertex); - if (b->verbose > 2) { - printf(" Edge flip results in left "); - printtriangle(m, b, &top); - printf(" and right "); - printtriangle(m, b, flipedge); - } -} - -/*****************************************************************************/ -/* */ -/* unflip() Transform two triangles to two different triangles by */ -/* flipping an edge clockwise within a quadrilateral. Reverses */ -/* the flip() operation so that the data structures representing */ -/* the triangles are back where they were before the flip(). */ -/* */ -/* Imagine the original triangles, abc and bad, oriented so that the */ -/* shared edge ab lies in a horizontal plane, with the vertex b on the left */ -/* and the vertex a on the right. The vertex c lies below the edge, and */ -/* the vertex d lies above the edge. The `flipedge' handle holds the edge */ -/* ab of triangle abc, and is directed left, from vertex a to vertex b. */ -/* */ -/* The triangles abc and bad are deleted and replaced by the triangles cdb */ -/* and dca. The triangles that represent abc and bad are NOT deallocated; */ -/* they are reused for cdb and dca, respectively. Hence, any handles that */ -/* may have held the original triangles are still valid, although not */ -/* directed as they were before. */ -/* */ -/* Upon completion of this routine, the `flipedge' handle holds the edge */ -/* cd of triangle cdb, and is directed up, from vertex c to vertex d. */ -/* (Hence, the two triangles have rotated clockwise.) */ -/* */ -/* WARNING: This transformation is geometrically valid only if the */ -/* quadrilateral adbc is convex. Furthermore, this transformation is */ -/* valid only if there is not a subsegment between the triangles abc and */ -/* bad. This routine does not check either of these preconditions, and */ -/* it is the responsibility of the calling routine to ensure that they are */ -/* met. If they are not, the streets shall be filled with wailing and */ -/* gnashing of teeth. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void unflip(struct mesh *m, struct behavior *b, struct otri *flipedge) -#else /* not ANSI_DECLARATORS */ -void unflip(m, b, flipedge) -struct mesh *m; -struct behavior *b; -struct otri *flipedge; /* Handle for the triangle abc. */ -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri botleft, botright; - struct otri topleft, topright; - struct otri top; - struct otri botlcasing, botrcasing; - struct otri toplcasing, toprcasing; - struct osub botlsubseg, botrsubseg; - struct osub toplsubseg, toprsubseg; - vertex leftvertex, rightvertex, botvertex; - vertex farvertex; - triangle ptr; /* Temporary variable used by sym(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - /* Identify the vertices of the quadrilateral. */ - org(*flipedge, rightvertex); - dest(*flipedge, leftvertex); - apex(*flipedge, botvertex); - sym(*flipedge, top); -#ifdef SELF_CHECK - if (top.tri == m->dummytri) { - printf("Internal error in unflip(): Attempt to flip on boundary.\n"); - lnextself(*flipedge); - return; - } - if (m->checksegments) { - tspivot(*flipedge, toplsubseg); - if (toplsubseg.ss != m->dummysub) { - printf("Internal error in unflip(): Attempt to flip a subsegment.\n"); - lnextself(*flipedge); - return; - } - } -#endif /* SELF_CHECK */ - apex(top, farvertex); - - /* Identify the casing of the quadrilateral. */ - lprev(top, topleft); - sym(topleft, toplcasing); - lnext(top, topright); - sym(topright, toprcasing); - lnext(*flipedge, botleft); - sym(botleft, botlcasing); - lprev(*flipedge, botright); - sym(botright, botrcasing); - /* Rotate the quadrilateral one-quarter turn clockwise. */ - bond(topleft, toprcasing); - bond(botleft, toplcasing); - bond(botright, botlcasing); - bond(topright, botrcasing); - - if (m->checksegments) { - /* Check for subsegments and rebond them to the quadrilateral. */ - tspivot(topleft, toplsubseg); - tspivot(botleft, botlsubseg); - tspivot(botright, botrsubseg); - tspivot(topright, toprsubseg); - if (toplsubseg.ss == m->dummysub) { - tsdissolve(botleft); - } else { - tsbond(botleft, toplsubseg); - } - if (botlsubseg.ss == m->dummysub) { - tsdissolve(botright); - } else { - tsbond(botright, botlsubseg); - } - if (botrsubseg.ss == m->dummysub) { - tsdissolve(topright); - } else { - tsbond(topright, botrsubseg); - } - if (toprsubseg.ss == m->dummysub) { - tsdissolve(topleft); - } else { - tsbond(topleft, toprsubseg); - } - } - - /* New vertex assignments for the rotated quadrilateral. */ - setorg(*flipedge, botvertex); - setdest(*flipedge, farvertex); - setapex(*flipedge, leftvertex); - setorg(top, farvertex); - setdest(top, botvertex); - setapex(top, rightvertex); - if (b->verbose > 2) { - printf(" Edge unflip results in left "); - printtriangle(m, b, flipedge); - printf(" and right "); - printtriangle(m, b, &top); - } -} - -/*****************************************************************************/ -/* */ -/* insertvertex() Insert a vertex into a Delaunay triangulation, */ -/* performing flips as necessary to maintain the Delaunay */ -/* property. */ -/* */ -/* The point `insertvertex' is located. If `searchtri.tri' is not NULL, */ -/* the search for the containing triangle begins from `searchtri'. If */ -/* `searchtri.tri' is NULL, a full point location procedure is called. */ -/* If `insertvertex' is found inside a triangle, the triangle is split into */ -/* three; if `insertvertex' lies on an edge, the edge is split in two, */ -/* thereby splitting the two adjacent triangles into four. Edge flips are */ -/* used to restore the Delaunay property. If `insertvertex' lies on an */ -/* existing vertex, no action is taken, and the value DUPLICATEVERTEX is */ -/* returned. On return, `searchtri' is set to a handle whose origin is the */ -/* existing vertex. */ -/* */ -/* Normally, the parameter `splitseg' is set to NULL, implying that no */ -/* subsegment should be split. In this case, if `insertvertex' is found to */ -/* lie on a segment, no action is taken, and the value VIOLATINGVERTEX is */ -/* returned. On return, `searchtri' is set to a handle whose primary edge */ -/* is the violated subsegment. */ -/* */ -/* If the calling routine wishes to split a subsegment by inserting a */ -/* vertex in it, the parameter `splitseg' should be that subsegment. In */ -/* this case, `searchtri' MUST be the triangle handle reached by pivoting */ -/* from that subsegment; no point location is done. */ -/* */ -/* `segmentflaws' and `triflaws' are flags that indicate whether or not */ -/* there should be checks for the creation of encroached subsegments or bad */ -/* quality triangles. If a newly inserted vertex encroaches upon */ -/* subsegments, these subsegments are added to the list of subsegments to */ -/* be split if `segmentflaws' is set. If bad triangles are created, these */ -/* are added to the queue if `triflaws' is set. */ -/* */ -/* If a duplicate vertex or violated segment does not prevent the vertex */ -/* from being inserted, the return value will be ENCROACHINGVERTEX if the */ -/* vertex encroaches upon a subsegment (and checking is enabled), or */ -/* SUCCESSFULVERTEX otherwise. In either case, `searchtri' is set to a */ -/* handle whose origin is the newly inserted vertex. */ -/* */ -/* insertvertex() does not use flip() for reasons of speed; some */ -/* information can be reused from edge flip to edge flip, like the */ -/* locations of subsegments. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -enum insertvertexresult insertvertex(struct mesh *m, struct behavior *b, - vertex newvertex, struct otri *searchtri, - struct osub *splitseg, - int segmentflaws, int triflaws) -#else /* not ANSI_DECLARATORS */ -enum insertvertexresult insertvertex(m, b, newvertex, searchtri, splitseg, - segmentflaws, triflaws) -struct mesh *m; -struct behavior *b; -vertex newvertex; -struct otri *searchtri; -struct osub *splitseg; -int segmentflaws; -int triflaws; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri horiz; - struct otri top; - struct otri botleft, botright; - struct otri topleft, topright; - struct otri newbotleft, newbotright; - struct otri newtopright; - struct otri botlcasing, botrcasing; - struct otri toplcasing, toprcasing; - struct otri testtri; - struct osub botlsubseg, botrsubseg; - struct osub toplsubseg, toprsubseg; - struct osub brokensubseg; - struct osub checksubseg; - struct osub rightsubseg; - struct osub newsubseg; - struct badsubseg *encroached; - struct flipstacker *newflip; - vertex first; - vertex leftvertex, rightvertex, botvertex, topvertex, farvertex; - vertex segmentorg, segmentdest; - REAL attrib; - REAL area; - enum insertvertexresult success; - enum locateresult intersect; - int doflip; - int mirrorflag; - int enq; - int i; - triangle ptr; /* Temporary variable used by sym(). */ - subseg sptr; /* Temporary variable used by spivot() and tspivot(). */ - - if (b->verbose > 1) { - printf(" Inserting (%.12g, %.12g).\n", newvertex[0], newvertex[1]); - } - - if (splitseg == (struct osub *) NULL) { - /* Find the location of the vertex to be inserted. Check if a good */ - /* starting triangle has already been provided by the caller. */ - if (searchtri->tri == m->dummytri) { - /* Find a boundary triangle. */ - horiz.tri = m->dummytri; - horiz.orient = 0; - symself(horiz); - /* Search for a triangle containing `newvertex'. */ - intersect = locate(m, b, newvertex, &horiz); - } else { - /* Start searching from the triangle provided by the caller. */ - otricopy(*searchtri, horiz); - intersect = preciselocate(m, b, newvertex, &horiz, 1); - } - } else { - /* The calling routine provides the subsegment in which */ - /* the vertex is inserted. */ - otricopy(*searchtri, horiz); - intersect = ONEDGE; - } - - if (intersect == ONVERTEX) { - /* There's already a vertex there. Return in `searchtri' a triangle */ - /* whose origin is the existing vertex. */ - otricopy(horiz, *searchtri); - otricopy(horiz, m->recenttri); - return DUPLICATEVERTEX; - } - if ((intersect == ONEDGE) || (intersect == OUTSIDE)) { - /* The vertex falls on an edge or boundary. */ - if (m->checksegments && (splitseg == (struct osub *) NULL)) { - /* Check whether the vertex falls on a subsegment. */ - tspivot(horiz, brokensubseg); - if (brokensubseg.ss != m->dummysub) { - /* The vertex falls on a subsegment, and hence will not be inserted. */ - if (segmentflaws) { - enq = b->nobisect != 2; - if (enq && (b->nobisect == 1)) { - /* This subsegment may be split only if it is an */ - /* internal boundary. */ - sym(horiz, testtri); - enq = testtri.tri != m->dummytri; - } - if (enq) { - /* Add the subsegment to the list of encroached subsegments. */ - encroached = (struct badsubseg *) poolalloc(&m->badsubsegs); - encroached->encsubseg = sencode(brokensubseg); - sorg(brokensubseg, encroached->subsegorg); - sdest(brokensubseg, encroached->subsegdest); - if (b->verbose > 2) { - printf( - " Queueing encroached subsegment (%.12g, %.12g) (%.12g, %.12g).\n", - encroached->subsegorg[0], encroached->subsegorg[1], - encroached->subsegdest[0], encroached->subsegdest[1]); - } - } - } - /* Return a handle whose primary edge contains the vertex, */ - /* which has not been inserted. */ - otricopy(horiz, *searchtri); - otricopy(horiz, m->recenttri); - return VIOLATINGVERTEX; - } - } - - /* Insert the vertex on an edge, dividing one triangle into two (if */ - /* the edge lies on a boundary) or two triangles into four. */ - lprev(horiz, botright); - sym(botright, botrcasing); - sym(horiz, topright); - /* Is there a second triangle? (Or does this edge lie on a boundary?) */ - mirrorflag = topright.tri != m->dummytri; - if (mirrorflag) { - lnextself(topright); - sym(topright, toprcasing); - maketriangle(m, b, &newtopright); - } else { - /* Splitting a boundary edge increases the number of boundary edges. */ - m->hullsize++; - } - maketriangle(m, b, &newbotright); - - /* Set the vertices of changed and new triangles. */ - org(horiz, rightvertex); - dest(horiz, leftvertex); - apex(horiz, botvertex); - setorg(newbotright, botvertex); - setdest(newbotright, rightvertex); - setapex(newbotright, newvertex); - setorg(horiz, newvertex); - for (i = 0; i < m->eextras; i++) { - /* Set the element attributes of a new triangle. */ - setelemattribute(newbotright, i, elemattribute(botright, i)); - } - if (b->vararea) { - /* Set the area constraint of a new triangle. */ - setareabound(newbotright, areabound(botright)); - } - if (mirrorflag) { - dest(topright, topvertex); - setorg(newtopright, rightvertex); - setdest(newtopright, topvertex); - setapex(newtopright, newvertex); - setorg(topright, newvertex); - for (i = 0; i < m->eextras; i++) { - /* Set the element attributes of another new triangle. */ - setelemattribute(newtopright, i, elemattribute(topright, i)); - } - if (b->vararea) { - /* Set the area constraint of another new triangle. */ - setareabound(newtopright, areabound(topright)); - } - } - - /* There may be subsegments that need to be bonded */ - /* to the new triangle(s). */ - if (m->checksegments) { - tspivot(botright, botrsubseg); - if (botrsubseg.ss != m->dummysub) { - tsdissolve(botright); - tsbond(newbotright, botrsubseg); - } - if (mirrorflag) { - tspivot(topright, toprsubseg); - if (toprsubseg.ss != m->dummysub) { - tsdissolve(topright); - tsbond(newtopright, toprsubseg); - } - } - } - - /* Bond the new triangle(s) to the surrounding triangles. */ - bond(newbotright, botrcasing); - lprevself(newbotright); - bond(newbotright, botright); - lprevself(newbotright); - if (mirrorflag) { - bond(newtopright, toprcasing); - lnextself(newtopright); - bond(newtopright, topright); - lnextself(newtopright); - bond(newtopright, newbotright); - } - - if (splitseg != (struct osub *) NULL) { - /* Split the subsegment into two. */ - setsdest(*splitseg, newvertex); - segorg(*splitseg, segmentorg); - segdest(*splitseg, segmentdest); - ssymself(*splitseg); - spivot(*splitseg, rightsubseg); - insertsubseg(m, b, &newbotright, mark(*splitseg)); - tspivot(newbotright, newsubseg); - setsegorg(newsubseg, segmentorg); - setsegdest(newsubseg, segmentdest); - sbond(*splitseg, newsubseg); - ssymself(newsubseg); - sbond(newsubseg, rightsubseg); - ssymself(*splitseg); - /* Transfer the subsegment's boundary marker to the vertex */ - /* if required. */ - if (vertexmark(newvertex) == 0) { - setvertexmark(newvertex, mark(*splitseg)); - } - } - - if (m->checkquality) { - poolrestart(&m->flipstackers); - m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers); - m->lastflip->flippedtri = encode(horiz); - m->lastflip->prevflip = (struct flipstacker *) &insertvertex; - } - -#ifdef SELF_CHECK - if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) { - printf("Internal error in insertvertex():\n"); - printf( - " Clockwise triangle prior to edge vertex insertion (bottom).\n"); - } - if (mirrorflag) { - if (counterclockwise(m, b, leftvertex, rightvertex, topvertex) < 0.0) { - printf("Internal error in insertvertex():\n"); - printf(" Clockwise triangle prior to edge vertex insertion (top).\n"); - } - if (counterclockwise(m, b, rightvertex, topvertex, newvertex) < 0.0) { - printf("Internal error in insertvertex():\n"); - printf( - " Clockwise triangle after edge vertex insertion (top right).\n"); - } - if (counterclockwise(m, b, topvertex, leftvertex, newvertex) < 0.0) { - printf("Internal error in insertvertex():\n"); - printf( - " Clockwise triangle after edge vertex insertion (top left).\n"); - } - } - if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) { - printf("Internal error in insertvertex():\n"); - printf( - " Clockwise triangle after edge vertex insertion (bottom left).\n"); - } - if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) { - printf("Internal error in insertvertex():\n"); - printf( - " Clockwise triangle after edge vertex insertion (bottom right).\n"); - } -#endif /* SELF_CHECK */ - if (b->verbose > 2) { - printf(" Updating bottom left "); - printtriangle(m, b, &botright); - if (mirrorflag) { - printf(" Updating top left "); - printtriangle(m, b, &topright); - printf(" Creating top right "); - printtriangle(m, b, &newtopright); - } - printf(" Creating bottom right "); - printtriangle(m, b, &newbotright); - } - - /* Position `horiz' on the first edge to check for */ - /* the Delaunay property. */ - lnextself(horiz); - } else { - /* Insert the vertex in a triangle, splitting it into three. */ - lnext(horiz, botleft); - lprev(horiz, botright); - sym(botleft, botlcasing); - sym(botright, botrcasing); - maketriangle(m, b, &newbotleft); - maketriangle(m, b, &newbotright); - - /* Set the vertices of changed and new triangles. */ - org(horiz, rightvertex); - dest(horiz, leftvertex); - apex(horiz, botvertex); - setorg(newbotleft, leftvertex); - setdest(newbotleft, botvertex); - setapex(newbotleft, newvertex); - setorg(newbotright, botvertex); - setdest(newbotright, rightvertex); - setapex(newbotright, newvertex); - setapex(horiz, newvertex); - for (i = 0; i < m->eextras; i++) { - /* Set the element attributes of the new triangles. */ - attrib = elemattribute(horiz, i); - setelemattribute(newbotleft, i, attrib); - setelemattribute(newbotright, i, attrib); - } - if (b->vararea) { - /* Set the area constraint of the new triangles. */ - area = areabound(horiz); - setareabound(newbotleft, area); - setareabound(newbotright, area); - } - - /* There may be subsegments that need to be bonded */ - /* to the new triangles. */ - if (m->checksegments) { - tspivot(botleft, botlsubseg); - if (botlsubseg.ss != m->dummysub) { - tsdissolve(botleft); - tsbond(newbotleft, botlsubseg); - } - tspivot(botright, botrsubseg); - if (botrsubseg.ss != m->dummysub) { - tsdissolve(botright); - tsbond(newbotright, botrsubseg); - } - } - - /* Bond the new triangles to the surrounding triangles. */ - bond(newbotleft, botlcasing); - bond(newbotright, botrcasing); - lnextself(newbotleft); - lprevself(newbotright); - bond(newbotleft, newbotright); - lnextself(newbotleft); - bond(botleft, newbotleft); - lprevself(newbotright); - bond(botright, newbotright); - - if (m->checkquality) { - poolrestart(&m->flipstackers); - m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers); - m->lastflip->flippedtri = encode(horiz); - m->lastflip->prevflip = (struct flipstacker *) NULL; - } - -#ifdef SELF_CHECK - if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) { - printf("Internal error in insertvertex():\n"); - printf(" Clockwise triangle prior to vertex insertion.\n"); - } - if (counterclockwise(m, b, rightvertex, leftvertex, newvertex) < 0.0) { - printf("Internal error in insertvertex():\n"); - printf(" Clockwise triangle after vertex insertion (top).\n"); - } - if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) { - printf("Internal error in insertvertex():\n"); - printf(" Clockwise triangle after vertex insertion (left).\n"); - } - if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) { - printf("Internal error in insertvertex():\n"); - printf(" Clockwise triangle after vertex insertion (right).\n"); - } -#endif /* SELF_CHECK */ - if (b->verbose > 2) { - printf(" Updating top "); - printtriangle(m, b, &horiz); - printf(" Creating left "); - printtriangle(m, b, &newbotleft); - printf(" Creating right "); - printtriangle(m, b, &newbotright); - } - } - - /* The insertion is successful by default, unless an encroached */ - /* subsegment is found. */ - success = SUCCESSFULVERTEX; - /* Circle around the newly inserted vertex, checking each edge opposite */ - /* it for the Delaunay property. Non-Delaunay edges are flipped. */ - /* `horiz' is always the edge being checked. `first' marks where to */ - /* stop circling. */ - org(horiz, first); - rightvertex = first; - dest(horiz, leftvertex); - /* Circle until finished. */ - while (1) { - /* By default, the edge will be flipped. */ - doflip = 1; - - if (m->checksegments) { - /* Check for a subsegment, which cannot be flipped. */ - tspivot(horiz, checksubseg); - if (checksubseg.ss != m->dummysub) { - /* The edge is a subsegment and cannot be flipped. */ - doflip = 0; -#ifndef CDT_ONLY - if (segmentflaws) { - /* Does the new vertex encroach upon this subsegment? */ - if (checkseg4encroach(m, b, &checksubseg)) { - success = ENCROACHINGVERTEX; - } - } -#endif /* not CDT_ONLY */ - } - } - - if (doflip) { - /* Check if the edge is a boundary edge. */ - sym(horiz, top); - if (top.tri == m->dummytri) { - /* The edge is a boundary edge and cannot be flipped. */ - doflip = 0; - } else { - /* Find the vertex on the other side of the edge. */ - apex(top, farvertex); - /* In the incremental Delaunay triangulation algorithm, any of */ - /* `leftvertex', `rightvertex', and `farvertex' could be vertices */ - /* of the triangular bounding box. These vertices must be */ - /* treated as if they are infinitely distant, even though their */ - /* "coordinates" are not. */ - if ((leftvertex == m->infvertex1) || (leftvertex == m->infvertex2) || - (leftvertex == m->infvertex3)) { - /* `leftvertex' is infinitely distant. Check the convexity of */ - /* the boundary of the triangulation. 'farvertex' might be */ - /* infinite as well, but trust me, this same condition should */ - /* be applied. */ - doflip = counterclockwise(m, b, newvertex, rightvertex, farvertex) - > 0.0; - } else if ((rightvertex == m->infvertex1) || - (rightvertex == m->infvertex2) || - (rightvertex == m->infvertex3)) { - /* `rightvertex' is infinitely distant. Check the convexity of */ - /* the boundary of the triangulation. 'farvertex' might be */ - /* infinite as well, but trust me, this same condition should */ - /* be applied. */ - doflip = counterclockwise(m, b, farvertex, leftvertex, newvertex) - > 0.0; - } else if ((farvertex == m->infvertex1) || - (farvertex == m->infvertex2) || - (farvertex == m->infvertex3)) { - /* `farvertex' is infinitely distant and cannot be inside */ - /* the circumcircle of the triangle `horiz'. */ - doflip = 0; - } else { - /* Test whether the edge is locally Delaunay. */ - doflip = incircle(m, b, leftvertex, newvertex, rightvertex, - farvertex) > 0.0; - } - if (doflip) { - /* We made it! Flip the edge `horiz' by rotating its containing */ - /* quadrilateral (the two triangles adjacent to `horiz'). */ - /* Identify the casing of the quadrilateral. */ - lprev(top, topleft); - sym(topleft, toplcasing); - lnext(top, topright); - sym(topright, toprcasing); - lnext(horiz, botleft); - sym(botleft, botlcasing); - lprev(horiz, botright); - sym(botright, botrcasing); - /* Rotate the quadrilateral one-quarter turn counterclockwise. */ - bond(topleft, botlcasing); - bond(botleft, botrcasing); - bond(botright, toprcasing); - bond(topright, toplcasing); - if (m->checksegments) { - /* Check for subsegments and rebond them to the quadrilateral. */ - tspivot(topleft, toplsubseg); - tspivot(botleft, botlsubseg); - tspivot(botright, botrsubseg); - tspivot(topright, toprsubseg); - if (toplsubseg.ss == m->dummysub) { - tsdissolve(topright); - } else { - tsbond(topright, toplsubseg); - } - if (botlsubseg.ss == m->dummysub) { - tsdissolve(topleft); - } else { - tsbond(topleft, botlsubseg); - } - if (botrsubseg.ss == m->dummysub) { - tsdissolve(botleft); - } else { - tsbond(botleft, botrsubseg); - } - if (toprsubseg.ss == m->dummysub) { - tsdissolve(botright); - } else { - tsbond(botright, toprsubseg); - } - } - /* New vertex assignments for the rotated quadrilateral. */ - setorg(horiz, farvertex); - setdest(horiz, newvertex); - setapex(horiz, rightvertex); - setorg(top, newvertex); - setdest(top, farvertex); - setapex(top, leftvertex); - for (i = 0; i < m->eextras; i++) { - /* Take the average of the two triangles' attributes. */ - attrib = 0.5 * (elemattribute(top, i) + elemattribute(horiz, i)); - setelemattribute(top, i, attrib); - setelemattribute(horiz, i, attrib); - } - if (b->vararea) { - if ((areabound(top) <= 0.0) || (areabound(horiz) <= 0.0)) { - area = -1.0; - } else { - /* Take the average of the two triangles' area constraints. */ - /* This prevents small area constraints from migrating a */ - /* long, long way from their original location due to flips. */ - area = 0.5 * (areabound(top) + areabound(horiz)); - } - setareabound(top, area); - setareabound(horiz, area); - } - - if (m->checkquality) { - newflip = (struct flipstacker *) poolalloc(&m->flipstackers); - newflip->flippedtri = encode(horiz); - newflip->prevflip = m->lastflip; - m->lastflip = newflip; - } - -#ifdef SELF_CHECK - if (newvertex != (vertex) NULL) { - if (counterclockwise(m, b, leftvertex, newvertex, rightvertex) < - 0.0) { - printf("Internal error in insertvertex():\n"); - printf(" Clockwise triangle prior to edge flip (bottom).\n"); - } - /* The following test has been removed because constrainededge() */ - /* sometimes generates inverted triangles that insertvertex() */ - /* removes. */ -/* - if (counterclockwise(m, b, rightvertex, farvertex, leftvertex) < - 0.0) { - printf("Internal error in insertvertex():\n"); - printf(" Clockwise triangle prior to edge flip (top).\n"); - } -*/ - if (counterclockwise(m, b, farvertex, leftvertex, newvertex) < - 0.0) { - printf("Internal error in insertvertex():\n"); - printf(" Clockwise triangle after edge flip (left).\n"); - } - if (counterclockwise(m, b, newvertex, rightvertex, farvertex) < - 0.0) { - printf("Internal error in insertvertex():\n"); - printf(" Clockwise triangle after edge flip (right).\n"); - } - } -#endif /* SELF_CHECK */ - if (b->verbose > 2) { - printf(" Edge flip results in left "); - lnextself(topleft); - printtriangle(m, b, &topleft); - printf(" and right "); - printtriangle(m, b, &horiz); - } - /* On the next iterations, consider the two edges that were */ - /* exposed (this is, are now visible to the newly inserted */ - /* vertex) by the edge flip. */ - lprevself(horiz); - leftvertex = farvertex; - } - } - } - if (!doflip) { - /* The handle `horiz' is accepted as locally Delaunay. */ -#ifndef CDT_ONLY - if (triflaws) { - /* Check the triangle `horiz' for quality. */ - testtriangle(m, b, &horiz); - } -#endif /* not CDT_ONLY */ - /* Look for the next edge around the newly inserted vertex. */ - lnextself(horiz); - sym(horiz, testtri); - /* Check for finishing a complete revolution about the new vertex, or */ - /* falling outside of the triangulation. The latter will happen */ - /* when a vertex is inserted at a boundary. */ - if ((leftvertex == first) || (testtri.tri == m->dummytri)) { - /* We're done. Return a triangle whose origin is the new vertex. */ - lnext(horiz, *searchtri); - lnext(horiz, m->recenttri); - return success; - } - /* Finish finding the next edge around the newly inserted vertex. */ - lnext(testtri, horiz); - rightvertex = leftvertex; - dest(horiz, leftvertex); - } - } -} - -/*****************************************************************************/ -/* */ -/* triangulatepolygon() Find the Delaunay triangulation of a polygon that */ -/* has a certain "nice" shape. This includes the */ -/* polygons that result from deletion of a vertex or */ -/* insertion of a segment. */ -/* */ -/* This is a conceptually difficult routine. The starting assumption is */ -/* that we have a polygon with n sides. n - 1 of these sides are currently */ -/* represented as edges in the mesh. One side, called the "base", need not */ -/* be. */ -/* */ -/* Inside the polygon is a structure I call a "fan", consisting of n - 1 */ -/* triangles that share a common origin. For each of these triangles, the */ -/* edge opposite the origin is one of the sides of the polygon. The */ -/* primary edge of each triangle is the edge directed from the origin to */ -/* the destination; note that this is not the same edge that is a side of */ -/* the polygon. `firstedge' is the primary edge of the first triangle. */ -/* From there, the triangles follow in counterclockwise order about the */ -/* polygon, until `lastedge', the primary edge of the last triangle. */ -/* `firstedge' and `lastedge' are probably connected to other triangles */ -/* beyond the extremes of the fan, but their identity is not important, as */ -/* long as the fan remains connected to them. */ -/* */ -/* Imagine the polygon oriented so that its base is at the bottom. This */ -/* puts `firstedge' on the far right, and `lastedge' on the far left. */ -/* The right vertex of the base is the destination of `firstedge', and the */ -/* left vertex of the base is the apex of `lastedge'. */ -/* */ -/* The challenge now is to find the right sequence of edge flips to */ -/* transform the fan into a Delaunay triangulation of the polygon. Each */ -/* edge flip effectively removes one triangle from the fan, committing it */ -/* to the polygon. The resulting polygon has one fewer edge. If `doflip' */ -/* is set, the final flip will be performed, resulting in a fan of one */ -/* (useless?) triangle. If `doflip' is not set, the final flip is not */ -/* performed, resulting in a fan of two triangles, and an unfinished */ -/* triangular polygon that is not yet filled out with a single triangle. */ -/* On completion of the routine, `lastedge' is the last remaining triangle, */ -/* or the leftmost of the last two. */ -/* */ -/* Although the flips are performed in the order described above, the */ -/* decisions about what flips to perform are made in precisely the reverse */ -/* order. The recursive triangulatepolygon() procedure makes a decision, */ -/* uses up to two recursive calls to triangulate the "subproblems" */ -/* (polygons with fewer edges), and then performs an edge flip. */ -/* */ -/* The "decision" it makes is which vertex of the polygon should be */ -/* connected to the base. This decision is made by testing every possible */ -/* vertex. Once the best vertex is found, the two edges that connect this */ -/* vertex to the base become the bases for two smaller polygons. These */ -/* are triangulated recursively. Unfortunately, this approach can take */ -/* O(n^2) time not only in the worst case, but in many common cases. It's */ -/* rarely a big deal for vertex deletion, where n is rarely larger than */ -/* ten, but it could be a big deal for segment insertion, especially if */ -/* there's a lot of long segments that each cut many triangles. I ought to */ -/* code a faster algorithm some day. */ -/* */ -/* The `edgecount' parameter is the number of sides of the polygon, */ -/* including its base. `triflaws' is a flag that determines whether the */ -/* new triangles should be tested for quality, and enqueued if they are */ -/* bad. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void triangulatepolygon(struct mesh *m, struct behavior *b, - struct otri *firstedge, struct otri *lastedge, - int edgecount, int doflip, int triflaws) -#else /* not ANSI_DECLARATORS */ -void triangulatepolygon(m, b, firstedge, lastedge, edgecount, doflip, triflaws) -struct mesh *m; -struct behavior *b; -struct otri *firstedge; -struct otri *lastedge; -int edgecount; -int doflip; -int triflaws; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri testtri; - struct otri besttri; - struct otri tempedge; - vertex leftbasevertex, rightbasevertex; - vertex testvertex; - vertex bestvertex; - int bestnumber; - int i; - triangle ptr; /* Temporary variable used by sym(), onext(), and oprev(). */ - - /* Identify the base vertices. */ - apex(*lastedge, leftbasevertex); - dest(*firstedge, rightbasevertex); - if (b->verbose > 2) { - printf(" Triangulating interior polygon at edge\n"); - printf(" (%.12g, %.12g) (%.12g, %.12g)\n", leftbasevertex[0], - leftbasevertex[1], rightbasevertex[0], rightbasevertex[1]); - } - /* Find the best vertex to connect the base to. */ - onext(*firstedge, besttri); - dest(besttri, bestvertex); - otricopy(besttri, testtri); - bestnumber = 1; - for (i = 2; i <= edgecount - 2; i++) { - onextself(testtri); - dest(testtri, testvertex); - /* Is this a better vertex? */ - if (incircle(m, b, leftbasevertex, rightbasevertex, bestvertex, - testvertex) > 0.0) { - otricopy(testtri, besttri); - bestvertex = testvertex; - bestnumber = i; - } - } - if (b->verbose > 2) { - printf(" Connecting edge to (%.12g, %.12g)\n", bestvertex[0], - bestvertex[1]); - } - if (bestnumber > 1) { - /* Recursively triangulate the smaller polygon on the right. */ - oprev(besttri, tempedge); - triangulatepolygon(m, b, firstedge, &tempedge, bestnumber + 1, 1, - triflaws); - } - if (bestnumber < edgecount - 2) { - /* Recursively triangulate the smaller polygon on the left. */ - sym(besttri, tempedge); - triangulatepolygon(m, b, &besttri, lastedge, edgecount - bestnumber, 1, - triflaws); - /* Find `besttri' again; it may have been lost to edge flips. */ - sym(tempedge, besttri); - } - if (doflip) { - /* Do one final edge flip. */ - flip(m, b, &besttri); -#ifndef CDT_ONLY - if (triflaws) { - /* Check the quality of the newly committed triangle. */ - sym(besttri, testtri); - testtriangle(m, b, &testtri); - } -#endif /* not CDT_ONLY */ - } - /* Return the base triangle. */ - otricopy(besttri, *lastedge); -} - -/*****************************************************************************/ -/* */ -/* deletevertex() Delete a vertex from a Delaunay triangulation, ensuring */ -/* that the triangulation remains Delaunay. */ -/* */ -/* The origin of `deltri' is deleted. The union of the triangles adjacent */ -/* to this vertex is a polygon, for which the Delaunay triangulation is */ -/* found. Two triangles are removed from the mesh. */ -/* */ -/* Only interior vertices that do not lie on segments or boundaries may be */ -/* deleted. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -void deletevertex(struct mesh *m, struct behavior *b, struct otri *deltri) -#else /* not ANSI_DECLARATORS */ -void deletevertex(m, b, deltri) -struct mesh *m; -struct behavior *b; -struct otri *deltri; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri countingtri; - struct otri firstedge, lastedge; - struct otri deltriright; - struct otri lefttri, righttri; - struct otri leftcasing, rightcasing; - struct osub leftsubseg, rightsubseg; - vertex delvertex; - vertex neworg; - int edgecount; - triangle ptr; /* Temporary variable used by sym(), onext(), and oprev(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - org(*deltri, delvertex); - if (b->verbose > 1) { - printf(" Deleting (%.12g, %.12g).\n", delvertex[0], delvertex[1]); - } - vertexdealloc(m, delvertex); - - /* Count the degree of the vertex being deleted. */ - onext(*deltri, countingtri); - edgecount = 1; - while (!otriequal(*deltri, countingtri)) { -#ifdef SELF_CHECK - if (countingtri.tri == m->dummytri) { - printf("Internal error in deletevertex():\n"); - printf(" Attempt to delete boundary vertex.\n"); - internalerror(); - } -#endif /* SELF_CHECK */ - edgecount++; - onextself(countingtri); - } - -#ifdef SELF_CHECK - if (edgecount < 3) { - printf("Internal error in deletevertex():\n Vertex has degree %d.\n", - edgecount); - internalerror(); - } -#endif /* SELF_CHECK */ - if (edgecount > 3) { - /* Triangulate the polygon defined by the union of all triangles */ - /* adjacent to the vertex being deleted. Check the quality of */ - /* the resulting triangles. */ - onext(*deltri, firstedge); - oprev(*deltri, lastedge); - triangulatepolygon(m, b, &firstedge, &lastedge, edgecount, 0, - !b->nobisect); - } - /* Splice out two triangles. */ - lprev(*deltri, deltriright); - dnext(*deltri, lefttri); - sym(lefttri, leftcasing); - oprev(deltriright, righttri); - sym(righttri, rightcasing); - bond(*deltri, leftcasing); - bond(deltriright, rightcasing); - tspivot(lefttri, leftsubseg); - if (leftsubseg.ss != m->dummysub) { - tsbond(*deltri, leftsubseg); - } - tspivot(righttri, rightsubseg); - if (rightsubseg.ss != m->dummysub) { - tsbond(deltriright, rightsubseg); - } - - /* Set the new origin of `deltri' and check its quality. */ - org(lefttri, neworg); - setorg(*deltri, neworg); - if (!b->nobisect) { - testtriangle(m, b, deltri); - } - - /* Delete the two spliced-out triangles. */ - triangledealloc(m, lefttri.tri); - triangledealloc(m, righttri.tri); -} - -#endif /* not CDT_ONLY */ - -/*****************************************************************************/ -/* */ -/* undovertex() Undo the most recent vertex insertion. */ -/* */ -/* Walks through the list of transformations (flips and a vertex insertion) */ -/* in the reverse of the order in which they were done, and undoes them. */ -/* The inserted vertex is removed from the triangulation and deallocated. */ -/* Two triangles (possibly just one) are also deallocated. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -void undovertex(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void undovertex(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri fliptri; - struct otri botleft, botright, topright; - struct otri botlcasing, botrcasing, toprcasing; - struct otri gluetri; - struct osub botlsubseg, botrsubseg, toprsubseg; - vertex botvertex, rightvertex; - triangle ptr; /* Temporary variable used by sym(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - /* Walk through the list of transformations (flips and a vertex insertion) */ - /* in the reverse of the order in which they were done, and undo them. */ - while (m->lastflip != (struct flipstacker *) NULL) { - /* Find a triangle involved in the last unreversed transformation. */ - decode(m->lastflip->flippedtri, fliptri); - - /* We are reversing one of three transformations: a trisection of one */ - /* triangle into three (by inserting a vertex in the triangle), a */ - /* bisection of two triangles into four (by inserting a vertex in an */ - /* edge), or an edge flip. */ - if (m->lastflip->prevflip == (struct flipstacker *) NULL) { - /* Restore a triangle that was split into three triangles, */ - /* so it is again one triangle. */ - dprev(fliptri, botleft); - lnextself(botleft); - onext(fliptri, botright); - lprevself(botright); - sym(botleft, botlcasing); - sym(botright, botrcasing); - dest(botleft, botvertex); - - setapex(fliptri, botvertex); - lnextself(fliptri); - bond(fliptri, botlcasing); - tspivot(botleft, botlsubseg); - tsbond(fliptri, botlsubseg); - lnextself(fliptri); - bond(fliptri, botrcasing); - tspivot(botright, botrsubseg); - tsbond(fliptri, botrsubseg); - - /* Delete the two spliced-out triangles. */ - triangledealloc(m, botleft.tri); - triangledealloc(m, botright.tri); - } else if (m->lastflip->prevflip == (struct flipstacker *) &insertvertex) { - /* Restore two triangles that were split into four triangles, */ - /* so they are again two triangles. */ - lprev(fliptri, gluetri); - sym(gluetri, botright); - lnextself(botright); - sym(botright, botrcasing); - dest(botright, rightvertex); - - setorg(fliptri, rightvertex); - bond(gluetri, botrcasing); - tspivot(botright, botrsubseg); - tsbond(gluetri, botrsubseg); - - /* Delete the spliced-out triangle. */ - triangledealloc(m, botright.tri); - - sym(fliptri, gluetri); - if (gluetri.tri != m->dummytri) { - lnextself(gluetri); - dnext(gluetri, topright); - sym(topright, toprcasing); - - setorg(gluetri, rightvertex); - bond(gluetri, toprcasing); - tspivot(topright, toprsubseg); - tsbond(gluetri, toprsubseg); - - /* Delete the spliced-out triangle. */ - triangledealloc(m, topright.tri); - } - - /* This is the end of the list, sneakily encoded. */ - m->lastflip->prevflip = (struct flipstacker *) NULL; - } else { - /* Undo an edge flip. */ - unflip(m, b, &fliptri); - } - - /* Go on and process the next transformation. */ - m->lastflip = m->lastflip->prevflip; - } -} - -#endif /* not CDT_ONLY */ - -/** **/ -/** **/ -/********* Mesh transformation routines end here *********/ - -/********* Divide-and-conquer Delaunay triangulation begins here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* The divide-and-conquer bounding box */ -/* */ -/* I originally implemented the divide-and-conquer and incremental Delaunay */ -/* triangulations using the edge-based data structure presented by Guibas */ -/* and Stolfi. Switching to a triangle-based data structure doubled the */ -/* speed. However, I had to think of a few extra tricks to maintain the */ -/* elegance of the original algorithms. */ -/* */ -/* The "bounding box" used by my variant of the divide-and-conquer */ -/* algorithm uses one triangle for each edge of the convex hull of the */ -/* triangulation. These bounding triangles all share a common apical */ -/* vertex, which is represented by NULL and which represents nothing. */ -/* The bounding triangles are linked in a circular fan about this NULL */ -/* vertex, and the edges on the convex hull of the triangulation appear */ -/* opposite the NULL vertex. You might find it easiest to imagine that */ -/* the NULL vertex is a point in 3D space behind the center of the */ -/* triangulation, and that the bounding triangles form a sort of cone. */ -/* */ -/* This bounding box makes it easy to represent degenerate cases. For */ -/* instance, the triangulation of two vertices is a single edge. This edge */ -/* is represented by two bounding box triangles, one on each "side" of the */ -/* edge. These triangles are also linked together in a fan about the NULL */ -/* vertex. */ -/* */ -/* The bounding box also makes it easy to traverse the convex hull, as the */ -/* divide-and-conquer algorithm needs to do. */ -/* */ -/*****************************************************************************/ - -/*****************************************************************************/ -/* */ -/* vertexsort() Sort an array of vertices by x-coordinate, using the */ -/* y-coordinate as a secondary key. */ -/* */ -/* Uses quicksort. Randomized O(n log n) time. No, I did not make any of */ -/* the usual quicksort mistakes. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void vertexsort(vertex *sortarray, int arraysize) -#else /* not ANSI_DECLARATORS */ -void vertexsort(sortarray, arraysize) -vertex *sortarray; -int arraysize; -#endif /* not ANSI_DECLARATORS */ - -{ - int left, right; - int pivot; - REAL pivotx, pivoty; - vertex temp; - - if (arraysize == 2) { - /* Recursive base case. */ - if ((sortarray[0][0] > sortarray[1][0]) || - ((sortarray[0][0] == sortarray[1][0]) && - (sortarray[0][1] > sortarray[1][1]))) { - temp = sortarray[1]; - sortarray[1] = sortarray[0]; - sortarray[0] = temp; - } - return; - } - /* Choose a random pivot to split the array. */ - pivot = (int) randomnation((unsigned int) arraysize); - pivotx = sortarray[pivot][0]; - pivoty = sortarray[pivot][1]; - /* Split the array. */ - left = -1; - right = arraysize; - while (left < right) { - /* Search for a vertex whose x-coordinate is too large for the left. */ - do { - left++; - } while ((left <= right) && ((sortarray[left][0] < pivotx) || - ((sortarray[left][0] == pivotx) && - (sortarray[left][1] < pivoty)))); - /* Search for a vertex whose x-coordinate is too small for the right. */ - do { - right--; - } while ((left <= right) && ((sortarray[right][0] > pivotx) || - ((sortarray[right][0] == pivotx) && - (sortarray[right][1] > pivoty)))); - if (left < right) { - /* Swap the left and right vertices. */ - temp = sortarray[left]; - sortarray[left] = sortarray[right]; - sortarray[right] = temp; - } - } - if (left > 1) { - /* Recursively sort the left subset. */ - vertexsort(sortarray, left); - } - if (right < arraysize - 2) { - /* Recursively sort the right subset. */ - vertexsort(&sortarray[right + 1], arraysize - right - 1); - } -} - -/*****************************************************************************/ -/* */ -/* vertexmedian() An order statistic algorithm, almost. Shuffles an */ -/* array of vertices so that the first `median' vertices */ -/* occur lexicographically before the remaining vertices. */ -/* */ -/* Uses the x-coordinate as the primary key if axis == 0; the y-coordinate */ -/* if axis == 1. Very similar to the vertexsort() procedure, but runs in */ -/* randomized linear time. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void vertexmedian(vertex *sortarray, int arraysize, int median, int axis) -#else /* not ANSI_DECLARATORS */ -void vertexmedian(sortarray, arraysize, median, axis) -vertex *sortarray; -int arraysize; -int median; -int axis; -#endif /* not ANSI_DECLARATORS */ - -{ - int left, right; - int pivot; - REAL pivot1, pivot2; - vertex temp; - - if (arraysize == 2) { - /* Recursive base case. */ - if ((sortarray[0][axis] > sortarray[1][axis]) || - ((sortarray[0][axis] == sortarray[1][axis]) && - (sortarray[0][1 - axis] > sortarray[1][1 - axis]))) { - temp = sortarray[1]; - sortarray[1] = sortarray[0]; - sortarray[0] = temp; - } - return; - } - /* Choose a random pivot to split the array. */ - pivot = (int) randomnation((unsigned int) arraysize); - pivot1 = sortarray[pivot][axis]; - pivot2 = sortarray[pivot][1 - axis]; - /* Split the array. */ - left = -1; - right = arraysize; - while (left < right) { - /* Search for a vertex whose x-coordinate is too large for the left. */ - do { - left++; - } while ((left <= right) && ((sortarray[left][axis] < pivot1) || - ((sortarray[left][axis] == pivot1) && - (sortarray[left][1 - axis] < pivot2)))); - /* Search for a vertex whose x-coordinate is too small for the right. */ - do { - right--; - } while ((left <= right) && ((sortarray[right][axis] > pivot1) || - ((sortarray[right][axis] == pivot1) && - (sortarray[right][1 - axis] > pivot2)))); - if (left < right) { - /* Swap the left and right vertices. */ - temp = sortarray[left]; - sortarray[left] = sortarray[right]; - sortarray[right] = temp; - } - } - /* Unlike in vertexsort(), at most one of the following */ - /* conditionals is true. */ - if (left > median) { - /* Recursively shuffle the left subset. */ - vertexmedian(sortarray, left, median, axis); - } - if (right < median - 1) { - /* Recursively shuffle the right subset. */ - vertexmedian(&sortarray[right + 1], arraysize - right - 1, - median - right - 1, axis); - } -} - -/*****************************************************************************/ -/* */ -/* alternateaxes() Sorts the vertices as appropriate for the divide-and- */ -/* conquer algorithm with alternating cuts. */ -/* */ -/* Partitions by x-coordinate if axis == 0; by y-coordinate if axis == 1. */ -/* For the base case, subsets containing only two or three vertices are */ -/* always sorted by x-coordinate. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void alternateaxes(vertex *sortarray, int arraysize, int axis) -#else /* not ANSI_DECLARATORS */ -void alternateaxes(sortarray, arraysize, axis) -vertex *sortarray; -int arraysize; -int axis; -#endif /* not ANSI_DECLARATORS */ - -{ - int divider; - - divider = arraysize >> 1; - if (arraysize <= 3) { - /* Recursive base case: subsets of two or three vertices will be */ - /* handled specially, and should always be sorted by x-coordinate. */ - axis = 0; - } - /* Partition with a horizontal or vertical cut. */ - vertexmedian(sortarray, arraysize, divider, axis); - /* Recursively partition the subsets with a cross cut. */ - if (arraysize - divider >= 2) { - if (divider >= 2) { - alternateaxes(sortarray, divider, 1 - axis); - } - alternateaxes(&sortarray[divider], arraysize - divider, 1 - axis); - } -} - -/*****************************************************************************/ -/* */ -/* mergehulls() Merge two adjacent Delaunay triangulations into a */ -/* single Delaunay triangulation. */ -/* */ -/* This is similar to the algorithm given by Guibas and Stolfi, but uses */ -/* a triangle-based, rather than edge-based, data structure. */ -/* */ -/* The algorithm walks up the gap between the two triangulations, knitting */ -/* them together. As they are merged, some of their bounding triangles */ -/* are converted into real triangles of the triangulation. The procedure */ -/* pulls each hull's bounding triangles apart, then knits them together */ -/* like the teeth of two gears. The Delaunay property determines, at each */ -/* step, whether the next "tooth" is a bounding triangle of the left hull */ -/* or the right. When a bounding triangle becomes real, its apex is */ -/* changed from NULL to a real vertex. */ -/* */ -/* Only two new triangles need to be allocated. These become new bounding */ -/* triangles at the top and bottom of the seam. They are used to connect */ -/* the remaining bounding triangles (those that have not been converted */ -/* into real triangles) into a single fan. */ -/* */ -/* On entry, `farleft' and `innerleft' are bounding triangles of the left */ -/* triangulation. The origin of `farleft' is the leftmost vertex, and */ -/* the destination of `innerleft' is the rightmost vertex of the */ -/* triangulation. Similarly, `innerright' and `farright' are bounding */ -/* triangles of the right triangulation. The origin of `innerright' and */ -/* destination of `farright' are the leftmost and rightmost vertices. */ -/* */ -/* On completion, the origin of `farleft' is the leftmost vertex of the */ -/* merged triangulation, and the destination of `farright' is the rightmost */ -/* vertex. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void mergehulls(struct mesh *m, struct behavior *b, struct otri *farleft, - struct otri *innerleft, struct otri *innerright, - struct otri *farright, int axis) -#else /* not ANSI_DECLARATORS */ -void mergehulls(m, b, farleft, innerleft, innerright, farright, axis) -struct mesh *m; -struct behavior *b; -struct otri *farleft; -struct otri *innerleft; -struct otri *innerright; -struct otri *farright; -int axis; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri leftcand, rightcand; - struct otri baseedge; - struct otri nextedge; - struct otri sidecasing, topcasing, outercasing; - struct otri checkedge; - vertex innerleftdest; - vertex innerrightorg; - vertex innerleftapex, innerrightapex; - vertex farleftpt, farrightpt; - vertex farleftapex, farrightapex; - vertex lowerleft, lowerright; - vertex upperleft, upperright; - vertex nextapex; - vertex checkvertex; - int changemade; - int badedge; - int leftfinished, rightfinished; - triangle ptr; /* Temporary variable used by sym(). */ - - dest(*innerleft, innerleftdest); - apex(*innerleft, innerleftapex); - org(*innerright, innerrightorg); - apex(*innerright, innerrightapex); - /* Special treatment for horizontal cuts. */ - if (b->dwyer && (axis == 1)) { - org(*farleft, farleftpt); - apex(*farleft, farleftapex); - dest(*farright, farrightpt); - apex(*farright, farrightapex); - /* The pointers to the extremal vertices are shifted to point to the */ - /* topmost and bottommost vertex of each hull, rather than the */ - /* leftmost and rightmost vertices. */ - while (farleftapex[1] < farleftpt[1]) { - lnextself(*farleft); - symself(*farleft); - farleftpt = farleftapex; - apex(*farleft, farleftapex); - } - sym(*innerleft, checkedge); - apex(checkedge, checkvertex); - while (checkvertex[1] > innerleftdest[1]) { - lnext(checkedge, *innerleft); - innerleftapex = innerleftdest; - innerleftdest = checkvertex; - sym(*innerleft, checkedge); - apex(checkedge, checkvertex); - } - while (innerrightapex[1] < innerrightorg[1]) { - lnextself(*innerright); - symself(*innerright); - innerrightorg = innerrightapex; - apex(*innerright, innerrightapex); - } - sym(*farright, checkedge); - apex(checkedge, checkvertex); - while (checkvertex[1] > farrightpt[1]) { - lnext(checkedge, *farright); - farrightapex = farrightpt; - farrightpt = checkvertex; - sym(*farright, checkedge); - apex(checkedge, checkvertex); - } - } - /* Find a line tangent to and below both hulls. */ - do { - changemade = 0; - /* Make innerleftdest the "bottommost" vertex of the left hull. */ - if (counterclockwise(m, b, innerleftdest, innerleftapex, innerrightorg) > - 0.0) { - lprevself(*innerleft); - symself(*innerleft); - innerleftdest = innerleftapex; - apex(*innerleft, innerleftapex); - changemade = 1; - } - /* Make innerrightorg the "bottommost" vertex of the right hull. */ - if (counterclockwise(m, b, innerrightapex, innerrightorg, innerleftdest) > - 0.0) { - lnextself(*innerright); - symself(*innerright); - innerrightorg = innerrightapex; - apex(*innerright, innerrightapex); - changemade = 1; - } - } while (changemade); - /* Find the two candidates to be the next "gear tooth." */ - sym(*innerleft, leftcand); - sym(*innerright, rightcand); - /* Create the bottom new bounding triangle. */ - maketriangle(m, b, &baseedge); - /* Connect it to the bounding boxes of the left and right triangulations. */ - bond(baseedge, *innerleft); - lnextself(baseedge); - bond(baseedge, *innerright); - lnextself(baseedge); - setorg(baseedge, innerrightorg); - setdest(baseedge, innerleftdest); - /* Apex is intentionally left NULL. */ - if (b->verbose > 2) { - printf(" Creating base bounding "); - printtriangle(m, b, &baseedge); - } - /* Fix the extreme triangles if necessary. */ - org(*farleft, farleftpt); - if (innerleftdest == farleftpt) { - lnext(baseedge, *farleft); - } - dest(*farright, farrightpt); - if (innerrightorg == farrightpt) { - lprev(baseedge, *farright); - } - /* The vertices of the current knitting edge. */ - lowerleft = innerleftdest; - lowerright = innerrightorg; - /* The candidate vertices for knitting. */ - apex(leftcand, upperleft); - apex(rightcand, upperright); - /* Walk up the gap between the two triangulations, knitting them together. */ - while (1) { - /* Have we reached the top? (This isn't quite the right question, */ - /* because even though the left triangulation might seem finished now, */ - /* moving up on the right triangulation might reveal a new vertex of */ - /* the left triangulation. And vice-versa.) */ - leftfinished = counterclockwise(m, b, upperleft, lowerleft, lowerright) <= - 0.0; - rightfinished = counterclockwise(m, b, upperright, lowerleft, lowerright) - <= 0.0; - if (leftfinished && rightfinished) { - /* Create the top new bounding triangle. */ - maketriangle(m, b, &nextedge); - setorg(nextedge, lowerleft); - setdest(nextedge, lowerright); - /* Apex is intentionally left NULL. */ - /* Connect it to the bounding boxes of the two triangulations. */ - bond(nextedge, baseedge); - lnextself(nextedge); - bond(nextedge, rightcand); - lnextself(nextedge); - bond(nextedge, leftcand); - if (b->verbose > 2) { - printf(" Creating top bounding "); - printtriangle(m, b, &nextedge); - } - /* Special treatment for horizontal cuts. */ - if (b->dwyer && (axis == 1)) { - org(*farleft, farleftpt); - apex(*farleft, farleftapex); - dest(*farright, farrightpt); - apex(*farright, farrightapex); - sym(*farleft, checkedge); - apex(checkedge, checkvertex); - /* The pointers to the extremal vertices are restored to the */ - /* leftmost and rightmost vertices (rather than topmost and */ - /* bottommost). */ - while (checkvertex[0] < farleftpt[0]) { - lprev(checkedge, *farleft); - farleftapex = farleftpt; - farleftpt = checkvertex; - sym(*farleft, checkedge); - apex(checkedge, checkvertex); - } - while (farrightapex[0] > farrightpt[0]) { - lprevself(*farright); - symself(*farright); - farrightpt = farrightapex; - apex(*farright, farrightapex); - } - } - return; - } - /* Consider eliminating edges from the left triangulation. */ - if (!leftfinished) { - /* What vertex would be exposed if an edge were deleted? */ - lprev(leftcand, nextedge); - symself(nextedge); - apex(nextedge, nextapex); - /* If nextapex is NULL, then no vertex would be exposed; the */ - /* triangulation would have been eaten right through. */ - if (nextapex != (vertex) NULL) { - /* Check whether the edge is Delaunay. */ - badedge = incircle(m, b, lowerleft, lowerright, upperleft, nextapex) > - 0.0; - while (badedge) { - /* Eliminate the edge with an edge flip. As a result, the */ - /* left triangulation will have one more boundary triangle. */ - lnextself(nextedge); - sym(nextedge, topcasing); - lnextself(nextedge); - sym(nextedge, sidecasing); - bond(nextedge, topcasing); - bond(leftcand, sidecasing); - lnextself(leftcand); - sym(leftcand, outercasing); - lprevself(nextedge); - bond(nextedge, outercasing); - /* Correct the vertices to reflect the edge flip. */ - setorg(leftcand, lowerleft); - setdest(leftcand, NULL); - setapex(leftcand, nextapex); - setorg(nextedge, NULL); - setdest(nextedge, upperleft); - setapex(nextedge, nextapex); - /* Consider the newly exposed vertex. */ - upperleft = nextapex; - /* What vertex would be exposed if another edge were deleted? */ - otricopy(sidecasing, nextedge); - apex(nextedge, nextapex); - if (nextapex != (vertex) NULL) { - /* Check whether the edge is Delaunay. */ - badedge = incircle(m, b, lowerleft, lowerright, upperleft, - nextapex) > 0.0; - } else { - /* Avoid eating right through the triangulation. */ - badedge = 0; - } - } - } - } - /* Consider eliminating edges from the right triangulation. */ - if (!rightfinished) { - /* What vertex would be exposed if an edge were deleted? */ - lnext(rightcand, nextedge); - symself(nextedge); - apex(nextedge, nextapex); - /* If nextapex is NULL, then no vertex would be exposed; the */ - /* triangulation would have been eaten right through. */ - if (nextapex != (vertex) NULL) { - /* Check whether the edge is Delaunay. */ - badedge = incircle(m, b, lowerleft, lowerright, upperright, nextapex) > - 0.0; - while (badedge) { - /* Eliminate the edge with an edge flip. As a result, the */ - /* right triangulation will have one more boundary triangle. */ - lprevself(nextedge); - sym(nextedge, topcasing); - lprevself(nextedge); - sym(nextedge, sidecasing); - bond(nextedge, topcasing); - bond(rightcand, sidecasing); - lprevself(rightcand); - sym(rightcand, outercasing); - lnextself(nextedge); - bond(nextedge, outercasing); - /* Correct the vertices to reflect the edge flip. */ - setorg(rightcand, NULL); - setdest(rightcand, lowerright); - setapex(rightcand, nextapex); - setorg(nextedge, upperright); - setdest(nextedge, NULL); - setapex(nextedge, nextapex); - /* Consider the newly exposed vertex. */ - upperright = nextapex; - /* What vertex would be exposed if another edge were deleted? */ - otricopy(sidecasing, nextedge); - apex(nextedge, nextapex); - if (nextapex != (vertex) NULL) { - /* Check whether the edge is Delaunay. */ - badedge = incircle(m, b, lowerleft, lowerright, upperright, - nextapex) > 0.0; - } else { - /* Avoid eating right through the triangulation. */ - badedge = 0; - } - } - } - } - if (leftfinished || (!rightfinished && - (incircle(m, b, upperleft, lowerleft, lowerright, upperright) > - 0.0))) { - /* Knit the triangulations, adding an edge from `lowerleft' */ - /* to `upperright'. */ - bond(baseedge, rightcand); - lprev(rightcand, baseedge); - setdest(baseedge, lowerleft); - lowerright = upperright; - sym(baseedge, rightcand); - apex(rightcand, upperright); - } else { - /* Knit the triangulations, adding an edge from `upperleft' */ - /* to `lowerright'. */ - bond(baseedge, leftcand); - lnext(leftcand, baseedge); - setorg(baseedge, lowerright); - lowerleft = upperleft; - sym(baseedge, leftcand); - apex(leftcand, upperleft); - } - if (b->verbose > 2) { - printf(" Connecting "); - printtriangle(m, b, &baseedge); - } - } -} - -/*****************************************************************************/ -/* */ -/* divconqrecurse() Recursively form a Delaunay triangulation by the */ -/* divide-and-conquer method. */ -/* */ -/* Recursively breaks down the problem into smaller pieces, which are */ -/* knitted together by mergehulls(). The base cases (problems of two or */ -/* three vertices) are handled specially here. */ -/* */ -/* On completion, `farleft' and `farright' are bounding triangles such that */ -/* the origin of `farleft' is the leftmost vertex (breaking ties by */ -/* choosing the highest leftmost vertex), and the destination of */ -/* `farright' is the rightmost vertex (breaking ties by choosing the */ -/* lowest rightmost vertex). */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void divconqrecurse(struct mesh *m, struct behavior *b, vertex *sortarray, - int vertices, int axis, - struct otri *farleft, struct otri *farright) -#else /* not ANSI_DECLARATORS */ -void divconqrecurse(m, b, sortarray, vertices, axis, farleft, farright) -struct mesh *m; -struct behavior *b; -vertex *sortarray; -int vertices; -int axis; -struct otri *farleft; -struct otri *farright; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri midtri, tri1, tri2, tri3; - struct otri innerleft, innerright; - REAL area; - int divider; - - if (b->verbose > 2) { - printf(" Triangulating %d vertices.\n", vertices); - } - if (vertices == 2) { - /* The triangulation of two vertices is an edge. An edge is */ - /* represented by two bounding triangles. */ - maketriangle(m, b, farleft); - setorg(*farleft, sortarray[0]); - setdest(*farleft, sortarray[1]); - /* The apex is intentionally left NULL. */ - maketriangle(m, b, farright); - setorg(*farright, sortarray[1]); - setdest(*farright, sortarray[0]); - /* The apex is intentionally left NULL. */ - bond(*farleft, *farright); - lprevself(*farleft); - lnextself(*farright); - bond(*farleft, *farright); - lprevself(*farleft); - lnextself(*farright); - bond(*farleft, *farright); - if (b->verbose > 2) { - printf(" Creating "); - printtriangle(m, b, farleft); - printf(" Creating "); - printtriangle(m, b, farright); - } - /* Ensure that the origin of `farleft' is sortarray[0]. */ - lprev(*farright, *farleft); - return; - } else if (vertices == 3) { - /* The triangulation of three vertices is either a triangle (with */ - /* three bounding triangles) or two edges (with four bounding */ - /* triangles). In either case, four triangles are created. */ - maketriangle(m, b, &midtri); - maketriangle(m, b, &tri1); - maketriangle(m, b, &tri2); - maketriangle(m, b, &tri3); - area = counterclockwise(m, b, sortarray[0], sortarray[1], sortarray[2]); - if (area == 0.0) { - /* Three collinear vertices; the triangulation is two edges. */ - setorg(midtri, sortarray[0]); - setdest(midtri, sortarray[1]); - setorg(tri1, sortarray[1]); - setdest(tri1, sortarray[0]); - setorg(tri2, sortarray[2]); - setdest(tri2, sortarray[1]); - setorg(tri3, sortarray[1]); - setdest(tri3, sortarray[2]); - /* All apices are intentionally left NULL. */ - bond(midtri, tri1); - bond(tri2, tri3); - lnextself(midtri); - lprevself(tri1); - lnextself(tri2); - lprevself(tri3); - bond(midtri, tri3); - bond(tri1, tri2); - lnextself(midtri); - lprevself(tri1); - lnextself(tri2); - lprevself(tri3); - bond(midtri, tri1); - bond(tri2, tri3); - /* Ensure that the origin of `farleft' is sortarray[0]. */ - otricopy(tri1, *farleft); - /* Ensure that the destination of `farright' is sortarray[2]. */ - otricopy(tri2, *farright); - } else { - /* The three vertices are not collinear; the triangulation is one */ - /* triangle, namely `midtri'. */ - setorg(midtri, sortarray[0]); - setdest(tri1, sortarray[0]); - setorg(tri3, sortarray[0]); - /* Apices of tri1, tri2, and tri3 are left NULL. */ - if (area > 0.0) { - /* The vertices are in counterclockwise order. */ - setdest(midtri, sortarray[1]); - setorg(tri1, sortarray[1]); - setdest(tri2, sortarray[1]); - setapex(midtri, sortarray[2]); - setorg(tri2, sortarray[2]); - setdest(tri3, sortarray[2]); - } else { - /* The vertices are in clockwise order. */ - setdest(midtri, sortarray[2]); - setorg(tri1, sortarray[2]); - setdest(tri2, sortarray[2]); - setapex(midtri, sortarray[1]); - setorg(tri2, sortarray[1]); - setdest(tri3, sortarray[1]); - } - /* The topology does not depend on how the vertices are ordered. */ - bond(midtri, tri1); - lnextself(midtri); - bond(midtri, tri2); - lnextself(midtri); - bond(midtri, tri3); - lprevself(tri1); - lnextself(tri2); - bond(tri1, tri2); - lprevself(tri1); - lprevself(tri3); - bond(tri1, tri3); - lnextself(tri2); - lprevself(tri3); - bond(tri2, tri3); - /* Ensure that the origin of `farleft' is sortarray[0]. */ - otricopy(tri1, *farleft); - /* Ensure that the destination of `farright' is sortarray[2]. */ - if (area > 0.0) { - otricopy(tri2, *farright); - } else { - lnext(*farleft, *farright); - } - } - if (b->verbose > 2) { - printf(" Creating "); - printtriangle(m, b, &midtri); - printf(" Creating "); - printtriangle(m, b, &tri1); - printf(" Creating "); - printtriangle(m, b, &tri2); - printf(" Creating "); - printtriangle(m, b, &tri3); - } - return; - } else { - /* Split the vertices in half. */ - divider = vertices >> 1; - /* Recursively triangulate each half. */ - divconqrecurse(m, b, sortarray, divider, 1 - axis, farleft, &innerleft); - divconqrecurse(m, b, &sortarray[divider], vertices - divider, 1 - axis, - &innerright, farright); - if (b->verbose > 1) { - printf(" Joining triangulations with %d and %d vertices.\n", divider, - vertices - divider); - } - /* Merge the two triangulations into one. */ - mergehulls(m, b, farleft, &innerleft, &innerright, farright, axis); - } -} - -#ifdef ANSI_DECLARATORS -long removeghosts(struct mesh *m, struct behavior *b, struct otri *startghost) -#else /* not ANSI_DECLARATORS */ -long removeghosts(m, b, startghost) -struct mesh *m; -struct behavior *b; -struct otri *startghost; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri searchedge; - struct otri dissolveedge; - struct otri deadtriangle; - vertex markorg; - long hullsize; - triangle ptr; /* Temporary variable used by sym(). */ - - if (b->verbose) { - printf(" Removing ghost triangles.\n"); - } - /* Find an edge on the convex hull to start point location from. */ - lprev(*startghost, searchedge); - symself(searchedge); - m->dummytri[0] = encode(searchedge); - /* Remove the bounding box and count the convex hull edges. */ - otricopy(*startghost, dissolveedge); - hullsize = 0; - do { - hullsize++; - lnext(dissolveedge, deadtriangle); - lprevself(dissolveedge); - symself(dissolveedge); - /* If no PSLG is involved, set the boundary markers of all the vertices */ - /* on the convex hull. If a PSLG is used, this step is done later. */ - if (!b->poly) { - /* Watch out for the case where all the input vertices are collinear. */ - if (dissolveedge.tri != m->dummytri) { - org(dissolveedge, markorg); - if (vertexmark(markorg) == 0) { - setvertexmark(markorg, 1); - } - } - } - /* Remove a bounding triangle from a convex hull triangle. */ - dissolve(dissolveedge); - /* Find the next bounding triangle. */ - sym(deadtriangle, dissolveedge); - /* Delete the bounding triangle. */ - triangledealloc(m, deadtriangle.tri); - } while (!otriequal(dissolveedge, *startghost)); - return hullsize; -} - -/*****************************************************************************/ -/* */ -/* divconqdelaunay() Form a Delaunay triangulation by the divide-and- */ -/* conquer method. */ -/* */ -/* Sorts the vertices, calls a recursive procedure to triangulate them, and */ -/* removes the bounding box, setting boundary markers as appropriate. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -long divconqdelaunay(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -long divconqdelaunay(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - vertex *sortarray; - struct otri hullleft, hullright; - int divider; - int i, j; - - if (b->verbose) { - printf(" Sorting vertices.\n"); - } - - /* Allocate an array of pointers to vertices for sorting. */ - sortarray = (vertex *) trimalloc(m->invertices * (int) sizeof(vertex)); - traversalinit(&m->vertices); - for (i = 0; i < m->invertices; i++) { - sortarray[i] = vertextraverse(m); - } - /* Sort the vertices. */ - vertexsort(sortarray, m->invertices); - /* Discard duplicate vertices, which can really mess up the algorithm. */ - i = 0; - for (j = 1; j < m->invertices; j++) { - if ((sortarray[i][0] == sortarray[j][0]) - && (sortarray[i][1] == sortarray[j][1])) { - if (!b->quiet) { - printf( -"Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n", - sortarray[j][0], sortarray[j][1]); - } - setvertextype(sortarray[j], UNDEADVERTEX); - m->undeads++; - } else { - i++; - sortarray[i] = sortarray[j]; - } - } - i++; - if (b->dwyer) { - /* Re-sort the array of vertices to accommodate alternating cuts. */ - divider = i >> 1; - if (i - divider >= 2) { - if (divider >= 2) { - alternateaxes(sortarray, divider, 1); - } - alternateaxes(&sortarray[divider], i - divider, 1); - } - } - - if (b->verbose) { - printf(" Forming triangulation.\n"); - } - - /* Form the Delaunay triangulation. */ - divconqrecurse(m, b, sortarray, i, 0, &hullleft, &hullright); - trifree((VOID *) sortarray); - - return removeghosts(m, b, &hullleft); -} - -/** **/ -/** **/ -/********* Divide-and-conquer Delaunay triangulation ends here *********/ - -/********* Incremental Delaunay triangulation begins here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* boundingbox() Form an "infinite" bounding triangle to insert vertices */ -/* into. */ -/* */ -/* The vertices at "infinity" are assigned finite coordinates, which are */ -/* used by the point location routines, but (mostly) ignored by the */ -/* Delaunay edge flip routines. */ -/* */ -/*****************************************************************************/ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -void boundingbox(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void boundingbox(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri inftri; /* Handle for the triangular bounding box. */ - REAL width; - - if (b->verbose) { - printf(" Creating triangular bounding box.\n"); - } - /* Find the width (or height, whichever is larger) of the triangulation. */ - width = m->xmax - m->xmin; - if (m->ymax - m->ymin > width) { - width = m->ymax - m->ymin; - } - if (width == 0.0) { - width = 1.0; - } - /* Create the vertices of the bounding box. */ - m->infvertex1 = (vertex) trimalloc(m->vertices.itembytes); - m->infvertex2 = (vertex) trimalloc(m->vertices.itembytes); - m->infvertex3 = (vertex) trimalloc(m->vertices.itembytes); - m->infvertex1[0] = m->xmin - 50.0 * width; - m->infvertex1[1] = m->ymin - 40.0 * width; - m->infvertex2[0] = m->xmax + 50.0 * width; - m->infvertex2[1] = m->ymin - 40.0 * width; - m->infvertex3[0] = 0.5 * (m->xmin + m->xmax); - m->infvertex3[1] = m->ymax + 60.0 * width; - - /* Create the bounding box. */ - maketriangle(m, b, &inftri); - setorg(inftri, m->infvertex1); - setdest(inftri, m->infvertex2); - setapex(inftri, m->infvertex3); - /* Link dummytri to the bounding box so we can always find an */ - /* edge to begin searching (point location) from. */ - m->dummytri[0] = (triangle) inftri.tri; - if (b->verbose > 2) { - printf(" Creating "); - printtriangle(m, b, &inftri); - } -} - -#endif /* not REDUCED */ - -/*****************************************************************************/ -/* */ -/* removebox() Remove the "infinite" bounding triangle, setting boundary */ -/* markers as appropriate. */ -/* */ -/* The triangular bounding box has three boundary triangles (one for each */ -/* side of the bounding box), and a bunch of triangles fanning out from */ -/* the three bounding box vertices (one triangle for each edge of the */ -/* convex hull of the inner mesh). This routine removes these triangles. */ -/* */ -/* Returns the number of edges on the convex hull of the triangulation. */ -/* */ -/*****************************************************************************/ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -long removebox(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -long removebox(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri deadtriangle; - struct otri searchedge; - struct otri checkedge; - struct otri nextedge, finaledge, dissolveedge; - vertex markorg; - long hullsize; - triangle ptr; /* Temporary variable used by sym(). */ - - if (b->verbose) { - printf(" Removing triangular bounding box.\n"); - } - /* Find a boundary triangle. */ - nextedge.tri = m->dummytri; - nextedge.orient = 0; - symself(nextedge); - /* Mark a place to stop. */ - lprev(nextedge, finaledge); - lnextself(nextedge); - symself(nextedge); - /* Find a triangle (on the boundary of the vertex set) that isn't */ - /* a bounding box triangle. */ - lprev(nextedge, searchedge); - symself(searchedge); - /* Check whether nextedge is another boundary triangle */ - /* adjacent to the first one. */ - lnext(nextedge, checkedge); - symself(checkedge); - if (checkedge.tri == m->dummytri) { - /* Go on to the next triangle. There are only three boundary */ - /* triangles, and this next triangle cannot be the third one, */ - /* so it's safe to stop here. */ - lprevself(searchedge); - symself(searchedge); - } - /* Find a new boundary edge to search from, as the current search */ - /* edge lies on a bounding box triangle and will be deleted. */ - m->dummytri[0] = encode(searchedge); - hullsize = -2l; - while (!otriequal(nextedge, finaledge)) { - hullsize++; - lprev(nextedge, dissolveedge); - symself(dissolveedge); - /* If not using a PSLG, the vertices should be marked now. */ - /* (If using a PSLG, markhull() will do the job.) */ - if (!b->poly) { - /* Be careful! One must check for the case where all the input */ - /* vertices are collinear, and thus all the triangles are part of */ - /* the bounding box. Otherwise, the setvertexmark() call below */ - /* will cause a bad pointer reference. */ - if (dissolveedge.tri != m->dummytri) { - org(dissolveedge, markorg); - if (vertexmark(markorg) == 0) { - setvertexmark(markorg, 1); - } - } - } - /* Disconnect the bounding box triangle from the mesh triangle. */ - dissolve(dissolveedge); - lnext(nextedge, deadtriangle); - sym(deadtriangle, nextedge); - /* Get rid of the bounding box triangle. */ - triangledealloc(m, deadtriangle.tri); - /* Do we need to turn the corner? */ - if (nextedge.tri == m->dummytri) { - /* Turn the corner. */ - otricopy(dissolveedge, nextedge); - } - } - triangledealloc(m, finaledge.tri); - - trifree((VOID *) m->infvertex1); /* Deallocate the bounding box vertices. */ - trifree((VOID *) m->infvertex2); - trifree((VOID *) m->infvertex3); - - return hullsize; -} - -#endif /* not REDUCED */ - -/*****************************************************************************/ -/* */ -/* incrementaldelaunay() Form a Delaunay triangulation by incrementally */ -/* inserting vertices. */ -/* */ -/* Returns the number of edges on the convex hull of the triangulation. */ -/* */ -/*****************************************************************************/ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -long incrementaldelaunay(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -long incrementaldelaunay(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri starttri; - vertex vertexloop; - - /* Create a triangular bounding box. */ - boundingbox(m, b); - if (b->verbose) { - printf(" Incrementally inserting vertices.\n"); - } - traversalinit(&m->vertices); - vertexloop = vertextraverse(m); - while (vertexloop != (vertex) NULL) { - starttri.tri = m->dummytri; - if (insertvertex(m, b, vertexloop, &starttri, (struct osub *) NULL, 0, 0) - == DUPLICATEVERTEX) { - if (!b->quiet) { - printf( -"Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n", - vertexloop[0], vertexloop[1]); - } - setvertextype(vertexloop, UNDEADVERTEX); - m->undeads++; - } - vertexloop = vertextraverse(m); - } - /* Remove the bounding box. */ - return removebox(m, b); -} - -#endif /* not REDUCED */ - -/** **/ -/** **/ -/********* Incremental Delaunay triangulation ends here *********/ - -/********* Sweepline Delaunay triangulation begins here *********/ -/** **/ -/** **/ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -void eventheapinsert(struct event **heap, int heapsize, struct event *newevent) -#else /* not ANSI_DECLARATORS */ -void eventheapinsert(heap, heapsize, newevent) -struct event **heap; -int heapsize; -struct event *newevent; -#endif /* not ANSI_DECLARATORS */ - -{ - REAL eventx, eventy; - int eventnum; - int parent; - int notdone; - - eventx = newevent->xkey; - eventy = newevent->ykey; - eventnum = heapsize; - notdone = eventnum > 0; - while (notdone) { - parent = (eventnum - 1) >> 1; - if ((heap[parent]->ykey < eventy) || - ((heap[parent]->ykey == eventy) - && (heap[parent]->xkey <= eventx))) { - notdone = 0; - } else { - heap[eventnum] = heap[parent]; - heap[eventnum]->heapposition = eventnum; - - eventnum = parent; - notdone = eventnum > 0; - } - } - heap[eventnum] = newevent; - newevent->heapposition = eventnum; -} - -#endif /* not REDUCED */ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -void eventheapify(struct event **heap, int heapsize, int eventnum) -#else /* not ANSI_DECLARATORS */ -void eventheapify(heap, heapsize, eventnum) -struct event **heap; -int heapsize; -int eventnum; -#endif /* not ANSI_DECLARATORS */ - -{ - struct event *thisevent; - REAL eventx, eventy; - int leftchild, rightchild; - int smallest; - int notdone; - - thisevent = heap[eventnum]; - eventx = thisevent->xkey; - eventy = thisevent->ykey; - leftchild = 2 * eventnum + 1; - notdone = leftchild < heapsize; - while (notdone) { - if ((heap[leftchild]->ykey < eventy) || - ((heap[leftchild]->ykey == eventy) - && (heap[leftchild]->xkey < eventx))) { - smallest = leftchild; - } else { - smallest = eventnum; - } - rightchild = leftchild + 1; - if (rightchild < heapsize) { - if ((heap[rightchild]->ykey < heap[smallest]->ykey) || - ((heap[rightchild]->ykey == heap[smallest]->ykey) - && (heap[rightchild]->xkey < heap[smallest]->xkey))) { - smallest = rightchild; - } - } - if (smallest == eventnum) { - notdone = 0; - } else { - heap[eventnum] = heap[smallest]; - heap[eventnum]->heapposition = eventnum; - heap[smallest] = thisevent; - thisevent->heapposition = smallest; - - eventnum = smallest; - leftchild = 2 * eventnum + 1; - notdone = leftchild < heapsize; - } - } -} - -#endif /* not REDUCED */ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -void eventheapdelete(struct event **heap, int heapsize, int eventnum) -#else /* not ANSI_DECLARATORS */ -void eventheapdelete(heap, heapsize, eventnum) -struct event **heap; -int heapsize; -int eventnum; -#endif /* not ANSI_DECLARATORS */ - -{ - struct event *moveevent; - REAL eventx, eventy; - int parent; - int notdone; - - moveevent = heap[heapsize - 1]; - if (eventnum > 0) { - eventx = moveevent->xkey; - eventy = moveevent->ykey; - do { - parent = (eventnum - 1) >> 1; - if ((heap[parent]->ykey < eventy) || - ((heap[parent]->ykey == eventy) - && (heap[parent]->xkey <= eventx))) { - notdone = 0; - } else { - heap[eventnum] = heap[parent]; - heap[eventnum]->heapposition = eventnum; - - eventnum = parent; - notdone = eventnum > 0; - } - } while (notdone); - } - heap[eventnum] = moveevent; - moveevent->heapposition = eventnum; - eventheapify(heap, heapsize - 1, eventnum); -} - -#endif /* not REDUCED */ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -void createeventheap(struct mesh *m, struct event ***eventheap, - struct event **events, struct event **freeevents) -#else /* not ANSI_DECLARATORS */ -void createeventheap(m, eventheap, events, freeevents) -struct mesh *m; -struct event ***eventheap; -struct event **events; -struct event **freeevents; -#endif /* not ANSI_DECLARATORS */ - -{ - vertex thisvertex; - int maxevents; - int i; - - maxevents = (3 * m->invertices) / 2; - *eventheap = (struct event **) trimalloc(maxevents * - (int) sizeof(struct event *)); - *events = (struct event *) trimalloc(maxevents * (int) sizeof(struct event)); - traversalinit(&m->vertices); - for (i = 0; i < m->invertices; i++) { - thisvertex = vertextraverse(m); - (*events)[i].eventptr = (VOID *) thisvertex; - (*events)[i].xkey = thisvertex[0]; - (*events)[i].ykey = thisvertex[1]; - eventheapinsert(*eventheap, i, *events + i); - } - *freeevents = (struct event *) NULL; - for (i = maxevents - 1; i >= m->invertices; i--) { - (*events)[i].eventptr = (VOID *) *freeevents; - *freeevents = *events + i; - } -} - -#endif /* not REDUCED */ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -int rightofhyperbola(struct mesh *m, struct otri *fronttri, vertex newsite) -#else /* not ANSI_DECLARATORS */ -int rightofhyperbola(m, fronttri, newsite) -struct mesh *m; -struct otri *fronttri; -vertex newsite; -#endif /* not ANSI_DECLARATORS */ - -{ - vertex leftvertex, rightvertex; - REAL dxa, dya, dxb, dyb; - - m->hyperbolacount++; - - dest(*fronttri, leftvertex); - apex(*fronttri, rightvertex); - if ((leftvertex[1] < rightvertex[1]) || - ((leftvertex[1] == rightvertex[1]) && - (leftvertex[0] < rightvertex[0]))) { - if (newsite[0] >= rightvertex[0]) { - return 1; - } - } else { - if (newsite[0] <= leftvertex[0]) { - return 0; - } - } - dxa = leftvertex[0] - newsite[0]; - dya = leftvertex[1] - newsite[1]; - dxb = rightvertex[0] - newsite[0]; - dyb = rightvertex[1] - newsite[1]; - return dya * (dxb * dxb + dyb * dyb) > dyb * (dxa * dxa + dya * dya); -} - -#endif /* not REDUCED */ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -REAL circletop(struct mesh *m, vertex pa, vertex pb, vertex pc, REAL ccwabc) -#else /* not ANSI_DECLARATORS */ -REAL circletop(m, pa, pb, pc, ccwabc) -struct mesh *m; -vertex pa; -vertex pb; -vertex pc; -REAL ccwabc; -#endif /* not ANSI_DECLARATORS */ - -{ - REAL xac, yac, xbc, ybc, xab, yab; - REAL aclen2, bclen2, ablen2; - - m->circletopcount++; - - xac = pa[0] - pc[0]; - yac = pa[1] - pc[1]; - xbc = pb[0] - pc[0]; - ybc = pb[1] - pc[1]; - xab = pa[0] - pb[0]; - yab = pa[1] - pb[1]; - aclen2 = xac * xac + yac * yac; - bclen2 = xbc * xbc + ybc * ybc; - ablen2 = xab * xab + yab * yab; - return pc[1] + (xac * bclen2 - xbc * aclen2 + sqrt(aclen2 * bclen2 * ablen2)) - / (2.0 * ccwabc); -} - -#endif /* not REDUCED */ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -void check4deadevent(struct otri *checktri, struct event **freeevents, - struct event **eventheap, int *heapsize) -#else /* not ANSI_DECLARATORS */ -void check4deadevent(checktri, freeevents, eventheap, heapsize) -struct otri *checktri; -struct event **freeevents; -struct event **eventheap; -int *heapsize; -#endif /* not ANSI_DECLARATORS */ - -{ - struct event *deadevent; - vertex eventvertex; - int eventnum; - - org(*checktri, eventvertex); - if (eventvertex != (vertex) NULL) { - deadevent = (struct event *) eventvertex; - eventnum = deadevent->heapposition; - deadevent->eventptr = (VOID *) *freeevents; - *freeevents = deadevent; - eventheapdelete(eventheap, *heapsize, eventnum); - (*heapsize)--; - setorg(*checktri, NULL); - } -} - -#endif /* not REDUCED */ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -struct splaynode *splay(struct mesh *m, struct splaynode *splaytree, - vertex searchpoint, struct otri *searchtri) -#else /* not ANSI_DECLARATORS */ -struct splaynode *splay(m, splaytree, searchpoint, searchtri) -struct mesh *m; -struct splaynode *splaytree; -vertex searchpoint; -struct otri *searchtri; -#endif /* not ANSI_DECLARATORS */ - -{ - struct splaynode *child, *grandchild; - struct splaynode *lefttree, *righttree; - struct splaynode *leftright; - vertex checkvertex; - int rightofroot, rightofchild; - - if (splaytree == (struct splaynode *) NULL) { - return (struct splaynode *) NULL; - } - dest(splaytree->keyedge, checkvertex); - if (checkvertex == splaytree->keydest) { - rightofroot = rightofhyperbola(m, &splaytree->keyedge, searchpoint); - if (rightofroot) { - otricopy(splaytree->keyedge, *searchtri); - child = splaytree->rchild; - } else { - child = splaytree->lchild; - } - if (child == (struct splaynode *) NULL) { - return splaytree; - } - dest(child->keyedge, checkvertex); - if (checkvertex != child->keydest) { - child = splay(m, child, searchpoint, searchtri); - if (child == (struct splaynode *) NULL) { - if (rightofroot) { - splaytree->rchild = (struct splaynode *) NULL; - } else { - splaytree->lchild = (struct splaynode *) NULL; - } - return splaytree; - } - } - rightofchild = rightofhyperbola(m, &child->keyedge, searchpoint); - if (rightofchild) { - otricopy(child->keyedge, *searchtri); - grandchild = splay(m, child->rchild, searchpoint, searchtri); - child->rchild = grandchild; - } else { - grandchild = splay(m, child->lchild, searchpoint, searchtri); - child->lchild = grandchild; - } - if (grandchild == (struct splaynode *) NULL) { - if (rightofroot) { - splaytree->rchild = child->lchild; - child->lchild = splaytree; - } else { - splaytree->lchild = child->rchild; - child->rchild = splaytree; - } - return child; - } - if (rightofchild) { - if (rightofroot) { - splaytree->rchild = child->lchild; - child->lchild = splaytree; - } else { - splaytree->lchild = grandchild->rchild; - grandchild->rchild = splaytree; - } - child->rchild = grandchild->lchild; - grandchild->lchild = child; - } else { - if (rightofroot) { - splaytree->rchild = grandchild->lchild; - grandchild->lchild = splaytree; - } else { - splaytree->lchild = child->rchild; - child->rchild = splaytree; - } - child->lchild = grandchild->rchild; - grandchild->rchild = child; - } - return grandchild; - } else { - lefttree = splay(m, splaytree->lchild, searchpoint, searchtri); - righttree = splay(m, splaytree->rchild, searchpoint, searchtri); - - pooldealloc(&m->splaynodes, (VOID *) splaytree); - if (lefttree == (struct splaynode *) NULL) { - return righttree; - } else if (righttree == (struct splaynode *) NULL) { - return lefttree; - } else if (lefttree->rchild == (struct splaynode *) NULL) { - lefttree->rchild = righttree->lchild; - righttree->lchild = lefttree; - return righttree; - } else if (righttree->lchild == (struct splaynode *) NULL) { - righttree->lchild = lefttree->rchild; - lefttree->rchild = righttree; - return lefttree; - } else { -/* printf("Holy Toledo!!!\n"); */ - leftright = lefttree->rchild; - while (leftright->rchild != (struct splaynode *) NULL) { - leftright = leftright->rchild; - } - leftright->rchild = righttree; - return lefttree; - } - } -} - -#endif /* not REDUCED */ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -struct splaynode *splayinsert(struct mesh *m, struct splaynode *splayroot, - struct otri *newkey, vertex searchpoint) -#else /* not ANSI_DECLARATORS */ -struct splaynode *splayinsert(m, splayroot, newkey, searchpoint) -struct mesh *m; -struct splaynode *splayroot; -struct otri *newkey; -vertex searchpoint; -#endif /* not ANSI_DECLARATORS */ - -{ - struct splaynode *newsplaynode; - - newsplaynode = (struct splaynode *) poolalloc(&m->splaynodes); - otricopy(*newkey, newsplaynode->keyedge); - dest(*newkey, newsplaynode->keydest); - if (splayroot == (struct splaynode *) NULL) { - newsplaynode->lchild = (struct splaynode *) NULL; - newsplaynode->rchild = (struct splaynode *) NULL; - } else if (rightofhyperbola(m, &splayroot->keyedge, searchpoint)) { - newsplaynode->lchild = splayroot; - newsplaynode->rchild = splayroot->rchild; - splayroot->rchild = (struct splaynode *) NULL; - } else { - newsplaynode->lchild = splayroot->lchild; - newsplaynode->rchild = splayroot; - splayroot->lchild = (struct splaynode *) NULL; - } - return newsplaynode; -} - -#endif /* not REDUCED */ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -struct splaynode *circletopinsert(struct mesh *m, struct behavior *b, - struct splaynode *splayroot, - struct otri *newkey, - vertex pa, vertex pb, vertex pc, REAL topy) -#else /* not ANSI_DECLARATORS */ -struct splaynode *circletopinsert(m, b, splayroot, newkey, pa, pb, pc, topy) -struct mesh *m; -struct behavior *b; -struct splaynode *splayroot; -struct otri *newkey; -vertex pa; -vertex pb; -vertex pc; -REAL topy; -#endif /* not ANSI_DECLARATORS */ - -{ - REAL ccwabc; - REAL xac, yac, xbc, ybc; - REAL aclen2, bclen2; - REAL searchpoint[2]; - struct otri dummytri; - - ccwabc = counterclockwise(m, b, pa, pb, pc); - xac = pa[0] - pc[0]; - yac = pa[1] - pc[1]; - xbc = pb[0] - pc[0]; - ybc = pb[1] - pc[1]; - aclen2 = xac * xac + yac * yac; - bclen2 = xbc * xbc + ybc * ybc; - searchpoint[0] = pc[0] - (yac * bclen2 - ybc * aclen2) / (2.0 * ccwabc); - searchpoint[1] = topy; - return splayinsert(m, splay(m, splayroot, (vertex) searchpoint, &dummytri), - newkey, (vertex) searchpoint); -} - -#endif /* not REDUCED */ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -struct splaynode *frontlocate(struct mesh *m, struct splaynode *splayroot, - struct otri *bottommost, vertex searchvertex, - struct otri *searchtri, int *farright) -#else /* not ANSI_DECLARATORS */ -struct splaynode *frontlocate(m, splayroot, bottommost, searchvertex, - searchtri, farright) -struct mesh *m; -struct splaynode *splayroot; -struct otri *bottommost; -vertex searchvertex; -struct otri *searchtri; -int *farright; -#endif /* not ANSI_DECLARATORS */ - -{ - int farrightflag; - triangle ptr; /* Temporary variable used by onext(). */ - - otricopy(*bottommost, *searchtri); - splayroot = splay(m, splayroot, searchvertex, searchtri); - - farrightflag = 0; - while (!farrightflag && rightofhyperbola(m, searchtri, searchvertex)) { - onextself(*searchtri); - farrightflag = otriequal(*searchtri, *bottommost); - } - *farright = farrightflag; - return splayroot; -} - -#endif /* not REDUCED */ - -#ifndef REDUCED - -#ifdef ANSI_DECLARATORS -long sweeplinedelaunay(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -long sweeplinedelaunay(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct event **eventheap; - struct event *events; - struct event *freeevents; - struct event *nextevent; - struct event *newevent; - struct splaynode *splayroot; - struct otri bottommost; - struct otri searchtri; - struct otri fliptri; - struct otri lefttri, righttri, farlefttri, farrighttri; - struct otri inserttri; - vertex firstvertex, secondvertex; - vertex nextvertex, lastvertex; - vertex connectvertex; - vertex leftvertex, midvertex, rightvertex; - REAL lefttest, righttest; - int heapsize; - int check4events, farrightflag; - triangle ptr; /* Temporary variable used by sym(), onext(), and oprev(). */ - - poolinit(&m->splaynodes, sizeof(struct splaynode), SPLAYNODEPERBLOCK, - SPLAYNODEPERBLOCK, 0); - splayroot = (struct splaynode *) NULL; - - if (b->verbose) { - printf(" Placing vertices in event heap.\n"); - } - createeventheap(m, &eventheap, &events, &freeevents); - heapsize = m->invertices; - - if (b->verbose) { - printf(" Forming triangulation.\n"); - } - maketriangle(m, b, &lefttri); - maketriangle(m, b, &righttri); - bond(lefttri, righttri); - lnextself(lefttri); - lprevself(righttri); - bond(lefttri, righttri); - lnextself(lefttri); - lprevself(righttri); - bond(lefttri, righttri); - firstvertex = (vertex) eventheap[0]->eventptr; - eventheap[0]->eventptr = (VOID *) freeevents; - freeevents = eventheap[0]; - eventheapdelete(eventheap, heapsize, 0); - heapsize--; - do { - if (heapsize == 0) { - printf("Error: Input vertices are all identical.\n"); - triexit(1); - } - secondvertex = (vertex) eventheap[0]->eventptr; - eventheap[0]->eventptr = (VOID *) freeevents; - freeevents = eventheap[0]; - eventheapdelete(eventheap, heapsize, 0); - heapsize--; - if ((firstvertex[0] == secondvertex[0]) && - (firstvertex[1] == secondvertex[1])) { - if (!b->quiet) { - printf( -"Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n", - secondvertex[0], secondvertex[1]); - } - setvertextype(secondvertex, UNDEADVERTEX); - m->undeads++; - } - } while ((firstvertex[0] == secondvertex[0]) && - (firstvertex[1] == secondvertex[1])); - setorg(lefttri, firstvertex); - setdest(lefttri, secondvertex); - setorg(righttri, secondvertex); - setdest(righttri, firstvertex); - lprev(lefttri, bottommost); - lastvertex = secondvertex; - while (heapsize > 0) { - nextevent = eventheap[0]; - eventheapdelete(eventheap, heapsize, 0); - heapsize--; - check4events = 1; - if (nextevent->xkey < m->xmin) { - decode(nextevent->eventptr, fliptri); - oprev(fliptri, farlefttri); - check4deadevent(&farlefttri, &freeevents, eventheap, &heapsize); - onext(fliptri, farrighttri); - check4deadevent(&farrighttri, &freeevents, eventheap, &heapsize); - - if (otriequal(farlefttri, bottommost)) { - lprev(fliptri, bottommost); - } - flip(m, b, &fliptri); - setapex(fliptri, NULL); - lprev(fliptri, lefttri); - lnext(fliptri, righttri); - sym(lefttri, farlefttri); - - if (randomnation(SAMPLERATE) == 0) { - symself(fliptri); - dest(fliptri, leftvertex); - apex(fliptri, midvertex); - org(fliptri, rightvertex); - splayroot = circletopinsert(m, b, splayroot, &lefttri, leftvertex, - midvertex, rightvertex, nextevent->ykey); - } - } else { - nextvertex = (vertex) nextevent->eventptr; - if ((nextvertex[0] == lastvertex[0]) && - (nextvertex[1] == lastvertex[1])) { - if (!b->quiet) { - printf( -"Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n", - nextvertex[0], nextvertex[1]); - } - setvertextype(nextvertex, UNDEADVERTEX); - m->undeads++; - check4events = 0; - } else { - lastvertex = nextvertex; - - splayroot = frontlocate(m, splayroot, &bottommost, nextvertex, - &searchtri, &farrightflag); -/* - otricopy(bottommost, searchtri); - farrightflag = 0; - while (!farrightflag && rightofhyperbola(m, &searchtri, nextvertex)) { - onextself(searchtri); - farrightflag = otriequal(searchtri, bottommost); - } -*/ - - check4deadevent(&searchtri, &freeevents, eventheap, &heapsize); - - otricopy(searchtri, farrighttri); - sym(searchtri, farlefttri); - maketriangle(m, b, &lefttri); - maketriangle(m, b, &righttri); - dest(farrighttri, connectvertex); - setorg(lefttri, connectvertex); - setdest(lefttri, nextvertex); - setorg(righttri, nextvertex); - setdest(righttri, connectvertex); - bond(lefttri, righttri); - lnextself(lefttri); - lprevself(righttri); - bond(lefttri, righttri); - lnextself(lefttri); - lprevself(righttri); - bond(lefttri, farlefttri); - bond(righttri, farrighttri); - if (!farrightflag && otriequal(farrighttri, bottommost)) { - otricopy(lefttri, bottommost); - } - - if (randomnation(SAMPLERATE) == 0) { - splayroot = splayinsert(m, splayroot, &lefttri, nextvertex); - } else if (randomnation(SAMPLERATE) == 0) { - lnext(righttri, inserttri); - splayroot = splayinsert(m, splayroot, &inserttri, nextvertex); - } - } - } - nextevent->eventptr = (VOID *) freeevents; - freeevents = nextevent; - - if (check4events) { - apex(farlefttri, leftvertex); - dest(lefttri, midvertex); - apex(lefttri, rightvertex); - lefttest = counterclockwise(m, b, leftvertex, midvertex, rightvertex); - if (lefttest > 0.0) { - newevent = freeevents; - freeevents = (struct event *) freeevents->eventptr; - newevent->xkey = m->xminextreme; - newevent->ykey = circletop(m, leftvertex, midvertex, rightvertex, - lefttest); - newevent->eventptr = (VOID *) encode(lefttri); - eventheapinsert(eventheap, heapsize, newevent); - heapsize++; - setorg(lefttri, newevent); - } - apex(righttri, leftvertex); - org(righttri, midvertex); - apex(farrighttri, rightvertex); - righttest = counterclockwise(m, b, leftvertex, midvertex, rightvertex); - if (righttest > 0.0) { - newevent = freeevents; - freeevents = (struct event *) freeevents->eventptr; - newevent->xkey = m->xminextreme; - newevent->ykey = circletop(m, leftvertex, midvertex, rightvertex, - righttest); - newevent->eventptr = (VOID *) encode(farrighttri); - eventheapinsert(eventheap, heapsize, newevent); - heapsize++; - setorg(farrighttri, newevent); - } - } - } - - pooldeinit(&m->splaynodes); - lprevself(bottommost); - return removeghosts(m, b, &bottommost); -} - -#endif /* not REDUCED */ - -/** **/ -/** **/ -/********* Sweepline Delaunay triangulation ends here *********/ - -/********* General mesh construction routines begin here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* delaunay() Form a Delaunay triangulation. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -long delaunay(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -long delaunay(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - long hulledges; - - m->eextras = 0; - initializetrisubpools(m, b); - -#ifdef REDUCED - if (!b->quiet) { - printf( - "Constructing Delaunay triangulation by divide-and-conquer method.\n"); - } - hulledges = divconqdelaunay(m, b); -#else /* not REDUCED */ - if (!b->quiet) { - printf("Constructing Delaunay triangulation "); - if (b->incremental) { - printf("by incremental method.\n"); - } else if (b->sweepline) { - printf("by sweepline method.\n"); - } else { - printf("by divide-and-conquer method.\n"); - } - } - if (b->incremental) { - hulledges = incrementaldelaunay(m, b); - } else if (b->sweepline) { - hulledges = sweeplinedelaunay(m, b); - } else { - hulledges = divconqdelaunay(m, b); - } -#endif /* not REDUCED */ - - if (m->triangles.items == 0) { - /* The input vertices were all collinear, so there are no triangles. */ - return 0l; - } else { - return hulledges; - } -} - -/*****************************************************************************/ -/* */ -/* reconstruct() Reconstruct a triangulation from its .ele (and possibly */ -/* .poly) file. Used when the -r switch is used. */ -/* */ -/* Reads an .ele file and reconstructs the original mesh. If the -p switch */ -/* is used, this procedure will also read a .poly file and reconstruct the */ -/* subsegments of the original mesh. If the -a switch is used, this */ -/* procedure will also read an .area file and set a maximum area constraint */ -/* on each triangle. */ -/* */ -/* Vertices that are not corners of triangles, such as nodes on edges of */ -/* subparametric elements, are discarded. */ -/* */ -/* This routine finds the adjacencies between triangles (and subsegments) */ -/* by forming one stack of triangles for each vertex. Each triangle is on */ -/* three different stacks simultaneously. Each triangle's subsegment */ -/* pointers are used to link the items in each stack. This memory-saving */ -/* feature makes the code harder to read. The most important thing to keep */ -/* in mind is that each triangle is removed from a stack precisely when */ -/* the corresponding pointer is adjusted to refer to a subsegment rather */ -/* than the next triangle of the stack. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef TRILIBRARY - -#ifdef ANSI_DECLARATORS -int reconstruct(struct mesh *m, struct behavior *b, int *trianglelist, - REAL *triangleattriblist, REAL *trianglearealist, - int elements, int corners, int attribs, - int *segmentlist,int *segmentmarkerlist, int numberofsegments) -#else /* not ANSI_DECLARATORS */ -int reconstruct(m, b, trianglelist, triangleattriblist, trianglearealist, - elements, corners, attribs, segmentlist, segmentmarkerlist, - numberofsegments) -struct mesh *m; -struct behavior *b; -int *trianglelist; -REAL *triangleattriblist; -REAL *trianglearealist; -int elements; -int corners; -int attribs; -int *segmentlist; -int *segmentmarkerlist; -int numberofsegments; -#endif /* not ANSI_DECLARATORS */ - -#else /* not TRILIBRARY */ - -#ifdef ANSI_DECLARATORS -long reconstruct(struct mesh *m, struct behavior *b, char *elefilename, - char *areafilename, char *polyfilename, FILE *polyfile) -#else /* not ANSI_DECLARATORS */ -long reconstruct(m, b, elefilename, areafilename, polyfilename, polyfile) -struct mesh *m; -struct behavior *b; -char *elefilename; -char *areafilename; -char *polyfilename; -FILE *polyfile; -#endif /* not ANSI_DECLARATORS */ - -#endif /* not TRILIBRARY */ - -{ -#ifdef TRILIBRARY - int vertexindex; - int attribindex; -#else /* not TRILIBRARY */ - FILE *elefile; - FILE *areafile; - char inputline[INPUTLINESIZE]; - char *stringptr; - int areaelements; -#endif /* not TRILIBRARY */ - struct otri triangleloop; - struct otri triangleleft; - struct otri checktri; - struct otri checkleft; - struct otri checkneighbor; - struct osub subsegloop; - triangle *vertexarray; - triangle *prevlink; - triangle nexttri; - vertex tdest, tapex; - vertex checkdest, checkapex; - vertex shorg; - vertex killvertex; - vertex segmentorg, segmentdest; - REAL area; - int corner[3]; - int end[2]; - int killvertexindex; - int incorners; - int segmentmarkers; - int boundmarker; - int aroundvertex; - long hullsize; - int notfound; - long elementnumber, segmentnumber; - int i, j; - triangle ptr; /* Temporary variable used by sym(). */ - -#ifdef TRILIBRARY - m->inelements = elements; - incorners = corners; - if (incorners < 3) { - printf("Error: Triangles must have at least 3 vertices.\n"); - triexit(1); - } - m->eextras = attribs; -#else /* not TRILIBRARY */ - /* Read the triangles from an .ele file. */ - if (!b->quiet) { - printf("Opening %s.\n", elefilename); - } - elefile = fopen(elefilename, "r"); - if (elefile == (FILE *) NULL) { - printf(" Error: Cannot access file %s.\n", elefilename); - triexit(1); - } - /* Read number of triangles, number of vertices per triangle, and */ - /* number of triangle attributes from .ele file. */ - stringptr = readline(inputline, elefile, elefilename); - m->inelements = (int) strtol(stringptr, &stringptr, 0); - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - incorners = 3; - } else { - incorners = (int) strtol(stringptr, &stringptr, 0); - if (incorners < 3) { - printf("Error: Triangles in %s must have at least 3 vertices.\n", - elefilename); - triexit(1); - } - } - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - m->eextras = 0; - } else { - m->eextras = (int) strtol(stringptr, &stringptr, 0); - } -#endif /* not TRILIBRARY */ - - initializetrisubpools(m, b); - - /* Create the triangles. */ - for (elementnumber = 1; elementnumber <= m->inelements; elementnumber++) { - maketriangle(m, b, &triangleloop); - /* Mark the triangle as living. */ - triangleloop.tri[3] = (triangle) triangleloop.tri; - } - - segmentmarkers = 0; - if (b->poly) { -#ifdef TRILIBRARY - m->insegments = numberofsegments; - segmentmarkers = segmentmarkerlist != (int *) NULL; -#else /* not TRILIBRARY */ - /* Read number of segments and number of segment */ - /* boundary markers from .poly file. */ - stringptr = readline(inputline, polyfile, b->inpolyfilename); - m->insegments = (int) strtol(stringptr, &stringptr, 0); - stringptr = findfield(stringptr); - if (*stringptr != '\0') { - segmentmarkers = (int) strtol(stringptr, &stringptr, 0); - } -#endif /* not TRILIBRARY */ - - /* Create the subsegments. */ - for (segmentnumber = 1; segmentnumber <= m->insegments; segmentnumber++) { - makesubseg(m, &subsegloop); - /* Mark the subsegment as living. */ - subsegloop.ss[2] = (subseg) subsegloop.ss; - } - } - -#ifdef TRILIBRARY - vertexindex = 0; - attribindex = 0; -#else /* not TRILIBRARY */ - if (b->vararea) { - /* Open an .area file, check for consistency with the .ele file. */ - if (!b->quiet) { - printf("Opening %s.\n", areafilename); - } - areafile = fopen(areafilename, "r"); - if (areafile == (FILE *) NULL) { - printf(" Error: Cannot access file %s.\n", areafilename); - triexit(1); - } - stringptr = readline(inputline, areafile, areafilename); - areaelements = (int) strtol(stringptr, &stringptr, 0); - if (areaelements != m->inelements) { - printf("Error: %s and %s disagree on number of triangles.\n", - elefilename, areafilename); - triexit(1); - } - } -#endif /* not TRILIBRARY */ - - if (!b->quiet) { - printf("Reconstructing mesh.\n"); - } - /* Allocate a temporary array that maps each vertex to some adjacent */ - /* triangle. I took care to allocate all the permanent memory for */ - /* triangles and subsegments first. */ - vertexarray = (triangle *) trimalloc(m->vertices.items * - (int) sizeof(triangle)); - /* Each vertex is initially unrepresented. */ - for (i = 0; i < m->vertices.items; i++) { - vertexarray[i] = (triangle) m->dummytri; - } - - if (b->verbose) { - printf(" Assembling triangles.\n"); - } - /* Read the triangles from the .ele file, and link */ - /* together those that share an edge. */ - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - elementnumber = b->firstnumber; - while (triangleloop.tri != (triangle *) NULL) { -#ifdef TRILIBRARY - /* Copy the triangle's three corners. */ - for (j = 0; j < 3; j++) { - corner[j] = trianglelist[vertexindex++]; - if ((corner[j] < b->firstnumber) || - (corner[j] >= b->firstnumber + m->invertices)) { - printf("Error: Triangle %ld has an invalid vertex index.\n", - elementnumber); - triexit(1); - } - } -#else /* not TRILIBRARY */ - /* Read triangle number and the triangle's three corners. */ - stringptr = readline(inputline, elefile, elefilename); - for (j = 0; j < 3; j++) { - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - printf("Error: Triangle %ld is missing vertex %d in %s.\n", - elementnumber, j + 1, elefilename); - triexit(1); - } else { - corner[j] = (int) strtol(stringptr, &stringptr, 0); - if ((corner[j] < b->firstnumber) || - (corner[j] >= b->firstnumber + m->invertices)) { - printf("Error: Triangle %ld has an invalid vertex index.\n", - elementnumber); - triexit(1); - } - } - } -#endif /* not TRILIBRARY */ - - /* Find out about (and throw away) extra nodes. */ - for (j = 3; j < incorners; j++) { -#ifdef TRILIBRARY - killvertexindex = trianglelist[vertexindex++]; -#else /* not TRILIBRARY */ - stringptr = findfield(stringptr); - if (*stringptr != '\0') { - killvertexindex = (int) strtol(stringptr, &stringptr, 0); -#endif /* not TRILIBRARY */ - if ((killvertexindex >= b->firstnumber) && - (killvertexindex < b->firstnumber + m->invertices)) { - /* Delete the non-corner vertex if it's not already deleted. */ - killvertex = getvertex(m, b, killvertexindex); - if (vertextype(killvertex) != DEADVERTEX) { - vertexdealloc(m, killvertex); - } - } -#ifndef TRILIBRARY - } -#endif /* not TRILIBRARY */ - } - - /* Read the triangle's attributes. */ - for (j = 0; j < m->eextras; j++) { -#ifdef TRILIBRARY - setelemattribute(triangleloop, j, triangleattriblist[attribindex++]); -#else /* not TRILIBRARY */ - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - setelemattribute(triangleloop, j, 0); - } else { - setelemattribute(triangleloop, j, - (REAL) strtod(stringptr, &stringptr)); - } -#endif /* not TRILIBRARY */ - } - - if (b->vararea) { -#ifdef TRILIBRARY - area = trianglearealist[elementnumber - b->firstnumber]; -#else /* not TRILIBRARY */ - /* Read an area constraint from the .area file. */ - stringptr = readline(inputline, areafile, areafilename); - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - area = -1.0; /* No constraint on this triangle. */ - } else { - area = (REAL) strtod(stringptr, &stringptr); - } -#endif /* not TRILIBRARY */ - setareabound(triangleloop, area); - } - - /* Set the triangle's vertices. */ - triangleloop.orient = 0; - setorg(triangleloop, getvertex(m, b, corner[0])); - setdest(triangleloop, getvertex(m, b, corner[1])); - setapex(triangleloop, getvertex(m, b, corner[2])); - /* Try linking the triangle to others that share these vertices. */ - for (triangleloop.orient = 0; triangleloop.orient < 3; - triangleloop.orient++) { - /* Take the number for the origin of triangleloop. */ - aroundvertex = corner[triangleloop.orient]; - /* Look for other triangles having this vertex. */ - nexttri = vertexarray[aroundvertex - b->firstnumber]; - /* Link the current triangle to the next one in the stack. */ - triangleloop.tri[6 + triangleloop.orient] = nexttri; - /* Push the current triangle onto the stack. */ - vertexarray[aroundvertex - b->firstnumber] = encode(triangleloop); - decode(nexttri, checktri); - if (checktri.tri != m->dummytri) { - dest(triangleloop, tdest); - apex(triangleloop, tapex); - /* Look for other triangles that share an edge. */ - do { - dest(checktri, checkdest); - apex(checktri, checkapex); - if (tapex == checkdest) { - /* The two triangles share an edge; bond them together. */ - lprev(triangleloop, triangleleft); - bond(triangleleft, checktri); - } - if (tdest == checkapex) { - /* The two triangles share an edge; bond them together. */ - lprev(checktri, checkleft); - bond(triangleloop, checkleft); - } - /* Find the next triangle in the stack. */ - nexttri = checktri.tri[6 + checktri.orient]; - decode(nexttri, checktri); - } while (checktri.tri != m->dummytri); - } - } - triangleloop.tri = triangletraverse(m); - elementnumber++; - } - -#ifdef TRILIBRARY - vertexindex = 0; -#else /* not TRILIBRARY */ - fclose(elefile); - if (b->vararea) { - fclose(areafile); - } -#endif /* not TRILIBRARY */ - - hullsize = 0; /* Prepare to count the boundary edges. */ - if (b->poly) { - if (b->verbose) { - printf(" Marking segments in triangulation.\n"); - } - /* Read the segments from the .poly file, and link them */ - /* to their neighboring triangles. */ - boundmarker = 0; - traversalinit(&m->subsegs); - subsegloop.ss = subsegtraverse(m); - segmentnumber = b->firstnumber; - while (subsegloop.ss != (subseg *) NULL) { -#ifdef TRILIBRARY - end[0] = segmentlist[vertexindex++]; - end[1] = segmentlist[vertexindex++]; - if (segmentmarkers) { - boundmarker = segmentmarkerlist[segmentnumber - b->firstnumber]; - } -#else /* not TRILIBRARY */ - /* Read the endpoints of each segment, and possibly a boundary marker. */ - stringptr = readline(inputline, polyfile, b->inpolyfilename); - /* Skip the first (segment number) field. */ - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - printf("Error: Segment %ld has no endpoints in %s.\n", segmentnumber, - polyfilename); - triexit(1); - } else { - end[0] = (int) strtol(stringptr, &stringptr, 0); - } - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - printf("Error: Segment %ld is missing its second endpoint in %s.\n", - segmentnumber, polyfilename); - triexit(1); - } else { - end[1] = (int) strtol(stringptr, &stringptr, 0); - } - if (segmentmarkers) { - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - boundmarker = 0; - } else { - boundmarker = (int) strtol(stringptr, &stringptr, 0); - } - } -#endif /* not TRILIBRARY */ - for (j = 0; j < 2; j++) { - if ((end[j] < b->firstnumber) || - (end[j] >= b->firstnumber + m->invertices)) { - printf("Error: Segment %ld has an invalid vertex index.\n", - segmentnumber); - triexit(1); - } - } - - /* set the subsegment's vertices. */ - subsegloop.ssorient = 0; - segmentorg = getvertex(m, b, end[0]); - segmentdest = getvertex(m, b, end[1]); - setsorg(subsegloop, segmentorg); - setsdest(subsegloop, segmentdest); - setsegorg(subsegloop, segmentorg); - setsegdest(subsegloop, segmentdest); - setmark(subsegloop, boundmarker); - /* Try linking the subsegment to triangles that share these vertices. */ - for (subsegloop.ssorient = 0; subsegloop.ssorient < 2; - subsegloop.ssorient++) { - /* Take the number for the destination of subsegloop. */ - aroundvertex = end[1 - subsegloop.ssorient]; - /* Look for triangles having this vertex. */ - prevlink = &vertexarray[aroundvertex - b->firstnumber]; - nexttri = vertexarray[aroundvertex - b->firstnumber]; - decode(nexttri, checktri); - sorg(subsegloop, shorg); - notfound = 1; - /* Look for triangles having this edge. Note that I'm only */ - /* comparing each triangle's destination with the subsegment; */ - /* each triangle's apex is handled through a different vertex. */ - /* Because each triangle appears on three vertices' lists, each */ - /* occurrence of a triangle on a list can (and does) represent */ - /* an edge. In this way, most edges are represented twice, and */ - /* every triangle-subsegment bond is represented once. */ - while (notfound && (checktri.tri != m->dummytri)) { - dest(checktri, checkdest); - if (shorg == checkdest) { - /* We have a match. Remove this triangle from the list. */ - *prevlink = checktri.tri[6 + checktri.orient]; - /* Bond the subsegment to the triangle. */ - tsbond(checktri, subsegloop); - /* Check if this is a boundary edge. */ - sym(checktri, checkneighbor); - if (checkneighbor.tri == m->dummytri) { - /* The next line doesn't insert a subsegment (because there's */ - /* already one there), but it sets the boundary markers of */ - /* the existing subsegment and its vertices. */ - insertsubseg(m, b, &checktri, 1); - hullsize++; - } - notfound = 0; - } - /* Find the next triangle in the stack. */ - prevlink = &checktri.tri[6 + checktri.orient]; - nexttri = checktri.tri[6 + checktri.orient]; - decode(nexttri, checktri); - } - } - subsegloop.ss = subsegtraverse(m); - segmentnumber++; - } - } - - /* Mark the remaining edges as not being attached to any subsegment. */ - /* Also, count the (yet uncounted) boundary edges. */ - for (i = 0; i < m->vertices.items; i++) { - /* Search the stack of triangles adjacent to a vertex. */ - nexttri = vertexarray[i]; - decode(nexttri, checktri); - while (checktri.tri != m->dummytri) { - /* Find the next triangle in the stack before this */ - /* information gets overwritten. */ - nexttri = checktri.tri[6 + checktri.orient]; - /* No adjacent subsegment. (This overwrites the stack info.) */ - tsdissolve(checktri); - sym(checktri, checkneighbor); - if (checkneighbor.tri == m->dummytri) { - insertsubseg(m, b, &checktri, 1); - hullsize++; - } - decode(nexttri, checktri); - } - } - - trifree((VOID *) vertexarray); - return hullsize; -} - -#endif /* not CDT_ONLY */ - -/** **/ -/** **/ -/********* General mesh construction routines end here *********/ - -/********* Segment insertion begins here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* finddirection() Find the first triangle on the path from one point */ -/* to another. */ -/* */ -/* Finds the triangle that intersects a line segment drawn from the */ -/* origin of `searchtri' to the point `searchpoint', and returns the result */ -/* in `searchtri'. The origin of `searchtri' does not change, even though */ -/* the triangle returned may differ from the one passed in. This routine */ -/* is used to find the direction to move in to get from one point to */ -/* another. */ -/* */ -/* The return value notes whether the destination or apex of the found */ -/* triangle is collinear with the two points in question. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -enum finddirectionresult finddirection(struct mesh *m, struct behavior *b, - struct otri *searchtri, - vertex searchpoint) -#else /* not ANSI_DECLARATORS */ -enum finddirectionresult finddirection(m, b, searchtri, searchpoint) -struct mesh *m; -struct behavior *b; -struct otri *searchtri; -vertex searchpoint; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri checktri; - vertex startvertex; - vertex leftvertex, rightvertex; - REAL leftccw, rightccw; - int leftflag, rightflag; - triangle ptr; /* Temporary variable used by onext() and oprev(). */ - - org(*searchtri, startvertex); - dest(*searchtri, rightvertex); - apex(*searchtri, leftvertex); - /* Is `searchpoint' to the left? */ - leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex); - leftflag = leftccw > 0.0; - /* Is `searchpoint' to the right? */ - rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex); - rightflag = rightccw > 0.0; - if (leftflag && rightflag) { - /* `searchtri' faces directly away from `searchpoint'. We could go left */ - /* or right. Ask whether it's a triangle or a boundary on the left. */ - onext(*searchtri, checktri); - if (checktri.tri == m->dummytri) { - leftflag = 0; - } else { - rightflag = 0; - } - } - while (leftflag) { - /* Turn left until satisfied. */ - onextself(*searchtri); - if (searchtri->tri == m->dummytri) { - printf("Internal error in finddirection(): Unable to find a\n"); - printf(" triangle leading from (%.12g, %.12g) to", startvertex[0], - startvertex[1]); - printf(" (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]); - internalerror(); - } - apex(*searchtri, leftvertex); - rightccw = leftccw; - leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex); - leftflag = leftccw > 0.0; - } - while (rightflag) { - /* Turn right until satisfied. */ - oprevself(*searchtri); - if (searchtri->tri == m->dummytri) { - printf("Internal error in finddirection(): Unable to find a\n"); - printf(" triangle leading from (%.12g, %.12g) to", startvertex[0], - startvertex[1]); - printf(" (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]); - internalerror(); - } - dest(*searchtri, rightvertex); - leftccw = rightccw; - rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex); - rightflag = rightccw > 0.0; - } - if (leftccw == 0.0) { - return LEFTCOLLINEAR; - } else if (rightccw == 0.0) { - return RIGHTCOLLINEAR; - } else { - return WITHIN; - } -} - -/*****************************************************************************/ -/* */ -/* segmentintersection() Find the intersection of an existing segment */ -/* and a segment that is being inserted. Insert */ -/* a vertex at the intersection, splitting an */ -/* existing subsegment. */ -/* */ -/* The segment being inserted connects the apex of splittri to endpoint2. */ -/* splitsubseg is the subsegment being split, and MUST adjoin splittri. */ -/* Hence, endpoints of the subsegment being split are the origin and */ -/* destination of splittri. */ -/* */ -/* On completion, splittri is a handle having the newly inserted */ -/* intersection point as its origin, and endpoint1 as its destination. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void segmentintersection(struct mesh *m, struct behavior *b, - struct otri *splittri, struct osub *splitsubseg, - vertex endpoint2) -#else /* not ANSI_DECLARATORS */ -void segmentintersection(m, b, splittri, splitsubseg, endpoint2) -struct mesh *m; -struct behavior *b; -struct otri *splittri; -struct osub *splitsubseg; -vertex endpoint2; -#endif /* not ANSI_DECLARATORS */ - -{ - struct osub opposubseg; - vertex endpoint1; - vertex torg, tdest; - vertex leftvertex, rightvertex; - vertex newvertex; - enum insertvertexresult success; - enum finddirectionresult collinear; - REAL ex, ey; - REAL tx, ty; - REAL etx, ety; - REAL split, denom; - int i; - triangle ptr; /* Temporary variable used by onext(). */ - subseg sptr; /* Temporary variable used by snext(). */ - - /* Find the other three segment endpoints. */ - apex(*splittri, endpoint1); - org(*splittri, torg); - dest(*splittri, tdest); - /* Segment intersection formulae; see the Antonio reference. */ - tx = tdest[0] - torg[0]; - ty = tdest[1] - torg[1]; - ex = endpoint2[0] - endpoint1[0]; - ey = endpoint2[1] - endpoint1[1]; - etx = torg[0] - endpoint2[0]; - ety = torg[1] - endpoint2[1]; - denom = ty * ex - tx * ey; - if (denom == 0.0) { - printf("Internal error in segmentintersection():"); - printf(" Attempt to find intersection of parallel segments.\n"); - internalerror(); - } - split = (ey * etx - ex * ety) / denom; - /* Create the new vertex. */ - newvertex = (vertex) poolalloc(&m->vertices); - /* Interpolate its coordinate and attributes. */ - for (i = 0; i < 2 + m->nextras; i++) { - newvertex[i] = torg[i] + split * (tdest[i] - torg[i]); - } - setvertexmark(newvertex, mark(*splitsubseg)); - setvertextype(newvertex, INPUTVERTEX); - if (b->verbose > 1) { - printf( - " Splitting subsegment (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n", - torg[0], torg[1], tdest[0], tdest[1], newvertex[0], newvertex[1]); - } - /* Insert the intersection vertex. This should always succeed. */ - success = insertvertex(m, b, newvertex, splittri, splitsubseg, 0, 0); - if (success != SUCCESSFULVERTEX) { - printf("Internal error in segmentintersection():\n"); - printf(" Failure to split a segment.\n"); - internalerror(); - } - /* Record a triangle whose origin is the new vertex. */ - setvertex2tri(newvertex, encode(*splittri)); - if (m->steinerleft > 0) { - m->steinerleft--; - } - - /* Divide the segment into two, and correct the segment endpoints. */ - ssymself(*splitsubseg); - spivot(*splitsubseg, opposubseg); - sdissolve(*splitsubseg); - sdissolve(opposubseg); - do { - setsegorg(*splitsubseg, newvertex); - snextself(*splitsubseg); - } while (splitsubseg->ss != m->dummysub); - do { - setsegorg(opposubseg, newvertex); - snextself(opposubseg); - } while (opposubseg.ss != m->dummysub); - - /* Inserting the vertex may have caused edge flips. We wish to rediscover */ - /* the edge connecting endpoint1 to the new intersection vertex. */ - collinear = finddirection(m, b, splittri, endpoint1); - dest(*splittri, rightvertex); - apex(*splittri, leftvertex); - if ((leftvertex[0] == endpoint1[0]) && (leftvertex[1] == endpoint1[1])) { - onextself(*splittri); - } else if ((rightvertex[0] != endpoint1[0]) || - (rightvertex[1] != endpoint1[1])) { - printf("Internal error in segmentintersection():\n"); - printf(" Topological inconsistency after splitting a segment.\n"); - internalerror(); - } - /* `splittri' should have destination endpoint1. */ -} - -/*****************************************************************************/ -/* */ -/* scoutsegment() Scout the first triangle on the path from one endpoint */ -/* to another, and check for completion (reaching the */ -/* second endpoint), a collinear vertex, or the */ -/* intersection of two segments. */ -/* */ -/* Returns one if the entire segment is successfully inserted, and zero if */ -/* the job must be finished by conformingedge() or constrainededge(). */ -/* */ -/* If the first triangle on the path has the second endpoint as its */ -/* destination or apex, a subsegment is inserted and the job is done. */ -/* */ -/* If the first triangle on the path has a destination or apex that lies on */ -/* the segment, a subsegment is inserted connecting the first endpoint to */ -/* the collinear vertex, and the search is continued from the collinear */ -/* vertex. */ -/* */ -/* If the first triangle on the path has a subsegment opposite its origin, */ -/* then there is a segment that intersects the segment being inserted. */ -/* Their intersection vertex is inserted, splitting the subsegment. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -int scoutsegment(struct mesh *m, struct behavior *b, struct otri *searchtri, - vertex endpoint2, int newmark) -#else /* not ANSI_DECLARATORS */ -int scoutsegment(m, b, searchtri, endpoint2, newmark) -struct mesh *m; -struct behavior *b; -struct otri *searchtri; -vertex endpoint2; -int newmark; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri crosstri; - struct osub crosssubseg; - vertex leftvertex, rightvertex; - enum finddirectionresult collinear; - subseg sptr; /* Temporary variable used by tspivot(). */ - - collinear = finddirection(m, b, searchtri, endpoint2); - dest(*searchtri, rightvertex); - apex(*searchtri, leftvertex); - if (((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) || - ((rightvertex[0] == endpoint2[0]) && (rightvertex[1] == endpoint2[1]))) { - /* The segment is already an edge in the mesh. */ - if ((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) { - lprevself(*searchtri); - } - /* Insert a subsegment, if there isn't already one there. */ - insertsubseg(m, b, searchtri, newmark); - return 1; - } else if (collinear == LEFTCOLLINEAR) { - /* We've collided with a vertex between the segment's endpoints. */ - /* Make the collinear vertex be the triangle's origin. */ - lprevself(*searchtri); - insertsubseg(m, b, searchtri, newmark); - /* Insert the remainder of the segment. */ - return scoutsegment(m, b, searchtri, endpoint2, newmark); - } else if (collinear == RIGHTCOLLINEAR) { - /* We've collided with a vertex between the segment's endpoints. */ - insertsubseg(m, b, searchtri, newmark); - /* Make the collinear vertex be the triangle's origin. */ - lnextself(*searchtri); - /* Insert the remainder of the segment. */ - return scoutsegment(m, b, searchtri, endpoint2, newmark); - } else { - lnext(*searchtri, crosstri); - tspivot(crosstri, crosssubseg); - /* Check for a crossing segment. */ - if (crosssubseg.ss == m->dummysub) { - return 0; - } else { - /* Insert a vertex at the intersection. */ - segmentintersection(m, b, &crosstri, &crosssubseg, endpoint2); - otricopy(crosstri, *searchtri); - insertsubseg(m, b, searchtri, newmark); - /* Insert the remainder of the segment. */ - return scoutsegment(m, b, searchtri, endpoint2, newmark); - } - } -} - -/*****************************************************************************/ -/* */ -/* conformingedge() Force a segment into a conforming Delaunay */ -/* triangulation by inserting a vertex at its midpoint, */ -/* and recursively forcing in the two half-segments if */ -/* necessary. */ -/* */ -/* Generates a sequence of subsegments connecting `endpoint1' to */ -/* `endpoint2'. `newmark' is the boundary marker of the segment, assigned */ -/* to each new splitting vertex and subsegment. */ -/* */ -/* Note that conformingedge() does not always maintain the conforming */ -/* Delaunay property. Once inserted, segments are locked into place; */ -/* vertices inserted later (to force other segments in) may render these */ -/* fixed segments non-Delaunay. The conforming Delaunay property will be */ -/* restored by enforcequality() by splitting encroached subsegments. */ -/* */ -/*****************************************************************************/ - -#ifndef REDUCED -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -void conformingedge(struct mesh *m, struct behavior *b, - vertex endpoint1, vertex endpoint2, int newmark) -#else /* not ANSI_DECLARATORS */ -void conformingedge(m, b, endpoint1, endpoint2, newmark) -struct mesh *m; -struct behavior *b; -vertex endpoint1; -vertex endpoint2; -int newmark; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri searchtri1, searchtri2; - struct osub brokensubseg; - vertex newvertex; - vertex midvertex1, midvertex2; - enum insertvertexresult success; - int i; - subseg sptr; /* Temporary variable used by tspivot(). */ - - if (b->verbose > 2) { - printf("Forcing segment into triangulation by recursive splitting:\n"); - printf(" (%.12g, %.12g) (%.12g, %.12g)\n", endpoint1[0], endpoint1[1], - endpoint2[0], endpoint2[1]); - } - /* Create a new vertex to insert in the middle of the segment. */ - newvertex = (vertex) poolalloc(&m->vertices); - /* Interpolate coordinates and attributes. */ - for (i = 0; i < 2 + m->nextras; i++) { - newvertex[i] = 0.5 * (endpoint1[i] + endpoint2[i]); - } - setvertexmark(newvertex, newmark); - setvertextype(newvertex, SEGMENTVERTEX); - /* No known triangle to search from. */ - searchtri1.tri = m->dummytri; - /* Attempt to insert the new vertex. */ - success = insertvertex(m, b, newvertex, &searchtri1, (struct osub *) NULL, - 0, 0); - if (success == DUPLICATEVERTEX) { - if (b->verbose > 2) { - printf(" Segment intersects existing vertex (%.12g, %.12g).\n", - newvertex[0], newvertex[1]); - } - /* Use the vertex that's already there. */ - vertexdealloc(m, newvertex); - org(searchtri1, newvertex); - } else { - if (success == VIOLATINGVERTEX) { - if (b->verbose > 2) { - printf(" Two segments intersect at (%.12g, %.12g).\n", - newvertex[0], newvertex[1]); - } - /* By fluke, we've landed right on another segment. Split it. */ - tspivot(searchtri1, brokensubseg); - success = insertvertex(m, b, newvertex, &searchtri1, &brokensubseg, - 0, 0); - if (success != SUCCESSFULVERTEX) { - printf("Internal error in conformingedge():\n"); - printf(" Failure to split a segment.\n"); - internalerror(); - } - } - /* The vertex has been inserted successfully. */ - if (m->steinerleft > 0) { - m->steinerleft--; - } - } - otricopy(searchtri1, searchtri2); - /* `searchtri1' and `searchtri2' are fastened at their origins to */ - /* `newvertex', and will be directed toward `endpoint1' and `endpoint2' */ - /* respectively. First, we must get `searchtri2' out of the way so it */ - /* won't be invalidated during the insertion of the first half of the */ - /* segment. */ - finddirection(m, b, &searchtri2, endpoint2); - if (!scoutsegment(m, b, &searchtri1, endpoint1, newmark)) { - /* The origin of searchtri1 may have changed if a collision with an */ - /* intervening vertex on the segment occurred. */ - org(searchtri1, midvertex1); - conformingedge(m, b, midvertex1, endpoint1, newmark); - } - if (!scoutsegment(m, b, &searchtri2, endpoint2, newmark)) { - /* The origin of searchtri2 may have changed if a collision with an */ - /* intervening vertex on the segment occurred. */ - org(searchtri2, midvertex2); - conformingedge(m, b, midvertex2, endpoint2, newmark); - } -} - -#endif /* not CDT_ONLY */ -#endif /* not REDUCED */ - -/*****************************************************************************/ -/* */ -/* delaunayfixup() Enforce the Delaunay condition at an edge, fanning out */ -/* recursively from an existing vertex. Pay special */ -/* attention to stacking inverted triangles. */ -/* */ -/* This is a support routine for inserting segments into a constrained */ -/* Delaunay triangulation. */ -/* */ -/* The origin of fixuptri is treated as if it has just been inserted, and */ -/* the local Delaunay condition needs to be enforced. It is only enforced */ -/* in one sector, however, that being the angular range defined by */ -/* fixuptri. */ -/* */ -/* This routine also needs to make decisions regarding the "stacking" of */ -/* triangles. (Read the description of constrainededge() below before */ -/* reading on here, so you understand the algorithm.) If the position of */ -/* the new vertex (the origin of fixuptri) indicates that the vertex before */ -/* it on the polygon is a reflex vertex, then "stack" the triangle by */ -/* doing nothing. (fixuptri is an inverted triangle, which is how stacked */ -/* triangles are identified.) */ -/* */ -/* Otherwise, check whether the vertex before that was a reflex vertex. */ -/* If so, perform an edge flip, thereby eliminating an inverted triangle */ -/* (popping it off the stack). The edge flip may result in the creation */ -/* of a new inverted triangle, depending on whether or not the new vertex */ -/* is visible to the vertex three edges behind on the polygon. */ -/* */ -/* If neither of the two vertices behind the new vertex are reflex */ -/* vertices, fixuptri and fartri, the triangle opposite it, are not */ -/* inverted; hence, ensure that the edge between them is locally Delaunay. */ -/* */ -/* `leftside' indicates whether or not fixuptri is to the left of the */ -/* segment being inserted. (Imagine that the segment is pointing up from */ -/* endpoint1 to endpoint2.) */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void delaunayfixup(struct mesh *m, struct behavior *b, - struct otri *fixuptri, int leftside) -#else /* not ANSI_DECLARATORS */ -void delaunayfixup(m, b, fixuptri, leftside) -struct mesh *m; -struct behavior *b; -struct otri *fixuptri; -int leftside; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri neartri; - struct otri fartri; - struct osub faredge; - vertex nearvertex, leftvertex, rightvertex, farvertex; - triangle ptr; /* Temporary variable used by sym(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - lnext(*fixuptri, neartri); - sym(neartri, fartri); - /* Check if the edge opposite the origin of fixuptri can be flipped. */ - if (fartri.tri == m->dummytri) { - return; - } - tspivot(neartri, faredge); - if (faredge.ss != m->dummysub) { - return; - } - /* Find all the relevant vertices. */ - apex(neartri, nearvertex); - org(neartri, leftvertex); - dest(neartri, rightvertex); - apex(fartri, farvertex); - /* Check whether the previous polygon vertex is a reflex vertex. */ - if (leftside) { - if (counterclockwise(m, b, nearvertex, leftvertex, farvertex) <= 0.0) { - /* leftvertex is a reflex vertex too. Nothing can */ - /* be done until a convex section is found. */ - return; - } - } else { - if (counterclockwise(m, b, farvertex, rightvertex, nearvertex) <= 0.0) { - /* rightvertex is a reflex vertex too. Nothing can */ - /* be done until a convex section is found. */ - return; - } - } - if (counterclockwise(m, b, rightvertex, leftvertex, farvertex) > 0.0) { - /* fartri is not an inverted triangle, and farvertex is not a reflex */ - /* vertex. As there are no reflex vertices, fixuptri isn't an */ - /* inverted triangle, either. Hence, test the edge between the */ - /* triangles to ensure it is locally Delaunay. */ - if (incircle(m, b, leftvertex, farvertex, rightvertex, nearvertex) <= - 0.0) { - return; - } - /* Not locally Delaunay; go on to an edge flip. */ - } /* else fartri is inverted; remove it from the stack by flipping. */ - flip(m, b, &neartri); - lprevself(*fixuptri); /* Restore the origin of fixuptri after the flip. */ - /* Recursively process the two triangles that result from the flip. */ - delaunayfixup(m, b, fixuptri, leftside); - delaunayfixup(m, b, &fartri, leftside); -} - -/*****************************************************************************/ -/* */ -/* constrainededge() Force a segment into a constrained Delaunay */ -/* triangulation by deleting the triangles it */ -/* intersects, and triangulating the polygons that */ -/* form on each side of it. */ -/* */ -/* Generates a single subsegment connecting `endpoint1' to `endpoint2'. */ -/* The triangle `starttri' has `endpoint1' as its origin. `newmark' is the */ -/* boundary marker of the segment. */ -/* */ -/* To insert a segment, every triangle whose interior intersects the */ -/* segment is deleted. The union of these deleted triangles is a polygon */ -/* (which is not necessarily monotone, but is close enough), which is */ -/* divided into two polygons by the new segment. This routine's task is */ -/* to generate the Delaunay triangulation of these two polygons. */ -/* */ -/* You might think of this routine's behavior as a two-step process. The */ -/* first step is to walk from endpoint1 to endpoint2, flipping each edge */ -/* encountered. This step creates a fan of edges connected to endpoint1, */ -/* including the desired edge to endpoint2. The second step enforces the */ -/* Delaunay condition on each side of the segment in an incremental manner: */ -/* proceeding along the polygon from endpoint1 to endpoint2 (this is done */ -/* independently on each side of the segment), each vertex is "enforced" */ -/* as if it had just been inserted, but affecting only the previous */ -/* vertices. The result is the same as if the vertices had been inserted */ -/* in the order they appear on the polygon, so the result is Delaunay. */ -/* */ -/* In truth, constrainededge() interleaves these two steps. The procedure */ -/* walks from endpoint1 to endpoint2, and each time an edge is encountered */ -/* and flipped, the newly exposed vertex (at the far end of the flipped */ -/* edge) is "enforced" upon the previously flipped edges, usually affecting */ -/* only one side of the polygon (depending upon which side of the segment */ -/* the vertex falls on). */ -/* */ -/* The algorithm is complicated by the need to handle polygons that are not */ -/* convex. Although the polygon is not necessarily monotone, it can be */ -/* triangulated in a manner similar to the stack-based algorithms for */ -/* monotone polygons. For each reflex vertex (local concavity) of the */ -/* polygon, there will be an inverted triangle formed by one of the edge */ -/* flips. (An inverted triangle is one with negative area - that is, its */ -/* vertices are arranged in clockwise order - and is best thought of as a */ -/* wrinkle in the fabric of the mesh.) Each inverted triangle can be */ -/* thought of as a reflex vertex pushed on the stack, waiting to be fixed */ -/* later. */ -/* */ -/* A reflex vertex is popped from the stack when a vertex is inserted that */ -/* is visible to the reflex vertex. (However, if the vertex behind the */ -/* reflex vertex is not visible to the reflex vertex, a new inverted */ -/* triangle will take its place on the stack.) These details are handled */ -/* by the delaunayfixup() routine above. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void constrainededge(struct mesh *m, struct behavior *b, - struct otri *starttri, vertex endpoint2, int newmark) -#else /* not ANSI_DECLARATORS */ -void constrainededge(m, b, starttri, endpoint2, newmark) -struct mesh *m; -struct behavior *b; -struct otri *starttri; -vertex endpoint2; -int newmark; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri fixuptri, fixuptri2; - struct osub crosssubseg; - vertex endpoint1; - vertex farvertex; - REAL area; - int collision; - int done; - triangle ptr; /* Temporary variable used by sym() and oprev(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - org(*starttri, endpoint1); - lnext(*starttri, fixuptri); - flip(m, b, &fixuptri); - /* `collision' indicates whether we have found a vertex directly */ - /* between endpoint1 and endpoint2. */ - collision = 0; - done = 0; - do { - org(fixuptri, farvertex); - /* `farvertex' is the extreme point of the polygon we are "digging" */ - /* to get from endpoint1 to endpoint2. */ - if ((farvertex[0] == endpoint2[0]) && (farvertex[1] == endpoint2[1])) { - oprev(fixuptri, fixuptri2); - /* Enforce the Delaunay condition around endpoint2. */ - delaunayfixup(m, b, &fixuptri, 0); - delaunayfixup(m, b, &fixuptri2, 1); - done = 1; - } else { - /* Check whether farvertex is to the left or right of the segment */ - /* being inserted, to decide which edge of fixuptri to dig */ - /* through next. */ - area = counterclockwise(m, b, endpoint1, endpoint2, farvertex); - if (area == 0.0) { - /* We've collided with a vertex between endpoint1 and endpoint2. */ - collision = 1; - oprev(fixuptri, fixuptri2); - /* Enforce the Delaunay condition around farvertex. */ - delaunayfixup(m, b, &fixuptri, 0); - delaunayfixup(m, b, &fixuptri2, 1); - done = 1; - } else { - if (area > 0.0) { /* farvertex is to the left of the segment. */ - oprev(fixuptri, fixuptri2); - /* Enforce the Delaunay condition around farvertex, on the */ - /* left side of the segment only. */ - delaunayfixup(m, b, &fixuptri2, 1); - /* Flip the edge that crosses the segment. After the edge is */ - /* flipped, one of its endpoints is the fan vertex, and the */ - /* destination of fixuptri is the fan vertex. */ - lprevself(fixuptri); - } else { /* farvertex is to the right of the segment. */ - delaunayfixup(m, b, &fixuptri, 0); - /* Flip the edge that crosses the segment. After the edge is */ - /* flipped, one of its endpoints is the fan vertex, and the */ - /* destination of fixuptri is the fan vertex. */ - oprevself(fixuptri); - } - /* Check for two intersecting segments. */ - tspivot(fixuptri, crosssubseg); - if (crosssubseg.ss == m->dummysub) { - flip(m, b, &fixuptri); /* May create inverted triangle at left. */ - } else { - /* We've collided with a segment between endpoint1 and endpoint2. */ - collision = 1; - /* Insert a vertex at the intersection. */ - segmentintersection(m, b, &fixuptri, &crosssubseg, endpoint2); - done = 1; - } - } - } - } while (!done); - /* Insert a subsegment to make the segment permanent. */ - insertsubseg(m, b, &fixuptri, newmark); - /* If there was a collision with an interceding vertex, install another */ - /* segment connecting that vertex with endpoint2. */ - if (collision) { - /* Insert the remainder of the segment. */ - if (!scoutsegment(m, b, &fixuptri, endpoint2, newmark)) { - constrainededge(m, b, &fixuptri, endpoint2, newmark); - } - } -} - -/*****************************************************************************/ -/* */ -/* insertsegment() Insert a PSLG segment into a triangulation. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void insertsegment(struct mesh *m, struct behavior *b, - vertex endpoint1, vertex endpoint2, int newmark) -#else /* not ANSI_DECLARATORS */ -void insertsegment(m, b, endpoint1, endpoint2, newmark) -struct mesh *m; -struct behavior *b; -vertex endpoint1; -vertex endpoint2; -int newmark; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri searchtri1, searchtri2; - triangle encodedtri; - vertex checkvertex; - triangle ptr; /* Temporary variable used by sym(). */ - - if (b->verbose > 1) { - printf(" Connecting (%.12g, %.12g) to (%.12g, %.12g).\n", - endpoint1[0], endpoint1[1], endpoint2[0], endpoint2[1]); - } - - /* Find a triangle whose origin is the segment's first endpoint. */ - checkvertex = (vertex) NULL; - encodedtri = vertex2tri(endpoint1); - if (encodedtri != (triangle) NULL) { - decode(encodedtri, searchtri1); - org(searchtri1, checkvertex); - } - if (checkvertex != endpoint1) { - /* Find a boundary triangle to search from. */ - searchtri1.tri = m->dummytri; - searchtri1.orient = 0; - symself(searchtri1); - /* Search for the segment's first endpoint by point location. */ - if (locate(m, b, endpoint1, &searchtri1) != ONVERTEX) { - printf( - "Internal error in insertsegment(): Unable to locate PSLG vertex\n"); - printf(" (%.12g, %.12g) in triangulation.\n", - endpoint1[0], endpoint1[1]); - internalerror(); - } - } - /* Remember this triangle to improve subsequent point location. */ - otricopy(searchtri1, m->recenttri); - /* Scout the beginnings of a path from the first endpoint */ - /* toward the second. */ - if (scoutsegment(m, b, &searchtri1, endpoint2, newmark)) { - /* The segment was easily inserted. */ - return; - } - /* The first endpoint may have changed if a collision with an intervening */ - /* vertex on the segment occurred. */ - org(searchtri1, endpoint1); - - /* Find a triangle whose origin is the segment's second endpoint. */ - checkvertex = (vertex) NULL; - encodedtri = vertex2tri(endpoint2); - if (encodedtri != (triangle) NULL) { - decode(encodedtri, searchtri2); - org(searchtri2, checkvertex); - } - if (checkvertex != endpoint2) { - /* Find a boundary triangle to search from. */ - searchtri2.tri = m->dummytri; - searchtri2.orient = 0; - symself(searchtri2); - /* Search for the segment's second endpoint by point location. */ - if (locate(m, b, endpoint2, &searchtri2) != ONVERTEX) { - printf( - "Internal error in insertsegment(): Unable to locate PSLG vertex\n"); - printf(" (%.12g, %.12g) in triangulation.\n", - endpoint2[0], endpoint2[1]); - internalerror(); - } - } - /* Remember this triangle to improve subsequent point location. */ - otricopy(searchtri2, m->recenttri); - /* Scout the beginnings of a path from the second endpoint */ - /* toward the first. */ - if (scoutsegment(m, b, &searchtri2, endpoint1, newmark)) { - /* The segment was easily inserted. */ - return; - } - /* The second endpoint may have changed if a collision with an intervening */ - /* vertex on the segment occurred. */ - org(searchtri2, endpoint2); - -#ifndef REDUCED -#ifndef CDT_ONLY - if (b->splitseg) { - /* Insert vertices to force the segment into the triangulation. */ - conformingedge(m, b, endpoint1, endpoint2, newmark); - } else { -#endif /* not CDT_ONLY */ -#endif /* not REDUCED */ - /* Insert the segment directly into the triangulation. */ - constrainededge(m, b, &searchtri1, endpoint2, newmark); -#ifndef REDUCED -#ifndef CDT_ONLY - } -#endif /* not CDT_ONLY */ -#endif /* not REDUCED */ -} - -/*****************************************************************************/ -/* */ -/* markhull() Cover the convex hull of a triangulation with subsegments. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void markhull(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void markhull(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri hulltri; - struct otri nexttri; - struct otri starttri; - triangle ptr; /* Temporary variable used by sym() and oprev(). */ - - /* Find a triangle handle on the hull. */ - hulltri.tri = m->dummytri; - hulltri.orient = 0; - symself(hulltri); - /* Remember where we started so we know when to stop. */ - otricopy(hulltri, starttri); - /* Go once counterclockwise around the convex hull. */ - do { - /* Create a subsegment if there isn't already one here. */ - insertsubseg(m, b, &hulltri, 1); - /* To find the next hull edge, go clockwise around the next vertex. */ - lnextself(hulltri); - oprev(hulltri, nexttri); - while (nexttri.tri != m->dummytri) { - otricopy(nexttri, hulltri); - oprev(hulltri, nexttri); - } - } while (!otriequal(hulltri, starttri)); -} - -/*****************************************************************************/ -/* */ -/* formskeleton() Create the segments of a triangulation, including PSLG */ -/* segments and edges on the convex hull. */ -/* */ -/* The PSLG segments are read from a .poly file. The return value is the */ -/* number of segments in the file. */ -/* */ -/*****************************************************************************/ - -#ifdef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void formskeleton(struct mesh *m, struct behavior *b, int *segmentlist, - int *segmentmarkerlist, int numberofsegments) -#else /* not ANSI_DECLARATORS */ -void formskeleton(m, b, segmentlist, segmentmarkerlist, numberofsegments) -struct mesh *m; -struct behavior *b; -int *segmentlist; -int *segmentmarkerlist; -int numberofsegments; -#endif /* not ANSI_DECLARATORS */ - -#else /* not TRILIBRARY */ - -#ifdef ANSI_DECLARATORS -void formskeleton(struct mesh *m, struct behavior *b, - FILE *polyfile, char *polyfilename) -#else /* not ANSI_DECLARATORS */ -void formskeleton(m, b, polyfile, polyfilename) -struct mesh *m; -struct behavior *b; -FILE *polyfile; -char *polyfilename; -#endif /* not ANSI_DECLARATORS */ - -#endif /* not TRILIBRARY */ - -{ -#ifdef TRILIBRARY - char polyfilename[6]; - int index; -#else /* not TRILIBRARY */ - char inputline[INPUTLINESIZE]; - char *stringptr; -#endif /* not TRILIBRARY */ - vertex endpoint1, endpoint2; - int segmentmarkers; - int end1, end2; - int boundmarker; - int i; - - if (b->poly) { - if (!b->quiet) { - printf("Recovering segments in Delaunay triangulation.\n"); - } -#ifdef TRILIBRARY - strcpy(polyfilename, "input"); - m->insegments = numberofsegments; - segmentmarkers = segmentmarkerlist != (int *) NULL; - index = 0; -#else /* not TRILIBRARY */ - /* Read the segments from a .poly file. */ - /* Read number of segments and number of boundary markers. */ - stringptr = readline(inputline, polyfile, polyfilename); - m->insegments = (int) strtol(stringptr, &stringptr, 0); - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - segmentmarkers = 0; - } else { - segmentmarkers = (int) strtol(stringptr, &stringptr, 0); - } -#endif /* not TRILIBRARY */ - /* If the input vertices are collinear, there is no triangulation, */ - /* so don't try to insert segments. */ - if (m->triangles.items == 0) { - return; - } - - /* If segments are to be inserted, compute a mapping */ - /* from vertices to triangles. */ - if (m->insegments > 0) { - makevertexmap(m, b); - if (b->verbose) { - printf(" Recovering PSLG segments.\n"); - } - } - - boundmarker = 0; - /* Read and insert the segments. */ - for (i = 0; i < m->insegments; i++) { -#ifdef TRILIBRARY - end1 = segmentlist[index++]; - end2 = segmentlist[index++]; - if (segmentmarkers) { - boundmarker = segmentmarkerlist[i]; - } -#else /* not TRILIBRARY */ - stringptr = readline(inputline, polyfile, b->inpolyfilename); - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - printf("Error: Segment %d has no endpoints in %s.\n", - b->firstnumber + i, polyfilename); - triexit(1); - } else { - end1 = (int) strtol(stringptr, &stringptr, 0); - } - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - printf("Error: Segment %d is missing its second endpoint in %s.\n", - b->firstnumber + i, polyfilename); - triexit(1); - } else { - end2 = (int) strtol(stringptr, &stringptr, 0); - } - if (segmentmarkers) { - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - boundmarker = 0; - } else { - boundmarker = (int) strtol(stringptr, &stringptr, 0); - } - } -#endif /* not TRILIBRARY */ - if ((end1 < b->firstnumber) || - (end1 >= b->firstnumber + m->invertices)) { - if (!b->quiet) { - printf("Warning: Invalid first endpoint of segment %d in %s.\n", - b->firstnumber + i, polyfilename); - } - } else if ((end2 < b->firstnumber) || - (end2 >= b->firstnumber + m->invertices)) { - if (!b->quiet) { - printf("Warning: Invalid second endpoint of segment %d in %s.\n", - b->firstnumber + i, polyfilename); - } - } else { - /* Find the vertices numbered `end1' and `end2'. */ - endpoint1 = getvertex(m, b, end1); - endpoint2 = getvertex(m, b, end2); - if ((endpoint1[0] == endpoint2[0]) && (endpoint1[1] == endpoint2[1])) { - if (!b->quiet) { - printf("Warning: Endpoints of segment %d are coincident in %s.\n", - b->firstnumber + i, polyfilename); - } - } else { - insertsegment(m, b, endpoint1, endpoint2, boundmarker); - } - } - } - } else { - m->insegments = 0; - } - if (b->convex || !b->poly) { - /* Enclose the convex hull with subsegments. */ - if (b->verbose) { - printf(" Enclosing convex hull with segments.\n"); - } - markhull(m, b); - } -} - -/** **/ -/** **/ -/********* Segment insertion ends here *********/ - -/********* Carving out holes and concavities begins here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* infecthull() Virally infect all of the triangles of the convex hull */ -/* that are not protected by subsegments. Where there are */ -/* subsegments, set boundary markers as appropriate. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void infecthull(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void infecthull(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri hulltri; - struct otri nexttri; - struct otri starttri; - struct osub hullsubseg; - triangle **deadtriangle; - vertex horg, hdest; - triangle ptr; /* Temporary variable used by sym(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - if (b->verbose) { - printf(" Marking concavities (external triangles) for elimination.\n"); - } - /* Find a triangle handle on the hull. */ - hulltri.tri = m->dummytri; - hulltri.orient = 0; - symself(hulltri); - /* Remember where we started so we know when to stop. */ - otricopy(hulltri, starttri); - /* Go once counterclockwise around the convex hull. */ - do { - /* Ignore triangles that are already infected. */ - if (!infected(hulltri)) { - /* Is the triangle protected by a subsegment? */ - tspivot(hulltri, hullsubseg); - if (hullsubseg.ss == m->dummysub) { - /* The triangle is not protected; infect it. */ - if (!infected(hulltri)) { - infect(hulltri); - deadtriangle = (triangle **) poolalloc(&m->viri); - *deadtriangle = hulltri.tri; - } - } else { - /* The triangle is protected; set boundary markers if appropriate. */ - if (mark(hullsubseg) == 0) { - setmark(hullsubseg, 1); - org(hulltri, horg); - dest(hulltri, hdest); - if (vertexmark(horg) == 0) { - setvertexmark(horg, 1); - } - if (vertexmark(hdest) == 0) { - setvertexmark(hdest, 1); - } - } - } - } - /* To find the next hull edge, go clockwise around the next vertex. */ - lnextself(hulltri); - oprev(hulltri, nexttri); - while (nexttri.tri != m->dummytri) { - otricopy(nexttri, hulltri); - oprev(hulltri, nexttri); - } - } while (!otriequal(hulltri, starttri)); -} - -/*****************************************************************************/ -/* */ -/* plague() Spread the virus from all infected triangles to any neighbors */ -/* not protected by subsegments. Delete all infected triangles. */ -/* */ -/* This is the procedure that actually creates holes and concavities. */ -/* */ -/* This procedure operates in two phases. The first phase identifies all */ -/* the triangles that will die, and marks them as infected. They are */ -/* marked to ensure that each triangle is added to the virus pool only */ -/* once, so the procedure will terminate. */ -/* */ -/* The second phase actually eliminates the infected triangles. It also */ -/* eliminates orphaned vertices. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void plague(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void plague(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri testtri; - struct otri neighbor; - triangle **virusloop; - triangle **deadtriangle; - struct osub neighborsubseg; - vertex testvertex; - vertex norg, ndest; - vertex deadorg, deaddest, deadapex; - int killorg; - triangle ptr; /* Temporary variable used by sym() and onext(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - if (b->verbose) { - printf(" Marking neighbors of marked triangles.\n"); - } - /* Loop through all the infected triangles, spreading the virus to */ - /* their neighbors, then to their neighbors' neighbors. */ - traversalinit(&m->viri); - virusloop = (triangle **) traverse(&m->viri); - while (virusloop != (triangle **) NULL) { - testtri.tri = *virusloop; - /* A triangle is marked as infected by messing with one of its pointers */ - /* to subsegments, setting it to an illegal value. Hence, we have to */ - /* temporarily uninfect this triangle so that we can examine its */ - /* adjacent subsegments. */ - uninfect(testtri); - if (b->verbose > 2) { - /* Assign the triangle an orientation for convenience in */ - /* checking its vertices. */ - testtri.orient = 0; - org(testtri, deadorg); - dest(testtri, deaddest); - apex(testtri, deadapex); - printf(" Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", - deadorg[0], deadorg[1], deaddest[0], deaddest[1], - deadapex[0], deadapex[1]); - } - /* Check each of the triangle's three neighbors. */ - for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { - /* Find the neighbor. */ - sym(testtri, neighbor); - /* Check for a subsegment between the triangle and its neighbor. */ - tspivot(testtri, neighborsubseg); - /* Check if the neighbor is nonexistent or already infected. */ - if ((neighbor.tri == m->dummytri) || infected(neighbor)) { - if (neighborsubseg.ss != m->dummysub) { - /* There is a subsegment separating the triangle from its */ - /* neighbor, but both triangles are dying, so the subsegment */ - /* dies too. */ - subsegdealloc(m, neighborsubseg.ss); - if (neighbor.tri != m->dummytri) { - /* Make sure the subsegment doesn't get deallocated again */ - /* later when the infected neighbor is visited. */ - uninfect(neighbor); - tsdissolve(neighbor); - infect(neighbor); - } - } - } else { /* The neighbor exists and is not infected. */ - if (neighborsubseg.ss == m->dummysub) { - /* There is no subsegment protecting the neighbor, so */ - /* the neighbor becomes infected. */ - if (b->verbose > 2) { - org(neighbor, deadorg); - dest(neighbor, deaddest); - apex(neighbor, deadapex); - printf( - " Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", - deadorg[0], deadorg[1], deaddest[0], deaddest[1], - deadapex[0], deadapex[1]); - } - infect(neighbor); - /* Ensure that the neighbor's neighbors will be infected. */ - deadtriangle = (triangle **) poolalloc(&m->viri); - *deadtriangle = neighbor.tri; - } else { /* The neighbor is protected by a subsegment. */ - /* Remove this triangle from the subsegment. */ - stdissolve(neighborsubseg); - /* The subsegment becomes a boundary. Set markers accordingly. */ - if (mark(neighborsubseg) == 0) { - setmark(neighborsubseg, 1); - } - org(neighbor, norg); - dest(neighbor, ndest); - if (vertexmark(norg) == 0) { - setvertexmark(norg, 1); - } - if (vertexmark(ndest) == 0) { - setvertexmark(ndest, 1); - } - } - } - } - /* Remark the triangle as infected, so it doesn't get added to the */ - /* virus pool again. */ - infect(testtri); - virusloop = (triangle **) traverse(&m->viri); - } - - if (b->verbose) { - printf(" Deleting marked triangles.\n"); - } - - traversalinit(&m->viri); - virusloop = (triangle **) traverse(&m->viri); - while (virusloop != (triangle **) NULL) { - testtri.tri = *virusloop; - - /* Check each of the three corners of the triangle for elimination. */ - /* This is done by walking around each vertex, checking if it is */ - /* still connected to at least one live triangle. */ - for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { - org(testtri, testvertex); - /* Check if the vertex has already been tested. */ - if (testvertex != (vertex) NULL) { - killorg = 1; - /* Mark the corner of the triangle as having been tested. */ - setorg(testtri, NULL); - /* Walk counterclockwise about the vertex. */ - onext(testtri, neighbor); - /* Stop upon reaching a boundary or the starting triangle. */ - while ((neighbor.tri != m->dummytri) && - (!otriequal(neighbor, testtri))) { - if (infected(neighbor)) { - /* Mark the corner of this triangle as having been tested. */ - setorg(neighbor, NULL); - } else { - /* A live triangle. The vertex survives. */ - killorg = 0; - } - /* Walk counterclockwise about the vertex. */ - onextself(neighbor); - } - /* If we reached a boundary, we must walk clockwise as well. */ - if (neighbor.tri == m->dummytri) { - /* Walk clockwise about the vertex. */ - oprev(testtri, neighbor); - /* Stop upon reaching a boundary. */ - while (neighbor.tri != m->dummytri) { - if (infected(neighbor)) { - /* Mark the corner of this triangle as having been tested. */ - setorg(neighbor, NULL); - } else { - /* A live triangle. The vertex survives. */ - killorg = 0; - } - /* Walk clockwise about the vertex. */ - oprevself(neighbor); - } - } - if (killorg) { - if (b->verbose > 1) { - printf(" Deleting vertex (%.12g, %.12g)\n", - testvertex[0], testvertex[1]); - } - setvertextype(testvertex, UNDEADVERTEX); - m->undeads++; - } - } - } - - /* Record changes in the number of boundary edges, and disconnect */ - /* dead triangles from their neighbors. */ - for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { - sym(testtri, neighbor); - if (neighbor.tri == m->dummytri) { - /* There is no neighboring triangle on this edge, so this edge */ - /* is a boundary edge. This triangle is being deleted, so this */ - /* boundary edge is deleted. */ - m->hullsize--; - } else { - /* Disconnect the triangle from its neighbor. */ - dissolve(neighbor); - /* There is a neighboring triangle on this edge, so this edge */ - /* becomes a boundary edge when this triangle is deleted. */ - m->hullsize++; - } - } - /* Return the dead triangle to the pool of triangles. */ - triangledealloc(m, testtri.tri); - virusloop = (triangle **) traverse(&m->viri); - } - /* Empty the virus pool. */ - poolrestart(&m->viri); -} - -/*****************************************************************************/ -/* */ -/* regionplague() Spread regional attributes and/or area constraints */ -/* (from a .poly file) throughout the mesh. */ -/* */ -/* This procedure operates in two phases. The first phase spreads an */ -/* attribute and/or an area constraint through a (segment-bounded) region. */ -/* The triangles are marked to ensure that each triangle is added to the */ -/* virus pool only once, so the procedure will terminate. */ -/* */ -/* The second phase uninfects all infected triangles, returning them to */ -/* normal. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void regionplague(struct mesh *m, struct behavior *b, - REAL attribute, REAL area) -#else /* not ANSI_DECLARATORS */ -void regionplague(m, b, attribute, area) -struct mesh *m; -struct behavior *b; -REAL attribute; -REAL area; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri testtri; - struct otri neighbor; - triangle **virusloop; - triangle **regiontri; - struct osub neighborsubseg; - vertex regionorg, regiondest, regionapex; - triangle ptr; /* Temporary variable used by sym() and onext(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - if (b->verbose > 1) { - printf(" Marking neighbors of marked triangles.\n"); - } - /* Loop through all the infected triangles, spreading the attribute */ - /* and/or area constraint to their neighbors, then to their neighbors' */ - /* neighbors. */ - traversalinit(&m->viri); - virusloop = (triangle **) traverse(&m->viri); - while (virusloop != (triangle **) NULL) { - testtri.tri = *virusloop; - /* A triangle is marked as infected by messing with one of its pointers */ - /* to subsegments, setting it to an illegal value. Hence, we have to */ - /* temporarily uninfect this triangle so that we can examine its */ - /* adjacent subsegments. */ - uninfect(testtri); - if (b->regionattrib) { - /* Set an attribute. */ - setelemattribute(testtri, m->eextras, attribute); - } - if (b->vararea) { - /* Set an area constraint. */ - setareabound(testtri, area); - } - if (b->verbose > 2) { - /* Assign the triangle an orientation for convenience in */ - /* checking its vertices. */ - testtri.orient = 0; - org(testtri, regionorg); - dest(testtri, regiondest); - apex(testtri, regionapex); - printf(" Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", - regionorg[0], regionorg[1], regiondest[0], regiondest[1], - regionapex[0], regionapex[1]); - } - /* Check each of the triangle's three neighbors. */ - for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { - /* Find the neighbor. */ - sym(testtri, neighbor); - /* Check for a subsegment between the triangle and its neighbor. */ - tspivot(testtri, neighborsubseg); - /* Make sure the neighbor exists, is not already infected, and */ - /* isn't protected by a subsegment. */ - if ((neighbor.tri != m->dummytri) && !infected(neighbor) - && (neighborsubseg.ss == m->dummysub)) { - if (b->verbose > 2) { - org(neighbor, regionorg); - dest(neighbor, regiondest); - apex(neighbor, regionapex); - printf(" Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", - regionorg[0], regionorg[1], regiondest[0], regiondest[1], - regionapex[0], regionapex[1]); - } - /* Infect the neighbor. */ - infect(neighbor); - /* Ensure that the neighbor's neighbors will be infected. */ - regiontri = (triangle **) poolalloc(&m->viri); - *regiontri = neighbor.tri; - } - } - /* Remark the triangle as infected, so it doesn't get added to the */ - /* virus pool again. */ - infect(testtri); - virusloop = (triangle **) traverse(&m->viri); - } - - /* Uninfect all triangles. */ - if (b->verbose > 1) { - printf(" Unmarking marked triangles.\n"); - } - traversalinit(&m->viri); - virusloop = (triangle **) traverse(&m->viri); - while (virusloop != (triangle **) NULL) { - testtri.tri = *virusloop; - uninfect(testtri); - virusloop = (triangle **) traverse(&m->viri); - } - /* Empty the virus pool. */ - poolrestart(&m->viri); -} - -/*****************************************************************************/ -/* */ -/* carveholes() Find the holes and infect them. Find the area */ -/* constraints and infect them. Infect the convex hull. */ -/* Spread the infection and kill triangles. Spread the */ -/* area constraints. */ -/* */ -/* This routine mainly calls other routines to carry out all these */ -/* functions. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void carveholes(struct mesh *m, struct behavior *b, REAL *holelist, int holes, - REAL *regionlist, int regions) -#else /* not ANSI_DECLARATORS */ -void carveholes(m, b, holelist, holes, regionlist, regions) -struct mesh *m; -struct behavior *b; -REAL *holelist; -int holes; -REAL *regionlist; -int regions; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri searchtri; - struct otri triangleloop; - struct otri *regiontris; - triangle **holetri; - triangle **regiontri; - vertex searchorg, searchdest; - enum locateresult intersect; - int i; - triangle ptr; /* Temporary variable used by sym(). */ - - if (!(b->quiet || (b->noholes && b->convex))) { - printf("Removing unwanted triangles.\n"); - if (b->verbose && (holes > 0)) { - printf(" Marking holes for elimination.\n"); - } - } - - if (regions > 0) { - /* Allocate storage for the triangles in which region points fall. */ - regiontris = (struct otri *) trimalloc(regions * - (int) sizeof(struct otri)); - } else { - regiontris = (struct otri *) NULL; - } - - if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) { - /* Initialize a pool of viri to be used for holes, concavities, */ - /* regional attributes, and/or regional area constraints. */ - poolinit(&m->viri, sizeof(triangle *), VIRUSPERBLOCK, VIRUSPERBLOCK, 0); - } - - if (!b->convex) { - /* Mark as infected any unprotected triangles on the boundary. */ - /* This is one way by which concavities are created. */ - infecthull(m, b); - } - - if ((holes > 0) && !b->noholes) { - /* Infect each triangle in which a hole lies. */ - for (i = 0; i < 2 * holes; i += 2) { - /* Ignore holes that aren't within the bounds of the mesh. */ - if ((holelist[i] >= m->xmin) && (holelist[i] <= m->xmax) - && (holelist[i + 1] >= m->ymin) && (holelist[i + 1] <= m->ymax)) { - /* Start searching from some triangle on the outer boundary. */ - searchtri.tri = m->dummytri; - searchtri.orient = 0; - symself(searchtri); - /* Ensure that the hole is to the left of this boundary edge; */ - /* otherwise, locate() will falsely report that the hole */ - /* falls within the starting triangle. */ - org(searchtri, searchorg); - dest(searchtri, searchdest); - if (counterclockwise(m, b, searchorg, searchdest, &holelist[i]) > - 0.0) { - /* Find a triangle that contains the hole. */ - intersect = locate(m, b, &holelist[i], &searchtri); - if ((intersect != OUTSIDE) && (!infected(searchtri))) { - /* Infect the triangle. This is done by marking the triangle */ - /* as infected and including the triangle in the virus pool. */ - infect(searchtri); - holetri = (triangle **) poolalloc(&m->viri); - *holetri = searchtri.tri; - } - } - } - } - } - - /* Now, we have to find all the regions BEFORE we carve the holes, because */ - /* locate() won't work when the triangulation is no longer convex. */ - /* (Incidentally, this is the reason why regional attributes and area */ - /* constraints can't be used when refining a preexisting mesh, which */ - /* might not be convex; they can only be used with a freshly */ - /* triangulated PSLG.) */ - if (regions > 0) { - /* Find the starting triangle for each region. */ - for (i = 0; i < regions; i++) { - regiontris[i].tri = m->dummytri; - /* Ignore region points that aren't within the bounds of the mesh. */ - if ((regionlist[4 * i] >= m->xmin) && (regionlist[4 * i] <= m->xmax) && - (regionlist[4 * i + 1] >= m->ymin) && - (regionlist[4 * i + 1] <= m->ymax)) { - /* Start searching from some triangle on the outer boundary. */ - searchtri.tri = m->dummytri; - searchtri.orient = 0; - symself(searchtri); - /* Ensure that the region point is to the left of this boundary */ - /* edge; otherwise, locate() will falsely report that the */ - /* region point falls within the starting triangle. */ - org(searchtri, searchorg); - dest(searchtri, searchdest); - if (counterclockwise(m, b, searchorg, searchdest, ®ionlist[4 * i]) > - 0.0) { - /* Find a triangle that contains the region point. */ - intersect = locate(m, b, ®ionlist[4 * i], &searchtri); - if ((intersect != OUTSIDE) && (!infected(searchtri))) { - /* Record the triangle for processing after the */ - /* holes have been carved. */ - otricopy(searchtri, regiontris[i]); - } - } - } - } - } - - if (m->viri.items > 0) { - /* Carve the holes and concavities. */ - plague(m, b); - } - /* The virus pool should be empty now. */ - - if (regions > 0) { - if (!b->quiet) { - if (b->regionattrib) { - if (b->vararea) { - printf("Spreading regional attributes and area constraints.\n"); - } else { - printf("Spreading regional attributes.\n"); - } - } else { - printf("Spreading regional area constraints.\n"); - } - } - if (b->regionattrib && !b->refine) { - /* Assign every triangle a regional attribute of zero. */ - traversalinit(&m->triangles); - triangleloop.orient = 0; - triangleloop.tri = triangletraverse(m); - while (triangleloop.tri != (triangle *) NULL) { - setelemattribute(triangleloop, m->eextras, 0.0); - triangleloop.tri = triangletraverse(m); - } - } - for (i = 0; i < regions; i++) { - if (regiontris[i].tri != m->dummytri) { - /* Make sure the triangle under consideration still exists. */ - /* It may have been eaten by the virus. */ - if (!deadtri(regiontris[i].tri)) { - /* Put one triangle in the virus pool. */ - infect(regiontris[i]); - regiontri = (triangle **) poolalloc(&m->viri); - *regiontri = regiontris[i].tri; - /* Apply one region's attribute and/or area constraint. */ - regionplague(m, b, regionlist[4 * i + 2], regionlist[4 * i + 3]); - /* The virus pool should be empty now. */ - } - } - } - if (b->regionattrib && !b->refine) { - /* Note the fact that each triangle has an additional attribute. */ - m->eextras++; - } - } - - /* Free up memory. */ - if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) { - pooldeinit(&m->viri); - } - if (regions > 0) { - trifree((VOID *) regiontris); - } -} - -/** **/ -/** **/ -/********* Carving out holes and concavities ends here *********/ - -/********* Mesh quality maintenance begins here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* tallyencs() Traverse the entire list of subsegments, and check each */ -/* to see if it is encroached. If so, add it to the list. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -void tallyencs(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void tallyencs(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct osub subsegloop; - int dummy; - - traversalinit(&m->subsegs); - subsegloop.ssorient = 0; - subsegloop.ss = subsegtraverse(m); - while (subsegloop.ss != (subseg *) NULL) { - /* If the segment is encroached, add it to the list. */ - dummy = checkseg4encroach(m, b, &subsegloop); - subsegloop.ss = subsegtraverse(m); - } -} - -#endif /* not CDT_ONLY */ - -/*****************************************************************************/ -/* */ -/* precisionerror() Print an error message for precision problems. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -void precisionerror() -{ - printf("Try increasing the area criterion and/or reducing the minimum\n"); - printf(" allowable angle so that tiny triangles are not created.\n"); -#ifdef SINGLE - printf("Alternatively, try recompiling me with double precision\n"); - printf(" arithmetic (by removing \"#define SINGLE\" from the\n"); - printf(" source file or \"-DSINGLE\" from the makefile).\n"); -#endif /* SINGLE */ -} - -#endif /* not CDT_ONLY */ - -/*****************************************************************************/ -/* */ -/* splitencsegs() Split all the encroached subsegments. */ -/* */ -/* Each encroached subsegment is repaired by splitting it - inserting a */ -/* vertex at or near its midpoint. Newly inserted vertices may encroach */ -/* upon other subsegments; these are also repaired. */ -/* */ -/* `triflaws' is a flag that specifies whether one should take note of new */ -/* bad triangles that result from inserting vertices to repair encroached */ -/* subsegments. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -void splitencsegs(struct mesh *m, struct behavior *b, int triflaws) -#else /* not ANSI_DECLARATORS */ -void splitencsegs(m, b, triflaws) -struct mesh *m; -struct behavior *b; -int triflaws; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri enctri; - struct otri testtri; - struct osub testsh; - struct osub currentenc; - struct badsubseg *encloop; - vertex eorg, edest, eapex; - vertex newvertex; - enum insertvertexresult success; - REAL segmentlength, nearestpoweroftwo; - REAL split; - REAL multiplier, divisor; - int acuteorg, acuteorg2, acutedest, acutedest2; - int dummy; - int i; - triangle ptr; /* Temporary variable used by stpivot(). */ - subseg sptr; /* Temporary variable used by snext(). */ - - /* Note that steinerleft == -1 if an unlimited number */ - /* of Steiner points is allowed. */ - while ((m->badsubsegs.items > 0) && (m->steinerleft != 0)) { - traversalinit(&m->badsubsegs); - encloop = badsubsegtraverse(m); - while ((encloop != (struct badsubseg *) NULL) && (m->steinerleft != 0)) { - sdecode(encloop->encsubseg, currentenc); - sorg(currentenc, eorg); - sdest(currentenc, edest); - /* Make sure that this segment is still the same segment it was */ - /* when it was determined to be encroached. If the segment was */ - /* enqueued multiple times (because several newly inserted */ - /* vertices encroached it), it may have already been split. */ - if (!deadsubseg(currentenc.ss) && - (eorg == encloop->subsegorg) && (edest == encloop->subsegdest)) { - /* To decide where to split a segment, we need to know if the */ - /* segment shares an endpoint with an adjacent segment. */ - /* The concern is that, if we simply split every encroached */ - /* segment in its center, two adjacent segments with a small */ - /* angle between them might lead to an infinite loop; each */ - /* vertex added to split one segment will encroach upon the */ - /* other segment, which must then be split with a vertex that */ - /* will encroach upon the first segment, and so on forever. */ - /* To avoid this, imagine a set of concentric circles, whose */ - /* radii are powers of two, about each segment endpoint. */ - /* These concentric circles determine where the segment is */ - /* split. (If both endpoints are shared with adjacent */ - /* segments, split the segment in the middle, and apply the */ - /* concentric circles for later splittings.) */ - - /* Is the origin shared with another segment? */ - stpivot(currentenc, enctri); - lnext(enctri, testtri); - tspivot(testtri, testsh); - acuteorg = testsh.ss != m->dummysub; - /* Is the destination shared with another segment? */ - lnextself(testtri); - tspivot(testtri, testsh); - acutedest = testsh.ss != m->dummysub; - - /* If we're using Chew's algorithm (rather than Ruppert's) */ - /* to define encroachment, delete free vertices from the */ - /* subsegment's diametral circle. */ - if (!b->conformdel && !acuteorg && !acutedest) { - apex(enctri, eapex); - while ((vertextype(eapex) == FREEVERTEX) && - ((eorg[0] - eapex[0]) * (edest[0] - eapex[0]) + - (eorg[1] - eapex[1]) * (edest[1] - eapex[1]) < 0.0)) { - deletevertex(m, b, &testtri); - stpivot(currentenc, enctri); - apex(enctri, eapex); - lprev(enctri, testtri); - } - } - - /* Now, check the other side of the segment, if there's a triangle */ - /* there. */ - sym(enctri, testtri); - if (testtri.tri != m->dummytri) { - /* Is the destination shared with another segment? */ - lnextself(testtri); - tspivot(testtri, testsh); - acutedest2 = testsh.ss != m->dummysub; - acutedest = acutedest || acutedest2; - /* Is the origin shared with another segment? */ - lnextself(testtri); - tspivot(testtri, testsh); - acuteorg2 = testsh.ss != m->dummysub; - acuteorg = acuteorg || acuteorg2; - - /* Delete free vertices from the subsegment's diametral circle. */ - if (!b->conformdel && !acuteorg2 && !acutedest2) { - org(testtri, eapex); - while ((vertextype(eapex) == FREEVERTEX) && - ((eorg[0] - eapex[0]) * (edest[0] - eapex[0]) + - (eorg[1] - eapex[1]) * (edest[1] - eapex[1]) < 0.0)) { - deletevertex(m, b, &testtri); - sym(enctri, testtri); - apex(testtri, eapex); - lprevself(testtri); - } - } - } - - /* Use the concentric circles if exactly one endpoint is shared */ - /* with another adjacent segment. */ - if (acuteorg || acutedest) { - segmentlength = sqrt((edest[0] - eorg[0]) * (edest[0] - eorg[0]) + - (edest[1] - eorg[1]) * (edest[1] - eorg[1])); - /* Find the power of two that most evenly splits the segment. */ - /* The worst case is a 2:1 ratio between subsegment lengths. */ - nearestpoweroftwo = 1.0; - while (segmentlength > 3.0 * nearestpoweroftwo) { - nearestpoweroftwo *= 2.0; - } - while (segmentlength < 1.5 * nearestpoweroftwo) { - nearestpoweroftwo *= 0.5; - } - /* Where do we split the segment? */ - split = nearestpoweroftwo / segmentlength; - if (acutedest) { - split = 1.0 - split; - } - } else { - /* If we're not worried about adjacent segments, split */ - /* this segment in the middle. */ - split = 0.5; - } - - /* Create the new vertex. */ - newvertex = (vertex) poolalloc(&m->vertices); - /* Interpolate its coordinate and attributes. */ - for (i = 0; i < 2 + m->nextras; i++) { - newvertex[i] = eorg[i] + split * (edest[i] - eorg[i]); - } - - if (!b->noexact) { - /* Roundoff in the above calculation may yield a `newvertex' */ - /* that is not precisely collinear with `eorg' and `edest'. */ - /* Improve collinearity by one step of iterative refinement. */ - multiplier = counterclockwise(m, b, eorg, edest, newvertex); - divisor = ((eorg[0] - edest[0]) * (eorg[0] - edest[0]) + - (eorg[1] - edest[1]) * (eorg[1] - edest[1])); - if ((multiplier != 0.0) && (divisor != 0.0)) { - multiplier = multiplier / divisor; - /* Watch out for NANs. */ - if (multiplier == multiplier) { - newvertex[0] += multiplier * (edest[1] - eorg[1]); - newvertex[1] += multiplier * (eorg[0] - edest[0]); - } - } - } - - setvertexmark(newvertex, mark(currentenc)); - setvertextype(newvertex, SEGMENTVERTEX); - if (b->verbose > 1) { - printf( - " Splitting subsegment (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n", - eorg[0], eorg[1], edest[0], edest[1], - newvertex[0], newvertex[1]); - } - /* Check whether the new vertex lies on an endpoint. */ - if (((newvertex[0] == eorg[0]) && (newvertex[1] == eorg[1])) || - ((newvertex[0] == edest[0]) && (newvertex[1] == edest[1]))) { - printf("Error: Ran out of precision at (%.12g, %.12g).\n", - newvertex[0], newvertex[1]); - printf("I attempted to split a segment to a smaller size than\n"); - printf(" can be accommodated by the finite precision of\n"); - printf(" floating point arithmetic.\n"); - precisionerror(); - triexit(1); - } - /* Insert the splitting vertex. This should always succeed. */ - success = insertvertex(m, b, newvertex, &enctri, ¤tenc, - 1, triflaws); - if ((success != SUCCESSFULVERTEX) && (success != ENCROACHINGVERTEX)) { - printf("Internal error in splitencsegs():\n"); - printf(" Failure to split a segment.\n"); - internalerror(); - } - if (m->steinerleft > 0) { - m->steinerleft--; - } - /* Check the two new subsegments to see if they're encroached. */ - dummy = checkseg4encroach(m, b, ¤tenc); - snextself(currentenc); - dummy = checkseg4encroach(m, b, ¤tenc); - } - - badsubsegdealloc(m, encloop); - encloop = badsubsegtraverse(m); - } - } -} - -#endif /* not CDT_ONLY */ - -/*****************************************************************************/ -/* */ -/* tallyfaces() Test every triangle in the mesh for quality measures. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -void tallyfaces(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void tallyfaces(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri triangleloop; - - if (b->verbose) { - printf(" Making a list of bad triangles.\n"); - } - traversalinit(&m->triangles); - triangleloop.orient = 0; - triangleloop.tri = triangletraverse(m); - while (triangleloop.tri != (triangle *) NULL) { - /* If the triangle is bad, enqueue it. */ - testtriangle(m, b, &triangleloop); - triangleloop.tri = triangletraverse(m); - } -} - -#endif /* not CDT_ONLY */ - -/*****************************************************************************/ -/* */ -/* splittriangle() Inserts a vertex at the circumcenter of a triangle. */ -/* Deletes the newly inserted vertex if it encroaches */ -/* upon a segment. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -void splittriangle(struct mesh *m, struct behavior *b, - struct badtriang *badtri) -#else /* not ANSI_DECLARATORS */ -void splittriangle(m, b, badtri) -struct mesh *m; -struct behavior *b; -struct badtriang *badtri; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri badotri; - vertex borg, bdest, bapex; - vertex newvertex; - REAL xi, eta; - enum insertvertexresult success; - int errorflag; - int i; - - decode(badtri->poortri, badotri); - org(badotri, borg); - dest(badotri, bdest); - apex(badotri, bapex); - /* Make sure that this triangle is still the same triangle it was */ - /* when it was tested and determined to be of bad quality. */ - /* Subsequent transformations may have made it a different triangle. */ - if (!deadtri(badotri.tri) && (borg == badtri->triangorg) && - (bdest == badtri->triangdest) && (bapex == badtri->triangapex)) { - if (b->verbose > 1) { - printf(" Splitting this triangle at its circumcenter:\n"); - printf(" (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", borg[0], - borg[1], bdest[0], bdest[1], bapex[0], bapex[1]); - } - - errorflag = 0; - /* Create a new vertex at the triangle's circumcenter. */ - newvertex = (vertex) poolalloc(&m->vertices); - findcircumcenter(m, b, borg, bdest, bapex, newvertex, &xi, &eta, 1); - - /* Check whether the new vertex lies on a triangle vertex. */ - if (((newvertex[0] == borg[0]) && (newvertex[1] == borg[1])) || - ((newvertex[0] == bdest[0]) && (newvertex[1] == bdest[1])) || - ((newvertex[0] == bapex[0]) && (newvertex[1] == bapex[1]))) { - if (!b->quiet) { - printf( - "Warning: New vertex (%.12g, %.12g) falls on existing vertex.\n", - newvertex[0], newvertex[1]); - errorflag = 1; - } - vertexdealloc(m, newvertex); - } else { - for (i = 2; i < 2 + m->nextras; i++) { - /* Interpolate the vertex attributes at the circumcenter. */ - newvertex[i] = borg[i] + xi * (bdest[i] - borg[i]) - + eta * (bapex[i] - borg[i]); - } - /* The new vertex must be in the interior, and therefore is a */ - /* free vertex with a marker of zero. */ - setvertexmark(newvertex, 0); - setvertextype(newvertex, FREEVERTEX); - - /* Ensure that the handle `badotri' does not represent the longest */ - /* edge of the triangle. This ensures that the circumcenter must */ - /* fall to the left of this edge, so point location will work. */ - /* (If the angle org-apex-dest exceeds 90 degrees, then the */ - /* circumcenter lies outside the org-dest edge, and eta is */ - /* negative. Roundoff error might prevent eta from being */ - /* negative when it should be, so I test eta against xi.) */ - if (eta < xi) { - lprevself(badotri); - } - - /* Insert the circumcenter, searching from the edge of the triangle, */ - /* and maintain the Delaunay property of the triangulation. */ - success = insertvertex(m, b, newvertex, &badotri, (struct osub *) NULL, - 1, 1); - if (success == SUCCESSFULVERTEX) { - if (m->steinerleft > 0) { - m->steinerleft--; - } - } else if (success == ENCROACHINGVERTEX) { - /* If the newly inserted vertex encroaches upon a subsegment, */ - /* delete the new vertex. */ - undovertex(m, b); - if (b->verbose > 1) { - printf(" Rejecting (%.12g, %.12g).\n", newvertex[0], newvertex[1]); - } - vertexdealloc(m, newvertex); - } else if (success == VIOLATINGVERTEX) { - /* Failed to insert the new vertex, but some subsegment was */ - /* marked as being encroached. */ - vertexdealloc(m, newvertex); - } else { /* success == DUPLICATEVERTEX */ - /* Couldn't insert the new vertex because a vertex is already there. */ - if (!b->quiet) { - printf( - "Warning: New vertex (%.12g, %.12g) falls on existing vertex.\n", - newvertex[0], newvertex[1]); - errorflag = 1; - } - vertexdealloc(m, newvertex); - } - } - if (errorflag) { - if (b->verbose) { - printf(" The new vertex is at the circumcenter of triangle\n"); - printf(" (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", - borg[0], borg[1], bdest[0], bdest[1], bapex[0], bapex[1]); - } - printf("This probably means that I am trying to refine triangles\n"); - printf(" to a smaller size than can be accommodated by the finite\n"); - printf(" precision of floating point arithmetic. (You can be\n"); - printf(" sure of this if I fail to terminate.)\n"); - precisionerror(); - } - } -} - -#endif /* not CDT_ONLY */ - -/*****************************************************************************/ -/* */ -/* enforcequality() Remove all the encroached subsegments and bad */ -/* triangles from the triangulation. */ -/* */ -/*****************************************************************************/ - -#ifndef CDT_ONLY - -#ifdef ANSI_DECLARATORS -void enforcequality(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void enforcequality(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct badtriang *badtri; - int i; - - if (!b->quiet) { - printf("Adding Steiner points to enforce quality.\n"); - } - /* Initialize the pool of encroached subsegments. */ - poolinit(&m->badsubsegs, sizeof(struct badsubseg), BADSUBSEGPERBLOCK, - BADSUBSEGPERBLOCK, 0); - if (b->verbose) { - printf(" Looking for encroached subsegments.\n"); - } - /* Test all segments to see if they're encroached. */ - tallyencs(m, b); - if (b->verbose && (m->badsubsegs.items > 0)) { - printf(" Splitting encroached subsegments.\n"); - } - /* Fix encroached subsegments without noting bad triangles. */ - splitencsegs(m, b, 0); - /* At this point, if we haven't run out of Steiner points, the */ - /* triangulation should be (conforming) Delaunay. */ - - /* Next, we worry about enforcing triangle quality. */ - if ((b->minangle > 0.0) || b->vararea || b->fixedarea || b->usertest) { - /* Initialize the pool of bad triangles. */ - poolinit(&m->badtriangles, sizeof(struct badtriang), BADTRIPERBLOCK, - BADTRIPERBLOCK, 0); - /* Initialize the queues of bad triangles. */ - for (i = 0; i < 4096; i++) { - m->queuefront[i] = (struct badtriang *) NULL; - } - m->firstnonemptyq = -1; - /* Test all triangles to see if they're bad. */ - tallyfaces(m, b); - /* Initialize the pool of recently flipped triangles. */ - poolinit(&m->flipstackers, sizeof(struct flipstacker), FLIPSTACKERPERBLOCK, - FLIPSTACKERPERBLOCK, 0); - m->checkquality = 1; - if (b->verbose) { - printf(" Splitting bad triangles.\n"); - } - while ((m->badtriangles.items > 0) && (m->steinerleft != 0)) { - /* Fix one bad triangle by inserting a vertex at its circumcenter. */ - badtri = dequeuebadtriang(m); - splittriangle(m, b, badtri); - if (m->badsubsegs.items > 0) { - /* Put bad triangle back in queue for another try later. */ - enqueuebadtriang(m, b, badtri); - /* Fix any encroached subsegments that resulted. */ - /* Record any new bad triangles that result. */ - splitencsegs(m, b, 1); - } else { - /* Return the bad triangle to the pool. */ - pooldealloc(&m->badtriangles, (VOID *) badtri); - } - } - } - /* At this point, if the "-D" switch was selected and we haven't run out */ - /* of Steiner points, the triangulation should be (conforming) Delaunay */ - /* and have no low-quality triangles. */ - - /* Might we have run out of Steiner points too soon? */ - if (!b->quiet && b->conformdel && (m->badsubsegs.items > 0) && - (m->steinerleft == 0)) { - printf("\nWarning: I ran out of Steiner points, but the mesh has\n"); - if (m->badsubsegs.items == 1) { - printf(" one encroached subsegment, and therefore might not be truly\n" - ); - } else { - printf(" %ld encroached subsegments, and therefore might not be truly\n" - , m->badsubsegs.items); - } - printf(" Delaunay. If the Delaunay property is important to you,\n"); - printf(" try increasing the number of Steiner points (controlled by\n"); - printf(" the -S switch) slightly and try again.\n\n"); - } -} - -#endif /* not CDT_ONLY */ - -/** **/ -/** **/ -/********* Mesh quality maintenance ends here *********/ - -/*****************************************************************************/ -/* */ -/* highorder() Create extra nodes for quadratic subparametric elements. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void highorder(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void highorder(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri triangleloop, trisym; - struct osub checkmark; - vertex newvertex; - vertex torg, tdest; - int i; - triangle ptr; /* Temporary variable used by sym(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - - if (!b->quiet) { - printf("Adding vertices for second-order triangles.\n"); - } - /* The following line ensures that dead items in the pool of nodes */ - /* cannot be allocated for the extra nodes associated with high */ - /* order elements. This ensures that the primary nodes (at the */ - /* corners of elements) will occur earlier in the output files, and */ - /* have lower indices, than the extra nodes. */ - m->vertices.deaditemstack = (VOID *) NULL; - - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - /* To loop over the set of edges, loop over all triangles, and look at */ - /* the three edges of each triangle. If there isn't another triangle */ - /* adjacent to the edge, operate on the edge. If there is another */ - /* adjacent triangle, operate on the edge only if the current triangle */ - /* has a smaller pointer than its neighbor. This way, each edge is */ - /* considered only once. */ - while (triangleloop.tri != (triangle *) NULL) { - for (triangleloop.orient = 0; triangleloop.orient < 3; - triangleloop.orient++) { - sym(triangleloop, trisym); - if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) { - org(triangleloop, torg); - dest(triangleloop, tdest); - /* Create a new node in the middle of the edge. Interpolate */ - /* its attributes. */ - newvertex = (vertex) poolalloc(&m->vertices); - for (i = 0; i < 2 + m->nextras; i++) { - newvertex[i] = 0.5 * (torg[i] + tdest[i]); - } - /* Set the new node's marker to zero or one, depending on */ - /* whether it lies on a boundary. */ - setvertexmark(newvertex, trisym.tri == m->dummytri); - setvertextype(newvertex, - trisym.tri == m->dummytri ? FREEVERTEX : SEGMENTVERTEX); - if (b->usesegments) { - tspivot(triangleloop, checkmark); - /* If this edge is a segment, transfer the marker to the new node. */ - if (checkmark.ss != m->dummysub) { - setvertexmark(newvertex, mark(checkmark)); - setvertextype(newvertex, SEGMENTVERTEX); - } - } - if (b->verbose > 1) { - printf(" Creating (%.12g, %.12g).\n", newvertex[0], newvertex[1]); - } - /* Record the new node in the (one or two) adjacent elements. */ - triangleloop.tri[m->highorderindex + triangleloop.orient] = - (triangle) newvertex; - if (trisym.tri != m->dummytri) { - trisym.tri[m->highorderindex + trisym.orient] = (triangle) newvertex; - } - } - } - triangleloop.tri = triangletraverse(m); - } -} - -/********* File I/O routines begin here *********/ -/** **/ -/** **/ - -/*****************************************************************************/ -/* */ -/* readline() Read a nonempty line from a file. */ -/* */ -/* A line is considered "nonempty" if it contains something that looks like */ -/* a number. Comments (prefaced by `#') are ignored. */ -/* */ -/*****************************************************************************/ - -#ifndef TRILIBRARY - -#ifdef ANSI_DECLARATORS -char *readline(char *string, FILE *infile, char *infilename) -#else /* not ANSI_DECLARATORS */ -char *readline(string, infile, infilename) -char *string; -FILE *infile; -char *infilename; -#endif /* not ANSI_DECLARATORS */ - -{ - char *result; - - /* Search for something that looks like a number. */ - do { - result = fgets(string, INPUTLINESIZE, infile); - if (result == (char *) NULL) { - printf(" Error: Unexpected end of file in %s.\n", infilename); - triexit(1); - } - /* Skip anything that doesn't look like a number, a comment, */ - /* or the end of a line. */ - while ((*result != '\0') && (*result != '#') - && (*result != '.') && (*result != '+') && (*result != '-') - && ((*result < '0') || (*result > '9'))) { - result++; - } - /* If it's a comment or end of line, read another line and try again. */ - } while ((*result == '#') || (*result == '\0')); - return result; -} - -#endif /* not TRILIBRARY */ - -/*****************************************************************************/ -/* */ -/* findfield() Find the next field of a string. */ -/* */ -/* Jumps past the current field by searching for whitespace, then jumps */ -/* past the whitespace to find the next field. */ -/* */ -/*****************************************************************************/ - -#ifndef TRILIBRARY - -#ifdef ANSI_DECLARATORS -char *findfield(char *string) -#else /* not ANSI_DECLARATORS */ -char *findfield(string) -char *string; -#endif /* not ANSI_DECLARATORS */ - -{ - char *result; - - result = string; - /* Skip the current field. Stop upon reaching whitespace. */ - while ((*result != '\0') && (*result != '#') - && (*result != ' ') && (*result != '\t')) { - result++; - } - /* Now skip the whitespace and anything else that doesn't look like a */ - /* number, a comment, or the end of a line. */ - while ((*result != '\0') && (*result != '#') - && (*result != '.') && (*result != '+') && (*result != '-') - && ((*result < '0') || (*result > '9'))) { - result++; - } - /* Check for a comment (prefixed with `#'). */ - if (*result == '#') { - *result = '\0'; - } - return result; -} - -#endif /* not TRILIBRARY */ - -/*****************************************************************************/ -/* */ -/* readnodes() Read the vertices from a file, which may be a .node or */ -/* .poly file. */ -/* */ -/*****************************************************************************/ - -#ifndef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void readnodes(struct mesh *m, struct behavior *b, char *nodefilename, - char *polyfilename, FILE **polyfile) -#else /* not ANSI_DECLARATORS */ -void readnodes(m, b, nodefilename, polyfilename, polyfile) -struct mesh *m; -struct behavior *b; -char *nodefilename; -char *polyfilename; -FILE **polyfile; -#endif /* not ANSI_DECLARATORS */ - -{ - FILE *infile; - vertex vertexloop; - char inputline[INPUTLINESIZE]; - char *stringptr; - char *infilename; - REAL x, y; - int firstnode; - int nodemarkers; - int currentmarker; - int i, j; - - if (b->poly) { - /* Read the vertices from a .poly file. */ - if (!b->quiet) { - printf("Opening %s.\n", polyfilename); - } - *polyfile = fopen(polyfilename, "r"); - if (*polyfile == (FILE *) NULL) { - printf(" Error: Cannot access file %s.\n", polyfilename); - triexit(1); - } - /* Read number of vertices, number of dimensions, number of vertex */ - /* attributes, and number of boundary markers. */ - stringptr = readline(inputline, *polyfile, polyfilename); - m->invertices = (int) strtol(stringptr, &stringptr, 0); - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - m->mesh_dim = 2; - } else { - m->mesh_dim = (int) strtol(stringptr, &stringptr, 0); - } - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - m->nextras = 0; - } else { - m->nextras = (int) strtol(stringptr, &stringptr, 0); - } - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - nodemarkers = 0; - } else { - nodemarkers = (int) strtol(stringptr, &stringptr, 0); - } - if (m->invertices > 0) { - infile = *polyfile; - infilename = polyfilename; - m->readnodefile = 0; - } else { - /* If the .poly file claims there are zero vertices, that means that */ - /* the vertices should be read from a separate .node file. */ - m->readnodefile = 1; - infilename = nodefilename; - } - } else { - m->readnodefile = 1; - infilename = nodefilename; - *polyfile = (FILE *) NULL; - } - - if (m->readnodefile) { - /* Read the vertices from a .node file. */ - if (!b->quiet) { - printf("Opening %s.\n", nodefilename); - } - infile = fopen(nodefilename, "r"); - if (infile == (FILE *) NULL) { - printf(" Error: Cannot access file %s.\n", nodefilename); - triexit(1); - } - /* Read number of vertices, number of dimensions, number of vertex */ - /* attributes, and number of boundary markers. */ - stringptr = readline(inputline, infile, nodefilename); - m->invertices = (int) strtol(stringptr, &stringptr, 0); - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - m->mesh_dim = 2; - } else { - m->mesh_dim = (int) strtol(stringptr, &stringptr, 0); - } - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - m->nextras = 0; - } else { - m->nextras = (int) strtol(stringptr, &stringptr, 0); - } - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - nodemarkers = 0; - } else { - nodemarkers = (int) strtol(stringptr, &stringptr, 0); - } - } - - if (m->invertices < 3) { - printf("Error: Input must have at least three input vertices.\n"); - triexit(1); - } - if (m->mesh_dim != 2) { - printf("Error: Triangle only works with two-dimensional meshes.\n"); - triexit(1); - } - if (m->nextras == 0) { - b->weighted = 0; - } - - initializevertexpool(m, b); - - /* Read the vertices. */ - for (i = 0; i < m->invertices; i++) { - vertexloop = (vertex) poolalloc(&m->vertices); - stringptr = readline(inputline, infile, infilename); - if (i == 0) { - firstnode = (int) strtol(stringptr, &stringptr, 0); - if ((firstnode == 0) || (firstnode == 1)) { - b->firstnumber = firstnode; - } - } - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - printf("Error: Vertex %d has no x coordinate.\n", b->firstnumber + i); - triexit(1); - } - x = (REAL) strtod(stringptr, &stringptr); - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - printf("Error: Vertex %d has no y coordinate.\n", b->firstnumber + i); - triexit(1); - } - y = (REAL) strtod(stringptr, &stringptr); - vertexloop[0] = x; - vertexloop[1] = y; - /* Read the vertex attributes. */ - for (j = 2; j < 2 + m->nextras; j++) { - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - vertexloop[j] = 0.0; - } else { - vertexloop[j] = (REAL) strtod(stringptr, &stringptr); - } - } - if (nodemarkers) { - /* Read a vertex marker. */ - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - setvertexmark(vertexloop, 0); - } else { - currentmarker = (int) strtol(stringptr, &stringptr, 0); - setvertexmark(vertexloop, currentmarker); - } - } else { - /* If no markers are specified in the file, they default to zero. */ - setvertexmark(vertexloop, 0); - } - setvertextype(vertexloop, INPUTVERTEX); - /* Determine the smallest and largest x and y coordinates. */ - if (i == 0) { - m->xmin = m->xmax = x; - m->ymin = m->ymax = y; - } else { - m->xmin = (x < m->xmin) ? x : m->xmin; - m->xmax = (x > m->xmax) ? x : m->xmax; - m->ymin = (y < m->ymin) ? y : m->ymin; - m->ymax = (y > m->ymax) ? y : m->ymax; - } - } - if (m->readnodefile) { - fclose(infile); - } - - /* Nonexistent x value used as a flag to mark circle events in sweepline */ - /* Delaunay algorithm. */ - m->xminextreme = 10 * m->xmin - 9 * m->xmax; -} - -#endif /* not TRILIBRARY */ - -/*****************************************************************************/ -/* */ -/* transfernodes() Read the vertices from memory. */ -/* */ -/*****************************************************************************/ - -#ifdef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void transfernodes(struct mesh *m, struct behavior *b, REAL *pointlist, - REAL *pointattriblist, int *pointmarkerlist, - int numberofpoints, int numberofpointattribs) -#else /* not ANSI_DECLARATORS */ -void transfernodes(m, b, pointlist, pointattriblist, pointmarkerlist, - numberofpoints, numberofpointattribs) -struct mesh *m; -struct behavior *b; -REAL *pointlist; -REAL *pointattriblist; -int *pointmarkerlist; -int numberofpoints; -int numberofpointattribs; -#endif /* not ANSI_DECLARATORS */ - -{ - vertex vertexloop; - REAL x, y; - int i, j; - int coordindex; - int attribindex; - - m->invertices = numberofpoints; - m->mesh_dim = 2; - m->nextras = numberofpointattribs; - m->readnodefile = 0; - if (m->invertices < 3) { - printf("Error: Input must have at least three input vertices.\n"); - triexit(1); - } - if (m->nextras == 0) { - b->weighted = 0; - } - - initializevertexpool(m, b); - - /* Read the vertices. */ - coordindex = 0; - attribindex = 0; - for (i = 0; i < m->invertices; i++) { - vertexloop = (vertex) poolalloc(&m->vertices); - /* Read the vertex coordinates. */ - x = vertexloop[0] = pointlist[coordindex++]; - y = vertexloop[1] = pointlist[coordindex++]; - /* Read the vertex attributes. */ - for (j = 0; j < numberofpointattribs; j++) { - vertexloop[2 + j] = pointattriblist[attribindex++]; - } - if (pointmarkerlist != (int *) NULL) { - /* Read a vertex marker. */ - setvertexmark(vertexloop, pointmarkerlist[i]); - } else { - /* If no markers are specified, they default to zero. */ - setvertexmark(vertexloop, 0); - } - setvertextype(vertexloop, INPUTVERTEX); - /* Determine the smallest and largest x and y coordinates. */ - if (i == 0) { - m->xmin = m->xmax = x; - m->ymin = m->ymax = y; - } else { - m->xmin = (x < m->xmin) ? x : m->xmin; - m->xmax = (x > m->xmax) ? x : m->xmax; - m->ymin = (y < m->ymin) ? y : m->ymin; - m->ymax = (y > m->ymax) ? y : m->ymax; - } - } - - /* Nonexistent x value used as a flag to mark circle events in sweepline */ - /* Delaunay algorithm. */ - m->xminextreme = 10 * m->xmin - 9 * m->xmax; -} - -#endif /* TRILIBRARY */ - -/*****************************************************************************/ -/* */ -/* readholes() Read the holes, and possibly regional attributes and area */ -/* constraints, from a .poly file. */ -/* */ -/*****************************************************************************/ - -#ifndef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void readholes(struct mesh *m, struct behavior *b, - FILE *polyfile, char *polyfilename, REAL **hlist, int *holes, - REAL **rlist, int *regions) -#else /* not ANSI_DECLARATORS */ -void readholes(m, b, polyfile, polyfilename, hlist, holes, rlist, regions) -struct mesh *m; -struct behavior *b; -FILE *polyfile; -char *polyfilename; -REAL **hlist; -int *holes; -REAL **rlist; -int *regions; -#endif /* not ANSI_DECLARATORS */ - -{ - REAL *holelist; - REAL *regionlist; - char inputline[INPUTLINESIZE]; - char *stringptr; - int index; - int i; - - /* Read the holes. */ - stringptr = readline(inputline, polyfile, polyfilename); - *holes = (int) strtol(stringptr, &stringptr, 0); - if (*holes > 0) { - holelist = (REAL *) trimalloc(2 * *holes * (int) sizeof(REAL)); - *hlist = holelist; - for (i = 0; i < 2 * *holes; i += 2) { - stringptr = readline(inputline, polyfile, polyfilename); - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - printf("Error: Hole %d has no x coordinate.\n", - b->firstnumber + (i >> 1)); - triexit(1); - } else { - holelist[i] = (REAL) strtod(stringptr, &stringptr); - } - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - printf("Error: Hole %d has no y coordinate.\n", - b->firstnumber + (i >> 1)); - triexit(1); - } else { - holelist[i + 1] = (REAL) strtod(stringptr, &stringptr); - } - } - } else { - *hlist = (REAL *) NULL; - } - -#ifndef CDT_ONLY - if ((b->regionattrib || b->vararea) && !b->refine) { - /* Read the area constraints. */ - stringptr = readline(inputline, polyfile, polyfilename); - *regions = (int) strtol(stringptr, &stringptr, 0); - if (*regions > 0) { - regionlist = (REAL *) trimalloc(4 * *regions * (int) sizeof(REAL)); - *rlist = regionlist; - index = 0; - for (i = 0; i < *regions; i++) { - stringptr = readline(inputline, polyfile, polyfilename); - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - printf("Error: Region %d has no x coordinate.\n", - b->firstnumber + i); - triexit(1); - } else { - regionlist[index++] = (REAL) strtod(stringptr, &stringptr); - } - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - printf("Error: Region %d has no y coordinate.\n", - b->firstnumber + i); - triexit(1); - } else { - regionlist[index++] = (REAL) strtod(stringptr, &stringptr); - } - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - printf( - "Error: Region %d has no region attribute or area constraint.\n", - b->firstnumber + i); - triexit(1); - } else { - regionlist[index++] = (REAL) strtod(stringptr, &stringptr); - } - stringptr = findfield(stringptr); - if (*stringptr == '\0') { - regionlist[index] = regionlist[index - 1]; - } else { - regionlist[index] = (REAL) strtod(stringptr, &stringptr); - } - index++; - } - } - } else { - /* Set `*regions' to zero to avoid an accidental free() later. */ - *regions = 0; - *rlist = (REAL *) NULL; - } -#endif /* not CDT_ONLY */ - - fclose(polyfile); -} - -#endif /* not TRILIBRARY */ - -/*****************************************************************************/ -/* */ -/* finishfile() Write the command line to the output file so the user */ -/* can remember how the file was generated. Close the file. */ -/* */ -/*****************************************************************************/ - -#ifndef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void finishfile(FILE *outfile, int argc, char **argv) -#else /* not ANSI_DECLARATORS */ -void finishfile(outfile, argc, argv) -FILE *outfile; -int argc; -char **argv; -#endif /* not ANSI_DECLARATORS */ - -{ - int i; - - fprintf(outfile, "# Generated by"); - for (i = 0; i < argc; i++) { - fprintf(outfile, " "); - fputs(argv[i], outfile); - } - fprintf(outfile, "\n"); - fclose(outfile); -} - -#endif /* not TRILIBRARY */ - -/*****************************************************************************/ -/* */ -/* writenodes() Number the vertices and write them to a .node file. */ -/* */ -/* To save memory, the vertex numbers are written over the boundary markers */ -/* after the vertices are written to a file. */ -/* */ -/*****************************************************************************/ - -#ifdef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void writenodes(struct mesh *m, struct behavior *b, REAL **pointlist, - REAL **pointattriblist, int **pointmarkerlist) -#else /* not ANSI_DECLARATORS */ -void writenodes(m, b, pointlist, pointattriblist, pointmarkerlist) -struct mesh *m; -struct behavior *b; -REAL **pointlist; -REAL **pointattriblist; -int **pointmarkerlist; -#endif /* not ANSI_DECLARATORS */ - -#else /* not TRILIBRARY */ - -#ifdef ANSI_DECLARATORS -void writenodes(struct mesh *m, struct behavior *b, char *nodefilename, - int argc, char **argv) -#else /* not ANSI_DECLARATORS */ -void writenodes(m, b, nodefilename, argc, argv) -struct mesh *m; -struct behavior *b; -char *nodefilename; -int argc; -char **argv; -#endif /* not ANSI_DECLARATORS */ - -#endif /* not TRILIBRARY */ - -{ -#ifdef TRILIBRARY - REAL *plist; - REAL *palist; - int *pmlist; - int coordindex; - int attribindex; -#else /* not TRILIBRARY */ - FILE *outfile; -#endif /* not TRILIBRARY */ - vertex vertexloop; - long outvertices; - int vertexnumber; - int i; - - if (b->jettison) { - outvertices = m->vertices.items - m->undeads; - } else { - outvertices = m->vertices.items; - } - -#ifdef TRILIBRARY - if (!b->quiet) { - printf("Writing vertices.\n"); - } - /* Allocate memory for output vertices if necessary. */ - if (*pointlist == (REAL *) NULL) { - *pointlist = (REAL *) trimalloc((int) (outvertices * 2 * sizeof(REAL))); - } - /* Allocate memory for output vertex attributes if necessary. */ - if ((m->nextras > 0) && (*pointattriblist == (REAL *) NULL)) { - *pointattriblist = (REAL *) trimalloc((int) (outvertices * m->nextras * - sizeof(REAL))); - } - /* Allocate memory for output vertex markers if necessary. */ - if (!b->nobound && (*pointmarkerlist == (int *) NULL)) { - *pointmarkerlist = (int *) trimalloc((int) (outvertices * sizeof(int))); - } - plist = *pointlist; - palist = *pointattriblist; - pmlist = *pointmarkerlist; - coordindex = 0; - attribindex = 0; -#else /* not TRILIBRARY */ - if (!b->quiet) { - printf("Writing %s.\n", nodefilename); - } - outfile = fopen(nodefilename, "w"); - if (outfile == (FILE *) NULL) { - printf(" Error: Cannot create file %s.\n", nodefilename); - triexit(1); - } - /* Number of vertices, number of dimensions, number of vertex attributes, */ - /* and number of boundary markers (zero or one). */ - fprintf(outfile, "%ld %d %d %d\n", outvertices, m->mesh_dim, - m->nextras, 1 - b->nobound); -#endif /* not TRILIBRARY */ - - traversalinit(&m->vertices); - vertexnumber = b->firstnumber; - vertexloop = vertextraverse(m); - while (vertexloop != (vertex) NULL) { - if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) { -#ifdef TRILIBRARY - /* X and y coordinates. */ - plist[coordindex++] = vertexloop[0]; - plist[coordindex++] = vertexloop[1]; - /* Vertex attributes. */ - for (i = 0; i < m->nextras; i++) { - palist[attribindex++] = vertexloop[2 + i]; - } - if (!b->nobound) { - /* Copy the boundary marker. */ - pmlist[vertexnumber - b->firstnumber] = vertexmark(vertexloop); - } -#else /* not TRILIBRARY */ - /* Vertex number, x and y coordinates. */ - fprintf(outfile, "%4d %.17g %.17g", vertexnumber, vertexloop[0], - vertexloop[1]); - for (i = 0; i < m->nextras; i++) { - /* Write an attribute. */ - fprintf(outfile, " %.17g", vertexloop[i + 2]); - } - if (b->nobound) { - fprintf(outfile, "\n"); - } else { - /* Write the boundary marker. */ - fprintf(outfile, " %d\n", vertexmark(vertexloop)); - } -#endif /* not TRILIBRARY */ - - setvertexmark(vertexloop, vertexnumber); - vertexnumber++; - } - vertexloop = vertextraverse(m); - } - -#ifndef TRILIBRARY - finishfile(outfile, argc, argv); -#endif /* not TRILIBRARY */ -} - -/*****************************************************************************/ -/* */ -/* numbernodes() Number the vertices. */ -/* */ -/* Each vertex is assigned a marker equal to its number. */ -/* */ -/* Used when writenodes() is not called because no .node file is written. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void numbernodes(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void numbernodes(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - vertex vertexloop; - int vertexnumber; - - traversalinit(&m->vertices); - vertexnumber = b->firstnumber; - vertexloop = vertextraverse(m); - while (vertexloop != (vertex) NULL) { - setvertexmark(vertexloop, vertexnumber); - if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) { - vertexnumber++; - } - vertexloop = vertextraverse(m); - } -} - -/*****************************************************************************/ -/* */ -/* writeelements() Write the triangles to an .ele file. */ -/* */ -/*****************************************************************************/ - -#ifdef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void writeelements(struct mesh *m, struct behavior *b, - int **trianglelist, REAL **triangleattriblist) -#else /* not ANSI_DECLARATORS */ -void writeelements(m, b, trianglelist, triangleattriblist) -struct mesh *m; -struct behavior *b; -int **trianglelist; -REAL **triangleattriblist; -#endif /* not ANSI_DECLARATORS */ - -#else /* not TRILIBRARY */ - -#ifdef ANSI_DECLARATORS -void writeelements(struct mesh *m, struct behavior *b, char *elefilename, - int argc, char **argv) -#else /* not ANSI_DECLARATORS */ -void writeelements(m, b, elefilename, argc, argv) -struct mesh *m; -struct behavior *b; -char *elefilename; -int argc; -char **argv; -#endif /* not ANSI_DECLARATORS */ - -#endif /* not TRILIBRARY */ - -{ -#ifdef TRILIBRARY - int *tlist; - REAL *talist; - int vertexindex; - int attribindex; -#else /* not TRILIBRARY */ - FILE *outfile; -#endif /* not TRILIBRARY */ - struct otri triangleloop; - vertex p1, p2, p3; - vertex mid1, mid2, mid3; - long elementnumber; - int i; - -#ifdef TRILIBRARY - if (!b->quiet) { - printf("Writing triangles.\n"); - } - /* Allocate memory for output triangles if necessary. */ - if (*trianglelist == (int *) NULL) { - *trianglelist = (int *) trimalloc((int) (m->triangles.items * - ((b->order + 1) * (b->order + 2) / - 2) * sizeof(int))); - } - /* Allocate memory for output triangle attributes if necessary. */ - if ((m->eextras > 0) && (*triangleattriblist == (REAL *) NULL)) { - *triangleattriblist = (REAL *) trimalloc((int) (m->triangles.items * - m->eextras * - sizeof(REAL))); - } - tlist = *trianglelist; - talist = *triangleattriblist; - vertexindex = 0; - attribindex = 0; -#else /* not TRILIBRARY */ - if (!b->quiet) { - printf("Writing %s.\n", elefilename); - } - outfile = fopen(elefilename, "w"); - if (outfile == (FILE *) NULL) { - printf(" Error: Cannot create file %s.\n", elefilename); - triexit(1); - } - /* Number of triangles, vertices per triangle, attributes per triangle. */ - fprintf(outfile, "%ld %d %d\n", m->triangles.items, - (b->order + 1) * (b->order + 2) / 2, m->eextras); -#endif /* not TRILIBRARY */ - - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - triangleloop.orient = 0; - elementnumber = b->firstnumber; - while (triangleloop.tri != (triangle *) NULL) { - org(triangleloop, p1); - dest(triangleloop, p2); - apex(triangleloop, p3); - if (b->order == 1) { -#ifdef TRILIBRARY - tlist[vertexindex++] = vertexmark(p1); - tlist[vertexindex++] = vertexmark(p2); - tlist[vertexindex++] = vertexmark(p3); -#else /* not TRILIBRARY */ - /* Triangle number, indices for three vertices. */ - fprintf(outfile, "%4ld %4d %4d %4d", elementnumber, - vertexmark(p1), vertexmark(p2), vertexmark(p3)); -#endif /* not TRILIBRARY */ - } else { - mid1 = (vertex) triangleloop.tri[m->highorderindex + 1]; - mid2 = (vertex) triangleloop.tri[m->highorderindex + 2]; - mid3 = (vertex) triangleloop.tri[m->highorderindex]; -#ifdef TRILIBRARY - tlist[vertexindex++] = vertexmark(p1); - tlist[vertexindex++] = vertexmark(p2); - tlist[vertexindex++] = vertexmark(p3); - tlist[vertexindex++] = vertexmark(mid1); - tlist[vertexindex++] = vertexmark(mid2); - tlist[vertexindex++] = vertexmark(mid3); -#else /* not TRILIBRARY */ - /* Triangle number, indices for six vertices. */ - fprintf(outfile, "%4ld %4d %4d %4d %4d %4d %4d", elementnumber, - vertexmark(p1), vertexmark(p2), vertexmark(p3), vertexmark(mid1), - vertexmark(mid2), vertexmark(mid3)); -#endif /* not TRILIBRARY */ - } - -#ifdef TRILIBRARY - for (i = 0; i < m->eextras; i++) { - talist[attribindex++] = elemattribute(triangleloop, i); - } -#else /* not TRILIBRARY */ - for (i = 0; i < m->eextras; i++) { - fprintf(outfile, " %.17g", elemattribute(triangleloop, i)); - } - fprintf(outfile, "\n"); -#endif /* not TRILIBRARY */ - - triangleloop.tri = triangletraverse(m); - elementnumber++; - } - -#ifndef TRILIBRARY - finishfile(outfile, argc, argv); -#endif /* not TRILIBRARY */ -} - -/*****************************************************************************/ -/* */ -/* writepoly() Write the segments and holes to a .poly file. */ -/* */ -/*****************************************************************************/ - -#ifdef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void writepoly(struct mesh *m, struct behavior *b, - int **segmentlist, int **segmentmarkerlist) -#else /* not ANSI_DECLARATORS */ -void writepoly(m, b, segmentlist, segmentmarkerlist) -struct mesh *m; -struct behavior *b; -int **segmentlist; -int **segmentmarkerlist; -#endif /* not ANSI_DECLARATORS */ - -#else /* not TRILIBRARY */ - -#ifdef ANSI_DECLARATORS -void writepoly(struct mesh *m, struct behavior *b, char *polyfilename, - REAL *holelist, int holes, REAL *regionlist, int regions, - int argc, char **argv) -#else /* not ANSI_DECLARATORS */ -void writepoly(m, b, polyfilename, holelist, holes, regionlist, regions, - argc, argv) -struct mesh *m; -struct behavior *b; -char *polyfilename; -REAL *holelist; -int holes; -REAL *regionlist; -int regions; -int argc; -char **argv; -#endif /* not ANSI_DECLARATORS */ - -#endif /* not TRILIBRARY */ - -{ -#ifdef TRILIBRARY - int *slist; - int *smlist; - int index; -#else /* not TRILIBRARY */ - FILE *outfile; - long holenumber, regionnumber; -#endif /* not TRILIBRARY */ - struct osub subsegloop; - vertex endpoint1, endpoint2; - long subsegnumber; - -#ifdef TRILIBRARY - if (!b->quiet) { - printf("Writing segments.\n"); - } - /* Allocate memory for output segments if necessary. */ - if (*segmentlist == (int *) NULL) { - *segmentlist = (int *) trimalloc((int) (m->subsegs.items * 2 * - sizeof(int))); - } - /* Allocate memory for output segment markers if necessary. */ - if (!b->nobound && (*segmentmarkerlist == (int *) NULL)) { - *segmentmarkerlist = (int *) trimalloc((int) (m->subsegs.items * - sizeof(int))); - } - slist = *segmentlist; - smlist = *segmentmarkerlist; - index = 0; -#else /* not TRILIBRARY */ - if (!b->quiet) { - printf("Writing %s.\n", polyfilename); - } - outfile = fopen(polyfilename, "w"); - if (outfile == (FILE *) NULL) { - printf(" Error: Cannot create file %s.\n", polyfilename); - triexit(1); - } - /* The zero indicates that the vertices are in a separate .node file. */ - /* Followed by number of dimensions, number of vertex attributes, */ - /* and number of boundary markers (zero or one). */ - fprintf(outfile, "%d %d %d %d\n", 0, m->mesh_dim, m->nextras, - 1 - b->nobound); - /* Number of segments, number of boundary markers (zero or one). */ - fprintf(outfile, "%ld %d\n", m->subsegs.items, 1 - b->nobound); -#endif /* not TRILIBRARY */ - - traversalinit(&m->subsegs); - subsegloop.ss = subsegtraverse(m); - subsegloop.ssorient = 0; - subsegnumber = b->firstnumber; - while (subsegloop.ss != (subseg *) NULL) { - sorg(subsegloop, endpoint1); - sdest(subsegloop, endpoint2); -#ifdef TRILIBRARY - /* Copy indices of the segment's two endpoints. */ - slist[index++] = vertexmark(endpoint1); - slist[index++] = vertexmark(endpoint2); - if (!b->nobound) { - /* Copy the boundary marker. */ - smlist[subsegnumber - b->firstnumber] = mark(subsegloop); - } -#else /* not TRILIBRARY */ - /* Segment number, indices of its two endpoints, and possibly a marker. */ - if (b->nobound) { - fprintf(outfile, "%4ld %4d %4d\n", subsegnumber, - vertexmark(endpoint1), vertexmark(endpoint2)); - } else { - fprintf(outfile, "%4ld %4d %4d %4d\n", subsegnumber, - vertexmark(endpoint1), vertexmark(endpoint2), mark(subsegloop)); - } -#endif /* not TRILIBRARY */ - - subsegloop.ss = subsegtraverse(m); - subsegnumber++; - } - -#ifndef TRILIBRARY -#ifndef CDT_ONLY - fprintf(outfile, "%d\n", holes); - if (holes > 0) { - for (holenumber = 0; holenumber < holes; holenumber++) { - /* Hole number, x and y coordinates. */ - fprintf(outfile, "%4ld %.17g %.17g\n", b->firstnumber + holenumber, - holelist[2 * holenumber], holelist[2 * holenumber + 1]); - } - } - if (regions > 0) { - fprintf(outfile, "%d\n", regions); - for (regionnumber = 0; regionnumber < regions; regionnumber++) { - /* Region number, x and y coordinates, attribute, maximum area. */ - fprintf(outfile, "%4ld %.17g %.17g %.17g %.17g\n", - b->firstnumber + regionnumber, - regionlist[4 * regionnumber], regionlist[4 * regionnumber + 1], - regionlist[4 * regionnumber + 2], - regionlist[4 * regionnumber + 3]); - } - } -#endif /* not CDT_ONLY */ - - finishfile(outfile, argc, argv); -#endif /* not TRILIBRARY */ -} - -/*****************************************************************************/ -/* */ -/* writeedges() Write the edges to an .edge file. */ -/* */ -/*****************************************************************************/ - -#ifdef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void writeedges(struct mesh *m, struct behavior *b, - int **edgelist, int **edgemarkerlist) -#else /* not ANSI_DECLARATORS */ -void writeedges(m, b, edgelist, edgemarkerlist) -struct mesh *m; -struct behavior *b; -int **edgelist; -int **edgemarkerlist; -#endif /* not ANSI_DECLARATORS */ - -#else /* not TRILIBRARY */ - -#ifdef ANSI_DECLARATORS -void writeedges(struct mesh *m, struct behavior *b, char *edgefilename, - int argc, char **argv) -#else /* not ANSI_DECLARATORS */ -void writeedges(m, b, edgefilename, argc, argv) -struct mesh *m; -struct behavior *b; -char *edgefilename; -int argc; -char **argv; -#endif /* not ANSI_DECLARATORS */ - -#endif /* not TRILIBRARY */ - -{ -#ifdef TRILIBRARY - int *elist; - int *emlist; - int index; -#else /* not TRILIBRARY */ - FILE *outfile; -#endif /* not TRILIBRARY */ - struct otri triangleloop, trisym; - struct osub checkmark; - vertex p1, p2; - long edgenumber; - triangle ptr; /* Temporary variable used by sym(). */ - subseg sptr; /* Temporary variable used by tspivot(). */ - -#ifdef TRILIBRARY - if (!b->quiet) { - printf("Writing edges.\n"); - } - /* Allocate memory for edges if necessary. */ - if (*edgelist == (int *) NULL) { - *edgelist = (int *) trimalloc((int) (m->edges * 2 * sizeof(int))); - } - /* Allocate memory for edge markers if necessary. */ - if (!b->nobound && (*edgemarkerlist == (int *) NULL)) { - *edgemarkerlist = (int *) trimalloc((int) (m->edges * sizeof(int))); - } - elist = *edgelist; - emlist = *edgemarkerlist; - index = 0; -#else /* not TRILIBRARY */ - if (!b->quiet) { - printf("Writing %s.\n", edgefilename); - } - outfile = fopen(edgefilename, "w"); - if (outfile == (FILE *) NULL) { - printf(" Error: Cannot create file %s.\n", edgefilename); - triexit(1); - } - /* Number of edges, number of boundary markers (zero or one). */ - fprintf(outfile, "%ld %d\n", m->edges, 1 - b->nobound); -#endif /* not TRILIBRARY */ - - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - edgenumber = b->firstnumber; - /* To loop over the set of edges, loop over all triangles, and look at */ - /* the three edges of each triangle. If there isn't another triangle */ - /* adjacent to the edge, operate on the edge. If there is another */ - /* adjacent triangle, operate on the edge only if the current triangle */ - /* has a smaller pointer than its neighbor. This way, each edge is */ - /* considered only once. */ - while (triangleloop.tri != (triangle *) NULL) { - for (triangleloop.orient = 0; triangleloop.orient < 3; - triangleloop.orient++) { - sym(triangleloop, trisym); - if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) { - org(triangleloop, p1); - dest(triangleloop, p2); -#ifdef TRILIBRARY - elist[index++] = vertexmark(p1); - elist[index++] = vertexmark(p2); -#endif /* TRILIBRARY */ - if (b->nobound) { -#ifndef TRILIBRARY - /* Edge number, indices of two endpoints. */ - fprintf(outfile, "%4ld %d %d\n", edgenumber, - vertexmark(p1), vertexmark(p2)); -#endif /* not TRILIBRARY */ - } else { - /* Edge number, indices of two endpoints, and a boundary marker. */ - /* If there's no subsegment, the boundary marker is zero. */ - if (b->usesegments) { - tspivot(triangleloop, checkmark); - if (checkmark.ss == m->dummysub) { -#ifdef TRILIBRARY - emlist[edgenumber - b->firstnumber] = 0; -#else /* not TRILIBRARY */ - fprintf(outfile, "%4ld %d %d %d\n", edgenumber, - vertexmark(p1), vertexmark(p2), 0); -#endif /* not TRILIBRARY */ - } else { -#ifdef TRILIBRARY - emlist[edgenumber - b->firstnumber] = mark(checkmark); -#else /* not TRILIBRARY */ - fprintf(outfile, "%4ld %d %d %d\n", edgenumber, - vertexmark(p1), vertexmark(p2), mark(checkmark)); -#endif /* not TRILIBRARY */ - } - } else { -#ifdef TRILIBRARY - emlist[edgenumber - b->firstnumber] = trisym.tri == m->dummytri; -#else /* not TRILIBRARY */ - fprintf(outfile, "%4ld %d %d %d\n", edgenumber, - vertexmark(p1), vertexmark(p2), trisym.tri == m->dummytri); -#endif /* not TRILIBRARY */ - } - } - edgenumber++; - } - } - triangleloop.tri = triangletraverse(m); - } - -#ifndef TRILIBRARY - finishfile(outfile, argc, argv); -#endif /* not TRILIBRARY */ -} - -/*****************************************************************************/ -/* */ -/* writevoronoi() Write the Voronoi diagram to a .v.node and .v.edge */ -/* file. */ -/* */ -/* The Voronoi diagram is the geometric dual of the Delaunay triangulation. */ -/* Hence, the Voronoi vertices are listed by traversing the Delaunay */ -/* triangles, and the Voronoi edges are listed by traversing the Delaunay */ -/* edges. */ -/* */ -/* WARNING: In order to assign numbers to the Voronoi vertices, this */ -/* procedure messes up the subsegments or the extra nodes of every */ -/* element. Hence, you should call this procedure last. */ -/* */ -/*****************************************************************************/ - -#ifdef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void writevoronoi(struct mesh *m, struct behavior *b, REAL **vpointlist, - REAL **vpointattriblist, int **vpointmarkerlist, - int **vedgelist, int **vedgemarkerlist, REAL **vnormlist) -#else /* not ANSI_DECLARATORS */ -void writevoronoi(m, b, vpointlist, vpointattriblist, vpointmarkerlist, - vedgelist, vedgemarkerlist, vnormlist) -struct mesh *m; -struct behavior *b; -REAL **vpointlist; -REAL **vpointattriblist; -int **vpointmarkerlist; -int **vedgelist; -int **vedgemarkerlist; -REAL **vnormlist; -#endif /* not ANSI_DECLARATORS */ - -#else /* not TRILIBRARY */ - -#ifdef ANSI_DECLARATORS -void writevoronoi(struct mesh *m, struct behavior *b, char *vnodefilename, - char *vedgefilename, int argc, char **argv) -#else /* not ANSI_DECLARATORS */ -void writevoronoi(m, b, vnodefilename, vedgefilename, argc, argv) -struct mesh *m; -struct behavior *b; -char *vnodefilename; -char *vedgefilename; -int argc; -char **argv; -#endif /* not ANSI_DECLARATORS */ - -#endif /* not TRILIBRARY */ - -{ -#ifdef TRILIBRARY - REAL *plist; - REAL *palist; - int *elist; - REAL *normlist; - int coordindex; - int attribindex; -#else /* not TRILIBRARY */ - FILE *outfile; -#endif /* not TRILIBRARY */ - struct otri triangleloop, trisym; - vertex torg, tdest, tapex; - REAL circumcenter[2]; - REAL xi, eta; - long vnodenumber, vedgenumber; - int p1, p2; - int i; - triangle ptr; /* Temporary variable used by sym(). */ - -#ifdef TRILIBRARY - if (!b->quiet) { - printf("Writing Voronoi vertices.\n"); - } - /* Allocate memory for Voronoi vertices if necessary. */ - if (*vpointlist == (REAL *) NULL) { - *vpointlist = (REAL *) trimalloc((int) (m->triangles.items * 2 * - sizeof(REAL))); - } - /* Allocate memory for Voronoi vertex attributes if necessary. */ - if (*vpointattriblist == (REAL *) NULL) { - *vpointattriblist = (REAL *) trimalloc((int) (m->triangles.items * - m->nextras * sizeof(REAL))); - } - *vpointmarkerlist = (int *) NULL; - plist = *vpointlist; - palist = *vpointattriblist; - coordindex = 0; - attribindex = 0; -#else /* not TRILIBRARY */ - if (!b->quiet) { - printf("Writing %s.\n", vnodefilename); - } - outfile = fopen(vnodefilename, "w"); - if (outfile == (FILE *) NULL) { - printf(" Error: Cannot create file %s.\n", vnodefilename); - triexit(1); - } - /* Number of triangles, two dimensions, number of vertex attributes, */ - /* no markers. */ - fprintf(outfile, "%ld %d %d %d\n", m->triangles.items, 2, m->nextras, 0); -#endif /* not TRILIBRARY */ - - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - triangleloop.orient = 0; - vnodenumber = b->firstnumber; - while (triangleloop.tri != (triangle *) NULL) { - org(triangleloop, torg); - dest(triangleloop, tdest); - apex(triangleloop, tapex); - findcircumcenter(m, b, torg, tdest, tapex, circumcenter, &xi, &eta, 0); -#ifdef TRILIBRARY - /* X and y coordinates. */ - plist[coordindex++] = circumcenter[0]; - plist[coordindex++] = circumcenter[1]; - for (i = 2; i < 2 + m->nextras; i++) { - /* Interpolate the vertex attributes at the circumcenter. */ - palist[attribindex++] = torg[i] + xi * (tdest[i] - torg[i]) - + eta * (tapex[i] - torg[i]); - } -#else /* not TRILIBRARY */ - /* Voronoi vertex number, x and y coordinates. */ - fprintf(outfile, "%4ld %.17g %.17g", vnodenumber, circumcenter[0], - circumcenter[1]); - for (i = 2; i < 2 + m->nextras; i++) { - /* Interpolate the vertex attributes at the circumcenter. */ - fprintf(outfile, " %.17g", torg[i] + xi * (tdest[i] - torg[i]) - + eta * (tapex[i] - torg[i])); - } - fprintf(outfile, "\n"); -#endif /* not TRILIBRARY */ - - * (int *) (triangleloop.tri + 6) = (int) vnodenumber; - triangleloop.tri = triangletraverse(m); - vnodenumber++; - } - -#ifndef TRILIBRARY - finishfile(outfile, argc, argv); -#endif /* not TRILIBRARY */ - -#ifdef TRILIBRARY - if (!b->quiet) { - printf("Writing Voronoi edges.\n"); - } - /* Allocate memory for output Voronoi edges if necessary. */ - if (*vedgelist == (int *) NULL) { - *vedgelist = (int *) trimalloc((int) (m->edges * 2 * sizeof(int))); - } - *vedgemarkerlist = (int *) NULL; - /* Allocate memory for output Voronoi norms if necessary. */ - if (*vnormlist == (REAL *) NULL) { - *vnormlist = (REAL *) trimalloc((int) (m->edges * 2 * sizeof(REAL))); - } - elist = *vedgelist; - normlist = *vnormlist; - coordindex = 0; -#else /* not TRILIBRARY */ - if (!b->quiet) { - printf("Writing %s.\n", vedgefilename); - } - outfile = fopen(vedgefilename, "w"); - if (outfile == (FILE *) NULL) { - printf(" Error: Cannot create file %s.\n", vedgefilename); - triexit(1); - } - /* Number of edges, zero boundary markers. */ - fprintf(outfile, "%ld %d\n", m->edges, 0); -#endif /* not TRILIBRARY */ - - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - vedgenumber = b->firstnumber; - /* To loop over the set of edges, loop over all triangles, and look at */ - /* the three edges of each triangle. If there isn't another triangle */ - /* adjacent to the edge, operate on the edge. If there is another */ - /* adjacent triangle, operate on the edge only if the current triangle */ - /* has a smaller pointer than its neighbor. This way, each edge is */ - /* considered only once. */ - while (triangleloop.tri != (triangle *) NULL) { - for (triangleloop.orient = 0; triangleloop.orient < 3; - triangleloop.orient++) { - sym(triangleloop, trisym); - if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) { - /* Find the number of this triangle (and Voronoi vertex). */ - p1 = * (int *) (triangleloop.tri + 6); - if (trisym.tri == m->dummytri) { - org(triangleloop, torg); - dest(triangleloop, tdest); -#ifdef TRILIBRARY - /* Copy an infinite ray. Index of one endpoint, and -1. */ - elist[coordindex] = p1; - normlist[coordindex++] = tdest[1] - torg[1]; - elist[coordindex] = -1; - normlist[coordindex++] = torg[0] - tdest[0]; -#else /* not TRILIBRARY */ - /* Write an infinite ray. Edge number, index of one endpoint, -1, */ - /* and x and y coordinates of a vector representing the */ - /* direction of the ray. */ - fprintf(outfile, "%4ld %d %d %.17g %.17g\n", vedgenumber, - p1, -1, tdest[1] - torg[1], torg[0] - tdest[0]); -#endif /* not TRILIBRARY */ - } else { - /* Find the number of the adjacent triangle (and Voronoi vertex). */ - p2 = * (int *) (trisym.tri + 6); - /* Finite edge. Write indices of two endpoints. */ -#ifdef TRILIBRARY - elist[coordindex] = p1; - normlist[coordindex++] = 0.0; - elist[coordindex] = p2; - normlist[coordindex++] = 0.0; -#else /* not TRILIBRARY */ - fprintf(outfile, "%4ld %d %d\n", vedgenumber, p1, p2); -#endif /* not TRILIBRARY */ - } - vedgenumber++; - } - } - triangleloop.tri = triangletraverse(m); - } - -#ifndef TRILIBRARY - finishfile(outfile, argc, argv); -#endif /* not TRILIBRARY */ -} - -#ifdef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void writeneighbors(struct mesh *m, struct behavior *b, int **neighborlist) -#else /* not ANSI_DECLARATORS */ -void writeneighbors(m, b, neighborlist) -struct mesh *m; -struct behavior *b; -int **neighborlist; -#endif /* not ANSI_DECLARATORS */ - -#else /* not TRILIBRARY */ - -#ifdef ANSI_DECLARATORS -void writeneighbors(struct mesh *m, struct behavior *b, char *neighborfilename, - int argc, char **argv) -#else /* not ANSI_DECLARATORS */ -void writeneighbors(m, b, neighborfilename, argc, argv) -struct mesh *m; -struct behavior *b; -char *neighborfilename; -int argc; -char **argv; -#endif /* not ANSI_DECLARATORS */ - -#endif /* not TRILIBRARY */ - -{ -#ifdef TRILIBRARY - int *nlist; - int index; -#else /* not TRILIBRARY */ - FILE *outfile; -#endif /* not TRILIBRARY */ - struct otri triangleloop, trisym; - long elementnumber; - int neighbor1, neighbor2, neighbor3; - triangle ptr; /* Temporary variable used by sym(). */ - -#ifdef TRILIBRARY - if (!b->quiet) { - printf("Writing neighbors.\n"); - } - /* Allocate memory for neighbors if necessary. */ - if (*neighborlist == (int *) NULL) { - *neighborlist = (int *) trimalloc((int) (m->triangles.items * 3 * - sizeof(int))); - } - nlist = *neighborlist; - index = 0; -#else /* not TRILIBRARY */ - if (!b->quiet) { - printf("Writing %s.\n", neighborfilename); - } - outfile = fopen(neighborfilename, "w"); - if (outfile == (FILE *) NULL) { - printf(" Error: Cannot create file %s.\n", neighborfilename); - triexit(1); - } - /* Number of triangles, three neighbors per triangle. */ - fprintf(outfile, "%ld %d\n", m->triangles.items, 3); -#endif /* not TRILIBRARY */ - - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - triangleloop.orient = 0; - elementnumber = b->firstnumber; - while (triangleloop.tri != (triangle *) NULL) { - * (int *) (triangleloop.tri + 6) = (int) elementnumber; - triangleloop.tri = triangletraverse(m); - elementnumber++; - } - * (int *) (m->dummytri + 6) = -1; - - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - elementnumber = b->firstnumber; - while (triangleloop.tri != (triangle *) NULL) { - triangleloop.orient = 1; - sym(triangleloop, trisym); - neighbor1 = * (int *) (trisym.tri + 6); - triangleloop.orient = 2; - sym(triangleloop, trisym); - neighbor2 = * (int *) (trisym.tri + 6); - triangleloop.orient = 0; - sym(triangleloop, trisym); - neighbor3 = * (int *) (trisym.tri + 6); -#ifdef TRILIBRARY - nlist[index++] = neighbor1; - nlist[index++] = neighbor2; - nlist[index++] = neighbor3; -#else /* not TRILIBRARY */ - /* Triangle number, neighboring triangle numbers. */ - fprintf(outfile, "%4ld %d %d %d\n", elementnumber, - neighbor1, neighbor2, neighbor3); -#endif /* not TRILIBRARY */ - - triangleloop.tri = triangletraverse(m); - elementnumber++; - } - -#ifndef TRILIBRARY - finishfile(outfile, argc, argv); -#endif /* not TRILIBRARY */ -} - -/*****************************************************************************/ -/* */ -/* writeoff() Write the triangulation to an .off file. */ -/* */ -/* OFF stands for the Object File Format, a format used by the Geometry */ -/* Center's Geomview package. */ -/* */ -/*****************************************************************************/ - -#ifndef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void writeoff(struct mesh *m, struct behavior *b, char *offfilename, - int argc, char **argv) -#else /* not ANSI_DECLARATORS */ -void writeoff(m, b, offfilename, argc, argv) -struct mesh *m; -struct behavior *b; -char *offfilename; -int argc; -char **argv; -#endif /* not ANSI_DECLARATORS */ - -{ - FILE *outfile; - struct otri triangleloop; - vertex vertexloop; - vertex p1, p2, p3; - long outvertices; - - if (!b->quiet) { - printf("Writing %s.\n", offfilename); - } - - if (b->jettison) { - outvertices = m->vertices.items - m->undeads; - } else { - outvertices = m->vertices.items; - } - - outfile = fopen(offfilename, "w"); - if (outfile == (FILE *) NULL) { - printf(" Error: Cannot create file %s.\n", offfilename); - triexit(1); - } - /* Number of vertices, triangles, and edges. */ - fprintf(outfile, "OFF\n%ld %ld %ld\n", outvertices, m->triangles.items, - m->edges); - - /* Write the vertices. */ - traversalinit(&m->vertices); - vertexloop = vertextraverse(m); - while (vertexloop != (vertex) NULL) { - if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) { - /* The "0.0" is here because the OFF format uses 3D coordinates. */ - fprintf(outfile, " %.17g %.17g %.17g\n", vertexloop[0], vertexloop[1], - 0.0); - } - vertexloop = vertextraverse(m); - } - - /* Write the triangles. */ - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - triangleloop.orient = 0; - while (triangleloop.tri != (triangle *) NULL) { - org(triangleloop, p1); - dest(triangleloop, p2); - apex(triangleloop, p3); - /* The "3" means a three-vertex polygon. */ - fprintf(outfile, " 3 %4d %4d %4d\n", vertexmark(p1) - b->firstnumber, - vertexmark(p2) - b->firstnumber, vertexmark(p3) - b->firstnumber); - triangleloop.tri = triangletraverse(m); - } - finishfile(outfile, argc, argv); -} - -#endif /* not TRILIBRARY */ - -/** **/ -/** **/ -/********* File I/O routines end here *********/ - -/*****************************************************************************/ -/* */ -/* quality_statistics() Print statistics about the quality of the mesh. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void quality_statistics(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void quality_statistics(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - struct otri triangleloop; - vertex p[3]; - REAL cossquaretable[8]; - REAL ratiotable[16]; - REAL dx[3], dy[3]; - REAL edgelength[3]; - REAL dotproduct; - REAL cossquare; - REAL triarea; - REAL shortest, longest; - REAL trilongest2; - REAL smallestarea, biggestarea; - REAL triminaltitude2; - REAL minaltitude; - REAL triaspect2; - REAL worstaspect; - REAL smallestangle, biggestangle; - REAL radconst, degconst; - int angletable[18]; - int aspecttable[16]; - int aspectindex; - int tendegree; - int acutebiggest; - int i, ii, j, k; - - printf("Mesh quality statistics:\n\n"); - radconst = PI / 18.0; - degconst = 180.0 / PI; - for (i = 0; i < 8; i++) { - cossquaretable[i] = cos(radconst * (REAL) (i + 1)); - cossquaretable[i] = cossquaretable[i] * cossquaretable[i]; - } - for (i = 0; i < 18; i++) { - angletable[i] = 0; - } - - ratiotable[0] = 1.5; ratiotable[1] = 2.0; - ratiotable[2] = 2.5; ratiotable[3] = 3.0; - ratiotable[4] = 4.0; ratiotable[5] = 6.0; - ratiotable[6] = 10.0; ratiotable[7] = 15.0; - ratiotable[8] = 25.0; ratiotable[9] = 50.0; - ratiotable[10] = 100.0; ratiotable[11] = 300.0; - ratiotable[12] = 1000.0; ratiotable[13] = 10000.0; - ratiotable[14] = 100000.0; ratiotable[15] = 0.0; - for (i = 0; i < 16; i++) { - aspecttable[i] = 0; - } - - worstaspect = 0.0; - minaltitude = m->xmax - m->xmin + m->ymax - m->ymin; - minaltitude = minaltitude * minaltitude; - shortest = minaltitude; - longest = 0.0; - smallestarea = minaltitude; - biggestarea = 0.0; - worstaspect = 0.0; - smallestangle = 0.0; - biggestangle = 2.0; - acutebiggest = 1; - - traversalinit(&m->triangles); - triangleloop.tri = triangletraverse(m); - triangleloop.orient = 0; - while (triangleloop.tri != (triangle *) NULL) { - org(triangleloop, p[0]); - dest(triangleloop, p[1]); - apex(triangleloop, p[2]); - trilongest2 = 0.0; - - for (i = 0; i < 3; i++) { - j = plus1mod3[i]; - k = minus1mod3[i]; - dx[i] = p[j][0] - p[k][0]; - dy[i] = p[j][1] - p[k][1]; - edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i]; - if (edgelength[i] > trilongest2) { - trilongest2 = edgelength[i]; - } - if (edgelength[i] > longest) { - longest = edgelength[i]; - } - if (edgelength[i] < shortest) { - shortest = edgelength[i]; - } - } - - triarea = counterclockwise(m, b, p[0], p[1], p[2]); - if (triarea < smallestarea) { - smallestarea = triarea; - } - if (triarea > biggestarea) { - biggestarea = triarea; - } - triminaltitude2 = triarea * triarea / trilongest2; - if (triminaltitude2 < minaltitude) { - minaltitude = triminaltitude2; - } - triaspect2 = trilongest2 / triminaltitude2; - if (triaspect2 > worstaspect) { - worstaspect = triaspect2; - } - aspectindex = 0; - while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex]) - && (aspectindex < 15)) { - aspectindex++; - } - aspecttable[aspectindex]++; - - for (i = 0; i < 3; i++) { - j = plus1mod3[i]; - k = minus1mod3[i]; - dotproduct = dx[j] * dx[k] + dy[j] * dy[k]; - cossquare = dotproduct * dotproduct / (edgelength[j] * edgelength[k]); - tendegree = 8; - for (ii = 7; ii >= 0; ii--) { - if (cossquare > cossquaretable[ii]) { - tendegree = ii; - } - } - if (dotproduct <= 0.0) { - angletable[tendegree]++; - if (cossquare > smallestangle) { - smallestangle = cossquare; - } - if (acutebiggest && (cossquare < biggestangle)) { - biggestangle = cossquare; - } - } else { - angletable[17 - tendegree]++; - if (acutebiggest || (cossquare > biggestangle)) { - biggestangle = cossquare; - acutebiggest = 0; - } - } - } - triangleloop.tri = triangletraverse(m); - } - - shortest = sqrt(shortest); - longest = sqrt(longest); - minaltitude = sqrt(minaltitude); - worstaspect = sqrt(worstaspect); - smallestarea *= 0.5; - biggestarea *= 0.5; - if (smallestangle >= 1.0) { - smallestangle = 0.0; - } else { - smallestangle = degconst * acos(sqrt(smallestangle)); - } - if (biggestangle >= 1.0) { - biggestangle = 180.0; - } else { - if (acutebiggest) { - biggestangle = degconst * acos(sqrt(biggestangle)); - } else { - biggestangle = 180.0 - degconst * acos(sqrt(biggestangle)); - } - } - - printf(" Smallest area: %16.5g | Largest area: %16.5g\n", - smallestarea, biggestarea); - printf(" Shortest edge: %16.5g | Longest edge: %16.5g\n", - shortest, longest); - printf(" Shortest altitude: %12.5g | Largest aspect ratio: %8.5g\n\n", - minaltitude, worstaspect); - - printf(" Triangle aspect ratio histogram:\n"); - printf(" 1.1547 - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", - ratiotable[0], aspecttable[0], ratiotable[7], ratiotable[8], - aspecttable[8]); - for (i = 1; i < 7; i++) { - printf(" %6.6g - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", - ratiotable[i - 1], ratiotable[i], aspecttable[i], - ratiotable[i + 7], ratiotable[i + 8], aspecttable[i + 8]); - } - printf(" %6.6g - %-6.6g : %8d | %6.6g - : %8d\n", - ratiotable[6], ratiotable[7], aspecttable[7], ratiotable[14], - aspecttable[15]); - printf(" (Aspect ratio is longest edge divided by shortest altitude)\n\n"); - - printf(" Smallest angle: %15.5g | Largest angle: %15.5g\n\n", - smallestangle, biggestangle); - - printf(" Angle histogram:\n"); - for (i = 0; i < 9; i++) { - printf(" %3d - %3d degrees: %8d | %3d - %3d degrees: %8d\n", - i * 10, i * 10 + 10, angletable[i], - i * 10 + 90, i * 10 + 100, angletable[i + 9]); - } - printf("\n"); -} - -/*****************************************************************************/ -/* */ -/* statistics() Print all sorts of cool facts. */ -/* */ -/*****************************************************************************/ - -#ifdef ANSI_DECLARATORS -void statistics(struct mesh *m, struct behavior *b) -#else /* not ANSI_DECLARATORS */ -void statistics(m, b) -struct mesh *m; -struct behavior *b; -#endif /* not ANSI_DECLARATORS */ - -{ - printf("\nStatistics:\n\n"); - printf(" Input vertices: %d\n", m->invertices); - if (b->refine) { - printf(" Input triangles: %d\n", m->inelements); - } - if (b->poly) { - printf(" Input segments: %d\n", m->insegments); - if (!b->refine) { - printf(" Input holes: %d\n", m->holes); - } - } - - printf("\n Mesh vertices: %ld\n", m->vertices.items - m->undeads); - printf(" Mesh triangles: %ld\n", m->triangles.items); - printf(" Mesh edges: %ld\n", m->edges); - printf(" Mesh exterior boundary edges: %ld\n", m->hullsize); - if (b->poly || b->refine) { - printf(" Mesh interior boundary edges: %ld\n", - m->subsegs.items - m->hullsize); - printf(" Mesh subsegments (constrained edges): %ld\n", - m->subsegs.items); - } - printf("\n"); - - if (b->verbose) { - quality_statistics(m, b); - printf("Memory allocation statistics:\n\n"); - printf(" Maximum number of vertices: %ld\n", m->vertices.maxitems); - printf(" Maximum number of triangles: %ld\n", m->triangles.maxitems); - if (m->subsegs.maxitems > 0) { - printf(" Maximum number of subsegments: %ld\n", m->subsegs.maxitems); - } - if (m->viri.maxitems > 0) { - printf(" Maximum number of viri: %ld\n", m->viri.maxitems); - } - if (m->badsubsegs.maxitems > 0) { - printf(" Maximum number of encroached subsegments: %ld\n", - m->badsubsegs.maxitems); - } - if (m->badtriangles.maxitems > 0) { - printf(" Maximum number of bad triangles: %ld\n", - m->badtriangles.maxitems); - } - if (m->flipstackers.maxitems > 0) { - printf(" Maximum number of stacked triangle flips: %ld\n", - m->flipstackers.maxitems); - } - if (m->splaynodes.maxitems > 0) { - printf(" Maximum number of splay tree nodes: %ld\n", - m->splaynodes.maxitems); - } - printf(" Approximate heap memory use (bytes): %ld\n\n", - m->vertices.maxitems * m->vertices.itembytes + - m->triangles.maxitems * m->triangles.itembytes + - m->subsegs.maxitems * m->subsegs.itembytes + - m->viri.maxitems * m->viri.itembytes + - m->badsubsegs.maxitems * m->badsubsegs.itembytes + - m->badtriangles.maxitems * m->badtriangles.itembytes + - m->flipstackers.maxitems * m->flipstackers.itembytes + - m->splaynodes.maxitems * m->splaynodes.itembytes); - - printf("Algorithmic statistics:\n\n"); - if (!b->weighted) { - printf(" Number of incircle tests: %ld\n", m->incirclecount); - } else { - printf(" Number of 3D orientation tests: %ld\n", m->orient3dcount); - } - printf(" Number of 2D orientation tests: %ld\n", m->counterclockcount); - if (m->hyperbolacount > 0) { - printf(" Number of right-of-hyperbola tests: %ld\n", - m->hyperbolacount); - } - if (m->circletopcount > 0) { - printf(" Number of circle top computations: %ld\n", - m->circletopcount); - } - if (m->circumcentercount > 0) { - printf(" Number of triangle circumcenter computations: %ld\n", - m->circumcentercount); - } - printf("\n"); - } -} - -/*****************************************************************************/ -/* */ -/* main() or triangulate() Gosh, do everything. */ -/* */ -/* The sequence is roughly as follows. Many of these steps can be skipped, */ -/* depending on the command line switches. */ -/* */ -/* - Initialize constants and parse the command line. */ -/* - Read the vertices from a file and either */ -/* - triangulate them (no -r), or */ -/* - read an old mesh from files and reconstruct it (-r). */ -/* - Insert the PSLG segments (-p), and possibly segments on the convex */ -/* hull (-c). */ -/* - Read the holes (-p), regional attributes (-pA), and regional area */ -/* constraints (-pa). Carve the holes and concavities, and spread the */ -/* regional attributes and area constraints. */ -/* - Enforce the constraints on minimum angle (-q) and maximum area (-a). */ -/* Also enforce the conforming Delaunay property (-q and -a). */ -/* - Compute the number of edges in the resulting mesh. */ -/* - Promote the mesh's linear triangles to higher order elements (-o). */ -/* - Write the output files and print the statistics. */ -/* - Check the consistency and Delaunay property of the mesh (-C). */ -/* */ -/*****************************************************************************/ - -#ifdef TRILIBRARY - -#ifdef ANSI_DECLARATORS -void triangulate(char *triswitches, struct triangulateio *in, - struct triangulateio *out, struct triangulateio *vorout) -#else /* not ANSI_DECLARATORS */ -void triangulate(triswitches, in, out, vorout) -char *triswitches; -struct triangulateio *in; -struct triangulateio *out; -struct triangulateio *vorout; -#endif /* not ANSI_DECLARATORS */ - -#else /* not TRILIBRARY */ - -#ifdef ANSI_DECLARATORS -int main(int argc, char **argv) -#else /* not ANSI_DECLARATORS */ -int main(argc, argv) -int argc; -char **argv; -#endif /* not ANSI_DECLARATORS */ - -#endif /* not TRILIBRARY */ - -{ - struct mesh m; - struct behavior b; - REAL *holearray; /* Array of holes. */ - REAL *regionarray; /* Array of regional attributes and area constraints. */ -#ifndef TRILIBRARY - FILE *polyfile; -#endif /* not TRILIBRARY */ -#ifndef NO_TIMER - /* Variables for timing the performance of Triangle. The types are */ - /* defined in sys/time.h. */ - struct timeval tv0, tv1, tv2, tv3, tv4, tv5, tv6; - struct timezone tz; -#endif /* not NO_TIMER */ - -#ifndef NO_TIMER - gettimeofday(&tv0, &tz); -#endif /* not NO_TIMER */ - - triangleinit(&m); -#ifdef TRILIBRARY - parsecommandline(1, &triswitches, &b); -#else /* not TRILIBRARY */ - parsecommandline(argc, argv, &b); -#endif /* not TRILIBRARY */ - m.steinerleft = b.steiner; - -#ifdef TRILIBRARY - transfernodes(&m, &b, in->pointlist, in->pointattributelist, - in->pointmarkerlist, in->numberofpoints, - in->numberofpointattributes); -#else /* not TRILIBRARY */ - readnodes(&m, &b, b.innodefilename, b.inpolyfilename, &polyfile); -#endif /* not TRILIBRARY */ - -#ifndef NO_TIMER - if (!b.quiet) { - gettimeofday(&tv1, &tz); - } -#endif /* not NO_TIMER */ - -#ifdef CDT_ONLY - m.hullsize = delaunay(&m, &b); /* Triangulate the vertices. */ -#else /* not CDT_ONLY */ - if (b.refine) { - /* Read and reconstruct a mesh. */ -#ifdef TRILIBRARY - m.hullsize = reconstruct(&m, &b, in->trianglelist, - in->triangleattributelist, in->trianglearealist, - in->numberoftriangles, in->numberofcorners, - in->numberoftriangleattributes, - in->segmentlist, in->segmentmarkerlist, - in->numberofsegments); -#else /* not TRILIBRARY */ - m.hullsize = reconstruct(&m, &b, b.inelefilename, b.areafilename, - b.inpolyfilename, polyfile); -#endif /* not TRILIBRARY */ - } else { - m.hullsize = delaunay(&m, &b); /* Triangulate the vertices. */ - } -#endif /* not CDT_ONLY */ - -#ifndef NO_TIMER - if (!b.quiet) { - gettimeofday(&tv2, &tz); - if (b.refine) { - printf("Mesh reconstruction"); - } else { - printf("Delaunay"); - } - printf(" milliseconds: %ld\n", 1000l * (tv2.tv_sec - tv1.tv_sec) + - (tv2.tv_usec - tv1.tv_usec) / 1000l); - } -#endif /* not NO_TIMER */ - - /* Ensure that no vertex can be mistaken for a triangular bounding */ - /* box vertex in insertvertex(). */ - m.infvertex1 = (vertex) NULL; - m.infvertex2 = (vertex) NULL; - m.infvertex3 = (vertex) NULL; - - if (b.usesegments) { - m.checksegments = 1; /* Segments will be introduced next. */ - if (!b.refine) { - /* Insert PSLG segments and/or convex hull segments. */ -#ifdef TRILIBRARY - formskeleton(&m, &b, in->segmentlist, - in->segmentmarkerlist, in->numberofsegments); -#else /* not TRILIBRARY */ - formskeleton(&m, &b, polyfile, b.inpolyfilename); -#endif /* not TRILIBRARY */ - } - } - -#ifndef NO_TIMER - if (!b.quiet) { - gettimeofday(&tv3, &tz); - if (b.usesegments && !b.refine) { - printf("Segment milliseconds: %ld\n", - 1000l * (tv3.tv_sec - tv2.tv_sec) + - (tv3.tv_usec - tv2.tv_usec) / 1000l); - } - } -#endif /* not NO_TIMER */ - - if (b.poly && (m.triangles.items > 0)) { -#ifdef TRILIBRARY - holearray = in->holelist; - m.holes = in->numberofholes; - regionarray = in->regionlist; - m.regions = in->numberofregions; -#else /* not TRILIBRARY */ - readholes(&m, &b, polyfile, b.inpolyfilename, &holearray, &m.holes, - ®ionarray, &m.regions); -#endif /* not TRILIBRARY */ - if (!b.refine) { - /* Carve out holes and concavities. */ - carveholes(&m, &b, holearray, m.holes, regionarray, m.regions); - } - } else { - /* Without a PSLG, there can be no holes or regional attributes */ - /* or area constraints. The following are set to zero to avoid */ - /* an accidental free() later. */ - m.holes = 0; - m.regions = 0; - } - -#ifndef NO_TIMER - if (!b.quiet) { - gettimeofday(&tv4, &tz); - if (b.poly && !b.refine) { - printf("Hole milliseconds: %ld\n", 1000l * (tv4.tv_sec - tv3.tv_sec) + - (tv4.tv_usec - tv3.tv_usec) / 1000l); - } - } -#endif /* not NO_TIMER */ - -#ifndef CDT_ONLY - if (b.quality && (m.triangles.items > 0)) { - enforcequality(&m, &b); /* Enforce angle and area constraints. */ - } -#endif /* not CDT_ONLY */ - -#ifndef NO_TIMER - if (!b.quiet) { - gettimeofday(&tv5, &tz); -#ifndef CDT_ONLY - if (b.quality) { - printf("Quality milliseconds: %ld\n", - 1000l * (tv5.tv_sec - tv4.tv_sec) + - (tv5.tv_usec - tv4.tv_usec) / 1000l); - } -#endif /* not CDT_ONLY */ - } -#endif /* not NO_TIMER */ - - /* Calculate the number of edges. */ - m.edges = (3l * m.triangles.items + m.hullsize) / 2l; - - if (b.order > 1) { - highorder(&m, &b); /* Promote elements to higher polynomial order. */ - } - if (!b.quiet) { - printf("\n"); - } - -#ifdef TRILIBRARY - if (b.jettison) { - out->numberofpoints = m.vertices.items - m.undeads; - } else { - out->numberofpoints = m.vertices.items; - } - out->numberofpointattributes = m.nextras; - out->numberoftriangles = m.triangles.items; - out->numberofcorners = (b.order + 1) * (b.order + 2) / 2; - out->numberoftriangleattributes = m.eextras; - out->numberofedges = m.edges; - if (b.usesegments) { - out->numberofsegments = m.subsegs.items; - } else { - out->numberofsegments = m.hullsize; - } - if (vorout != (struct triangulateio *) NULL) { - vorout->numberofpoints = m.triangles.items; - vorout->numberofpointattributes = m.nextras; - vorout->numberofedges = m.edges; - } -#endif /* TRILIBRARY */ - /* If not using iteration numbers, don't write a .node file if one was */ - /* read, because the original one would be overwritten! */ - if (b.nonodewritten || (b.noiterationnum && m.readnodefile)) { - if (!b.quiet) { -#ifdef TRILIBRARY - printf("NOT writing vertices.\n"); -#else /* not TRILIBRARY */ - printf("NOT writing a .node file.\n"); -#endif /* not TRILIBRARY */ - } - numbernodes(&m, &b); /* We must remember to number the vertices. */ - } else { - /* writenodes() numbers the vertices too. */ -#ifdef TRILIBRARY - writenodes(&m, &b, &out->pointlist, &out->pointattributelist, - &out->pointmarkerlist); -#else /* not TRILIBRARY */ - writenodes(&m, &b, b.outnodefilename, argc, argv); -#endif /* TRILIBRARY */ - } - if (b.noelewritten) { - if (!b.quiet) { -#ifdef TRILIBRARY - printf("NOT writing triangles.\n"); -#else /* not TRILIBRARY */ - printf("NOT writing an .ele file.\n"); -#endif /* not TRILIBRARY */ - } - } else { -#ifdef TRILIBRARY - writeelements(&m, &b, &out->trianglelist, &out->triangleattributelist); -#else /* not TRILIBRARY */ - writeelements(&m, &b, b.outelefilename, argc, argv); -#endif /* not TRILIBRARY */ - } - /* The -c switch (convex switch) causes a PSLG to be written */ - /* even if none was read. */ - if (b.poly || b.convex) { - /* If not using iteration numbers, don't overwrite the .poly file. */ - if (b.nopolywritten || b.noiterationnum) { - if (!b.quiet) { -#ifdef TRILIBRARY - printf("NOT writing segments.\n"); -#else /* not TRILIBRARY */ - printf("NOT writing a .poly file.\n"); -#endif /* not TRILIBRARY */ - } - } else { -#ifdef TRILIBRARY - writepoly(&m, &b, &out->segmentlist, &out->segmentmarkerlist); - out->numberofholes = m.holes; - out->numberofregions = m.regions; - if (b.poly) { - out->holelist = in->holelist; - out->regionlist = in->regionlist; - } else { - out->holelist = (REAL *) NULL; - out->regionlist = (REAL *) NULL; - } -#else /* not TRILIBRARY */ - writepoly(&m, &b, b.outpolyfilename, holearray, m.holes, regionarray, - m.regions, argc, argv); -#endif /* not TRILIBRARY */ - } - } -#ifndef TRILIBRARY -#ifndef CDT_ONLY - if (m.regions > 0) { - trifree((VOID *) regionarray); - } -#endif /* not CDT_ONLY */ - if (m.holes > 0) { - trifree((VOID *) holearray); - } - if (b.geomview) { - writeoff(&m, &b, b.offfilename, argc, argv); - } -#endif /* not TRILIBRARY */ - if (b.edgesout) { -#ifdef TRILIBRARY - writeedges(&m, &b, &out->edgelist, &out->edgemarkerlist); -#else /* not TRILIBRARY */ - writeedges(&m, &b, b.edgefilename, argc, argv); -#endif /* not TRILIBRARY */ - } - if (b.voronoi) { -#ifdef TRILIBRARY - writevoronoi(&m, &b, &vorout->pointlist, &vorout->pointattributelist, - &vorout->pointmarkerlist, &vorout->edgelist, - &vorout->edgemarkerlist, &vorout->normlist); -#else /* not TRILIBRARY */ - writevoronoi(&m, &b, b.vnodefilename, b.vedgefilename, argc, argv); -#endif /* not TRILIBRARY */ - } - if (b.neighbors) { -#ifdef TRILIBRARY - writeneighbors(&m, &b, &out->neighborlist); -#else /* not TRILIBRARY */ - writeneighbors(&m, &b, b.neighborfilename, argc, argv); -#endif /* not TRILIBRARY */ - } - - if (!b.quiet) { -#ifndef NO_TIMER - gettimeofday(&tv6, &tz); - printf("\nOutput milliseconds: %ld\n", - 1000l * (tv6.tv_sec - tv5.tv_sec) + - (tv6.tv_usec - tv5.tv_usec) / 1000l); - printf("Total running milliseconds: %ld\n", - 1000l * (tv6.tv_sec - tv0.tv_sec) + - (tv6.tv_usec - tv0.tv_usec) / 1000l); -#endif /* not NO_TIMER */ - - statistics(&m, &b); - } - -#ifndef REDUCED - if (b.docheck) { - checkmesh(&m, &b); - checkdelaunay(&m, &b); - } -#endif /* not REDUCED */ - - triangledeinit(&m, &b); -#ifndef TRILIBRARY - return 0; -#endif /* not TRILIBRARY */ -} diff --git a/src/bounce/garment/triangle/triangle.h b/src/bounce/garment/triangle/triangle.h deleted file mode 100644 index 9df1f39..0000000 --- a/src/bounce/garment/triangle/triangle.h +++ /dev/null @@ -1,289 +0,0 @@ -/*****************************************************************************/ -/* */ -/* (triangle.h) */ -/* */ -/* Include file for programs that call Triangle. */ -/* */ -/* Accompanies Triangle Version 1.6 */ -/* July 28, 2005 */ -/* */ -/* Copyright 1996, 2005 */ -/* Jonathan Richard Shewchuk */ -/* 2360 Woolsey #H */ -/* Berkeley, California 94705-1927 */ -/* jrs@cs.berkeley.edu */ -/* */ -/*****************************************************************************/ - -/*****************************************************************************/ -/* */ -/* How to call Triangle from another program */ -/* */ -/* */ -/* If you haven't read Triangle's instructions (run "triangle -h" to read */ -/* them), you won't understand what follows. */ -/* */ -/* Triangle must be compiled into an object file (triangle.o) with the */ -/* TRILIBRARY symbol defined (generally by using the -DTRILIBRARY compiler */ -/* switch). The makefile included with Triangle will do this for you if */ -/* you run "make trilibrary". The resulting object file can be called via */ -/* the procedure triangulate(). */ -/* */ -/* If the size of the object file is important to you, you may wish to */ -/* generate a reduced version of triangle.o. The REDUCED symbol gets rid */ -/* of all features that are primarily of research interest. Specifically, */ -/* the -DREDUCED switch eliminates Triangle's -i, -F, -s, and -C switches. */ -/* The CDT_ONLY symbol gets rid of all meshing algorithms above and beyond */ -/* constrained Delaunay triangulation. Specifically, the -DCDT_ONLY switch */ -/* eliminates Triangle's -r, -q, -a, -u, -D, -Y, -S, and -s switches. */ -/* */ -/* IMPORTANT: These definitions (TRILIBRARY, REDUCED, CDT_ONLY) must be */ -/* made in the makefile or in triangle.c itself. Putting these definitions */ -/* in this file (triangle.h) will not create the desired effect. */ -/* */ -/* */ -/* The calling convention for triangulate() follows. */ -/* */ -/* void triangulate(triswitches, in, out, vorout) */ -/* char *triswitches; */ -/* struct triangulateio *in; */ -/* struct triangulateio *out; */ -/* struct triangulateio *vorout; */ -/* */ -/* `triswitches' is a string containing the command line switches you wish */ -/* to invoke. No initial dash is required. Some suggestions: */ -/* */ -/* - You'll probably find it convenient to use the `z' switch so that */ -/* points (and other items) are numbered from zero. This simplifies */ -/* indexing, because the first item of any type always starts at index */ -/* [0] of the corresponding array, whether that item's number is zero or */ -/* one. */ -/* - You'll probably want to use the `Q' (quiet) switch in your final code, */ -/* but you can take advantage of Triangle's printed output (including the */ -/* `V' switch) while debugging. */ -/* - If you are not using the `q', `a', `u', `D', `j', or `s' switches, */ -/* then the output points will be identical to the input points, except */ -/* possibly for the boundary markers. If you don't need the boundary */ -/* markers, you should use the `N' (no nodes output) switch to save */ -/* memory. (If you do need boundary markers, but need to save memory, a */ -/* good nasty trick is to set out->pointlist equal to in->pointlist */ -/* before calling triangulate(), so that Triangle overwrites the input */ -/* points with identical copies.) */ -/* - The `I' (no iteration numbers) and `g' (.off file output) switches */ -/* have no effect when Triangle is compiled with TRILIBRARY defined. */ -/* */ -/* `in', `out', and `vorout' are descriptions of the input, the output, */ -/* and the Voronoi output. If the `v' (Voronoi output) switch is not used, */ -/* `vorout' may be NULL. `in' and `out' may never be NULL. */ -/* */ -/* Certain fields of the input and output structures must be initialized, */ -/* as described below. */ -/* */ -/*****************************************************************************/ - -/*****************************************************************************/ -/* */ -/* The `triangulateio' structure. */ -/* */ -/* Used to pass data into and out of the triangulate() procedure. */ -/* */ -/* */ -/* Arrays are used to store points, triangles, markers, and so forth. In */ -/* all cases, the first item in any array is stored starting at index [0]. */ -/* However, that item is item number `1' unless the `z' switch is used, in */ -/* which case it is item number `0'. Hence, you may find it easier to */ -/* index points (and triangles in the neighbor list) if you use the `z' */ -/* switch. Unless, of course, you're calling Triangle from a Fortran */ -/* program. */ -/* */ -/* Description of fields (except the `numberof' fields, which are obvious): */ -/* */ -/* `pointlist': An array of point coordinates. The first point's x */ -/* coordinate is at index [0] and its y coordinate at index [1], followed */ -/* by the coordinates of the remaining points. Each point occupies two */ -/* REALs. */ -/* `pointattributelist': An array of point attributes. Each point's */ -/* attributes occupy `numberofpointattributes' REALs. */ -/* `pointmarkerlist': An array of point markers; one int per point. */ -/* */ -/* `trianglelist': An array of triangle corners. The first triangle's */ -/* first corner is at index [0], followed by its other two corners in */ -/* counterclockwise order, followed by any other nodes if the triangle */ -/* represents a nonlinear element. Each triangle occupies */ -/* `numberofcorners' ints. */ -/* `triangleattributelist': An array of triangle attributes. Each */ -/* triangle's attributes occupy `numberoftriangleattributes' REALs. */ -/* `trianglearealist': An array of triangle area constraints; one REAL per */ -/* triangle. Input only. */ -/* `neighborlist': An array of triangle neighbors; three ints per */ -/* triangle. Output only. */ -/* */ -/* `segmentlist': An array of segment endpoints. The first segment's */ -/* endpoints are at indices [0] and [1], followed by the remaining */ -/* segments. Two ints per segment. */ -/* `segmentmarkerlist': An array of segment markers; one int per segment. */ -/* */ -/* `holelist': An array of holes. The first hole's x and y coordinates */ -/* are at indices [0] and [1], followed by the remaining holes. Two */ -/* REALs per hole. Input only, although the pointer is copied to the */ -/* output structure for your convenience. */ -/* */ -/* `regionlist': An array of regional attributes and area constraints. */ -/* The first constraint's x and y coordinates are at indices [0] and [1], */ -/* followed by the regional attribute at index [2], followed by the */ -/* maximum area at index [3], followed by the remaining area constraints. */ -/* Four REALs per area constraint. Note that each regional attribute is */ -/* used only if you select the `A' switch, and each area constraint is */ -/* used only if you select the `a' switch (with no number following), but */ -/* omitting one of these switches does not change the memory layout. */ -/* Input only, although the pointer is copied to the output structure for */ -/* your convenience. */ -/* */ -/* `edgelist': An array of edge endpoints. The first edge's endpoints are */ -/* at indices [0] and [1], followed by the remaining edges. Two ints per */ -/* edge. Output only. */ -/* `edgemarkerlist': An array of edge markers; one int per edge. Output */ -/* only. */ -/* `normlist': An array of normal vectors, used for infinite rays in */ -/* Voronoi diagrams. The first normal vector's x and y magnitudes are */ -/* at indices [0] and [1], followed by the remaining vectors. For each */ -/* finite edge in a Voronoi diagram, the normal vector written is the */ -/* zero vector. Two REALs per edge. Output only. */ -/* */ -/* */ -/* Any input fields that Triangle will examine must be initialized. */ -/* Furthermore, for each output array that Triangle will write to, you */ -/* must either provide space by setting the appropriate pointer to point */ -/* to the space you want the data written to, or you must initialize the */ -/* pointer to NULL, which tells Triangle to allocate space for the results. */ -/* The latter option is preferable, because Triangle always knows exactly */ -/* how much space to allocate. The former option is provided mainly for */ -/* people who need to call Triangle from Fortran code, though it also makes */ -/* possible some nasty space-saving tricks, like writing the output to the */ -/* same arrays as the input. */ -/* */ -/* Triangle will not free() any input or output arrays, including those it */ -/* allocates itself; that's up to you. You should free arrays allocated by */ -/* Triangle by calling the trifree() procedure defined below. (By default, */ -/* trifree() just calls the standard free() library procedure, but */ -/* applications that call triangulate() may replace trimalloc() and */ -/* trifree() in triangle.c to use specialized memory allocators.) */ -/* */ -/* Here's a guide to help you decide which fields you must initialize */ -/* before you call triangulate(). */ -/* */ -/* `in': */ -/* */ -/* - `pointlist' must always point to a list of points; `numberofpoints' */ -/* and `numberofpointattributes' must be properly set. */ -/* `pointmarkerlist' must either be set to NULL (in which case all */ -/* markers default to zero), or must point to a list of markers. If */ -/* `numberofpointattributes' is not zero, `pointattributelist' must */ -/* point to a list of point attributes. */ -/* - If the `r' switch is used, `trianglelist' must point to a list of */ -/* triangles, and `numberoftriangles', `numberofcorners', and */ -/* `numberoftriangleattributes' must be properly set. If */ -/* `numberoftriangleattributes' is not zero, `triangleattributelist' */ -/* must point to a list of triangle attributes. If the `a' switch is */ -/* used (with no number following), `trianglearealist' must point to a */ -/* list of triangle area constraints. `neighborlist' may be ignored. */ -/* - If the `p' switch is used, `segmentlist' must point to a list of */ -/* segments, `numberofsegments' must be properly set, and */ -/* `segmentmarkerlist' must either be set to NULL (in which case all */ -/* markers default to zero), or must point to a list of markers. */ -/* - If the `p' switch is used without the `r' switch, then */ -/* `numberofholes' and `numberofregions' must be properly set. If */ -/* `numberofholes' is not zero, `holelist' must point to a list of */ -/* holes. If `numberofregions' is not zero, `regionlist' must point to */ -/* a list of region constraints. */ -/* - If the `p' switch is used, `holelist', `numberofholes', */ -/* `regionlist', and `numberofregions' is copied to `out'. (You can */ -/* nonetheless get away with not initializing them if the `r' switch is */ -/* used.) */ -/* - `edgelist', `edgemarkerlist', `normlist', and `numberofedges' may be */ -/* ignored. */ -/* */ -/* `out': */ -/* */ -/* - `pointlist' must be initialized (NULL or pointing to memory) unless */ -/* the `N' switch is used. `pointmarkerlist' must be initialized */ -/* unless the `N' or `B' switch is used. If `N' is not used and */ -/* `in->numberofpointattributes' is not zero, `pointattributelist' must */ -/* be initialized. */ -/* - `trianglelist' must be initialized unless the `E' switch is used. */ -/* `neighborlist' must be initialized if the `n' switch is used. If */ -/* the `E' switch is not used and (`in->numberofelementattributes' is */ -/* not zero or the `A' switch is used), `elementattributelist' must be */ -/* initialized. `trianglearealist' may be ignored. */ -/* - `segmentlist' must be initialized if the `p' or `c' switch is used, */ -/* and the `P' switch is not used. `segmentmarkerlist' must also be */ -/* initialized under these circumstances unless the `B' switch is used. */ -/* - `edgelist' must be initialized if the `e' switch is used. */ -/* `edgemarkerlist' must be initialized if the `e' switch is used and */ -/* the `B' switch is not. */ -/* - `holelist', `regionlist', `normlist', and all scalars may be ignored.*/ -/* */ -/* `vorout' (only needed if `v' switch is used): */ -/* */ -/* - `pointlist' must be initialized. If `in->numberofpointattributes' */ -/* is not zero, `pointattributelist' must be initialized. */ -/* `pointmarkerlist' may be ignored. */ -/* - `edgelist' and `normlist' must both be initialized. */ -/* `edgemarkerlist' may be ignored. */ -/* - Everything else may be ignored. */ -/* */ -/* After a call to triangulate(), the valid fields of `out' and `vorout' */ -/* will depend, in an obvious way, on the choice of switches used. Note */ -/* that when the `p' switch is used, the pointers `holelist' and */ -/* `regionlist' are copied from `in' to `out', but no new space is */ -/* allocated; be careful that you don't free() the same array twice. On */ -/* the other hand, Triangle will never copy the `pointlist' pointer (or any */ -/* others); new space is allocated for `out->pointlist', or if the `N' */ -/* switch is used, `out->pointlist' remains uninitialized. */ -/* */ -/* All of the meaningful `numberof' fields will be properly set; for */ -/* instance, `numberofedges' will represent the number of edges in the */ -/* triangulation whether or not the edges were written. If segments are */ -/* not used, `numberofsegments' will indicate the number of boundary edges. */ -/* */ -/*****************************************************************************/ - -struct triangulateio { - REAL *pointlist; /* In / out */ - REAL *pointattributelist; /* In / out */ - int *pointmarkerlist; /* In / out */ - int numberofpoints; /* In / out */ - int numberofpointattributes; /* In / out */ - - int *trianglelist; /* In / out */ - REAL *triangleattributelist; /* In / out */ - REAL *trianglearealist; /* In only */ - int *neighborlist; /* Out only */ - int numberoftriangles; /* In / out */ - int numberofcorners; /* In / out */ - int numberoftriangleattributes; /* In / out */ - - int *segmentlist; /* In / out */ - int *segmentmarkerlist; /* In / out */ - int numberofsegments; /* In / out */ - - REAL *holelist; /* In / pointer to array copied out */ - int numberofholes; /* In / copied out */ - - REAL *regionlist; /* In / pointer to array copied out */ - int numberofregions; /* In / copied out */ - - int *edgelist; /* Out only */ - int *edgemarkerlist; /* Not used with Voronoi diagram; out only */ - REAL *normlist; /* Used only with Voronoi diagram; out only */ - int numberofedges; /* Out only */ -}; - -#ifdef ANSI_DECLARATORS -void triangulate(char *, struct triangulateio *, struct triangulateio *, - struct triangulateio *); -void trifree(VOID *memptr); -#else /* not ANSI_DECLARATORS */ -void triangulate(); -void trifree(); -#endif /* not ANSI_DECLARATORS */ diff --git a/src/bounce/garment/triangle/tricall.c b/src/bounce/garment/triangle/tricall.c deleted file mode 100644 index 23f254c..0000000 --- a/src/bounce/garment/triangle/tricall.c +++ /dev/null @@ -1,273 +0,0 @@ -/*****************************************************************************/ -/* */ -/* (tricall.c) */ -/* */ -/* Example program that demonstrates how to call Triangle. */ -/* */ -/* Accompanies Triangle Version 1.6 */ -/* July 19, 1996 */ -/* */ -/* This file is placed in the public domain (but the file that it calls */ -/* is still copyrighted!) by */ -/* Jonathan Richard Shewchuk */ -/* 2360 Woolsey #H */ -/* Berkeley, California 94705-1927 */ -/* jrs@cs.berkeley.edu */ -/* */ -/*****************************************************************************/ - -/* If SINGLE is defined when triangle.o is compiled, it should also be */ -/* defined here. If not, it should not be defined here. */ - -/* #define SINGLE */ - -#ifdef SINGLE -#define REAL float -#else /* not SINGLE */ -#define REAL double -#endif /* not SINGLE */ - -#include -#include -#include "triangle.h" - -/*****************************************************************************/ -/* */ -/* report() Print the input or output. */ -/* */ -/*****************************************************************************/ - -void report(io, markers, reporttriangles, reportneighbors, reportsegments, - reportedges, reportnorms) -struct triangulateio *io; -int markers; -int reporttriangles; -int reportneighbors; -int reportsegments; -int reportedges; -int reportnorms; -{ - int i, j; - - for (i = 0; i < io->numberofpoints; i++) { - printf("Point %4d:", i); - for (j = 0; j < 2; j++) { - printf(" %.6g", io->pointlist[i * 2 + j]); - } - if (io->numberofpointattributes > 0) { - printf(" attributes"); - } - for (j = 0; j < io->numberofpointattributes; j++) { - printf(" %.6g", - io->pointattributelist[i * io->numberofpointattributes + j]); - } - if (markers) { - printf(" marker %d\n", io->pointmarkerlist[i]); - } else { - printf("\n"); - } - } - printf("\n"); - - if (reporttriangles || reportneighbors) { - for (i = 0; i < io->numberoftriangles; i++) { - if (reporttriangles) { - printf("Triangle %4d points:", i); - for (j = 0; j < io->numberofcorners; j++) { - printf(" %4d", io->trianglelist[i * io->numberofcorners + j]); - } - if (io->numberoftriangleattributes > 0) { - printf(" attributes"); - } - for (j = 0; j < io->numberoftriangleattributes; j++) { - printf(" %.6g", io->triangleattributelist[i * - io->numberoftriangleattributes + j]); - } - printf("\n"); - } - if (reportneighbors) { - printf("Triangle %4d neighbors:", i); - for (j = 0; j < 3; j++) { - printf(" %4d", io->neighborlist[i * 3 + j]); - } - printf("\n"); - } - } - printf("\n"); - } - - if (reportsegments) { - for (i = 0; i < io->numberofsegments; i++) { - printf("Segment %4d points:", i); - for (j = 0; j < 2; j++) { - printf(" %4d", io->segmentlist[i * 2 + j]); - } - if (markers) { - printf(" marker %d\n", io->segmentmarkerlist[i]); - } else { - printf("\n"); - } - } - printf("\n"); - } - - if (reportedges) { - for (i = 0; i < io->numberofedges; i++) { - printf("Edge %4d points:", i); - for (j = 0; j < 2; j++) { - printf(" %4d", io->edgelist[i * 2 + j]); - } - if (reportnorms && (io->edgelist[i * 2 + 1] == -1)) { - for (j = 0; j < 2; j++) { - printf(" %.6g", io->normlist[i * 2 + j]); - } - } - if (markers) { - printf(" marker %d\n", io->edgemarkerlist[i]); - } else { - printf("\n"); - } - } - printf("\n"); - } -} - -/*****************************************************************************/ -/* */ -/* main() Create and refine a mesh. */ -/* */ -/*****************************************************************************/ - -int main() -{ - struct triangulateio in, mid, out, vorout; - - /* Define input points. */ - - in.numberofpoints = 4; - in.numberofpointattributes = 1; - in.pointlist = (REAL *) malloc(in.numberofpoints * 2 * sizeof(REAL)); - in.pointlist[0] = 0.0; - in.pointlist[1] = 0.0; - in.pointlist[2] = 1.0; - in.pointlist[3] = 0.0; - in.pointlist[4] = 1.0; - in.pointlist[5] = 10.0; - in.pointlist[6] = 0.0; - in.pointlist[7] = 10.0; - in.pointattributelist = (REAL *) malloc(in.numberofpoints * - in.numberofpointattributes * - sizeof(REAL)); - in.pointattributelist[0] = 0.0; - in.pointattributelist[1] = 1.0; - in.pointattributelist[2] = 11.0; - in.pointattributelist[3] = 10.0; - in.pointmarkerlist = (int *) malloc(in.numberofpoints * sizeof(int)); - in.pointmarkerlist[0] = 0; - in.pointmarkerlist[1] = 2; - in.pointmarkerlist[2] = 0; - in.pointmarkerlist[3] = 0; - - in.numberofsegments = 0; - in.numberofholes = 0; - in.numberofregions = 1; - in.regionlist = (REAL *) malloc(in.numberofregions * 4 * sizeof(REAL)); - in.regionlist[0] = 0.5; - in.regionlist[1] = 5.0; - in.regionlist[2] = 7.0; /* Regional attribute (for whole mesh). */ - in.regionlist[3] = 0.1; /* Area constraint that will not be used. */ - - printf("Input point set:\n\n"); - report(&in, 1, 0, 0, 0, 0, 0); - - /* Make necessary initializations so that Triangle can return a */ - /* triangulation in `mid' and a voronoi diagram in `vorout'. */ - - mid.pointlist = (REAL *) NULL; /* Not needed if -N switch used. */ - /* Not needed if -N switch used or number of point attributes is zero: */ - mid.pointattributelist = (REAL *) NULL; - mid.pointmarkerlist = (int *) NULL; /* Not needed if -N or -B switch used. */ - mid.trianglelist = (int *) NULL; /* Not needed if -E switch used. */ - /* Not needed if -E switch used or number of triangle attributes is zero: */ - mid.triangleattributelist = (REAL *) NULL; - mid.neighborlist = (int *) NULL; /* Needed only if -n switch used. */ - /* Needed only if segments are output (-p or -c) and -P not used: */ - mid.segmentlist = (int *) NULL; - /* Needed only if segments are output (-p or -c) and -P and -B not used: */ - mid.segmentmarkerlist = (int *) NULL; - mid.edgelist = (int *) NULL; /* Needed only if -e switch used. */ - mid.edgemarkerlist = (int *) NULL; /* Needed if -e used and -B not used. */ - - vorout.pointlist = (REAL *) NULL; /* Needed only if -v switch used. */ - /* Needed only if -v switch used and number of attributes is not zero: */ - vorout.pointattributelist = (REAL *) NULL; - vorout.edgelist = (int *) NULL; /* Needed only if -v switch used. */ - vorout.normlist = (REAL *) NULL; /* Needed only if -v switch used. */ - - /* Triangulate the points. Switches are chosen to read and write a */ - /* PSLG (p), preserve the convex hull (c), number everything from */ - /* zero (z), assign a regional attribute to each element (A), and */ - /* produce an edge list (e), a Voronoi diagram (v), and a triangle */ - /* neighbor list (n). */ - - triangulate("pczAevn", &in, &mid, &vorout); - - printf("Initial triangulation:\n\n"); - report(&mid, 1, 1, 1, 1, 1, 0); - printf("Initial Voronoi diagram:\n\n"); - report(&vorout, 0, 0, 0, 0, 1, 1); - - /* Attach area constraints to the triangles in preparation for */ - /* refining the triangulation. */ - - /* Needed only if -r and -a switches used: */ - mid.trianglearealist = (REAL *) malloc(mid.numberoftriangles * sizeof(REAL)); - mid.trianglearealist[0] = 3.0; - mid.trianglearealist[1] = 1.0; - - /* Make necessary initializations so that Triangle can return a */ - /* triangulation in `out'. */ - - out.pointlist = (REAL *) NULL; /* Not needed if -N switch used. */ - /* Not needed if -N switch used or number of attributes is zero: */ - out.pointattributelist = (REAL *) NULL; - out.trianglelist = (int *) NULL; /* Not needed if -E switch used. */ - /* Not needed if -E switch used or number of triangle attributes is zero: */ - out.triangleattributelist = (REAL *) NULL; - - /* Refine the triangulation according to the attached */ - /* triangle area constraints. */ - - triangulate("prazBP", &mid, &out, (struct triangulateio *) NULL); - - printf("Refined triangulation:\n\n"); - report(&out, 0, 1, 0, 0, 0, 0); - - /* Free all allocated arrays, including those allocated by Triangle. */ - - free(in.pointlist); - free(in.pointattributelist); - free(in.pointmarkerlist); - free(in.regionlist); - free(mid.pointlist); - free(mid.pointattributelist); - free(mid.pointmarkerlist); - free(mid.trianglelist); - free(mid.triangleattributelist); - free(mid.trianglearealist); - free(mid.neighborlist); - free(mid.segmentlist); - free(mid.segmentmarkerlist); - free(mid.edgelist); - free(mid.edgemarkerlist); - free(vorout.pointlist); - free(vorout.pointattributelist); - free(vorout.edgelist); - free(vorout.normlist); - free(out.pointlist); - free(out.pointattributelist); - free(out.trianglelist); - free(out.triangleattributelist); - - return 0; -} diff --git a/src/bounce/meshgen/cylinder_mesh.cpp b/src/bounce/meshgen/cylinder_mesh.cpp index 9d8f99d..dc69bfc 100644 --- a/src/bounce/meshgen/cylinder_mesh.cpp +++ b/src/bounce/meshgen/cylinder_mesh.cpp @@ -29,11 +29,12 @@ void cymCreateMesh(cymMesh& output, u32 segments) u32 indexCount = 3 * (2 * segments) + 2 * 3 * (segments - 2); u32* indices = (u32*)b3Alloc(indexCount * sizeof(u32)); - float32 angle = 2.0f * B3_PI / float32(segments); - b3Quat q(b3Vec3_y, angle); + scalar angle = scalar(2) * B3_PI / scalar(segments); + b3Quat q; + q.SetAxisAngle(b3Vec3_y, angle); // Lower - b3Vec3 v(1.0f, -0.5f, 0.0f); + b3Vec3 v(scalar(1), scalar(-0.5), scalar(0)); for (u32 i = 0; i < segments; ++i) { vertices[i] = v; @@ -41,7 +42,7 @@ void cymCreateMesh(cymMesh& output, u32 segments) } // Upper - v.Set(1.0f, 0.5f, 0.0f); + v.Set(scalar(1), scalar(0.5), scalar(0)); for (u32 i = 0; i < segments; ++i) { vertices[segments + i] = v; diff --git a/src/bounce/meshgen/sphere_mesh.cpp b/src/bounce/meshgen/sphere_mesh.cpp index 8b6f882..4053ee1 100644 --- a/src/bounce/meshgen/sphere_mesh.cpp +++ b/src/bounce/meshgen/sphere_mesh.cpp @@ -18,7 +18,7 @@ #include -static inline void smAddVertex(smMesh& mesh, float32 x, float32 y, float32 z) +static inline void smAddVertex(smMesh& mesh, scalar x, scalar y, scalar z) { mesh.vertices[mesh.vertexCount++].Set(x, y, z); } @@ -34,12 +34,12 @@ static inline void smSetAsOctahedron(smMesh& mesh) { B3_ASSERT(mesh.vertexCount == 0); - smAddVertex(mesh, 0.0f, -1.0f, 0.0f); - smAddVertex(mesh, 0.0f, 0.0f, 1.0f); - smAddVertex(mesh, -1.0f, 0.0f, 0.0f); - smAddVertex(mesh, 0.0f, 0.0f, -1.0f); - smAddVertex(mesh, 1.0f, 0.0f, 0.0f); - smAddVertex(mesh, 0.0f, 1.0f, 0.0f); + smAddVertex(mesh, scalar(0), -scalar(1), scalar(0)); + smAddVertex(mesh, scalar(0), scalar(0), scalar(1)); + smAddVertex(mesh, -scalar(1), scalar(0), scalar(0)); + smAddVertex(mesh, scalar(0), scalar(0), -scalar(1)); + smAddVertex(mesh, scalar(1), scalar(0), scalar(0)); + smAddVertex(mesh, scalar(0), scalar(1), scalar(0)); B3_ASSERT(mesh.indexCount == 0); @@ -109,10 +109,11 @@ static inline u32 smSubdivideEdge(smMesh& out, smEdgeVertexMap& map, b3Vec3 v1 = in.vertices[i1]; b3Vec3 v2 = in.vertices[i2]; - b3Vec3 v = 0.5f * (v1 + v2); - v.Normalize(); + + b3Vec3 c = scalar(0.5) * (v1 + v2); + c.Normalize(); - smAddVertex(out, v.x, v.y, v.z); + smAddVertex(out, c.x, c.y, c.z); smEdgeVertexPair newPair; newPair.edge = newEdge; diff --git a/src/bounce/quickhull/qh_hull.cpp b/src/bounce/quickhull/qh_hull.cpp index 2b11141..8daa4ed 100644 --- a/src/bounce/quickhull/qh_hull.cpp +++ b/src/bounce/quickhull/qh_hull.cpp @@ -19,14 +19,14 @@ #include #include -static float32 qhFindAABB(u32 iMin[3], u32 iMax[3], const b3Vec3* vs, u32 count) +static scalar qhFindAABB(u32 iMin[3], u32 iMax[3], const b3Vec3* vs, u32 count) { - b3Vec3 min(B3_MAX_FLOAT, B3_MAX_FLOAT, B3_MAX_FLOAT); + b3Vec3 min(B3_MAX_SCALAR, B3_MAX_SCALAR, B3_MAX_SCALAR); iMin[0] = 0; iMin[1] = 0; iMin[2] = 0; - b3Vec3 max(-B3_MAX_FLOAT, -B3_MAX_FLOAT, -B3_MAX_FLOAT); + b3Vec3 max(-B3_MAX_SCALAR, -B3_MAX_SCALAR, -B3_MAX_SCALAR); iMax[0] = 0; iMax[1] = 0; iMax[2] = 0; @@ -51,18 +51,18 @@ static float32 qhFindAABB(u32 iMin[3], u32 iMax[3], const b3Vec3* vs, u32 count) } } - return 3.0f * (b3Abs(max.x) + b3Abs(max.y) + b3Abs(max.z)) * B3_EPSILON; + return scalar(3) * (b3Abs(max.x) + b3Abs(max.y) + b3Abs(max.z)) * B3_EPSILON; } qhHull::qhHull() { - m_vertexList.head = NULL; + m_vertexList.head = nullptr; m_vertexList.count = 0; - m_faceList.head = NULL; + m_faceList.head = nullptr; m_faceList.count = 0; - m_buffer = NULL; + m_buffer = nullptr; m_vertexCapacity = 0; m_vertexCount = 0; @@ -113,7 +113,7 @@ qhHull::~qhHull() void qhHull::Construct(const b3Vec3* vs, u32 count) { - B3_ASSERT(m_buffer == NULL); + B3_ASSERT(m_buffer == nullptr); B3_ASSERT(count > 0 && count >= 4); // Compute memory buffer size for the worst case. @@ -149,7 +149,7 @@ void qhHull::Construct(const b3Vec3* vs, u32 count) // Initialize free lists m_vertexCapacity = V; m_vertexCount = m_vertexCapacity; - m_freeVertices = NULL; + m_freeVertices = nullptr; qhVertex* vertices = (qhVertex*)m_buffer; for (u32 i = 0; i < V; ++i) { @@ -160,7 +160,7 @@ void qhHull::Construct(const b3Vec3* vs, u32 count) m_edgeCapacity = HE; m_edgeCount = m_edgeCapacity; - m_freeEdges = NULL; + m_freeEdges = nullptr; qhHalfEdge* edges = (qhHalfEdge*)((u8*)vertices + V * sizeof(qhVertex)); for (u32 i = 0; i < HE; ++i) { @@ -171,12 +171,12 @@ void qhHull::Construct(const b3Vec3* vs, u32 count) m_faceCapacity = F; m_faceCount = m_faceCapacity; - m_freeFaces = NULL; + m_freeFaces = nullptr; qhFace* faces = (qhFace*)((u8*)edges + HE * sizeof(qhHalfEdge)); for (u32 i = 0; i < F; ++i) { qhFace* f = faces + i; - f->conflictList.head = NULL; + f->conflictList.head = nullptr; f->conflictList.count = 0; f->active = true; FreeFace(f); @@ -227,7 +227,7 @@ void qhHull::Construct(const b3Vec3* vs, u32 count) for (qhFace* f = m_faceList.head; f; f = f->next) { - float32 d = b3Distance(v, f->plane); + scalar d = b3Distance(v, f->plane); B3_ASSERT(d < m_tolerance); } } @@ -252,14 +252,14 @@ bool qhHull::BuildInitialHull(const b3Vec3* vertices, u32 vertexCount) m_tolerance = qhFindAABB(aabbMin, aabbMax, vertices, vertexCount); // Find the longest segment. - float32 d0 = 0.0f; + scalar d0 = scalar(0); for (u32 i = 0; i < 3; ++i) { b3Vec3 A = vertices[aabbMin[i]]; b3Vec3 B = vertices[aabbMax[i]]; - float32 d = b3DistanceSquared(A, B); + scalar d = b3DistanceSquared(A, B); if (d > d0) { @@ -286,7 +286,7 @@ bool qhHull::BuildInitialHull(const b3Vec3* vertices, u32 vertexCount) { // Find the triangle which has the largest area. - float32 a0 = 0.0f; + scalar a0 = scalar(0); for (u32 i = 0; i < vertexCount; ++i) { @@ -297,7 +297,7 @@ bool qhHull::BuildInitialHull(const b3Vec3* vertices, u32 vertexCount) b3Vec3 C = vertices[i]; - float32 a = b3AreaSquared(A, B, C); + scalar a = b3AreaSquared(A, B, C); if (a > a0) { @@ -307,7 +307,7 @@ bool qhHull::BuildInitialHull(const b3Vec3* vertices, u32 vertexCount) } // Colinear check. - if (a0 <= (2.0f * B3_EPSILON) * (2.0f * B3_EPSILON)) + if (a0 <= (scalar(2) * B3_EPSILON) * (scalar(2) * B3_EPSILON)) { B3_ASSERT(false); return false; @@ -327,7 +327,7 @@ bool qhHull::BuildInitialHull(const b3Vec3* vertices, u32 vertexCount) { // Find the furthest point from the triangle plane. - float32 d0 = 0.0f; + scalar d0 = scalar(0); for (u32 i = 0; i < vertexCount; ++i) { @@ -338,7 +338,7 @@ bool qhHull::BuildInitialHull(const b3Vec3* vertices, u32 vertexCount) b3Vec3 D = vertices[i]; - float32 d = b3Abs(b3Distance(D, plane)); + scalar d = b3Abs(b3Distance(D, plane)); if (d > d0) { @@ -365,7 +365,7 @@ bool qhHull::BuildInitialHull(const b3Vec3* vertices, u32 vertexCount) qhVertex* v3 = AddVertex(C); qhVertex* v4 = AddVertex(D); - if (b3Distance(D, plane) < 0.0f) + if (b3Distance(D, plane) < scalar(0)) { AddFace(v1, v2, v3); AddFace(v4, v2, v1); @@ -396,12 +396,12 @@ bool qhHull::BuildInitialHull(const b3Vec3* vertices, u32 vertexCount) b3Vec3 p = vertices[i]; // Ignore internal points since they can't be in the hull. - float32 d0 = m_tolerance; - qhFace* f0 = NULL; + scalar d0 = m_tolerance; + qhFace* f0 = nullptr; - for (qhFace* f = m_faceList.head; f != NULL; f = f->next) + for (qhFace* f = m_faceList.head; f != nullptr; f = f->next) { - float32 d = b3Distance(p, f->plane); + scalar d = b3Distance(p, f->plane); if (d > d0) { d0 = d; @@ -424,14 +424,14 @@ bool qhHull::BuildInitialHull(const b3Vec3* vertices, u32 vertexCount) qhVertex* qhHull::FindEyeVertex() const { // Find the furthest conflict point. - float32 d0 = m_tolerance; - qhVertex* v0 = NULL; + scalar d0 = m_tolerance; + qhVertex* v0 = nullptr; - for (qhFace* f = m_faceList.head; f != NULL; f = f->next) + for (qhFace* f = m_faceList.head; f != nullptr; f = f->next) { - for (qhVertex* v = f->conflictList.head; v != NULL; v = v->next) + for (qhVertex* v = f->conflictList.head; v != nullptr; v = v->next) { - float32 d = b3Distance(v->position, f->plane); + scalar d = b3Distance(v->position, f->plane); if (d > d0) { d0 = d; @@ -454,9 +454,9 @@ void qhHull::AddEyeVertex(qhVertex* eye) void qhHull::FindHorizon(qhVertex* eye) { // Mark faces - for (qhFace* face = m_faceList.head; face != NULL; face = face->next) + for (qhFace* face = m_faceList.head; face != nullptr; face = face->next) { - float32 d = b3Distance(eye->position, face->plane); + scalar d = b3Distance(eye->position, face->plane); if (d > m_tolerance) { face->mark = qhFaceMark::e_visible; @@ -469,7 +469,7 @@ void qhHull::FindHorizon(qhVertex* eye) // Find the horizon m_horizonCount = 0; - for (qhFace* face = m_faceList.head; face != NULL; face = face->next) + for (qhFace* face = m_faceList.head; face != nullptr; face = face->next) { if (face->mark == qhFaceMark::e_invisible) { @@ -569,7 +569,7 @@ void qhHull::AddNewFaces(qhVertex* eye) m_conflictVertices[m_conflictCount++] = v; // Remove vertex from face - v->conflictFace = NULL; + v->conflictFace = nullptr; v = f->conflictList.Remove(v); } @@ -600,8 +600,8 @@ void qhHull::ResolveOrphans() b3Vec3 p = v->position; - float32 d0 = m_tolerance; - qhFace* f0 = NULL; + scalar d0 = m_tolerance; + qhFace* f0 = nullptr; for (u32 j = 0; j < m_newFaceCount; ++j) { @@ -613,7 +613,7 @@ void qhHull::ResolveOrphans() continue; } - float32 d = b3Distance(p, nf->plane); + scalar d = b3Distance(p, nf->plane); if (d > d0) { d0 = d; @@ -639,7 +639,7 @@ qhVertex* qhHull::AddVertex(const b3Vec3& position) { qhVertex* v = AllocateVertex(); v->position = position; - v->conflictFace = NULL; + v->conflictFace = nullptr; m_vertexList.PushFront(v); @@ -648,13 +648,13 @@ qhVertex* qhHull::AddVertex(const b3Vec3& position) qhHalfEdge* qhHull::FindHalfEdge(const qhVertex* v1, const qhVertex* v2) const { - for (qhFace* face = m_faceList.head; face != NULL; face = face->next) + for (qhFace* face = m_faceList.head; face != nullptr; face = face->next) { qhHalfEdge* e = face->edge; do { B3_ASSERT(e->active == true); - B3_ASSERT(e->twin != NULL); + B3_ASSERT(e->twin != nullptr); B3_ASSERT(e->twin->active == true); if (e->tail == v1 && e->twin->tail == v2) @@ -670,7 +670,7 @@ qhHalfEdge* qhHull::FindHalfEdge(const qhVertex* v1, const qhVertex* v2) const e = e->next; } while (e != face->edge); } - return NULL; + return nullptr; } static B3_FORCE_INLINE b3Vec3 b3Newell(const b3Vec3& a, const b3Vec3& b) @@ -696,7 +696,7 @@ static void b3ResetFaceData(qhFace* face) } while (e != face->edge); B3_ASSERT(count >= 3); - c /= float32(count); + c /= scalar(count); // Compute normal b3Vec3 n; @@ -721,12 +721,12 @@ static void b3ResetFaceData(qhFace* face) // Centroid face->center = c; - float32 len = b3Length(n); + scalar len = b3Length(n); B3_ASSERT(len > B3_EPSILON); n /= len; // Area - face->area = 0.5f * len; + face->area = scalar(0.5) * len; // Normal face->plane.normal = n; @@ -890,7 +890,7 @@ qhFace* qhHull::RemoveEdge(qhHalfEdge* edge) qhFace* face2 = edge->twin->face; // Edge must be shared. - B3_ASSERT(face2 != NULL); + B3_ASSERT(face2 != nullptr); B3_ASSERT(face2->active == true); B3_ASSERT(face2 != face1); @@ -962,7 +962,7 @@ bool qhHull::FixFace(qhFace* face) // Search a incoming (and outgoing edge) in the face 1 // which have the same neighbour face. - qhHalfEdge* edge = NULL; + qhHalfEdge* edge = nullptr; qhHalfEdge* ein = face->edge; do @@ -993,115 +993,115 @@ bool qhHull::FixFace(qhFace* face) qhFace* qhHull::AddFace(qhVertex* v1, qhVertex* v2, qhVertex* v3) { // Each vertex must be free. - //B3_ASSERT(v1->edge == NULL); - //B3_ASSERT(v2->edge == NULL); - //B3_ASSERT(v3->edge == NULL); + //B3_ASSERT(v1->edge == nullptr); + //B3_ASSERT(v2->edge == nullptr); + //B3_ASSERT(v3->edge == nullptr); qhFace* face = AllocateFace(); qhHalfEdge* e1 = FindHalfEdge(v1, v2); - if (e1 == NULL) + if (e1 == nullptr) { e1 = AllocateEdge(); e1->tail = v1; e1->face = face; - e1->prev = NULL; - e1->next = NULL; + e1->prev = nullptr; + e1->next = nullptr; e1->twin = AllocateEdge(); e1->twin->tail = v2; - e1->twin->face = NULL; - e1->twin->prev = NULL; - e1->twin->next = NULL; + e1->twin->face = nullptr; + e1->twin->prev = nullptr; + e1->twin->next = nullptr; e1->twin->twin = e1; } else { // Edge must be free. - B3_ASSERT(e1->face == NULL); + B3_ASSERT(e1->face == nullptr); e1->face = face; B3_ASSERT(e1->tail == v1); - B3_ASSERT(e1->twin != NULL); + B3_ASSERT(e1->twin != nullptr); B3_ASSERT(e1->twin->active == true); B3_ASSERT(e1->twin->tail == v2); } qhHalfEdge* e2 = FindHalfEdge(v2, v3); - if (e2 == NULL) + if (e2 == nullptr) { e2 = AllocateEdge(); e2->tail = v2; e2->face = face; - e2->prev = NULL; - e2->next = NULL; + e2->prev = nullptr; + e2->next = nullptr; e2->twin = AllocateEdge(); e2->twin->tail = v3; - e2->twin->face = NULL; - e2->twin->prev = NULL; - e2->twin->next = NULL; + e2->twin->face = nullptr; + e2->twin->prev = nullptr; + e2->twin->next = nullptr; e2->twin->twin = e2; } else { // Edge must be free. - B3_ASSERT(e2->face == NULL); + B3_ASSERT(e2->face == nullptr); e2->face = face; B3_ASSERT(e2->tail == v2); - B3_ASSERT(e2->twin != NULL); + B3_ASSERT(e2->twin != nullptr); B3_ASSERT(e2->twin->active == true); B3_ASSERT(e2->twin->tail == v3); } qhHalfEdge* e3 = FindHalfEdge(v3, v1); - if (e3 == NULL) + if (e3 == nullptr) { e3 = AllocateEdge(); e3->tail = v3; e3->face = face; - e3->prev = NULL; - e3->next = NULL; + e3->prev = nullptr; + e3->next = nullptr; e3->twin = AllocateEdge(); e3->twin->tail = v1; - e3->twin->face = NULL; - e3->twin->prev = NULL; - e3->twin->next = NULL; + e3->twin->face = nullptr; + e3->twin->prev = nullptr; + e3->twin->next = nullptr; e3->twin->twin = e3; } else { // Edge must be free. - B3_ASSERT(e3->face == NULL); + B3_ASSERT(e3->face == nullptr); e3->face = face; B3_ASSERT(e3->tail == v3); - B3_ASSERT(e3->twin != NULL); + B3_ASSERT(e3->twin != nullptr); B3_ASSERT(e3->twin->active == true); B3_ASSERT(e3->twin->tail == v1); } - B3_ASSERT(e1->prev == NULL); + B3_ASSERT(e1->prev == nullptr); e1->prev = e3; - B3_ASSERT(e1->next == NULL); + B3_ASSERT(e1->next == nullptr); e1->next = e2; - B3_ASSERT(e2->prev == NULL); + B3_ASSERT(e2->prev == nullptr); e2->prev = e1; - B3_ASSERT(e2->next == NULL); + B3_ASSERT(e2->next == nullptr); e2->next = e3; - B3_ASSERT(e3->prev == NULL); + B3_ASSERT(e3->prev == nullptr); e3->prev = e2; - B3_ASSERT(e3->next == NULL); + B3_ASSERT(e3->next == nullptr); e3->next = e1; face->edge = e1; b3ResetFaceData(face); - face->conflictList.head = NULL; + face->conflictList.head = nullptr; face->conflictList.count = 0; Validate(face); @@ -1124,7 +1124,7 @@ qhFace* qhHull::RemoveFace(qhFace* face) e = e->next; // Is the edge a boundary? - if (e0->twin->face == NULL) + if (e0->twin->face == nullptr) { // Edge is non-shared. FreeEdge(e0->twin); @@ -1134,11 +1134,11 @@ qhFace* qhHull::RemoveFace(qhFace* face) { // Edge is shared. // Mark the twin edge as a boundary edge. - B3_ASSERT(e0->twin != NULL); + B3_ASSERT(e0->twin != nullptr); B3_ASSERT(e0->twin->twin == e0); - e0->face = NULL; - e0->prev = NULL; - e0->next = NULL; + e0->face = nullptr; + e0->prev = nullptr; + e0->next = nullptr; } } while (e != face->edge); @@ -1154,7 +1154,7 @@ qhFace* qhHull::RemoveFace(qhFace* face) bool qhHull::MergeFace(qhFace* face1) { // Non-convex edge - qhHalfEdge* edge = NULL; + qhHalfEdge* edge = nullptr; qhHalfEdge* e = face1->edge; @@ -1163,11 +1163,11 @@ bool qhHull::MergeFace(qhFace* face1) qhHalfEdge* twin = e->twin; qhFace* face2 = twin->face; - B3_ASSERT(face2 != NULL); + B3_ASSERT(face2 != nullptr); B3_ASSERT(face2 != face1); - float32 d1 = b3Distance(face2->center, face1->plane); - float32 d2 = b3Distance(face1->center, face2->plane); + scalar d1 = b3Distance(face2->center, face1->plane); + scalar d2 = b3Distance(face1->center, face2->plane); if (d1 < -m_tolerance && d2 < -m_tolerance) { @@ -1194,7 +1194,7 @@ bool qhHull::MergeFace(qhFace* face1) bool qhHull::MergeLargeFace(qhFace* face1) { // Find a non-convex edge - qhHalfEdge* edge = NULL; + qhHalfEdge* edge = nullptr; qhHalfEdge* e = face1->edge; @@ -1205,13 +1205,13 @@ bool qhHull::MergeLargeFace(qhFace* face1) qhHalfEdge* twin = e->twin; qhFace* face2 = twin->face; - B3_ASSERT(face2 != NULL); + B3_ASSERT(face2 != nullptr); B3_ASSERT(face2 != face1); if (face1->area > face2->area) { // Face 1 merge - float32 d = b3Distance(face2->center, face1->plane); + scalar d = b3Distance(face2->center, face1->plane); if (d < -m_tolerance) { // Edge is convex wrt to the face 1 @@ -1226,7 +1226,7 @@ bool qhHull::MergeLargeFace(qhFace* face1) else { // Face 2 merge - float32 d = b3Distance(face1->center, face2->plane); + scalar d = b3Distance(face1->center, face2->plane); if (d < -m_tolerance) { // Edge is convex wrt to the face 2 @@ -1286,7 +1286,7 @@ void qhHull::MergeNewFaces() void qhHull::Translate(const b3Vec3& translation) { // Shift vertices - for (qhVertex* v = m_vertexList.head; v != NULL; v = v->next) + for (qhVertex* v = m_vertexList.head; v != nullptr; v = v->next) { v->position += translation; } @@ -1300,7 +1300,7 @@ void qhHull::Translate(const b3Vec3& translation) void qhHull::ValidateConvexity() const { - for (qhFace* face = m_faceList.head; face != NULL; face = face->next) + for (qhFace* face = m_faceList.head; face != nullptr; face = face->next) { B3_ASSERT(face->active == true); @@ -1310,22 +1310,22 @@ void qhHull::ValidateConvexity() const B3_ASSERT(edge->active == true); B3_ASSERT(edge->face == face); - B3_ASSERT(edge->twin != NULL); + B3_ASSERT(edge->twin != nullptr); B3_ASSERT(edge->twin->active == true); qhFace* other = edge->twin->face; // Ensure closed volume - B3_ASSERT(other != NULL); + B3_ASSERT(other != nullptr); B3_ASSERT(other->active == true); // Ensure topological health B3_ASSERT(face != other); // Ensure edge convexity - float32 d1 = b3Distance(other->center, face->plane); + scalar d1 = b3Distance(other->center, face->plane); B3_ASSERT(d1 < -m_tolerance); - float32 d2 = b3Distance(face->center, other->plane); + scalar d2 = b3Distance(face->center, other->plane); B3_ASSERT(d2 < -m_tolerance); // Ensure polygon convexity @@ -1344,8 +1344,8 @@ void qhHull::ValidateConvexity() const const qhHalfEdge* eother = edge->prev; do { - float32 d = b3Distance(eother->tail->position, plane); - B3_ASSERT(d <= 0.0f); + scalar d = b3Distance(eother->tail->position, plane); + B3_ASSERT(d <= scalar(0)); eother = eother->prev; } while (eother != edge->next); @@ -1424,10 +1424,10 @@ void qhHull::Validate(const qhFace* face) const B3_ASSERT(edge->active == true); B3_ASSERT(edge->face == face); - B3_ASSERT(edge->twin != NULL); + B3_ASSERT(edge->twin != nullptr); B3_ASSERT(edge->twin->active == true); - if (edge->twin->face != NULL) + if (edge->twin->face != nullptr) { B3_ASSERT(edge->twin->face->active == true); B3_ASSERT(edge->twin->face != face); @@ -1445,10 +1445,10 @@ void qhHull::Validate(const qhFace* face) const B3_ASSERT(edge->active == true); B3_ASSERT(edge->face == face); - B3_ASSERT(edge->twin != NULL); + B3_ASSERT(edge->twin != nullptr); B3_ASSERT(edge->twin->active == true); - if (edge->twin->face != NULL) + if (edge->twin->face != nullptr) { B3_ASSERT(edge->twin->face->active == true); B3_ASSERT(edge->twin->face != face); @@ -1471,16 +1471,16 @@ void qhHull::Validate(const qhFace* face) const void qhHull::Validate() const { - for (qhVertex* vertex = m_vertexList.head; vertex != NULL; vertex = vertex->next) + for (qhVertex* vertex = m_vertexList.head; vertex != nullptr; vertex = vertex->next) { B3_ASSERT(vertex->active == true); } - for (qhFace* face = m_faceList.head; face != NULL; face = face->next) + for (qhFace* face = m_faceList.head; face != nullptr; face = face->next) { B3_ASSERT(face->active == true); - for (qhVertex* vertex = face->conflictList.head; vertex != NULL; vertex = vertex->next) + for (qhVertex* vertex = face->conflictList.head; vertex != nullptr; vertex = vertex->next) { B3_ASSERT(vertex->active == true); } @@ -1502,12 +1502,12 @@ void qhHull::Validate() const void qhHull::Draw() const { - for (qhFace* face = m_faceList.head; face != NULL; face = face->next) + for (qhFace* face = m_faceList.head; face != nullptr; face = face->next) { b3Vec3 c = face->center; b3Vec3 n = face->plane.normal; - b3Draw_draw->DrawSegment(c, c + n, b3Color(1.0f, 1.0f, 1.0f)); + b3Draw_draw->DrawSegment(c, c + n, b3Color(scalar(1), scalar(1), scalar(1))); const qhHalfEdge* edge = face->edge; do @@ -1517,8 +1517,8 @@ void qhHull::Draw() const const qhHalfEdge* next = edge->next; qhVertex* v3 = next->tail; - b3Draw_draw->DrawSegment(v2->position, v3->position, b3Color(0.0f, 0.0f, 0.0f, 1.0f)); - b3Draw_draw->DrawSolidTriangle(n, v1->position, v2->position, v3->position, b3Color(1.0f, 1.0f, 1.0f, 0.5f)); + b3Draw_draw->DrawSegment(v2->position, v3->position, b3Color(scalar(0), scalar(0), scalar(0), scalar(1))); + b3Draw_draw->DrawSolidTriangle(n, v1->position, v2->position, v3->position, b3Color(scalar(1), scalar(1), scalar(1), scalar(0.5))); edge = next; } while (edge->next != face->edge); @@ -1526,8 +1526,8 @@ void qhHull::Draw() const qhVertex* v = face->conflictList.head; while (v) { - b3Draw_draw->DrawPoint(v->position, 4.0f, b3Color(1.0f, 1.0f, 0.0f)); - b3Draw_draw->DrawSegment(c, v->position, b3Color(1.0f, 1.0f, 0.0f)); + b3Draw_draw->DrawPoint(v->position, 4.0f, b3Color(scalar(1), scalar(1), scalar(0))); + b3Draw_draw->DrawSegment(c, v->position, b3Color(scalar(1), scalar(1), scalar(0))); v = v->next; } } diff --git a/src/bounce/rope/rope.cpp b/src/bounce/rope/rope.cpp index d74582d..1eb5b26 100644 --- a/src/bounce/rope/rope.cpp +++ b/src/bounce/rope/rope.cpp @@ -38,8 +38,8 @@ struct b3RopeBody b3Quat E = b3Conjugate(m_p); b3Transform X; - X.rotation = b3QuatMat33(E); - X.position.SetZero(); + X.rotation = E; + X.translation.SetZero(); return X; } @@ -48,7 +48,7 @@ struct b3RopeBody // Body // - float32 m_m, m_I; + scalar m_m, m_I; // Joint @@ -109,9 +109,9 @@ struct b3RopeBody b3Rope::b3Rope() { m_gravity.SetZero(); - m_kd1 = 0.0f; - m_kd2 = 0.0f; - m_links = NULL; + m_kd1 = scalar(0); + m_kd2 = scalar(0); + m_links = nullptr; m_count = 0; } @@ -134,11 +134,11 @@ void b3Rope::Initialize(const b3RopeDef& def) { b3RopeBody* b = m_links + i; - float32 m = def.masses[i]; + scalar m = def.masses[i]; // Simplify r = 1 b->m_m = m; - b->m_I = m * 0.4f; + b->m_I = m * scalar(0.4); } m_v.SetZero(); @@ -146,8 +146,8 @@ void b3Rope::Initialize(const b3RopeDef& def) m_w.SetZero(); m_q.SetIdentity(); - m_links->m_X.rotation = b3QuatMat33(m_q); - m_links->m_X.position = m_p; + m_links->m_X.rotation = m_q; + m_links->m_X.translation = m_p; for (u32 i = 1; i < m_count; ++i) { @@ -157,18 +157,18 @@ void b3Rope::Initialize(const b3RopeDef& def) b3Vec3 p0 = def.vertices[i - 1]; b->m_X.rotation.SetIdentity(); - b->m_X.position = p; + b->m_X.translation = p; // Set the joint anchor to the parent body position to simulate a rope. b3Transform X_J; X_J.rotation.SetIdentity(); - X_J.position = p0; + X_J.translation = p0; b->m_X_i_J = b3MulT(X_J, b0->m_X); b->m_X_J_j = b3MulT(b->m_X, X_J); - b3Vec3 d = -b->m_X_J_j.position; + b3Vec3 d = -b->m_X_J_j.translation; b3Vec3 w1(b3Vec3_x); b3Vec3 v1 = b3Cross(w1, d); @@ -193,7 +193,7 @@ void b3Rope::Initialize(const b3RopeDef& def) } } -void b3Rope::Step(float32 h) +void b3Rope::Step(scalar h) { if (m_count == 0) { @@ -208,10 +208,10 @@ void b3Rope::Step(float32 h) b->m_invX = b3Inverse(b->m_X); // Convert global velocity to local velocity. - b->m_sv.w = b->m_invX.rotation * m_w; - b->m_sv.v = b->m_invX.rotation * m_v; + b->m_sv.w = b3Mul(b->m_invX.rotation, m_w); + b->m_sv.v = b3Mul(b->m_invX.rotation, m_v); - if (b->m_m == 0.0f) + if (b->m_m == scalar(0)) { b->m_I_A.SetZero(); b->m_F_A.SetZero(); @@ -225,7 +225,7 @@ void b3Rope::Step(float32 h) // Convert global force to local force. b3ForceVec F; - F.n = b->m_invX.rotation * m_gravity; + F.n = b3Mul(b->m_invX.rotation, m_gravity); F.f.SetZero(); // Damping force @@ -252,8 +252,8 @@ void b3Rope::Step(float32 h) // Flip the translation because r should be the vector // from the center of mass of the parent link to the // center of mass of this link in this link's frame. - link->m_X_i_j.E = X_i_j.rotation; - link->m_X_i_j.r = -X_i_j.position; + link->m_X_i_j.E = b3QuatMat33(X_i_j.rotation); + link->m_X_i_j.r = -X_i_j.translation; b3MotionVec joint_v = link->v_J(); @@ -277,7 +277,7 @@ void b3Rope::Step(float32 h) // Convert global force to local force. b3ForceVec F; - F.n = link->m_invX.rotation * m_gravity; + F.n = b3Mul(link->m_invX.rotation, m_gravity); F.f.SetZero(); link->m_I_A.SetLocalInertia(link->m_m, I); @@ -360,7 +360,7 @@ void b3Rope::Step(float32 h) { b3RopeBody* body = m_links; - if (body->m_m == 0.0f) + if (body->m_m == scalar(0)) { body->m_sa.SetZero(); } @@ -369,8 +369,8 @@ void b3Rope::Step(float32 h) // a = I^-1 * F if (m_count == 1) { - float32 inv_m = body->m_m > 0.0f ? 1.0f / body->m_m : 0.0f; - float32 invI = body->m_I > 0.0f ? 1.0f / body->m_I : 0.0f; + scalar inv_m = body->m_m > scalar(0) ? scalar(1) / body->m_m : scalar(0); + scalar invI = body->m_I > scalar(0) ? scalar(1) / body->m_I : scalar(0); body->m_sa.w = -invI * body->m_F_A.f; body->m_sa.v = -inv_m * body->m_F_A.n; @@ -409,7 +409,7 @@ void b3Rope::Step(float32 h) } // Integrate - float32 max_w = 8.0f * 2.0f * B3_PI; + scalar max_w = scalar(8) * scalar(2) * B3_PI; b3Vec3 min(-max_w, -max_w, -max_w); b3Vec3 max(max_w, max_w, max_w); @@ -427,8 +427,8 @@ void b3Rope::Step(float32 h) // Integrate velocity m_p += h * m_v; - b3Quat q_w(m_w.x, m_w.y, m_w.z, 0.0f); - b3Quat q_dot = 0.5f * q_w * m_q; + b3Quat q_w(m_w.x, m_w.y, m_w.z, scalar(0)); + b3Quat q_dot = scalar(0.5) * q_w * m_q; m_q += h * q_dot; m_q.Normalize(); } @@ -444,16 +444,16 @@ void b3Rope::Step(float32 h) link->m_v = b3Clamp(link->m_v, min, max); // Integrate velocity - b3Quat q_w(link->m_v.x, link->m_v.y, link->m_v.z, 0.0f); - b3Quat q_dot = 0.5f * link->m_p * q_w; + b3Quat q_w(link->m_v.x, link->m_v.y, link->m_v.z, scalar(0)); + b3Quat q_dot = scalar(0.5) * link->m_p * q_w; link->m_p += h * q_dot; link->m_p.Normalize(); } // Propagate down transforms - m_links->m_X.rotation = b3QuatMat33(m_q); - m_links->m_X.position = m_p; + m_links->m_X.rotation = m_q; + m_links->m_X.translation = m_p; m_links->m_invX = b3Inverse(m_links->m_X); for (u32 j = 1; j < m_count; ++j) @@ -480,7 +480,7 @@ void b3Rope::Draw() const b3RopeBody* b = m_links; b3Draw_draw->DrawTransform(b->m_X); - b3Draw_draw->DrawSolidSphere(b->m_X.position, 0.2f, b->m_X.rotation, b3Color_green); + b3Draw_draw->DrawSolidSphere(b->m_X.translation, scalar(0.2), b->m_X.rotation, b3Color_green); } for (u32 i = 1; i < m_count; ++i) @@ -492,12 +492,12 @@ void b3Rope::Draw() const b3Transform X_J0 = b->m_X * b->m_X_J_j; b3Draw_draw->DrawTransform(X_J); - b3Draw_draw->DrawPoint(X_J.position, 5.0f, b3Color_red); + b3Draw_draw->DrawPoint(X_J.translation, scalar(5), b3Color_red); b3Draw_draw->DrawTransform(X_J0); - b3Draw_draw->DrawPoint(X_J0.position, 5.0f, b3Color_red); + b3Draw_draw->DrawPoint(X_J0.translation, scalar(5), b3Color_red); b3Draw_draw->DrawTransform(b->m_X); - b3Draw_draw->DrawSolidSphere(b->m_X.position, 0.2f, b->m_X.rotation, b3Color_green); + b3Draw_draw->DrawSolidSphere(b->m_X.translation, scalar(0.2), b->m_X.rotation, b3Color_green); } } \ No newline at end of file diff --git a/src/bounce/softbody/contacts/softbody_contact_solver.cpp b/src/bounce/softbody/contacts/softbody_contact_solver.cpp index 21721c9..0427536 100644 --- a/src/bounce/softbody/contacts/softbody_contact_solver.cpp +++ b/src/bounce/softbody/contacts/softbody_contact_solver.cpp @@ -17,7 +17,9 @@ */ #include -#include +#include +#include +#include #include #include #include @@ -25,255 +27,145 @@ b3SoftBodyContactSolver::b3SoftBodyContactSolver(const b3SoftBodyContactSolverDef& def) { + m_step = def.step; m_allocator = def.allocator; m_positions = def.positions; m_velocities = def.velocities; - m_bodyContactCount = def.bodyContactCount; - m_bodyContacts = def.bodyContacts; + m_shapeContactCount = def.shapeContactCount; + m_shapeContacts = def.shapeContacts; } b3SoftBodyContactSolver::~b3SoftBodyContactSolver() { - m_allocator->Free(m_bodyPositionConstraints); - m_allocator->Free(m_bodyVelocityConstraints); + m_allocator->Free(m_shapePositionConstraints); + m_allocator->Free(m_shapeVelocityConstraints); } -void b3SoftBodyContactSolver::InitializeBodyContactConstraints() +void b3SoftBodyContactSolver::InitializeShapeContactConstraints() { - m_bodyVelocityConstraints = (b3SoftBodySolverBodyContactVelocityConstraint*)m_allocator->Allocate(m_bodyContactCount * sizeof(b3SoftBodySolverBodyContactVelocityConstraint)); - m_bodyPositionConstraints = (b3SoftBodySolverBodyContactPositionConstraint*)m_allocator->Allocate(m_bodyContactCount * sizeof(b3SoftBodySolverBodyContactPositionConstraint)); + m_shapeVelocityConstraints = (b3SoftBodySolverShapeContactVelocityConstraint*)m_allocator->Allocate(m_shapeContactCount * sizeof(b3SoftBodySolverShapeContactVelocityConstraint)); + m_shapePositionConstraints = (b3SoftBodySolverShapeContactPositionConstraint*)m_allocator->Allocate(m_shapeContactCount * sizeof(b3SoftBodySolverShapeContactPositionConstraint)); - for (u32 i = 0; i < m_bodyContactCount; ++i) + for (u32 i = 0; i < m_shapeContactCount; ++i) { - b3NodeBodyContact* c = m_bodyContacts[i]; - b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; - b3SoftBodySolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; + b3SoftBodySphereAndShapeContact* c = m_shapeContacts[i]; + b3SoftBodySolverShapeContactVelocityConstraint* vc = m_shapeVelocityConstraints + i; + b3SoftBodySolverShapeContactPositionConstraint* pc = m_shapePositionConstraints + i; - vc->indexA = c->m_n1->m_vertex; - vc->bodyB = c->m_s2->GetBody(); + b3SoftBodySphereShape* s1 = c->m_s1; + b3SoftBodyNode* n1 = s1->m_node; - vc->invMassA = c->m_n1->m_type == e_staticSoftBodyNode ? 0.0f : c->m_n1->m_invMass; - vc->invMassB = vc->bodyB->GetInverseMass(); + b3SoftBodyWorldShape* ws2 = c->m_s2; + const b3Shape* s2 = ws2->m_shape; - vc->invIA.SetZero(); - vc->invIB = vc->bodyB->GetWorldInverseInertia(); + vc->indexA = n1->m_meshIndex; + vc->invMassA = n1->m_type == e_staticSoftBodyNode ? scalar(0) : n1->m_invMass; - vc->friction = b3MixFriction(c->m_n1->m_friction, c->m_s2->GetFriction()); + vc->friction = b3MixFriction(s1->m_friction, s2->GetFriction()); - pc->indexA = c->m_n1->m_vertex; - pc->bodyB = vc->bodyB; + pc->indexA = n1->m_meshIndex; + pc->invMassA = n1->m_type == e_staticSoftBodyNode ? scalar(0) : n1->m_invMass; - pc->invMassA = c->m_n1->m_type == e_staticSoftBodyNode ? 0.0f : c->m_n1->m_invMass; - pc->invMassB = vc->bodyB->m_invMass; + pc->radiusA = s1->m_radius; + pc->radiusB = s2->m_radius; - pc->invIA.SetZero(); - pc->invIB = vc->bodyB->m_worldInvI; - - pc->radiusA = c->m_n1->m_radius; - pc->radiusB = c->m_s2->m_radius; - - pc->localCenterA.SetZero(); - pc->localCenterB = pc->bodyB->m_sweep.localCenter; - - pc->normalA = c->m_normal1; - pc->localPointA = c->m_localPoint1; - pc->localPointB = c->m_localPoint2; + pc->normalB = c->m_normal2; + pc->pointB = c->m_point2; } - for (u32 i = 0; i < m_bodyContactCount; ++i) + for (u32 i = 0; i < m_shapeContactCount; ++i) { - b3NodeBodyContact* c = m_bodyContacts[i]; - b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; - b3SoftBodySolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; + b3SoftBodySphereAndShapeContact* c = m_shapeContacts[i]; + b3SoftBodySolverShapeContactVelocityConstraint* vc = m_shapeVelocityConstraints + i; + b3SoftBodySolverShapeContactPositionConstraint* pc = m_shapePositionConstraints + i; u32 indexA = vc->indexA; - b3Body* bodyB = vc->bodyB; + scalar mA = vc->invMassA; + b3Vec3 cA = m_positions[indexA]; - float32 mA = vc->invMassA; - float32 mB = vc->invMassB; - - b3Mat33 iA = vc->invIA; - b3Mat33 iB = vc->invIB; - - b3Vec3 xA = m_positions[indexA]; - b3Vec3 xB = bodyB->m_sweep.worldCenter; - - b3Quat qA; qA.SetIdentity(); - b3Quat qB = bodyB->m_sweep.orientation; - - b3Vec3 localCenterA = pc->localCenterA; - b3Vec3 localCenterB = pc->localCenterB; - - b3Transform xfA; - xfA.rotation = b3QuatMat33(qA); - xfA.position = xA - b3Mul(xfA.rotation, localCenterA); - - b3Transform xfB; - xfB.rotation = b3QuatMat33(qB); - xfB.position = xB - b3Mul(xfB.rotation, localCenterB); - - b3NodeBodyContactWorldPoint wp; - wp.Initialize(c, pc->radiusA, xfA, pc->radiusB, xfB); + b3SoftBodySphereAndShapeContactWorldPoint wp; + wp.Initialize(c, pc->radiusA, cA, pc->radiusB); vc->normal = wp.normal; vc->tangent1 = c->m_tangent1; vc->tangent2 = c->m_tangent2; - vc->point = wp.point; - b3Vec3 point = vc->point; - - b3Vec3 rA = point - xA; - b3Vec3 rB = point - xB; - - vc->rA = rA; - vc->rB = rB; - - vc->normalImpulse = c->m_normalImpulse; - vc->tangentImpulse = c->m_tangentImpulse; + vc->normalImpulse = m_step.dt_ratio * c->m_normalImpulse; + vc->tangentImpulse = m_step.dt_ratio * c->m_tangentImpulse; { - b3Vec3 n = vc->normal; - - b3Vec3 rnA = b3Cross(rA, n); - b3Vec3 rnB = b3Cross(rB, n); - - float32 K = mA + mB + b3Dot(iA * rnA, rnA) + b3Dot(iB * rnB, rnB); - - vc->normalMass = K > 0.0f ? 1.0f / K : 0.0f; - vc->velocityBias = 0.0f; + vc->normalMass = mA > scalar(0) ? scalar(1) / mA : scalar(0); } { - b3Vec3 t1 = vc->tangent1; - b3Vec3 t2 = vc->tangent2; - - b3Vec3 rn1A = b3Cross(rA, t1); - b3Vec3 rn1B = b3Cross(rB, t1); - b3Vec3 rn2A = b3Cross(rA, t2); - b3Vec3 rn2B = b3Cross(rB, t2); - - // dot(t1, t2) = 0 - // J1_l1 * M1 * J2_l1 = J1_l2 * M2 * J2_l2 = 0 - float32 k11 = mA + mB + b3Dot(iA * rn1A, rn1A) + b3Dot(iB * rn1B, rn1B); - float32 k12 = b3Dot(iA * rn1A, rn2A) + b3Dot(iB * rn1B, rn2B); - float32 k22 = mA + mB + b3Dot(iA * rn2A, rn2A) + b3Dot(iB * rn2B, rn2B); - - b3Mat22 K; - K.x.Set(k11, k12); - K.y.Set(k12, k22); - - vc->tangentMass = b3Inverse(K); + vc->tangentMass = mA > scalar(0) ? scalar(1) / mA : scalar(0); } } } void b3SoftBodyContactSolver::WarmStart() { - for (u32 i = 0; i < m_bodyContactCount; ++i) + for (u32 i = 0; i < m_shapeContactCount; ++i) { - b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; + b3SoftBodySolverShapeContactVelocityConstraint* vc = m_shapeVelocityConstraints + i; u32 indexA = vc->indexA; - b3Body* bodyB = vc->bodyB; - b3Vec3 vA = m_velocities[indexA]; - b3Vec3 vB = bodyB->GetLinearVelocity(); - - b3Vec3 wA; wA.SetZero(); - b3Vec3 wB = bodyB->GetAngularVelocity(); - - float32 mA = vc->invMassA; - float32 mB = vc->invMassB; - - b3Mat33 iA = vc->invIA; - b3Mat33 iB = vc->invIB; + scalar mA = vc->invMassA; b3Vec3 P = vc->normalImpulse * vc->normal; - vA -= mA * P; - wA -= iA * b3Cross(vc->rA, P); - - vB += mB * P; - wB += iB * b3Cross(vc->rB, P); + vA += mA * P; b3Vec3 P1 = vc->tangentImpulse.x * vc->tangent1; b3Vec3 P2 = vc->tangentImpulse.y * vc->tangent2; - vA -= mA * (P1 + P2); - wA -= iA * b3Cross(vc->rA, P1 + P2); - - vB += mB * (P1 + P2); - wB += iB * b3Cross(vc->rB, P1 + P2); + vA += mA * (P1 + P2); m_velocities[indexA] = vA; - - bodyB->SetLinearVelocity(vB); - bodyB->SetAngularVelocity(wB); } } -void b3SoftBodyContactSolver::SolveBodyContactVelocityConstraints() +void b3SoftBodyContactSolver::SolveShapeContactVelocityConstraints() { - for (u32 i = 0; i < m_bodyContactCount; ++i) + for (u32 i = 0; i < m_shapeContactCount; ++i) { - b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; + b3SoftBodySolverShapeContactVelocityConstraint* vc = m_shapeVelocityConstraints + i; u32 indexA = vc->indexA; - b3Body* bodyB = vc->bodyB; - b3Vec3 vA = m_velocities[indexA]; - b3Vec3 vB = bodyB->GetLinearVelocity(); - - b3Vec3 wA; wA.SetZero(); - b3Vec3 wB = bodyB->GetAngularVelocity(); - - float32 mA = vc->invMassA; - float32 mB = vc->invMassB; - - b3Mat33 iA = vc->invIA; - b3Mat33 iB = vc->invIB; + scalar mA = vc->invMassA; b3Vec3 normal = vc->normal; - b3Vec3 point = vc->point; - - b3Vec3 rA = vc->rA; - b3Vec3 rB = vc->rB; // Solve normal constraint. { - b3Vec3 dv = vB + b3Cross(wB, rB) - vA - b3Cross(wA, rA); - float32 Cdot = b3Dot(normal, dv); + scalar Cdot = b3Dot(normal, vA); - float32 impulse = vc->normalMass * (-Cdot + vc->velocityBias); + scalar impulse = -vc->normalMass * Cdot; - float32 oldImpulse = vc->normalImpulse; - vc->normalImpulse = b3Max(vc->normalImpulse + impulse, 0.0f); + scalar oldImpulse = vc->normalImpulse; + vc->normalImpulse = b3Max(vc->normalImpulse + impulse, scalar(0)); impulse = vc->normalImpulse - oldImpulse; b3Vec3 P = impulse * normal; - vA -= mA * P; - wA -= iA * b3Cross(rA, P); - - vB += mB * P; - wB += iB * b3Cross(rB, P); + vA += mA * P; } // Solve tangent constraints. { - b3Vec3 dv = vB + b3Cross(wB, rB) - vA - b3Cross(wA, rA); - b3Vec2 Cdot; - Cdot.x = b3Dot(dv, vc->tangent1); - Cdot.y = b3Dot(dv, vc->tangent2); + Cdot.x = b3Dot(vA, vc->tangent1); + Cdot.y = b3Dot(vA, vc->tangent2); b3Vec2 impulse = vc->tangentMass * -Cdot; b3Vec2 oldImpulse = vc->tangentImpulse; vc->tangentImpulse += impulse; - float32 maxImpulse = vc->friction * vc->normalImpulse; + scalar maxImpulse = vc->friction * vc->normalImpulse; if (b3Dot(vc->tangentImpulse, vc->tangentImpulse) > maxImpulse * maxImpulse) { vc->tangentImpulse.Normalize(); @@ -284,28 +176,22 @@ void b3SoftBodyContactSolver::SolveBodyContactVelocityConstraints() b3Vec3 P1 = impulse.x * vc->tangent1; b3Vec3 P2 = impulse.y * vc->tangent2; + b3Vec3 P = P1 + P2; - vA -= mA * P; - wA -= iA * b3Cross(rA, P); - - vB += mB * P; - wB += iB * b3Cross(rB, P); + vA += mA * P; } m_velocities[indexA] = vA; - - bodyB->SetLinearVelocity(vB); - bodyB->SetAngularVelocity(wB); } } void b3SoftBodyContactSolver::StoreImpulses() { - for (u32 i = 0; i < m_bodyContactCount; ++i) + for (u32 i = 0; i < m_shapeContactCount; ++i) { - b3NodeBodyContact* c = m_bodyContacts[i]; - b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; + b3SoftBodySphereAndShapeContact* c = m_shapeContacts[i]; + b3SoftBodySolverShapeContactVelocityConstraint* vc = m_shapeVelocityConstraints + i; c->m_normalImpulse = vc->normalImpulse; c->m_tangentImpulse = vc->tangentImpulse; @@ -314,100 +200,56 @@ void b3SoftBodyContactSolver::StoreImpulses() struct b3SoftBodySolverBodyContactSolverPoint { - void Initialize(const b3SoftBodySolverBodyContactPositionConstraint* pc, const b3Transform& xfA, const b3Transform& xfB) + void Initialize(const b3SoftBodySolverShapeContactPositionConstraint* pc, const b3Vec3& cA) { - b3Vec3 nA = pc->normalA; + b3Vec3 nB = pc->normalB; + b3Vec3 cB = pc->pointB; - b3Vec3 cA = xfA * pc->localPointA; - b3Vec3 cB = xfB * pc->localPointB; + scalar rA = pc->radiusA; + scalar rB = pc->radiusB; - float32 rA = pc->radiusA; - float32 rB = pc->radiusB; - - b3Vec3 pA = cA + rA * nA; - b3Vec3 pB = cB - rB * nA; - - point = cB; - normal = nA; - separation = b3Dot(cB - cA, nA) - rA - rB; + normal = nB; + separation = b3Dot(cA - cB, nB) - rA - rB; } b3Vec3 normal; - b3Vec3 point; - float32 separation; + scalar separation; }; -bool b3SoftBodyContactSolver::SolveBodyContactPositionConstraints() +bool b3SoftBodyContactSolver::SolveShapeContactPositionConstraints() { - float32 minSeparation = 0.0f; + scalar minSeparation = scalar(0); - for (u32 i = 0; i < m_bodyContactCount; ++i) + for (u32 i = 0; i < m_shapeContactCount; ++i) { - b3SoftBodySolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; + b3SoftBodySolverShapeContactPositionConstraint* pc = m_shapePositionConstraints + i; u32 indexA = pc->indexA; - float32 mA = pc->invMassA; - b3Mat33 iA = pc->invIA; - b3Vec3 localCenterA = pc->localCenterA; - - b3Body* bodyB = pc->bodyB; - float32 mB = pc->invMassB; - b3Mat33 iB = pc->invIB; - b3Vec3 localCenterB = pc->localCenterB; - + scalar mA = pc->invMassA; b3Vec3 cA = m_positions[indexA]; - b3Quat qA; qA.SetIdentity(); - - b3Vec3 cB = bodyB->m_sweep.worldCenter; - b3Quat qB = bodyB->m_sweep.orientation; // Solve normal constraint - b3Transform xfA; - xfA.rotation = b3QuatMat33(qA); - xfA.position = cA - b3Mul(xfA.rotation, localCenterA); - - b3Transform xfB; - xfB.rotation = b3QuatMat33(qB); - xfB.position = cB - b3Mul(xfB.rotation, localCenterB); - b3SoftBodySolverBodyContactSolverPoint cpcp; - cpcp.Initialize(pc, xfA, xfB); + cpcp.Initialize(pc, cA); b3Vec3 normal = cpcp.normal; - b3Vec3 point = cpcp.point; - float32 separation = cpcp.separation; + scalar separation = cpcp.separation; // Update max constraint error. minSeparation = b3Min(minSeparation, separation); // Allow some slop and prevent large corrections. - float32 C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, 0.0f); - - // Compute effective mass. - b3Vec3 rA = point - cA; - b3Vec3 rB = point - cB; - - b3Vec3 rnA = b3Cross(rA, normal); - b3Vec3 rnB = b3Cross(rB, normal); - float32 K = mA + mB + b3Dot(rnA, iA * rnA) + b3Dot(rnB, iB * rnB); + scalar C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, scalar(0)); // Compute normal impulse. - float32 impulse = K > 0.0f ? -C / K : 0.0f; + scalar impulse = mA > scalar(0) ? -C / mA : scalar(0); + b3Vec3 P = impulse * normal; - cA -= mA * P; - qA -= b3Derivative(qA, iA * b3Cross(rA, P)); - qA.Normalize(); - - cB += mB * P; - qB += b3Derivative(qB, iB * b3Cross(rB, P)); - qB.Normalize(); + cA += mA * P; m_positions[indexA] = cA; - - bodyB->m_sweep.worldCenter = cB; - bodyB->m_sweep.orientation = qB; } - return minSeparation >= -3.0f * B3_LINEAR_SLOP; + return minSeparation >= scalar(-3) * B3_LINEAR_SLOP; } \ No newline at end of file diff --git a/src/bounce/softbody/contacts/softbody_node_body_contact.cpp b/src/bounce/softbody/contacts/softbody_sphere_shape_contact.cpp similarity index 59% rename from src/bounce/softbody/contacts/softbody_node_body_contact.cpp rename to src/bounce/softbody/contacts/softbody_sphere_shape_contact.cpp index 483ae3a..7f08400 100644 --- a/src/bounce/softbody/contacts/softbody_node_body_contact.cpp +++ b/src/bounce/softbody/contacts/softbody_sphere_shape_contact.cpp @@ -16,19 +16,21 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include +#include +#include +#include #include #include #include -void b3NodeBodyContact::Update() +void b3SoftBodySphereAndShapeContact::Update() { b3Sphere sphere; - sphere.radius = m_n1->m_radius; - sphere.vertex = m_n1->m_position; + sphere.radius = m_s1->m_radius; + sphere.vertex = m_s1->m_node->m_position; - b3Shape* shape = m_s2; - b3Body* body = shape->GetBody(); + const b3Shape* shape = m_s2->m_shape; + const b3Body* body = shape->GetBody(); b3Transform xf = body->GetTransform(); b3TestSphereOutput out; @@ -39,24 +41,21 @@ void b3NodeBodyContact::Update() } m_active = true; - m_normal1 = -out.normal; - m_localPoint1.SetZero(); - m_localPoint2 = body->GetLocalPoint(out.point); - m_tangent1 = b3Perp(m_normal1); - m_tangent2 = b3Cross(m_tangent1, m_normal1); + m_normal2 = out.normal; + m_point2 = out.point; + m_tangent1 = b3Perp(m_normal2); + m_tangent2 = b3Cross(m_tangent1, m_normal2); } -void b3NodeBodyContactWorldPoint::Initialize(const b3NodeBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) +void b3SoftBodySphereAndShapeContactWorldPoint::Initialize(const b3SoftBodySphereAndShapeContact* c, scalar rA, const b3Vec3& cA, scalar rB) { - b3Vec3 nA = c->m_normal1; + b3Vec3 cB = c->m_point2; + b3Vec3 nB = c->m_normal2; - b3Vec3 cA = xfA * c->m_localPoint1; - b3Vec3 cB = xfB * c->m_localPoint2; + b3Vec3 pA = cA - rA * nB; + b3Vec3 pB = cB + rB * nB; - b3Vec3 pA = cA + rA * nA; - b3Vec3 pB = cB - rB * nA; - - point = 0.5f * (pA + pB); - normal = nA; - separation = b3Dot(cB - cA, nA) - rA - rB; + point = scalar(0.5) * (pA + pB); + normal = nB; + separation = b3Dot(cA - cB, nB) - rA - rB; } diff --git a/src/bounce/softbody/joints/softbody_anchor.cpp b/src/bounce/softbody/joints/softbody_anchor.cpp new file mode 100644 index 0000000..918995e --- /dev/null +++ b/src/bounce/softbody/joints/softbody_anchor.cpp @@ -0,0 +1,128 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include + +void b3SoftBodyAnchorDef::Initialize(b3Body* bA, b3SoftBodyNode* nB, const b3Vec3& anchor) +{ + bodyA = bA; + nodeB = nB; + + localAnchorA = bodyA->GetLocalPoint(anchor); + localAnchorB = anchor - nodeB->GetPosition(); +} + +b3SoftBodyAnchor::b3SoftBodyAnchor(const b3SoftBodyAnchorDef& def) +{ + m_bodyA = def.bodyA; + m_nodeB = def.nodeB; + m_localAnchorA = def.localAnchorA; + m_localAnchorB = def.localAnchorB; + m_impulse.SetZero(); +} + +b3Vec3 b3SoftBodyAnchor::GetAnchorA() const +{ + return m_bodyA->GetWorldPoint(m_localAnchorA); +} + +b3Vec3 b3SoftBodyAnchor::GetAnchorB() const +{ + return m_localAnchorB + m_nodeB->GetPosition(); +} + +void b3SoftBodyAnchor::InitializeConstraints(const b3SoftBodySolverData* data) +{ + m_mA = m_bodyA->GetInverseMass(); + m_iA = m_bodyA->GetWorldInverseInertia(); + + m_indexB = m_nodeB->m_meshIndex; + m_mB = m_nodeB->m_invMass; + + b3Vec3 xA = m_bodyA->GetPosition(); + b3Quat qA = m_bodyA->GetOrientation(); + + b3Vec3 xB = data->positions[m_indexB]; + + b3Vec3 localCenterA = m_bodyA->GetSweep().localCenter; + + // Compute effective mass for the block solver + m_rA = b3Mul(qA, m_localAnchorA - localCenterA); + b3Vec3 rB = m_localAnchorB; + + b3Mat33 M = b3Diagonal(m_mA + m_mB); + b3Mat33 RA = b3Skew(m_rA); + b3Mat33 RAT = b3Transpose(RA); + + m_mass = M + RA * m_iA * RAT; + + b3Vec3 C = xB + rB - xA - m_rA; + + m_velocityBias = -data->step.inv_dt * B3_BAUMGARTE * C; +} + +void b3SoftBodyAnchor::WarmStart(const b3SoftBodySolverData* data) +{ + b3Vec3 vA = m_bodyA->GetLinearVelocity(); + b3Vec3 wA = m_bodyA->GetAngularVelocity(); + + b3Vec3 vB = data->velocities[m_indexB]; + + vA -= m_mA * m_impulse; + wA -= m_iA * b3Cross(m_rA, m_impulse); + + vB += m_mB * m_impulse; + + m_bodyA->SetLinearVelocity(vA); + m_bodyA->SetAngularVelocity(wA); + + data->velocities[m_indexB] = vB; +} + +void b3SoftBodyAnchor::SolveVelocityConstraints(const b3SoftBodySolverData* data) +{ + b3Vec3 vA = m_bodyA->GetLinearVelocity(); + b3Vec3 wA = m_bodyA->GetAngularVelocity(); + + b3Vec3 vB = data->velocities[m_indexB]; + + b3Vec3 Cdot = vB - vA - b3Cross(wA, m_rA); + b3Vec3 impulse = m_mass.Solve(-Cdot + m_velocityBias); + + m_impulse += impulse; + + vA -= m_mA * impulse; + wA -= m_iA * b3Cross(m_rA, impulse); + + vB += m_mB * impulse; + + m_bodyA->SetLinearVelocity(vA); + m_bodyA->SetAngularVelocity(wA); + + data->velocities[m_indexB] = vB; +} + +bool b3SoftBodyAnchor::SolvePositionConstraints(const b3SoftBodySolverData* data) +{ + B3_NOT_USED(data); + return true; +} \ No newline at end of file diff --git a/src/bounce/softbody/shapes/softbody_sphere_shape.cpp b/src/bounce/softbody/shapes/softbody_sphere_shape.cpp new file mode 100644 index 0000000..a4c5ecd --- /dev/null +++ b/src/bounce/softbody/shapes/softbody_sphere_shape.cpp @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include + +b3AABB b3SoftBodySphereShape::ComputeAABB() const +{ + b3AABB aabb; + aabb.Set(m_node->GetPosition(), m_radius); + return aabb; +} + +void b3SoftBodySphereShape::Synchronize(const b3Vec3& displacement) +{ + b3AABB aabb = ComputeAABB(); + m_body->m_contactManager.m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); +} + +void b3SoftBodySphereShape::DestroyContacts() +{ + b3SoftBodySphereAndShapeContact* c = m_body->m_contactManager.m_sphereAndShapeContactList.m_head; + while (c) + { + if (c->m_s1 == this) + { + b3SoftBodySphereAndShapeContact* quack = c; + c = c->m_next; + m_body->m_contactManager.Destroy(quack); + continue; + } + + c = c->m_next; + } +} \ No newline at end of file diff --git a/src/bounce/softbody/shapes/softbody_world_shape.cpp b/src/bounce/softbody/shapes/softbody_world_shape.cpp new file mode 100644 index 0000000..337e0b3 --- /dev/null +++ b/src/bounce/softbody/shapes/softbody_world_shape.cpp @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include + +b3AABB b3SoftBodyWorldShape::ComputeAABB() const +{ + const b3Body* body = m_shape->GetBody(); + b3Transform xf = body->GetTransform(); + + b3AABB aabb; + m_shape->ComputeAABB(&aabb, xf); + + return aabb; +} + +void b3SoftBodyWorldShape::Synchronize(const b3Vec3& displacement) +{ + b3AABB aabb = ComputeAABB(); + m_body->m_contactManager.m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); +} + +void b3SoftBodyWorldShape::DestroyContacts() +{ + b3SoftBodySphereAndShapeContact* c = m_body->m_contactManager.m_sphereAndShapeContactList.m_head; + while (c) + { + b3SoftBodySphereAndShapeContact* c0 = c; + c = c->m_next; + + if (c0->m_s2 == this) + { + m_body->m_contactManager.Destroy(c0); + } + } +} \ No newline at end of file diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 1571dc3..6982b42 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -19,440 +19,33 @@ #include #include #include +#include #include +#include +#include +#include +#include #include +#include #include -// C = A * B -static B3_FORCE_INLINE void b3Mul(float32* C, float32* A, u32 AM, u32 AN, float32* B, u32 BM, u32 BN) -{ - B3_ASSERT(AN == BM); - - for (u32 i = 0; i < AM; ++i) - { - for (u32 j = 0; j < BN; ++j) - { - C[i + AM * j] = 0.0f; - - for (u32 k = 0; k < AN; ++k) - { - C[i + AM * j] += A[i + AM * k] * B[k + BM * j]; - } - } - } -} - -// B = A^T -static B3_FORCE_INLINE void b3Transpose(float32* B, float32* A, u32 AM, u32 AN) -{ - for (u32 i = 0; i < AM; ++i) - { - for (u32 j = 0; j < AN; ++j) - { - B[j + AN * i] = A[i + AM * j]; - } - } -} - -// Compute the elasticity matrix given Young modulus and Poisson's ratio -// This is a 6 x 6 matrix -static B3_FORCE_INLINE void b3ComputeD(float32 out[36], - float32 E, float32 nu) -{ - float32 lambda = (nu * E) / ((1 + nu) * (1 - 2 * nu)); - float32 mu = E / (2 * (1 + nu)); - - float32 D[36] = - { - lambda + 2 * mu, lambda, lambda, 0, 0, 0, - lambda, lambda + 2 * mu, lambda, 0, 0, 0, - lambda, lambda, lambda + 2 * mu, 0, 0, 0, - 0, 0, 0, mu, 0, 0, - 0, 0, 0, 0, mu, 0, - 0, 0, 0, 0, 0, mu - }; - - for (u32 i = 0; i < 36; ++i) - { - out[i] = D[i]; - } -} - -// Compute B = S * N, -// where S is the operational matrix and N are the shape functions -// This is a 6 x 12 matrix -// A derivation and corresponding simplification for this matrix -// can be found here: -// https://github.com/erleben/OpenTissue/blob/master/OpenTissue/dynamics/fem/fem_compute_b.h -static B3_FORCE_INLINE void b3ComputeB(float32 out[72], - const b3Mat33& invE) -{ - // cofactor = det(E)^-1 * cofactor(E) - b3Mat33 cofactor = b3Transpose(invE); - - // minor = det(E)^-1 * minor(E) - b3Mat33 minor; - - minor.x.x = cofactor.x.x; - minor.x.y = -cofactor.x.y; - minor.x.z = cofactor.x.z; - - minor.y.x = -cofactor.y.x; - minor.y.y = cofactor.y.y; - minor.y.z = -cofactor.y.z; - - minor.z.x = cofactor.z.x; - minor.z.y = -cofactor.z.y; - minor.z.z = cofactor.z.z; - - float32 e11 = -minor.x.x; // -det(E)^-1 * det(E_11) - float32 e12 = minor.y.x; // det(E)^-1 * det(E_12) - float32 e13 = -minor.z.x; // -det(E)^-1 * det(E_13) - - float32 e21 = minor.x.y; // det(E)^-1 * det(E_21) - float32 e22 = -minor.y.y; // -det(E)^-1 * det(E_22) - float32 e23 = minor.z.y; // det(E)^-1 * det(E_23) - - float32 e31 = -minor.x.z; // -det(E)^-1 * det(E_31) - float32 e32 = minor.y.z; // det(E)^-1 * det(E_32) - float32 e33 = -minor.z.z; // -det(E)^-1 * det(E_33) - - float32 b1 = -e11 - e12 - e13; - float32 c1 = -e21 - e22 - e23; - float32 d1 = -e31 - e32 - e33; - - float32 b2 = e11; - float32 c2 = e21; - float32 d2 = e31; - - float32 b3 = e12; - float32 c3 = e22; - float32 d3 = e32; - - float32 b4 = e13; - float32 c4 = e23; - float32 d4 = e33; - - float32 B[72] = - { - b1, 0, 0, c1, d1, 0, - 0, c1, 0, b1, 0, d1, - 0, 0, d1, 0, b1, c1, - - b2, 0, 0, c2, d2, 0, - 0, c2, 0, b2, 0, d2, - 0, 0, d2, 0, b2, c2, - - b3, 0, 0, c3, d3, 0, - 0, c3, 0, b3, 0, d3, - 0, 0, d3, 0, b3, c3, - - b4, 0, 0, c4, d4, 0, - 0, c4, 0, b4, 0, d4, - 0, 0, d4, 0, b4, c4, - }; - - for (u32 i = 0; i < 72; ++i) - { - out[i] = B[i]; - } -} - -static B3_FORCE_INLINE void b3SetK(b3Mat33 K[16], float32 Ke[144]) -{ - b3Mat33& k11 = K[0 + 4 * 0]; - b3Mat33& k12 = K[0 + 4 * 1]; - b3Mat33& k13 = K[0 + 4 * 2]; - b3Mat33& k14 = K[0 + 4 * 3]; - - b3Mat33& k21 = K[1 + 4 * 0]; - b3Mat33& k22 = K[1 + 4 * 1]; - b3Mat33& k23 = K[1 + 4 * 2]; - b3Mat33& k24 = K[1 + 4 * 3]; - - b3Mat33& k31 = K[2 + 4 * 0]; - b3Mat33& k32 = K[2 + 4 * 1]; - b3Mat33& k33 = K[2 + 4 * 2]; - b3Mat33& k34 = K[2 + 4 * 3]; - - b3Mat33& k41 = K[3 + 4 * 0]; - b3Mat33& k42 = K[3 + 4 * 1]; - b3Mat33& k43 = K[3 + 4 * 2]; - b3Mat33& k44 = K[3 + 4 * 3]; - - // k11 - // a11 a12 a13 - // a21 a22 a23 - // a31 a32 a33 - k11.x.x = Ke[0 + 12 * 0]; - k11.x.y = Ke[1 + 12 * 0]; - k11.x.z = Ke[2 + 12 * 0]; - - k11.y.x = Ke[0 + 12 * 1]; - k11.y.y = Ke[1 + 12 * 1]; - k11.y.z = Ke[2 + 12 * 1]; - - k11.z.x = Ke[0 + 12 * 2]; - k11.z.y = Ke[1 + 12 * 2]; - k11.z.z = Ke[2 + 12 * 2]; - - // k12 - // a14 a15 a16 - // a24 a25 a26 - // a34 a35 a36 - k12.x.x = Ke[0 + 12 * 3]; - k12.x.y = Ke[1 + 12 * 3]; - k12.x.z = Ke[2 + 12 * 3]; - - k12.y.x = Ke[0 + 12 * 4]; - k12.y.y = Ke[1 + 12 * 4]; - k12.y.z = Ke[2 + 12 * 4]; - - k12.z.x = Ke[0 + 12 * 5]; - k12.z.y = Ke[1 + 12 * 5]; - k12.z.z = Ke[2 + 12 * 5]; - - // k13 - // a17 a18 a19 - // a27 a28 a29 - // a37 a38 a39 - k13.x.x = Ke[0 + 12 * 6]; - k13.x.y = Ke[1 + 12 * 6]; - k13.x.z = Ke[2 + 12 * 6]; - - k13.y.x = Ke[0 + 12 * 7]; - k13.y.y = Ke[1 + 12 * 7]; - k13.y.z = Ke[2 + 12 * 7]; - - k13.z.x = Ke[0 + 12 * 8]; - k13.z.y = Ke[1 + 12 * 8]; - k13.z.z = Ke[2 + 12 * 8]; - - // k14 - // a1_10 a1_11 a1_12 - // a2_10 a2_11 a2_12 - // a3_10 a3_11 a3_12 - k14.x.x = Ke[0 + 12 * 9]; - k14.x.y = Ke[1 + 12 * 9]; - k14.x.z = Ke[2 + 12 * 9]; - - k14.y.x = Ke[0 + 12 * 10]; - k14.y.y = Ke[1 + 12 * 10]; - k14.y.z = Ke[2 + 12 * 10]; - - k14.z.x = Ke[0 + 12 * 11]; - k14.z.y = Ke[1 + 12 * 11]; - k14.z.z = Ke[2 + 12 * 11]; - - // k21 - // a41 a42 a43 - // a51 a52 a53 - // a61 a62 a63 - //k21.x.x = Ke[3 + 12 * 0]; - //k21.x.y = Ke[4 + 12 * 0]; - //k21.x.z = Ke[5 + 12 * 0]; - - //k21.y.x = Ke[3 + 12 * 1]; - //k21.y.y = Ke[4 + 12 * 1]; - //k21.y.z = Ke[5 + 12 * 1]; - - //k21.z.x = Ke[3 + 12 * 2]; - //k21.z.y = Ke[4 + 12 * 2]; - //k21.z.z = Ke[5 + 12 * 2]; - k21 = b3Transpose(k12); - - // k22 - // a44 a45 a46 - // a54 a55 a56 - // a64 a65 a66 - k22.x.x = Ke[3 + 12 * 3]; - k22.x.y = Ke[4 + 12 * 3]; - k22.x.z = Ke[5 + 12 * 3]; - - k22.y.x = Ke[3 + 12 * 4]; - k22.y.y = Ke[4 + 12 * 4]; - k22.y.z = Ke[5 + 12 * 4]; - - k22.z.x = Ke[3 + 12 * 5]; - k22.z.y = Ke[4 + 12 * 5]; - k22.z.z = Ke[5 + 12 * 5]; - - // k23 - // a47 a48 a49 - // a57 a58 a59 - // a67 a68 a69 - k23.x.x = Ke[3 + 12 * 6]; - k23.x.y = Ke[4 + 12 * 6]; - k23.x.z = Ke[5 + 12 * 6]; - - k23.y.x = Ke[3 + 12 * 7]; - k23.y.y = Ke[4 + 12 * 7]; - k23.y.z = Ke[5 + 12 * 7]; - - k23.z.x = Ke[3 + 12 * 8]; - k23.z.y = Ke[4 + 12 * 8]; - k23.z.z = Ke[5 + 12 * 8]; - - // k24 - // a4_10 a4_11 a4_12 - // a5_10 a5_11 a5_12 - // a6_10 a6_11 a6_12 - k24.x.x = Ke[3 + 12 * 9]; - k24.x.y = Ke[4 + 12 * 9]; - k24.x.z = Ke[5 + 12 * 9]; - - k24.y.x = Ke[3 + 12 * 10]; - k24.y.y = Ke[4 + 12 * 10]; - k24.y.z = Ke[5 + 12 * 10]; - - k24.z.x = Ke[3 + 12 * 11]; - k24.z.y = Ke[4 + 12 * 11]; - k24.z.z = Ke[5 + 12 * 11]; - - // k31 - // a71 a72 a73 - // a81 a82 a83 - // a91 a92 a93 - //k31.x.x = Ke[6 + 12 * 0]; - //k31.x.y = Ke[7 + 12 * 0]; - //k31.x.z = Ke[8 + 12 * 0]; - - //k31.y.x = Ke[6 + 12 * 1]; - //k31.y.y = Ke[7 + 12 * 1]; - //k31.y.z = Ke[8 + 12 * 1]; - - //k31.z.x = Ke[6 + 12 * 2]; - //k31.z.y = Ke[7 + 12 * 2]; - //k31.z.z = Ke[8 + 12 * 2]; - k31 = b3Transpose(k13); - - // k32 - // a74 a75 a76 - // a84 a85 a86 - // a94 a95 a96 - //k32.x.x = Ke[6 + 12 * 3]; - //k32.x.y = Ke[7 + 12 * 3]; - //k32.x.z = Ke[8 + 12 * 3]; - - //k32.y.x = Ke[6 + 12 * 4]; - //k32.y.y = Ke[7 + 12 * 4]; - //k32.y.z = Ke[8 + 12 * 4]; - - //k32.z.x = Ke[6 + 12 * 5]; - //k32.z.y = Ke[7 + 12 * 5]; - //k32.z.z = Ke[8 + 12 * 5]; - k32 = b3Transpose(k23); - - // k33 - // a77 a78 a79 - // a87 a88 a89 - // a97 a98 a99 - k33.x.x = Ke[6 + 12 * 6]; - k33.x.y = Ke[7 + 12 * 6]; - k33.x.z = Ke[8 + 12 * 6]; - - k33.y.x = Ke[6 + 12 * 7]; - k33.y.y = Ke[7 + 12 * 7]; - k33.y.z = Ke[8 + 12 * 7]; - - k33.z.x = Ke[6 + 12 * 8]; - k33.z.y = Ke[7 + 12 * 8]; - k33.z.z = Ke[8 + 12 * 8]; - - // k34 - // a7_10 a7_11 a7_12 - // a8_10 a8_11 a8_12 - // a9_10 a9_11 a9_12 - k34.x.x = Ke[6 + 12 * 9]; - k34.x.y = Ke[7 + 12 * 9]; - k34.x.z = Ke[8 + 12 * 9]; - - k34.y.x = Ke[6 + 12 * 10]; - k34.y.y = Ke[7 + 12 * 10]; - k34.y.z = Ke[8 + 12 * 10]; - - k34.z.x = Ke[6 + 12 * 11]; - k34.z.y = Ke[7 + 12 * 11]; - k34.z.z = Ke[8 + 12 * 11]; - - // k41 - // a10_1 a10_2 a10_3 - // a11_1 a11_2 a11_3 - // a12_1 a12_2 a12_3 - //k41.x.x = Ke[9 + 12 * 0]; - //k41.x.y = Ke[10 + 12 * 0]; - //k41.x.z = Ke[11 + 12 * 0]; - - //k41.y.x = Ke[9 + 12 * 1]; - //k41.y.y = Ke[10 + 12 * 1]; - //k41.y.z = Ke[11 + 12 * 1]; - - //k41.z.x = Ke[9 + 12 * 2]; - //k41.z.y = Ke[10 + 12 * 2]; - //k41.z.z = Ke[11 + 12 * 2]; - k41 = b3Transpose(k14); - - // k42 - // a10_4 a10_5 a10_6 - // a11_4 a11_5 a11_6 - // a12_4 a12_5 a12_6 - //k42.x.x = Ke[9 + 12 * 3]; - //k42.x.y = Ke[10 + 12 * 3]; - //k42.x.z = Ke[11 + 12 * 3]; - - //k42.y.x = Ke[9 + 12 * 4]; - //k42.y.y = Ke[10 + 12 * 4]; - //k42.y.z = Ke[11 + 12 * 4]; - - //k42.z.x = Ke[9 + 12 * 5]; - //k42.z.y = Ke[10 + 12 * 5]; - //k42.z.z = Ke[11 + 12 * 5]; - k42 = b3Transpose(k24); - - // k43 - // a10_7 a10_8 a10_9 - // a11_7 a11_8 a11_9 - // a12_7 a12_8 a12_9 - //k43.x.x = Ke[9 + 12 * 6]; - //k43.x.y = Ke[10 + 12 * 6]; - //k43.x.z = Ke[11 + 12 * 6]; - - //k43.y.x = Ke[9 + 12 * 7]; - //k43.y.y = Ke[10 + 12 * 7]; - //k43.y.z = Ke[11 + 12 * 7]; - - //k43.z.x = Ke[9 + 12 * 8]; - //k43.z.y = Ke[10 + 12 * 8]; - //k43.z.z = Ke[11 + 12 * 8]; - k43 = b3Transpose(k34); - - // k44 - // a10_10 a10_11 a10_12 - // a11_10 a11_11 a11_12 - // a12_10 a12_11 a12_12 - k44.x.x = Ke[9 + 12 * 9]; - k44.x.y = Ke[10 + 12 * 9]; - k44.x.z = Ke[11 + 12 * 9]; - - k44.y.x = Ke[9 + 12 * 10]; - k44.y.y = Ke[10 + 12 * 10]; - k44.y.z = Ke[11 + 12 * 10]; - - k44.z.x = Ke[9 + 12 * 11]; - k44.z.y = Ke[10 + 12 * 11]; - k44.z.z = Ke[11 + 12 * 11]; -} - -b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) +b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) : + m_sphereShapeBlocks(sizeof(b3SoftBodySphereShape)), + m_worldShapeBlocks(sizeof(b3SoftBodyWorldShape)) { B3_ASSERT(def.mesh); - B3_ASSERT(def.density > 0.0f); + B3_ASSERT(scalar(0) < def.density); + B3_ASSERT(scalar(0) <= def.massDamping); + B3_ASSERT(scalar(0) <= def.stiffnessDamping); m_mesh = def.mesh; m_density = def.density; + m_massDamping = def.massDamping; + m_stiffnessDamping = def.stiffnessDamping; + m_gravity.SetZero(); - m_world = nullptr; m_contactManager.m_body = this; + m_inv_dt0 = scalar(0); const b3SoftBodyMesh* m = m_mesh; @@ -460,56 +53,73 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) m_nodes = (b3SoftBodyNode*)b3Alloc(m->vertexCount * sizeof(b3SoftBodyNode)); for (u32 i = 0; i < m->vertexCount; ++i) { - b3SoftBodyNode* n = m_nodes + i; + b3SoftBodyNode* n = new (m_nodes + i) b3SoftBodyNode(); n->m_body = this; n->m_type = e_dynamicSoftBodyNode; n->m_position = m->vertices[i]; n->m_velocity.SetZero(); n->m_force.SetZero(); - n->m_mass = 0.0f; - n->m_invMass = 0.0f; - n->m_massDamping = 0.0f; - n->m_radius = 0.0f; - n->m_friction = 0.0f; - n->m_userData = nullptr; - n->m_vertex = i; + n->m_translation.SetZero(); + n->m_mass = scalar(0); + n->m_invMass = scalar(0); + n->m_meshIndex = i; - b3AABB3 aabb; - aabb.Set(n->m_position, 0.0f); - - n->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, n); + b3SoftBodySphereShapeDef sd; + sd.node = n; + sd.radius = def.radius; + sd.density = def.density; + sd.friction = def.friction; + + CreateSphereShape(sd); } // Compute mass ComputeMass(); // Initialize elements + m_KP = new (b3Alloc(sizeof(b3SparseMat33Pattern))) b3SparseMat33Pattern(m->vertexCount); m_elements = (b3SoftBodyElement*)b3Alloc(m->tetrahedronCount * sizeof(b3SoftBodyElement)); for (u32 ei = 0; ei < m->tetrahedronCount; ++ei) { b3SoftBodyMeshTetrahedron* mt = m->tetrahedrons + ei; - b3SoftBodyElement* e = m_elements + ei; - - e->E = def.E; - e->nu = def.nu; - e->c_yield = def.c_yield; - e->c_creep = def.c_creep; - e->c_max = def.c_max; + b3SoftBodyElement* e = new (m_elements + ei) b3SoftBodyElement(); + + e->m_body = this; + e->m_q.SetIdentity(); + e->m_E = def.E; + e->m_nu = def.nu; + e->m_c_yield = def.c_yield; + e->m_c_creep = def.c_creep; + e->m_c_max = def.c_max; u32 v1 = mt->v1; u32 v2 = mt->v2; u32 v3 = mt->v3; u32 v4 = mt->v4; + u32 vs[4] = { v1, v2, v3, v4 }; + + for (u32 i = 0; i < 4; ++i) + { + u32 vi = vs[i]; + + for (u32 j = 0; j < 4; ++j) + { + u32 vj = vs[j]; + + e->m_Kp[i + 4 * j] = m_KP->CreateElement(vi, vj); + } + } + b3Vec3 p1 = m->vertices[v1]; b3Vec3 p2 = m->vertices[v2]; b3Vec3 p3 = m->vertices[v3]; b3Vec3 p4 = m->vertices[v4]; - float32 V = b3Volume(p1, p2, p3, p4); + e->m_V = b3Volume(p1, p2, p3, p4); - B3_ASSERT(V > 0.0f); + B3_ASSERT(e->m_V > scalar(0)); b3Vec3 e1 = p2 - p1; b3Vec3 e2 = p3 - p1; @@ -517,91 +127,153 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) b3Mat33 E(e1, e2, e3); - e->invE = b3Inverse(E); - - // 6 x 6 - float32 D[36]; - b3ComputeD(D, e->E, e->nu); - - // 6 x 12 - float32* B = e->B; - b3ComputeB(B, e->invE); - - // 12 x 6 - float32 BT[72]; - b3Transpose(BT, B, 6, 12); - - // 12 x 6 - float32 BT_D[72]; - b3Mul(BT_D, BT, 12, 6, D, 6, 6); - - // 12 x 12 - float32 BT_D_B[144]; - b3Mul(BT_D_B, BT_D, 12, 6, B, 6, 12); - for (u32 i = 0; i < 144; ++i) - { - BT_D_B[i] *= V; - } - - b3SetK(e->K, BT_D_B); - - // 12 x 6 - float32* P = e->P; - b3Mul(P, BT, 12, 6, D, 6, 6); - for (u32 i = 0; i < 72; ++i) - { - P[i] *= V; - } + e->m_invE = b3Inverse(E); for (u32 i = 0; i < 6; ++i) { - e->epsilon_plastic[i] = 0.0f; + e->m_epsilon_plastic[i] = scalar(0); } - } - // Initialize triangles - m_triangles = (b3SoftBodyTriangle*)b3Alloc(4 * m_mesh->tetrahedronCount * sizeof(b3SoftBodyTriangle)); - for (u32 i = 0; i < m_mesh->tetrahedronCount; ++i) - { - b3SoftBodyMeshTetrahedron* mt = m_mesh->tetrahedrons + i; - - u32 v1 = mt->v1; - u32 v2 = mt->v2; - u32 v3 = mt->v3; - u32 v4 = mt->v4; - - b3SoftBodyTriangle* t1 = m_triangles + 4 * i + 0; - b3SoftBodyTriangle* t2 = m_triangles + 4 * i + 1; - b3SoftBodyTriangle* t3 = m_triangles + 4 * i + 2; - b3SoftBodyTriangle* t4 = m_triangles + 4 * i + 3; - - t1->v1 = v1; - t1->v2 = v2; - t1->v3 = v3; - t1->tetrahedron = i; - - t2->v1 = v1; - t2->v2 = v3; - t2->v3 = v4; - t2->tetrahedron = i; - - t3->v1 = v1; - t3->v2 = v4; - t3->v3 = v2; - t3->tetrahedron = i; - - t4->v1 = v2; - t4->v2 = v4; - t4->v3 = v3; - t4->tetrahedron = i; + e->ComputeMatrices(); } } b3SoftBody::~b3SoftBody() { + // Destroy nodes + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + m_nodes[i].~b3SoftBodyNode(); + } b3Free(m_nodes); + + // Destroy elements + for (u32 i = 0; i < m_mesh->tetrahedronCount; ++i) + { + m_elements[i].~b3SoftBodyElement(); + } b3Free(m_elements); - b3Free(m_triangles); + + // Destroy patterns + m_KP->~b3SparseMat33Pattern(); + b3Free(m_KP); + + // Destroy joints + b3SoftBodyAnchor* a = m_anchorList.m_head; + while (a) + { + b3SoftBodyAnchor* boom = a; + a = a->m_next; + + boom->~b3SoftBodyAnchor(); + b3Free(boom); + } +} + +b3SoftBodySphereShape* b3SoftBody::CreateSphereShape(const b3SoftBodySphereShapeDef& def) +{ + // Does the shape exist already? + for (b3SoftBodySphereShape* s = m_sphereShapeList.m_head; s; s = s->m_next) + { + if (s->m_node == def.node) + { + return s; + } + } + + void* mem = m_sphereShapeBlocks.Allocate(); + b3SoftBodySphereShape* s = new (mem)b3SoftBodySphereShape(); + + s->m_type = e_softBodySphereShape; + s->m_radius = def.radius; + s->m_density = def.density; + s->m_friction = def.friction; + s->m_node = def.node; + s->m_body = this; + + // Create broadphase + b3AABB aabb = s->ComputeAABB(); + s->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, s); + + // Put into list + m_sphereShapeList.PushFront(s); + + return s; +} + +void b3SoftBody::DestroySphereShape(b3SoftBodySphereShape* shape) +{ + // Destroy contacts + shape->DestroyContacts(); + + // Remove from broadphase + m_contactManager.m_broadPhase.DestroyProxy(shape->m_broadPhaseId); + + // Remove from list + m_sphereShapeList.Remove(shape); + + // Free memory + shape->~b3SoftBodySphereShape(); + m_sphereShapeBlocks.Free(shape); +} + +b3SoftBodyWorldShape* b3SoftBody::CreateWorldShape(const b3SoftBodyWorldShapeDef& def) +{ + // Does the shape exist already? + for (b3SoftBodyWorldShape* s = m_worldShapeList.m_head; s; s = s->m_next) + { + if (s->m_shape == def.shape) + { + return s; + } + } + + void* mem = m_worldShapeBlocks.Allocate(); + b3SoftBodyWorldShape* s = new (mem)b3SoftBodyWorldShape(); + + s->m_type = e_softBodyWorldShape; + s->m_shape = def.shape; + s->m_body = this; + + // Create broadphase + b3AABB aabb = s->ComputeAABB(); + s->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, s); + + // Push to list + m_worldShapeList.PushFront(s); + + return s; +} + +void b3SoftBody::DestroyWorldShape(b3SoftBodyWorldShape* shape) +{ + // Destroy contacts + shape->DestroyContacts(); + + // Remove from broadphase + m_contactManager.m_broadPhase.DestroyProxy(shape->m_broadPhaseId); + + // Remove from list + m_worldShapeList.Remove(shape); + + // Free memory + shape->~b3SoftBodyWorldShape(); + m_worldShapeBlocks.Free(shape); +} + +b3SoftBodyAnchor* b3SoftBody::CreateAnchor(const b3SoftBodyAnchorDef& def) +{ + void* p = (b3SoftBodyAnchor*)b3Alloc(sizeof(b3SoftBodyAnchor)); + b3SoftBodyAnchor* a = new (p) b3SoftBodyAnchor(def); + m_anchorList.PushFront(a); + return a; +} + +void b3SoftBody::DestroyAnchor(b3SoftBodyAnchor* anchor) +{ + m_anchorList.Remove(anchor); + anchor->~b3SoftBodyAnchor(); + b3Free(anchor); } bool b3SoftBody::RayCastSingle(b3SoftBodyRayCastSingleOutput* output, const b3Vec3& p1, const b3Vec3& p2) const @@ -609,17 +281,15 @@ bool b3SoftBody::RayCastSingle(b3SoftBodyRayCastSingleOutput* output, const b3Ve b3RayCastInput input; input.p1 = p1; input.p2 = p2; - input.maxFraction = 1.0f; - - u32 triangle = ~0; - u32 tetrahedron = ~0; + input.maxFraction = scalar(1); + u32 triangle0 = u32(~0); b3RayCastOutput output0; - output0.fraction = B3_MAX_FLOAT; + output0.fraction = B3_MAX_SCALAR; - for (u32 i = 0; i < 4 * m_mesh->tetrahedronCount; ++i) + for (u32 i = 0; i < m_mesh->triangleCount; ++i) { - b3SoftBodyTriangle* t = m_triangles + i; + b3SoftBodyMeshTriangle* t = m_mesh->triangles + i; b3Vec3 v1 = m_nodes[t->v1].m_position; b3Vec3 v2 = m_nodes[t->v2].m_position; @@ -630,20 +300,16 @@ bool b3SoftBody::RayCastSingle(b3SoftBodyRayCastSingleOutput* output, const b3Ve { if (subOutput.fraction < output0.fraction) { - triangle = i; - tetrahedron = t->tetrahedron; + triangle0 = i; output0.fraction = subOutput.fraction; output0.normal = subOutput.normal; } } } - if (tetrahedron != ~0) + if (triangle0 != ~0) { - output->tetrahedron = tetrahedron; - output->v1 = m_triangles[triangle].v1; - output->v2 = m_triangles[triangle].v2; - output->v3 = m_triangles[triangle].v3; + output->triangle = triangle0; output->fraction = output0.fraction; output->normal = output0.normal; @@ -653,28 +319,28 @@ bool b3SoftBody::RayCastSingle(b3SoftBodyRayCastSingleOutput* output, const b3Ve return false; } -b3SoftBodyNode* b3SoftBody::GetVertexNode(u32 i) +b3SoftBodyNode* b3SoftBody::GetNode(u32 i) { B3_ASSERT(i < m_mesh->vertexCount); return m_nodes + i; } -b3SoftBodyElement* b3SoftBody::GetTetrahedronElement(u32 i) +b3SoftBodyElement* b3SoftBody::GetElement(u32 i) { B3_ASSERT(i < m_mesh->tetrahedronCount); return m_elements + i; } -float32 b3SoftBody::GetEnergy() const +scalar b3SoftBody::GetEnergy() const { - float32 E = 0.0f; + scalar E = scalar(0); for (u32 i = 0; i < m_mesh->vertexCount; ++i) { b3SoftBodyNode* n = m_nodes + i; E += n->m_mass * b3Dot(n->m_velocity, n->m_velocity); } - return 0.5f * E; + return scalar(0.5) * E; } void b3SoftBody::ComputeMass() @@ -682,12 +348,12 @@ void b3SoftBody::ComputeMass() for (u32 i = 0; i < m_mesh->vertexCount; ++i) { b3SoftBodyNode* n = m_nodes + i; - n->m_mass = 0.0f; - n->m_invMass = 0.0f; + n->m_mass = scalar(0); + n->m_invMass = scalar(0); } - const float32 inv4 = 1.0f / 4.0f; - const float32 rho = m_density; + const scalar inv4 = scalar(1) / scalar(4); + const scalar rho = m_density; for (u32 i = 0; i < m_mesh->tetrahedronCount; ++i) { @@ -698,10 +364,10 @@ void b3SoftBody::ComputeMass() b3Vec3 v3 = m_mesh->vertices[tetrahedron->v3]; b3Vec3 v4 = m_mesh->vertices[tetrahedron->v4]; - float32 volume = b3Volume(v1, v2, v3, v4); - B3_ASSERT(volume > 0.0f); + scalar volume = b3Volume(v1, v2, v3, v4); + B3_ASSERT(volume > scalar(0)); - float32 mass = rho * volume; + scalar mass = rho * volume; b3SoftBodyNode* n1 = m_nodes + tetrahedron->v1; b3SoftBodyNode* n2 = m_nodes + tetrahedron->v2; @@ -718,12 +384,12 @@ void b3SoftBody::ComputeMass() for (u32 i = 0; i < m_mesh->vertexCount; ++i) { b3SoftBodyNode* n = m_nodes + i; - B3_ASSERT(n->m_mass > 0.0f); - n->m_invMass = 1.0f / n->m_mass; + B3_ASSERT(n->m_mass > scalar(0)); + n->m_invMass = scalar(1) / n->m_mass; } } -void b3SoftBody::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) +void b3SoftBody::Solve(const b3SoftBodyTimeStep& step) { B3_PROFILE("Soft Body Solve"); @@ -732,8 +398,8 @@ void b3SoftBody::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations b3SoftBodySolver solver(def); - // Push the body contacts - for (b3NodeBodyContact* c = m_contactManager.m_nodeBodyContactList.m_head; c; c = c->m_next) + // Push the shape contacts + for (b3SoftBodySphereAndShapeContact* c = m_contactManager.m_sphereAndShapeContactList.m_head; c; c = c->m_next) { if (c->m_active) { @@ -741,62 +407,62 @@ void b3SoftBody::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations } } - solver.Solve(dt, gravity, velocityIterations, positionIterations); + // Push the anchors + for (b3SoftBodyAnchor* a = m_anchorList.m_head; a; a = a->m_next) + { + solver.Add(a); + } + + solver.Solve(step, m_gravity); } -void b3SoftBody::Step(float32 dt, u32 velocityIterations, u32 positionIterations) +void b3SoftBody::Step(scalar dt, u32 velocityIterations, u32 positionIterations) { B3_PROFILE("Soft Body Step"); // Update contacts - m_contactManager.UpdateBodyContacts(); + m_contactManager.UpdateContacts(); + + // Time step parameters + b3SoftBodyTimeStep step; + step.dt = dt; + step.velocityIterations = velocityIterations; + step.positionIterations = positionIterations; + step.inv_dt = dt > scalar(0) ? scalar(1) / dt : scalar(0); + step.dt_ratio = m_inv_dt0 * dt; // Integrate state, solve constraints. - if (dt > 0.0f) + if (step.dt > scalar(0)) { - Solve(dt, m_gravity, velocityIterations, positionIterations); + Solve(step); } - // Clear forces + if (step.dt > scalar(0)) + { + m_inv_dt0 = step.inv_dt; + } + + // Clear state buffers for (u32 i = 0; i < m_mesh->vertexCount; ++i) { m_nodes[i].m_force.SetZero(); + m_nodes[i].m_translation.SetZero(); } - - // Synchronize nodes - for (u32 i = 0; i < m_mesh->vertexCount; ++i) + + // Synchronize shapes + for (b3SoftBodyWorldShape* s = m_worldShapeList.m_head; s; s = s->m_next) { - b3SoftBodyNode* n = m_nodes + i; - - if (n->m_type == e_staticSoftBodyNode) - { - continue; - } - - b3Vec3 displacement = dt * n->m_velocity; - - n->Synchronize(displacement); + s->Synchronize(b3Vec3_zero); } + // Synchronize spheres + for (b3SoftBodySphereShape* s = m_sphereShapeList.m_head; s; s = s->m_next) + { + s->Synchronize(b3Vec3_zero); + } + // Find new contacts - m_contactManager.FindNewBodyContacts(); -} - -void b3SoftBody::SetWorld(b3World* world) -{ - if (!world && m_world) - { - // Destroy body contacts - b3NodeBodyContact* c = m_contactManager.m_nodeBodyContactList.m_head; - while (c) - { - b3NodeBodyContact* kaboom = c; - c = c->m_next; - m_contactManager.Destroy(kaboom); - } - } - - m_world = world; + m_contactManager.FindNewContacts(); } void b3SoftBody::Draw() const @@ -811,17 +477,17 @@ void b3SoftBody::Draw() const if (n->m_type == e_staticSoftBodyNode) { - b3Draw_draw->DrawPoint(v, 4.0f, b3Color_white); + b3Draw_draw->DrawPoint(v, scalar(4), b3Color_white); } if (n->m_type == e_kinematicSoftBodyNode) { - b3Draw_draw->DrawPoint(v, 4.0f, b3Color_blue); + b3Draw_draw->DrawPoint(v, scalar(4), b3Color_blue); } if (n->m_type == e_dynamicSoftBodyNode) { - b3Draw_draw->DrawPoint(v, 4.0f, b3Color_green); + b3Draw_draw->DrawPoint(v, scalar(4), b3Color_green); } } @@ -834,9 +500,9 @@ void b3SoftBody::Draw() const b3Vec3 v3 = m_nodes[t->v3].m_position; b3Vec3 v4 = m_nodes[t->v4].m_position; - b3Vec3 c = (v1 + v2 + v3 + v4) / 4.0f; + b3Vec3 c = (v1 + v2 + v3 + v4) / scalar(4); - float32 s = 0.9f; + scalar s(0.9); v1 = s * (v1 - c) + c; v2 = s * (v2 - c) + c; @@ -848,27 +514,66 @@ void b3SoftBody::Draw() const b3Vec3 n1 = b3Cross(v2 - v1, v3 - v1); n1.Normalize(); - b3Draw_draw->DrawSolidTriangle(-n1, v1, v2, v3, b3Color_blue); + b3Draw_draw->DrawSolidTriangle(n1, v1, v2, v3, b3Color_blue); // v1, v3, v4 b3Draw_draw->DrawTriangle(v1, v3, v4, b3Color_black); b3Vec3 n2 = b3Cross(v3 - v1, v4 - v1); n2.Normalize(); - b3Draw_draw->DrawSolidTriangle(-n2, v1, v3, v4, b3Color_blue); + b3Draw_draw->DrawSolidTriangle(n2, v1, v3, v4, b3Color_blue); // v1, v4, v2 b3Draw_draw->DrawTriangle(v1, v4, v2, b3Color_black); b3Vec3 n3 = b3Cross(v4 - v1, v2 - v1); n3.Normalize(); - b3Draw_draw->DrawSolidTriangle(-n3, v1, v4, v2, b3Color_blue); + b3Draw_draw->DrawSolidTriangle(n3, v1, v4, v2, b3Color_blue); // v2, v4, v3 b3Draw_draw->DrawTriangle(v2, v4, v3, b3Color_black); b3Vec3 n4 = b3Cross(v4 - v2, v3 - v2); n4.Normalize(); - b3Draw_draw->DrawSolidTriangle(-n4, v2, v4, v3, b3Color_blue); + b3Draw_draw->DrawSolidTriangle(n4, v2, v4, v3, b3Color_blue); + } + + /* + for (u32 i = 0; i < m->triangleCount; ++i) + { + b3SoftBodyMeshTriangle* t = m->triangles + i; + + b3Vec3 v1 = m_nodes[t->v1].m_position; + b3Vec3 v2 = m_nodes[t->v2].m_position; + b3Vec3 v3 = m_nodes[t->v3].m_position; + + b3Vec3 c = (v1 + v2 + v3) / scalar(3); + + scalar s = scalar(0.9); + + v1 = s * (v1 - c) + c; + v2 = s * (v2 - c) + c; + v3 = s * (v3 - c) + c; + + b3Draw_draw->DrawTriangle(v1, v2, v3, b3Color_black); + + b3Vec3 n = b3Cross(v2 - v1, v3 - v1); + n.Normalize(); + b3Draw_draw->DrawSolidTriangle(n, v1, v2, v3, b3Color_blue); + + b3Draw_draw->DrawSegment(c, c + n, b3Color_white); + } + */ + + for (b3SoftBodyAnchor* a = m_anchorList.m_head; a; a = a->m_next) + { + b3Vec3 pA = a->GetAnchorA(); + b3Vec3 pB = a->GetAnchorB(); + + b3Draw_draw->DrawPoint(pA, scalar(4), b3Color_red); + + b3Draw_draw->DrawPoint(pB, scalar(4), b3Color_green); + + b3Draw_draw->DrawSegment(pA, pB, b3Color_yellow); } } \ No newline at end of file diff --git a/src/bounce/softbody/softbody_contact_manager.cpp b/src/bounce/softbody/softbody_contact_manager.cpp index 68bbab4..32482c5 100644 --- a/src/bounce/softbody/softbody_contact_manager.cpp +++ b/src/bounce/softbody/softbody_contact_manager.cpp @@ -20,146 +20,138 @@ #include #include #include -#include -#include +#include +#include #include #include b3SoftBodyContactManager::b3SoftBodyContactManager() : - m_nodeBodyContactBlocks(sizeof(b3NodeBodyContact)) + m_sphereAndShapeContactBlocks(sizeof(b3SoftBodySphereAndShapeContact)) { } -class b3SoftBodyContactManagerFindNewBodyContactsQueryListener : public b3QueryListener +void b3SoftBodyContactManager::FindNewContacts() { -public: - virtual bool ReportShape(b3Shape* s2) - { - cm->AddNSPair(n1, s2); + B3_PROFILE("Soft Body Find New Contacts"); - // Keep looking for overlaps - return true; - } - - b3SoftBodyContactManager* cm; - b3SoftBodyNode* n1; -}; - -void b3SoftBodyContactManager::FindNewBodyContacts() -{ - B3_PROFILE("Soft Body Find New Body Contacts"); - - // Is there a world attached to this body? - if (m_body->m_world == nullptr) - { - return; - } - - for (u32 i = 0; i < m_body->m_mesh->vertexCount; ++i) - { - b3SoftBodyNode* n = m_body->m_nodes + i; - - if (n->m_type != e_dynamicSoftBodyNode) - { - continue; - } - - b3AABB3 aabb = m_broadPhase.GetAABB(n->m_broadPhaseId); - - b3SoftBodyContactManagerFindNewBodyContactsQueryListener listener; - listener.cm = this; - listener.n1 = n; - - m_body->m_world->QueryAABB(&listener, aabb); - } + m_broadPhase.FindPairs(this); } -void b3SoftBodyContactManager::AddNSPair(b3SoftBodyNode* n1, b3Shape* s2) +void b3SoftBodyContactManager::AddPair(void* data1, void* data2) { - // Check if there is a contact between the two entities. - for (b3NodeBodyContact* c = m_nodeBodyContactList.m_head; c; c = c->m_next) + b3SoftBodyShape* shape1 = (b3SoftBodyShape*)data1; + b3SoftBodyShape* shape2 = (b3SoftBodyShape*)data2; + + if (shape1->m_type > shape2->m_type) { - if (c->m_n1 == n1 && c->m_s2 == s2) + b3Swap(shape1, shape2); + } + + if (shape1->m_type == e_softBodySphereShape && shape2->m_type == e_softBodyWorldShape) + { + b3SoftBodySphereShape* s1 = (b3SoftBodySphereShape*)shape1; + b3SoftBodyWorldShape* ws2 = (b3SoftBodyWorldShape*)shape2; + + b3SoftBodyNode* n1 = s1->m_node; + + const b3Shape* s2 = ws2->m_shape; + const b3Body* b2 = s2->GetBody(); + + if (b2->GetType() != e_staticBody) { - // A contact already exists. + // Only collisions with static bodies are supported. return; } - } - bool isntDynamic1 = n1->m_type != e_dynamicSoftBodyNode; - bool isntDynamic2 = s2->GetBody()->GetType() != e_dynamicBody; - - if (isntDynamic1 && isntDynamic2) - { - // The entities must not collide with each other. - return; - } - - // Create a new contact. - b3NodeBodyContact* c = CreateNodeBodyContact(); - - c->m_n1 = n1; - c->m_s2 = s2; - c->m_active = false; - c->m_normalImpulse = 0.0f; - c->m_tangentImpulse.SetZero(); - - // Add the contact to the body contact list. - m_nodeBodyContactList.PushFront(c); -} - -void b3SoftBodyContactManager::UpdateBodyContacts() -{ - B3_PROFILE("Soft Body Update Body Contacts"); - - // Update the state of node-body contacts. - b3NodeBodyContact* c = m_nodeBodyContactList.m_head; - while (c) - { - bool isntDynamic1 = c->m_n1->m_type != e_dynamicSoftBodyNode; - bool isntDynamic2 = c->m_s2->GetBody()->GetType() != e_dynamicBody; - - // Cease the contact if entities must not collide with each other. - if (isntDynamic1 && isntDynamic2) + if (n1->m_type != e_dynamicSoftBodyNode) { - b3NodeBodyContact* quack = c; - c = c->m_next; - Destroy(quack); - continue; + // The entities must not collide with each other. + return; } - b3AABB3 aabb1 = m_broadPhase.GetAABB(c->m_n1->m_broadPhaseId); - b3AABB3 aabb2 = c->m_s2->GetAABB(); - - // Destroy the contact if entities AABBs are not overlapping. - bool overlap = b3TestOverlap(aabb1, aabb2); - if (overlap == false) + // Check if there is a contact between the two entities. + for (b3SoftBodySphereAndShapeContact* c = m_sphereAndShapeContactList.m_head; c; c = c->m_next) { - b3NodeBodyContact* quack = c; - c = c->m_next; - Destroy(quack); - continue; + if (c->m_s1 == s1 && c->m_s2 == ws2) + { + // A contact already exists. + return; + } } - // The contact persists. - c->Update(); + // Create a new contact. + b3SoftBodySphereAndShapeContact* c = CreateSphereAndShapeContact(); - c = c->m_next; + c->m_s1 = s1; + c->m_s2 = ws2; + c->m_active = false; + c->m_normalImpulse = scalar(0); + c->m_tangentImpulse.SetZero(); + + // Add the contact to the soft body contact list. + m_sphereAndShapeContactList.PushFront(c); } } -b3NodeBodyContact* b3SoftBodyContactManager::CreateNodeBodyContact() +void b3SoftBodyContactManager::UpdateContacts() { - void* block = m_nodeBodyContactBlocks.Allocate(); - return new(block) b3NodeBodyContact(); + B3_PROFILE("Soft Body Update Contacts"); + + { + // Update the state of sphere and shape contacts. + b3SoftBodySphereAndShapeContact* c = m_sphereAndShapeContactList.m_head; + while (c) + { + b3SoftBodySphereShape* s1 = c->m_s1; + b3SoftBodyNode* n1 = s1->m_node; + + b3SoftBodyWorldShape* ws2 = c->m_s2; + const b3Shape* s2 = ws2->m_shape; + const b3Body* b2 = s2->GetBody(); + + bool isntDynamic1 = n1->GetType() != e_dynamicSoftBodyNode; + bool isntDynamic2 = b2->GetType() != e_dynamicBody; + + // Cease the contact if entities must not collide with each other. + if (isntDynamic1 && isntDynamic2) + { + b3SoftBodySphereAndShapeContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + u32 proxy1 = c->m_s1->m_broadPhaseId; + u32 proxy2 = c->m_s2->m_broadPhaseId; + + // Destroy the contact if primitive AABBs are not overlapping. + bool overlap = m_broadPhase.TestOverlap(proxy1, proxy2); + if (overlap == false) + { + b3SoftBodySphereAndShapeContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + // The contact persists. + c->Update(); + + c = c->m_next; + } + } } -void b3SoftBodyContactManager::Destroy(b3NodeBodyContact* c) +b3SoftBodySphereAndShapeContact* b3SoftBodyContactManager::CreateSphereAndShapeContact() { - m_nodeBodyContactList.Remove(c); + void* block = m_sphereAndShapeContactBlocks.Allocate(); + return new(block) b3SoftBodySphereAndShapeContact(); +} - c->~b3NodeBodyContact(); - - m_nodeBodyContactBlocks.Free(c); +void b3SoftBodyContactManager::Destroy(b3SoftBodySphereAndShapeContact* c) +{ + m_sphereAndShapeContactList.Remove(c); + c->~b3SoftBodySphereAndShapeContact(); + m_sphereAndShapeContactBlocks.Free(c); } \ No newline at end of file diff --git a/src/bounce/softbody/softbody_element.cpp b/src/bounce/softbody/softbody_element.cpp new file mode 100644 index 0000000..0491488 --- /dev/null +++ b/src/bounce/softbody/softbody_element.cpp @@ -0,0 +1,192 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +// Compute the elasticity matrix given Young modulus and Poisson's ratio +// This is a 6 x 6 matrix +static B3_FORCE_INLINE void b3ComputeD(scalar out[36], + scalar E, scalar nu) +{ + scalar lambda = (nu * E) / ((scalar(1) + nu) * (scalar(1) - scalar(2) * nu)); + scalar mu = E / (scalar(2) * (scalar(1) + nu)); + + scalar D[36] = + { + lambda + 2 * mu, lambda, lambda, 0, 0, 0, + lambda, lambda + 2 * mu, lambda, 0, 0, 0, + lambda, lambda, lambda + 2 * mu, 0, 0, 0, + 0, 0, 0, mu, 0, 0, + 0, 0, 0, 0, mu, 0, + 0, 0, 0, 0, 0, mu + }; + + for (u32 i = 0; i < 36; ++i) + { + out[i] = D[i]; + } +} + +// Compute B = S * N, +// where S is the operational matrix and N are the shape functions +// This is a 6 x 12 matrix +// A derivation and corresponding simplification for this matrix +// can be found here: +// https://github.com/erleben/OpenTissue/blob/master/OpenTissue/dynamics/fem/fem_compute_b.h +static B3_FORCE_INLINE void b3ComputeB(scalar out[72], + const b3Mat33& invE) +{ + // cofactor = det(E)^-1 * cofactor(E) + b3Mat33 cofactor = b3Transpose(invE); + + // minor = det(E)^-1 * minor(E) + b3Mat33 minor; + + minor.x.x = cofactor.x.x; + minor.x.y = -cofactor.x.y; + minor.x.z = cofactor.x.z; + + minor.y.x = -cofactor.y.x; + minor.y.y = cofactor.y.y; + minor.y.z = -cofactor.y.z; + + minor.z.x = cofactor.z.x; + minor.z.y = -cofactor.z.y; + minor.z.z = cofactor.z.z; + + scalar e11 = -minor.x.x; + scalar e12 = minor.y.x; + scalar e13 = -minor.z.x; + + scalar e21 = minor.x.y; + scalar e22 = -minor.y.y; + scalar e23 = minor.z.y; + + scalar e31 = -minor.x.z; + scalar e32 = minor.y.z; + scalar e33 = -minor.z.z; + + scalar b1 = -e11 - e12 - e13; + scalar c1 = -e21 - e22 - e23; + scalar d1 = -e31 - e32 - e33; + + scalar b2 = e11; + scalar c2 = e21; + scalar d2 = e31; + + scalar b3 = e12; + scalar c3 = e22; + scalar d3 = e32; + + scalar b4 = e13; + scalar c4 = e23; + scalar d4 = e33; + + scalar B[72] = + { + b1, 0, 0, c1, d1, 0, + 0, c1, 0, b1, 0, d1, + 0, 0, d1, 0, b1, c1, + + b2, 0, 0, c2, d2, 0, + 0, c2, 0, b2, 0, d2, + 0, 0, d2, 0, b2, c2, + + b3, 0, 0, c3, d3, 0, + 0, c3, 0, b3, 0, d3, + 0, 0, d3, 0, b3, c3, + + b4, 0, 0, c4, d4, 0, + 0, c4, 0, b4, 0, d4, + 0, 0, d4, 0, b4, c4, + }; + + for (u32 i = 0; i < 72; ++i) + { + out[i] = B[i]; + } +} + +// Return the element in a block matrix given the indices +// of the element in its corresponding expanded matrix. +static B3_FORCE_INLINE scalar& b3GetElement(b3Mat33 K[16], u32 i, u32 j) +{ + B3_ASSERT(i < 3 * 4); + B3_ASSERT(j < 3 * 4); + + u32 i0 = i / 3; + u32 j0 = j / 3; + + b3Mat33& a = K[i0 + 4 * j0]; + + u32 ii = i - 3 * i0; + u32 jj = j - 3 * j0; + + return a(ii, jj); +} + +static B3_FORCE_INLINE void b3SetK(b3Mat33 K[16], scalar Ke[144]) +{ + for (u32 i = 0; i < 12; ++i) + { + for (u32 j = 0; j < 12; ++j) + { + scalar k1 = Ke[i + 12 * j]; + scalar& k2 = b3GetElement(K, i, j); + + k2 = k1; + } + } +} + +void b3SoftBodyElement::ComputeMatrices() +{ + // 6 x 6 + scalar D[36]; + b3ComputeD(D, m_E, m_nu); + + // 6 x 12 + scalar* B = m_B; + b3ComputeB(B, m_invE); + + // 12 x 6 + scalar BT[72]; + b3Transpose(BT, B, 6, 12); + + // 12 x 6 + scalar BT_D[72]; + b3Mul(BT_D, BT, 12, 6, D, 6, 6); + + // 12 x 12 + scalar BT_D_B[144]; + b3Mul(BT_D_B, BT_D, 12, 6, B, 6, 12); + for (u32 i = 0; i < 144; ++i) + { + BT_D_B[i] *= m_V; + } + + b3SetK(m_K, BT_D_B); + + // 12 x 6 + scalar* P = m_P; + b3Mul(P, BT, 12, 6, D, 6, 6); + for (u32 i = 0; i < 72; ++i) + { + P[i] *= m_V; + } +} \ No newline at end of file diff --git a/src/bounce/softbody/softbody_force_solver.cpp b/src/bounce/softbody/softbody_force_solver.cpp index a9eade5..b7d3675 100644 --- a/src/bounce/softbody/softbody_force_solver.cpp +++ b/src/bounce/softbody/softbody_force_solver.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -39,8 +40,9 @@ bool b3_enableStiffnessWarping = true; b3SoftBodyForceSolver::b3SoftBodyForceSolver(const b3SoftBodyForceSolverDef& def) { + m_step = def.step; m_body = def.body; - m_allocator = &m_body->m_stackAllocator; + m_stack = &m_body->m_stackAllocator; m_mesh = m_body->m_mesh; m_nodes = m_body->m_nodes; m_elements = m_body->m_elements; @@ -53,39 +55,44 @@ b3SoftBodyForceSolver::~b3SoftBodyForceSolver() // Extract rotation from deformation // https://animation.rwth-aachen.de/media/papers/2016-MIG-StableRotation.pdf -static void b3ExtractRotation(b3Mat33& out, b3Quat& q, const b3Mat33& A, u32 maxIterations = 20) +static b3Quat b3ExtractRotation(const b3Mat33& A, const b3Quat& q0, u32 maxIterations = 20) { + b3Quat q = q0; + for (u32 iteration = 0; iteration < maxIterations; ++iteration) { - b3Mat33 R = b3QuatMat33(q); + b3Mat33 R = q.GetXYZAxes(); - float32 s = b3Abs(b3Dot(R.x, A.x) + b3Dot(R.y, A.y) + b3Dot(R.z, A.z)); + scalar s = b3Abs(b3Dot(R.x, A.x) + b3Dot(R.y, A.y) + b3Dot(R.z, A.z)); - if (s == 0.0f) + if (s == scalar(0)) { break; } - float32 inv_s = 1.0f / s + 1.0e-9f; + const scalar kTol = scalar(1.0e-9); + + scalar inv_s = scalar(1) / s + kTol; b3Vec3 v = b3Cross(R.x, A.x) + b3Cross(R.y, A.y) + b3Cross(R.z, A.z); b3Vec3 omega = inv_s * v; - float32 w = b3Length(omega); + scalar w = b3Length(omega); - if (w < 1.0e-9f) + if (w < kTol) { break; } - b3Quat omega_q(omega / w, w); + b3Quat omega_q; + omega_q.SetAxisAngle(omega / w, w); q = omega_q * q; q.Normalize(); } - out = b3QuatMat33(q); + return q; } // Solve A * x = b @@ -104,16 +111,16 @@ static void b3SolveMPCG(b3DenseVec3& x, b3Mat33 a = A(i, i); // Sylvester Criterion to ensure PD-ness - B3_ASSERT(b3Det(a.x, a.y, a.z) > 0.0f); + B3_ASSERT(b3Det(a.x, a.y, a.z) > scalar(0)); - B3_ASSERT(a.x.x > 0.0f); - float32 xx = 1.0f / a.x.x; + B3_ASSERT(a.x.x > scalar(0)); + scalar xx = scalar(1) / a.x.x; - B3_ASSERT(a.y.y > 0.0f); - float32 yy = 1.0f / a.y.y; + B3_ASSERT(a.y.y > scalar(0)); + scalar yy = scalar(1) / a.y.y; - B3_ASSERT(a.z.z > 0.0f); - float32 zz = 1.0f / a.z.z; + B3_ASSERT(a.z.z > scalar(0)); + scalar zz = scalar(1) / a.z.z; P[i] = b3Diagonal(a.x.x, a.y.y, a.z.z); invP[i] = b3Diagonal(xx, yy, zz); @@ -121,12 +128,12 @@ static void b3SolveMPCG(b3DenseVec3& x, x = z; - float32 delta_0 = b3Dot(S * b, P * (S * b)); + scalar delta_0 = b3Dot(S * b, P * (S * b)); b3DenseVec3 r = S * (b - A * x); b3DenseVec3 c = S * (invP * r); - float32 delta_new = b3Dot(r, c); + scalar delta_new = b3Dot(r, c); u32 iteration = 0; for (;;) @@ -143,18 +150,18 @@ static void b3SolveMPCG(b3DenseVec3& x, b3DenseVec3 q = S * (A * c); - float32 alpha = delta_new / b3Dot(c, q); + scalar alpha = delta_new / b3Dot(c, q); x = x + alpha * c; r = r - alpha * q; b3DenseVec3 s = invP * r; - float32 delta_old = delta_new; + scalar delta_old = delta_new; delta_new = b3Dot(r, s); - float32 beta = delta_new / delta_old; + scalar beta = delta_new / delta_old; c = S * (s + beta * c); @@ -164,66 +171,34 @@ static void b3SolveMPCG(b3DenseVec3& x, b3_softBodySolverIterations = iteration; } -// C = A * B -static B3_FORCE_INLINE void b3Mul(float32* C, float32* A, u32 AM, u32 AN, float32* B, u32 BM, u32 BN) +void b3SoftBodyForceSolver::Solve(const b3Vec3& gravity) { - B3_ASSERT(AN == BM); + scalar h = m_step.dt; + scalar inv_h = m_step.inv_dt; - for (u32 i = 0; i < AM; ++i) - { - for (u32 j = 0; j < BN; ++j) - { - C[i + AM * j] = 0.0f; + scalar alpha = m_body->m_massDamping; + scalar beta = m_body->m_stiffnessDamping; - for (u32 k = 0; k < AN; ++k) - { - C[i + AM * j] += A[i + AM * k] * B[k + BM * j]; - } - } - } -} - -// ||v|| -static B3_FORCE_INLINE float32 b3Length(float32* v, u32 n) -{ - float32 result = 0.0f; - for (u32 i = 0; i < n; ++i) - { - result += v[i] * v[i]; - } - return b3Sqrt(result); -} - -void b3SoftBodyForceSolver::Solve(float32 dt, const b3Vec3& gravity) -{ - float32 h = dt; - float32 inv_h = 1.0f / h; - - b3SparseMat33 M(m_mesh->vertexCount); - b3SparseMat33 C(m_mesh->vertexCount); - b3SparseMat33 K(m_mesh->vertexCount); + b3DiagMat33 M(m_mesh->vertexCount); + b3SparseMat33Pattern& KP = *m_body->m_KP; b3DenseVec3 x(m_mesh->vertexCount); b3DenseVec3 p(m_mesh->vertexCount); b3DenseVec3 v(m_mesh->vertexCount); - b3DenseVec3 fe(m_mesh->vertexCount); - b3DenseVec3 f0(m_mesh->vertexCount); + b3DenseVec3 y(m_mesh->vertexCount); + b3DenseVec3 f_elastic(m_mesh->vertexCount); b3DenseVec3 f_plastic(m_mesh->vertexCount); + b3DenseVec3 fe(m_mesh->vertexCount); b3DenseVec3 z(m_mesh->vertexCount); b3DiagMat33 S(m_mesh->vertexCount); - + for (u32 i = 0; i < m_mesh->vertexCount; ++i) { b3SoftBodyNode* n = m_nodes + i; - - M(i, i) = b3Diagonal(n->m_mass); - - // Rayleigh damping - // C = alpha * M + beta * K - // Here the stiffness coefficient beta is zero - C(i, i) = b3Diagonal(n->m_massDamping * n->m_mass); - + B3_ASSERT(n->m_mass > scalar(0)); + M[i] = b3Diagonal(n->m_mass); x[i] = m_mesh->vertices[i]; p[i] = n->m_position; + y[i] = n->m_translation; v[i] = n->m_velocity; fe[i] = n->m_force; z[i] = n->m_velocity; @@ -241,19 +216,22 @@ void b3SoftBodyForceSolver::Solve(float32 dt, const b3Vec3& gravity) } // Element assembly - f0.SetZero(); + f_elastic.SetZero(); f_plastic.SetZero(); + KP.SetZero(); + for (u32 ei = 0; ei < m_mesh->tetrahedronCount; ++ei) { b3SoftBodyMeshTetrahedron* mt = m_mesh->tetrahedrons + ei; b3SoftBodyElement* e = m_elements + ei; - b3Mat33* Ke = e->K; + b3Mat33** Kp = e->m_Kp; + b3Mat33* Ke = e->m_K; - float32* Be = e->B; - float32* Pe = e->P; - float32* epsilon_plastic = e->epsilon_plastic; + scalar* Be = e->m_B; + scalar* Pe = e->m_P; + scalar* epsilon_plastic = e->m_epsilon_plastic; u32 v1 = mt->v1; u32 v2 = mt->v2; @@ -274,9 +252,13 @@ void b3SoftBodyForceSolver::Solve(float32 dt, const b3Vec3& gravity) b3Mat33 E(e1, e2, e3); - b3Mat33 A = E * e->invE; + b3Mat33 A = E * e->m_invE; - b3ExtractRotation(R, e->q, A); + b3Quat q = b3ExtractRotation(A, e->m_q); + + e->m_q = q; + + R = q.GetXYZAxes(); } else { @@ -285,17 +267,11 @@ void b3SoftBodyForceSolver::Solve(float32 dt, const b3Vec3& gravity) b3Mat33 RT = b3Transpose(R); - u32 vs[4] = { v1, v2, v3, v4 }; - for (u32 i = 0; i < 4; ++i) { - u32 vi = vs[i]; - for (u32 j = 0; j < 4; ++j) { - u32 vj = vs[j]; - - K(vi, vj) += R * Ke[i + 4 * j] * RT; + *Kp[i + 4 * j] += R * Ke[i + 4 * j] * RT; } } @@ -305,59 +281,63 @@ void b3SoftBodyForceSolver::Solve(float32 dt, const b3Vec3& gravity) b3Vec3 x3 = x[v3]; b3Vec3 x4 = x[v4]; - b3Vec3 xs[4] = { x1, x2, x3, x4 }; - - b3Vec3 f0s[4]; + // Displacements in unrotated frame + b3Vec3 us[4]; + us[0] = RT * p1 - x1; + us[1] = RT * p2 - x2; + us[2] = RT * p3 - x3; + us[3] = RT * p4 - x4; + // Forces in unrotated frame + b3Vec3 fs[4]; for (u32 i = 0; i < 4; ++i) { - f0s[i].SetZero(); - + fs[i].SetZero(); for (u32 j = 0; j < 4; ++j) { - f0s[i] += R * Ke[i + 4 * j] * xs[j]; + fs[i] += Ke[i + 4 * j] * us[j]; } } + + // Rotate the forces to deformed frame + fs[0] = R * fs[0]; + fs[1] = R * fs[1]; + fs[2] = R * fs[2]; + fs[3] = R * fs[3]; - f0[v1] += f0s[0]; - f0[v2] += f0s[1]; - f0[v3] += f0s[2]; - f0[v4] += f0s[3]; + // Negate f + f_elastic[v1] -= fs[0]; + f_elastic[v2] -= fs[1]; + f_elastic[v3] -= fs[2]; + f_elastic[v4] -= fs[3]; // Plasticity - b3Vec3 ps[4] = { p1, p2, p3, p4 }; - - b3Vec3 RT_x_x0[4]; - for (u32 i = 0; i < 4; ++i) - { - RT_x_x0[i] = RT * ps[i] - xs[i]; - } // 6 x 1 - float32 epsilon_total[6]; - b3Mul(epsilon_total, Be, 6, 12, &RT_x_x0[0].x, 12, 1); + scalar epsilon_total[6]; + b3Mul(epsilon_total, Be, 6, 12, &us[0].x, 12, 1); // 6 x 1 - float32 epsilon_elastic[6]; + scalar epsilon_elastic[6]; for (u32 i = 0; i < 6; ++i) { epsilon_elastic[i] = epsilon_total[i] - epsilon_plastic[i]; } - float32 len_epsilon_elastic = b3Length(epsilon_elastic, 6); - if (len_epsilon_elastic > e->c_yield) + scalar len_epsilon_elastic = b3Length(epsilon_elastic, 6); + if (len_epsilon_elastic > e->m_c_yield) { - float32 amount = h * b3Min(e->c_creep, inv_h); + scalar amount = h * b3Min(e->m_c_creep, inv_h); for (u32 i = 0; i < 6; ++i) { epsilon_plastic[i] += amount * epsilon_elastic[i]; } } - float32 len_epsilon_plastic = b3Length(epsilon_plastic, 6); - if (len_epsilon_plastic > e->c_max) + scalar len_epsilon_plastic = b3Length(epsilon_plastic, 6); + if (len_epsilon_plastic > e->m_c_max) { - float32 scale = e->c_max / len_epsilon_plastic; + scalar scale = e->m_c_max / len_epsilon_plastic; for (u32 i = 0; i < 6; ++i) { epsilon_plastic[i] *= scale; @@ -366,10 +346,12 @@ void b3SoftBodyForceSolver::Solve(float32 dt, const b3Vec3& gravity) b3Vec3 fs_plastic[4]; b3Mul(&fs_plastic[0].x, Pe, 12, 6, epsilon_plastic, 6, 1); - for (u32 i = 0; i < 4; ++i) - { - fs_plastic[i] = R * fs_plastic[i]; - } + + // Rotate the forces to deformed frame + fs_plastic[0] = R * fs_plastic[0]; + fs_plastic[1] = R * fs_plastic[1]; + fs_plastic[2] = R * fs_plastic[2]; + fs_plastic[3] = R * fs_plastic[3]; f_plastic[v1] += fs_plastic[0]; f_plastic[v2] += fs_plastic[1]; @@ -377,20 +359,38 @@ void b3SoftBodyForceSolver::Solve(float32 dt, const b3Vec3& gravity) f_plastic[v4] += fs_plastic[3]; } - f0 = -f0; + b3SparseMat33 K(KP); + + // Rayleigh damping matrix + b3SparseMat33 C = alpha * M + beta * K; + + // ODE: + // M * a2 + C * v2 + K * (x2 - u) = f + // where + // x2 = x1 + h * v2 + // v2 = v1 + h * a2 + // a2 = (v2 - v1) / h + // We identify the force due to the translation by rewriting the ODE: + // M * a2 + C * v2 + K * ((x2 + y) - u)) = f + // M * a2 + C * v2 + K * ((x2 - u) + y) = f + // M * a2 + C * v2 + K * (x2 - u) + K * y = f + // Solve for v2: + // (M + h * C + h * h * K) * v2 = M * v1 + h * (fe - K * (x1 - u) - K * y) + b3DenseVec3 f_translation = -(K * y); b3SparseMat33 A = M + h * C + h * h * K; - b3SparseMat33View viewA(A); + b3SparseMat33View AV(A); - b3DenseVec3 b = M * v - h * (K * p + f0 - (f_plastic + fe)); + b3DenseVec3 b = M * v + h * (fe + f_elastic + f_plastic + f_translation); b3DenseVec3 sx(m_mesh->vertexCount); - b3SolveMPCG(sx, viewA, b, z, S); + b3SolveMPCG(sx, AV, b, z, S); - // Copy velocity back to the particle + // Copy velocity back to the nodes for (u32 i = 0; i < m_mesh->vertexCount; ++i) { m_nodes[i].m_velocity = sx[i]; + m_nodes[i].m_position += y[i]; } } \ No newline at end of file diff --git a/src/bounce/softbody/softbody_mesh.cpp b/src/bounce/softbody/softbody_mesh.cpp index 995e12a..d124c0c 100644 --- a/src/bounce/softbody/softbody_mesh.cpp +++ b/src/bounce/softbody/softbody_mesh.cpp @@ -24,6 +24,8 @@ b3QSoftBodyMesh::b3QSoftBodyMesh() { vertexCount = 0; vertices = nullptr; + triangleCount = 0; + triangles = nullptr; tetrahedronCount = 0; tetrahedrons = nullptr; } @@ -31,10 +33,11 @@ b3QSoftBodyMesh::b3QSoftBodyMesh() b3QSoftBodyMesh::~b3QSoftBodyMesh() { b3Free(vertices); + b3Free(triangles); b3Free(tetrahedrons); } -void b3QSoftBodyMesh::SetAsSphere(float32 radius, u32 subdivisions) +void b3QSoftBodyMesh::SetAsSphere(scalar radius, u32 subdivisions) { smMesh mesh; smCreateMesh(mesh, subdivisions); @@ -48,6 +51,22 @@ void b3QSoftBodyMesh::SetAsSphere(float32 radius, u32 subdivisions) vertices[1 + i] = mesh.vertices[i]; } + B3_ASSERT(triangleCount == 0); + triangleCount = mesh.indexCount / 3; + triangles = (b3SoftBodyMeshTriangle*)b3Alloc(triangleCount * sizeof(b3SoftBodyMeshTriangle)); + for (u32 i = 0; i < mesh.indexCount / 3; ++i) + { + u32 v1 = mesh.indices[3 * i + 0]; + u32 v2 = mesh.indices[3 * i + 1]; + u32 v3 = mesh.indices[3 * i + 2]; + + b3SoftBodyMeshTriangle* t = triangles + i; + + t->v1 = 1 + v1; + t->v2 = 1 + v2; + t->v3 = 1 + v3; + } + B3_ASSERT(tetrahedronCount == 0); tetrahedronCount = mesh.indexCount / 3; tetrahedrons = (b3SoftBodyMeshTetrahedron*)b3Alloc(tetrahedronCount * sizeof(b3SoftBodyMeshTetrahedron)); @@ -59,9 +78,9 @@ void b3QSoftBodyMesh::SetAsSphere(float32 radius, u32 subdivisions) b3SoftBodyMeshTetrahedron* t = tetrahedrons + i; - t->v1 = 1 + v3; + t->v1 = 1 + v1; t->v2 = 1 + v2; - t->v3 = 1 + v1; + t->v3 = 1 + v3; t->v4 = 0; } @@ -71,7 +90,7 @@ void b3QSoftBodyMesh::SetAsSphere(float32 radius, u32 subdivisions) } } -void b3QSoftBodyMesh::SetAsCylinder(float32 radius, float32 ey, u32 segments) +void b3QSoftBodyMesh::SetAsCylinder(scalar radius, scalar ey, u32 segments) { cymMesh mesh; cymCreateMesh(mesh, segments); @@ -85,6 +104,22 @@ void b3QSoftBodyMesh::SetAsCylinder(float32 radius, float32 ey, u32 segments) vertices[1 + i] = mesh.vertices[i]; } + B3_ASSERT(triangleCount == 0); + triangleCount = mesh.indexCount / 3; + triangles = (b3SoftBodyMeshTriangle*)b3Alloc(triangleCount * sizeof(b3SoftBodyMeshTriangle)); + for (u32 i = 0; i < mesh.indexCount / 3; ++i) + { + u32 v1 = mesh.indices[3 * i + 0]; + u32 v2 = mesh.indices[3 * i + 1]; + u32 v3 = mesh.indices[3 * i + 2]; + + b3SoftBodyMeshTriangle* t = triangles + i; + + t->v1 = 1 + v1; + t->v2 = 1 + v2; + t->v3 = 1 + v3; + } + B3_ASSERT(tetrahedronCount == 0); tetrahedronCount = mesh.indexCount / 3; tetrahedrons = (b3SoftBodyMeshTetrahedron*)b3Alloc(tetrahedronCount * sizeof(b3SoftBodyMeshTetrahedron)); @@ -96,13 +131,13 @@ void b3QSoftBodyMesh::SetAsCylinder(float32 radius, float32 ey, u32 segments) b3SoftBodyMeshTetrahedron* t = tetrahedrons + i; - t->v1 = 1 + v3; + t->v1 = 1 + v1; t->v2 = 1 + v2; - t->v3 = 1 + v1; + t->v3 = 1 + v3; t->v4 = 0; } - float32 height = 2.0f * ey; + scalar height = scalar(2) * ey; for (u32 i = 0; i < vertexCount; ++i) { diff --git a/src/bounce/softbody/softbody_node.cpp b/src/bounce/softbody/softbody_node.cpp index cd7bb55..7a1b11f 100644 --- a/src/bounce/softbody/softbody_node.cpp +++ b/src/bounce/softbody/softbody_node.cpp @@ -18,24 +18,46 @@ #include #include +#include -void b3SoftBodyNode::Synchronize(const b3Vec3& displacement) +void b3SoftBodyNode::SetType(b3SoftBodyNodeType type) { - b3AABB3 aabb; - aabb.Set(m_position, m_radius); + if (m_type == type) + { + return; + } - m_body->m_contactManager.m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); + m_type = type; + m_force.SetZero(); + + if (type == e_staticSoftBodyNode) + { + m_velocity.SetZero(); + SynchronizeSpheres(); + } + + DestroyContacts(); +} + +void b3SoftBodyNode::SynchronizeSpheres() +{ + for (b3SoftBodySphereShape* s = m_body->m_sphereShapeList.m_head; s; s = s->m_next) + { + if (s->m_node == this) + { + s->Synchronize(b3Vec3_zero); + } + } } void b3SoftBodyNode::DestroyContacts() { - // Destroy body contacts - b3NodeBodyContact* c = m_body->m_contactManager.m_nodeBodyContactList.m_head; + b3SoftBodySphereAndShapeContact* c = m_body->m_contactManager.m_sphereAndShapeContactList.m_head; while (c) { - if (c->m_n1 == this) + if (c->m_s1->m_node == this) { - b3NodeBodyContact* quack = c; + b3SoftBodySphereAndShapeContact* quack = c; c = c->m_next; m_body->m_contactManager.Destroy(quack); continue; diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index 42b4ad6..f7b3d13 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -17,51 +17,62 @@ */ #include -#include -#include #include +#include +#include #include #include -#include -#include +#include b3SoftBodySolver::b3SoftBodySolver(const b3SoftBodySolverDef& def) { m_body = def.body; - m_allocator = &m_body->m_stackAllocator; + m_stack = &m_body->m_stackAllocator; m_mesh = m_body->m_mesh; m_nodes = m_body->m_nodes; m_elements = m_body->m_elements; - m_bodyContactCapacity = m_body->m_contactManager.m_nodeBodyContactList.m_count; - m_bodyContactCount = 0; - m_bodyContacts = (b3NodeBodyContact**)m_allocator->Allocate(m_bodyContactCapacity * sizeof(b3NodeBodyContact*)); + + m_shapeContactCapacity = m_body->m_contactManager.m_sphereAndShapeContactList.m_count; + m_shapeContactCount = 0; + m_shapeContacts = (b3SoftBodySphereAndShapeContact**)m_stack->Allocate(m_shapeContactCapacity * sizeof(b3SoftBodySphereAndShapeContact*)); + + m_anchorCapacity = m_body->m_anchorList.m_count; + m_anchorCount = 0; + m_anchors = (b3SoftBodyAnchor**)m_stack->Allocate(m_anchorCapacity * sizeof(b3SoftBodyAnchor*)); } b3SoftBodySolver::~b3SoftBodySolver() { - m_allocator->Free(m_bodyContacts); + m_stack->Free(m_anchors); + m_stack->Free(m_shapeContacts); } -void b3SoftBodySolver::Add(b3NodeBodyContact* c) +void b3SoftBodySolver::Add(b3SoftBodySphereAndShapeContact* c) { - m_bodyContacts[m_bodyContactCount++] = c; + m_shapeContacts[m_shapeContactCount++] = c; } -void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) +void b3SoftBodySolver::Add(b3SoftBodyAnchor* a) +{ + m_anchors[m_anchorCount++] = a; +} + +void b3SoftBodySolver::Solve(const b3SoftBodyTimeStep& step, const b3Vec3& gravity) { { // Solve internal dynamics b3SoftBodyForceSolverDef forceSolverDef; + forceSolverDef.step = step; forceSolverDef.body = m_body; b3SoftBodyForceSolver forceSolver(forceSolverDef); - forceSolver.Solve(dt, gravity); + forceSolver.Solve(gravity); } // Copy node state to state buffer - b3Vec3* positions = (b3Vec3*)m_allocator->Allocate(m_mesh->vertexCount * sizeof(b3Vec3)); - b3Vec3* velocities = (b3Vec3*)m_allocator->Allocate(m_mesh->vertexCount * sizeof(b3Vec3)); + b3Vec3* positions = (b3Vec3*)m_stack->Allocate(m_mesh->vertexCount * sizeof(b3Vec3)); + b3Vec3* velocities = (b3Vec3*)m_stack->Allocate(m_mesh->vertexCount * sizeof(b3Vec3)); for (u32 i = 0; i < m_mesh->vertexCount; ++i) { @@ -69,32 +80,53 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter velocities[i] = m_nodes[i].m_velocity; } + b3SoftBodySolverData solverData; + solverData.step = step; + solverData.positions = positions; + solverData.velocities = velocities; + { // Solve constraints b3SoftBodyContactSolverDef contactSolverDef; - contactSolverDef.allocator = m_allocator; + contactSolverDef.step = step; + contactSolverDef.allocator = m_stack; contactSolverDef.positions = positions; contactSolverDef.velocities = velocities; - contactSolverDef.bodyContactCount = m_bodyContactCount; - contactSolverDef.bodyContacts = m_bodyContacts; + contactSolverDef.shapeContactCount = m_shapeContactCount; + contactSolverDef.shapeContacts = m_shapeContacts; b3SoftBodyContactSolver contactSolver(contactSolverDef); { // Inititalize constraints - contactSolver.InitializeBodyContactConstraints(); + contactSolver.InitializeShapeContactConstraints(); + + for (u32 i = 0; i < m_anchorCount; ++i) + { + m_anchors[i]->InitializeConstraints(&solverData); + } } { // Warm-start velocity constraint solver contactSolver.WarmStart(); + + for (u32 i = 0; i < m_anchorCount; ++i) + { + m_anchors[i]->WarmStart(&solverData); + } } { // Solve velocity constraints - for (u32 i = 0; i < velocityIterations; ++i) + for (u32 i = 0; i < step.velocityIterations; ++i) { - contactSolver.SolveBodyContactVelocityConstraints(); + contactSolver.SolveShapeContactVelocityConstraints(); + + for (u32 j = 0; j < m_anchorCount; ++j) + { + m_anchors[j]->SolveVelocityConstraints(&solverData); + } } } @@ -104,7 +136,7 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter } // Integrate velocities - float32 h = dt; + scalar h = step.dt; for (u32 i = 0; i < m_mesh->vertexCount; ++i) { positions[i] += h * velocities[i]; @@ -113,34 +145,25 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter // Solve position constraints { bool positionSolved = false; - for (u32 i = 0; i < positionIterations; ++i) + for (u32 i = 0; i < step.positionIterations; ++i) { - bool bodyContactsSolved = contactSolver.SolveBodyContactPositionConstraints(); - - if (bodyContactsSolved) + bool bodyContactsSolved = contactSolver.SolveShapeContactPositionConstraints(); + + bool anchorsSolved = false; + for (u32 j = 0; j < m_anchorCount; ++j) + { + bool anchorSolved = m_anchors[j]->SolvePositionConstraints(&solverData); + anchorsSolved = anchorsSolved && anchorSolved; + } + + positionSolved = bodyContactsSolved && anchorsSolved; + + if (positionSolved) { - positionSolved = true; break; } } } - - // Synchronize bodies - for (u32 i = 0; i < m_bodyContactCount; ++i) - { - b3Body* body = m_bodyContacts[i]->m_s2->GetBody(); - - if (body->GetType() == e_staticBody) - { - continue; - } - - body->SynchronizeTransform(); - - body->m_worldInvI = b3RotateToFrame(body->m_invI, body->m_xf.rotation); - - body->SynchronizeShapes(); - } } // Copy state buffers back to the nodes @@ -150,6 +173,6 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter m_nodes[i].m_velocity = velocities[i]; } - m_allocator->Free(velocities); - m_allocator->Free(positions); + m_stack->Free(velocities); + m_stack->Free(positions); } \ No newline at end of file diff --git a/src/bounce/sparse/sparse.cpp b/src/bounce/sparse/sparse.cpp new file mode 100644 index 0000000..e81a396 --- /dev/null +++ b/src/bounce/sparse/sparse.cpp @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +b3FrameAllocator* b3FrameAllocator_sparseAllocator = nullptr; \ No newline at end of file