<reponame>MarcSkovMadsen/panel-sketch<gh_stars>1-10
from pyp5js import *

add_library("p5.dom.js")


MY_POINTS = [
    (100, 50),
    (300, 100),
    (200, 300),
    (100, 300),
]
FRAME_IDX = 0
POINT_SIZE = 10
CNV = None


def setup():
    global CNV
    CNV = createCanvas(400, 400)

    BUTTON_PREV = createButton("Previous frame")
    BUTTON_PREV.position(CNV.position().x, CNV.height + CNV.position().y)
    BUTTON_PREV.mousePressed(prev_frame)

    BUTTON_NEXT = createButton("Next frame")
    BUTTON_NEXT.position(CNV.position().x + BUTTON_PREV.size().width, BUTTON_PREV.position().y)
    BUTTON_NEXT.mousePressed(next_frame)

    background(190)
    draw_labels(MY_POINTS)


def draw():
    background(190)
    draw_closed_curve_vertex(MY_POINTS, FRAME_IDX)
    draw_labels(MY_POINTS)


def mouseClicked():
    global FRAME_IDX
    global MY_POINTS
    if is_point_in_canvas(mouseX, mouseY):
        i = get_point_index(mouseX, mouseY)
        if i != None:
            MY_POINTS.pop(i)
            if FRAME_IDX >= len(MY_POINTS):
                # cap i if it exceeds maximum length now.
                FRAME_IDX = len(MY_POINTS) - 1
        else:
            MY_POINTS.append((mouseX, mouseY))


def get_point_index(x, y):
    for idx, (p_x, p_y) in enumerate(MY_POINTS):
        if (p_x - POINT_SIZE < x and x < p_x + POINT_SIZE) and (
            p_y - POINT_SIZE < y and y < p_y + POINT_SIZE
        ):
            return idx


def is_point_in_canvas(x, y):
    if (x < 0 or x > CNV.width) or (y < 0 or y > CNV.height):
        return False
    return True


def next_frame():
    global FRAME_IDX
    if FRAME_IDX < len(MY_POINTS) - 1:
        FRAME_IDX += 1


def prev_frame():
    global FRAME_IDX
    if FRAME_IDX > 0:
        FRAME_IDX -= 1


def draw_closed_curve_vertex(points, max_idx):
    if len(points) < 2:
        return
    used_points = []
    beginShape()

    # start by using the last point as the initial control point
    idx = len(points) - 1
    curveVertex(*points[idx])
    used_points.append(idx)

    # add each point to the curve
    for idx, p in enumerate(points):
        if idx > max_idx:
            break
        curveVertex(*p)
        used_points.append(idx)

    # to close the curve, we need to create the last curve.
    # for that, we must go to the first point
    idx = 0
    curveVertex(*points[idx])
    used_points.append(idx)

    # and use the next point as a control point.
    idx = 1
    curveVertex(*points[idx])
    used_points.append(idx)
    endShape()

    textSize(10)
    noStroke()
    text(
        "Points used to draw this curve (first and last are control points only)",
        5,
        CNV.height - 30,
    )

    textSize(20)
    text(", ".join(used_points), 10, CNV.height - 10)
    stroke(0)

    for i in range(len(used_points) - 1):
        draw_dotted_line(points[used_points[i]], points[used_points[i + 1]])


def draw_labels(points):
    strokeWeight(POINT_SIZE)
    for idx, p in enumerate(points):
        ts = 32
        textSize(ts)
        textY = p[1] - ts / 2

        if p[1] > CNV.height / 2:
            textY = p[1] + ts

        noStroke()
        text(idx, p[0], textY)
        stroke(0)
        point(*p)

    strokeWeight(1)


def draw_dotted_line(p1, p2):
    stroke(100)
    strokeWeight(3)
    for i in range(11):
        x = lerp(p1[0], p2[0], i / 10)
        y = lerp(p1[1], p2[1], i / 10)
        point(x, y)

    stroke(0)
    strokeWeight(1)

from pyp5js import *

add_library("p5.dom.js")


