Exemple #1
0
    def do(self):
        universe = context.application.model.universe
        scaling = numpy.dot(self.parameters.matrix,
                            numpy.linalg.inv(universe.cell.matrix))
        primitive.SetProperty(
            universe, "cell",
            universe.cell.copy_with(matrix=self.parameters.matrix))

        for child in universe.children:
            if isinstance(child, GLTransformationMixin) and isinstance(
                    child.transformation, Translation):
                new_transformation = child.transformation.copy_with(
                    t=numpy.dot(scaling, child.transformation.t))
                primitive.SetProperty(child, "transformation",
                                      new_transformation)
Exemple #2
0
    def do(self):
        victims = context.application.cache.nodes
        edit_properties = context.application.edit_properties
        # Copy the old state, only the supported field names
        old_states = {}
        for victim in victims:
            old_state = victim.__getstate__()
            tmp = {}
            for attribute_name in edit_properties.attribute_names:
                value = old_state.get(attribute_name, None)
                if value is not None:
                    tmp[attribute_name] = value
            old_states[victim] = tmp

        # Let the user make changes
        edit_properties.run(victims)
        for changed_name in edit_properties.changed_names:
            for victim, old_state in old_states.iteritems():
                old_value = old_state.get(changed_name)
                if old_value is None:
                    old_value = victim.properties_by_name[
                        changed_name].default(victim)
                primitive.SetProperty(victim,
                                      changed_name,
                                      old_value,
                                      done=True)
Exemple #3
0
    def do(self):
        # A) collect all extra attributes of the selected nodes
        extra_attrs = {}
        for node in context.application.cache.nodes:
            for key, value in node.extra.iteritems():
                other_value = extra_attrs.get(key)
                if other_value is None:
                    extra_attrs[key] = value
                elif isinstance(other_value, Undefined):
                    continue
                elif other_value != value:
                    extra_attrs[key] = Undefined()
        for key, value in extra_attrs.items():
            if isinstance(value, Undefined):
                continue
            for node in context.application.cache.nodes:
                other_value = node.extra.get(key)
                if other_value != value:
                    extra_attrs[key] = Undefined()
                    break

        # B) present the extra attributes in a popup window with editing features
        extra_dialog = ExtraDialog()
        modified_extra_attrs, remove_keys = extra_dialog.run(extra_attrs)
        if len(modified_extra_attrs) > 0 or len(remove_keys) > 0:
            # C) the modified extra attributes are applied to the select objects
            for node in context.application.cache.nodes:
                new_value = node.extra.copy()
                new_value.update(modified_extra_attrs)
                for remove_key in remove_keys:
                    new_value.pop(remove_key, None)
                primitive.SetProperty(node, "extra", new_value)
Exemple #4
0
    def do(self):
        cache = context.application.cache

        graph = create_molecular_graph(cache.nodes)
        parent = cache.node

        Frame = context.application.plugins.get_node("Frame")
        for group in graph.independent_vertices:
            atoms = [graph.molecule.atoms[i] for i in group]
            new_positions = self.calc_new_positions(group, atoms, graph,
                                                    parent)
            if new_positions is None:
                # this happens for groups of atoms that are inherently periodic.
                continue
            frame = Frame(name=chemical_formula(atoms)[1])
            primitive.Add(frame, parent, index=0)
            for node, atom in zip(group, atoms):
                primitive.Move(atom, frame)
                new_position = new_positions[node]
                translation = Translation(
                    atom.get_parentframe_up_to(parent).inv * new_position)
                primitive.SetProperty(atom, "transformation", translation)
            for atom in atoms:
                # take a copy of the references since they are going to be
                # refreshed (changed and reverted) during the loop.
                for reference in list(atom.references):
                    referent = reference.parent
                    if referent.parent != frame:
                        has_to_move = True
                        for child in referent.children:
                            if child.target.parent != frame:
                                has_to_move = False
                                break
                        if has_to_move:
                            primitive.Move(referent, frame)
