diff --git a/2020/sketch_2020_02_11a/0083.png b/2020/sketch_2020_02_11a/0083.png new file mode 100644 index 00000000..52fd856e Binary files /dev/null and b/2020/sketch_2020_02_11a/0083.png differ diff --git a/2020/sketch_2020_02_11a/0117.png b/2020/sketch_2020_02_11a/0117.png new file mode 100644 index 00000000..b31e8e02 Binary files /dev/null and b/2020/sketch_2020_02_11a/0117.png differ diff --git a/2020/sketch_2020_02_11a/0175.png b/2020/sketch_2020_02_11a/0175.png new file mode 100644 index 00000000..3fea60bb Binary files /dev/null and b/2020/sketch_2020_02_11a/0175.png differ diff --git a/2020/sketch_2020_02_11a/0197.png b/2020/sketch_2020_02_11a/0197.png new file mode 100644 index 00000000..171172bf Binary files /dev/null and b/2020/sketch_2020_02_11a/0197.png differ diff --git a/2020/sketch_2020_02_11a/0230.png b/2020/sketch_2020_02_11a/0230.png new file mode 100644 index 00000000..836509d1 Binary files /dev/null and b/2020/sketch_2020_02_11a/0230.png differ diff --git a/2020/sketch_2020_02_11a/0265.png b/2020/sketch_2020_02_11a/0265.png new file mode 100644 index 00000000..85876f0c Binary files /dev/null and b/2020/sketch_2020_02_11a/0265.png differ diff --git a/2020/sketch_2020_02_11a/forms.py b/2020/sketch_2020_02_11a/forms.py new file mode 100644 index 00000000..a28d9fc9 --- /dev/null +++ b/2020/sketch_2020_02_11a/forms.py @@ -0,0 +1,165 @@ +def b_poly_arc_augmented(op_list, or_list=None, check_intersection=False): + if not op_list: return + if or_list == None: + r2_list = [0] * len(op_list) + else: + r2_list = or_list[:] + assert len(op_list) == len(r2_list), \ + "Number of points and radii not the same" + # remove overlapping adjacent points + p_list, r_list = [], [] + for i1, p1 in enumerate(op_list): + i2 = (i1 - 1) + p2, r2, r1 = op_list[i2], r2_list[i2], r2_list[i1] + if dist(p1[0], p1[1], p2[0], p2[1]) > 1: # or p1 != p2: + p_list.append(p1) + r_list.append(r1) + else: + r2_list[i2] = min(r1, r2) + # invert radius + for i1, p1 in enumerate(p_list): + i0 = (i1 - 1) + p0 = p_list[i0] + i2 = (i1 + 1) % len(p_list) + p2 = p_list[i2] + a = area(p0, p1, p2) / 1000. + if or_list == None: + r_list[i1] = a + else: + # if abs(a) < 1: + # r_list[i1] = r_list[i1] * abs(a) + if a <= 0: + r_list[i1] = -r_list[i1] + # reduce radius that won't fit + for i1, p1 in enumerate(p_list): + i2 = (i1 + 1) % len(p_list) + p2, r2, r1 = p_list[i2], r_list[i2], r_list[i1] + r_list[i1], r_list[i2] = reduce_radius(p1, p2, r1, r2) + # calculate the tangents + a_list = [] + for i1, p1 in enumerate(p_list): + i2 = (i1 + 1) % len(p_list) + p2, r2, r1 = p_list[i2], r_list[i2], r_list[i1] + a = circ_circ_tangent(p1, p2, r1, r2) + a_list.append(a) + # draw + beginShape() + for i1, ia in enumerate(a_list): + i2 = (i1 + 1) % len(a_list) + p1, p2, r1, r2 = p_list[i1], p_list[i2], r_list[i1], r_list[i2] + a1, p11, p12 = ia + a2, p21, p22 = a_list[i2] + circle(p1[0], p1[1], 10, 10) + if a1 != None and a2 != None: + start = a1 if a1 < a2 else a1 - TWO_PI + if r2 < 0: + a2 = a2 - TWO_PI + b_arc(p2[0], p2[1], r2 * 2, r2 * 2, start, a2, mode=2) + else: + # when the the segment is smaller than the diference between + # radius, circ_circ_tangent won't renturn the angle + # ellipse(p2[0], p2[1], r2 * 2, r2 * 2) # debug + if a1: + vertex(p12[0], p12[1]) + if a2: + vertex(p21[0], p21[1]) + endShape(CLOSE) + +def reduce_radius(p1, p2, r1, r2): + d = dist(p1[0], p1[1], p2[0], p2[1]) + ri = abs(r1 - r2) + if d - ri <= 0: + if abs(r1) > abs(r2): + r1 = map(d, ri + 1, 0, r1, r2) + else: + r2 = map(d, ri + 1, 0, r2, r1) + return(r1, r2) + +def circ_circ_tangent(p1, p2, r1, r2): + d = dist(p1[0], p1[1], p2[0], p2[1]) + ri = r1 - r2 + line_angle = atan2(p1[0] - p2[0], p2[1] - p1[1]) + if d - abs(ri) >= 0: + theta = asin(ri / float(d)) + x1 = -cos(line_angle + theta) * r1 + y1 = -sin(line_angle + theta) * r1 + x2 = -cos(line_angle + theta) * r2 + y2 = -sin(line_angle + theta) * r2 + return (line_angle + theta, + (p1[0] - x1, p1[1] - y1), + (p2[0] - x2, p2[1] - y2)) + else: + return (None, + (p1[0], p1[1]), + (p2[0], p2[1])) + +def b_arc(cx, cy, w, h, start_angle, end_angle, mode=0): + """ + A bezier approximation of an arc + using the same signature as the original Processing arc() + mode: 0 "normal" arc, using beginShape() and endShape() + 1 "middle" used in recursive call of smaller arcs + 2 "naked" like normal, but without beginShape() and endShape() + for use inside a larger PShape + """ + theta = end_angle - start_angle + # Compute raw Bezier coordinates. + if mode != 1 or abs(theta) < HALF_PI: + x0 = cos(theta / 2.0) + y0 = sin(theta / 2.0) + x3 = x0 + y3 = 0 - y0 + x1 = (4.0 - x0) / 3.0 + if y0 != 0: + y1 = ((1.0 - x0) * (3.0 - x0)) / (3.0 * y0) # y0 != 0... + else: + y1 = 0 + x2 = x1 + y2 = 0 - y1 + # Compute rotationally-offset Bezier coordinates, using: + # x' = cos(angle) * x - sin(angle) * y + # y' = sin(angle) * x + cos(angle) * y + bezAng = start_angle + theta / 2.0 + cBezAng = cos(bezAng) + sBezAng = sin(bezAng) + rx0 = cBezAng * x0 - sBezAng * y0 + ry0 = sBezAng * x0 + cBezAng * y0 + rx1 = cBezAng * x1 - sBezAng * y1 + ry1 = sBezAng * x1 + cBezAng * y1 + rx2 = cBezAng * x2 - sBezAng * y2 + ry2 = sBezAng * x2 + cBezAng * y2 + rx3 = cBezAng * x3 - sBezAng * y3 + ry3 = sBezAng * x3 + cBezAng * y3 + # Compute scaled and translated Bezier coordinates. + rx, ry = w / 2.0, h / 2.0 + px0 = cx + rx * rx0 + py0 = cy + ry * ry0 + px1 = cx + rx * rx1 + py1 = cy + ry * ry1 + px2 = cx + rx * rx2 + py2 = cy + ry * ry2 + px3 = cx + rx * rx3 + py3 = cy + ry * ry3 + # Debug points... comment this out! + # stroke(0) + # ellipse(px3, py3, 15, 15) + # ellipse(px0, py0, 5, 5) + # Drawing + if mode == 0: # 'normal' arc (not 'middle' nor 'naked') + beginShape() + if mode != 1: # if not 'middle' + vertex(px3, py3) + if abs(theta) < HALF_PI: + bezierVertex(px2, py2, px1, py1, px0, py0) + else: + # to avoid distortion, break into 2 smaller arcs + b_arc(cx, cy, w, h, start_angle, end_angle - theta / 2.0, mode=1) + b_arc(cx, cy, w, h, start_angle + theta / 2.0, end_angle, mode=1) + if mode == 0: # end of a 'normal' arc + endShape() + +def area(p0, p1, p2): + a = (p1[0] * (p2[1] - p0[1]) + + p2[0] * (p0[1] - p1[1]) + + p0[0] * (p1[1] - p2[1])) + return a diff --git a/2020/sketch_2020_02_11a/line_geometry.py b/2020/sketch_2020_02_11a/line_geometry.py new file mode 100644 index 00000000..ef5087aa --- /dev/null +++ b/2020/sketch_2020_02_11a/line_geometry.py @@ -0,0 +1,164 @@ + +def intersecting(poly_points): + ed = edges(poly_points) + intersect = False + for p1, p2 in ed[::-1]: + for p3, p4 in ed[2::]: + # test only non consecutive edges + if (p1 != p3) and (p2 != p3) and (p1 != p4): + if line_instersect(Line(p1, p2), Line(p3, p4)): + intersect = True + break + return intersect + +def is_inside(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_instersect(v, h): + return True + + return False + + +def inter_lines(L, poly_points): + inter_points = [] + for p1, p2 in edges(poly_points): + inter = line_instersect(Line(p1, p2), 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 p1, p2 in pairs: + if p2: + inter_lines.append(Line(PVector(p1.x, p1.y), + PVector(p2.x, p2.y))) + return inter_lines + + +class Line(): + """ I should change this to a named tuple... """ + def __init__(self, p1, p2): + self.p1 = PVector(*p1) + self.p2 = PVector(*p2) + + def __getitem__(self, i): + return (self.p1, self.p2)[i] + + def plot(self): + line(self.p1.x, self.p1.y, self.p2.x, self.p2.y) + + def lerp(self, other, t): + p1 = PVector.lerp(self.p1, other.p1, t) + p2 = PVector.lerp(self.p2, other.p2, t) + return Line(p1, p2) + +def line_instersect(line_a, line_b): + """ + code adapted from Bernardo Fontes + https://github.com/berinhard/sketches/ + """ + x1, y1 = line_a.p1.x, line_a.p1.y + x2, y2 = line_a.p2.x, line_a.p2.y + x3, y3 = line_b.p1.x, line_b.p1.y + x4, y4 = line_b.p2.x, line_b.p2.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.p1.x + uA * (line_a.p2.x - line_a.p1.x) + y = line_a.p1.y + uA * (line_a.p2.y - line_a.p1.y) + + return PVector(x, y) + # """ + # code adapted from + # https://stackoverflow.com/questions/27745972/test-if-polylines-intersects-using-python + # """ + # xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0]) + # ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1]) + + # def det(a, b): + # return a[0] * b[1] - a[1] * b[0] + + # div = det(xdiff, ydiff) + # if div == 0: + # return None + + # d = (det(*line1), det(*line2)) + # x = det(d, xdiff) / div + # y = det(d, ydiff) / div + # return x, y + # return PVector(x, y) + + +def edges(poly_points): + return pairwise(poly_points) + [(poly_points[-1], poly_points[0])] + +def pairwise(iterable): + import itertools + "s -> (s0,s1), (s1,s2), (s2, s3), ..." + a, b = itertools.tee(iterable) + next(b, None) + return zip(a, b) + +def edges_as_sets(poly_points): + edge_set_pairs = set() + for edge in edges(poly_points): + s = frozenset(edge) + edge_set_pairs.add(s) + return frozenset(edge_set_pairs) + + +def min_max(points): + points = iter(points) + try: + p = points.next() + min_x, min_y = max_x, max_y = p.x, p.y + except StopIteration: + raise ValueError, "min_max requires at least one point" + for p in points: + if p.x < min_x: + min_x = p.x + elif p.x > max_x: + max_x = p.x + if p.y < min_y: + min_y = p.y + elif p.y > max_y: + max_y = p.y + return (PVector(min_x, min_y), + PVector(max_x, max_y)) + +def par_hatch(points, divisions, *sides): + vectors = [PVector(p.x, p.y) 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 diff --git a/2020/sketch_2020_02_11a/sketch_2020_02_11a.png b/2020/sketch_2020_02_11a/sketch_2020_02_11a.png new file mode 100644 index 00000000..b8131652 Binary files /dev/null and b/2020/sketch_2020_02_11a/sketch_2020_02_11a.png differ diff --git a/2020/sketch_2020_02_11a/sketch_2020_02_11a.pyde b/2020/sketch_2020_02_11a/sketch_2020_02_11a.pyde new file mode 100644 index 00000000..6a32b698 --- /dev/null +++ b/2020/sketch_2020_02_11a/sketch_2020_02_11a.pyde @@ -0,0 +1,91 @@ +# Alexandre B A Villares - https://abav.lugaralgum.com/sketch-a-day + +from random import choice, sample, shuffle +from itertools import product, permutations, combinations +from forms import b_poly_arc_augmented +from line_geometry import * + +NUM_POINTS = 6 +BORDER = 100 +SIZE = 100 +RDS = 50 + +def setup(): + size(500, 500) + global ensambles + ensambles = create_ensambles(create_points(), 30) + +def create_ensambles(polys, r=1): + ens = [] + for poly in polys: + for i in range(64): + rads = [] + rad_opts = num_to_base(i, 2, 6) + for c in rad_opts: + if c == "0": + rads.append(-1 * r) + else: + rads.append(1 * r) + shuffle(rads) + print(rads) + ens.append((poly, rads)) + return ens + +def create_points(): + """ non intersecting poly """ + grid = list(product(range(BORDER, width - BORDER + 1, SIZE), + range(BORDER, height - BORDER + 1, SIZE))) + points = sample(grid, 6) + total = list(combinations(grid, 6)) + polys = list(permutations(points, NUM_POINTS)) + tested = set() + for poly in polys[:]: + edges = edges_as_sets(poly) + if edges not in tested and edges: + tested.add(edges) + else: + polys.remove(poly) + print("inicial: {}".format(len(polys))) + ni_polys = [] + for poly in polys: + if not intersecting(poly): + ni_polys.append(poly) + print("sem auto-cruzar: {}".format(len(ni_polys))) + return list(ni_polys) + +def draw(): + background(200) + # b_poly_arc_augmented(ens, [30] * 6) + background(200) + scale(1/16.) + e = 0 + total = len(ensambles) + for j in range(16): + for i in range(16): + pushMatrix() + translate(width * i, height * j) + if e < total: + noFill() + strokeWeight(16) + b_poly_arc_augmented(ensambles[e][0], ensambles[e][1]) + popMatrix() + e += 1 + +def keyPressed(): + if key == "p" or key == 'P': + saveFrame("####.png") + if key == ' ': + ensambles[:] = create_ensambles(create_points(), 30) + + +def num_to_base(num, base, pad=0): + BS = "" + for i in range(base): + BS += str(i) + result = "" + while num: + result += BS[num % base] + num //= base + while len(result) < pad: + result = result + "0" + return result[::-1]