Laden...

Objekte auf virtuellem Zeichenbereich erkennen bei PAN+ZOOM mit variablem Center-Point (z.B. m D2D1)

Erstellt von Tweak vor 9 Jahren Letzter Beitrag vor 9 Jahren 2.131 Views
T
Tweak Themenstarter:in
3 Beiträge seit 2014
vor 9 Jahren
Objekte auf virtuellem Zeichenbereich erkennen bei PAN+ZOOM mit variablem Center-Point (z.B. m D2D1)

Hallo,

ich habe in dem Programm, welches ich gerade entwickele, in der GUI eine Art großen virtuellen "Desktop"-Bereich der mittels halten der linken Maustaste und bewegen der Maus in alle Richtungen verschoben werden kann und mit dem Mausrad kann ich in den Inhalt hinein- oder hinauszoomen (man kann sich das von der Bedienung her so wie bei MS VISIO vorstellen). Die einzelnen Objekte auf dem Desktop kann ich wiederum mit der Maus anklicken und auf dem virtuellen Desktop verschieben. Da alle Objekte einen rechteckigen Rand haben ist die Abfrage der virtuellen Mauskoordinaten nicht schwierig.

Für das Rendern auf dem Bildschirm verwende ich Direct2D 1.0 und als Programmiersprache C++ (was für meine Fragestellung in diesem Forum aber angesichts meines reinen Logik-Problems nicht schlimm sein sollte, da es ja analog auch auf C# zutrifft sobald man z.B. selbiges mit SharpDX und C# realisieren möchte).

Das oben beschriebene funktioniert soweit alles prima. Allerdings mit einem gravierendem Haken: es klappt nur wenn ich das Zentrum der Zoom-Skalierung von meinem virtuellen Desktop auf die oberste linke Ecke (X = 0.0f / Y = 0.0f) setze. Dadurch ist das Zooming im Vergleich zu anderen Programmen unnatürlich in der Bedienung. Es wäre besser wenn ich das Zoom-Zentrum auf die aktuelle Mauszeiger-Position oder zumindest auf die Bildschirmmitte ausrichten könnte.

Doch sobald ich das Zoom-Zentrum auf andere Koordinaten als 0/0 setze gelingt es mir nicht mehr die Objekte zu erkennen auf die der Mauszeiger gerade zeigt.

Ich gehe derzeit wie folgt vor:

Vor dem Zeichnen auf dem virtuellen Desktop wende ich die folgende Matrix-Transformation an:

float zoomScale = (float)m_desktopZoom / 100.0f;
float zoomCenterX = 0.0f; // <-- !
float zoomCenterY = 0.0f; // <-- !
m_pDesktopRenderTarget->SetTransform(D2D1::Matrix3x2F::Translation(m_desktopPanX, m_desktopPanY) * D2D1::Matrix3x2F::Scale(D2D1::SizeF(zoomScale, zoomScale), D2D1::Point2F(zoomCenterX, zoomCenterY)));

m_desktopZoom enthält den Zoom-Faktor im Bereich von 50% bis 200%
m_desktopPanX und Y enthalten den Wert der aktuellen Verschiebung um den gerade auf dem Bildschirm sichtbaren Bereich auszuwählen

Da die Scale-Transformation in D2D1 einen Basiswert von 1.0f für 100% erwartet errechne ich vorher die Variable zoomScale in dem ich den Prozentwert durch 100 teile.

zoomCenterX und Y sollen eigentlich den Zoom-Mittelpunkt definieren, derzeit klappt aber wie gesagt nur mit 0/0.

Das spannende ist nun die Funktion zum ermitteln ob sich der Mauszeiger über einem so transformierten Objektes befindet (sie liefert ein TRUE zurück wenn sich der Mauszeiger innerhalb des Objektes befindet):

float zoomScale = (float)m_desktopZoom / 100.0f;
float objectLeft = currentDesktopObject->GetLeft() + m_desktopPanX;
float objectTop = currentDesktopObject->GetTop() + m_desktopPanY;
float objectRight = currentDesktopObject->GetLeft() + currentDesktopObject->GetWidth() + m_desktopPanX;
float objectBottom = currentDesktopObject->GetTop() + currentDesktopObject->GetHeight() + m_desktopPanY;
float scaledMouseX = m_mouseX / zoomScale; 
float scaledMouseY = m_mouseY / zoomScale;
return scaledMouseX >= objectLeft && scaledMouseX < objectRight && scaledMouseY >= objectTop && scaledMouseY < objectBottom;

Wie man sieht errechne ich als erstes wieder die zoomScale, anschließend addiere ich die Panning-Werte zu den Objekt Positions- und Größenwerten (aus currentDesktopObject, welches als Funktions-Übergabeparameter das jeweilige Objekt aus einer Funktions-übergeordneten For-Each-Schleife ist um alle Objekte auf ein Mausover zu prüfen), die aktuellen Mauskoordinaten teile ich durch den Zoom-Faktor.

Klappt wunderbar, bis ich das zoomCenter verschiebe... und es gelingt mir einfach nicht hier eine korrekte Formel zu finden wie ich die "virtuellen" Mauskoordinaten berechnen muss. Ich glaube das meine Schwierigkeit ist, dass ich bei verschobenen Zoom-Point ja von Objekten ausgehen muss die sich nicht nur nach unten rechts hin vergrößeren, sondern z.B. (beim Zoom-Mittelpunkt im Mittelpunkt des Desktops) geviertelt in alle 4 Himmelsrichtungen oder gar krumme Werte je Richtung je nach Zoom-Mittelpunkt?!

Trotz intensivem tagelangem probieren und lesen im Internet finde ich keine Lösung, ich stehe echt auf dem Schlauch... kann mir da wohl jemand bitte einen Tipp geben?

Einen guten Rutsch wünscht
Tweak

C
2.121 Beiträge seit 2010
vor 9 Jahren

Das zoomen an sich funktioniert?

Was genau geht denn nicht? Hast du ausprobiert wie sich der Fehler ändert wenn du das zoomCenter immer mehr von 0/0 weg bewegst? Das sagt evtl. auch schon was aus, also je weiter weg umso größer werden die Abweichungen der berechneten Koordinaten oder so ähnlich.

Rechne dir von Hand aus wo ein Objekt nach Zoom und Verschieben liegen muss und prüfe nach was dein Test macht. Stimmen die ganzen Werte die du verwendest? Wie du die berechnest sieht man nicht. Ich tippe der Fehler liegt in den Pan Werten und dann in den objectLeft etc. Werten. Kann es sein dass hier die Verschiebung durch das Zoomen noch nicht mit reingerechnet ist? Das würde erklären warum es nur beim zoomCenter 0/0 richtig funktioniert.

Du vermischst die Berechnungen in deinem Test. Ins Objekt fließt die Verschiebung rein, in die Maus der Zoom. Ich würde entweder die Objektkoordinaten in Bildschirmkoordinaten umrechnen, dann braucht bei der Maus kein Zoom mehr mit rein. Oder du rechnest die Mausposition in Objektoordinaten um.
Ersteres wäre dann besser wenn du den Objekten ihre aktuellen Koordinaten nach Zoom/Verschiebung neu berechnest, dann brauchst du das beim Maustest nicht immer tun.

Du musst dich festlegen auf was du diese ganzen Werte beziehen willst. Bedenke dass ein Zoom des Bilds auch wieder eine Verschiebung der Koordinaten bewirkt. Deswegen ist die Reihenfolge wichtig in der du Verschiebung und Zoom berechnest.

Ich würde die Pan Werte erst mal weglassen und nur mit dem Zoomwert und zwei sich entsprechenden Punkten arbeiten. Einer ist ein Punkt auf dem Bildschirm in Pixel. Der andere ist der Punkt im Bild, der auf diesen Bildschirmpunkt abgebildet wird. Einer davon ist dein zoomCenter.
Mit denen und dem Zoomwert kannst du dann Koordinaten von einem System in das andere umrechnen.

49.485 Beiträge seit 2005
vor 9 Jahren

Hallo Tweak,

in Anwenden von Transformationen in Direct2D wird verwiesen auf Matrix3x2F::Scale(D2D1_SIZE_F, D2D1_POINT_2F) method und dort sieht man wie gezoomt wird, wenn man einen centerPoint angibt. Man sieht, dass die Strecke vom Urprung bis zum centerPoint unverändert bleibt also gerade nicht skaliert wird. Das ist nach deiner Beschreibung nicht, was du willst.

Ich würde den centerPoint einfach (0,0) lassen und stattdessen die nötige Verschiebung auf M_desktopPanX/Y addieren.

Gedanklich würde ich dazu zunächst ein Transformation des Mauskoordinaten auf den Ursprung vornehmen, dann skalieren, und dann alles wieder um den gleichen Wert zurückverschieben.

herbivore

T
Tweak Themenstarter:in
3 Beiträge seit 2014
vor 9 Jahren

Vielen Dank für eure Antworten!

Das hat mir schon gute Denkanstösse gegeben, ich probiere die Tage ein bisschen herum und falls ich die Lösung finde poste ich sie hier 😃