Exemple #5
0
    def fn():
        from molmod import UnitCell
        context.application.model.file_open("test/input/methane_box22_125.xyz")
        universe = context.application.model.universe

        context.application.action_manager.record_primitives = False
        unit_cell = UnitCell(
            numpy.identity(3, float) * 22 * angstrom, numpy.ones(3, bool))
        primitive.SetProperty(universe, "cell", unit_cell)

        context.application.main.select_nodes([universe])
        AutoConnectPhysical = context.application.plugins.get_action(
            "AutoConnectPhysical")
        assert AutoConnectPhysical.analyze_selection()
        AutoConnectPhysical()

        context.application.main.select_nodes([universe])
        FrameMolecules = context.application.plugins.get_action(
            "FrameMolecules")
        assert FrameMolecules.analyze_selection()
        FrameMolecules()

        Bond = context.application.plugins.get_node("Bond")
        for frame in universe.children:
            for bond in frame.children:
                if isinstance(bond, Bond):
                    bond.calc_vector_dimensions()
                    assert bond.length < 2 * angstrom
Exemple #6
0
 def do(self):
     counter = 1
     for node in context.application.cache.children:
         if isinstance(node, Atom):
             primitive.SetProperty(
                 node, "name", periodic[node.number].symbol + str(counter))
             counter += 1
Exemple #7
0
 def do(self):
     victim = context.application.cache.node
     Frame = context.application.plugins.get_node("Frame")
     frame = Frame(name="Frame of " + victim.name)
     primitive.Add(frame, victim.parent, index=victim.get_index())
     primitive.Transform(frame, victim.transformation)
     primitive.Move(victim, frame)
     primitive.SetProperty(victim, "transformation",
                           victim.Transformation())
    def do(self):
        universe = context.application.cache.node
        # first make sure the cell is right handed
        if numpy.linalg.det(
                universe.cell.matrix) < 0 and universe.cell.active.sum() == 3:
            new_matrix = universe.cell.matrix.copy()
            temp = new_matrix[:, 0].copy()
            new_matrix[:, 0] = new_matrix[:, 1]
            new_matrix[:, 1] = temp
            new_cell = UnitCell(new_matrix, universe.cell.active)
            primitive.SetProperty(universe, "cell", new_cell)

        # then rotate the unit cell box to the normalized frame:
        rotation = universe.cell.alignment_a
        for child in context.application.cache.transformed_children:
            primitive.Transform(child, rotation)
        new_cell = UnitCell(numpy.dot(rotation.r, universe.cell.matrix),
                            universe.cell.active)
        primitive.SetProperty(universe, "cell", new_cell)
Exemple #9
0
 def finish(self):
     if self.changed:
         self.parameters.transformation = self.old_transformation.inv * self.victim.transformation
         primitive.SetProperty(self.victim,
                               "transformation",
                               self.old_transformation,
                               done=True)
         self.victim.invalidate_transformation_list(
         )  # make sure that bonds and connectors are updated after transformation
     InteractiveWithMemory.finish(self)
Exemple #10
0
 def apply_normal(self):
     model, iter = self.tree_selection.get_selected()
     parent = self.frame2
     if parent.parent is not None:
         parent = parent.parent
     transformation = self.frame1.get_frame_relative_to(parent)
     if self.cb_inverse.get_active() and len(model.get_value(iter,
                                                             3)[3]) > 0:
         transformation = model.get_value(iter, 3)[1].inv * transformation
     else:
         transformation = model.get_value(iter, 3)[1] * transformation
     primitive.SetProperty(self.frame2, "transformation", transformation)
Exemple #11
0
 def do(self):
     universe = context.application.model.universe
     for child in universe.children:
         if isinstance(child, GLTransformationMixin) and isinstance(
                 child.transformation, Translation):
             cell_index = universe.cell.to_fractional(
                 child.transformation.t)
             cell_index = numpy.floor(cell_index)
             if cell_index.any():
                 t = child.transformation.t - universe.cell.to_cartesian(
                     cell_index)
                 new_transformation = child.transformation.copy_with(t=t)
                 primitive.SetProperty(child, "transformation",
                                       new_transformation)
