Bauklötzchen in Kurven legen/Kringel vermeiden

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

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von Syntacks_Error »

Das sieht doch hervorragend aus! Die Absätze oder Brüche liegen, glaube ich, an den Originaldaten, die nicht so ganz zur obigen vereinfachten Version passen. Die Originallinie ist mehrfach unterbrochen und nochmals unterteilt. Dehalb habe ich die der erste Prozedur makeCoordinatesList(posxystring$) nur alle zweiten Koorinaten eingelesen " for ... Step 2); möglicherweise wird deshalb mitunter an den falschen Punkten angesetzt.

Ich baue das mal in das eigentliche Programm ein und melde mich dann, erst einmal vielen Dank an alle!

Mein Progammierstil scheint ja noch schlechter zu sein als befürchtet.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
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 »

Oder man schreibt einfach

Code: Alles auswählen

_diff - 360
:-D
Bild
Benutzeravatar
#NULL
Beiträge: 2235
Registriert: 20.04.2006 09:50

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von #NULL »

@nic, das könnte sein :mrgreen:

übrigens, je nachdem was dein Code so macht, behalte im Hinterkopf (oder wo auch immer) das z.b. bei +/-abbiration (oder generell beim ändern eines Winkelwertes) ein Wert außerhalb von 0..360 rauskommen könnte. den PB Winkel-Funktionen ist das aber vermutlich egal.
my pb stuff..
Bild..jedenfalls war das mal so.
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von Syntacks_Error »

Also ich weiß ja nicht, wie groß der Schritt für die Menschheit ist, aber für mich ... Der Einbau hat auf Anhieb geklappt, ich da habe auch noch einmal richtig herumgemalt, nirgendwo ein Kringel oder eine Kurve falschrum. Ein wenigen Fällen wird zwar nur ein einziges gedrehtes Klötzschen gesetzt, so dass die Kurve da reichlich eckig ist, das liegt aber vermutlich an Randbedingungen, die ich noch anpassen muss. Jedenfalls besteht kein Zusammenhang mit den Absätzen oder Brüchen.

Ich hatte da schon einige Zeit ohne Erfolg herumgeknobelt und dachte schon, das wird nix mehr, also nochmals vielen Dank.

Bei den Winkelsummen steht bei mir eigentlich immer so etwas : If degree > 360 : degree = degree - 360 : endif
Aber pure basic scheinen Winkel > 360 oder < 0 in der Tat nicht zu stören.
Benutzeravatar
#NULL
Beiträge: 2235
Registriert: 20.04.2006 09:50

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von #NULL »

Ich denke mal, dass manche Ecken so eckig sind passiert wenn die Winkeländerung über den 0-Punkt geht.
z.b. von 350° nach 10°, dann ist diff = +20, also erster If-Zweig, aber gleich mit dem ersten Schritt ist 358° ja schon größer als 10°, also stop=1.
my pb stuff..
Bild..jedenfalls war das mal so.
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von Syntacks_Error »

Ach so. Ich hatte eine Funktion im Verdacht, die den Rotationswinkel ändert.

Ich versuche erst einmal, ob ich die Knicke selbst rundbiegen kann.
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von Syntacks_Error »

Auch das mit dem Durchgang über die 360 Grad kriege ich nicht hin. Die Idee war, die Winkeldifferenz zum nächsten Wegpunkt zu bestimmen, daraus und aus der Winkeländerung jedes Klötzchens (hier 8 Grad pro Klotz) deren nötige Anzahl zu bestimmen und dann einfach diese Anzahl zu legen. Das wäre dann aquivalent zu "until olddegree >= newdegree". Dabei wollte ich dann auch die Info benutzen, ob der Winkel links oder rechts dreht (_diff > oder < 0)

Ich schaffe es aber nicht einmal die Winkeldifferenz zu bestimmtn. Die vom Programm berechneten Winkel (olddegree/newdegree) sind komisch, ich kann sie nicht nachvollziehen. So ganz falsch können sie aber auch nicht sein, das ganze funktioniert ja ;-)

Gibt es dafür eine Lösung?
Benutzeravatar
#NULL
Beiträge: 2235
Registriert: 20.04.2006 09:50

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von #NULL »

folgende Änderungen:
- Procedure.f normalizeAngle(a.f)

Code: Alles auswählen

Procedure.f normalizeAngle(a.f)
  ; normalisieren zu 0..360
  While a < 0
    a + 360
  Wend
  While a >= 360
    a - 360
  Wend
  ProcedureReturn a
EndProcedure
- Procedure.f angleDiff(aFrom.f, aTo.f)
--- normalisiert die Eingabewinkel zuerst, damit wird z.b. -2*360 zu 0 wird
--- prüft jetzt auch den Überlauf in die andere Richtung, also wenn diff < -180

Code: Alles auswählen

Procedure.f angleDiff(aFrom.f, aTo.f)
  Protected aDiff.f
  aDiff = normalizeAngle(aTo) - normalizeAngle(aFrom)
  
  ; bei mehr als einer halben drehung, die andere richtung nehmen
  If aDiff >= 180
    aDiff - 360
  EndIf
  If aDiff <= -180
    aDiff + 360
  EndIf
  ProcedureReturn aDiff
