Пример #1
0
    def replace(obj_to_remove: MeshObject,
                obj_to_add: MeshObject,
                check_collision_with: [MeshObject] = [],
                scale: bool = True,
                relative_pose_sampler: Callable[[MeshObject], None] = None):
        """ Scale, translate, rotate obj_to_add to match obj_to_remove and check if there is a bounding box collision
        returns a boolean.

        :param obj_to_remove: An object to remove from the scene.
        :param obj_to_add: An object to put in the scene instead of obj_to_remove.
        :param check_collision_with: A list of objects, which are not checked for collisions with.
        :param scale: Scales obj_to_add to match obj_to_remove dimensions.
        :param relative_pose_sampler: A function that randomly perturbs the pose of the object to replace with (after it has been aligned to the object to replace).
        """
        # New object takes location, rotation and rough scale of original object
        obj_to_add.set_location(obj_to_remove.get_location())
        obj_to_add.set_rotation_euler(obj_to_remove.get_rotation())
        if scale:
            obj_to_add.set_scale(
                ObjectReplacer._bb_ratio(obj_to_remove.get_bound_box(True),
                                         obj_to_add.get_bound_box(True)))
        if relative_pose_sampler is not None:
            relative_pose_sampler(obj_to_add)
        bpy.context.view_layer.update()

        # Check for collision between the new object and other objects in the scene
        for obj in check_collision_with:  # for each object
            if obj != obj_to_add and obj_to_remove != obj:
                if check_bb_intersection(obj.blender_obj,
                                         obj_to_add.blender_obj):
                    if check_intersection(obj.blender_obj,
                                          obj_to_add.blender_obj)[0]:
                        return False
        return True
Пример #2
0
    def run(self):
        """ 
        For each object,
            1- Place the object outside sampling volume
            2- Until we have objects remaining and have not run out of tries, Sample a point
            3- Put the top object in queue at the sampled point
            4- If no collision then keep the position else reset
        Here we use any general sampling method supported by us
        """
        pos_sampler_params = self.config.get_raw_dict("pos_sampler")
        rot_sampler_params = self.config.get_raw_dict("rot_sampler")

        # 2- Until we have objects remaining and have not run out of tries, Sample a point
        placed = []  # List of objects successfully placed
        max_tries = self.config.get_int(
            "max_iterations", 1000
        )  # After this many tries we give up on current object and continue with rest
        cache = {}  # cache to fasten collision detection
        for obj in bpy.context.scene.objects:  # for each object
            if obj.type == "MESH":
                print("Trying to put ", obj.name)
                prior_location = obj.location
                prior_rotation = obj.rotation_euler
                no_collision = True
                for i in range(max_tries):  # Try max_iter amount of times
                    # 3- Put the top object in queue at the sampled point in space
                    position = Utility.sample_based_on_config(
                        pos_sampler_params)
                    rotation = Utility.sample_based_on_config(
                        rot_sampler_params)
                    obj.location = position  # assign it a new position
                    obj.rotation_euler = rotation  # and a rotation
                    bpy.context.view_layer.update()  # then udpate scene
                    no_collision = True
                    for already_placed in placed:  # Now check for collisions
                        intersection = check_bb_intersection(
                            obj, already_placed
                        )  # First check if bounding boxes collides
                        if intersection:  # if they do
                            intersection, cache = check_intersection(
                                obj, already_placed
                            )  # then check for more refined collisions
                        if intersection:
                            no_collision = False
                            break
                    # 4- If no collision then keep the position else reset
                    if no_collision:  # if no collisions
                        print("No collision detected, Moving forward!")
                        placed.append(obj)
                        break  # then stop trying and keep assigned position and orientation
                    else:  # if any collisions then reset object to initial state
                        print("collision detected, Retrying!!")
                        obj.location = prior_location
                        obj.rotation_euler = prior_rotation
                        bpy.context.view_layer.update()
                if not no_collision:
                    print("giving up on ", obj.name)
