# Matthew Plummer Fernandez hommage # Draw interesting parametric curves and instantiate meshes on its path import sys, code, random, os, math, bpy, canvas, mathutils

sys.path.append(os.path.dirname(os.path.dirname(__file__))) import helpers

class Fernandez(canvas.Canvas):

MESH_OCCURENCE = 1 # 1 mesh every X point of the curve

FUNCTIONS = [
  # decorated knot (can expand to 20 units)
  [
    lambda t: math.cos( 2 * t * math.pi * 2 ) * ( 1 + 0.45 * math.cos( 3 * t * math.pi * 2 ) + 0.4 * math.cos( 9 * t * math.pi * 2 ) ),
    lambda t: math.sin( 2 * t * math.pi * 2 ) * ( 1 + 0.45 * math.cos( 3 * t * math.pi * 2 ) + 0.4 * math.cos( 9 * t * math.pi * 2 ) ),
    lambda t: 0.2 * math.sin( 9 * t * math.pi * 2 )
  ],
  # another knot like structure
  [
    lambda t: 10 * (math.cos(t) + math.cos(3 * t)) + math.cos(2 * t) + math.cos(4 * t),
    lambda t: 6 * math.sin(t) + 10 * math.sin(3 * t),
    lambda t: 4 * math.sin(3 * t) * math.sin(5 * t / 2) + 4 * math.sin(4 * t) - 2 * math.sin(6 * t)
  ],
  # Some weird sphere like structure (contained within a 1x1x1 cube)
  [
    lambda t: math.sin(t) * math.cos(20*t),
    lambda t: math.sin(t) * math.sin(20*t),
    lambda t: math.cos(t)
  ],
  # Some weird funnel like structure (contained within a 1x1x1 cube)
  [
    lambda t: math.sin(t) * math.cos(20*t),
    lambda t: math.sin(t) * math.sin(20*t),
    lambda t: math.sin(t)
  ]
]

def render(self):
  base_particle = helpers.infer_primitive(random.choice(self.PRIMITIVES), location = (100, 100, 100), radius=1)
  art = self.matthew_curve(self.SUBJECT, 20)
  self.spawn_particles_system(art, base_particle)

  for f in range(self.NUMBER_OF_FRAMES):
    bpy.context.scene.frame_set(f)
    art.particle_systems["ParticleSystem"].seed += 1
    settings = bpy.data.particles[-1]
    settings.particle_size += 0.005
    art.scale += mathutils.Vector((0.02,0.02,0.02))
    helpers.add_frame([art], ['particle_systems["ParticleSystem"].seed', 'scale'])
    helpers.add_frame([settings], ['particle_size'])

def rand_curve(self):
  return random.choice(self.FUNCTIONS)

def matthew_curve(self, obj, time, scale = 0.2):
  fx, fy, fz = self.rand_curve()
  verts =  [(fx(t), fy(t), fz(t)) for t in helpers.pitched_array(0, time, 0.2)]
  bpy.context.scene.objects.active = obj
  bpy.ops.object.select_all(action='DESELECT')
  for idx, coord in enumerate(verts[0::self.MESH_OCCURENCE]):
    new_obj = helpers.duplicate_object(obj)
    new_obj.select = True
    new_obj.location = coord
    new_obj.scale = (0.02,0.02,0.02) if idx % 2 == 0 else (0.05, 0.05, 0.05)
    new_obj.rotation_euler.z += idx * (2 * math.pi) / len(verts)
    bpy.context.scene.objects.active = new_obj
  bpy.ops.object.join()
  res = bpy.context.object
  res.name = 'fernandez'
  helpers.resize(res)
  helpers.center(res)
  helpers.decimate(res)
  helpers.assign_material(res, helpers.random_material(self.MATERIALS_NAMES))
  support = helpers.create_mesh('fernandez_support', verts, [], (0,0,0),  [[v, v+1] for v in range(0, (len(verts) - 1))])
  helpers.resize(support)
  helpers.center(support)
  helpers.extrude(support)
  helpers.assign_material(support, helpers.random_material(self.MATERIALS_NAMES))
  return res

def spawn_particles_system(self, base, obj):
  base.modifiers.new("Particles", type='PARTICLE_SYSTEM')
  settings = bpy.data.particles[-1]
  settings.emit_from = 'VERT'
  settings.physics_type = 'NO'
  settings.count = 2000 # default 1000
  settings.particle_size = 0.01
  settings.render_type = 'OBJECT'
  settings.dupli_object = obj
  settings.show_unborn = True
  settings.use_dead = True
  settings.size_random = 0.5
  bpy.ops.object.duplicates_make_real()