Beliebige Fläche (Screen) mit Dreiecken füllen

Fragen zu Grafik- & Soundproblemen und zur Spieleprogrammierung haben hier ihren Platz.
Agent
Beiträge: 296
Registriert: 13.09.2004 11:28
Kontaktdaten:

Re: Beliebige Fläche (Screen) mit Dreiecken füllen

Beitrag von Agent »

@stargate:

Vielen Dank soweit. Ich werde mir das was Du da gezaubert hast mal ansehen :-)


@dragon:
Ja, das wird sicher auch genügen. Wenn Du das schon (mehr oder weniger) fertig hast würde ich mit über einen Post mit den Source freuen :-)


Vielen Dank euch beiden für die super Hilfe!
Agent_Sasori
It's not a bug - it's a feature!
http://www.StephenKalisch.de | http://www.ria-tec.com | http://www.dirsync.de
DarkDragon
Beiträge: 6291
Registriert: 29.08.2004 08:37
Computerausstattung: Hoffentlich bald keine mehr
Kontaktdaten:

Re: Beliebige Fläche (Screen) mit Dreiecken füllen

Beitrag von DarkDragon »

Code: Alles auswählen

EnableExplicit

Structure VPoint
  X.d
  Y.d
EndStructure

Structure VPointWeight Extends VPoint
  Weight.d
EndStructure

;- Misc Helper
;{

Procedure.i Min(A.i, B.i)
  If A <= B
    ProcedureReturn A
  EndIf
  
  ProcedureReturn B
EndProcedure

Procedure.i Max(A.i, B.i)
  If A >= B
    ProcedureReturn A
  EndIf
  
  ProcedureReturn B
EndProcedure

Procedure.i Gray(Color.i)
  ProcedureReturn Min(Max(Int(0.299 * Red(Color) + 0.587 * Green(Color) + 0.114 * Blue(Color)), 0), 255)
EndProcedure

;}

;- Distance Functions
;{

Prototype.d DistanceFunction(*P1.VPoint, *P2.VPoint)

DisableDebugger
Procedure.d DistanceEuclidSquared(*P1.VPoint, *P2.VPoint)
  Protected X.d = *P1\X - *P2\X
  Protected Y.d = *P1\Y - *P2\Y
  ProcedureReturn (X * X + Y * Y)
EndProcedure
EnableDebugger

;}

;- Voronoi Functions
;{

DisableDebugger
Procedure.i ClosestPoint(Array VPoint.VPoint(1), *Point.VPoint, Distance.DistanceFunction)
  Protected K.i
  Protected Dist.d
  Protected MinDist.d = Infinity()
  Protected MinIndex.i = -1
  
  For K = ArraySize(VPoint()) To 0 Step -1
    Dist = Distance(@VPoint(K), *Point)
    If Dist < MinDist
      MinIndex = K
      MinDist = Dist
    EndIf
  Next K
  
  ProcedureReturn MinIndex
EndProcedure

Procedure DrawVoronoi(Array VPoint.VPoint(1), Distance.DistanceFunction)
  Protected X.i
  Protected Y.i
  Protected Point.VPoint
  Protected PointIndex.i
  
  For X = OutputWidth() - 1 To 0 Step -1
    For Y = OutputHeight() - 1 To 0 Step -1
      Point\X = X
      Point\Y = Y
      
      PointIndex = ClosestPoint(VPoint(), @Point, Distance)
      If PointIndex >= 0
        Plot(X, Y, PointIndex)
      EndIf
    Next Y
  Next X
EndProcedure

Procedure RelaxVoronoi(Array VPoint.VPoint(1), Array Weights.d(2), Width.i, Height.i, ResX.i, ResY.i, Distance.DistanceFunction)
  Protected Dim NewVPoint.VPointWeight(ArraySize(VPoint()))
  Protected X.i, Y.i
  Protected Point.VPoint
  Protected PointIndex.i
  Protected Weight.d
  Protected K.i
  
  X = 0
  While X < Width
    Y = 0
    While Y < Height
      Point\X = X
      Point\Y = Y
      
      PointIndex = ClosestPoint(VPoint(), @Point, Distance)
      If PointIndex >= 0
        Weight = Weights(X, Y)
        NewVPoint(PointIndex)\X + X * Weight
        NewVPoint(PointIndex)\Y + Y * Weight
        NewVPoint(PointIndex)\Weight + Weight
      EndIf
      Y + ResY
    Wend
    X + ResX
  Wend
  
  For K = ArraySize(VPoint()) To 0 Step -1
    If NewVPoint(K)\Weight > 0.0
      NewVPoint(K)\Weight = 1.0 / NewVPoint(K)\Weight
      NewVPoint(K)\X * NewVPoint(K)\Weight
      NewVPoint(K)\Y * NewVPoint(K)\Weight
      
      VPoint(K)\X = NewVPoint(K)\X
      VPoint(K)\Y = NewVPoint(K)\Y
    EndIf
  Next K
EndProcedure
EnableDebugger

;}

