# 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')
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.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?

• – batFINGER Dec 22 '15 at 16:27
• what do you plan to do with the imported geometry? just render it or some kind of kinematic simulation? – zeffii Dec 24 '15 at 18:47
• This may interest you – sambler Dec 25 '15 at 6:18

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()
points = bpy.context.object

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

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

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[1].co = molecule['atoms'][bond['atoms'][1]]['location']
spline.bezier_points[1].handle_left_type = 'VECTOR'
spline.bezier_points[1].handle_right_type = 'VECTOR'


I may be not reading this correctly is this what you're after for scaling?

for atom in atoms:
ob = sphere.copy()
ob.select = False
ob.location = atom["location"]
if (atom["element"] == "C"):
ob.scale = [.3,.3,.3]
if (atom["element"] == "H"):
ob.scale = [.1,.1,.1]
ob.data = sphere.data.copy()