Welche Idee steckt dahinter, Rechtecke mit zwei Punkten zu definieren? [geschlossen]

14

Es ist nicht so, dass dies keinen Sinn ergibt, aber es funktioniert in 99% der Fälle nur umständlich.

In 2D-Grafiken werden Rechtecke häufig als Punktpaar initialisiert, gespeichert und bearbeitet. In keiner bestimmten Sprache,

class Rect:
   p1, p2: point

Es ist sinnvoller, ein Rechteck wie folgt als zwei x-Werte und zwei y-Werte zu definieren:

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

Mit zwei Punkten müsste man, wenn man irgendwo im Quellcode den y-Wert von oben verwenden möchte, rect.p1.y sagen (hmmm, halt und denk, ist es p1 oder p2), aber Mit den vier Werten als einfache Datenelemente ist es klar und direkt: rect.ytop (kein Denken erforderlich!) Die Verwendung von zwei Punkten bedeutet, dass Sie beim Umgang mit der Vertikalen die Horizontale verwickeln müssen. Es gibt eine irrelevante Beziehung zwischen unabhängigen Elementen.

Wie ist diese Zwei-Punkte-Idee entstanden und warum bleibt sie bestehen? Hat es Vorteile gegenüber bloßen x- und y-Koordinaten?

HINZUGEFÜGTER HINWEIS: Diese Frage bezieht sich auf XY-ausgerichtete Rechtecke, z. B. in Windows-Managern und GUI-Toolkits, und nicht auf beliebige Formen in der Zeichen- und Mal-App.

DarenW
quelle
8
Haben Sie eine erhebliche Menge an Code mit Rects geschrieben?
6
Ein Rechteck wird durch zwei Punkte definiert, daher ist es sehr sinnvoll, sie als zwei Punkte darzustellen.
Adam Crossland
2
Ein Rechteck wird natürlicher als ein Bereich von x-Werten und ein Bereich von y-Werten definiert.
DarenW
2
Gute Frage! Ich habe selbst nie darüber nachgedacht, aber Sie machen ein interessantes Argument! FWIW, Windows hat eine RECT wie Sie auch beschreiben (oben, links, unten, rechts)
Dean Harding
3
Wie definiert man ein Rechteck mit zwei Punkten? Wird angenommen, dass es keine Rotation gibt?
Nick T

Antworten:

8

Haben Sie gedacht, dass es weniger fehleranfällig ist?

Wenn Sie (Point1, Point2) verwenden, ist es sehr klar, was Sie angeben . Wenn Sie 2 Punkte angeben, ist der einzige mögliche Fehler, dass der Benutzer beim Konstruieren der Punkte das x und das y vertauscht hat, da die Reihenfolge der Punkte keine Rolle spielt.

Wenn Sie 4 Ganzzahlen angeben, kann jemand, der nicht aufpasst, (x1, x2, y1, y2) angeben, wann Sie möchten (x1, y1, x2, y2) oder umgekehrt. Einige APIs wie die Rect- Struktur von WCF definieren ein Rechteck als (x, y, width, height), was dann zu Verwirrung darüber führen kann, was (1, 2, 3, 4) bedeutet. Ist das (x, y, w, h) oder (x1, y1, x2, y2) oder (x1, x2, y1, y2)?

Alles in allem scheint mir (Punkt 1, Punkt 2) ein bisschen sicherer zu sein.

MIA
quelle
3
Was ist mit so etwas wie Rect (xrange (x1, x2), yrange (y1, y2))? Das scheint die ultimative Sicherheit und Eleganz für die API-Nutzung zu sein.
DarenW
9

Ich habe es immer gemocht, ein Rechteck als Punkt + Breite und Höhe zu definieren, wobei der Punkt die linke obere Ecke des Rechtecks ​​ist.

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

Fügen Sie dann die Methoden hinzu, die Sie zum Abrufen der anderen Metriken benötigen. Wie die Java- Version

