class Notes(ModelObject): info = ModelObjectInfo("plugins/basic/notes.svg") authors = [authors.toon_verstraelen] # # Properties # def set_notes(self, notes, init=False): self.notes = notes properties = [ Property("notes", StringIO.StringIO(), lambda self: self.notes, set_notes) ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo("Basic", (0, 10), fields.edit.TextView( label_text="Notes", attribute_name="notes", )) ])
class ConscanResults(ReferentBase): info = ModelObjectInfo("plugins/builder/conscan_results.svg", "ShowConscanResultsWindow") authors = [authors.toon_verstraelen] # # State # def initnonstate(self): ReferentBase.initnonstate(self) self.set_children( [Reference(prefix="First"), Reference(prefix="Second")]) # # Properties # def get_connections(self): def get_index(node): if node is None: return -1 node = node() if node is None: return -1 if node.model != context.application.model: return -1 return node.get_index() return [(quality, transformation, [ (get_index(node1), get_index(node2)) for node1, node2 in pairs ], [(get_index(node1), get_index(node2)) for node1, node2 in inverse_pairs]) for quality, transformation, pairs, inverse_pairs in self.connections] def set_connections(self, connections, init=False): def get_ref(frame_index, index): if index is -1: return None else: return weakref.ref( self.children[frame_index].target.children[index]) self.connections = [ (quality, transformation, [(get_ref(0, index1), get_ref(1, index2)) for index1, index2 in pairs], [(get_ref(0, index1), get_ref(1, index2)) for index1, index2 in inverse_pairs]) for quality, transformation, pairs, inverse_pairs in connections ] properties = [ Property("connections", [], get_connections, set_connections), ]
class UserColorMixin(gobject.GObject): __metaclass__ = NodeClass # # Properties # def set_user_color(self, user_color, init=False): self.user_color = user_color if not init: self.invalidate_draw_list() properties = [ Property("user_color", Undefined(numpy.array([0.7, 0.7, 0.7, 1.0])), lambda self: self.user_color, set_user_color, signal=True) ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Markup", (1, 7), fields.optional.CheckOptional( fields.edit.Color( label_text="User defined color", attribute_name="user_color", ))), ]) # # Draw # def get_color(self): if isinstance(self.user_color, Undefined): return self.default_color else: return self.user_color def draw(self): context.application.vis_backend.set_color(*self.get_color())
class GLPeriodicContainer(GLContainerBase): # # Properties # def update_vectors(self): for node in self.children: if isinstance(node, GLReferentBase): node.invalidate_draw_list() node.invalidate_boundingbox_list() def set_cell(self, cell, init=False): self.cell = cell if not init: self.invalidate_boundingbox_list() self.invalidate_draw_list() self.update_vectors() # # Properties # properties = [ Property("cell", default_unit_cell, lambda self: self.cell, set_cell), ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Unit cell", (5, 0), fields.composed.Cell( label_text="Periodic cell", attribute_name="cell", )), ])
class ColorMixin(gobject.GObject): __metaclass__ = NodeClass # # Properties # def set_color(self, color, init=False): self.color = color if not init: self.invalidate_draw_list() properties = [ Property("color", numpy.array([0.7, 0.7, 0.7, 1.0]), lambda self: self.color, set_color) ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Markup", (1, 0), fields.edit.Color( label_text="Color", attribute_name="color", )) ]) # # Draw # def draw(self): context.application.vis_backend.set_color(*self.color)
class GLTransformationMixin(GLMixin): # # State # def initnonstate(self, Transformation): GLMixin.initnonstate(self) self.Transformation = Transformation # # Properties # def default_transformation(self): return self.Transformation.identity() def set_transformation(self, transformation, init=False): if not transformation.__class__ == self.Transformation: # create an object of the proper type and take only the attributes # of interest. if isinstance(transformation, Translation): t = transformation.t else: t = None if isinstance(transformation, Rotation): r = transformation.r else: r = None if self.Transformation == Translation: if t is None: transformation = Translation.identity() else: transformation = Translation(t) elif self.Transformation == Rotation: if r is None: transformation = Rotation.identity() else: transformation = Rotation(r) else: # self.Transformation == Complete: if r is None: r = numpy.identity(3, float) if t is None: t = numpy.zeros(3, float) transformation = Complete(r, t) self.transformation = transformation if not init: self.invalidate_transformation_list() properties = [ Property("transformation", default_transformation, lambda self: self.transformation, set_transformation) ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Transformation", (3, 0), fields.composed.Translation( label_text="Translation with vector t", attribute_name="transformation", )), DialogFieldInfo( "Transformation", (3, 1), fields.composed.Rotation( label_text="Rotation around axis n", attribute_name="transformation", )), DialogFieldInfo( "Transformation", (3, 2), fields.composed.Complete( label_text= "The transformation is a rotation followed by a translation.", attribute_name="transformation", )), DialogFieldInfo("Transformation", (3, 3), fields.read.Handedness()), ]) # # OpenGL # def initialize_gl(self): vb = context.application.vis_backend self.transformation_list = vb.create_list() ##print "Created transformation list (%i): %s" % (self.transformation_list, self.get_name()) self.transformation_list_valid = True GLMixin.initialize_gl(self) def cleanup_gl(self): GLMixin.cleanup_gl(self) vb = context.application.vis_backend ##print "Deleting transformation list (%i): %s" % (self.transformation_list, self.get_name()) vb.delete_list(self.transformation_list) del self.transformation_list del self.transformation_list_valid # # Invalidation # def invalidate_transformation_list(self): ##print "CALL %s: on-transformation-list-invalidated" % self.get_name() if self.gl_active and self.transformation_list_valid: self.transformation_list_valid = False context.application.main.drawing_area.queue_draw() context.application.scene.add_revalidation( self.revalidate_transformation_list) self.emit("on-transformation-list-invalidated") ##print "EMIT %s: on-transformation-list-invalidated" % self.get_name() if isinstance(self.parent, GLMixin): self.parent.invalidate_boundingbox_list() def invalidate_all_lists(self): self.invalidate_transformation_list() GLMixin.invalidate_all_lists(self) # # Revalidation # def revalidate_transformation_list(self): if self.gl_active: vb = context.application.vis_backend ##print "Compiling transformation list (%i): %s" % (self.transformation_list, self.get_name()) vb.begin_list(self.transformation_list) vb.transform(self.transformation) vb.end_list() self.transformation_list_valid = True def revalidate_total_list(self): if self.gl_active: vb = context.application.vis_backend ##print "Compiling total list (%i): %s" % (self.total_list, self.get_name()) vb.begin_list(self.total_list) if self.visible: vb.push_name(self.draw_list) vb.push_matrix() vb.call_list(self.transformation_list) self.draw_selection() vb.call_list(self.draw_list) vb.pop_matrix() vb.pop_name() vb.end_list() self.total_list_valid = True # # Frame # def get_bounding_box_in_parent_frame(self): return self.bounding_box.transformed(self.transformation) def get_absolute_frame(self): if not isinstance(self.parent, GLMixin): return self.transformation else: return self.get_absolute_parentframe() * self.transformation def get_frame_up_to(self, upper_parent): if (upper_parent == self): return Complete.identity() elif (self.parent == upper_parent): return self.transformation else: return self.get_parentframe_up_to( upper_parent) * self.transformation
class Point(GLGeometricBase, ColorMixin): info = ModelObjectInfo("plugins/basic/point.svg") authors = [authors.toon_verstraelen] def initnonstate(self): GLGeometricBase.initnonstate(self, Translation) # # Properties # def set_spike_length(self, spike_length, init=False): self.spike_length = spike_length if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() def set_spike_thickness(self, spike_thickness, init=False): self.spike_thickness = spike_thickness if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() properties = [ Property("spike_length", 0.3, lambda self: self.spike_length, set_spike_length), Property("spike_thickness", 0.1, lambda self: self.spike_thickness, set_spike_thickness), ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Geometry", (2, 7), fields.faulty.Length(label_text="Spike length", attribute_name="spike_length", low=0.0, low_inclusive=False)), DialogFieldInfo( "Geometry", (2, 8), fields.faulty.Length(label_text="Spike thickness", attribute_name="spike_thickness", low=0.0, low_inclusive=False)) ]) # # Draw # def draw_spike(self): ColorMixin.draw(self) vb = context.application.vis_backend vb.draw_quad_strip( (numpy.array([0.5773502692, -0.5773502692, -0.5773502692]), [ numpy.array( [self.spike_length, self.spike_length, self.spike_length]) ]), (numpy.array([1, 0, 0 ]), [numpy.array([self.spike_thickness, 0, 0])]), (numpy.array([-0.5773502692, 0.5773502692, -0.5773502692]), [ numpy.array( [self.spike_length, self.spike_length, self.spike_length]) ]), (numpy.array([0, 1, 0 ]), [numpy.array([0, self.spike_thickness, 0])]), (numpy.array([-0.5773502692, -0.5773502692, 0.5773502692]), [ numpy.array( [self.spike_length, self.spike_length, self.spike_length]) ]), (numpy.array([0, 0, 1 ]), [numpy.array([0, 0, self.spike_thickness])]), (numpy.array([0.5773502692, -0.5773502692, -0.5773502692]), [ numpy.array( [self.spike_length, self.spike_length, self.spike_length]) ]), (numpy.array([1, 0, 0 ]), [numpy.array([self.spike_thickness, 0, 0])])) def draw(self): GLGeometricBase.draw(self) vb = context.application.vis_backend vb.push_matrix() for i in range(2): for i in range(4): self.draw_spike() vb.rotate(90, 1.0, 0.0, 0.0) vb.rotate(180, 0.0, 1.0, 0.0) vb.pop_matrix() # # Revalidation # def revalidate_bounding_box(self): GLGeometricBase.revalidate_bounding_box(self) self.bounding_box.extend_with_corners( numpy.array( [[-self.spike_length, -self.spike_length, -self.spike_length], [self.spike_length, self.spike_length, self.spike_length]]))
class Arrow(Vector, ColorMixin): info = ModelObjectInfo("plugins/basic/arrow.svg") authors = [authors.toon_verstraelen] # # Properties # def set_radius(self, radius, init=False): self.radius = radius if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() def set_quality(self, quality, init=False): self.quality = quality if not init: self.invalidate_draw_list() def set_arrow_length(self, arrow_length, init=False): self.arrow_length = arrow_length if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() def set_arrow_radius(self, arrow_radius, init=False): self.arrow_radius = arrow_radius if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() def set_arrow_position(self, arrow_position, init=False): self.arrow_position = arrow_position if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() properties = [ Property("radius", 0.15, lambda self: self.radius, set_radius), Property("quality", 15, lambda self: self.quality, set_quality), Property("arrow_length", 0.6, lambda self: self.arrow_length, set_arrow_length), Property("arrow_radius", 0.3, lambda self: self.arrow_radius, set_arrow_radius), Property("arrow_position", 0.5, lambda self: self.arrow_position, set_arrow_position) ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Geometry", (2, 2), fields.faulty.Length( label_text="Radius", attribute_name="radius", low=0.0, low_inclusive=False, )), DialogFieldInfo( "Geometry", (2, 3), fields.faulty.Length( label_text="Arrow length", attribute_name="arrow_length", low=0.0, )), DialogFieldInfo( "Geometry", (2, 4), fields.faulty.Length( label_text="Arrow radius", attribute_name="arrow_radius", low=0.0, low_inclusive=False, )), DialogFieldInfo( "Geometry", (2, 5), fields.faulty.Float( label_text="Arrow position", attribute_name="arrow_position", low=0.0, high=1.0, )), DialogFieldInfo( "Markup", (1, 3), fields.faulty.Int( label_text="Quality", attribute_name="quality", minimum=3, )), ]) # # Draw # def draw(self): Vector.draw(self) ColorMixin.draw(self) vb = context.application.vis_backend if self.length == 0.0: return # usefull variable if self.arrow_radius <= 0: arrowtop_length = self.arrow_length else: arrowtop_length = self.arrow_length / self.arrow_radius * self.radius # stick and bottom if (self.length - arrowtop_length > 0) and (self.radius > 0): vb.draw_cylinder(self.radius, self.length - arrowtop_length, self.quality) vb.set_quadric_inside() vb.draw_disk(self.radius, self.quality) # arrowtop if (self.radius > 0): if (arrowtop_length > 0): vb.push_matrix() vb.translate(0.0, 0.0, self.length - arrowtop_length) vb.set_quadric_outside() vb.draw_cone(self.radius, 0, arrowtop_length, self.quality) vb.pop_matrix() else: vb.push_matrix() vb.translate(0.0, 0.0, self.length) vb.set_quadric_outside() vb.draw_disk(self.radius, self.quality) vb.pop_matrix() # arrow if (self.arrow_radius - self.radius > 0) and (self.arrow_length - arrowtop_length) > 0: vb.push_matrix() vb.translate(0.0, 0.0, (self.length - self.arrow_length) * (self.arrow_position)) vb.set_quadric_outside() vb.draw_cone(self.arrow_radius, self.radius, self.arrow_length - arrowtop_length, self.quality) vb.set_quadric_inside() vb.draw_disk(self.arrow_radius, self.quality) vb.pop_matrix() vb.set_quadric_outside() # # Revalidation # def revalidate_bounding_box(self): Vector.revalidate_bounding_box(self) if self.length > 0.0: self.bounding_box.extend_with_corners( numpy.array([[0.0, 0.0, 0.0], [0.0, 0.0, self.length]])) temp = { True: self.radius, False: self.arrow_radius }[self.radius > self.arrow_radius] self.bounding_box.extend_with_corners( numpy.array([[-temp, -temp, 0.0], [temp, temp, 0.0]]))
class Plane(GLReferentBase, ColorMixin): info = ModelObjectInfo("plugins/basic/plane.svg") authors = [authors.toon_verstraelen] # # State # def set_targets(self, targets, init=False): self.set_children([SpatialReference("Point") for target in targets]) GLReferentBase.set_targets(self, targets, init) # # Properties # def set_margin(self, margin, init=False): self.margin = margin if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() properties = [ Property("margin", 1.0, lambda self: self.margin, set_margin), ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Geometry", (2, 10), fields.faulty.Length( label_text="Margin", attribute_name="margin", low=0.0, low_inclusive=True, )), ]) # # Draw # def update_normal(self): self.points = numpy.array([ child.target.get_frame_relative_to(self.parent).t for child in self.children if child.target is not None ], float) self.center = sum(self.points) / len(self.points) tmp = self.points - self.center tensor = (tmp.ravel()**2).sum() * numpy.identity(3) - numpy.dot( tmp.transpose(), tmp) evals, evecs = numpy.linalg.eigh(tensor) indices = evals.argsort() self.x = evecs[:, indices[0]] self.y = evecs[:, indices[1]] if numpy.linalg.det(evecs) < 0: self.normal = evecs[:, indices[2]] else: self.normal = -evecs[:, indices[2]] px = numpy.dot(tmp, self.x) py = numpy.dot(tmp, self.y) px_low = px.min() - self.margin px_high = px.max() + self.margin py_low = py.min() - self.margin py_high = py.max() + self.margin self.l_l = self.x * px_low + self.y * py_low + self.center self.l_h = self.x * px_low + self.y * py_high + self.center self.h_l = self.x * px_high + self.y * py_low + self.center self.h_h = self.x * px_high + self.y * py_high + self.center def draw(self): GLReferentBase.draw(self) ColorMixin.draw(self) self.update_normal() vb = context.application.vis_backend vb.draw_quads(( self.normal, [ self.l_l + 0.001 * self.normal, self.l_h + 0.001 * self.normal, self.h_h + 0.001 * self.normal, self.h_l + 0.001 * self.normal, ], ), ( -self.normal, [ self.h_l - 0.001 * self.normal, self.h_h - 0.001 * self.normal, self.l_h - 0.001 * self.normal, self.l_l - 0.001 * self.normal, ], )) # # Revalidation # def revalidate_bounding_box(self): GLReferentBase.revalidate_bounding_box(self) self.update_normal() self.bounding_box.extend_with_point(self.l_l) self.bounding_box.extend_with_point(self.l_h) self.bounding_box.extend_with_point(self.h_l) self.bounding_box.extend_with_point(self.h_h) # # Vector # def define_target(self, reference, new_target): GLReferentBase.define_target(self, reference, new_target) self.invalidate_boundingbox_list() self.invalidate_draw_list() def target_moved(self, reference, target): GLReferentBase.target_moved(self, reference, target) self.invalidate_boundingbox_list() self.invalidate_draw_list()
class Box(GLGeometricBase, ColorMixin): info = ModelObjectInfo("plugins/basic/box.svg") authors = [authors.toon_verstraelen] def initnonstate(self): GLGeometricBase.initnonstate(self, Complete) # # Properties # def set_size(self, size, init=False): self.size = size if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() properties = [ Property("size", numpy.array([1.0, 1.0, 1.0]), lambda self: self.size, set_size), ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Geometry", (2, 6), fields.composed.ComposedArray( FieldClass=fields.faulty.Length, array_name="%s", suffices=["Width", "Height", "Depth"], label_text="Box size", attribute_name="size", )) ]) # # Draw # def draw(self): GLGeometricBase.draw(self) ColorMixin.draw(self) vb = context.application.vis_backend x, y, z = numpy.identity(3) sides = numpy.array([(x, y, z, z), (x, y, -z, -z), (y, z, x, x), (y, z, -x, -x), (z, x, y, y), (z, x, -y, -y)], float) sides[:, :3] *= 0.5 * self.size vb.draw_quads(*[(normal, [ a + b + c, a - b + c, -a - b + c, -a + b + c, ]) for a, b, c, normal in sides]) # # Revalidation # def revalidate_bounding_box(self): GLGeometricBase.revalidate_bounding_box(self) self.bounding_box.extend_with_corners( numpy.array([-0.5 * self.size, 0.5 * self.size]))
class Universe(GLPeriodicContainer, FrameAxes): info = ModelObjectInfo("plugins/basic/universe.svg") authors = [authors.toon_verstraelen] clip_margin = 0.1 # # State # def initnonstate(self): GLPeriodicContainer.initnonstate(self) self.model_center = Translation.identity() def update_center(self): self.model_center = Translation( 0.5 * numpy.dot(self.cell.matrix, self.repetitions * self.cell.active)) # # Properties # def set_cell(self, cell, init=False): GLPeriodicContainer.set_cell(self, cell, init) if not init: self.update_clip_planes() self.update_center() self.invalidate_total_list() self.invalidate_box_list() def set_repetitions(self, repetitions, init=False): self.repetitions = repetitions if not init: self.update_clip_planes() self.update_center() self.invalidate_box_list() self.invalidate_total_list() def set_box_visible(self, box_visible, init=False): self.box_visible = box_visible if not init: self.invalidate_total_list() def set_clipping(self, clipping, init=False): self.clipping = clipping if not init: self.invalidate_total_list() self.invalidate_box_list() self.update_clip_planes() properties = [ Property("repetitions", numpy.array([1, 1, 1], int), lambda self: self.repetitions, set_repetitions), Property("box_visible", True, lambda self: self.box_visible, set_box_visible), Property("clipping", False, lambda self: self.clipping, set_clipping), ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Unit cell", (5, 2), fields.composed.Repetitions( label_text="Repetitions", attribute_name="repetitions", )), DialogFieldInfo( "Markup", (1, 5), fields.edit.CheckButton( label_text="Show periodic box (if active)", attribute_name="box_visible", )), DialogFieldInfo( "Markup", (1, 6), fields.edit.CheckButton( label_text="Clip the unit cell contents.", attribute_name="clipping", )), ]) # # Tree # @classmethod def check_add(Class, ModelObjectClass): if not GLPeriodicContainer.check_add(ModelObjectClass): return False if issubclass(ModelObjectClass, Universe): return False return True # # OpenGL # def initialize_gl(self): vb = context.application.vis_backend self.set_clip_planes() self.update_center() self.box_list = vb.create_list() ##print "Created box list (%i): %s" % (self.box_list, self.get_name()) self.box_list_valid = True GLPeriodicContainer.initialize_gl(self) def cleanup_gl(self): vb = context.application.vis_backend GLPeriodicContainer.cleanup_gl(self) ##print "Deleting box list (%i): %s" % (self.box_list, self.get_name()) vb.delete_list(self.box_list) del self.box_list del self.box_list_valid self.unset_clip_planes() # # Clipping # def update_clip_planes(self): if self.gl_active > 0: self.unset_clip_planes() self.set_clip_planes() def set_clip_planes(self): if not self.clipping: return clip_planes = context.application.scene.clip_planes assert len(clip_planes) == 0 active, inactive = self.cell.active_inactive for index in active: axis = self.cell.matrix[:, index] ortho = self.cell.reciprocal[index] / numpy.linalg.norm( self.cell.reciprocal[index]) length = abs(numpy.dot(ortho, axis)) repetitions = self.repetitions[index] clip_planes.append(numpy.array(list(ortho) + [self.clip_margin])) clip_planes.append( numpy.array( list(-ortho) + [repetitions * length + self.clip_margin])) context.application.main.drawing_area.queue_draw() def unset_clip_planes(self): context.application.scene.clip_planes = [] context.application.main.drawing_area.queue_draw() def shortest_vector(self, delta): return self.cell.shortest_vector(delta) # # Invalidation # def invalidate_box_list(self): if self.gl_active > 0 and self.box_list_valid: self.box_list_valid = False context.application.main.drawing_area.queue_draw() context.application.scene.add_revalidation( self.revalidate_box_list) ##print "EMIT %s: on-box-list-invalidated" % self.get_name() def invalidate_all_lists(self): self.invalidate_box_list() GLPeriodicContainer.invalidate_all_lists(self) # # Draw # def draw_box(self): vb = context.application.vis_backend vb.set_line_width(2) vb.set_specular(False) col = {True: 4.0, False: 2.5}[self.selected] sat = {True: 0.0, False: 0.5}[self.selected] gray = {True: 4.0, False: 2.5}[self.selected] def draw_three(origin): if self.cell.active[0]: vb.set_color(col, sat, sat) vb.draw_line(origin, origin + self.cell.matrix[:, 0]) if self.cell.active[1]: vb.set_color(sat, col, sat) vb.draw_line(origin, origin + self.cell.matrix[:, 1]) if self.cell.active[2]: vb.set_color(sat, sat, col) vb.draw_line(origin, origin + self.cell.matrix[:, 2]) def draw_gray(origin, axis1, axis2, n1, n2, delta, nd): c = context.application.configuration.periodic_box_color vb.set_color(gray * c[0], gray * c[1], gray * c[2], c[3]) if n1 == 0 and n2 == 0: return for i1 in xrange(n1 + 1): if i1 == 0: b2 = 1 vb.draw_line(origin + delta, origin + nd * delta) else: b2 = 0 for i2 in xrange(b2, n2 + 1): vb.draw_line(origin + i1 * axis1 + i2 * axis2, origin + i1 * axis1 + i2 * axis2 + nd * delta) def draw_ortho(origin, axis1, axis2, n1, n2, delta): c = context.application.configuration.periodic_box_color vb.set_color(gray * c[0], gray * c[1], gray * c[2], c[3]) if n1 == 0 and n2 == 0: return for i1 in xrange(n1 + 1): for i2 in xrange(n2 + 1): vb.draw_line( origin + i1 * axis1 + i2 * axis2 - 0.5 * delta, origin + i1 * axis1 + i2 * axis2 + 0.5 * delta) origin = numpy.zeros(3, float) draw_three(origin) repetitions = self.repetitions * self.cell.active if self.cell.active[2]: draw_gray(origin, self.cell.matrix[:, 0], self.cell.matrix[:, 1], repetitions[0], repetitions[1], self.cell.matrix[:, 2], repetitions[2]) else: draw_ortho(origin, self.cell.matrix[:, 0], self.cell.matrix[:, 1], repetitions[0], repetitions[1], self.cell.matrix[:, 2]) if self.cell.active[0]: draw_gray(origin, self.cell.matrix[:, 1], self.cell.matrix[:, 2], repetitions[1], repetitions[2], self.cell.matrix[:, 0], repetitions[0]) else: draw_ortho(origin, self.cell.matrix[:, 1], self.cell.matrix[:, 2], repetitions[1], repetitions[2], self.cell.matrix[:, 0]) if self.cell.active[1]: draw_gray(origin, self.cell.matrix[:, 2], self.cell.matrix[:, 0], repetitions[2], repetitions[0], self.cell.matrix[:, 1], repetitions[1]) else: draw_ortho(origin, self.cell.matrix[:, 2], self.cell.matrix[:, 0], repetitions[2], repetitions[0], self.cell.matrix[:, 1]) vb.set_specular(True) def draw(self): FrameAxes.draw(self, self.selected) GLPeriodicContainer.draw(self) # # Revalidation # def revalidate_box_list(self): if self.gl_active > 0: ##print "Compiling box list (%i): %s" % (self.box_list, self.get_name()) vb = context.application.vis_backend vb.begin_list(self.box_list) if sum(self.cell.active) > 0: self.draw_box() vb.end_list() self.box_list_valid = True def revalidate_total_list(self): if self.gl_active > 0: ##print "Compiling total list (%i): %s" % (self.total_list, self.get_name()) vb = context.application.vis_backend vb.begin_list(self.total_list) if self.visible: vb.push_name(self.draw_list) if sum(self.cell.active) == 0: if self.selected: vb.call_list(self.boundingbox_list) else: if self.selected: vb.set_bright(True) else: vb.set_bright(False) if self.box_visible: vb.call_list(self.box_list) # repeat the draw list for all the unit cell images. if self.clipping: repetitions = (self.repetitions + 2) * self.cell.active + 1 - self.cell.active else: repetitions = self.repetitions * self.cell.active + 1 - self.cell.active for position in iter_all_positions(repetitions): vb.push_matrix() t = numpy.dot( self.cell.matrix, numpy.array(position) - self.cell.active * self.clipping) vb.translate(*t) vb.call_list(self.draw_list) vb.pop_matrix() vb.pop_name() vb.end_list() self.total_list_valid = True def revalidate_bounding_box(self): GLPeriodicContainer.revalidate_bounding_box(self) FrameAxes.extend_bounding_box(self, self.bounding_box) # # Signal handlers # def on_select_chaged(self, selected): GLPeriodicContainer.on_select_chaged(self, selected) self.invalidate_box_list()
class Sphere(GLGeometricBase, ColorMixin): info = ModelObjectInfo("plugins/basic/sphere.svg") authors = [authors.toon_verstraelen] def initnonstate(self): GLGeometricBase.initnonstate(self, Translation) # # Properties # def set_radius(self, radius, init=False): self.radius = radius if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() def set_quality(self, quality, init=False): self.quality = quality if not init: self.invalidate_draw_list() properties = [ Property("radius", 0.5, lambda self: self.radius, set_radius), Property("quality", 15, lambda self: self.quality, set_quality), ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo("Geometry", (2, 2), fields.faulty.Length( label_text="Radius", attribute_name="radius", low=0.0, low_inclusive=False, )), DialogFieldInfo("Markup", (1, 3), fields.faulty.Int( label_text="Quality", attribute_name="quality", minimum=3, )) ]) # # Draw # def draw(self): GLGeometricBase.draw(self) ColorMixin.draw(self) vb = context.application.vis_backend vb.draw_sphere(self.radius, self.quality) # # Revalidation # def revalidate_bounding_box(self): GLGeometricBase.revalidate_bounding_box(self) self.bounding_box.extend_with_corners(numpy.array([ [-self.radius, -self.radius, -self.radius], [ self.radius, self.radius, self.radius] ]))
class Bond(Vector): info = ModelObjectInfo("plugins/molecular/bond.svg") authors = [authors.toon_verstraelen] # # State # def initnonstate(self): Vector.initnonstate(self) self.handlers = {} # # Properties # def set_quality(self, quality, init=False): self.quality = quality if not init: self.invalidate_draw_list() def set_bond_type(self, bond_type, init=False): self.bond_type = bond_type if not init: self.invalidate_draw_list() properties = [ Property("quality", 15, lambda self: self.quality, set_quality), Property("bond_type", BOND_SINGLE, lambda self: self.bond_type, set_bond_type) ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo("Markup", (1, 3), fields.faulty.Int( label_text="Quality", attribute_name="quality", minimum=3, )), DialogFieldInfo("Molecular", (6, 1), fields.edit.ComboBox( choices=[ (BOND_SINGLE, "Single bond"), (BOND_DOUBLE, "Double bond"), (BOND_TRIPLE, "Triple bond"), (BOND_HYBRID, "Hybrid bond"), (BOND_HYDROGEN, "Hydrogen bond"), ], label_text="Bond type", attribute_name="bond_type", )), ]) # # References # def define_target(self, reference, new_target): Vector.define_target(self, reference, new_target) self.handlers[new_target] = [ new_target.connect("on_number_changed", self.atom_property_changed), new_target.connect("on_user_radius_changed", self.atom_property_changed), new_target.connect("on_user_color_changed", self.atom_property_changed), ] def undefine_target(self, reference, old_target): Vector.undefine_target(self, reference, old_target) for handler in self.handlers[old_target]: old_target.disconnect(handler) def check_target(self, reference, target): return isinstance(target, context.application.plugins.get_node("Atom")) def atom_property_changed(self, atom): self.invalidate_boundingbox_list() self.invalidate_draw_list() # # Draw # def draw(self): Vector.draw(self) if self.length <= 0: return half_length = 0.5 * (self.end_position - self.begin_position) if half_length <= 0: return half_radius = 0.5 * (self.begin_radius + self.end_radius) begin = self.children[0].target end = self.children[1].target vb = context.application.vis_backend vb.set_color(*begin.get_color()) vb.translate(0.0, 0.0, self.begin_position) vb.draw_cone(self.begin_radius, half_radius, half_length, self.quality) vb.translate(0.0, 0.0, half_length) vb.set_color(*end.get_color()) vb.draw_cone(half_radius, self.end_radius, half_length, self.quality) # # Revalidation # def revalidate_bounding_box(self): Vector.revalidate_bounding_box(self) if self.length > 0: temp = {True: self.begin_radius, False: self.end_radius}[self.begin_radius > self.end_radius] self.bounding_box.extend_with_point(numpy.array([-temp, -temp, self.begin_position])) self.bounding_box.extend_with_point(numpy.array([temp, temp, self.end_position])) def calc_vector_dimensions(self): Vector.calc_vector_dimensions(self) begin = self.children[0].target end = self.children[1].target if self.length <= 0.0: return c = (begin.get_radius() - end.get_radius()) / self.length if abs(c) > 1: self.begin_radius = 0 self.end_radius = 0 self.begin_position = 0 self.end_position = 0 else: scale = 0.4 s = numpy.sqrt(1 - c**2) self.begin_radius = scale * begin.get_radius() * s self.end_radius = scale * end.get_radius() * s self.begin_position = scale * c * begin.get_radius() self.end_position = self.length + scale * c * end.get_radius()
class ModelObject(Node): info = ModelObjectInfo(default_action_name="EditProperties") # # State # def __init__(self, **initstate): # some debugging code #Node.count += 1 #print " PLUS => Initializing " + str(self.__class__) + " (" + str(Node.count) + ") " + hex(id(self)) Node.__init__(self) # initialisation of non state variables self.initnonstate() # further initialize the state (the properties) self.initstate(**initstate) def __del__(self): pass # some debugging code #Node.count -= 1 #print " MIN => Deleting " + str(self.__class__) + " (" + str(Node.count) + ") " + hex(id(self)) def __getstate__(self): result = {} for p in self.properties: value = p.get(self) equal = (value == p.default(self)) if isinstance(equal, numpy.ndarray): equal = equal.all() if not equal: result[p.name] = value return result def initstate(self, **initstate): for p in self.properties: if p.name in initstate: p.set(self, initstate[p.name], init=True) else: p.set(self, p.default(self), init=True) def initnonstate(self): self.references = [] # # Properties # @classmethod def class_name(cls): temp = str(cls) return temp[temp.rfind(".")+1:-2] def default_name(self): return self.class_name() def get_name(self): return self.name def set_name(self, name, init=False): self.name = name def set_extra(self, extra, init=False): self.extra = extra properties = [ Property("name", default_name, get_name, set_name), Property("extra", {}, (lambda self: self.extra), set_extra), ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo("Basic", (0, 0), fields.faulty.Name( label_text="Name", attribute_name="name", )), ]) # # Tree # def delete_referents(self): while len(self.references) > 0: primitive.Delete(self.references[0].parent) #for reference in copy.copy(self.references): # if reference.model is not None: # #print "Deleting Referent %s(%i)" % (reference.parent.get_name(), id(reference.parent)) # primitive.Delete(reference.parent) def move(self, new_parent, index=-1): if (index > 0) and (self.parent == new_parent) and index > self.get_index(): index -= 1 self.parent.remove(self) new_parent.add(self, index) self.emit("on-move") #print "EMIT: on-move" # # Flags # def get_fixed(self): return (self.model is not None) and (self.parent is None)
class GLMixin(gobject.GObject): __metaclass__ = NodeClass double_sided = False # # State # def initnonstate(self): self.gl_active = False self.connect("on-selected", self.on_select_changed) self.connect("on-deselected", self.on_select_changed) # # Properties # def set_visible(self, visible, init=False): if init: self.visible = visible elif self.visible != visible: self.visible = visible self.invalidate_total_list() properties = [ Property("visible", True, lambda self: self.visible, set_visible) ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Markup", (1, 2), fields.edit.CheckButton( label_text="Visible (also hides children)", attribute_name="visible", )), DialogFieldInfo( "Basic", (0, 3), fields.read.BBox( label_text="Bounding box", attribute_name="bounding_box", )), ]) # # OpenGL # def initialize_gl(self): assert not self.gl_active vb = context.application.vis_backend self.gl_active = True self.bounding_box = BoundingBox() self.draw_list = vb.create_list(self) self.boundingbox_list = vb.create_list() self.total_list = vb.create_list() ##print "Created lists (%i, %i, %i): %s" % (self.draw_list, self.boundingbox_list, self.total_list, self.get_name()) self.draw_list_valid = True self.boundingbox_list_valid = True self.total_list_valid = True self.invalidate_all_lists() if isinstance(self.parent, GLMixin): self.parent.invalidate_all_lists() def cleanup_gl(self): assert self.gl_active self.gl_active = False vb = context.application.vis_backend ##print "Deleting lists (%i, %i, %i): %s" % (self.draw_list, self.boundingbox_list, self.total_list, self.get_name()) vb.delete_list(self.draw_list) vb.delete_list(self.boundingbox_list) vb.delete_list(self.total_list) del self.bounding_box del self.draw_list del self.boundingbox_list del self.total_list del self.draw_list_valid del self.boundingbox_list_valid del self.total_list_valid if isinstance(self.parent, GLMixin): self.parent.invalidate_all_lists() # # Invalidation # def invalidate_draw_list(self): if self.gl_active and self.draw_list_valid: self.draw_list_valid = False context.application.main.drawing_area.queue_draw() context.application.scene.add_revalidation( self.revalidate_draw_list) self.emit("on-draw-list-invalidated") ##print "EMIT %s: on-draw-list-invalidated" % self.get_name() if isinstance(self.parent, GLMixin): self.parent.invalidate_boundingbox_list() def invalidate_boundingbox_list(self): ##print "TRY: %s: on-boundingbox-list-invalidated" % self.get_name() if self.gl_active and self.boundingbox_list_valid: self.boundingbox_list_valid = False context.application.main.drawing_area.queue_draw() context.application.scene.add_revalidation( self.revalidate_boundingbox_list) self.emit("on-boundingbox-list-invalidated") ##print "EMIT %s: on-boundingbox-list-invalidated" % self.get_name() if isinstance(self.parent, GLMixin): self.parent.invalidate_boundingbox_list() def invalidate_total_list(self): if self.gl_active and self.total_list_valid: self.total_list_valid = False context.application.main.drawing_area.queue_draw() context.application.scene.add_revalidation( self.revalidate_total_list) self.emit("on-total-list-invalidated") ##print "EMIT %s: on-total-list-invalidated" % self.get_name() if isinstance(self.parent, GLMixin): self.parent.invalidate_boundingbox_list() def invalidate_all_lists(self): self.invalidate_total_list() self.invalidate_boundingbox_list() self.invalidate_draw_list() # # Revalidation # def revalidate_draw_list(self): if self.gl_active: vb = context.application.vis_backend ##print "Compiling draw list (%i): %s" % (self.draw_list, self.get_name()) vb.begin_list(self.draw_list) self.prepare_draw() self.draw() self.finish_draw() vb.end_list() self.draw_list_valid = True def revalidate_boundingbox_list(self): if self.gl_active: vb = context.application.vis_backend ##print "Compiling selection list (%i): %s" % (self.boundingbox_list, self.get_name()) vb.begin_list(self.boundingbox_list) self.revalidate_bounding_box() self.bounding_box.draw() vb.end_list() self.boundingbox_list_valid = True def revalidate_bounding_box(self): self.bounding_box.clear() def revalidate_total_list(self): if self.gl_active: vb = context.application.vis_backend ##print "Compiling total list (%i): %s" % (self.total_list, self.get_name()) vb.begin_list(self.total_list) if self.visible: vb.push_name(self.draw_list) self.draw_selection() vb.call_list(self.draw_list) vb.pop_name() vb.end_list() self.total_list_valid = True # # Draw # def draw_selection(self): vb = context.application.vis_backend if self.selected: vb.set_bright(True) else: vb.set_bright(False) def call_list(self): ##print "Executing total list (%i): %s" % (self.total_list, self.get_name()) context.application.vis_backend.call_list(self.total_list) def prepare_draw(self): pass def draw(self): pass def finish_draw(self): pass # # Frame # def get_bounding_box_in_parent_frame(self): return self.bounding_box def get_absolute_frame(self): return self.get_absolute_parentframe() def get_absolute_parentframe(self): if not isinstance(self.parent, GLMixin): return Complete.identity() else: return self.parent.get_absolute_frame() def get_frame_up_to(self, upper_parent): if (upper_parent == self) or (self.parent == upper_parent): return Complete.identity() else: return self.get_parentframe_up_to(upper_parent) def get_parentframe_up_to(self, upper_parent): if not isinstance(self.parent, GLMixin): assert upper_parent is None, "upper_parent must be (an indirect) parent of self." return Complete.identity() elif self.parent == upper_parent: return Complete.identity() else: return self.parent.get_frame_up_to(upper_parent) def get_frame_relative_to(self, other): common = common_parent([self, other]) return other.get_frame_up_to(common).inv * self.get_frame_up_to(common) # # Signal handlers # def on_select_changed(self, foo): self.invalidate_total_list()
class Spring(Vector, ColorMixin): info = ModelObjectInfo("plugins/builder/spring.svg") authors = [authors.toon_verstraelen] # # Properties # def set_radius(self, radius, init=False): self.radius = radius if not init: self.invalidate_draw_list() def set_quality(self, quality, init=False): self.quality = quality if not init: self.invalidate_draw_list() def set_rest_length(self, rest_length, init=False): self.rest_length = rest_length if not init: self.invalidate_draw_list() properties = [ Property("radius", 0.5, lambda self: self.radius, set_radius), Property("quality", 15, lambda self: self.quality, set_quality), Property("rest_length", 0.0, lambda self: self.rest_length, set_rest_length), ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Geometry", (2, 2), fields.faulty.Length( label_text="Radius", attribute_name="radius", low=0.0, low_inclusive=False, )), DialogFieldInfo( "Markup", (1, 3), fields.faulty.Int( label_text="Quality", attribute_name="quality", minimum=3, )), DialogFieldInfo( "Basic", (0, 8), fields.faulty.Length( label_text="Rest length", attribute_name="rest_length", low=0.0, low_inclusive=True, )), ]) # # Draw # def draw(self): Vector.draw(self) ColorMixin.draw(self) vb = context.application.vis_backend if self.length > self.rest_length: l_cyl = self.rest_length l_cone = 0.5 * (self.length - l_cyl) if l_cone > 0: vb.draw_cone(self.radius, 0.0, l_cone, self.quality) vb.translate(0.0, 0.0, l_cone) if l_cyl > 0: vb.draw_cone(0.5 * self.radius, 0.5 * self.radius, l_cyl, self.quality) vb.translate(0.0, 0.0, l_cyl) if l_cone > 0: vb.draw_cone(0.0, self.radius, l_cone, self.quality) else: l_cyl = self.length l_cone = 0.5 * (self.rest_length - self.length) if l_cone > 0: vb.translate(0.0, 0.0, -l_cone) vb.draw_cone(0.0, self.radius, l_cone, self.quality) vb.translate(0.0, 0.0, l_cone) if l_cyl > 0: vb.draw_cylinder(0.5 * self.radius, l_cyl, self.quality) vb.translate(0.0, 0.0, l_cyl) if l_cone > 0: vb.draw_cone(self.radius, 0.0, l_cone, self.quality) # # Revalidation # def revalidate_bounding_box(self): Vector.revalidate_bounding_box(self) if self.length > 0: self.bounding_box.extend_with_point( numpy.array([-self.radius, -self.radius, 0])) self.bounding_box.extend_with_point( numpy.array([self.radius, self.radius, self.length]))
class FrameAxes(object): __metaclass__ = NodeClass # # Properties # def set_axis_thickness(self, axis_thickness, init=False): self.axis_thickness = axis_thickness if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() def set_axis_length(self, axis_length, init=False): self.axis_length = axis_length if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() def set_axes_visible(self, axes_visible, init=False): self.axes_visible = axes_visible if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() properties = [ Property("axis_thickness", 0.1 * angstrom, lambda self: self.axis_thickness, set_axis_thickness), Property("axis_length", 1.0 * angstrom, lambda self: self.axis_length, set_axis_length), Property("axes_visible", True, lambda self: self.axes_visible, set_axes_visible) ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Geometry", (2, 0), fields.faulty.Length( label_text="Axis thickness", attribute_name="axis_thickness", low=0.0, low_inclusive=False, )), DialogFieldInfo( "Geometry", (2, 1), fields.faulty.Length( label_text="Axes length", attribute_name="axis_length", low=0.0, low_inclusive=False, )), DialogFieldInfo( "Markup", (1, 4), fields.edit.CheckButton( label_text="Show frame axes", attribute_name="axes_visible", )), ]) # # Draw # def draw(self, light): if self.axes_visible: col = {True: 2.0, False: 1.2}[light] sat = {True: 0.2, False: 0.1}[light] vb = context.application.vis_backend vb.push_matrix() # x-axis vb.set_color(col, sat, sat) draw_axis_spike(self.axis_thickness, self.axis_length) # y-axis vb.rotate(120, 1.0, 1.0, 1.0) vb.set_color(sat, col, sat) draw_axis_spike(self.axis_thickness, self.axis_length) # z-axis vb.rotate(120, 1.0, 1.0, 1.0) vb.set_color(sat, sat, col) draw_axis_spike(self.axis_thickness, self.axis_length) vb.pop_matrix() # # Revalidation # def extend_bounding_box(self, bounding_box): bounding_box.extend_with_corners( numpy.array([[ -self.axis_thickness, -self.axis_thickness, -self.axis_thickness ], [self.axis_length, self.axis_length, self.axis_length]]))
class Atom(GLGeometricBase, UserColorMixin): info = ModelObjectInfo("plugins/molecular/atom.svg") authors = [authors.toon_verstraelen] # # State # def initnonstate(self): GLGeometricBase.initnonstate(self, Translation) # # Properties # def set_user_radius(self, user_radius, init=False): self.user_radius = user_radius if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() def set_quality(self, quality, init=False): self.quality = quality if not init: self.invalidate_draw_list() def set_number(self, number, init=False): self.number = number atom_info = periodic[number] if atom_info.vdw_radius is not None: self.default_radius = atom_info.vdw_radius * 0.2 else: self.default_radius = 1.0 color = [atom_info.red, atom_info.green, atom_info.blue, 1.0] if None in color: self.default_color = numpy.array([0.7, 0.7, 0.7, 1.0], float) else: self.default_color = numpy.array(color, float) if not init: self.invalidate_draw_list() self.invalidate_boundingbox_list() properties = [ Property("user_radius", Undefined(0.5), lambda self: self.user_radius, set_user_radius, signal=True), Property("quality", 15, lambda self: self.quality, set_quality), Property("number", 6, lambda self: self.number, set_number, signal=True), ] # # Dialog fields (see action EditProperties) # dialog_fields = set([ DialogFieldInfo( "Geometry", (2, 9), fields.optional.CheckOptional( fields.faulty.Length( label_text="User defined radius", attribute_name="user_radius", low=0.0, low_inclusive=False, ))), DialogFieldInfo( "Markup", (1, 3), fields.faulty.Int( label_text="Quality", attribute_name="quality", minimum=3, )), DialogFieldInfo( "Molecular", (6, 0), fields.edit.Element( attribute_name="number", show_popup=False, )), ]) # # Draw # def get_radius(self): if isinstance(self.user_radius, Undefined): return self.default_radius else: return self.user_radius def draw(self): GLGeometricBase.draw(self) UserColorMixin.draw(self) vb = context.application.vis_backend vb.draw_sphere(self.get_radius(), self.quality) # # Revalidation # def revalidate_bounding_box(self): GLGeometricBase.revalidate_bounding_box(self) radius = self.get_radius() self.bounding_box.extend_with_corners( numpy.array([[-radius, -radius, -radius], [radius, radius, radius]])) # # Tools # def num_bonds(self): Bond = context.application.plugins.get_node("Bond") return sum( isinstance(reference.parent, Bond) for reference in self.references) def iter_bonds(self): Bond = context.application.plugins.get_node("Bond") for reference in self.references: referent = reference.parent if isinstance(referent, Bond): yield referent def iter_neighbors(self): for bond in self.iter_bonds(): first = bond.children[0].target if first == self: neighbor = bond.children[1].target else: neighbor = first if isinstance(neighbor, Atom): yield neighbor