py5coding / py5generator

Meta-programming project that creates the py5 library code.
https://py5coding.org/
GNU General Public License v3.0
53 stars 13 forks source link

Inconsistency in how `no_loop()` works with Live Coding #554

Open hx2A opened 1 week ago

hx2A commented 1 week ago

From discussion https://github.com/py5coding/py5generator/discussions/545

I tried converting @vsquared 's code to module mode and found it did not work:

# Reference for Turtle Graphics in Processing
# https://gist.githubusercontent.com/nataliefreed/8483050/raw/9e3f1d0f44bcb0c872762e4b984358d375e7b5fa/turtle.pde
import py5

end = 0.0
loc = 0.0
newLoc = 0.0
h = 600.0
w = (h / 1.325) / 1.325
orientation = py5.radians(90)
rotation = -45
showLines = False

def harriss(iteration):
    global w

    if iteration > 0:
        iteration -= 1
        fd(w, iteration)
        lt(py5.radians(90))
        w = w / 1.325
        harriss(iteration)

def setup():
    global loc
    global showLines

    py5.size(800, 750)
    surface = py5.get_surface()
    surface.set_title("Harriss Spiral Design")
    showLines = False
    loc = py5.Py5Vector(py5.width / 2 + 100, py5.height / 2 + 200)
    py5.no_loop()

def draw():
    global orientation
    global rotation
    global h
    global w

    # Base Spiral
    harriss(7)
    # Right Upper Spiral
    jumpTo(495, 230)
    h = 300.0
    w = (h / 1.325) / 1.325
    orientation = py5.radians(0)
    rotation = 45
    harriss(8)
    # Left Upper Spiral
    jumpTo(240, 238)
    h = 250
    w = (h / 1.325) / 1.325
    rotation = -45
    orientation = py5.radians(90)
    harriss(7)
    # Left Lower Spiral
    jumpTo(240, 425)
    h = 200
    w = (h / 1.325) / 1.325
    rotation = 225
    orientation = py5.radians(180)
    harriss(6)
    # Center Lower Spiral
    jumpTo(390, 425)
    h = 175
    w = (h / 1.325) / 1.325
    rotation = 135
    orientation = py5.radians(-90)
    harriss(5)

# ======= Turtle Graphics Functions =========

def fd(w, iteration):
    global loc
    global newLoc
    global end
    global rotation

    polar(w, orientation)
    end = loc + newLoc

    if showLines:
        py5.stroke(0)
        py5.stroke_weight(1)
        py5.line(loc.x, loc.y, end.x, end.y)

    py5.no_fill()
    py5.stroke(py5.random(255), 0, py5.random(255))
    py5.stroke_weight(12)
    if (iteration == 7) or (iteration == 3):
        py5.arc(
            loc.x + w / 2,
            loc.y - w / 2,
            w * 1.414,
            w * 1.414,
            py5.radians(rotation),
            py5.radians(rotation + 90),
        )
    if (iteration == 6) or (iteration == 2):
        py5.arc(
            loc.x - w / 2,
            loc.y - w / 2,
            w * 1.414,
            w * 1.414,
            py5.radians(rotation),
            py5.radians(rotation + 90),
        )
    if (iteration == 5) or (iteration == 1):
        py5.arc(
            loc.x - w / 2,
            loc.y + w / 2,
            w * 1.414,
            w * 1.414,
            py5.radians(rotation),
            py5.radians(rotation + 90),
        )
    if (iteration == 4) or (iteration == 0):
        py5.arc(
            loc.x + w / 2,
            loc.y + w / 2,
            w * 1.414,
            w * 1.414,
            py5.radians(rotation),
            py5.radians(rotation + 90),
        )

    loc = end
    rotation -= 90

def lt(theta):
    global orientation
    orientation += theta

def jumpTo(x, y):
    global loc
    loc = py5.Py5Vector(x, y)

def polar(r, theta):
    global newLoc
    newLoc = py5.Py5Vector(r * py5.cos(theta), r * py5.sin(-theta))

py5.run_sketch()

The call to no_loop() in setup() causes the draw() function to never execute. I think how module mode handles this is correct; imported mode execution should not execute the draw() function.

villares commented 1 week ago

Wait, let me check this... looks the same to me! could it be platform dependent?

Isn't this the expected behavior? I checked also on Processsing Python mode (I used to have a page about this).

image

image

image

My expectation was fulfilled that that no_loop() inside setup() would let draw() run once and then stop. And then, no_loop()' insidedraw()` fill prevent it from running again.

image

hx2A commented 1 week ago

@villares , no you are right. I took another look and I see it works correctly for imported mode and module mode. I was running this with Live Coding, and the problem is how Live Coding handles no_loop(). Calling that in setup() means it does not call draw() a single time. My initial conclusion was based on my incorrect assumption that the Live Coding feature handled no_loop() correctly, and was the same as what I'd get with the python interpreter.

So this is a bug, it is just a bug with Live Coding, not module mode / imported mode.

And this means I found a bug in the release minutes after doing the release. 😞

villares commented 1 week ago

Oh, I see, but don't let this spoil the joy of a great new release my friend!

hx2A commented 1 week ago

Oh, I see, but don't let this spoil the joy of a great new release my friend!

Ha, these kinds of things come with the territory. It will probably be an easy fix. :)

hx2A commented 1 day ago

Ha, these kinds of things come with the territory. It will probably be an easy fix. :)

Nope. Not easy either.