Martin Wickman
quelle
4
Ist die obere y + Höhe, y-Höhe oder nur y?
Cameron MacFarland
2
@ Cameron MacFarland: Das hängt vom Koordinatensystem der Anwendung ab, das für ein kleines Rechteck keine Rolle spielt.
Jon Purdy
2
@ Martin Wickman: Was ist der Vorteil gegenüber der Verwendung von 2 Punkten?
Kramii
@ Kramii: Ein Vorteil ist, dass Sie nur einen Punkt übersetzen müssen, wenn Sie das gesamte Rechteck bewegen. Übrigens können Sie immer den "fehlenden Punkt" berechnen, wenn Sie ihn benötigen (Kompromiss zwischen CPU und Speicher).
Martin Wickman
Dies erscheint auch im wirklichen Leben. Ich finde es schwierig, da eines der häufigsten Dinge, die ich mit Rechtecken mache, der Test ist, ob ein Punkt darin enthalten ist. Zeichnen ist auch üblich. In beiden Fällen muss eine Addition durchgeführt werden, was Hochleistungstaktzähler wie mich stört.
DarenW
7

Tatsächlich wird ein Rechteck nicht durch 2 Punkte definiert. Ein Rechteck kann nur durch zwei Punkte definiert werden, wenn es parallel zu den Achsen ist.

Es gibt verschiedene Möglichkeiten, Rechtecke darzustellen, die parallel zu den Achsen sind:

  1. Zwei diagonal gegenüberliegende Punkte
  2. Ein Eckpunkt, Höhe und Breite
  3. Mittelpunkt, halbe Höhe und Breite (ungewöhnlich, aber manchmal nützlich).
  4. Als zwei X-Koordinaten und zwei Y-Koordinaten

Für (1) verwenden viele Bibliotheken eine Konvention, um zu bestimmen, welche zwei Punkte verwendet werden - beispielsweise topLeft und bottomRight.

Die Wahl der Darstellung mag vom ursprünglichen Zweck der Rechteckdefinition abhängen, aber ich stelle mir vor, dass dies oft willkürlich ist . Die Darstellungen entsprechen den Informationen, die sie enthalten. Sie unterscheiden sich jedoch in der Leichtigkeit, mit der Eigenschaften des Rechtecks ​​berechnet werden können, und in der Bequemlichkeit, mit der Operationen am Rechteck ausgeführt werden können.

Vorteile der Definition (1) gegenüber anderen sind:

  • Konsistenz der API mit anderen Polygonen, Linien usw.
  • topLeft, bottomRight kann an jede Methode übergeben werden, die Punkte akzeptiert
  • Methoden der Point-Klasse können in topLeft und bottomRight aufgerufen werden
  • Die meisten Eigenschaften können leicht abgeleitet werden, z. bottomLeft, topRight, width, height, center, diagonal length, etc.
Kramii
quelle
6

Na ja, p1: Pointund p2: Pointhaben die beidenint sowieso jeweils zwei Koordinaten ? Ist Ihre Klasse also nicht dasselbe?

Und wenn Sie diese beiden Punkte als erstklassige PointObjekte speichern , können Sie sie dann nicht noch nützlicher nutzen? In den meisten mir bekannten grafischen Koordinatensystemen werden Punkte auf diese Weise in Unterklassen unterteilt, um eine Hierarchie von Objekten zu erstellen point -> circle -> ellipse.

Wenn Sie also ein Objekt erstellen, das die PointKlasse nicht verwendet , haben Sie dieses Objekt vom Rest der Klassenhierarchie getrennt.

Robert Harvey
quelle
1
Der Vorteil, den ich bei der Darstellung von OP sehe, ist, wenn Sie den niedrigeren y-Wert eines Rechtecks ​​kennen möchten, wissen Sie, dass es "ybottom" ist, wobei Sie wie bei p1 / p2 herausfinden müssen, welcher niedriger ist. Dies ist der Fall, es sei denn, Sie garantieren, dass p1 auf jeden Fall die niedrigeren Werte aufweist.
Jason Viers
1
Während es wahr ist, dass die beiden unterschiedlichen Strukturen sich auf vier Koordinaten beschränken, führt die Zwei-Punkte-Version eine fremde Ebene ein, die ein x und ein y sammelt, ohne besondere Begründung, welches x zu welchem ​​y gehört. Ich sehe in dieser zusätzlichen Ebene nach vielen Jahren der Grafikprogrammierung keinerlei Nutzen.
DarenW
1
@ Jason: Guter Punkt. Mit dem ytop/ ybottom-Ansatz müsste es jedoch auch eine Garantie geben, ybottomdie tatsächlich darunter liegt ytop.
Dr. Wily's Apprentice
Oder rufen Sie sie y1 und y2 an und verwenden Sie min (y1, y2) und max (y1, y2) - das wäre natürlich ungeschickter als der Zugriff über zwei Punkte p1, p2.
DarenW
Die Benennung von top / bottom verhindert nichts, da nichts bottomx <topx verhindert, es sei denn, Sie codieren speziell dafür. Ich denke, es würde nur Verwirrung stiften.
lkg
5

