Bauklötzchen in Kurven legen/Kringel vermeiden

Anfängerfragen zum Programmieren mit PureBasic.
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von Syntacks_Error »

Hallo,

ich scheitere wieder einmal an der Geometrie.

Mein Program legt Bauklötzen (Länge = 3) entlang einer Linie. Also nicht wirklich Bauklötzchen,
nur so zur Veranschaulichung.

Dazu erhält es Kooridinaten von Punkten, durch die die Linie läuft.
Im Programm wird diese Linie durch die anfängliche blaue Linie angezeigt.

Das geht nun so:
Zuerst werden Klötzchen vom ersten Punkt zum zweiten Punkt in gerader Linie gelegt.
Dann wird die Entfernung zum nächsten Punkt und die Winkeldifferenz zu ihm berechnet.

Das Programm schwenkt nämlich nicht gleich auf den neuen Winkel ein, sondern
legt erst eine Kurve in Richtung auf den neuen Punkt. Dazu dreht es die nächsten
Klötzchen um jeweils einen bestimmten Winkelbetrag (Hier: 8 Grad) und legt sie aneinander,
bis ein Klötzchen so ungefähr in Richtung des neuen Punktes zeigt. Dann legte es weiter
Klötzchen in gerader Linie, bis es so etwa den neuen Punkt erreicht hat und peilt
dann den nächsten Punkt an und so weiter.

Das funktioniert auch ganz gut, aber: Unter bestimmten Bedingungen, ich weiß nicht welche,
dreht das Programm die Klötchen in die falsche Richtung und produziert so Kringel bis fast
zu Vollkreisen, bis es wieder in die richtige Richtung zeigt.

Das Problem liegt sicherlich darin, dem Programm zu sagen, wann es den Drehwinkel zum
Ankunftswinkel addieren und wann es ihn substrahieren muss. Ich kriege nur nicht heraus,
wie das richtig funktiniert.

Die geraden Linen werden bei "mit Kurven" gelb angezeigt, die Kurven selbst rot. Die gelben
Linien zeigen wegen der Kurven natürlich eine erhebliche Abweichung von der "Ideallinie",
macht aber nichts.

Das ganze ist nur ein sehr reduzierter Auszug aus dem Originalprogramm, das deutlich
komplexer ist. Insoweit wundere ich mich, dass ich das überhaupt hingekriegt habe ;-)
Um so doofer wäre es natürlich, wenn ich an den Kringeln scheitern würde.

Irgendeine Idee, wie ich für jeden Fall bestimme, ob das Programm beim
Drehen der Klötzchen den Drehwinkel addieren oder substrahiern muss?


Code: Alles auswählen

Structure coordinates
  x.i
  y.i
EndStructure

Global NewList coordinates.coordinates()

Procedure makeCoordinatesList(posxystring$)
  number = CountString(posxystring$,",") 
  For loop = 1 To number Step 2 ; Step 2, weil hier die Lücken zwischen den Abschnitten aus dem Original fehlen
   AddElement(coordinates())
   With coordinates()
    \x = Val(StringField(posxystring$,loop,","))
    \y = Val(StringField(posxystring$,loop + 1,","))
   EndWith
  Next
EndProcedure

Procedure.f ATan3(y.i,x.i) 
  If y > 0 And x < 0 ; links oben
   ProcedureReturn ATan(y/x) * 180/#PI +180
  ElseIf y < 0 And x < 0 ; links unten
   ProcedureReturn  ATan(y/x) *180/#PI + 180
  ElseIf y < 0 And x > 0 ; rechts unten
   ProcedureReturn ATan(y/x) *180/#PI + 360 
  ElseIf y > 0 And x > 0; rechts oben
   ProcedureReturn ATan(y/x) *180/#PI
  ElseIf y < 0 And x = 0 ; links  (x u. y vertauscht wg. Koordinatensystem! Real also x < 0, y = 0)
   ProcedureReturn 270
  ElseIf y > 0 And x = 0 ; rechts (         ""       )
   ProcedureReturn 90
  ElseIf y = 0 And x < 0 ; unten  (         ""       )
   ProcedureReturn 180
  ElseIf y = 0 And x > 0 ; oben   (         ""       )
   ProcedureReturn 360  
  EndIf
