Hello SamT,
You can try the following script to crop the mesh to the selected shapes:
import Metashape, time
from PySide2 import QtGui, QtCore, QtWidgets
def point_inside(point, poly):
x, y = point.x, point.y
inside = False
p1x, p1y = poly[0]
for i in range(len(poly) + 1):
p2x, p2y = poly[i % len(poly)]
if y >= min(p1y, p2y):
if y <= max(p1y, p2y):
if x <= max(p1x, p2x):
if p1y != p2y:
xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
if p1x == p2x or x <= xinters:
inside = not inside
p1x, p1y = p2x, p2y
return inside
###########################
class CropMesh(QtWidgets.QDialog):
def __init__(self, parent):
QtWidgets.QDialog.__init__(self, parent)
self.btnQuit = QtWidgets.QPushButton("Close")
self.btnP1 = QtWidgets.QPushButton("Start")
self.pBar = QtWidgets.QProgressBar()
self.pBar.setTextVisible(False)
layout = QtWidgets.QGridLayout()
layout.addWidget(self.pBar,0, 0, 1, 2)
layout.addWidget(self.btnP1, 2, 0)
layout.addWidget(self.btnQuit, 2, 1)
self.setLayout(layout)
proc_height = lambda: self.estimate_height()
QtCore.QObject.connect(self.btnP1, QtCore.SIGNAL("clicked()"), proc_height)
QtCore.QObject.connect(self.btnQuit, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("reject()"))
self.exec()
def estimate_height(self):
doc = Metashape.app.document
chunk = doc.chunk
if not chunk:
print("Empty document, script aborted.")
return 0
model = chunk.model
if not model:
print("No mesh model, script aborted.")
return 0
if not chunk.shapes:
print("No shapes, script aborted.")
shapes = [shape for shape in chunk.shapes if shape.selected and (shape.type == Metashape.Shape.Type.Polygon)]
if len(shapes) < 1:
print("No shapes selected, script aborted.")
return 0
#shape = shapes[0]
t0 = time.time()
print("Script started...")
self.pBar.setValue(0)
app.processEvents()
self.btnP1.setDisabled(True)
self.btnQuit.setDisabled(True)
T = chunk.transform.matrix
m = chunk.crs.localframe(T.mulp(chunk.region.center))
M = m* T
scale = chunk.transform.scale
m_vertices = model.vertices
faces = model.faces
s_vertices = dict()
polygons = dict()
INIT_PROG = 1
self.pBar.setValue(INIT_PROG)
app.processEvents()
SHAPES_PROG = 10
processed = 0
for shape in shapes:
s_vertices[shape] = [m.mulp(chunk.crs.unproject(v)) for v in shape.vertices] #
polygons[shape] = [[v.x, v.y] for v in s_vertices[shape]]
processed += 1
self.pBar.setValue(INIT_PROG + int(SHAPES_PROG * processed / len(shapes)))
app.processEvents()
FACE_PROG = 80
face_step = int(len(faces) / FACE_PROG)
iface = 0
processed = 0
for face in faces:
for shape in shapes:
vert_center = list()
for i in range(3):
vert_center.append(m_vertices[face.vertices[i]].coord)
vert_center[i] = M.mulp(vert_center[i])
sum_area = False
for i in range(3):
if point_inside(vert_center[i], polygons[shape]):
sum_area = True
if sum_area:
v1 = vert_center[0] - vert_center[1]
v2 = vert_center[0] - vert_center[2]
area = Metashape.Vector.cross(v2, v1).norm() / 2 #* scale
face.selected = True
iface += 1
if iface > face_step:
iface = iface - face_step
processed += 1
self.pBar.setValue(INIT_PROG + SHAPES_PROG + int(processed))
app.processEvents()
OUTPUT_PROG = 9
processed = 0
chunk.model.cropSelection()
self.pBar.setValue(100)
app.processEvents()
Metashape.app.update()
t1 = time.time()
t1 -= t0
t1 = float(t1)
print("Script finished in " + "{:.2f}".format(t1) + " seconds.")
self.btnP1.setDisabled(False)
self.btnQuit.setDisabled(False)
app.processEvents()
return 1
def crop_mesh():
global app
app = QtWidgets.QApplication.instance()
parent = app.activeWindow()
dlg = CropMesh(parent)
label = "Custom menu/Crop mesh by shape"
Metashape.app.addMenuItem(label, crop_mesh)
print("To execute this script press {}".format(label))