Bonjour,
Je souhaite développer un logiciel de CAD.
Quel est le principe des sélections d'objet?
Par exemple, comment sélectionner une ligne en un clic de souris n'importe où sur la ligne?
Merci
Bonjour,
Je souhaite développer un logiciel de CAD.
Quel est le principe des sélections d'objet?
Par exemple, comment sélectionner une ligne en un clic de souris n'importe où sur la ligne?
Merci
Bon courage, c'est pas gagné...
Il faut calculer la distance entre le clic souris et chaque ligne (problème de géométrie simple), puis prendre la plus proche.
Tu me confirmes ce que je pensais.
Ce ne serait pas trop compliqué s'il n'y avait que des ligne...
Merci
Bonjour,
je pense que tu devrais travailler en vectoriel.
Donc une droite serait représentée par : OM = t.u
ou u est un vecteur directeur et t un réel qui te permet de parcourir la droite
Un segment serait représenté par : OM = t.u
avec t cette fois borné ce qui te permet de parcourir seulement le segment
Cela vaut en 2D comme en 3D. En 3D, s'ajoute aussi le problème de la projection de tes éléments sur un plan 2D (l'écran).
Mettons que nous soyons en 2D.
Tu cliques sur un point de ton segment : tu recupères un point M(Mx,My) (Mx et My replacé dans le repère Oxy);
M appartient au segment OM=t.u (ou t est un reel borné et u(Ux, Uy)).
si Mx / Ux = My / Uy et si tM = Mx / Ux appartient à l'interval qui limite t.
(pour être plus cool sur le point cliqué tu peut faire un truc du style Mx / Ux ~ My / Uy comme ça t'est pas obligé de cliqué exactement sur le segment).
Pour une droite, juste la 1ere condition te sera nécessaire (étant donné que t n'est pas borné pour une droite).
Pour représenter un rectangle (ensemble de 4 segments agencés spécifiquement) et le sélectionner à partir d'un point d'un de ses cotés, tu n'as qu'a testé l'appartenance à 1 des 4 segments qui le constitue. (pour cela, crée toi une classe segment, une classe rectangle).
Pour des formes plus complexes, essaie de te ratacher à des formes plus simples comme le segment, etc.
Pour tester l'appartenance d'un point cliqué M(Mx, My) à un cercle de centre C(Cx,Cy) et de rayon R, tu compare la distance MC et R. (on reste en vectoriel).
En 3D, c tout pareil. Tu as juste en plus la phase de conversion des coordonnées 2d de la souris en 3d...
J'espère que je suis pas hors sujet.
Flo.
Non seulement, ta solution n’est pas hors sujet, mais elle me semble très pertinente !
Un détail : faut-il crée une structure ‘vecteur’ ( X, Y, angle, distance) ou existe-t-il un type de donnée?
Connais-tu une adresse où on peut voir du code ?
Merci beaucoup pour ton post.
Salut !
En, complément de ce qu'à dit Flo. tu peux aussi consulter le Post-it
de JEG et sa jAPI ! A consommer sans aucune modération !
A titre perso, je travaille avec (x,y,z) dans le genre :
A plus !
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 class T3DPoint { public : //Pour le sélectionner bool Sel; //Coordonnées 3D int x; int y; int z; //Coordonnées du point à l'écran //pour éviter, dans certains cas, d'avoir à tout calculer à chaque fois atx; aty; //etc.. };
Salut,
tu n'as pas précisé si tu voulais travailler en 2D ou 3D.
Je choisis donc la 2D (mais c'est généralisable).
Si ton problème était le mien, la classe de base serait TCoords comme le souligne henderson (avec son T3DPoint) .
Ensuite la classe TSegment , par exemple, est à mon sens fondamentale (en fait ça serait plus un TVecteur).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 class TCoords { public: int x; int y; TCoords(int new_x, int new_y); };
dans le *.cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 class TSegment { private: // vecteur directeur du segment calculé à la construction du segment TCoords vecteur_directeur; // un des 2 points extremes du segment calculé à la construction du segment TCoords premier_point_du_segment; public: // constructeur d'un TSegment défini par les coordonnées des points extremes premier_point et second_point TSegment(TCoords premier_point, TCoords second_point); // pour voir si un point fait parti du segment bool IsInsideSegment(TCoords point); };
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 TSegment::TSegment(TCoords premier_point, TCoords second_point) { premier_point_du_segment.x = premier_point.x; premier_point_du_segment.y = premier_point.y; vecteur_directeur.x = second_point.x - premier_point.x; vecteur_directeur.y = second_point.y - premier_point.y; }pour le TQuadrilatère (qui inclut le trapèze, le carré, le rectangle, le losange et le parallélograme)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 bool TSegment::IsInsideSegment(TCoords point) { TCoords vecteur; // calcul du vecteur definit par point et premier_point_du_segment vecteur.x = point.x - premier_point_du_segment.x; vecteur.y = point.y - premier_point_du_segment.y; // calcul des 2 ratios (1 par axe). C'est le // Mx / Ux et My / Uy de tout a l'heure double tx = (double)vecteur.x / (double)vecteur_directeur.x; double ty = (double)vecteur.y / (double)vecteur_directeur.y; // on verifie qu'ils sont égaux à la tolérance prés (j'ai mis 0.1 mais à toi de voir quelle valeur convient le mieux). C'est le // Mx / Ux ~ My / Uy de tout a l'heure // sinon renvoie false if(abs(tx - ty) > 0.1) return false; // ensuite on vérifie le tM = Mx / Ux qui doit appartenir à [0;1] // la le vecteur directeur du segment à la longueur du segment donc t est compris entre 0 et 1 (c plus simple comme ça) // sinon renvoie false if((tx > 1.0) || (tx < 0.0)) return false; // point appartient bien au segment, renvoie true return true; }
dans le *.cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 class TQuadrilatère { private: // 2 segments suffisent pour définir un quadrilatère et ses sous-sections // mais pour faire plus simple j'en mets 4 TSegment cotes_opposes[4]; public: // constructeur du'un qualdrilatère à partir de ses 4 sommets TQuadrilatère(TCoords points[4]); // verification si un point appartient bien qau quadrilatere bool IsPointOnQuadrilatere(TCoords point); };
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 TQuadrilatère::TQuadrilatère(TCoords points[4]) { cotes_opposes[0].x = points[0].x - points[1].x; cotes_opposes[1].x = points[1].x - points[2].x; cotes_opposes[2].x = points[2].x - points[3].x; cotes_opposes[2].x = points[3].x - points[0].x; // .... et pareil avec les .y }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 bool TQuadrilatère::IsPointOnQuadrilatere(TCoords point) { bool OnQuadrilatere = false; // verifie l'appartenance à 1 des 4 segments for(int iSegment = 0; iSegment < 4; iSegment++) OnQuadrilatere |= cotes_opposes[iSegment].IsInsideSegment(point) return OnQuadrilatere; }
Bon j'ai fait tout ça vite... mais le principe devrais t'aider pour commencer.
Tu as là tout ce qu'il te faut pour les segments, carre, rectangle, trapeze etc...
Ya plus qu'à.
Une TForm, une TImage.
Et c'est parti.
Si je peux encore t'aider, n'hésite pas.
Flo.
un seul tout petit pb dans le code de Flo. : abs retourne un entier.
Ligneà remplacer par
Code : Sélectionner tout - Visualiser dans une fenêtre à part if(abs(tx - ty) > 0.1)Sans oublier
Code : Sélectionner tout - Visualiser dans une fenêtre à part if(fabs(tx - ty) > 0.1)
Code : Sélectionner tout - Visualiser dans une fenêtre à part #include <math.h>
Sinon tout fonctionne à merveille. Du caviar!
Merci encore.
Salut !
Il y a aussi une autre solution qui reste vrai pour n'importe quel objet graphique,
et qui consiste à sélectionner l'objet par le biais de ses sommets. Le choix entre
déplacer le sommet ou déplacer l'objet peut alors être fixé à l'aide des touches alt
ou ctrl du paramètre Shift de la OnMouseDown du Control (TForm, TPaintBox etc..).
Si on suppose une représentation par vues (haut, bas, gauche... et 3D) pour des
objets 3D, alors :
Comme cette fonction est sollicitée par les objets graphiques pour chacun de leurs sommets,
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 bool __fastcall T3DPoint::HasFocus(int X, int Y) { //On travaille ici sur la position écran du point et non sur ses (x,y,z) int minx = atx - 3; int maxx = atx + 3; int miny = aty - 3; int maxy = aty + 3; return ((X >= minx) && (X < maxx) && (Y >= miny) && (Y < maxy)); }
on en déduit l'objet qui est sélectionné mais aussi un sommet de l'objet en particulier.
A noter que dans mon cas, j'avais opté pour une modélisation du cercle par (n) points, ce
qui avait l'avantage de pouvoir représenter ce cercle sous n'importe quel angle, en 3D.
Sans doute à ne pas faire mais existe t-il mieux ?
On peut aussi utiliser des poignées en tant que sommets d'un rectangle dans lequel s'incrit
l'objet graphique ou bien également en calculant son isobarycentre, et ce, toujours à partir
des positions écran.
Toujours faire attention également à l'ordre d'apparition des objets graphiques. Il peut
arriver qu'un contour soit contenu dans un autre ! Le problème disparait avec la sélection
des sommets sauf si deux objets ont, à un moment donné de leur représentation, une position
écran identique pour l'un de leurs sommets, ce qui, en soi, est parfois judicieux d'exploiter !
Bien entendu, tout dépend si l'utilisateur peut remodeler l'objet graphique, en modifiant
la position d'un sommet, par exemple !
A plus !
Oui effectivement.
Cela me semble même encore plus simple que ma 1ere solution.
La construction des classes serait donc differente.
On oublie TSegment puisque les objets graphiques sont définis par leurs sommets.
Peut-être, pour une utilisation plus aisée, on peut même essayer de combiner les 2 représentations (de sorte à ne pas être limité aux sommets d'un objet, surtout si ils sont confondus).
Pour le cercle (respectivement la sphère), on peut utiliser le carré (le cube) qui le contient. Du coup sélectionner un cercle ou une sphère revient à sélectionner un carré ou un cube (qu'on pourra représenter graphiquement par une couleur sombre pour qu'il n'altére pas la vue globale de la scène (voir même utiliser un checkbox pour afficher ou cacher ces boites englobantes)). On se ramène ainsi à un problème plus facile que le n points de henderson peut-être.
Qu'en pensez-vous ?
Flo.
Pour bien cerner la problématique, il ne faut pas penser la sélection uniquement comme une recherche de l'objet pointé, mais aussi comme un accrochage.
Par exemple l'utilisateur doit pouvoir tracer une ligne d'un point à un autre qui est la projection perpendiculaire à un objet, ou qui est un point quelconque sur l'objet (accrochage "proche"). Dans ce cas, l'utilisateur ne pointera pas le sommet de l'objet mais la zonne de l'objet qui l'intéresse.
Je pense que les solutions sont complémentaires:
-la solution de Henderson pour les accrochages extrémités
-la solution de Flo pour le reste (accrochage proche, perpendiculaire, centre, milieu...etc...)
Le problème des objets confondus (ou si proches qu'ils semblent l'être) peut être résolu par la fonction "multipic": premier objet en surbrillance, passage au second par molette ou clic droit, validation de l'objet choisi par clic gauche.
Et sinon par quelle méthode as-tu géré les cercles ?
Plus j'y réfléchis et plus mon histoire de carré (ou cube) englobant me parait le meilleur compromis entre simplicité et efficacité pour sélectionner un cercle (ou une sphère). Avec un checkbox pour cacher ou afficher ces boites englobantes. Du coup cela te permettrais même de travailler avec des ellipses ou des ellipsoides.
Tu aurais une classe TCercle qui hériterait de la classe TCarre (ou TQuadrilatere), tu déduis les membres hérités de TCarre (les 4 sommets du carre ou ses 4 segments / vecteurs) à partir du centre et du rayon du cercle, sachant que le coté du carré sera égal à la moitié du rayon, etc.
Je suis curieux de voir le résultat final de ton travail (juste l'exe) si c'est possible
Tiens nous au courant de ton avancement.
Flo.
Je vous tiendrai au courant.
Mais c'est le début du début... j'essaie de rassembler les techniques avant de me lancer dans l'aventure.
Je ne comprend pas bien ton histoire de TCercle fils de TCarre... pourquoi tout ça?
Un cercle doit être reconnu (ta méthode par distance au centre me semble efficace) et doit proposer 5 poignées (le centre et 4 sur le cercle (droite, gauche, haut et bas)).
Pour mon projet, en particulier, un cercle restera toujours un cercle (pas d'étirement). Et je pense m'orienter vers une décomposition en segment (compatibilité de formats et intégration de courbes dans des polylignes).
Euh oui pardon autant pour moi,
j'avais pas bien saisi ton post sur l'importance de l'accrochage. J'étais resté sur la simple sélection d'objets graphiques.
D'où le drame ... .
Mille pardons.
A+.
Flo.
...ben, euh, yapadmal!!!
Je suis confus.
Ton aide m'a été précieuse et je réalise que je ne t'ai pas répondu: c'est bien de la 2D.
Merci donc,
Laurent
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager