def __init__(self, parent=None, show=True, off_screen=True):
        MainWindow.__init__(self, parent)

        self.frame = QFrame()
        vlayout = QVBoxLayout()
        self.vtk_widget = QtInteractor(
            parent=self.frame,
            off_screen=off_screen,
            stereo=False,
        )
        vlayout.addWidget(self.vtk_widget.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        mainMenu = _create_menu_bar(parent=self)

        fileMenu = mainMenu.addMenu('File')
        self.exit_action = QAction('Exit', self)
        self.exit_action.setShortcut('Ctrl+Q')
        self.exit_action.triggered.connect(self.close)
        fileMenu.addAction(self.exit_action)

        meshMenu = mainMenu.addMenu('Mesh')
        self.add_sphere_action = QAction('Add Sphere', self)
        self.exit_action.setShortcut('Ctrl+A')
        self.add_sphere_action.triggered.connect(self.add_sphere)
        meshMenu.addAction(self.add_sphere_action)

        self.signal_close.connect(self.vtk_widget.close)

        if show:
            self.show()
Example #2
0
File: test.py Project: yetisir/krak
    def __init__(self, parent=None, show=True):
        Qt.QMainWindow.__init__(self, parent)

        # create the frame
        self.frame = Qt.QFrame()
        vlayout = Qt.QVBoxLayout()

        # add the pyvista interactor object
        self.plotter = QtInteractor(self.frame)
        vlayout.addWidget(self.plotter.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        # simple menu to demo functions
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        exitButton = Qt.QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        # allow adding a sphere
        meshMenu = mainMenu.addMenu('Mesh')
        self.add_sphere_action = Qt.QAction('Add Sphere', self)
        self.add_sphere_action.triggered.connect(self.add_sphere)
        meshMenu.addAction(self.add_sphere_action)

        if show:
            self.show()
Example #3
0
class Bp3DWidget(QWidget):

    def __init__(self):
        super(Bp3DWidget, self).__init__()
        self.frame = QFrame()
        layout = QVBoxLayout()
        colormap = ColorMap(*zip(*Gradients["bipolar"]["ticks"])).getLookupTable() / 255.0
        self.colormap = ListedColormap(colormap)
        self.plotter = QtInteractor(self.frame)
        layout.addWidget(self.plotter.interactor)
        self.frame.setLayout(layout)
        self.setLayout(layout)

    @pyqtSlot()
    def on_data_changed(self):
        sender = self.sender()
        grid = Bp3DWidget.__create_grid(sender.data)
        self.plotter.clear()
        self.plotter.add_mesh(grid, scalars=np.rot90(sender.data)[::-1], cmap=self.colormap)

    @staticmethod
    def __create_grid(data):
        shape = np.shape(data)
        # M.B. plot add to params
        az_grid = np.deg2rad(np.linspace(-60, 60, shape[1]))
        el_grid = np.deg2rad(np.linspace(-60, 60, shape[0]))
        az_mesh, el_mesh = np.meshgrid(az_grid, el_grid)
        r_mesh = data
        x_mesh = np.cos(az_mesh) * np.cos(el_mesh) * r_mesh
        y_mesh = np.sin(az_mesh) * np.cos(el_mesh) * r_mesh
        z_mesh = np.sin(el_mesh) * r_mesh
        grid = pv.StructuredGrid(x_mesh, y_mesh, z_mesh)
        return grid
Example #4
0
    def __init__(self, parent=None, show=True):
        Qt.QMainWindow.__init__(self, parent)

        # create the frame
        self.frame = Qt.QFrame()
        vlayout = Qt.QVBoxLayout()

        # add the pyvista interactor object
        self.plotter = QtInteractor(self.frame)
        vlayout.addWidget(self.plotter.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        # simple menu
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        editMenu = mainMenu.addMenu('Edit')

        # opening a mesh file
        self.open_mesh_action = Qt.QAction('Open Mesh...', self)
        self.open_mesh_action.triggered.connect(self.open_mesh)
        fileMenu.addAction(self.open_mesh_action)

        # exit button
        exitButton = Qt.QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        # create cubic skeleton
        self.cubic_skeleton_action = Qt.QAction('Cubic Skeleton', self)
        self.cubic_skeleton_action.triggered.connect(self.cubic_skeleton)
        editMenu.addAction(self.cubic_skeleton_action)

        # Create voxelized model
        self.create_voxelized_model = Qt.QAction('Voxelize Model', self)
        self.create_voxelized_model.triggered.connect(self.voxelize_model)
        editMenu.addAction(self.create_voxelized_model)

        # split mesh based on max cube faces
        # self.max_cube_slice_action = Qt.QAction('Slice27', self)
        # self.cubic_skeleton_action.triggered.connect(self.max_cube_slice)
        # editMenu.addAction(self.max_cube_slice_action)

        if show:
            self.show()

        self.plotter.add_axes(interactive=None,
                              line_width=2,
                              color=None,
                              x_color=None,
                              y_color=None,
                              z_color=None,
                              xlabel='X',
                              ylabel='Y',
                              zlabel='Z',
                              labels_off=False,
                              box=None,
                              box_args=None)
Example #5
0
 def __init__(self):
     super(Bp3DWidget, self).__init__()
     self.frame = QFrame()
     layout = QVBoxLayout()
     colormap = ColorMap(*zip(*Gradients["bipolar"]["ticks"])).getLookupTable() / 255.0
     self.colormap = ListedColormap(colormap)
     self.plotter = QtInteractor(self.frame)
     layout.addWidget(self.plotter.interactor)
     self.frame.setLayout(layout)
     self.setLayout(layout)
Example #6
0
    def __init__(self, parent=None, show=True):
        Qt.QMainWindow.__init__(self, parent)

        # create the frame
        self.frame = Qt.QFrame()
        vlayout = Qt.QVBoxLayout()

        # add the pyvista interactor object
        self.plotter = QtInteractor(self.frame)

        vlayout.addWidget(self.plotter.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        # simple menu
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        editMenu = mainMenu.addMenu('Edit')
        
        # opening a mesh file
        self.open_mesh_action = Qt.QAction('Open Mesh...', self)
        self.open_mesh_action.triggered.connect(self.open_mesh)
        fileMenu.addAction(self.open_mesh_action)

        # set centroid
        self.skewed_centroid_action = Qt.QAction('Skewed Centroid', self)
        self.skewed_centroid_action.triggered.connect(self.skewed_centroid_check)
        fileMenu.addAction(self.skewed_centroid_action)
        
        # save screenshot
        self.save_screenshot_action = Qt.QAction('Save Screenshot', self)
        self.save_screenshot_action.triggered.connect(self.save_screenshot)
        fileMenu.addAction(self.save_screenshot_action)

        # exit button
        exitButton = Qt.QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)
        
        # Show max cube & raytracing process
        self.max_cube_action = Qt.QAction('Max Cube', self)
        self.max_cube_action.triggered.connect(self.show_max_cube)
        editMenu.addAction(self.max_cube_action)

        # Show max cube & raytracing process
        self.max_cuboid_action = Qt.QAction('Max Cuboid', self)
        self.max_cuboid_action.triggered.connect(self.max_cuboid)
        editMenu.addAction(self.max_cuboid_action)
        
        if show:
            self.show()
        
        self.plotter.add_axes(interactive=None, line_width=2, x_color=None, y_color=None, z_color=None, xlabel='X', ylabel='Y', zlabel='Z', labels_off=False, box= None, box_args=None)
Example #7
0
    def load_and_prep_query_mesh(self, data):
        if not data: return
        self.load_button.setFont(QtGui.QFont("arial", 10))

        # Normalize query mesh
        normed_data = self.normalizer.mono_run_pipeline(data)
        normed_mesh = pv.PolyData(normed_data["history"][-1]["data"]["vertices"], normed_data["history"][-1]["data"]["faces"])
        normed_data['poly_data'] = normed_mesh
        # Extract features
        n_singletons, n_distributionals, mapping_of_labels = get_sizes_features(features_file=self.config_data["FEATURE_DATA_FILE"],with_labels=True, drop_feat=["timestamp"])
        mapping_of_labels_reversed = {val: key for key, val in mapping_of_labels.items()}
        features_dict = FeatureExtractor.mono_run_pipeline_old(normed_data)
        features_dict_carefully_selected = OrderedDict(
            sorted({mapping_of_labels.get(key): val
                    for key, val in features_dict.items() if key in mapping_of_labels}.items(), key=lambda t: t[0]))
        features_df = pd.DataFrame([features_dict_carefully_selected]).T.reset_index()
        self.hist_labels = [val for key, val in mapping_of_labels.items() if "hist_" in key]
        self.skeleton_labels = [val for key, val in mapping_of_labels.items() if "skeleton_" in key]

        # feature_formatted_keys = sing_labels + dist_labels
        # features_df = pd.DataFrame({'key': list(feature_formatted_keys), 'value': list(
        #     [list(f) if isinstance(f, np.ndarray) else f for f in list(features_dict.values())[3:]])})

        # Update plotter & feature table
        # since unfortunately Qtinteractor which plots the mesh cannot be updated (remove and add new mesh)
        # it needs to be removed and newly generated each time a mesh gets loaded
        self.tableWidget.deleteLater()
        self.vlayout.removeWidget(self.QTIplotter)
        self.QTIplotter = QtInteractor(self.frame)
        self.vlayout.addWidget(self.QTIplotter)
        self.tableWidget = TableWidget(features_dict_carefully_selected, self, mapping_of_labels_reversed)
        self.tableWidget.show()
        self.tableWidget.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
        self.vlayout.addWidget(self.tableWidget)
        self.QTIplotter.add_mesh(normed_mesh, show_edges=True)
        self.QTIplotter.isometric_view()
        self.QTIplotter.show_bounds(grid='front', location='outer', all_edges=True)
        self.vlayout.addWidget(self.graphWidget)

        # Compare shapes
        if self.smlw:
            self.smlw.deleteLater()
            if len(self.smlw.smw_list) != 0: self.smlw.smw_list[0].deleteLater()
        self.smlw = SimilarMeshesListWindow(features_dict)

        self.buttons = self.tableWidget.get_buttons_in_table()
        self.hist_dict = features_df.set_index("index").tail(n=len(self.hist_labels)).to_dict()
        for key, value in self.buttons.items():
            value.clicked.connect(lambda state, x=key, y=features_dict_carefully_selected[key]: self.plot_selected_hist(x, y))
        self.smlw.show()
Example #8
0
    def __init__(self, mesh, features):
        super().__init__()
        with open('config.json') as f:
            self.config_data = json.load(f)
        self.setWindowTitle('Similar Mesh Window')
        self.mesh = mesh
        self.mesh_features = features
        self.QTIplotter = QtInteractor()
        self.vlayout = Qt.QVBoxLayout()
        self.setLayout(self.vlayout)

        # Create and add widgets to layout

        n_singletons, n_distributionals, mapping_of_labels = get_sizes_features(features_file=self.config_data["FEATURE_DATA_FILE"],with_labels=True, drop_feat=["timestamp"])
        mapping_of_labels_reversed = {val: key for key, val in mapping_of_labels.items()}
        features_dict_carefully_selected = OrderedDict(
            sorted({mapping_of_labels.get(key): val
                    for key, val in self.mesh_features.items() if key in mapping_of_labels}.items(), key=lambda t: t[0]))
        features_df = pd.DataFrame([features_dict_carefully_selected]).T.reset_index()
        self.hist_labels = [val for key, val in mapping_of_labels.items() if "hist_" in key]
        # Create Table widget
        self.tableWidget = TableWidget(features_dict_carefully_selected, self, mapping_of_labels_reversed)
        self.tableWidget.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)

        # Create Plots widget
        self.graphWidget = pg.PlotWidget()
        self.graphWidget.setBackground('w')

        self.hist_dict = features_df.set_index("index").tail(n=len(self.hist_labels)).to_dict()
        self.buttons = self.tableWidget.get_buttons_in_table()

        for key, value in self.buttons.items():
            value.clicked.connect(lambda state, x=key, y=features_dict_carefully_selected[key]: self.plot_selected_hist(x, y))

        self.vlayout.addWidget(self.QTIplotter.interactor)
        self.vlayout.addWidget(self.tableWidget)
        self.vlayout.addWidget(self.graphWidget)

        # Position SimilarMeshWindow
        screen_topleft = QDesktopWidget().availableGeometry().topLeft()
        screen_height = QDesktopWidget().availableGeometry().height()
        width = (QDesktopWidget().availableGeometry().width() * 0.4)
        self.move((QDesktopWidget().availableGeometry().width() * 0.4) + ((QDesktopWidget().availableGeometry().width() * 0.2)), 0)
        self.resize(width, screen_height - 50)

        # Set widgets
        self.QTIplotter.add_mesh(self.mesh, show_edges=True)
        self.QTIplotter.isometric_view()
        self.QTIplotter.show_bounds(grid='front', location='outer', all_edges=True)
Example #9
0
  def __init__(self, parent):
    super().__init__(parent)

    layout = QtWidgets.QHBoxLayout(self)
    layout.setContentsMargins(0, 0, 0, 0)
    
    self.plotter = QtInteractor(self, multi_samples=8, line_smoothing=True, point_smoothing=True)
    layout.addWidget(self.plotter.interactor)

    self.plotter.enable_anti_aliasing()
    
    for key in ["Up", "Down"]:
      self.plotter.clear_events_for_key(key)

    self.clear()    
    self.setLayout(layout)
    def __init__(self, parent=None, show=True):
        Qt.QMainWindow.__init__(self, parent)

        # create the frame
        self.frame = Qt.QFrame()
        vlayout = Qt.QVBoxLayout()

        # add the pyvista interactor object
        self.plotter = QtInteractor(self.frame)
        vlayout.addWidget(self.plotter.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        # simple menu
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        editMenu = mainMenu.addMenu('Edit')

        # opening a mesh file
        self.open_mesh_action = Qt.QAction('Open Mesh...', self)
        self.open_mesh_action.triggered.connect(self.open_mesh)
        fileMenu.addAction(self.open_mesh_action)

        # exit button
        exitButton = Qt.QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        #Create secondary cubes
        self.cubic_skeleton_action = Qt.QAction('Cubic Skeleton', self)
        self.cubic_skeleton_action.triggered.connect(self.cubic_skeleton)
        editMenu.addAction(self.cubic_skeleton_action)

        if show:
            self.show()
Example #11
0
class TstWindow(MainWindow):
    def __init__(self, parent=None, show=True, off_screen=True):
        MainWindow.__init__(self, parent)

        self.frame = QFrame()
        vlayout = QVBoxLayout()
        self.vtk_widget = QtInteractor(parent=self.frame,
                                       off_screen=off_screen)
        vlayout.addWidget(self.vtk_widget.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        mainMenu = _create_menu_bar(parent=self)

        fileMenu = mainMenu.addMenu('File')
        self.exit_action = QAction('Exit', self)
        self.exit_action.setShortcut('Ctrl+Q')
        self.exit_action.triggered.connect(self.close)
        fileMenu.addAction(self.exit_action)

        meshMenu = mainMenu.addMenu('Mesh')
        self.add_sphere_action = QAction('Add Sphere', self)
        self.exit_action.setShortcut('Ctrl+A')
        self.add_sphere_action.triggered.connect(self.add_sphere)
        meshMenu.addAction(self.add_sphere_action)

        self.signal_close.connect(self.vtk_widget.close)

        if show:
            self.show()

    def add_sphere(self):
        sphere = pyvista.Sphere(phi_resolution=6, theta_resolution=6)
        self.vtk_widget.add_mesh(sphere)
        self.vtk_widget.reset_camera()
Example #12
0
class MainWindow(Qt.QMainWindow):
    def __init__(self, parent=None, show=True):
        Qt.QMainWindow.__init__(self, parent)

        # create the frame
        self.frame = Qt.QFrame()
        vlayout = Qt.QVBoxLayout()

        # add the pyvista interactor object
        self.plotter = QtInteractor(self.frame)

        vlayout.addWidget(self.plotter.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        # simple menu
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        editMenu = mainMenu.addMenu('Edit')
        
        # opening a mesh file
        self.open_mesh_action = Qt.QAction('Open Mesh...', self)
        self.open_mesh_action.triggered.connect(self.open_mesh)
        fileMenu.addAction(self.open_mesh_action)

        # set centroid
        self.skewed_centroid_action = Qt.QAction('Skewed Centroid', self)
        self.skewed_centroid_action.triggered.connect(self.skewed_centroid_check)
        fileMenu.addAction(self.skewed_centroid_action)
        
        # save screenshot
        self.save_screenshot_action = Qt.QAction('Save Screenshot', self)
        self.save_screenshot_action.triggered.connect(self.save_screenshot)
        fileMenu.addAction(self.save_screenshot_action)

        # exit button
        exitButton = Qt.QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)
        
        # Show max cube & raytracing process
        self.max_cube_action = Qt.QAction('Max Cube', self)
        self.max_cube_action.triggered.connect(self.show_max_cube)
        editMenu.addAction(self.max_cube_action)

        # Show max cube & raytracing process
        self.max_cuboid_action = Qt.QAction('Max Cuboid', self)
        self.max_cuboid_action.triggered.connect(self.max_cuboid)
        editMenu.addAction(self.max_cuboid_action)
        
        if show:
            self.show()
        
        self.plotter.add_axes(interactive=None, line_width=2, x_color=None, y_color=None, z_color=None, xlabel='X', ylabel='Y', zlabel='Z', labels_off=False, box= None, box_args=None)
    
    def open_mesh(self):
        """ add a mesh to the pyqt frame """
        global int_surface, ext_surface, mesh_vol, mesh
        global x_range, y_range, z_range, Vol_centroid
        global open_mesh_run
        global mesh_name

        # track pre-processing starting time
        open_mesh_start = time.time()

        # open file
        file_info = QtWidgets.QFileDialog.getOpenFileName()
        file_path = file_info[0]
        
        # determine file type and if conversion needed
        _, file_name = os.path.split(file_path)
        mesh_name, mesh_type = os.path.splitext(file_name)

        # read mesh & transform according to principal axes
        print(file_path)
        pre = trimesh.load(file_path)
        orient = pre.principal_inertia_transform
        pre = pre.apply_transform(orient)
        post_file_path = 'data/'+ mesh_name + '_oriented.stl'
        pre.export(post_file_path)
        ext_surface = pv.read(post_file_path)

        # scale meshes accordingly
        if mesh_name == 'elephant':
            ext_surface.points *= 12  # Elephant
        elif mesh_name == 'Bracket S24D1':
            ext_surface.points /= 10  # Bracket
        elif mesh_name == 'knight':
            ext_surface.points /= 2 # Knight

        # create internal offset
        thickness = 0.1 # inches
        grid = pv.create_grid(ext_surface).triangulate()
        solid = grid.clip_surface(ext_surface)
        solid.compute_implicit_distance(ext_surface, inplace=True)
        imp_dis_max = max(solid['implicit_distance'])

        shell_threshold = imp_dis_max - thickness
        shell = solid.clip_scalar('implicit_distance', value = shell_threshold)
        int_surface = shell.extract_geometry()

        meshfix = mf.MeshFix(int_surface)
        meshfix.repair(verbose=True)

        mesh = solid.clip_surface(int_surface, invert=False)
        
        # print mesh info
        print("Mesh Name:", mesh_name)
        print("Mesh Type:", mesh_type[1:])

        # find mesh centroid and translate the mesh so that's the origin
        Vol_centroid = np.array([0,0,0])
        self.skewed_centroid_action.setCheckable(True)

        # reset plotter
        self.reset_plotter(Vol_centroid)

        # find the max and min of x,y,z axes of mesh
        ranges = mesh.bounds
        x_range = abs(ranges[0] - ranges[1])
        y_range = abs(ranges[2] - ranges[3])
        z_range = abs(ranges[4] - ranges[5])
        print("x:", float(format(x_range, ".2f")), "in")
        print("y:", float(format(y_range, ".2f")), "in")
        print("z:", float(format(z_range, ".2f")), "in")

        # mesh volume
        mesh_vol = float(format(mesh.volume, ".2f"))
        print("Mesh Volume:", mesh_vol, "in^3")

        # track pre-processing ending time & duration
        open_mesh_end = time.time()
        open_mesh_run = open_mesh_end - open_mesh_start
        print("Pre-Processing run time: %g seconds" % (open_mesh_run))

        print("Mesh Cells:", mesh.n_cells)

    def save_screenshot(self):
        ''' saves screenshot of current render window'''
        screenshot_path = 'screenshot/' + mesh_name + '.png'
        self.plotter.screenshot(screenshot_path)

    def skewed_centroid_check(self):
        ''' depending if the menu item is checked or not, the centroid is either skewed 
        with the 2nd moment of inertia or being the origin of the principal axes '''

        if self.skewed_centroid_action.isChecked():
            Vol_centroid = self.centroid(ext_surface)
        else:
            Vol_centroid = np.array([0,0,0])

        self.reset_plotter(Vol_centroid)

        return Vol_centroid

    def reset_plotter(self, Vol_centroid):
        """ clear plotter of mesh or interactive options """
        # clear plotter
        self.plotter.clear()
        
        # callback opened mesh
        self.plotter.add_mesh(mesh, show_edges = True, color="w", opacity=0.3)
        
        # show origin
        self.plotter.add_axes_at_origin(xlabel='X', ylabel='Y', zlabel='Z', line_width=6, labels_off=True)

        self.plotter.add_mesh(pv.PolyData(Vol_centroid), color='r', point_size=40, render_points_as_spheres=True)
        
    def centroid(self, mesh):
        """ find centroid volumetrically and indicate on graph """
        global V

        # find the vertices & the vertex indices of each triangular face
        V = np.array(mesh.points)
        col = len(V)
        f_ind = np.array(mesh.faces.reshape((-1,4))[:, 1:4])
        
        # define an arbitrary start point from middle of max and min of X,Y,Z of
        # all points: in a convex manifold it falls inside the volume (requires
        # segmentation for general application)
        start = np.array(mesh.center)
        X_start = start[0]
        Y_start = start[1]
        Z_start = start[2]
        
        # initialize variables
        centroids = []
        Vol_total = 0
        Sum_vol_x = 0
        Sum_vol_y = 0
        Sum_vol_z = 0
        
        # find centroid from all tetrahedra made with arbitrary center and triangular faces
        for i in range(0, col-1, 3):          
            # find the center of each tetrahedron (average of X,Y,Z of 
            # 4 vertices, 3 from the triangle, and one arbitrary start point)
            X_cent = (X_start + V[f_ind[i,0],0] + V[f_ind[i+1,0],0] + V[f_ind[i+2,0],0]) / 4
            Y_cent = (Y_start + V[f_ind[i,1],1] + V[f_ind[i+1,1],1] + V[f_ind[i+2,1],1]) / 4
            Z_cent = (Z_start + V[f_ind[i,2],2] + V[f_ind[i+1,2],2] + V[f_ind[i+2,2],2]) / 4
    
            # compute the volume of each tetrahedron
            V1 = np.array([V[f_ind[i,0],0], V[f_ind[i,1],1], V[f_ind[i,2],2]])**2 - np.array([X_start, Y_start, Z_start])**2
            V2 = np.array([V[f_ind[i+1,0],0], V[f_ind[i+1,1],1], V[f_ind[i+1,2],2]])**2 - np.array([V[f_ind[i,0],0], V[f_ind[i,1],1], V[f_ind[i,2],2]])**2
            V3 = np.array([V[f_ind[i+2,0],0], V[f_ind[i+2,1],1], V[f_ind[i+2,2],2]])**2 - np.array([V[f_ind[i+1,0],0], V[f_ind[i+1,1],1], V[f_ind[i+1,2],2]])**2
            V1 = V1.reshape((-1,1))
            V2 = V2.reshape((-1,1))
            V3 = V3.reshape((-1,1))
            Vol = abs(np.linalg.det(np.hstack([V1, V2, V3]))) / 6
    
            # tally up each cycle
            Vol_total = Vol_total + Vol
            Sum_vol_x = Sum_vol_x + Vol * X_cent
            Sum_vol_y = Sum_vol_y + Vol * Y_cent
            Sum_vol_z = Sum_vol_z + Vol * Z_cent
            centroids.append([X_cent,Y_cent,Z_cent])
        
        # find & show centroid
        centroids = np.asarray(centroids)
        Vol_centroid = [Sum_vol_x, Sum_vol_y, Sum_vol_z] / Vol_total
        
        return Vol_centroid

    def show_max_cube(self):
        # check which centroid is used
        Vol_centroid = self.skewed_centroid_check()
        
        _, max_normal, intxn = self.max_cube_ray(int_surface, Vol_centroid, ext = True)
        self.max_cuboid(int_surface, intxn, Vol_centroid, max_normal)
        
    def max_cube_ray(self, mesh, Vol_centroid, ext = False):
        """ add a maximally inscribed cube within the opened mesh (via ray tracing) """
        global r_len
        global face_center, max_cube_vol, max_cube, max_cuboid
        global max_cube_start, max_cube_end, max_cube_run
        global max_cube_V, max_cube_F

        # initiate variables
        max_cube = 0
        max_cuboid = 0

        # find mesh vertices
        V = np.array(mesh.points)

        # find the nearest possible cube vertex from top rays & mesh intersection
        top_vert = self.cube_center_ray(mesh, Vol_centroid, 'z')
        top = self.furthest_pt(top_vert, Vol_centroid)

        # find the nearest possible cube vertex from bottom rays & mesh intersection
        bottom_vert = self.cube_center_ray(mesh, Vol_centroid, '-z')
        bottom = self.furthest_pt(bottom_vert, Vol_centroid)

        # find the nearest possible cube vertex between the two
        if top[0] < bottom[0]:
            p = top[1]
            V = top[2]
        else:
            p = bottom[1]
            V = bottom[2]
        
        # set the furthest ray intersection of the mesh as starting vertex of the cube
        intxn = V[p,:]

        # create and show max cube
        max_cube_V, max_cube_F, max_cube_vol = self.create_cube(intxn, Vol_centroid, np.array([0,0,Vol_centroid[2]]))
        max_cube = pv.PolyData(max_cube_V, max_cube_F)
        self.plotter.add_mesh(max_cube, show_edges=True, line_width=5, color="orange", opacity = 0.8)

        # find & show max cube face centers
        cell_center = pv.PolyData(max_cube_V, max_cube_F).cell_centers()
        face_center = np.array(cell_center.points)
        # self.plotter.add_mesh(cell_center, color="r", point_size=8, render_points_as_spheres=True)

        # find max cube face normals
        max_normal = pv.PolyData(max_cube_V, max_cube_F).cell_normals

        # max cube volume
        if (ext == False):
            max_cube_vol = float(format(max_cube_vol, ".2f"))
            print("Cube Center Volume:", max_cube_vol, "in^3")

        return face_center, max_normal, intxn

    def cube_center_ray(self, mesh, start, dir):
        ''' from starting point shoot out n rays to find vertices of possible cubes '''
        global r_num, r_rot, r_dec, r_len

        # initialize variables
        r_num = 1
        r_rot = np.pi/2
        r_dec = -2*np.pi/r_num
        ray_size = np.zeros((4, 3))
        r_dir = ray_size
        r_dir_norm = ray_size
        r_end = ray_size
        r_int = []
        ori_r_int = []

        l_wid = 10
        pt_size = 25
        rays = [0] * 4
        ints = [0] * 4

        # set ray length
        r_len = np.sqrt((x_range/2)**2 + (y_range/2)**2 + (z_range/2)**2)
        
        # create rays by rotating the first, which creates the cube with xyz axes as its face normals
        for i in range(0, r_num):
            for j in range(0, 4):
                if (j == 0) and (dir == 'z'):
                    r_dir[0] = np.array([np.sqrt(2)/2 * np.cos(np.pi/4 + r_dec * i), np.sqrt(2)/2 * np.sin(np.pi/4 + r_dec * i), 0.5])
                    r_dir_norm[0] = r_dir[0] / np.linalg.norm(r_dir[0])
                    r_end[0] = start + r_dir_norm[0] * r_len
                    # set rotation matrix about 'z'
                    R = self.rot_axis(np.array([0,0,1]))
                elif (j == 0) and (dir == '-z'):
                    r_dir[0] = np.array([np.sqrt(2)/2 * np.cos(np.pi/4 + r_dec * i), np.sqrt(2)/2 * np.sin(np.pi/4 + r_dec * i), -0.5])
                    r_dir_norm[0] = r_dir[0] / np.linalg.norm(r_dir[0])
                    r_end[0] = start + r_dir_norm[0] * r_len
                    # set rotation matrix about '-z'
                    R = self.rot_axis(np.array([0,0,-1]))
                else:
                    r_end[j] = np.dot(R(j*r_rot), (r_end[0] - start).T).T
                    r_end[j] = r_end[j] + start

                # perform ray trace
                r_pts, _ = mesh.ray_trace(start, r_end[j])

                # show rays
                # rays[j] = self.plotter.add_mesh(pv.Line(Vol_centroid, r_end[j]), color='w', line_width=l_wid)
                # ints[j] = self.plotter.add_mesh(pv.PolyData(r_pts[0]), color='w', point_size=pt_size)

                # create an array of ray intersections
                r_int = np.append(r_int, r_pts[0])
            
            r_int = np.reshape(r_int, (4,3))
            _, ori_p, ori_V = self.nearest_pt(r_int, start)
            r_int = []
            ori_r_int = np.append(ori_r_int, ori_V[ori_p,:])

        ori_r_int = np.reshape(ori_r_int, (r_num,3))
        
        return ori_r_int

    def nearest_pt(self, vert, starting_pt):
        """ find nearest vertex: for segmented convex manifold, a cube with volume centroid as 
        center and nearest vertex as cube vertex, it falls inside the volume """
        # find nearest point from the list of points
        c = len(vert)
        dist = np.zeros(c)
        for i in range(0, c):
            dist[i] = np.sqrt((vert[i,0] - starting_pt[0])**2 + (vert[i,1] - starting_pt[1])**2
                            + (vert[i,2] - starting_pt[2])**2)
                
        # find index of the nearest point
        nearest = min(dist)
        p = np.where(dist == nearest)
        p = p[0].item()

        return nearest, p, vert

    def furthest_pt(self, vert, starting_pt):
        global p, furthest, dist
        """ find furthest vertex among the list of nearest vertices """
        # find furthest point from the list of points
        c = len(vert)
        dist = np.zeros(c)
        for i in range(0, c):
            dist[i] = np.sqrt((vert[i,0] - starting_pt[0])**2 + (vert[i,1] - starting_pt[1])**2
                            + (vert[i,2] - starting_pt[2])**2)

        # find index of the furthest point
        furthest = max(dist)
        p = np.where(dist == furthest)
        p = p[0][0]

        return furthest, p, vert

    def create_cube(self, vertex, starting_pt, axis):
        ''' create cube from the nearest pt & centroid '''
        global edge_length

        if (axis[0] == 0) and (axis[1] == 0) and (axis[2] == 0):
            axis[2] = 1
            vert_trans = np.array([0,0,0])
        elif (starting_pt[0] == 0) and (starting_pt[1] == 0) and (starting_pt[2] == 0):
            vert_trans = np.array([0,0,0])
        else:
            vert_trans = starting_pt
            for i in range(0,3):
                if round(axis[i]) == 1 or round(axis[i]) == -1:
                    vert_trans[i] == 0
        # find the other 7 vertices
        # 3 vertices can be found by rotating the first point 90 degrees 3 times around Z axis of centroid
        # 4 vertices can be found by translating the first four vertices twice the half edge
        # found from the distance times sin(pi/4)
        R = self.rot_axis(axis / np.linalg.norm(axis))
        
        # construct the array of the first 4 vertices
        V_1 = np.array(vertex - vert_trans)
        V_2 = np.dot(R(np.pi/2), V_1.T).T
        V_3 = np.dot(R(np.pi), V_1.T).T
        V_4 = np.dot(R(3*np.pi/2), V_1.T).T
        # cube_V_start = np.array([V_1, V_2, V_3, V_4])
        cube_V_start = np.array([V_1, V_2, V_3, V_4]) + np.ones((4,1)) * [vert_trans]
        cube_V_start_center = np.array(pv.PolyData(cube_V_start).center)

        # show nearest vertex of cube
        V_1 = np.array(vertex)
        self.plotter.add_mesh(pv.PolyData(V_1), color="y", point_size=30.0, render_points_as_spheres=True)
        
        # find the translation distance
        trans_dis = starting_pt - cube_V_start_center
        trans_dir = trans_dis / np.linalg.norm(trans_dis)
        dia_dis = np.sqrt((V_1[0]-cube_V_start_center[0])**2 + (V_1[1]-cube_V_start_center[1])**2 + (V_1[2]-cube_V_start_center[2])**2)
        half_edge = np.ones((4,1)) * [trans_dir] * dia_dis * np.sin(np.pi/4)
        edge_length = dia_dis * np.sin(np.pi/4) * 2
        print("Center cube edge length:", edge_length)
        cube_trans = np.asarray(2*half_edge, dtype=np.float64)

        # construct the cube
        cube_V_end = np.add(cube_V_start, cube_trans)
        cube_V = np.vstack((cube_V_start, cube_V_end))
        cube_F = np.hstack([[4,0,1,2,3],
                        [4,0,3,7,4],
                        [4,0,1,5,4],
                        [4,1,2,6,5],
                        [4,2,3,7,6],
                        [4,4,5,6,7]])

        # cube volume
        cube_vol = (2 * np.linalg.norm(half_edge[0,:]))**3

        return cube_V, cube_F, cube_vol

    def rot_axis(self, axis):
        ''' create a rotational matrix about an arbitrary axis '''
        t = sp.Symbol('t')

        R_t = Matrix([[sp.cos(t)+axis[0]**2*(1-sp.cos(t)), axis[0]*axis[1]*(1-sp.cos(t))-axis[2]*sp.sin(t), axis[0]*axis[2]*(1-sp.cos(t))+axis[1]*sp.sin(t)],
            [axis[1]*axis[0]*(1-sp.cos(t))+axis[2]*sp.sin(t), sp.cos(t)+axis[1]**2*(1-sp.cos(t)), axis[1]*axis[2]*(1-sp.cos(t))-axis[0]*sp.sin(t)],
            [axis[2]*axis[0]*(1-sp.cos(t))-axis[1]*sp.sin(t), axis[2]*axis[1]*(1-sp.cos(t))+axis[0]*sp.sin(t), sp.cos(t)+axis[2]**2*(1-sp.cos(t))]])
        R = lambdify(t, R_t)
        return R

    def max_cuboid(self, mesh, nearest_pt, Vol_centroid, max_normal):
        ''' extend max cube into maximally inscribed cuboid '''
        global face_center, max_cuboid, max_cuboid_vol
        # fix max_normals
        dir_check = (face_center - Vol_centroid) * 2 / edge_length
        x_check = np.abs(np.around(max_normal[0,0] - dir_check[0,0]))
        y_check = np.abs(np.around(max_normal[0,1] - dir_check[0,1]))
        z_check = np.abs(np.around(max_normal[0,2] - dir_check[0,2]))
        print(x_check)
        print(y_check)
        print(z_check)

        print(max_normal)
        print(dir_check)

        if (x_check == 2) or (y_check == 2) or (z_check == 2):
            max_normal = -max_normal
 
        # find the 3 out of 6 normal directions the max cube can be extended towards
        ext_dir = np.empty(shape=(3,3)) 
        main_dir = nearest_pt - Vol_centroid

        ind = 0
        for i in range(0, 6):
            if np.dot(main_dir, max_normal[i]) < 0:
                ext_dir[ind] = max_normal[i]
                ind += 1

        # extend faces by shooting a ray from the 4 vertices on each extendable face
        # in the direction of its face normal. Find the nearest intersection and
        # it would be the limit of extension for that face
        for i in range(0, 3):
            F_ind = np.where((np.around(max_normal) == np.around(ext_dir[i])).all(axis=1))
            F_ind = F_ind[0][0]
            np.reshape(max_cube_F, (6,5))
            faces = np.reshape(max_cube_F, (6,5))
            print(faces)
            V_ind = faces[F_ind, 1:5]
            print(V_ind)
            current_V = np.vstack([max_cube_V[V_ind[0]], max_cube_V[V_ind[1]], max_cube_V[V_ind[2]], max_cube_V[V_ind[3]]])
            print(current_V)
            ext_V = self.ext_ray(mesh, current_V, ext_dir[i])
            max_cube_V[V_ind] = ext_V

        # create & show extended max cube
        max_cuboid = pv.PolyData(max_cube_V, max_cube_F)
        self.plotter.add_mesh(max_cuboid, show_edges=True, line_width=5, color="y", opacity = 0.4)

        # find face centers of extended max cube
        cell_center = max_cuboid.cell_centers()
        face_center = np.array(cell_center.points)

        # find face normals of the extended max cube
        max_normal = max_cuboid.cell_normals

        # extended max cube volume
        max_cuboid_vol = float(format(max_cuboid.volume, ".2f"))
        print("Extended Max Cube Volume:", max_cuboid_vol)

    def ext_ray(self, mesh, current_V, ext_dir):
        ''' shoot rays from vertices of a cube face towards face normal & obtain intersections with mesh '''
        # initialize variables
        ext_end = current_V + ext_dir * np.ones((4,1)) * r_len
        ext_dis = np.zeros(4)
        ext_rays = [0] * 6 * r_num
        ext_ints = [0] * 6 * r_num

        # set raytracing parameters
        l_wid = 3
        pt_size = 10

        # perform ray tracing per extending face vertex
        for i in range(0,4):
            ext_int, _ = mesh.ray_trace(current_V[i], ext_end[i])
            ext_dis[i] = np.sqrt((ext_int[0][0] - current_V[i][0])**2 + (ext_int[0][1] - current_V[i][1])**2
                                 + (ext_int[0][2] - current_V[i][2])**2)

            # show rays
            # ext_rays[i] = self.plotter.add_mesh(pv.Line(current_V[i], ext_end[i]), color='w', line_width=l_wid)
            # ext_ints[i] = self.plotter.add_mesh(pv.PolyData(ext_int[0]), color='w', point_size=pt_size)

        # extend vertices by the shortest intersection distance
        ext_V = current_V + ext_dir * np.ones((4,1)) * min(ext_dis)
        
        return ext_V

    def closeEvent(self, event):
        reply = QMessageBox.question(self, "Window Close", "Are you sure you want to quit program?",
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()
Example #13
0
    def __init__(self):
        QtWidgets.QWidget.__init__(self)

        # main/ real usable GUI

        #set windowsize
        self.setFixedSize(1000, 900)

        #meshplot frame with interactor
        self.frame_mp = QtWidgets.QFrame(self)
        self.frame_mp.setGeometry(100, 20, 500, 250)
        vlayout_mp = QtWidgets.QVBoxLayout()
        self.plotter_mp = QtInteractor(self.frame_mp)
        vlayout_mp.addWidget(self.plotter_mp.interactor)
        vlayout_mp.setContentsMargins(0, 0, 0, 0)
        self.frame_mp.setLayout(vlayout_mp)

        #full PCplot frame with all points
        self.frame_pc = QtWidgets.QFrame(self)
        self.frame_pc.setGeometry(100, 320, 500, 250)
        vlayout_pc = QtWidgets.QVBoxLayout()
        self.plotter_pc = QtInteractor(self.frame_pc)
        vlayout_pc.addWidget(self.plotter_pc.interactor)
        vlayout_pc.setContentsMargins(0, 0, 0, 0)
        self.frame_pc.setLayout(vlayout_pc)

        #sample PCplot frame with variable k
        self.frame_spc = QtWidgets.QFrame(self)
        self.frame_spc.setGeometry(100, 620, 500, 250)
        vlayout_spc = QtWidgets.QVBoxLayout()
        self.plotter_spc = QtInteractor(self.frame_spc)
        vlayout_spc.addWidget(self.plotter_spc.interactor)
        vlayout_spc.setContentsMargins(0, 0, 0, 0)
        self.frame_spc.setLayout(vlayout_spc)

        #GUI Functions / Buttons
        #Plot and Inspect single files
        self.lab_inspectorlab = QtWidgets.QLabel('Display Mesh:', self)
        self.lab_inspectorlab.setGeometry(630, 20, 200, 20)

        self.button_sel_file = QtWidgets.QPushButton('Select File', self)
        self.button_sel_file.setGeometry(650, 70, 100, 25)

        #select sampletype of PointCloud
        self.lab_inspectorlab = QtWidgets.QLabel('Model Sampler Settings:',
                                                 self)
        self.lab_inspectorlab.setGeometry(630, 170, 200, 20)

        self.lab_sampkind = QtWidgets.QLabel('sampletype:', self)
        self.lab_sampkind.setGeometry(650, 220, 100, 20)

        self.lab_vertexcloud = QtWidgets.QLabel('Vertices', self)
        self.lab_vertexcloud.setGeometry(750, 220, 100, 20)

        self.box_vertexcloud = QtWidgets.QCheckBox(self)
        self.box_vertexcloud.setGeometry(800, 220, 100, 20)

        self.lab_surfacecloud = QtWidgets.QLabel('Surface', self)
        self.lab_surfacecloud.setGeometry(850, 220, 100, 20)

        self.box_surfacecloud = QtWidgets.QCheckBox(self)
        self.box_surfacecloud.setGeometry(900, 220, 100, 20)

        #samplesize Option config
        self.lab_sampsize = QtWidgets.QLabel('samplesize:', self)
        self.lab_sampsize.setGeometry(650, 270, 100, 20)

        self.input_sampsize = QtWidgets.QLineEdit(self)
        self.input_sampsize.setGeometry(750, 270, 70, 20)

        self.button_set_sampsize = QtWidgets.QPushButton(
            'Apply Settings', self)
        self.button_set_sampsize.setGeometry(850, 268, 100, 25)

        #plot PointCloud Sample (also plots full pointcloud)
        self.lab_plotsth = QtWidgets.QLabel('Start / Stop Plot:', self)
        self.lab_plotsth.setGeometry(630, 350, 100, 20)

        self.button_plot_samp_cancel = QtWidgets.QPushButton(
            'STOP PLOTS', self)
        self.button_plot_samp_cancel.setGeometry(650, 400, 100, 25)

        self.button_testmode = QtWidgets.QPushButton('PLOT', self)
        self.button_testmode.setGeometry(800, 400, 100, 25)

        #save sample as pytorch tensor object
        self.lab_savesth = QtWidgets.QLabel(
            'Save the Sampled Pointcloud as Tensor:', self)
        self.lab_savesth.setGeometry(630, 450, 200, 20)

        self.button_save_sample = QtWidgets.QPushButton('Save', self)
        self.button_save_sample.setGeometry(650, 500, 100, 25)

        #Conversion of Folders to Surfacesamples
        self.lab_conversionlab = QtWidgets.QLabel(
            'Convert Folder to Dataset-Ready Surfaceasamples:', self)
        self.lab_conversionlab.setGeometry(630, 620, 250, 20)

        self.lab_perspnumlab = QtWidgets.QLabel('Num of Perspectives:', self)
        self.lab_perspnumlab.setGeometry(650, 670, 210, 20)

        self.input_perspnum = QtWidgets.QLineEdit(self)
        self.input_perspnum.setGeometry(800, 670, 70, 20)
        self.input_perspnum.setText("1")

        self.button_sel_folder = QtWidgets.QPushButton('Select Folder', self)
        self.button_sel_folder.setGeometry(650, 720, 100, 25)

        self.button_save_folder = QtWidgets.QPushButton('Convert', self)
        self.button_save_folder.setGeometry(800, 720, 100, 25)

        self.lab_conversionlab = QtWidgets.QLabel('Progress:', self)
        self.lab_conversionlab.setGeometry(650, 770, 250, 20)

        self.progbar_folderconv = QtWidgets.QProgressBar(self)
        self.progbar_folderconv.setGeometry(720, 770, 180, 20)

        #status label
        self.lab_label_status = QtWidgets.QLabel('Status:', self)
        self.lab_label_status.setGeometry(650, 850, 100, 20)

        self.label_status = QtWidgets.QLabel('', self)
        self.label_status.setGeometry(750, 850, 200, 20)
Example #14
0
class MainGUIWindow(QtWidgets.QWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)

        # main/ real usable GUI

        #set windowsize
        self.setFixedSize(1000, 900)

        #meshplot frame with interactor
        self.frame_mp = QtWidgets.QFrame(self)
        self.frame_mp.setGeometry(100, 20, 500, 250)
        vlayout_mp = QtWidgets.QVBoxLayout()
        self.plotter_mp = QtInteractor(self.frame_mp)
        vlayout_mp.addWidget(self.plotter_mp.interactor)
        vlayout_mp.setContentsMargins(0, 0, 0, 0)
        self.frame_mp.setLayout(vlayout_mp)

        #full PCplot frame with all points
        self.frame_pc = QtWidgets.QFrame(self)
        self.frame_pc.setGeometry(100, 320, 500, 250)
        vlayout_pc = QtWidgets.QVBoxLayout()
        self.plotter_pc = QtInteractor(self.frame_pc)
        vlayout_pc.addWidget(self.plotter_pc.interactor)
        vlayout_pc.setContentsMargins(0, 0, 0, 0)
        self.frame_pc.setLayout(vlayout_pc)

        #sample PCplot frame with variable k
        self.frame_spc = QtWidgets.QFrame(self)
        self.frame_spc.setGeometry(100, 620, 500, 250)
        vlayout_spc = QtWidgets.QVBoxLayout()
        self.plotter_spc = QtInteractor(self.frame_spc)
        vlayout_spc.addWidget(self.plotter_spc.interactor)
        vlayout_spc.setContentsMargins(0, 0, 0, 0)
        self.frame_spc.setLayout(vlayout_spc)

        #GUI Functions / Buttons
        #Plot and Inspect single files
        self.lab_inspectorlab = QtWidgets.QLabel('Display Mesh:', self)
        self.lab_inspectorlab.setGeometry(630, 20, 200, 20)

        self.button_sel_file = QtWidgets.QPushButton('Select File', self)
        self.button_sel_file.setGeometry(650, 70, 100, 25)

        #select sampletype of PointCloud
        self.lab_inspectorlab = QtWidgets.QLabel('Model Sampler Settings:',
                                                 self)
        self.lab_inspectorlab.setGeometry(630, 170, 200, 20)

        self.lab_sampkind = QtWidgets.QLabel('sampletype:', self)
        self.lab_sampkind.setGeometry(650, 220, 100, 20)

        self.lab_vertexcloud = QtWidgets.QLabel('Vertices', self)
        self.lab_vertexcloud.setGeometry(750, 220, 100, 20)

        self.box_vertexcloud = QtWidgets.QCheckBox(self)
        self.box_vertexcloud.setGeometry(800, 220, 100, 20)

        self.lab_surfacecloud = QtWidgets.QLabel('Surface', self)
        self.lab_surfacecloud.setGeometry(850, 220, 100, 20)

        self.box_surfacecloud = QtWidgets.QCheckBox(self)
        self.box_surfacecloud.setGeometry(900, 220, 100, 20)

        #samplesize Option config
        self.lab_sampsize = QtWidgets.QLabel('samplesize:', self)
        self.lab_sampsize.setGeometry(650, 270, 100, 20)

        self.input_sampsize = QtWidgets.QLineEdit(self)
        self.input_sampsize.setGeometry(750, 270, 70, 20)

        self.button_set_sampsize = QtWidgets.QPushButton(
            'Apply Settings', self)
        self.button_set_sampsize.setGeometry(850, 268, 100, 25)

        #plot PointCloud Sample (also plots full pointcloud)
        self.lab_plotsth = QtWidgets.QLabel('Start / Stop Plot:', self)
        self.lab_plotsth.setGeometry(630, 350, 100, 20)

        self.button_plot_samp_cancel = QtWidgets.QPushButton(
            'STOP PLOTS', self)
        self.button_plot_samp_cancel.setGeometry(650, 400, 100, 25)

        self.button_testmode = QtWidgets.QPushButton('PLOT', self)
        self.button_testmode.setGeometry(800, 400, 100, 25)

        #save sample as pytorch tensor object
        self.lab_savesth = QtWidgets.QLabel(
            'Save the Sampled Pointcloud as Tensor:', self)
        self.lab_savesth.setGeometry(630, 450, 200, 20)

        self.button_save_sample = QtWidgets.QPushButton('Save', self)
        self.button_save_sample.setGeometry(650, 500, 100, 25)

        #Conversion of Folders to Surfacesamples
        self.lab_conversionlab = QtWidgets.QLabel(
            'Convert Folder to Dataset-Ready Surfaceasamples:', self)
        self.lab_conversionlab.setGeometry(630, 620, 250, 20)

        self.lab_perspnumlab = QtWidgets.QLabel('Num of Perspectives:', self)
        self.lab_perspnumlab.setGeometry(650, 670, 210, 20)

        self.input_perspnum = QtWidgets.QLineEdit(self)
        self.input_perspnum.setGeometry(800, 670, 70, 20)
        self.input_perspnum.setText("1")

        self.button_sel_folder = QtWidgets.QPushButton('Select Folder', self)
        self.button_sel_folder.setGeometry(650, 720, 100, 25)

        self.button_save_folder = QtWidgets.QPushButton('Convert', self)
        self.button_save_folder.setGeometry(800, 720, 100, 25)

        self.lab_conversionlab = QtWidgets.QLabel('Progress:', self)
        self.lab_conversionlab.setGeometry(650, 770, 250, 20)

        self.progbar_folderconv = QtWidgets.QProgressBar(self)
        self.progbar_folderconv.setGeometry(720, 770, 180, 20)

        #status label
        self.lab_label_status = QtWidgets.QLabel('Status:', self)
        self.lab_label_status.setGeometry(650, 850, 100, 20)

        self.label_status = QtWidgets.QLabel('', self)
        self.label_status.setGeometry(750, 850, 200, 20)

    #update GUI functions, triggered by signals
    @QtCore.pyqtSlot(str)
    def updateStatus(self, status):
        self.label_status.setText(status)

    def updateProgress(self, value):
        self.progbar_folderconv.setValue(value)

    def plotmesh(self, filestr):
        self.plotter_mp.clear()
        self.plotter_mp.add_text("Mesh View", font_size=10)
        mesh = pv.read(filestr)
        self.plotter_mp.add_mesh(mesh, show_edges=True, color="#8cfaab")
        self.plotter_mp.reset_camera()

    def plotcloud(self, point_cloud):
        self.plotter_pc.clear()
        self.plotter_pc.add_text("PointCloud View", font_size=10)
        self.plotter_pc.add_mesh(point_cloud,
                                 render_points_as_spheres=True,
                                 color="#8cfaab")
        self.plotter_pc.reset_camera()

    def plotcloudsamp(self, point_cloud):
        self.plotter_spc.clear()
        self.plotter_spc.add_text("PointCloud Sample", font_size=10)
        self.plotter_spc.add_mesh(point_cloud,
                                  render_points_as_spheres=True,
                                  color="#8cfaab")
        self.plotter_spc.reset_camera()
Example #15
0
    def __init__(self, parent=None, show=True):
        Qt.QMainWindow.__init__(self, parent)

        # create the frame
        self.frame = Qt.QFrame()
        vlayout = Qt.QVBoxLayout()

        # add the pyvista interactor object
        self.plotter = QtInteractor(self.frame)
        vlayout.addWidget(self.plotter.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        # simple menu
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        editMenu = mainMenu.addMenu('Edit')
        
        # opening a mesh file
        self.open_mesh_action = Qt.QAction('Open Mesh...', self)
        self.open_mesh_action.triggered.connect(self.open_mesh)
        fileMenu.addAction(self.open_mesh_action)
        
        # exit button
        exitButton = Qt.QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        # inserting maximally inscribed cube
        self.max_cube_action = Qt.QAction('Max Cube', self)
        #self.max_cube_action.setCheckable(True)
        self.max_cube_action.triggered.connect(self.max_cube)
        editMenu.addAction(self.max_cube_action)
        
        #Create Cone in Mesh
        self.next_cubes_action = Qt.QAction('Next Cubes', self)
        self.next_cubes_action.triggered.connect(self.next_cubes)
        editMenu.addAction(self.next_cubes_action)
        
        # slice mesh horizontally based on internal cubes
        self.cube_hslice_action = Qt.QAction('Cube H-Slice', self)
        self.cube_hslice_action.triggered.connect(self.cube_hslice)
        editMenu.addAction(self.cube_hslice_action)

        # slice mesh (interactively)
        self.slice_action = Qt.QAction('Slice', self)
        self.slice_action.triggered.connect(self.slice)
        editMenu.addAction(self.slice_action)
        
        # slice mesh with clipping (interactively)
        self.clip_slice_action = Qt.QAction('Clip Slice', self)
        self.clip_slice_action.triggered.connect(self.clip_slice)
        editMenu.addAction(self.clip_slice_action)

        # create bounding box(es) for mesh (interactively)
        self.bounding_action = Qt.QAction('Bounding', self)
        self.bounding_action.triggered.connect(self.bounding_bar)
        editMenu.addAction(self.bounding_action)
        
        if show:
            self.show()

        self.plotter.add_axes(interactive=None, line_width=2, color=None, x_color=None, y_color=None, z_color=None, xlabel='X', ylabel='Y', zlabel='Z', labels_off=False, box=None, box_args=None)
Example #16
0
class MainWindow(Qt.QMainWindow):
    def __init__(self, parent=None, show=True):
        Qt.QMainWindow.__init__(self, parent)

        # create the frame
        self.frame = Qt.QFrame()
        vlayout = Qt.QVBoxLayout()

        # add the pyvista interactor object
        self.plotter = QtInteractor(self.frame)
        vlayout.addWidget(self.plotter.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        # simple menu
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        editMenu = mainMenu.addMenu('Edit')
        
        # opening a mesh file
        self.open_mesh_action = Qt.QAction('Open Mesh...', self)
        self.open_mesh_action.triggered.connect(self.open_mesh)
        fileMenu.addAction(self.open_mesh_action)
        
        # exit button
        exitButton = Qt.QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        # inserting maximally inscribed cube
        self.max_cube_action = Qt.QAction('Max Cube', self)
        #self.max_cube_action.setCheckable(True)
        self.max_cube_action.triggered.connect(self.max_cube)
        editMenu.addAction(self.max_cube_action)
        
        #Create Cone in Mesh
        self.next_cubes_action = Qt.QAction('Next Cubes', self)
        self.next_cubes_action.triggered.connect(self.next_cubes)
        editMenu.addAction(self.next_cubes_action)
        
        # slice mesh horizontally based on internal cubes
        self.cube_hslice_action = Qt.QAction('Cube H-Slice', self)
        self.cube_hslice_action.triggered.connect(self.cube_hslice)
        editMenu.addAction(self.cube_hslice_action)

        # slice mesh (interactively)
        self.slice_action = Qt.QAction('Slice', self)
        self.slice_action.triggered.connect(self.slice)
        editMenu.addAction(self.slice_action)
        
        # slice mesh with clipping (interactively)
        self.clip_slice_action = Qt.QAction('Clip Slice', self)
        self.clip_slice_action.triggered.connect(self.clip_slice)
        editMenu.addAction(self.clip_slice_action)

        # create bounding box(es) for mesh (interactively)
        self.bounding_action = Qt.QAction('Bounding', self)
        self.bounding_action.triggered.connect(self.bounding_bar)
        editMenu.addAction(self.bounding_action)
        
        if show:
            self.show()

        self.plotter.add_axes(interactive=None, line_width=2, color=None, x_color=None, y_color=None, z_color=None, xlabel='X', ylabel='Y', zlabel='Z', labels_off=False, box=None, box_args=None)

    def open_mesh(self):
        """ add a mesh to the pyqt frame """
        global mesh

        # open file
        file_info = QtWidgets.QFileDialog.getOpenFileName()
        file_dir = file_info[0]
        
        # determine file type and if conversion needed
        head, tail = os.path.split(file_dir)
        root, ext = os.path.splitext(tail)

        # convert mesh file type
        #if ext != ".vtk" or ext != ".VTK":
        #    mesh = meshio.read(file_dir)
        #    meshio.write(root + ".vtk", mesh)
        #    mesh = pv.read(head + "/" + root + ".vtk")
            # need to store elsewhere or delete .vtk file in the future
        #else:
        #    mesh = pv.read(file_dir)

        # read mesh & transform according to principal axes
        pre = trimesh.load_mesh(file_dir)
        orient = pre.principal_inertia_transform
        pre = pre.apply_transform(orient)
        pre.export('data/'+ root + '_oriented.STL')
        mesh = pv.read('data/'+ root + '_oriented.STL')

        # show transformed mesh
        self.plotter.add_mesh(mesh, show_edges=True, color="w", opacity=0.6)

        # reset plotter
        self.reset_plotter()

        # find mesh centroid and translate the mesh so that's the origin
        self.centroid()

        # show bounding box
        self.plotter.add_bounding_box(opacity=0.5, color="y")

    def reset_plotter(self):
        """ clear plotter of mesh or interactive options """
        # clear plotter
        self.plotter.clear()
        self.plotter.clear_plane_widgets()
        self.plotter.reset_camera()
        self.update()
        
        # callback opened mesh
        self.plotter.add_mesh(mesh, show_edges=True, color="w", opacity=0.6)
        
        # show origin
        #self.plotter.add_axes_at_origin(xlabel='X', ylabel='Y', zlabel='Z', line_width=1, labels_off=False)

        # show floors
        #self.plotter.add_floor('-y')
        #self.plotter.add_floor('-z')
    def next_cubes(self):
        #change cones to c0 to c5
        global create_cone
        hi = 12
        ang = np.arctan(1/(np.sqrt(2)/2))
        ang = float(90 - np.degrees(ang))
        create_cone = pv.Cone(center=face_center[0] + [0,0,hi/2], direction = [0.0,0.0,-1.0], height = hi, radius=None, resolution= 100, angle = ang, capping=False)
        create_cone2 = pv.Cone(center=face_center[1] + [-hi/2,-hi/2,0], direction = [1,1,0.0], height = hi, radius=None, resolution= 100, angle = ang, capping=False)
        create_cone3 = pv.Cone(center=face_center[2] + [0,0,hi/2], direction = [0,0,-1], height = hi, radius=None, resolution= 100, angle = ang, capping=False)
        #create_cone3 = pv.Cone(center=face_center[2] + [hi/2,hi,0], direction = [-1,-1,0.0], height = hi, radius=None, resolution= 100, angle = ang, capping=False)
        create_cone4 = pv.Cone(center=face_center[3] + [hi/4,hi/4,0], direction = [-1,-1,0], height = hi, radius=None, resolution= 100, angle = ang, capping=False)
        create_cone5 = pv.Cone(center=face_center[4] + [0,hi/2,0], direction = [0,-1,0], height = hi, radius=None, resolution= 100, angle = ang, capping=False)
        create_cone6 = pv.Cone(center=face_center[5] + [0,0,-hi/2], direction = [0,0,1], height = hi, radius=None, resolution= 100, angle = ang, capping=False)
        #create_cone7 = pv.Cone(center=cell_center1[0] + [0,0,hi/2], direction = [0,0,-1], height = hi, radius=None, resolution= 100, angle = ang, capping=False)
        clipped = mesh.clip_surface(create_cone, invert=True)
        cone_intersections = np.array(clipped.points)
        #top = self.nearest_pt(create_cone, face_center[0] + [0,0,hi/2])
        clipped6 = mesh.clip_surface(create_cone6, invert=True)
        
        cone_intersection6 = np.array(clipped6.points)
     
        #nearest_cone = min(cone_intersection6)
       
        #self.nearest_pt(create_cone, face_center[0])
        print("Bottom Intersection:",cone_intersection6)
        #nearest_point = np.amin(cone_intersection6[0])
        nearest_point = min(cone_intersections, key=min)
        nearest_point6 = min(cone_intersection6, key=min)
        
        x_Min = face_center[5][0]
        x_Max = nearest_point6[0]
        x = x_Max - x_Min
        y_Min = face_center[5][1]
        y_Max = nearest_point6[1] 
        y = y_Max - y_Min
        z_Min = face_center[5][2]
        z_Max = nearest_point6[2]
        z = z_Max - z_Min
        x1 = nearest_point[0] - face_center[0][0]
        y1 = nearest_point[1] - face_center[0][1]
        z1 = nearest_point[2] - face_center[0][2]
        
        #new_array = np.array(x1,y1,z1)
        #smallest = np.amin(new_array)
        
        #cube_bounds = np.array(face_center[0], x_length1, face_center[1], y_length2, face_center[3], z_length3)
        print(nearest_point, "Closeest Value")
        #print(largest)
        #print(smallest)
        print(x_Max)
        print(x1, "x")
        print(y1, "y")
        print(z1, "z")
        #print(cube_bounds, "Cube Boundarys")
        #cone_center = create_cone.cell_centers()
        #cone_center_points = np.array(cone_center.points)
        self.plotter.add_mesh(create_cone, color="y", opacity=0.6)
        #self.plotter.add_mesh(top, show_edges=True, color="r", opacity=0.6)
        self.plotter.add_mesh(clipped, show_edges=True, color="r", opacity=0.6)
        #self.plotter.add_mesh(create_cone2, show_edges=True, color="y", opacity=0.6)
        #self.plotter.add_mesh(create_cone3, show_edges=True, color="y", opacity=0.6)
        #self.plotter.add_mesh(create_cone4, show_edges=True, color="y", opacity=0.6)
        #self.plotter.add_mesh(create_cone5, show_edges=True, color="y", opacity=0.6)
        self.plotter.add_mesh(create_cone6, show_edges=True, color="y", opacity=0.6)
        self.plotter.add_mesh(clipped6, show_edges=True, color="r", opacity=0.6)
        "Z is the Max point of the minium points calcualted in order to create cube within space"
        next_cube = pv.Cube(center = (x_Min,y_Min,face_center[0][2] + (abs(z1)/2)), x_length= abs(z1), y_length = abs(z1), z_length = abs(z1), bounds=None)
        next_cube6 = pv.Cube(center = (x_Min,y_Min,face_center[5][2] - (abs(z)/2)), x_length= abs(z), y_length= abs(z), z_length = abs(z), bounds=None)
        cell_center1 = next_cube.cell_centers()
        #create_cone7 = pv.Cone(center=cell_center1[0] + [0,0,hi/2], direction = [0,0,-1], height = hi, radius=None, resolution= 100, angle = ang, capping=False)
       # clipped7 = mesh.clip_surface(create_cone7, invert=True)
        #cone_intersection7 = np.array(clipped7.points)
       # nearest_point7 = min(cone_intersection7, key=min)
        #x7 = nearest_point7[0] - cell_center1[0][0]
        #y7 = nearest_point7[1] - cell_center1[0][1]
        #z7 = nearest_point7[2] - cell_center1[0][2]
        #next_cube7 = pv.Cube(center = (x_Min,y_Min,cell_center1[0][2] - (abs(x7)/2)), x_length= abs(x7), y_length= abs(x7), z_length = abs(x7), bounds=None)
        next_cube_center_top = np.array(cell_center1.points)
        cell_center6 = next_cube6.cell_centers()
        next_cube__center_bottom = np.array(cell_center6.points)
        print(next_cube__center_bottom)
        self.plotter.add_mesh(next_cube, show_edges=True, color = "b", opacity = 0.6)
        self.plotter.add_mesh(cell_center1, color="r", point_size=8.0, render_points_as_spheres=True)
        
        self.plotter.add_mesh(next_cube6, show_edges=True, color = "b", opacity = 0.6)
        self.plotter.add_mesh(cell_center6, color="r", point_size=8.0, render_points_as_spheres=True)
        #self.plotter.add_mesh(next_cube7, show_edges=True, color = "b", opacity = 0.6)
        #self.plotter.add_mesh(cell_center1, color="r", point_size=8.0, render_points_as_spheres=True)
        #clip1 = mesh.clip_surface(create_cone, invert=True)
        #clip2 = mesh.clip_surface(c2, invert=True)
        #clip = [clip1, clip2]
        #self.plotter.add_mesh(clip1, opacity=0.6, show_edges=True, color="w")
        #self.plotter.add_mesh(clip[1], opacity=0.6, show_edges=True, color="g")
        #self.plotter.add_mesh(cone_center, color="r", point_size=8.0, render_points_as_spheres=True)
        #print("Cone Center:",cone_center_points)
        
    def centroid(self):
        """ find centroid volumetrically and indicate on graph """
        global Vol_centroid, V

        # find the vertices & the vertex indices of each triangular face
        V = np.array(mesh.points)
        col = len(V)
        f_ind = np.array(mesh.faces.reshape((-1,4))[:, 1:4])
        
        # define an arbitrary start point from middle of max and min of X,Y,Z of
        # all points: in a convex manifold it falls inside the volume (requires
        # segmentation for general application)
        start = np.array(mesh.center)
        X_start = start[0]
        Y_start = start[1]
        Z_start = start[2]
        
        # initialize variables
        centroids = []
        Vol_total = 0
        Sum_vol_x = 0
        Sum_vol_y = 0
        Sum_vol_z = 0
        
        # find centroid from all tetrahedra made with arbitrary center and triangular faces
        for i in range(0, col-1, 3):          
            # find the center of each tetrahedron (average of X,Y,Z of 
            # 4 vertices, 3 from the triangle, and one arbitrary start point)
            X_cent = (X_start + V[f_ind[i,0],0] + V[f_ind[i+1,0],0] + V[f_ind[i+2,0],0]) / 4
            Y_cent = (Y_start + V[f_ind[i,1],1] + V[f_ind[i+1,1],1] + V[f_ind[i+2,1],1]) / 4
            Z_cent = (Z_start + V[f_ind[i,2],2] + V[f_ind[i+1,2],2] + V[f_ind[i+2,2],2]) / 4
    
            # compute the volume of each tetrahedron
            V1 = np.array([V[f_ind[i,0],0], V[f_ind[i,1],1], V[f_ind[i,2],2]])**2 - np.array([X_start, Y_start, Z_start])**2
            V2 = np.array([V[f_ind[i+1,0],0], V[f_ind[i+1,1],1], V[f_ind[i+1,2],2]])**2 - np.array([V[f_ind[i,0],0], V[f_ind[i,1],1], V[f_ind[i,2],2]])**2
            V3 = np.array([V[f_ind[i+2,0],0], V[f_ind[i+2,1],1], V[f_ind[i+2,2],2]])**2 - np.array([V[f_ind[i+1,0],0], V[f_ind[i+1,1],1], V[f_ind[i+1,2],2]])**2
            V1 = V1.reshape((-1,1))
            V2 = V2.reshape((-1,1))
            V3 = V3.reshape((-1,1))
            Vol = abs(np.linalg.det(np.hstack([V1, V2, V3]))) / 6
    
            # tally up each cycle
            Vol_total = Vol_total + Vol
            Sum_vol_x = Sum_vol_x + Vol * X_cent
            Sum_vol_y = Sum_vol_y + Vol * Y_cent
            Sum_vol_z = Sum_vol_z + Vol * Z_cent
            centroids.append([X_cent,Y_cent,Z_cent])
        
        # find & show centroid
        centroids = np.asarray(centroids)
        Vol_centroid = [Sum_vol_x, Sum_vol_y, Sum_vol_z] / Vol_total
        print("Total Volume:", Vol_total)
        print("Centroid:", Vol_centroid)

    def max_cube(self):
        """ add a maximally inscribed cube within the opened mesh """
        global max_c1, max_c2

        # reset plotter
        self.reset_plotter()

        # project cones to from centroid to find maximally inscribed cube
        ranges = mesh.bounds
        h = (abs(ranges[4]) + abs(ranges[5]))/3
        ang = np.arctan(0.5/(np.sqrt(2)/2))
        ang = float(90 - np.degrees(ang))
        max_c1 = pv.Cone(center=Vol_centroid+[0,0,h/2], direction=[0.0, 0.0, -1.0], height=h, radius=None, capping=False, angle=ang, resolution=100)
        max_c2 = pv.Cone(center=Vol_centroid-[0,0,h/2], direction=[0.0, 0.0, 1.0], height=h, radius=None, capping=False, angle=ang, resolution=100)
        #self.plotter.add_mesh(max_c1,color="r", opacity=0.2)
        #self.plotter.add_mesh(max_c2,color="r", opacity=0.2)

        # show centroid
        self.plotter.add_mesh(pv.PolyData(Vol_centroid), color='r', point_size=20.0, render_points_as_spheres=True)
        
        # find the nearest possible max cube vertex
        top = self.nearest_pt(max_c1, Vol_centroid)
        bottom = self.nearest_pt(max_c2, Vol_centroid)
        if top[0] < bottom[0]:
            p = top[1]
            V = top[2]
        else:
            p = bottom[1]
            V = bottom[2]
        
        # create and show max cube
        self.create_cube(V[p,:], Vol_centroid)
        self.plotter.add_mesh(max_cube, show_edges=True, color="b", opacity=0.6)
        #self.plotter.add_mesh(cell_center, color="r", point_size=8.0, render_points_as_spheres=True)

        # re-assign V as points of mesh
        V = np.array(mesh.points)

    def nearest_pt(self, cone, starting_pt):
        """ find nearest vertex: for segmented convex manifold, a cube with volume centroid as 
        center and nearest vertex as cube vertex, it falls inside the volume """
        global vert, p, clip

        clip = mesh.clip_surface(cone, invert=True)
        #clip2 = mesh.clip_surface(cone, invert=False)
        #diff = clip1.boolean_difference(clip2)
        #vert = np.array(diff.points)
        #self.plotter.add_mesh(clip, opacity=0.6, show_edges=True, color="g")

        # find nearest point in the clipped mesh
        vert = np.array(clip.points)
        c = len(vert)
        dist = np.zeros(c)
        for i in range(0, c):
            dist[i] = np.sqrt((vert[i,0] - starting_pt[0])**2 + (vert[i,1] - starting_pt[1])**2
                            + (vert[i,2] - starting_pt[2])**2)
                
        # find index of the nearest point
        nearest = min(dist)
        p = np.where(dist == nearest)
        p = p[0].item()

        return nearest, p, vert
            
    def create_cube(self, vertex, centroid):
        ''' create cube from the nearest pt & centroid '''
        global face_center, max_cube   

        # find the other 7 vertices
        # 3 vertices can be found by rotating the first point 90 degrees 3 times around Z axis of centroid
        # 4 vertices can be found by translating the first four vertices twice the half edge
        # found from the distance times sin(pi/4)
        t = sp.Symbol('t')
        Rz_t = Matrix([[sp.cos(t), -sp.sin(t), 0],
            [sp.sin(t), sp.cos(t), 0],
            [0, 0, 1]])
        #Txy = Matrix([[1, 0, centroid[0]],
        #    [0, 1, centroid[1]],
        #    [0, 0, 1]])
        #T_x_y = Matrix([[1, 0, -centroid[0]],
        #    [0, 1, -centroid[1]],
        #    [0, 0, 1]])
        #Rz_Txy_t = Txy * Rz_t * T_x_y
        #Rz_Txy = lambdify(t, Rz_Txy_t)
        Rz = lambdify(t, Rz_t)
        
        # construct the array of the first 4 vertices
        V_a = np.array(vertex-[centroid[0], centroid[1], 0])
        a_2 = np.dot(Rz(np.pi/2), V_a.T).T + np.array([centroid[0], centroid[1], 0])
        a_3 = np.dot(Rz(np.pi), V_a.T).T + np.array([centroid[0], centroid[1], 0])
        a_4 = np.dot(Rz(3*np.pi/2), V_a.T).T + np.array([centroid[0], centroid[1], 0])
        V_a = np.array(vertex)
        cube_V_start = np.array([V_a, a_2, a_3, a_4])
        print(cube_V_start)
        
        # find the translation distance
        half_edge = np.ones((4,1)) * [[0, 0, np.sign(centroid[2]-vertex[2])]] * np.sqrt((vertex[0]-centroid[0])**2 + (vertex[1]-centroid[1])**2) * sp.sin(sp.pi/4)
        translate = np.asarray(2*half_edge, dtype=np.float64)

        # construct the cube
        cube_V_end = np.add(cube_V_start, translate)
        cube_V = np.vstack((cube_V_start, cube_V_end))

        # construct the 6 faces of the cube
        cube_F =np.hstack([[4,0,1,2,3],
                        [4,0,3,7,4],
                        [4,0,1,5,4],
                        [4,1,2,6,5],
                        [4,2,3,7,6],
                        [4,4,5,6,7]])

        max_cube = pv.PolyData(cube_V, cube_F)

        cell_center = max_cube.cell_centers()
        face_center = np.array(cell_center.points)
        #print("Center of Faces:",face_center)
        
    def next_cubes(self):
        ''' create cubes within the mesh from the face centers of the first cube'''
        hi = 12
        ang = np.arctan(1/(np.sqrt(2)/2))
        ang = float(90 - np.degrees(ang))
        create_cone = pv.Cone(center=face_center[0] + [0,0,hi/2], direction = [0.0,0.0,-1.0], height = hi, radius=None, resolution= 100, angle = ang, capping=False)
        create_cone2 = pv.Cone(center=face_center[1] + [0,0,hi/2], direction = [-1.0,-1.0,0.0], height = hi, radius=None, resolution= 100, angle = ang, capping=False)
        create_cone5 = pv.Cone(center=face_center[5] + [0,0,-hi/2], direction = [0,0,1], height = hi, radius=None, resolution= 100, angle = ang, capping=False)
        clipped = mesh.clip_surface(create_cone, invert=True)
        clipped5 = mesh.clip_surface(create_cone5, invert=True)
        cone_intersection5 = np.array(clipped5.points)
        #nearest_cone = min(cone_intersection5)
        
        print("Bottom Intersection:",cone_intersection5)
        #cone_center = create_cone.cell_centers()
        #cone_center_points = np.array(cone_center.points)
        self.plotter.add_mesh(create_cone, color="y", opacity=0.6)
        self.plotter.add_mesh(clipped, show_edges=True, color="r", opacity=0.6)
        self.plotter.add_mesh(create_cone2, show_edges=True, color="y", opacity=0.6)
        self.plotter.add_mesh(create_cone5, show_edges=True, color="y", opacity=0.6)
        self.plotter.add_mesh(clipped5, show_edges=True, color="r", opacity=0.6)
        #clip1 = mesh.clip_surface(create_cone, invert=True)
        #clip2 = mesh.clip_surface(max_c2, invert=True)
        #clip = [clip1, clip2]
        #self.plotter.add_mesh(clip1, opacity=0.6, show_edges=True, color="w")
        #self.plotter.add_mesh(clip[1], opacity=0.6, show_edges=True, color="g")
        #self.plotter.add_mesh(cone_center, color="r", point_size=8.0, render_points_as_spheres=True)
        #print("Cone Center:",cone_center_points)

    def cube_hslice(self):
        """ slice mesh horizontally based on internal cubes """
        # reset plotter
        self.reset_plotter()

        # create sliced parts
        part1 = mesh.clip_closed_surface('z', origin=face_center[0])
        part2_a = mesh.clip_closed_surface('-z', origin=face_center[0])
        part2 = part2_a.clip_closed_surface('z', origin=face_center[5])
        part3 = mesh.clip_closed_surface('-z', origin=face_center[5])

        # display sliced parts
        self.plotter.clear()
        self.plotter.add_mesh(max_cube, show_edges=True, color="b", opacity=0.6)
        self.plotter.add_mesh(pv.PolyData(Vol_centroid), color='r', point_size=20.0, render_points_as_spheres=True)
        self.plotter.add_mesh(part1, show_edges=True, color="r", opacity=0.4)
        self.plotter.add_mesh(part2, show_edges=True, color="w", opacity=0.4)
        self.plotter.add_mesh(part3, show_edges=True, color="g", opacity=0.4)

    def slice(self):
        """ slice the mesh interactively """
        # reset plotter
        self.reset_plotter()

        self.plotter.add_mesh_slice_orthogonal(mesh)
    
    def clip_slice(self):
        """ slice & clip the mesh interactively """     
        # reset plotter
        self.reset_plotter()

        self.plotter.add_mesh_clip_plane(mesh)

    def bounding(self, level):
        level = int(level)
        bound = mesh.obbTree
        bound.SetMaxLevel(10)
        bound.GenerateRepresentation(level, boxes)
        self.plotter.add_mesh(boxes, opacity=0.2, color="g")
        return

    def bounding_bar(self):
        """ show various levels of OBB (Oriented Bounding Box) interactively """  
        # initialize bounding boxes mesh
        global boxes
        boxes = pv.PolyData()

        # reset plotter
        self.reset_plotter()

        self.plotter.add_slider_widget(self.bounding, [0, 10], title='Level')

    def closeEvent(self, event):
        reply = QMessageBox.question(self, "Window Close", "Are you sure you want to quit program?",
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()
Example #17
0
class MainWindow(Qt.QMainWindow):
    def __init__(self, parent=None, show=True):
        Qt.QMainWindow.__init__(self, parent)

        # create the frame
        self.frame = Qt.QFrame()
        vlayout = Qt.QVBoxLayout()

        # add the pyvista interactor object
        self.plotter = QtInteractor(self.frame)
        vlayout.addWidget(self.plotter.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        # simple menu
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        editMenu = mainMenu.addMenu('Edit')
        
        # opening a mesh file
        self.open_mesh_action = Qt.QAction('Open Mesh...', self)
        self.open_mesh_action.triggered.connect(self.open_mesh)
        fileMenu.addAction(self.open_mesh_action)
        
        # exit button
        exitButton = Qt.QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        # inserting maximally inscribed cube via cone intersection
        # self.max_cube_cone_action = Qt.QAction('Max Cube - Cone', self)
        # self.max_cube_cone_action.triggered.connect(self.max_cube_cone)
        # editMenu.addAction(self.max_cube_cone_action)

        # inserting maximally inscribed cube via ray tracing
        self.max_cube_ray_action = Qt.QAction('Max Cube - Ray', self)
        self.max_cube_ray_action.triggered.connect(self.max_cube_ray)
        editMenu.addAction(self.max_cube_ray_action)
        
        # inserting maximally inscribed cube via ray tracing
        self.ext_max_cube_action = Qt.QAction('Extend Max Cube', self)
        self.ext_max_cube_action.triggered.connect(self.ext_max_cube)
        editMenu.addAction(self.ext_max_cube_action)

        #Create Cone in Mesh
        self.next_cubes_action = Qt.QAction('Next Cubes', self)
        self.next_cubes_action.triggered.connect(self.next_cubes_ray)
        editMenu.addAction(self.next_cubes_action)
        
        # slice mesh horizontally based on internal cubes
        self.cube_hslice_action = Qt.QAction('Cube H-Slice', self)
        self.cube_hslice_action.triggered.connect(self.cube_hslice)
        editMenu.addAction(self.cube_hslice_action)

        # slice mesh (interactively)
        self.slice_action = Qt.QAction('Slice', self)
        self.slice_action.triggered.connect(self.slice)
        editMenu.addAction(self.slice_action)
        
        # slice mesh with clipping (interactively)
        self.clip_slice_action = Qt.QAction('Clip Slice', self)
        self.clip_slice_action.triggered.connect(self.clip_slice)
        editMenu.addAction(self.clip_slice_action)

        # create bounding box(es) for mesh (interactively)
        self.bounding_action = Qt.QAction('Bounding', self)
        self.bounding_action.triggered.connect(self.bounding_bar)
        editMenu.addAction(self.bounding_action)
        
        if show:
            self.show()

        self.plotter.add_axes(interactive=None, line_width=2, color=None, x_color=None, y_color=None, z_color=None, xlabel='X', ylabel='Y', zlabel='Z', labels_off=False, box=None, box_args=None)

    def open_mesh(self):
        """ add a mesh to the pyqt frame """
        global mesh, mesh_vol

        # open file
        file_info = QtWidgets.QFileDialog.getOpenFileName()
        print(file_info)
        file_path = file_info[0]
        
        # determine file type and if conversion needed
        file_dir, file_name = os.path.split(file_path)
        mesh_name, mesh_type = os.path.splitext(file_name)

        # convert mesh file type
        #if ext != ".vtk" or ext != ".VTK":
        #    mesh = meshio.read(file_path)
        #    meshio.write(root + ".vtk", mesh)
        #    mesh = pv.read(head + "/" + root + ".vtk")
            # need to store elsewhere or delete .vtk file in the future
        #else:
        #    mesh = pv.read(file_path)

        # read mesh & transform according to principal axes
        pre = trimesh.load_mesh(file_path)
        orient = pre.principal_inertia_transform
        pre = pre.apply_transform(orient)
        pre.export('data/'+ mesh_name + '_oriented.STL')
        mesh = pv.read('data/'+ mesh_name + '_oriented.STL')

        # print mesh info
        print("Mesh Name:", mesh_name)
        print("Mesh Type:", mesh_type[1:])

        # show transformed mesh
        self.plotter.add_mesh(mesh, show_edges=True, color="w", opacity=0.6)

        # reset plotter
        self.reset_plotter()

        # find mesh centroid and translate the mesh so that's the origin
        self.centroid()

        # show bounding box
        # self.plotter.add_bounding_box(opacity=0.5, color="y")

        # mesh volume
        mesh_vol = float(format(mesh.volume, ".5f"))
        print("Mesh Volume:", mesh_vol)

    def reset_plotter(self):
        """ clear plotter of mesh or interactive options """
        # clear plotter
        self.plotter.clear()
        self.plotter.clear_plane_widgets()
        self.plotter.reset_camera()
        
        # callback opened mesh
        self.plotter.add_mesh(mesh, show_edges=True, color="w", opacity=0.6)
        
        # show origin
        self.plotter.add_axes_at_origin(xlabel='X', ylabel='Y', zlabel='Z', line_width=6, labels_off=True)

        # show floors
        #self.plotter.add_floor('-y')
        #self.plotter.add_floor('-z')
        
    def centroid(self):
        """ find centroid volumetrically and indicate on graph """
        global Vol_centroid, V

        # find the vertices & the vertex indices of each triangular face
        V = np.array(mesh.points)
        col = len(V)
        f_ind = np.array(mesh.faces.reshape((-1,4))[:, 1:4])
        
        # define an arbitrary start point from middle of max and min of X,Y,Z of
        # all points: in a convex manifold it falls inside the volume (requires
        # segmentation for general application)
        start = np.array(mesh.center)
        X_start = start[0]
        Y_start = start[1]
        Z_start = start[2]
        
        # initialize variables
        centroids = []
        Vol_total = 0
        Sum_vol_x = 0
        Sum_vol_y = 0
        Sum_vol_z = 0
        
        # find centroid from all tetrahedra made with arbitrary center and triangular faces
        for i in range(0, col-1, 3):          
            # find the center of each tetrahedron (average of X,Y,Z of 
            # 4 vertices, 3 from the triangle, and one arbitrary start point)
            X_cent = (X_start + V[f_ind[i,0],0] + V[f_ind[i+1,0],0] + V[f_ind[i+2,0],0]) / 4
            Y_cent = (Y_start + V[f_ind[i,1],1] + V[f_ind[i+1,1],1] + V[f_ind[i+2,1],1]) / 4
            Z_cent = (Z_start + V[f_ind[i,2],2] + V[f_ind[i+1,2],2] + V[f_ind[i+2,2],2]) / 4
    
            # compute the volume of each tetrahedron
            V1 = np.array([V[f_ind[i,0],0], V[f_ind[i,1],1], V[f_ind[i,2],2]])**2 - np.array([X_start, Y_start, Z_start])**2
            V2 = np.array([V[f_ind[i+1,0],0], V[f_ind[i+1,1],1], V[f_ind[i+1,2],2]])**2 - np.array([V[f_ind[i,0],0], V[f_ind[i,1],1], V[f_ind[i,2],2]])**2
            V3 = np.array([V[f_ind[i+2,0],0], V[f_ind[i+2,1],1], V[f_ind[i+2,2],2]])**2 - np.array([V[f_ind[i+1,0],0], V[f_ind[i+1,1],1], V[f_ind[i+1,2],2]])**2
            V1 = V1.reshape((-1,1))
            V2 = V2.reshape((-1,1))
            V3 = V3.reshape((-1,1))
            Vol = abs(np.linalg.det(np.hstack([V1, V2, V3]))) / 6
    
            # tally up each cycle
            Vol_total = Vol_total + Vol
            Sum_vol_x = Sum_vol_x + Vol * X_cent
            Sum_vol_y = Sum_vol_y + Vol * Y_cent
            Sum_vol_z = Sum_vol_z + Vol * Z_cent
            centroids.append([X_cent,Y_cent,Z_cent])
        
        # find & show centroid
        centroids = np.asarray(centroids)
        Vol_centroid = [Sum_vol_x, Sum_vol_y, Sum_vol_z] / Vol_total
        # print("Total Volume:", Vol_total)
        # print("Centroid:", Vol_centroid)

    # def max_cube_cone(self):
    #     """ add a maximally inscribed cube within the opened mesh (via cone intersection) """
    #     global ranges, max_c1, max_c2, nearest_vert
    #     global face_center, max_cube, max_normal, max_cube_vol
    #     global cube_V, cube_F
    #     global Vol_centroid, V

    #     # find the vertices & the vertex indices of each triangular face
    #     V = np.array(mesh.points)

    #     # reset plotter
    #     self.reset_plotter()

    #     # show centroid
    #     #Vol_centroid = np.array([0,0,0])
    #     self.plotter.add_mesh(pv.PolyData(Vol_centroid), color='r', point_size=20.0, render_points_as_spheres=True)

    #     # project cones to from centroid to find maximally inscribed cube
    #     ranges = mesh.bounds
    #     h = (abs(ranges[4]) + abs(ranges[5]))/3
    #     ang = np.arctan(0.5/(np.sqrt(2)/2))
    #     ang = float(90 - np.degrees(ang))
    #     max_c1 = pv.Cone(center=Vol_centroid+[0,0,h/2], direction=[0,0,-1], height=h, radius=None, capping=False, angle=ang, resolution=100)
    #     max_c2 = pv.Cone(center=Vol_centroid-[0,0,h/2], direction=[0,0,1], height=h, radius=None, capping=False, angle=ang, resolution=100)
    #     self.plotter.add_mesh(max_c1, color="r", show_edges=True, opacity=0.4)
    #     self.plotter.add_mesh(max_c2, color="r", show_edges=True, opacity=0.4)
        
    #     # find the nearest possible cube vertex from top cone & mesh intersection
    #     top_clip = mesh.clip_surface(max_c1, invert=True)
    #     top_vert = np.array(top_clip.points)
    #     top = self.nearest_pt(top_vert, Vol_centroid)

    #     # find the nearest possible cube vertex from bottom cone & mesh intersection
    #     bottom_clip = mesh.clip_surface(max_c2, invert=True)
    #     bottom_vert = np.array(bottom_clip.points)
    #     bottom = self.nearest_pt(bottom_vert, Vol_centroid)

    #     # show top & bottom clipped surfaces
    #     #self.plotter.add_mesh(top_clip, opacity=0.6, show_edges=True, color="g")
    #     #self.plotter.add_mesh(bottom_clip, opacity=0.6, show_edges=True, color="g")

    #     # find the nearest possible cube vertex between the two
    #     if top[0] < bottom[0]:
    #         p = top[1]
    #         V = top[2]
    #     else:
    #         p = bottom[1]
    #         V = bottom[2]
        
    #     # create max cube from nearest possible cube vertex
    #     cube_V, cube_F = self.create_cube(V[p,:], Vol_centroid, np.array([0,0,1]))
    #     max_cube = pv.PolyData(cube_V, cube_F)

    #     # show max cube
    #     self.plotter.add_mesh(max_cube, show_edges=True, color="b", opacity=0.6)

    #     # record nearest vertex
    #     nearest_vert = V[p,:]

    #     # find & show max cube face centers
    #     cell_center = max_cube.cell_centers()
    #     face_center = np.array(cell_center.points)
    #     self.plotter.add_mesh(cell_center, color="r", point_size=8.0, render_points_as_spheres=True)

    #     # find max cube face normals
    #     max_normal = max_cube.cell_normals

    #     # max cube volume
    #     max_cube_vol = float(format(max_cube.volume, ".5f"))
    #     print("Max Cube Volume:", max_cube_vol)

    def max_cube_ray(self):
        """ add a maximally inscribed cube within the opened mesh (via ray tracing) """
        global ranges, nearest_vert
        global face_center, max_cube, max_normal, max_cube_vol
        global max_cube_V, max_cube_F
        global Vol_centroid, V
        global max_cube_start, max_cube_end, max_cube_run

        # track starting time
        max_cube_start = time.time()

        # find the vertices & the vertex indices of each triangular face
        V = np.array(mesh.points)

        # reset plotter
        self.reset_plotter()

        # show centroid
        Vol_centroid = np.array([0,0,0])
        self.plotter.add_mesh(pv.PolyData(Vol_centroid), color='r', point_size=20.0, render_points_as_spheres=True)

        # project rays from centroid to find maximally inscribed cube
        ranges = mesh.bounds

        # find the nearest possible cube vertex from top rays & mesh intersection
        top_vert = self.cube_center_ray(Vol_centroid, 'z')
        top = self.nearest_pt(top_vert, Vol_centroid)

        # find the nearest possible cube vertex from bottom rays & mesh intersection
        bottom_vert = self.cube_center_ray(Vol_centroid, '-z')
        bottom = self.nearest_pt(bottom_vert, Vol_centroid)

        # find the nearest possible cube vertex between the two
        if top[0] < bottom[0]:
            p = top[1]
            V = top[2]
        else:
            p = bottom[1]
            V = bottom[2]
        
        # create and show max cube
        max_cube_V, max_cube_F, max_cube_vol = self.create_cube(V[p,:], Vol_centroid, np.array([0,0,Vol_centroid[2]]))
        max_cube = pv.PolyData(max_cube_V, max_cube_F)
        self.plotter.add_mesh(max_cube, show_edges=True, line_width=3, color="g", opacity=0.6)

        # record nearest vertex
        nearest_vert = V[p,:]

        # find & show max cube face centers
        cell_center = max_cube.cell_centers()
        face_center = np.array(cell_center.points)
        print("Max Cube Face Centers:", face_center)
        self.plotter.add_mesh(cell_center, color="r", point_size=8, render_points_as_spheres=True)

        # find max cube face normals
        max_normal = max_cube.cell_normals
        print("Max Cube Normals:", max_normal)

        # max cube volume
        max_cube_vol = float(format(max_cube_vol, ".5f"))
        print("Max Cube Volume:", max_cube_vol)

        # track ending time & duration
        max_cube_end = time.time()
        max_cube_run = max_cube_end - max_cube_start

    def cube_center_ray(self, start, dir):
        ''' from starting point shoot out 8 rays to find vertices of a possible cube,
        whose face normals would be in either in x,y,z direction, or rotated 45 deg along z-axis '''
        # set ray directions
        if dir == 'z':
            r1_dir = np.array([1,1,1])
            #r2_dir = np.array([1,0,1])
            r3_dir = np.array([1,-1,1])
            #r4_dir = np.array([0,-1,1])
            r5_dir = np.array([-1,-1,1])
            #r6_dir = np.array([-1,0,1])
            r7_dir = np.array([-1,1,1])
            #r8_dir = np.array([0,1,1])
        elif dir == '-z':
            r1_dir = np.array([1,1,-1])
            #r2_dir = np.array([1,0,-1])
            r3_dir = np.array([1,-1,-1])
            #r4_dir = np.array([0,-1,-1])
            r5_dir = np.array([-1,-1,-1])
            #r6_dir = np.array([-1,0,-1])
            r7_dir = np.array([-1,1,-1])
            #r8_dir = np.array([0,1,-1])

        # set ray length
        r_len = abs(ranges[4] - ranges[5])/2 * np.sqrt(0.5**2 + (np.sqrt(2)/2)**2)

        # set ray end points
        r1_end = start + r1_dir / np.linalg.norm(r1_dir) * r_len
        #r2_end = start + r2_dir / np.linalg.norm(r2_dir) * r_len
        r3_end = start + r3_dir / np.linalg.norm(r3_dir) * r_len
        #r4_end = start + r4_dir / np.linalg.norm(r4_dir) * r_len
        r5_end = start + r5_dir / np.linalg.norm(r5_dir) * r_len
        #r6_end = start + r6_dir / np.linalg.norm(r6_dir) * r_len
        r7_end = start + r7_dir / np.linalg.norm(r7_dir) * r_len
        #r8_end = start + r8_dir / np.linalg.norm(r8_dir) * r_len
        
        # perform ray trace
        r1_pts, r1_ind = mesh.ray_trace(start, r1_end)
        #r2_pts, r2_ind = mesh.ray_trace(start, r2_end)
        r3_pts, r3_ind = mesh.ray_trace(start, r3_end)
        #r4_pts, r4_ind = mesh.ray_trace(start, r4_end)
        r5_pts, r5_ind = mesh.ray_trace(start, r5_end)
        #r6_pts, r6_nd = mesh.ray_trace(start, r6_end)
        r7_pts, r7_ind = mesh.ray_trace(start, r7_end)
        #r8_pts, r8_ind = mesh.ray_trace(start, r8_end)

        # initialize rays
        r1 = pv.Line(start, r1_end)
        #r2 = pv.Line(start, r2_end)
        r3 = pv.Line(start, r3_end)
        #r4 = pv.Line(start, r4_end)
        r5 = pv.Line(start, r5_end)
        #r6 = pv.Line(start, r6_end)
        r7 = pv.Line(start, r7_end)
        #r8 = pv.Line(start, r8_end)

        # initialize intersections
        r1_int = pv.PolyData(r1_pts[0])
        #r2_int = pv.PolyData(r2_pts[0])
        r3_int = pv.PolyData(r3_pts[0])
        #r4_int = pv.PolyData(r4_pts[0])
        r5_int = pv.PolyData(r5_pts[0])
        #r6_int = pv.PolyData(r6_pts[0])
        r7_int = pv.PolyData(r7_pts[0])
        #r8_int = pv.PolyData(r8_pts[0])

        # show rays
        l_wid = 6
        #self.plotter.add_mesh(r1, color='w', line_width=l_wid)
        #self.plotter.add_mesh(r2, color='w', line_width=l_wid)
        #self.plotter.add_mesh(r3, color='w', line_width=l_wid)
        #self.plotter.add_mesh(r4, color='w', line_width=l_wid)
        #self.plotter.add_mesh(r5, color='w', line_width=l_wid)
        #self.plotter.add_mesh(r6, color='w', line_width=l_wid)
        #self.plotter.add_mesh(r7, color='w', line_width=l_wid)
        #self.plotter.add_mesh(r8, color='w', line_width=l_wid)

        # show intersections
        pt_size = 20
        #self.plotter.add_mesh(r1_int, color='w', point_size=pt_size)
        #self.plotter.add_mesh(r2_int, color='w', point_size=pt_size)
        #self.plotter.add_mesh(r3_int, color='w', point_size=pt_size)
        #self.plotter.add_mesh(r4_int, color='w', point_size=pt_size)
        #self.plotter.add_mesh(r5_int, color='w', point_size=pt_size)
        #self.plotter.add_mesh(r6_int, color='w', point_size=pt_size)
        #elf.plotter.add_mesh(r7_int, color='w', point_size=pt_size)
        #self.plotter.add_mesh(r8_int, color='w', point_size=pt_size)

        # array of intersections
        # r_int = np.vstack([r1_int.points, r2_int.points, r3_int.points, r4_int.points,
        #             r5_int.points, r6_int.points, r7_int.points, r8_int.points])
        r_int = np.vstack([r1_int.points, r3_int.points, r5_int.points, r7_int.points])

        return r_int

    def nearest_pt(self, vert, starting_pt):
        """ find nearest vertex: for segmented convex manifold, a cube with volume centroid as 
        center and nearest vertex as cube vertex, it falls inside the volume """
        # find nearest point from the list of points
        c = len(vert)
        dist = np.zeros(c)
        for i in range(0, c):
            dist[i] = np.sqrt((vert[i,0] - starting_pt[0])**2 + (vert[i,1] - starting_pt[1])**2
                            + (vert[i,2] - starting_pt[2])**2)
                
        # find index of the nearest point
        nearest = min(dist)
        p = np.where(dist == nearest)
        p = p[0].item()

        return nearest, p, vert
            
    def rot_axis(self, axis):
        ''' create a rotational matrix about an arbitrary axis '''
        t = sp.Symbol('t')

        R_t = Matrix([[sp.cos(t)+axis[0]**2*(1-sp.cos(t)), axis[0]*axis[1]*(1-sp.cos(t))-axis[2]*sp.sin(t), axis[0]*axis[2]*(1-sp.cos(t))+axis[1]*sp.sin(t)],
            [axis[1]*axis[0]*(1-sp.cos(t))+axis[2]*sp.sin(t), sp.cos(t)+axis[1]**2*(1-sp.cos(t)), axis[1]*axis[2]*(1-sp.cos(t))-axis[0]*sp.sin(t)],
            [axis[2]*axis[0]*(1-sp.cos(t))-axis[1]*sp.sin(t), axis[2]*axis[1]*(1-sp.cos(t))+axis[0]*sp.sin(t), sp.cos(t)+axis[2]**2*(1-sp.cos(t))]])
        R = lambdify(t, R_t)
        return R

    def create_cube(self, vertex, starting_pt, axis):
        ''' create cube from the nearest pt & centroid '''
        if (axis[0] == 0) and (axis[1] == 0) and (axis[2] == 0):
            axis[2] = 1
            vert_trans = np.array([0,0,0])
        elif (Vol_centroid[0] == 0) and (Vol_centroid[1] == 0) and (Vol_centroid[2] == 0):
            vert_trans = np.array([0,0,0])
        else:
            vert_trans = starting_pt
            for i in range(0,3):
                if round(axis[i]) == 1 or round(axis[i]) == -1:
                    vert_trans[i] == 0
        # find the other 7 vertices
        # 3 vertices can be found by rotating the first point 90 degrees 3 times around Z axis of centroid
        # 4 vertices can be found by translating the first four vertices twice the half edge
        # found from the distance times sin(pi/4)
        R = self.rot_axis(axis / np.linalg.norm(axis))
        
        # construct the array of the first 4 vertices
        V_1 = np.array(vertex - vert_trans)
        V_2 = np.dot(R(np.pi/2), V_1.T).T
        V_3 = np.dot(R(np.pi), V_1.T).T
        V_4 = np.dot(R(3*np.pi/2), V_1.T).T
        # cube_V_start = np.array([V_1, V_2, V_3, V_4])
        cube_V_start = np.array([V_1, V_2, V_3, V_4]) + np.ones((4,1)) * [vert_trans]
        cube_V_start_center = np.array(pv.PolyData(cube_V_start).center)

        # show nearest vertex of cube
        V_1 = np.array(vertex)
        #self.plotter.add_mesh(pv.PolyData(V_1), color="y", point_size=30.0, render_points_as_spheres=True)
        
        # find the translation distance
        trans_dis = starting_pt - cube_V_start_center
        trans_dir = trans_dis / np.linalg.norm(trans_dis)
        dia_dis = np.sqrt((V_1[0]-cube_V_start_center[0])**2 + (V_1[1]-cube_V_start_center[1])**2 + (V_1[2]-cube_V_start_center[2])**2)
        half_edge = np.ones((4,1)) * [trans_dir] * dia_dis * np.sin(np.pi/4)
        cube_trans = np.asarray(2*half_edge, dtype=np.float64)

        # construct the cube
        cube_V_end = np.add(cube_V_start, cube_trans)
        cube_V = np.vstack((cube_V_start, cube_V_end))
        cube_F = np.hstack([[4,0,1,2,3],
                        [4,0,3,7,4],
                        [4,0,1,5,4],
                        [4,1,2,6,5],
                        [4,2,3,7,6],
                        [4,4,5,6,7]])

        # test for cube-ness
        # face_edge = np.sqrt((V_1[0]-V_2[0])**2 + (V_1[1]-V_2[1])**2 + (V_1[2]-V_2[2])**2)
        # trans_edge = np.sqrt((V_1[0]-cube_V_end[0][0])**2 + (V_1[1]-cube_V_end[0][1])**2 + (V_1[2]-cube_V_end[0][2])**2)
        # if round(face_edge, 4) == round(trans_edge, 4):
        #     print('Face Edge = ', round(face_edge, 4))
        #     print('Translation Edge = ', round(trans_edge, 4))
        #     print('Cube\n')
        # else:
        #     print('Face Edge = ', round(face_edge, 4))
        #     print('Translation Edge = ', round(trans_edge, 4))
        #     print('Not Cube\n')

        # cube volume
        cube_vol = (2 * np.linalg.norm(half_edge[0,:]))**3

        return cube_V, cube_F, cube_vol
    
    def ext_max_cube(self):
        ''' extend max cube into maximally inscribed cuboid '''
        global face_center, ext_max_cube, max_normal, ext_max_cube_vol

        # find the 3 out of 6 normal directions the max cube can be extended towards
        ext_dir = np.empty(shape=(3,3)) 
        main_dir = Vol_centroid - nearest_vert
        ind = 0
        for i in range(0, 6):
            if np.dot(max_normal[i,:], main_dir) > 0:
                ext_dir[ind,:] = max_normal[i,:]
                ind +=1

        # extend faces by shooting a ray from the 4 vertices on each extendable face
        # in the direction of its face normal. Find the nearest intersection and
        # it would be the limit of extension for that face
        for i in range(0, 3):
            F_ind = np.where((max_normal == ext_dir[i]).all(axis=1))
            np.reshape(max_cube_F, (6,5))
            faces = np.reshape(max_cube_F, (6,5))
            V_ind = faces[F_ind][0,1:5]
            current_V = np.vstack([max_cube_V[V_ind[0]], max_cube_V[V_ind[1]], max_cube_V[V_ind[2]], max_cube_V[V_ind[3]]])
            ext_V = self.ext_ray(current_V, ext_dir[i])
            max_cube_V[V_ind] = ext_V

        # create & show extended max cube
        ext_max_cube = pv.PolyData(max_cube_V, max_cube_F)
        self.plotter.add_mesh(ext_max_cube, show_edges=True, color="y", opacity=0.6)

        # find face centers of extended max cube
        cell_center = ext_max_cube.cell_centers()
        face_center = np.array(cell_center.points)

        # find face normals of the extended max cube
        max_normal = ext_max_cube.cell_normals

        # extended max cube volume
        ext_max_cube_vol = float(format(ext_max_cube.volume, ".5f"))
        print("Extended Max Cube Volume:", ext_max_cube_vol)

    def ext_ray(self, current_V, ext_dir):
        ''' shoot rays from vertices of a cube face towards face normal & obtain intersections with mesh '''
        # initialize variables
        ext_end = current_V + ext_dir * np.ones((4,1))
        ext_int = [None] * 4
        ext_dis = np.zeros(4)

        # perform ray tracing per extending face vertex
        for i in range(0,4):
            ext_int, ind = mesh.ray_trace(current_V[i], ext_end[i])
            ext_dis[i] = np.sqrt((ext_int[0][0] - current_V[i][0])**2 + (ext_int[0][1] - current_V[i][1])**2
                                 + (ext_int[0][2] - current_V[i][2])**2)

        # extend vertices by the shortest intersection distance
        ext_V = current_V + ext_dir * np.ones((4,1)) * min(ext_dis)
        
        return ext_V

    def next_cubes_ray(self):
        ''' create cubes within the mesh from the face centers of the first cube'''
        global next_cube_vol, max_normal

        # track starting time
        next_cube_start = time.time()
        
        # initiate variable
        next_cube_vol_sum = 0

        # fix max_normal
        normal = face_center[0] - Vol_centroid
        if (np.sign(normal[2]) != np.sign(max_normal[0,2])):
            max_normal =  np.negative(max_normal)

        # loop through all 6 faces of max cube
        for i in range(0, 6):
            # create rotaional matrix about max cube normals
            R = self.rot_axis(max_normal[i])
        
            # initialize variables (8 rays)
            # r_dir = np.zeros((8, 3))
            # r_dir_norm = np.zeros((8, 3))
            # r_end = np.zeros((8, 3))

            # initialize variables (4 rays)
            ray_size = np.zeros((4, 3))
            r_dir = ray_size
            #r_flat_dir = ray_size
            #r_flat_dir_norm = ray_size
            r_dir_norm = ray_size
            r_end = ray_size
            #r_flat_end = ray_size

            # initialize ray trace parameters
            l_wid = 5
            pt_size = 20
            x_range = abs(ranges[0] - ranges[1])
            y_range = abs(ranges[2] - ranges[3])
            z_range = abs(ranges[4] - ranges[5])
            r_len = np.sqrt((x_range/2)**2 + (y_range/2)**2 + (z_range/2)**2) * np.sqrt(1**2 + (np.sqrt(2)/2)**2)
            r_int = np.array([])

            # create a set of 8 rays from each face
            # for j in range(0, 8):
            #     if j == 0:
            #         if (i == 0) or (i == 5):
            #             r_dir[j] = np.array(max_normal[i] + [0.5,0.5,0])
            #             r_dir_norm[j] = r_dir[j] / np.linalg.norm(r_dir[j])
            #             r_end[j] = face_center[i] + r_dir_norm[j] * r_len_z
            #         else:
            #             r_dir[j] = np.array(max_normal[i] + [0,0,np.sqrt(2)/2])
            #             r_dir_norm[j] = r_dir[j] / np.linalg.norm(r_dir[j])
            #             r_end[j] = face_center[i] + r_dir_norm[j] * r_len_xy
            #     else:
            #         r_dir[j] = np.dot(R(j*np.pi/4), r_dir[0].T).T
            #         r_dir_norm[j] = r_dir[j] / np.linalg.norm(r_dir[j])
            #         if (i == 0) or (i == 5):
            #             r_end[j] = face_center[i] + r_dir_norm[j] * r_len_z
            #         else:
            #             r_end[j] = face_center[i] + r_dir_norm[j] * r_len_xy
                
            #     # perform ray trace
            #     r_pts, r_ind = mesh.ray_trace(face_center[i], r_end[j])

            #     # show rays
            #     self.plotter.add_mesh(pv.Line(face_center[i], r_end[j]), color='y', line_width=l_wid)
            #     self.plotter.add_mesh(pv.PolyData(r_pts[0]), color='y', point_size=pt_size)

            #     # create an array of ray intersections
            #     r_int = np.append(r_int, r_pts[0])
            
            for j in range(0, 4):
                if j == 0:
                    r_dir[0] = np.array(max_normal[i] + ([1,1,1] - abs(max_normal[i])) / 2)
                    r_dir_norm[0] = r_dir[0] / np.linalg.norm(r_dir[0])
                    r_end[0] = face_center[i] + r_dir_norm[0] * r_len
                else:
                    r_end[j] = np.dot(R(j*np.pi/2), (r_end[0]-Vol_centroid).T).T
                    r_end[j] = r_end[j] + Vol_centroid

                # max_cube_faces = np.reshape(max_cube_F, (6,5))
                # r_flat_dir[j] = max_cube_V[max_cube_faces[i,j+1]] - face_center[i]

                # perform ray trace
                r_pts, r_ind = mesh.ray_trace(face_center[i], r_end[j])

                # r_flat_dir_norm[j] = r_flat_dir[j] / np.linalg.norm(r_flat_dir[j])
                # r_flat_end[j] = face_center[i] + r_flat_dir_norm[j] * r_len
                # r_flat_pts, r_flat_ind = mesh.ray_trace(face_center[i], r_flat_end[j])

                # show rays
                # self.plotter.add_mesh(pv.Line(face_center[i], r_end[j]), color='w', line_width=l_wid)
                # self.plotter.add_mesh(pv.PolyData(r_pts[0]), color='w', point_size=pt_size)
                
                # self.plotter.add_mesh(pv.Line(face_center[i], r_flat_end[j]), color='w', line_width=l_wid)
                # self.plotter.add_mesh(pv.PolyData(r_flat_pts[0]), color='w', point_size=pt_size)

                # create an array of ray intersections
                r_int = np.append(r_int, r_pts[0])
                # r_int = np.append(r_int, r_flat_pts[0])

            # find nearest vertice among the ray intersections
            r_int = np.reshape(r_int, (4,3))
            r = self.nearest_pt(r_int, face_center[i])

            # create cube from nearest vertice
            next_cube_V, next_cube_F, next_cube_vol = self.create_cube(r[2][r[1],:], face_center[i], max_normal[i])
            next_cube = pv.PolyData(next_cube_V, next_cube_F)
            self.plotter.add_mesh(next_cube, show_edges=True, line_width=3, color="g", opacity=0.6)
            
            # show cut
            # u1 = next_cube.triangulate().boolean_union(mesh)
            # u2 = u1.boolean_difference(next_cube.triangulate())
            #diff = next_cube.delaunay_3d().clip_surface(mesh, invert=False)
            #self.plotter.clear()
            #self.plotter.add_mesh(next_cube.triangulate(), show_edges=True, line_width=3, color="g", opacity=0.6)
            #self.plotter.add_mesh(diff, show_edges=True, line_width=3, color="y", opacity=0.6)

            # testing
            # test_cube_V = next_cube_V[:-4,:]
            # test_cube_F = np.array([4,0,1,2,3])
            # test_cube = pv.PolyData(test_cube_V, test_cube_F)
            # self.plotter.add_mesh(test_cube, show_edges=True, line_width=3, color="g", opacity=0.6)
            
            # show next cube
            #self.plotter.add_mesh(next_cube, show_edges=True, line_width=3, color="g", opacity=0.6)

            # next cube volume
            next_cube_vol_sum = next_cube_vol_sum + next_cube_vol
        
        # show packing efficiency
        next_cube_vol_sum = float(format(next_cube_vol_sum, ".5f"))
        pack_vol = float(format((max_cube_vol + next_cube_vol_sum), ".5f"))
        pack_percent = "{:.1%}".format(pack_vol / mesh_vol)
        print("Next Cubes Volume:", next_cube_vol_sum)
        print("Packed Volume:", pack_vol)
        print("Packing Efficiency:", pack_percent)

        # track starting time
        next_cube_end = time.time()
        next_cube_run = next_cube_end - next_cube_start
        print("Total elapsed run time: %g seconds" % (max_cube_run + next_cube_run))


    def cube_hslice(self):
        """ slice mesh horizontally based on internal cubes """
        # reset plotter
        self.reset_plotter()
        
        # create sliced parts
        part1 = mesh.clip_closed_surface('z', origin=face_center[0])
        part2_a = mesh.clip_closed_surface('-z', origin=face_center[0])
        part2 = part2_a.clip_closed_surface('z', origin=face_center[5])
        part3 = mesh.clip_closed_surface('-z', origin=face_center[5])

        # display sliced parts
        self.plotter.clear()
        self.plotter.add_mesh(max_cube, show_edges=True, color="b", opacity=0.6)
        self.plotter.add_mesh(pv.PolyData(Vol_centroid), color='r', point_size=20.0, render_points_as_spheres=True)
        self.plotter.add_mesh(part1, show_edges=True, color="r", opacity=0.4)
        self.plotter.add_mesh(part2, show_edges=True, color="w", opacity=0.4)
        self.plotter.add_mesh(part3, show_edges=True, color="g", opacity=0.4)

        # volume test
        chunck_vol = format(part2.volume, ".5f")
        print(chunck_vol)

    def slice(self):
        """ slice the mesh interactively """
        # reset plotter
        self.reset_plotter()

        self.plotter.add_mesh_slice_orthogonal(mesh)
    
    def clip_slice(self):
        """ slice & clip the mesh interactively """     
        # reset plotter
        self.reset_plotter()

        self.plotter.add_mesh_clip_plane(mesh)

    def bounding(self, level):
        level = int(level)
        bound = mesh.obbTree
        bound.SetMaxLevel(10)
        bound.GenerateRepresentation(level, boxes)
        self.plotter.add_mesh(boxes, opacity=0.2, color="g")
        return

    def bounding_bar(self):
        """ show various levels of OBB (Oriented Bounding Box) interactively """  
        # initialize bounding boxes mesh
        global boxes
        boxes = pv.PolyData()

        # reset plotter
        self.reset_plotter()

        self.plotter.add_slider_widget(self.bounding, [0, 10], title='Level')

    def closeEvent(self, event):
        reply = QMessageBox.question(self, "Window Close", "Are you sure you want to quit program?",
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()
Example #18
0
    def __init__(self, datalog=None, parent=None, show=True):
        self.time_speed = 1
        self.earth_av = 7.2921150 * 360.0 * 1e-5 / (2 * np.pi)
        self.init_sideral = 0
        self.current_sideral = 0
        self.vector_point = np.zeros(3)
        self.datalog_flag = False
        self.show_ref_vector_point = False
        self.run_flag = False
        self.pause_flag = False
        self.stop_flag = False
        self.gs_flag = False
        self.thread = None
        self.countTime = 0
        self.last_index = 0
        self.max_index = 0
        self.screen = None
        self.simulation_index = -1
        self.q_t_i2b = None
        self.spacecraft_pos_i = None
        self.datalog = datalog
        self.data_handler = None
        QMainWindow.__init__(self, parent)
        self.window = Ui_MainWindow()
        self.window.setupUi(self)
        self.quaternion_t0 = None

        # Set-up signals and slots
        # self.window.actionGeneratePlot.triggered.connect(self.plot_slot)
        # self.window.control_spinbox.valueChanged.connect(self.window.control_slider.setValue)
        # self.window.control_slider.valueChanged.connect(self.window.control_spinbox.setValue)

        #########################################

        vlayout = QVBoxLayout()
        self.window.view_frame.setLayout(vlayout)

        self.vtk_widget = QtInteractor(self.window.view_frame, shape=(1, 2))
        self.vtk_widget.set_background([0.25, 0.25, 0.25])
        vlayout.addWidget(self.vtk_widget)

        self.preview_plot_widget = QtWidgets.QVBoxLayout(
            self.window.PlotWidget)
        self.canvas_ = FigureCanvas(Figure(figsize=(5, 3)))
        self.preview_plot_widget.addWidget(self.canvas_)
        self.plot_canvas = self.canvas_.figure.subplots()
        self.plot_canvas.grid()
        self.plot_canvas.set_xlabel('Time [s]')

        GeometricElements.__init__(self, self.vtk_widget)
        self.add_bar()
        self.add_i_frame_attitude()
        self.add_gs_item()

        # --------------------------------------------------------------------------------------------------------------
        # simple menu to functions
        main_menu = self.menuBar()
        # --------------------------------------------------------------------------------------------------------------
        # File option
        self.window.actionLoadCsv.triggered.connect(self.load_csv_file)
        if datalog is not None:
            self.load_csv_file()
        # --------------------------------------------------------------------------------------------------------------

        self.window.actionRun.triggered.connect(self.run_simulation)
        self.window.actionPause.triggered.connect(self.pause_simulation)
        self.window.actionStop.triggered.connect(self.stop_simulation)
        self.window.actionAddGS.triggered.connect(self.add_gs_item)

        # sim_menu.addAction(run_action)
        # sim_menu.addAction(pause_action)
        # sim_menu.addAction(stop_action)
        # --------------------------------------------------------------------------------------------------------------
        self.window.actionGeneratePlot.triggered.connect(self.add_graph2d)
        self.window.PlotSelectedData.clicked.connect(self.plot_selected_data)
        self.window.listWidget.clicked.connect(self.preview_plot_data)
Example #19
0
class MainWindow(Qt.QMainWindow):
    def __init__(self, parent=None, show=True):
        Qt.QMainWindow.__init__(self, parent)

        # create the frame
        self.frame = Qt.QFrame()
        vlayout = Qt.QVBoxLayout()

        # add the pyvista interactor object
        self.plotter = QtInteractor(self.frame)
        vlayout.addWidget(self.plotter.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        # simple menu
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        editMenu = mainMenu.addMenu('Edit')

        # opening a mesh file
        self.open_mesh_action = Qt.QAction('Open Mesh...', self)
        self.open_mesh_action.triggered.connect(self.open_mesh)
        fileMenu.addAction(self.open_mesh_action)

        # exit button
        exitButton = Qt.QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        # create cubic skeleton
        self.cubic_skeleton_action = Qt.QAction('Cubic Skeleton', self)
        self.cubic_skeleton_action.triggered.connect(self.cubic_skeleton)
        editMenu.addAction(self.cubic_skeleton_action)

        # split mesh based on max cube faces
        # self.max_cube_slice_action = Qt.QAction('Slice27', self)
        # self.cubic_skeleton_action.triggered.connect(self.max_cube_slice)
        # editMenu.addAction(self.max_cube_slice_action)

        if show:
            self.show()

        self.plotter.add_axes(interactive=None,
                              line_width=2,
                              color=None,
                              x_color=None,
                              y_color=None,
                              z_color=None,
                              xlabel='X',
                              ylabel='Y',
                              zlabel='Z',
                              labels_off=False,
                              box=None,
                              box_args=None)

    def open_mesh(self):
        """ add a mesh to the pyqt frame """
        global mesh, mesh_vol

        # open file
        file_info = QtWidgets.QFileDialog.getOpenFileName()
        print(file_info)
        file_path = file_info[0]

        # determine file type and if conversion needed
        file_dir, file_name = os.path.split(file_path)
        mesh_name, mesh_type = os.path.splitext(file_name)

        # convert mesh file type
        #if ext != ".vtk" or ext != ".VTK":
        #    mesh = meshio.read(file_path)
        #    meshio.write(root + ".vtk", mesh)
        #    mesh = pv.read(head + "/" + root + ".vtk")
        # need to store elsewhere or delete .vtk file in the future
        #else:
        #    mesh = pv.read(file_path)

        # read mesh & transform according to principal axes
        pre = trimesh.load_mesh(file_path)
        orient = pre.principal_inertia_transform
        pre = pre.apply_transform(orient)
        pre.export('data/' + mesh_name + '_oriented.STL')
        mesh = pv.read('data/' + mesh_name + '_oriented.STL')

        # print mesh info
        print("Mesh Name:", mesh_name)
        print("Mesh Type:", mesh_type[1:])

        # show transformed mesh
        #self.plotter.add_mesh(mesh, show_edges=True, color="w", opacity=0.6)

        # reset plotter
        self.reset_plotter()

        # find mesh centroid and translate the mesh so that's the origin
        self.centroid()

        # show bounding box
        # self.plotter.add_bounding_box(opacity=0.5, color="y")

        # mesh volume
        mesh_vol = float(format(mesh.volume, ".5f"))
        print("Mesh Volume:", mesh_vol)

    def reset_plotter(self):
        """ clear plotter of mesh or interactive options """
        # clear plotter
        self.plotter.clear()
        #self.plotter.clear_plane_widgets()
        #self.plotter.reset_camera()

        # callback opened mesh
        self.plotter.add_mesh(mesh, show_edges=True, color="w", opacity=0.6)

        # show origin
        self.plotter.add_axes_at_origin(xlabel='X',
                                        ylabel='Y',
                                        zlabel='Z',
                                        line_width=6,
                                        labels_off=True)

    def centroid(self):
        """ find centroid volumetrically and indicate on graph """
        global Vol_centroid, V

        # find the vertices & the vertex indices of each triangular face
        V = np.array(mesh.points)
        col = len(V)
        f_ind = np.array(mesh.faces.reshape((-1, 4))[:, 1:4])

        # define an arbitrary start point from middle of max and min of X,Y,Z of
        # all points: in a convex manifold it falls inside the volume (requires
        # segmentation for general application)
        start = np.array(mesh.center)
        X_start = start[0]
        Y_start = start[1]
        Z_start = start[2]

        # initialize variables
        centroids = []
        Vol_total = 0
        Sum_vol_x = 0
        Sum_vol_y = 0
        Sum_vol_z = 0

        # find centroid from all tetrahedra made with arbitrary center and triangular faces
        for i in range(0, col - 1, 3):
            # find the center of each tetrahedron (average of X,Y,Z of
            # 4 vertices, 3 from the triangle, and one arbitrary start point)
            X_cent = (X_start + V[f_ind[i, 0], 0] + V[f_ind[i + 1, 0], 0] +
                      V[f_ind[i + 2, 0], 0]) / 4
            Y_cent = (Y_start + V[f_ind[i, 1], 1] + V[f_ind[i + 1, 1], 1] +
                      V[f_ind[i + 2, 1], 1]) / 4
            Z_cent = (Z_start + V[f_ind[i, 2], 2] + V[f_ind[i + 1, 2], 2] +
                      V[f_ind[i + 2, 2], 2]) / 4

            # compute the volume of each tetrahedron
            V1 = np.array([
                V[f_ind[i, 0], 0], V[f_ind[i, 1], 1], V[f_ind[i, 2], 2]
            ])**2 - np.array([X_start, Y_start, Z_start])**2
            V2 = np.array([
                V[f_ind[i + 1, 0], 0], V[f_ind[i + 1, 1], 1], V[f_ind[i + 1,
                                                                      2], 2]
            ])**2 - np.array(
                [V[f_ind[i, 0], 0], V[f_ind[i, 1], 1], V[f_ind[i, 2], 2]])**2
            V3 = np.array([
                V[f_ind[i + 2, 0], 0], V[f_ind[i + 2, 1], 1], V[f_ind[i + 2,
                                                                      2], 2]
            ])**2 - np.array([
                V[f_ind[i + 1, 0], 0], V[f_ind[i + 1, 1], 1], V[f_ind[i + 1,
                                                                      2], 2]
            ])**2
            V1 = V1.reshape((-1, 1))
            V2 = V2.reshape((-1, 1))
            V3 = V3.reshape((-1, 1))
            Vol = abs(np.linalg.det(np.hstack([V1, V2, V3]))) / 6

            # tally up each cycle
            Vol_total = Vol_total + Vol
            Sum_vol_x = Sum_vol_x + Vol * X_cent
            Sum_vol_y = Sum_vol_y + Vol * Y_cent
            Sum_vol_z = Sum_vol_z + Vol * Z_cent
            centroids.append([X_cent, Y_cent, Z_cent])

        # find & show centroid
        centroids = np.asarray(centroids)
        Vol_centroid = [Sum_vol_x, Sum_vol_y, Sum_vol_z] / Vol_total

    def cubic_skeleton(self):
        ''' fill mesh with cubic skeleton'''
        global max_cube_stored

        max_cube_stored = 0

        # user input number of rays for next cubes
        # self.plotter.add_text_slider_widget(self.max_cube_ray, ['10 rays','15 rays', '20 rays'], value=0)
        self.plotter.add_text_slider_widget(self.next_cubes_ray,
                                            ['10 rays', '15 rays', '20 rays'],
                                            value=1)

    def max_cube_ray(self, value):
        """ add a maximally inscribed cube within the opened mesh (via ray tracing) """
        global x_range, y_range, z_range, Vol_centroid
        global face_center, max_normal, max_cube_vol, max_cube
        global max_cube_start, max_cube_end, max_cube_run
        global top_rays, top_ints, bottom_rays, bottom_ints

        # bypass error
        try:
            top_rays, top_ints, bottom_rays, bottom_ints, max_cube, r_num, max_cube_stored
        except NameError:
            top_rays = None
            top_ints = None
            bottom_rays = None
            bottom_ints = None
            max_cube = None
            max_cube_stored = None
            r_num = 0

        # remove old rays
        if (r_num != 0) and (r_num == int(value[0])):
            return
        elif (r_num != 0) and (max_cube_stored != None):
            self.plotter.remove_actor(max_cube_stored)
            for i in range(0, r_num):
                self.plotter.remove_actor(top_rays[i])
                self.plotter.remove_actor(top_ints[i])
                self.plotter.remove_actor(bottom_rays[i])
                self.plotter.remove_actor(bottom_ints[i])

        # track starting time
        max_cube_start = time.time()

        # find mesh vertices
        V = np.array(mesh.points)

        # find the max and min of x,y,z axes of mesh
        ranges = mesh.bounds
        x_range = abs(ranges[0] - ranges[1])
        y_range = abs(ranges[2] - ranges[3])
        z_range = abs(ranges[4] - ranges[5])

        # show centroid
        Vol_centroid = np.array(
            [0, 0, 0])  # overwrite centroid with origin at principle axes
        self.plotter.add_mesh(pv.PolyData(Vol_centroid),
                              color='r',
                              point_size=20.0,
                              render_points_as_spheres=True)

        # find the nearest possible cube vertex from top rays & mesh intersection
        top_vert, top_rays, top_ints = self.cube_center_ray(
            Vol_centroid, 'z', value)
        top = self.furthest_pt(top_vert, Vol_centroid)

        # find the nearest possible cube vertex from bottom rays & mesh intersection
        bottom_vert, bottom_rays, bottom_ints = self.cube_center_ray(
            Vol_centroid, '-z', value)
        bottom = self.furthest_pt(bottom_vert, Vol_centroid)

        # find the nearest possible cube vertex between the two
        if top[0] < bottom[0]:
            p = top[1]
            V = top[2]
        else:
            p = bottom[1]
            V = bottom[2]

        # create and show max cube
        max_cube_V, max_cube_F, max_cube_vol = self.create_cube(
            V[p, :], Vol_centroid, np.array([0, 0, Vol_centroid[2]]))
        max_cube = self.plotter.add_mesh(pv.PolyData(max_cube_V, max_cube_F),
                                         show_edges=True,
                                         line_width=3,
                                         color="g",
                                         opacity=0.6)
        max_cube_stored = max_cube

        # find & show max cube face centers
        cell_center = pv.PolyData(max_cube_V, max_cube_F).cell_centers()
        face_center = np.array(cell_center.points)
        #self.plotter.add_mesh(cell_center, color="r", point_size=8, render_points_as_spheres=True)

        # find max cube face normals
        max_normal = pv.PolyData(max_cube_V, max_cube_F).cell_normals

        # max cube volume
        max_cube_vol = float(format(max_cube_vol, ".5f"))
        print("Max Cube Volume:", max_cube_vol)

        # track ending time & duration
        max_cube_end = time.time()
        max_cube_run = max_cube_end - max_cube_start

        return

    def cube_center_ray(self, start, dir, value):
        ''' from starting point shoot out n rays to find vertices of possible cubes '''
        global r_num, r_rot, r_dec

        # initialize variables
        idx = value.index(" ")
        r_num = 0
        for i in range(0, idx):
            r_num = r_num + int(value[i]) + (idx - i)**10
        r_rot = np.pi / 2
        r_dec = -2 * np.pi / r_num
        l_wid = 5
        pt_size = 20
        ray_size = np.zeros((4, 3))
        r_dir = ray_size
        r_dir_norm = ray_size
        r_end = ray_size
        rays = [0] * r_num
        ints = [0] * r_num
        r_int = []
        ori_r_int = []

        # set ray length
        r_len = np.sqrt((x_range / 2)**2 + (y_range / 2)**2 + (z_range / 2)**2)

        # create rays by rotating the first, which creates the cube with xyz axes as its face normals
        for i in range(0, r_num):
            for j in range(0, 4):
                if (j == 0) and (dir == 'z'):
                    r_dir[0] = np.array([
                        np.sqrt(2) / 2 * np.cos(np.pi / 4 + r_dec * i),
                        np.sqrt(2) / 2 * np.sin(np.pi / 4 + r_dec * i), 0.5
                    ])
                    r_dir_norm[0] = r_dir[0] / np.linalg.norm(r_dir[0])
                    r_end[0] = Vol_centroid + r_dir_norm[0] * r_len
                    # set rotation matrix about 'z'
                    R = self.rot_axis(np.array([0, 0, 1]))
                elif (j == 0) and (dir == '-z'):
                    r_dir[0] = np.array([
                        np.sqrt(2) / 2 * np.cos(np.pi / 4 + r_dec * i),
                        np.sqrt(2) / 2 * np.sin(np.pi / 4 + r_dec * i), -0.5
                    ])
                    r_dir_norm[0] = r_dir[0] / np.linalg.norm(r_dir[0])
                    r_end[0] = Vol_centroid + r_dir_norm[0] * r_len
                    # set rotation matrix about '-z'
                    R = self.rot_axis(np.array([0, 0, -1]))
                else:
                    r_end[j] = np.dot(R(j * r_rot),
                                      (r_end[0] - Vol_centroid).T).T
                    r_end[j] = r_end[j] + Vol_centroid

                # perform ray trace
                r_pts, r_ind = mesh.ray_trace(Vol_centroid, r_end[j])

                # show rays
                # rays[j] = self.plotter.add_mesh(pv.Line(Vol_centroid, r_end[j]), color='w', line_width=l_wid)
                # ints[j] = self.plotter.add_mesh(pv.PolyData(r_pts[0]), color='w', point_size=pt_size)

                # create an array of ray intersections
                r_int = np.append(r_int, r_pts[0])

            r_int = np.reshape(r_int, (4, 3))
            ori_nearest, ori_p, ori_V = self.nearest_pt(r_int, Vol_centroid)
            r_int = []
            ori_r_int = np.append(ori_r_int, ori_V[ori_p, :])

        ori_r_int = np.reshape(ori_r_int, (r_num, 3))
        return ori_r_int, rays, ints

    def nearest_pt(self, vert, starting_pt):
        """ find nearest vertex: for segmented convex manifold, a cube with volume centroid as 
        center and nearest vertex as cube vertex, it falls inside the volume """
        # find nearest point from the list of points
        c = len(vert)
        dist = np.zeros(c)
        for i in range(0, c):
            dist[i] = np.sqrt((vert[i, 0] - starting_pt[0])**2 +
                              (vert[i, 1] - starting_pt[1])**2 +
                              (vert[i, 2] - starting_pt[2])**2)

        # find index of the nearest point
        nearest = min(dist)
        p = np.where(dist == nearest)
        p = p[0].item()

        return nearest, p, vert

    def furthest_pt(self, vert, starting_pt):
        global p, furthest, dist
        """ find furthest vertex among the list of nearest vertices """
        # find furthest point from the list of points
        c = len(vert)
        dist = np.zeros(c)
        for i in range(0, c):
            dist[i] = np.sqrt((vert[i, 0] - starting_pt[0])**2 +
                              (vert[i, 1] - starting_pt[1])**2 +
                              (vert[i, 2] - starting_pt[2])**2)

        # find index of the furthest point
        furthest = max(dist)
        p = np.where(dist == furthest)
        p = p[0][0]

        return furthest, p, vert

    def create_cube(self, vertex, starting_pt, axis):
        ''' create cube from the nearest pt & centroid '''
        if (axis[0] == 0) and (axis[1] == 0) and (axis[2] == 0):
            axis[2] = 1
            vert_trans = np.array([0, 0, 0])
        elif (starting_pt[0] == 0) and (starting_pt[1]
                                        == 0) and (starting_pt[2] == 0):
            vert_trans = np.array([0, 0, 0])
        else:
            vert_trans = starting_pt
            for i in range(0, 3):
                if round(axis[i]) == 1 or round(axis[i]) == -1:
                    vert_trans[i] == 0
        # find the other 7 vertices
        # 3 vertices can be found by rotating the first point 90 degrees 3 times around Z axis of centroid
        # 4 vertices can be found by translating the first four vertices twice the half edge
        # found from the distance times sin(pi/4)
        R = self.rot_axis(axis / np.linalg.norm(axis))

        # construct the array of the first 4 vertices
        V_1 = np.array(vertex - vert_trans)
        V_2 = np.dot(R(np.pi / 2), V_1.T).T
        V_3 = np.dot(R(np.pi), V_1.T).T
        V_4 = np.dot(R(3 * np.pi / 2), V_1.T).T
        # cube_V_start = np.array([V_1, V_2, V_3, V_4])
        cube_V_start = np.array([V_1, V_2, V_3, V_4]) + np.ones(
            (4, 1)) * [vert_trans]
        cube_V_start_center = np.array(pv.PolyData(cube_V_start).center)

        # show nearest vertex of cube
        V_1 = np.array(vertex)
        self.plotter.add_mesh(pv.PolyData(V_1),
                              color="y",
                              point_size=30.0,
                              render_points_as_spheres=True)

        # find the translation distance
        trans_dis = starting_pt - cube_V_start_center
        trans_dir = trans_dis / np.linalg.norm(trans_dis)
        dia_dis = np.sqrt((V_1[0] - cube_V_start_center[0])**2 +
                          (V_1[1] - cube_V_start_center[1])**2 +
                          (V_1[2] - cube_V_start_center[2])**2)
        half_edge = np.ones((4, 1)) * [trans_dir] * dia_dis * np.sin(np.pi / 4)
        cube_trans = np.asarray(2 * half_edge, dtype=np.float64)

        # construct the cube
        cube_V_end = np.add(cube_V_start, cube_trans)
        cube_V = np.vstack((cube_V_start, cube_V_end))
        cube_F = np.hstack([[4, 0, 1, 2, 3], [4, 0, 3, 7, 4], [4, 0, 1, 5, 4],
                            [4, 1, 2, 6, 5], [4, 2, 3, 7, 6], [4, 4, 5, 6, 7]])

        # cube volume
        cube_vol = (2 * np.linalg.norm(half_edge[0, :]))**3

        return cube_V, cube_F, cube_vol

    def rot_axis(self, axis):
        ''' create a rotational matrix about an arbitrary axis '''
        t = sp.Symbol('t')

        R_t = Matrix(
            [[
                sp.cos(t) + axis[0]**2 * (1 - sp.cos(t)),
                axis[0] * axis[1] * (1 - sp.cos(t)) - axis[2] * sp.sin(t),
                axis[0] * axis[2] * (1 - sp.cos(t)) + axis[1] * sp.sin(t)
            ],
             [
                 axis[1] * axis[0] * (1 - sp.cos(t)) + axis[2] * sp.sin(t),
                 sp.cos(t) + axis[1]**2 * (1 - sp.cos(t)),
                 axis[1] * axis[2] * (1 - sp.cos(t)) - axis[0] * sp.sin(t)
             ],
             [
                 axis[2] * axis[0] * (1 - sp.cos(t)) - axis[1] * sp.sin(t),
                 axis[2] * axis[1] * (1 - sp.cos(t)) + axis[0] * sp.sin(t),
                 sp.cos(t) + axis[2]**2 * (1 - sp.cos(t))
             ]])
        R = lambdify(t, R_t)
        return R

    def next_cubes_ray(self, value):
        ''' create cubes within the mesh from the face centers of the first cube'''
        global next_cube_vol, max_normal
        global next_rays, next_ints, next_cubes

        # find max cube
        self.max_cube_ray(value)

        # # bypass error
        # try:
        #     next_rays, next_ints, next_cubes, r_num
        # except NameError:
        #     next_rays = None
        #     next_ints = None
        #     next_cubes = None
        #     r_num = 0

        # # remove old rays
        # if (r_num != 0) and (r_num == int(value[0])):
        #     return
        # elif (r_num != 0) and (next_cubes != None):
        #     for i in range(0,6):
        #         self.plotter.remove_actor(next_cubes[i])
        #         for j in range(0, r_num):
        #             self.plotter.remove_actor(next_rays[i*r_num+j])
        #             self.plotter.remove_actor(next_ints[i*r_num+j])

        # track starting time
        next_cube_start = time.time()

        # initiate variables
        next_cube_vol_sum = 0
        next_cubes = [0] * 6
        next_rays = [0] * 6 * r_num
        next_ints = [0] * 6 * r_num

        # fix max_normal
        normal = face_center[0] - Vol_centroid
        if (np.sign(normal[2]) != np.sign(max_normal[0, 2])):
            max_normal = np.negative(max_normal)

        # loop through all 6 faces of max cube
        for i in range(0, 6):
            # create rotaional matrix about max cube normals
            R = self.rot_axis(max_normal[i])

            # initialize variables
            ray_size = np.zeros((4, 3))
            r_dir = ray_size
            r_dir_norm = ray_size
            r_end = ray_size

            # initialize ray trace parameters
            l_wid = 3
            pt_size = 10
            r_len = np.sqrt((x_range / 2)**2 + (y_range / 2)**2 +
                            (z_range / 2)**2)
            r_int = []
            ori_r_int = []

            for j in range(0, r_num):
                for k in range(0, 4):
                    if k == 0:
                        if (i == 0) or (i == 5):
                            r_dir[0] = np.array([
                                np.sqrt(2) / 2 * np.cos(np.pi / 4 + r_dec * j),
                                np.sqrt(2) / 2 * np.sin(np.pi / 4 + r_dec * j),
                                max_normal[i][2]
                            ])
                        else:
                            x, y = sp.symbols('x,y')
                            f = sp.Eq(
                                max_normal[i][0] * x + max_normal[i][1] * y, 0)
                            g = sp.Eq(x**2 + y**2, 0.5**2)
                            inc = sp.solve([f, g], (x, y))
                            r_dir[0] = np.array(max_normal[i] +
                                                [inc[0][0], inc[0][1], 0.5])
                        r_dir_norm[0] = r_dir[0] / np.linalg.norm(r_dir[0])
                        r_end[0] = face_center[i] + r_dir_norm[0] * r_len
                        r_end[0] = np.dot(R(j * r_dec),
                                          (r_end[0] - Vol_centroid).T).T
                    else:
                        r_end[k] = np.dot(R(k * r_rot),
                                          (r_end[0] - Vol_centroid).T).T
                        r_end[k] = r_end[k] + Vol_centroid

                    # perform ray trace
                    r_pts, r_ind = mesh.ray_trace(face_center[i], r_end[k])

                    # show rays
                    # next_rays[i*r_num+k] = self.plotter.add_mesh(pv.Line(face_center[i], r_end[k]), color='w', line_width=l_wid)
                    # next_ints[i*r_num+k] = self.plotter.add_mesh(pv.PolyData(r_pts[0]), color='w', point_size=pt_size)

                    # create an array of ray intersections
                    r_int = np.append(r_int, r_pts[0])

                # find nearest vertice among the ray intersections
                r_int = np.reshape(r_int, (4, 3))
                ori_nearest, ori_p, ori_V = self.nearest_pt(
                    r_int, face_center[i])
                r_int = []
                ori_r_int = np.append(ori_r_int, ori_V[ori_p, :])

            ori_r_int = np.reshape(ori_r_int, (r_num, 3))
            face = self.furthest_pt(ori_r_int, face_center[i])

            # create cube from nearest vertice
            next_cube_V, next_cube_F, next_cube_vol = self.create_cube(
                face[2][face[1], :], face_center[i], max_normal[i])
            next_cubes[i] = self.plotter.add_mesh(pv.PolyData(
                next_cube_V, next_cube_F),
                                                  show_edges=True,
                                                  line_width=3,
                                                  color="g",
                                                  opacity=0.6)

            # next cube volume
            next_cube_vol_sum = next_cube_vol_sum + next_cube_vol

        # show packing efficiency
        next_cube_vol_sum = float(format(next_cube_vol_sum, ".5f"))
        pack_vol = float(format((max_cube_vol + next_cube_vol_sum), ".5f"))
        pack_percent = "{:.1%}".format(pack_vol / mesh_vol)
        print("Next Cubes Volume:", next_cube_vol_sum)
        print("Packed Volume:", pack_vol)
        print("Packing Efficiency:", pack_percent)

        # track starting time
        next_cube_end = time.time()
        next_cube_run = next_cube_end - next_cube_start
        print("Total elapsed run time: %g seconds" %
              (max_cube_run + next_cube_run))

        return

    def closeEvent(self, event):
        reply = QMessageBox.question(self, "Window Close",
                                     "Are you sure you want to quit program?",
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()
Example #20
0
    def __init__(self, parent=None, show=True, winsize=(640,480), campos=[(0.0,0.0,-5.0),(0.0,0.0,-2.0),(0.0,-1.0,0.0)],kinectid=0):
        '''
        Basic class to show 3D projection
        :param parent: inherit from Qt.Mainvindow
        :param show:
        '''
        Qt.QMainWindow.__init__(self, parent)

        self.pc_points = None
        self.pc_colors = None

        # initial size
        self.setMinimumSize(winsize[0],winsize[1])

        # create the frame
        self.frame = Qt.QFrame()
        vlayout = Qt.QVBoxLayout()
        vlayout.setContentsMargins(0,0,0,0)

        #update rate
        self.UPDATE_RATE = 80

        # add a menu
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        exitButton = Qt.QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        self.add_save_action = Qt.QAction('Save out.ply & out.jpg', self)
        self.add_save_action.triggered.connect(self.save_ply)
        fileMenu.addAction(self.add_save_action)


        # add the pyvista interactor object
        self.plotter = QtInteractor(self.frame,auto_update=0.0,point_smoothing=False)
        self.plotter.show_axes_all()

        # give depth perspective for the points
        #self.plotter.disable_eye_dome_lighting()
        self.plotter.enable_depth_peeling()
        self.plotter.disable_parallel_projection()

        # set initial camera pozition
        self.cpos = np.array(campos)
        self.plotter.camera_position = self.cpos

        vlayout.addWidget(self.plotter.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        # start serving the kinect frames
        self.kin = KinectHandler(kinectid)

        # set up timer for cyclic update
        timer = QtCore.QTimer(self)
        timer.timeout.connect(self.PlotUpdate)
        timer.start(self.UPDATE_RATE)

        if show:
            self.show()
Example #21
0
class MainWindow(Qt.QMainWindow):

    def __init__(self, parent=None, show=True, winsize=(640,480), campos=[(0.0,0.0,-5.0),(0.0,0.0,-2.0),(0.0,-1.0,0.0)],kinectid=0):
        '''
        Basic class to show 3D projection
        :param parent: inherit from Qt.Mainvindow
        :param show:
        '''
        Qt.QMainWindow.__init__(self, parent)

        self.pc_points = None
        self.pc_colors = None

        # initial size
        self.setMinimumSize(winsize[0],winsize[1])

        # create the frame
        self.frame = Qt.QFrame()
        vlayout = Qt.QVBoxLayout()
        vlayout.setContentsMargins(0,0,0,0)

        #update rate
        self.UPDATE_RATE = 80

        # add a menu
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        exitButton = Qt.QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        self.add_save_action = Qt.QAction('Save out.ply & out.jpg', self)
        self.add_save_action.triggered.connect(self.save_ply)
        fileMenu.addAction(self.add_save_action)


        # add the pyvista interactor object
        self.plotter = QtInteractor(self.frame,auto_update=0.0,point_smoothing=False)
        self.plotter.show_axes_all()

        # give depth perspective for the points
        #self.plotter.disable_eye_dome_lighting()
        self.plotter.enable_depth_peeling()
        self.plotter.disable_parallel_projection()

        # set initial camera pozition
        self.cpos = np.array(campos)
        self.plotter.camera_position = self.cpos

        vlayout.addWidget(self.plotter.interactor)

        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)

        # start serving the kinect frames
        self.kin = KinectHandler(kinectid)

        # set up timer for cyclic update
        timer = QtCore.QTimer(self)
        timer.timeout.connect(self.PlotUpdate)
        timer.start(self.UPDATE_RATE)

        if show:
            self.show()

    def PlotUpdate(self):
        '''
        Plot the point cloud
        :return:
        '''

        self.pc_points, self.pc_colors = self.kin.get_registred_depth_rgb()

        if self.pc_colors is not None and self.pc_colors is not None:
            # clean the view
            self.plotter.clear()
            point_cloud = pv.PolyData(self.pc_points)

            if args.rgb == 'c':
                point_cloud["colors"] = self.pc_colors.reshape(-1,3)
            else:
                dc = cv2.normalize(self.pc_points[:, -1], None, 0.0, 1.0, cv2.NORM_MINMAX)
                point_cloud["colors"] = dc

            # add data to be visualized
            self.plotter.add_mesh(point_cloud, point_size=2.0, lighting=False, render_points_as_spheres=False, color=True, interpolate_before_map=False, show_scalar_bar=False)

    def save_ply(self):
        '''
        Save curent layout to an out.ply file
        :return:
        '''

        ply_header = '''ply
        format ascii 1.0
        element vertex %(vert_num)d
        property float x
        property float y
        property float z
        property uchar red
        property uchar green
        property uchar blue
        end_header
        '''

        verts = self.pc_points.reshape(-1, 3)
        colors = np.ones(verts.shape)
        verts = np.hstack([verts, self.pc_colors.reshape(-1,3)])
        
        with open('out.ply', 'wb') as f:
            f.write((ply_header % dict(vert_num=len(verts))).encode('utf-8'))
            np.savetxt(f, verts, fmt='%f %f %f %d %d %d ')
        
        #save image
        img = cv2.cvtColor(self.kin.rgb,cv2.COLOR_BGR2RGB)
        cv2.imwrite('out.jpg',img)
Example #22
0
    def createUI(self):

        self.x1 = self.createSlider(0, 0, self.dims[0])
        self.x2 = self.createSlider(self.dims[0], 0, self.dims[0])
        self.y1 = self.createSlider(0, 0, self.dims[1])
        self.y2 = self.createSlider(self.dims[1], 0, self.dims[1])
        self.z1 = self.createSlider(0, 0, self.dims[2])
        self.z2 = self.createSlider(self.dims[2], 0, self.dims[2])

        self.field = QComboBox()
        for name in self.db.getFields():
            self.field.addItem(name)
        self.field.setCurrentText(self.db.getFields()[0])

        self.type = QComboBox()
        for name in ('vr', 'threshold', 'isovalue', 'slice'):
            self.type.addItem(name)
        self.type.setCurrentText('vr')

        self.opacity = QComboBox()
        for name in ('linear', 'geom', 'geom_r', 'sigmoid', 'sigmoid_3',
                     'sigmoid_4', 'sigmoid_5', 'sigmoid_6', 'sigmoid_7',
                     'sigmoid_8', 'sigmoid_9', 'sigmoid_10'):
            self.opacity.addItem(name)
        self.opacity.setCurrentText('sigmoid')

        self.cmap = QComboBox()
        for name in ('gist_ncar', 'bone', 'cool', 'viridis', 'coolwarm',
                     'magma'):
            self.cmap.addItem(name)
        self.cmap.setCurrentText('gist_ncar')

        self.blending = QComboBox()
        for name in ('additive', 'maximum', 'minimum', 'composite', 'average'):
            self.blending.addItem(name)
        self.blending.setCurrentText('additive')

        self.max_mb = QComboBox()
        for name in ('8', '16', '32', '64', '128', '256', '512', '1024',
                     '2048'):
            self.max_mb.addItem(name)
        self.max_mb.setCurrentText('32')

        self.run = QPushButton("Run")
        self.run.clicked.connect(self.showVolume)

        main_layout = QVBoxLayout()

        # plotter
        if True:
            frame = QFrame()
            self.plotter = QtInteractor(frame)
            main_layout.addWidget(self.plotter)

        if True:
            layout = self.createVerticalLayout([
                self.createGridLayout(
                    ([QLabel("x1"), self.x1,
                      QLabel("x2"),
                      self.x2], [QLabel("y1"), self.y1,
                                 QLabel("y2"), self.y2],
                     [QLabel("z1"), self.z1,
                      QLabel("z2"), self.z2])),
                self.createHorizontalLayout([QLabel("field"), self.field]),
                self.createHorizontalLayout([QLabel("type"), self.type]),
                self.createHorizontalLayout([QLabel("opacity"), self.opacity]),
                self.createHorizontalLayout([QLabel("cmap"), self.cmap]),
                self.createHorizontalLayout(
                    [QLabel("blending"), self.blending]),
                self.createHorizontalLayout([QLabel("MBytes"), self.max_mb]),
                self.run
            ])
            main_layout.addLayout(layout)

        central_widget = QFrame()
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)
        self.showMaximized()
Example #23
0
class Explorer3d(QMainWindow):

    # construtor
    def __init__(self, url, parent=None):

        super().__init__(parent)

        self.db = LoadDataset(url)
        self.dims = self.db.getLogicBox()[1]
        print("dims", self.dims)

        self.createUI()
        self.showVolume()
        self.show()

    # createUI
    def createUI(self):

        self.x1 = self.createSlider(0, 0, self.dims[0])
        self.x2 = self.createSlider(self.dims[0], 0, self.dims[0])
        self.y1 = self.createSlider(0, 0, self.dims[1])
        self.y2 = self.createSlider(self.dims[1], 0, self.dims[1])
        self.z1 = self.createSlider(0, 0, self.dims[2])
        self.z2 = self.createSlider(self.dims[2], 0, self.dims[2])

        self.field = QComboBox()
        for name in self.db.getFields():
            self.field.addItem(name)
        self.field.setCurrentText(self.db.getFields()[0])

        self.type = QComboBox()
        for name in ('vr', 'threshold', 'isovalue', 'slice'):
            self.type.addItem(name)
        self.type.setCurrentText('vr')

        self.opacity = QComboBox()
        for name in ('linear', 'geom', 'geom_r', 'sigmoid', 'sigmoid_3',
                     'sigmoid_4', 'sigmoid_5', 'sigmoid_6', 'sigmoid_7',
                     'sigmoid_8', 'sigmoid_9', 'sigmoid_10'):
            self.opacity.addItem(name)
        self.opacity.setCurrentText('sigmoid')

        self.cmap = QComboBox()
        for name in ('gist_ncar', 'bone', 'cool', 'viridis', 'coolwarm',
                     'magma'):
            self.cmap.addItem(name)
        self.cmap.setCurrentText('gist_ncar')

        self.blending = QComboBox()
        for name in ('additive', 'maximum', 'minimum', 'composite', 'average'):
            self.blending.addItem(name)
        self.blending.setCurrentText('additive')

        self.max_mb = QComboBox()
        for name in ('8', '16', '32', '64', '128', '256', '512', '1024',
                     '2048'):
            self.max_mb.addItem(name)
        self.max_mb.setCurrentText('32')

        self.run = QPushButton("Run")
        self.run.clicked.connect(self.showVolume)

        main_layout = QVBoxLayout()

        # plotter
        if True:
            frame = QFrame()
            self.plotter = QtInteractor(frame)
            main_layout.addWidget(self.plotter)

        if True:
            layout = self.createVerticalLayout([
                self.createGridLayout(
                    ([QLabel("x1"), self.x1,
                      QLabel("x2"),
                      self.x2], [QLabel("y1"), self.y1,
                                 QLabel("y2"), self.y2],
                     [QLabel("z1"), self.z1,
                      QLabel("z2"), self.z2])),
                self.createHorizontalLayout([QLabel("field"), self.field]),
                self.createHorizontalLayout([QLabel("type"), self.type]),
                self.createHorizontalLayout([QLabel("opacity"), self.opacity]),
                self.createHorizontalLayout([QLabel("cmap"), self.cmap]),
                self.createHorizontalLayout(
                    [QLabel("blending"), self.blending]),
                self.createHorizontalLayout([QLabel("MBytes"), self.max_mb]),
                self.run
            ])
            main_layout.addLayout(layout)

        central_widget = QFrame()
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)
        self.showMaximized()

    # getVolumeBounds
    def getVolumeBounds(self):
        x1 = self.x1.value()
        x2 = self.x2.value()
        x1, x2 = min(x1, x2), max(x1, x2)
        y1 = self.x1.value()
        y2 = self.x2.value()
        y1, y2 = min(y1, y2), max(y1, y2)
        z1 = self.x1.value()
        z2 = self.x2.value()
        z1, z2 = min(z1, z2), max(z1, z2)
        return x1, x2, y1, y2, z1, z2

    # extractVolume
    def extractVolume(self):
        field = self.db.getField(self.field.currentText())

        x1, x2, y1, y2, z1, z2 = self.getVolumeBounds()

        # guess quality
        dtype = field.dtype
        sample_size = dtype.getByteSize()
        mem_size = int(
            (x2 - x1) * (y2 - y1) * (z2 - z1) * sample_size / (1024 * 1024))

        max_mb = int(self.max_mb.currentText())

        quality = 0
        while mem_size > max_mb:
            quality -= 1
            mem_size = int(mem_size / 2)

        data = self.db.read(x=[x1, x2],
                            y=[y1, y2],
                            z=[z1, z2],
                            field=self.field.currentText(),
                            quality=quality)

        print("Extracted volume", "shape", data.shape,
              "dtype", data.dtype, "mb",
              int(sys.getsizeof(data) / (1024 * 1024)), "quality", quality)
        return data

    # showVolume
    def showVolume(self):
        data = self.extractVolume()
        self.plotter.clear()
        # self.plotter.show_bounds(grid=True, location='back')

        grid = pv.UniformGrid()
        grid.dimensions = numpy.array(data.shape) + 1
        x1, x2, y1, y2, z1, z2 = self.getVolumeBounds()
        grid.origin = (0, 0, 0)  # The bottom left corner of the data set
        w, h, d = data.shape
        grid.spacing = ((x2 - x1) / w, (y2 - y1) / h, (z2 - z1) / d
                        )  # These are the cell sizes along each axis
        grid.cell_arrays["values"] = data.flatten(order="F")

        # /////////////////////////////////////////////////////////
        # example https://docs.pyvista.org/examples/00-load/create-uniform-grid.html
        if self.type.currentText() == 'vr':
            self.plotter.add_volume(grid,
                                    opacity=self.opacity.currentText(),
                                    cmap=self.cmap.currentText(),
                                    blending=self.blending.currentText())

        elif self.type.currentText() == 'threshold':
            self.plotter.add_mesh_threshold(grid)

        elif self.type.currentText() == 'isovalue':
            self.plotter.add_mesh_isovalue(
                pv.wrap(data))  # TODO, aspect ratio not working

        elif self.type.currentText() == 'slice':
            self.plotter.add_mesh_slice(grid)

        else:
            raise Exception("internal error")

        self.plotter.reset_camera()

    # createSlider
    def createSlider(self, value, m, M):
        ret = QSlider(Qt.Horizontal, self)
        ret.setRange(m, M)
        ret.setValue(value)
        ret.setFocusPolicy(Qt.NoFocus)
        ret.setPageStep(5)
        # ret.valueChanged.connect(self.showVolume)
        return ret

    # createHorizontalLayout
    def createHorizontalLayout(self, widgets):
        ret = QHBoxLayout()
        for widget in widgets:
            try:
                ret.addWidget(widget)
            except:
                ret.addLayout(widget)
        return ret

    # createVerticalLayout
    def createVerticalLayout(self, widgets):
        ret = QVBoxLayout()
        for widget in widgets:
            try:
                ret.addWidget(widget)
            except:
                ret.addLayout(widget)
        return ret

    # createGridLayout
    def createGridLayout(self, rows):
        ret = QGridLayout()
        nrow = 0
        for row in rows:
            ncol = 0
            for widget in row:
                if widget:
                    ret.addWidget(widget, nrow, ncol)
                ncol += 1
            nrow += 1
        return ret
Example #24
0
class Viewer(GeometricElements, QtWidgets.QMainWindow):
    def __init__(self, datalog=None, parent=None, show=True):
        self.time_speed = 1
        self.earth_av = 7.2921150 * 360.0 * 1e-5 / (2 * np.pi)
        self.init_sideral = 0
        self.current_sideral = 0
        self.vector_point = np.zeros(3)
        self.datalog_flag = False
        self.show_ref_vector_point = False
        self.run_flag = False
        self.pause_flag = False
        self.stop_flag = False
        self.gs_flag = False
        self.thread = None
        self.countTime = 0
        self.last_index = 0
        self.max_index = 0
        self.screen = None
        self.simulation_index = -1
        self.q_t_i2b = None
        self.spacecraft_pos_i = None
        self.datalog = datalog
        self.data_handler = None
        QMainWindow.__init__(self, parent)
        self.window = Ui_MainWindow()
        self.window.setupUi(self)
        self.quaternion_t0 = None

        # Set-up signals and slots
        # self.window.actionGeneratePlot.triggered.connect(self.plot_slot)
        # self.window.control_spinbox.valueChanged.connect(self.window.control_slider.setValue)
        # self.window.control_slider.valueChanged.connect(self.window.control_spinbox.setValue)

        #########################################

        vlayout = QVBoxLayout()
        self.window.view_frame.setLayout(vlayout)

        self.vtk_widget = QtInteractor(self.window.view_frame, shape=(1, 2))
        self.vtk_widget.set_background([0.25, 0.25, 0.25])
        vlayout.addWidget(self.vtk_widget)

        self.preview_plot_widget = QtWidgets.QVBoxLayout(
            self.window.PlotWidget)
        self.canvas_ = FigureCanvas(Figure(figsize=(5, 3)))
        self.preview_plot_widget.addWidget(self.canvas_)
        self.plot_canvas = self.canvas_.figure.subplots()
        self.plot_canvas.grid()
        self.plot_canvas.set_xlabel('Time [s]')

        GeometricElements.__init__(self, self.vtk_widget)
        self.add_bar()
        self.add_i_frame_attitude()
        self.add_gs_item()

        # --------------------------------------------------------------------------------------------------------------
        # simple menu to functions
        main_menu = self.menuBar()
        # --------------------------------------------------------------------------------------------------------------
        # File option
        self.window.actionLoadCsv.triggered.connect(self.load_csv_file)
        if datalog is not None:
            self.load_csv_file()
        # --------------------------------------------------------------------------------------------------------------

        self.window.actionRun.triggered.connect(self.run_simulation)
        self.window.actionPause.triggered.connect(self.pause_simulation)
        self.window.actionStop.triggered.connect(self.stop_simulation)
        self.window.actionAddGS.triggered.connect(self.add_gs_item)

        # sim_menu.addAction(run_action)
        # sim_menu.addAction(pause_action)
        # sim_menu.addAction(stop_action)
        # --------------------------------------------------------------------------------------------------------------
        self.window.actionGeneratePlot.triggered.connect(self.add_graph2d)
        self.window.PlotSelectedData.clicked.connect(self.plot_selected_data)
        self.window.listWidget.clicked.connect(self.preview_plot_data)
        # --------------------------------------------------------------------------------------------------------------

    def add_item_to_list(self):
        _translate = QtCore.QCoreApplication.translate
        for elem in self.data_handler.auxiliary_datalog_keys:
            item = QtWidgets.QListWidgetItem()
            item.setCheckState(QtCore.Qt.Unchecked)
            item.setText(_translate("MainWindow", elem))
            self.window.listWidget.addItem(item)

        flat_basic_list = [
            item for sublist in self.data_handler.basic_datalog_keys[1:]
            for item in sublist
        ]
        for elem in flat_basic_list:
            item = QtWidgets.QListWidgetItem()
            item.setCheckState(QtCore.Qt.Unchecked)
            item.setText(_translate("MainWindow", elem))
            self.window.listWidget.addItem(item)
        return

    def add_graph2d(self):
        self.screen = MainGraph(self.data_handler)
        self.screen.win.show()

    def load_csv_file(self):
        def read_data(file_path):
            df = pd.read_csv(file_path, delimiter=',')
            sim_data = df
            return sim_data

        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        filename, _ = QFileDialog.getOpenFileName(self,
                                                  "Select CSV data file",
                                                  "",
                                                  "CSV Files (*.csv)",
                                                  options=options)
        if filename:
            if self.datalog is not None:
                self.window.listWidget.clear()
            self.datalog = read_data(filename)
            self.datalog_flag = True
            print('Data log loaded')
        else:
            print('Could not load data log')

        if self.datalog_flag:
            self.data_handler = DataHandler(self.datalog)
            self.data_handler.create_variable()

            # Add actors to orbit view
            self.spacecraft_pos_i = np.array([
                self.data_handler.basic_datalog[
                    self.data_handler.basic_datalog_keys[2][0]],
                self.data_handler.basic_datalog[
                    self.data_handler.basic_datalog_keys[2][1]],
                self.data_handler.basic_datalog[
                    self.data_handler.basic_datalog_keys[2][2]]
            ]).transpose()
            self.max_index = len(self.spacecraft_pos_i)
            self.add_orbit(self.spacecraft_pos_i)
            # self.add_aries_arrow()

            # Add actors to attitude view
            self.q_t_i2b = np.array([
                self.data_handler.basic_datalog[
                    self.data_handler.basic_datalog_keys[4][0]],
                self.data_handler.basic_datalog[
                    self.data_handler.basic_datalog_keys[4][1]],
                self.data_handler.basic_datalog[
                    self.data_handler.basic_datalog_keys[4][2]],
                self.data_handler.basic_datalog[
                    self.data_handler.basic_datalog_keys[4][3]]
            ]).transpose()

            self.add_spacecraft_2_orbit(self.spacecraft_pos_i[0, :],
                                        self.q_t_i2b)
            self.add_spacecraft_2_attitude(self.q_t_i2b)

            try:
                date_time_data = self.data_handler.auxiliary_datalog[
                    'Date time']
                init_time = date_time_data[0]
                datetime_array = datetime.strptime(init_time,
                                                   '%Y-%m-%d %H:%M:%S')

            except KeyError:
                dp = InitDateForm()
                # self.vtk_widget.setWindowModality(QtCore.Qt.ApplicationModal)
                dp.exec()
                print(dp.current_date)
                init_time = dp.current_date.toUTC().toString(Qt.ISODate)
                datetime_array = datetime.strptime(init_time,
                                                   '%Y-%m-%dT%H:%M:%SZ')

            init_jd = self.jday(datetime_array.year, datetime_array.month,
                                datetime_array.day, datetime_array.hour,
                                datetime_array.minute, datetime_array.second)
            self.init_sideral = rad2deg * self.gstime(init_jd)
            self.sphere.rotate_z(self.init_sideral)
            if self.gs_flag:
                self.update_gs_location(rad2deg * self.gstime(init_jd))

            if 'Vector_tar_i(X) [-]' in self.data_handler.auxiliary_datalog.keys(
            ):
                self.vector_point = np.array([
                    self.data_handler.auxiliary_datalog['Vector_tar_i(X) [-]']
                    [0],
                    self.data_handler.auxiliary_datalog['Vector_tar_i(Y) [-]']
                    [0],
                    self.data_handler.auxiliary_datalog['Vector_tar_i(Z) [-]']
                    [0]
                ])
            if np.linalg.norm(self.vector_point) != 0:
                self.show_ref_vector_point = True
                self.add_vector_line_in_orbit(self.spacecraft_pos_i[0, :],
                                              self.vector_point)
            self.add_b_frame_attitude(
                show_ref_vector_point=self.show_ref_vector_point,
                vector_point=self.vector_point)
            self.add_item_to_list()

    def run_simulation(self):
        self.run_flag = True
        self.stop_flag = False
        self.pause_flag = False
        self.run_orbit_3d()
        print('Running...')

    def pause_simulation(self):
        self.pause_flag = True
        self.run_flag = False
        print('Paused...')

    def stop_simulation(self):
        self.stop_flag = True
        if self.thread:
            self.thread.join()
            self.thread = None
        self.update_meshes(0)
        self.reset_time()

    def reset_time(self):
        self.countTime = 0
        self.simulation_index = 1

    def update_time(self):
        self.countTime = self.data_handler.stepTime * self.simulation_index

    def update_meshes(self, index):
        if index == 0:
            print('Resetting')
            self.sphere.rotate_z(-self.current_sideral)
            tr_vector = self.spacecraft_pos_i[0, :] - np.array([0, 0, 34 / 2])

            self.spacecraft_model_2_orbit.translate(
                -self.spacecraft_model_2_orbit.center_of_mass())
            self.update_attitude(index, self.spacecraft_model_2_attitude,
                                 self.spacecraft_model_2_orbit)
            self.spacecraft_model_2_orbit.translate(
                self.spacecraft_model_2_orbit.center_of_mass())
            if self.show_ref_vector_point:
                self.update_vector_line(self.spacecraft_pos_i[index, :],
                                        self.vector_point)

            self.spacecraft_model_2_orbit.translate(tr_vector)
            self.body_x_i.translate(tr_vector)
            self.body_y_i.translate(tr_vector)
            self.body_z_i.translate(tr_vector)
        else:
            # Update Earth
            sideral = self.earth_av * self.data_handler.stepTime * int(
                self.time_speed)
            self.current_sideral += sideral
            self.sphere.rotate_z(sideral)
            if self.gs_flag:
                self.update_gs_location(sideral)

            # Update Orbit
            tr_vector = self.spacecraft_pos_i[
                index, :] - self.spacecraft_pos_i[self.last_index, :]
            self.spacecraft_model_2_orbit.translate(
                -self.spacecraft_pos_i[self.last_index, :])
            self.update_attitude(index, self.spacecraft_model_2_attitude,
                                 self.spacecraft_model_2_orbit)
            self.spacecraft_model_2_orbit.translate(
                self.spacecraft_pos_i[self.last_index, :])
            if self.show_ref_vector_point:
                self.update_vector_line(self.spacecraft_pos_i[index, :],
                                        self.vector_point)
            self.spacecraft_model_2_orbit.translate(tr_vector)
            self.body_x_i.translate(tr_vector)
            self.body_y_i.translate(tr_vector)
            self.body_z_i.translate(tr_vector)
        # Update widget
        self.vtk_widget.update()
        self.last_index = index

    def rotate_th(self):
        self.vtk_widget.subplot(0, 0)
        self.reset_time()
        while not self.stop_flag and self.countTime < self.data_handler.endTime:
            self.update_meshes(self.simulation_index)
            # time.sleep(self.data_handler.stepTime / self.time_speed)
            time.sleep(self.data_handler.stepTime)
            # Update time
            self.simulation_index += 1 * int(self.time_speed)
            self.update_time()

            while self.pause_flag:
                if self.stop_flag:
                    return
                elif self.run_flag:
                    self.pause_flag = False
                else:
                    time.sleep(1)

    def run_orbit_3d(self):
        if self.thread is None:
            self.thread = Thread(target=self.rotate_th, daemon=True)
            self.thread.start()

    def sim_speed(self, value):
        self.time_speed = value
        return

    def update_vector_line(self, center_point, vector_point):
        self.vtk_widget.subplot(0, 0)
        new_points = np.array(
            [center_point, center_point + 2e7 * vector_point])
        self.vector_line_from_sc.points = new_points
        cells = np.full((len(new_points) - 1, 3), 2, dtype=np.int)
        cells[:, 1] = np.arange(0, len(new_points) - 1, dtype=np.int)
        cells[:, 2] = np.arange(1, len(new_points), dtype=np.int)
        self.vector_line_from_sc.lines = cells

    def update_gs_location(self, sideral):
        self.tar_pos_eci.rotate_z(sideral)
        return

    def update_attitude(self, n, sc_model1, sc_model2=None):
        quaternion_tn = Quaternion(self.q_t_i2b[n, :]).unit
        inv_quaternion = self.quaternion_t0.inverse
        d_quaternion = quaternion_tn * inv_quaternion

        k_matrix = d_quaternion.transformation_matrix
        self.body_x.transform(k_matrix)
        self.body_y.transform(k_matrix)
        self.body_z.transform(k_matrix)
        sc_model1.transform(k_matrix)
        if sc_model2 is not None:
            sc_model2.transform(k_matrix)
        self.quaternion_t0 = quaternion_tn

        if self.show_ref_vector_point:
            curr_vector_point = np.array([
                self.data_handler.auxiliary_datalog['Vector_tar_i(X) [-]'][n],
                self.data_handler.auxiliary_datalog['Vector_tar_i(Y) [-]'][n],
                self.data_handler.auxiliary_datalog['Vector_tar_i(Z) [-]'][n]
            ])

            vec = np.cross(self.vector_point, curr_vector_point)
            arg = np.dot(self.vector_point, curr_vector_point)
            if arg > 1.0:
                arg = 1.0
            elif arg < -1.0:
                arg = -1.0

            ang = np.arccos(arg)
            if np.linalg.norm(vec) == 0:
                vec = np.array([0, 0, 1])
            self.body_ref_point.transform(
                Quaternion(axis=vec, angle=ang).transformation_matrix)
            self.vector_point = curr_vector_point

    def preview_plot_data(self):
        self.plot_canvas.cla()
        is_data = False
        self.plot_canvas.grid()
        self.plot_canvas.set_xlabel('Time [s]')
        flat_basic_list = [
            item for sublist in self.data_handler.basic_datalog_keys[1:]
            for item in sublist
        ]
        len_aux_keys = len(self.data_handler.auxiliary_datalog_keys)
        for index in range(self.window.listWidget.count()):
            if self.window.listWidget.item(index).checkState() == Qt.Checked:
                if index < len_aux_keys:
                    elem = self.data_handler.auxiliary_datalog_keys[index]
                    self.plot_canvas.plot(
                        self.data_handler.basic_datalog['time[sec]'],
                        self.data_handler.auxiliary_datalog[elem],
                        label=elem)
                else:
                    elem = flat_basic_list[index - len_aux_keys]
                    self.plot_canvas.plot(
                        self.data_handler.basic_datalog['time[sec]'],
                        self.data_handler.basic_datalog[elem],
                        label=elem)
                is_data = True
        if is_data:
            self.plot_canvas.legend()

        self.canvas_.draw()
        return

    def plot_selected_data(self):
        plt.figure()
        plt.grid()
        plt.xlabel('Time [s]')
        flat_basic_list = [
            item for sublist in self.data_handler.basic_datalog_keys[1:]
            for item in sublist
        ]
        len_aux_keys = len(self.data_handler.auxiliary_datalog_keys)
        for index in range(self.window.listWidget.count()):
            if self.window.listWidget.item(index).checkState() == Qt.Checked:
                if index < len_aux_keys:
                    elem = self.data_handler.auxiliary_datalog_keys[index]
                    plt.plot(self.data_handler.basic_datalog['time[sec]'],
                             self.data_handler.auxiliary_datalog[elem],
                             label=elem)
                else:
                    elem = flat_basic_list[index - len_aux_keys]
                    plt.plot(self.data_handler.basic_datalog['time[sec]'],
                             self.data_handler.basic_datalog[elem],
                             label=elem)
        plt.legend()
        plt.show()
        return

    def gstime(self, jdut1):
        tut1 = (jdut1 - 2451545.0) / 36525.0
        temp = -6.2e-6 * tut1 * tut1 * tut1 + 0.093104 * tut1 * tut1 + \
               (876600.0 * 3600 + 8640184.812866) * tut1 + 67310.54841  # sec
        temp = (temp * deg2rad /
                240.0) % twopi  # 360/86400 = 1/240, to deg, to rad

        #  ------------------------ check quadrants ---------------------
        if temp < 0.0:
            temp += twopi
        return temp

    def jday(self, year, mon, day, hr, minute, sec):
        return (367.0 * year - 7.0 * (year +
                                      ((mon + 9.0) // 12.0)) * 0.25 // 1.0 +
                275.0 * mon // 9.0 + day + 1721013.5 +
                ((sec / 60.0 + minute) / 60.0 + hr) / 24.0  # ut in days
                #  - 0.5*sgn(100.0*year + mon - 190002.5) + 0.5;
                )
Example #25
0
class MainWindow(Qt.QMainWindow):
    def __init__(self, parent=None, show=True):
        Qt.QMainWindow.__init__(self, parent)
        with open('config.json') as f:
            self.config_data = json.load(f)
        self.query_matcher = QueryMatcher(self.config_data["FEATURE_DATA_FILE"])
        self.supported_file_types = [".ply", ".off"]
        self.buttons = {}
        self.ds = reader.DataSet("")
        self.meshes = []
        self.normalizer = Normalizer()
        self.smlw = None
        self.setWindowTitle('Source Mesh Window')
        self.frame = Qt.QFrame()
        self.QTIplotter = None
        self.vlayout = Qt.QVBoxLayout()
        self.frame.setLayout(self.vlayout)
        self.setCentralWidget(self.frame)
        self.hist_dict = {}
        self.setAcceptDrops(True)
        # Create main menu
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        exitButton = Qt.QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        viewMenu = mainMenu.addMenu('View')
        exitButton = Qt.QAction('Plot tSNE', self)
        exitButton.triggered.connect(self.plot_tsne)
        viewMenu.addAction(exitButton)

        # Create load button and init action
        self.load_button = QPushButton("Load or drop mesh to query")
        self.load_button.clicked.connect(lambda: self.load_and_prep_query_mesh(self.open_file_name_dialog()))
        self.load_button.setFont(QtGui.QFont("arial", 30))
        # Create Plots widget
        self.graphWidget = pg.PlotWidget()
        self.graphWidget.setBackground('w')

        # Create and add widgets to layout

        n_sing, n_hist, mapping_of_labels = get_sizes_features(features_file=self.config_data["FEATURE_DATA_FILE"],with_labels=True)

        # self.hist_labels = list({**FeatureExtractor.get_pipeline_functions()[1]}.values())
        self.hist_labels = [val for key, val in mapping_of_labels.items() if "hist_" in key]
        self.tableWidget = TableWidget({}, self, {})
        self.tableWidget.hide()
        self.vlayout.addWidget(self.load_button)

        # Position MainWindow
        screen_topleft = QDesktopWidget().availableGeometry().topLeft()
        screen_height = QDesktopWidget().availableGeometry().height()
        width = (QDesktopWidget().availableGeometry().width() * 0.4)
        self.move(screen_topleft)
        self.resize(width, screen_height - 50)

        if show:
            self.show()

    def dragEnterEvent(self, e):
        if e.mimeData().hasUrls():
            e.accept()
        else:
            e.ignore()

    def dropEvent(self, e):
        if len(e.mimeData().urls()) == 1:
            file = QUrl.toLocalFile(e.mimeData().urls()[0])
            self.load_and_prep_query_mesh(DataSet._read(file))
        else:
            error_dialog = QtWidgets.QErrorMessage(parent=self)
            error_dialog.showMessage("Please drag only one mesh at the time.")

    def check_file(self, fileName):
        if fileName[-4:] not in self.supported_file_types:
            error_dialog = QtWidgets.QErrorMessage(parent=self)
            error_dialog.showMessage(("Selected file not supported." f"\nPlease select mesh files of type: {self.supported_file_types}"))
            return False

    def open_file_name_dialog(self):
        fileName, _ = QFileDialog.getOpenFileName(self, caption="Choose shape to view.", filter="All Files (*);; Model Files (.obj, .off, .ply, .stl)")
        if not (fileName or self.check_file(fileName)):
            return False

        mesh = DataSet._read(fileName)
        return mesh

    def load_and_prep_query_mesh(self, data):
        if not data: return
        self.load_button.setFont(QtGui.QFont("arial", 10))

        # Normalize query mesh
        normed_data = self.normalizer.mono_run_pipeline(data)
        normed_mesh = pv.PolyData(normed_data["history"][-1]["data"]["vertices"], normed_data["history"][-1]["data"]["faces"])
        normed_data['poly_data'] = normed_mesh
        # Extract features
        n_singletons, n_distributionals, mapping_of_labels = get_sizes_features(features_file=self.config_data["FEATURE_DATA_FILE"],with_labels=True, drop_feat=["timestamp"])
        mapping_of_labels_reversed = {val: key for key, val in mapping_of_labels.items()}
        features_dict = FeatureExtractor.mono_run_pipeline_old(normed_data)
        features_dict_carefully_selected = OrderedDict(
            sorted({mapping_of_labels.get(key): val
                    for key, val in features_dict.items() if key in mapping_of_labels}.items(), key=lambda t: t[0]))
        features_df = pd.DataFrame([features_dict_carefully_selected]).T.reset_index()
        self.hist_labels = [val for key, val in mapping_of_labels.items() if "hist_" in key]
        self.skeleton_labels = [val for key, val in mapping_of_labels.items() if "skeleton_" in key]

        # feature_formatted_keys = sing_labels + dist_labels
        # features_df = pd.DataFrame({'key': list(feature_formatted_keys), 'value': list(
        #     [list(f) if isinstance(f, np.ndarray) else f for f in list(features_dict.values())[3:]])})

        # Update plotter & feature table
        # since unfortunately Qtinteractor which plots the mesh cannot be updated (remove and add new mesh)
        # it needs to be removed and newly generated each time a mesh gets loaded
        self.tableWidget.deleteLater()
        self.vlayout.removeWidget(self.QTIplotter)
        self.QTIplotter = QtInteractor(self.frame)
        self.vlayout.addWidget(self.QTIplotter)
        self.tableWidget = TableWidget(features_dict_carefully_selected, self, mapping_of_labels_reversed)
        self.tableWidget.show()
        self.tableWidget.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
        self.vlayout.addWidget(self.tableWidget)
        self.QTIplotter.add_mesh(normed_mesh, show_edges=True)
        self.QTIplotter.isometric_view()
        self.QTIplotter.show_bounds(grid='front', location='outer', all_edges=True)
        self.vlayout.addWidget(self.graphWidget)

        # Compare shapes
        if self.smlw:
            self.smlw.deleteLater()
            if len(self.smlw.smw_list) != 0: self.smlw.smw_list[0].deleteLater()
        self.smlw = SimilarMeshesListWindow(features_dict)

        self.buttons = self.tableWidget.get_buttons_in_table()
        self.hist_dict = features_df.set_index("index").tail(n=len(self.hist_labels)).to_dict()
        for key, value in self.buttons.items():
            value.clicked.connect(lambda state, x=key, y=features_dict_carefully_selected[key]: self.plot_selected_hist(x, y))
        self.smlw.show()

    def plot_selected_hist(self, hist_title, hist_data):
        self.graphWidget.clear()
        styles = {"color": "#f00", "font-size": "15px"}
        pen = pg.mkPen(color=(255, 0, 0), width=5, style=QtCore.SolidLine)
        self.graphWidget.setTitle(hist_title, color="b", size="15pt")
        self.graphWidget.setLabel("left", "Values", **styles)
        self.graphWidget.setLabel("bottom", "Bins", **styles)
        self.graphWidget.addLegend()
        self.graphWidget.showGrid(x=True, y=True)
        self.graphWidget.setXRange(1, len(hist_data))
        self.graphWidget.setYRange(min(hist_data), max(hist_data))
        self.graphWidget.plot(np.arange(0, len(hist_data)), hist_data, pen=pen)

    def plot_tsne(self):
        labels = [dic["label"].replace("_", " ").title() for dic in self.query_matcher.features_raw]
        filename = "tsne_visualizer"

        tsne_plotter = TsneVisualiser(
            self.query_matcher,
            labels,  # labels_coarse,
            filename,
            False,
            False)
        tsne_plotter.plot()
Example #26
0
class Viewer3D(QtWidgets.QWidget):
  def __init__(self, parent):
    super().__init__(parent)

    layout = QtWidgets.QHBoxLayout(self)
    layout.setContentsMargins(0, 0, 0, 0)
    
    self.plotter = QtInteractor(self, multi_samples=8, line_smoothing=True, point_smoothing=True)
    layout.addWidget(self.plotter.interactor)

    self.plotter.enable_anti_aliasing()
    
    for key in ["Up", "Down"]:
      self.plotter.clear_events_for_key(key)

    self.clear()    
    self.setLayout(layout)


  def clear(self):
    self.plotter.camera_set = False
    self.actors = []
    self.plotter.clear()


  def fix_camera(self):
    self.plotter.camera_set = True    

  def enable(self, enabled):
    if enabled:
      self.plotter.enable()
      self.plotter.interactor.setHidden(False)
    else:
      self.plotter.disable()
      self.plotter.interactor.setHidden(True)


  def add_mesh(self, mesh, transform=None, **kwargs):
      actor = self.plotter.add_mesh(mesh, **kwargs)

      if transform is not None: 
        actor.SetUserTransform(vtk_transform(transform))

      self.actors.append(actor)
      return actor


  def set_point_size(self, size):
    for actor in self.actors:
        actor.GetProperty().SetPointSize(size)
    self.update()

  def set_line_size(self, size):
    for actor in self.actors:
        actor.GetProperty().SetLineWidth(size)
    self.update()

  def update(self):
    self.plotter.interactor.update()


  def current_viewport(self):
    vp = save_viewport(self.plotter)
    return vp

  def set_viewport(self, viewport):
    set_viewport(self.plotter, viewport)
    self.update()
    

  def camera_viewport(self, camera, pose):
    return camera_viewport(camera.intrinsic, pose, self.plotter.window_size)