Browse Source

first commit

Bachir Soussi Chiadmi 5 years ago
commit
8f6baf46cd
3 changed files with 340 additions and 0 deletions
  1. 10 0
      README.md
  2. 330 0
      mapgen.py
  3. BIN
      samples/map-00.png

+ 10 - 0
README.md

@@ -0,0 +1,10 @@
+# imaginery maps
+
+pseudo country limit python generator
+
+```mapgen.py [-n, --number][--svg][--bmp]```
+
+![exmple](samples/map-00.png)
+
+
+

+ 330 - 0
mapgen.py

@@ -0,0 +1,330 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import sys, getopt
+import os, glob, errno
+import math, random
+from PIL import Image, ImageDraw
+import svgwrite
+import datetime
+from sympy.geometry import Circle, Point
+
+#     __  ___      _
+#    /  |/  /___ _(_)___
+#   / /|_/ / __ `/ / __ \
+#  / /  / / /_/ / / / / /
+# /_/  /_/\__,_/_/_/ /_/
+def main(argv):
+   # Options
+   export_svg = False
+   export_bmp = False
+
+   try:
+      opts, args = getopt.getopt(argv,"n:",["number=", "svg", "bmp"])
+      print(opts)
+   except getopt.GetoptError as err:
+      print('mapgen.py [-n, --number][--svg][--bmp]')
+      print(str(err))
+      sys.exit(2)
+   for opt, arg in opts:
+      if opt == '-h':
+         print('mapgen.py [-n, --number]')
+         sys.exit()
+      elif opt in ("-n", "--number"):
+         number = int(arg)
+      elif opt in ("--svg"):
+         export_svg = True
+      elif opt in ("--bmp"):
+         export_bmp = True
+
+   if not export_svg and not export_bmp:
+      print('please explicitly provide --svg and/or --bmp option')
+      sys.exit()
+
+   # sys.exit()
+
+   # create maps export folder
+   base = "maps"
+   now = datetime.datetime.now()
+   now = now.strftime("%Y-%m-%d_%X")
+   print(now)
+   directory = base + "/" + now
+   try:
+      os.makedirs(directory)
+   except OSError as exception:
+      if exception.errno != errno.EEXIST:
+         raise
+
+   # generate n maps
+   for i in range(0, number):
+      index = str(i) if i > 9 else '0'+str(i)
+      print(index)
+      generateMap(index, directory, export_svg, export_bmp)
+
+
+
+#     __  ___
+#    /  |/  /___ _____
+#   / /|_/ / __ `/ __ \
+#  / /  / / /_/ / /_/ /
+# /_/  /_/\__,_/ .___/
+#             /_/
+def generateMap(index, directory, svg, bmp):
+   nv = random.randint(5,15)
+   points = generatePolygon( ctrX=500, ctrY=500, aveRadius=300, irregularity=0.7, spikeyness=0.3, numVerts=nv )
+   # print(points)
+   lines = fractalize(points)
+   # print(lines)
+
+   if svg:
+      generateSVG(lines, directory, index)
+
+   if bmp:
+      generateBmp(lines, directory, index)
+
+#     ____        __
+#    / __ \____  / /_  ______ _____  ____
+#   / /_/ / __ \/ / / / / __ `/ __ \/ __ \
+#  / ____/ /_/ / / /_/ / /_/ / /_/ / / / /
+# /_/    \____/_/\__, /\__, /\____/_/ /_/
+#               /____//____/
+#  http://stackoverflow.com/questions/8997099/algorithm-to-generate-random-2d-polygon
+def generatePolygon( ctrX, ctrY, aveRadius, irregularity, spikeyness, numVerts ) :
+   '''Start with the centre of the polygon at ctrX, ctrY,
+    then creates the polygon by sampling points on a circle around the centre.
+    Randon noise is added by varying the angular spacing between sequential points,
+    and by varying the radial distance of each point from the centre.
+
+    Params:
+    ctrX, ctrY - coordinates of the "centre" of the polygon
+    aveRadius - in px, the average radius of this polygon, this roughly controls how large the polygon is, really only useful for order of magnitude.
+    irregularity - [0,1] indicating how much variance there is in the angular spacing of vertices. [0,1] will map to [0, 2pi/numberOfVerts]
+    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]
+    numVerts - self-explanatory
+
+    Returns a list of vertices, in CCW order.
+    '''
+
+   irregularity = clip( irregularity, 0,1 ) * 2*math.pi / numVerts
+   spikeyness = clip( spikeyness, 0,1 ) * aveRadius
+
+   # generate n angle steps
+   angleSteps = []
+   lower = (2*math.pi / numVerts) - irregularity
+   upper = (2*math.pi / numVerts) + irregularity
+   sum = 0
+   for i in range(numVerts) :
+      tmp = random.uniform(lower, upper)
+      angleSteps.append( tmp )
+      sum = sum + tmp
+
+   # normalize the steps so that point 0 and point n+1 are the same
+   k = sum / (2*math.pi)
+   for i in range(numVerts) :
+     angleSteps[i] = angleSteps[i] / k
+
+   # now generate the points
+   points = []
+   angle = random.uniform(0, 2*math.pi)
+   for i in range(numVerts) :
+     r_i = clip( random.gauss(aveRadius, spikeyness), 0, 2*aveRadius )
+     x = ctrX + r_i*math.cos(angle)
+     y = ctrY + r_i*math.sin(angle)
+     points.append( (int(x),int(y)) )
+
+     angle = angle + angleSteps[i]
+
+   return points
+
+def clip(x, min, max) :
+   if( min > max ) :  return x
+   elif( x < min ) :  return min
+   elif( x > max ) :  return max
+   else :             return x
+
+#     ______                __        ___
+#    / ____/________ ______/ /_____ _/ (_)___  ___
+#   / /_  / ___/ __ `/ ___/ __/ __ `/ / /_  / / _ \
+#  / __/ / /  / /_/ / /__/ /_/ /_/ / / / / /_/  __/
+# /_/   /_/   \__,_/\___/\__/\__,_/_/_/ /___/\___/
+def fractalize(points) :
+   # print("Fractalize")
+   # print(points)
+   lines = []
+
+   # probality of line fractalization
+   proba_fract = random.randint(1,7)
+
+   # loop through points 2 by 2 to obtain lines
+   # line can be fractalized or not
+   for p in range(0, len(points)):
+      p1 = points[p]
+      p2 = points[0] if p >= len(points)-1 else points[p+1];
+      line_pts = [p1,p2]
+      # print(line_pts)
+
+      # fractalize the line or not (juste leave it as two points)
+      if random.randint(1,10) > proba_fract:
+         # fractal type
+         fract_types = "coast internal strait".split()
+         ft = random.choice(fract_types)
+         if ft == "coast":
+            fract_depth = 14
+            fract_intesity = random.randint(550,600)* 0.001
+         elif ft == "internal":
+            fract_depth = 9
+            fract_intesity = random.randint(520,550)* 0.001
+         elif ft == "strait":
+            fract_depth = 5
+            fract_intesity = random.randint(501,510)* 0.001
+
+         # number of fracatllization
+         for i in range(fract_depth):
+            # loop throug points to divide then
+            fpts = []
+            for v in range(0, len(line_pts)-1):
+               fp1 = line_pts[v]
+               fpts.append(fp1)
+               fp2 = line_pts[v+1];
+               d = distance(fp1,fp2)
+               # print(d)
+               # r = d*0.52
+               r = d*fract_intesity
+
+               fps1,fps2 = circle_intersection((fp1[0],fp1[1],r), (fp2[0],fp2[1],r))
+               # print(fps1)
+               # print(ps2)
+               if random.randint(1,2) == 2:
+                  fpts.append(fps2)
+               else:
+                  fpts.append(fps1)
+
+            # add the last point
+            fpts.append(fp2)
+            line_pts = fpts
+
+      # add the line, fractalized or not
+      lines.append(line_pts)
+
+   return lines
+
+# Distance function
+def distance(p1,p2):
+   sq1 = (p1[0]-p2[0])*(p1[0]-p2[0])
+   sq2 = (p1[1]-p2[1])*(p1[1]-p2[1])
+   return math.sqrt(sq1 + sq2)
+
+def circle_intersection(circle1, circle2):
+   '''
+   @summary: calculates intersection points of two circles
+   @param circle1: tuple(x,y,radius)
+   @param circle2: tuple(x,y,radius)
+   @result: tuple of intersection points (which are (x,y) tuple)
+   '''
+   # return self.circle_intersection_sympy(circle1,circle2)
+   x1,y1,r1 = circle1
+   x2,y2,r2 = circle2
+   # http://stackoverflow.com/a/3349134/798588
+   dx,dy = x2-x1,y2-y1
+   d = math.sqrt(dx*dx+dy*dy)
+   if d > r1+r2:
+      print("#1")
+      return None # no solutions, the circles are separate
+   if d < abs(r1-r2):
+      print("#2")
+      return None # no solutions because one circle is contained within the other
+   if d == 0 and r1 == r2:
+      print("#3")
+      return None # circles are coincident and there are an infinite number of solutions
+
+   a = (r1*r1-r2*r2+d*d)/(2*d)
+   h = math.sqrt(r1*r1-a*a)
+   xm = x1 + a*dx/d
+   ym = y1 + a*dy/d
+   xs1 = xm + h*dy/d
+   xs2 = xm - h*dy/d
+   ys1 = ym - h*dx/d
+   ys2 = ym + h*dx/d
+
+   return (xs1,ys1),(xs2,ys2)
+
+#    ______    ________
+#   / ___/ |  / / ____/
+#   \__ \| | / / / __
+#  ___/ /| |/ / /_/ /
+# /____/ |___/\____/
+def generateSVG(lines, directory, index):
+
+   svg = svgwrite.Drawing(filename = directory+"/map-"+index+".svg",size = ("1000px", "1000px"))
+
+   # polygone (white background)
+   polygone = []
+   for l in range(0, len(lines)):
+      for p in range(0, len(lines[l])):
+         polygone.append(lines[l][p])
+
+   bgline = svg.polyline(polygone,
+      stroke = "white",
+      stroke_width = "30",
+      stroke_linejoin= "round",
+      stroke_linecap = "round",
+      fill = "white")
+   svg.add(bgline)
+
+   # strokes
+   for l in range(0, len(lines)):
+      # change randomly stroke attributes
+      if len(lines[l]) < 3 or (random.randint(0,10) > 8 and len(lines[l]) < 10):
+         sw = "1"
+         sda = "4 4"
+         sdo = "5"
+      else:
+         sw = "1"
+         sda = "0 0"
+         sdo = "0"
+
+      line = svg.polyline(lines[l],
+         stroke = "black",
+         stroke_width = sw,
+         stroke_linejoin= "round",
+         stroke_linecap = "round",
+         stroke_dasharray = sda,
+         stroke_dashoffset = sdo,
+         fill = "none")
+      svg.add(line)
+
+   svg.save()
+
+#     __    _ __
+#    / /_  (_) /_____ ___  ____ _____
+#   / __ \/ / __/ __ `__ \/ __ `/ __ \
+#  / /_/ / / /_/ / / / / / /_/ / /_/ /
+# /_.___/_/\__/_/ /_/ /_/\__,_/ .___/
+#                            /_/
+def generateBmp(lines, directory, index):
+   black = (0,0,0)
+   white=(255,255,255)
+   im = Image.new('RGB', (1000, 1000), white)
+   imPxAccess = im.load()
+   draw = ImageDraw.Draw(im)
+   # tupVerts = list(map(tuple,verts))
+
+   polygone = []
+   for l in range(0, len(lines)):
+      for p in range(0, len(lines[l])):
+         polygone.append(lines[l][p])
+
+
+   # either use .polygon(), if you want to fill the area with a solid colour
+   # draw.polygon( polygone, outline=black,fill=white )
+
+   # or .line() if you want to control the line thickness, or use both methods together!
+   draw.line( polygone+[polygone[0]], width=1, fill=black )
+
+   # im.show()
+   im.save(directory+'/map-'+str(index)+'.bmp')
+   # now you can save the image (im), or do whatever else you want with it.
+
+
+if __name__ == "__main__":
+   main(sys.argv[1:])

BIN
samples/map-00.png