MY_POINTS = [
    (100, 50),
    (300, 100),
    (200, 300),
    (100, 300),
]
FRAME_IDX = 0
POINT_SIZE = 10
CNV = None


def setup():
    global CNV
    CNV = createCanvas(400, 400)

    BUTTON_PREV = createButton("Previous frame")
    BUTTON_PREV.position(CNV.position().x, CNV.height + CNV.position().y)
    BUTTON_PREV.mousePressed(prev_frame)

    BUTTON_NEXT = createButton("Next frame")
    BUTTON_NEXT.position(CNV.position().x + BUTTON_PREV.size().width, BUTTON_PREV.position().y)
    BUTTON_NEXT.mousePressed(next_frame)

    background(190)
    draw_labels(MY_POINTS)


def draw():
    background(190)
    draw_closed_curve_vertex(MY_POINTS, FRAME_IDX)
    draw_labels(MY_POINTS)


def mouseClicked():
    global FRAME_IDX
    global MY_POINTS
    if is_point_in_canvas(mouseX, mouseY):
        i = get_point_index(mouseX, mouseY)
        if i != None:
            MY_POINTS.pop(i)
            if FRAME_IDX >= len(MY_POINTS):
                # cap i if it exceeds maximum length now.
                FRAME_IDX = len(MY_POINTS) - 1
        else:
            MY_POINTS.append((mouseX, mouseY))


def get_point_index(x, y):
    for idx, (p_x, p_y) in enumerate(MY_POINTS):
        if (p_x - POINT_SIZE < x and x < p_x + POINT_SIZE) and (
            p_y - POINT_SIZE < y and y < p_y + POINT_SIZE
        ):
            return idx


def is_point_in_canvas(x, y):
    if (x < 0 or x > CNV.width) or (y < 0 or y > CNV.height):
        return False
    return True


def next_frame():
    global FRAME_IDX
    if FRAME_IDX < len(MY_POINTS) - 1:
        FRAME_IDX += 1


def prev_frame():
    global FRAME_IDX
    if FRAME_IDX > 0:
        FRAME_IDX -= 1


def draw_closed_curve_vertex(points, max_idx):
    if len(points) < 2:
        return
    used_points = []
    beginShape()

    # start by using the last point as the initial control point
    idx = len(points) - 1
    curveVertex(*points[idx])
    used_points.append(idx)

    # add each point to the curve
    for idx, p in enumerate(points):
        if idx > max_idx:
            break
        curveVertex(*p)
        used_points.append(idx)

    # to close the curve, we need to create the last curve.
    # for that, we must go to the first point
    idx = 0
    curveVertex(*points[idx])
    used_points.append(idx)

    # and use the next point as a control point.
    idx = 1
    curveVertex(*points[idx])
    used_points.append(idx)
    endShape()

    textSize(10)
    noStroke()
    text(
        "Points used to draw this curve (first and last are control points only)",
        5,
        CNV.height - 30,
    )

    textSize(20)
    text(", ".join(used_points), 10, CNV.height - 10)
    stroke(0)

    for i in range(len(used_points) - 1):
        draw_dotted_line(points[used_points[i]], points[used_points[i + 1]])


def draw_labels(points):
    strokeWeight(POINT_SIZE)
    for idx, p in enumerate(points):
        ts = 32
        textSize(ts)
        textY = p[1] - ts / 2

        if p[1] > CNV.height / 2:
            textY = p[1] + ts

        noStroke()
        text(idx, p[0], textY)
        stroke(0)
        point(*p)

    strokeWeight(1)


def draw_dotted_line(p1, p2):
    stroke(100)
    strokeWeight(3)
    for i in range(11):
        x = lerp(p1[0], p2[0], i / 10)
        y = lerp(p1[1], p2[1], i / 10)
        point(x, y)

    stroke(0)
    strokeWeight(1)
# cap i if it exceeds maximum length now. # start by using the last point as the initial control point # add each point to the curve # to close the curve, we need to create the last curve. # for that, we must go to the first point # and use the next point as a control point.