Clonal Colonies I will be included in the Center for Visual Music Salon Nov 18.
The full Clonal Colonies appears Nov 17-18 as part of Technarte Los Angeles, at USC School of Cinematic Arts.
Triptych Unfolding will appear in the Greenwich Sound/Image Colloquium, Nov 13.
Bret Battey blogging sundry ideas, favorable events, works in progress, and miscellaneous solutions in digital music and video-music research and creation. I see this as a subsidiary of my web site, BatHatMedia.com.
Monday, November 7, 2016
Thursday, October 6, 2016
CVM Salon 12 October 2016
I will be at the Center for Visual Music Salon in Oxford on Wed 12 October — to be part of a screening of new and classic visual-music pieces and to answer questions about my work. More details are available here.
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)!
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.
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)
Blender Script for creating animated motion-trail ribbon
Given an object for which a motion-path has been generated, will create a Beziér-spline ribbon that follows the motion of the object for a given tail length (designated in frames).
# --------- # Tested in Blender 2.75a and 2.76 # Makes a Bezier-spline motion trail for an object. # Set the length of the tail in frames via the tail_length variable below. # To run, select object, generate a motion path for the object (Editor>Motion Paths) across the total desired frame range, and Run Script. # Warning: this can take a long time to run with long motion paths and long tail sizes # Once the spline has been generated, extrude and/or add bevel and taper objects to the Geometry settings of the spline. # Bret Battey / BatHatMedia.com September 2015 # --------- import bpy from bpy.props import * from mathutils import * from math import * import time # ----- FUNCTION DEFINITIONS ----- # Time utils thanks to http://blenderscripting.blogspot.co.uk/search/label/time def get_last_time(): if len(time_list) < 2: return "ERROR: must have two time entries to calculate the difference" return time_list[-1] - time_list[-2] def mark_time(): time_list.append(time.time()) time_list = [] ##------------------------------------------------------------ #### Curve creation functions # sets bezierhandles to auto def setBezierHandles(obj, mode = 'AUTOMATIC'): scene = bpy.context.scene if obj.type != 'CURVE': return scene.objects.active = obj bpy.ops.object.mode_set(mode='EDIT', toggle=True) bpy.ops.curve.select_all(action='SELECT') bpy.ops.curve.handle_type_set(type=mode) bpy.ops.object.mode_set(mode='OBJECT', toggle=True) # create new CurveObject def createCurve(verts, name): # create curve scene = bpy.context.scene newCurve = bpy.data.curves.new(name, type = 'CURVE') # curvedatablock newSpline = newCurve.splines.new(type = 'BEZIER') # spline # The new spline already has one point. Add the remaining needed. newSpline.bezier_points.add(verts-1) # set curveOptions newCurve.dimensions = '3D' # create object with newCurve new_obj = bpy.data.objects.new(name, newCurve) # object scene.objects.link(new_obj) # place in active scene new_obj.select = True # set as selected scene.objects.active = new_obj # set as active # set bezierhandles setBezierHandles(new_obj) return new_obj #based on animation_rotobezier.py def keyframeBezier(Obj, frame): Data = Obj.data for Spline in Data.splines: for CV in Spline.bezier_points: CV.keyframe_insert(data_path='co', frame = frame) CV.keyframe_insert(data_path='handle_left', frame = frame) CV.keyframe_insert(data_path='handle_right', frame = frame) # ------------- SCRIPT START tail_length = 60 # length of the spline in frames object = bpy.context.object path = object.motion_path frame_start = path.frame_start frame_end = path.frame_end path_length = path.length # --- create the Bezier curve trail = createCurve(tail_length,object.name+" motion_trail") mark_time() # start measuring elapsed time # --- key frame the curve at every frame in the motion path for i in range(tail_length-1,path_length): # motion path index print("Point "+str(i)) for j in range(tail_length): # curve point index trail.data.splines[0].bezier_points[j].co = path.points[(i-tail_length)+j].co # set the coordinates of the motion tail to the corresponding point on the motion path # keyframe it keyframeBezier(trail, frame_start+i) mark_time() print('The motion trail keyframing took {: 5g} seconds'.format(get_last_time()))
Blender Script for object-motion measurements
In Blender, given an object that has a motion path, will provide animated empties which indicate the object's velocity, acceleration, magnitude, and accumulated distance moved.
# --------- # Tested on Blender 2.75a and 2.76 # For a given object, makes an empty, which moves to indicate the velocity (displacement between frames) for each axis of the original object # and another empty providing magnitude of the movement # and another empty providing accumulative displacement for each axis, # and yet another for acceleration. # To run, select object, generate a motion path for the object (Editor>Motion Paths) across the total desired frame range, and Run Script. # Bret Battey / BatHatMedia.com Dec 2015 # --------- import bpy from bpy.props import * from mathutils import * from math import * import time # ----- FUNCTION DEFINITIONS ----- # Time utils thanks to http://blenderscripting.blogspot.co.uk/search/label/time def get_last_time(): if len(time_list) < 2: return "ERROR: must have two time entries to calculate the difference" return time_list[-1] - time_list[-2] def mark_time(): time_list.append(time.time()) time_list = [] # ------------- SCRIPT START object = bpy.context.object path = object.motion_path frame_start = path.frame_start frame_end = path.frame_end path_length = path.length # velocity empty bpy.ops.object.empty_add(type='PLAIN_AXES', radius=1, view_align=False, location=(0,0,0), layers=object.layers) velempty = bpy.context.object velempty.name = object.name + '_velocity' # vector magnitude bpy.ops.object.empty_add(type='PLAIN_AXES', radius=1, view_align=False, location=(0,0,0), layers=object.layers) magempty = bpy.context.object magempty.name = object.name + '_magnitude' # accumulative distance traveled bpy.ops.object.empty_add(type='PLAIN_AXES', radius=1, view_align=False, location=(0,0,0), layers=object.layers) accumempty = bpy.context.object accumempty.name = object.name + '_accum_distance' # acceleration bpy.ops.object.empty_add(type='PLAIN_AXES', radius=1, view_align=False, location=(0,0,0), layers=object.layers) accelempty = bpy.context.object accelempty.name = object.name + '_acceleration' mark_time() # start measuring elapsed time for i in range(0,path_length-2): # motion path index nextPointCo = path.points[i+1].co # next time point thisPointCo = path.points[i].co # this time point dif = nextPointCo-thisPointCo # x,y,z distances mag = sqrt(dif[0]*dif[0]+dif[1]*dif[1]+dif[2]*dif[2]) # vector magnitude via pythagorean theorem velempty.location = dif # set vel empty's location to the velocity vector magempty.location[2] = mag # set z position of mag empty's location to the magnitude frame = frame_start+i # get absolute frame number velempty.keyframe_insert(data_path='location',frame=frame,index=-1) # set keyframe for velocity. index -1 = set all three axes magempty.keyframe_insert(data_path='location',frame=frame,index=2) # set keyframe for velocity. index -1 = set all three axes if i == 0: accumempty.keyframe_insert(data_path='location',frame=frame,index=-1) # set keyframe. index -1 = set all three axes accumdif = accumempty.location # start accumulated distance with a convenient 0,0,0 vector else: accumdif[0] += abs(dif[0]) # add absolute value of velocity to the accumulative distance accumdif[1] += abs(dif[1]) accumdif[2] += abs(dif[2]) accumempty.location = accumdif # move empty to this point accumempty.keyframe_insert(data_path='location',frame=frame,index=-1) # set keyframe. index -1 = set all three axes accelempty.location = dif-lastdif # acceleration = dif between this frame's velocity and the previous frame's accelempty.keyframe_insert(data_path='location',frame=frame-1,index=-1) # set keyframe ONE FRAME BACK. index -1 = set all three axes lastdif = dif mark_time() print('The keyframing took {: 5g} seconds'.format(get_last_time()))
Subscribe to:
Posts (Atom)