# encoding: utf-8
# frozen_string_literal: true
# Adapted to use Propane Vec2D and Vec3D classes by Martin Prout

# Use Face Struct triangle 'mesh'.
Face = Struct.new(:a, :b, :c) # triangle mesh face
require 'propane'

# Monkey patch the Vec3D class to support rotations
Vec3D.class_eval do # re-open the Vec3D class to add the rotate methods
  def rotate_y!(theta)
    co = Math.cos(theta)
    si = Math.sin(theta)
    xx = co * x - si * z
    self.z = si * x + co * z
    self.x = xx
    self
  end

  def rotate_x!(theta)
    co = Math.cos(theta)
    si = Math.sin(theta)
    zz = co * z - si * y
    self.y = si * z + co * y
    self.z = zz
    self
  end
end

# After a toxiclibs "MeshDoodle" sketch by Karsten Schmidt
class Doodle < Propane::App
  attr_reader :prev, :p, :q, :rotation, :faces, :pos, :weight

  def setup
    size(600, 600, P3D)
    @weight = 0
    @prev = Vec3D.new
    @p = Vec3D.new
    @q = Vec3D.new
    @rotation = Vec2D.new
    @faces = []
  end

  def draw
    background(0)
    lights
    translate(width / 2, height / 2, 0)
    rotate_x(rotation.x)
    rotate_y(rotation.y)
    no_stroke
    begin_shape(TRIANGLES)
    # iterate over all faces/triangles of the 'mesh'
    faces.each do |f|
      # create vertices for each corner point
      f.a.to_vertex(renderer)
      f.b.to_vertex(renderer)
      f.c.to_vertex(renderer)
    end
    end_shape
    # update rotation
    @rotation += Vec2D.new(0.014, 0.0237)
  end

  def mouse_moved
    # get 3D rotated mouse position
    @pos = Vec3D.new(mouse_x - width / 2, mouse_y - height / 2, 0)
    pos.rotate_x!(rotation.x)
    pos.rotate_y!(rotation.y)
    # use distance to previous point as target stroke weight
    @weight += (sqrt(pos.dist(prev)) * 2 - weight) * 0.1
    # define offset points for the triangle strip
    a = pos + Vec3D.new(0, 0, weight)
    b = pos + Vec3D.new(0, 0, -weight)
    # add 2 faces to the mesh
    faces << Face.new(p, b, q)
    faces << Face.new(p, a, b)
    # store current points for next iteration
    @prev = pos
    @p = a
    @q = b
  end

  # An example of AppRenderer usage for Vec3D => vertex conversion
  def renderer
    @renderer ||= Propane::Render::AppRender.new(self)
  end
end

Doodle.new title: 'Ribbon Doodle'