EndProcedure


Procedure.s plotlineLow(x1,y1,x2,y2,image,colour)
  colour = RGB(0,0,255)

  dx = x2 - x1
  dy = y2 - y1
  yi = 1
  If dy < 0
   yi = -1
   dy = -dy
  EndIf
  D = 2*dy - dx
  y = y1
  StartDrawing(ImageOutput(image))
  For x = x1 To x2
     Plot(x,y,colour)
      If D > 0
      y = y + yi
      D = D - 2*dx
     EndIf
     D = D + 2*dy
    Next
  StopDrawing() 
EndProcedure

Procedure.s plotLineHigh(x1,y1,x2,y2,image,colour)
  colour = RGB(0,0,255)
  dx = x2 - x1
  dy = y2 - y1
  xi = 1
  If dx < 0
   xi = -1
   dx = -dx
  EndIf
  D = 2*dx - dy
  x = x1
  StartDrawing(ImageOutput(image))
  For y = y1 To y2
    Plot(x,y,colour)
   If D > 0
    x = x + xi
    D = D - 2*dy
   EndIf
   D = D + 2*dx
  Next
  StopDrawing() 
EndProcedure


Procedure selectPlotHighLow(x1,y1,x2,y2,image,colour)
  If Abs(y2 - y1) < Abs(x2 - x1)
    If x1 > x2
     plotLineLow(x2, y2, x1, y1,image,colour)
    Else
     plotLineLow(x1,y1, x2,y2,image,colour)
    EndIf
  Else
    If y1 > y2
     plotLinehigh(x2, y2, x1, y1,image,colour)
    Else
      plotLinehigh(x1, y1, x2, y2,image,colour)
    EndIf
  EndIf
 EndProcedure
 
 
Procedure.f getDistance(startX,startY,endX,endY)
  xDistanceSquare = (endX - startX) * (endX - startX)
  yDistanceSquare =  (endY - startY) * (endY - startY)  
  distance.f = Sqr(xDistanceSquare + yDistanceSquare)
 ProcedureReturn distance
EndProcedure 
 
 Procedure makeLinesWithCurves(image,colour)
 
  length.f = 3; Länge der "Bauklötzchen", hier = 4 pixel 
  
  ForEach coordinates()  
     
   If run = 0 ; erster Abschnitt noch ohne Kurven
     startx = coordinates()\x ; Anfang der Linienabschnitte
     starty = coordinates()\y  
   EndIf
      
   If NextElement(coordinates())
    
    endx = coordinates()\x ; Ende der Linienabschnitte 
    endy = coordinates()\y 
          
     If Not ListIndex(coordinates()) = ListSize(coordinates()) ; letztes Element ist bereits erreicht - nix mehr malen
      distance.f = getDistance(startX,startY,endX,endY) ; Entfernung zwischen zwei Punkten
      olddegree.f = degree.f ; Letzer Winkel eines Abschnittes
      olddegree1.f = degree.f 
      degree.f = ATan3(endx-startx, endy-starty) ; Winkel zun nächsten Punkt(aktueller Winkel)
      Debug StrF(degree,2) ; Im Original ist das Koordinatensystem verdreht (Atan3), deshalb werden die Winkel scheinbar falsch angezeigt, ist aber berücksichtigt
      newdegree.f = degree.f ; aktueller Winkel
      newdegree1.f = degree.f
     
      abirration = 8     ; Winkeländerung in der Kurve pro Pixel  
     
