I have a very large number of curves (OpenStreetMap data imported as curves) on which I wish to array objects (streetlights). Since there are thousands of curve objects, duplicating my streetlight object and then manually adding an array and curve modifier and point them to the right curve is a slow if not impossible process when there is this many curve objects.

The best solution I can think of is a script, however I am not familiar with the process. I've only gone as far as making the script add an array modifier.

The imported curves are all numbered as Road0, Road1, Road2, Road3, etc

Here is my script so far

import bpy

bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(-0, 0, 0), "orient_type":'GLOBAL', "orient_matrix":((1, 0, 0), (0, 1, 0), (0, 0, 1)), "orient_matrix_type":'GLOBAL', "constraint_axis":(False, False, False), "mirror":True, "use_proportional_edit":False, "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "use_proportional_connected":False, "use_proportional_projected":False, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "gpencil_strokes":False, "cursor_transform":False, "texture_space":False, "remove_on_cancel":False, "release_confirm":False, "use_accurate":False})
bpy.context.object.modifiers["Array"].fit_type = 'FIT_CURVE'
bpy.context.object.modifiers["Array"].relative_offset_displace[0] = 50
bpy.context.object.modifiers["Array"].curve = bpy.data.objects["RoadTertartiary0"]

I basically need a way to make that last 0 increment every time I run the script

Sample Blend file:

Any help is appreciated.

  • $\begingroup$ Could you add some information about the curves and the object to be dupli? Eventually, a simple blend file with sample of them that you could upload here using blend-exchange.giantcowfilms.com $\endgroup$ – lemon Aug 15 at 11:05
  • $\begingroup$ There's nothing special I'd say about the objects and curves, I have included the blend file now. $\endgroup$ – John 514 Aug 15 at 11:44

This script can do the job I think:

Basically, it will loop over the collection where the roads are,

then copy the base lamp, put it at the current road location and create the two needed modifiers (array and curve).

the copies are placed in a separate collection.

The script is commented below:

import bpy
import bmesh

# get an existing collection or create it if it does not exist
# if created, the collection is added to the scene main collection
def get_or_create_collection( scene, collection_name ):
    collection = bpy.data.collections.get( collection_name )
    if collection is None:
        collection = bpy.data.collections.new( collection_name )
        scene.collection.children.link( collection )
    return collection

# duplicate an object and link it to a collection
# copy_data allow to know if the objects will share their data or not (are dupli or not)
def duplicate( obj, collection, copy_data ):
    new_obj = obj.copy()
    if copy_data:
        new_obj.data = obj.data.copy()
    collection.objects.link( new_obj )
    return new_obj

# get the scene
scene = bpy.context.scene

# get the base lamp
base_lamp = bpy.data.objects["Lamp_LowPoly"]

# names of the collections
road_collection_name = "map_6.osm_roads_tertiary"
lamps_collection_name = "created_lamps"

# get the source collection
road_collection = bpy.data.collections.get( road_collection_name )

if road_collection is None or base_lamp is None:
    print( "will not work" )
    # get the destination collection
    lamps_collection = get_or_create_collection( scene, lamps_collection_name )

    for road in [r for r in road_collection.objects if r.type == 'CURVE']:
        # Duplicate the base lamp (True => not linked)
        lamp = duplicate( base_lamp, lamps_collection, True )
        # Set the lamp at the road location so that curve can work
        lamp.location = road.location
        # Add the array modifier
        array_modifier = lamp.modifiers.new( "array", 'ARRAY' )
        array_modifier.fit_type = 'FIT_CURVE'
        array_modifier.relative_offset_displace[0] = 50
        array_modifier.curve = road
        # Add the curve modifier
        curve_modifier = lamp.modifiers.new( "curve", 'CURVE' )
        curve_modifier.object = road

    print( 'done' )

  • $\begingroup$ That is impressive. Thank you so much. I will study this and adapt it if I ever have to do this again. Again, thank you. $\endgroup$ – John 514 Aug 15 at 12:55
  • $\begingroup$ FYI I've set duplicate( base_lamp, lamps_collection, True ) with 'True' above because you did "linked":False in your script. But I think it is not necessary in your case. $\endgroup$ – lemon Aug 15 at 12:59
  • $\begingroup$ That is fine, I'm merging them into one object in the end anyway :) $\endgroup$ – John 514 Aug 15 at 16:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.