예제 #1
0
    def run(self):
        """ Extracts floors in the following steps:
        1. Searchs for the specified object.
        2. Splits the surfaces which point upwards at a specified level away.
        """

        mesh_objects = []
        for obj in self.config.get_list("selector"):
            if obj.type != "MESH":
                warnings.warn(
                    "The object: {} is not a mesh but was selected in the FloorExtractor!"
                    .format(obj.name))
                continue
            mesh_objects.append(MeshObject(obj))

        floors = FloorExtractor.extract(
            mesh_objects=mesh_objects,
            compare_angle_degrees=radians(
                self.config.get_float('compare_angle_degrees', 7.5)),
            compare_height=self.config.get_float('compare_height', 0.15),
            new_name_for_object=self.config.get_string("name_for_split_obj",
                                                       "Floor"),
            should_skip_if_object_is_already_there=self.config.get_bool(
                "should_skip_if_object_is_already_there", False))

        add_properties = self.config.get_raw_dict("add_properties", {})
        if add_properties:
            config = Config({"add_properties": add_properties})
            loader_interface = LoaderInterface(config)
            loader_interface._set_properties(floors)
예제 #2
0
    def run(self):

        if not os.path.exists(self.json_path):
            raise Exception("The given path does not exists: {}".format(
                self.json_path))
        if not self.json_path.endswith(".json"):
            raise Exception(
                "The given path does not point to a .json file: {}".format(
                    self.json_path))
        if not os.path.exists(self.future_model_path):
            raise Exception(
                "The 3D future model path does not exist: {}".format(
                    self.future_model_path))

        # load data from json file
        with open(self.json_path, "r") as json_file:
            data = json.load(json_file)

        self._create_mesh_objects_from_file(data)

        all_loaded_furniture = self._load_furniture_objs(data)

        self._move_and_duplicate_furniture(data, all_loaded_furniture)

        # add an identifier to the obj
        for obj in self.created_objects:
            obj["is_3d_front"] = True

        LoaderInterface._set_properties(self.created_objects)
    def run(self):
        """ Extracts floors in the following steps:
        1. Searchs for the specified object.
        2. Splits the surfaces which point upwards at a specified level away.
        """

        entities = self.config.get_list("selector")
        compare_angle = radians(
            self.config.get_float('compare_angle_degrees', 7.5))
        compare_height = self.config.get_float('compare_height', 0.15)
        new_name_for_object = self.config.get_string("name_for_split_obj",
                                                     "Floor")
        add_properties = self.config.get_raw_dict("add_properties", {})

        # set the up_vector
        up_vec = mathutils.Vector([0, 0, 1])
        up_vec_upwards = self.config.get_bool("up_vector_upwards", True)
        if not up_vec_upwards:
            up_vec *= -1.0

        height_list = []
        if self.config.has_param("height_list_path"):
            height_file_path = Utility.resolve_path(
                self.config.get_string('height_list_path'))
            with open(height_file_path) as file:
                import ast
                height_list = [
                    float(val) for val in ast.literal_eval(file.read())
                ]

        bpy.ops.object.select_all(action='DESELECT')
        newly_created_objects = []
        for obj in entities:
            if obj.type != "MESH":
                warnings.warn(
                    "The object: {} is not a mesh but was selected in the FloorExtractor!"
                    .format(obj.name))
                continue
            obj.select_set(True)
            bpy.context.view_layer.objects.active = obj
            bpy.ops.object.mode_set(mode='EDIT')
            mesh = obj.data
            bm = bmesh.from_edit_mesh(mesh)
            bm.faces.ensure_lookup_table()

            if height_list:
                bpy.ops.mesh.select_all(action='DESELECT')
                counter = 0
                for height_val in height_list:
                    counter = FloorExtractor.split_at_height_value(
                        bm, height_val, compare_height, up_vec, compare_angle,
                        obj.matrix_world)
                if counter:
                    bpy.ops.mesh.separate(type='SELECTED')
            else:
                try:
                    from sklearn.cluster import MeanShift, estimate_bandwidth
                except ImportError:
                    raise ImportError(
                        "If no height_list_path is defined, the sklearn lib has to be installed: "
                        "By adding \"scikit-learn\" to the \"setup\"/\"pip\" in the config file."
                    )

                # no height list was provided, try to estimate them on its own

                # first get a list of all height values of the median points, which are inside of the defined
                # compare angle range
                list_of_median_poses = [
                    FloorExtractor.get_median_face_pose(f, obj.matrix_world)[2]
                    for f in bm.faces if FloorExtractor.check_face_angle(
                        f, obj.matrix_world, up_vec, compare_angle)
                ]

                list_of_median_poses = np.reshape(list_of_median_poses,
                                                  (-1, 1))
                # The following bandwidth can be automatically detected using
                bandwidth = estimate_bandwidth(list_of_median_poses,
                                               quantile=0.2,
                                               n_samples=500)

                ms = MeanShift(bandwidth=bandwidth, bin_seeding=True)
                ms.fit(list_of_median_poses)

                # if the up vector is negative the maximum value is searched
                if up_vec_upwards:
                    height_value = np.min(ms.cluster_centers_)
                else:
                    height_value = np.max(ms.cluster_centers_)
                bpy.ops.mesh.select_all(action='DESELECT')
                counter = FloorExtractor.split_at_height_value(
                    bm, height_value, compare_height, up_vec, compare_angle,
                    obj.matrix_world)
                if counter:
                    bpy.ops.mesh.separate(type='SELECTED')
                selected_objects = bpy.context.selected_objects
                if selected_objects:
                    if len(selected_objects) == 2:
                        selected_objects = [
                            o for o in selected_objects
                            if o != bpy.context.view_layer.objects.active
                        ]
                        selected_objects[0].name = new_name_for_object
                        newly_created_objects.append(selected_objects[0])
                    else:
                        raise Exception(
                            "There is more than one selection after splitting, this should not happen!"
                        )
                else:
                    raise Exception("No floor object was constructed!")

            bpy.ops.object.mode_set(mode='OBJECT')
            bpy.ops.object.select_all(action='DESELECT')

        if add_properties:
            config = Config({"add_properties": add_properties})
            loader_interface = LoaderInterface(config)
            loader_interface._set_properties(newly_created_objects)
