Skip to content

Commit

Permalink
More infrastructure. Most modes look stable with most flags.
Browse files Browse the repository at this point in the history
  • Loading branch information
ajjackson committed Mar 10, 2016
1 parent 133020e commit 6d4ad85
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 60 deletions.
15 changes: 11 additions & 4 deletions addons/vsim2blender/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,17 @@ def __init__(self, options, parser=False):
'scale_vib',
'zoom')

self.float_tuple_keys = (
'miller',
'offset_box')

self.int_keys = (
'end_frame',
'mode_index',
'n_frames',
'start_frame')

self.tuple_keys = (
'offset_box',
self.int_tuple_keys = (
'supercell')

def get(self, key, fallback):
Expand All @@ -124,9 +127,13 @@ def get(self, key, fallback):
return self.config.getfloat('general', key)
elif key in self.int_keys:
return self.config.getint('general', key)
elif key in self.tuple_keys:
elif key in self.float_tuple_keys:
return tuple(map(float,
self.config.get('general, key').split()
self.config.get('general', key).split()
))
elif key in self.int_tuple_keys:
return tuple(map(int,
self.config.get('general', key).split()
))
else:
return fallback
63 changes: 37 additions & 26 deletions addons/vsim2blender/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
import math
from mathutils import Vector
from json import loads
from vsim2blender import read_config
from vsim2blender import read_config, Opts


def setup_camera(lattice_vectors, field_of_view=0.5,
scene=bpy.context.scene, config=read_config()):
scene=bpy.context.scene, opts=Opts({})):
"""
Set up a camera looking along the y axis
Expand All @@ -15,42 +16,49 @@ def setup_camera(lattice_vectors, field_of_view=0.5,
:type field_of_view: float
:param scene: Scene in which to insert camera object
:type scene: bpy Scene
:param config: config settings. The following parameters in [general] are used:
camera_rot
:param opts: Initialised Opts object. The following parameters,
either specified in the dict when Opts is initialised or under
[general] in the specified config file, are used:
camera_rot
*(float cast to string)*
Camera tilt adjustment in degrees
miller
*(3-list of floats cast to str)*
Miller indices of target view. Floating-point values may be
used for fine adjustments if desired.
Miller indices of target view. Floating-point values may
be used for fine adjustments if desired.
supercell
*(3-list of ints cast to str)*
Supercell dimensions
zoom
*(float)* Camera zoom adjustment
:type config: configparser.ConfigParser
:type opts: vsim2blender.Opts
"""

camera_rot = config.getfloat('general', 'camera_rot', fallback=0)
miller = loads(config.get('general', 'miller', fallback='[0, 1, 0]'))
supercell = loads(config.get('general', 'supercell', fallback='[2, 2, 2]'))
zoom = config.getfloat('general', 'zoom', fallback=1.)
camera_rot = opts.get('camera_rot', 0)
miller = opts.get('miller', (0, 1, 0))
supercell = opts.get('supercell', (2, 2, 2))
zoom = opts.get('zoom', 1.)

a, b, c = [n * x for n, x in zip(supercell, lattice_vectors)]
supercell_centre = 0.5 * sum([a,b,c], Vector((0.,0.,0.)))
vertices = [x for x in [Vector((0,0,0)), a, a+b, b, c, c+a, c+a+b, c+b]]
supercell_centre = 0.5 * sum([a, b, c], Vector((0., 0., 0.)))
vertices = list([Vector((0, 0, 0)), a, a+b,
b, c, c+a, c+a+b, c+b])

# Create an empty at the centre of the model for the camera to target
# Create an empty at the centre of the model for
# the camera to target
bpy.ops.object.add(type='EMPTY', location=supercell_centre)
empty_center = bpy.context.object

# Use Miller index to create a vector to the camera
# with arbitrary magnitude. Distances perpendicular to this vector
# determine required camera distance

camera_direction_vector = sum([i * x for i, x in zip(miller, reciprocal(lattice_vectors))],
Vector((0,0,0)))

camera_direction_vector = sum([i * x for i, x in
zip(miller,
reciprocal(lattice_vectors))],
Vector((0, 0, 0)))

vertices_from_center = [v - supercell_centre for v in vertices]

Expand All @@ -67,8 +75,7 @@ def setup_camera(lattice_vectors, field_of_view=0.5,
bpy.context.scene.camera = camera
bpy.data.cameras[camera.name].angle = field_of_view
bpy.data.cameras[camera.name].clip_end = 1e8



# Use tracking to point camera at center of structure
bpy.ops.object.constraint_add(type='TRACK_TO')
camera.constraints['Track To'].target = empty_center
Expand All @@ -83,7 +90,8 @@ def setup_camera(lattice_vectors, field_of_view=0.5,

# Tweak zoom level
bpy.data.cameras[camera.name].lens = zoom * 75



def dist_to_view_point(point, camera_direction_vector, field_of_view):
"""
Calculate the required camera distance along a vector to keep a
Expand All @@ -96,19 +104,22 @@ def dist_to_view_point(point, camera_direction_vector, field_of_view):
:type camera_direction_vector: 3-Vector
:param field_of_view: The camera field of view in radians
:type field_of_view: Float
"""
projection = point.project(camera_direction_vector)
rejection = point - projection
cone_width = rejection.length
distance = cone_width / math.sin(field_of_view)
return distance


def reciprocal(lattice_vectors):
"""
Get reciprocal lattice vectors
Follows the equations outlined by Ashcroft & Mermin (Solid State Physics Ch 5, 1976)
Follows the equations outlined by Ashcroft & Mermin
(Solid State Physics Ch 5, 1976)
b1 = 2 pi (a2 x a3)/(a1 . (a2 x a3))
b2 = 2 pi (a3 x a1)/(a1 . (a2 x a3))
b3 = 2 pi (a1 x a2)/(a1 . (a2 x a3))
Expand All @@ -122,7 +133,7 @@ def reciprocal(lattice_vectors):
a1, a2, a3 = lattice_vectors

denominator = a1.dot(a2.cross(a3)) / (2. * math.pi)
b1, b2, b3 = [x1.cross(x2)/denominator for x1, x2 in ((a2,a3),
(a3,a1),
(a1,a2))]
b1, b2, b3 = [x1.cross(x2)/denominator for x1, x2 in ((a2, a3),
(a3, a1),
(a1, a2))]
return (b1, b2, b3)
11 changes: 5 additions & 6 deletions addons/vsim2blender/plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,10 @@ def open_mode(**options):
start_frame = opts.get('start_frame', 0)
n_frames = opts.get('n_frames', 30)

# Preview images default to static, others default to animated
preview = opts.get('preview', False)
if preview:
static = True
static = opts.get('static', True)
else:
static = opts.get('static', False)

Expand Down Expand Up @@ -400,7 +401,7 @@ def open_mode(**options):
# cameras as 'cameras' have different attributes, so need to look up
# camera in bpy.data.cameras to set field of view.

camera.setup_camera(lattice_vectors, field_of_view=0.2, config=opts.config)
camera.setup_camera(lattice_vectors, field_of_view=0.2, opts=opts)

bpy.context.scene.world = bpy.data.worlds['World']
bpy.data.worlds['World'].horizon_color = str2list(opts.config.get(
Expand Down Expand Up @@ -472,7 +473,7 @@ def setup_render_freestyle(**options):
start_frame = opts.get('start_frame', 0)
n_frames = opts.get('n_frames', 30)

if opts.get('preview', False) or opts.get('static', False):
if opts.get('static', False):
end_frame = start_frame
else:
end_frame = opts.get('end_frame', start_frame + n_frames - 1)
Expand Down Expand Up @@ -589,12 +590,10 @@ def render(scene=False, output_file=False, preview=False):
:type preview: str
"""
print(preview)
print(output_file)
if preview:
output_file = preview

if (not output_file) or output_file == 'False':
if (not output_file) or output_file == 'False' or output_file == '':
pass

else:
Expand Down
57 changes: 36 additions & 21 deletions ascii_phonons/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ def get(self, key, fallback):
return tuple(map(float,
self.config.get('general, key').split()
))
else:
return self.config.get('general', key)
else:
return fallback

Expand All @@ -103,7 +105,8 @@ def call_blender(**options):
of .png image files.
"""
blender_osx = "/Applications/Blender/blender.app/Contents/MacOS/blender"
blender_osx = ("/Applications/Blender/blender.app" +
"/Contents/MacOS/blender")

opts = Opts(options)

Expand Down Expand Up @@ -148,7 +151,8 @@ def call_blender(**options):
vsim2blender.plotter.open_mode(**{options})
vsim2blender.plotter.setup_render_freestyle(**{options})
vsim2blender.plotter.render(output_file='{out_file}', preview='{preview}')
vsim2blender.plotter.render(output_file='{out_file}',
preview='{preview}')
""".format(options=str(options), add_path=addons_path,
config=opts.get('config', ''),
out_file=output_file,
Expand All @@ -165,19 +169,24 @@ def call_blender(**options):

remove(python_tmp_file)

if opts.get('gif', False) and output_file:
if opts.get('gif', False) and output_file and not opts.get('static',
False):
frames = range(opts.get('start_frame', 0),
opts.get('end_frame',
opts.get('n_frames', 30)) + 1)
tmp_files = [''.join((output_file,
'{0:04.0f}'.format(i),
'.png'))
for i in range(n_frames)]
for i in frames]
convert_call_args = (['convert', '-delay', '10'] +
tmp_files + ['-loop', '0', gif_name])
try:
call(convert_call_args)
except OSError as err:
raise Exception("\n\nCould not run Imagemagick convert to create" +
" .gif.\n Error message: {0}\n".format(err) +
"Are you sure you have Imagemagick installed?\n")
raise Exception("\n\nCould not run Imagemagick convert" +
" to create .gif.\n Error message:" +
" {0}\nAre you sure".format(err) +
" you have Imagemagick installed?\n")

for f in tmp_files:
remove(f)
Expand All @@ -186,25 +195,25 @@ def call_blender(**options):
def montage_static(**options):
"""Render images for all phonon modes and present as array"""
opts = Opts(options)
mode_data = list(_qpt_freq_iter(opts.get('input_file')))
mode_data = list(_qpt_freq_iter(opts.get('input_file', None)))

for param, default in (('output_file', 'phonon'),):
if not opts.get(param, False):
options[param] = default

call_args = ['montage', '-font', 'Helvetica', '-pointsize', '18']

# Render smaller image
options.update({'preview': True})

# The output filename is used as the root for temporary images
# These are requested as "preview" images to reduce rescaling
output_basename = opts.get('output_file', 'phonon')

for index, (qpt, freq) in enumerate(mode_data):
options.update({'output_file': '.'.join((output_basename,
options.update({'preview': '.'.join((output_basename,
str(index)))})
options.update({'mode_index': index})
call_blender(**options)
call_args.extend(['-label', _flabelformat(freq),
options['output_file'] + '.png'])
options['preview'] + '.png'])
call_args.append(output_basename + '_montage.png')

call(call_args)
Expand All @@ -229,12 +238,14 @@ def montage_anim(**options):
opts.get('n_frames', 30) - 1)

# Render smaller image, take over gif generation
options.update({'preview': True, 'gif': False})
# 'static' is explicitly set to False to override
# 'preview' defaults
options.update({'gif': False, 'static': False})
output_basename = opts.get('output_file', 'phonon')
labels = []
for index, (qpt, freq) in enumerate(mode_data):
options.update({'output_file': '.'.join((output_basename,
str(index), ''))})
options.update({'preview': '.'.join((output_basename,
str(index), ''))})
options.update({'mode_index': index})
call_blender(**options)
labels.append(_flabelformat(freq))
Expand All @@ -243,6 +254,7 @@ def montage_anim(**options):

frames = range(opts.get('start_frame', 0),
opts.get('end_frame', 29) + 1)

for frame in frames:
montage_call_args = ['montage', '-font', 'Helvetica',
'-pointsize', '18']
Expand All @@ -259,15 +271,18 @@ def montage_anim(**options):
try:
call(montage_call_args)
except OSError as err:
raise Exception("\n\nCould not run Imagemagick convert to create "
".gif.\n Error message: {0}\n".format(err) +
"Are you sure you have Imagemagick installed?\n")
raise Exception("\n\nCould not run Imagemagick convert " +
"to create .gif.\n Error message: " +
"{0}\nAre you sure you have".format(err) +
" Imagemagick installed?\n")

print("Joining images into .gif file")

convert_call_args = (['convert', '-delay', '10'] +
['.'.join((output_basename, '{0}'.format(frame),
'montage.png')) for frame in frames]
['.'.join((output_basename,
'{0}'.format(frame),
'montage.png'))
for frame in frames] +
['-loop', '0', output_basename + '.gif'])
call(convert_call_args)
print("Cleaning up...")
Expand Down
4 changes: 1 addition & 3 deletions scripts/ascii-phonons
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ from __future__ import print_function
import argparse
import os
import sys
import ascii_phonons

pathname = os.path.abspath(sys.argv[0])
project_root = os.path.dirname(os.path.dirname(pathname))
sys.path = [project_root] + sys.path

import ascii_phonons

if __name__ == "__main__":
parser = argparse.ArgumentParser()
Expand Down Expand Up @@ -82,7 +81,6 @@ if __name__ == "__main__":
if 'no_box' in options:
options['show_box'] = False

print(options['config'])
opts = ascii_phonons.Opts(options)

if opts.get('montage', False) and opts.get('static', False):
Expand Down

0 comments on commit 6d4ad85

Please sign in to comment.