Exemple #12
0
 def do(self):
     vectors = context.application.cache.nodes
     universe = context.application.model.root[0]
     new_cell = universe.cell
     try:
         for vector in vectors:
             new_cell = new_cell.add_cell_vector(
                 vector.shortest_vector_relative_to(universe))
     except ValueError:
         if len(vectors) == 1:
             raise UserError(
                 "Failed to add the selected vector as cell vector since it would make the unit cell singular."
             )
         else:
             raise UserError(
                 "Failed to add the selected vectors as cell vectors since they would make the unit cell singular."
             )
     primitive.SetProperty(universe, "cell", new_cell)
Exemple #13
0
 def do(self):
     for vector in context.application.cache.nodes:
         primitive.SetProperty(vector, "targets",
                               vector.get_targets()[::-1])
Exemple #14
0
    def do(self):
        # the indices (n,m) that define the tube, see e.g. the wikipedia page
        # about nanotubes for the interpretation of these indices:
        # http://en.wikipedia.org/wiki/Carbon_nanotube
        n = self.parameters.n
        m = self.parameters.m

        periodic_tube = isinstance(self.parameters.tube_length, Undefined)

        universe = context.application.model.universe

        def define_flat():
            "Reads and converts the unit cell vectors from the current model."
            # some parts of the algorithm have been arranged sub functions like
            # these, to reduce the number of local variables in self.do. This
            # should also clarify the code.
            active, inactive = universe.cell.active_inactive
            lengths, angles = universe.cell.parameters
            a = lengths[active[0]]
            b = lengths[active[1]]
            theta = angles[inactive[0]]
            return (numpy.array([a, 0], float),
                    numpy.array([b * numpy.cos(theta), b * numpy.sin(theta)],
                                float))

        flat_a, flat_b = define_flat()

        def create_pattern():
            "Read the atom positions and transform them to the flat coordinates"
            active, inactive = universe.cell.active_inactive
            a = universe.cell.matrix[:, active[0]]
            b = universe.cell.matrix[:, active[1]]
            c = numpy.cross(a, b)
            tmp_cell = UnitCell(numpy.array([a, b, c]).transpose())
            rotation = tmp_cell.alignment_a

            return [(atom.number, rotation * atom.get_absolute_frame().t)
                    for atom in iter_atoms([universe])]

        pattern = create_pattern()

        def define_big_periodic():
            "Based on (n,m) calculate the size of the periodic sheet (that will be folded into a tube)."
            big_a = n * flat_a - m * flat_b
            norm_a = numpy.linalg.norm(big_a)
            radius = norm_a / (2 * numpy.pi)
            big_x = big_a / norm_a
            big_y = numpy.array([-big_x[1], big_x[0]], float)

            big_b = None
            stack_vector = flat_b - flat_a * numpy.dot(
                big_x, flat_b) / numpy.dot(big_x, flat_a)
            stack_length = numpy.linalg.norm(stack_vector)
            nominator = numpy.linalg.norm(stack_vector - flat_b)
            denominator = numpy.linalg.norm(flat_a)
            fraction = nominator / denominator
            stack_size = 1
            while True:
                repeat = fraction * stack_size
                if stack_length * stack_size > self.parameters.max_length:
                    break
                if abs(repeat - round(repeat)
                       ) * denominator < self.parameters.max_error:
                    big_b = stack_vector * stack_size
                    break
                stack_size += 1
            if big_b is None:
                raise UserError(
                    "Could not create a periodic tube shorter than the given maximum length."
                )
            rotation = numpy.array([big_x, big_y], float)
            return big_a, big_b, rotation, stack_vector, stack_size, radius

        def define_big_not_periodic():
            "Based on (n,m) calculate the size of the non-periodic sheet (that will be folded into a tube)."
            big_a = n * flat_a - m * flat_b
            norm_a = numpy.linalg.norm(big_a)
            radius = norm_a / (2 * numpy.pi)
            big_x = big_a / norm_a
            big_y = numpy.array([-big_x[1], big_x[0]], float)

            stack_vector = flat_b - flat_a * numpy.dot(
                big_x, flat_b) / numpy.dot(big_x, flat_a)
            stack_length = numpy.linalg.norm(stack_vector)
            stack_size = int(self.parameters.tube_length / stack_length)
            big_b = stack_vector * stack_size
            rotation = numpy.array([big_x, big_y], float)
            return big_a, big_b, rotation, stack_vector, stack_size, radius

        if periodic_tube:
            big_a, big_b, rotation, stack_vector, stack_size, radius = define_big_periodic(
            )
        else:
            big_a, big_b, rotation, stack_vector, stack_size, radius = define_big_not_periodic(
            )

        def iter_translations():
            "Yields the indices of the periodic images that are part of the tube."
            to_fractional = numpy.linalg.inv(
                numpy.array([big_a, big_b]).transpose())
            col_len = int(
                numpy.linalg.norm(big_a + m * stack_vector) /
                numpy.linalg.norm(flat_a)) + 4
            shift = numpy.dot(stack_vector - flat_b,
                              flat_a) / numpy.linalg.norm(flat_a)**2
            for row in xrange(-m - 1, stack_size + 1):
                col_start = int(numpy.floor(row * shift)) - 1
                for col in xrange(col_start, col_start + col_len):
                    p = col * flat_a + row * flat_b
                    i = numpy.dot(to_fractional, p)
                    if (i >= 0).all() and (i < 1 - 1e-15).all():
                        yield p
                    #yield p, (i >= 0).all() and (i < 1).all()

        def iter_pattern():
            for number, coordinate in pattern:
                yield number, coordinate.copy()

        # first delete everything the universe:
        while len(universe.children) > 0:
            primitive.Delete(universe.children[0])

        # add the new atoms
        Atom = context.application.plugins.get_node("Atom")
        if self.parameters.flat:
            rot_a = numpy.dot(rotation, big_a)
            rot_b = numpy.dot(rotation, big_b)
            big_matrix = numpy.array([
                [rot_a[0], rot_b[0], 0],
                [rot_a[1], rot_b[1], 0],
                [0, 0, 10 * angstrom],
            ], float)
            big_cell = UnitCell(
                big_matrix, numpy.array([True, periodic_tube, False], bool))
            primitive.SetProperty(universe, "cell", big_cell)
            for p in iter_translations():
                for number, coordinate in iter_pattern():
                    coordinate[:2] += p
                    coordinate[:2] = numpy.dot(rotation, coordinate[:2])
                    translation = Translation(coordinate)
                    primitive.Add(
                        Atom(number=number, transformation=translation),
                        universe)
        else:
            tube_length = numpy.linalg.norm(big_b)
            big_matrix = numpy.diag([radius * 2, radius * 2, tube_length])
            big_cell = UnitCell(
                big_matrix, numpy.array([False, False, periodic_tube], bool))
            primitive.SetProperty(universe, "cell", big_cell)
            for p in iter_translations():
                for number, coordinate in iter_pattern():
                    coordinate[:2] += p
                    coordinate[:2] = numpy.dot(rotation, coordinate[:2])
                    translation = Translation(
                        numpy.array([
                            (radius + coordinate[2]) *
                            numpy.cos(coordinate[0] / radius),
                            (radius + coordinate[2]) *
                            numpy.sin(coordinate[0] / radius),
                            coordinate[1],
                        ]))
                    primitive.Add(
                        Atom(number=number, transformation=translation),
                        universe)
