mapgen.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. import os, glob, errno
  4. import math, random
  5. from PIL import Image, ImageDraw
  6. import svgwrite
  7. import datetime
  8. from sympy.geometry import Circle, Point
  9. # http://stackoverflow.com/questions/8997099/algorithm-to-generate-random-2d-polygon
  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. lines = fractalize(points)
  48. return lines
  49. def clip(x, min, max) :
  50. if( min > max ) : return x
  51. elif( x < min ) : return min
  52. elif( x > max ) : return max
  53. else : return x
  54. def fractalize(points) :
  55. # print("Fractalize")
  56. # print(points)
  57. lines = []
  58. for p in range(0, len(points)):
  59. p1 = points[p]
  60. # print(p1)
  61. # points.append(p1)
  62. p2 = points[0] if p >= len(points)-1 else points[p+1];
  63. pts = [p1,p2]
  64. # fractalize the line
  65. if random.randint(1,10) > 3:
  66. # number of fracatllization
  67. for i in range(10):
  68. # loop throug points to divide then
  69. fpts = []
  70. for v in range(0, len(pts)-1):
  71. fp1 = pts[v]
  72. fpts.append(fp1)
  73. fp2 = pts[v+1];
  74. d = distance(fp1,fp2)
  75. # print(d)
  76. r = d*0.52
  77. fps1,fps2 = circle_intersection((fp1[0],fp1[1],r), (fp2[0],fp2[1],r))
  78. # print(fps1)
  79. # print(ps2)
  80. if random.randint(1,2) == 2:
  81. fpts.append(fps2)
  82. else:
  83. fpts.append(fps1)
  84. # add the last point
  85. fpts.append(fp2)
  86. pts = fpts
  87. # add the line, fractalized or not
  88. lines.append(pts)
  89. return lines
  90. # Distance function
  91. def distance(p1,p2):
  92. sq1 = (p1[0]-p2[0])*(p1[0]-p2[0])
  93. sq2 = (p1[1]-p2[1])*(p1[1]-p2[1])
  94. return math.sqrt(sq1 + sq2)
  95. def circle_intersection(circle1, circle2):
  96. '''
  97. @summary: calculates intersection points of two circles
  98. @param circle1: tuple(x,y,radius)
  99. @param circle2: tuple(x,y,radius)
  100. @result: tuple of intersection points (which are (x,y) tuple)
  101. '''
  102. # return self.circle_intersection_sympy(circle1,circle2)
  103. x1,y1,r1 = circle1
  104. x2,y2,r2 = circle2
  105. # http://stackoverflow.com/a/3349134/798588
  106. dx,dy = x2-x1,y2-y1
  107. d = math.sqrt(dx*dx+dy*dy)
  108. if d > r1+r2:
  109. print("#1")
  110. return None # no solutions, the circles are separate
  111. if d < abs(r1-r2):
  112. print("#2")
  113. return None # no solutions because one circle is contained within the other
  114. if d == 0 and r1 == r2:
  115. print("#3")
  116. return None # circles are coincident and there are an infinite number of solutions
  117. a = (r1*r1-r2*r2+d*d)/(2*d)
  118. h = math.sqrt(r1*r1-a*a)
  119. xm = x1 + a*dx/d
  120. ym = y1 + a*dy/d
  121. xs1 = xm + h*dy/d
  122. xs2 = xm - h*dy/d
  123. ys1 = ym - h*dx/d
  124. ys2 = ym + h*dx/d
  125. return (xs1,ys1),(xs2,ys2)
  126. # create maps export folder
  127. base = "maps"
  128. now = datetime.datetime.now()
  129. now = now.strftime("%Y-%m-%d_%X")
  130. print(now)
  131. directory = base + "/" + now
  132. try:
  133. os.makedirs(directory)
  134. except OSError as exception:
  135. if exception.errno != errno.EEXIST:
  136. raise
  137. for i in range(0, 99):
  138. print(i)
  139. nv = random.randint(5,30)
  140. lines = generatePolygon( ctrX=500, ctrY=500, aveRadius=300, irregularity=1, spikeyness=0.4, numVerts=nv )
  141. # print(lines)
  142. index = str(i) if i > 9 else '0'+str(i)
  143. # ______ ________
  144. # / ___/ | / / ____/
  145. # \__ \| | / / / __
  146. # ___/ /| |/ / /_/ /
  147. # /____/ |___/\____/
  148. svg = svgwrite.Drawing(filename = directory+"/map"+index+".svg",size = ("1000px", "1000px"))
  149. # polygone (white background)
  150. polygone = []
  151. for l in range(0, len(lines)):
  152. for p in range(0, len(lines[l])):
  153. polygone.append(lines[l][p])
  154. bgline = svg.polyline(polygone,
  155. stroke = "white",
  156. stroke_width = "30",
  157. stroke_linejoin= "round",
  158. stroke_linecap = "round",
  159. fill = "white")
  160. svg.add(bgline)
  161. # strokes
  162. for l in range(0, len(lines)):
  163. # change randomly stroke attributes
  164. if random.randint(0,10) > 8: # and len(lines[L]) < 3:
  165. sw = "1"
  166. sda = "4 4"
  167. sdo = "5"
  168. else:
  169. sw = "1"
  170. sda = "0 0"
  171. sdo = "0"
  172. line = svg.polyline(lines[l],
  173. stroke = "black",
  174. stroke_width = sw,
  175. stroke_linejoin= "round",
  176. stroke_linecap = "round",
  177. stroke_dasharray = sda,
  178. stroke_dashoffset = sdo,
  179. fill = "none")
  180. svg.add(line)
  181. svg.save()
  182. # __ _ __
  183. # / /_ (_) /_____ ___ ____ _____
  184. # / __ \/ / __/ __ `__ \/ __ `/ __ \
  185. # / /_/ / / /_/ / / / / / /_/ / /_/ /
  186. # /_.___/_/\__/_/ /_/ /_/\__,_/ .___/
  187. # /_/
  188. # black = (0,0,0)
  189. # white=(255,255,255)
  190. # im = Image.new('RGB', (1000, 1000), white)
  191. # imPxAccess = im.load()
  192. # draw = ImageDraw.Draw(im)
  193. # tupVerts = list(map(tuple,verts))
  194. #
  195. # # either use .polygon(), if you want to fill the area with a solid colour
  196. # # draw.polygon( tupVerts, outline=black,fill=white )
  197. #
  198. # # or .line() if you want to control the line thickness, or use both methods together!
  199. # draw.line( tupVerts+[tupVerts[0]], width=1, fill=black )
  200. #
  201. # # im.show()
  202. # im.save(directory+'/map-'+str(index)+'.bmp')
  203. # # now you can save the image (im), or do whatever else you want with it.