mapgen.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. # http://stackoverflow.com/questions/8997099/algorithm-to-generate-random-2d-polygon
  4. import os, glob, errno
  5. import math, random
  6. from PIL import Image, ImageDraw
  7. import svgwrite
  8. import datetime
  9. from sympy.geometry import Circle, Point
  10. def generatePolygon( ctrX, ctrY, aveRadius, irregularity, spikeyness, numVerts ) :
  11. '''Start with the centre of the polygon at ctrX, ctrY,
  12. then creates the polygon by sampling points on a circle around the centre.
  13. Randon noise is added by varying the angular spacing between sequential points,
  14. and by varying the radial distance of each point from the centre.
  15. Params:
  16. ctrX, ctrY - coordinates of the "centre" of the polygon
  17. aveRadius - in px, the average radius of this polygon, this roughly controls how large the polygon is, really only useful for order of magnitude.
  18. irregularity - [0,1] indicating how much variance there is in the angular spacing of vertices. [0,1] will map to [0, 2pi/numberOfVerts]
  19. spikeyness - [0,1] indicating how much variance there is in each vertex from the circle of radius aveRadius. [0,1] will map to [0, aveRadius]
  20. numVerts - self-explanatory
  21. Returns a list of vertices, in CCW order.
  22. '''
  23. irregularity = clip( irregularity, 0,1 ) * 2*math.pi / numVerts
  24. spikeyness = clip( spikeyness, 0,1 ) * aveRadius
  25. # generate n angle steps
  26. angleSteps = []
  27. lower = (2*math.pi / numVerts) - irregularity
  28. upper = (2*math.pi / numVerts) + irregularity
  29. sum = 0
  30. for i in range(numVerts) :
  31. tmp = random.uniform(lower, upper)
  32. angleSteps.append( tmp )
  33. sum = sum + tmp
  34. # normalize the steps so that point 0 and point n+1 are the same
  35. k = sum / (2*math.pi)
  36. for i in range(numVerts) :
  37. angleSteps[i] = angleSteps[i] / k
  38. # now generate the points
  39. points = []
  40. angle = random.uniform(0, 2*math.pi)
  41. for i in range(numVerts) :
  42. r_i = clip( random.gauss(aveRadius, spikeyness), 0, 2*aveRadius )
  43. x = ctrX + r_i*math.cos(angle)
  44. y = ctrY + r_i*math.sin(angle)
  45. points.append( (int(x),int(y)) )
  46. angle = angle + angleSteps[i]
  47. for i in range(10):
  48. points = fractalize(points)
  49. return points
  50. def clip(x, min, max) :
  51. if( min > max ) : return x
  52. elif( x < min ) : return min
  53. elif( x > max ) : return max
  54. else : return x
  55. def fractalize(pts) :
  56. print("Fractalize")
  57. print(pts)
  58. points = []
  59. for v in range(0, len(pts)):
  60. p1 = pts[v]
  61. print(p1)
  62. points.append(p1)
  63. p2 = pts[0] if v >= len(pts)-1 else pts[v+1];
  64. d = distance(p1,p2)
  65. # print(d)
  66. r = d*0.52
  67. ps1,ps2 = circle_intersection((p1[0],p1[1],r), (p2[0],p2[1],r))
  68. print(ps1)
  69. # print(ps2)
  70. if random.randint(1,2) == 2:
  71. points.append(ps2)
  72. else:
  73. points.append(ps1)
  74. return points
  75. # Distance function
  76. def distance(p1,p2):
  77. sq1 = (p1[0]-p2[0])*(p1[0]-p2[0])
  78. sq2 = (p1[1]-p2[1])*(p1[1]-p2[1])
  79. return math.sqrt(sq1 + sq2)
  80. def circle_intersection(circle1, circle2):
  81. '''
  82. @summary: calculates intersection points of two circles
  83. @param circle1: tuple(x,y,radius)
  84. @param circle2: tuple(x,y,radius)
  85. @result: tuple of intersection points (which are (x,y) tuple)
  86. '''
  87. # return self.circle_intersection_sympy(circle1,circle2)
  88. x1,y1,r1 = circle1
  89. x2,y2,r2 = circle2
  90. # http://stackoverflow.com/a/3349134/798588
  91. dx,dy = x2-x1,y2-y1
  92. d = math.sqrt(dx*dx+dy*dy)
  93. if d > r1+r2:
  94. print("#1")
  95. return None # no solutions, the circles are separate
  96. if d < abs(r1-r2):
  97. print("#2")
  98. return None # no solutions because one circle is contained within the other
  99. if d == 0 and r1 == r2:
  100. print("#3")
  101. return None # circles are coincident and there are an infinite number of solutions
  102. a = (r1*r1-r2*r2+d*d)/(2*d)
  103. h = math.sqrt(r1*r1-a*a)
  104. xm = x1 + a*dx/d
  105. ym = y1 + a*dy/d
  106. xs1 = xm + h*dy/d
  107. xs2 = xm - h*dy/d
  108. ys1 = ym - h*dx/d
  109. ys2 = ym + h*dx/d
  110. return (xs1,ys1),(xs2,ys2)
  111. # create maps export folder
  112. base = "maps"
  113. now = datetime.datetime.now()
  114. now = now.strftime("%Y-%m-%d_%X")
  115. print(now)
  116. directory = base + "/" + now
  117. try:
  118. os.makedirs(directory)
  119. except OSError as exception:
  120. if exception.errno != errno.EEXIST:
  121. raise
  122. for i in range(0, 1):
  123. nv = random.randint(5,30)
  124. verts = generatePolygon( ctrX=500, ctrY=500, aveRadius=300, irregularity=1, spikeyness=0.4, numVerts=nv )
  125. index = str(i) if i > 9 else '0'+str(i)
  126. # ______ ________
  127. # / ___/ | / / ____/
  128. # \__ \| | / / / __
  129. # ___/ /| |/ / /_/ /
  130. # /____/ |___/\____/
  131. svg = svgwrite.Drawing(filename = directory+"/map"+index+".svg",size = ("1000px", "1000px"))
  132. # print(len(verts))
  133. for v in range(0, len(verts)):
  134. # print('---- '+str(v)+' ----')
  135. p1 = verts[v]
  136. p2 = verts[0] if v >= len(verts)-1 else verts[v+1];
  137. # print(p1)
  138. # print(p2)
  139. # if random.randint(0,10) > 8:
  140. # sda = "4 4"
  141. # sdo = "5"
  142. # else:
  143. sda = "0 0"
  144. sdo = "0"
  145. line = svg.line(p1, p2,
  146. stroke_width = "1",
  147. stroke = "black",
  148. stroke_linecap = "round",
  149. stroke_dasharray = sda,
  150. stroke_dashoffset = sdo)
  151. svg.add(line)
  152. svg.save()
  153. # __ _ __
  154. # / /_ (_) /_____ ___ ____ _____
  155. # / __ \/ / __/ __ `__ \/ __ `/ __ \
  156. # / /_/ / / /_/ / / / / / /_/ / /_/ /
  157. # /_.___/_/\__/_/ /_/ /_/\__,_/ .___/
  158. # /_/
  159. # black = (0,0,0)
  160. # white=(255,255,255)
  161. # im = Image.new('RGB', (1000, 1000), white)
  162. # imPxAccess = im.load()
  163. # draw = ImageDraw.Draw(im)
  164. # tupVerts = list(map(tuple,verts))
  165. #
  166. # # either use .polygon(), if you want to fill the area with a solid colour
  167. # # draw.polygon( tupVerts, outline=black,fill=white )
  168. #
  169. # # or .line() if you want to control the line thickness, or use both methods together!
  170. # draw.line( tupVerts+[tupVerts[0]], width=1, fill=black )
  171. #
  172. # # im.show()
  173. # im.save(directory+'/map-'+str(index)+'.bmp')
  174. # # now you can save the image (im), or do whatever else you want with it.