Quelle est l'idée derrière la définition de rectangles à deux points? [fermé]


14

Ce n'est pas que cela n'a pas de sens, mais cela fonctionne simplement 99% du temps.

Souvent, dans les graphiques 2D, les rectangles sont initialisés, stockés et manipulés comme une paire de points. Dans aucune langue particulière,

class Rect:
   p1, p2: point

Il est plus logique de définir un rectangle comme deux valeurs x et deux valeurs y, comme ceci:

class Rect
   xleft, xright: int
   ytop, ybottom: int

Avec deux points, si à un endroit du code source vous voulez utiliser la valeur y du haut, vous devez dire rect.p1.y (hmmm, arrêtez et pensez, est-ce p1 ou p2) mais avec les quatre valeurs en tant que membres de données simples, c'est clair et direct: rect.ytop (aucune réflexion requise!) L'utilisation de deux points signifie qu'en traitant avec la verticale, vous devez emmêler l'horizontale; il y a une relation étrangère entre des éléments indépendants.

Comment est née cette idée en deux points et pourquoi persiste-t-elle? At-il un avantage sur les coordonnées nues x et y?

NOTE AJOUTÉE: Cette question est dans le contexte des rectangles alignés XY, comme dans les gestionnaires de fenêtres et les boîtes à outils GUI, pas dans le contexte des formes arbitraires dans l'application de dessin et de peinture.


8
Avez-vous écrit une quantité substantielle de code en utilisant Rects?

6
Un rectangle est défini par deux points, donc les représenter comme deux points a beaucoup de sens.
Adam Crossland

2
Un rectangle est plus naturellement défini comme une plage de valeurs x et une plage de valeurs y.
DarenW

2
Grande question! Je n'y ai jamais pensé moi-même, mais vous faites un argument intéressant! FWIW, Windows a un RECT comme vous le décrivez aussi (en haut, à gauche, en bas, à droite)
Dean Harding

3
Comment définissez-vous un rectangle à deux points? Est-il supposé n'avoir aucune rotation?
Nick T

Réponses:


8

Avez-vous considéré qu'il est moins sujet aux erreurs?

Si vous utilisez (Point1, Point2), il est alors très clair ce que vous spécifiez. Si vous fournissez 2 points, la seule erreur possible est que l'utilisateur a mélangé ses x et y lors de la construction des points car l'ordre des points n'a pas d'importance.

Si vous fournissez 4 entiers, alors si quelqu'un ne fait pas attention, il peut fournir (x1, x2, y1, y2) quand vous le souhaitez (x1, y1, x2, y2) ou vice versa. De plus, certaines API telles que la structure Rect de WCF définissent un rectangle comme (x, y, largeur, hauteur), ce qui pourrait alors créer de la confusion sur ce que (1, 2, 3, 4) signifie. Est-ce (x, y, w, h) ou (x1, y1, x2, y2) ou (x1, x2, y1, y2)?

Dans l'ensemble, (Point1, Point2) me semble un peu plus sûr.


3
Qu'en est-il de quelque chose comme Rect (xrange (x1, x2), yrange (y1, y2))? Cela semble être le summum de la sécurité et de l'élégance de l'utilisation des API.
DarenW

9

J'ai toujours aimé définir un rectangle comme un point + largeur et hauteur, où le point est le coin supérieur gauche du rectangle.

class Rect {
  float x, y;
  float width, height;
}

Et puis ajoutez les méthodes dont vous avez besoin pour récupérer les autres métriques. Comme la version Java


4
Est-ce que la hauteur y + supérieure, la hauteur y ou juste y?
Cameron MacFarland du

2
@Cameron MacFarland: Cela dépend du système de coordonnées de l'application, ce qui ne concerne pas un rectangle modeste.
Jon Purdy

2
@Martin Wickman: Quel est l'avantage par rapport à l'utilisation de 2 points?
Kramii

@Kramii: Un avantage est qu'il suffit de traduire un point si vous déplacez tout le rect. Btw, vous pouvez toujours calculer le "point manquant" si vous en avez besoin (compromis CPU / mémoire).
Martin Wickman

Cela apparaît aussi dans la vraie vie. Je trouve cela délicat car l'une des choses les plus courantes que je fais avec les rectangles est de tester si un point est contenu à l'intérieur. Le dessin est également courant. Dans les deux cas, l'addition doit être effectuée, ce qui dérange les compteurs de cycle d'horloge hautes performances comme moi.
DarenW

7

