kopia lustrzana https://github.com/villares/sketch-a-day
rodzic
0aaba32163
commit
5ced1e8e99
Plik diff jest za duży
Load Diff
|
|
@ -0,0 +1,273 @@
|
||||||
|
# -*- coding: UTF-8 -*-
|
||||||
|
"""
|
||||||
|
From github.com/villares/villares/line_geometry.py
|
||||||
|
|
||||||
|
2020-09-25
|
||||||
|
2020-10-15 Fixed "line_instersection" typo, added dist() & removed TOLERANCE
|
||||||
|
2020-10-17 Added point_in_screen(), renamed poly() -> draw_poly()
|
||||||
|
2020-10-19 Fixed line_intersection typo, again :/, cleaned up stuff
|
||||||
|
"""
|
||||||
|
from __future__ import division
|
||||||
|
|
||||||
|
class Line():
|
||||||
|
|
||||||
|
def __init__(self, a, b):
|
||||||
|
self.a = PVector(*a)
|
||||||
|
self.b = PVector(*b)
|
||||||
|
|
||||||
|
def __getitem__(self, i):
|
||||||
|
return (self.a, self.b)[i]
|
||||||
|
|
||||||
|
def dist(self):
|
||||||
|
return PVector.dist(self.a, self.b)
|
||||||
|
|
||||||
|
def plot(self):
|
||||||
|
line(self.a.x, self.a.y, self.b.x, self.b.y)
|
||||||
|
|
||||||
|
draw = plot
|
||||||
|
|
||||||
|
def lerp(self, other, t):
|
||||||
|
a = PVector.lerp(self.a, other.a, t)
|
||||||
|
b = PVector.lerp(self.b, other.b, t)
|
||||||
|
return Line(a, b)
|
||||||
|
|
||||||
|
def intersect(self, other):
|
||||||
|
return line_intersect(self, other)
|
||||||
|
|
||||||
|
def contains_point(self, x, y, tolerance=0.1):
|
||||||
|
return point_over_line(x, y,
|
||||||
|
self[0][0], self[0][1],
|
||||||
|
self[1][0], self[1][1],
|
||||||
|
tolerance)
|
||||||
|
|
||||||
|
point_over = contains_point
|
||||||
|
|
||||||
|
def point_colinear(self, x, y, tolerance=EPSILON):
|
||||||
|
return points_are_colinear(x, y,
|
||||||
|
self[0][0], self[0][1],
|
||||||
|
self[1][0], self[1][1],
|
||||||
|
tolerance)
|
||||||
|
|
||||||
|
def line_intersect(line_a, line_b):
|
||||||
|
"""
|
||||||
|
code adapted from Bernardo Fontes
|
||||||
|
https://github.com/berinhard/sketches/
|
||||||
|
"""
|
||||||
|
x1, y1 = line_a.a.x, line_a.a.y
|
||||||
|
x2, y2 = line_a.b.x, line_a.b.y
|
||||||
|
x3, y3 = line_b.a.x, line_b.a.y
|
||||||
|
x4, y4 = line_b.b.x, line_b.b.y
|
||||||
|
try:
|
||||||
|
uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / \
|
||||||
|
((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
|
||||||
|
uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / \
|
||||||
|
((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
|
||||||
|
except ZeroDivisionError:
|
||||||
|
return
|
||||||
|
if not(0 <= uA <= 1 and 0 <= uB <= 1):
|
||||||
|
return
|
||||||
|
x = line_a.a.x + uA * (line_a.b.x - line_a.a.x)
|
||||||
|
y = line_a.a.y + uA * (line_a.b.y - line_a.a.y)
|
||||||
|
return PVector(x, y)
|
||||||
|
|
||||||
|
def point_over_line(px, py, lax, lay, lbx, lby,
|
||||||
|
tolerance=0.1):
|
||||||
|
"""
|
||||||
|
Check if point is over line using the sum of
|
||||||
|
the distances from the point to the line ends
|
||||||
|
(the result has to be near equal for True).
|
||||||
|
"""
|
||||||
|
ab = dist(lax, lay, lbx, lby)
|
||||||
|
pa = dist(lax, lay, px, py)
|
||||||
|
pb = dist(px, py, lbx, lby)
|
||||||
|
return (pa + pb) <= ab + tolerance
|
||||||
|
|
||||||
|
def points_are_colinear(ax, ay, bx, by, cx, cy,
|
||||||
|
tolerance=EPSILON):
|
||||||
|
"""
|
||||||
|
Test for colinearity by calculating the area
|
||||||
|
of a triangle formed by the 3 points.
|
||||||
|
"""
|
||||||
|
area = triangle_area((ax, ay), (bx, by), (cx, cy))
|
||||||
|
return abs(area) < tolerance
|
||||||
|
|
||||||
|
def triangle_area(a, b, c):
|
||||||
|
area = (a[0] * (b[1] - c[1]) +
|
||||||
|
b[0] * (c[1] - a[1]) +
|
||||||
|
c[0] * (a[1] - b[1]))
|
||||||
|
return area
|
||||||
|
|
||||||
|
# class Poly():
|
||||||
|
|
||||||
|
# def __init__(iterable):
|
||||||
|
# self.__points = [p for p in iterable]
|
||||||
|
|
||||||
|
# def __iter__(self):
|
||||||
|
# return iter(self.__points)
|
||||||
|
|
||||||
|
# def plot(self):
|
||||||
|
# poly(self)
|
||||||
|
|
||||||
|
# draw = poly
|
||||||
|
|
||||||
|
|
||||||
|
def draw_poly(points, holes=None, closed=True):
|
||||||
|
"""
|
||||||
|
Aceita como pontos sequencias de tuplas, lista ou vetores com (x, y) ou (x, y, z).
|
||||||
|
Note que `holes` espera uma sequencias de sequencias ou uma única sequencia de
|
||||||
|
pontos. Por default faz um polígono fechado.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def depth(seq):
|
||||||
|
"""
|
||||||
|
usada para checar se temos um furo ou vários
|
||||||
|
devolve 2 para um só furo, 3 para vários furos
|
||||||
|
"""
|
||||||
|
if (isinstance(seq, list) or
|
||||||
|
isinstance(seq, tuple) or
|
||||||
|
isinstance(seq, PVector)):
|
||||||
|
return 1 + max(depth(item) for item in seq)
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
beginShape() # inicia o PShape
|
||||||
|
for p in points:
|
||||||
|
if len(p) == 2 or p[2] == 0:
|
||||||
|
vertex(p[0], p[1])
|
||||||
|
else:
|
||||||
|
vertex(*p) # desempacota pontos em 3d
|
||||||
|
# tratamento dos furos, se houver
|
||||||
|
holes = holes or [] # equivale a: holes if holes else []
|
||||||
|
if holes and depth(holes) == 2: # sequência única de pontos
|
||||||
|
holes = (holes,) # envolve em um tupla
|
||||||
|
for hole in holes: # para cada furo
|
||||||
|
beginContour() # inicia o furo
|
||||||
|
for p in hole:
|
||||||
|
if len(p) == 2 or p[2] == 0:
|
||||||
|
vertex(p[0], p[1])
|
||||||
|
else:
|
||||||
|
vertex(*p)
|
||||||
|
endContour() # final e um furo
|
||||||
|
# encerra o PShape
|
||||||
|
if closed:
|
||||||
|
endShape(CLOSE)
|
||||||
|
else:
|
||||||
|
endShape()
|
||||||
|
|
||||||
|
poly = draw_poly
|
||||||
|
|
||||||
|
def edges_as_sets(poly_points):
|
||||||
|
"""
|
||||||
|
Return a frozenset of poly edges as frozensets of 2 points.
|
||||||
|
"""
|
||||||
|
return frozenset(frozenset(edge) for edge in edges(poly_points))
|
||||||
|
|
||||||
|
def edges(poly_points):
|
||||||
|
"""
|
||||||
|
Return a list of edges (tuples containing pairs of points)
|
||||||
|
for a list of points that represent a closed polygon
|
||||||
|
"""
|
||||||
|
return pairwise(poly_points) + [(poly_points[-1], poly_points[0])]
|
||||||
|
|
||||||
|
def pairwise(iterable):
|
||||||
|
from itertools import tee
|
||||||
|
"s -> (s0, s1), (s1, s2), (s2, s3), ..."
|
||||||
|
a, b = tee(iterable)
|
||||||
|
next(b, None)
|
||||||
|
return zip(a, b)
|
||||||
|
|
||||||
|
|
||||||
|
def min_max(points):
|
||||||
|
"""
|
||||||
|
Return two PVectors with the most extreme coordinates,
|
||||||
|
resulting in "bounding box" corners.
|
||||||
|
"""
|
||||||
|
points = iter(points)
|
||||||
|
try:
|
||||||
|
p = points.next()
|
||||||
|
min_x, min_y = max_x, max_y = p[0], p[1]
|
||||||
|
except StopIteration:
|
||||||
|
raise ValueError, "min_max requires at least one point"
|
||||||
|
for p in points:
|
||||||
|
if p[0] < min_x:
|
||||||
|
min_x = p[0]
|
||||||
|
elif p[0] > max_x:
|
||||||
|
max_x = p[0]
|
||||||
|
if p[1] < min_y:
|
||||||
|
min_y = p[1]
|
||||||
|
elif p[1] > max_y:
|
||||||
|
max_y = p[1]
|
||||||
|
return (PVector(min_x, min_y),
|
||||||
|
PVector(max_x, max_y))
|
||||||
|
|
||||||
|
def par_hatch(points, divisions, *sides):
|
||||||
|
vectors = [PVector(p[0], p[1]) for p in points]
|
||||||
|
lines = []
|
||||||
|
if not sides:
|
||||||
|
sides = [0]
|
||||||
|
for s in sides:
|
||||||
|
a, b = vectors[-1 + s], vectors[+0 + s]
|
||||||
|
d, c = vectors[-2 + s], vectors[-3 + s]
|
||||||
|
for i in range(1, divisions):
|
||||||
|
s0 = PVector.lerp(a, b, i / float(divisions))
|
||||||
|
s1 = PVector.lerp(d, c, i / float(divisions))
|
||||||
|
lines.append(Line(s0, s1))
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def is_poly_self_intersecting(poly_points):
|
||||||
|
ed = edges(poly_points)
|
||||||
|
intersect = False
|
||||||
|
for a, b in ed[::-1]:
|
||||||
|
for c, d in ed[2::]:
|
||||||
|
# test only non consecutive edges
|
||||||
|
if (a != c) and (b != c) and (a != d):
|
||||||
|
if line_intersect(Line(a, b), Line(c, d)):
|
||||||
|
intersect = True
|
||||||
|
break
|
||||||
|
return intersect
|
||||||
|
|
||||||
|
def point_inside_poly(x, y, poly_points):
|
||||||
|
min_, max_ = min_max(poly_points)
|
||||||
|
if x < min_.x or y < min_.y or x > max_.x or y > max_.y:
|
||||||
|
return False
|
||||||
|
|
||||||
|
a = PVector(x, min_.y)
|
||||||
|
b = PVector(x, max_.y)
|
||||||
|
v_lines = inter_lines(Line(a, b), poly_points)
|
||||||
|
if not v_lines:
|
||||||
|
return False
|
||||||
|
|
||||||
|
a = PVector(min_.x, y)
|
||||||
|
b = PVector(max_.x, y)
|
||||||
|
h_lines = inter_lines(Line(a, b), poly_points)
|
||||||
|
if not h_lines:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for v in v_lines:
|
||||||
|
for h in h_lines:
|
||||||
|
if line_intersect(v, h):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def inter_lines(L, poly_points):
|
||||||
|
inter_points = []
|
||||||
|
for a, b in edges(poly_points):
|
||||||
|
inter = line_intersect(Line(a, b), L)
|
||||||
|
if inter:
|
||||||
|
inter_points.append(inter)
|
||||||
|
if not inter_points:
|
||||||
|
return []
|
||||||
|
inter_lines = []
|
||||||
|
if len(inter_points) > 1:
|
||||||
|
inter_points.sort()
|
||||||
|
pairs = zip(inter_points[::2], inter_points[1::2])
|
||||||
|
for a, b in pairs:
|
||||||
|
if b:
|
||||||
|
inter_lines.append(Line(PVector(a.x, a.y),
|
||||||
|
PVector(b.x, b.y)))
|
||||||
|
return inter_lines
|
||||||
|
|
||||||
|
def point_in_screen(p):
|
||||||
|
return 0 <= p[0] <= width and 0 <= p[1] <= height
|
||||||
Plik binarny nie jest wyświetlany.
|
Po Szerokość: | Wysokość: | Rozmiar: 188 KiB |
|
|
@ -0,0 +1,148 @@
|
||||||
|
"""
|
||||||
|
Explorando um experimento de geração de formas proposto por Leopoldo Leal
|
||||||
|
|
||||||
|
USE 'R' pre-calc poly paths for shape-maker pins
|
||||||
|
(non self-intersecting polys from 6 points in a grid)
|
||||||
|
|
||||||
|
OK - salvar em disco
|
||||||
|
OK - implementar no outro sketch pickle para ler os polígonos
|
||||||
|
OK - regra da exclusão de lines_shown ou colunas cheias
|
||||||
|
OK - exclusão de duplicaçoes com menos de 4 linhas ou 4 colunas
|
||||||
|
"""
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
from random import choice, sample, shuffle
|
||||||
|
from itertools import product, permutations, combinations
|
||||||
|
from line_geometry import is_poly_self_intersecting, draw_poly, edges_as_sets
|
||||||
|
add_library('pdf')
|
||||||
|
|
||||||
|
DEBUG = False
|
||||||
|
SIZE, BORDER, HEIGHT = 100, 100, 500
|
||||||
|
NUM_POINTS = 6
|
||||||
|
poly_groups = []
|
||||||
|
reduction = 10
|
||||||
|
lines_shown = 15
|
||||||
|
save_pdf = False # press 'p' to save a PDF
|
||||||
|
pin_size = 50 # for drawing the points repr.
|
||||||
|
|
||||||
|
def setup():
|
||||||
|
size(1280, 640)
|
||||||
|
create_point_groups()
|
||||||
|
# recalc_polys() # press "SHIFT+R"
|
||||||
|
|
||||||
|
def draw():
|
||||||
|
global save_pdf # necessário para desligar o 'flag'
|
||||||
|
if save_pdf: # inicia a gravação
|
||||||
|
beginRecord(PDF, "####.pdf")
|
||||||
|
background(200)
|
||||||
|
scale(1. / reduction)
|
||||||
|
for line_n in range(len(poly_groups[:lines_shown])):
|
||||||
|
pushMatrix()
|
||||||
|
translate(0, HEIGHT * line_n)
|
||||||
|
for i in range(16):
|
||||||
|
# use the first poly from poly_groups to draw pins
|
||||||
|
draw_pins(i, poly_groups[line_n][0])
|
||||||
|
translate(500, 0)
|
||||||
|
for i in range(len(poly_groups[line_n])):
|
||||||
|
pushMatrix()
|
||||||
|
translate(HEIGHT * i, 0)
|
||||||
|
draw_polys(i, poly_groups[line_n])
|
||||||
|
popMatrix()
|
||||||
|
i += 1
|
||||||
|
popMatrix()
|
||||||
|
if save_pdf: # termina a gravação
|
||||||
|
endRecord()
|
||||||
|
save_pdf = False
|
||||||
|
|
||||||
|
def create_point_groups():
|
||||||
|
global grid, point_groups
|
||||||
|
grid = list(product(range(BORDER, HEIGHT - BORDER + 1, SIZE),
|
||||||
|
range(BORDER, HEIGHT - BORDER + 1, SIZE)))
|
||||||
|
naive_point_groups = list(combinations(grid, 6)) # [120::80]]
|
||||||
|
control_set = set()
|
||||||
|
point_groups = []
|
||||||
|
for points in naive_point_groups:
|
||||||
|
tp = translated_points(points)
|
||||||
|
if tp not in control_set:
|
||||||
|
control_set.add(tp)
|
||||||
|
point_groups.append(points)
|
||||||
|
print("number of 6 point groups:{}".format(len(point_groups)))
|
||||||
|
|
||||||
|
def translated_points(points):
|
||||||
|
"""Return points translated to 0,0"""
|
||||||
|
minX = min(x for x, y in points)
|
||||||
|
minY = min(y for x, y in points)
|
||||||
|
return tuple(sorted((x - minX, y - minY)
|
||||||
|
for x, y in points))
|
||||||
|
|
||||||
|
def recalc_polys():
|
||||||
|
global grid, point_groups, poly_groups
|
||||||
|
poly_groups = [create_polys(points) for points in point_groups]
|
||||||
|
poly_groups = [polys for polys in poly_groups if polys]
|
||||||
|
print("6 point groups that generated good poly paths:{}".format(len(poly_groups)))
|
||||||
|
|
||||||
|
def create_polys(points, no_four_rule=True):
|
||||||
|
"""
|
||||||
|
Generate non-intersecting polygons' from points.
|
||||||
|
- check_lines avoids polys with 4 points in a row or col.
|
||||||
|
"""
|
||||||
|
all_polys = list(permutations(points, NUM_POINTS))
|
||||||
|
tested, polys = set(), []
|
||||||
|
for poly in all_polys:
|
||||||
|
edges = edges_as_sets(poly)
|
||||||
|
if edges not in tested and edges:
|
||||||
|
tested.add(edges)
|
||||||
|
polys.append(poly)
|
||||||
|
new_polys = [poly for poly in polys
|
||||||
|
if (not no_four_rule or check_lines(poly))
|
||||||
|
and not is_poly_self_intersecting(poly)]
|
||||||
|
print("non-crossing paths: {}".format(len(new_polys)))
|
||||||
|
return list(new_polys)
|
||||||
|
|
||||||
|
def check_lines(poly):
|
||||||
|
"""return False for polys with 4 colinear points."""
|
||||||
|
from collections import Counter
|
||||||
|
xs = Counter()
|
||||||
|
ys = Counter()
|
||||||
|
for x, y in poly:
|
||||||
|
xs[x] += 1
|
||||||
|
ys[y] += 1
|
||||||
|
if xs.most_common(1)[0][1] > 3 or ys.most_common(1)[0][1] > 3:
|
||||||
|
return False # polígono ruim, tem line_n ou coluna cheia
|
||||||
|
else:
|
||||||
|
return True # polígono bom
|
||||||
|
|
||||||
|
def draw_polys(i, polys):
|
||||||
|
if i < len(polys):
|
||||||
|
fill(0)
|
||||||
|
draw_poly(polys[i])
|
||||||
|
|
||||||
|
def draw_pins(i, points):
|
||||||
|
# resetMatrix()
|
||||||
|
noStroke()
|
||||||
|
fill(255, 100)
|
||||||
|
if grid[i] in points:
|
||||||
|
fill(0, 100)
|
||||||
|
circle(grid[i][0],
|
||||||
|
grid[i][1], pin_size * 2)
|
||||||
|
|
||||||
|
def keyPressed():
|
||||||
|
global save_pdf
|
||||||
|
if key == "p" or key == 'P':
|
||||||
|
save_pdf = True
|
||||||
|
if key == 'R':
|
||||||
|
recalc_polys()
|
||||||
|
|
||||||
|
if key == "s":
|
||||||
|
with open("data/poly.data", "wb") as file_out:
|
||||||
|
pickle.dump(poly_groups, file_out)
|
||||||
|
println("poly groups saved")
|
||||||
|
|
||||||
|
if key == "l":
|
||||||
|
with open("data/poly.data", "rb") as file_in:
|
||||||
|
saved_project = pickle.load(file_in)
|
||||||
|
poly_groups[:] = saved_project
|
||||||
|
println("poly groups loaded: {}".format(len(poly_groups)))
|
||||||
|
|
||||||
|
if key == "L":
|
||||||
|
shuffle(poly_groups)
|
||||||
|
|
@ -26,6 +26,12 @@ Here are listed some of the tools I have been using:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
[sketch_2020_11_02polys](https://github.com/villares/sketch-a-day/tree/master/2020/sketch_2020_11_02polys) [[Py.Processing](https://villares.github.io/como-instalar-o-processing-modo-python/index-EN)]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
[sketch_2020_11_01a](https://github.com/villares/sketch-a-day/tree/master/2020/sketch_2020_11_01a) [[Py.Processing](https://villares.github.io/como-instalar-o-processing-modo-python/index-EN)]
|
[sketch_2020_11_01a](https://github.com/villares/sketch-a-day/tree/master/2020/sketch_2020_11_01a) [[Py.Processing](https://villares.github.io/como-instalar-o-processing-modo-python/index-EN)]
|
||||||
|
|
|
||||||
Ładowanie…
Reference in New Issue