Deshalb mag ich Delphi's TRect. Es ist definiert als ein Variantendatensatz (Union-Struktur in C-Speak), der entweder als TopLeft- und BottomRight-Punkt oder als Ganzzahl von Top, Left, Bottom und Right interpretiert werden kann, je nachdem, was momentan praktischer ist.

Mason Wheeler
quelle
1
Ja, sehr nützliche Funktion.
Orbling
4

Sicherlich, wenn Sie Ihr Rechteck wie folgt definieren:

class Rect
{
    Point bottomLeft;
    Point topRight;
}

dann wissen Sie sofort, welcher Punkt welcher ist.

Noch besser wäre es, zusätzliche Eigenschaften hinzuzufügen, mit denen Sie das Rechteck so bearbeiten können, wie Sie es für Ihre Anwendung benötigen. Diese würden einfach die zugrunde liegende Datenstruktur aktualisieren.

Indem Sie der Form eine Transformation hinzufügen, können Sie Ihr Rechteck so ausrichten, wie Sie möchten. Sie benötigen noch einen achsenausgerichteten Begrenzungsrahmen für schnelle Annahme- / Ablehnungsprüfungen :)

Wenn Ihr Modell jedoch Rechtecke in beliebiger Ausrichtung ohne Transformation zulässt, haben "unten links" und "oben rechts" keine Bedeutung, was zu "p1" und "p2" (oder etwas Äquivalentem) führt.

ChrisF
quelle
Was passiert also, wenn Sie das Rechteck um 90 Grad drehen? Verfolgen Sie jetzt zwei verschiedene Punkte als ursprünglich, oder befindet sich Ihr "topRight" jetzt links von "bottomLeft"?
Inaimathi
@Inaimathi - Wenn Sie eine Transformationsmatrix hinzufügen, können Sie das Rechteck an den Achsen ausrichten. Dies hängt jedoch von Ihrer Anwendung ab.
ChrisF
2

Ich halte es für sinnvoller, wenn ein Rechteck durch eine x- und eine y-Ausdehnung und einen Punkt dargestellt wird. Sie können den Positionspunkt sogar zur Mitte des Rechtecks ​​machen, damit er unabhängig von der Drehung ist

aber es war wahrscheinlich am einfachsten, es als zwei Punkte zu codieren!

Steven A. Lowe
quelle
Wie wäre es einfacher, zwei Punkte zu codieren?
DarenW
@ DaremW: Typische Funktionen der Draw-Rectangle-Bibliothek verwenden Punkte oben links und unten rechts als Argumente
Steven A. Lowe
Aber warum sind diese APIs so konzipiert? Das heißt, neben der sinnlosen Nachahmung früherer Bibliotheken.
DarenW
@DarenW: Ich vermute, dass die Bibliotheken eine Bresenham-Strichzeichnungsroutine verwenden, die zwei Punkte als Eingabe verwendet und sehr effizient ist
Steven A. Lowe
2

Ich mag es nicht, weil wir einen potenziellen Freiheitsgrad ausgeschöpft haben, der im Wesentlichen eine willkürliche Rotation zulässt. Ein allgemeines 2D-Rechteck hat fünf Unbekannte (Freiheitsgrade). Wir könnten sie als die Koordinaten eines Punktes, die Längen der beiden Seiten, die mit diesem Punkt einen Scheitelpunkt bilden, und den Winkel von der Horizontalen der ersten Linie angeben (die andere wird mit einem um 90 Grad größeren Winkel angenommen). Eine unendliche Anzahl anderer Möglichkeiten könnte ebenfalls verwendet werden, es gibt jedoch fünf unabhängige Größen, die spezifiziert werden müssen. Einige Entscheidungen führen zu einer einfacheren Algebra als andere, je nachdem, was mit ihnen gemacht wird.