Procedure ShowImage(Image.i)
  Protected Window.i
  
  Window = OpenWindow(#PB_Any, #PB_Default, #PB_Default, ImageWidth(Image), ImageHeight(Image), "Result")
  
  ImageGadget(#PB_Any, 0, 0, ImageWidth(Image), ImageHeight(Image), ImageID(Image))
  
  Repeat
  Until WaitWindowEvent() = #PB_Event_CloseWindow
  
  CloseWindow(Window)
EndProcedure

Procedure LoadWeightImage(Filename.s, Array Weights.d(2))
  Protected Image.i
  Protected X.i, Y.i
  
  Image = LoadImage(#PB_Any, Filename)
  If Image <> 0
    If StartDrawing(ImageOutput(Image))
      For X = Min(OutputWidth() - 1, ArraySize(Weights(), 1)) To 0 Step -1
        For Y = Min(OutputHeight() - 1, ArraySize(Weights(), 2)) To 0 Step -1
          Weights(X, Y) = Gray(Point(X, Y)) / 255.0
        Next Y
      Next X
      
      StopDrawing()
    EndIf
    
    FreeImage(Image)
  EndIf
EndProcedure

Define K.i, X.i, Y.i

#WIDTH = 960      ; image height
#HEIGHT = 480     ; image width
#COUNT = 200      ; amount of voronoi cells
#ITERATIONS = 10  ; relax iterations

; load weight image
Dim Weights.d(#WIDTH - 1, #HEIGHT - 1)

For X = ArraySize(Weights(), 1) To 0 Step -1
  For Y = ArraySize(Weights(), 2) To 0 Step -1
    Weights(X, Y) = 1.0
  Next Y
Next X

; load a weight image (should have the same dimensions)
; LoadWeightImage("weights.png", Weights())

; initialize random points
Dim VPoint.VPoint(#COUNT - 1)

For K = 0 To ArraySize(VPoint())
  Repeat
    VPoint(K)\X = Random(#WIDTH - 1)
    VPoint(K)\Y = Random(#HEIGHT - 1)
  Until Weights(Int(VPoint(K)\X), Int(VPoint(K)\Y)) > Random(100) * 0.01
Next K

; relax
For K = 0 To #ITERATIONS
  RelaxVoronoi(VPoint(), Weights(), #WIDTH, #HEIGHT, 4, 4, @DistanceEuclidSquared())
Next K

; draw
CreateImage(0, #WIDTH, #HEIGHT)
If StartDrawing(ImageOutput(0))
  DrawVoronoi(VPoint(), @DistanceEuclidSquared())
  StopDrawing()
EndIf

ShowImage(0)
Das ganze geht viel schneller, wenn man die Zellen in Form von Konen von oben rendert.
Angenommen es gäbe einen Algorithmus mit imaginärer Laufzeit O(i * n), dann gilt O((i * n)^2) = O(-1 * n^2) d.h. wenn man diesen Algorithmus verschachtelt ist er fertig, bevor er angefangen hat.
Agent
Beiträge: 296
Registriert: 13.09.2004 11:28
Kontaktdaten:

Re: Beliebige Fläche (Screen) mit Dreiecken füllen

Beitrag von Agent »

Hallo Ihr zwei.

Ich habe mir beides angesehen. Stargate's trifft natürlich die ursprüngliche Anforderung am ehesten. Das von Dragon sagt mir am Ende jedoch mehr zu. Die Berechnung scheint allerdings lange zu dauern. Damit sollte ein Hintergrund realisiert werden, der sich - im Idealfall - permanent bewegt.

Ich danke euch beiden nochmal ganz herzlich für die Mühe. Mal sehen was ich wie verwende oder modifiziere. Zumindest hätte ich einen guten Ansatz :-)

:allright:
Agent_Sasori
It's not a bug - it's a feature!
http://www.StephenKalisch.de | http://www.ria-tec.com | http://www.dirsync.de
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Beliebige Fläche (Screen) mit Dreiecken füllen

Beitrag von STARGÅTE »

Damit sollte ein Hintergrund realisiert werden, der sich - im Idealfall - permanent bewegt.
Bewegen heißt dann aber nicht, dass sich die Verbindungslinien ändern. Dass heißt, du brauchst nur einmal die Berechnung am Anfang machen und brauchst danach nur die Knoten verschieben, was allein betrachtet kein Zeitverlust darstellt. Klar ist dann nur, dass die Dreicke u.U. unglücklich verzerrt werden.

Vielleicht ist auch ein ganz anderer Ansatz nötig, für dein Ziel. Was genau hast du denn eigentlich vor?
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
DarkDragon
Beiträge: 6291
Registriert: 29.08.2004 08:37
Computerausstattung: Hoffentlich bald keine mehr
Kontaktdaten:

Re: Beliebige Fläche (Screen) mit Dreiecken füllen

Beitrag von DarkDragon »

Agent hat geschrieben:Ich habe mir beides angesehen. Stargate's trifft natürlich die ursprüngliche Anforderung am ehesten. Das von Dragon sagt mir am Ende jedoch mehr zu. Die Berechnung scheint allerdings lange zu dauern. Damit sollte ein Hintergrund realisiert werden, der sich - im Idealfall - permanent bewegt.
Evtl. benötigst du aber auch keine Relaxierung. Momentan werden ja #ITERATIONS Iterationen zur gleichmäßigen Anordnung verwendet. Evtl. schau ich demnächst mal ob ich das mit PB hardwarebeschleunigt hinbekomme.
Angenommen es gäbe einen Algorithmus mit imaginärer Laufzeit O(i * n), dann gilt O((i * n)^2) = O(-1 * n^2) d.h. wenn man diesen Algorithmus verschachtelt ist er fertig, bevor er angefangen hat.
Agent
Beiträge: 296
Registriert: 13.09.2004 11:28
Kontaktdaten:

Re: Beliebige Fläche (Screen) mit Dreiecken füllen

Beitrag von Agent »

Nur nicht zuviel der Mühe!
Das ganze soll ein Hintergrund für ein Menü sein.

Insgesammt geht es um ein 2D Spiel.

Ich hänge momentan eher daran eine zufällig generierte Welt (map) zu erschaffen.
Hierbei möchte ich sowas wie Biome machen, das heißt, ich möchte, betrachtet man die Map als ganzes Bereiche schaffen.

Beispiel:
Ein bisschen Grünland, dazwischen wasser (z.b. See), dann wieder Steine etc. Ich möchte untern vorgefertigte Maps nutzen. Vorerst würde das auch gehen, schöner wäre aber, dies würde generiert... habt ihr sowas auch zufällig in der Schublade? Ich werde aber jetzt nochmal im Forum suchen...
Agent_Sasori
It's not a bug - it's a feature!
http://www.StephenKalisch.de | http://www.ria-tec.com | http://www.dirsync.de
Antworten