Circle selfmade

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Circle selfmade

Beitrag von cxAlex »

Aufgrund eines Posts im englischen Forum hab ich mich an einer Circle() Procedure versucht. Nun ist sie leicht langsamer als die von PB. auch erkennt man das PB einen etwas anderen Algo verwenden muss, hat jemand eine Idee?:

Code: Alles auswählen

Procedure _Circle(x0, y0, rad, color = 0)
  Static x, y, rad2
  
  rad2 = rad*rad
  
  x = 0
  y = 1
  
  Plot(x0, y0 + rad, color)
  Plot(x0, y0-rad, color)
  Plot(x0 + rad, y0, color)
  Plot(x0-rad, y0, color)
  
  While x<y
    
    x + 1
    y = Sqr(rad2-x*x)
    
    Plot(x0-x, y0-y, color)
    Plot(x0-x, y0 + y, color)
    Plot(x0 + x, y0-y, color)
    Plot(x0 + x, y0 + y, color)
    Plot(x0-y, y0-x, color)
    Plot(x0-y, y0 + x, color)
    Plot(x0 + y, y0-x, color)
    Plot(x0 + y, y0 + x, color)
    
  Wend
  
  
EndProcedure

OpenWindow(0, 0, 0, 450, 450, "demo", #PB_Window_SystemMenu)

#cnt = 100

If StartDrawing(WindowOutput(0))
    

    t = ElapsedMilliseconds()
    For i = 1 To #cnt
      _Circle(120, 225, i, $ff)
    Next
    t1 = ElapsedMilliseconds()-t
    DrawingMode(#PB_2DDrawing_Outlined)
    
    DrawingMode(#PB_2DDrawing_Outlined)
    t = ElapsedMilliseconds()
    For i = 1 To #cnt
      Circle(345, 225, i, $ff)
    Next
    t2 = ElapsedMilliseconds()-t
    
    
  StopDrawing()
EndIf

MessageRequester("str", Str(t1) + Chr(13) + Str(t2))

Repeat
  Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7032
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Beitrag von STARGÅTE »

da man bei sehr großen kreisen keinen unterschied mehr sieht ob es ein VielEck oder Kreis ist, mach ich das zB mit Lines:

Code: Alles auswählen

Procedure _Circle(x0, y0, rad, color = 0) 
  Static x1,x2,y1,y2, n=0, line=50
  factor.f = 1/line*2*#PI
  x1 = x0+rad
  y1 = y0+0
  For n = 1 To line
   x2 = x0+Cos(n*factor)*rad
   y2 = y0+Sin(n*factor)*rad
   LineXY(x1,y1,x2,y2,Color)
   x1 = x2
   y1 = y2
  Next  
EndProcedure
ist auf jedenfall schon mal schneller als das plotten ^^
aber kommt leider nicht an PB ran ...
man könnte aber noch zeit gut machen, wenn man bei kleinen kreisen weniger line nimmt ...
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Beitrag von cxAlex »

Auch eine Methode Stargate. Nur muss man bei großen Kreisen die Anzahl der Linien manuell erhöhen (ein Kreis mit Radius 800 sieht damit schon komisch aus).

Dieser Code ist bei mir jetzt immer gleichschnell bis leicht schneller als PB, ich bin zufrieden :D

Code: Alles auswählen

Procedure _Circle(x0, y0, rad, color = 0)
  Static x, y, rad2, oldcolor
  
  oldcolor = FrontColor(color)
  rad2 = rad*rad
  x = 0
  
  Plot(x0, y0 + rad)
  Plot(x0, y0-rad)
  Plot(x0 + rad, y0)
  Plot(x0-rad, y0)
  
  Repeat
    
    x + 1
    y = Sqr(rad2-x*x)
    
    Plot(x0-x, y0-y)
    Plot(x0-x, y0 + y)
    Plot(x0 + x, y0-y)
    Plot(x0 + x, y0 + y)
    Plot(x0-y, y0-x)
    Plot(x0-y, y0 + x)
    Plot(x0 + y, y0-x)
    Plot(x0 + y, y0 + x)
    
  Until x>y
  
  FrontColor(oldcolor)
  
EndProcedure

OpenWindow(0, 0, 0, 650, 450, "demo", #PB_Window_SystemMenu)

#cnt = 1000

If StartDrawing(WindowOutput(0))
    
    
    t = ElapsedMilliseconds()
    For i = 1 To #cnt
      _Circle(110, 225, i, $ff)
    Next
    t1 = ElapsedMilliseconds()-t
    DrawingMode(#PB_2DDrawing_Outlined)
        
    DrawingMode(#PB_2DDrawing_Outlined)
    t = ElapsedMilliseconds()
    For i = 1 To #cnt
      Circle(545, 225, i, $ff)
    Next
    t3 = ElapsedMilliseconds()-t
    
    
  StopDrawing()
EndIf

MessageRequester("str", "cxAlex: "+Str(t1) + Chr(13) +"PB: "+Str(t3))

Repeat
  Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

@STARGÅTE

LineXY() ist wesentlich langsamer als Line(), weil sie extra noch den abschließenden Punkt plottet.
die einfache Line() zu verwenden sollte einen deutlichen Vorteil bringen, selbst wenn man eine Differenz errechnen muss.

außerdem spart es irre zeit, eine vorgefertigte Sinustabelle zu benutzen,
anstatt der nativen FPU-Funktionen:
http://www.purebasic.fr/german/viewtopic.php?t=5884
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8812
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Beitrag von NicTheQuick »

Ihr solltet euch das hier mal anschauen: Kreise zeichnen mit Turbo

Da braucht man weder eine Sinus, noch Wurzel, noch Quadratzahlen. Nur Plus und Mal.

Der Rest der Seite dürfte für den ein oder anderen auch interessant sein.
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Beitrag von Helle »

Mit Bresenham hatte ich auch experimentiert (nur Kreis-Umfang):

Code: Alles auswählen

Procedure Bresenham(X0, Y0, Radius, Color) 
  F = 1 - Radius    
  Plot(X0, Y0 + Radius, Color)
  Plot(X0 + Radius, Y0, Color)
  Plot(X0, Y0 - Radius, Color)
  Plot(X0 - Radius, Y0, Color)
  Y = Radius
  While X < Y    
    X + 1    
    If F < 0    
      F = F + (X << 1) - 1    
     Else    
      F = F + (X - Y) << 1    
      Y - 1    
    EndIf    
    X2 = X0 + X
    Y2 = Y0 + Y
    X3 = X0 - X
    Y3 = Y0 - Y
    Plot(X2, Y2, Color)
    Plot(Y2, X2, Color)
    Plot(X3, Y2, Color)
    Plot(Y3, X2, Color)
    Plot(X2, Y3, Color)
    Plot(Y2, X3, Color)
    Plot(X3, Y3, Color)
    Plot(Y3, X3, Color)
  Wend    
EndProcedure 


OpenWindow(0, 0, 0, 400, 400, "Bresenham-Kreis", #PB_Window_SystemMenu) 
  If StartDrawing(WindowOutput(0))
    Bresenham(200, 200, 150, 255) 
    StopDrawing()
  EndIf
  Repeat 
    Event = WaitWindowEvent() 
  Until Event = #PB_Event_CloseWindow
CloseWindow(0)
Ist aber (bei mir) langsamer! Der Grund dürfte sein, dass auf modernen CPU´s für SQR() nur noch ca. 6 CPU-Taktzyklen benötigt werden.
Zum Thema Geschwindigkeit: Ich wollte hier ja auch was zum Besten geben (schneller als die anderen obigen Vollkreis-Codes), habe dann testweise das Ganze auf meinem (3-Jahre alten Notebook) laufen lassen und siehe da, auf dem Notebook (On-Board-Intel-Grafik) war PB wieder wesentlich schneller! Habe dann entnervt aufgegeben... :mrgreen:

Gruß
Helle
Antworten