EndProcedure
- unittest (CompilerIf)

Code: Alles auswählen

; unittest
CompilerIf 0
  Define.f a1, a2
  a1 =   0.0 : a2 =  45.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;   45
  a1 =  90.0 : a2 =  80.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  -10
  a1 = 170.0 : a2 = 190.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;   20
  a1 = 100.0 : a2 = 260.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  160
  a1 =  90.0 : a2 = 270.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  180
  a1 =  80.0 : a2 = 280.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ; -160
  a1 =  10.0 : a2 = 350.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  -20
  a1 =  10.0 : a2 = 360.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  -10
  a1 =  10.0 : a2 =   0.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  -10
  a1 = 360.0 : a2 = 350.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  -10
  a1 =   0.0 : a2 = 350.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  -10
  a1 = -45.0 : a2 =  0.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;   45
  a1 = -45.0 : a2 = -90.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2);  -45
  
  ; -10 -> 10
  a1 = -4*360-10 : a2 = -2*360+10 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  20
  
  ; 10 -> -10
  a1 =  4*360+10 : a2 =  2*360-10 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ; -20
  End
CompilerEndIf
- der Hauptcode prüft jetzt für den stop nach Änderung des Winkels, ob diff sich umgekehrt hat; olddegree wird nach Änderung sicherheitshalber nochmal normalisiert.-

Code: Alles auswählen

        _diff.f = angleDiff(olddegree1, newdegree1)
        
        If _diff >= 0
          olddegree + abirration
          If angleDiff(olddegree, newdegree) <= 0
            stop = 1
          EndIf : ;Original
        Else
          olddegree - abirration
          If angleDiff(olddegree, newdegree) >= 0
            stop = 1
          EndIf
        EndIf
        olddegree = normalizeAngle(olddegree)

Einmal komplett (die Absätze sind wie gehabt, aber die eckigen Ecken sind jetzt rund):

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.f normalizeAngle(a.f)
  ; normalisieren zu 0..360
  While a < 0
    a + 360
  Wend
  While a >= 360
    a - 360
  Wend
  ProcedureReturn a
EndProcedure

Procedure.f angleDiff(aFrom.f, aTo.f)
  Protected aDiff.f
  aDiff = normalizeAngle(aTo) - normalizeAngle(aFrom)
  
  ; bei mehr als einer halben drehung, die andere richtung nehmen
  If aDiff >= 180
    aDiff - 360
  EndIf
  If aDiff <= -180
    aDiff + 360
  EndIf
  ProcedureReturn aDiff
EndProcedure

; unittest
CompilerIf 0
  Define.f a1, a2
  a1 =   0.0 : a2 =  45.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;   45
  a1 =  90.0 : a2 =  80.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  -10
  a1 = 170.0 : a2 = 190.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;   20
  a1 = 100.0 : a2 = 260.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  160
  a1 =  90.0 : a2 = 270.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  180
  a1 =  80.0 : a2 = 280.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ; -160
  a1 =  10.0 : a2 = 350.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  -20
  a1 =  10.0 : a2 = 360.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  -10
  a1 =  10.0 : a2 =   0.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  -10
  a1 = 360.0 : a2 = 350.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  -10
  a1 =   0.0 : a2 = 350.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  -10
  a1 = -45.0 : a2 =  0.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;   45
  a1 = -45.0 : a2 = -90.0 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2);  -45
  
  ; -10 -> 10
  a1 = -4*360-10 : a2 = -2*360+10 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ;  20
  
  ; 10 -> -10
  a1 =  4*360+10 : a2 =  2*360-10 : Debug "" + a1 + " -> " + a2 + " = " + angleDiff(a1, a2) ; -20
  End
CompilerEndIf
 
 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!
        
        _diff.f = angleDiff(olddegree1, newdegree1)
        
        If _diff >= 0
          olddegree + abirration
          If angleDiff(olddegree, newdegree) <= 0
            stop = 1
          EndIf : ;Original
        Else
          olddegree - abirration
          If angleDiff(olddegree, newdegree) >= 0
            stop = 1
          EndIf
        EndIf
        olddegree = normalizeAngle(olddegree)
     
      ;----------------------------     
   
       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
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 »

Noch ein Hinweis..
Winkeldifferenzen darfst du mit der obigen Funktion nicht normalisieren, sondern nur absolute Winkel.
Bei dir sind absolute Winkel ja im Bereich 0..360, und Winkeldifferenzen im Bereich -180..0..180.
Generell ist es vielleicht einfacher, auch alle absoluten Winkel im selben Bereich -180..180 zu speichern, macht aber nicht wirklich einen Unterschied.
my pb stuff..
Bild..jedenfalls war das mal so.
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Re: Bauklötzchen in Kurven legen/Kringel vermeiden

Beitrag von Syntacks_Error »

Fantastisch. habe ich natürlich gleich eingebaut, scheint prima zu funktionieren. Am Wochenende werde ich dann mal versuchen, das Ganze auch zu verstehen. Nochmals vielen Dank!
Antworten