Omega Centauri
quelle
5
Es ist wichtig zu wissen, dass eine "Standard" -Rechteckstruktur kein streng definiertes mathematisches Rechteck ist, sondern eine vereinfachte Version, die immer parallel zu den X- und Y-Achsen verläuft, da sie für eine ganz bestimmte Sache erstellt wurde: die Definition rechteckiger Bereiche für einen Fenstermanager, der (fast) immer parallel zur X- und Y-Achse ist.
Mason Wheeler
+1, die linke / rechte / obere / untere Struktur ist vorsortiert und hat daher weniger Informationen
Javier
Guter Punkt, ungefähr xy ausgerichtete Rechtecke. Ich hatte das im Hinterkopf und auch Ausmaße, wie sie in der 3D-Modellierung verwendet werden, und einige andere Dinge, aber alle xy (vielleicht auch -z) ausgerichtet.
DarenW
1

Ist das nicht genau dasselbe wie 2 Punkte? Wie ist das umständlich ... die meisten Zeichenroutinen erfordern Punkte, keine separaten x / y-Komponenten.

GroßmeisterB
quelle
1

Wenn Sie Rechtecke als Punktpaare definieren, können Sie den Punkt als Scheitelpunkt für eine andere Form wiederverwenden. Nur ein Gedanke...

RobotHumans
quelle
Aber es scheint, als würde man mit einem halben Deck spielen und nur zwei Punkte behalten, um eine viereckige Form zu definieren. Wenn Sie links oben etwas Cooles brauchen, aber wenn Sie rechts oben etwas brauchen, müssen Sie relativ gesehen einige ausgefallene Daten erfassen.
DarenW
Wenn es einen Accessor gibt, der als Punkt AX Punkt BY und Punkt BX Punkt AY definiert ist, wird er im Speicher / auf der Festplatte leichter. Ich denke, er speichert halb so viele Scheitelpunkte und zeigt nur auf sie (abhängig von der maximalen Rastergröße). Ich verstehe, wohin Sie mit Ihrem Reichweitengedanken gehen, aber Sie spielen nicht mit dem halben Deck, Sie speichern nur keine doppelten Daten ... und die anderen Punkte, die gemacht werden müssen, sind die Speicherbeschränkungen? Welche Auswirkungen hat die Neuimplementierung auf das Umrüsten von Korrekturen in allen miteinander verbundenen Codes?
RobotHumans
und da Sie keine "Arbeit" machen müssen, um die ersten beiden Scheitelpunkte aus dem Speicher zu bekommen, ist es wahrscheinlich schneller
RobotHumans
1

Ich glaube, es geht hauptsächlich darum, Gleichförmigkeit zwischen allen Formprimitiven herzustellen.

Natürlich können Sie Rechtecke auf viele verschiedene Arten definieren, aber wie definieren Sie ein Dreieck, einen Stern oder einen Kreis so, dass ähnliche Datenstrukturen verwendet werden können?

Alle Polygone können durch ihre Punkte definiert werden, wobei eine kurze Logik benötigt wird, um zu bestimmen, was mit den Punkten geschehen soll.

Grafikbibliotheken bearbeiten diese Polygone in erster Linie in Bezug auf Eckpunkte und Kanten, sodass Punkte und die Linien zwischen ihnen bei allen Berechnungen auf diese beiden Features angewendet werden, also auf das und die Facetten, aber das ist nur eine Funktion der Kanten.

Orbling
quelle
1

In zwei Dimensionen ist das Speichern eines Rechtecks ​​als zwei Punkte klarer als das Definieren einer bestimmten Ecke und einer Breite und Höhe. Berücksichtigen Sie negative Breite oder Höhe oder die Berechnungen, die erforderlich sind, um die einzelnen Optionen voneinander zu bestimmen.

Das Durchführen von Rotationen auf einem durch Punkte definierten Rechteck ist auch viel einfacher als bei einem Punkt plus Breite und Höhe.

Ich würde erwarten, dass die Verkapselung diese Unterscheidung als Benutzer der Klasse unwichtig macht.

