Thursday, November 18, 2010

Generating A Fractal Mountain Range

I stumbled across this page about generating random fractal terrain. I went ahead and made a simple wxpython app to put the ideas I learned into practice. Below is the result and source code.



import wx
import math
import random

class Point():
 def __init__(self, x, y):
  self.X = x
  self.Y = y
  
 def RandY(self, displacement):
  self.Y = self.Y + (random.uniform(-1,1) * displacement)
  
class Line():
 def __init__(self, p1, p2):
  self.P1 = p1
  self.P2 = p2
  
 def FindMid(self):
  p = Point((self.P1.X + self.P2.X) / 2.0, (self.P1.Y + self.P2.Y) / 2.0)
  return p
  
 def Draw(self, dc):
  dc.SetPen(wx.Pen(wx.BLACK))
  dc.DrawLine(self.P1.X, self.P1.Y, self.P2.X, self.P2.Y)
  dc.DrawLine(self.P1.X, self.P1.Y, self.P1.X, 700)
  dc.DrawLine(self.P2.X, self.P2.Y, self.P2.X, 700)
  
def Fractal(line, number, rand, dc):
 if number == 0:
  line.Draw(dc)
 else:
  p = line.FindMid()
  p.RandY(rand)
  rand = rand/2
  Fractal(Line(line.P1, p), number-1, rand, dc)
  Fractal(Line(p, line.P2), number-1, rand, dc)

class Mountain2D(wx.Frame):
 def __init__(self, parent):
  wx.Frame.__init__(self, parent, id=wx.ID_ANY, title='Fractial Mountain!', style=wx.DEFAULT_FRAME_STYLE & ~wx.RESIZE_BORDER)
  
  self.DrawingPanel = DrawingPanel(self)
  
  sizer = wx.BoxSizer(wx.VERTICAL)
  sizer.Add(self.DrawingPanel, 1, wx.EXPAND|wx.ALL, 5)
  self.SetSizer(sizer)
  self.Fit()
  self.Show(True)
  
class DrawingPanel(wx.Panel):
 def __init__(self, parent):
  wx.Panel.__init__(self, parent, id=wx.ID_ANY, size=(600,500), style=wx.SIMPLE_BORDER)
  
  self.Bind(wx.EVT_PAINT, self.OnPaint)
  self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
  
 def OnEraseBackground(self, event):
  pass
 
 def OnPaint(self, event):
  dc = wx.BufferedPaintDC(self)
  self.Draw(dc)
  
 def Draw(self, dc):
  dc.SetBrush(wx.WHITE_BRUSH)
  dc.DrawRectangle(-1, -1, self.Size.width, self.Size.height)
  
  # Draw the sky
  scale = 255.0 / self.Size.height
  for i in range(0,self.Size.height):
   dc.SetPen(wx.Pen(wx.Colour(0, 255 - int(scale * i), 255 - int(scale * i))))
   dc.DrawLine(0, i, self.Size.width, i)
   
  # Draw some stars
  for i in range(0,150):
   dc.SetPen(wx.Pen(wx.Colour(255, 255, 255)))
   dc.SetBrush(wx.Brush(wx.Colour(255, 255, 255), wx.SOLID))
   dc.DrawCircle(random.uniform(0, self.Size.width), random.uniform(-1,self.Size.height / 1.5), random.uniform(1, 3))
  
  P1 = Point(self.Size.width * 0.0, self.Size.height * 0.60)
  P2 = Point(self.Size.width * 1.0, self.Size.height * 0.60)
  
  LINE = Line(P1, P2)
  
  Fractal(LINE, 10, 200, dc)

if __name__ == '__main__':
 app = wx.App(0)
 frame = Mountain2D(None)
 app.MainLoop()

No comments:

Post a Comment