;     Kurven legen .................................
      
      If curve  ; 1 Abschnitt keine Kurve
      
      stop = 0 
      StartDrawing(ImageOutput(image))   
      
      ; Anfang Kurven legen, Farbe = Rot
      Repeat
        
        ;-------------------------------
        ; Hier wird bestimmt, ob der Winkel in der Kurve gegenüber dem Ankunftswinkel (olddegree) vergrößert oder verkleinert wird.
        ; Und das kriege ich nicht hin - siehe die Kringel beim Kurvenzeichnen!
        
      
       If newdegree1 - olddegree1  >= 0 :  olddegree + abirration : If olddegree > newdegree :  stop = 1 : EndIf : ;Original
        Else : olddegree - abirration :If olddegree < newdegree :  stop = 1 : EndIf 
       EndIf
      
      ;----------------------------
      
    
       sinus.f = Sin(Radian(olddegree.f)) 
       cosinus.f = Cos(Radian(olddegree.f))

       slX.f = (sinus.f * length.f); Berechnet Veränderung von x im Winkel auf der Strecke.
       slY.f = (cosinus.f * length.f) ; berechnet Veränderung von y im Winkel aif der Strecle
       degree.f = ATan3(endx-startx, endy-starty) ; Berechnet den aktuellen Winkel zum Zielpunkt nach Setzen eines Klötzchens
       Plot(startx,starty,RGB(255,0,0))
       startX = startX + slX ; Veränderung startx/starty = x/y-Koordinaten nach dem Setzen eines Klötchens 
       startY = startY + slY 
      Until stop ; Olddegree = ankunftswinkel ist jetzt wegen +/- abirration so ungefähr gleich dem Zielwinkel
      StopDrawing()
     EndIf 
;    ;ende Kurven legen .................................
      
      degree.f = ATan3(endx-startx, endy-starty) ; Winkel vom Kurvenende zum nächsten Punkt/Gerade Linie
      distance.f = getDistance(startX,startY,endX,endY) ; NeuBerechnung der Entfernung
      sinus.f = Sin(Radian(degree))
      cosinus.f = Cos(Radian(degree))
      slX.f = (sinus * length.f); Berechnet Veränderung von x im Winkel auf der strecke
      slY.f = (cosinus * length.f) ; berechnet Veränderung von y im Winkel
    
      ;Gerade Linie
      
      StartDrawing(ImageOutput(image))
      While distance.f  > 0; Abbruch wenn, wenn Entfrnung negativ, Zilpunkt ist mehr oder weniger erreicht
        
       Plot(startX,startY,RGB(234,255,0))
       
       startX = startX + slX ; veränderung startx in der Schleife/Nächste Platte; 
       startY = startY + slY 
       distance.f = distance.f - length.f
      Wend
      StopDrawing()
      curve + 1 ; erster Abschnitt ist eine Gerade, ab jetzt Kurven
     EndIf
   EndIf
  Next    ;Foreach roads()1
 
EndProcedure
 

Procedure makeLines(image,colour)
 
  ForEach coordinates()
   With coordinates()
    startx = \x
    starty = \y
   EndWith 
   If endx  ; Daher keine Linie zum ersten Punkt
    selectPlotHighLow(startx,starty,endx,endy,image,colour) ; Erstmal gerade Linie zum nächsten Punkt
  EndIf
  endx = startx
  endy = starty
 Next 
 EndProcedure

Global imagewidth = 1400
Global imagehight = 900

