vector.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. /*******************************************************************
  2. Fichier : Vector.cpp
  3. Date : 28/04/98
  4. Version : 1.001
  5. Description : Classe de gestion des vecteurs
  6. |*******************************************************************|
  7. Bugs:
  8. Le troisieme point n'est pas pris en compte pour le calcul du
  9. vecteur orthogonal
  10. La fonction angle n'est pas validee
  11. Le calcul du vecteur orthogonal serait surement + rapide en calculant
  12. le produit scalaire du vecteur avec le vecteur forme par le point
  13. et son projete orthogonal sur le vecteur
  14. Notes:
  15. on pourrait utiliser controlfp plutot qu'ajouter .5 lors des conversions
  16. flottant vers entier
  17. La longueur (nombre de points requis pour trace le vecteur) du
  18. vecteur est une valeur signee, afin de permettre la recuperation
  19. des points n'appartenant pas au vecteur, mais a la droite dont
  20. le vecteur est support.
  21. operator [-1], retourne par exemple le point juste avant m_pt1
  22. |*******************************************************************|
  23. Historique :
  24. 28/04/98 1.001 : Fonction de translation d'un vecteur
  25. 20/06/97 1.000 : Première version
  26. |*******************************************************************/
  27. #include "scale.h"
  28. /*----------------------------------------------------------\
  29. Fonctions locales
  30. \----------------------------------------------------------*/
  31. inline long Double2Long(double d)
  32. {
  33. if (d < 0)
  34. {
  35. d -= .5;
  36. }
  37. else
  38. {
  39. d += .5;
  40. }
  41. return (long (d));
  42. }
  43. /*----------------------------------------------------------\
  44. | CVector |
  45. |-----------------------------------------------------------|
  46. | DESCRIPTION : |
  47. | Constructeurs de l'objet |
  48. |-----------------------------------------------------------|
  49. | PARAMETRES : |
  50. | Si aucun, un vecteur nul est créé |
  51. | pt1, pt2 : deux points définissants le support du vecteur|
  52. \----------------------------------------------------------*/
  53. CVector::CVector (void)
  54. : m_pt1 (0,0), // petite optimisation, sinon le constructeur vide
  55. m_pt2 (0,0) // est appelé avant que l'on affecte réellement les points
  56. {
  57. Update ();
  58. } // end of CVector
  59. CVector::CVector(const Point &pt1, const Point &pt2)
  60. : m_pt1 (pt1),
  61. m_pt2 (pt2)
  62. {
  63. Update ();
  64. } // end of CVector
  65. CVector::CVector(int x1, int y1, int x2, int y2)
  66. : m_pt1 (x1, y1),
  67. m_pt2 (x2, y2)
  68. {
  69. Update ();
  70. } // end of CVector
  71. /*----------------------------------------------------------\
  72. | = |
  73. |-----------------------------------------------------------|
  74. | DESCRIPTION : |
  75. | Opérateur de copie |
  76. |-----------------------------------------------------------|
  77. | PARAMETRES : |
  78. | Si aucun, un vecteur nul est créé |
  79. | pt1, pt2 : deux points définissants le support du vecteur|
  80. \----------------------------------------------------------*/
  81. const CVector &CVector::operator = (const CVector &vSource)
  82. {
  83. m_pt1 = vSource.m_pt1;
  84. m_pt2 = vSource.m_pt2;
  85. Update();
  86. return (*this);
  87. } // fin de =
  88. /*----------------------------------------------------------\
  89. | Update |
  90. |-----------------------------------------------------------|
  91. | DESCRIPTION : |
  92. | Calcule les valeurs de tous les membres à partir des |
  93. | points extrêmes du vecteur |
  94. | A appeler à chaque modification de l'un de ces points. |
  95. \----------------------------------------------------------*/
  96. void CVector::Update(void)
  97. {
  98. // calcul des variations
  99. m_lDeltaX = m_pt2.x - m_pt1.x;
  100. m_lDeltaY = m_pt2.y - m_pt1.y;
  101. // calcul de la norme
  102. // cela ne devrait jamais arriver (sur un écran), mais en cas de bug...
  103. // la somme des produits peut être > à 2x10e9 et donner un résultat
  104. // négatif, ce qui n'arrange pas vraiment la fonction sqrt
  105. // le DWORD final bien que théoriquement insuffisant, fonctionne car
  106. // on traite des vecteurs sur un écran, on évite simplement un plantage
  107. // en cas de bug
  108. // MAIS on fausse alors le calcul. Une gestion d'exception adequat serait plus judicieuse !!
  109. long test1 = ((m_lDeltaX * m_lDeltaX) + (m_lDeltaY * m_lDeltaY));
  110. //DWORD test2 = DWORD ((m_lDeltaX * m_lDeltaX) + (m_lDeltaY * m_lDeltaY));
  111. //double test3 = sqrt(test1);
  112. double test3 = sqrt((double) test1); // MAJ2005
  113. //double test4 = sqrt(test2);
  114. //m_dblNorm = sqrt (DWORD ((m_lDeltaX * m_lDeltaX) + (m_lDeltaY * m_lDeltaY)));
  115. m_dblNorm = sqrt ((double) long ((m_lDeltaX * m_lDeltaX) + (m_lDeltaY * m_lDeltaY)));
  116. m_dblNorm = test3;
  117. // détermination de la variation la plus importante
  118. m_fHorizontal = (abs (m_lDeltaX) > abs (m_lDeltaY));
  119. // calcul du nombre de points requis pour tracer une droite
  120. if (m_fHorizontal) m_dwSize = abs (m_lDeltaX);
  121. else m_dwSize = abs (m_lDeltaY);
  122. } // fin de Update
  123. /*----------------------------------------------------------\
  124. | operator [] |
  125. |-----------------------------------------------------------|
  126. | DESCRIPTION : |
  127. | Retourne les coordonnées d'un point du segement |
  128. |-----------------------------------------------------------|
  129. | PARAMETRES : |
  130. | lIndex : position du point sur le segment |
  131. | signé afin de pouvoir obtenir les coordonées |
  132. | des points de la droite aussi. |
  133. |-----------------------------------------------------------|
  134. | RETOUR : |
  135. | le point voulu |
  136. \----------------------------------------------------------*/
  137. Point CVector::operator [] (long lIndex) const
  138. {
  139. // pour le vecteur nul il aurait mieux valu ne pas appeler la fonction
  140. if (Nul ())
  141. {
  142. return Point (0,0);
  143. }
  144. else if (lIndex)
  145. {
  146. // le double est essentiel, sinon tout est converti en NON signé!!!
  147. return (Point (m_pt1.x + Double2Long (m_lDeltaX * lIndex / double (Length ())),
  148. m_pt1.y + Double2Long (m_lDeltaY * lIndex / double (Length ()))
  149. )
  150. );
  151. }
  152. else
  153. return (m_pt1);
  154. } // end of operator []
  155. /*----------------------------------------------------------\
  156. | PosFromPoint |
  157. |-----------------------------------------------------------|
  158. | DESCRIPTION : |
  159. | Retourne la position que doit avoir le point s'il fait |
  160. | partie de la droite |
  161. |-----------------------------------------------------------|
  162. | PARAMETRES : |
  163. | pt : le point en question |
  164. |-----------------------------------------------------------|
  165. | RETOURNE : |
  166. | la position estimée |
  167. \----------------------------------------------------------*/
  168. long CVector::PosFromPoint (const Point &pt) const
  169. {
  170. // calcul de la position approximative
  171. if (Nul ())
  172. {
  173. return (0);
  174. }
  175. else
  176. {
  177. // que de problèmes je me suis faits pour pas grand-chose !!
  178. // en fonction du sens, on obtient directement la position
  179. // du point, puisqu'il y a UN point ajouté à chaque fois
  180. // sans recouvrement dans le sens de variation
  181. // LONG ((pt.x - m_pt1.x) * Length () / double (m_lDeltaX)) = (pt.x - m_pt1.x) * sign(m_lDeltaX)
  182. // = m_lDeltaX !!
  183. // ça sert parfois de prendre le temps de réfléchir !!
  184. if (m_fHorizontal)
  185. return (m_lDeltaX > 0 ? pt.x - m_pt1.x : m_pt1.x - pt.x);
  186. else
  187. return (m_lDeltaY > 0 ? pt.y - m_pt1.y : m_pt1.y - pt.y);
  188. }
  189. } // fin de PosFromPoint
  190. /*----------------------------------------------------------\
  191. | PointInVector |
  192. |-----------------------------------------------------------|
  193. | DESCRIPTION : |
  194. | Détermine si un point donné appartient au segment |
  195. |-----------------------------------------------------------|
  196. | PARAMETRES : |
  197. | pt : le point en question |
  198. |-----------------------------------------------------------|
  199. | RETOURNE : |
  200. | un booléen |
  201. \----------------------------------------------------------*/
  202. bool CVector::PointInVector (const Point &pt) const
  203. {
  204. // calcul de la position approximative
  205. // la position en X, doit être la même que la position en Y, logique...
  206. // donc un seul calcul suffit
  207. if (Nul ())
  208. {
  209. return (false);
  210. }
  211. else
  212. {
  213. long lPos = PosFromPoint (pt);
  214. // on regarde aussi si l'un des deux extrêmes ne fait pas partie du segment
  215. // à cause des arrondis
  216. // si la position est hors limite, on court-circuite le test, d'autant
  217. if ((lPos < 0) || (long (lPos) > Length ()))
  218. return (false);
  219. else
  220. // le point PEUT appartenir au segment
  221. return ( (operator [] (lPos ) == pt)
  222. || (operator [] (lPos + 1) == pt)
  223. || ((lPos > 0) && (operator [] (lPos - 1) == pt))
  224. );
  225. }
  226. } // fin de PointInVector
  227. /*----------------------------------------------------------\
  228. | PointOnLine |
  229. |-----------------------------------------------------------|
  230. | DESCRIPTION : |
  231. | Détermine si un point donné appartient à la droite dont |
  232. | ce vecteur est support |
  233. |-----------------------------------------------------------|
  234. | PARAMETRES : |
  235. | pt : le point en question |
  236. |-----------------------------------------------------------|
  237. | RETOURNE : |
  238. | un booléen |
  239. \----------------------------------------------------------*/
  240. bool CVector::PointOnLine (const Point &pt) const
  241. {
  242. // Comme ces classe de vecteur est basée sur une représentation
  243. // pixel, on ne peut pas vérifier que les vecteurs sont colinéaires
  244. // (à cause de la précision)
  245. if (Nul ())
  246. {
  247. return (false);
  248. }
  249. else
  250. {
  251. long lPos = PosFromPoint (pt);
  252. // seul différence avec PointInVector : on ne vérifie pas les bornes
  253. // vis à vis de lPos
  254. return ( (operator [] (lPos ) == pt)
  255. || (operator [] (lPos + 1) == pt)
  256. || ((lPos > 0) && (operator [] (lPos - 1) == pt))
  257. );
  258. }
  259. } // fin de PointOnLine
  260. /*----------------------------------------------------------\
  261. | Angle |
  262. |-----------------------------------------------------------|
  263. | DESCRIPTION : |
  264. | Retourne l'angle formé par le vecteur et un point formant|
  265. | un vecteur avec le point de départ |
  266. |-----------------------------------------------------------|
  267. | PARAMETRES : |
  268. | pt : le point en question |
  269. |-----------------------------------------------------------|
  270. | RETOURNE : |
  271. | l'angle en degrés (sens trigonométrique) |
  272. | retourne une valeur entre 0 et 180 |
  273. \----------------------------------------------------------*/
  274. double CVector::Angle (const Point &pt) const
  275. {
  276. // calcul du cosinus de l'angle
  277. if (Nul () || (pt == m_pt1))
  278. return (0.0);
  279. else
  280. {
  281. // produit scalaire : v1.v2 = x1.x2 + y1.y2 = ||v1||.||v2||.cos(ß)
  282. CVector v (m_pt1, pt);
  283. return (acos ((v * *this) / (Norm () * v.Norm ())) * 180. / 3.1415927);
  284. // return (acos ((v * *this) / (Norm () * v.Norm ())) * 180. / M_PI);
  285. }
  286. } // fin de Angle
  287. /*----------------------------------------------------------\
  288. | Orthogonal |
  289. |-----------------------------------------------------------|
  290. | DESCRIPTION : |
  291. | Retourne un vecteur orthogonal au segment débutant au |
  292. | point donné. |
  293. |-----------------------------------------------------------|
  294. | PARAMETRES : |
  295. | ptStart : point de départ du vecteur orthogonal, devant |
  296. | appartenir au vecteur source |
  297. | ptDir : point donnant la direction du nouveau vecteur |
  298. |-----------------------------------------------------------|
  299. | RETOURNE : |
  300. | un vecteur orthogonal |
  301. \----------------------------------------------------------*/
  302. CVector CVector::Orthogonal( const Point &ptStart, const Point &ptDir ) const
  303. {
  304. CVector v;
  305. // Le point de départ doit appartenir à la droite dont ce vecteur est support
  306. // Le point de direction ne doit pas appartenir à la droite dont ce vecteur est support
  307. if ( PointOnLine (ptStart)
  308. && !PointOnLine (ptDir)
  309. )
  310. {
  311. // il suffit d'inverser les coordonnées en X et Y pour obtenir
  312. // un vecteur orthogonal
  313. v.m_pt1 = ptStart;
  314. // si le vecteur est du "bon côté", l'angle entre le nouveau vecteur
  315. // et le point, ne doit pas excéder 90°
  316. v.m_pt2.x = ptStart.x - m_lDeltaY;
  317. v.m_pt2.y = ptStart.y + m_lDeltaX;
  318. v.Update (); // avant de calculer l'angle
  319. if (v.Angle (ptDir) > 90.0)
  320. {
  321. v.m_pt2.x = ptStart.x + m_lDeltaY;
  322. v.m_pt2.y = ptStart.y - m_lDeltaX;
  323. v.Update();
  324. assert( v.Angle( ptDir ) < 90.0 );
  325. }
  326. }
  327. v.Update ();
  328. return (v);
  329. } // fin de Orthogonal
  330. /*----------------------------------------------------------\
  331. | Orthogonal |
  332. |-----------------------------------------------------------|
  333. | DESCRIPTION : |
  334. | Retourne un vecteur orthogonal au segment débutant au |
  335. | point donné. |
  336. |-----------------------------------------------------------|
  337. | PARAMETRES : |
  338. | ptStart : point de départ du vecteur orthogonal, devant |
  339. | appartenir au vecteur source |
  340. | uType : 0/1 pour indiquer le sens du vecteur |
  341. |-----------------------------------------------------------|
  342. | RETOURNE : |
  343. | un vecteur orthogonal |
  344. \----------------------------------------------------------*/
  345. CVector CVector::Orthogonal( const Point &ptStart, unsigned int uType ) const
  346. {
  347. CVector v;
  348. // Le point de départ doit appartenir à la droite dont ce vecteur est support
  349. if (PointOnLine (ptStart))
  350. {
  351. // il suffit d'inverser les coordonnées en X et Y pour obtenir
  352. // un vecteur orthogonal
  353. v.m_pt1 = ptStart;
  354. // NE PAS changer la signification de uType, CEIM::Measure compte dessus
  355. if (uType)
  356. {
  357. v.m_pt2.x = ptStart.x + m_lDeltaY;
  358. v.m_pt2.y = ptStart.y - m_lDeltaX;
  359. }
  360. else
  361. {
  362. v.m_pt2.x = ptStart.x - m_lDeltaY;
  363. v.m_pt2.y = ptStart.y + m_lDeltaX;
  364. }
  365. }
  366. v.Update ();
  367. return (v);
  368. } // fin de Orthogonal
  369. /*----------------------------------------------------------\
  370. | Projected |
  371. |-----------------------------------------------------------|
  372. | DESCRIPTION : |
  373. | Retourne le projeté orthogonal du point sur le vecteur |
  374. | Le point retourné est assuré d'appartenir au vecteur |
  375. |-----------------------------------------------------------|
  376. | PARAMETRES : |
  377. | pt : point à projeter |
  378. |-----------------------------------------------------------|
  379. | RETOURNE : |
  380. | le projeté orthogonal |
  381. \----------------------------------------------------------*/
  382. Point CVector::Projected (const Point &pt) const
  383. {
  384. // Soient A et B les 2 points du vecteur, C le point en paramètre, M le projeté
  385. // orthogonal de C sur AB.
  386. //
  387. // 1° le projeté appartenant au vecteur, il doit appartenir au support
  388. // (Y - Ya)*m_lDeltaX - (X - Xa)*m_lDeltaY = 0
  389. // 2° par le produit scalaire on retrouve une équation à 2 inconnues
  390. // (X - Xc)*m_lDeltaX + (Y - Yc)*m_lDeltaY = 0
  391. // petite optimisation
  392. if (m_lDeltaY == 0)
  393. return (Point (pt.x, m_pt1.y));
  394. else if (m_lDeltaX == 0)
  395. return (Point (m_pt1.x, pt.y));
  396. else
  397. {
  398. // X(1 + SLOPE²) = Yc.SLOPE + Xa.SLOPE² - Ya.SLOPE + Xc
  399. // Y = (X - Xa).SLOPE + Ya
  400. double dblSlope = double (m_lDeltaY) / double (m_lDeltaX),
  401. dblX,
  402. dblY;
  403. dblX = dblSlope * (pt.y + m_pt1.x * dblSlope - m_pt1.y) + pt.x;
  404. dblX /= (1 + dblSlope * dblSlope);
  405. dblY = (dblX - m_pt1.x) * dblSlope + m_pt1.y;
  406. // suppression de l'erreur induite par l'arrondi
  407. // via l'appel à []
  408. return (operator [] (PosFromPoint (Point (Double2Long (dblX), Double2Long (dblY)))));
  409. }
  410. } // end of Projected
  411. /*----------------------------------------------------------\
  412. | SetLength |
  413. |-----------------------------------------------------------|
  414. | DESCRIPTION : |
  415. | Impose une longueur au segment à partir du premier point |
  416. |-----------------------------------------------------------|
  417. | PARAMETRES : |
  418. | lNewSize : nouvelle taille souhaitée pour le segment |
  419. | signé pour autoriser une inversion du vecteur|
  420. \----------------------------------------------------------*/
  421. void CVector::SetLength (long lNewSize)
  422. {
  423. m_pt2 = operator [] (lNewSize);
  424. Update ();
  425. } // fin de SetLength
  426. /*----------------------------------------------------------\
  427. | Mask |
  428. |-----------------------------------------------------------|
  429. | DESCRIPTION : |
  430. | Limite l'étendue du vecteur à l'intérieur du rectangle |
  431. |-----------------------------------------------------------|
  432. | PARAMETRES : |
  433. | rcLimit : rectangle limitant l'étendue du vecteur |
  434. \----------------------------------------------------------*/
  435. void CVector::Mask (const Rect &rcLimit)
  436. {
  437. // les bords droit et inférieur ne sont pas considérés comme
  438. // faisant partie du rectangle
  439. // ATTENTION les coordonnées sont croissantes vers le bas
  440. long lX1, lY1, lX2, lY2;
  441. // à chaque problème, on regarde si l'autre point résoud le
  442. // problème rencontré, si ce n'est pas le cas, il n'y a rien
  443. // à faire
  444. lX1 = m_pt1.x - rcLimit.left;
  445. lX2 = m_pt2.x - rcLimit.left;
  446. if (lX1 < 0)
  447. {
  448. if (lX2 < 0)
  449. {
  450. // le 2° point est aussi à gauche du bord gauche
  451. //NullVector ();
  452. return;
  453. }
  454. }
  455. else if (lX1 >= rcLimit.right)
  456. {
  457. }
  458. lY1 = m_pt1.y - rcLimit.top;
  459. lY2 = m_pt2.y - rcLimit.top;
  460. } // fin de Mask
  461. double CVector::Norm (const CScale *pScale) const
  462. {
  463. if ( pScale )
  464. {
  465. return (pScale->Valid () ? pScale->Distance (m_pt1, m_pt2) : 0);
  466. }
  467. return 0.0;
  468. }
  469. void CVector::MoveTo (const Point &ptNewStart)
  470. {
  471. m_pt2 += ptNewStart - m_pt1;
  472. m_pt1 = ptNewStart;
  473. // pas la peine d'appeler Update, ce n'est qu'une translation
  474. }
  475. /*
  476. Quelques tests
  477. CVector v1 (CPoint(4,6), CPoint (12,3)),
  478. v2;
  479. LONG l;
  480. for (l = 0; l <= v1.Length (); l++)
  481. afxDump << v1 [l] << "\n";
  482. afxDump << v1.PointInVector (CPoint (4, 6)) << "," << v1.PointOnLine (CPoint ( 4, 6)) << "\n";
  483. afxDump << v1.PointInVector (CPoint (5, 6)) << "," << v1.PointOnLine (CPoint ( 5, 6)) << "\n";
  484. afxDump << v1.PointInVector (CPoint (11,4)) << "," << v1.PointOnLine (CPoint (11, 4)) << "\n";
  485. afxDump << v1.PointInVector (CPoint (15,2)) << "," << v1.PointOnLine (CPoint (15, 2)) << "\n";
  486. afxDump << v1.PointInVector (CPoint (0, 7)) << "," << v1.PointOnLine (CPoint ( 0, 7)) << "\n";
  487. afxDump << v1.PointInVector (CPoint (-120,300)) << "," << v1.PointOnLine (CPoint (-120,300)) << "\n";
  488. v1.SetLength (v1.Length () * 2);
  489. for (;l <= v1.Length (); l++)
  490. afxDump << v1 [l] << "\n";
  491. afxDump << v1.PointInVector (CPoint (4, 6)) << "," << v1.PointOnLine (CPoint ( 4, 6)) << "\n";
  492. afxDump << v1.PointInVector (CPoint (5, 6)) << "," << v1.PointOnLine (CPoint ( 5, 6)) << "\n";
  493. afxDump << v1.PointInVector (CPoint (11,4)) << "," << v1.PointOnLine (CPoint (11, 4)) << "\n";
  494. afxDump << v1.PointInVector (CPoint (15,2)) << "," << v1.PointOnLine (CPoint (15, 2)) << "\n";
  495. afxDump << v1.PointInVector (CPoint (0, 7)) << "," << v1.PointOnLine (CPoint ( 0, 7)) << "\n";
  496. afxDump << v1.PointInVector (CPoint (-120,300)) << "," << v1.PointOnLine (CPoint (-120,300)) << "\n";
  497. // vérification du premier sens
  498. v2 = v1.Orthogonal (v1.StartPoint (), CPoint (10,7));
  499. for (l = 0; l <= v2.Length (); l++)
  500. afxDump << v2 [l] << "\n";
  501. v2 = v1.Orthogonal (v1.StartPoint (), CPoint (2,8));
  502. for (l = 0; l <= v2.Length (); l++)
  503. afxDump << v2 [l] << "\n";
  504. // vérification du sens opposé
  505. v2 = v1.Orthogonal (v1.StartPoint (), CPoint (9,4)); // aussi près que possible du vecteur source
  506. for (l = 0; l <= v2.Length (); l++)
  507. afxDump << v2 [l] << "\n";
  508. v2 = v1.Orthogonal (v1.StartPoint (), CPoint (0,6));
  509. for (l = 0; l <= v2.Length (); l++)
  510. afxDump << v2 [l] << "\n";
  511. */