commit 8f6baf46cd55c6d86d7977d1bb594eefeaf464d5 Author: Bachir Soussi Chiadmi Date: Fri Jun 15 14:09:33 2018 +0200 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..6a248c7 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# imaginery maps + +pseudo country limit python generator + +```mapgen.py [-n, --number][--svg][--bmp]``` + +![exmple](samples/map-00.png) + + + diff --git a/mapgen.py b/mapgen.py new file mode 100644 index 0000000..562256f --- /dev/null +++ b/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:]) diff --git a/samples/map-00.png b/samples/map-00.png new file mode 100644 index 0000000..000e5aa Binary files /dev/null and b/samples/map-00.png differ