Пример #3
0
    def check_pose_for_object(obj: bpy.types.Object,
                              position: mathutils.Vector,
                              rotation: mathutils.Vector, bvh_cache: dict,
                              objects_to_check_against: list,
                              list_of_objects_with_no_inside_check: list):
        """
        Checks if a object placed at the given pose intersects with any object given in the list.

        The bvh_cache adds all current objects to the bvh tree, which increases the speed.

        If an object is already in the cache it is removed, before performing the check.

        :param obj: Object which should be checked. Type: :class:`bpy.types.Object`
        :param position: 3D Vector of the location of the object. Type: :class:`mathutils.Vector`
        :param rotation: 3D Vector of the rotation in euler angles. If this is None, the rotation is not changed \
                         Type: :class:`mathutils.Vector`
        :param bvh_cache: Dict of all the bvh trees, removes the `obj` from the cache before adding it again. \
                          Type: :class:`dict`
        :param objects_to_check_against: List of objects which the object is checked again \
                                         Type: :class:`list`
        :param list_of_objects_with_no_inside_check: List of objects on which no inside check is performed. \
                                                     This check is only done for the objects in \
                                                     `objects_to_check_against`. Type: :class:`list`
        :return: Type: :class:`bool`, True if no collision was found, false if at least one collision was found
        """
        # assign it a new pose
        obj.location = position
        if rotation:
            obj.rotation_euler = rotation
        bpy.context.view_layer.update()
        # Remove bvh cache, as object has changed
        if obj.name in bvh_cache:
            del bvh_cache[obj.name]

        no_collision = True
        # Now check for collisions
        for already_placed in objects_to_check_against:
            # First check if bounding boxes collides
            intersection = check_bb_intersection(obj, already_placed)
            # if they do
            if intersection:
                skip_inside_check = already_placed in list_of_objects_with_no_inside_check
                # then check for more refined collisions
                intersection, bvh_cache = check_intersection(
                    obj,
                    already_placed,
                    bvh_cache=bvh_cache,
                    skip_inside_check=skip_inside_check)
            if intersection:
                no_collision = False
                break
        return no_collision
Пример #4
0
    def collision(first_obj, second_obj):
        """ Checks if two object intersect.

        :param first_obj: The first object for which the check is carried out. Type: blender object.
        :param second_obj: The second object for which the check is carried out. Type: blender Object.
        :return: True if objects are intersecting, if not - False.
        """
        intersection = check_bb_intersection(first_obj, second_obj)
        if intersection:
            # check for more refined collisions
            intersection, cache = check_intersection(first_obj, second_obj)

        return intersection
Пример #5
0
    def _replace_and_edit(self, obj_to_remove, obj_to_add, scale=True):
        """
        Scale, translate, rotate obj_to_add to match obj_to_remove and check if there is a bounding box collision
        returns a boolean.

        :param obj_to_remove: object to remove from the scene. Type: blender object.
        :param obj_to_add: object to put in the scene instead of obj_to_remove. Type: blender object.
        :param scale: Scales obj_to_add to match obj_to_remove dimensions. Type: bool.
        """
        def _bb_ratio(bb1, bb2):
            """
            Rough estimation of the ratios between two bounding boxes sides, not axis aligned

            :param bb1: bounding box 1. Type: float multi-dimensional array of 8 * 3.
            :param bb2: bounding box 2. Type: float multi-dimensional array of 8 * 3.
            returns the ratio between each side of the bounding box. Type: a list of floats.
            """
            def _two_points_distance(point1, point2):
                """
                Eclidian distance between two points

                :param point1: Point 1 as a list of three floats. Type: list.
                :param point2: Point 2 as a list of three floats. Type: list.
                returns a float.
                """
                return np.linalg.norm(np.array(point1) - np.array(point2))

            ratio_a = _two_points_distance(
                bb1[0], bb1[3]) / _two_points_distance(bb2[0], bb2[3])
            ratio_b = _two_points_distance(
                bb1[0], bb1[4]) / _two_points_distance(bb2[0], bb2[4])
            ratio_c = _two_points_distance(
                bb1[0], bb1[1]) / _two_points_distance(bb2[0], bb2[1])
            return [ratio_a, ratio_b, ratio_c]

        # New object takes location, rotation and rough scale of original object
        obj_to_add.location = obj_to_remove.location
        obj_to_add.rotation_euler = obj_to_remove.rotation_euler
        if scale:
            obj_to_add.scale = _bb_ratio(obj_to_remove.bound_box,
                                         obj_to_add.bound_box)

        # Check for collision between the new object and other objects in the scene
        for obj in get_all_mesh_objects():  # for each object

            if obj != obj_to_add and obj_to_remove != obj and obj not in self._ignore_collision_with:
                if check_bb_intersection(obj, obj_to_add):
                    if check_intersection(obj, obj_to_add)[0]:
                        return False
        return True