Ein Rechteck sollte aus drei Punkten bestehen, die in drei Dimensionen gut definiert sind. Ich bin mir nicht ganz sicher, ob ein Rechteck in vier oder mehr Dimensionen definiert werden muss.

Armand
quelle
1

Es ist völlig willkürlich. Sie benötigen vier Informationen, um ein Rechteck zu zeichnen. Die Bibliotheksdesigner haben beschlossen, es mit zwei Punkten (jeweils mit einer xy-Koordinate) darzustellen, hätten es aber auch problemlos mit x / y / w / h oder oben / unten / links / rechts tun können.

Ich nehme an, die eigentliche Frage des OP lautet: Warum wurde diese besondere Wahl getroffen?

Barry Brown
quelle
1

Die Auswahl der Parameter ist nur für die Designer / Programmierer auf niedriger Ebene wichtig.

Hochrangige Benutzer müssen nur über Folgendes nachdenken:

  • IsPointInRect
  • Bereich
  • Schnittpunkt (oder Ausschnitt)
  • HasOverlap (wie Intersection.Area> 0)
  • Union (wird eine Liste von Rechtecken)
  • Subtraktion (eine Liste von Rechtecken, die die gleiche Punktmenge darstellen, die in Rect A, aber nicht in Rect B enthalten ist)
  • Verwandeln
    • Verschiebung in X und Y
    • Drehung (0, 90, 180, 270)
    • Skalierung in X und Y (siehe Hinweis)
  • Einfache Syntax für die Eigenschaften Xmin, Xmax, Ymin, Ymax, Width, Height, sodass der Benutzer die genaue Auswahl der Parameter nicht kennen muss.

Hinweis: Um den Genauigkeitsverlust während der Skalierungstransformation zu minimieren, ist es manchmal angebracht, eine zweite Rect-Klasse zu implementieren, die Gleitkommakoordinaten verwendet, damit Zwischenergebnisse in einer Folge von Transformationen genau gespeichert und in der nur auf ganze Zahlen gerundet werden können letzter Schritt.

rwong
quelle
0

Wie @Steven sagt, sollte es sich um einen (x, y) Punkt und einen (w, h) Größenvektor handeln. Das ist, weil es leicht ist, in eine Mehrdeutigkeit zu geraten. Angenommen, Sie haben das folgende ausgefüllte Rechteck ab Punkt (0,0).

  012
0 XXX
1 XXX
2 XXX

Klar ist es Breite, Höhe sind (3,3), aber was ist der zweite Punkt? Ist es (2,2) oder (3,3)?

Diese Mehrdeutigkeit kann alle Arten von Problemen verursachen.

Ich habe gelernt , vor den harten Weg Jahren , dass es besser ist , von grafischen Koordinaten wie die Linien zu denken , zwischen den Pixeln, nicht wie die Linien sind die Pixel auf . Auf diese Weise gibt es keine Mehrdeutigkeit.

Mike Dunlavey
quelle
2
Die ursprünglichen QuickDraw-Routinen auf dem Mac (die aus den 1980er Jahren stammten) verwendeten das mathematische Modell, dass die Punkte unendlich klein waren. Die Punkte auf dem Bildschirm liegen zwischen den Pixeln. Eine von (3,5) nach (10,5) gezogene Linie hatte also eine Länge von 7 und besetzte 7 Pixel. In heutigen Koordinaten hätte diese Linie eine Länge von 8.
Barry Brown
@Barry: Das ist sinnvoll, da XOR häufig verwendet wird und wenn Sie Linien aneinander reihen, möchten Sie, dass sie eine Verbindung herstellen, ohne dass ein Pixel fehlt, auf dem sie sich treffen. Ich habe tatsächlich eine Siggraph-Arbeit zum Füllen von Polygonen unter Berücksichtigung beider Koordinatensysteme veröffentlicht.
Mike Dunlavey
0
Pa(x,y)*-----------------------------------*Pb(x,y)
       |                                   |
       |                                   |
       |                                   |
       |                                   |
       |                                   |
       |                                   |
Pc(x,y)*-----------------------------------*Pd(x,y)

Wir können sowohl Pb als auch Pc folgendermaßen definieren:

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

und

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

Aus Symmetriegründen müssen also nicht alle vier Punkte definiert werden

Dunkle Nacht
quelle