Converting Vectors to vertices in py5

Previously I reported on creating a Vec2D class for py5 and using it to create sketches. Now I’ve added a (work in progress) Vec3D class. I have found a neat way to use py5.vertices with both vector types, by implementing a tuple() method for the vector types. Here is Andres Colubri trefoil sketch converted to run on py5:-

import py5
import math
from surface.surface import *
PI = math.pi

W = 1024
H = 768

def settings():
    py5.size(1024, 768, py5.P3D)

def setup():
    global pg, trefoil  # PGraphics, PShape
    sketch_title('Trefoil')
    py5.texture_mode(py5.NORMAL)
    py5.no_stroke()

    # Creating offscreen surface for 3D rendering.
    pg = py5.create_graphics(32, 512, py5.P3D)
    with pg.begin_draw():
        pg.background(0, 0)
        pg.no_stroke()
        pg.fill(255, 0, 0, 200)
    # Saving trefoil surface into a PShape3D object
    trefoil = create_trefoil(350, 60, 15, pg)

def draw():
    py5.background(0)
    with pg.begin_draw():
        pg.ellipse(py5.random(pg.width), py5.random(pg.height), 4, 4)
    py5.ambient(250, 250, 250)
    py5.point_light(255, 255, 255, 0, 0, 200)

    with py5.push_matrix():
        py5.translate(W / 2, H / 2, -200)
        py5.rotate_x(py5.frame_count * PI / 500)
        py5.rotate_y(py5.frame_count * PI / 500)
        py5.shape(trefoil)

def sketch_title(name):
    py5.get_surface().set_title(name)

py5.run_sketch()

And the surface module:-

"""
Code to draw a trefoil knot surface, normals and texture coordinates.
Adapted from the parametric equations example by Philip Rideout:
http://iphone-3d-programming.labs.oreilly.com/ch03.html
"""
import py5
from vector.vec3d import Vec3D
import math
TWO_PI = math.tau

def create_trefoil(s, ny, nx, tex):
    """
    This function draws a trefoil knot surface as a triangle mesh derived
    from its parametric equation.
    """

    obj = py5.create_shape()
    with obj.begin_shape(py5.TRIANGLES):
        obj.texture(tex)

        for j in range(nx):
            u0 = float(j) / nx
            u1 = float(j + 1) / nx
            for i in range(ny):
                v0 = float(i) / ny
                v1 = float(i + 1) / ny

                p0 = eval_point(u0, v0)
                n0 = eval_normal(u0, v0)

                p1 = eval_point(u0, v1)
                n1 = eval_normal(u0, v1)

                p2 = eval_point(u1, v1)
                n2 = eval_normal(u1, v1)

                # Triangle p0-p1-p2
                obj.normal(n0.x, n0.y, n0.z)
                obj.vertex(s * p0.x, s * p0.y, s * p0.z, u0, v0)
                obj.normal(n1.x, n1.y, n1.z)
                obj.vertex(s * p1.x, s * p1.y, s * p1.z, u0, v1)
                obj.normal(n2.x, n2.y, n2.z)
                obj.vertex(s * p2.x, s * p2.y, s * p2.z, u1, v1)

                p1 = eval_point(u1, v0)
                n1 = eval_normal(u1, v0)

                # Triangle p0-p2-p1
                obj.normal(n0.x, n0.y, n0.z)
                obj.vertex(s * p0.x, s * p0.y, s * p0.z, u0, v0)
                obj.normal(n2.x, n2.y, n2.z)
                obj.vertex(s * p2.x, s * p2.y, s * p2.z, u1, v1)
                obj.normal(n1.x, n1.y, n1.z)
                obj.vertex(s * p1.x, s * p1.y, s * p1.z, u1, v0)
    return obj

def eval_normal(u, v):
    """
    Evaluates the surface normal corresponding to normalized parameters (u, v)
    """

    # Compute the tangents and their cross product.
    p = eval_point(u, v)
    tang_u = eval_point(u + 0.01, v)
    tang_v = eval_point(u, v + 0.01)
    tang_u -= p
    tang_v -= p

    norm_uv = tang_v.cross(tang_u)
    norm_uv.normalize()
    return norm_uv


def eval_point(u, v):
    """
    Evaluates the surface point corresponding to normalized parameters (u, v)
    """

    a, b, c, d = 0.5, 0.3, 0.5, 0.1
    s = TWO_PI * u
    t = (TWO_PI * (1 - v)) * 2

    r = a + b * math.cos(1.5 * t)
    x = r * math.cos(t)
    y = r * math.sin(t)
    z = c * math.sin(1.5 * t)

    dv = Vec3D()
    dv.x = (-1.5 * b * math.sin(1.5 * t) * math.cos(t) -
            (a + b * math.cos(1.5 * t)) * math.sin(t))
    dv.y = (-1.5 * b * math.sin(1.5 * t) * math.sin(t) +
            (a + b * math.cos(1.5 * t)) * math.cos(t))
    dv.z = 1.5 * c * math.cos(1.5 * t)

    q = dv
    q.normalize()
    qvn = Vec3D(q.y, -q.x, 0)
    qvn.normalize()
    ww = q.cross(qvn)

    pt = Vec3D()
    pt.x = x + d * (qvn.x * math.cos(s) + ww.x * math.sin(s))
    pt.y = y + d * (qvn.y * math.cos(s) + ww.y * math.sin(s))
    pt.z = z + d * ww.z * math.sin(s)
    return pt

For more worked examples and the Vec2D and Vec3D classes see this github repo

2 Likes