Files
mapgen/mapgen.py
T
2017-03-14 21:38:29 +01:00

209 lines
6.0 KiB
Python
Executable File

#!/usr/bin/python
# -*- coding: utf-8 -*-
# http://stackoverflow.com/questions/8997099/algorithm-to-generate-random-2d-polygon
import os, glob, errno
import math, random
from PIL import Image, ImageDraw
import svgwrite
import datetime
from sympy.geometry import Circle, Point
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]
for i in range(10):
points = fractalize(points)
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(pts) :
print("Fractalize")
print(pts)
points = []
for v in range(0, len(pts)):
p1 = pts[v]
print(p1)
points.append(p1)
p2 = pts[0] if v >= len(pts)-1 else pts[v+1];
d = distance(p1,p2)
# print(d)
r = d*0.52
ps1,ps2 = circle_intersection((p1[0],p1[1],r), (p2[0],p2[1],r))
print(ps1)
# print(ps2)
if random.randint(1,2) == 2:
points.append(ps2)
else:
points.append(ps1)
return points
# 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)
# 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
for i in range(0, 1):
nv = random.randint(5,30)
verts = generatePolygon( ctrX=500, ctrY=500, aveRadius=300, irregularity=1, spikeyness=0.4, numVerts=nv )
index = str(i) if i > 9 else '0'+str(i)
# ______ ________
# / ___/ | / / ____/
# \__ \| | / / / __
# ___/ /| |/ / /_/ /
# /____/ |___/\____/
svg = svgwrite.Drawing(filename = directory+"/map"+index+".svg",size = ("1000px", "1000px"))
# print(len(verts))
for v in range(0, len(verts)):
# print('---- '+str(v)+' ----')
p1 = verts[v]
p2 = verts[0] if v >= len(verts)-1 else verts[v+1];
# print(p1)
# print(p2)
# if random.randint(0,10) > 8:
# sda = "4 4"
# sdo = "5"
# else:
sda = "0 0"
sdo = "0"
line = svg.line(p1, p2,
stroke_width = "1",
stroke = "black",
stroke_linecap = "round",
stroke_dasharray = sda,
stroke_dashoffset = sdo)
svg.add(line)
svg.save()
# __ _ __
# / /_ (_) /_____ ___ ____ _____
# / __ \/ / __/ __ `__ \/ __ `/ __ \
# / /_/ / / /_/ / / / / / /_/ / /_/ /
# /_.___/_/\__/_/ /_/ /_/\__,_/ .___/
# /_/
# 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))
#
# # either use .polygon(), if you want to fill the area with a solid colour
# # draw.polygon( tupVerts, outline=black,fill=white )
#
# # or .line() if you want to control the line thickness, or use both methods together!
# draw.line( tupVerts+[tupVerts[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.