예제 #4
0
    def run(self):
        """ Extracts floors in the following steps:
        1. Searchs for the specified object.
        2. Splits the surfaces which point upwards at a specified level away.
        """

        entities = self.config.get_list("selector")
        compare_angle = radians(
            self.config.get_float('compare_angle_degrees', 7.5))
        compare_height = self.config.get_float('compare_height', 0.15)
        new_name_for_object = self.config.get_string("name_for_split_obj",
                                                     "Floor")
        add_properties = self.config.get_raw_dict("add_properties", {})
        should_skip_if_object_is_already_there = self.config.get_bool(
            "should_skip_if_object_is_already_there", False)

        # set the up_vector
        up_vec = mathutils.Vector([0, 0, 1])
        up_vec_upwards = self.config.get_bool("up_vector_upwards", True)
        if not up_vec_upwards:
            up_vec *= -1.0

        height_list = []
        if self.config.has_param("height_list_path"):
            height_file_path = Utility.resolve_path(
                self.config.get_string('height_list_path'))
            with open(height_file_path) as file:
                import ast
                height_list = [
                    float(val) for val in ast.literal_eval(file.read())
                ]

        object_names = [
            obj.name for obj in bpy.context.scene.objects if obj.type == "MESH"
        ]

        def clean_up_name(name: str):
            """
            Clean up the given name from Floor1 to floor

            :param name: given name
            :return: str: cleaned up name
            """
            name = ''.join([i for i in name
                            if not i.isdigit()])  # remove digits
            name = name.lower().replace(
                ".", "").strip()  # remove dots and whitespace
            return name

        object_names = [clean_up_name(name) for name in object_names]
        if should_skip_if_object_is_already_there and new_name_for_object.lower(
        ) in object_names:
            # if should_skip is True and if there is an object, which name is the same as the one for the newly
            # split object, than the execution is skipped
            return

        bpy.ops.object.select_all(action='DESELECT')
        newly_created_objects = []
        for obj in entities:
            if obj.type != "MESH":
                warnings.warn(
                    "The object: {} is not a mesh but was selected in the FloorExtractor!"
                    .format(obj.name))
                continue
            obj.select_set(True)
            bpy.context.view_layer.objects.active = obj
            bpy.ops.object.mode_set(mode='EDIT')
            mesh = obj.data
            bm = bmesh.from_edit_mesh(mesh)
            bm.faces.ensure_lookup_table()

            if height_list:
                bpy.ops.mesh.select_all(action='DESELECT')
                counter = 0
                for height_val in height_list:
                    counter = FloorExtractor.split_at_height_value(
                        bm, height_val, compare_height, up_vec, compare_angle,
                        obj.matrix_world)
                if counter:
                    bpy.ops.mesh.separate(type='SELECTED')
            else:
                try:
                    from sklearn.cluster import MeanShift, estimate_bandwidth
                except ImportError:
                    raise ImportError(
                        "If no height_list_path is defined, the sklearn lib has to be installed: "
                        "By adding \"scikit-learn\" to the \"setup\"/\"pip\" in the config file."
                    )

                # no height list was provided, try to estimate them on its own

                # first get a list of all height values of the median points, which are inside of the defined
                # compare angle range
                list_of_median_poses = [
                    FloorExtractor.get_median_face_pose(f, obj.matrix_world)[2]
                    for f in bm.faces if FloorExtractor.check_face_angle(
                        f, obj.matrix_world, up_vec, compare_angle)
                ]
                if not list_of_median_poses:
                    print(
                        "Object with name: {} is skipped no faces were relevant, try with "
                        "flipped up_vec".format(obj.name))
                    list_of_median_poses = [
                        FloorExtractor.get_median_face_pose(
                            f, obj.matrix_world)[2] for f in bm.faces
                        if FloorExtractor.check_face_angle(
                            f, obj.matrix_world, -up_vec, compare_angle)
                    ]
                    if not list_of_median_poses:
                        print("Still no success for: {} skip object.".format(
                            obj.name))
                        bpy.ops.object.mode_set(mode='OBJECT')
                        bpy.ops.object.select_all(action='DESELECT')
                        continue

                    successful_up_vec = -up_vec
                else:
                    successful_up_vec = up_vec

                list_of_median_poses = np.reshape(list_of_median_poses,
                                                  (-1, 1))
                if np.var(list_of_median_poses) < 1e-4:
                    # All faces are already correct
                    height_value = np.mean(list_of_median_poses)
                else:
                    ms = MeanShift(bandwidth=0.2, bin_seeding=True)
                    ms.fit(list_of_median_poses)

                    # if the up vector is negative the maximum value is searched
                    if up_vec_upwards:
                        height_value = np.min(ms.cluster_centers_)
                    else:
                        height_value = np.max(ms.cluster_centers_)
                bpy.ops.mesh.select_all(action='DESELECT')
                counter = FloorExtractor.split_at_height_value(
                    bm, height_value, compare_height, successful_up_vec,
                    compare_angle, obj.matrix_world)
                if counter:
                    bpy.ops.mesh.separate(type='SELECTED')
                selected_objects = bpy.context.selected_objects
                if selected_objects:
                    if len(selected_objects) == 2:
                        selected_objects = [
                            o for o in selected_objects
                            if o != bpy.context.view_layer.objects.active
                        ]
                        selected_objects[0].name = new_name_for_object
                        newly_created_objects.append(selected_objects[0])
                    else:
                        raise Exception(
                            "There is more than one selection after splitting, this should not happen!"
                        )
                else:
                    raise Exception("No floor object was constructed!")

            bpy.ops.object.mode_set(mode='OBJECT')
            bpy.ops.object.select_all(action='DESELECT')

        if add_properties:
            config = Config({"add_properties": add_properties})
            loader_interface = LoaderInterface(config)
            loader_interface._set_properties(newly_created_objects)