Exemple #15
0
    def do(self):
        cache = context.application.cache
        parent = cache.parent
        involved_frames = cache.spring_problem.frames
        springs = cache.spring_problem.springs
        max_step = []

        old_transformations = [(frame, frame.transformation)
                               for frame in involved_frames
                               if frame is not None]

        variable_indices = dict((frame, index) for index, frame in enumerate(
            frame for frame in involved_frames if frame is not None))

        cost_function = iterative.expr.Root(1, 10, True)
        for frame in involved_frames:
            if frame is None:
                pass
            elif self.parameters.allow_rotation and isinstance(
                    frame.transformation, Complete):
                variable = iterative.var.Frame(
                    frame.transformation.r,
                    frame.transformation.t,
                )
                cost_function.register_state_variable(variable)
                constraint = iterative.expr.Orthonormality(1e-10)
                constraint.register_input_variable(variable)
                max_step.extend([
                    0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 1.0, 1.0, 1.0
                ])
            elif isinstance(frame.transformation, Translation):
                variable = iterative.var.Translation(
                    frame.transformation.r,
                    frame.transformation.t,
                )
                cost_function.register_state_variable(variable)
                max_step.extend([1.0, 1.0, 1.0])
            else:
                raise UserError(
                    "The involved frames shoud be at least capable of being translated."
                )

        for spring, frames in springs.iteritems():
            spring_term = iterative.expr.Spring(spring.rest_length)
            for target, frame in frames.iteritems():
                if frame is None:
                    spring_term.register_input_variable(
                        iterative.expressions.NoFrame(),
                        target.get_frame_up_to(parent).t)
                else:
                    spring_term.register_input_variable(
                        cost_function.state_variables[variable_indices[frame]],
                        target.get_frame_up_to(frame).t)

        max_step = numpy.array(max_step, float)
        minimize = iterative.alg.DefaultMinimize(
            cost_function,
            max_step,
            max_step * 1e-8,
        )

        result = self.report_dialog.run(
            minimize,
            involved_frames,
            self.parameters.update_interval,
            self.parameters.update_steps,
            len(springs),
        )
        if result != gtk.RESPONSE_OK:
            for frame, transformation in old_transformations:
                frame.transformation = transformation
                frame.invalidate_transformation_list()
            raise CancelException

        for frame, transformation in old_transformations:
            primitive.SetProperty(
                frame,
                "transformation",
                transformation,
                done=True,
            )
