Monday, September 12, 2016

New Version of Nodewebba (beta 0.05) Now Available

The latest version of Nodewebba (September 2016) includes Hours/Minutes/Seconds display, and
MIDI control presets are separated from the main/node presets. Startup/reseed logic has been refined to provide consistent startup behaviors. Plus there is now an application icon (gasp)!

Mercurius screening at Fundacio Telefonica, Peru, 21 Sep 2016

Mercurius will be screening as part of "The Best of Punto y Raya" (Proyección especial: Lo mejor de punto y raya), 21 September 2016, 19:00 - 20:00, at Espacio Fundación Telefónica
Av Arequipa 1155 Santa Beatriz, 28004 Lima, Peru. This screening is part of the ESPACIO 360° festival.

Clonal Colonies 2nd Movement to screen at the Punto y Raya Festival 2016 at ZKM

The second movement of Clonal Colonies will be screened as part of the 2016 Punto y Raya Festival, which will run October 20th-23rd, 2016 at ZKM (Zentrum für Kunst und Medientechnologie) in Karlsruhe, Germany. More information is available here.

Wednesday, August 31, 2016

Nodewebba Demo at ICMC Sep 15, Utrecht

I will be presenting a poster/demo of my Nodewebba software at the 2016 International Computer Music Conference (ICMC) in Utrecht, on Sep 15, 12:00-14:30.

Sunday, June 19, 2016

Clonal Colonies at NYCEMF

