dashing/splitall.py

101 lines
2.8 KiB
Python

import math
import random
import itertools
import numpy as np
import pyclipper
import matplotlib as mpl
import matplotlib.pyplot as plt
import dashing
random.seed(38)
def pattern(frac, degrees):
a = 30 * frac
b = 30 - a
angle = math.radians(degrees)
dx = math.cos(angle)
dy = math.sin(angle)
return dashing.HatchPattern.fromString(f"""
*
{degrees},{-a*dx/2},{-a*dy/2},{10*dy},{-10*dx},{a},{-b}""", 2)
def canon(path):
"""Convert a path to the canonical version of itself
Paths are guaranteed to be CCW by clipper, so it suffices to place the "min"
point as the 0th element, however "min" is defined. (The usual Python definition
of tuple ordering is fine)"""
path = tuple(tuple(pi) for pi in path)
i = np.argmin(path)
res = path[i:] + path[:i]
return res
def splitall(parts):
parts = [canon(pi) for pi in parts]
pc = pyclipper.Pyclipper()
def clip(p1, p2, op):
pc.Clear()
pc.AddPath(p1, pyclipper.PT_SUBJECT, True)
pc.AddPath(p2, pyclipper.PT_CLIP, True)
r = pc.Execute(op, pyclipper.PFT_EVENODD, pyclipper.PFT_EVENODD)
return r
def split_one(p1, p2):
yield from clip(p1, p2, pyclipper.CT_INTERSECTION) # p1 & p2
yield from clip(p1, p2, pyclipper.CT_DIFFERENCE) # p1 - p2
result = set()
for pi in parts:
sub_parts = set((pi,))
for pj in parts:
if pi is pj: continue
sub_parts = set(canon(p) for ps in sub_parts for p in split_one(ps, pj) if p)
# TODO sometimes gives two results for same boundary
result |= sub_parts
return result
FN = 360
angles = np.linspace(0, np.pi*2, FN, False)
sincos = np.stack( [np.cos(angles), np.sin(angles)]).T
def polycircle(x, y, r):
return np.round([x,y] + r * sincos).astype(np.int32)
def xcoord(): return random.uniform(0, 1600)
def ycoord(): return random.uniform(0, 1000)
def rad(): return random.uniform(400, 800)
paths_in = [polycircle(xcoord(), ycoord(), rad()) for _ in range(6)]
#p1 = polycircle(0, 0, 800)
#p2 = polycircle(-500, -300, 900)
#p3 = polycircle(-500, 300, 800)
paths = splitall(paths_in)
print("#", len(paths))
for path in paths:
print(f"polygon{path}")
#paths = [p1, p2, p3]
colors = mpl.colormaps['viridis'](np.linspace(0, 1, len(paths)))
#plt.figure(figsize=(16, 10))
#plt.axis('equal')
for i, p in enumerate(paths):
boundary = [ [ p[i], p[(i+1) % len(p)] ] for i in range(0, len(p), 1) ]
pat = pattern(random.uniform(.1, .8), 30)
res = pat.hatch(boundary, dashing.WindingRule.EvenOdd)
print(len(res))
if not res: continue
for p1, p2 in res:
plt.plot([p1[0], p2[0]], [p1[1], p2[1]], color=colors[i])
#continue
x = [pi[0] for pi in p] + [p[0][0]]
y = [pi[1] for pi in p] + [p[0][1]]
plt.plot(x, y, color=colors[i])
#, facecolor=colors[i]
plt.axis('off')
plt.axis("image")
plt.show()