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