|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
import sys
|
|
|
|
|
from PySide6.QtWidgets import QApplication, QMainWindow, QGraphicsView, QFileDialog, QColorDialog
|
|
|
|
|
from PySide6.QtUiTools import QUiLoader
|
|
|
|
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
|
|
|
|
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
|
|
|
|
|
from matplotlib.figure import Figure
|
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
from PySide6.QtWidgets import QVBoxLayout
|
|
|
|
|
import creat_3d
|
|
|
|
|
import numpy as np
|
|
|
|
|
import matplotlib as mpl
|
|
|
|
|
from matplotlib.path import Path
|
|
|
|
|
from matplotlib.patches import Patch
|
|
|
|
|
from PySide6.QtGui import QPalette
|
|
|
|
|
import random
|
|
|
|
|
from scipy.spatial import ConvexHull
|
|
|
|
|
# plt.legend(prop={'family': 'SimHei', 'size': 15})
|
|
|
|
|
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MainWindow(QMainWindow):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
super().__init__()
|
|
|
|
|
self.toolbar = None
|
|
|
|
|
self.canvas = None
|
|
|
|
|
self.file_paths = []
|
|
|
|
|
self.get_color = lambda n: list(map(lambda i: "#" + "%06x" % random.randint(0, 0xFFFFFF),range(n)))
|
|
|
|
|
self.len_color = ['k','k'] # 图例颜色选择值
|
|
|
|
|
self.sho_color = ['k','k']
|
|
|
|
|
self.ove_color = ['b','b']
|
|
|
|
|
self.colorbar = 'jet'
|
|
|
|
|
# self.num = len(self.color) # 记录选择颜色的次数
|
|
|
|
|
self.oilwell_color = 'k'
|
|
|
|
|
self.waterwell_color = 'k'
|
|
|
|
|
self.chose = 0
|
|
|
|
|
self.len_num, self.short_num, self.over_num = 0,0,0
|
|
|
|
|
self.box = 0
|
|
|
|
|
self.axes = 0
|
|
|
|
|
self.angle_axes = 0
|
|
|
|
|
self.length_axes = 0
|
|
|
|
|
self.datachange = 0
|
|
|
|
|
self.wellchange = 0
|
|
|
|
|
self.waterwell_chose=0
|
|
|
|
|
self.oilwell_chose=0
|
|
|
|
|
self.scatter = 0
|
|
|
|
|
self.all_axes = 0
|
|
|
|
|
self.len_axes_r, self.sho_axes_r,self.ver_axes_up=0,0,0
|
|
|
|
|
self.axes_size = 8
|
|
|
|
|
self.len_view=0
|
|
|
|
|
self.short_view=0
|
|
|
|
|
self.over_view=0
|
|
|
|
|
# 加载UI文件
|
|
|
|
|
self.ui = QUiLoader().load('QT/3d_plot_2.ui')
|
|
|
|
|
self.color_bar()
|
|
|
|
|
self.ui.colorbar_chose.currentTextChanged.connect(self.update_colorbar) # 更新colorbar
|
|
|
|
|
self.ui.chose_oilwell_color.clicked.connect(lambda: self.oil_well_color()) # 选择油井颜色
|
|
|
|
|
self.ui.chose_oilwell_color.clicked.connect(lambda: self.plot_density()) # 更新油井颜色
|
|
|
|
|
self.ui.chose_waterwell_color.clicked.connect(lambda: self.water_well_color()) # 选择水井颜色
|
|
|
|
|
self.ui.chose_waterwell_color.clicked.connect(lambda: self.plot_density()) # 更新水井颜色
|
|
|
|
|
# 更新图窗
|
|
|
|
|
self.ui.graphicsView.repaint()
|
|
|
|
|
self.ui.graphicsView.update()
|
|
|
|
|
self.graphics_layout = QVBoxLayout(self.ui.graphicsView)
|
|
|
|
|
|
|
|
|
|
self.ui.chose_datafile_button.clicked.connect(self.choose_datafile)
|
|
|
|
|
self.ui.chose_wellfile_button.clicked.connect(self.choose_wellfile)
|
|
|
|
|
|
|
|
|
|
self.ui.data_file_lineEdit.textChanged.connect(lambda: self.data_change())
|
|
|
|
|
self.ui.well_file_lineEdit.textChanged.connect(lambda: self.well_change())
|
|
|
|
|
# 绘制或清除数据和画布
|
|
|
|
|
self.ui.clear_data.clicked.connect(lambda: self.clear_data())
|
|
|
|
|
|
|
|
|
|
# 绘图设置中心比例
|
|
|
|
|
self.ui.center_scale.clicked.connect(lambda: self.center_scale())
|
|
|
|
|
self.ui.center_scale.clicked.connect(lambda: self.plot_density())
|
|
|
|
|
self.ui.level_chose.valueChanged.connect(lambda: self.plot_density())
|
|
|
|
|
# self.ui.gWeight_chose.valueChanged.connect(lambda: self.plot_density())
|
|
|
|
|
|
|
|
|
|
# 绘制bounding——box
|
|
|
|
|
self.ui.pushButton.clicked.connect(lambda: self.bounding_box())
|
|
|
|
|
|
|
|
|
|
# self.ui.pushButton_2.clicked.connect(lambda: self.Axes())
|
|
|
|
|
|
|
|
|
|
self.ui.pushButton_3.clicked.connect(lambda: self.axes_angles())
|
|
|
|
|
self.ui.axes_length.clicked.connect(lambda: self.axes_length())
|
|
|
|
|
self.ui.scatter_density.clicked.connect(lambda: self.scatter_density())
|
|
|
|
|
self.ui.all_axes.clicked.connect(lambda: self.all_axes_plot())
|
|
|
|
|
self.ui.axes_wordsize.valueChanged.connect(lambda: self.plot_density())
|
|
|
|
|
self.ui.len_view.clicked.connect(lambda: self._len_view())
|
|
|
|
|
self.ui.short_view.clicked.connect(lambda: self._short_view())
|
|
|
|
|
self.ui.over_view.clicked.connect(lambda: self._over_view())
|
|
|
|
|
self.ui.len_axes.clicked.connect(lambda: self._len_axes())
|
|
|
|
|
self.ui.short_axes.clicked.connect(lambda: self._short_axes())
|
|
|
|
|
self.ui.over_axes.clicked.connect(lambda: self._over_axes())
|
|
|
|
|
self.ui.len_color.clicked.connect(self.len_color_chose) # 选择长轴颜色
|
|
|
|
|
self.ui.short_color.clicked.connect(self.sho_color_chose) # 选择长轴颜色
|
|
|
|
|
self.ui.over_color.clicked.connect(self.ove_color_chose) # 选择长轴颜色
|
|
|
|
|
|
|
|
|
|
def _len_axes(self):
|
|
|
|
|
if self.len_axes_r==0:
|
|
|
|
|
self.len_axes_r = 1
|
|
|
|
|
else:
|
|
|
|
|
self.len_axes_r=0
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def _short_axes(self):
|
|
|
|
|
if self.sho_axes_r==0:
|
|
|
|
|
self.sho_axes_r=1
|
|
|
|
|
else:
|
|
|
|
|
self.sho_axes_r=0
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def _over_axes(self):
|
|
|
|
|
if self.ver_axes_up==0:
|
|
|
|
|
self.ver_axes_up=1
|
|
|
|
|
else:
|
|
|
|
|
self.ver_axes_up=0
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def _len_view(self):
|
|
|
|
|
if self.len_view==0:
|
|
|
|
|
self.len_view=1
|
|
|
|
|
self.over_view = 0
|
|
|
|
|
self.short_view = 0
|
|
|
|
|
else:
|
|
|
|
|
self.len_view=0
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def _short_view(self):
|
|
|
|
|
if self.short_view==0:
|
|
|
|
|
self.short_view=1
|
|
|
|
|
self.over_view = 0
|
|
|
|
|
self.len_view = 0
|
|
|
|
|
else:
|
|
|
|
|
self.short_view=0
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def _over_view(self):
|
|
|
|
|
if self.over_view == 0:
|
|
|
|
|
self.over_view = 1
|
|
|
|
|
self.len_view = 0
|
|
|
|
|
self.short_view = 0
|
|
|
|
|
else:
|
|
|
|
|
self.over_view = 0
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def all_axes_plot(self):
|
|
|
|
|
if self.all_axes==0:
|
|
|
|
|
self.all_axes=1
|
|
|
|
|
else:
|
|
|
|
|
self.all_axes=0
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def scatter_density(self):
|
|
|
|
|
if self.scatter==0:
|
|
|
|
|
self.scatter=1
|
|
|
|
|
else:
|
|
|
|
|
self.scatter=0
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def data_change(self):
|
|
|
|
|
if self.datachange == 1:
|
|
|
|
|
self.plot_density()
|
|
|
|
|
self.dat_c = 0
|
|
|
|
|
|
|
|
|
|
def well_change(self):
|
|
|
|
|
if self.wellchange == 1:
|
|
|
|
|
self.waterwell_chose=1
|
|
|
|
|
self.oilwell_chose=1
|
|
|
|
|
self.plot_density()
|
|
|
|
|
self.well_c = 0
|
|
|
|
|
|
|
|
|
|
def bounding_box(self):
|
|
|
|
|
if self.box == 0:
|
|
|
|
|
self.box = 1
|
|
|
|
|
else:
|
|
|
|
|
self.box = 0
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def Axes(self):
|
|
|
|
|
if self.axes==0:
|
|
|
|
|
self.axes=1
|
|
|
|
|
else:
|
|
|
|
|
self.axes=0
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def axes_angles(self):
|
|
|
|
|
if self.waterwell_chose==0:
|
|
|
|
|
self.waterwell_chose=1
|
|
|
|
|
else:
|
|
|
|
|
self.waterwell_chose=0
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def axes_length(self):
|
|
|
|
|
if self.length_axes==0:
|
|
|
|
|
self.length_axes=1
|
|
|
|
|
else:
|
|
|
|
|
self.length_axes=0
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def self_levels(self):
|
|
|
|
|
self.levels = self.ui.level_chose.value() # 选择层参数
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def self_gWeight(self):
|
|
|
|
|
self.gWeight = self.self.ui.gWeight_chose.value()
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def scene_fig(self):
|
|
|
|
|
fig = Figure()
|
|
|
|
|
# 在Figure对象中添加子图
|
|
|
|
|
ax = fig.add_subplot(111)
|
|
|
|
|
return fig, ax
|
|
|
|
|
|
|
|
|
|
def canvas_adjust(self, fig):
|
|
|
|
|
if self.canvas is not None:
|
|
|
|
|
# 从布局中删除旧的 canvas 和 toolbar
|
|
|
|
|
item = self.graphics_layout.takeAt(0)
|
|
|
|
|
while item:
|
|
|
|
|
widget = item.widget()
|
|
|
|
|
if widget:
|
|
|
|
|
widget.setParent(None)
|
|
|
|
|
item = self.graphics_layout.takeAt(0)
|
|
|
|
|
self.graphics_layout.removeWidget(self.canvas)
|
|
|
|
|
self.canvas = FigureCanvas(fig)
|
|
|
|
|
self.toolbar = NavigationToolbar(self.canvas, self.ui.graphicsView)
|
|
|
|
|
self.toolbar.setParent(self.canvas)
|
|
|
|
|
self.graphics_layout.addWidget(self.canvas)
|
|
|
|
|
self.adjustSize()
|
|
|
|
|
|
|
|
|
|
def choose_datafile(self):
|
|
|
|
|
self.datachange = 0
|
|
|
|
|
file_dialog = QFileDialog(self)
|
|
|
|
|
file_dialog.setFileMode(QFileDialog.ExistingFiles)
|
|
|
|
|
if file_dialog.exec():
|
|
|
|
|
self.file_paths += file_dialog.selectedFiles()
|
|
|
|
|
self.ui.data_file_lineEdit.setText(','.join(self.file_paths))
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def choose_wellfile(self):
|
|
|
|
|
self.wellchange=0
|
|
|
|
|
self.waterwell_chose=1
|
|
|
|
|
self.oilwell_chose=1
|
|
|
|
|
file_dialog = QFileDialog(self)
|
|
|
|
|
file_dialog.setFileMode(QFileDialog.ExistingFiles)
|
|
|
|
|
if file_dialog.exec():
|
|
|
|
|
file_paths = file_dialog.selectedFiles()
|
|
|
|
|
self.ui.well_file_lineEdit.setText(','.join(file_paths))
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def len_color_chose(self):
|
|
|
|
|
col = QColorDialog.getColor()
|
|
|
|
|
self.len_color[self.len_num] = col.name()
|
|
|
|
|
self.len_num += 1
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def sho_color_chose(self):
|
|
|
|
|
col = QColorDialog.getColor()
|
|
|
|
|
self.sho_color[self.short_num] = col.name()
|
|
|
|
|
self.short_num += 1
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def ove_color_chose(self):
|
|
|
|
|
col = QColorDialog.getColor()
|
|
|
|
|
self.ove_color[self.over_num] = col.name()
|
|
|
|
|
self.over_num += 1
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def oil_well_color(self):
|
|
|
|
|
col = QColorDialog.getColor()
|
|
|
|
|
self.oilwell_color = col.name()
|
|
|
|
|
|
|
|
|
|
def water_well_color(self):
|
|
|
|
|
col = QColorDialog.getColor()
|
|
|
|
|
self.waterwell_color = col.name()
|
|
|
|
|
|
|
|
|
|
def color_bar(self):
|
|
|
|
|
self.ui.colorbar_chose.addItem("jet")
|
|
|
|
|
self.ui.colorbar_chose.addItem("viridis")
|
|
|
|
|
self.ui.colorbar_chose.addItem("coolwarm")
|
|
|
|
|
self.ui.colorbar_chose.addItem('plasma')
|
|
|
|
|
self.ui.colorbar_chose.addItem("magma")
|
|
|
|
|
self.ui.colorbar_chose.addItem("inferno")
|
|
|
|
|
|
|
|
|
|
def update_colorbar(self):
|
|
|
|
|
colorbar_name = self.ui.colorbar_chose.currentText()
|
|
|
|
|
self.colorbar = plt.get_cmap(colorbar_name)
|
|
|
|
|
self.plot_density()
|
|
|
|
|
|
|
|
|
|
def clear_data(self):
|
|
|
|
|
self.file_paths = []
|
|
|
|
|
self.len_color = ['k', 'k']
|
|
|
|
|
self.sho_color = ['k', 'k']
|
|
|
|
|
self.ove_color = ['b', 'b']
|
|
|
|
|
self.len_num, self.short_num, self.over_num = 0, 0, 0
|
|
|
|
|
self.chose = 0
|
|
|
|
|
self.box = 0
|
|
|
|
|
self.axes = 0
|
|
|
|
|
self.waterwell_chose = 0
|
|
|
|
|
self.oilwell_chose = 0
|
|
|
|
|
self.length_axes = 0
|
|
|
|
|
self.datachange = 0
|
|
|
|
|
self.wellchange = 0
|
|
|
|
|
self.scatter = 0
|
|
|
|
|
self.all_axes = 0
|
|
|
|
|
self.len_axes_r, self.sho_axes_r, self.ver_axes_up = 0, 0, 0
|
|
|
|
|
self.axes_size = 8
|
|
|
|
|
self.len_view = 0
|
|
|
|
|
self.short_view = 0
|
|
|
|
|
self.over_view = 0
|
|
|
|
|
self.ui.data_file_lineEdit.clear()
|
|
|
|
|
self.ui.well_file_lineEdit.clear()
|
|
|
|
|
self.canvas.figure.clf() # 清除画布上的内容
|
|
|
|
|
self.canvas.draw()
|
|
|
|
|
|
|
|
|
|
def center_scale(self):
|
|
|
|
|
if self.oilwell_chose == 0:
|
|
|
|
|
self.oilwell_chose = 1
|
|
|
|
|
else:
|
|
|
|
|
self.oilwell_chose = 0
|
|
|
|
|
# self.num = 0
|
|
|
|
|
|
|
|
|
|
def plot_density(self):
|
|
|
|
|
fig, ax = self.scene_fig()
|
|
|
|
|
data_name = self.ui.data_file_lineEdit.text().split(',') # 文件输入 =================1
|
|
|
|
|
level = self.ui.level_chose.value() # 层参数 =================2
|
|
|
|
|
self.datachange=1
|
|
|
|
|
data = creat_3d.create()
|
|
|
|
|
legend_elements = []
|
|
|
|
|
if self.len_num == 2:
|
|
|
|
|
self.len_num = 0
|
|
|
|
|
if self.short_num ==2:
|
|
|
|
|
self.short_num = 0
|
|
|
|
|
if self.over_num==2:
|
|
|
|
|
self.over_num=0
|
|
|
|
|
for i in range(len(data_name)):
|
|
|
|
|
data.data_pre(data_name[i])
|
|
|
|
|
data.contours_pre(level)
|
|
|
|
|
vertices = data.vertices
|
|
|
|
|
faces = data.faces
|
|
|
|
|
"""画出密度等高线"""
|
|
|
|
|
# 绘制等值面
|
|
|
|
|
fig = plt.figure()
|
|
|
|
|
ax = fig.add_subplot(111, projection='3d')
|
|
|
|
|
if self.scatter == 1:
|
|
|
|
|
vertices = data.data
|
|
|
|
|
x = data.data[:, 0]
|
|
|
|
|
y = data.data[:, 1]
|
|
|
|
|
z = data.data[:, 2]
|
|
|
|
|
ax.scatter(x, y, z, s=5, alpha=0.5)
|
|
|
|
|
else:
|
|
|
|
|
ax.plot_trisurf(vertices[:, 0], vertices[:, 1], faces, vertices[:, 2],
|
|
|
|
|
cmap=self.colorbar, alpha=0.4, edgecolor='none')
|
|
|
|
|
|
|
|
|
|
# 添加三维网格对象填充曲面内部
|
|
|
|
|
mesh = Poly3DCollection(vertices[faces], alpha=0.2, cmap=self.colorbar)
|
|
|
|
|
ax.add_collection3d(mesh)
|
|
|
|
|
ax.set_xlim(vertices[:, 0].min()-100, vertices[:, 0].max()+100)
|
|
|
|
|
ax.set_ylim(vertices[:, 1].min() - 100, vertices[:, 1].max() + 100)
|
|
|
|
|
ax.set_zlim(vertices[:, 2].min() - 100, vertices[:, 2].max() + 100)
|
|
|
|
|
# 设置坐标轴标签
|
|
|
|
|
ax.set_xlabel('X')
|
|
|
|
|
ax.set_ylabel('Y')
|
|
|
|
|
ax.set_zlabel('Z')
|
|
|
|
|
ax.grid(None)
|
|
|
|
|
|
|
|
|
|
# self.OBB_3dbox(vertices, ax)
|
|
|
|
|
wells_name = self.ui.well_file_lineEdit.text() # 井文件输入 =================6
|
|
|
|
|
if wells_name == '':
|
|
|
|
|
z_value = vertices[:, 2].min()/2+vertices[:, 2].max()/2
|
|
|
|
|
self.OBB_3dbox(vertices, ax, z_value)
|
|
|
|
|
print('ax.azim {}'.format(ax.azim))
|
|
|
|
|
|
|
|
|
|
print('ax.elev {}'.format(ax.elev))
|
|
|
|
|
self.canvas_adjust(fig)
|
|
|
|
|
else:
|
|
|
|
|
wells = creat_3d.well_to_edge()
|
|
|
|
|
wells.wells_name_and_position(wells_name)
|
|
|
|
|
self.wellchange = 1
|
|
|
|
|
|
|
|
|
|
"""画井位信息"""
|
|
|
|
|
typee = wells.type
|
|
|
|
|
points = wells.position
|
|
|
|
|
namee = wells.name
|
|
|
|
|
for i in range(len(points)):
|
|
|
|
|
if typee[i][0] == 0 and self.oilwell_chose==1:
|
|
|
|
|
# oil_color = 'black' # 输入油井颜色选择 =================8
|
|
|
|
|
ax.scatter(points[i][0], points[i][1], points[i][2], marker='o', edgecolors=self.oilwell_color, facecolors='none', s=70)
|
|
|
|
|
ax.scatter(points[i][0], points[i][1], points[i][2], marker='o', edgecolors=self.oilwell_color, facecolors='none', s=40,
|
|
|
|
|
linewidths=0.5)
|
|
|
|
|
ax.scatter(points[i][0], points[i][1], points[i][2], marker='o', edgecolors=self.oilwell_color, facecolors='none', s=20)
|
|
|
|
|
ax.plot([points[i][0], points[i][0]], [points[i][1], points[i][1]],
|
|
|
|
|
[points[i][2], points[i][2] - 100], color='r')
|
|
|
|
|
ax.text(points[i][0], points[i][1], points[i][2] - 100, f'{namee[i][0]}',
|
|
|
|
|
fontdict={'size': '8', 'color': 'b'}, zorder=len(data_name) + 2) # 井名信息
|
|
|
|
|
elif typee[i][0]==1:
|
|
|
|
|
if self.waterwell_chose == 1:
|
|
|
|
|
ax.scatter(points[i][0], points[i][1], points[i][2], marker='o', edgecolors=self.waterwell_color, facecolors=self.waterwell_color, s=70, zorder=len(data_name)+1)
|
|
|
|
|
ax.plot([points[i][0],points[i][0]],[points[i][1],points[i][1]],[points[i][2],points[i][2]-100], color='r')
|
|
|
|
|
ax.text(points[i][0], points[i][1], points[i][2] -100, f'{namee[i][0]}',
|
|
|
|
|
fontdict={'size': '8', 'color': 'b'}, zorder=len(data_name) + 2) # 井名信息
|
|
|
|
|
continue
|
|
|
|
|
self.OBB_3dbox(vertices, ax, points[i][2])
|
|
|
|
|
self.canvas_adjust(fig)
|
|
|
|
|
|
|
|
|
|
def OBB_3dbox(self, points, ax, z_value):
|
|
|
|
|
"""
|
|
|
|
|
计算三维数据点的OBB最小包围盒
|
|
|
|
|
参数:
|
|
|
|
|
points: 三维数据点数组,形状为(N, 3)
|
|
|
|
|
返回:
|
|
|
|
|
obb_center: OBB包围盒中心点,形状为(3,)
|
|
|
|
|
obb_axes: OBB包围盒坐标轴,形状为(3, 3)
|
|
|
|
|
obb_extents: OBB包围盒各坐标轴的长度,形状为(3,)
|
|
|
|
|
"""
|
|
|
|
|
# 计算凸包
|
|
|
|
|
hull = ConvexHull(points)
|
|
|
|
|
hull_points = points[hull.vertices, :]
|
|
|
|
|
|
|
|
|
|
# 计算凸包的质心
|
|
|
|
|
hull_center = np.mean(hull_points, axis=0)
|
|
|
|
|
|
|
|
|
|
# 计算凸包的协方差矩阵
|
|
|
|
|
hull_cov = np.cov(hull_points, rowvar=False)
|
|
|
|
|
|
|
|
|
|
# 对协方差矩阵进行SVD分解
|
|
|
|
|
u, s, vt = np.linalg.svd(hull_cov)
|
|
|
|
|
|
|
|
|
|
# 计算OBB包围盒的坐标轴和长度
|
|
|
|
|
axes = vt.T
|
|
|
|
|
# 将点云变换到以重心为原点的坐标系下
|
|
|
|
|
transformed_points = np.dot(points, vt)
|
|
|
|
|
# x,y,z = transformed_points[:,0],transformed_points[:,1],transformed_points[:,2]
|
|
|
|
|
x, y, z = points[:, 0], points[:, 1], points[:, 2]
|
|
|
|
|
# 计算变换后的点云的最小包围盒
|
|
|
|
|
min_point = np.min(transformed_points, axis=0)
|
|
|
|
|
max_point = np.max(transformed_points, axis=0)
|
|
|
|
|
x_max, y_max, z_max = max_point[0], max_point[1], max_point[2]
|
|
|
|
|
x_min, y_min, z_min = min_point[0], min_point[1], min_point[2]
|
|
|
|
|
x_length = x_max - x_min
|
|
|
|
|
y_length = y_max - y_min
|
|
|
|
|
z_length = z_max - z_min
|
|
|
|
|
extents = [x_length, y_length, z_length]
|
|
|
|
|
center_new = min_point/2+max_point/2 # 新坐标轴下中心点坐标
|
|
|
|
|
center = np.dot(center_new, axes)
|
|
|
|
|
|
|
|
|
|
# 八个顶点坐标
|
|
|
|
|
vertices = np.array([
|
|
|
|
|
[x_min, y_min, z_min], [x_max, y_min, z_min], [x_max, y_max, z_min], [x_min, y_max, z_min],
|
|
|
|
|
[x_min, y_min, z_max], [x_max, y_min, z_max], [x_max, y_max, z_max], [x_min, y_max, z_max]
|
|
|
|
|
])
|
|
|
|
|
vertices = np.dot(vertices, axes)
|
|
|
|
|
# 计算所有棱的端点坐标
|
|
|
|
|
ed_1 = [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3]
|
|
|
|
|
ed_2 = [1, 2, 3, 0, 5, 6, 7, 4, 4, 5, 6, 7]
|
|
|
|
|
if self.box==1:
|
|
|
|
|
for i, j in zip(np.array(ed_1), np.array(ed_2)):
|
|
|
|
|
ax.plot([vertices[i][0], vertices[j][0]], [vertices[i][1], vertices[j][1]], zs=[vertices[i][2], vertices[j][2]], color='grey')
|
|
|
|
|
|
|
|
|
|
self.plot_axes(ax, axes, vertices, z_value)
|
|
|
|
|
return center, axes, extents
|
|
|
|
|
|
|
|
|
|
def plot_axes(self, ax, axes, vertices, z):
|
|
|
|
|
# 标注轴向
|
|
|
|
|
direct1 = vertices[0] - vertices[1]
|
|
|
|
|
direct2 = vertices[2] - vertices[1]
|
|
|
|
|
direct3= vertices[4] - vertices[0]
|
|
|
|
|
ang_1 = np.arccos(np.dot(direct1, np.array(([1, 0, 0]))) / np.linalg.norm(direct1)) / np.pi * 180
|
|
|
|
|
ang_2 = np.arccos(np.dot(direct2, np.array(([0, 1, 0]))) / np.linalg.norm(direct2)) / np.pi * 180
|
|
|
|
|
ang_3 = np.arccos(np.dot(direct3, np.array(([0, 0, 1]))) / np.linalg.norm(direct3)) / np.pi * 180
|
|
|
|
|
if self.over_view==1:
|
|
|
|
|
ax.view_init(elev=90, azim=-90) # 俯视图
|
|
|
|
|
|
|
|
|
|
print(np.dot(direct1,direct2), ang_1, ang_2, ang_3)
|
|
|
|
|
# 标注长短轴
|
|
|
|
|
if np.linalg.norm(vertices[0] - vertices[1]) >= np.linalg.norm(vertices[1] - vertices[2]):
|
|
|
|
|
label1 = '长轴'
|
|
|
|
|
label2 = '短轴'
|
|
|
|
|
# 垂直长轴方向
|
|
|
|
|
if self.len_view==1:
|
|
|
|
|
if vertices[0][0] > 0:
|
|
|
|
|
ax.view_init(elev=ang_3, azim=90 - ang_2)
|
|
|
|
|
else:
|
|
|
|
|
ax.view_init(elev=ang_3, azim=90 - ang_1)
|
|
|
|
|
|
|
|
|
|
# # 垂直短轴方向
|
|
|
|
|
if self.short_view==1:
|
|
|
|
|
if vertices[0][0] > 0:
|
|
|
|
|
ax.view_init(elev=ang_3, azim=ang_1+180)
|
|
|
|
|
else:
|
|
|
|
|
ax.view_init(elev=ang_3, azim=ang_2+180)
|
|
|
|
|
else:
|
|
|
|
|
label1 = '短轴'
|
|
|
|
|
label2 = '长轴'
|
|
|
|
|
# 垂直长轴方向
|
|
|
|
|
if self.len_view==1:
|
|
|
|
|
if vertices[0][0] > 0:
|
|
|
|
|
ax.view_init(elev=ang_3, azim=360-ang_1)
|
|
|
|
|
else:
|
|
|
|
|
ax.view_init(elev=ang_3, azim=360-ang_2)
|
|
|
|
|
# # 垂直短轴方向
|
|
|
|
|
if self.short_view==1:
|
|
|
|
|
if vertices[0][0] > 0:
|
|
|
|
|
ax.view_init(elev=ang_3, azim=270 - ang_1)
|
|
|
|
|
else:
|
|
|
|
|
ax.view_init(elev=ang_3, azim=270 - ang_2)
|
|
|
|
|
# 画长短轴
|
|
|
|
|
center = [0, 0, z]
|
|
|
|
|
if (self.all_axes == 1 or self.len_axes_r==1) and label1 == '长轴':
|
|
|
|
|
ax.quiver(*center, *np.dot(axes[0],vertices[0]-center)*axes[0]
|
|
|
|
|
,color=self.len_color[0], length=1, arrow_length_ratio=0.01)
|
|
|
|
|
ax.quiver(*center, *np.dot(axes[0], vertices[2]-center) * axes[0],
|
|
|
|
|
color=self.len_color[1], length=1, arrow_length_ratio=0.01)
|
|
|
|
|
elif (self.all_axes == 1 or self.len_axes_r==1) and label2 == '长轴':
|
|
|
|
|
ax.quiver(*center, *np.dot(axes[1], vertices[1]-center) * axes[1],
|
|
|
|
|
color=self.len_color[0], length=1, arrow_length_ratio=0.01)
|
|
|
|
|
ax.quiver(*center, *np.dot(axes[1], vertices[3]-center) * axes[1],
|
|
|
|
|
color=self.len_color[1], length=1, arrow_length_ratio=0.01)
|
|
|
|
|
if (self.all_axes == 1 or self.sho_axes_r==1) and label1 == '短轴':
|
|
|
|
|
ax.quiver(*center, *np.dot(axes[0], vertices[0] - center) * axes[0]
|
|
|
|
|
, color=self.sho_color[0], length=1, arrow_length_ratio=0.01)
|
|
|
|
|
ax.quiver(*center, *np.dot(axes[0], vertices[2] - center) * axes[0],
|
|
|
|
|
color=self.sho_color[1], length=1, arrow_length_ratio=0.01)
|
|
|
|
|
elif (self.all_axes == 1 or self.sho_axes_r==1) and label2 == '短轴':
|
|
|
|
|
ax.quiver(*center, *np.dot(axes[1], vertices[1] - center) * axes[1],
|
|
|
|
|
color=self.sho_color[0], length=1, arrow_length_ratio=0.01)
|
|
|
|
|
ax.quiver(*center, *np.dot(axes[1], vertices[3] - center) * axes[1],
|
|
|
|
|
color=self.sho_color[1], length=1, arrow_length_ratio=0.01)
|
|
|
|
|
if self.all_axes == 1 or self.ver_axes_up == 1:
|
|
|
|
|
ax.quiver(*center, *axes[2] * np.dot(axes[2], vertices[4] - center), color=self.ove_color[0], length=1,
|
|
|
|
|
arrow_length_ratio=0.01)
|
|
|
|
|
ax.quiver(*center, *axes[2] * np.dot(axes[2], vertices[0] - center), color=self.ove_color[1], length=1,
|
|
|
|
|
arrow_length_ratio=0.01)
|
|
|
|
|
if self.length_axes == 1:
|
|
|
|
|
len_1 = np.abs(np.dot(axes[0], vertices[0]-center))
|
|
|
|
|
len_2 = np.abs(np.dot(axes[0], vertices[2]-center))
|
|
|
|
|
len_3 = np.abs(np.dot(axes[1], vertices[1]-center))
|
|
|
|
|
len_4 = np.abs(np.dot(axes[1], vertices[3]-center))
|
|
|
|
|
if vertices[0][0]-vertices[1][0] >= 0:
|
|
|
|
|
lb1 = '右半轴长'
|
|
|
|
|
lb2 = '左半轴长'
|
|
|
|
|
l1 = 3/4
|
|
|
|
|
l2 = 6/5
|
|
|
|
|
else:
|
|
|
|
|
lb1 = '左半轴长'
|
|
|
|
|
lb2 = '右半轴长'
|
|
|
|
|
l1 = 6 / 5
|
|
|
|
|
l2 = 3 / 4
|
|
|
|
|
if vertices[2][0]-vertices[1][0] >= 0:
|
|
|
|
|
lb3 = '右半轴长'
|
|
|
|
|
lb4 = '左半轴长'
|
|
|
|
|
l3 = 3 / 4
|
|
|
|
|
l4 = 6 / 5
|
|
|
|
|
else:
|
|
|
|
|
lb3 = '左半轴长'
|
|
|
|
|
lb4 = '右半轴长'
|
|
|
|
|
l3 = 6 / 5
|
|
|
|
|
l4 = 3 / 4
|
|
|
|
|
lz = 1
|
|
|
|
|
if ang_1<=90:
|
|
|
|
|
rota1=-ang_1
|
|
|
|
|
else:
|
|
|
|
|
rota1=180-ang_1
|
|
|
|
|
if ang_2<=90:
|
|
|
|
|
rota2=ang_2
|
|
|
|
|
else:
|
|
|
|
|
rota2=180-ang_2
|
|
|
|
|
word_size = self.ui.axes_wordsize.value()
|
|
|
|
|
if (self.all_axes == 1 or self.len_axes_r == 1) and label1 == '长轴':
|
|
|
|
|
ax.text(center[0] + l1 * (vertices[0][0] / 2 - vertices[1][0] / 2),
|
|
|
|
|
center[1] + l1 * (vertices[0][1] / 2 - vertices[1][1] / 2),
|
|
|
|
|
center[2] + lz * (vertices[0][2] / 2 - vertices[1][2] / 2),
|
|
|
|
|
label1+lb1 + f'{round(len_1,2)}m',
|
|
|
|
|
fontdict={'size': word_size, 'color': self.len_color[0], 'family': 'SimHei'}, rotation=rota1) # 标出距离
|
|
|
|
|
ax.text(center[0]+l2*(-vertices[0][0] / 2 + vertices[1][0] / 2),
|
|
|
|
|
center[1] + l2*(-vertices[0][1] / 2 + vertices[1][1] / 2),
|
|
|
|
|
center[2] + lz * (-vertices[0][2] / 2 + vertices[1][2] / 2),
|
|
|
|
|
label1+ lb2 + f'{round(len_2, 2)}m',
|
|
|
|
|
fontdict={'size': word_size, 'color': self.len_color[1], 'family': 'SimHei'}, rotation=rota1) # 标出距离
|
|
|
|
|
elif (self.all_axes == 1 or self.len_axes_r == 1) and label2 == '长轴':
|
|
|
|
|
ax.text(center[0] + l3 * (vertices[2][0] / 2 - vertices[1][0] / 2),
|
|
|
|
|
center[1] + l3 * (vertices[2][1] / 2 - vertices[1][1] / 2),
|
|
|
|
|
center[2] + lz * (vertices[2][2] / 2 - vertices[1][2] / 2),
|
|
|
|
|
label2+lb3 + f'{round(len_4, 2)}m',
|
|
|
|
|
fontdict={'size': word_size, 'color': self.len_color[1], 'family': 'SimHei'}, rotation=rota2) # 标出距离
|
|
|
|
|
ax.text(center[0] + l4 * (-vertices[2][0] / 2 + vertices[1][0] / 2),
|
|
|
|
|
center[1] + l4 * (-vertices[2][1] / 2 + vertices[1][1] / 2),
|
|
|
|
|
center[2] + lz * (-vertices[2][2] / 2 + vertices[1][2] / 2),
|
|
|
|
|
label2 + lb4 + f'{round(len_3, 2)}m',
|
|
|
|
|
fontdict={'size': word_size, 'color': self.len_color[0], 'family': 'SimHei'}, rotation=rota2) # 标出距离
|
|
|
|
|
if (self.all_axes == 1 or self.sho_axes_r == 1) and label1 == '短轴':
|
|
|
|
|
ax.text(center[0] + l1 * (vertices[0][0] / 2 - vertices[1][0] / 2),
|
|
|
|
|
center[1] + l1 * (vertices[0][1] / 2 - vertices[1][1] / 2),
|
|
|
|
|
center[2] + lz * (vertices[0][2] / 2 - vertices[1][2] / 2),
|
|
|
|
|
label1 + lb1 + f'{round(len_1, 2)}m',
|
|
|
|
|
fontdict={'size': word_size, 'color': self.sho_color[0], 'family': 'SimHei'},
|
|
|
|
|
rotation=rota1) # 标出距离
|
|
|
|
|
ax.text(center[0] + l2 * (-vertices[0][0] / 2 + vertices[1][0] / 2),
|
|
|
|
|
center[1] + l2 * (-vertices[0][1] / 2 + vertices[1][1] / 2),
|
|
|
|
|
center[2] + lz * (-vertices[0][2] / 2 + vertices[1][2] / 2),
|
|
|
|
|
label1 + lb2 + f'{round(len_2, 2)}m',
|
|
|
|
|
fontdict={'size': word_size, 'color': self.sho_color[1], 'family': 'SimHei'},
|
|
|
|
|
rotation=rota1) # 标出距离
|
|
|
|
|
elif (self.all_axes == 1 or self.sho_axes_r == 1) and label2 == '短轴':
|
|
|
|
|
ax.text(center[0] + l3 * (vertices[2][0] / 2 - vertices[1][0] / 2),
|
|
|
|
|
center[1] + l3 * (vertices[2][1] / 2 - vertices[1][1] / 2),
|
|
|
|
|
center[2] + lz * (vertices[2][2] / 2 - vertices[1][2] / 2),
|
|
|
|
|
label2 + lb3 + f'{round(len_3, 2)}m',
|
|
|
|
|
fontdict={'size': word_size, 'color': self.sho_color[0], 'family': 'SimHei'},
|
|
|
|
|
rotation=rota2) # 标出距离
|
|
|
|
|
ax.text(center[0] + l4 * (-vertices[2][0] / 2 + vertices[1][0] / 2),
|
|
|
|
|
center[1] + l4 * (-vertices[2][1] / 2 + vertices[1][1] / 2),
|
|
|
|
|
center[2] + lz * (-vertices[2][2] / 2 + vertices[1][2] / 2),
|
|
|
|
|
label2 + lb4 + f'{round(len_4, 2)}m',
|
|
|
|
|
fontdict={'size': word_size, 'color': self.sho_color[1], 'family': 'SimHei'},
|
|
|
|
|
rotation=rota2) # 标出距离
|
|
|
|
|
len_5 = np.abs(np.dot(axes[2], vertices[4]-center))
|
|
|
|
|
len_6 = np.abs(np.dot(axes[2], vertices[0]-center))
|
|
|
|
|
if self.all_axes == 1 or self.ver_axes_up == 1:
|
|
|
|
|
ax.text(center[0] + 6 / 5 * (vertices[4][0] / 2 - vertices[0][0] / 2),
|
|
|
|
|
center[1] + 6 / 5 * (vertices[4][1] / 2 - vertices[0][1] / 2),
|
|
|
|
|
center[2] + 6 / 5 * (vertices[4][2] / 2 - vertices[0][2] / 2),
|
|
|
|
|
'纵轴' + '上半轴' + f'{round(len_5, 2)}m',
|
|
|
|
|
fontdict={'size': word_size, 'color': self.ove_color[0], 'family': 'SimHei'},
|
|
|
|
|
rotation=rota2) # 标出距离
|
|
|
|
|
ax.text(center[0] + 6 / 5 * (-vertices[4][0] / 2 + vertices[0][0] / 2),
|
|
|
|
|
center[1] + 6 / 5 * (-vertices[4][1] / 2 + vertices[0][1] / 2),
|
|
|
|
|
center[2] + 6 / 5 * (-vertices[4][2] / 2 + vertices[0][2] / 2),
|
|
|
|
|
'纵轴下半轴' + f'{round(len_6, 2)}m',
|
|
|
|
|
fontdict={'size': word_size, 'color': self.ove_color[1], 'family': 'SimHei'},
|
|
|
|
|
rotation=rota2) # 标出距离
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
app = QApplication([])
|
|
|
|
|
stats = MainWindow()
|
|
|
|
|
stats.ui.show()
|
|
|
|
|
app.exec()
|