En fait, un rectagle n'est pas défini par 2 points. Un rectangle ne peut être défini par deux points que s'il est parallèle aux axes.

Il existe plusieurs façons de représenter des rectangles parallèles aux axes:

  1. Deux points opposés en diagonale
  2. Un point d'angle, hauteur et largeur
  3. Point central, demi-hauteur et largeur (peu commun, mais parfois utile).
  4. Comme deux coordonnées X et deux coordonnées Y

Pour (1), de nombreuses bibliothèques utilisent une convention pour déterminer les deux points utilisés - topLeft et bottomRight, par exemple.

Le choix de la représentation peut être motivé par l'objectif initial de la définition du rectangle, mais j'imagine que c'est souvent arbitraire . Les représentations sont équivalentes dans les informations qu'elles véhiculent. Ils diffèrent cependant par la facilité avec laquelle les propriétés du rectangle peuvent être calculées et la commodité avec laquelle les opérations peuvent être effectuées sur le rectangle.

Les avantages de la définition (1) par rapport aux autres comprennent:

  • Cohérence de l'API avec d'autres polygones, lignes, etc.
  • topLeft, bottomRight peut être transmis à toute méthode acceptant des points
  • Les méthodes de la classe Point peuvent être appelées sur topLeft, bottomRight
  • La plupart des propriétés peuvent être dérivées facilement, par exemple. bottomLeft, topRight, largeur, hauteur, centre, longueur diagonale, etc.

6

Eh bien, p1: Pointet p2: Pointchacun aura de toute façon deux intcoordonnées , alors votre classe ne revient-elle pas à la même chose?

Et si vous stockez ces deux points en tant Pointqu'objets de première classe , n'en tirez-vous pas un peu plus d'utilité? Dans la plupart des systèmes de coordonnées graphiques que je connais, les points sont sous-classés de cette manière pour créer une hiérarchie d'objets: point -> circle -> ellipseetc.

Donc, si vous créez un objet qui n'utilise pas la Pointclasse, vous avez dissocié cet objet du reste de la hiérarchie des classes.


1
L'avantage que je vois de la représentation de OP est que si vous voulez connaître la valeur y inférieure d'un rectangle, vous savez que c'est "ybottom", où, comme avec p1 / p2, vous devez déterminer laquelle est la plus basse. C'est à moins que vous garantissiez que p1 sera de toute façon les valeurs les plus basses.
Jason Viers

1
Bien qu'il soit vrai que les deux structures différentes se résument à quatre coordonnées, la version à deux points introduit un niveau étranger qui rassemble un x et un y, sans justification particulière à laquelle x va avec quel y. Je ne vois pas ce niveau supplémentaire comme fournissant un quelconque utilitaire, après de nombreuses années de programmation graphique.
DarenW

1
@ Jason: Bon point. Avec l' approche ytop/ ybottom, cependant, il devrait également y avoir une garantie quelque part qui ybottomest en fait en dessous ytop.
Apprenti du Dr Wily

Ou appelez-les y1 et y2, et utilisez min (y1, y2) et max (y1, y2) - bien sûr, ce serait encore plus maladroit que d'accéder par deux points p1, p2.
DarenW

la dénomination de top / bottom ne vous achète rien car rien n'empêche bottomx <topx, à moins que vous ne codiez spécifiquement pour cela. Je pense que cela ne ferait qu'ajouter à la confusion.
lkg

5