window = OpenWindow(#PB_Any,300,50,imagewidth,imagehight,"Bauklötzchen",#PB_Window_MinimizeGadget| #PB_Window_MaximizeGadget|#PB_Window_SizeGadget)
image = CreateImage(#PB_Any,imagewidth,imagehight,24,RGB(110,110,110))
imgad = ImageGadget(#PB_Any,0,0,imagewidth,imageheight,ImageID(image))
;x/y-Koordinaten
posXYstring$ = "228,605,193,496,193,495,225,432,226,430,271,388,272,388,336,409,337,410,371,457,372,458,337,512,335,514,334,549,334,549,384,587,384," +
              "587,433,633,435,635,439,672,439,672,508,640,510,638,482,573,481,571,448,525,447,523,507,481,508,480,506,430,506,430,521,384,522,383," +
              "677,376,680,376,715,437,715,437,661,497,659,498,596,491,594,490,574,544,574,546,649,599,649,599,708,616,710,616,727,660,727,663,683," +
              "693,680,694,770,739,770,739,856,696,856,696,803,618,802,616,798,539,798,538,856,508,856,508,793,451,792,450,819,397,819,397,894,384," +
              "897,384,977,334,978,333,958,148,958,147,794,87,793,87,635,120,632,121,600,170,600,170,644,253,645,255,753,275,754,275,833,251,836,250,764,189,"
makeCoordinatesList(posXYstring$)

makeLines(image,3) ; 3 = blau
ResizeGadget(imgad, 0,0,#PB_Ignore,#PB_Ignore)



Repeat
  
event = WaitWindowEvent()  

If message = 0
 message = MessageRequester("Test", "Linien mit Kurven auf 'OK' klicken",#PB_MessageRequester_Ok) 
 makeLinesWithCurves(image,2) ;2 = Grün
 ResizeGadget(imgad, 0,0,#PB_Ignore,#PB_Ignore)
EndIf
   


If event = #PB_Event_CloseWindow
  close = 1
EndIf
Until  close 

Edit by NicTheQuick: Zeile gekürzt
Demivec
Beiträge: 49
Registriert: 22.02.2008 20:49
Wohnort: Utah, USA

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von Demivec »

Ersetzen Sie diesen Code :

Code: Alles auswählen

;     Kurven legen .................................
      
      If curve  ; 1 Abschnitt keine Kurve
      
      stop = 0 
      StartDrawing(ImageOutput(image))   
      
      ; Anfang Kurven legen, Farbe = Rot
      Repeat
        
        ;-------------------------------
        ; Hier wird bestimmt, ob der Winkel in der Kurve gegenüber dem Ankunftswinkel (olddegree) vergrößert oder verkleinert wird.
        ; Und das kriege ich nicht hin - siehe die Kringel beim Kurvenzeichnen!
        
      
       If newdegree1 - olddegree1  >= 0 :  olddegree + abirration : If olddegree > newdegree :  stop = 1 : EndIf : ;Original
        Else : olddegree - abirration :If olddegree < newdegree :  stop = 1 : EndIf 
       EndIf
      
      ;----------------------------
Mit diesem Code:

Code: Alles auswählen

;     Kurven legen .................................
      
      If curve  ; 1 Abschnitt keine Kurve
        
        stop = 0 
        StartDrawing(ImageOutput(image))   
        
        ; Anfang Kurven legen, Farbe = Rot
        rotationdir = Bool(Abs(newdegree1 - olddegree1) < = 180) ; 1 = gegen den Uhrzeigersinn, 0 = Uhrzeigersinn
        rotationdiff.f = Mod(Abs(newdegree1 - olddegree1) + 360, 360) ;absoluter Drehbetrag
        Repeat
          
          ;-------------------------------
          ; Hier wird bestimmt, ob der Winkel in der Kurve gegenüber dem Ankunftswinkel (olddegree) vergrößert oder verkleinert wird.
          ; Und das kriege ich nicht hin - siehe die Kringel beim Kurvenzeichnen!
          
          
          If rotationdir: olddegree - abirration: Else: olddegree + abirration: EndIf
          rotationdiff - abirration
          If rotationdiff < abirration / 2: stop = 1: EndIf
          
          ;----------------------------
Zuletzt geändert von Demivec am 24.10.2018 22:04, insgesamt 2-mal geändert.
Bild
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von Syntacks_Error »

Hallo,

vielen Dank, aber leider legt das Programm jetzt nur noch ein einziges Klötzchen in einer Kurve.:

Hier kommt ja immer +1/0 heraus, soll ja auch so sein:
rotationdir = Bool(Abs(newdegree1 - olddegree1) < = 180) ; 1 = gegen den Uhrzeigersinn, 0 = Uhrzeigersinn

So dass bei
rotationdiff.f = Mod(rotationdir + 360, 360) ;absoluter Drehbetrag
auch immer 1 oder 0 herauskommt, Mod(1/0+360,360)

Innerhalb der Schleife

Repeat
If rotationdir: olddegree - abirration: Else: olddegree + abirration: EndIf
rotationdiff - abirration

ist dann wegen
If rotationdiff < abirration: stop = 1: EndIf ; = if -7 < 0/1 ...
immer gleich Schluss.

Wenn das mit dem rotationsdir(...) hinhaut, und gefühlt sieht das gut aus, wäre das die Lösung, wenn Winkeldifferenz (rotationsdiff) berechnet werden kann. Wenn ich die Drehrichtung kenne, müsste das doch möglich sein? Ich weiß hier nur wieder nicht weiter.

Ich habe den Codeteil mehrmals ausgetauscht, es blieb aber immer bei einem Klötzchen ...
Benutzeravatar
#NULL
Beiträge: 2235
Registriert: 20.04.2006 09:50

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von #NULL »

Code: Alles auswählen

rotationdir = Bool(Abs(newdegree1 - olddegree1) < = 180) ; 1 = gegen den Uhrzeigersinn, 0 = Uhrzeigersinn
Ich habe es mir nicht genau angeschaut, aber vielleicht musst du das noch normalisieren oder so? Bei Drehung von 10° nach 350°, also 350-10 erhälst du ja 340, statt -20.
my pb stuff..
Bild..jedenfalls war das mal so.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8677
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von NicTheQuick »

Ich verstehe das Problem glaube ich noch nicht ganz. Willst du sowas wie Spline machen?
Bild
Demivec
Beiträge: 49
Registriert: 22.02.2008 20:49
Wohnort: Utah, USA

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von Demivec »

Ich habe den vorherigen Code geändert (es gab einen Tippfehler).
Bild
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von Syntacks_Error »

Der neue Code malt wieder Kurven, produziert aber viele Kringel und manchmal wohl auch die falsche Drehrichtung

Ich habe 'mal nachgesehen, was das mit dem Spline ist und verstehe, dass ich das nicht verstehe ;-(. Es gibt hier sogar Prozeduren dafür, aber die verstehe ich auch nicht und werde sie auch nicht anwenden können. Vectordrawing kann ich nicht verwenden, weil ich die xy-Koodinaten jedes Punktes kennen muss und außerdem die Kontrolle über den Drehwinkel haben möchte. Daher die handgestrickten Malprozeduren. Ob es ein Problem gibt, das das Programm löst, wird sich noch herausstellen, wenn es denn funktionieren sollte ;-).
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8677
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von NicTheQuick »

Das ist ja der Witz an Spline. Man definiert jeden Punkt und den Drehwinkel und dann kann man sich alle Punkte dazwischen berechnen, sodass es eine schöne Kurve wird. Quellcodes gibt es dazu überall.
Bild
Benutzeravatar
#NULL
Beiträge: 2235
Registriert: 20.04.2006 09:50

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von #NULL »

Ich vestehe zwar deinen Code nicht, und das hier kommt deiner Vorstellung vielleicht schon näher, auch wenn da noch so komische Absätze sind. Der letzte If Block ist wie gehabt, nur das er _diff verwendet.

Code: Alles auswählen

        ;-------------------------------
        ; Hier wird bestimmt, ob der Winkel in der Kurve gegenüber dem Ankunftswinkel (olddegree) vergrößert oder verkleinert wird.
        ; Und das kriege ich nicht hin - siehe die Kringel beim Kurvenzeichnen!
       
        _diff.f = newdegree1 - olddegree1
        
        ; normalisieren zu 0..360
        While _diff < 0
          _diff + 360
        Wend
        While _diff >= 360
          _diff - 360
        Wend
        
        ;Debug "_diff:"+_diff
        
        ; bei mehr als einer halben drehung, die andere richtung nehmen
        If _diff >= 180
          _diff = -(_diff - 180)   ; z.b. von 0 nach 200 --> diff = 200 --> diff = -160
        EndIf
        
        If _diff >= 0
          olddegree + abirration
          If olddegree > newdegree
            stop = 1
          EndIf : ;Original
        Else
          olddegree - abirration
          If olddegree < newdegree
            stop = 1
          EndIf
       EndIf
     
      ;----------------------------
my pb stuff..
Bild..jedenfalls war das mal so.
Benutzeravatar
#NULL
Beiträge: 2235
Registriert: 20.04.2006 09:50

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von #NULL »

..richtiger wäre

Code: Alles auswählen

_diff = -(180-(_diff - 180))
..macht aber keinen Unterschied, solange im If danach nur das Vorzeichen verwendet wird. Aber so stimmt wenigstens die Rechnung im Kommentar :mrgreen:
my pb stuff..
Bild..jedenfalls war das mal so.
Antworten