Exemple #16
0
    def do(self):
        # create the repetitions vector
        repetitions = []

        if hasattr(self.parameters, "repetitions_a"):
            repetitions.append(self.parameters.repetitions_a)
        else:
            repetitions.append(1)

        if hasattr(self.parameters, "repetitions_b"):
            repetitions.append(self.parameters.repetitions_b)
        else:
            repetitions.append(1)

        if hasattr(self.parameters, "repetitions_c"):
            repetitions.append(self.parameters.repetitions_c)
        else:
            repetitions.append(1)

        repetitions = numpy.array(repetitions, int)

        # serialize the positioned children
        universe = context.application.model.universe

        positioned = [
            node for node in universe.children
            if (isinstance(node, GLTransformationMixin)
                and isinstance(node.transformation, Translation))
        ]
        if len(positioned) == 0: return

        serialized = StringIO.StringIO()
        dump_to_file(serialized, positioned)

        # create the replica's

        # replicate the positioned objects
        new_children = {}
        for cell_index in iter_all_positions(repetitions):
            cell_index = numpy.array(cell_index)
            cell_hash = tuple(cell_index)
            serialized.seek(0)
            nodes = load_from_file(serialized)
            new_children[cell_hash] = nodes
            for node in nodes:
                t = node.transformation.t + numpy.dot(universe.cell.matrix,
                                                      cell_index)
                new_transformation = node.transformation.copy_with(t=t)
                node.set_transformation(new_transformation)

        # forget about serialized stuff
        serialized.close()
        del serialized

        new_connectors = []
        # replicate the objects that connect these positioned objects
        for cell_index in iter_all_positions(repetitions):
            cell_index = numpy.array(cell_index)
            cell_hash = tuple(cell_index)
            for connector in universe.children:
                # Only applicable to ReferentMixin with only SpatialReference
                # children
                if not isinstance(connector, ReferentMixin):
                    continue
                skip = False
                for reference in connector.children:
                    if not isinstance(reference, SpatialReference):
                        skip = True
                        break
                if skip:
                    continue

                # first locate the new first target for this cell_index
                first_target_orig = connector.children[0].target
                first_target_index = positioned.index(first_target_orig)
                first_target = new_children[cell_hash][first_target_index]
                assert first_target is not None
                new_targets = [first_target]

                for reference in connector.children[1:]:
                    # then find the other new targets, taking into account
                    # periodicity
                    other_target_orig = reference.target
                    shortest_vector = universe.shortest_vector(
                        (other_target_orig.transformation.t -
                         first_target_orig.transformation.t))
                    translation = first_target.transformation.t + shortest_vector
                    other_target_pos = translation
                    other_cell_index = numpy.floor(
                        universe.cell.to_fractional(other_target_pos)).astype(
                            int)
                    other_cell_index %= repetitions
                    other_cell_hash = tuple(other_cell_index)
                    other_target_index = positioned.index(other_target_orig)
                    other_cell_children = new_children.get(other_cell_hash)
                    assert other_cell_children is not None
                    other_target = other_cell_children[other_target_index]
                    assert other_target is not None
                    new_targets.append(other_target)

                state = connector.__getstate__()
                state["targets"] = new_targets
                new_connectors.append(connector.__class__(**state))

        # remove the existing nodes
        while len(universe.children) > 0:
            primitive.Delete(universe.children[0])
        del positioned

        # multiply the cell matrix and reset the number of repetitions
        new_matrix = universe.cell * repetitions
        primitive.SetProperty(universe, "cell", new_matrix)
        primitive.SetProperty(universe, "repetitions",
                              numpy.array([1, 1, 1], int))

        # add the new nodes
        for nodes in new_children.itervalues():
            for node in nodes:
                primitive.Add(node, universe)

        for connector in new_connectors:
            primitive.Add(connector, universe)
