#!/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:])