Low-level location, rotation and scale for sphere, cylinders I am interested in constructing figures of atoms using Blender from an external XYZ file containing atomic positions and species.

I have succesfully read the XYZ and found all atoms and their connections using MatLAB and exported everything as a JSON file, which the Blender Python easily reads. However, as soon as the structures become larger the processing time increases (since I presently use bpy.ops that redraws the scene for each added atom).

I want to change my approach to use a more low-level form of python.

JSON file (here, Benzene)

{
    "camera": {
        "position": [0.0000, 0.0000, 32.4760],
        "rotation": [0.0000, 0.0000, 0.0000]
    },
    "atoms": [
        { "element": "C", "location": [ -0.7150, -1.2384, 0.0000 ] },
        { "element": "C", "location": [ 0.7150, -1.2384, 0.0000 ] },
        { "element": "C", "location": [ -1.4300, 0.0000, 0.0000 ] },
        { "element": "C", "location": [ -0.7150, 1.2384, 0.0000 ] },
        { "element": "C", "location": [ 0.7150, 1.2384, 0.0000 ] },
        { "element": "C", "location": [ 1.4300, 0.0000, 0.0000 ] },
        { "element": "H", "location": [ -1.2150, -2.1044, 0.0000 ] },
        { "element": "H", "location": [ 1.2150, -2.1044, 0.0000 ] },
        { "element": "H", "location": [ -2.4300, 0.0000, 0.0000 ] },
        { "element": "H", "location": [ -1.2150, 2.1044, 0.0000 ] },
        { "element": "H", "location": [ 1.2150, 2.1044, 0.0000 ] },
        { "element": "H", "location": [ 2.4300, 0.0000, 0.0000 ] }
    ],
    "bonds": [
        { "atoms": [ 0, 1 ], "order": 1 },
        { "atoms": [ 0, 2 ], "order": 1 },
        { "atoms": [ 0, 6 ], "order": 1 },
        { "atoms": [ 1, 5 ], "order": 1 },
        { "atoms": [ 1, 7 ], "order": 1 },
        { "atoms": [ 2, 3 ], "order": 1 },
        { "atoms": [ 2, 8 ], "order": 1 },
        { "atoms": [ 3, 4 ], "order": 1 },
        { "atoms": [ 3, 9 ], "order": 1 },
        { "atoms": [ 4, 5 ], "order": 1 },
        { "atoms": [ 4, 10 ], "order": 1 },
        { "atoms": [ 5, 11 ], "order": 1 }
    ]
}


I read in the JSON and contain all elements in molecule["atoms"]. I then loop over every atom and create a copy of a sphere

    # Draw atoms
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.mesh.primitive_uv_sphere_add()
bpy.ops.object.shade_smooth()
sphere = bpy.context.object
for atom in molecule["atoms"]:
    ob = sphere.copy()
    ob.select = False
    ob.location = atom["location"]
    ob.data = sphere.data.copy()
    bpy.context.scene.objects.link(ob)
bpy.ops.object.delete()
bpy.context.scene.update()


But the sphere size (and rotation for when I get to the bonds) confuse me. After having searched for days and days, I still haven't found a simple command to scale, translate and rotate without using bpy.ops.

Am I missing something?
If the Atomic blender addons (which include an XYZ importer) don't help, I would suggest using dupliverts instead of creating multiple objects when creating a large number of objects. My earlier tests have shown that using dupliverts can be quicker than creating individual objects.

This example that works with your sample data creates a new mesh that only has vertices that are placed at the location of each atom, this is then set as the parent of a sphere and dupliverts is enabled, this places a copy of the sphere at each vertex location. Based on a previous answer I then created a curve and added a new spline for each bond. That makes only three objects to display the data.

Using your sample data, multiplied by 1000 (with a small z offset for each) it takes 3.3 secs on my corei7 machine.

import bpy, bmesh, bpy_extras

mesh = bpy.data.meshes.new("Atoms")
bm = bmesh.new()

for atom in molecule['atoms']:
    bm.verts.new(atom['location'])

bm.to_mesh(mesh)
mesh.update()
bpy_extras.object_utils.object_data_add(bpy.context, mesh)
points = bpy.context.object

bpy.ops.object.select_all(action='DESELECT')
bpy.ops.mesh.primitive_uv_sphere_add()
bpy.ops.object.shade_smooth()
sphere = bpy.context.object
sphere.scale = (0.2,0.2,0.2)

sphere.parent = points
points.dupli_type = "VERTS"

bpy.ops.curve.primitive_bezier_curve_add()
curve = bpy.context.object
curve.data.dimensions = '3D'
curve.data.fill_mode = 'FULL'
curve.data.bevel_depth = 0.05
curve.data.bevel_resolution = 4

c_splines = curve.data.splines
c_splines.remove(c_splines[0])

for bond in molecule['bonds']:
    spline = c_splines.new('BEZIER')
    spline.bezier_points[0].co = molecule['atoms'][bond['atoms'][0]]['location']
    spline.bezier_points[0].handle_left_type = 'VECTOR'
    spline.bezier_points[0].handle_right_type = 'VECTOR'

    spline.bezier_points.add(1)
    spline.bezier_points[1].co = molecule['atoms'][bond['atoms'][1]]['location']
    spline.bezier_points[1].handle_left_type = 'VECTOR'
    spline.bezier_points[1].handle_right_type = 'VECTOR'