Exemple #17
0
        def extend_to_cluster(axis, interval):
            if (interval is None) or isinstance(interval, Undefined): return
            assert universe.cell.active[axis]
            interval.sort()
            index_min = int(numpy.floor(interval[0]))
            index_max = int(numpy.ceil(interval[1]))

            old_points = [
                node for node in universe.children
                if (isinstance(node, GLTransformationMixin)
                    and isinstance(node.transformation, Translation))
            ]
            if len(old_points) == 0: return

            old_connections = [
                node for node in universe.children
                if (isinstance(node, ReferentMixin) and reduce(
                    (lambda x, y: x and y),
                    (isinstance(child, SpatialReference)
                     for child in node.children),
                    True,
                ))
            ]

            # replication of the points
            new_points = {}
            for old_point in old_points:
                # determine the wrapped position
                old_pos = old_point.transformation.t.copy()
                old_frac = universe.cell.to_fractional(old_pos)
                old_index = numpy.floor(old_frac).astype(int)
                old_pos -= universe.cell.to_cartesian(old_index)
                old_frac -= old_index
                del old_index
                # make copies
                for cell_index in xrange(index_min, index_max):
                    position = old_pos + universe.cell.matrix[:,
                                                              axis] * cell_index
                    if (old_frac[axis] + cell_index < interval[0]) or (
                            old_frac[axis] + cell_index > interval[1]):
                        continue
                    state = old_point.__getstate__()
                    state["transformation"] = state[
                        "transformation"].copy_with(t=position)
                    new_point = old_point.__class__(**state)
                    new_points[(old_point, cell_index)] = new_point

            new_connections = []
            # replication of the connections
            for cell_index in xrange(index_min - 1, index_max + 1):
                for connection in old_connections:
                    old_target0 = connection.children[0].target
                    new_target0 = new_points.get((old_target0, cell_index))
                    if new_target0 is None: continue

                    new_targets = [new_target0]

                    for reference in connection.children[1:]:
                        abort = True
                        old_target1 = reference.target
                        for offset in 0, 1, -1:
                            new_target1 = new_points.get(
                                (old_target1, cell_index + offset))
                            if new_target1 is not None:
                                delta = new_target0.transformation.t - new_target1.transformation.t
                                if vector_acceptable(
                                        delta, universe.cell.matrix[:, axis]):
                                    new_targets.append(new_target1)
                                    abort = False
                                    break
                        if abort: break
                    if abort:
                        del new_targets
                        continue

                    state = connection.__getstate__()
                    state["targets"] = new_targets
                    new_connections.append(connection.__class__(**state))

            # remove the existing points and connections

            for node in old_connections:
                primitive.Delete(node)
            del old_connections
            for node in old_points:
                primitive.Delete(node)
            del old_points

            # remove the periodicity

            new_active = universe.cell.active.copy()
            new_active[axis] = False
            new_cell = universe.cell.copy_with(active=new_active)
            primitive.SetProperty(universe, "cell", new_cell)

            # add the new nodes

            for node in new_points.itervalues():
                primitive.Add(node, universe)

            for connection in new_connections:
                primitive.Add(connection, universe)
