def do_molgrid(self): self.molgrid = Grid.from_prefix("molecule", self.work) if self.molgrid is not None: self.molgrid.weights = self.molgrid.load("weights") else: # we have to generate a new grid. The grid is constructed taking # into account the following considerations: # 1) Grid points within the cusp region are discarded # 2) The rest of the molecular and surrounding volume is sampled # with spherical grids centered on the atoms. Around each atom, # 'scale_steps' of shells are placed with lebedev grid points # (num_lebedev). The lebedev weights are used in the fit to # avoid preferential directions within one shell. # 3) The radii of the shells start from scale_min*(cusp_radius+0.2) # and go up to scale_max*(cusp_radius+0.2). # 4) Each shell will be randomly rotated around the atom to avoid # global preferential directions in the grid. # 5) The default parameters for the grid should be sufficient for # sane ESP fitting. The ESP cost function should discard points # with a density larger than a threshold, i.e. 1e-5 a.u. A # gradual transition between included and discarded points around # this threshold will improve the quality of the fit. lebedev_xyz, lebedev_weights = get_lebedev_grid(50) self.do_noble_radii() scale_min = 1.5 scale_max = 30.0 scale_steps = 30 scale_factor = (scale_max/scale_min)**(1.0/(scale_steps-1)) scales = scale_min*scale_factor**numpy.arange(scale_steps) points = [] weights = [] pb = log.pb("Constructing molecular grid", scale_steps) for scale in scales: pb() radii = scale*self.noble_radii for i in xrange(self.molecule.size): rot = Rotation.random() for j in xrange(len(lebedev_xyz)): my_point = radii[i]*numpy.dot(rot.r, lebedev_xyz[j]) + self.molecule.coordinates[i] distances = numpy.sqrt(((self.molecule.coordinates - my_point)**2).sum(axis=1)) if (distances < scales[0]*self.noble_radii).any(): continue points.append(my_point) weights.append(lebedev_weights[j]) pb() points = numpy.array(points) weights = numpy.array(weights) self.molgrid = Grid("molecule", self.work, points) self.molgrid.weights = weights self.molgrid.dump("weights", weights)
def ask_parameters(self): cache = context.application.cache nodes = cache.nodes last = cache.last next_to_last = cache.next_to_last if isinstance(last, Vector): if (len(nodes) >= 2) and isinstance(next_to_last, Vector): parent = nodes[-2].parent b1 = last.children[0].translation_relative_to(parent) e1 = last.children[1].translation_relative_to(parent) b2 = next_to_last.children[0].translation_relative_to(parent) e2 = next_to_last.children[1].translation_relative_to(parent) if (b1 is not None) and (e1 is not None) and (b2 is not None) and (e2 is not None): angle = compute_angle(e1 - b1, e2 - b2) axis = numpy.cross(e1 - b1, e2 - b2) self.parameters.center = Translation(0.5*(b1+b2)) self.parameters.rotation = Rotation.from_properties(angle, axis, False) else: parent = next_to_last.parent b = last.children[0].translation_relative_to(parent) e = last.children[1].translation_relative_to(parent) if (b is not None) and (e is not None): self.parameters.center = Translation(b) self.parameters.rotation = Rotation.from_properties(numpy.pi*0.25, e - b, False) elif isinstance(last, GLTransformationMixin) and isinstance(last.transformation, Translation): parent = last.parent self.parameters.center = Translation(last.get_frame_relative_to(parent).t) self.parameters.rotation = Rotation.identity() else: self.parameters.center = Translation(calculate_center(cache.translations)) if self.parameters_dialog.run(self.parameters) != gtk.RESPONSE_OK: self.parameters.clear()
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()
def do_rotation(self, drawing_area, rotation_angle, rotation_axis): camera = context.application.camera # Note that the camera must be rotated in the opposite direction to # let the scene rotate in the right direction. rotation = Rotation.from_properties(-rotation_angle, rotation_axis, False) camera.rotation = camera.rotation * rotation drawing_area.queue_draw()
def fn(): context.application.model.file_open("test/input/precursor.zml") context.application.main.select_nodes(context.application.model.universe.children) ScanForConnections = context.application.plugins.get_action("ScanForConnections") rotation2 = Rotation.from_properties(1*numpy.pi, [0, 1, 0], False) parameters = ScanForConnections.default_parameters() parameters.connect_description1 = ( Expression("isinstance(node, Atom) and node.number == 8 and node.num_bonds() == 1"), Expression("node.get_radius()"), ) parameters.repulse_description1 = ( Expression("isinstance(node, Atom) and (node.number == 8 or node.number == 14)"), Expression("node.get_radius()*1.5"), ) parameters.action_radius = 4*angstrom parameters.hit_tolerance = 0.1*angstrom parameters.rotation2 = rotation2 assert ScanForConnections.analyze_selection(parameters) ScanForConnections(parameters) # Try to save the result to file an open it again. context.application.model.file_save("test/output/tmp.zml") FileNew = context.application.plugins.get_action("FileNew") FileNew() context.application.model.file_open("test/output/tmp.zml") # Do some consistency tests on the connection scanner results: for quality, transformation, pairs, inverse_pairs in context.application.model.folder.children[0].get_connections(): assert_arrays_almost_equal(transformation.r, rotation2.r) assert len(pairs) >= 2 if len(inverse_pairs) > 0: assert len(pairs) == len(inverse_pairs)
def fn(): FileNew = context.application.plugins.get_action("FileNew") FileNew() context.application.main.select_nodes([context.application.model.universe]) Frame = context.application.plugins.get_node("Frame") frame = Frame(transformation=Translation(numpy.random.uniform(-5, 5, 3))) context.application.model.universe.add(frame) frame.set_transformation(Rotation.random())
def reset(self): config = context.application.configuration self.rotation_center = Translation.identity() self.rotation = Rotation.identity() self.eye = Translation([0, 0, config.viewer_distance]) self.opening_angle = config.opening_angle self.window_size = config.window_size self.window_depth = config.window_depth
def fn(): context.application.model.file_open("test/input/core_objects.zml") context.application.main.toggle_selection(context.application.model.universe.children[3], on=True) parameters = Parameters() parameters.rotation = Rotation.from_properties(1.0, numpy.array([0.1, 1.4, 0.3]), False) RotateDialog = context.application.plugins.get_action("RotateDialog") assert RotateDialog.analyze_selection(parameters) RotateDialog(parameters)
def reset(self): config = context.application.configuration self.rotation_center = Translation.identity() self.rotation = Rotation.identity() self.eye = Translation([0,0,config.viewer_distance]) self.opening_angle = config.opening_angle self.window_size = config.window_size self.window_depth = config.window_depth
def do_rotation(self, rotation_angle, rotation_axis): rotation_axis = numpy.dot(self.eye_rotation, rotation_axis) if self.rotation_axis is not None: if numpy.dot(self.rotation_axis, rotation_axis) > 0: rotation_axis = self.rotation_axis else: rotation_axis = -self.rotation_axis rotation = Rotation.from_properties(rotation_angle, rotation_axis, False) self.victim.set_transformation(self.rotation_center * (rotation * (self.rotation_center.inv * self.victim.transformation))) context.application.main.drawing_area.queue_draw() self.changed = True
def generate_points(self, center, rs): points = numpy.zeros((self.num_lebedev*len(rs),3), float) start = 0 for r in rs: end = start + self.num_lebedev xyz = self.lebedev_xyz if self.do_random: rot = Rotation.random() xyz = numpy.dot(xyz,rot.r) points[start:end] = r*xyz+center start = end return points
def generate_points(self, center, rs): points = numpy.zeros((self.num_lebedev * len(rs), 3), float) start = 0 for r in rs: end = start + self.num_lebedev xyz = self.lebedev_xyz if self.do_random: rot = Rotation.random() xyz = numpy.dot(xyz, rot.r) points[start:end] = r * xyz + center start = end return points
def ask_parameters(self): cache = context.application.cache nodes = cache.nodes last = cache.last next_to_last = cache.next_to_last if isinstance(last, Vector): if (len(nodes) >= 2) and isinstance(next_to_last, Vector): parent = nodes[-2].parent b1 = last.children[0].translation_relative_to(parent) e1 = last.children[1].translation_relative_to(parent) b2 = next_to_last.children[0].translation_relative_to(parent) e2 = next_to_last.children[1].translation_relative_to(parent) if (b1 is not None) and (e1 is not None) and ( b2 is not None) and (e2 is not None): angle = compute_angle(e1 - b1, e2 - b2) axis = numpy.cross(e1 - b1, e2 - b2) self.parameters.center = Translation(0.5 * (b1 + b2)) self.parameters.rotation = Rotation.from_properties( angle, axis, False) else: parent = next_to_last.parent b = last.children[0].translation_relative_to(parent) e = last.children[1].translation_relative_to(parent) if (b is not None) and (e is not None): self.parameters.center = Translation(b) self.parameters.rotation = Rotation.from_properties( numpy.pi * 0.25, e - b, False) elif isinstance(last, GLTransformationMixin) and isinstance( last.transformation, Translation): parent = last.parent self.parameters.center = Translation( last.get_frame_relative_to(parent).t) self.parameters.rotation = Rotation.identity() else: self.parameters.center = Translation( calculate_center(cache.translations)) if self.parameters_dialog.run(self.parameters) != gtk.RESPONSE_OK: self.parameters.clear()
def test_pair_ego_precursor_rot(): geometry = Geometry(*get_precursor_model()) rot = Rotation.from_properties(-0.5*np.pi, [-1, 1, 0], False) sender = Sender() scanner = PairScanner( send=sender, geometry1=geometry, geometry2=None, action_radius=10.0, hit_tolerance=0.1, rotation2=rot, ) scanner.run()
def do_rotation(self, rotation_angle, rotation_axis): rotation_axis = numpy.dot(self.eye_rotation, rotation_axis) if self.rotation_axis is not None: if numpy.dot(self.rotation_axis, rotation_axis) > 0: rotation_axis = self.rotation_axis else: rotation_axis = -self.rotation_axis rotation = Rotation.from_properties(rotation_angle, rotation_axis, False) self.victim.set_transformation( self.rotation_center * (rotation * (self.rotation_center.inv * self.victim.transformation))) context.application.main.drawing_area.queue_draw() self.changed = True
def test_pair_simple_rot(): geometry1 = Geometry(*get_simple_model1()) geometry2 = Geometry(*get_simple_model2()) rot = Rotation.from_properties(-0.5*np.pi, [0, 1, 0], False) sender = Sender() scanner = PairScanner( send=sender, geometry1=geometry1, geometry2=geometry2, action_radius=10.0, hit_tolerance=0.1, rotation2=rot, ) scanner.run() assert len(sender.connections) == 2
def default_parameters(cls): rotation2 = Rotation.from_properties(0.0, [1, 0, 0], False) result = Parameters() result.connect_description1 = (Expression("True"), Expression("node.get_radius()")) result.repulse_description1 = (Expression("True"), Expression("node.get_radius()")) result.connect_description2 = (Expression("True"), Expression("node.get_radius()")) result.repulse_description2 = (Expression("True"), Expression("node.get_radius()")) result.action_radius = 7*angstrom result.distance_tolerance = 0.1*angstrom result.hit_tolerance = 0.1*angstrom result.allow_inversions = True result.minimum_triangle_size = 0.1*angstrom result.rotation_tolerance = 0.05 result.rotation2 = Undefined(rotation2) return result
def compute_transformations(self): Scanner.compute_transformations(self) if self.allow_inversions: # derive the connections with inversion rotations, based on those # without inversion rotations new_connections = [] maximum = len(self.connections) for progress, connection in enumerate(self.connections): self.send(ProgressMessage("mirror", progress, maximum)) new = copy.deepcopy(connection) mirror = (Translation(connection.triangle1.center) * Rotation.from_properties( numpy.pi, connection.triangle1.normal, True) * Translation(-connection.triangle1.center)) new.set_transformation(mirror * new.transformation) new_connections.append(new) self.connections.extend(new_connections) self.send(ProgressMessage("mirror", maximum, maximum))
def compute_transformations(self): Scanner.compute_transformations(self) if self.allow_inversions: # derive the connections with inversion rotations, based on those # without inversion rotations new_connections = [] maximum = len(self.connections) for progress, connection in enumerate(self.connections): self.send(ProgressMessage("mirror", progress, maximum)) new = copy.deepcopy(connection) mirror = ( Translation(connection.triangle1.center) * Rotation.from_properties(numpy.pi, connection.triangle1.normal, True) * Translation(-connection.triangle1.center) ) new.set_transformation(mirror * new.transformation) new_connections.append(new) self.connections.extend(new_connections) self.send(ProgressMessage("mirror", maximum, maximum))
def add_hydrogens(atom): existing_bonds = list(atom.iter_bonds()) bond_length = bonds.get_length(atom.number, 1, BOND_SINGLE) num_hydrogens = self.parameters.num_hydrogens if len(existing_bonds) == 0: t = atom.transformation.t + numpy.array([0, bond_length, 0]) H = Atom(name="auto H", number=1, transformation=Translation(t)) primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) existing_bonds.append(bond) num_hydrogens -= 1 main_direction = numpy.zeros(3, float) for bond in existing_bonds: shortest_vector = bond.shortest_vector_relative_to(atom.parent) if bond.children[1].target == atom: shortest_vector *= -1 main_direction += shortest_vector main_direction /= numpy.linalg.norm(main_direction) normal = random_orthonormal(main_direction) rotation = Rotation.from_properties( 2 * numpy.pi / float(num_hydrogens), main_direction, False) h_pos = bond_length * ( main_direction * numpy.cos(self.parameters.valence_angle) + normal * numpy.sin(self.parameters.valence_angle)) for i in range(num_hydrogens): t = atom.transformation.t + h_pos H = Atom(name="auto H", number=1, transformation=Translation(t)) primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) h_pos = rotation * h_pos
def fn(): context.application.model.file_open("test/input/precursor.zml") context.application.main.select_nodes( context.application.model.universe.children) ScanForConnections = context.application.plugins.get_action( "ScanForConnections") rotation2 = Rotation.from_properties(1 * numpy.pi, [0, 1, 0], False) parameters = ScanForConnections.default_parameters() parameters.connect_description1 = ( Expression( "isinstance(node, Atom) and node.number == 8 and node.num_bonds() == 1" ), Expression("node.get_radius()"), ) parameters.repulse_description1 = ( Expression( "isinstance(node, Atom) and (node.number == 8 or node.number == 14)" ), Expression("node.get_radius()*1.5"), ) parameters.action_radius = 4 * angstrom parameters.hit_tolerance = 0.1 * angstrom parameters.rotation2 = rotation2 assert ScanForConnections.analyze_selection(parameters) ScanForConnections(parameters) # Try to save the result to file an open it again. context.application.model.file_save("test/output/tmp.zml") FileNew = context.application.plugins.get_action("FileNew") FileNew() context.application.model.file_open("test/output/tmp.zml") # Do some consistency tests on the connection scanner results: for quality, transformation, pairs, inverse_pairs in context.application.model.folder.children[ 0].get_connections(): assert_arrays_almost_equal(transformation.r, rotation2.r) assert len(pairs) >= 2 if len(inverse_pairs) > 0: assert len(pairs) == len(inverse_pairs)
def default_parameters(cls): rotation2 = Rotation.from_properties(0.0, [1, 0, 0], False) result = Parameters() result.connect_description1 = (Expression("True"), Expression("node.get_radius()")) result.repulse_description1 = (Expression("True"), Expression("node.get_radius()")) result.connect_description2 = (Expression("True"), Expression("node.get_radius()")) result.repulse_description2 = (Expression("True"), Expression("node.get_radius()")) result.action_radius = 7 * angstrom result.distance_tolerance = 0.1 * angstrom result.hit_tolerance = 0.1 * angstrom result.allow_inversions = True result.minimum_triangle_size = 0.1 * angstrom result.rotation_tolerance = 0.05 result.rotation2 = Undefined(rotation2) return result
def add_hydrogens(atom): existing_bonds = list(atom.iter_bonds()) bond_length = bonds.get_length(atom.number, 1, BOND_SINGLE) num_hydrogens = self.parameters.num_hydrogens if len(existing_bonds) == 0: t = atom.transformation.t + numpy.array([0,bond_length,0]) H = Atom(name="auto H", number=1, transformation=Translation(t)) primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) existing_bonds.append(bond) num_hydrogens -= 1 main_direction = numpy.zeros(3, float) for bond in existing_bonds: shortest_vector = bond.shortest_vector_relative_to(atom.parent) if bond.children[1].target == atom: shortest_vector *= -1 main_direction += shortest_vector main_direction /= numpy.linalg.norm(main_direction) normal = random_orthonormal(main_direction) rotation = Rotation.from_properties(2*numpy.pi / float(num_hydrogens), main_direction, False) h_pos = bond_length*( main_direction*numpy.cos(self.parameters.valence_angle) + normal*numpy.sin(self.parameters.valence_angle) ) for i in range(num_hydrogens): t = atom.transformation.t + h_pos H = Atom(name="auto H", number=1, transformation=Translation(t)) primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) h_pos = rotation * h_pos
def do(self): cache = context.application.cache # Translate where possible, if necessary translated_nodes = cache.translated_nodes if len(translated_nodes) > 0: if isinstance(cache.last, GLTransformationMixin) and \ isinstance(cache.last.transformation, Translation): absolute_inversion_center = cache.last.get_absolute_frame().t else: absolute_inversion_center = numpy.zeros(3, float) victims_by_parent = list_by_parent(cache.transformed_nodes) for parent, victims in victims_by_parent.iteritems(): local_inversion_center = parent.get_absolute_frame( ).inv * absolute_inversion_center for victim in victims: translation = Translation( 2 * (local_inversion_center - victim.transformation.t)) primitive.Transform(victim, translation) # Apply an inversion rotation where possible inversion = Rotation(-numpy.identity(3, float)) for victim in cache.rotated_nodes: primitive.Transform(victim, inversion, after=False)
def replace(self, gl_object): if not gl_object.get_fixed(): new = self.get_new(gl_object.transformation.t) # select a target object, i.e. the one the will be connected with # the bonds/vectors/... of the replaced object if(self.current_object == "Fragment"): # fragments are inserted as frames # take atom with index 1 as target target_object = new.children[1] else: target_object = new # check if all connections to the replaced object are applicable # to the new object. if not, then do not perform the replacement # and return early. for reference in gl_object.references[::-1]: if not reference.check_target(target_object): return # add the new object parent = gl_object.parent primitive.Add(new, parent) if(self.current_object == "Fragment"): # Fix the rotation and translation of the molecular fragment. # Rotation Bond = context.application.plugins.get_node("Bond") if len(gl_object.references) == 1 and isinstance(gl_object.references[0].parent, Bond): bond1 = gl_object.references[0].parent direction1 = bond1.shortest_vector_relative_to(parent) if bond1.children[0].target != gl_object: direction1 *= -1 bond2 = new.children[0].references[0].parent direction2 = bond2.shortest_vector_relative_to(parent) if bond2.children[0].target != target_object: direction2 *= -1 axis = numpy.cross(direction2, direction1) if numpy.linalg.norm(axis) < 1e-8: axis = random_orthonormal(direction1) angle = compute_angle(direction1, direction2) rotation = Rotation.from_properties(angle, axis, False) primitive.Transform(new, rotation) else: bond1 = None # Tranlsation pos_old = new.children[1].get_frame_relative_to(parent).t pos_new = gl_object.transformation.t translation = Translation(pos_new - pos_old) primitive.Transform(new, translation) if bond1 != None: # bond length old_length = numpy.linalg.norm(direction1) new_length = bonds.get_length(new.children[1].number, bond1.get_neighbor(gl_object).number) translation = Translation(-direction1/old_length*(new_length-old_length)) primitive.Transform(new, translation) # let the references to the replaced object point to the new object for reference in gl_object.references[::-1]: try: primitive.SetTarget(reference, target_object) except primitive.PrimitiveError: primitive.Delete(reference.parent) # delete the replaced object primitive.Delete(gl_object) if(self.current_object == "Fragment"): # Delete the first atom in the fragment primitive.Delete(new.children[0]) # Unframe the fragment UnframeAbsolute = context.application.plugins.get_action("UnframeAbsolute") UnframeAbsolute([new])
def do(self): cache = context.application.cache geometry_nodes = [[], []] def get_parameters(node, point_type, geometry_index, filter_expression, radius_expression): template = "An exception occured in the %%s expression\nfor the %s points of geometry %i." % (point_type, geometry_index+1) try: is_type = filter_expression(node) except Exception: raise UserError(template % "filter") if is_type: try: radius = radius_expression(node) except Exception: raise UserError(template % "radius") geometry_nodes[geometry_index].append(node) return True, radius else: return False, None def read_geometry(frame, geometry_index, connect_description, repulse_description): coordinates = [] connect_masks = [] radii = [] for child in frame.children: if isinstance(child, GLTransformationMixin) and \ isinstance(child.transformation, Translation): is_connect, radius = get_parameters( child, "connecting", geometry_index, *connect_description ) if is_connect: coordinates.append(child.transformation.t) connect_masks.append(True) radii.append(radius) else: is_repulse, radius = get_parameters( child, "repulsive", geometry_index, *repulse_description ) if is_repulse: coordinates.append(child.transformation.t) connect_masks.append(False) radii.append(radius) return Geometry( numpy.array(coordinates, float), numpy.array(connect_masks, bool), numpy.array(radii, float), ) inp = {} inp["geometry1"] = read_geometry(cache.nodes[0], 0, self.parameters.connect_description1, self.parameters.repulse_description1) if len(cache.nodes) == 2: inp["geometry2"] = read_geometry(cache.nodes[1], 1, self.parameters.connect_description2, self.parameters.repulse_description2) else: inp["geometry2"] = None inp["action_radius"] = self.parameters.action_radius inp["hit_tolerance"] = self.parameters.hit_tolerance if not isinstance(self.parameters.allow_inversions, Undefined): inp["allow_rotations"] = True inp["allow_inversions"] = self.parameters.allow_inversions inp["minimum_triangle_area"] = self.parameters.minimum_triangle_size**2 else: inp["allow_rotations"] = False if isinstance(self.parameters.rotation2, Undefined): inp["rotation2"] = Rotation.identity() else: inp["rotation2"] = self.parameters.rotation2 if inp["allow_rotations"]: connections = self.triangle_report_dialog.run(inp) else: connections = self.pair_report_dialog.run(inp) if connections is not None and len(connections) > 0: if len(cache.nodes) == 1: frame1 = cache.nodes[0] Duplicate = context.application.plugins.get_action("Duplicate") Duplicate() frame2 = cache.nodes[0] geometry_nodes[1] = geometry_nodes[0] else: frame1, frame2 = cache.nodes conscan_results = ConscanResults( targets=[frame1, frame2], connections=[( float(connection.quality), connection.transformation, [ (geometry_nodes[0][first].get_index(), geometry_nodes[1][second].get_index()) for first, second in connection.pairs ], [ (geometry_nodes[0][second].get_index(), geometry_nodes[1][first].get_index()) for first, second in connection.pairs if connection.invertible ], ) for connection in connections], ) primitive.Add(conscan_results, context.application.model.folder)
def add_hydrogens(atom): existing_bonds = list(atom.iter_bonds()) num_bonds = len(existing_bonds) bond_length = bonds.get_length(atom.number, 1, BOND_SINGLE) if num_bonds == 0: t = atom.transformation.t + numpy.array([0, bond_length, 0]) H = Atom(name="auto H", number=1, transformation=Translation(t)) primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) existing_bonds.append(bond) num_bonds = 1 used_valence = 0 oposite_direction = numpy.zeros(3, float) for bond in existing_bonds: shortest_vector = bond.shortest_vector_relative_to(atom.parent) if bond.children[1].target == atom: shortest_vector *= -1 oposite_direction -= shortest_vector if bond.bond_type == BOND_SINGLE: used_valence += 1 elif bond.bond_type == BOND_DOUBLE: used_valence += 2 elif bond.bond_type == BOND_TRIPLE: used_valence += 3 oposite_direction /= numpy.linalg.norm(oposite_direction) num_hydrogens = valence_el( atom.number) - 2 * lone_pairs(atom.number) - used_valence if num_hydrogens <= 0: return hybride_count = num_hydrogens + lone_pairs( atom.number) + num_bonds - (used_valence - num_bonds) num_sites = num_hydrogens + lone_pairs(atom.number) rotation = Rotation.from_properties( 2 * numpy.pi / float(num_sites), oposite_direction, False) opening_key = (hybride_count, num_sites) opening_angle = self.opening_angles.get(opening_key) if opening_angle is None: return if num_bonds == 1: first_bond = existing_bonds[0] other_atom = first_bond.children[0].target if other_atom == atom: other_atom = first_bond.children[1].target other_bonds = [ bond for bond in other_atom.iter_bonds() if bond != first_bond ] if len(other_bonds) > 0: normal = other_bonds[0].shortest_vector_relative_to( atom.parent) normal -= numpy.dot(normal, oposite_direction) * oposite_direction normal /= numpy.linalg.norm(normal) if other_bonds[0].children[0].target == other_atom: normal *= -1 else: normal = random_orthonormal(oposite_direction) elif num_bonds == 2: normal = numpy.cross( oposite_direction, existing_bonds[0].shortest_vector_relative_to(atom.parent)) normal /= numpy.linalg.norm(normal) elif num_bonds == 3: normal = random_orthonormal(oposite_direction) else: return h_pos = bond_length * (oposite_direction * numpy.cos(opening_angle) + normal * numpy.sin(opening_angle)) for i in range(num_hydrogens): t = atom.transformation.t + h_pos H = Atom(name="auto H", number=1, transformation=Translation(t)) primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) h_pos = rotation * h_pos
def endElement(self, name): if name == "zml_file": return # now that we have gatherd all information of this tag, create an appropriate object # first find the tags involved in this operation current_tag = self.hierarchy[-1][-1] child_tags = [] if not current_tag.being_processed: current_tag = self.hierarchy[-2][-1] child_tags = self.hierarchy[-1] # do it if name == "str": current_tag.value = str(current_tag.content) elif name == "float": current_tag.value = float(current_tag.content) elif name == "int": current_tag.value = int(current_tag.content) elif name == "bool": temp = current_tag.content.lower().strip() if temp == 'true': current_tag.value = True else: current_tag.value = False elif name == "list": current_tag.value = [tag.value for tag in child_tags] elif name == "dict": current_tag.value = dict((tag.label, tag.value) for tag in child_tags) elif name == "tuple": current_tag.value = tuple(tag.value for tag in child_tags) elif name == "shape": current_tag.value = tuple(int(item) for item in current_tag.content.split()) elif name == "cells": current_tag.value = numpy.array([eval(item) for item in current_tag.content.split()]) elif name == "array": child_dict = dict((tag.name, tag.value) for tag in child_tags) current_tag.value = numpy.reshape(child_dict["cells"], child_dict["shape"]) elif name == "grid": current_tag.value = numpy.reshape(numpy.array([eval(item) for item in current_tag.content.split()]), (int(current_tag.attributes["rows"]), int(current_tag.attributes["cols"]), -1)) elif name == "binary": current_tag.value = StringIO.StringIO() current_tag.content.seek(0) base64.decode(current_tag.content, current_tag.value) elif name == "translation": current_tag.value = Translation(child_tags[0].value) elif name == "rotation": current_tag.value = Rotation(child_tags[0].value) elif name == "transformation": child_dict = dict((tag.label, tag.value) for tag in child_tags) current_tag.value = Complete( child_dict["rotation_matrix"], child_dict["translation_vector"], ) elif name == "unit_cell": child_dict = dict((tag.label, tag.value) for tag in child_tags) current_tag.value = UnitCell( child_dict["matrix"], child_dict["active"], ) elif name == "expression": current_tag.value = Expression(current_tag.content) elif name == "reference": current_tag.value = None referent_tag = self.hierarchy[-3][-1] target_ids = self.target_ids.get(referent_tag) if target_ids is None: target_ids = [] self.target_ids[referent_tag] = target_ids target_ids.append(int(current_tag.attributes["to"])) elif name == "model_object": Class = context.application.plugins.get_node(str(current_tag.attributes["class"])) current_tag.state = dict((tag.label, tag.value) for tag in child_tags) current_tag.value = Class() self.model_object_tags[int(current_tag.attributes["id"])] = current_tag else: pass # close the door current_tag.content = None current_tag.close() if len(child_tags) > 0: self.hierarchy.pop()
def replace(self, gl_object): if not gl_object.get_fixed(): new = self.get_new(gl_object.transformation.t) # select a target object, i.e. the one the will be connected with # the bonds/vectors/... of the replaced object if (self.current_object == "Fragment"): # fragments are inserted as frames # take atom with index 1 as target target_object = new.children[1] else: target_object = new # check if all connections to the replaced object are applicable # to the new object. if not, then do not perform the replacement # and return early. for reference in gl_object.references[::-1]: if not reference.check_target(target_object): return # add the new object parent = gl_object.parent primitive.Add(new, parent) if (self.current_object == "Fragment"): # Fix the rotation and translation of the molecular fragment. # Rotation Bond = context.application.plugins.get_node("Bond") if len(gl_object.references) == 1 and isinstance( gl_object.references[0].parent, Bond): bond1 = gl_object.references[0].parent direction1 = bond1.shortest_vector_relative_to(parent) if bond1.children[0].target != gl_object: direction1 *= -1 bond2 = new.children[0].references[0].parent direction2 = bond2.shortest_vector_relative_to(parent) if bond2.children[0].target != target_object: direction2 *= -1 axis = numpy.cross(direction2, direction1) if numpy.linalg.norm(axis) < 1e-8: axis = random_orthonormal(direction1) angle = compute_angle(direction1, direction2) rotation = Rotation.from_properties(angle, axis, False) primitive.Transform(new, rotation) else: bond1 = None # Tranlsation pos_old = new.children[1].get_frame_relative_to(parent).t pos_new = gl_object.transformation.t translation = Translation(pos_new - pos_old) primitive.Transform(new, translation) if bond1 != None: # bond length old_length = numpy.linalg.norm(direction1) new_length = bonds.get_length( new.children[1].number, bond1.get_neighbor(gl_object).number) translation = Translation(-direction1 / old_length * (new_length - old_length)) primitive.Transform(new, translation) # let the references to the replaced object point to the new object for reference in gl_object.references[::-1]: try: primitive.SetTarget(reference, target_object) except primitive.PrimitiveError: primitive.Delete(reference.parent) # delete the replaced object primitive.Delete(gl_object) if (self.current_object == "Fragment"): # Delete the first atom in the fragment primitive.Delete(new.children[0]) # Unframe the fragment UnframeAbsolute = context.application.plugins.get_action( "UnframeAbsolute") UnframeAbsolute([new])
def align_cell(self, lcs=None, swap=True): """Align the unit cell with respect to the Cartesian Axes frame **Optional Arguments:** lcs The linear combinations of the unit cell that must get aligned. This is a 2x3 array, where each row represents a linear combination of cell vectors. The first row is for alignment with the x-axis, second for the z-axis. The default value is:: np.array([ [1, 0, 0], [0, 0, 1], ]) swap By default, the first alignment is done with the z-axis, then with the x-axis. The order is reversed when swap is set to False. The alignment of the first linear combination is always perfect. The alignment of the second linear combination is restricted to a plane. The cell is always made right-handed. The coordinates are also rotated with respect to the origin, but never inverted. The attributes of the system are modified in-place. Note that this method only works on 3D periodic systems. """ from molmod import Rotation, deg # define the target target = np.array([ [1, 0, 0], [0, 0, 1], ]) # default value for linear combination if lcs is None: lcs = target.copy() # The starting values pos = self.pos rvecs = self.cell.rvecs.copy() if rvecs.shape != (3,3): raise TypeError('The align_cell method only supports 3D periodic systems.') # Optionally swap a cell vector if the cell is not right-handed. if np.linalg.det(rvecs) < 0: # Find a reasonable vector to swap... index = rvecs.sum(axis=1).argmin() rvecs[index] *= -1 # Define the source source = np.dot(lcs, rvecs) # Do the swapping if swap: target = target[::-1] source = source[::-1] # auxiliary function def get_angle_axis(t, s): cos = np.dot(s, t)/np.linalg.norm(s)/np.linalg.norm(t) angle = np.arccos(np.clip(cos, -1, 1)) axis = np.cross(s, t) return angle, axis # first alignment angle, axis = get_angle_axis(target[0], source[0]) if np.linalg.norm(axis) > 0: r1 = Rotation.from_properties(angle, axis, False) pos = r1*pos rvecs = r1*rvecs source = r1*source # second alignment # Make sure the source is orthogonal to target[0] s1p = source[1] - target[0]*np.dot(target[0], source[1]) angle, axis = get_angle_axis(target[1], s1p) r2 = Rotation.from_properties(angle, axis, False) pos = r2*pos rvecs = r2*rvecs # assign self.pos = pos self.cell = Cell(rvecs)
def do(self): cache = context.application.cache rotation = Rotation(cache.node.transformation.r) CenterAlignBase.do(self, cache.parent, cache.transformed_neighbors, rotation)
def add_hydrogens(atom): existing_bonds = list(atom.iter_bonds()) num_bonds = len(existing_bonds) bond_length = bonds.get_length(atom.number, 1, BOND_SINGLE) if num_bonds == 0: t = atom.transformation.t + numpy.array([0,bond_length,0]) H = Atom(name="auto H", number=1, transformation=Translation(t)) primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) existing_bonds.append(bond) num_bonds = 1 used_valence = 0 oposite_direction = numpy.zeros(3, float) for bond in existing_bonds: shortest_vector = bond.shortest_vector_relative_to(atom.parent) if bond.children[1].target == atom: shortest_vector *= -1 oposite_direction -= shortest_vector if bond.bond_type == BOND_SINGLE: used_valence += 1 elif bond.bond_type == BOND_DOUBLE: used_valence += 2 elif bond.bond_type == BOND_TRIPLE: used_valence += 3 oposite_direction /= numpy.linalg.norm(oposite_direction) num_hydrogens = valence_el(atom.number) - 2*lone_pairs(atom.number) - used_valence if num_hydrogens <= 0: return hybride_count = num_hydrogens + lone_pairs(atom.number) + num_bonds - (used_valence - num_bonds) num_sites = num_hydrogens + lone_pairs(atom.number) rotation = Rotation.from_properties(2*numpy.pi / float(num_sites), oposite_direction, False) opening_key = (hybride_count, num_sites) opening_angle = self.opening_angles.get(opening_key) if opening_angle is None: return if num_bonds == 1: first_bond = existing_bonds[0] other_atom = first_bond.children[0].target if other_atom == atom: other_atom = first_bond.children[1].target other_bonds = [bond for bond in other_atom.iter_bonds() if bond != first_bond] if len(other_bonds) > 0: normal = other_bonds[0].shortest_vector_relative_to(atom.parent) normal -= numpy.dot(normal, oposite_direction) * oposite_direction normal /= numpy.linalg.norm(normal) if other_bonds[0].children[0].target == other_atom: normal *= -1 else: normal = random_orthonormal(oposite_direction) elif num_bonds == 2: normal = numpy.cross(oposite_direction, existing_bonds[0].shortest_vector_relative_to(atom.parent)) normal /= numpy.linalg.norm(normal) elif num_bonds == 3: normal = random_orthonormal(oposite_direction) else: return h_pos = bond_length*(oposite_direction*numpy.cos(opening_angle) + normal*numpy.sin(opening_angle)) for i in range(num_hydrogens): t = atom.transformation.t + h_pos H = Atom(name="auto H", number=1, transformation=Translation(t)) primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) h_pos = rotation * h_pos
def convert_to_value(self, representation): properties = ComposedInTable.convert_to_value(self, representation) return MathRotation.from_properties(*properties)
def do(self): cache = context.application.cache geometry_nodes = [[], []] def get_parameters(node, point_type, geometry_index, filter_expression, radius_expression): template = "An exception occured in the %%s expression\nfor the %s points of geometry %i." % ( point_type, geometry_index + 1) try: is_type = filter_expression(node) except Exception: raise UserError(template % "filter") if is_type: try: radius = radius_expression(node) except Exception: raise UserError(template % "radius") geometry_nodes[geometry_index].append(node) return True, radius else: return False, None def read_geometry(frame, geometry_index, connect_description, repulse_description): coordinates = [] connect_masks = [] radii = [] for child in frame.children: if isinstance(child, GLTransformationMixin) and \ isinstance(child.transformation, Translation): is_connect, radius = get_parameters( child, "connecting", geometry_index, *connect_description) if is_connect: coordinates.append(child.transformation.t) connect_masks.append(True) radii.append(radius) else: is_repulse, radius = get_parameters( child, "repulsive", geometry_index, *repulse_description) if is_repulse: coordinates.append(child.transformation.t) connect_masks.append(False) radii.append(radius) return Geometry( numpy.array(coordinates, float), numpy.array(connect_masks, bool), numpy.array(radii, float), ) inp = {} inp["geometry1"] = read_geometry(cache.nodes[0], 0, self.parameters.connect_description1, self.parameters.repulse_description1) if len(cache.nodes) == 2: inp["geometry2"] = read_geometry( cache.nodes[1], 1, self.parameters.connect_description2, self.parameters.repulse_description2) else: inp["geometry2"] = None inp["action_radius"] = self.parameters.action_radius inp["hit_tolerance"] = self.parameters.hit_tolerance if not isinstance(self.parameters.allow_inversions, Undefined): inp["allow_rotations"] = True inp["allow_inversions"] = self.parameters.allow_inversions inp["minimum_triangle_area"] = self.parameters.minimum_triangle_size**2 else: inp["allow_rotations"] = False if isinstance(self.parameters.rotation2, Undefined): inp["rotation2"] = Rotation.identity() else: inp["rotation2"] = self.parameters.rotation2 if inp["allow_rotations"]: connections = self.triangle_report_dialog.run(inp) else: connections = self.pair_report_dialog.run(inp) if connections is not None and len(connections) > 0: if len(cache.nodes) == 1: frame1 = cache.nodes[0] Duplicate = context.application.plugins.get_action("Duplicate") Duplicate() frame2 = cache.nodes[0] geometry_nodes[1] = geometry_nodes[0] else: frame1, frame2 = cache.nodes conscan_results = ConscanResults( targets=[frame1, frame2], connections=[( float(connection.quality), connection.transformation, [(geometry_nodes[0][first].get_index(), geometry_nodes[1][second].get_index()) for first, second in connection.pairs], [(geometry_nodes[0][second].get_index(), geometry_nodes[1][first].get_index()) for first, second in connection.pairs if connection.invertible], ) for connection in connections], ) primitive.Add(conscan_results, context.application.model.folder)
def align_cell(self, lcs=None, swap=True): """Align the unit cell with respect to the Cartesian Axes frame **Optional Arguments:** lcs The linear combinations of the unit cell that must get aligned. This is a 2x3 array, where each row represents a linear combination of cell vectors. The first row is for alignment with the x-axis, second for the z-axis. The default value is:: np.array([ [1, 0, 0], [0, 0, 1], ]) swap By default, the first alignment is done with the z-axis, then with the x-axis. The order is reversed when swap is set to False. The alignment of the first linear combination is always perfect. The alignment of the second linear combination is restricted to a plane. The cell is always made right-handed. The coordinates are also rotated with respect to the origin, but never inverted. The attributes of the system are modified in-place. Note that this method only works on 3D periodic systems. """ from molmod import Rotation, deg # define the target target = np.array([ [1, 0, 0], [0, 0, 1], ]) # default value for linear combination if lcs is None: lcs = target.copy() # The starting values pos = self.pos rvecs = self.cell.rvecs.copy() if rvecs.shape != (3, 3): raise TypeError( 'The align_cell method only supports 3D periodic systems.') # Optionally swap a cell vector if the cell is not right-handed. if np.linalg.det(rvecs) < 0: # Find a reasonable vector to swap... index = rvecs.sum(axis=1).argmin() rvecs[index] *= -1 # Define the source source = np.dot(lcs, rvecs) # Do the swapping if swap: target = target[::-1] source = source[::-1] # auxiliary function def get_angle_axis(t, s): cos = np.dot(s, t) / np.linalg.norm(s) / np.linalg.norm(t) angle = np.arccos(np.clip(cos, -1, 1)) axis = np.cross(s, t) return angle, axis # first alignment angle, axis = get_angle_axis(target[0], source[0]) if np.linalg.norm(axis) > 0: r1 = Rotation.from_properties(angle, axis, False) pos = r1 * pos rvecs = r1 * rvecs source = r1 * source # second alignment # Make sure the source is orthogonal to target[0] s1p = source[1] - target[0] * np.dot(target[0], source[1]) angle, axis = get_angle_axis(target[1], s1p) r2 = Rotation.from_properties(angle, axis, False) pos = r2 * pos rvecs = r2 * rvecs # assign self.pos = pos self.cell = Cell(rvecs)
def compute_transformation(self, connection): # print connection.pairs triangle1 = connection.triangle1 triangle2 = connection.triangle2 triangles = [triangle1, triangle2] # print "BEFORE TRANSFORMING\n" # for triangle in triangles: # #print "-------" # for coordinate in triangle.coordinates: # #print coordinate # print "---------" # *** t1: translation of triangle in geometry2 to origin t1 = Translation(-triangle2.center) # print triangle2.center triangle2.apply_to_coordinates(t1) # also move triangle1 to the origin t_tmp = Translation(-triangle1.center) # was t0 triangle1.apply_to_coordinates(t_tmp) # print "AFTER CENTERING (T1 and T0)" # for triangle in triangles: # #print "-------" # for coordinate in triangle.coordinates: # #print coordinate # *** r2: make the two triangles coplanar # print "NORMALS" # print triangle1.normal # print triangle2.normal rotation_axis = numpy.cross(triangle2.normal, triangle1.normal) if numpy.dot(rotation_axis, rotation_axis) < 1e-8: rotation_axis = random_orthonormal(triangle2.normal) rotation_angle = angle(triangle2.normal, triangle1.normal) # print "R2 %s, %s" % (rotation_angle/numpy.pi*180, rotation_axis) r2 = Rotation.from_properties(rotation_angle, rotation_axis, False) triangle2.apply_to_coordinates(r2) # print "AFTER R2" # for triangle in triangles: # #print "-------" # for coordinate in triangle.coordinates: # #print coordinate # bring both triangles in the x-y plane, by a rotation around an axis # orthogonal to the Z-axis. rotation_axis = numpy.array([triangle1.normal[1], -triangle1.normal[0], 0.0], float) if numpy.dot(rotation_axis, rotation_axis) < 1e-8: rotation_axis = numpy.array([1.0, 0.0, 0.0], float) cos_angle = triangle1.normal[2] if cos_angle >= 1.0: rotation_angle = 0 elif cos_angle <= -1.0: rotation_angle = numpy.pi else: rotation_angle = numpy.arccos(cos_angle) # print "RT %s, %s" % (rotation_angle/numpy.pi*180, rotation_axis) r_flat = Rotation.from_properties(rotation_angle, rotation_axis, False) triangle1.apply_to_coordinates(r_flat) triangle2.apply_to_coordinates(r_flat) # print "AFTER RT" # for triangle in triangles: # #print "-------" # for coordinate in triangle.coordinates: # #print coordinate # *** r3: second rotation that makes both triangle coinced H = lambda a, b, c, d: a * c + b * d + (a + b) * (c + d) c = H( triangle1.coordinates[0][0], triangle1.coordinates[1][0], triangle2.coordinates[0][0], triangle2.coordinates[1][0], ) + H( triangle1.coordinates[0][1], triangle1.coordinates[1][1], triangle2.coordinates[0][1], triangle2.coordinates[1][1], ) s = H( triangle1.coordinates[0][1], triangle1.coordinates[1][1], triangle2.coordinates[0][0], triangle2.coordinates[1][0], ) - H( triangle1.coordinates[0][0], triangle1.coordinates[1][0], triangle2.coordinates[0][1], triangle2.coordinates[1][1], ) # if c > s: c, s = -c, -s # print "cos=%f sin=%f" % (c, s) rotation_angle = numpy.arctan2(s, c) # print "R3 %s, %s" % (rotation_angle/numpy.pi*180, triangle1.normal) r3 = Rotation.from_properties(rotation_angle, triangle1.normal, False) r_tmp = Rotation.from_properties(rotation_angle, numpy.array([0, 0, 1], float), False) triangle2.apply_to_coordinates(r_tmp) # print "AFTER R3" # for triangle in triangles: # #print "-------" # for coordinate in triangle.coordinates: # #print coordinate # t4: translate the triangle to the definitive coordinate t4 = Translation(triangle1.center) # print "AFTER T4" # for triangle in triangles: # #print "-------" # for coordinate in triangle.coordinates: # #print coordinate return t4 * r3 * r2 * t1
def default_parameters(cls): result = Parameters() result.center = Translation.identity() result.rotation = Rotation.identity() return result
def compute_transformation(self, connection): #print connection.pairs triangle1 = connection.triangle1 triangle2 = connection.triangle2 triangles = [triangle1, triangle2] #print "BEFORE TRANSFORMING\n" #for triangle in triangles: # #print "-------" # for coordinate in triangle.coordinates: # #print coordinate #print "---------" # *** t1: translation of triangle in geometry2 to origin t1 = Translation(-triangle2.center) #print triangle2.center triangle2.apply_to_coordinates(t1) # also move triangle1 to the origin t_tmp = Translation(-triangle1.center) # was t0 triangle1.apply_to_coordinates(t_tmp) #print "AFTER CENTERING (T1 and T0)" #for triangle in triangles: # #print "-------" # for coordinate in triangle.coordinates: # #print coordinate # *** r2: make the two triangles coplanar #print "NORMALS" #print triangle1.normal #print triangle2.normal rotation_axis = numpy.cross(triangle2.normal, triangle1.normal) if numpy.dot(rotation_axis, rotation_axis) < 1e-8: rotation_axis = random_orthonormal(triangle2.normal) rotation_angle = angle(triangle2.normal, triangle1.normal) #print "R2 %s, %s" % (rotation_angle/numpy.pi*180, rotation_axis) r2 = Rotation.from_properties(rotation_angle, rotation_axis, False) triangle2.apply_to_coordinates(r2) #print "AFTER R2" #for triangle in triangles: # #print "-------" # for coordinate in triangle.coordinates: # #print coordinate # bring both triangles in the x-y plane, by a rotation around an axis # orthogonal to the Z-axis. rotation_axis = numpy.array( [triangle1.normal[1], -triangle1.normal[0], 0.0], float) if numpy.dot(rotation_axis, rotation_axis) < 1e-8: rotation_axis = numpy.array([1.0, 0.0, 0.0], float) cos_angle = triangle1.normal[2] if cos_angle >= 1.0: rotation_angle = 0 elif cos_angle <= -1.0: rotation_angle = numpy.pi else: rotation_angle = numpy.arccos(cos_angle) #print "RT %s, %s" % (rotation_angle/numpy.pi*180, rotation_axis) r_flat = Rotation.from_properties(rotation_angle, rotation_axis, False) triangle1.apply_to_coordinates(r_flat) triangle2.apply_to_coordinates(r_flat) #print "AFTER RT" #for triangle in triangles: # #print "-------" # for coordinate in triangle.coordinates: # #print coordinate # *** r3: second rotation that makes both triangle coinced H = lambda a, b, c, d: a * c + b * d + (a + b) * (c + d) c = (H(triangle1.coordinates[0][0], triangle1.coordinates[1][0], triangle2.coordinates[0][0], triangle2.coordinates[1][0]) + H(triangle1.coordinates[0][1], triangle1.coordinates[1][1], triangle2.coordinates[0][1], triangle2.coordinates[1][1])) s = (H(triangle1.coordinates[0][1], triangle1.coordinates[1][1], triangle2.coordinates[0][0], triangle2.coordinates[1][0]) - H(triangle1.coordinates[0][0], triangle1.coordinates[1][0], triangle2.coordinates[0][1], triangle2.coordinates[1][1])) #if c > s: c, s = -c, -s #print "cos=%f sin=%f" % (c, s) rotation_angle = numpy.arctan2(s, c) #print "R3 %s, %s" % (rotation_angle/numpy.pi*180, triangle1.normal) r3 = Rotation.from_properties(rotation_angle, triangle1.normal, False) r_tmp = Rotation.from_properties(rotation_angle, numpy.array([0, 0, 1], float), False) triangle2.apply_to_coordinates(r_tmp) #print "AFTER R3" #for triangle in triangles: # #print "-------" # for coordinate in triangle.coordinates: # #print coordinate # t4: translate the triangle to the definitive coordinate t4 = Translation(triangle1.center) #print "AFTER T4" #for triangle in triangles: # #print "-------" # for coordinate in triangle.coordinates: # #print coordinate return t4 * r3 * r2 * t1