Пример #6
0
    def run(self):
        """
        Samples positions and rotations of selected object inside the sampling volume while performing mesh and
        bounding box collision checks in the following steps:
        1. While we have objects remaining and have not run out of tries - sample a point. 
        2. If no collisions are found keep the point.
        """
        # While we have objects remaining and have not run out of tries - sample a point
        # List of successfully placed objects
        placed = []
        # After this many tries we give up on current object and continue with the rest
        max_tries = self.config.get_int("max_iterations", 1000)
        objects = self.config.get_list("objects_to_sample",
                                       get_all_mesh_objects())

        # cache to fasten collision detection
        bvh_cache = {}

        # for every selected object
        for obj in objects:
            if obj.type == "MESH":
                no_collision = True

                # Try max_iter amount of times
                for i in range(max_tries):

                    # Put the top object in queue at the sampled point in space
                    position = self.config.get_vector3d("pos_sampler")
                    rotation = self.config.get_vector3d("rot_sampler")
                    # assign it a new pose
                    obj.location = position
                    obj.rotation_euler = rotation
                    bpy.context.view_layer.update()
                    # Remove bvh cache, as object has changed
                    if obj.name in bvh_cache:
                        del bvh_cache[obj.name]

                    no_collision = True

                    # Now check for collisions
                    for already_placed in placed:
                        # First check if bounding boxes collides
                        intersection = check_bb_intersection(
                            obj, already_placed)
                        # if they do
                        if intersection:
                            # then check for more refined collisions
                            intersection, bvh_cache = check_intersection(
                                obj, already_placed, bvh_cache=bvh_cache)

                        if intersection:
                            no_collision = False
                            break

                    # If no collision then keep the position
                    if no_collision:
                        break

                placed.append(obj)

                if not no_collision:
                    print("Could not place " + obj.name +
                          " without a collision.")
                else:
                    print("It took " + str(i + 1) + " tries to place " +
                          obj.name)
    def run(self):
        # While we have objects remaining and have not run out of tries - sample a point
        # List of successfully placed objects
        placed = []
        # After this many tries we give up on current object and continue with the rest
        max_tries = self.config.get_int("max_iterations", 1000)
        objects = self.config.get_list("objects_to_sample")

        # cache to fasten collision detection
        cache = {}

        # for every selected object
        for obj in objects:
            if obj.type == "MESH":

                print("Trying to put ", obj.name)
                prior_location = obj.location
                prior_rotation = obj.rotation_euler
                no_collision = True

                # Try max_iter amount of times
                for i in range(max_tries):

                    # Put the top object in queue at the sampled point in space
                    position = self.config.get_vector3d("pos_sampler")
                    rotation = self.config.get_vector3d("rot_sampler")
                    # assign it a new position
                    obj.location = position
                    # and a rotation
                    obj.rotation_euler = rotation
                    # then update scene
                    bpy.context.view_layer.update()
                    no_collision = True

                    # Now check for collisions
                    for already_placed in placed:
                        # First check if bounding boxes collides
                        intersection = check_bb_intersection(
                            obj, already_placed)
                        # if they do
                        if intersection:
                            # then check for more refined collisions
                            intersection, cache = check_intersection(
                                obj, already_placed)

                        if intersection:
                            no_collision = False
                            break

                    # If no collision then keep the position, else: reset
                    if no_collision:
                        print("No collision detected, moving forward!")
                        placed.append(obj)
                        # then stop trying and keep assigned position and orientation
                        break
                    # if any collisions then reset object to initial state
                    else:
                        print("Collision detected, retrying!!")
                        obj.location = prior_location
                        obj.rotation_euler = prior_rotation
                        bpy.context.view_layer.update()
                if not no_collision:
                    print("Giving up on ", obj.name)