C'est pourquoi j'aime Delphi TRect. Il est défini comme un enregistrement variant (structure d'union en langage C) qui peut être interprété comme un point TopLeft et un point BottomRight, ou des entiers Top, Left, Bottom et Right, selon ce qui est plus pratique pour le moment.


1
Oui, fonctionnalité très utile.
Orbling

4

Sûrement, si vous définissez votre rectangle comme:

class Rect
{
    Point bottomLeft;
    Point topRight;
}

alors vous savez tout de suite quel point est lequel.

Encore mieux serait d'ajouter des propriétés supplémentaires qui vous ont permis de manipuler le rectangle de la manière dont vous aviez besoin pour votre application. Celles-ci ne feraient que mettre à jour la structure de données sous-jacente.

En ajoutant une transformation à la forme, vous pouvez orienter votre rectangle comme vous le souhaitez. Vous auriez toujours besoin d'un cadre de délimitation aligné sur l'axe pour des vérifications d'acceptation / rejet rapides :)

Cependant, si votre modèle autorise les rectangles dans n'importe quelle orientation sans appliquer de transformation, alors "en bas à gauche" et "en haut à droite" n'ont aucune signification, ce qui ramène à "p1" et "p2" (ou quelque chose d'équivalent).


Alors, que se passe-t-il lorsque vous faites pivoter le rectangle de 90 degrés? Suivez-vous maintenant deux points différents de ceux que vous étiez au départ, ou votre "topRight" est-il maintenant à gauche de "bottomLeft"?
Inaimathi

@Inaimathi - si vous ajoutez une matrice de transformation, vous pouvez garder le rectangle orienté avec les axes. Cependant, cela dépend de votre application.
ChrisF

2

je pense qu'il est plus logique qu'un rectangle soit représenté par une étendue x et y et un point; vous pouvez même placer le point de localisation au centre du rectangle afin qu'il soit indépendant de la rotation

mais il était probablement plus facile de le coder en deux points!


Comment serait-il plus facile de coder en deux points?
DarenW

@DaremW: les fonctions de bibliothèque de dessin rectangle typiques prennent des points en haut à gauche et en bas à droite comme arguments
Steven A. Lowe

Mais pourquoi ces API sont-elles conçues de cette façon? En plus d'imiter sans réfléchir les bibliothèques antérieures, c'est.
DarenW

@ DarenW: je suppose que les bibliothèques utilisent une routine de dessin au trait Bresenham, qui prend deux points en entrée et est très efficace
Steven A. Lowe

2

Je n'aime pas ça parce que nous avons rejeté un degré de liberté potentiel, qui permet essentiellement une rotation arbitraire. Un rectangle 2D général a cinq inconnues (degrés de liberté). Nous pourrions les spécifier comme les coordonnées d'un point, les longueurs des deux côtés qui forment un sommet avec ce point, et l'angle par rapport à l'horizontale de la première ligne (l'autre étant supposé avoir un angle supérieur de 90 degrés). Un nombre infini d'autres possibilités pourraient également être utilisées, mais il y a cinq quantités indépendantes qui doivent être spécifiées. Certains choix conduiront à une algèbre plus facile que d'autres, selon ce qui sera fait avec eux.


5
Il est important de réaliser qu'une structure rectangle "standard" n'est pas un rectangle mathématique défini de manière rigoureuse, mais une version simplifiée qui est toujours parallèle aux axes X et Y, car elle a été créée pour être utilisée pour une chose très spécifique: définir des régions rectangulaires pour un gestionnaire de fenêtres, qui sont (presque) toujours parallèles aux axes X et Y.
Mason Wheeler

+1, la gauche / droite / haut / bas est la structure prétrié, et a donc moins d' informations
Javier

Bon point, à propos des rectangles alignés xy. J'avais à l'esprit cela, ainsi que les étendues utilisées dans la modélisation 3D, et d'autres choses, mais toutes les xy (peut-être aussi -z) alignées.
DarenW

1

N'est-ce pas exactement la même chose que 2 points? Comment est-ce gênant ... la plupart des routines de dessin nécessitent des points, pas des composants x / y séparés.


1

La définition de rectangles comme paires de points vous permet de réutiliser le point comme sommet pour une autre forme. Juste une pensée...


Mais on dirait jouer avec un demi-deck, ne garder que deux points pour définir une forme à quatre coins. S'il vous arrive d'avoir besoin en haut à gauche, cool, mais si vous avez besoin en haut à droite, vous devez faire une capture de données sophistiquée, relativement parlant.
DarenW

s'il y a un accesseur qui est défini comme le point AX, le point BY et le point BX, le point AY, il est plus léger en mémoire / sur le disque. J'arrive où vous allez avec votre pensée de gamme, mais vous ne jouez pas avec la moitié du jeu, vous ne stockez simplement pas de données en double ... et les autres points à faire sont quelles sont les contraintes de mémoire? quelles sont les implications de la réimplémentation des outils de réoutillage dans tout le code interconnecté.
RobotHumans

et comme vous n'avez pas à faire de "travail" pour retirer les deux premiers sommets de la mémoire, c'est probablement plus rapide
RobotHumans

1

Je crois que c'est principalement pour établir l'uniformité entre toutes les primitives de forme.

Bien sûr, vous pouvez définir un rectangle de différentes manières, mais comment définir un triangle, une étoile ou un cercle de manière à utiliser des structures de données similaires?

Tous les polygones peuvent être définis par leurs points, avec une petite quantité de logique pour déterminer quoi faire avec les points.

Les bibliothèques graphiques opèrent principalement sur ces polygones en termes de sommets et d'arêtes, donc les points et les lignes entre eux, tous les calculs fonctionnent sur ces deux entités, bien cela et les facettes, mais cela en soi n'est qu'une fonction des arêtes.


1

En deux dimensions, le stockage d'un rectangle en deux points est plus clair que la définition d'un coin particulier et d'une largeur et d'une hauteur - considérez une largeur ou une hauteur négative, ou les calculs requis pour déterminer chaque option à partir de l'autre.

Effectuer des rotations sur un rectangle défini par des points est également beaucoup plus simple que celui défini avec un point plus une largeur et une hauteur.

Je m'attendrais à ce que l'encapsulation rende cette différenciation sans importance en tant qu'utilisateur de la classe.

Un rectangle doit être défini comme trois points pour être bien défini en 3 dimensions. Je ne suis pas entièrement sûr de la nécessité de définir un rectangle en 4 dimensions ou plus.


1

C'est complètement arbitraire. Vous avez besoin de quatre informations pour dessiner un rectangle. Le ou les concepteurs de la bibliothèque ont décidé de le représenter avec deux points (chacun avec une coordonnée xy), mais auraient pu facilement le faire avec x / y / w / h ou en haut / en bas / à gauche / à droite.

Je suppose que la vraie question du PO est: pourquoi ce choix a-t-il été fait?


1

Le choix des paramètres n'est important que pour les concepteurs / codeurs de bas niveau.

Les utilisateurs de haut niveau n'ont qu'à penser à:

  • IsPointInRect
  • Zone
  • Intersection (ou détourage)
  • HasOverlap (identique à Intersection.Area> 0)
  • Union (devient une liste de rectangles)
  • Soustraction (une liste de rectangles qui représentent le même ensemble de points qui est dans le rect A mais pas dans le rect B)
  • Transformer
    • Décalages en X et Y
    • Rotation (0, 90, 180, 270)
    • Mise à l'échelle en X et Y (voir note)
  • Syntaxe simple pour les propriétés Xmin, Xmax, Ymin, Ymax, Largeur, Hauteur afin que l'utilisateur n'ait pas besoin de connaître le choix exact des paramètres.

Remarque: afin de minimiser la perte de précision lors de la transformation de mise à l'échelle, il est parfois approprié d'implémenter une deuxième classe Rect qui utilise des coordonnées à virgule flottante, afin que les résultats intermédiaires puissent être stockés avec précision dans une séquence de transformations et uniquement arrondis à un entier dans le dernière étape.


0

Comme le dit @Steven, je pense que cela devrait être en termes d'un (x, y) point et d'un vecteur de taille (w, h). C'est parce qu'il est facile de tomber dans une ambiguïté. Supposons que vous ayez le rectangle rempli suivant commençant au point (0,0).

  012
0 XXX
1 XXX
2 XXX

C'est clairement sa largeur, sa hauteur sont (3,3), mais quel est son deuxième point? Est-ce (2,2) ou (3,3)?

Cette ambiguïté peut provoquer toutes sortes de problèmes.

J'ai appris il y a des années de dure qu'il est préférable de penser à coordonnées graphiques comme les lignes entre les pixels, et non comme les lignes les pixels sont sur . De cette façon, il n'y a aucune ambiguïté.


2
Les routines QuickDraw originales sur Mac (celles des années 1980) utilisaient le modèle mathématique selon lequel les points étaient infiniment petits. Les points sur l'écran se situent entre les pixels. Ainsi, une ligne tirée de (3,5) à (10,5) avait une longueur de 7 et occupait 7 pixels. Dans les coordonnées d'aujourd'hui, cette ligne aurait une longueur de 8.
Barry Brown

@Barry: Cela a du sens, car il utilisait beaucoup XOR, et si vous enchaînez des lignes, vous voulez qu'elles se connectent sans pixel manquant là où elles se rencontrent. J'ai en fait publié un papier siggraph sur le remplissage des polygones, en considérant les deux systèmes de coordonnées.
Mike Dunlavey

0
Pa(x,y)*-----------------------------------*Pb(x,y)
       |                                   |
       |                                   |
       |                                   |
       |                                   |
       |                                   |
       |                                   |
Pc(x,y)*-----------------------------------*Pd(x,y)

Nous pouvons définir les deux Pb et Pc ainsi:

Pb (Pd (x), Pa (y))

et

Pc (Pa (x), Pd (y))

Il n'est donc pas nécessaire de définir les quatre points en raison de la symétrie

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.