diff --git a/2020/sketch_2020_03_03a/sketch_2020_03_03a.pyde b/2020/sketch_2020_03_03a/sketch_2020_03_03a.pyde index 175fef09..ca9fa38a 100644 --- a/2020/sketch_2020_03_03a/sketch_2020_03_03a.pyde +++ b/2020/sketch_2020_03_03a/sketch_2020_03_03a.pyde @@ -1,5 +1,5 @@ -add_library('gifAnimation') -from gif_animation_helper import gif_export +# add_library('gifAnimation') +# from gif_animation_helper import gif_export from arcs import * @@ -51,9 +51,9 @@ def draw(): else: t = 0 i = (i + 1) % num_transitions - if i == 0: - gif_export(GifMaker, finish=True) - gif_export(GifMaker, filename="sketch") + # if i == 0: + # gif_export(GifMaker, finish=True) + # gif_export(GifMaker, filename="sketch") def lerp_arrow(a, b, t): result = [] diff --git a/2020/sketch_2020_03_04a/arcs.py b/2020/sketch_2020_03_04a/arcs.py new file mode 100644 index 00000000..f5f0eb6b --- /dev/null +++ b/2020/sketch_2020_03_04a/arcs.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +from __future__ import division + +ROTATION = {0: 0, + BOTTOM: 0, + DOWN: 0, + 1: HALF_PI, + LEFT: HALF_PI, + 2: PI, + TOP: PI, + UP: PI, + 3: PI + HALF_PI, + RIGHT: PI + HALF_PI, + BOTTOM + RIGHT: 0, + DOWN + RIGHT: 0, + DOWN + LEFT: HALF_PI, + BOTTOM + LEFT: HALF_PI, + TOP + LEFT: PI, + UP + LEFT: PI, + TOP + RIGHT: PI + HALF_PI, + UP + RIGHT: PI + HALF_PI, + } + +def quarter_circle(x, y, radius, quadrant): + circle_arc(x, y, radius, ROTATION[quadrant], HALF_PI) + +def half_circle(x, y, radius, quadrant): + circle_arc(x, y, radius, ROTATION[quadrant], PI) + +def circle_arc(x, y, radius, start_ang, sweep_ang): + arc(x, y, radius * 2, radius * 2, start_ang, start_ang + sweep_ang) + +def b_circle_arc(x, y, radius, start_ang, sweep_ang, mode=0): + """ + Alternative interface for b_arc + """ + b_arc(x, y, radius * 2, radius * 2, start_ang, start_ang + sweep_ang, + mode=mode) + +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" or stand-alone 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) <= QUARTER_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) <= QUARTER_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 p_circle_arc(x, y, radius, start_ang, sweep_ang, mode=0, num_points=None): + """ + Alternative interface for b_arc + """ + p_arc(x, y, radius * 2, radius * 2, start_ang, start_ang + sweep_ang, + mode=mode, num_points=num_points) + +def p_arc(cx, cy, w, h, start_angle, end_angle, mode=0, num_points=None): + """ + A poly approximation of an arc + using the same signature as the original Processing arc() + mode: 0 "normal" arc, using beginShape() and endShape() + 2 "naked" like normal, but without beginShape() and endShape() + for use inside a larger PShape + """ + if not num_points: + num_points = 36 + # start_angle = start_angle if start_angle < end_angle else start_angle - TWO_PI + sweep_angle = end_angle - start_angle + if mode == 0: + beginShape() + if sweep_angle < 0: + start_angle, end_angle = end_angle, start_angle + sweep_angle = -sweep_angle + angle = sweep_angle / int(num_points) + a = end_angle + while a >= start_angle: + sx = cx + cos(a) * w / 2. + sy = cy + sin(a) * h / 2. + vertex(sx, sy) + a -= angle + elif sweep_angle > 0: + angle = sweep_angle / int(num_points) + a = start_angle + while a <= end_angle: + sx = cx + cos(a) * w / 2. + sy = cy + sin(a) * h / 2. + vertex(sx, sy) + a += angle + else: + sx = cx + cos(start_angle) * w / 2. + sy = cy + sin(start_angle) * h / 2. + vertex(sx, sy) + if mode == 0: + endShape() diff --git a/2020/sketch_2020_03_04a/gif_animation_helper.py b/2020/sketch_2020_03_04a/gif_animation_helper.py new file mode 100644 index 00000000..98e81539 --- /dev/null +++ b/2020/sketch_2020_03_04a/gif_animation_helper.py @@ -0,0 +1,44 @@ +""" +Alexandre B A Villares http://abav.lugaralgum.com - GPL v3 + +A helper for the Processing gifAnimation library https://github.com/extrapixel/gif-animation/tree/3.0 +Download from https://github.com/villares/processing-play/blob/master/export_GIF/unzip_and_move_to_libraries_GifAnimation.zip +This helper was inspired by an example by Art Simon https://github.com/APCSPrinciples/AnimatedGIF/ + +v2019_09_23 + +# add at the start of your sketch: + add_library('gifAnimation') + from gif_animation_helper import gif_export +# add at the end of draw(): + gif_export(GifMaker, "filename") +""" + +def gif_export(GifMaker, # gets a reference to the library + filename="exported", # .gif will be added + repeat=0, # 0 makes it an "endless" animation + quality=200, # quality range 0 - 255 + delay=170, # this is quick + frames=0, # 0 will stop on keyPressed or frameCount >= 100000 + finish=False): # force stop + global gifExporter + try: + gifExporter + except NameError: + gifExporter = GifMaker(this, filename + ".gif") + gifExporter.setRepeat(repeat) + gifExporter.setQuality(quality) + gifExporter.setDelay(delay) + print("gif recording started") + + + gifExporter.addFrame() + + if (frames == 0 and keyPressed and key == "e" or + frames != 0 and frameCount >= frames): + finish = True + + if finish: + gifExporter.finish() + print("gif saved, exit") + exit() diff --git a/2020/sketch_2020_03_04a/sketch_2020_03_04a.pyde b/2020/sketch_2020_03_04a/sketch_2020_03_04a.pyde new file mode 100644 index 00000000..4904f30f --- /dev/null +++ b/2020/sketch_2020_03_04a/sketch_2020_03_04a.pyde @@ -0,0 +1,156 @@ +add_library('gifAnimation') +from gif_animation_helper import gif_export + +from arcs import * + +many_arrows = [] +num_arrows, num_transitions = 3, 4 +i, t = 0, 0 + +def setup(): + size(400, 400) + colorMode(HSB) + strokeJoin(ROUND) + frameRate(30) + smooth(8) + for _ in range(num_transitions): + many_arrows.append(create_arrows()) + global starting_arrows + many_arrows.append(many_arrows[0][:]) + + +def create_arrows(): + arrows = [] + for _ in range(num_arrows): + arrows.append(random_arrow()) + return arrows + +def draw(): + global t, i + background(200) + if t <= width: + tt = map(t, 0, width, 0, 1) + else: + tt = 1 + + ini_arrows, fin_arrows = many_arrows[i], many_arrows[i + 1] + for a, b in zip(ini_arrows, fin_arrows): + mid_arrow = lerp_arrow(a, b, tt) + rad, start, sweep, thick, h = mid_arrow + noFill() + strokeWeight(4) + if mouse_on_arrow(mid_arrow): + stroke(255) + else: + stroke(h, 255, 200) + if thick > 0: + start = TWO_PI * tt + else: + start = TWO_PI * -tt + arc_arrow(width / 2, height / 2, rad, start, sweep, thick) + + if t < width: + t = lerp(t, width + 1, .1) + else: + t = 0 + i = (i + 1) % num_transitions + if i == 0: + gif_export(GifMaker, finish=True) + gif_export(GifMaker, filename="sketch") + +def lerp_arrow(a, b, t): + result = [] + for c_a, c_b in zip(a, b): + result.append(lerp(c_a, c_b, t)) + return result + +def random_arrow(): + d = -1 if random(100) >= 50 else 1 + return [int(random(10, height / 12)) * 5, + 0, # start + int(6 * random(QUARTER_PI, TWO_PI - QUARTER_PI) / 6), # sweep + int(random(2, height / 100)) * 10 * d, # thickness + random(256) # hue + ] + +def mouse_on_arrow(a, precision=10): + mx, my = width / 2, height / 2 + rad, start, sweep, thick, _ = a + start += thick * radians(frameCount % 361) / 10. + same_rad = abs(abs(rad) - dist(mouseX, mouseY, mx, my)) < precision + mouse_ang = atan2(mouseY - my, mouseX - mx) + # in_sweep = start < mouse_ang < degrees(start + sweep) % 360 + return same_rad #and in_sweep + + +def keyPressed(): + if key == ' ': + many_arrows[:] = [] + for _ in range(3): + many_arrows.append(create_arrows()) + if key == 's': + saveFrame('#####.png') + +# def mouseWheel(e): +# w = e.getAmount() +# player_arrow[3] += int(w) * 10 +# if player_arrow[3] < 10: +# player_arrow[3] = 10 + +# def mouseDragged(): +# dx = mouseX - pmouseX +# dy = mouseY - pmouseY +# player_arrow[2] += radians(dx) +# player_arrow[0] += dy + +def arc_arrow(x, y, radius, start_ang, sweep_ang, + thickness=None, correction=1): + """ + Draws an arrow in a circular arc shape + """ + if thickness is None: + thickness = radius / 2 + if abs(radius) * .75 < abs(thickness / 2): + thickness = radius * 1.5 if thickness > 0 else -radius * 1.5 + if radius < 0: + thickness = -thickness + + beginShape() + b_circle_arc(x, y, radius + thickness / 2, + start_ang, sweep_ang, mode=2) + mid_ending(x, y, radius, start_ang + sweep_ang, thickness, correction) + b_circle_arc(x, y, radius - thickness / 2, + start_ang + sweep_ang, -sweep_ang, mode=2) + mid_ending(x, y, radius, start_ang, thickness, correction) + endShape(CLOSE) + +def mid_ending(x, y, radius, angle, thickness, correction): + if radius == 0: + radius = 1 + half_thick = thickness / 2. + pa = point_on_arc(x, y, radius + half_thick, angle) + pb = point_on_arc(x, y, radius - half_thick, angle) + mp = mid_point(pa, pb) + tangent_ep = point_on_arc(mp[0], mp[1], half_thick, angle + HALF_PI) + offset = half_thick / radius + midline_ep = point_on_arc(x, y, radius, angle + offset) + if correction == 0: # tangent + vertex(*tangent_ep) + elif correction == 2: # on mid-line + vertex(*midline_ep) + else: # half-way + vertex(*mid_point(tangent_ep, midline_ep)) + +def mid_point(pa, pb): + if len(pa) == 3 and len(pb) == 3: + return ((pa[0] + pb[0]) / 2, + (pa[1] + pb[1]) / 2, + (pa[2] + pb[2]) / 2) + else: + return ((pa[0] + pb[0]) / 2, + (pa[1] + pb[1]) / 2) + +def point_on_arc(cx, cy, radius, angle): + x = cx + radius * cos(angle) + y = cy + radius * sin(angle) + return (x, y)