diff --git a/2020/sketch_2020_09_23a/arc_filleted_poly.py b/2020/sketch_2020_09_23a/arc_filleted_poly.py new file mode 100644 index 00000000..bfdba518 --- /dev/null +++ b/2020/sketch_2020_09_23a/arc_filleted_poly.py @@ -0,0 +1,127 @@ +# -*- coding: UTF-8 -*- + + +def arc_filleted_poly(p_list, r_list, open_poly=False, arc_func=arc): + """ + draws a 'filleted' polygon with variable radius + dependent on roundedCorner() + """ + p_list = list(p_list) + r_list = list(r_list) + + if not open_poly: + # with pushStyle(): + # noStroke() + # beginShape() + # for p0, p1 in zip(p_list, [p_list[-1]] + p_list[:-1]): + # m = (PVector(p0.x, p0.y) + PVector(p1.x, p1.y)) / 2 + # vertex(m.x, m.y) + # endShape(CLOSE) + for p0, p1, p2, r in zip(p_list, + [p_list[-1]] + p_list[:-1], + [p_list[-2]] + [p_list[-1]] + p_list[:-2], + [r_list[-1]] + r_list[:-1] + ): + m1 = (PVector(p0.x, p0.y) + PVector(p1.x, p1.y)) / 2 + m2 = (PVector(p2.x, p2.y) + PVector(p1.x, p1.y)) / 2 + roundedCorner(p1, m1, m2, r, arc_func) + else: + for p0, p1, p2, r in zip(p_list[:-1], + [p_list[-1]] + p_list[:-2], + [p_list[-2]] + [p_list[-1]] + p_list[:-3], + [r_list[-1]] + r_list[:-2] + ): + m1 = (PVector(p0.x, p0.y) + PVector(p1.x, p1.y)) / 2 + m2 = (PVector(p2.x, p2.y) + PVector(p1.x, p1.y)) / 2 + roundedCorner(p1, m1, m2, r, arc_func) + + +def roundedCorner(pc, p1, p2, r, arc_func): + """ + Based on Stackoverflow C# rounded corner post + https://stackoverflow.com/questions/24771828/algorithm-for-creating-rounded-corners-in-a-polygon + """ + + def GetProportionPoint(pt, segment, L, dx, dy): + factor = float(segment) / L if L != 0 else segment + return PVector((pt.x - dx * factor), (pt.y - dy * factor)) + + # Vector 1 + dx1 = pc.x - p1.x + dy1 = pc.y - p1.y + + # Vector 2 + dx2 = pc.x - p2.x + dy2 = pc.y - p2.y + + # Angle between vector 1 and vector 2 divided by 2 + angle = (atan2(dy1, dx1) - atan2(dy2, dx2)) / 2 + + # The length of segment between angular point and the + # points of intersection with the circle of a given radius + tng = abs(tan(angle)) + segment = r / tng if tng != 0 else r + + # Check the segment + length1 = sqrt(dx1 * dx1 + dy1 * dy1) + length2 = sqrt(dx2 * dx2 + dy2 * dy2) + + min_len = min(length1, length2) + + if segment > min_len: + segment = min_len + max_r = min_len * abs(tan(angle)) + else: + max_r = r + + # Points of intersection are calculated by the proportion between + # length of vector and the length of the segment. + p1Cross = GetProportionPoint(pc, segment, length1, dx1, dy1) + p2Cross = GetProportionPoint(pc, segment, length2, dx2, dy2) + + # Calculation of the coordinates of the circle + # center by the addition of angular vectors. + dx = pc.x * 2 - p1Cross.x - p2Cross.x + dy = pc.y * 2 - p1Cross.y - p2Cross.y + + L = sqrt(dx * dx + dy * dy) + d = sqrt(segment * segment + max_r * max_r) + + circlePoint = GetProportionPoint(pc, d, L, dx, dy) + + # StartAngle and EndAngle of arc + startAngle = atan2(p1Cross.y - circlePoint.y, p1Cross.x - circlePoint.x) + endAngle = atan2(p2Cross.y - circlePoint.y, p2Cross.x - circlePoint.x) + + # Sweep angle + sweepAngle = endAngle - startAngle + + # Some additional checks + if sweepAngle < 0: + startAngle, endAngle = endAngle, startAngle + sweepAngle = -sweepAngle + + if sweepAngle > PI: + startAngle, endAngle = endAngle, startAngle + sweepAngle = TWO_PI - sweepAngle + + # Draw result using graphics + # noStroke() + # with pushStyle(): + # noStroke() + # beginShape() + # vertex(p1.x, p1.y) + # vertex(p1Cross.x, p1Cross.y) + # vertex(p2Cross.x, p2Cross.y) + # vertex(p2.x, p2.y) + # endShape(CLOSE) + + circle(p1.x, p1.y, 5) + circle(p1Cross.x, p1Cross.y, 5) + circle(p2Cross.x, p2Cross.y, 5) + circle(p2.x, p2.y, 5) + + line(p1.x, p1.y, p1Cross.x, p1Cross.y) + line(p2.x, p2.y, p2Cross.x, p2Cross.y) + arc_func(circlePoint.x, circlePoint.y, 2 * max_r, 2 * max_r, + startAngle, startAngle + sweepAngle) diff --git a/2020/sketch_2020_09_23a/ponto.py b/2020/sketch_2020_09_23a/ponto.py new file mode 100644 index 00000000..6109bd0a --- /dev/null +++ b/2020/sketch_2020_09_23a/ponto.py @@ -0,0 +1,26 @@ +# -*- coding: UTF-8 -*- + + +class Ponto(): + + SIZE = 5 + + def __init__(self, x, y): + self.ox = x + self.oy = y + self.x = x + self.y = y + self.f = 255 + + def __len__(self): + return 2 + + def __iter__(self): + return iter((self.x, self.y)) + + def __getitem__(self, i): + return (self.x, self.y)[i] + + def draw(self): + fill(self.f) + circle(self.x, self.y, Ponto.SIZE) diff --git a/2020/sketch_2020_09_23a/sketch_2020_09_23a.gif b/2020/sketch_2020_09_23a/sketch_2020_09_23a.gif new file mode 100644 index 00000000..ae754b87 Binary files /dev/null and b/2020/sketch_2020_09_23a/sketch_2020_09_23a.gif differ diff --git a/2020/sketch_2020_09_23a/sketch_2020_09_23a.pyde b/2020/sketch_2020_09_23a/sketch_2020_09_23a.pyde new file mode 100644 index 00000000..a9e01c79 --- /dev/null +++ b/2020/sketch_2020_09_23a/sketch_2020_09_23a.pyde @@ -0,0 +1,81 @@ +from random import sample +from itertools import product +from villares.line_geometry import * # github.com/villares/villares +from arc_filleted_poly import arc_filleted_poly +from ponto import Ponto + + + +BORDER = 50 +SIZE = 150 +r_list = [10, 20, 30, 40, -10, -20, -30, -40] +p_list = [] +dragg = [] + +def setup(): + global grid + size(400, 400) + grid = list(product(range(BORDER, height - BORDER + 1, SIZE), + range(BORDER, height - BORDER + 1, SIZE))) + make_points(8) + +def draw(): + background(200) + + if len(dragg) == 2: + d_line = Line(*dragg) + strokeWeight(0.5) + d_line.draw() + lines = inter_lines(d_line, p_list) + for inter_line in lines: + strokeWeight(2) + inter_line.draw() + + + noFill() + strokeWeight(1) + stroke(255) + poly(map(tuple, p_list)) + stroke(0) + fill(100, 100) + arc_filleted_poly(p_list, map(abs, r_list)) + + for p in p_list: + if len(dragg) == 2: + if area(p, dragg[0], dragg[1]) > 0: + p.f = color(255, 0, 0) + else: + p.f = color(0, 0, 255) + else: + p.f = 255 + p.draw() + + +def keyPressed(): + if key == ' ': make_points(8); saveFrame("###.png") + if key == 'm': move_points() + +def mousePressed(): + dragg[:] = [(mouseX, mouseY)] + +def mouseDragged(): + if len(dragg) == 1: + dragg.append((mouseX, mouseY)) + else: + dragg[1] = (mouseX, mouseY) + +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 + +# def mouseReleased(): +# dragg[:] = [] + + +def make_points(num): + p_list[:] = [] + for x, y in sample(grid, num): + p_list.append(Ponto(x, y)) + diff --git a/README.md b/README.md index 3bfe5746..dfae3f83 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,12 @@ Some of the tools I have used: --- +![sketch_2020_09_23a](2020/sketch_2020_09_23a/sketch_2020_09_23a.gif) + +[sketch_2020_09_23a](https://github.com/villares/sketch-a-day/tree/master/2020/sketch_2020_09_23a) [[Py.Processing](https://villares.github.io/como-instalar-o-processing-modo-python/index-EN)] + +--- + ![sketch_2020_09_22a](2020/sketch_2020_09_22a/sketch_2020_09_22a.gif) [sketch_2020_09_22a](https://github.com/villares/sketch-a-day/tree/master/2020/sketch_2020_09_22a) [[Py.Processing](https://villares.github.io/como-instalar-o-processing-modo-python/index-EN)]