Exemple #18
0
 def do(self):
     for node in context.application.cache.transformed_nodes:
         primitive.SetProperty(node, "transformation",
                               node.Transformation.identity())
Exemple #19
0
    def do(self):
        class Record(object):
            def __init__(self, name, quaternion):
                self.name = name
                self.quaternion = quaternion
                self.cost_function = 0.0

        rounded_quaternions = []

        cache = context.application.cache
        if len(cache.nodes) == 1:
            victim = cache.last
            master = None
            factor, selected_quaternion = rotation_matrix_to_quaternion(
                victim.transformation.r)
        elif len(cache.nodes) == 2:
            master = cache.last
            victim = cache.next_to_last
            factor, selected_quaternion = rotation_matrix_to_quaternion(
                victim.get_frame_relative_to(master).r)

        step = 15
        for axis_name, axis in self.axes.iteritems():
            for angle_index in xrange(1, 360 / step):
                angle = angle_index * step
                name = "%s (%s)" % (axis_name, angle)
                rad = angle * numpy.pi / 360
                quaternion = numpy.concatenate(
                    ([numpy.cos(rad)], numpy.sin(rad) * axis), 1)
                rounded_quaternions.append(Record(name, quaternion))

        new_quaternions = [
            Record("Identity", numpy.array([1.0, 0.0, 0.0, 0.0]))
        ]
        for record1 in rounded_quaternions:
            for record2 in rounded_quaternions:
                if record1.name[0] != record2.name[0]:
                    new_quaternions.append(
                        Record(
                            "%s after %s" % (record1.name, record2.name),
                            quaternion_product(record1.quaternion,
                                               record2.quaternion)))

        def filter_out_high_cost(records):
            for record in records:
                #print selected_quaternion, record.quaternion
                cosine = numpy.dot(selected_quaternion, record.quaternion)
                if cosine > 1: cosine = 1
                elif cosine < -1: cosine = -1
                cost_function = int(numpy.arccos(cosine) * 180.0 / numpy.pi)
                if cost_function < 10:
                    record.cost_function = cost_function
                else:
                    record.quaternion = None

            return filter(lambda record: record.quaternion is not None,
                          records)

        new_quaternions = filter_out_high_cost(new_quaternions)

        for index1, record1 in enumerate(new_quaternions):
            if record1.quaternion is not None:
                for record2 in new_quaternions[:index1]:
                    if record2.quaternion is not None:
                        if 1 - numpy.dot(record1.quaternion,
                                         record2.quaternion) < 1e-3:
                            record2.quaternion = None
                for record2 in rounded_quaternions:
                    if 1 - numpy.dot(record1.quaternion,
                                     record2.quaternion) < 1e-3:
                        record1.quaternion = None
                        break

        new_quaternions = filter(lambda record: record.quaternion is not None,
                                 new_quaternions)

        rounded_quaternions = filter_out_high_cost(rounded_quaternions)

        rounded_quaternions.extend(new_quaternions)

        if len(rounded_quaternions) == 0:
            raise UserError("No similar rounded rotations found.")

        rounded_quaternions.sort(key=(lambda x: x.cost_function))

        self.select_quaternion.main_field.records = rounded_quaternions
        user_record = Record("", rounded_quaternions[0].quaternion)
        if self.select_quaternion.run(user_record) != gtk.RESPONSE_OK:
            raise CancelException

        if user_record.quaternion is not None:
            if len(cache.nodes) == 1:
                r = factor * quaternion_to_rotation_matrix(
                    user_record.quaternion)
                new_transformation = victim.transformation.copy_with(r=r)
                primitive.SetProperty(victim, "transformation",
                                      new_transformation)
            elif len(cache.nodes) == 2:
                r = numpy.dot(
                    master.get_frame_relative_to(victim).r,
                    factor *
                    quaternion_to_rotation_matrix(user_record.quaternion),
                )
                old_transformation = victim.transformation.copy_with(r=r)
                primitive.SetProperty(victim,
                                      "transformation",
                                      old_transformation,
                                      done=True)