![]() |
Pixelkoordinaten einer Linie
Hi Leute,
ich will eine Prüfung durchführen, ob sich zwischen zwei Punkten ein Punkt befindet und habe mir gedacht eine Linie zwischen diesen zwei Punkten zu zeichnen:
Delphi-Quellcode:
Kann ich mir die Koordinatenliste aller einzelnen Punkte ausgeben lassen oder gibt es eine Möglichkeit eine "virtuelle" Linie zu erzeugen und deren Koordinaten sich ausgeben zu lassen?
image1.canvas.moveto(10,10);
image1.canvas.lineto(20,20); Gruß Calculon -- |
Re: Pixelkoordinaten einer Linie
Stichwort: Lineare Funktion
Jede Linie (naja, außer einer senkrechten) lässt sich als lineare Funktion darstellen. Eine lineare Funktion ist eine Funktion f(x)=mx+t. Wenn du also zwei Punkte P1(x1|y1) und P2(x2|y2) hast, kannst du m und t folgendermaßen berechnen: t=y1 m=(y2-y1)/(x2-x1) Wenn du jetzt einen Punkt P3(x3|y3) hast, musst du nur noch die Koordinaten einsetzen, wenn also folgende Gleichung gilt, liegt P3 auf der Gerade P1P2: y3=x3*m+t Wenn du wissen willst, ob P3 auf der Strecke [P1P2] liegt, musst du noch überprüfen, ob x1<=x2<=x3. Eine Ausname ist, wie gesagt, eine senkrechte Gerade (also mit y1=y2), da musst du überprüfen, ob y3=y1. Bei einer senkrechten Strecke musst du außerdem noch überprüfen ob y1<=y2<=y3. |
Re: Pixelkoordinaten einer Linie
Vielen Dank für die Lehrstunde! Setze so das um! Muss mal anmerken: :dp:
|
Re: Pixelkoordinaten einer Linie
Hi,
ich würde zuerst den Winkel zwischen dem Startpunkt und dem Endpunkt der Linie errechnen. Dann würde ich den Winkel zwischen dem Startpunkt und dem zu Prüfenden Punkt ermitteln und die beiden Winkel vergleichen: Wenn sie nicht überinstimmen, kann auch der Punkt nicht auf der Linie liegen. Wenn der Winkel jedoch überinstimmt, prüfst du, wie weit er vom Anfangs- und Endpunkt der Linie entfernt ist: die Distanz darf bei beiden maximal die Länge der Linie betragen, ansonsten ist er nicht auf der linie. Achtung: Ich bin nur ein Siebt-(bald Acht-)klässler, kann also durchaus sein, dass es einfachere/schnellere Methoden gibt, um das zu prüfen :wink: //Edit: da war wohl jemand schneller :/ |
Re: Pixelkoordinaten einer Linie
Geht auch mit Vektoren.
Man zieht einfach eine Gerade durch die 2 Punkte und Prüft, ob der 3. Punkt auch auf der Geraden Liegt. Gerade: [x y] = [p1.x p2.y] + lambda * [p2.x-p1.x p2.y-p2.y] Das ist die Gerade. Je nachdem welche Zahl man für lambda einsetzt, berechnet man einen anderen Punkt auf der Geraden. Gibt es ein lambda, durch das man den 3. Punkt erhält, liegt dieser auf der Geraden. (Achtung: muss nicht zwischen den Punkten liegen. Kann man später aber mit dem x-Wert überprüfen) Gleichungssystem: p3.x = p1.x + lambda * (p2.x-p1.x) p3.y = p1.y + lambda * (p2.y-p1.y) Oder kurz:
Delphi-Quellcode:
Oder vielleicht auch das, wenn das obere nicht funktionieren sollte:
if (p1.x-p3.x)/(p1.x-p2.x) = (p1.y-p3.y)/(p1.y-p2.y) then -> auf der Geraden
Delphi-Quellcode:
Keine Ahnung ob das jetzt so stimmt, ist schon etwas her, dass ich das in der Schule gelernt habe, aber müsste schon funktionieren. ;)
if abs((p1.x-p3.x)/(p1.x-p2.x)-(p1.y-p3.y)/(p1.y-p2.y))<0.001 then -> auf der Geraden
|
Re: Pixelkoordinaten einer Linie
@NamenLozer: Das geht natürlich auch, aber dafür braucht man deutlich mehr Rechenschritte und Arcus-Sinus/Cosinus usw., ich glaube fast, dass meine Methode einfacher ist. (Und schneller)
|
Re: Pixelkoordinaten einer Linie
@3/8: Ja, das habe ich mir schon fast gedacht, dass es auch schneller geht, aber so komischen Kram hatten wir in der Schule leider noch nicht^^. Und wenn wir es dann kriegen, hab ich es wahrscheinlich irgendwie schon so gelernt, ist eigentlich immer so :roll:
Den ganzen sin/cos kram habe ich mir auch selber beigebracht, offiziell haben wir sogar dieses Schuljahr erst Minuszahlen kennen gelernt (Und dann wundern sich alle über Pisa :wall: ) |
Re: Pixelkoordinaten einer Linie
Die Frage ist nur, ob die Antwort zum Problem passt. Leider wurde es nicht genau beschrieben.
Interessant ist doch, ob es hier um Pixel oder Punkte geht. (Integer oder real) die vorgeschlagenen verfahren laufen auf das zweite herraus, wobei der Borg nicht erklärt hat, wann eine Gleichung wahr ist, also wann zwei Zahlen gleich sind. Wenn das Ganze also ein Zeichenprogramm werden soll, wird man mit den Verfahren nicht glücklich, da sie alles sehr genau nehmen. @ torpedo: ''(Achtung: muss nicht zwischen den Punkten liegen. Kann man später aber mit dem x-Wert überprüfen) '' Überleg nochmal, was das Lamda eigentlich bedeutet und ob man nicht vielleicht auch nur damit entscheiden kann, ob der Punkt zwischen den beiden andere liegt. @ 3of8: besuchst du nicht diese Fernuni Hagen und hörst Mathe? In dem Fall solltest du f(x)=mx+c (c<>0) wirklich nicht als lineare Funktion bezeichnen. @ NamensLozer: ein Sinus/Cosinus dauert etwa 200 Ticks, eine Multiplikation etwa 8, was du brauchst ist aber der asin, der dann schon mal seine 2000 Ticks braucht. Wenn es also anders geht, sollte man die Finger von den Trigonometrischen Funktionen lassen |
Re: Pixelkoordinaten einer Linie
Zitat:
|
Re: Pixelkoordinaten einer Linie
Erstmal studiere ich Informatik, die Mathematik, die ich da habe, ist in etwa so, dass ich mich jedes mal freue, wenn ich eine Zahl sehe.
Also für mich ist f(x)=mx+t eine ziemlich lineare Funktion. An der Schule lernt man eine lineare Funktion als f(x)=mx+t kennen. Wenn man das ganze jetzt streng mit Vektorräumen macht, ist es natürlich keine lineare Funktion mehr, da schon das erste Axiom (Homogenität) nicht mehr erfüllt ist. (a*(mx+t)<>m(a*x)+t). Wir wollen ja nicht gleich mit Kanonen auf Spatzen schießen, indem wir das ganze hier auf Universitätsniveau heben. ;) |
Re: Pixelkoordinaten einer Linie
Zitat:
Zitat:
So und da ich hier nicht nur meckern will, kommt hier mein Vorschlag: (Da du nichts über dein Alter geschrieben hast, kann es sein, dass du die Lösung noch nicht verstehst, das ganze ist auf Abiniveau) Du beginnst mit dem Vorschlag von torpedo und stellst so eine Parametergleichung auf. Jetzt kannst du jeden Punkt auf der Geraden durch einen Wert von lambda bezeichnen. Jetzt nimmst du dir den satz von Pythagoras und bestimmst den Punkt auf der Geraden, der am nächsten zum untersuchten Punkt liegt. Jeder Punkt ist als (a,b)+p*(v,w) darstellbar, dein untersuchter Punkt liege bei (x,y). Jetzt also schnell d=sqrt( (a+p*v-x)^2 + (b+p*w-y)) nach p abgelitten, Null setzen, und deinen MinimalAbstand finden. Jetzt testest du, ob der Parameter zwischen Null und Eins liegt. Warum, kann dir Torpedo erlären. Wenn du also so einen Punkt gefunden hast, schaust du nach, ob der Abstand an dieser Stelle kleiner als ein vorher von dir festgelegter Wert ist, z.B. 2Px. In diesem Fall liegt dein Punkt auf der Geraden, sonst nicht. Hier hast du den Vorteil, dass du recht exakt einstellen kannst, wie weit dein Punkt von der Geraden entfernt sein darf, und immer noch drauf liegt. Wenn du diesen Wert änderst, könntest du z.B. auch auf dickere Linien testen, was mit den anderen Verfahren nicht möglich ist, da sie ihr Augenmerk auf die mathematische Exaktheit gelegt haben. Die Rechnung oben mit dem Ableiten und Nullsetzen ist nicht allzu schön, da du noch eine quadratische Gleichung lösen musst, (glaube ich), den Code könnte ich dir aber geben, da ich sowas für meine Physikengine berechnen musste. Alternativ kannst du das gleiche Verfahren auch auf Mittelstufen Niveau fahren, ohne das es am ergebniss etwas ändert: Du stellst eine Geradengleichung wie 3of8 auf (f(x)=mx+c). Jetzt nimmst du eine weitere Gerade auf, die durch den zu betrachtenden Punkt geht, aber die Steigung -1/m hat. (g(x)=-1/m*x+d) Diese Gerade verläuft senkrecht durch die erste Gerade. (einfach mal ausprobieren). Jetzt berechnest du den Schnittpunkt der Geraden und überprüfst, ob er zwischen den beiden Punkten liegt, die die erste Gerade definieren. (einfach Kontrollieren, ob sein x (und y) wert zwischen den Werten der Punkte liegt) Falls er nicht drauf liegt, bist du hier fertig und der zu betrachtende Punkt liegt nicht zwischen den Punkten. Sonst: Dieser Punkt ist der Punkt auf der geraden, der den kleinsten Abstand zum untersuchten Punkt hat. Diesen Abstand kannst du einfach mit dem Pythagoras berechnen. Dieser Wert, den du jetzt in der Hand hällst, ist der Abstand des Punkts zu Geraden. Wenn der Punkt mathematisch exakt auf der Linie sein soll, testest du den Wert auf Gleichheit mit Null, wenn du eine breitere Linie hast, einfach darauf, ob er kleiner als die halbe Linienbreite ist. Damit hast du ein auf die Linienbreite anpassbares Verfahren auf Mittelstufen Niveau. (Nur für senkrechte und waagrechte Linien musst du eine Sonderbehandlung einbauen, aber die ist wirklich einfach) |
Re: Pixelkoordinaten einer Linie
Zitat:
Zitat:
Meine Function sieht nun so aus und scheint zu funktionieren:
Delphi-Quellcode:
Das mit den Vektoren werd' ich mir mal auch genauer anschauen. Auf jeden Fall Danke für die Hilfe!
function TSpieler.FreieSicht(x1,y1,x2,y2,nummer:integer):boolean;
var m: double; // <-- Steigungskonstante x3, y3: integer; // <-- Spielerkoordinaten begin x3 := mdaten.daten[nummer].X; y3 := mdaten.daten[nummer].Y; // Wenn P1 und P2 senkrecht stehen: if (x1 = x2) and (x3 = x1) then begin if ((y3 >= y1) and (y3 <= y2)) or ((y3 <= y1) and (y3 >= y2)) then begin result := true; // innerhalb der Strecke [P1P2] end else begin result := false; // außerhalb der Strecke [P1P2] end; exit; end; m := (y2-y1)/(x2-x1); // Berechnung der Steigung if (y3 = round(x3 * m + y1)) then begin if ((x3 >= x1) and (x3 <= x2)) or ((x3 <= x1) and (x3 >= x2)) then begin result := true; // innerhalb der Strecke [P1P2] end else begin result := false; // außerhalb der Strecke [P1P2] end; exit; end else begin result := false; exit; end; end; Gruß Calculon -- |
Re: Pixelkoordinaten einer Linie
Delphi-Quellcode:
Das finde ich etwas einfacher. ;)
function TSpieler.FreieSicht(x1,y1,x2,y2,nummer:integer):boolean;
var m: double; // <-- Steigungskonstante x3, y3: integer; // <-- Spielerkoordinaten begin x3 := mdaten.daten[nummer].X; y3 := mdaten.daten[nummer].Y; // Wenn P1 und P2 senkrecht stehen: if (x1 = x2) and (x3 = x1) then Result:=y3 >= y1) and (y3 <= y2)) or ((y3 <= y1) and (y3 >= y2) else begin m := (y2-y1)/(x2-x1); // Berechnung der Steigung Result:=((x3 >= x1) and (x3 <= x2)) or ((x3 <= x1) and (x3 >= x2)) and (y3 = round(x3 * m + y1)); end; end; |
Re: Pixelkoordinaten einer Linie
Zitat:
Je näher an Punkt 1, desto näher ist Lambda bei 0 und je näher bei Punkt 2, desto näher ist es bei 1. Grund: Der Richtungsvektor hat seinen Startpunkt in P1 und die Spitze in P2. Also wenn man ihn mit 1 multipliziert bleibt er gleich lang und zeigt weiterhin auf P2. Wenn man ihn mit 0 multipliziert schrumpft er auch auf 0 und bleibt beim Startpunkt P1. |
Re: Pixelkoordinaten einer Linie
hast du damit auch mal ein paar Testläufe gemacht? Also, wenn dein Spieler bei 0/0 steht, nach 100/100 schaut, und du den Punkt 49/50 testest?
Delphi-Quellcode:
Nur so: vielleicht solltest du 'Daten' eher in 'Position' umbenennen, sonst wirds unübersichtlich.
x3 := mdaten.daten[nummer].X;
y3 := mdaten.daten[nummer].Y; |
Re: Pixelkoordinaten einer Linie
Zitat:
Zitat:
Gruß Calculon -- |
Re: Pixelkoordinaten einer Linie
Zitat:
|
Re: Pixelkoordinaten einer Linie
Hallo,
um noch einmal zur Ausgangsfrage nach einer "virtuellen" Linie zurückzukommen: der folgende Code verwendet die GDI-Funktion ![]()
Delphi-Quellcode:
Das Laufzeitverhalten dürfte schlechter als das der bereits vorgestellten Lösungen sein. Falls eine große Anzahl von Linien überprüft werden muß, sollte man vielleicht einen bounding box test vorschalten.
type
PHitInfo = ^THitInfo; THitInfo = record px, py : Integer; accuracy : Integer; hit : Boolean; end; procedure HitTest (X, Y: Integer; lpData: LParam); stdcall; begin with PHitInfo(lpData)^ do if (Sqr(px - X) + Sqr(py - Y) <= Sqr(accuracy)) then hit := True; end; // P1, P2 beschreiben die Linie // x, y sind die Koordinaten des zu testenden Punktes // accuracy ist eine (optionale) Empfindlichkeit für den Test (0=exakt) function LineHit (const P1, P2: TPoint; x, y: Integer; accuracy: Integer = 0): Boolean; var Info : THitInfo; begin Info.px := x; Info.py := y; Info.accuracy := accuracy; Info.hit := False; LineDDA(P1.X, P1.Y, P2.X, P2.Y, @HitTest, Integer(@Info)); Result := Info.hit; end; Gruß Hawkeye |
Re: Pixelkoordinaten einer Linie
Habe jetzt mal das mit den Vektoren und der Differenzialrechnung durchgerechnet:
Delphi-Quellcode:
Scheint auch zu funktionieren. Das ist dann Lambda für den Punkt auf der Geraden, der P3 am nächsten ist.
lambda := (sqr(p1.x)-p1.x*(p2.x+p3.x)+p2.x*p3.x+(p1.y-p2.y)*(p1.y-p3.y))/(sqr(p1.x)-2*p1.x*p2.x+sqr(p2.x)+sqr((p1.y-p2.y)));
|
Re: Pixelkoordinaten einer Linie
könntest du das mal ausführlicher hinschreiben? Mich wundert besonders die Wurzel aus einzelnen Koordinaten.
|
Re: Pixelkoordinaten einer Linie
Zitat:
|
Re: Pixelkoordinaten einer Linie
Ich habe das ganze einfach im Taschenrechner eingegeben und ableiten lassen (dauert ganz schön lange...) dann auf 0 gesetzt und auf lambda auflösen lassen.
Dann ist die oben genannte Formel rausgekommen. Habe es auch durchgetestet. Ich kann die Punkte drehen und plazieren wie ich will, es scheint immer zu stimmen. Den Abstand zur Geraden rechne ich aus, indem ich noch einen Vektor vom Punkt P4 auf der Geraden (den ich mit Lambda ausgerechnet habe) zum untersuchten Punkt erstelle und den Betrag berechne: abstand := sqrt(sqr(p3.x-p4.x)+sqr(p3.y-p4.y)); Als Quellcode: (nur schnell runtergetippt, nicht getestet!)
Delphi-Quellcode:
Kann sein dass Delphi hier mit Integer rechnet (wegen TPoint), dann kommt das falsche raus. Ansonsten sollte es stimmen.
function Abstand (const p1, p2, p3: TPoint): double;
var p4: TPoint; lambda: double; begin lambda := (sqr(p1.x)-p1.x*(p2.x+p3.x)+p2.x*p3.x+(p1.y-p2.y)*(p1.y-p3.y))/(sqr(p1.x)-2*p1.x*p2.x+sqr(p2.x)+sqr((p1.y-p2.y))); p4.x := p1.x+lambda*(p2.x-p1.x); p4.y := p1.y+lambda*(p2.y-p1.y); Result := sqrt(sqr(p3.x-p4.x)+sqr(p3.y-p4.y)); end; |
Re: Pixelkoordinaten einer Linie
sqr...sqrt...kann doch mal passieren...
Genau so hatte ich mir das vorgestellt. Wobei die Variante mit den Geraden auch noch per Hand einfach zu berechnen ist. Du ignorierst aber noch das Lambda aus [0,1] sein muss. Wenn Lambda nicht in diesem Intervall liegt, müsstest du irgendeinen Fehler zurückgeben, z.B. den Abstand auf -1 setzen, dass klar ist, dass der Punkt auf keiner noch so breiten Linie liegt. Aber ein bischen Arbeit soll der Fragensteller auch noch haben. Welchen Taschenrechner hast du da drauf losgelassen? So ein kleines Rechnzentrum wie den Ti 92 Voyage? |
Re: Pixelkoordinaten einer Linie
Habe 10 Minuten den TI 89 Titanium rechnen lassen. Als er dann immer noch nicht fertig war habe ich die Testversion von Derive6 runtergeladen und es damit lösen lassen (weniger als 1 Sekunde :shock:).
|
Re: Pixelkoordinaten einer Linie
Delphi-Quellcode:
function Abstand (const p1, p2, p3: TPoint): double;
var p4: TPoint; lambda: double; begin lambda := (sqr(p1.x)-p1.x*(p2.x+p3.x)+p2.x*p3.x+(p1.y-p2.y)*(p1.y-p3.y))/(sqr(p1.x)-2*p1.x*p2.x+sqr(p2.x)+sqr((p1.y-p2.y))); p4.x := p1.x+lambda*(p2.x-p1.x); p4.y := p1.y+lambda*(p2.y-p1.y); Result := sqrt(sqr(p3.x-p4.x)+sqr(p3.y-p4.y)); end; Zitat:
Delphi-Quellcode:
Funktioniert super! Jetzt hab' ich noch'n Verständnisproblem. Die Abweichung von 0 wird hier mathematisch angegeben. Kann ich die Werte in Pixel umrechnen? Wie kann ich Toleranzen definieren?
function Abstand (const x1, y1, x2, y2, x3, y3: Integer): double;
var x4, y4: double; lambda: double; begin lambda := (sqr(x1)-x1*(x2+x3)+x2*x3+(y1-y2)*(y1-y3))/(sqr(x1)-2*x1*x2+sqr(x2)+sqr((y1-y2))); x4 := x1+lambda*(x2-x1); y4 := y1+lambda*(y2-y1); Result := sqrt(sqr(x3-x4)+sqr(y3-y4)); end; Bsp.: P1(0,0), P2(200,200); P3(100,101) Die function liefert: 0,707.. Das heißt der Punkt liegt mathematisch (numerisch) nicht exakt zwischen P1 und P2, jetzt hab' ich aber keine pixelmäßige Vorstellung der Abweichung (heißt das ca. 70% eines Pixels Abweichung?). [edit] Hab's jetzt so und funktioniert wunderbar. Nochmals ein drittes Dankeschön an die ganzen Antworten!
Delphi-Quellcode:
[/edit]
function TSpieler.FreieSicht(const x1,y1,x2,y2,dicke,spielernummer: Integer): boolean;
var x3, y3: integer; x4, y4, lambda: double; begin x3 := mdaten.daten[spielernummer].X; y3 := mdaten.daten[spielernummer].Y; lambda := (sqr(x1)-x1*(x2+x3)+x2*x3+(y1-y2)*(y1-y3))/(sqr(x1)-2*x1*x2+sqr(x2)+sqr((y1-y2))); x4 := x1+lambda*(x2-x1); y4 := y1+lambda*(y2-y1); // edit: (Danke für den Hinweis @Nikolas) {if not (((x3 >= x1) and (x3 <= x2)) or ((x3 <= x1) and (x3 >= x2))) then begin result := false; exit; end;} // <-- nö, passt au no net { muss mal doch genauer überlegen, wie man die Eingrenzung durchführen muss; } // /edit if round(sqrt(sqr(x3-x4)+sqr(y3-y4))) <= dicke then result := true else result := false; end; Gruß Calculon -- |
Re: Pixelkoordinaten einer Linie
Zitat:
schau mal was rauskommt, wenn deine Linie durch 0/0 und 50/50 gegeben ist und teste mal den Punkt 100/100... Zitat:
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:51 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz