00001
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "stdpacs.h"
00025
00026 #include "edge_collide.h"
00027
00028 using namespace NLMISC;
00029 using namespace std;
00030
00031
00032
00033 namespace NLPACS
00034 {
00035
00036
00037 static const float EdgeCollideEpsilon= 1e-5f;
00038
00039
00040
00041 void CEdgeCollide::make(const CVector2f &p0, const CVector2f &p1)
00042 {
00043 P0= p0;
00044 P1= p1;
00045
00046 Dir= P1-P0;
00047 Dir.normalize();
00048 A0= P0*Dir;
00049 A1= P1*Dir;
00050
00051 Norm.x= Dir.y;
00052 Norm.y= -Dir.x;
00053 C= - P0*Norm;
00054 }
00055
00056
00057
00058 CRational64 CEdgeCollide::testPointMove(const CVector2f &start, const CVector2f &end, TPointMoveProblem &moveBug)
00059 {
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071 if(P0==P1)
00072 {
00073 moveBug= EdgeNull;
00074 return -1;
00075 }
00076
00077
00078 if(start==end)
00079 return 1;
00080
00081
00082
00083 CVector2f normEdge;
00084 CVector2f normMove;
00085 CVector2f deltaEdge;
00086 CVector2f deltaMove;
00087
00088
00089 deltaEdge= P1-P0;
00090 normEdge.x= -deltaEdge.y;
00091 normEdge.y= deltaEdge.x;
00092
00093
00094 deltaMove= end-start;
00095 normMove.x= -deltaMove.y;
00096 normMove.y= deltaMove.x;
00097
00098
00099
00100 double moveD0= (double)normEdge.x*(double)(start.x-P0.x) + (double)normEdge.y*(double)(start.y-P0.y);
00101 double moveD1= (double)normEdge.x*(double)(end.x -P0.x) + (double)normEdge.y*(double)(end.y -P0.y);
00102
00103
00104
00105 double edgeD0= (double)normMove.x*(double)(P0.x-start.x) + (double)normMove.y*(double)(P0.y-start.y);
00106 double edgeD1= (double)normMove.x*(double)(P1.x-start.x) + (double)normMove.y*(double)(P1.y-start.y);
00107
00108
00109
00110 sint sgnMove0, sgnMove1;
00111 sgnMove0= fsgn(moveD0);
00112 sgnMove1= fsgn(moveD1);
00113
00114
00115 if(sgnMove0==0 && sgnMove1==0)
00116 {
00117
00118
00119
00120 double moveA0= (double)deltaEdge.x*(double)(start.x-P0.x) + (double)deltaEdge.y*(double)(start.y-P0.y);
00121 double moveA1= (double)deltaEdge.x*(double)(end.x -P0.x) + (double)deltaEdge.y*(double)(end.y -P0.y);
00122 double edgeA0= 0;
00123 double edgeA1= (double)deltaEdge.x*(double)deltaEdge.x + (double)deltaEdge.y*(double)deltaEdge.y;
00124
00125
00126 if(moveA1>=edgeA0 && edgeA1>=moveA0)
00127 {
00128 moveBug= ParallelEdges;
00129 return -1;
00130 }
00131 else
00132 return 1;
00133 }
00134
00135
00136 if( sgnMove0==sgnMove1)
00137 return 1;
00138
00139
00140 sint sgnEdge0, sgnEdge1;
00141 sgnEdge0= fsgn(edgeD0);
00142 sgnEdge1= fsgn(edgeD1);
00143
00144
00145 nlassert(sgnEdge0!=0 || sgnEdge1!=0);
00146
00147
00148
00149 if( sgnEdge0==sgnEdge1 )
00150 return 1;
00151
00152
00153 if(sgnEdge0==0 || sgnEdge1==0)
00154 {
00155 moveBug= TraverseEndPoint;
00156 return -1;
00157 }
00158 else if(sgnMove1==0)
00159 {
00160 moveBug= StopOnEdge;
00161 return -1;
00162 }
00163 else if(sgnMove0==0)
00164 {
00165
00166 moveBug= StartOnEdge;
00167 return -1;
00168 }
00169
00170
00171
00172
00173
00174 double numerator= (0-moveD0)*1024*1024;
00175 double denominator= (moveD1-moveD0)*1024*1024;
00176 sint64 numeratorInt= (sint64)numerator;
00177 sint64 denominatorInt= (sint64)denominator;
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188 return CRational64(numeratorInt, denominatorInt);
00189 }
00190
00191
00192
00193 static inline float testCirclePoint(const CVector2f &start, const CVector2f &delta, float radius, const CVector2f &point)
00194 {
00195
00196 float a,b,c;
00197 float dta;
00198 float r0, r1, res;
00199 CVector2f relC, relV;
00200
00201
00202
00203
00204 relC= start-point;
00205 relV= delta;
00206 a= relV.x*relV.x + relV.y*relV.y;
00207
00208
00209 if(a==0)
00210 {
00211
00212 if(relC.norm()<radius && relC*delta<0)
00213 return 0;
00214 else
00215 return 1;
00216 }
00217 else
00218 {
00219 b= 2* (relC.x*relV.x + relC.y*relV.y);
00220 c= relC.x*relC.x + relC.y*relC.y - radius*radius;
00221
00222 dta= b*b - 4*a*c;
00223 if(dta>=0)
00224 {
00225 dta= (float)sqrt(dta);
00226 r0= (-b -dta)/(2*a);
00227 r1= (-b +dta)/(2*a);
00228
00229 if(r0>r1)
00230 swap(r0,r1);
00231
00232 if(r1<=0)
00233 {
00234 res= 1;
00235 }
00236
00237 else if(r0>=0)
00238 {
00239 res= min(1.f, r0);
00240 }
00241 else
00242 {
00243
00244
00245
00246 if(b>0)
00247 res= 1;
00248 else
00249 res=0;
00250 }
00251 }
00252 else
00253 {
00254
00255 res= 1;
00256 }
00257 }
00258
00259 return res;
00260 }
00261
00262
00263
00264 float CEdgeCollide::testCircleMove(const CVector2f &start, const CVector2f &delta, float radius, CVector2f &normal)
00265 {
00266
00267 if( delta.isNull() )
00268 return 1;
00269
00270
00271 double dist= start*Norm + C;
00272
00273 double speed= delta*Norm;
00274
00275
00276 bool sensPos= dist>0;
00277 bool sensSpeed= speed>0;
00278
00279
00280 dist= fabs(dist) - radius;
00281 speed= fabs(speed);
00282 if( dist > speed )
00283 return 1;
00284
00285
00286
00287 if(dist>=0)
00288 {
00289
00290 if(sensPos==sensSpeed )
00291 return 1;
00292
00293
00294 if(speed==0)
00295 return 1;
00296
00297
00298 double t= dist/speed;
00299
00300
00301
00302
00303 CVector2d proj= CVector2d(start) + CVector2d(delta)*t;
00304
00305 proj+= Norm * (sensSpeed?radius:-radius);
00306
00307 double aProj= proj*Dir;
00308
00309
00310 if( aProj>=A0 && aProj<=A1)
00311 {
00312
00313 if(sensPos)
00314 normal= Norm;
00315 else
00316 normal= -Norm;
00317
00318
00319 return (float)t;
00320 }
00321 }
00322
00323
00324 else
00325 {
00326
00327
00328
00329 float aProj= start*Dir;
00330
00331
00332 if( aProj>=A0 && aProj<=A1)
00333 {
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344 if(sensPos==sensSpeed && (-dist)<0.5*radius)
00345 {
00346 return 1;
00347 }
00348 else
00349 {
00350
00351
00352 if(sensPos)
00353 normal= Norm;
00354 else
00355 normal= -Norm;
00356
00357 return 0;
00358 }
00359 }
00360 }
00361
00362
00363
00364
00365 float tmin, ttmp;
00366
00367 tmin= testCirclePoint(start, delta, radius, P0);
00368
00369 ttmp= testCirclePoint(start, delta, radius, P1);
00370 tmin= min(tmin, ttmp);
00371
00372
00373 if(tmin<1)
00374 {
00375
00376 CVector2f colPoint= tmin==ttmp? P1 : P0;
00377
00378 CVector2f colPos= start + delta*tmin;
00379
00380
00381 normal= colPos - colPoint;
00382 normal.normalize();
00383 }
00384
00385 return tmin;
00386 }
00387
00388
00389
00390
00391 bool CEdgeCollide::testEdgeMove(const CVector2f &q0, const CVector2f &q1, const CVector2f &delta, float &tMin, float &tMax, bool &normalOnBox)
00392 {
00393 double a,b,cv,cc, d,e,f;
00394 CVector2d tmp;
00395
00396
00397
00398 tmp= q1 - q0;
00399
00400 tmp/= tmp.sqrnorm();
00401 a= tmp.x;
00402 b= tmp.y;
00403
00404
00405 cv= - CVector2d(b,-a)*delta;
00406 cc= - CVector2d(b,-a)*q0;
00407
00408
00409
00410 tmp= P1 - P0;
00411
00412 tmp/= tmp.sqrnorm();
00413 d= tmp.x;
00414 e= tmp.y;
00415 f= - CVector2d(e,-d)*P0;
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433 double det= a*e - b*d;
00434
00435 if(det==0 || fabs(det)<delta.norm()*EdgeCollideEpsilon)
00436 return false;
00437
00438
00439 CVector2d pInt, vInt;
00440 pInt.x= ( d*cc - f*a ) / det;
00441 pInt.y= ( e*cc - f*b ) / det;
00442 vInt.x= ( d*cv ) / det;
00443 vInt.y= ( e*cv ) / det;
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455 double uc, uv;
00456 double vc, vv;
00457
00458 uc= (pInt-q0) * CVector2d(a,b);
00459 uv= (vInt-delta) * CVector2d(a,b);
00460 vc= (pInt-P0) * CVector2d(d,e);
00461 vv= (vInt) * CVector2d(d,e);
00462
00463
00464
00465
00466
00467
00468
00469 double tu0, tu1, tv0, tv1;
00470
00471 bool allU=false, allV=false;
00472
00473
00474 if(uv==0 || fabs(uv)<EdgeCollideEpsilon)
00475 {
00476
00477 if(uc<0 || uc>1)
00478 return false;
00479
00480 tu0 =tu1= 0;
00481 allU= true;
00482 }
00483 else
00484 {
00485 tu0= (0-uc)/uv;
00486 tu1= (1-uc)/uv;
00487 }
00488
00489
00490 if(vv==0 || fabs(vv)<EdgeCollideEpsilon)
00491 {
00492
00493 if(vc<0 || vc>1)
00494 return false;
00495
00496 tv0 =tv1= 0;
00497 allV= true;
00498 }
00499 else
00500 {
00501 tv0= (0-vc)/vv;
00502 tv1= (1-vc)/vv;
00503 }
00504
00505
00506
00507
00508
00509 if(tu0>tu1)
00510 swap(tu0, tu1);
00511 if(tv0>tv1)
00512 swap(tv0, tv1);
00513
00514 normalOnBox= false;
00515 if(!allU && !allV)
00516 {
00517
00518 if(tu0>tv1 || tv0>tu1)
00519 return false;
00520 else
00521 {
00522
00523 tMin= (float)max(tu0, tv0);
00524 tMax= (float)min(tu1, tv1);
00525
00526 if(tv0>tu0)
00527 normalOnBox= true;
00528 }
00529 }
00530 else if(allU)
00531 {
00532
00533 tMin= (float)tv0;
00534 tMax= (float)tv1;
00535
00536 normalOnBox= true;
00537 }
00538 else if(allV)
00539 {
00540
00541 tMin= (float)tu0;
00542 tMax= (float)tu1;
00543 }
00544 else
00545 {
00546
00547 tMin= -1000;
00548 tMax= 1000;
00549 }
00550
00551 return true;
00552 }
00553
00554
00555
00556 float CEdgeCollide::testBBoxMove(const CVector2f &start, const CVector2f &delta, const CVector2f bbox[4], CVector2f &normal)
00557 {
00558
00559 float dist= start*Norm + C;
00560
00561
00562 bool sensPos= dist>0;
00563
00564
00565
00566
00567
00568
00569 float tMin = 0.f, tMax = 0.f;
00570 bool edgeCollided= false;
00571 bool normalOnBox= false;
00572 CVector2f boxNormal(0.f, 0.f);
00573 for(sint i=0;i<4;i++)
00574 {
00575 float t0, t1;
00576 bool nob;
00577 CVector2f a= bbox[i];
00578 CVector2f b= bbox[(i+1)&3];
00579
00580
00581 if(testEdgeMove(a, b, delta, t0, t1, nob))
00582 {
00583 if(edgeCollided)
00584 {
00585 tMin= min(t0, tMin);
00586 tMax= max(t1, tMax);
00587 }
00588 else
00589 {
00590 edgeCollided= true;
00591 tMin= t0;
00592 tMax= t1;
00593 }
00594
00595
00596 if(tMin==t0)
00597 {
00598 normalOnBox= nob;
00599 if(nob)
00600 {
00601 CVector2f dab;
00602
00603 dab= b-a;
00604
00605 boxNormal.x= -dab.y;
00606 boxNormal.y= dab.x;
00607 }
00608 }
00609 }
00610 }
00611
00612
00613 if(edgeCollided && tMin<1 && tMax>0)
00614 {
00615
00616 if(normalOnBox)
00617 {
00618
00619 normal= boxNormal;
00620 }
00621 else
00622 {
00623
00624 if(sensPos)
00625 normal= Norm;
00626 else
00627 normal= -Norm;
00628 }
00629
00630
00631 if(tMin>0)
00632
00633 return tMin;
00634 else
00635 {
00636
00637
00638
00639
00640
00641 if( tMax<0.2*(-tMin) )
00642 return 1;
00643 else
00644 return 0;
00645 }
00646 }
00647 else
00648 return 1;
00649
00650 }
00651
00652
00653
00654 bool CEdgeCollide::testBBoxCollide(const CVector2f bbox[4])
00655 {
00656
00657 CVector2f p0= P0, p1= P1;
00658
00659 for(sint i=0; i<4; i++)
00660 {
00661 CVector2f a= bbox[i];
00662 CVector2f b= bbox[(i+1)&3];
00663 CVector2f delta= b-a, norm;
00664
00665 norm.x= delta.y;
00666 norm.y= -delta.x;
00667
00668 float d0= (p0-a)*norm;
00669 float d1= (p1-a)*norm;
00670
00671
00672 if( d0>0 && d1>0)
00673 return false;
00674
00675 if( d0>0 || d1>0)
00676 {
00677 CVector2f intersect= p0 + (p1-p0)* ((0-d0)/(d1-d0));
00678 if(d1>0)
00679 p1= intersect;
00680 else
00681 p0= intersect;
00682 }
00683 }
00684
00685
00686 return true;
00687 }
00688
00689
00690
00691 }