Clonal Colonies screens Sunday, 19 June, 2016 at the New York City Electronic Music Festival, part of a show curated by the Fresh Minds Festival. (OK, so that's today – but I just received notice today!) This joins lots of other De Montfort University activity in the festival: the Acousmatic Music from MTI show in Concert 20 and Francesc Marti's Speech 2 in Concert 21.

Friday, May 20, 2016

Luna Series Screening in Tallinn June 1

The "Luna Series" (Mercurius, Lacus Temporis, and Sinus Aestum) will be screened with audiovisual works by Domenico de June 1, 2016 in Tallinn, Estonia in the Opera Studio of the Estonian Academy of Music. More information is available at the event's Facebook Page.

Tuesday, February 16, 2016

"Replicator Grid" Blender script

This Blender script creates a 2D grid of replicated objects with a descending line of parenting control, running left to right, then down to the next row. The following video demonstrates:

 
 
# ---------
# Tested in Blender 2.75a and 2.76
# Makes a 'replicator grid' -- multiple copies of a given object, parented in a tranformation-control chain.
# Provides a single empty that controls the relative size, displacement and rotation of each successive object in relation to
# its parent.
#
# Usage: 
# Change the number of rows and columns below.
# Select target object to be replicated.
# Run the script.
# If you did this with an object named 'cube', for example,
# You will end up with a 'cube_replicator' empty, below which all of the duplicated objects
# have been parented.
# And you will have a new cube_replicator_control empty, which can be moved, scaled and rotated
# to control the overall replicator grid.

# Bret Battey / BatHatMedia.com September 2015
# ---------
# 

import bpy
from bpy.props import *

# specify number of rows and columns here!
rows = 5
columns = 5

# The following function is adapted from Nick Keeline "Cloud Generator" 
# addNewObject in object_cloud_gen.py

def duplicateObject(scene, name, copyobj):

    # Create new mesh
    mesh = bpy.data.meshes.new(name)

    # Create a new object.
    ob_new = bpy.data.objects.new(name, mesh)
    tempme = copyobj.data
    ob_new.data = tempme.copy()
    ob_new.scale = copyobj.scale
    ob_new.location = copyobj.location

    # Link new object to the given scene and select it.
    scene.objects.link(ob_new)
    ob_new.select = True

    return ob_new            


# Build the parenting chain of replications tied to empties and control 
# constraints. If we built a parented chain of objects, we wouldn't be able to 
# transform each object independently. So instead we build a parented chain of
# empties, then parent each duplicated object to one of the empties.

def gridOfReps(rows, cols, source_obj, ctrl, scene):
    for row in range(rows):
        for col in range(cols):
            #Create a locator empty
            bpy.ops.object.add(type="EMPTY", location=(0,0,0))
            rep_loc = bpy.context.object
            # The first empty becomes the root of the whole beast, and the 
            # source object gets parented to it
            if((row == 0) and (col == 0)):
                rep_loc.name = source_obj.name+"_replicator"
                parent_obj = rep_loc
                prev_row_locator = rep_loc
                source_parent = source_obj.parent
                # If the source object has a parent, locate and parent 
                # the new replicator base empty to that.
                if((source_parent) and (source_parent.type == 'EMPTY')):
                    rep_loc.location = source_parent.location
                    rep_loc.parent = source_parent
                # Otherwise just position the base empty to the source obj,
                # zero the source obj location, and parent to the base
                else:  
                    rep_loc.location = source_obj.location
                    source_obj.location = (0,0,0)
                    source_obj.parent = rep_loc   
            else: 
                rep_loc.name = "rep_loc_r"+str(row)+"c_"+str(col)  # name can't 
                    #be specified via the .add method
                rep_loc.parent = parent_obj # parent to the previous locator
                # First locator in each row other than 0 gets special treatment
                if((row > 0) and (col == 0)):
                    # First location constraint locks to the prev. row locator
                    constraint = rep_loc.constraints.new("COPY_LOCATION")  
                    constraint.target = prev_row_locator
                    constraint.use_x = True
                    constraint.use_y = True
                    constraint.use_z = True       
                    constraint.owner_space = "WORLD"
                    constraint.target_space = "WORLD"
                    constraint.influence = 1.0
                    # Second location constraint implements the z offset 
                    # from the controller      
                    constraint = rep_loc.constraints.new("COPY_LOCATION")  
                    constraint.target = ctrl
                    constraint.use_x = False 
                    constraint.use_y = False
                    constraint.use_z = True       
                    constraint.owner_space = "LOCAL"
                    constraint.target_space = "WORLD"
                    constraint.influence = 1.0
                    constraint.use_offset = True   # use the controller's 
                    # location as an offset rather than an absolute location
                    # And ready this locator to be basis of next row position
                    prev_row_locator = rep_loc
                else:
                    # For other items in a row, offset location x on basis of 
                    # controller
                    constraint = rep_loc.constraints.new("COPY_LOCATION")  
                    constraint.target = ctrl
                    constraint.use_x = True  # for offsetting a row, only x is 
                        # drawn from the controller 
                    constraint.use_y = False
                    constraint.use_z = False       
                    constraint.owner_space = "LOCAL"
                    constraint.target_space = "WORLD"
                    constraint.influence = 1.0
                    constraint.use_offset = True   # use the controller's 
                        #location as an offset rather than an absolute location
                # For all locators except the root, apply these influences from 
                    #the controller:
                # Copy_Rotation Constraint 
                constraint = rep_loc.constraints.new("COPY_ROTATION")  
                constraint.target = ctrl
                constraint.owner_space = "LOCAL"
                constraint.target_space = "WORLD"
                constraint.influence = 1.0
                # Copy_Scale Constraint. This will also cascade down the chain 
                    # due to the combined effect with the parenting 
                constraint = rep_loc.constraints.new("COPY_SCALE")  
                constraint.target = ctrl
                constraint.owner_space = "LOCAL"
                constraint.target_space = "WORLD"
                constraint.influence = 1.0
                # In all cases (except root), duplicate source object and parent
                    # to the replicator empty.
                # This strategy allows later transformations of the visible 
                    #object without influencing the children
                new_obj = duplicateObject(scene, "rep", source_obj)
                new_obj.parent = rep_loc    
                # Advance parent assignment
                parent_obj = rep_loc    
    return parent_obj

# get the source object

source_obj = bpy.context.object
source_loc = source_obj.location
source_scene = bpy.context.scene

# create and name the controller empty

bpy.ops.object.add(type="EMPTY", location=(0,0,0))
ctrl = bpy.context.object  
ctrl.name = source_obj.name+"_replicator_control"  # name can't be specified 
        #via the .add method

#replicate!
last_locator = gridOfReps(rows, columns, source_obj, ctrl, source_scene)