| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- /*******************************************************************
- Fichier : Vector.cpp
- Date : 28/04/98
- Version : 1.001
- Description : Classe de gestion des vecteurs
- |*******************************************************************|
- Bugs:
- Le troisieme point n'est pas pris en compte pour le calcul du
- vecteur orthogonal
- La fonction angle n'est pas validee
- Le calcul du vecteur orthogonal serait surement + rapide en calculant
- le produit scalaire du vecteur avec le vecteur forme par le point
- et son projete orthogonal sur le vecteur
- Notes:
- on pourrait utiliser controlfp plutot qu'ajouter .5 lors des conversions
- flottant vers entier
- La longueur (nombre de points requis pour trace le vecteur) du
- vecteur est une valeur signee, afin de permettre la recuperation
- des points n'appartenant pas au vecteur, mais a la droite dont
- le vecteur est support.
- operator [-1], retourne par exemple le point juste avant m_pt1
- |*******************************************************************|
- Historique :
- 28/04/98 1.001 : Fonction de translation d'un vecteur
- 20/06/97 1.000 : Première version
- |*******************************************************************/
- #include "scale.h"
-
- /*----------------------------------------------------------\
- Fonctions locales
- \----------------------------------------------------------*/
- inline long Double2Long(double d)
- {
- if (d < 0)
- {
- d -= .5;
- }
- else
- {
- d += .5;
- }
-
- return (long (d));
- }
-
- /*----------------------------------------------------------\
- | CVector |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Constructeurs de l'objet |
- |-----------------------------------------------------------|
- | PARAMETRES : |
- | Si aucun, un vecteur nul est créé |
- | pt1, pt2 : deux points définissants le support du vecteur|
- \----------------------------------------------------------*/
- CVector::CVector (void)
- : m_pt1 (0,0), // petite optimisation, sinon le constructeur vide
- m_pt2 (0,0) // est appelé avant que l'on affecte réellement les points
- {
- Update ();
- } // end of CVector
-
- CVector::CVector(const Point &pt1, const Point &pt2)
- : m_pt1 (pt1),
- m_pt2 (pt2)
- {
- Update ();
- } // end of CVector
-
- CVector::CVector(int x1, int y1, int x2, int y2)
- : m_pt1 (x1, y1),
- m_pt2 (x2, y2)
- {
- Update ();
- } // end of CVector
-
- /*----------------------------------------------------------\
- | = |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Opérateur de copie |
- |-----------------------------------------------------------|
- | PARAMETRES : |
- | Si aucun, un vecteur nul est créé |
- | pt1, pt2 : deux points définissants le support du vecteur|
- \----------------------------------------------------------*/
- const CVector &CVector::operator = (const CVector &vSource)
- {
- m_pt1 = vSource.m_pt1;
- m_pt2 = vSource.m_pt2;
- Update();
-
- return (*this);
- } // fin de =
-
- /*----------------------------------------------------------\
- | Update |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Calcule les valeurs de tous les membres à partir des |
- | points extrêmes du vecteur |
- | A appeler à chaque modification de l'un de ces points. |
- \----------------------------------------------------------*/
- void CVector::Update(void)
- {
- // calcul des variations
- m_lDeltaX = m_pt2.x - m_pt1.x;
- m_lDeltaY = m_pt2.y - m_pt1.y;
-
- // calcul de la norme
- // cela ne devrait jamais arriver (sur un écran), mais en cas de bug...
- // la somme des produits peut être > à 2x10e9 et donner un résultat
- // négatif, ce qui n'arrange pas vraiment la fonction sqrt
- // le DWORD final bien que théoriquement insuffisant, fonctionne car
- // on traite des vecteurs sur un écran, on évite simplement un plantage
- // en cas de bug
- // MAIS on fausse alors le calcul. Une gestion d'exception adequat serait plus judicieuse !!
- long test1 = ((m_lDeltaX * m_lDeltaX) + (m_lDeltaY * m_lDeltaY));
- //DWORD test2 = DWORD ((m_lDeltaX * m_lDeltaX) + (m_lDeltaY * m_lDeltaY));
- //double test3 = sqrt(test1);
- double test3 = sqrt((double) test1); // MAJ2005
- //double test4 = sqrt(test2);
- //m_dblNorm = sqrt (DWORD ((m_lDeltaX * m_lDeltaX) + (m_lDeltaY * m_lDeltaY)));
- m_dblNorm = sqrt ((double) long ((m_lDeltaX * m_lDeltaX) + (m_lDeltaY * m_lDeltaY)));
- m_dblNorm = test3;
-
- // détermination de la variation la plus importante
- m_fHorizontal = (abs (m_lDeltaX) > abs (m_lDeltaY));
-
- // calcul du nombre de points requis pour tracer une droite
- if (m_fHorizontal) m_dwSize = abs (m_lDeltaX);
- else m_dwSize = abs (m_lDeltaY);
- } // fin de Update
-
- /*----------------------------------------------------------\
- | operator [] |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Retourne les coordonnées d'un point du segement |
- |-----------------------------------------------------------|
- | PARAMETRES : |
- | lIndex : position du point sur le segment |
- | signé afin de pouvoir obtenir les coordonées |
- | des points de la droite aussi. |
- |-----------------------------------------------------------|
- | RETOUR : |
- | le point voulu |
- \----------------------------------------------------------*/
- Point CVector::operator [] (long lIndex) const
- {
- // pour le vecteur nul il aurait mieux valu ne pas appeler la fonction
- if (Nul ())
- {
- return Point (0,0);
- }
- else if (lIndex)
- {
- // le double est essentiel, sinon tout est converti en NON signé!!!
- return (Point (m_pt1.x + Double2Long (m_lDeltaX * lIndex / double (Length ())),
- m_pt1.y + Double2Long (m_lDeltaY * lIndex / double (Length ()))
- )
- );
- }
- else
- return (m_pt1);
- } // end of operator []
-
- /*----------------------------------------------------------\
- | PosFromPoint |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Retourne la position que doit avoir le point s'il fait |
- | partie de la droite |
- |-----------------------------------------------------------|
- | PARAMETRES : |
- | pt : le point en question |
- |-----------------------------------------------------------|
- | RETOURNE : |
- | la position estimée |
- \----------------------------------------------------------*/
- long CVector::PosFromPoint (const Point &pt) const
- {
- // calcul de la position approximative
- if (Nul ())
- {
- return (0);
- }
- else
- {
- // que de problèmes je me suis faits pour pas grand-chose !!
- // en fonction du sens, on obtient directement la position
- // du point, puisqu'il y a UN point ajouté à chaque fois
- // sans recouvrement dans le sens de variation
- // LONG ((pt.x - m_pt1.x) * Length () / double (m_lDeltaX)) = (pt.x - m_pt1.x) * sign(m_lDeltaX)
- // = m_lDeltaX !!
- // ça sert parfois de prendre le temps de réfléchir !!
- if (m_fHorizontal)
- return (m_lDeltaX > 0 ? pt.x - m_pt1.x : m_pt1.x - pt.x);
- else
- return (m_lDeltaY > 0 ? pt.y - m_pt1.y : m_pt1.y - pt.y);
- }
- } // fin de PosFromPoint
-
- /*----------------------------------------------------------\
- | PointInVector |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Détermine si un point donné appartient au segment |
- |-----------------------------------------------------------|
- | PARAMETRES : |
- | pt : le point en question |
- |-----------------------------------------------------------|
- | RETOURNE : |
- | un booléen |
- \----------------------------------------------------------*/
- bool CVector::PointInVector (const Point &pt) const
- {
- // calcul de la position approximative
- // la position en X, doit être la même que la position en Y, logique...
- // donc un seul calcul suffit
- if (Nul ())
- {
- return (false);
- }
- else
- {
- long lPos = PosFromPoint (pt);
-
- // on regarde aussi si l'un des deux extrêmes ne fait pas partie du segment
- // à cause des arrondis
- // si la position est hors limite, on court-circuite le test, d'autant
- if ((lPos < 0) || (long (lPos) > Length ()))
- return (false);
- else
- // le point PEUT appartenir au segment
- return ( (operator [] (lPos ) == pt)
- || (operator [] (lPos + 1) == pt)
- || ((lPos > 0) && (operator [] (lPos - 1) == pt))
- );
- }
- } // fin de PointInVector
-
- /*----------------------------------------------------------\
- | PointOnLine |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Détermine si un point donné appartient à la droite dont |
- | ce vecteur est support |
- |-----------------------------------------------------------|
- | PARAMETRES : |
- | pt : le point en question |
- |-----------------------------------------------------------|
- | RETOURNE : |
- | un booléen |
- \----------------------------------------------------------*/
- bool CVector::PointOnLine (const Point &pt) const
- {
- // Comme ces classe de vecteur est basée sur une représentation
- // pixel, on ne peut pas vérifier que les vecteurs sont colinéaires
- // (à cause de la précision)
- if (Nul ())
- {
- return (false);
- }
- else
- {
- long lPos = PosFromPoint (pt);
-
- // seul différence avec PointInVector : on ne vérifie pas les bornes
- // vis à vis de lPos
- return ( (operator [] (lPos ) == pt)
- || (operator [] (lPos + 1) == pt)
- || ((lPos > 0) && (operator [] (lPos - 1) == pt))
- );
- }
- } // fin de PointOnLine
-
- /*----------------------------------------------------------\
- | Angle |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Retourne l'angle formé par le vecteur et un point formant|
- | un vecteur avec le point de départ |
- |-----------------------------------------------------------|
- | PARAMETRES : |
- | pt : le point en question |
- |-----------------------------------------------------------|
- | RETOURNE : |
- | l'angle en degrés (sens trigonométrique) |
- | retourne une valeur entre 0 et 180 |
- \----------------------------------------------------------*/
- double CVector::Angle (const Point &pt) const
- {
- // calcul du cosinus de l'angle
- if (Nul () || (pt == m_pt1))
- return (0.0);
- else
- {
- // produit scalaire : v1.v2 = x1.x2 + y1.y2 = ||v1||.||v2||.cos(ß)
- CVector v (m_pt1, pt);
-
- return (acos ((v * *this) / (Norm () * v.Norm ())) * 180. / 3.1415927);
- // return (acos ((v * *this) / (Norm () * v.Norm ())) * 180. / M_PI);
- }
- } // fin de Angle
-
- /*----------------------------------------------------------\
- | Orthogonal |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Retourne un vecteur orthogonal au segment débutant au |
- | point donné. |
- |-----------------------------------------------------------|
- | PARAMETRES : |
- | ptStart : point de départ du vecteur orthogonal, devant |
- | appartenir au vecteur source |
- | ptDir : point donnant la direction du nouveau vecteur |
- |-----------------------------------------------------------|
- | RETOURNE : |
- | un vecteur orthogonal |
- \----------------------------------------------------------*/
- CVector CVector::Orthogonal( const Point &ptStart, const Point &ptDir ) const
- {
- CVector v;
-
- // Le point de départ doit appartenir à la droite dont ce vecteur est support
- // Le point de direction ne doit pas appartenir à la droite dont ce vecteur est support
- if ( PointOnLine (ptStart)
- && !PointOnLine (ptDir)
- )
- {
- // il suffit d'inverser les coordonnées en X et Y pour obtenir
- // un vecteur orthogonal
- v.m_pt1 = ptStart;
- // si le vecteur est du "bon côté", l'angle entre le nouveau vecteur
- // et le point, ne doit pas excéder 90°
- v.m_pt2.x = ptStart.x - m_lDeltaY;
- v.m_pt2.y = ptStart.y + m_lDeltaX;
- v.Update (); // avant de calculer l'angle
- if (v.Angle (ptDir) > 90.0)
- {
- v.m_pt2.x = ptStart.x + m_lDeltaY;
- v.m_pt2.y = ptStart.y - m_lDeltaX;
- v.Update();
- assert( v.Angle( ptDir ) < 90.0 );
- }
- }
-
- v.Update ();
- return (v);
- } // fin de Orthogonal
-
- /*----------------------------------------------------------\
- | Orthogonal |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Retourne un vecteur orthogonal au segment débutant au |
- | point donné. |
- |-----------------------------------------------------------|
- | PARAMETRES : |
- | ptStart : point de départ du vecteur orthogonal, devant |
- | appartenir au vecteur source |
- | uType : 0/1 pour indiquer le sens du vecteur |
- |-----------------------------------------------------------|
- | RETOURNE : |
- | un vecteur orthogonal |
- \----------------------------------------------------------*/
- CVector CVector::Orthogonal( const Point &ptStart, unsigned int uType ) const
- {
- CVector v;
-
- // Le point de départ doit appartenir à la droite dont ce vecteur est support
- if (PointOnLine (ptStart))
- {
- // il suffit d'inverser les coordonnées en X et Y pour obtenir
- // un vecteur orthogonal
- v.m_pt1 = ptStart;
- // NE PAS changer la signification de uType, CEIM::Measure compte dessus
- if (uType)
- {
- v.m_pt2.x = ptStart.x + m_lDeltaY;
- v.m_pt2.y = ptStart.y - m_lDeltaX;
- }
- else
- {
- v.m_pt2.x = ptStart.x - m_lDeltaY;
- v.m_pt2.y = ptStart.y + m_lDeltaX;
- }
- }
-
- v.Update ();
- return (v);
- } // fin de Orthogonal
-
- /*----------------------------------------------------------\
- | Projected |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Retourne le projeté orthogonal du point sur le vecteur |
- | Le point retourné est assuré d'appartenir au vecteur |
- |-----------------------------------------------------------|
- | PARAMETRES : |
- | pt : point à projeter |
- |-----------------------------------------------------------|
- | RETOURNE : |
- | le projeté orthogonal |
- \----------------------------------------------------------*/
- Point CVector::Projected (const Point &pt) const
- {
- // Soient A et B les 2 points du vecteur, C le point en paramètre, M le projeté
- // orthogonal de C sur AB.
- //
- // 1° le projeté appartenant au vecteur, il doit appartenir au support
- // (Y - Ya)*m_lDeltaX - (X - Xa)*m_lDeltaY = 0
- // 2° par le produit scalaire on retrouve une équation à 2 inconnues
- // (X - Xc)*m_lDeltaX + (Y - Yc)*m_lDeltaY = 0
-
- // petite optimisation
- if (m_lDeltaY == 0)
- return (Point (pt.x, m_pt1.y));
- else if (m_lDeltaX == 0)
- return (Point (m_pt1.x, pt.y));
- else
- {
- // X(1 + SLOPE²) = Yc.SLOPE + Xa.SLOPE² - Ya.SLOPE + Xc
- // Y = (X - Xa).SLOPE + Ya
- double dblSlope = double (m_lDeltaY) / double (m_lDeltaX),
- dblX,
- dblY;
-
- dblX = dblSlope * (pt.y + m_pt1.x * dblSlope - m_pt1.y) + pt.x;
- dblX /= (1 + dblSlope * dblSlope);
- dblY = (dblX - m_pt1.x) * dblSlope + m_pt1.y;
- // suppression de l'erreur induite par l'arrondi
- // via l'appel à []
- return (operator [] (PosFromPoint (Point (Double2Long (dblX), Double2Long (dblY)))));
- }
- } // end of Projected
-
- /*----------------------------------------------------------\
- | SetLength |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Impose une longueur au segment à partir du premier point |
- |-----------------------------------------------------------|
- | PARAMETRES : |
- | lNewSize : nouvelle taille souhaitée pour le segment |
- | signé pour autoriser une inversion du vecteur|
- \----------------------------------------------------------*/
- void CVector::SetLength (long lNewSize)
- {
- m_pt2 = operator [] (lNewSize);
- Update ();
- } // fin de SetLength
-
- /*----------------------------------------------------------\
- | Mask |
- |-----------------------------------------------------------|
- | DESCRIPTION : |
- | Limite l'étendue du vecteur à l'intérieur du rectangle |
- |-----------------------------------------------------------|
- | PARAMETRES : |
- | rcLimit : rectangle limitant l'étendue du vecteur |
- \----------------------------------------------------------*/
- void CVector::Mask (const Rect &rcLimit)
- {
- // les bords droit et inférieur ne sont pas considérés comme
- // faisant partie du rectangle
- // ATTENTION les coordonnées sont croissantes vers le bas
- long lX1, lY1, lX2, lY2;
-
- // à chaque problème, on regarde si l'autre point résoud le
- // problème rencontré, si ce n'est pas le cas, il n'y a rien
- // à faire
- lX1 = m_pt1.x - rcLimit.left;
- lX2 = m_pt2.x - rcLimit.left;
- if (lX1 < 0)
- {
- if (lX2 < 0)
- {
- // le 2° point est aussi à gauche du bord gauche
- //NullVector ();
- return;
- }
- }
- else if (lX1 >= rcLimit.right)
- {
- }
- lY1 = m_pt1.y - rcLimit.top;
- lY2 = m_pt2.y - rcLimit.top;
- } // fin de Mask
-
- double CVector::Norm (const CScale *pScale) const
- {
- if ( pScale )
- {
- return (pScale->Valid () ? pScale->Distance (m_pt1, m_pt2) : 0);
- }
-
- return 0.0;
- }
-
- void CVector::MoveTo (const Point &ptNewStart)
- {
- m_pt2 += ptNewStart - m_pt1;
- m_pt1 = ptNewStart;
- // pas la peine d'appeler Update, ce n'est qu'une translation
- }
- /*
- Quelques tests
-
- CVector v1 (CPoint(4,6), CPoint (12,3)),
- v2;
- LONG l;
-
- for (l = 0; l <= v1.Length (); l++)
- afxDump << v1 [l] << "\n";
- afxDump << v1.PointInVector (CPoint (4, 6)) << "," << v1.PointOnLine (CPoint ( 4, 6)) << "\n";
- afxDump << v1.PointInVector (CPoint (5, 6)) << "," << v1.PointOnLine (CPoint ( 5, 6)) << "\n";
- afxDump << v1.PointInVector (CPoint (11,4)) << "," << v1.PointOnLine (CPoint (11, 4)) << "\n";
- afxDump << v1.PointInVector (CPoint (15,2)) << "," << v1.PointOnLine (CPoint (15, 2)) << "\n";
- afxDump << v1.PointInVector (CPoint (0, 7)) << "," << v1.PointOnLine (CPoint ( 0, 7)) << "\n";
- afxDump << v1.PointInVector (CPoint (-120,300)) << "," << v1.PointOnLine (CPoint (-120,300)) << "\n";
-
- v1.SetLength (v1.Length () * 2);
- for (;l <= v1.Length (); l++)
- afxDump << v1 [l] << "\n";
- afxDump << v1.PointInVector (CPoint (4, 6)) << "," << v1.PointOnLine (CPoint ( 4, 6)) << "\n";
- afxDump << v1.PointInVector (CPoint (5, 6)) << "," << v1.PointOnLine (CPoint ( 5, 6)) << "\n";
- afxDump << v1.PointInVector (CPoint (11,4)) << "," << v1.PointOnLine (CPoint (11, 4)) << "\n";
- afxDump << v1.PointInVector (CPoint (15,2)) << "," << v1.PointOnLine (CPoint (15, 2)) << "\n";
- afxDump << v1.PointInVector (CPoint (0, 7)) << "," << v1.PointOnLine (CPoint ( 0, 7)) << "\n";
- afxDump << v1.PointInVector (CPoint (-120,300)) << "," << v1.PointOnLine (CPoint (-120,300)) << "\n";
-
- // vérification du premier sens
- v2 = v1.Orthogonal (v1.StartPoint (), CPoint (10,7));
- for (l = 0; l <= v2.Length (); l++)
- afxDump << v2 [l] << "\n";
-
- v2 = v1.Orthogonal (v1.StartPoint (), CPoint (2,8));
- for (l = 0; l <= v2.Length (); l++)
- afxDump << v2 [l] << "\n";
-
- // vérification du sens opposé
- v2 = v1.Orthogonal (v1.StartPoint (), CPoint (9,4)); // aussi près que possible du vecteur source
- for (l = 0; l <= v2.Length (); l++)
- afxDump << v2 [l] << "\n";
-
- v2 = v1.Orthogonal (v1.StartPoint (), CPoint (0,6));
- for (l = 0; l <= v2.Length (); l++)
- afxDump << v2 [l] << "\n";
- */
|